Merge pull request #4070 from jbradberry/upgrade-django-2.2

Upgrade Django to 2.2

Reviewed-by: https://github.com/softwarefactory-project-zuul[bot]
This commit is contained in:
softwarefactory-project-zuul[bot]
2019-07-15 22:56:22 +00:00
committed by GitHub
58 changed files with 292 additions and 375 deletions

View File

@@ -25,9 +25,8 @@ import hashlib
try: try:
import django import django
from django.utils.encoding import force_bytes
from django.db.backends.base.schema import BaseDatabaseSchemaEditor
from django.db.backends.base import schema from django.db.backends.base import schema
from django.db.backends.utils import names_digest
HAS_DJANGO = True HAS_DJANGO = True
except ImportError: except ImportError:
HAS_DJANGO = False HAS_DJANGO = False
@@ -37,30 +36,33 @@ if HAS_DJANGO is True:
# This line exists to make sure we don't regress on FIPS support if we # This line exists to make sure we don't regress on FIPS support if we
# upgrade Django; if you're upgrading Django and see this error, # upgrade Django; if you're upgrading Django and see this error,
# update the version check below, and confirm that FIPS still works. # update the version check below, and confirm that FIPS still works.
if django.__version__ != '1.11.20': # If operating in a FIPS environment, `hashlib.md5()` will raise a `ValueError`,
raise RuntimeError("Django version other than 1.11.20 detected {}. \ # but will support the `usedforsecurity` keyword on RHEL and Centos systems.
Subclassing BaseDatabaseSchemaEditor is known to work for Django 1.11.20 \
and may not work in newer Django versions.".format(django.__version__))
# Keep an eye on https://code.djangoproject.com/ticket/28401
target_version = '2.2.2'
if django.__version__ != target_version:
raise RuntimeError(
"Django version other than {target} detected: {current}. "
"Overriding `names_digest` is known to work for Django {target} "
"and may not work in other Django versions.".format(target=target_version,
current=django.__version__)
)
class FipsBaseDatabaseSchemaEditor(BaseDatabaseSchemaEditor):
@classmethod
def _digest(cls, *args):
"""
Generates a 32-bit digest of a set of arguments that can be used to
shorten identifying names.
"""
try: try:
h = hashlib.md5() names_digest('foo', 'bar', 'baz', length=8)
except ValueError: except ValueError:
def names_digest(*args, length):
"""
Generate a 32-bit digest of a set of arguments that can be used to shorten
identifying names. Support for use in FIPS environments.
"""
h = hashlib.md5(usedforsecurity=False) h = hashlib.md5(usedforsecurity=False)
for arg in args: for arg in args:
h.update(force_bytes(arg)) h.update(arg.encode())
return h.hexdigest()[:8] return h.hexdigest()[:length]
schema.names_digest = names_digest
schema.BaseDatabaseSchemaEditor = FipsBaseDatabaseSchemaEditor
def find_commands(management_dir): def find_commands(management_dir):

View File

@@ -401,21 +401,21 @@ class ListAPIView(generics.ListAPIView, GenericAPIView):
continue continue
if getattr(field, 'related_model', None): if getattr(field, 'related_model', None):
fields.add('{}__search'.format(field.name)) fields.add('{}__search'.format(field.name))
for rel in self.model._meta.related_objects: for related in self.model._meta.related_objects:
name = rel.related_name name = related.related_name
if isinstance(rel, OneToOneRel) and self.model._meta.verbose_name.startswith('unified'): if isinstance(related, OneToOneRel) and self.model._meta.verbose_name.startswith('unified'):
# Add underscores for polymorphic subclasses for user utility # Add underscores for polymorphic subclasses for user utility
name = rel.related_model._meta.verbose_name.replace(" ", "_") name = related.related_model._meta.verbose_name.replace(" ", "_")
if skip_related_name(name) or name.endswith('+'): if skip_related_name(name) or name.endswith('+'):
continue continue
fields.add('{}__search'.format(name)) fields.add('{}__search'.format(name))
m2m_rel = [] m2m_related = []
m2m_rel += self.model._meta.local_many_to_many m2m_related += self.model._meta.local_many_to_many
if issubclass(self.model, UnifiedJobTemplate) and self.model != UnifiedJobTemplate: if issubclass(self.model, UnifiedJobTemplate) and self.model != UnifiedJobTemplate:
m2m_rel += UnifiedJobTemplate._meta.local_many_to_many m2m_related += UnifiedJobTemplate._meta.local_many_to_many
if issubclass(self.model, UnifiedJob) and self.model != UnifiedJob: if issubclass(self.model, UnifiedJob) and self.model != UnifiedJob:
m2m_rel += UnifiedJob._meta.local_many_to_many m2m_related += UnifiedJob._meta.local_many_to_many
for relationship in m2m_rel: for relationship in m2m_related:
if skip_related_name(relationship.name): if skip_related_name(relationship.name):
continue continue
if relationship.related_model._meta.app_label != 'main': if relationship.related_model._meta.app_label != 'main':

View File

@@ -95,7 +95,7 @@ class ModelAccessPermission(permissions.BasePermission):
''' '''
# Don't allow anonymous users. 401, not 403, hence no raised exception. # Don't allow anonymous users. 401, not 403, hence no raised exception.
if not request.user or request.user.is_anonymous(): if not request.user or request.user.is_anonymous:
return False return False
# Always allow superusers # Always allow superusers

View File

@@ -2560,7 +2560,7 @@ class CredentialSerializer(BaseSerializer):
def validate_credential_type(self, credential_type): def validate_credential_type(self, credential_type):
if self.instance and credential_type.pk != self.instance.credential_type.pk: if self.instance and credential_type.pk != self.instance.credential_type.pk:
for rel in ( for related_objects in (
'ad_hoc_commands', 'ad_hoc_commands',
'insights_inventories', 'insights_inventories',
'unifiedjobs', 'unifiedjobs',
@@ -2569,7 +2569,7 @@ class CredentialSerializer(BaseSerializer):
'projectupdates', 'projectupdates',
'workflowjobnodes' 'workflowjobnodes'
): ):
if getattr(self.instance, rel).count() > 0: if getattr(self.instance, related_objects).count() > 0:
raise ValidationError( raise ValidationError(
_('You cannot change the credential type of the credential, as it may break the functionality' _('You cannot change the credential type of the credential, as it may break the functionality'
' of the resources using it.'), ' of the resources using it.'),
@@ -4640,37 +4640,37 @@ class ActivityStreamSerializer(BaseSerializer):
return "" return ""
def get_related(self, obj): def get_related(self, obj):
rel = {} data = {}
if obj.actor is not None: if obj.actor is not None:
rel['actor'] = self.reverse('api:user_detail', kwargs={'pk': obj.actor.pk}) data['actor'] = self.reverse('api:user_detail', kwargs={'pk': obj.actor.pk})
for fk, __ in self._local_summarizable_fk_fields: for fk, __ in self._local_summarizable_fk_fields:
if not hasattr(obj, fk): if not hasattr(obj, fk):
continue continue
m2m_list = self._get_rel(obj, fk) m2m_list = self._get_related_objects(obj, fk)
if m2m_list: if m2m_list:
rel[fk] = [] data[fk] = []
id_list = [] id_list = []
for thisItem in m2m_list: for item in m2m_list:
if getattr(thisItem, 'id', None) in id_list: if getattr(item, 'id', None) in id_list:
continue continue
id_list.append(getattr(thisItem, 'id', None)) id_list.append(getattr(item, 'id', None))
if hasattr(thisItem, 'get_absolute_url'): if hasattr(item, 'get_absolute_url'):
rel_url = thisItem.get_absolute_url(self.context.get('request')) url = item.get_absolute_url(self.context.get('request'))
else: else:
view_name = fk + '_detail' view_name = fk + '_detail'
rel_url = self.reverse('api:' + view_name, kwargs={'pk': thisItem.id}) url = self.reverse('api:' + view_name, kwargs={'pk': item.id})
rel[fk].append(rel_url) data[fk].append(url)
if fk == 'schedule': if fk == 'schedule':
rel['unified_job_template'] = thisItem.unified_job_template.get_absolute_url(self.context.get('request')) data['unified_job_template'] = item.unified_job_template.get_absolute_url(self.context.get('request'))
if obj.setting and obj.setting.get('category', None): if obj.setting and obj.setting.get('category', None):
rel['setting'] = self.reverse( data['setting'] = self.reverse(
'api:setting_singleton_detail', 'api:setting_singleton_detail',
kwargs={'category_slug': obj.setting['category']} kwargs={'category_slug': obj.setting['category']}
) )
return rel return data
def _get_rel(self, obj, fk): def _get_related_objects(self, obj, fk):
related_model = ActivityStream._meta.get_field(fk).related_model related_model = ActivityStream._meta.get_field(fk).related_model
related_manager = getattr(obj, fk) related_manager = getattr(obj, fk)
if issubclass(related_model, PolymorphicModel) and hasattr(obj, '_prefetched_objects_cache'): if issubclass(related_model, PolymorphicModel) and hasattr(obj, '_prefetched_objects_cache'):
@@ -4703,7 +4703,7 @@ class ActivityStreamSerializer(BaseSerializer):
try: try:
if not hasattr(obj, fk): if not hasattr(obj, fk):
continue continue
m2m_list = self._get_rel(obj, fk) m2m_list = self._get_related_objects(obj, fk)
if m2m_list: if m2m_list:
summary_fields[fk] = [] summary_fields[fk] = []
for thisItem in m2m_list: for thisItem in m2m_list:

View File

@@ -2997,7 +2997,7 @@ class WorkflowJobTemplateNodeChildrenBaseList(EnforceParentRelationshipMixin, Su
relationships = ['success_nodes', 'failure_nodes', 'always_nodes'] relationships = ['success_nodes', 'failure_nodes', 'always_nodes']
relationships.remove(self.relationship) relationships.remove(self.relationship)
qs = functools.reduce(lambda x, y: (x | y), qs = functools.reduce(lambda x, y: (x | y),
(Q(**{'{}__in'.format(rel): [sub.id]}) for rel in relationships)) (Q(**{'{}__in'.format(r): [sub.id]}) for r in relationships))
if models.WorkflowJobTemplateNode.objects.filter(Q(pk=parent.id) & qs).exists(): if models.WorkflowJobTemplateNode.objects.filter(Q(pk=parent.id) & qs).exists():
return {"Error": _("Relationship not allowed.")} return {"Error": _("Relationship not allowed.")}

View File

@@ -21,7 +21,8 @@ class Migration(migrations.Migration):
('modified', models.DateTimeField(default=None, editable=False)), ('modified', models.DateTimeField(default=None, editable=False)),
('key', models.CharField(max_length=255)), ('key', models.CharField(max_length=255)),
('value', jsonfield.fields.JSONField(null=True)), ('value', jsonfield.fields.JSONField(null=True)),
('user', models.ForeignKey(related_name='settings', default=None, editable=False, to=settings.AUTH_USER_MODEL, null=True)), ('user', models.ForeignKey(related_name='settings', default=None, editable=False,
to=settings.AUTH_USER_MODEL, on_delete=models.CASCADE, null=True)),
], ],
options={ options={
'abstract': False, 'abstract': False,

View File

@@ -24,7 +24,7 @@ def ws_connect(message):
headers = dict(message.content.get('headers', '')) headers = dict(message.content.get('headers', ''))
message.reply_channel.send({"accept": True}) message.reply_channel.send({"accept": True})
message.content['method'] = 'FAKE' message.content['method'] = 'FAKE'
if message.user.is_authenticated(): if message.user.is_authenticated:
message.reply_channel.send( message.reply_channel.send(
{"text": json.dumps({"accept": True, "user": message.user.id})} {"text": json.dumps({"accept": True, "user": message.user.id})}
) )

View File

@@ -11,8 +11,9 @@ from jinja2 import Environment, StrictUndefined
from jinja2.exceptions import UndefinedError, TemplateSyntaxError from jinja2.exceptions import UndefinedError, TemplateSyntaxError
# Django # Django
import django from django.contrib.postgres.fields import JSONField as upstream_JSONBField
from django.core import exceptions as django_exceptions from django.core import exceptions as django_exceptions
from django.core.serializers.json import DjangoJSONEncoder
from django.db.models.signals import ( from django.db.models.signals import (
post_save, post_save,
post_delete, post_delete,
@@ -37,7 +38,6 @@ import jsonschema.exceptions
# Django-JSONField # Django-JSONField
from jsonfield import JSONField as upstream_JSONField from jsonfield import JSONField as upstream_JSONField
from jsonbfield.fields import JSONField as upstream_JSONBField
# DRF # DRF
from rest_framework import serializers from rest_framework import serializers
@@ -76,10 +76,10 @@ class JSONField(upstream_JSONField):
def db_type(self, connection): def db_type(self, connection):
return 'text' return 'text'
def from_db_value(self, value, expression, connection, context): def from_db_value(self, value, expression, connection):
if value in {'', None} and not self.null: if value in {'', None} and not self.null:
return {} return {}
return super(JSONField, self).from_db_value(value, expression, connection, context) return super(JSONField, self).from_db_value(value, expression, connection)
class JSONBField(upstream_JSONBField): class JSONBField(upstream_JSONBField):
@@ -91,12 +91,12 @@ class JSONBField(upstream_JSONBField):
def get_db_prep_value(self, value, connection, prepared=False): def get_db_prep_value(self, value, connection, prepared=False):
if connection.vendor == 'sqlite': if connection.vendor == 'sqlite':
# sqlite (which we use for tests) does not support jsonb; # sqlite (which we use for tests) does not support jsonb;
return json.dumps(value) return json.dumps(value, cls=DjangoJSONEncoder)
return super(JSONBField, self).get_db_prep_value( return super(JSONBField, self).get_db_prep_value(
value, connection, prepared value, connection, prepared
) )
def from_db_value(self, value, expression, connection, context): def from_db_value(self, value, expression, connection):
# Work around a bug in django-jsonfield # Work around a bug in django-jsonfield
# https://bitbucket.org/schinckel/django-jsonfield/issues/57/cannot-use-in-the-same-project-as-djangos # https://bitbucket.org/schinckel/django-jsonfield/issues/57/cannot-use-in-the-same-project-as-djangos
if isinstance(value, str): if isinstance(value, str):
@@ -112,14 +112,9 @@ class AutoSingleRelatedObjectDescriptor(ReverseOneToOneDescriptor):
def __get__(self, instance, instance_type=None): def __get__(self, instance, instance_type=None):
try: try:
return super(AutoSingleRelatedObjectDescriptor, return super(AutoSingleRelatedObjectDescriptor, self).__get__(instance, instance_type)
self).__get__(instance, instance_type)
except self.related.related_model.DoesNotExist: except self.related.related_model.DoesNotExist:
obj = self.related.related_model(**{self.related.field.name: instance}) obj = self.related.related_model(**{self.related.field.name: instance})
if self.related.field.rel.parent_link:
raise NotImplementedError('not supported with polymorphic!')
for f in instance._meta.local_fields:
setattr(obj, f.name, getattr(instance, f.name))
obj.save() obj.save()
return obj return obj
@@ -453,21 +448,6 @@ class JSONSchemaField(JSONBField):
params={'value': value}, params={'value': value},
) )
def get_db_prep_value(self, value, connection, prepared=False):
if connection.vendor == 'sqlite':
# sqlite (which we use for tests) does not support jsonb;
return json.dumps(value)
return super(JSONSchemaField, self).get_db_prep_value(
value, connection, prepared
)
def from_db_value(self, value, expression, connection, context):
# Work around a bug in django-jsonfield
# https://bitbucket.org/schinckel/django-jsonfield/issues/57/cannot-use-in-the-same-project-as-djangos
if isinstance(value, str):
return json.loads(value)
return value
@JSONSchemaField.format_checker.checks('vault_id') @JSONSchemaField.format_checker.checks('vault_id')
def format_vault_id(value): def format_vault_id(value):
@@ -986,7 +966,7 @@ class OAuth2ClientSecretField(models.CharField):
encrypt_value(value), connection, prepared encrypt_value(value), connection, prepared
) )
def from_db_value(self, value, expression, connection, context): def from_db_value(self, value, expression, connection):
if value and value.startswith('$encrypted$'): if value and value.startswith('$encrypted$'):
return decrypt_value(get_encryption_key('value', pk=None), value) return decrypt_value(get_encryption_key('value', pk=None), value)
return value return value
@@ -1022,38 +1002,6 @@ class OrderedManyToManyDescriptor(ManyToManyDescriptor):
'%s__position' % self.through._meta.model_name '%s__position' % self.through._meta.model_name
) )
def add(self, *objs):
# Django < 2 doesn't support this method on
# ManyToManyFields w/ an intermediary model
# We should be able to remove this code snippet when we
# upgrade Django.
# see: https://github.com/django/django/blob/stable/1.11.x/django/db/models/fields/related_descriptors.py#L926
if not django.__version__.startswith('1.'):
raise RuntimeError(
'This method is no longer necessary in Django>=2'
)
try:
self.through._meta.auto_created = True
super(OrderedManyRelatedManager, self).add(*objs)
finally:
self.through._meta.auto_created = False
def remove(self, *objs):
# Django < 2 doesn't support this method on
# ManyToManyFields w/ an intermediary model
# We should be able to remove this code snippet when we
# upgrade Django.
# see: https://github.com/django/django/blob/stable/1.11.x/django/db/models/fields/related_descriptors.py#L944
if not django.__version__.startswith('1.'):
raise RuntimeError(
'This method is no longer necessary in Django>=2'
)
try:
self.through._meta.auto_created = True
super(OrderedManyRelatedManager, self).remove(*objs)
finally:
self.through._meta.auto_created = False
return OrderedManyRelatedManager return OrderedManyRelatedManager
return add_custom_queryset_to_many_related_manager( return add_custom_queryset_to_many_related_manager(

View File

@@ -73,7 +73,7 @@ class ActivityStreamMiddleware(threading.local, MiddlewareMixin):
super().__init__(get_response) super().__init__(get_response)
def process_request(self, request): def process_request(self, request):
if hasattr(request, 'user') and hasattr(request.user, 'is_authenticated') and request.user.is_authenticated(): if hasattr(request, 'user') and request.user.is_authenticated:
user = request.user user = request.user
else: else:
user = None user = None

View File

@@ -44,7 +44,7 @@ class Migration(migrations.Migration):
('modified', models.DateTimeField(default=None, editable=False)), ('modified', models.DateTimeField(default=None, editable=False)),
('host_name', models.CharField(default='', max_length=1024, editable=False)), ('host_name', models.CharField(default='', max_length=1024, editable=False)),
('event', models.CharField(max_length=100, choices=[('runner_on_failed', 'Host Failed'), ('runner_on_ok', 'Host OK'), ('runner_on_unreachable', 'Host Unreachable'), ('runner_on_skipped', 'Host Skipped')])), ('event', models.CharField(max_length=100, choices=[('runner_on_failed', 'Host Failed'), ('runner_on_ok', 'Host OK'), ('runner_on_unreachable', 'Host Unreachable'), ('runner_on_skipped', 'Host Skipped')])),
('event_data', jsonfield.fields.JSONField(default={}, blank=True)), ('event_data', jsonfield.fields.JSONField(default=dict, blank=True)),
('failed', models.BooleanField(default=False, editable=False)), ('failed', models.BooleanField(default=False, editable=False)),
('changed', models.BooleanField(default=False, editable=False)), ('changed', models.BooleanField(default=False, editable=False)),
('counter', models.PositiveIntegerField(default=0)), ('counter', models.PositiveIntegerField(default=0)),
@@ -62,7 +62,7 @@ class Migration(migrations.Migration):
('expires', models.DateTimeField(default=django.utils.timezone.now)), ('expires', models.DateTimeField(default=django.utils.timezone.now)),
('request_hash', models.CharField(default='', max_length=40, blank=True)), ('request_hash', models.CharField(default='', max_length=40, blank=True)),
('reason', models.CharField(default='', help_text='Reason the auth token was invalidated.', max_length=1024, blank=True)), ('reason', models.CharField(default='', help_text='Reason the auth token was invalidated.', max_length=1024, blank=True)),
('user', models.ForeignKey(related_name='auth_tokens', to=settings.AUTH_USER_MODEL)), ('user', models.ForeignKey(related_name='auth_tokens', on_delete=models.CASCADE, to=settings.AUTH_USER_MODEL)),
], ],
), ),
migrations.CreateModel( migrations.CreateModel(
@@ -198,7 +198,7 @@ class Migration(migrations.Migration):
('created', models.DateTimeField(default=None, editable=False)), ('created', models.DateTimeField(default=None, editable=False)),
('modified', models.DateTimeField(default=None, editable=False)), ('modified', models.DateTimeField(default=None, editable=False)),
('event', models.CharField(max_length=100, choices=[('runner_on_failed', 'Host Failed'), ('runner_on_ok', 'Host OK'), ('runner_on_error', 'Host Failure'), ('runner_on_skipped', 'Host Skipped'), ('runner_on_unreachable', 'Host Unreachable'), ('runner_on_no_hosts', 'No Hosts Remaining'), ('runner_on_async_poll', 'Host Polling'), ('runner_on_async_ok', 'Host Async OK'), ('runner_on_async_failed', 'Host Async Failure'), ('runner_on_file_diff', 'File Difference'), ('playbook_on_start', 'Playbook Started'), ('playbook_on_notify', 'Running Handlers'), ('playbook_on_no_hosts_matched', 'No Hosts Matched'), ('playbook_on_no_hosts_remaining', 'No Hosts Remaining'), ('playbook_on_task_start', 'Task Started'), ('playbook_on_vars_prompt', 'Variables Prompted'), ('playbook_on_setup', 'Gathering Facts'), ('playbook_on_import_for_host', 'internal: on Import for Host'), ('playbook_on_not_import_for_host', 'internal: on Not Import for Host'), ('playbook_on_play_start', 'Play Started'), ('playbook_on_stats', 'Playbook Complete')])), ('event', models.CharField(max_length=100, choices=[('runner_on_failed', 'Host Failed'), ('runner_on_ok', 'Host OK'), ('runner_on_error', 'Host Failure'), ('runner_on_skipped', 'Host Skipped'), ('runner_on_unreachable', 'Host Unreachable'), ('runner_on_no_hosts', 'No Hosts Remaining'), ('runner_on_async_poll', 'Host Polling'), ('runner_on_async_ok', 'Host Async OK'), ('runner_on_async_failed', 'Host Async Failure'), ('runner_on_file_diff', 'File Difference'), ('playbook_on_start', 'Playbook Started'), ('playbook_on_notify', 'Running Handlers'), ('playbook_on_no_hosts_matched', 'No Hosts Matched'), ('playbook_on_no_hosts_remaining', 'No Hosts Remaining'), ('playbook_on_task_start', 'Task Started'), ('playbook_on_vars_prompt', 'Variables Prompted'), ('playbook_on_setup', 'Gathering Facts'), ('playbook_on_import_for_host', 'internal: on Import for Host'), ('playbook_on_not_import_for_host', 'internal: on Not Import for Host'), ('playbook_on_play_start', 'Play Started'), ('playbook_on_stats', 'Playbook Complete')])),
('event_data', jsonfield.fields.JSONField(default={}, blank=True)), ('event_data', jsonfield.fields.JSONField(default=dict, blank=True)),
('failed', models.BooleanField(default=False, editable=False)), ('failed', models.BooleanField(default=False, editable=False)),
('changed', models.BooleanField(default=False, editable=False)), ('changed', models.BooleanField(default=False, editable=False)),
('host_name', models.CharField(default='', max_length=1024, editable=False)), ('host_name', models.CharField(default='', max_length=1024, editable=False)),
@@ -241,7 +241,7 @@ class Migration(migrations.Migration):
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('created', models.DateTimeField(auto_now_add=True)), ('created', models.DateTimeField(auto_now_add=True)),
('modified', models.DateTimeField(auto_now=True)), ('modified', models.DateTimeField(auto_now=True)),
('instance', models.ForeignKey(to='main.Instance')), ('instance', models.ForeignKey(on_delete=models.CASCADE, to='main.Instance')),
], ],
), ),
migrations.CreateModel( migrations.CreateModel(
@@ -287,7 +287,7 @@ class Migration(migrations.Migration):
('created', models.DateTimeField(default=None, editable=False)), ('created', models.DateTimeField(default=None, editable=False)),
('modified', models.DateTimeField(default=None, editable=False)), ('modified', models.DateTimeField(default=None, editable=False)),
('ldap_dn', models.CharField(default='', max_length=1024)), ('ldap_dn', models.CharField(default='', max_length=1024)),
('user', awx.main.fields.AutoOneToOneField(related_name='profile', editable=False, to=settings.AUTH_USER_MODEL)), ('user', awx.main.fields.AutoOneToOneField(related_name='profile', editable=False, on_delete=models.CASCADE, to=settings.AUTH_USER_MODEL)),
], ],
), ),
migrations.CreateModel( migrations.CreateModel(
@@ -304,7 +304,7 @@ class Migration(migrations.Migration):
('dtend', models.DateTimeField(default=None, null=True, editable=False)), ('dtend', models.DateTimeField(default=None, null=True, editable=False)),
('rrule', models.CharField(max_length=255)), ('rrule', models.CharField(max_length=255)),
('next_run', models.DateTimeField(default=None, null=True, editable=False)), ('next_run', models.DateTimeField(default=None, null=True, editable=False)),
('extra_data', jsonfield.fields.JSONField(default={}, blank=True)), ('extra_data', jsonfield.fields.JSONField(default=dict, blank=True)),
('created_by', models.ForeignKey(related_name="{u'class': 'schedule', u'app_label': 'main'}(class)s_created+", on_delete=django.db.models.deletion.SET_NULL, default=None, editable=False, to=settings.AUTH_USER_MODEL, null=True)), ('created_by', models.ForeignKey(related_name="{u'class': 'schedule', u'app_label': 'main'}(class)s_created+", on_delete=django.db.models.deletion.SET_NULL, default=None, editable=False, to=settings.AUTH_USER_MODEL, null=True)),
('modified_by', models.ForeignKey(related_name="{u'class': 'schedule', u'app_label': 'main'}(class)s_modified+", on_delete=django.db.models.deletion.SET_NULL, default=None, editable=False, to=settings.AUTH_USER_MODEL, null=True)), ('modified_by', models.ForeignKey(related_name="{u'class': 'schedule', u'app_label': 'main'}(class)s_modified+", on_delete=django.db.models.deletion.SET_NULL, default=None, editable=False, to=settings.AUTH_USER_MODEL, null=True)),
('tags', taggit.managers.TaggableManager(to='taggit.Tag', through='taggit.TaggedItem', blank=True, help_text='A comma-separated list of tags.', verbose_name='Tags')), ('tags', taggit.managers.TaggableManager(to='taggit.Tag', through='taggit.TaggedItem', blank=True, help_text='A comma-separated list of tags.', verbose_name='Tags')),
@@ -343,7 +343,7 @@ class Migration(migrations.Migration):
('name', models.CharField(max_length=512)), ('name', models.CharField(max_length=512)),
('old_pk', models.PositiveIntegerField(default=None, null=True, editable=False)), ('old_pk', models.PositiveIntegerField(default=None, null=True, editable=False)),
('launch_type', models.CharField(default='manual', max_length=20, editable=False, choices=[('manual', 'Manual'), ('relaunch', 'Relaunch'), ('callback', 'Callback'), ('scheduled', 'Scheduled'), ('dependency', 'Dependency')])), ('launch_type', models.CharField(default='manual', max_length=20, editable=False, choices=[('manual', 'Manual'), ('relaunch', 'Relaunch'), ('callback', 'Callback'), ('scheduled', 'Scheduled'), ('dependency', 'Dependency')])),
('cancel_flag', models.BooleanField(default=False, editable=False)), ('cancel_flag', models.BooleanField(blank=True, default=False, editable=False)),
('status', models.CharField(default='new', max_length=20, editable=False, choices=[('new', 'New'), ('pending', 'Pending'), ('waiting', 'Waiting'), ('running', 'Running'), ('successful', 'Successful'), ('failed', 'Failed'), ('error', 'Error'), ('canceled', 'Canceled')])), ('status', models.CharField(default='new', max_length=20, editable=False, choices=[('new', 'New'), ('pending', 'Pending'), ('waiting', 'Waiting'), ('running', 'Running'), ('successful', 'Successful'), ('failed', 'Failed'), ('error', 'Error'), ('canceled', 'Canceled')])),
('failed', models.BooleanField(default=False, editable=False)), ('failed', models.BooleanField(default=False, editable=False)),
('started', models.DateTimeField(default=None, null=True, editable=False)), ('started', models.DateTimeField(default=None, null=True, editable=False)),
@@ -351,7 +351,7 @@ class Migration(migrations.Migration):
('elapsed', models.DecimalField(editable=False, max_digits=12, decimal_places=3)), ('elapsed', models.DecimalField(editable=False, max_digits=12, decimal_places=3)),
('job_args', models.TextField(default='', editable=False, blank=True)), ('job_args', models.TextField(default='', editable=False, blank=True)),
('job_cwd', models.CharField(default='', max_length=1024, editable=False, blank=True)), ('job_cwd', models.CharField(default='', max_length=1024, editable=False, blank=True)),
('job_env', jsonfield.fields.JSONField(default={}, editable=False, blank=True)), ('job_env', jsonfield.fields.JSONField(default=dict, editable=False, blank=True)),
('job_explanation', models.TextField(default='', editable=False, blank=True)), ('job_explanation', models.TextField(default='', editable=False, blank=True)),
('start_args', models.TextField(default='', editable=False, blank=True)), ('start_args', models.TextField(default='', editable=False, blank=True)),
('result_stdout_text', models.TextField(default='', editable=False, blank=True)), ('result_stdout_text', models.TextField(default='', editable=False, blank=True)),
@@ -380,7 +380,7 @@ class Migration(migrations.Migration):
migrations.CreateModel( migrations.CreateModel(
name='AdHocCommand', name='AdHocCommand',
fields=[ fields=[
('unifiedjob_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='main.UnifiedJob')), ('unifiedjob_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, on_delete=django.db.models.deletion.CASCADE, serialize=False, to='main.UnifiedJob')),
('job_type', models.CharField(default='run', max_length=64, choices=[('run', 'Run'), ('check', 'Check')])), ('job_type', models.CharField(default='run', max_length=64, choices=[('run', 'Run'), ('check', 'Check')])),
('limit', models.CharField(default='', max_length=1024, blank=True)), ('limit', models.CharField(default='', max_length=1024, blank=True)),
('module_name', models.CharField(default='', max_length=1024, blank=True)), ('module_name', models.CharField(default='', max_length=1024, blank=True)),
@@ -394,7 +394,7 @@ class Migration(migrations.Migration):
migrations.CreateModel( migrations.CreateModel(
name='InventorySource', name='InventorySource',
fields=[ fields=[
('unifiedjobtemplate_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='main.UnifiedJobTemplate')), ('unifiedjobtemplate_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, on_delete=django.db.models.deletion.CASCADE, serialize=False, to='main.UnifiedJobTemplate')),
('source', models.CharField(default='', max_length=32, blank=True, choices=[('', 'Manual'), ('file', 'Local File, Directory or Script'), ('rax', 'Rackspace Cloud Servers'), ('ec2', 'Amazon EC2'), ('gce', 'Google Compute Engine'), ('azure', 'Microsoft Azure'), ('vmware', 'VMware vCenter'), ('openstack', 'OpenStack'), ('custom', 'Custom Script')])), ('source', models.CharField(default='', max_length=32, blank=True, choices=[('', 'Manual'), ('file', 'Local File, Directory or Script'), ('rax', 'Rackspace Cloud Servers'), ('ec2', 'Amazon EC2'), ('gce', 'Google Compute Engine'), ('azure', 'Microsoft Azure'), ('vmware', 'VMware vCenter'), ('openstack', 'OpenStack'), ('custom', 'Custom Script')])),
('source_path', models.CharField(default='', max_length=1024, editable=False, blank=True)), ('source_path', models.CharField(default='', max_length=1024, editable=False, blank=True)),
('source_vars', models.TextField(default='', help_text='Inventory source variables in YAML or JSON format.', blank=True)), ('source_vars', models.TextField(default='', help_text='Inventory source variables in YAML or JSON format.', blank=True)),
@@ -411,7 +411,7 @@ class Migration(migrations.Migration):
migrations.CreateModel( migrations.CreateModel(
name='InventoryUpdate', name='InventoryUpdate',
fields=[ fields=[
('unifiedjob_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='main.UnifiedJob')), ('unifiedjob_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, on_delete=django.db.models.deletion.CASCADE, serialize=False, to='main.UnifiedJob')),
('source', models.CharField(default='', max_length=32, blank=True, choices=[('', 'Manual'), ('file', 'Local File, Directory or Script'), ('rax', 'Rackspace Cloud Servers'), ('ec2', 'Amazon EC2'), ('gce', 'Google Compute Engine'), ('azure', 'Microsoft Azure'), ('vmware', 'VMware vCenter'), ('openstack', 'OpenStack'), ('custom', 'Custom Script')])), ('source', models.CharField(default='', max_length=32, blank=True, choices=[('', 'Manual'), ('file', 'Local File, Directory or Script'), ('rax', 'Rackspace Cloud Servers'), ('ec2', 'Amazon EC2'), ('gce', 'Google Compute Engine'), ('azure', 'Microsoft Azure'), ('vmware', 'VMware vCenter'), ('openstack', 'OpenStack'), ('custom', 'Custom Script')])),
('source_path', models.CharField(default='', max_length=1024, editable=False, blank=True)), ('source_path', models.CharField(default='', max_length=1024, editable=False, blank=True)),
('source_vars', models.TextField(default='', help_text='Inventory source variables in YAML or JSON format.', blank=True)), ('source_vars', models.TextField(default='', help_text='Inventory source variables in YAML or JSON format.', blank=True)),
@@ -427,7 +427,7 @@ class Migration(migrations.Migration):
migrations.CreateModel( migrations.CreateModel(
name='Job', name='Job',
fields=[ fields=[
('unifiedjob_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='main.UnifiedJob')), ('unifiedjob_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, on_delete=django.db.models.deletion.CASCADE, serialize=False, to='main.UnifiedJob')),
('job_type', models.CharField(default='run', max_length=64, choices=[('run', 'Run'), ('check', 'Check'), ('scan', 'Scan')])), ('job_type', models.CharField(default='run', max_length=64, choices=[('run', 'Run'), ('check', 'Check'), ('scan', 'Scan')])),
('playbook', models.CharField(default='', max_length=1024, blank=True)), ('playbook', models.CharField(default='', max_length=1024, blank=True)),
('forks', models.PositiveIntegerField(default=0, blank=True)), ('forks', models.PositiveIntegerField(default=0, blank=True)),
@@ -435,7 +435,7 @@ class Migration(migrations.Migration):
('verbosity', models.PositiveIntegerField(default=0, blank=True, choices=[(0, '0 (Normal)'), (1, '1 (Verbose)'), (2, '2 (More Verbose)'), (3, '3 (Debug)'), (4, '4 (Connection Debug)'), (5, '5 (WinRM Debug)')])), ('verbosity', models.PositiveIntegerField(default=0, blank=True, choices=[(0, '0 (Normal)'), (1, '1 (Verbose)'), (2, '2 (More Verbose)'), (3, '3 (Debug)'), (4, '4 (Connection Debug)'), (5, '5 (WinRM Debug)')])),
('extra_vars', models.TextField(default='', blank=True)), ('extra_vars', models.TextField(default='', blank=True)),
('job_tags', models.CharField(default='', max_length=1024, blank=True)), ('job_tags', models.CharField(default='', max_length=1024, blank=True)),
('force_handlers', models.BooleanField(default=False)), ('force_handlers', models.BooleanField(blank=True, default=False)),
('skip_tags', models.CharField(default='', max_length=1024, blank=True)), ('skip_tags', models.CharField(default='', max_length=1024, blank=True)),
('start_at_task', models.CharField(default='', max_length=1024, blank=True)), ('start_at_task', models.CharField(default='', max_length=1024, blank=True)),
('become_enabled', models.BooleanField(default=False)), ('become_enabled', models.BooleanField(default=False)),
@@ -448,7 +448,7 @@ class Migration(migrations.Migration):
migrations.CreateModel( migrations.CreateModel(
name='JobTemplate', name='JobTemplate',
fields=[ fields=[
('unifiedjobtemplate_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='main.UnifiedJobTemplate')), ('unifiedjobtemplate_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, on_delete=django.db.models.deletion.CASCADE, serialize=False, to='main.UnifiedJobTemplate')),
('job_type', models.CharField(default='run', max_length=64, choices=[('run', 'Run'), ('check', 'Check'), ('scan', 'Scan')])), ('job_type', models.CharField(default='run', max_length=64, choices=[('run', 'Run'), ('check', 'Check'), ('scan', 'Scan')])),
('playbook', models.CharField(default='', max_length=1024, blank=True)), ('playbook', models.CharField(default='', max_length=1024, blank=True)),
('forks', models.PositiveIntegerField(default=0, blank=True)), ('forks', models.PositiveIntegerField(default=0, blank=True)),
@@ -456,14 +456,14 @@ class Migration(migrations.Migration):
('verbosity', models.PositiveIntegerField(default=0, blank=True, choices=[(0, '0 (Normal)'), (1, '1 (Verbose)'), (2, '2 (More Verbose)'), (3, '3 (Debug)'), (4, '4 (Connection Debug)'), (5, '5 (WinRM Debug)')])), ('verbosity', models.PositiveIntegerField(default=0, blank=True, choices=[(0, '0 (Normal)'), (1, '1 (Verbose)'), (2, '2 (More Verbose)'), (3, '3 (Debug)'), (4, '4 (Connection Debug)'), (5, '5 (WinRM Debug)')])),
('extra_vars', models.TextField(default='', blank=True)), ('extra_vars', models.TextField(default='', blank=True)),
('job_tags', models.CharField(default='', max_length=1024, blank=True)), ('job_tags', models.CharField(default='', max_length=1024, blank=True)),
('force_handlers', models.BooleanField(default=False)), ('force_handlers', models.BooleanField(blank=True, default=False)),
('skip_tags', models.CharField(default='', max_length=1024, blank=True)), ('skip_tags', models.CharField(default='', max_length=1024, blank=True)),
('start_at_task', models.CharField(default='', max_length=1024, blank=True)), ('start_at_task', models.CharField(default='', max_length=1024, blank=True)),
('become_enabled', models.BooleanField(default=False)), ('become_enabled', models.BooleanField(default=False)),
('host_config_key', models.CharField(default='', max_length=1024, blank=True)), ('host_config_key', models.CharField(default='', max_length=1024, blank=True)),
('ask_variables_on_launch', models.BooleanField(default=False)), ('ask_variables_on_launch', models.BooleanField(default=False)),
('survey_enabled', models.BooleanField(default=False)), ('survey_enabled', models.BooleanField(default=False)),
('survey_spec', jsonfield.fields.JSONField(default={}, blank=True)), ('survey_spec', jsonfield.fields.JSONField(default=dict, blank=True)),
], ],
options={ options={
'ordering': ('name',), 'ordering': ('name',),
@@ -473,7 +473,7 @@ class Migration(migrations.Migration):
migrations.CreateModel( migrations.CreateModel(
name='Project', name='Project',
fields=[ fields=[
('unifiedjobtemplate_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='main.UnifiedJobTemplate')), ('unifiedjobtemplate_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, on_delete=django.db.models.deletion.CASCADE, serialize=False, to='main.UnifiedJobTemplate')),
('local_path', models.CharField(help_text='Local path (relative to PROJECTS_ROOT) containing playbooks and related files for this project.', max_length=1024, blank=True)), ('local_path', models.CharField(help_text='Local path (relative to PROJECTS_ROOT) containing playbooks and related files for this project.', max_length=1024, blank=True)),
('scm_type', models.CharField(default='', max_length=8, verbose_name='SCM Type', blank=True, choices=[('', 'Manual'), ('git', 'Git'), ('hg', 'Mercurial'), ('svn', 'Subversion')])), ('scm_type', models.CharField(default='', max_length=8, verbose_name='SCM Type', blank=True, choices=[('', 'Manual'), ('git', 'Git'), ('hg', 'Mercurial'), ('svn', 'Subversion')])),
('scm_url', models.CharField(default='', max_length=1024, verbose_name='SCM URL', blank=True)), ('scm_url', models.CharField(default='', max_length=1024, verbose_name='SCM URL', blank=True)),
@@ -492,7 +492,7 @@ class Migration(migrations.Migration):
migrations.CreateModel( migrations.CreateModel(
name='ProjectUpdate', name='ProjectUpdate',
fields=[ fields=[
('unifiedjob_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='main.UnifiedJob')), ('unifiedjob_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, on_delete=django.db.models.deletion.CASCADE, serialize=False, to='main.UnifiedJob')),
('local_path', models.CharField(help_text='Local path (relative to PROJECTS_ROOT) containing playbooks and related files for this project.', max_length=1024, blank=True)), ('local_path', models.CharField(help_text='Local path (relative to PROJECTS_ROOT) containing playbooks and related files for this project.', max_length=1024, blank=True)),
('scm_type', models.CharField(default='', max_length=8, verbose_name='SCM Type', blank=True, choices=[('', 'Manual'), ('git', 'Git'), ('hg', 'Mercurial'), ('svn', 'Subversion')])), ('scm_type', models.CharField(default='', max_length=8, verbose_name='SCM Type', blank=True, choices=[('', 'Manual'), ('git', 'Git'), ('hg', 'Mercurial'), ('svn', 'Subversion')])),
('scm_url', models.CharField(default='', max_length=1024, verbose_name='SCM URL', blank=True)), ('scm_url', models.CharField(default='', max_length=1024, verbose_name='SCM URL', blank=True)),
@@ -505,7 +505,7 @@ class Migration(migrations.Migration):
migrations.CreateModel( migrations.CreateModel(
name='SystemJob', name='SystemJob',
fields=[ fields=[
('unifiedjob_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='main.UnifiedJob')), ('unifiedjob_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, on_delete=django.db.models.deletion.CASCADE, serialize=False, to='main.UnifiedJob')),
('job_type', models.CharField(default='', max_length=32, blank=True, choices=[('cleanup_jobs', 'Remove jobs older than a certain number of days'), ('cleanup_activitystream', 'Remove activity stream entries older than a certain number of days'), ('cleanup_deleted', 'Purge previously deleted items from the database'), ('cleanup_facts', 'Purge and/or reduce the granularity of system tracking data')])), ('job_type', models.CharField(default='', max_length=32, blank=True, choices=[('cleanup_jobs', 'Remove jobs older than a certain number of days'), ('cleanup_activitystream', 'Remove activity stream entries older than a certain number of days'), ('cleanup_deleted', 'Purge previously deleted items from the database'), ('cleanup_facts', 'Purge and/or reduce the granularity of system tracking data')])),
('extra_vars', models.TextField(default='', blank=True)), ('extra_vars', models.TextField(default='', blank=True)),
], ],
@@ -517,7 +517,7 @@ class Migration(migrations.Migration):
migrations.CreateModel( migrations.CreateModel(
name='SystemJobTemplate', name='SystemJobTemplate',
fields=[ fields=[
('unifiedjobtemplate_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='main.UnifiedJobTemplate')), ('unifiedjobtemplate_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, on_delete=django.db.models.deletion.CASCADE, serialize=False, to='main.UnifiedJobTemplate')),
('job_type', models.CharField(default='', max_length=32, blank=True, choices=[('cleanup_jobs', 'Remove jobs older than a certain number of days'), ('cleanup_activitystream', 'Remove activity stream entries older than a certain number of days'), ('cleanup_deleted', 'Purge previously deleted items from the database'), ('cleanup_facts', 'Purge and/or reduce the granularity of system tracking data')])), ('job_type', models.CharField(default='', max_length=32, blank=True, choices=[('cleanup_jobs', 'Remove jobs older than a certain number of days'), ('cleanup_activitystream', 'Remove activity stream entries older than a certain number of days'), ('cleanup_deleted', 'Purge previously deleted items from the database'), ('cleanup_facts', 'Purge and/or reduce the granularity of system tracking data')])),
], ],
bases=('main.unifiedjobtemplate', models.Model), bases=('main.unifiedjobtemplate', models.Model),
@@ -550,7 +550,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='unifiedjobtemplate', model_name='unifiedjobtemplate',
name='polymorphic_ctype', name='polymorphic_ctype',
field=models.ForeignKey(related_name='polymorphic_main.unifiedjobtemplate_set+', editable=False, to='contenttypes.ContentType', null=True), field=models.ForeignKey(related_name='polymorphic_main.unifiedjobtemplate_set+', editable=False, on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType', null=True),
), ),
migrations.AddField( migrations.AddField(
model_name='unifiedjobtemplate', model_name='unifiedjobtemplate',
@@ -575,7 +575,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='unifiedjob', model_name='unifiedjob',
name='polymorphic_ctype', name='polymorphic_ctype',
field=models.ForeignKey(related_name='polymorphic_main.unifiedjob_set+', editable=False, to='contenttypes.ContentType', null=True), field=models.ForeignKey(related_name='polymorphic_main.unifiedjob_set+', editable=False, on_delete=django.db.models.deletion.CASCADE, to='contenttypes.ContentType', null=True),
), ),
migrations.AddField( migrations.AddField(
model_name='unifiedjob', model_name='unifiedjob',
@@ -595,7 +595,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='schedule', model_name='schedule',
name='unified_job_template', name='unified_job_template',
field=models.ForeignKey(related_name='schedules', to='main.UnifiedJobTemplate'), field=models.ForeignKey(related_name='schedules', on_delete=django.db.models.deletion.CASCADE, to='main.UnifiedJobTemplate'),
), ),
migrations.AddField( migrations.AddField(
model_name='permission', model_name='permission',
@@ -610,12 +610,12 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='joborigin', model_name='joborigin',
name='unified_job', name='unified_job',
field=models.OneToOneField(related_name='job_origin', to='main.UnifiedJob'), field=models.OneToOneField(related_name='job_origin', on_delete=django.db.models.deletion.CASCADE, to='main.UnifiedJob'),
), ),
migrations.AddField( migrations.AddField(
model_name='inventory', model_name='inventory',
name='organization', name='organization',
field=models.ForeignKey(related_name='inventories', to='main.Organization', help_text='Organization containing this inventory.'), field=models.ForeignKey(related_name='inventories', on_delete=django.db.models.deletion.CASCADE, to='main.Organization', help_text='Organization containing this inventory.'),
), ),
migrations.AddField( migrations.AddField(
model_name='inventory', model_name='inventory',
@@ -625,7 +625,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='host', model_name='host',
name='inventory', name='inventory',
field=models.ForeignKey(related_name='hosts', to='main.Inventory'), field=models.ForeignKey(related_name='hosts', on_delete=django.db.models.deletion.CASCADE, to='main.Inventory'),
), ),
migrations.AddField( migrations.AddField(
model_name='host', model_name='host',
@@ -650,7 +650,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='group', model_name='group',
name='inventory', name='inventory',
field=models.ForeignKey(related_name='groups', to='main.Inventory'), field=models.ForeignKey(related_name='groups', on_delete=django.db.models.deletion.CASCADE, to='main.Inventory'),
), ),
migrations.AddField( migrations.AddField(
model_name='group', model_name='group',
@@ -680,12 +680,12 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='credential', model_name='credential',
name='team', name='team',
field=models.ForeignKey(related_name='credentials', default=None, blank=True, to='main.Team', null=True), field=models.ForeignKey(related_name='credentials', on_delete=django.db.models.deletion.SET_NULL, default=None, blank=True, to='main.Team', null=True),
), ),
migrations.AddField( migrations.AddField(
model_name='credential', model_name='credential',
name='user', name='user',
field=models.ForeignKey(related_name='credentials', default=None, blank=True, to=settings.AUTH_USER_MODEL, null=True), field=models.ForeignKey(related_name='credentials', on_delete=django.db.models.deletion.SET_NULL, default=None, blank=True, to=settings.AUTH_USER_MODEL, null=True),
), ),
migrations.AddField( migrations.AddField(
model_name='adhoccommandevent', model_name='adhoccommandevent',
@@ -774,7 +774,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='projectupdate', model_name='projectupdate',
name='project', name='project',
field=models.ForeignKey(related_name='project_updates', editable=False, to='main.Project'), field=models.ForeignKey(related_name='project_updates', on_delete=django.db.models.deletion.CASCADE, editable=False, to='main.Project'),
), ),
migrations.AddField( migrations.AddField(
model_name='project', model_name='project',
@@ -814,12 +814,12 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='jobhostsummary', model_name='jobhostsummary',
name='job', name='job',
field=models.ForeignKey(related_name='job_host_summaries', editable=False, to='main.Job'), field=models.ForeignKey(related_name='job_host_summaries', on_delete=django.db.models.deletion.CASCADE, editable=False, to='main.Job'),
), ),
migrations.AddField( migrations.AddField(
model_name='jobevent', model_name='jobevent',
name='job', name='job',
field=models.ForeignKey(related_name='job_events', editable=False, to='main.Job'), field=models.ForeignKey(related_name='job_events', on_delete=django.db.models.deletion.CASCADE, editable=False, to='main.Job'),
), ),
migrations.AddField( migrations.AddField(
model_name='job', model_name='job',
@@ -859,7 +859,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='inventoryupdate', model_name='inventoryupdate',
name='inventory_source', name='inventory_source',
field=models.ForeignKey(related_name='inventory_updates', editable=False, to='main.InventorySource'), field=models.ForeignKey(related_name='inventory_updates', on_delete=django.db.models.deletion.CASCADE, editable=False, to='main.InventorySource'),
), ),
migrations.AddField( migrations.AddField(
model_name='inventoryupdate', model_name='inventoryupdate',
@@ -874,12 +874,12 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='inventorysource', model_name='inventorysource',
name='group', name='group',
field=awx.main.fields.AutoOneToOneField(related_name='inventory_source', null=True, default=None, editable=False, to='main.Group'), field=awx.main.fields.AutoOneToOneField(related_name='inventory_source', on_delete=django.db.models.deletion.SET_NULL, null=True, default=None, editable=False, to='main.Group'),
), ),
migrations.AddField( migrations.AddField(
model_name='inventorysource', model_name='inventorysource',
name='inventory', name='inventory',
field=models.ForeignKey(related_name='inventory_sources', default=None, editable=False, to='main.Inventory', null=True), field=models.ForeignKey(related_name='inventory_sources', on_delete=django.db.models.deletion.SET_NULL, default=None, editable=False, to='main.Inventory', null=True),
), ),
migrations.AddField( migrations.AddField(
model_name='inventorysource', model_name='inventorysource',
@@ -916,7 +916,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='adhoccommandevent', model_name='adhoccommandevent',
name='ad_hoc_command', name='ad_hoc_command',
field=models.ForeignKey(related_name='ad_hoc_command_events', editable=False, to='main.AdHocCommand'), field=models.ForeignKey(related_name='ad_hoc_command_events', on_delete=django.db.models.deletion.CASCADE, editable=False, to='main.AdHocCommand'),
), ),
migrations.AddField( migrations.AddField(
model_name='adhoccommand', model_name='adhoccommand',

View File

@@ -13,7 +13,6 @@ from django.conf import settings
from django.utils.timezone import now from django.utils.timezone import now
import jsonfield.fields import jsonfield.fields
import jsonbfield.fields
import taggit.managers import taggit.managers
@@ -144,7 +143,7 @@ class Migration(migrations.Migration):
('category', models.CharField(max_length=128)), ('category', models.CharField(max_length=128)),
('value', models.TextField(blank=True)), ('value', models.TextField(blank=True)),
('value_type', models.CharField(max_length=12, choices=[('string', 'String'), ('int', 'Integer'), ('float', 'Decimal'), ('json', 'JSON'), ('bool', 'Boolean'), ('password', 'Password'), ('list', 'List')])), ('value_type', models.CharField(max_length=12, choices=[('string', 'String'), ('int', 'Integer'), ('float', 'Decimal'), ('json', 'JSON'), ('bool', 'Boolean'), ('password', 'Password'), ('list', 'List')])),
('user', models.ForeignKey(related_name='settings', default=None, editable=False, to=settings.AUTH_USER_MODEL, null=True)), ('user', models.ForeignKey(related_name='settings', default=None, editable=False, to=settings.AUTH_USER_MODEL, on_delete=models.SET_NULL, null=True)),
], ],
), ),
# Notification changes # Notification changes
@@ -185,7 +184,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='notification', model_name='notification',
name='notification_template', name='notification_template',
field=models.ForeignKey(related_name='notifications', editable=False, to='main.NotificationTemplate'), field=models.ForeignKey(related_name='notifications', editable=False, on_delete=models.CASCADE, to='main.NotificationTemplate'),
), ),
migrations.AddField( migrations.AddField(
model_name='activitystream', model_name='activitystream',
@@ -239,8 +238,8 @@ class Migration(migrations.Migration):
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('timestamp', models.DateTimeField(default=None, help_text='Date and time of the corresponding fact scan gathering time.', editable=False)), ('timestamp', models.DateTimeField(default=None, help_text='Date and time of the corresponding fact scan gathering time.', editable=False)),
('module', models.CharField(max_length=128)), ('module', models.CharField(max_length=128)),
('facts', jsonbfield.fields.JSONField(default={}, help_text='Arbitrary JSON structure of module facts captured at timestamp for a single host.', blank=True)), ('facts', awx.main.fields.JSONBField(default=dict, help_text='Arbitrary JSON structure of module facts captured at timestamp for a single host.', blank=True)),
('host', models.ForeignKey(related_name='facts', to='main.Host', help_text='Host for the facts that the fact scan captured.')), ('host', models.ForeignKey(related_name='facts', to='main.Host', on_delete=models.CASCADE, help_text='Host for the facts that the fact scan captured.')),
], ],
), ),
migrations.AlterIndexTogether( migrations.AlterIndexTogether(
@@ -318,7 +317,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='project', model_name='project',
name='organization', name='organization',
field=models.ForeignKey(related_name='projects', to='main.Organization', blank=True, null=True), field=models.ForeignKey(related_name='projects', to='main.Organization', on_delete=models.CASCADE, blank=True, null=True),
), ),
migrations.AlterField( migrations.AlterField(
model_name='team', model_name='team',
@@ -367,7 +366,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='credential', model_name='credential',
name='organization', name='organization',
field=models.ForeignKey(related_name='credentials', default=None, blank=True, to='main.Organization', null=True), field=models.ForeignKey(related_name='credentials', on_delete=models.CASCADE, default=None, blank=True, to='main.Organization', null=True),
), ),
# #
@@ -382,7 +381,7 @@ class Migration(migrations.Migration):
('members', models.ManyToManyField(related_name='roles', to=settings.AUTH_USER_MODEL)), ('members', models.ManyToManyField(related_name='roles', to=settings.AUTH_USER_MODEL)),
('parents', models.ManyToManyField(related_name='children', to='main.Role')), ('parents', models.ManyToManyField(related_name='children', to='main.Role')),
('implicit_parents', models.TextField(default='[]')), ('implicit_parents', models.TextField(default='[]')),
('content_type', models.ForeignKey(default=None, to='contenttypes.ContentType', null=True)), ('content_type', models.ForeignKey(default=None, to='contenttypes.ContentType', on_delete=models.CASCADE, null=True)),
('object_id', models.PositiveIntegerField(default=None, null=True)), ('object_id', models.PositiveIntegerField(default=None, null=True)),
], ],
@@ -398,8 +397,8 @@ class Migration(migrations.Migration):
('role_field', models.TextField()), ('role_field', models.TextField()),
('content_type_id', models.PositiveIntegerField()), ('content_type_id', models.PositiveIntegerField()),
('object_id', models.PositiveIntegerField()), ('object_id', models.PositiveIntegerField()),
('ancestor', models.ForeignKey(related_name='+', to='main.Role')), ('ancestor', models.ForeignKey(on_delete=models.CASCADE, related_name='+', to='main.Role')),
('descendent', models.ForeignKey(related_name='+', to='main.Role')), ('descendent', models.ForeignKey(on_delete=models.CASCADE, related_name='+', to='main.Role')),
], ],
options={ options={
'db_table': 'main_rbac_role_ancestors', 'db_table': 'main_rbac_role_ancestors',
@@ -569,7 +568,7 @@ class Migration(migrations.Migration):
('name', models.CharField(max_length=512)), ('name', models.CharField(max_length=512)),
('created_by', models.ForeignKey(related_name="{u'class': 'label', u'app_label': 'main'}(class)s_created+", on_delete=django.db.models.deletion.SET_NULL, default=None, editable=False, to=settings.AUTH_USER_MODEL, null=True)), ('created_by', models.ForeignKey(related_name="{u'class': 'label', u'app_label': 'main'}(class)s_created+", on_delete=django.db.models.deletion.SET_NULL, default=None, editable=False, to=settings.AUTH_USER_MODEL, null=True)),
('modified_by', models.ForeignKey(related_name="{u'class': 'label', u'app_label': 'main'}(class)s_modified+", on_delete=django.db.models.deletion.SET_NULL, default=None, editable=False, to=settings.AUTH_USER_MODEL, null=True)), ('modified_by', models.ForeignKey(related_name="{u'class': 'label', u'app_label': 'main'}(class)s_modified+", on_delete=django.db.models.deletion.SET_NULL, default=None, editable=False, to=settings.AUTH_USER_MODEL, null=True)),
('organization', models.ForeignKey(related_name='labels', to='main.Organization', help_text='Organization this label belongs to.')), ('organization', models.ForeignKey(related_name='labels', on_delete=django.db.models.deletion.CASCADE, to='main.Organization', help_text='Organization this label belongs to.')),
('tags', taggit.managers.TaggableManager(to='taggit.Tag', through='taggit.TaggedItem', blank=True, help_text='A comma-separated list of tags.', verbose_name='Tags')), ('tags', taggit.managers.TaggableManager(to='taggit.Tag', through='taggit.TaggedItem', blank=True, help_text='A comma-separated list of tags.', verbose_name='Tags')),
], ],
options={ options={
@@ -599,12 +598,12 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='label', model_name='label',
name='organization', name='organization',
field=models.ForeignKey(related_name='labels', on_delete=django.db.models.deletion.SET_NULL, default=None, blank=True, to='main.Organization', help_text='Organization this label belongs to.', null=True), field=models.ForeignKey(related_name='labels', on_delete=django.db.models.deletion.CASCADE, default=None, blank=True, to='main.Organization', help_text='Organization this label belongs to.', null=True),
), ),
migrations.AlterField( migrations.AlterField(
model_name='label', model_name='label',
name='organization', name='organization',
field=models.ForeignKey(related_name='labels', to='main.Organization', help_text='Organization this label belongs to.'), field=models.ForeignKey(related_name='labels', on_delete=django.db.models.deletion.CASCADE, to='main.Organization', help_text='Organization this label belongs to.'),
), ),
# InventorySource Credential # InventorySource Credential
migrations.AddField( migrations.AddField(
@@ -630,12 +629,12 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='credential', model_name='credential',
name='deprecated_team', name='deprecated_team',
field=models.ForeignKey(related_name='deprecated_credentials', default=None, blank=True, to='main.Team', null=True), field=models.ForeignKey(related_name='deprecated_credentials', on_delete=django.db.models.deletion.SET_NULL, default=None, blank=True, to='main.Team', null=True),
), ),
migrations.AlterField( migrations.AlterField(
model_name='credential', model_name='credential',
name='deprecated_user', name='deprecated_user',
field=models.ForeignKey(related_name='deprecated_credentials', default=None, blank=True, to=settings.AUTH_USER_MODEL, null=True), field=models.ForeignKey(related_name='deprecated_credentials', on_delete=django.db.models.deletion.SET_NULL, default=None, blank=True, to=settings.AUTH_USER_MODEL, null=True),
), ),
migrations.AlterField( migrations.AlterField(
model_name='credential', model_name='credential',

View File

@@ -116,7 +116,7 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='team', model_name='team',
name='organization', name='organization',
field=models.ForeignKey(related_name='teams', to='main.Organization'), field=models.ForeignKey(related_name='teams', on_delete=models.CASCADE, to='main.Organization'),
preserve_default=False, preserve_default=False,
), ),
] + _squashed.operations(SQUASHED_30, applied=True) ] + _squashed.operations(SQUASHED_30, applied=True)

View File

@@ -74,7 +74,7 @@ class Migration(migrations.Migration):
migrations.CreateModel( migrations.CreateModel(
name='WorkflowJob', name='WorkflowJob',
fields=[ fields=[
('unifiedjob_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='main.UnifiedJob')), ('unifiedjob_ptr', models.OneToOneField(parent_link=True, auto_created=True, on_delete=models.CASCADE, primary_key=True, serialize=False, to='main.UnifiedJob')),
('extra_vars', models.TextField(default='', blank=True)), ('extra_vars', models.TextField(default='', blank=True)),
], ],
options={ options={
@@ -100,7 +100,7 @@ class Migration(migrations.Migration):
migrations.CreateModel( migrations.CreateModel(
name='WorkflowJobTemplate', name='WorkflowJobTemplate',
fields=[ fields=[
('unifiedjobtemplate_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, serialize=False, to='main.UnifiedJobTemplate')), ('unifiedjobtemplate_ptr', models.OneToOneField(parent_link=True, auto_created=True, primary_key=True, on_delete=models.CASCADE, serialize=False, to='main.UnifiedJobTemplate')),
('extra_vars', models.TextField(default='', blank=True)), ('extra_vars', models.TextField(default='', blank=True)),
('admin_role', awx.main.fields.ImplicitRoleField(related_name='+', parent_role='singleton:system_administrator', to='main.Role', null='True')), ('admin_role', awx.main.fields.ImplicitRoleField(related_name='+', parent_role='singleton:system_administrator', to='main.Role', null='True')),
], ],
@@ -116,7 +116,7 @@ class Migration(migrations.Migration):
('failure_nodes', models.ManyToManyField(related_name='workflowjobtemplatenodes_failure', to='main.WorkflowJobTemplateNode', blank=True)), ('failure_nodes', models.ManyToManyField(related_name='workflowjobtemplatenodes_failure', to='main.WorkflowJobTemplateNode', blank=True)),
('success_nodes', models.ManyToManyField(related_name='workflowjobtemplatenodes_success', to='main.WorkflowJobTemplateNode', blank=True)), ('success_nodes', models.ManyToManyField(related_name='workflowjobtemplatenodes_success', to='main.WorkflowJobTemplateNode', blank=True)),
('unified_job_template', models.ForeignKey(related_name='workflowjobtemplatenodes', on_delete=django.db.models.deletion.SET_NULL, default=None, blank=True, to='main.UnifiedJobTemplate', null=True)), ('unified_job_template', models.ForeignKey(related_name='workflowjobtemplatenodes', on_delete=django.db.models.deletion.SET_NULL, default=None, blank=True, to='main.UnifiedJobTemplate', null=True)),
('workflow_job_template', models.ForeignKey(related_name='workflow_job_template_nodes', default=None, blank=True, to='main.WorkflowJobTemplate', null=True)), ('workflow_job_template', models.ForeignKey(related_name='workflow_job_template_nodes', on_delete=models.SET_NULL, default=None, blank=True, to='main.WorkflowJobTemplate', null=True)),
], ],
options={ options={
'abstract': False, 'abstract': False,
@@ -161,7 +161,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='workflowjobnode', model_name='workflowjobnode',
name='char_prompts', name='char_prompts',
field=jsonfield.fields.JSONField(default={}, blank=True), field=jsonfield.fields.JSONField(default=dict, blank=True),
), ),
migrations.AddField( migrations.AddField(
model_name='workflowjobnode', model_name='workflowjobnode',
@@ -191,7 +191,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='workflowjobtemplatenode', model_name='workflowjobtemplatenode',
name='char_prompts', name='char_prompts',
field=jsonfield.fields.JSONField(default={}, blank=True), field=jsonfield.fields.JSONField(default=dict, blank=True),
), ),
migrations.AddField( migrations.AddField(
model_name='workflowjobtemplatenode', model_name='workflowjobtemplatenode',
@@ -211,7 +211,7 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='workflowjobnode', model_name='workflowjobnode',
name='workflow_job', name='workflow_job',
field=models.ForeignKey(related_name='workflow_job_nodes', default=None, blank=True, to='main.WorkflowJob', null=True), field=models.ForeignKey(related_name='workflow_job_nodes', on_delete=django.db.models.deletion.CASCADE, default=None, blank=True, to='main.WorkflowJob', null=True),
), ),
migrations.AlterField( migrations.AlterField(
model_name='workflowjobtemplate', model_name='workflowjobtemplate',
@@ -227,12 +227,12 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='job', model_name='job',
name='artifacts', name='artifacts',
field=jsonfield.fields.JSONField(default={}, editable=False, blank=True), field=jsonfield.fields.JSONField(default=dict, editable=False, blank=True),
), ),
migrations.AddField( migrations.AddField(
model_name='workflowjobnode', model_name='workflowjobnode',
name='ancestor_artifacts', name='ancestor_artifacts',
field=jsonfield.fields.JSONField(default={}, editable=False, blank=True), field=jsonfield.fields.JSONField(default=dict, editable=False, blank=True),
), ),
# Job timeout settings # Job timeout settings
migrations.AddField( migrations.AddField(
@@ -397,7 +397,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='workflowjob', model_name='workflowjob',
name='survey_passwords', name='survey_passwords',
field=jsonfield.fields.JSONField(default={}, editable=False, blank=True), field=jsonfield.fields.JSONField(default=dict, editable=False, blank=True),
), ),
migrations.AddField( migrations.AddField(
model_name='workflowjobtemplate', model_name='workflowjobtemplate',
@@ -407,33 +407,33 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='workflowjobtemplate', model_name='workflowjobtemplate',
name='survey_spec', name='survey_spec',
field=jsonfield.fields.JSONField(default={}, blank=True), field=jsonfield.fields.JSONField(default=dict, blank=True),
), ),
# JSON field changes # JSON field changes
migrations.AlterField( migrations.AlterField(
model_name='adhoccommandevent', model_name='adhoccommandevent',
name='event_data', name='event_data',
field=awx.main.fields.JSONField(default={}, blank=True), field=awx.main.fields.JSONField(default=dict, blank=True),
), ),
migrations.AlterField( migrations.AlterField(
model_name='job', model_name='job',
name='artifacts', name='artifacts',
field=awx.main.fields.JSONField(default={}, editable=False, blank=True), field=awx.main.fields.JSONField(default=dict, editable=False, blank=True),
), ),
migrations.AlterField( migrations.AlterField(
model_name='job', model_name='job',
name='survey_passwords', name='survey_passwords',
field=awx.main.fields.JSONField(default={}, editable=False, blank=True), field=awx.main.fields.JSONField(default=dict, editable=False, blank=True),
), ),
migrations.AlterField( migrations.AlterField(
model_name='jobevent', model_name='jobevent',
name='event_data', name='event_data',
field=awx.main.fields.JSONField(default={}, blank=True), field=awx.main.fields.JSONField(default=dict, blank=True),
), ),
migrations.AlterField( migrations.AlterField(
model_name='jobtemplate', model_name='jobtemplate',
name='survey_spec', name='survey_spec',
field=awx.main.fields.JSONField(default={}, blank=True), field=awx.main.fields.JSONField(default=dict, blank=True),
), ),
migrations.AlterField( migrations.AlterField(
model_name='notification', model_name='notification',
@@ -453,37 +453,37 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='schedule', model_name='schedule',
name='extra_data', name='extra_data',
field=awx.main.fields.JSONField(default={}, blank=True), field=awx.main.fields.JSONField(default=dict, blank=True),
), ),
migrations.AlterField( migrations.AlterField(
model_name='unifiedjob', model_name='unifiedjob',
name='job_env', name='job_env',
field=awx.main.fields.JSONField(default={}, editable=False, blank=True), field=awx.main.fields.JSONField(default=dict, editable=False, blank=True),
), ),
migrations.AlterField( migrations.AlterField(
model_name='workflowjob', model_name='workflowjob',
name='survey_passwords', name='survey_passwords',
field=awx.main.fields.JSONField(default={}, editable=False, blank=True), field=awx.main.fields.JSONField(default=dict, editable=False, blank=True),
), ),
migrations.AlterField( migrations.AlterField(
model_name='workflowjobnode', model_name='workflowjobnode',
name='ancestor_artifacts', name='ancestor_artifacts',
field=awx.main.fields.JSONField(default={}, editable=False, blank=True), field=awx.main.fields.JSONField(default=dict, editable=False, blank=True),
), ),
migrations.AlterField( migrations.AlterField(
model_name='workflowjobnode', model_name='workflowjobnode',
name='char_prompts', name='char_prompts',
field=awx.main.fields.JSONField(default={}, blank=True), field=awx.main.fields.JSONField(default=dict, blank=True),
), ),
migrations.AlterField( migrations.AlterField(
model_name='workflowjobtemplate', model_name='workflowjobtemplate',
name='survey_spec', name='survey_spec',
field=awx.main.fields.JSONField(default={}, blank=True), field=awx.main.fields.JSONField(default=dict, blank=True),
), ),
migrations.AlterField( migrations.AlterField(
model_name='workflowjobtemplatenode', model_name='workflowjobtemplatenode',
name='char_prompts', name='char_prompts',
field=awx.main.fields.JSONField(default={}, blank=True), field=awx.main.fields.JSONField(default=dict, blank=True),
), ),
# Job Project Update # Job Project Update
migrations.AddField( migrations.AddField(

View File

@@ -55,12 +55,12 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='inventorysource', model_name='inventorysource',
name='deprecated_group', name='deprecated_group',
field=models.OneToOneField(related_name='deprecated_inventory_source', null=True, default=None, to='main.Group'), field=models.OneToOneField(related_name='deprecated_inventory_source', on_delete=models.CASCADE, null=True, default=None, to='main.Group'),
), ),
migrations.AlterField( migrations.AlterField(
model_name='inventorysource', model_name='inventorysource',
name='inventory', name='inventory',
field=models.ForeignKey(related_name='inventory_sources', default=None, to='main.Inventory', null=True), field=models.ForeignKey(related_name='inventory_sources', default=None, to='main.Inventory', on_delete=models.CASCADE, null=True),
), ),
# Smart Inventory # Smart Inventory
@@ -78,13 +78,13 @@ class Migration(migrations.Migration):
name='SmartInventoryMembership', name='SmartInventoryMembership',
fields=[ fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('host', models.ForeignKey(related_name='+', to='main.Host')), ('host', models.ForeignKey(related_name='+', on_delete=models.CASCADE, to='main.Host')),
], ],
), ),
migrations.AddField( migrations.AddField(
model_name='smartinventorymembership', model_name='smartinventorymembership',
name='inventory', name='inventory',
field=models.ForeignKey(related_name='+', to='main.Inventory'), field=models.ForeignKey(on_delete=models.CASCADE, related_name='+', to='main.Inventory'),
), ),
migrations.AddField( migrations.AddField(
model_name='host', model_name='host',
@@ -105,19 +105,19 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='inventory', model_name='inventory',
name='organization', name='organization',
field=models.ForeignKey(related_name='inventories', on_delete=models.deletion.SET_NULL, to='main.Organization', help_text='Organization containing this inventory.', null=True), field=models.ForeignKey(related_name='inventories', on_delete=models.SET_NULL, to='main.Organization', help_text='Organization containing this inventory.', null=True),
), ),
# Facts # Facts
migrations.AlterField( migrations.AlterField(
model_name='fact', model_name='fact',
name='facts', name='facts',
field=awx.main.fields.JSONBField(default={}, help_text='Arbitrary JSON structure of module facts captured at timestamp for a single host.', blank=True), field=awx.main.fields.JSONBField(default=dict, help_text='Arbitrary JSON structure of module facts captured at timestamp for a single host.', blank=True),
), ),
migrations.AddField( migrations.AddField(
model_name='host', model_name='host',
name='ansible_facts', name='ansible_facts',
field=awx.main.fields.JSONBField(default={}, help_text='Arbitrary JSON structure of most recent ansible_facts, per-host.', blank=True), field=awx.main.fields.JSONBField(default=dict, help_text='Arbitrary JSON structure of most recent ansible_facts, per-host.', blank=True),
), ),
migrations.AddField( migrations.AddField(
model_name='host', model_name='host',
@@ -148,12 +148,12 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='inventorysource', model_name='inventorysource',
name='source_project', name='source_project',
field=models.ForeignKey(related_name='scm_inventory_sources', default=None, blank=True, to='main.Project', help_text='Project containing inventory file used as source.', null=True), field=models.ForeignKey(related_name='scm_inventory_sources', on_delete=models.CASCADE, default=None, blank=True, to='main.Project', help_text='Project containing inventory file used as source.', null=True),
), ),
migrations.AddField( migrations.AddField(
model_name='inventoryupdate', model_name='inventoryupdate',
name='source_project_update', name='source_project_update',
field=models.ForeignKey(related_name='scm_inventory_updates', default=None, blank=True, to='main.ProjectUpdate', help_text='Inventory files from this Project Update were used for the inventory update.', null=True), field=models.ForeignKey(related_name='scm_inventory_updates', on_delete=models.CASCADE, default=None, blank=True, to='main.ProjectUpdate', help_text='Inventory files from this Project Update were used for the inventory update.', null=True),
), ),
migrations.AddField( migrations.AddField(
model_name='project', model_name='project',
@@ -200,7 +200,7 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='notificationtemplate', model_name='notificationtemplate',
name='organization', name='organization',
field=models.ForeignKey(related_name='notification_templates', to='main.Organization', null=True), field=models.ForeignKey(related_name='notification_templates', on_delete=models.CASCADE, to='main.Organization', null=True),
), ),
migrations.AlterUniqueTogether( migrations.AlterUniqueTogether(
name='notificationtemplate', name='notificationtemplate',
@@ -312,7 +312,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='inventory', model_name='inventory',
name='insights_credential', name='insights_credential',
field=models.ForeignKey(related_name='insights_inventories', on_delete=models.deletion.SET_NULL, default=None, blank=True, to='main.Credential', help_text='Credentials to be used by hosts belonging to this inventory when accessing Red Hat Insights API.', null=True), field=models.ForeignKey(related_name='insights_inventories', on_delete=models.SET_NULL, default=None, blank=True, to='main.Credential', help_text='Credentials to be used by hosts belonging to this inventory when accessing Red Hat Insights API.', null=True),
), ),
migrations.AlterField( migrations.AlterField(
model_name='inventory', model_name='inventory',
@@ -382,10 +382,10 @@ class Migration(migrations.Migration):
('name', models.CharField(max_length=512)), ('name', models.CharField(max_length=512)),
('kind', models.CharField(max_length=32, choices=[('ssh', 'Machine'), ('vault', 'Vault'), ('net', 'Network'), ('scm', 'Source Control'), ('cloud', 'Cloud'), ('insights', 'Insights')])), ('kind', models.CharField(max_length=32, choices=[('ssh', 'Machine'), ('vault', 'Vault'), ('net', 'Network'), ('scm', 'Source Control'), ('cloud', 'Cloud'), ('insights', 'Insights')])),
('managed_by_tower', models.BooleanField(default=False, editable=False)), ('managed_by_tower', models.BooleanField(default=False, editable=False)),
('inputs', awx.main.fields.CredentialTypeInputField(default={}, blank=True, help_text='Enter inputs using either JSON or YAML syntax. Use the radio button to toggle between the two. Refer to the Ansible Tower documentation for example syntax.')), ('inputs', awx.main.fields.CredentialTypeInputField(default=dict, blank=True, help_text='Enter inputs using either JSON or YAML syntax. Use the radio button to toggle between the two. Refer to the Ansible Tower documentation for example syntax.')),
('injectors', awx.main.fields.CredentialTypeInjectorField(default={}, blank=True, help_text='Enter injectors using either JSON or YAML syntax. Use the radio button to toggle between the two. Refer to the Ansible Tower documentation for example syntax.')), ('injectors', awx.main.fields.CredentialTypeInjectorField(default=dict, blank=True, help_text='Enter injectors using either JSON or YAML syntax. Use the radio button to toggle between the two. Refer to the Ansible Tower documentation for example syntax.')),
('created_by', models.ForeignKey(related_name="{u'class': 'credentialtype', u'app_label': 'main'}(class)s_created+", on_delete=models.deletion.SET_NULL, default=None, editable=False, to=settings.AUTH_USER_MODEL, null=True)), ('created_by', models.ForeignKey(related_name="{u'class': 'credentialtype', u'app_label': 'main'}(class)s_created+", on_delete=models.SET_NULL, default=None, editable=False, to=settings.AUTH_USER_MODEL, null=True)),
('modified_by', models.ForeignKey(related_name="{u'class': 'credentialtype', u'app_label': 'main'}(class)s_modified+", on_delete=models.deletion.SET_NULL, default=None, editable=False, to=settings.AUTH_USER_MODEL, null=True)), ('modified_by', models.ForeignKey(related_name="{u'class': 'credentialtype', u'app_label': 'main'}(class)s_modified+", on_delete=models.SET_NULL, default=None, editable=False, to=settings.AUTH_USER_MODEL, null=True)),
('tags', taggit.managers.TaggableManager(to='taggit.Tag', through='taggit.TaggedItem', blank=True, help_text='A comma-separated list of tags.', verbose_name='Tags')), ('tags', taggit.managers.TaggableManager(to='taggit.Tag', through='taggit.TaggedItem', blank=True, help_text='A comma-separated list of tags.', verbose_name='Tags')),
], ],
options={ options={
@@ -399,23 +399,23 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='credential', model_name='credential',
name='inputs', name='inputs',
field=awx.main.fields.CredentialInputField(default={}, blank=True), field=awx.main.fields.CredentialInputField(default=dict, blank=True),
), ),
migrations.AddField( migrations.AddField(
model_name='credential', model_name='credential',
name='credential_type', name='credential_type',
field=models.ForeignKey(related_name='credentials', to='main.CredentialType', null=True), field=models.ForeignKey(related_name='credentials', on_delete=models.CASCADE, to='main.CredentialType', null=True),
preserve_default=False, preserve_default=False,
), ),
migrations.AddField( migrations.AddField(
model_name='job', model_name='job',
name='vault_credential', name='vault_credential',
field=models.ForeignKey(related_name='jobs_as_vault_credential+', on_delete=models.deletion.SET_NULL, default=None, blank=True, to='main.Credential', null=True), field=models.ForeignKey(related_name='jobs_as_vault_credential+', on_delete=models.SET_NULL, default=None, blank=True, to='main.Credential', null=True),
), ),
migrations.AddField( migrations.AddField(
model_name='jobtemplate', model_name='jobtemplate',
name='vault_credential', name='vault_credential',
field=models.ForeignKey(related_name='jobtemplates_as_vault_credential+', on_delete=models.deletion.SET_NULL, default=None, blank=True, to='main.Credential', null=True), field=models.ForeignKey(related_name='jobtemplates_as_vault_credential+', on_delete=models.SET_NULL, default=None, blank=True, to='main.Credential', null=True),
), ),
migrations.AddField( migrations.AddField(
model_name='job', model_name='job',
@@ -452,7 +452,7 @@ class Migration(migrations.Migration):
('name', models.CharField(unique=True, max_length=250)), ('name', models.CharField(unique=True, max_length=250)),
('created', models.DateTimeField(auto_now_add=True)), ('created', models.DateTimeField(auto_now_add=True)),
('modified', models.DateTimeField(auto_now=True)), ('modified', models.DateTimeField(auto_now=True)),
('controller', models.ForeignKey(related_name='controlled_groups', default=None, editable=False, to='main.InstanceGroup', help_text='Instance Group to remotely control this group.', null=True)), ('controller', models.ForeignKey(related_name='controlled_groups', on_delete=models.CASCADE, default=None, editable=False, to='main.InstanceGroup', help_text='Instance Group to remotely control this group.', null=True)),
('instances', models.ManyToManyField(help_text='Instances that are members of this InstanceGroup', related_name='rampart_groups', editable=False, to='main.Instance')), ('instances', models.ManyToManyField(help_text='Instances that are members of this InstanceGroup', related_name='rampart_groups', editable=False, to='main.Instance')),
], ],
), ),
@@ -464,7 +464,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='unifiedjob', model_name='unifiedjob',
name='instance_group', name='instance_group',
field=models.ForeignKey(on_delete=models.deletion.SET_NULL, default=None, blank=True, to='main.InstanceGroup', help_text='The Rampart/Instance group the job was run under', null=True), field=models.ForeignKey(on_delete=models.SET_NULL, default=None, blank=True, to='main.InstanceGroup', help_text='The Rampart/Instance group the job was run under', null=True),
), ),
migrations.AddField( migrations.AddField(
model_name='unifiedjobtemplate', model_name='unifiedjobtemplate',

View File

@@ -103,12 +103,12 @@ class Migration(migrations.Migration):
migrations.AlterField( migrations.AlterField(
model_name='credential', model_name='credential',
name='credential_type', name='credential_type',
field=models.ForeignKey(related_name='credentials', to='main.CredentialType', null=False, help_text='Specify the type of credential you want to create. Refer to the Ansible Tower documentation for details on each type.') field=models.ForeignKey(related_name='credentials', to='main.CredentialType', on_delete=models.CASCADE, null=False, help_text='Specify the type of credential you want to create. Refer to the Ansible Tower documentation for details on each type.')
), ),
migrations.AlterField( migrations.AlterField(
model_name='credential', model_name='credential',
name='inputs', name='inputs',
field=awx.main.fields.CredentialInputField(default={}, help_text='Enter inputs using either JSON or YAML syntax. Use the radio button to toggle between the two. Refer to the Ansible Tower documentation for example syntax.', blank=True), field=awx.main.fields.CredentialInputField(default=dict, help_text='Enter inputs using either JSON or YAML syntax. Use the radio button to toggle between the two. Refer to the Ansible Tower documentation for example syntax.', blank=True),
), ),
migrations.RemoveField( migrations.RemoveField(
model_name='job', model_name='job',

View File

@@ -20,7 +20,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='schedule', model_name='schedule',
name='char_prompts', name='char_prompts',
field=awx.main.fields.JSONField(default={}, blank=True), field=awx.main.fields.JSONField(default=dict, blank=True),
), ),
migrations.AddField( migrations.AddField(
model_name='schedule', model_name='schedule',
@@ -35,7 +35,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='schedule', model_name='schedule',
name='survey_passwords', name='survey_passwords',
field=awx.main.fields.JSONField(default={}, editable=False, blank=True), field=awx.main.fields.JSONField(default=dict, editable=False, blank=True),
), ),
migrations.AddField( migrations.AddField(
model_name='workflowjobnode', model_name='workflowjobnode',
@@ -45,12 +45,12 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='workflowjobnode', model_name='workflowjobnode',
name='extra_data', name='extra_data',
field=awx.main.fields.JSONField(default={}, blank=True), field=awx.main.fields.JSONField(default=dict, blank=True),
), ),
migrations.AddField( migrations.AddField(
model_name='workflowjobnode', model_name='workflowjobnode',
name='survey_passwords', name='survey_passwords',
field=awx.main.fields.JSONField(default={}, editable=False, blank=True), field=awx.main.fields.JSONField(default=dict, editable=False, blank=True),
), ),
migrations.AddField( migrations.AddField(
model_name='workflowjobtemplatenode', model_name='workflowjobtemplatenode',
@@ -60,12 +60,12 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='workflowjobtemplatenode', model_name='workflowjobtemplatenode',
name='extra_data', name='extra_data',
field=awx.main.fields.JSONField(default={}, blank=True), field=awx.main.fields.JSONField(default=dict, blank=True),
), ),
migrations.AddField( migrations.AddField(
model_name='workflowjobtemplatenode', model_name='workflowjobtemplatenode',
name='survey_passwords', name='survey_passwords',
field=awx.main.fields.JSONField(default={}, editable=False, blank=True), field=awx.main.fields.JSONField(default=dict, editable=False, blank=True),
), ),
# Run data migration before removing the old credential field # Run data migration before removing the old credential field
migrations.RunPython(migration_utils.set_current_apps_for_migrations, migrations.RunPython.noop), migrations.RunPython(migration_utils.set_current_apps_for_migrations, migrations.RunPython.noop),
@@ -83,9 +83,9 @@ class Migration(migrations.Migration):
name='JobLaunchConfig', name='JobLaunchConfig',
fields=[ fields=[
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('extra_data', awx.main.fields.JSONField(blank=True, default={})), ('extra_data', awx.main.fields.JSONField(blank=True, default=dict)),
('survey_passwords', awx.main.fields.JSONField(blank=True, default={}, editable=False)), ('survey_passwords', awx.main.fields.JSONField(blank=True, default=dict, editable=False)),
('char_prompts', awx.main.fields.JSONField(blank=True, default={})), ('char_prompts', awx.main.fields.JSONField(blank=True, default=dict)),
('credentials', models.ManyToManyField(related_name='joblaunchconfigs', to='main.Credential')), ('credentials', models.ManyToManyField(related_name='joblaunchconfigs', to='main.Credential')),
('inventory', models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='joblaunchconfigs', to='main.Inventory')), ('inventory', models.ForeignKey(blank=True, default=None, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name='joblaunchconfigs', to='main.Inventory')),
('job', models.OneToOneField(editable=False, on_delete=django.db.models.deletion.CASCADE, related_name='launch_config', to='main.UnifiedJob')), ('job', models.OneToOneField(editable=False, on_delete=django.db.models.deletion.CASCADE, related_name='launch_config', to='main.UnifiedJob')),
@@ -94,51 +94,51 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='workflowjobtemplate', model_name='workflowjobtemplate',
name='ask_variables_on_launch', name='ask_variables_on_launch',
field=awx.main.fields.AskForField(default=False), field=awx.main.fields.AskForField(blank=True, default=False),
), ),
migrations.AlterField( migrations.AlterField(
model_name='jobtemplate', model_name='jobtemplate',
name='ask_credential_on_launch', name='ask_credential_on_launch',
field=awx.main.fields.AskForField(default=False), field=awx.main.fields.AskForField(blank=True, default=False),
), ),
migrations.AlterField( migrations.AlterField(
model_name='jobtemplate', model_name='jobtemplate',
name='ask_diff_mode_on_launch', name='ask_diff_mode_on_launch',
field=awx.main.fields.AskForField(default=False), field=awx.main.fields.AskForField(blank=True, default=False),
), ),
migrations.AlterField( migrations.AlterField(
model_name='jobtemplate', model_name='jobtemplate',
name='ask_inventory_on_launch', name='ask_inventory_on_launch',
field=awx.main.fields.AskForField(default=False), field=awx.main.fields.AskForField(blank=True, default=False),
), ),
migrations.AlterField( migrations.AlterField(
model_name='jobtemplate', model_name='jobtemplate',
name='ask_job_type_on_launch', name='ask_job_type_on_launch',
field=awx.main.fields.AskForField(default=False), field=awx.main.fields.AskForField(blank=True, default=False),
), ),
migrations.AlterField( migrations.AlterField(
model_name='jobtemplate', model_name='jobtemplate',
name='ask_limit_on_launch', name='ask_limit_on_launch',
field=awx.main.fields.AskForField(default=False), field=awx.main.fields.AskForField(blank=True, default=False),
), ),
migrations.AlterField( migrations.AlterField(
model_name='jobtemplate', model_name='jobtemplate',
name='ask_skip_tags_on_launch', name='ask_skip_tags_on_launch',
field=awx.main.fields.AskForField(default=False), field=awx.main.fields.AskForField(blank=True, default=False),
), ),
migrations.AlterField( migrations.AlterField(
model_name='jobtemplate', model_name='jobtemplate',
name='ask_tags_on_launch', name='ask_tags_on_launch',
field=awx.main.fields.AskForField(default=False), field=awx.main.fields.AskForField(blank=True, default=False),
), ),
migrations.AlterField( migrations.AlterField(
model_name='jobtemplate', model_name='jobtemplate',
name='ask_variables_on_launch', name='ask_variables_on_launch',
field=awx.main.fields.AskForField(default=False), field=awx.main.fields.AskForField(blank=True, default=False),
), ),
migrations.AlterField( migrations.AlterField(
model_name='jobtemplate', model_name='jobtemplate',
name='ask_verbosity_on_launch', name='ask_verbosity_on_launch',
field=awx.main.fields.AskForField(default=False), field=awx.main.fields.AskForField(blank=True, default=False),
), ),
] ]

View File

@@ -20,7 +20,7 @@ class Migration(migrations.Migration):
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created', models.DateTimeField(default=None, editable=False)), ('created', models.DateTimeField(default=None, editable=False)),
('modified', models.DateTimeField(default=None, editable=False)), ('modified', models.DateTimeField(default=None, editable=False)),
('event_data', awx.main.fields.JSONField(blank=True, default={})), ('event_data', awx.main.fields.JSONField(blank=True, default=dict)),
('uuid', models.CharField(default='', editable=False, max_length=1024)), ('uuid', models.CharField(default='', editable=False, max_length=1024)),
('counter', models.PositiveIntegerField(default=0, editable=False)), ('counter', models.PositiveIntegerField(default=0, editable=False)),
('stdout', models.TextField(default='', editable=False)), ('stdout', models.TextField(default='', editable=False)),
@@ -40,7 +40,7 @@ class Migration(migrations.Migration):
('created', models.DateTimeField(default=None, editable=False)), ('created', models.DateTimeField(default=None, editable=False)),
('modified', models.DateTimeField(default=None, editable=False)), ('modified', models.DateTimeField(default=None, editable=False)),
('event', models.CharField(choices=[('runner_on_failed', 'Host Failed'), ('runner_on_ok', 'Host OK'), ('runner_on_error', 'Host Failure'), ('runner_on_skipped', 'Host Skipped'), ('runner_on_unreachable', 'Host Unreachable'), ('runner_on_no_hosts', 'No Hosts Remaining'), ('runner_on_async_poll', 'Host Polling'), ('runner_on_async_ok', 'Host Async OK'), ('runner_on_async_failed', 'Host Async Failure'), ('runner_item_on_ok', 'Item OK'), ('runner_item_on_failed', 'Item Failed'), ('runner_item_on_skipped', 'Item Skipped'), ('runner_retry', 'Host Retry'), ('runner_on_file_diff', 'File Difference'), ('playbook_on_start', 'Playbook Started'), ('playbook_on_notify', 'Running Handlers'), ('playbook_on_include', 'Including File'), ('playbook_on_no_hosts_matched', 'No Hosts Matched'), ('playbook_on_no_hosts_remaining', 'No Hosts Remaining'), ('playbook_on_task_start', 'Task Started'), ('playbook_on_vars_prompt', 'Variables Prompted'), ('playbook_on_setup', 'Gathering Facts'), ('playbook_on_import_for_host', 'internal: on Import for Host'), ('playbook_on_not_import_for_host', 'internal: on Not Import for Host'), ('playbook_on_play_start', 'Play Started'), ('playbook_on_stats', 'Playbook Complete'), ('debug', 'Debug'), ('verbose', 'Verbose'), ('deprecated', 'Deprecated'), ('warning', 'Warning'), ('system_warning', 'System Warning'), ('error', 'Error')], max_length=100)), ('event', models.CharField(choices=[('runner_on_failed', 'Host Failed'), ('runner_on_ok', 'Host OK'), ('runner_on_error', 'Host Failure'), ('runner_on_skipped', 'Host Skipped'), ('runner_on_unreachable', 'Host Unreachable'), ('runner_on_no_hosts', 'No Hosts Remaining'), ('runner_on_async_poll', 'Host Polling'), ('runner_on_async_ok', 'Host Async OK'), ('runner_on_async_failed', 'Host Async Failure'), ('runner_item_on_ok', 'Item OK'), ('runner_item_on_failed', 'Item Failed'), ('runner_item_on_skipped', 'Item Skipped'), ('runner_retry', 'Host Retry'), ('runner_on_file_diff', 'File Difference'), ('playbook_on_start', 'Playbook Started'), ('playbook_on_notify', 'Running Handlers'), ('playbook_on_include', 'Including File'), ('playbook_on_no_hosts_matched', 'No Hosts Matched'), ('playbook_on_no_hosts_remaining', 'No Hosts Remaining'), ('playbook_on_task_start', 'Task Started'), ('playbook_on_vars_prompt', 'Variables Prompted'), ('playbook_on_setup', 'Gathering Facts'), ('playbook_on_import_for_host', 'internal: on Import for Host'), ('playbook_on_not_import_for_host', 'internal: on Not Import for Host'), ('playbook_on_play_start', 'Play Started'), ('playbook_on_stats', 'Playbook Complete'), ('debug', 'Debug'), ('verbose', 'Verbose'), ('deprecated', 'Deprecated'), ('warning', 'Warning'), ('system_warning', 'System Warning'), ('error', 'Error')], max_length=100)),
('event_data', awx.main.fields.JSONField(blank=True, default={})), ('event_data', awx.main.fields.JSONField(blank=True, default=dict)),
('failed', models.BooleanField(default=False, editable=False)), ('failed', models.BooleanField(default=False, editable=False)),
('changed', models.BooleanField(default=False, editable=False)), ('changed', models.BooleanField(default=False, editable=False)),
('uuid', models.CharField(default='', editable=False, max_length=1024)), ('uuid', models.CharField(default='', editable=False, max_length=1024)),
@@ -65,7 +65,7 @@ class Migration(migrations.Migration):
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')), ('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
('created', models.DateTimeField(default=None, editable=False)), ('created', models.DateTimeField(default=None, editable=False)),
('modified', models.DateTimeField(default=None, editable=False)), ('modified', models.DateTimeField(default=None, editable=False)),
('event_data', awx.main.fields.JSONField(blank=True, default={})), ('event_data', awx.main.fields.JSONField(blank=True, default=dict)),
('uuid', models.CharField(default='', editable=False, max_length=1024)), ('uuid', models.CharField(default='', editable=False, max_length=1024)),
('counter', models.PositiveIntegerField(default=0, editable=False)), ('counter', models.PositiveIntegerField(default=0, editable=False)),
('stdout', models.TextField(default='', editable=False)), ('stdout', models.TextField(default='', editable=False)),

View File

@@ -17,7 +17,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='workflowjob', model_name='workflowjob',
name='char_prompts', name='char_prompts',
field=awx.main.fields.JSONField(blank=True, default={}), field=awx.main.fields.JSONField(blank=True, default=dict),
), ),
migrations.AddField( migrations.AddField(
model_name='workflowjob', model_name='workflowjob',
@@ -27,7 +27,7 @@ class Migration(migrations.Migration):
migrations.AddField( migrations.AddField(
model_name='workflowjobtemplate', model_name='workflowjobtemplate',
name='ask_inventory_on_launch', name='ask_inventory_on_launch',
field=awx.main.fields.AskForField(default=False), field=awx.main.fields.AskForField(blank=True, default=False),
), ),
migrations.AddField( migrations.AddField(
model_name='workflowjobtemplate', model_name='workflowjobtemplate',

View File

@@ -34,7 +34,7 @@ class Migration(migrations.Migration):
('modified', models.DateTimeField(default=None, editable=False)), ('modified', models.DateTimeField(default=None, editable=False)),
('description', models.TextField(blank=True, default='')), ('description', models.TextField(blank=True, default='')),
('input_field_name', models.CharField(max_length=1024)), ('input_field_name', models.CharField(max_length=1024)),
('metadata', awx.main.fields.DynamicCredentialInputField(blank=True, default={})), ('metadata', awx.main.fields.DynamicCredentialInputField(blank=True, default=dict)),
('created_by', models.ForeignKey(default=None, editable=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name="{'class': 'credentialinputsource', 'model_name': 'credentialinputsource', 'app_label': 'main'}(class)s_created+", to=settings.AUTH_USER_MODEL)), ('created_by', models.ForeignKey(default=None, editable=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name="{'class': 'credentialinputsource', 'model_name': 'credentialinputsource', 'app_label': 'main'}(class)s_created+", to=settings.AUTH_USER_MODEL)),
('modified_by', models.ForeignKey(default=None, editable=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name="{'class': 'credentialinputsource', 'model_name': 'credentialinputsource', 'app_label': 'main'}(class)s_modified+", to=settings.AUTH_USER_MODEL)), ('modified_by', models.ForeignKey(default=None, editable=False, null=True, on_delete=django.db.models.deletion.SET_NULL, related_name="{'class': 'credentialinputsource', 'model_name': 'credentialinputsource', 'app_label': 'main'}(class)s_modified+", to=settings.AUTH_USER_MODEL)),
('source_credential', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='target_input_sources', to='main.Credential')), ('source_credential', models.ForeignKey(null=True, on_delete=django.db.models.deletion.CASCADE, related_name='target_input_sources', to='main.Credential')),

View File

@@ -30,7 +30,7 @@ SQUASHED_30 = {
migrations.AddField( migrations.AddField(
model_name='job', model_name='job',
name='survey_passwords', name='survey_passwords',
field=jsonfield.fields.JSONField(default={}, editable=False, blank=True), field=jsonfield.fields.JSONField(default=dict, editable=False, blank=True),
), ),
], ],
'0031_v302_migrate_survey_passwords': [ '0031_v302_migrate_survey_passwords': [

View File

@@ -62,24 +62,6 @@ from awx.main.models.oauth import ( # noqa
from oauth2_provider.models import Grant, RefreshToken # noqa -- needed django-oauth-toolkit model migrations from oauth2_provider.models import Grant, RefreshToken # noqa -- needed django-oauth-toolkit model migrations
# Monkeypatch Django serializer to ignore django-taggit fields (which break
# the dumpdata command; see https://github.com/alex/django-taggit/issues/155).
from django.core.serializers.python import Serializer as _PythonSerializer
_original_handle_m2m_field = _PythonSerializer.handle_m2m_field
def _new_handle_m2m_field(self, obj, field):
try:
field.rel.through._meta
except AttributeError:
return
return _original_handle_m2m_field(self, obj, field)
_PythonSerializer.handle_m2m_field = _new_handle_m2m_field
# Add custom methods to User model for permissions checks. # Add custom methods to User model for permissions checks.
from django.contrib.auth.models import User # noqa from django.contrib.auth.models import User # noqa
from awx.main.access import ( # noqa from awx.main.access import ( # noqa
@@ -158,7 +140,7 @@ User.add_to_class('is_system_auditor', user_is_system_auditor)
def user_is_in_enterprise_category(user, category): def user_is_in_enterprise_category(user, category):
ret = (category,) in user.enterprise_auth.all().values_list('provider') and not user.has_usable_password() ret = (category,) in user.enterprise_auth.values_list('provider') and not user.has_usable_password()
# NOTE: this if-else block ensures existing enterprise users are still able to # NOTE: this if-else block ensures existing enterprise users are still able to
# log in. Remove it in a future release # log in. Remove it in a future release
if category == 'radius': if category == 'radius':

View File

@@ -105,7 +105,7 @@ class Credential(PasswordFieldsModel, CommonModelNameNotUnique, ResourceMixin):
) )
inputs = CredentialInputField( inputs = CredentialInputField(
blank=True, blank=True,
default={}, default=dict,
help_text=_('Enter inputs using either JSON or YAML syntax. Use the ' help_text=_('Enter inputs using either JSON or YAML syntax. Use the '
'radio button to toggle between the two. Refer to the ' 'radio button to toggle between the two. Refer to the '
'Ansible Tower documentation for example syntax.') 'Ansible Tower documentation for example syntax.')
@@ -343,14 +343,14 @@ class CredentialType(CommonModelNameNotUnique):
) )
inputs = CredentialTypeInputField( inputs = CredentialTypeInputField(
blank=True, blank=True,
default={}, default=dict,
help_text=_('Enter inputs using either JSON or YAML syntax. Use the ' help_text=_('Enter inputs using either JSON or YAML syntax. Use the '
'radio button to toggle between the two. Refer to the ' 'radio button to toggle between the two. Refer to the '
'Ansible Tower documentation for example syntax.') 'Ansible Tower documentation for example syntax.')
) )
injectors = CredentialTypeInjectorField( injectors = CredentialTypeInjectorField(
blank=True, blank=True,
default={}, default=dict,
help_text=_('Enter injectors using either JSON or YAML syntax. Use the ' help_text=_('Enter injectors using either JSON or YAML syntax. Use the '
'radio button to toggle between the two. Refer to the ' 'radio button to toggle between the two. Refer to the '
'Ansible Tower documentation for example syntax.') 'Ansible Tower documentation for example syntax.')
@@ -1117,7 +1117,7 @@ class CredentialInputSource(PrimordialModel):
) )
metadata = DynamicCredentialInputField( metadata = DynamicCredentialInputField(
blank=True, blank=True,
default={} default=dict
) )
def clean_target_credential(self): def clean_target_credential(self):

View File

@@ -149,7 +149,7 @@ class BasePlaybookEvent(CreatedModifiedModel):
) )
event_data = JSONField( event_data = JSONField(
blank=True, blank=True,
default={}, default=dict,
) )
failed = models.BooleanField( failed = models.BooleanField(
default=False, default=False,
@@ -567,7 +567,7 @@ class BaseCommandEvent(CreatedModifiedModel):
event_data = JSONField( event_data = JSONField(
blank=True, blank=True,
default={}, default=dict,
) )
uuid = models.CharField( uuid = models.CharField(
max_length=1024, max_length=1024,

View File

@@ -650,7 +650,7 @@ class Host(CommonModelNameNotUnique, RelatedJobsMixin):
) )
ansible_facts = JSONBField( ansible_facts = JSONBField(
blank=True, blank=True,
default={}, default=dict,
help_text=_('Arbitrary JSON structure of most recent ansible_facts, per-host.'), help_text=_('Arbitrary JSON structure of most recent ansible_facts, per-host.'),
) )
ansible_facts_modified = models.DateTimeField( ansible_facts_modified = models.DateTimeField(

View File

@@ -485,7 +485,7 @@ class Job(UnifiedJob, JobOptions, SurveyJobMixin, JobNotificationMixin, TaskMana
) )
artifacts = JSONField( artifacts = JSONField(
blank=True, blank=True,
default={}, default=dict,
editable=False, editable=False,
) )
scm_revision = models.CharField( scm_revision = models.CharField(
@@ -847,7 +847,7 @@ class LaunchTimeConfigBase(BaseModel):
# This is a solution to the nullable CharField problem, specific to prompting # This is a solution to the nullable CharField problem, specific to prompting
char_prompts = JSONField( char_prompts = JSONField(
blank=True, blank=True,
default={} default=dict
) )
def prompts_dict(self, display=False): def prompts_dict(self, display=False):
@@ -927,11 +927,11 @@ class LaunchTimeConfig(LaunchTimeConfigBase):
# Special case prompting fields, even more special than the other ones # Special case prompting fields, even more special than the other ones
extra_data = JSONField( extra_data = JSONField(
blank=True, blank=True,
default={} default=dict
) )
survey_passwords = prevent_search(JSONField( survey_passwords = prevent_search(JSONField(
blank=True, blank=True,
default={}, default=dict,
editable=False, editable=False,
)) ))
# Credentials needed for non-unified job / unified JT models # Credentials needed for non-unified job / unified JT models

View File

@@ -100,7 +100,7 @@ class SurveyJobTemplateMixin(models.Model):
) )
survey_spec = prevent_search(JSONField( survey_spec = prevent_search(JSONField(
blank=True, blank=True,
default={}, default=dict,
)) ))
ask_variables_on_launch = AskForField( ask_variables_on_launch = AskForField(
blank=True, blank=True,
@@ -360,7 +360,7 @@ class SurveyJobMixin(models.Model):
survey_passwords = prevent_search(JSONField( survey_passwords = prevent_search(JSONField(
blank=True, blank=True,
default={}, default=dict,
editable=False, editable=False,
)) ))

View File

@@ -641,7 +641,7 @@ class UnifiedJob(PolymorphicModel, PasswordFieldsModel, CommonModelNameNotUnique
) )
job_env = prevent_search(JSONField( job_env = prevent_search(JSONField(
blank=True, blank=True,
default={}, default=dict,
editable=False, editable=False,
)) ))
job_explanation = models.TextField( job_explanation = models.TextField(

View File

@@ -180,7 +180,7 @@ class WorkflowJobNode(WorkflowNodeBase):
) )
ancestor_artifacts = JSONField( ancestor_artifacts = JSONField(
blank=True, blank=True,
default={}, default=dict,
editable=False, editable=False,
) )
do_not_run = models.BooleanField( do_not_run = models.BooleanField(

View File

@@ -101,7 +101,7 @@ def test_instance_group_is_isolated(instance_group, isolated_instance_group):
assert not instance_group.is_isolated assert not instance_group.is_isolated
assert isolated_instance_group.is_isolated assert isolated_instance_group.is_isolated
isolated_instance_group.instances = [] isolated_instance_group.instances.set([])
assert isolated_instance_group.is_isolated assert isolated_instance_group.is_isolated

View File

@@ -12,7 +12,6 @@ def run_command(name, *args, **options):
command_runner = options.pop('command_runner', call_command) command_runner = options.pop('command_runner', call_command)
stdin_fileobj = options.pop('stdin_fileobj', None) stdin_fileobj = options.pop('stdin_fileobj', None)
options.setdefault('verbosity', 1) options.setdefault('verbosity', 1)
options.setdefault('interactive', False)
original_stdin = sys.stdin original_stdin = sys.stdin
original_stdout = sys.stdout original_stdout = sys.stdout
original_stderr = sys.stderr original_stderr = sys.stderr

View File

@@ -11,9 +11,9 @@ from django.urls import resolve
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.core.serializers.json import DjangoJSONEncoder from django.core.serializers.json import DjangoJSONEncoder
from django.db.backends.sqlite3.base import SQLiteCursorWrapper from django.db.backends.sqlite3.base import SQLiteCursorWrapper
from jsonbfield.fields import JSONField
# AWX # AWX
from awx.main.fields import JSONBField
from awx.main.models.projects import Project from awx.main.models.projects import Project
from awx.main.models.ha import Instance from awx.main.models.ha import Instance
@@ -737,7 +737,7 @@ def get_db_prep_save(self, value, connection, **kwargs):
@pytest.fixture @pytest.fixture
def monkeypatch_jsonbfield_get_db_prep_save(mocker): def monkeypatch_jsonbfield_get_db_prep_save(mocker):
JSONField.get_db_prep_save = get_db_prep_save JSONBField.get_db_prep_save = get_db_prep_save
@pytest.fixture @pytest.fixture

View File

@@ -457,4 +457,4 @@ def test_duplicate_name_within_template(job_template):
with pytest.raises(IntegrityError) as ierror: with pytest.raises(IntegrityError) as ierror:
s2.save() s2.save()
assert str(ierror.value) == "columns unified_job_template_id, name are not unique" assert str(ierror.value) == "UNIQUE constraint failed: main_schedule.unified_job_template_id, main_schedule.name"

View File

@@ -20,7 +20,6 @@ from django.test import TransactionTestCase
from django.core.exceptions import ValidationError from django.core.exceptions import ValidationError
@pytest.mark.django_db
class TestWorkflowDAGFunctional(TransactionTestCase): class TestWorkflowDAGFunctional(TransactionTestCase):
def workflow_job(self, states=['new', 'new', 'new', 'new', 'new']): def workflow_job(self, states=['new', 'new', 'new', 'new', 'new']):
""" """

View File

@@ -1,5 +1,3 @@
import pytest
from django.test import TransactionTestCase from django.test import TransactionTestCase
from awx.main.models import ( from awx.main.models import (
@@ -8,7 +6,6 @@ from awx.main.models import (
) )
@pytest.mark.django_db
class TestCapacityMapping(TransactionTestCase): class TestCapacityMapping(TransactionTestCase):
def sample_cluster(self): def sample_cluster(self):

View File

@@ -42,7 +42,7 @@ def test_job_notification_data(inventory, machine_credential, project):
survey_passwords={"SSN": encrypted_str}, survey_passwords={"SSN": encrypted_str},
project=project, project=project,
) )
job.credentials = [machine_credential] job.credentials.set([machine_credential])
notification_data = job.notification_data(block=0) notification_data = job.notification_data(block=0)
assert json.loads(notification_data['extra_vars'])['SSN'] == encrypted_str assert json.loads(notification_data['extra_vars'])['SSN'] == encrypted_str

View File

@@ -7,7 +7,6 @@ from awx.main.access import UserAccess, RoleAccess, TeamAccess
from awx.main.models import User, Organization, Inventory from awx.main.models import User, Organization, Inventory
@pytest.mark.django_db
class TestSysAuditorTransactional(TransactionTestCase): class TestSysAuditorTransactional(TransactionTestCase):
def rando(self): def rando(self):
return User.objects.create(username='rando', password='rando', email='rando@com.com') return User.objects.create(username='rando', password='rando', email='rando@com.com')

View File

@@ -186,11 +186,8 @@ class TestResourceAccessList:
def mock_request(self): def mock_request(self):
return mock.MagicMock( return mock.MagicMock(
user=mock.MagicMock( user=mock.MagicMock(is_anonymous=False, is_superuser=False),
is_anonymous=mock.MagicMock(return_value=False), method='GET')
is_superuser=False
), method='GET')
def mock_view(self, parent=None): def mock_view(self, parent=None):
view = ResourceAccessList() view = ResourceAccessList()
@@ -200,7 +197,6 @@ class TestResourceAccessList:
view.get_parent_object = lambda: parent view.get_parent_object = lambda: parent
return view return view
def test_parent_access_check_failed(self, mocker, mock_organization): def test_parent_access_check_failed(self, mocker, mock_organization):
mock_access = mocker.MagicMock(__name__='for logger', return_value=False) mock_access = mocker.MagicMock(__name__='for logger', return_value=False)
with mocker.patch('awx.main.access.BaseAccess.can_read', mock_access): with mocker.patch('awx.main.access.BaseAccess.can_read', mock_access):
@@ -208,7 +204,6 @@ class TestResourceAccessList:
self.mock_view(parent=mock_organization).check_permissions(self.mock_request()) self.mock_view(parent=mock_organization).check_permissions(self.mock_request())
mock_access.assert_called_once_with(mock_organization) mock_access.assert_called_once_with(mock_organization)
def test_parent_access_check_worked(self, mocker, mock_organization): def test_parent_access_check_worked(self, mocker, mock_organization):
mock_access = mocker.MagicMock(__name__='for logger', return_value=True) mock_access = mocker.MagicMock(__name__='for logger', return_value=True)
with mocker.patch('awx.main.access.BaseAccess.can_read', mock_access): with mocker.patch('awx.main.access.BaseAccess.can_read', mock_access):

View File

@@ -6,7 +6,7 @@ from unittest.mock import PropertyMock
from awx.api.urls import urlpatterns as api_patterns from awx.api.urls import urlpatterns as api_patterns
# Django # Django
from django.urls import RegexURLResolver, RegexURLPattern from django.urls import URLResolver, URLPattern
@pytest.fixture(autouse=True) @pytest.fixture(autouse=True)
@@ -20,24 +20,24 @@ def all_views():
''' '''
returns a set of all views in the app returns a set of all views in the app
''' '''
patterns = set([]) patterns = set()
url_views = set([]) url_views = set()
# Add recursive URL patterns # Add recursive URL patterns
unprocessed = set(api_patterns) unprocessed = set(api_patterns)
while unprocessed: while unprocessed:
to_process = unprocessed.copy() to_process = unprocessed.copy()
unprocessed = set([]) unprocessed = set()
for pattern in to_process: for pattern in to_process:
if hasattr(pattern, 'lookup_str') and not pattern.lookup_str.startswith('awx.api'): if hasattr(pattern, 'lookup_str') and not pattern.lookup_str.startswith('awx.api'):
continue continue
patterns.add(pattern) patterns.add(pattern)
if isinstance(pattern, RegexURLResolver): if isinstance(pattern, URLResolver):
for sub_pattern in pattern.url_patterns: for sub_pattern in pattern.url_patterns:
if sub_pattern not in patterns: if sub_pattern not in patterns:
unprocessed.add(sub_pattern) unprocessed.add(sub_pattern)
# Get view classes # Get view classes
for pattern in patterns: for pattern in patterns:
if isinstance(pattern, RegexURLPattern) and hasattr(pattern.callback, 'view_class'): if isinstance(pattern, URLPattern) and hasattr(pattern.callback, 'view_class'):
url_views.add(pattern.callback.view_class) url_views.add(pattern.callback.view_class)
return url_views return url_views

View File

@@ -57,5 +57,5 @@ def test_really_long_event_fields(field):
}) })
manager.create.assert_called_with(**{ manager.create.assert_called_with(**{
'job_id': 123, 'job_id': 123,
'event_data': {field: 'X' * 1021 + '...'} 'event_data': {field: 'X' * 1023 + ''}
}) })

View File

@@ -208,7 +208,7 @@ def _check_unique_together_fields(model, ut):
field = model._meta.get_field(field_name) field = model._meta.get_field(field_name)
if field_name == 'name': if field_name == 'name':
has_name = True has_name = True
elif type(field) == models.ForeignKey and field.rel.to != model: elif type(field) == models.ForeignKey and field.related_model != model:
fk_names.append(field_name) fk_names.append(field_name)
elif issubclass(type(field), models.CharField) and field.choices: elif issubclass(type(field), models.CharField) and field.choices:
fields.append(field_name) fields.append(field_name)
@@ -256,7 +256,7 @@ def _dfs(configuration, model, graph, dead_ends, new_deadends, parents):
fields, fk_names = configuration[model][0][:], configuration[model][1][:] fields, fk_names = configuration[model][0][:], configuration[model][1][:]
adj_list = [] adj_list = []
for fk_name in fk_names: for fk_name in fk_names:
next_model = model._meta.get_field(fk_name).rel.to next_model = model._meta.get_field(fk_name).related_model
if issubclass(next_model, ContentType): if issubclass(next_model, ContentType):
continue continue
if next_model not in configuration or\ if next_model not in configuration or\

View File

@@ -60,7 +60,7 @@ def handle_error(request, status=404, **kwargs):
return render(request, 'error.html', kwargs, status=status) return render(request, 'error.html', kwargs, status=status)
def handle_400(request): def handle_400(request, exception):
kwargs = { kwargs = {
'name': _('Bad Request'), 'name': _('Bad Request'),
'content': _('The request could not be understood by the server.'), 'content': _('The request could not be understood by the server.'),
@@ -68,7 +68,7 @@ def handle_400(request):
return handle_error(request, 400, **kwargs) return handle_error(request, 400, **kwargs)
def handle_403(request): def handle_403(request, exception):
kwargs = { kwargs = {
'name': _('Forbidden'), 'name': _('Forbidden'),
'content': _('You don\'t have permission to access the requested resource.'), 'content': _('You don\'t have permission to access the requested resource.'),
@@ -76,7 +76,7 @@ def handle_403(request):
return handle_error(request, 403, **kwargs) return handle_error(request, 403, **kwargs)
def handle_404(request): def handle_404(request, exception):
kwargs = { kwargs = {
'name': _('Not Found'), 'name': _('Not Found'),
'content': _('The requested resource could not be found.'), 'content': _('The requested resource could not be found.'),

View File

@@ -13,6 +13,7 @@ from django.dispatch import receiver
from django.contrib.auth.models import User from django.contrib.auth.models import User
from django.conf import settings as django_settings from django.conf import settings as django_settings
from django.core.signals import setting_changed from django.core.signals import setting_changed
from django.utils.encoding import force_text
# django-auth-ldap # django-auth-ldap
from django_auth_ldap.backend import LDAPSettings as BaseLDAPSettings from django_auth_ldap.backend import LDAPSettings as BaseLDAPSettings
@@ -98,7 +99,7 @@ class LDAPBackend(BaseLDAPBackend):
settings = property(_get_settings, _set_settings) settings = property(_get_settings, _set_settings)
def authenticate(self, username, password): def authenticate(self, request, username, password):
if self.settings.START_TLS and ldap.OPT_X_TLS_REQUIRE_CERT in self.settings.CONNECTION_OPTIONS: if self.settings.START_TLS and ldap.OPT_X_TLS_REQUIRE_CERT in self.settings.CONNECTION_OPTIONS:
# with python-ldap, if you want to set connection-specific TLS # with python-ldap, if you want to set connection-specific TLS
# parameters, you must also specify OPT_X_TLS_NEWCTX = 0 # parameters, you must also specify OPT_X_TLS_NEWCTX = 0
@@ -124,7 +125,7 @@ class LDAPBackend(BaseLDAPBackend):
raise ImproperlyConfigured( raise ImproperlyConfigured(
"{} must be an {} instance.".format(setting_name, type_) "{} must be an {} instance.".format(setting_name, type_)
) )
return super(LDAPBackend, self).authenticate(None, username, password) return super(LDAPBackend, self).authenticate(request, username, password)
except Exception: except Exception:
logger.exception("Encountered an error authenticating to LDAP") logger.exception("Encountered an error authenticating to LDAP")
return None return None
@@ -179,7 +180,7 @@ def _decorate_enterprise_user(user, provider):
def _get_or_set_enterprise_user(username, password, provider): def _get_or_set_enterprise_user(username, password, provider):
created = False created = False
try: try:
user = User.objects.all().prefetch_related('enterprise_auth').get(username=username) user = User.objects.prefetch_related('enterprise_auth').get(username=username)
except User.DoesNotExist: except User.DoesNotExist:
user = User(username=username) user = User(username=username)
enterprise_auth = _decorate_enterprise_user(user, provider) enterprise_auth = _decorate_enterprise_user(user, provider)
@@ -196,10 +197,10 @@ class RADIUSBackend(BaseRADIUSBackend):
Custom Radius backend to verify license status Custom Radius backend to verify license status
''' '''
def authenticate(self, username, password): def authenticate(self, request, username, password):
if not django_settings.RADIUS_SERVER: if not django_settings.RADIUS_SERVER:
return None return None
return super(RADIUSBackend, self).authenticate(None, username, password) return super(RADIUSBackend, self).authenticate(request, username, password)
def get_user(self, user_id): def get_user(self, user_id):
if not django_settings.RADIUS_SERVER: if not django_settings.RADIUS_SERVER:
@@ -209,7 +210,7 @@ class RADIUSBackend(BaseRADIUSBackend):
return user return user
def get_django_user(self, username, password=None): def get_django_user(self, username, password=None):
return _get_or_set_enterprise_user(username, password, 'radius') return _get_or_set_enterprise_user(force_text(username), force_text(password), 'radius')
class TACACSPlusBackend(object): class TACACSPlusBackend(object):
@@ -217,7 +218,7 @@ class TACACSPlusBackend(object):
Custom TACACS+ auth backend for AWX Custom TACACS+ auth backend for AWX
''' '''
def authenticate(self, username, password): def authenticate(self, request, username, password):
if not django_settings.TACACSPLUS_HOST: if not django_settings.TACACSPLUS_HOST:
return None return None
try: try:
@@ -284,13 +285,13 @@ class SAMLAuth(BaseSAMLAuth):
idp_config = self.setting('ENABLED_IDPS')[idp_name] idp_config = self.setting('ENABLED_IDPS')[idp_name]
return TowerSAMLIdentityProvider(idp_name, **idp_config) return TowerSAMLIdentityProvider(idp_name, **idp_config)
def authenticate(self, *args, **kwargs): def authenticate(self, request, *args, **kwargs):
if not all([django_settings.SOCIAL_AUTH_SAML_SP_ENTITY_ID, django_settings.SOCIAL_AUTH_SAML_SP_PUBLIC_CERT, if not all([django_settings.SOCIAL_AUTH_SAML_SP_ENTITY_ID, django_settings.SOCIAL_AUTH_SAML_SP_PUBLIC_CERT,
django_settings.SOCIAL_AUTH_SAML_SP_PRIVATE_KEY, django_settings.SOCIAL_AUTH_SAML_ORG_INFO, django_settings.SOCIAL_AUTH_SAML_SP_PRIVATE_KEY, django_settings.SOCIAL_AUTH_SAML_ORG_INFO,
django_settings.SOCIAL_AUTH_SAML_TECHNICAL_CONTACT, django_settings.SOCIAL_AUTH_SAML_SUPPORT_CONTACT, django_settings.SOCIAL_AUTH_SAML_TECHNICAL_CONTACT, django_settings.SOCIAL_AUTH_SAML_SUPPORT_CONTACT,
django_settings.SOCIAL_AUTH_SAML_ENABLED_IDPS]): django_settings.SOCIAL_AUTH_SAML_ENABLED_IDPS]):
return None return None
user = super(SAMLAuth, self).authenticate(*args, **kwargs) user = super(SAMLAuth, self).authenticate(request, *args, **kwargs)
# Comes from https://github.com/omab/python-social-auth/blob/v0.2.21/social/backends/base.py#L91 # Comes from https://github.com/omab/python-social-auth/blob/v0.2.21/social/backends/base.py#L91
if getattr(user, 'is_new', False): if getattr(user, 'is_new', False):
_decorate_enterprise_user(user, 'saml') _decorate_enterprise_user(user, 'saml')
@@ -307,7 +308,7 @@ class SAMLAuth(BaseSAMLAuth):
return super(SAMLAuth, self).get_user(user_id) return super(SAMLAuth, self).get_user(user_id)
def _update_m2m_from_groups(user, ldap_user, rel, opts, remove=True): def _update_m2m_from_groups(user, ldap_user, related, opts, remove=True):
''' '''
Hepler function to update m2m relationship based on LDAP group membership. Hepler function to update m2m relationship based on LDAP group membership.
''' '''
@@ -328,10 +329,10 @@ def _update_m2m_from_groups(user, ldap_user, rel, opts, remove=True):
should_add = True should_add = True
if should_add: if should_add:
user.save() user.save()
rel.add(user) related.add(user)
elif remove and user in rel.all(): elif remove and user in related.all():
user.save() user.save()
rel.remove(user) related.remove(user)
@receiver(populate_user, dispatch_uid='populate-ldap-user') @receiver(populate_user, dispatch_uid='populate-ldap-user')

View File

@@ -39,7 +39,7 @@ class SocialAuthMiddleware(SocialAuthExceptionMiddleware):
request.successful_authenticator = None request.successful_authenticator = None
if not request.path.startswith('/sso/') and 'migrations_notran' not in request.path: if not request.path.startswith('/sso/') and 'migrations_notran' not in request.path:
if request.user and request.user.is_authenticated(): if request.user and request.user.is_authenticated:
# The rest of the code base rely hevily on type/inheritance checks, # The rest of the code base rely hevily on type/inheritance checks,
# LazyObject sent from Django auth middleware can be buggy if not # LazyObject sent from Django auth middleware can be buggy if not
# converted back to its original object. # converted back to its original object.

View File

@@ -17,7 +17,7 @@ class Migration(migrations.Migration):
fields=[ fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)), ('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('provider', models.CharField(max_length=32, choices=[(b'radius', 'RADIUS'), (b'tacacs+', 'TACACS+')])), ('provider', models.CharField(max_length=32, choices=[(b'radius', 'RADIUS'), (b'tacacs+', 'TACACS+')])),
('user', models.ForeignKey(related_name='enterprise_auth', to=settings.AUTH_USER_MODEL)), ('user', models.ForeignKey(related_name='enterprise_auth', on_delete=models.CASCADE, to=settings.AUTH_USER_MODEL)),
], ],
), ),
migrations.AlterUniqueTogether( migrations.AlterUniqueTogether(

View File

@@ -50,7 +50,7 @@ def prevent_inactive_login(backend, details, user=None, *args, **kwargs):
raise AuthInactive(backend) raise AuthInactive(backend)
def _update_m2m_from_expression(user, rel, expr, remove=True): def _update_m2m_from_expression(user, related, expr, remove=True):
''' '''
Helper function to update m2m relationship based on user matching one or Helper function to update m2m relationship based on user matching one or
more expressions. more expressions.
@@ -73,12 +73,12 @@ def _update_m2m_from_expression(user, rel, expr, remove=True):
if ex.match(user.username) or ex.match(user.email): if ex.match(user.username) or ex.match(user.email):
should_add = True should_add = True
if should_add: if should_add:
rel.add(user) related.add(user)
elif remove: elif remove:
rel.remove(user) related.remove(user)
def _update_org_from_attr(user, rel, attr, remove, remove_admins): def _update_org_from_attr(user, related, attr, remove, remove_admins):
from awx.main.models import Organization from awx.main.models import Organization
org_ids = [] org_ids = []
@@ -87,7 +87,7 @@ def _update_org_from_attr(user, rel, attr, remove, remove_admins):
org = Organization.objects.get_or_create(name=org_name)[0] org = Organization.objects.get_or_create(name=org_name)[0]
org_ids.append(org.id) org_ids.append(org.id)
getattr(org, rel).members.add(user) getattr(org, related).members.add(user)
if remove: if remove:
[o.member_role.members.remove(user) for o in [o.member_role.members.remove(user) for o in

View File

@@ -27,6 +27,7 @@ def existing_tacacsplus_user():
user = User.objects.get(username="foo") user = User.objects.get(username="foo")
except User.DoesNotExist: except User.DoesNotExist:
user = User(username="foo") user = User(username="foo")
user.set_unusable_password()
user.save() user.save()
enterprise_auth = UserEnterpriseAuth(user=user, provider='tacacs+') enterprise_auth = UserEnterpriseAuth(user=user, provider='tacacs+')
enterprise_auth.save() enterprise_auth.save()

View File

@@ -8,7 +8,10 @@ from awx.sso.backends import _get_or_set_enterprise_user
@pytest.mark.django_db @pytest.mark.django_db
def test_fetch_user_if_exist(existing_tacacsplus_user): def test_fetch_user_if_exist(existing_tacacsplus_user):
with mock.patch('awx.sso.backends.logger') as mocked_logger:
new_user = _get_or_set_enterprise_user("foo", "password", "tacacs+") new_user = _get_or_set_enterprise_user("foo", "password", "tacacs+")
mocked_logger.debug.assert_not_called()
mocked_logger.warn.assert_not_called()
assert new_user == existing_tacacsplus_user assert new_user == existing_tacacsplus_user

View File

@@ -4,7 +4,7 @@ from unittest import mock
def test_empty_host_fails_auth(tacacsplus_backend): def test_empty_host_fails_auth(tacacsplus_backend):
with mock.patch('awx.sso.backends.django_settings') as settings: with mock.patch('awx.sso.backends.django_settings') as settings:
settings.TACACSPLUS_HOST = '' settings.TACACSPLUS_HOST = ''
ret_user = tacacsplus_backend.authenticate(u"user", u"pass") ret_user = tacacsplus_backend.authenticate(None, u"user", u"pass")
assert ret_user is None assert ret_user is None
@@ -16,7 +16,7 @@ def test_client_raises_exception(tacacsplus_backend):
mock.patch('tacacs_plus.TACACSClient', return_value=client): mock.patch('tacacs_plus.TACACSClient', return_value=client):
settings.TACACSPLUS_HOST = 'localhost' settings.TACACSPLUS_HOST = 'localhost'
settings.TACACSPLUS_AUTH_PROTOCOL = 'ascii' settings.TACACSPLUS_AUTH_PROTOCOL = 'ascii'
ret_user = tacacsplus_backend.authenticate(u"user", u"pass") ret_user = tacacsplus_backend.authenticate(None, u"user", u"pass")
assert ret_user is None assert ret_user is None
logger.exception.assert_called_once_with( logger.exception.assert_called_once_with(
"TACACS+ Authentication Error: foo" "TACACS+ Authentication Error: foo"
@@ -32,7 +32,7 @@ def test_client_return_invalid_fails_auth(tacacsplus_backend):
mock.patch('tacacs_plus.TACACSClient', return_value=client): mock.patch('tacacs_plus.TACACSClient', return_value=client):
settings.TACACSPLUS_HOST = 'localhost' settings.TACACSPLUS_HOST = 'localhost'
settings.TACACSPLUS_AUTH_PROTOCOL = 'ascii' settings.TACACSPLUS_AUTH_PROTOCOL = 'ascii'
ret_user = tacacsplus_backend.authenticate(u"user", u"pass") ret_user = tacacsplus_backend.authenticate(None, u"user", u"pass")
assert ret_user is None assert ret_user is None
@@ -48,5 +48,5 @@ def test_client_return_valid_passes_auth(tacacsplus_backend):
mock.patch('awx.sso.backends._get_or_set_enterprise_user', return_value=user): mock.patch('awx.sso.backends._get_or_set_enterprise_user', return_value=user):
settings.TACACSPLUS_HOST = 'localhost' settings.TACACSPLUS_HOST = 'localhost'
settings.TACACSPLUS_AUTH_PROTOCOL = 'ascii' settings.TACACSPLUS_AUTH_PROTOCOL = 'ascii'
ret_user = tacacsplus_backend.authenticate(u"user", u"pass") ret_user = tacacsplus_backend.authenticate(None, u"user", u"pass")
assert ret_user == user assert ret_user == user

View File

@@ -40,7 +40,7 @@ class CompleteView(BaseRedirectView):
def dispatch(self, request, *args, **kwargs): def dispatch(self, request, *args, **kwargs):
response = super(CompleteView, self).dispatch(request, *args, **kwargs) response = super(CompleteView, self).dispatch(request, *args, **kwargs)
if self.request.user and self.request.user.is_authenticated(): if self.request.user and self.request.user.is_authenticated:
logger.info(smart_text(u"User {} logged in".format(self.request.user.username))) logger.info(smart_text(u"User {} logged in".format(self.request.user.username)))
response.set_cookie('userLoggedIn', 'true') response.set_cookie('userLoggedIn', 'true')
current_user = UserSerializer(self.request.user) current_user = UserSerializer(self.request.user)

View File

@@ -1,5 +1,5 @@
{% extends "rest_framework/api.html" %} {% extends "rest_framework/api.html" %}
{% load i18n staticfiles %} {% load i18n static %}
{% block title %}{{ name }} &middot; {% trans 'AWX' %}{% endblock %} {% block title %}{{ name }} &middot; {% trans 'AWX' %}{% endblock %}

View File

@@ -1,6 +1,6 @@
{# Partial copy of login_base.html from rest_framework with AWX change. #} {# Partial copy of login_base.html from rest_framework with AWX change. #}
{% extends 'rest_framework/api.html' %} {% extends 'rest_framework/api.html' %}
{% load i18n staticfiles %} {% load i18n static %}
{% block breadcrumbs %} {% block breadcrumbs %}
{% endblock %} {% endblock %}

View File

@@ -40,12 +40,5 @@ if social_django.__version__ != '2.1.0':
still works".format(social_django.__version__)) still works".format(social_django.__version__))
if not django.__version__.startswith('1.'):
raise RuntimeError("Django version other than 1.XX detected {}. \
Inherit from WSGIHandler to support short-circuit Django Middleware. \
This is known to work for Django 1.XX and may not work with other, \
even minor, versions.".format(django.__version__))
# Return the default Django WSGI application. # Return the default Django WSGI application.
application = get_wsgi_application() application = get_wsgi_application()

View File

@@ -1,28 +0,0 @@
Copyright (c) Django Software Foundation and individual contributors.
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
1. Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
2. Redistributions in binary form must reproduce the above copyright
notice, this list of conditions and the following disclaimer in the
documentation and/or other materials provided with the distribution.
3. Neither the name of Django nor the names of its contributors may be used
to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -0,0 +1,25 @@
Copyright (c) 2016, Andi Albrecht <albrecht.andi@gmail.com>
All rights reserved.
Redistribution and use in source and binary forms, with or without modification,
are permitted provided that the following conditions are met:
* Redistributions of source code must retain the above copyright notice,
this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above copyright notice,
this list of conditions and the following disclaimer in the documentation
and/or other materials provided with the distribution.
* Neither the name of the authors nor the names of its contributors may be
used to endorse or promote products derived from this software without
specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

View File

@@ -7,12 +7,12 @@ channels==1.1.8
celery==4.3.0 celery==4.3.0
daphne==1.3.0 # Last before backwards-incompatible channels 2 upgrade daphne==1.3.0 # Last before backwards-incompatible channels 2 upgrade
twisted[tls]>=17.1 # from daphne, see https://github.com/django/daphne/pull/257 twisted[tls]>=17.1 # from daphne, see https://github.com/django/daphne/pull/257
Django==1.11.20 Django==2.2.2
django-auth-ldap==1.7.0 django-auth-ldap==1.7.0
django-cors-headers==2.4.0 django-cors-headers==2.4.0
django-crum==0.7.2 django-crum==0.7.2
django-extensions==2.0.0 django-extensions==2.0.0
django-jsonfield==1.0.1 django-jsonfield==1.2.0
django-oauth-toolkit==1.1.3 django-oauth-toolkit==1.1.3
django-polymorphic==2.0.2 django-polymorphic==2.0.2
django-pglocks==1.0.2 django-pglocks==1.0.2

View File

@@ -27,7 +27,7 @@ django-auth-ldap==1.7.0
django-cors-headers==2.4.0 django-cors-headers==2.4.0
django-crum==0.7.2 django-crum==0.7.2
django-extensions==2.0.0 django-extensions==2.0.0
django-jsonfield==1.0.1 django-jsonfield==1.2.0
django-oauth-toolkit==1.1.3 django-oauth-toolkit==1.1.3
django-pglocks==1.0.2 django-pglocks==1.0.2
django-polymorphic==2.0.2 django-polymorphic==2.0.2
@@ -35,7 +35,7 @@ django-radius==1.3.3
django-solo==1.1.3 django-solo==1.1.3
django-split-settings==0.3.0 django-split-settings==0.3.0
django-taggit==0.22.2 django-taggit==0.22.2
django==1.11.20 django==2.2.2
djangorestframework-yaml==1.0.3 djangorestframework-yaml==1.0.3
djangorestframework==3.9.4 djangorestframework==3.9.4
@@ -102,6 +102,7 @@ six==1.12.0 # via ansible-runner, asgi-amqp, asgiref, autobahn, au
slackclient==1.1.2 slackclient==1.1.2
social-auth-app-django==2.1.0 social-auth-app-django==2.1.0
social-auth-core==3.0.0 social-auth-core==3.0.0
sqlparse==0.3.0 # via django
tacacs_plus==1.0 tacacs_plus==1.0
tempora==1.14.1 # via irc, jaraco.logging tempora==1.14.1 # via irc, jaraco.logging
twilio==6.10.4 twilio==6.10.4

View File

@@ -1,3 +1,2 @@
git+https://github.com/ansible/ansiconv.git@tower_1.0.0#egg=ansiconv git+https://github.com/ansible/ansiconv.git@tower_1.0.0#egg=ansiconv
git+https://github.com/ansible/django-qsstats-magic.git@py3#egg=django-qsstats-magic git+https://github.com/ansible/django-qsstats-magic.git@py3#egg=django-qsstats-magic
git+https://github.com/ansible/django-jsonbfield@fix-sqlite_serialization#egg=jsonbfield

View File

@@ -1,11 +1,9 @@
FROM centos:7 FROM fedora:27
ARG UID=0 ARG UID=0
RUN yum -y update && yum -y install epel-release
# sync with installer/roles/image_build/templates/Dockerfile.j2 # sync with installer/roles/image_build/templates/Dockerfile.j2
RUN yum -y install acl \ RUN dnf -y install acl \
alsa-lib \ alsa-lib \
ansible \ ansible \
atk \ atk \
@@ -14,12 +12,15 @@ RUN yum -y install acl \
curl \ curl \
cyrus-sasl \ cyrus-sasl \
cyrus-sasl-devel \ cyrus-sasl-devel \
findutils \
gcc \ gcc \
gcc-c++ \ gcc-c++ \
GConf2 \ GConf2 \
git \ git \
glibc-locale-source \
gtk3 \ gtk3 \
ipa-gothic-fonts \ ipa-gothic-fonts \
iproute \
krb5-devel \ krb5-devel \
krb5-libs \ krb5-libs \
krb5-workstation \ krb5-workstation \
@@ -45,16 +46,16 @@ RUN yum -y install acl \
nodejs \ nodejs \
openldap-devel \ openldap-devel \
openssh-server \ openssh-server \
postgresql-contrib \
postgresql-devel \ postgresql-devel \
python-devel \ python-devel \
python-pip \ python-pip \
python-psutil \ python-psutil \
python-psycopg2 \ python-psycopg2 \
python-setuptools \ python-setuptools \
python36-devel \ python3-devel \
python36-setuptools \ python3-setuptools \
rsync \ rsync \
setools-libs \
subversion \ subversion \
sudo \ sudo \
swig \ swig \