mirror of
https://github.com/ansible/awx.git
synced 2026-01-21 06:28:01 -03:30
Merge branch 'stable' into devel
* stable: (30 commits) Remove ansible-tower.repo during packer cleanup Update package changelogs Enable SE boolean httpd_execmem Check that venv is activated before loading wsgi app (#3148) also limit creation of system auditors to superusers Make check for idle session more specific Use foreman.id instead flake8 fixups fixing tests for new team role assignment restrictions driveby cleanup do not allow assignment of system roles or user.admin_role to teams Add copyright back with utf-8 encoding Complete Sat6 integration Force requests to emit application/json fix issue with rbac_migrations logger Enable software collections repo on RHEL 6 + RHSM Fixed credential migration issue involving null inventory fields in job templates Allow instant cancel for new jobs Show new jobs in UI Jobs tab use existing logging infrastructure ...
This commit is contained in:
commit
572f62e22a
@ -1573,6 +1573,7 @@ class InventoryScanJobTemplateList(SubListAPIView):
|
||||
|
||||
class HostList(ListCreateAPIView):
|
||||
|
||||
always_allow_superuser = False
|
||||
model = Host
|
||||
serializer_class = HostSerializer
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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):
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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()
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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')
|
||||
|
||||
@ -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)
|
||||
|
||||
@ -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)))
|
||||
|
||||
@ -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')
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
|
||||
|
||||
@ -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):
|
||||
|
||||
@ -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'))
|
||||
|
||||
@ -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'),
|
||||
])
|
||||
|
||||
@ -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)
|
||||
|
||||
0
awx/plugins/inventory/cloudforms.py
Normal file → Executable file
0
awx/plugins/inventory/cloudforms.py
Normal file → Executable file
3
awx/plugins/inventory/foreman.py
Normal file → Executable file
3
awx/plugins/inventory/foreman.py
Normal file → Executable file
@ -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 <agx@sigxcpu.org>
|
||||
#
|
||||
# This script is free software: you can redistribute it and/or modify
|
||||
|
||||
@ -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',
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -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/.
|
||||
|
||||
@ -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) {
|
||||
|
||||
@ -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
|
||||
});
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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 = [
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user