mirror of
https://github.com/ansible/awx.git
synced 2024-10-31 23:51:09 +03:00
Merge branch 'release_3.0.1' into stable
This commit is contained in:
commit
4e93475be1
@ -5,7 +5,7 @@ import os
|
||||
import sys
|
||||
import warnings
|
||||
|
||||
__version__ = '3.0.0'
|
||||
__version__ = '3.0.1'
|
||||
|
||||
__all__ = ['__version__']
|
||||
|
||||
|
@ -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…
Reference in New Issue
Block a user