1
0
mirror of https://github.com/ansible/awx.git synced 2024-11-01 08:21:15 +03:00

Merge pull request #541 from matburt/database_config_source

Database config source
This commit is contained in:
Matthew Jones 2016-01-12 11:20:43 -05:00
commit 84c2e71907
25 changed files with 1109 additions and 54 deletions

View File

@ -6,7 +6,6 @@ import urllib
# Django
from django.utils.timezone import now as tz_now
from django.conf import settings
# Django REST Framework
from rest_framework import authentication
@ -15,7 +14,7 @@ from rest_framework import HTTP_HEADER_ENCODING
# AWX
from awx.main.models import UnifiedJob, AuthToken
from awx.main.conf import tower_settings
class TokenAuthentication(authentication.TokenAuthentication):
'''
@ -90,7 +89,7 @@ class TokenAuthentication(authentication.TokenAuthentication):
# Token invalidated due to session limit config being reduced
# Session limit reached invalidation will also take place on authentication
if settings.AUTH_TOKEN_PER_USER != -1:
if tower_settings.AUTH_TOKEN_PER_USER != -1:
if not token.in_valid_tokens(now=now):
token.invalidate(reason='limit_reached')
raise exceptions.AuthenticationFailed(AuthToken.reason_long('limit_reached'))

View File

@ -144,6 +144,7 @@ class APIView(views.APIView):
'new_in_220': getattr(self, 'new_in_220', False),
'new_in_230': getattr(self, 'new_in_230', False),
'new_in_240': getattr(self, 'new_in_240', False),
'new_in_300': getattr(self, 'new_in_300', False),
}
def get_description(self, html=False):
@ -160,7 +161,7 @@ class APIView(views.APIView):
'''
ret = super(APIView, self).metadata(request)
added_in_version = '1.2'
for version in ('2.4.0', '2.3.0', '2.2.0', '2.1.0', '2.0.0', '1.4.8', '1.4.5', '1.4', '1.3'):
for version in ('3.0.0', '2.4.0', '2.3.0', '2.2.0', '2.1.0', '2.0.0', '1.4.8', '1.4.5', '1.4', '1.3'):
if getattr(self, 'new_in_%s' % version.replace('.', ''), False):
added_in_version = version
break

View File

@ -38,6 +38,7 @@ from awx.main.constants import SCHEDULEABLE_PROVIDERS
from awx.main.models import * # noqa
from awx.main.utils import get_type_for_model, get_model_for_type, build_url, timestamp_apiformat
from awx.main.redact import REPLACE_STR
from awx.main.conf import tower_settings
from awx.api.license import feature_enabled
@ -267,7 +268,7 @@ class BaseSerializer(serializers.ModelSerializer):
return choices
def get_url(self, obj):
if obj is None:
if obj is None or not hasattr(obj, 'get_absolute_url'):
return ''
elif isinstance(obj, User):
return reverse('api:user_detail', args=(obj.pk,))
@ -521,8 +522,9 @@ class UnifiedJobSerializer(BaseSerializer):
def get_result_stdout(self, obj):
obj_size = obj.result_stdout_size
if obj_size > settings.STDOUT_MAX_BYTES_DISPLAY:
return "Standard Output too large to display (%d bytes), only download supported for sizes over %d bytes" % (obj_size, settings.STDOUT_MAX_BYTES_DISPLAY)
if obj_size > tower_settings.STDOUT_MAX_BYTES_DISPLAY:
return "Standard Output too large to display (%d bytes), only download supported for sizes over %d bytes" % (obj_size,
tower_settings.STDOUT_MAX_BYTES_DISPLAY)
return obj.result_stdout
class UnifiedJobListSerializer(UnifiedJobSerializer):
@ -569,8 +571,9 @@ class UnifiedJobStdoutSerializer(UnifiedJobSerializer):
def get_result_stdout(self, obj):
obj_size = obj.result_stdout_size
if obj_size > settings.STDOUT_MAX_BYTES_DISPLAY:
return "Standard Output too large to display (%d bytes), only download supported for sizes over %d bytes" % (obj_size, settings.STDOUT_MAX_BYTES_DISPLAY)
if obj_size > tower_settings.STDOUT_MAX_BYTES_DISPLAY:
return "Standard Output too large to display (%d bytes), only download supported for sizes over %d bytes" % (obj_size,
tower_settings.STDOUT_MAX_BYTES_DISPLAY)
return obj.result_stdout
def get_types(self):
@ -2106,6 +2109,39 @@ class ActivityStreamSerializer(BaseSerializer):
last_name = obj.actor.last_name)
return summary_fields
class TowerSettingsSerializer(BaseSerializer):
class Meta:
model = TowerSettings
fields = ('key', 'description', 'category', 'value', 'value_type', 'user')
read_only_fields = ('description', 'category', 'value_type', 'user')
def from_native(self, data, files):
if data['key'] not in settings.TOWER_SETTINGS_MANIFEST:
self._errors = {'key': 'Key {0} is not a valid settings key'.format(data['key'])}
return
current_val = TowerSettings.objects.filter(key=data['key'])
if current_val.exists():
current_val.delete()
manifest_val = settings.TOWER_SETTINGS_MANIFEST[data['key']]
data['description'] = manifest_val['description']
data['category'] = manifest_val['category']
data['value_type'] = manifest_val['type']
return super(TowerSettingsSerializer, self).from_native(data, files)
def validate(self, attrs):
manifest = settings.TOWER_SETTINGS_MANIFEST
if attrs['key'] not in manifest:
raise serializers.ValidationError(dict(key=["Key {0} is not a valid settings key".format(attrs['key'])]))
# TODO: Type checking/coercion, contextual validation
return attrs
def save_object(self, obj, **kwargs):
manifest_val = settings.TOWER_SETTINGS_MANIFEST[obj.key]
obj.description = manifest_val['description']
obj.category = manifest_val['category']
obj.value_type = manifest_val['type']
return super(TowerSettingsSerializer, self).save_object(obj, **kwargs)
class AuthTokenSerializer(serializers.Serializer):

View File

@ -6,3 +6,4 @@
{% if new_in_220 %}> _New in Ansible Tower 2.2.0_{% endif %}
{% if new_in_230 %}> _New in Ansible Tower 2.3.0_{% endif %}
{% if new_in_240 %}> _New in Ansible Tower 2.4.0_{% endif %}
{% if new_in_300 %}> _New in Ansible Tower 3.0.0_{% endif %}

View File

@ -220,6 +220,10 @@ activity_stream_urls = patterns('awx.api.views',
url(r'^(?P<pk>[0-9]+)/$', 'activity_stream_detail'),
)
settings_urls = patterns('awx.api.views',
url(r'^$', 'settings_list'),
url(r'^reset/$', 'settings_reset'))
v1_urls = patterns('awx.api.views',
url(r'^$', 'api_v1_root_view'),
url(r'^ping/$', 'api_v1_ping_view'),
@ -230,6 +234,7 @@ v1_urls = patterns('awx.api.views',
url(r'^dashboard/$', 'dashboard_view'),
url(r'^dashboard/graphs/jobs/$', 'dashboard_jobs_graph_view'),
url(r'^dashboard/graphs/inventory/$', 'dashboard_inventory_graph_view'),
url(r'^settings/', include(settings_urls)),
url(r'^schedules/', include(schedule_urls)),
url(r'^organizations/', include(organization_urls)),
url(r'^users/', include(user_urls)),

View File

@ -12,6 +12,7 @@ import socket
import sys
import errno
from base64 import b64encode
from collections import namedtuple
# Django
from django.conf import settings
@ -69,6 +70,7 @@ from awx.api.renderers import * # noqa
from awx.api.serializers import * # noqa
from awx.fact.models import * # noqa
from awx.main.utils import emit_websocket_notification
from awx.main.conf import tower_settings
def api_exception_handler(exc):
'''
@ -113,6 +115,7 @@ class ApiV1RootView(APIView):
data['authtoken'] = reverse('api:auth_token_view')
data['ping'] = reverse('api:api_v1_ping_view')
data['config'] = reverse('api:api_v1_config_view')
data['settings'] = reverse('api:settings_list')
data['me'] = reverse('api:user_me_list')
data['dashboard'] = reverse('api:dashboard_view')
data['organizations'] = reverse('api:organization_list')
@ -189,9 +192,9 @@ class ApiV1ConfigView(APIView):
'''Return various sitewide configuration settings.'''
license_reader = TaskSerializer()
license_data = license_reader.from_file(show_key=request.user.is_superuser)
license_data = license_reader.from_database(show_key=request.user.is_superuser)
pendo_state = settings.PENDO_TRACKING_STATE if settings.PENDO_TRACKING_STATE in ('off', 'anonymous', 'detailed') else 'off'
pendo_state = tower_settings.PENDO_TRACKING_STATE if tower_settings.PENDO_TRACKING_STATE in ('off', 'anonymous', 'detailed') else 'off'
data = dict(
time_zone=settings.TIME_ZONE,
@ -261,9 +264,7 @@ class ApiV1ConfigView(APIView):
# If the license is valid, write it to disk.
if license_data['valid_key']:
fh = open(TASK_FILE, "w")
fh.write(data_actual)
fh.close()
tower_settings.LICENSE = data_actual
# Spawn a task to ensure that MongoDB is started (or stopped)
# as appropriate, based on whether the license uses it.
@ -592,7 +593,7 @@ class AuthTokenView(APIView):
# Note: This header is normally added in the middleware whenever an
# auth token is included in the request header.
headers = {
'Auth-Token-Timeout': int(settings.AUTH_TOKEN_EXPIRATION)
'Auth-Token-Timeout': int(tower_settings.AUTH_TOKEN_EXPIRATION)
}
return Response({'token': token.key, 'expires': token.expires}, headers=headers)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
@ -2859,8 +2860,9 @@ class UnifiedJobStdout(RetrieveAPIView):
def retrieve(self, request, *args, **kwargs):
unified_job = self.get_object()
obj_size = unified_job.result_stdout_size
if request.accepted_renderer.format != 'txt_download' and obj_size > settings.STDOUT_MAX_BYTES_DISPLAY:
response_message = "Standard Output too large to display (%d bytes), only download supported for sizes over %d bytes" % (obj_size, settings.STDOUT_MAX_BYTES_DISPLAY)
if request.accepted_renderer.format != 'txt_download' and obj_size > tower_settings.STDOUT_MAX_BYTES_DISPLAY:
response_message = "Standard Output too large to display (%d bytes), only download supported for sizes over %d bytes" % (obj_size,
tower_settings.STDOUT_MAX_BYTES_DISPLAY)
if request.accepted_renderer.format == 'json':
return Response({'range': {'start': 0, 'end': 1, 'absolute_end': 1}, 'content': response_message})
else:
@ -2959,6 +2961,61 @@ class ActivityStreamDetail(RetrieveAPIView):
# Okay, let it through.
return super(type(self), self).get(request, *args, **kwargs)
class SettingsList(ListCreateAPIView):
model = TowerSettings
serializer_class = TowerSettingsSerializer
authentication_classes = [TokenGetAuthentication] + api_settings.DEFAULT_AUTHENTICATION_CLASSES
new_in_300 = True
filter_backends = ()
def get_queryset(self):
# TODO: docs
if not self.request.user.is_superuser:
# NOTE: Shortcutting the rbac class due to the merging of the settings manifest and the database
# we'll need to extend this more in the future when we have user settings
return []
SettingsTuple = namedtuple('Settings', ['key', 'description', 'category', 'value', 'value_type', 'user'])
all_defined_settings = {s.key: SettingsTuple(s.key,
s.description,
s.category,
s.value_converted,
s.value_type,
s.user) for s in TowerSettings.objects.all()}
manifest_settings = settings.TOWER_SETTINGS_MANIFEST
settings_actual = []
for settings_key in manifest_settings:
if settings_key in all_defined_settings:
settings_actual.append(all_defined_settings[settings_key])
else:
m_entry = manifest_settings[settings_key]
settings_actual.append(SettingsTuple(settings_key,
m_entry['description'],
m_entry['category'],
m_entry['default'],
m_entry['type'],
None))
return settings_actual
def delete(self, request, *args, **kwargs):
if not request.user.can_access(self.model, 'delete', None):
raise PermissionDenied()
TowerSettings.objects.all().delete()
return Response()
class SettingsReset(APIView):
view_name = "Reset a settings value"
new_in_300 = True
def post(self, request):
# NOTE: Extend more with user settings
if not request.user.can_access(TowerSettings, 'delete', None):
raise PermissionDenied()
settings_key = request.DATA.get('key', None)
if settings_key is not None:
TowerSettings.objects.filter(key=settings_key).delete()
return Response(status=status.HTTP_204_NO_CONTENT)
# Create view functions for all of the class-based views to simplify inclusion
# in URL patterns and reverse URL lookups, converting CamelCase names to

View File

@ -7,7 +7,6 @@ import sys
import logging
# Django
from django.conf import settings
from django.db.models import F, Q
from django.contrib.auth.models import User
@ -19,6 +18,7 @@ from awx.main.utils import * # noqa
from awx.main.models import * # noqa
from awx.api.license import LicenseForbids
from awx.main.task_engine import TaskSerializer
from awx.main.conf import tower_settings
__all__ = ['get_user_queryset', 'check_user_access']
@ -196,7 +196,7 @@ class UserAccess(BaseAccess):
qs = self.model.objects.filter(is_active=True).distinct()
if self.user.is_superuser:
return qs
if settings.ORG_ADMINS_CAN_SEE_ALL_USERS and self.user.admin_of_organizations.filter(active=True).exists():
if tower_settings.ORG_ADMINS_CAN_SEE_ALL_USERS and self.user.admin_of_organizations.filter(active=True).exists():
return qs
return qs.filter(
Q(pk=self.user.pk) |
@ -1566,6 +1566,10 @@ class ActivityStreamAccess(BaseAccess):
ad_hoc_command_qs = self.user.get_queryset(AdHocCommand)
qs.filter(ad_hoc_command__in=ad_hoc_command_qs)
# TowerSettings Filter
settings_qs = self.user.get_queryset(TowerSettings)
qs.filter(tower_settings__in=settings_qs)
# organization_qs = self.user.get_queryset(Organization)
# user_qs = self.user.get_queryset(User)
# inventory_qs = self.user.get_queryset(Inventory)
@ -1636,6 +1640,30 @@ class CustomInventoryScriptAccess(BaseAccess):
return True
return False
class TowerSettingsAccess(BaseAccess):
'''
- I can see settings when
- I am a super user
- I can edit settings when
- I am a super user
- I can clear settings when
- I am a super user
'''
model = TowerSettings
def get_queryset(self):
if self.user.is_superuser:
return self.model.objects.all()
return self.model.objects.none()
def can_change(self, obj, data):
return self.user.is_superuser
def can_delete(self, obj):
return self.user.is_superuser
register_access(User, UserAccess)
register_access(Organization, OrganizationAccess)
register_access(Inventory, InventoryAccess)
@ -1661,3 +1689,4 @@ register_access(UnifiedJobTemplate, UnifiedJobTemplateAccess)
register_access(UnifiedJob, UnifiedJobAccess)
register_access(ActivityStream, ActivityStreamAccess)
register_access(CustomInventoryScript, CustomInventoryScriptAccess)
register_access(TowerSettings, TowerSettingsAccess)

51
awx/main/conf.py Normal file
View File

@ -0,0 +1,51 @@
# Copyright (c) 2015 Ansible, Inc..
# All Rights Reserved.
import logging
from django.conf import settings as django_settings
from django.db.utils import ProgrammingError
from django.db import OperationalError
from awx.main.models.configuration import TowerSettings
logger = logging.getLogger('awx.main.conf')
class TowerConfiguration(object):
# TODO: Caching so we don't have to hit the database every time for settings
def __getattr__(self, key):
settings_manifest = django_settings.TOWER_SETTINGS_MANIFEST
if key not in settings_manifest:
raise AttributeError("Tower Setting with key '{0}' is not defined in the manifest".format(key))
default_value = settings_manifest[key]['default']
ts = TowerSettings.objects.filter(key=key)
try:
if not ts.exists():
try:
val_actual = getattr(django_settings, key)
except AttributeError:
val_actual = default_value
return val_actual
return ts[0].value_converted
except (ProgrammingError, OperationalError), e:
# Database is not available yet, usually during migrations so lets use the default
logger.debug("Database settings not available yet, using defaults ({0})".format(e))
return default_value
def __setattr__(self, key, value):
settings_manifest = django_settings.TOWER_SETTINGS_MANIFEST
if key not in settings_manifest:
raise AttributeError("Tower Setting with key '{0}' does not exist".format(key))
settings_entry = settings_manifest[key]
settings_actual = TowerSettings.objects.filter(key=key)
if not settings_actual.exists():
settings_actual = TowerSettings(key=key,
description=settings_entry['description'],
category=settings_entry['category'],
value=value,
value_type=settings_entry['type'])
else:
settings_actual['value'] = value
settings_actual.save()
tower_settings = TowerConfiguration()

View File

@ -28,6 +28,7 @@ from awx.main.models import * # noqa
from awx.main.utils import ignore_inventory_computed_fields, check_proot_installed, wrap_args_with_proot
from awx.main.signals import disable_activity_stream
from awx.main.task_engine import TaskSerializer as LicenseReader
from awx.main.conf import tower_settings
logger = logging.getLogger('awx.main.commands.inventory_import')
@ -356,7 +357,7 @@ class ExecutableJsonLoader(BaseLoader):
data = {}
stdout, stderr = '', ''
try:
if self.is_custom and getattr(settings, 'AWX_PROOT_ENABLED', False):
if self.is_custom and getattr(tower_settings, 'AWX_PROOT_ENABLED', False):
if not check_proot_installed():
raise RuntimeError("proot is not installed but is configured for use")
kwargs = {'proot_temp_dir': self.source_dir} # TODO: Remove proot dir

View File

@ -11,10 +11,10 @@ from django.db import IntegrityError
from django.http import HttpResponseRedirect
from django.template.response import TemplateResponse
from django.utils.functional import curry
from django.conf import settings
from awx import __version__ as version
from awx.main.models import ActivityStream, Instance
from awx.main.conf import tower_settings
from awx.api.authentication import TokenAuthentication
@ -117,6 +117,6 @@ class AuthTokenTimeoutMiddleware(object):
if not TokenAuthentication._get_x_auth_token_header(request):
return response
response['Auth-Token-Timeout'] = int(settings.AUTH_TOKEN_EXPIRATION)
response['Auth-Token-Timeout'] = int(tower_settings.AUTH_TOKEN_EXPIRATION)
return response

View File

@ -0,0 +1,557 @@
# -*- coding: utf-8 -*-
from south.utils import datetime_utils as datetime
from south.db import db
from south.v2 import SchemaMigration
from django.db import models
class Migration(SchemaMigration):
def forwards(self, orm):
# Adding model 'TowerSettings'
db.create_table(u'main_towersettings', (
(u'id', self.gf('django.db.models.fields.AutoField')(primary_key=True)),
('created', self.gf('django.db.models.fields.DateTimeField')(default=None)),
('modified', self.gf('django.db.models.fields.DateTimeField')(default=None)),
('key', self.gf('django.db.models.fields.CharField')(unique=True, max_length=255)),
('description', self.gf('django.db.models.fields.TextField')()),
('category', self.gf('django.db.models.fields.CharField')(max_length=128)),
('value', self.gf('django.db.models.fields.TextField')()),
('value_type', self.gf('django.db.models.fields.CharField')(max_length=12)),
('user', self.gf('django.db.models.fields.related.ForeignKey')(default=None, related_name='settings', null=True, to=orm['auth.User'])),
))
db.send_create_signal('main', ['TowerSettings'])
# Adding M2M table for field tower_settings on 'ActivityStream'
m2m_table_name = db.shorten_name(u'main_activitystream_tower_settings')
db.create_table(m2m_table_name, (
('id', models.AutoField(verbose_name='ID', primary_key=True, auto_created=True)),
('activitystream', models.ForeignKey(orm['main.activitystream'], null=False)),
('towersettings', models.ForeignKey(orm['main.towersettings'], null=False))
))
db.create_unique(m2m_table_name, ['activitystream_id', 'towersettings_id'])
def backwards(self, orm):
# Deleting model 'TowerSettings'
db.delete_table(u'main_towersettings')
# Removing M2M table for field tower_settings on 'ActivityStream'
db.delete_table(db.shorten_name(u'main_activitystream_tower_settings'))
models = {
u'auth.group': {
'Meta': {'object_name': 'Group'},
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '80'}),
'permissions': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.Permission']", 'symmetrical': 'False', 'blank': 'True'})
},
u'auth.permission': {
'Meta': {'ordering': "(u'content_type__app_label', u'content_type__model', u'codename')", 'unique_together': "((u'content_type', u'codename'),)", 'object_name': 'Permission'},
'codename': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'content_type': ('django.db.models.fields.related.ForeignKey', [], {'to': u"orm['contenttypes.ContentType']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '50'})
},
u'auth.user': {
'Meta': {'object_name': 'User'},
'date_joined': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'email': ('django.db.models.fields.EmailField', [], {'max_length': '75', 'blank': 'True'}),
'first_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'groups': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Group']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'is_active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'is_staff': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'is_superuser': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_login': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'last_name': ('django.db.models.fields.CharField', [], {'max_length': '30', 'blank': 'True'}),
'password': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'user_permissions': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "u'user_set'", 'blank': 'True', 'to': u"orm['auth.Permission']"}),
'username': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '30'})
},
u'contenttypes.contenttype': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('app_label', 'model'),)", 'object_name': 'ContentType', 'db_table': "'django_content_type'"},
'app_label': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'model': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '100'})
},
'main.activitystream': {
'Meta': {'object_name': 'ActivityStream'},
'actor': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'activity_stream'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'ad_hoc_command': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.AdHocCommand']", 'symmetrical': 'False', 'blank': 'True'}),
'changes': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'credential': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Credential']", 'symmetrical': 'False', 'blank': 'True'}),
'custom_inventory_script': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.CustomInventoryScript']", 'symmetrical': 'False', 'blank': 'True'}),
'group': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Group']", 'symmetrical': 'False', 'blank': 'True'}),
'host': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Host']", 'symmetrical': 'False', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'inventory': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Inventory']", 'symmetrical': 'False', 'blank': 'True'}),
'inventory_source': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.InventorySource']", 'symmetrical': 'False', 'blank': 'True'}),
'inventory_update': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.InventoryUpdate']", 'symmetrical': 'False', 'blank': 'True'}),
'job': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Job']", 'symmetrical': 'False', 'blank': 'True'}),
'job_template': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.JobTemplate']", 'symmetrical': 'False', 'blank': 'True'}),
'object1': ('django.db.models.fields.TextField', [], {}),
'object2': ('django.db.models.fields.TextField', [], {}),
'object_relationship_type': ('django.db.models.fields.TextField', [], {'blank': 'True'}),
'operation': ('django.db.models.fields.CharField', [], {'max_length': '13'}),
'organization': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Organization']", 'symmetrical': 'False', 'blank': 'True'}),
'permission': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Permission']", 'symmetrical': 'False', 'blank': 'True'}),
'project': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Project']", 'symmetrical': 'False', 'blank': 'True'}),
'project_update': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.ProjectUpdate']", 'symmetrical': 'False', 'blank': 'True'}),
'schedule': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Schedule']", 'symmetrical': 'False', 'blank': 'True'}),
'team': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.Team']", 'symmetrical': 'False', 'blank': 'True'}),
'timestamp': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'tower_settings': ('django.db.models.fields.related.ManyToManyField', [], {'to': "orm['main.TowerSettings']", 'symmetrical': 'False', 'blank': 'True'}),
'unified_job': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'activity_stream_as_unified_job+'", 'blank': 'True', 'to': "orm['main.UnifiedJob']"}),
'unified_job_template': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'activity_stream_as_unified_job_template+'", 'blank': 'True', 'to': "orm['main.UnifiedJobTemplate']"}),
'user': ('django.db.models.fields.related.ManyToManyField', [], {'to': u"orm['auth.User']", 'symmetrical': 'False', 'blank': 'True'})
},
'main.adhoccommand': {
'Meta': {'object_name': 'AdHocCommand', '_ormbases': ['main.UnifiedJob']},
'become_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'credential': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'ad_hoc_commands'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Credential']"}),
'forks': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'}),
'hosts': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'ad_hoc_commands'", 'symmetrical': 'False', 'through': "orm['main.AdHocCommandEvent']", 'to': "orm['main.Host']"}),
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'ad_hoc_commands'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Inventory']"}),
'job_type': ('django.db.models.fields.CharField', [], {'default': "'run'", 'max_length': '64'}),
'limit': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'module_args': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'module_name': ('django.db.models.fields.CharField', [], {'default': "'command'", 'max_length': '1024', 'blank': 'True'}),
u'unifiedjob_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.UnifiedJob']", 'unique': 'True', 'primary_key': 'True'}),
'verbosity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'})
},
'main.adhoccommandevent': {
'Meta': {'ordering': "('-pk',)", 'unique_together': "[('ad_hoc_command', 'host_name')]", 'object_name': 'AdHocCommandEvent'},
'ad_hoc_command': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'ad_hoc_command_events'", 'to': "orm['main.AdHocCommand']"}),
'changed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'counter': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
'event': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'event_data': ('jsonfield.fields.JSONField', [], {'default': '{}', 'blank': 'True'}),
'failed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'host': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'ad_hoc_command_events'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Host']"}),
'host_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'})
},
'main.authtoken': {
'Meta': {'object_name': 'AuthToken'},
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'expires': ('django.db.models.fields.DateTimeField', [], {'default': 'datetime.datetime.now'}),
'key': ('django.db.models.fields.CharField', [], {'max_length': '40', 'primary_key': 'True'}),
'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'reason': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'request_hash': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '40', 'blank': 'True'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'auth_tokens'", 'to': u"orm['auth.User']"})
},
'main.credential': {
'Meta': {'ordering': "('kind', 'name')", 'unique_together': "[('user', 'team', 'kind', 'name')]", 'object_name': 'Credential'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'become_method': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '32', 'blank': 'True'}),
'become_password': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'become_username': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'cloud': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'credential\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'host': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'kind': ('django.db.models.fields.CharField', [], {'default': "'ssh'", 'max_length': '32'}),
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'credential\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
'password': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'project': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}),
'security_token': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'ssh_key_data': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'ssh_key_unlock': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'team': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'credentials'", 'null': 'True', 'blank': 'True', 'to': "orm['main.Team']"}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'credentials'", 'null': 'True', 'blank': 'True', 'to': u"orm['auth.User']"}),
'username': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'vault_password': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'})
},
'main.custominventoryscript': {
'Meta': {'ordering': "('name',)", 'unique_together': "[('name', 'organization')]", 'object_name': 'CustomInventoryScript'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'custominventoryscript\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'custominventoryscript\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
'organization': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'custom_inventory_scripts'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Organization']"}),
'script': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'})
},
'main.group': {
'Meta': {'ordering': "('name',)", 'unique_together': "(('name', 'inventory'),)", 'object_name': 'Group'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'group\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'groups_with_active_failures': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'has_active_failures': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'has_inventory_sources': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'hosts': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'groups'", 'blank': 'True', 'to': "orm['main.Host']"}),
'hosts_with_active_failures': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'groups'", 'to': "orm['main.Inventory']"}),
'inventory_sources': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'groups'", 'symmetrical': 'False', 'to': "orm['main.InventorySource']"}),
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'group\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
'parents': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'children'", 'blank': 'True', 'to': "orm['main.Group']"}),
'total_groups': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'total_hosts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'variables': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'})
},
'main.host': {
'Meta': {'ordering': "('inventory', 'name')", 'unique_together': "(('name', 'inventory'),)", 'object_name': 'Host'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'host\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'has_active_failures': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'has_inventory_sources': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'instance_id': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}),
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'hosts'", 'to': "orm['main.Inventory']"}),
'inventory_sources': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'hosts'", 'symmetrical': 'False', 'to': "orm['main.InventorySource']"}),
'last_job': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'hosts_as_last_job+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Job']"}),
'last_job_host_summary': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'hosts_as_last_job_summary+'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.JobHostSummary']", 'blank': 'True', 'null': 'True'}),
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'host\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
'variables': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'})
},
'main.instance': {
'Meta': {'object_name': 'Instance'},
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
'hostname': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '250'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'primary': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'uuid': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '40'})
},
'main.inventory': {
'Meta': {'ordering': "('name',)", 'unique_together': "[('name', 'organization')]", 'object_name': 'Inventory'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'inventory\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'groups_with_active_failures': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'has_active_failures': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'has_inventory_sources': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'hosts_with_active_failures': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'inventory_sources_with_failures': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'inventory\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
'organization': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'inventories'", 'to': "orm['main.Organization']"}),
'total_groups': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'total_hosts': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'total_inventory_sources': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'variables': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'})
},
'main.inventorysource': {
'Meta': {'object_name': 'InventorySource', '_ormbases': ['main.UnifiedJobTemplate']},
'credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'inventorysources'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
'group': ('awx.main.fields.AutoOneToOneField', [], {'default': 'None', 'related_name': "'inventory_source'", 'unique': 'True', 'null': 'True', 'to': "orm['main.Group']"}),
'group_by': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'instance_filters': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'inventory_sources'", 'null': 'True', 'to': "orm['main.Inventory']"}),
'overwrite': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'overwrite_vars': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'source': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '32', 'blank': 'True'}),
'source_path': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'source_regions': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'source_script': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['main.CustomInventoryScript']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
'source_vars': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
u'unifiedjobtemplate_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.UnifiedJobTemplate']", 'unique': 'True', 'primary_key': 'True'}),
'update_cache_timeout': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'update_on_launch': ('django.db.models.fields.BooleanField', [], {'default': 'False'})
},
'main.inventoryupdate': {
'Meta': {'object_name': 'InventoryUpdate', '_ormbases': ['main.UnifiedJob']},
'credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'inventoryupdates'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
'group_by': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'instance_filters': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'inventory_source': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'inventory_updates'", 'to': "orm['main.InventorySource']"}),
'license_error': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'overwrite': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'overwrite_vars': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'source': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '32', 'blank': 'True'}),
'source_path': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'source_regions': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'source_script': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['main.CustomInventoryScript']", 'null': 'True', 'on_delete': 'models.SET_NULL', 'blank': 'True'}),
'source_vars': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
u'unifiedjob_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.UnifiedJob']", 'unique': 'True', 'primary_key': 'True'})
},
'main.job': {
'Meta': {'ordering': "('id',)", 'object_name': 'Job', '_ormbases': ['main.UnifiedJob']},
'become_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'cloud_credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs_as_cloud_credential+'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
'credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
'extra_vars': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'force_handlers': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'forks': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'}),
'hosts': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'jobs'", 'symmetrical': 'False', 'through': "orm['main.JobHostSummary']", 'to': "orm['main.Host']"}),
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Inventory']"}),
'job_tags': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'job_template': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.JobTemplate']", 'blank': 'True', 'null': 'True'}),
'job_type': ('django.db.models.fields.CharField', [], {'default': "'run'", 'max_length': '64'}),
'limit': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'playbook': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Project']", 'blank': 'True', 'null': 'True'}),
'skip_tags': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'start_at_task': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
u'unifiedjob_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.UnifiedJob']", 'unique': 'True', 'primary_key': 'True'}),
'verbosity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'})
},
'main.jobevent': {
'Meta': {'ordering': "('pk',)", 'object_name': 'JobEvent'},
'changed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'counter': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
'event': ('django.db.models.fields.CharField', [], {'max_length': '100'}),
'event_data': ('jsonfield.fields.JSONField', [], {'default': '{}', 'blank': 'True'}),
'failed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'host': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'job_events_as_primary_host'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Host']"}),
'host_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
'hosts': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'job_events'", 'symmetrical': 'False', 'to': "orm['main.Host']"}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'job': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'job_events'", 'to': "orm['main.Job']"}),
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
'parent': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'children'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.JobEvent']"}),
'play': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
'role': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
'task': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'})
},
'main.jobhostsummary': {
'Meta': {'ordering': "('-pk',)", 'unique_together': "[('job', 'host_name')]", 'object_name': 'JobHostSummary'},
'changed': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
'dark': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'failed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'failures': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'host': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'job_host_summaries'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Host']"}),
'host_name': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'job': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'job_host_summaries'", 'to': "orm['main.Job']"}),
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
'ok': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'processed': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'}),
'skipped': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0'})
},
'main.joborigin': {
'Meta': {'object_name': 'JobOrigin'},
'created': ('django.db.models.fields.DateTimeField', [], {'auto_now_add': 'True', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'instance': ('django.db.models.fields.related.ForeignKey', [], {'to': "orm['main.Instance']"}),
'modified': ('django.db.models.fields.DateTimeField', [], {'auto_now': 'True', 'blank': 'True'}),
'unified_job': ('django.db.models.fields.related.OneToOneField', [], {'related_name': "'job_origin'", 'unique': 'True', 'to': "orm['main.UnifiedJob']"})
},
'main.jobtemplate': {
'Meta': {'ordering': "('name',)", 'object_name': 'JobTemplate', '_ormbases': ['main.UnifiedJobTemplate']},
'ask_variables_on_launch': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'become_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'cloud_credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobtemplates_as_cloud_credential+'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
'credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobtemplates'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
'extra_vars': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'force_handlers': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'forks': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'}),
'host_config_key': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobtemplates'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Inventory']"}),
'job_tags': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'job_type': ('django.db.models.fields.CharField', [], {'default': "'run'", 'max_length': '64'}),
'limit': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'playbook': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobtemplates'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Project']", 'blank': 'True', 'null': 'True'}),
'skip_tags': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'start_at_task': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'survey_enabled': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'survey_spec': ('jsonfield.fields.JSONField', [], {'default': '{}', 'blank': 'True'}),
u'unifiedjobtemplate_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.UnifiedJobTemplate']", 'unique': 'True', 'primary_key': 'True'}),
'verbosity': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'})
},
'main.organization': {
'Meta': {'ordering': "('name',)", 'object_name': 'Organization'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'admins': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'admin_of_organizations'", 'blank': 'True', 'to': u"orm['auth.User']"}),
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'organization\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'organization\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
'projects': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'organizations'", 'blank': 'True', 'to': "orm['main.Project']"}),
'users': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'organizations'", 'blank': 'True', 'to': u"orm['auth.User']"})
},
'main.permission': {
'Meta': {'object_name': 'Permission'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'permission\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'inventory': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'permissions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Inventory']"}),
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'permission\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
'permission_type': ('django.db.models.fields.CharField', [], {'max_length': '64'}),
'project': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'permissions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Project']"}),
'run_ad_hoc_commands': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'team': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'permissions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Team']"}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'blank': 'True', 'related_name': "'permissions'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"})
},
'main.profile': {
'Meta': {'object_name': 'Profile'},
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'ldap_dn': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024'}),
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
'user': ('awx.main.fields.AutoOneToOneField', [], {'related_name': "'profile'", 'unique': 'True', 'to': u"orm['auth.User']"})
},
'main.project': {
'Meta': {'ordering': "('id',)", 'object_name': 'Project', '_ormbases': ['main.UnifiedJobTemplate']},
'credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'projects'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
'local_path': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}),
'scm_branch': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
'scm_clean': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'scm_delete_on_next_update': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'scm_delete_on_update': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'scm_type': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '8', 'blank': 'True'}),
'scm_update_cache_timeout': ('django.db.models.fields.PositiveIntegerField', [], {'default': '0', 'blank': 'True'}),
'scm_update_on_launch': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'scm_url': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
u'unifiedjobtemplate_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.UnifiedJobTemplate']", 'unique': 'True', 'primary_key': 'True'})
},
'main.projectupdate': {
'Meta': {'object_name': 'ProjectUpdate', '_ormbases': ['main.UnifiedJob']},
'credential': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'projectupdates'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.Credential']", 'blank': 'True', 'null': 'True'}),
'local_path': ('django.db.models.fields.CharField', [], {'max_length': '1024', 'blank': 'True'}),
'project': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'project_updates'", 'to': "orm['main.Project']"}),
'scm_branch': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '256', 'blank': 'True'}),
'scm_clean': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'scm_delete_on_update': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'scm_type': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '8', 'blank': 'True'}),
'scm_url': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
u'unifiedjob_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.UnifiedJob']", 'unique': 'True', 'primary_key': 'True'})
},
'main.schedule': {
'Meta': {'ordering': "['-next_run']", 'object_name': 'Schedule'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'schedule\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'dtend': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
'dtstart': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
'enabled': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'extra_data': ('jsonfield.fields.JSONField', [], {'default': '{}', 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'schedule\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'name': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '512'}),
'next_run': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
'rrule': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
'unified_job_template': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'schedules'", 'to': "orm['main.UnifiedJobTemplate']"})
},
'main.systemjob': {
'Meta': {'ordering': "('id',)", 'object_name': 'SystemJob', '_ormbases': ['main.UnifiedJob']},
'extra_vars': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'job_type': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '32', 'blank': 'True'}),
'system_job_template': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'jobs'", 'on_delete': 'models.SET_NULL', 'default': 'None', 'to': "orm['main.SystemJobTemplate']", 'blank': 'True', 'null': 'True'}),
u'unifiedjob_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.UnifiedJob']", 'unique': 'True', 'primary_key': 'True'})
},
'main.systemjobtemplate': {
'Meta': {'object_name': 'SystemJobTemplate', '_ormbases': ['main.UnifiedJobTemplate']},
'job_type': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '32', 'blank': 'True'}),
u'unifiedjobtemplate_ptr': ('django.db.models.fields.related.OneToOneField', [], {'to': "orm['main.UnifiedJobTemplate']", 'unique': 'True', 'primary_key': 'True'})
},
'main.team': {
'Meta': {'ordering': "('organization__name', 'name')", 'unique_together': "[('organization', 'name')]", 'object_name': 'Team'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'team\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'team\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
'organization': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'teams'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Organization']"}),
'projects': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'teams'", 'blank': 'True', 'to': "orm['main.Project']"}),
'users': ('django.db.models.fields.related.ManyToManyField', [], {'symmetrical': 'False', 'related_name': "'teams'", 'blank': 'True', 'to': u"orm['auth.User']"})
},
'main.towersettings': {
'Meta': {'object_name': 'TowerSettings'},
'category': ('django.db.models.fields.CharField', [], {'max_length': '128'}),
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
'description': ('django.db.models.fields.TextField', [], {}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'key': ('django.db.models.fields.CharField', [], {'unique': 'True', 'max_length': '255'}),
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
'user': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'settings'", 'null': 'True', 'to': u"orm['auth.User']"}),
'value': ('django.db.models.fields.TextField', [], {}),
'value_type': ('django.db.models.fields.CharField', [], {'max_length': '12'})
},
'main.unifiedjob': {
'Meta': {'object_name': 'UnifiedJob'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'cancel_flag': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'celery_task_id': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '100', 'blank': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'unifiedjob\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'dependent_jobs': ('django.db.models.fields.related.ManyToManyField', [], {'related_name': "'dependent_jobs_rel_+'", 'to': "orm['main.UnifiedJob']"}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'elapsed': ('django.db.models.fields.DecimalField', [], {'max_digits': '12', 'decimal_places': '3'}),
'failed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'finished': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'job_args': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'job_cwd': ('django.db.models.fields.CharField', [], {'default': "''", 'max_length': '1024', 'blank': 'True'}),
'job_env': ('jsonfield.fields.JSONField', [], {'default': '{}', 'blank': 'True'}),
'job_explanation': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'launch_type': ('django.db.models.fields.CharField', [], {'default': "'manual'", 'max_length': '20'}),
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'unifiedjob\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
'old_pk': ('django.db.models.fields.PositiveIntegerField', [], {'default': 'None', 'null': 'True'}),
'polymorphic_ctype': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'polymorphic_main.unifiedjob_set'", 'null': 'True', 'to': u"orm['contenttypes.ContentType']"}),
'result_stdout_file': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'result_stdout_text': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'result_traceback': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'schedule': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'to': "orm['main.Schedule']", 'null': 'True', 'on_delete': 'models.SET_NULL'}),
'start_args': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'started': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
'status': ('django.db.models.fields.CharField', [], {'default': "'new'", 'max_length': '20'}),
'unified_job_template': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'unifiedjob_unified_jobs'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.UnifiedJobTemplate']"})
},
'main.unifiedjobtemplate': {
'Meta': {'unique_together': "[('polymorphic_ctype', 'name')]", 'object_name': 'UnifiedJobTemplate'},
'active': ('django.db.models.fields.BooleanField', [], {'default': 'True'}),
'created': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
'created_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'unifiedjobtemplate\', \'app_label\': \'main\'}(class)s_created+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'current_job': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'unifiedjobtemplate_as_current_job+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.UnifiedJob']"}),
'description': ('django.db.models.fields.TextField', [], {'default': "''", 'blank': 'True'}),
'has_schedules': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
u'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
'last_job': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'unifiedjobtemplate_as_last_job+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.UnifiedJob']"}),
'last_job_failed': ('django.db.models.fields.BooleanField', [], {'default': 'False'}),
'last_job_run': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
'modified': ('django.db.models.fields.DateTimeField', [], {'default': 'None'}),
'modified_by': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': '"{\'class\': \'unifiedjobtemplate\', \'app_label\': \'main\'}(class)s_modified+"', 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': u"orm['auth.User']"}),
'name': ('django.db.models.fields.CharField', [], {'max_length': '512'}),
'next_job_run': ('django.db.models.fields.DateTimeField', [], {'default': 'None', 'null': 'True'}),
'next_schedule': ('django.db.models.fields.related.ForeignKey', [], {'default': 'None', 'related_name': "'unifiedjobtemplate_as_next_schedule+'", 'null': 'True', 'on_delete': 'models.SET_NULL', 'to': "orm['main.Schedule']"}),
'old_pk': ('django.db.models.fields.PositiveIntegerField', [], {'default': 'None', 'null': 'True'}),
'polymorphic_ctype': ('django.db.models.fields.related.ForeignKey', [], {'related_name': "'polymorphic_main.unifiedjobtemplate_set'", 'null': 'True', 'to': u"orm['contenttypes.ContentType']"}),
'status': ('django.db.models.fields.CharField', [], {'default': "'ok'", 'max_length': '32'})
}
}
complete_apps = ['main']

View File

@ -16,6 +16,7 @@ from awx.main.models.ad_hoc_commands import * # noqa
from awx.main.models.schedules import * # noqa
from awx.main.models.activity_stream import * # noqa
from awx.main.models.ha import * # noqa
from awx.main.models.configuration import * # noqa
# Monkeypatch Django serializer to ignore django-taggit fields (which break
# the dumpdata command; see https://github.com/alex/django-taggit/issues/155).
@ -58,3 +59,4 @@ activity_stream_registrar.connect(AdHocCommand)
# activity_stream_registrar.connect(Profile)
activity_stream_registrar.connect(Schedule)
activity_stream_registrar.connect(CustomInventoryScript)
activity_stream_registrar.connect(TowerSettings)

View File

@ -53,6 +53,7 @@ class ActivityStream(models.Model):
ad_hoc_command = models.ManyToManyField("AdHocCommand", blank=True)
schedule = models.ManyToManyField("Schedule", blank=True)
custom_inventory_script = models.ManyToManyField("CustomInventoryScript", blank=True)
tower_settings = models.ManyToManyField("TowerSettings", blank=True)
def get_absolute_url(self):
return reverse('api:activity_stream_detail', args=(self.pk,))

View File

@ -21,6 +21,7 @@ from jsonfield import JSONField
from awx.main.models.base import * # noqa
from awx.main.models.unified_jobs import * # noqa
from awx.main.utils import decrypt_field
from awx.main.conf import tower_settings
logger = logging.getLogger('awx.main.models.ad_hoc_commands')
@ -29,8 +30,8 @@ __all__ = ['AdHocCommand', 'AdHocCommandEvent']
class AdHocCommand(UnifiedJob):
MODULE_NAME_CHOICES = [(x,x) for x in settings.AD_HOC_COMMANDS]
MODULE_NAME_DEFAULT = 'command' if 'command' in settings.AD_HOC_COMMANDS else None
MODULE_NAME_CHOICES = [(x,x) for x in tower_settings.AD_HOC_COMMANDS]
MODULE_NAME_DEFAULT = 'command' if 'command' in tower_settings.AD_HOC_COMMANDS else None
class Meta(object):
app_label = 'main'
@ -104,7 +105,7 @@ class AdHocCommand(UnifiedJob):
if type(self.module_name) not in (str, unicode):
raise ValidationError("Invalid type for ad hoc command")
module_name = self.module_name.strip() or 'command'
if module_name not in settings.AD_HOC_COMMANDS:
if module_name not in tower_settings.AD_HOC_COMMANDS:
raise ValidationError('Unsupported module for ad hoc commands.')
return module_name

View File

@ -0,0 +1,62 @@
# Copyright (c) 2015 Ansible, Inc.
# All Rights Reserved.
# Python
import json
# Django
from django.db import models
from django.utils.translation import ugettext_lazy as _
# Tower
from awx.main.models.base import CreatedModifiedModel
class TowerSettings(CreatedModifiedModel):
class Meta:
app_label = 'main'
SETTINGS_TYPE_CHOICES = [
('string', _("String")),
('int', _('Integer')),
('float', _('Decimal')),
('json', _('JSON')),
('bool', _('Boolean')),
('password', _('Password')),
('list', _('List'))
]
key = models.CharField(
max_length=255,
unique=True
)
description = models.TextField()
category = models.CharField(max_length=128)
value = models.TextField()
value_type = models.CharField(
max_length=12,
choices=SETTINGS_TYPE_CHOICES
)
user = models.ForeignKey(
'auth.User',
related_name='settings',
default=None,
null=True,
editable=False,
)
@property
def value_converted(self):
if self.value_type == 'json':
converted_type = json.loads(self.value)
elif self.value_type == 'password':
converted_type = self.value
elif self.value_type == 'list':
converted_type = [x.strip() for x in self.value.split(',')]
elif self.value_type == 'bool':
converted_type = self.value in [True, "true", "True", 1, "1", "yes"]
elif self.value_type == 'string':
converted_type = self.value
else:
t = __builtins__[self.value_type]
converted_type = t(self.value)
return converted_type

View File

@ -24,6 +24,7 @@ from awx.main.models.unified_jobs import * # noqa
from awx.main.utils import decrypt_field, ignore_inventory_computed_fields
from awx.main.utils import emit_websocket_notification
from awx.main.redact import PlainTextCleaner
from awx.main.conf import tower_settings
logger = logging.getLogger('awx.main.models.jobs')
@ -318,9 +319,9 @@ class JobTemplate(UnifiedJobTemplate, JobOptions):
@property
def cache_timeout_blocked(self):
if Job.objects.filter(job_template=self, status__in=['pending', 'waiting', 'running']).count() > getattr(settings, 'SCHEDULE_MAX_JOBS', 10):
if Job.objects.filter(job_template=self, status__in=['pending', 'waiting', 'running']).count() > getattr(tower_settings, 'SCHEDULE_MAX_JOBS', 10):
logger.error("Job template %s could not be started because there are more than %s other jobs from that template waiting to run" %
(self.name, getattr(settings, 'SCHEDULE_MAX_JOBS', 10)))
(self.name, getattr(tower_settings, 'SCHEDULE_MAX_JOBS', 10)))
return True
return False

View File

@ -18,6 +18,7 @@ from django.utils.translation import ugettext_lazy as _
# AWX
from awx.main.fields import AutoOneToOneField
from awx.main.models.base import * # noqa
from awx.main.conf import tower_settings
__all__ = ['Organization', 'Team', 'Permission', 'Profile', 'AuthToken']
@ -242,7 +243,7 @@ class AuthToken(BaseModel):
if not now:
now = tz_now()
if not self.pk or not self.is_expired(now=now):
self.expires = now + datetime.timedelta(seconds=settings.AUTH_TOKEN_EXPIRATION)
self.expires = now + datetime.timedelta(seconds=tower_settings.AUTH_TOKEN_EXPIRATION)
if save:
self.save()
@ -259,12 +260,12 @@ class AuthToken(BaseModel):
if now is None:
now = tz_now()
invalid_tokens = AuthToken.objects.none()
if settings.AUTH_TOKEN_PER_USER != -1:
if tower_settings.AUTH_TOKEN_PER_USER != -1:
invalid_tokens = AuthToken.objects.filter(
user=user,
expires__gt=now,
reason='',
).order_by('-created')[settings.AUTH_TOKEN_PER_USER:]
).order_by('-created')[tower_settings.AUTH_TOKEN_PER_USER:]
return invalid_tokens
def generate_key(self):
@ -293,7 +294,7 @@ class AuthToken(BaseModel):
valid_n_tokens_qs = self.user.auth_tokens.filter(
expires__gt=now,
reason='',
).order_by('-created')[0:settings.AUTH_TOKEN_PER_USER]
).order_by('-created')[0:tower_settings.AUTH_TOKEN_PER_USER]
valid_n_tokens = valid_n_tokens_qs.values_list('key', flat=True)
return bool(self.key in valid_n_tokens)

View File

@ -3,7 +3,6 @@
import logging
from django.conf import settings
from django.db.models.signals import pre_save, post_save, post_delete, m2m_changed
logger = logging.getLogger('awx.main.registrar')
@ -14,7 +13,8 @@ class ActivityStreamRegistrar(object):
self.models = []
def connect(self, model):
if not getattr(settings, 'ACTIVITY_STREAM_ENABLED', True):
from awx.main.conf import tower_settings
if not getattr(tower_settings, 'ACTIVITY_STREAM_ENABLED', True):
return
from awx.main.signals import activity_stream_create, activity_stream_update, activity_stream_delete, activity_stream_associate

View File

@ -8,7 +8,6 @@ import threading
import json
# Django
from django.conf import settings
from django.db.models.signals import pre_save, post_save, pre_delete, post_delete, m2m_changed
from django.dispatch import receiver
@ -22,6 +21,7 @@ from awx.api.serializers import * # noqa
from awx.main.utils import model_instance_diff, model_to_dict, camelcase_to_underscore, emit_websocket_notification
from awx.main.utils import ignore_inventory_computed_fields, ignore_inventory_group_removal, _inventory_updates
from awx.main.tasks import update_inventory_computed_fields
from awx.main.conf import tower_settings
__all__ = []
@ -273,7 +273,7 @@ def update_host_last_job_after_job_deleted(sender, **kwargs):
class ActivityStreamEnabled(threading.local):
def __init__(self):
self.enabled = getattr(settings, 'ACTIVITY_STREAM_ENABLED', True)
self.enabled = getattr(tower_settings, 'ACTIVITY_STREAM_ENABLED', True)
def __nonzero__(self):
return bool(self.enabled)
@ -306,6 +306,7 @@ model_serializer_mapping = {
JobTemplate: JobTemplateSerializer,
Job: JobSerializer,
AdHocCommand: AdHocCommandSerializer,
TowerSettings: TowerSettingsSerializer,
}
def activity_stream_create(sender, instance, created, **kwargs):
@ -320,6 +321,10 @@ def activity_stream_create(sender, instance, created, **kwargs):
object1=object1,
changes=json.dumps(model_to_dict(instance, model_serializer_mapping)))
activity_entry.save()
#TODO: Weird situation where cascade SETNULL doesn't work
# it might actually be a good idea to remove all of these FK references since
# we don't really use them anyway.
if type(instance) is not TowerSettings:
getattr(activity_entry, object1).add(instance)
def activity_stream_update(sender, instance, **kwargs):
@ -347,6 +352,7 @@ def activity_stream_update(sender, instance, **kwargs):
object1=object1,
changes=json.dumps(changes))
activity_entry.save()
if type(instance) is not TowerSettings:
getattr(activity_entry, object1).add(instance)
def activity_stream_delete(sender, instance, **kwargs):

View File

@ -44,6 +44,7 @@ from django.utils.timezone import now
from awx.main.constants import CLOUD_PROVIDERS
from awx.main.models import * # noqa
from awx.main.queue import FifoQueue
from awx.main.conf import tower_settings
from awx.main.utils import (get_ansible_version, get_ssh_version, decrypt_field, update_scm_url,
ignore_inventory_computed_fields, emit_websocket_notification,
check_proot_installed, build_proot_temp_dir, wrap_args_with_proot)
@ -348,7 +349,7 @@ class BaseTask(Task):
python_paths.insert(0, local_site_packages)
env['PYTHONPATH'] = os.pathsep.join(python_paths)
if self.should_use_proot:
env['PROOT_TMP_DIR'] = settings.AWX_PROOT_BASE_PATH
env['PROOT_TMP_DIR'] = tower_settings.AWX_PROOT_BASE_PATH
return env
def build_safe_env(self, instance, **kwargs):
@ -461,7 +462,7 @@ class BaseTask(Task):
instance = self.update_model(instance.pk)
if instance.cancel_flag:
try:
if settings.AWX_PROOT_ENABLED:
if tower_settings.AWX_PROOT_ENABLED:
# NOTE: Refactor this once we get a newer psutil across the board
if not psutil:
os.kill(child.pid, signal.SIGKILL)
@ -654,9 +655,9 @@ class RunJob(BaseTask):
'''
plugin_dir = self.get_path_to('..', 'plugins', 'callback')
plugin_dirs = [plugin_dir]
if hasattr(settings, 'AWX_ANSIBLE_CALLBACK_PLUGINS') and \
settings.AWX_ANSIBLE_CALLBACK_PLUGINS:
plugin_dirs.append(settings.AWX_ANSIBLE_CALLBACK_PLUGINS)
if hasattr(tower_settings, 'AWX_ANSIBLE_CALLBACK_PLUGINS') and \
tower_settings.AWX_ANSIBLE_CALLBACK_PLUGINS:
plugin_dirs.append(tower_settings.AWX_ANSIBLE_CALLBACK_PLUGINS)
plugin_path = ':'.join(plugin_dirs)
env = super(RunJob, self).build_env(job, **kwargs)
# Set environment variables needed for inventory and job event
@ -850,7 +851,7 @@ class RunJob(BaseTask):
'''
Return whether this task should use proot.
'''
return getattr(settings, 'AWX_PROOT_ENABLED', False)
return getattr(tower_settings, 'AWX_PROOT_ENABLED', False)
def pre_run_hook(self, job, **kwargs):
if job.job_type == PERM_INVENTORY_SCAN:
@ -1475,7 +1476,7 @@ class RunAdHocCommand(BaseTask):
'''
Return whether this task should use proot.
'''
return getattr(settings, 'AWX_PROOT_ENABLED', False)
return getattr(tower_settings, 'AWX_PROOT_ENABLED', False)
def post_run_hook(self, ad_hoc_command, **kwargs):
'''

View File

@ -19,3 +19,4 @@ from awx.main.tests.commands import * # noqa
from awx.main.tests.fact import * # noqa
from awx.main.tests.unified_jobs import * # noqa
from awx.main.tests.ha import * # noqa
from awx.main.tests.settings import * # noqa

105
awx/main/tests/settings.py Normal file
View File

@ -0,0 +1,105 @@
# Copyright (c) 2016 Ansible, Inc.
# All Rights Reserved.
from awx.main.tests.base import BaseTest
from awx.main.models import * # noqa
from django.core.urlresolvers import reverse
from django.test.utils import override_settings
TEST_TOWER_SETTINGS_MANIFEST = {
"TEST_SETTING_INT": {
"name": "An Integer Field",
"description": "An Integer Field",
"default": 1,
"type": "int",
"category": "test"
},
"TEST_SETTING_STRING": {
"name": "A String Field",
"description": "A String Field",
"default": "test",
"type": "string",
"category": "test"
},
"TEST_SETTING_BOOL": {
"name": "A Bool Field",
"description": "A Bool Field",
"default": True,
"type": "bool",
"category": "test"
},
"TEST_SETTING_LIST": {
"name": "A List Field",
"description": "A List Field",
"default": ["A", "Simple", "List"],
"type": "list",
"category": "test"
}
}
@override_settings(TOWER_SETTINGS_MANIFEST=TEST_TOWER_SETTINGS_MANIFEST)
class SettingsTest(BaseTest):
def setUp(self):
super(SettingsTest, self).setUp()
self.setup_instances()
self.setup_users()
def get_settings(self, expected_count=4):
result = self.get(reverse('api:settings_list'), expect=200)
self.assertEqual(result['count'], expected_count)
return result['results']
def get_individual_setting(self, setting):
all_settings = self.get_settings()
setting_actual = None
for setting_item in all_settings:
if setting_item['key'] == setting:
setting_actual = setting_item
break
self.assertIsNotNone(setting_actual)
return setting_actual
def set_setting(self, key, value):
self.post(reverse('api:settings_list'), data={"key": key, "value": value}, expect=201)
def test_get_settings(self):
# Regular user should see nothing (no user settings yet)
with self.current_user(self.normal_django_user):
self.get_settings(expected_count=0)
# anonymous user should get a 401
self.get(reverse('api:settings_list'), expect=401)
# super user can see everything
with self.current_user(self.super_django_user):
self.get_settings(expected_count=len(TEST_TOWER_SETTINGS_MANIFEST))
def test_set_and_reset_settings(self):
settings_reset = reverse('api:settings_reset')
with self.current_user(self.super_django_user):
# Set and reset a single setting
setting_int = self.get_individual_setting('TEST_SETTING_INT')
self.assertEqual(setting_int['value'], TEST_TOWER_SETTINGS_MANIFEST['TEST_SETTING_INT']['default'])
self.set_setting('TEST_SETTING_INT', 2)
setting_int = self.get_individual_setting('TEST_SETTING_INT')
self.assertEqual(setting_int['value'], 2)
self.post(settings_reset, data={"key": 'TEST_SETTING_INT'}, expect=204)
setting_int = self.get_individual_setting('TEST_SETTING_INT')
self.assertEqual(setting_int['value'], TEST_TOWER_SETTINGS_MANIFEST['TEST_SETTING_INT']['default'])
def test_clear_all_settings(self):
settings_list = reverse('api:settings_list')
with self.current_user(self.super_django_user):
self.set_setting('TEST_SETTING_INT', 2)
self.set_setting('TEST_SETTING_STRING', "foo")
self.set_setting('TEST_SETTING_BOOL', False)
self.set_setting('TEST_SETTING_LIST', [1,2,3])
all_settings = self.get_settings()
for setting_entry in all_settings:
self.assertNotEqual(setting_entry['value'],
TEST_TOWER_SETTINGS_MANIFEST[setting_entry['key']]['default'])
self.delete(settings_list, expect=200)
all_settings = self.get_settings()
for setting_entry in all_settings:
self.assertEqual(setting_entry['value'],
TEST_TOWER_SETTINGS_MANIFEST[setting_entry['key']]['default'])

View File

@ -16,6 +16,7 @@ from django.test.utils import override_settings
# AWX
from awx.main.models import * # noqa
from awx.main.tests.base import BaseTest
from awx.main.conf import tower_settings
__all__ = ['AuthTokenTimeoutTest', 'AuthTokenLimitTest', 'AuthTokenProxyTest', 'UsersTest', 'LdapTest']
@ -38,7 +39,7 @@ class AuthTokenTimeoutTest(BaseTest):
response = self._generic_rest(dashboard_url, expect=200, method='get', return_response_object=True, client_kwargs=kwargs)
self.assertIn('Auth-Token-Timeout', response)
self.assertEqual(response['Auth-Token-Timeout'], str(settings.AUTH_TOKEN_EXPIRATION))
self.assertEqual(response['Auth-Token-Timeout'], str(tower_settings.AUTH_TOKEN_EXPIRATION))
class AuthTokenLimitTest(BaseTest):
def setUp(self):

View File

@ -448,8 +448,8 @@ def build_proot_temp_dir():
'''
Create a temporary directory for proot to use.
'''
from django.conf import settings
path = tempfile.mkdtemp(prefix='ansible_tower_proot_', dir=settings.AWX_PROOT_BASE_PATH)
from awx.main.conf import tower_settings
path = tempfile.mkdtemp(prefix='ansible_tower_proot_', dir=tower_settings.AWX_PROOT_BASE_PATH)
os.chmod(path, stat.S_IRUSR | stat.S_IWUSR | stat.S_IXUSR)
return path
@ -462,13 +462,14 @@ def wrap_args_with_proot(args, cwd, **kwargs):
- /var/log/supervisor
- /tmp (except for own tmp files)
'''
from awx.main.conf import tower_settings
from django.conf import settings
new_args = [getattr(settings, 'AWX_PROOT_CMD', 'proot'), '-v',
str(getattr(settings, 'AWX_PROOT_VERBOSITY', '0')), '-r', '/']
hide_paths = ['/etc/tower', '/var/lib/awx', '/var/log',
tempfile.gettempdir(), settings.PROJECTS_ROOT,
settings.JOBOUTPUT_ROOT]
hide_paths.extend(getattr(settings, 'AWX_PROOT_HIDE_PATHS', None) or [])
hide_paths.extend(getattr(tower_settings, 'AWX_PROOT_HIDE_PATHS', None) or [])
for path in sorted(set(hide_paths)):
if not os.path.exists(path):
continue
@ -484,7 +485,7 @@ def wrap_args_with_proot(args, cwd, **kwargs):
show_paths = [cwd, kwargs['private_data_dir']]
else:
show_paths = [cwd]
show_paths.extend(getattr(settings, 'AWX_PROOT_SHOW_PATHS', None) or [])
show_paths.extend(getattr(tower_settings, 'AWX_PROOT_SHOW_PATHS', None) or [])
for path in sorted(set(show_paths)):
if not os.path.exists(path):
continue

View File

@ -262,11 +262,11 @@ SERVER_EMAIL = 'root@localhost'
# Default email address to use for various automated correspondence from
# the site managers.
DEFAULT_FROM_EMAIL = 'webmaster@localhost'
DEFAULT_FROM_EMAIL = 'tower@localhost'
# Subject-line prefix for email messages send with django.core.mail.mail_admins
# or ...mail_managers. Make sure to include the trailing space.
EMAIL_SUBJECT_PREFIX = '[AWX] '
EMAIL_SUBJECT_PREFIX = '[Tower] '
# The email backend to use. For possible shortcuts see django.core.mail.
# The default is to use the SMTP backend.
@ -688,6 +688,141 @@ FACT_CACHE_PORT = 6564
ORG_ADMINS_CAN_SEE_ALL_USERS = True
TOWER_SETTINGS_MANIFEST = {
"SCHEDULE_MAX_JOBS": {
"name": "Maximum Scheduled Jobs",
"description": "Maximum number of the same job template that can be waiting to run when launching from a schedule before no more are created",
"default": SCHEDULE_MAX_JOBS,
"type": "int",
"category": "jobs",
},
"STDOUT_MAX_BYTES_DISPLAY": {
"name": "Standard Output Maximum Display Size",
"description": "Maximum Size of Standard Output in bytes to display before requiring the output be downloaded",
"default": STDOUT_MAX_BYTES_DISPLAY,
"type": "int",
"category": "jobs",
},
"AUTH_TOKEN_EXPIRATION": {
"name": "Idle Time Force Log Out",
"description": "Number of seconds that a user is inactive before they will need to login again",
"type": "int",
"default": AUTH_TOKEN_EXPIRATION,
"category": "authentication",
},
"AUTH_TOKEN_PER_USER": {
"name": "Maximum number of simultaneous logins",
"description": "Maximum number of simultaneous logins a user may have. To disable enter -1",
"type": "int",
"default": AUTH_TOKEN_PER_USER,
"category": "authentication",
},
# "AUTH_BASIC_ENABLED": {
# "name": "Enable HTTP Basic Auth",
# "description": "Enable HTTP Basic Auth for the API Browser",
# "default": AUTH_BASIC_ENABLED,
# "type": "bool",
# "category": "authentication",
# },
# "AUTH_LDAP_SERVER_URI": {
# "name": "LDAP Server URI",
# "description": "URI Location of the LDAP Server",
# "default": AUTH_LDAP_SERVER_URI,
# "type": "string",
# "category": "authentication",
# },
# "RADIUS_SERVER": {
# "name": "Radius Server Host",
# "description": "Host to communicate with for Radius Authentication",
# "default": RADIUS_SERVER,
# "type": "string",
# "category": "authentication",
# },
# "RADIUS_PORT": {
# "name": "Radius Server Port",
# "description": "Port on the Radius host for Radius Authentication",
# "default": RADIUS_PORT,
# "type": "string",
# "category": "authentication",
# },
# "RADIUS_SECRET": {
# "name": "Radius Server Secret",
# "description": "Secret used when negotiating with the Radius server",
# "default": RADIUS_SECRET,
# "type": "string",
# "category": "authentication",
# },
"AWX_PROOT_ENABLED": {
"name": "Enable PRoot for Job Execution",
"description": "Isolates an Ansible job from protected parts of the Tower system to prevent exposing sensitive information",
"default": AWX_PROOT_ENABLED,
"type": "bool",
"category": "jobs",
},
"AWX_PROOT_HIDE_PATHS": {
"name": "Paths to hide from PRoot jobs",
"description": "Extra paths to hide from PRoot isolated processes",
"default": AWX_PROOT_HIDE_PATHS,
"type": "list",
"category": "jobs",
},
"AWX_PROOT_SHOW_PATHS": {
"name": "Paths to expose to PRoot jobs",
"description": "Explicit whitelist of paths to expose to PRoot jobs",
"default": AWX_PROOT_SHOW_PATHS,
"type": "list",
"category": "jobs",
},
"AWX_PROOT_BASE_PATH": {
"name": "Base PRoot execution path",
"description": "The location that PRoot will create its temporary working directory",
"default": AWX_PROOT_BASE_PATH,
"type": "string",
"category": "jobs",
},
"AWX_ANSIBLE_CALLBACK_PLUGINS": {
"name": "Ansible Callback Plugins",
"description": "Colon Seperated Paths for extra callback plugins to be used when running jobs",
"default": AWX_ANSIBLE_CALLBACK_PLUGINS,
"type": "string",
"category": "jobs",
},
"PENDO_TRACKING_STATE": {
"name": "Analytics Tracking State",
"description": "Enable or Disable Analytics Tracking",
"default": PENDO_TRACKING_STATE,
"type": "string",
"category": "ui",
},
"AD_HOC_COMMANDS": {
"name": "Ansible Modules Allowed for Ad Hoc Jobs",
"description": "A colon-seperated whitelist of modules allowed to be used by ad-hoc jobs",
"default": AD_HOC_COMMANDS,
"type": "list",
"category": "jobs",
},
"ACTIVITY_STREAM_ENABLED": {
"name": "Enable Activity Stream",
"description": "Enable capturing activity for the Tower activity stream",
"default": ACTIVITY_STREAM_ENABLED,
"type": "bool",
"category": "system",
},
"ORG_ADMINS_CAN_SEE_ALL_USERS": {
"name": "All Users Visible to Organization Admins",
"description": "Controls whether any Organization Admin can view all users, even those not associated with their Organization",
"default": ORG_ADMINS_CAN_SEE_ALL_USERS,
"type": "bool",
"category": "system",
},
"LICENSE": {
"name": "Tower License",
"description": "Controls what features and functionality is enabled in Tower.",
"default": "{}",
"type": "string",
"category": "system",
},
}
# Logging configuration.
LOGGING = {
'version': 1,