diff --git a/awx/__init__.py b/awx/__init__.py index c8081e8794..81659056aa 100644 --- a/awx/__init__.py +++ b/awx/__init__.py @@ -5,7 +5,7 @@ import os import sys import warnings -__version__ = '3.0.0' +__version__ = '3.0.1' __all__ = ['__version__'] diff --git a/awx/api/views.py b/awx/api/views.py index 11ae8f6766..7d1a855ed7 100644 --- a/awx/api/views.py +++ b/awx/api/views.py @@ -1573,6 +1573,7 @@ class InventoryScanJobTemplateList(SubListAPIView): class HostList(ListCreateAPIView): + always_allow_superuser = False model = Host serializer_class = HostSerializer diff --git a/awx/main/access.py b/awx/main/access.py index 5b2ee91851..d8fe5b245b 100644 --- a/awx/main/access.py +++ b/awx/main/access.py @@ -245,16 +245,18 @@ class UserAccess(BaseAccess): def can_add(self, data): - if data is not None and 'is_superuser' in data: - if to_python_boolean(data['is_superuser'], allow_none=True) and not self.user.is_superuser: + if data is not None and ('is_superuser' in data or 'is_system_auditor' in data): + if (to_python_boolean(data.get('is_superuser', 'false'), allow_none=True) or + to_python_boolean(data.get('is_system_auditor', 'false'), allow_none=True)) and not self.user.is_superuser: return False if self.user.is_superuser: return True return Organization.accessible_objects(self.user, 'admin_role').exists() def can_change(self, obj, data): - if data is not None and 'is_superuser' in data: - if to_python_boolean(data['is_superuser'], allow_none=True) and not self.user.is_superuser: + if data is not None and ('is_superuser' in data or 'is_system_auditor' in data): + if (to_python_boolean(data.get('is_superuser', 'false'), allow_none=True) or + to_python_boolean(data.get('is_system_auditor', 'false'), allow_none=True)) and not self.user.is_superuser: return False # A user can be changed if they are themselves, or by org admins or # superusers. Change permission implies changing only certain fields @@ -720,18 +722,25 @@ class TeamAccess(BaseAccess): def can_attach(self, obj, sub_obj, relationship, *args, **kwargs): """Reverse obj and sub_obj, defer to RoleAccess if this is an assignment of a resource role to the team.""" - if isinstance(sub_obj, Role) and isinstance(sub_obj.content_object, ResourceMixin): - role_access = RoleAccess(self.user) - return role_access.can_attach(sub_obj, obj, 'member_role.parents', - *args, **kwargs) + if isinstance(sub_obj, Role): + if sub_obj.content_object is None: + raise PermissionDenied("The {} role cannot be assigned to a team".format(sub_obj.name)) + elif isinstance(sub_obj.content_object, User): + raise PermissionDenied("The admin_role for a User cannot be assigned to a team") + + if isinstance(sub_obj.content_object, ResourceMixin): + role_access = RoleAccess(self.user) + return role_access.can_attach(sub_obj, obj, 'member_role.parents', + *args, **kwargs) return super(TeamAccess, self).can_attach(obj, sub_obj, relationship, *args, **kwargs) def can_unattach(self, obj, sub_obj, relationship, *args, **kwargs): - if isinstance(sub_obj, Role) and isinstance(sub_obj.content_object, ResourceMixin): - role_access = RoleAccess(self.user) - return role_access.can_unattach(sub_obj, obj, 'member_role.parents', - *args, **kwargs) + if isinstance(sub_obj, Role): + if isinstance(sub_obj.content_object, ResourceMixin): + role_access = RoleAccess(self.user) + return role_access.can_unattach(sub_obj, obj, 'member_role.parents', + *args, **kwargs) return super(TeamAccess, self).can_unattach(obj, sub_obj, relationship, *args, **kwargs) @@ -906,8 +915,7 @@ class JobTemplateAccess(BaseAccess): project = get_value(Project, 'project') if 'job_type' in data and data['job_type'] == PERM_INVENTORY_SCAN: if inventory: - org = inventory.organization - accessible = self.user in org.admin_role + accessible = self.user in inventory.use_role else: accessible = False if not project and accessible: @@ -979,7 +987,8 @@ class JobTemplateAccess(BaseAccess): for k, v in data.items(): if hasattr(obj, k) and getattr(obj, k) != v: - if k not in field_whitelist and v != getattr(obj, '%s_id' % k, None): + if k not in field_whitelist and v != getattr(obj, '%s_id' % k, None) \ + and not (hasattr(obj, '%s_id' % k) and getattr(obj, '%s_id' % k) is None and v == ''): # Equate '' to None in the case of foreign keys return False return True @@ -1681,8 +1690,7 @@ class RoleAccess(BaseAccess): if not check_user_access(self.user, sub_obj.__class__, 'read', sub_obj): return False - if obj.object_id and \ - isinstance(obj.content_object, ResourceMixin) and \ + if isinstance(obj.content_object, ResourceMixin) and \ self.user in obj.content_object.admin_role: return True return False diff --git a/awx/main/management/commands/inventory_import.py b/awx/main/management/commands/inventory_import.py index 73bb0f99f4..21031f23cc 100644 --- a/awx/main/management/commands/inventory_import.py +++ b/awx/main/management/commands/inventory_import.py @@ -481,6 +481,7 @@ def load_inventory_source(source, all_group=None, group_filter_re=None, # Sanity check: We sanitize these module names for our API but Ansible proper doesn't follow # good naming conventions source = source.replace('azure.py', 'windows_azure.py') + source = source.replace('satellite6.py', 'foreman.py') logger.debug('Analyzing type of source: %s', source) original_all_group = all_group if not os.path.exists(source): diff --git a/awx/main/migrations/0010_v300_create_system_job_templates.py b/awx/main/migrations/0010_v300_create_system_job_templates.py index abf336efaa..e8b227a11d 100644 --- a/awx/main/migrations/0010_v300_create_system_job_templates.py +++ b/awx/main/migrations/0010_v300_create_system_job_templates.py @@ -4,9 +4,6 @@ from __future__ import unicode_literals from django.db import migrations, models from django.utils.timezone import now -from awx.api.license import feature_enabled - - def create_system_job_templates(apps, schema_editor): ''' Create default system job templates if not present. Create default schedules @@ -80,7 +77,7 @@ def create_system_job_templates(apps, schema_editor): polymorphic_ctype=sjt_ct, ), ) - if created and feature_enabled('system_tracking', bypass_database=True): + if created: sched = Schedule( name='Cleanup Fact Schedule', rrule='DTSTART:%s RRULE:FREQ=MONTHLY;INTERVAL=1;BYMONTHDAY=1' % now_str, diff --git a/awx/main/migrations/_rbac.py b/awx/main/migrations/_rbac.py index 4a1115c4b3..b60ac65691 100644 --- a/awx/main/migrations/_rbac.py +++ b/awx/main/migrations/_rbac.py @@ -8,25 +8,8 @@ from collections import defaultdict from awx.main.utils import getattrd from awx.main.models.rbac import Role, batch_role_ancestor_rebuilding -logger = logging.getLogger(__name__) +logger = logging.getLogger('rbac_migrations') -def log_migration(wrapped): - '''setup the logging mechanism for each migration method - as it runs, Django resets this, so we use a decorator - to re-add the handler for each method. - ''' - handler = logging.FileHandler("/tmp/tower_rbac_migrations.log", mode="a", encoding="UTF-8") - formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') - handler.setLevel(logging.DEBUG) - handler.setFormatter(formatter) - - def wrapper(*args, **kwargs): - logger.handlers = [] - logger.addHandler(handler) - return wrapped(*args, **kwargs) - return wrapper - -@log_migration def create_roles(apps, schema_editor): ''' Implicit role creation happens in our post_save hook for all of our @@ -56,7 +39,6 @@ def create_roles(apps, schema_editor): obj.save() -@log_migration def migrate_users(apps, schema_editor): User = apps.get_model('auth', "User") Role = apps.get_model('main', "Role") @@ -89,7 +71,6 @@ def migrate_users(apps, schema_editor): sa_role.members.add(user) logger.warning(smart_text(u"added superuser: {}".format(user.username))) -@log_migration def migrate_organization(apps, schema_editor): Organization = apps.get_model('main', "Organization") for org in Organization.objects.iterator(): @@ -100,7 +81,6 @@ def migrate_organization(apps, schema_editor): org.member_role.members.add(user) logger.info(smart_text(u"added member: {}, {}".format(org.name, user.username))) -@log_migration def migrate_team(apps, schema_editor): Team = apps.get_model('main', 'Team') for t in Team.objects.iterator(): @@ -172,7 +152,6 @@ def _discover_credentials(instances, cred, orgfunc): _update_credential_parents(org, cred) -@log_migration def migrate_credential(apps, schema_editor): Credential = apps.get_model('main', "Credential") JobTemplate = apps.get_model('main', 'JobTemplate') @@ -180,7 +159,7 @@ def migrate_credential(apps, schema_editor): InventorySource = apps.get_model('main', 'InventorySource') for cred in Credential.objects.iterator(): - results = [x for x in JobTemplate.objects.filter(Q(credential=cred) | Q(cloud_credential=cred)).all()] + \ + results = [x for x in JobTemplate.objects.filter(Q(credential=cred) | Q(cloud_credential=cred), inventory__isnull=False).all()] + \ [x for x in InventorySource.objects.filter(credential=cred).all()] if cred.deprecated_team is not None and results: if len(results) == 1: @@ -210,7 +189,6 @@ def migrate_credential(apps, schema_editor): logger.warning(smart_text(u"orphaned credential found Credential(name={}, kind={}, host={}), superuser only".format(cred.name, cred.kind, cred.host, ))) -@log_migration def migrate_inventory(apps, schema_editor): Inventory = apps.get_model('main', 'Inventory') Permission = apps.get_model('main', 'Permission') @@ -254,7 +232,6 @@ def migrate_inventory(apps, schema_editor): execrole.members.add(perm.user) logger.info(smart_text(u'added User({}) access to Inventory({})'.format(perm.user.username, inventory.name))) -@log_migration def migrate_projects(apps, schema_editor): ''' I can see projects when: @@ -368,7 +345,6 @@ def migrate_projects(apps, schema_editor): -@log_migration def migrate_job_templates(apps, schema_editor): ''' NOTE: This must be run after orgs, inventory, projects, credential, and @@ -420,6 +396,11 @@ def migrate_job_templates(apps, schema_editor): jt_queryset = JobTemplate.objects.select_related('inventory', 'project', 'inventory__organization', 'execute_role') for jt in jt_queryset.iterator(): + if jt.inventory is None: + # If inventory is None, then only system admins and org admins can + # do anything with the JT in 2.4 + continue + jt_permission_qs = Permission.objects.filter( inventory=jt.inventory, project=jt.project, @@ -494,7 +475,6 @@ def migrate_job_templates(apps, schema_editor): -@log_migration def rebuild_role_hierarchy(apps, schema_editor): logger.info('Computing role roots..') start = time() diff --git a/awx/main/migrations/_system_tracking.py b/awx/main/migrations/_system_tracking.py index 9c77fb10f3..931c5c467c 100644 --- a/awx/main/migrations/_system_tracking.py +++ b/awx/main/migrations/_system_tracking.py @@ -9,25 +9,8 @@ from awx.fact.utils.dbtransform import KeyTransform from mongoengine.connection import ConnectionError from pymongo.errors import OperationFailure -logger = logging.getLogger(__name__) +logger = logging.getLogger('system_tracking_migrations') -def log_migration(wrapped): - '''setup the logging mechanism for each migration method - as it runs, Django resets this, so we use a decorator - to re-add the handler for each method. - ''' - handler = logging.FileHandler("/tmp/tower_system_tracking_migrations.log", mode="a", encoding="UTF-8") - formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') - handler.setLevel(logging.DEBUG) - handler.setFormatter(formatter) - - def wrapper(*args, **kwargs): - logger.handlers = [] - logger.addHandler(handler) - return wrapped(*args, **kwargs) - return wrapper - -@log_migration def migrate_facts(apps, schema_editor): Fact = apps.get_model('main', "Fact") Host = apps.get_model('main', "Host") @@ -52,7 +35,7 @@ def migrate_facts(apps, schema_editor): migrated_count = 0 not_migrated_count = 0 transform = KeyTransform([('.', '\uff0E'), ('$', '\uff04')]) - for factver in FactVersion.objects.all(): + for factver in FactVersion.objects.all().no_cache(): try: host = Host.objects.only('id').get(inventory__id=factver.host.inventory_id, name=factver.host.hostname) fact_obj = transform.replace_outgoing(factver.fact) diff --git a/awx/main/migrations/_team_cleanup.py b/awx/main/migrations/_team_cleanup.py index 1a937d1f88..7c0db85309 100644 --- a/awx/main/migrations/_team_cleanup.py +++ b/awx/main/migrations/_team_cleanup.py @@ -2,25 +2,8 @@ import logging from django.utils.encoding import smart_text -logger = logging.getLogger(__name__) +logger = logging.getLogger('rbac_migrations') -def log_migration(wrapped): - '''setup the logging mechanism for each migration method - as it runs, Django resets this, so we use a decorator - to re-add the handler for each method. - ''' - handler = logging.FileHandler("/tmp/tower_rbac_migrations.log", mode="a", encoding="UTF-8") - formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s') - handler.setLevel(logging.DEBUG) - handler.setFormatter(formatter) - - def wrapper(*args, **kwargs): - logger.handlers = [] - logger.addHandler(handler) - return wrapped(*args, **kwargs) - return wrapper - -@log_migration def migrate_team(apps, schema_editor): '''If an orphan team exists that is still active, delete it.''' Team = apps.get_model('main', 'Team') diff --git a/awx/main/models/unified_jobs.py b/awx/main/models/unified_jobs.py index 36b2dfe75e..f7106eb7ab 100644 --- a/awx/main/models/unified_jobs.py +++ b/awx/main/models/unified_jobs.py @@ -873,7 +873,7 @@ class UnifiedJob(PolymorphicModel, PasswordFieldsModel, CommonModelNameNotUnique if not self.cancel_flag: self.cancel_flag = True cancel_fields = ['cancel_flag'] - if self.status in ('pending', 'waiting'): + if self.status in ('pending', 'waiting', 'new'): self.status = 'canceled' cancel_fields.append('status') self.save(update_fields=cancel_fields) diff --git a/awx/main/notifications/webhook_backend.py b/awx/main/notifications/webhook_backend.py index fbefe78037..e74f39f654 100644 --- a/awx/main/notifications/webhook_backend.py +++ b/awx/main/notifications/webhook_backend.py @@ -3,7 +3,6 @@ import logging import requests -import json from django.utils.encoding import smart_text @@ -32,7 +31,7 @@ class WebhookBackend(TowerBaseEmailBackend): self.headers['User-Agent'] = "Tower {}".format(get_awx_version()) for m in messages: r = requests.post("{}".format(m.recipients()[0]), - data=json.dumps(m.body), + json=m.body, headers=self.headers) if r.status_code >= 400: logger.error(smart_text("Error sending notification webhook: {}".format(r.text))) diff --git a/awx/main/tasks.py b/awx/main/tasks.py index 2325702da6..17268515b5 100644 --- a/awx/main/tasks.py +++ b/awx/main/tasks.py @@ -47,7 +47,6 @@ from django.contrib.auth.models import User from awx.main.constants import CLOUD_PROVIDERS from awx.main.models import * # noqa from awx.main.models import UnifiedJob -from awx.main.models.label import Label from awx.main.queue import FifoQueue from awx.main.conf import tower_settings from awx.main.task_engine import TaskSerializer, TASK_TIMEOUT_INTERVAL @@ -123,13 +122,6 @@ def run_administrative_checks(self): tower_admin_emails, fail_silently=True) -@task(bind=True) -def run_label_cleanup(self): - qs = Label.get_orphaned_labels() - labels_count = qs.count() - qs.delete() - return labels_count - @task(bind=True) def cleanup_authtokens(self): AuthToken.objects.filter(expires__lt=now()).delete() @@ -1307,9 +1299,11 @@ class RunInventoryUpdate(BaseTask): cp.set(section, 'password', decrypt_field(credential, 'password')) section = 'ansible' + cp.add_section(section) cp.set(section, 'group_patterns', '["{app}-{tier}-{color}", "{app}-{color}", "{app}", "{tier}"]') section = 'cache' + cp.add_section(section) cp.set(section, 'path', '/tmp') cp.set(section, 'max_age', '0') diff --git a/awx/main/tests/functional/test_rbac_api.py b/awx/main/tests/functional/test_rbac_api.py index d5ccbdf5d0..54dcc8deb5 100644 --- a/awx/main/tests/functional/test_rbac_api.py +++ b/awx/main/tests/functional/test_rbac_api.py @@ -12,7 +12,7 @@ def mock_feature_enabled(feature, bypass_database=None): @pytest.fixture def role(): - return Role.objects.create() + return Role.objects.create(role_field='admin_role') # @@ -210,33 +210,33 @@ def test_get_teams_roles_list(get, team, organization, admin): @pytest.mark.django_db -def test_add_role_to_teams(team, role, post, admin): - assert team.member_role.children.filter(id=role.id).count() == 0 +def test_add_role_to_teams(team, post, admin): + assert team.member_role.children.filter(id=team.member_role.id).count() == 0 url = reverse('api:team_roles_list', args=(team.id,)) - response = post(url, {'id': role.id}, admin) + response = post(url, {'id': team.member_role.id}, admin) assert response.status_code == 204 - assert team.member_role.children.filter(id=role.id).count() == 1 + assert team.member_role.children.filter(id=team.member_role.id).count() == 1 - response = post(url, {'id': role.id}, admin) + response = post(url, {'id': team.member_role.id}, admin) assert response.status_code == 204 - assert team.member_role.children.filter(id=role.id).count() == 1 + assert team.member_role.children.filter(id=team.member_role.id).count() == 1 response = post(url, {}, admin) assert response.status_code == 400 - assert team.member_role.children.filter(id=role.id).count() == 1 + assert team.member_role.children.filter(id=team.member_role.id).count() == 1 @pytest.mark.django_db -def test_remove_role_from_teams(team, role, post, admin): - assert team.member_role.children.filter(id=role.id).count() == 0 +def test_remove_role_from_teams(team, post, admin): + assert team.member_role.children.filter(id=team.member_role.id).count() == 0 url = reverse('api:team_roles_list', args=(team.id,)) - response = post(url, {'id': role.id}, admin) + response = post(url, {'id': team.member_role.id}, admin) assert response.status_code == 204 - assert team.member_role.children.filter(id=role.id).count() == 1 + assert team.member_role.children.filter(id=team.member_role.id).count() == 1 - response = post(url, {'disassociate': role.id, 'id': role.id}, admin) + response = post(url, {'disassociate': team.member_role.id, 'id': team.member_role.id}, admin) assert response.status_code == 204 - assert team.member_role.children.filter(id=role.id).count() == 0 + assert team.member_role.children.filter(id=team.member_role.id).count() == 0 diff --git a/awx/main/tests/functional/test_rbac_team.py b/awx/main/tests/functional/test_rbac_team.py index 0c16ba9f6f..6907589462 100644 --- a/awx/main/tests/functional/test_rbac_team.py +++ b/awx/main/tests/functional/test_rbac_team.py @@ -10,17 +10,17 @@ def test_team_attach_unattach(team, user): access = TeamAccess(u) team.member_role.members.add(u) - assert not access.can_attach(team, u.admin_role, 'member_role.children', None) - assert not access.can_unattach(team, u.admin_role, 'member_role.children') + assert not access.can_attach(team, team.member_role, 'member_role.children', None) + assert not access.can_unattach(team, team.member_role, 'member_role.children') team.admin_role.members.add(u) - assert access.can_attach(team, u.admin_role, 'member_role.children', None) - assert access.can_unattach(team, u.admin_role, 'member_role.children') + assert access.can_attach(team, team.member_role, 'member_role.children', None) + assert access.can_unattach(team, team.member_role, 'member_role.children') u2 = user('non-member', False) access = TeamAccess(u2) - assert not access.can_attach(team, u2.admin_role, 'member_role.children', None) - assert not access.can_unattach(team, u2.admin_role, 'member_role.chidlren') + assert not access.can_attach(team, team.member_role, 'member_role.children', None) + assert not access.can_unattach(team, team.member_role, 'member_role.chidlren') @pytest.mark.django_db def test_team_access_superuser(team, user): diff --git a/awx/main/tests/functional/test_rbac_user.py b/awx/main/tests/functional/test_rbac_user.py index c5959a2c32..de2b9aa8b1 100644 --- a/awx/main/tests/functional/test_rbac_user.py +++ b/awx/main/tests/functional/test_rbac_user.py @@ -75,3 +75,16 @@ def test_org_user_removed(user, organization): organization.member_role.members.remove(member) assert admin not in member.admin_role + +@pytest.mark.django_db +def test_org_admin_create_sys_auditor(org_admin): + access = UserAccess(org_admin) + assert not access.can_add(data=dict( + username='new_user', password="pa$$sowrd", email="asdf@redhat.com", + is_system_auditor='true')) + +@pytest.mark.django_db +def test_org_admin_edit_sys_auditor(org_admin, alice, organization): + organization.member_role.members.add(alice) + access = UserAccess(org_admin) + assert not access.can_change(obj=alice, data=dict(is_system_auditor='true')) diff --git a/awx/main/tests/unit/settings/test_defaults.py b/awx/main/tests/unit/settings/test_defaults.py index f59ddfb60b..d1c80fce3e 100644 --- a/awx/main/tests/unit/settings/test_defaults.py +++ b/awx/main/tests/unit/settings/test_defaults.py @@ -4,7 +4,6 @@ from django.conf import settings from datetime import timedelta @pytest.mark.parametrize("job_name,function_path", [ - ('label_cleanup', 'awx.main.tasks.run_label_cleanup'), ('admin_checks', 'awx.main.tasks.run_administrative_checks'), ('tower_scheduler', 'awx.main.tasks.tower_periodic_scheduler'), ]) diff --git a/awx/main/tests/unit/test_tasks.py b/awx/main/tests/unit/test_tasks.py index ce88f019ce..fb491a015b 100644 --- a/awx/main/tests/unit/test_tasks.py +++ b/awx/main/tests/unit/test_tasks.py @@ -7,7 +7,6 @@ from awx.main.models import ( ) from awx.main.tasks import ( - run_label_cleanup, send_notifications, run_administrative_checks, ) @@ -21,16 +20,6 @@ def apply_patches(_patches): yield [p.stop() for p in _patches] -def test_run_label_cleanup(mocker): - qs = mocker.Mock(**{'count.return_value': 3, 'delete.return_value': None}) - mock_label = mocker.patch('awx.main.models.label.Label.get_orphaned_labels',return_value=qs) - - ret = run_label_cleanup() - - mock_label.assert_called_with() - qs.delete.assert_called_with() - assert 3 == ret - def test_send_notifications_not_list(): with pytest.raises(TypeError): send_notifications(None) diff --git a/awx/plugins/inventory/cloudforms.py b/awx/plugins/inventory/cloudforms.py old mode 100644 new mode 100755 diff --git a/awx/plugins/inventory/foreman.py b/awx/plugins/inventory/foreman.py old mode 100644 new mode 100755 index 57367b1e77..ce057690df --- a/awx/plugins/inventory/foreman.py +++ b/awx/plugins/inventory/foreman.py @@ -1,9 +1,8 @@ #!/usr/bin/python +# vim: set fileencoding=utf-8 : # # NOTE FOR TOWER: change foreman_ to sattelite_ for the group prefix # -# vim: set fileencoding=utf-8 : -# # Copyright (C) 2016 Guido Günther # # This script is free software: you can redistribute it and/or modify diff --git a/awx/settings/defaults.py b/awx/settings/defaults.py index a4110e8286..aa8f69866b 100644 --- a/awx/settings/defaults.py +++ b/awx/settings/defaults.py @@ -345,10 +345,6 @@ CELERYBEAT_SCHEDULE = { 'task': 'awx.main.tasks.run_administrative_checks', 'schedule': timedelta(days=30) }, - 'label_cleanup': { - 'task': 'awx.main.tasks.run_label_cleanup', - 'schedule': timedelta(days=7) - }, 'authtoken_cleanup': { 'task': 'awx.main.tasks.cleanup_authtokens', 'schedule': timedelta(days=30) @@ -676,6 +672,16 @@ OPENSTACK_HOST_FILTER = r'^.+$' OPENSTACK_EXCLUDE_EMPTY_GROUPS = True OPENSTACK_INSTANCE_ID_VAR = 'openstack.id' +# --------------------- +# ----- Foreman ----- +# --------------------- +SATELLITE6_ENABLED_VAR = 'foreman.enabled' +SATELLITE6_ENABLED_VALUE = 'true' +SATELLITE6_GROUP_FILTER = r'^.+$' +SATELLITE6_HOST_FILTER = r'^.+$' +SATELLITE6_EXCLUDE_EMPTY_GROUPS = True +SATELLITE6_INSTANCE_ID_VAR = 'foreman.id' + # --------------------- # -- Activity Stream -- # --------------------- @@ -941,7 +947,34 @@ LOGGING = { 'maxBytes': 1024 * 1024 * 5, # 5 MB 'backupCount': 5, 'formatter':'simple', - } + }, + 'fact_receiver': { + 'level': 'WARNING', + 'class':'logging.handlers.RotatingFileHandler', + 'filters': ['require_debug_false'], + 'filename': os.path.join(LOG_ROOT, 'fact_receiver.log'), + 'maxBytes': 1024 * 1024 * 5, # 5 MB + 'backupCount': 5, + 'formatter':'simple', + }, + 'system_tracking_migrations': { + 'level': 'WARNING', + 'class':'logging.handlers.RotatingFileHandler', + 'filters': ['require_debug_false'], + 'filename': os.path.join(LOG_ROOT, 'tower_system_tracking_migrations.log'), + 'maxBytes': 1024 * 1024 * 5, # 5 MB + 'backupCount': 5, + 'formatter':'simple', + }, + 'rbac_migrations': { + 'level': 'WARNING', + 'class':'logging.handlers.RotatingFileHandler', + 'filters': ['require_debug_false'], + 'filename': os.path.join(LOG_ROOT, 'tower_rbac_migrations.log'), + 'maxBytes': 1024 * 1024 * 5, # 5 MB + 'backupCount': 5, + 'formatter':'simple', + }, }, 'loggers': { 'django': { @@ -1000,6 +1033,14 @@ LOGGING = { 'handlers': ['console', 'file', 'tower_warnings'], 'level': 'DEBUG', }, + 'system_tracking_migrations': { + 'handlers': ['console', 'file', 'tower_warnings'], + 'level': 'DEBUG', + }, + 'rbac_migrations': { + 'handlers': ['console', 'file', 'tower_warnings'], + 'level': 'DEBUG', + }, } } diff --git a/awx/settings/production.py b/awx/settings/production.py index 1f33f2bbbd..6efe6c397d 100644 --- a/awx/settings/production.py +++ b/awx/settings/production.py @@ -49,56 +49,13 @@ ANSIBLE_VENV_PATH = "/var/lib/awx/venv/ansible" TOWER_USE_VENV = True TOWER_VENV_PATH = "/var/lib/awx/venv/tower" -LOGGING['handlers']['tower_warnings'] = { - 'level': 'WARNING', - 'class':'logging.handlers.RotatingFileHandler', - 'filters': ['require_debug_false'], - 'filename': '/var/log/tower/tower.log', - 'maxBytes': 1024 * 1024 * 5, # 5 MB - 'backupCount': 5, - 'formatter':'simple', -} - - -LOGGING['handlers']['callback_receiver'] = { - 'level': 'WARNING', - 'class':'logging.handlers.RotatingFileHandler', - 'filters': ['require_debug_false'], - 'filename': '/var/log/tower/callback_receiver.log', - 'maxBytes': 1024 * 1024 * 5, # 5 MB - 'backupCount': 5, - 'formatter':'simple', -} - -LOGGING['handlers']['socketio_service'] = { - 'level': 'WARNING', - 'class':'logging.handlers.RotatingFileHandler', - 'filters': ['require_debug_false'], - 'filename': '/var/log/tower/socketio_service.log', - 'maxBytes': 1024 * 1024 * 5, # 5 MB - 'backupCount': 5, - 'formatter':'simple', -} - -LOGGING['handlers']['task_system'] = { - 'level': 'INFO', - 'class':'logging.handlers.RotatingFileHandler', - 'filters': ['require_debug_false'], - 'filename': '/var/log/tower/task_system.log', - 'maxBytes': 1024 * 1024 * 5, # 5 MB - 'backupCount': 5, - 'formatter':'simple', -} - -LOGGING['handlers']['fact_receiver'] = { - 'level': 'WARNING', - 'class':'logging.handlers.RotatingFileHandler', - 'filters': ['require_debug_false'], - 'filename': '/var/log/tower/fact_receiver.log', - 'maxBytes': 1024 * 1024 * 5, # 5 MB - 'backupCount': 5, - 'formatter':'simple', -} +LOGGING['handlers']['tower_warnings']['filename'] = '/var/log/tower/tower.log' +LOGGING['handlers']['callback_receiver']['filename'] = '/var/log/tower/callback_receiver.log' +LOGGING['handlers']['socketio_service']['filename'] = '/var/log/tower/socketio_service.log' +LOGGING['handlers']['task_system']['filename'] = '/var/log/tower/task_system.log' +LOGGING['handlers']['fact_receiver']['filename'] = '/var/log/tower/fact_receiver.log' +LOGGING['handlers']['system_tracking_migrations']['filename'] = '/var/log/tower/tower_system_tracking_migrations.log' +LOGGING['handlers']['rbac_migrations']['filename'] = '/var/log/tower/tower_rbac_migrations.log' # Load settings from any .py files in the global conf.d directory specified in # the environment, defaulting to /etc/tower/conf.d/. diff --git a/awx/ui/client/src/app.js b/awx/ui/client/src/app.js index e25d53ac51..6b90c16b5d 100644 --- a/awx/ui/client/src/app.js +++ b/awx/ui/client/src/app.js @@ -872,7 +872,7 @@ var tower = angular.module('Tower', [ } else { var lastUser = $cookieStore.get('current_user'), timestammp = Store('sessionTime'); - if(lastUser && lastUser.id && timestammp && timestammp[lastUser.id]){ + if(lastUser && lastUser.id && timestammp && timestammp[lastUser.id] && timestammp[lastUser.id].loggedIn){ var stime = timestammp[lastUser.id].time, now = new Date().getTime(); if ((stime - now) <= 0) { diff --git a/awx/ui/client/src/controllers/Jobs.js b/awx/ui/client/src/controllers/Jobs.js index a7408259a2..eafbfbb981 100644 --- a/awx/ui/client/src/controllers/Jobs.js +++ b/awx/ui/client/src/controllers/Jobs.js @@ -67,7 +67,7 @@ export function JobsListController ($rootScope, $log, $scope, $compile, $statePa list: AllJobsList, id: 'active-jobs', pageSize: 20, - url: GetBasePath('unified_jobs') + '?status__in=pending,waiting,running,completed,failed,successful,error,canceled&order_by=-finished', + url: GetBasePath('unified_jobs') + '?status__in=pending,waiting,running,completed,failed,successful,error,canceled,new&order_by=-finished', searchParams: search_params, spinner: false }); diff --git a/config/wsgi.py b/config/wsgi.py index 9999eb08c8..c053481836 100644 --- a/config/wsgi.py +++ b/config/wsgi.py @@ -1 +1,4 @@ +import sys +if sys.prefix != '/var/lib/awx/venv/tower': + raise RuntimeError('Tower virtualenv not activated. Check WSGIPythonHome in Apache configuration.') from awx.wsgi import application # NOQA diff --git a/tools/data_generators/rbac_dummy_data_generator.py b/tools/data_generators/rbac_dummy_data_generator.py index ad90dc74a9..f9e583c3a3 100755 --- a/tools/data_generators/rbac_dummy_data_generator.py +++ b/tools/data_generators/rbac_dummy_data_generator.py @@ -3,11 +3,6 @@ # All Rights Reserved import os import sys -os.environ.setdefault("DJANGO_SETTINGS_MODULE", "awx.settings.development") # noqa - -import django -django.setup() # noqa - # Python from collections import defaultdict @@ -15,7 +10,7 @@ from optparse import make_option, OptionParser # Django - +import django from django.utils.timezone import now from django.contrib.auth.models import User from django.db import transaction @@ -23,7 +18,8 @@ from django.db import transaction # awx from awx.main.models import * # noqa - +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "awx.settings.development") # noqa +django.setup() # noqa option_list = [