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:
Matthew Jones 2016-08-01 11:51:26 -04:00
commit 572f62e22a
23 changed files with 137 additions and 194 deletions

View File

@ -1573,6 +1573,7 @@ class InventoryScanJobTemplateList(SubListAPIView):
class HostList(ListCreateAPIView):
always_allow_superuser = False
model = Host
serializer_class = HostSerializer

View File

@ -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

View File

@ -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):

View File

@ -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,

View File

@ -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()

View File

@ -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)

View File

@ -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')

View File

@ -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)

View File

@ -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)))

View File

@ -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')

View File

@ -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

View File

@ -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):

View File

@ -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'))

View File

@ -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'),
])

View File

@ -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
View File

3
awx/plugins/inventory/foreman.py Normal file → Executable file
View 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

View File

@ -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',
},
}
}

View File

@ -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/.

View File

@ -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) {

View File

@ -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
});

View File

@ -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

View File

@ -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 = [