Merge branch 'devel' into workflow-channels

This commit is contained in:
Wayne Witzel III
2016-11-16 11:01:10 -05:00
248 changed files with 2051 additions and 829 deletions

View File

@@ -28,14 +28,19 @@ from awx.main.models.channels import * # noqa
# 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.
from django.contrib.auth.models import User # noqa
from awx.main.access import * # noqa
@@ -46,26 +51,32 @@ User.add_to_class('can_access', check_user_access)
User.add_to_class('accessible_objects', user_accessible_objects)
User.add_to_class('admin_role', user_admin_role)
@property
def user_get_organizations(user):
return Organization.objects.filter(member_role__members=user)
@property
def user_get_admin_of_organizations(user):
return Organization.objects.filter(admin_role__members=user)
@property
def user_get_auditor_of_organizations(user):
return Organization.objects.filter(auditor_role__members=user)
User.add_to_class('organizations', user_get_organizations)
User.add_to_class('admin_of_organizations', user_get_admin_of_organizations)
User.add_to_class('auditor_of_organizations', user_get_auditor_of_organizations)
@property
def user_is_system_auditor(user):
return Role.singleton('system_auditor').members.filter(id=user.id).exists()
@user_is_system_auditor.setter
def user_is_system_auditor(user, tf):
if user.id:
@@ -74,6 +85,7 @@ def user_is_system_auditor(user, tf):
else:
Role.singleton('system_auditor').members.remove(user)
User.add_to_class('is_system_auditor', user_is_system_auditor)
# Import signal handlers only after models have been defined.

View File

@@ -17,13 +17,11 @@ from django.utils.translation import ugettext_lazy as _
from django.core.exceptions import ValidationError
from django.core.urlresolvers import reverse
# Django-JSONField
from jsonfield import JSONField
# AWX
from awx.main.models.base import * # noqa
from awx.main.models.unified_jobs import * # noqa
from awx.main.models.notifications import JobNotificationMixin
from awx.main.fields import JSONField
logger = logging.getLogger('awx.main.models.ad_hoc_commands')
@@ -211,6 +209,7 @@ class AdHocCommand(UnifiedJob, JobNotificationMixin):
def get_notification_friendly_name(self):
return "AdHoc Command"
class AdHocCommandEvent(CreatedModifiedModel):
'''
An event/message logged from the ad hoc event callback for each host.

View File

@@ -320,6 +320,7 @@ class CommonModelNameNotUnique(PrimordialModel):
unique=False,
)
class NotificationFieldsModel(BaseModel):
class Meta:

View File

@@ -1,5 +1,6 @@
from django.db import models
class ChannelGroup(models.Model):
group = models.CharField(max_length=200, unique=True)
channels = models.TextField()

View File

@@ -8,6 +8,7 @@ from jsonbfield.fields import JSONField
__all__ = ('Fact', )
class Fact(models.Model):
"""A model representing a fact returned from Ansible.
Facts are stored as JSON dictionaries.
@@ -20,8 +21,8 @@ class Fact(models.Model):
help_text=_('Host for the facts that the fact scan captured.'),
)
timestamp = models.DateTimeField(
default=None,
editable=False,
default=None,
editable=False,
help_text=_('Date and time of the corresponding fact scan gathering time.')
)
module = models.CharField(max_length=128)

View File

@@ -39,9 +39,11 @@ class Instance(models.Model):
# NOTE: TODO: Likely to repurpose this once standalone ramparts are a thing
return "tower"
class TowerScheduleState(SingletonModel):
schedule_last_run = models.DateTimeField(auto_now_add=True)
class JobOrigin(models.Model):
"""A model representing the relationship between a unified job and
the instance that was responsible for starting that job.

View File

@@ -21,9 +21,6 @@ from django.utils.translation import ugettext_lazy as _
from django.core.exceptions import ValidationError
from django.core.urlresolvers import reverse
# Django-JSONField
from jsonfield import JSONField
# AWX
from awx.main.constants import CLOUD_PROVIDERS
from awx.main.models.base import * # noqa
@@ -40,6 +37,7 @@ from awx.main.redact import PlainTextCleaner
from awx.main.fields import ImplicitRoleField
from awx.main.models.mixins import ResourceMixin, SurveyJobTemplateMixin, SurveyJobMixin
from awx.main.models.base import PERM_INVENTORY_SCAN
from awx.main.fields import JSONField
from awx.main.consumers import emit_channel_notification
@@ -190,6 +188,7 @@ class JobOptions(BaseModel):
else:
return []
class JobTemplate(UnifiedJobTemplate, JobOptions, SurveyJobTemplateMixin, ResourceMixin):
'''
A job template is a reusable job definition for applying a project (with
@@ -394,6 +393,7 @@ class JobTemplate(UnifiedJobTemplate, JobOptions, SurveyJobTemplateMixin, Resour
any_notification_templates = set(any_notification_templates + list(base_notification_templates.filter(organization_notification_templates_for_any=self.project.organization)))
return dict(error=list(error_notification_templates), success=list(success_notification_templates), any=list(any_notification_templates))
class Job(UnifiedJob, JobOptions, SurveyJobMixin, JobNotificationMixin):
'''
A job applies a project (with playbook) to an inventory source with a given
@@ -648,6 +648,7 @@ class Job(UnifiedJob, JobOptions, SurveyJobMixin, JobNotificationMixin):
def get_notification_friendly_name(self):
return "Job"
class JobHostSummary(CreatedModifiedModel):
'''
Per-host statistics for each job.

View File

@@ -12,6 +12,7 @@ from awx.main.models.unified_jobs import UnifiedJobTemplate, UnifiedJob
__all__ = ('Label', )
class Label(CommonModelNameNotUnique):
'''
Generic Tag. Designed for tagging Job Templates, but expandable to other models.
@@ -37,7 +38,7 @@ class Label(CommonModelNameNotUnique):
return \
Label.objects.filter(
organization=None,
jobtemplate_labels__isnull=True
unifiedjobtemplate_labels__isnull=True
)
def is_detached(self):
@@ -55,4 +56,3 @@ class Label(CommonModelNameNotUnique):
return True
else:
return False

View File

@@ -5,17 +5,18 @@ import json
from django.db import models
from django.contrib.contenttypes.models import ContentType
from django.contrib.auth.models import User # noqa
from jsonfield import JSONField
# AWX
from awx.main.models.rbac import (
Role, RoleAncestorEntry, get_roles_on_resource
)
from awx.main.utils import parse_yaml_or_json
from awx.main.fields import JSONField
__all__ = ['ResourceMixin', 'SurveyJobTemplateMixin', 'SurveyJobMixin']
class ResourceMixin(models.Model):
class Meta:
@@ -216,4 +217,3 @@ class SurveyJobMixin(models.Model):
return json.dumps(extra_vars)
else:
return self.extra_vars

View File

@@ -18,14 +18,14 @@ from awx.main.notifications.pagerduty_backend import PagerDutyBackend
from awx.main.notifications.hipchat_backend import HipChatBackend
from awx.main.notifications.webhook_backend import WebhookBackend
from awx.main.notifications.irc_backend import IrcBackend
from awx.main.fields import JSONField
# Django-JSONField
from jsonfield import JSONField
logger = logging.getLogger('awx.main.models.notifications')
__all__ = ['NotificationTemplate', 'Notification']
class NotificationTemplate(CommonModel):
NOTIFICATION_TYPES = [('email', _('Email'), CustomEmailBackend),
@@ -117,6 +117,7 @@ class NotificationTemplate(CommonModel):
notification_obj = EmailMessage(subject, backend_obj.format_body(body), sender, recipients)
return backend_obj.send_messages([notification_obj])
class Notification(CreatedModifiedModel):
'''
A notification event emitted when a NotificationTemplate is run
@@ -172,6 +173,7 @@ class Notification(CreatedModifiedModel):
def get_absolute_url(self):
return reverse('api:notification_detail', args=(self.pk,))
class JobNotificationMixin(object):
def get_notification_templates(self):
raise RuntimeError("Define me")
@@ -194,4 +196,3 @@ class JobNotificationMixin(object):
def build_notification_failed_message(self):
return self._build_notification_message('failed')

View File

@@ -72,7 +72,6 @@ class Organization(CommonModel, NotificationFieldsModel, ResourceMixin):
return self.name
class Team(CommonModelNameNotUnique, ResourceMixin):
'''
A team is a group of users that work on common projects.
@@ -191,6 +190,7 @@ class Profile(CreatedModifiedModel):
default='',
)
"""
Since expiration and session expiration is event driven a token could be
invalidated for both reasons. Further, we only support a single reason for a
@@ -199,6 +199,8 @@ session token being invalid. For this case, mark the token as expired.
Note: Again, because the value of reason is event based. The reason may not be
set (i.e. may equal '') even though a session is expired or a limit is reached.
"""
class AuthToken(BaseModel):
'''
Custom authentication tokens per user with expiration and request-specific

View File

@@ -7,9 +7,6 @@ import os
import re
import urlparse
# JSONField
from jsonfield import JSONField
# Django
from django.conf import settings
from django.db import models
@@ -34,6 +31,7 @@ from awx.main.models.rbac import (
ROLE_SINGLETON_SYSTEM_ADMINISTRATOR,
ROLE_SINGLETON_SYSTEM_AUDITOR,
)
from awx.main.fields import JSONField
__all__ = ['Project', 'ProjectUpdate']
@@ -393,6 +391,7 @@ class Project(UnifiedJobTemplate, ProjectOptions, ResourceMixin):
def get_absolute_url(self):
return reverse('api:project_detail', args=(self.pk,))
class ProjectUpdate(UnifiedJob, ProjectOptions, JobNotificationMixin):
'''
Internal job for tracking project updates from SCM.

View File

@@ -79,6 +79,7 @@ def check_singleton(func):
return func(*args, **kwargs)
return wrapper
@contextlib.contextmanager
def batch_role_ancestor_rebuilding(allow_nesting=False):
'''
@@ -426,6 +427,7 @@ class Role(models.Model):
def is_ancestor_of(self, role):
return role.ancestors.filter(id=self.id).exists()
class RoleAncestorEntry(models.Model):
class Meta:

View File

@@ -11,14 +11,12 @@ from django.db import models
from django.db.models.query import QuerySet
from django.utils.timezone import now, make_aware, get_default_timezone
# Django-JSONField
from jsonfield import JSONField
# AWX
from awx.main.models.base import * # noqa
from awx.main.utils import ignore_inventory_computed_fields
from awx.main.consumers import emit_channel_notification
from django.core.urlresolvers import reverse
from awx.main.fields import JSONField
logger = logging.getLogger('awx.main.models.schedule')

View File

@@ -20,9 +20,6 @@ from django.utils.timezone import now
from django.utils.encoding import smart_text
from django.apps import apps
# Django-JSONField
from jsonfield import JSONField
# Django-Polymorphic
from polymorphic import PolymorphicModel
@@ -35,6 +32,7 @@ from awx.main.models.schedules import Schedule
from awx.main.utils import decrypt_field, _inventory_updates
from awx.main.redact import UriCleaner, REPLACE_STR
from awx.main.consumers import emit_channel_notification
from awx.main.fields import JSONField
__all__ = ['UnifiedJobTemplate', 'UnifiedJob']
@@ -358,6 +356,7 @@ class UnifiedJobTemplate(PolymorphicModel, CommonModelNameNotUnique, Notificatio
dest_field.add(*list(src_field_value.all().values_list('id', flat=True)))
return unified_job
class UnifiedJobTypeStringMixin(object):
@classmethod
def _underscore_to_camel(cls, word):
@@ -381,6 +380,7 @@ class UnifiedJobTypeStringMixin(object):
return None
return model.objects.get(id=job_id)
class UnifiedJob(PolymorphicModel, PasswordFieldsModel, CommonModelNameNotUnique, UnifiedJobTypeStringMixin):
'''
Concrete base class for unified job run by the task engine.

View File

@@ -9,8 +9,6 @@ from django.db import models
from django.core.urlresolvers import reverse
#from django import settings as tower_settings
from jsonfield import JSONField
# AWX
from awx.main.models import UnifiedJobTemplate, UnifiedJob
from awx.main.models.notifications import (
@@ -26,6 +24,7 @@ from awx.main.fields import ImplicitRoleField
from awx.main.models.mixins import ResourceMixin, SurveyJobTemplateMixin, SurveyJobMixin
from awx.main.redact import REPLACE_STR
from awx.main.utils import parse_yaml_or_json
from awx.main.fields import JSONField
from copy import copy
@@ -33,6 +32,7 @@ __all__ = ['WorkflowJobTemplate', 'WorkflowJob', 'WorkflowJobOptions', 'Workflow
CHAR_PROMPTS_LIST = ['job_type', 'job_tags', 'skip_tags', 'limit']
class WorkflowNodeBase(CreatedModifiedModel):
class Meta:
abstract = True
@@ -159,6 +159,7 @@ class WorkflowNodeBase(CreatedModifiedModel):
return ['workflow_job', 'unified_job_template',
'inventory', 'credential', 'char_prompts']
class WorkflowJobTemplateNode(WorkflowNodeBase):
workflow_job_template = models.ForeignKey(
'WorkflowJobTemplate',
@@ -184,6 +185,7 @@ class WorkflowJobTemplateNode(WorkflowNodeBase):
create_kwargs[field_name] = getattr(self, field_name)
return WorkflowJobNode.objects.create(**create_kwargs)
class WorkflowJobNode(WorkflowNodeBase):
job = models.OneToOneField(
'UnifiedJob',
@@ -206,7 +208,7 @@ class WorkflowJobNode(WorkflowNodeBase):
default={},
editable=False,
)
def get_absolute_url(self):
return reverse('api:workflow_job_node_detail', args=(self.pk,))
@@ -260,6 +262,7 @@ class WorkflowJobNode(WorkflowNodeBase):
data['launch_type'] = 'workflow'
return data
class WorkflowJobOptions(BaseModel):
class Meta:
abstract = True
@@ -271,8 +274,8 @@ class WorkflowJobOptions(BaseModel):
extra_vars_dict = VarsDictProperty('extra_vars', True)
class WorkflowJobTemplate(UnifiedJobTemplate, WorkflowJobOptions, SurveyJobTemplateMixin, ResourceMixin):
class WorkflowJobTemplate(UnifiedJobTemplate, WorkflowJobOptions, SurveyJobTemplateMixin, ResourceMixin):
class Meta:
app_label = 'main'
@@ -374,6 +377,7 @@ class WorkflowJobTemplate(UnifiedJobTemplate, WorkflowJobOptions, SurveyJobTempl
warning_data[node.pk] = node_prompts_warnings
return warning_data
class WorkflowJobInheritNodesMixin(object):
def _inherit_relationship(self, old_node, new_node, node_ids_map, node_type):
old_related_nodes = self._get_all_by_type(old_node, node_type)
@@ -415,10 +419,9 @@ class WorkflowJobInheritNodesMixin(object):
new_node = new_nodes[index]
for node_type in ['success_nodes', 'failure_nodes', 'always_nodes']:
self._inherit_relationship(old_node, new_node, node_ids_map, node_type)
class WorkflowJob(UnifiedJob, WorkflowJobOptions, SurveyJobMixin, JobNotificationMixin, WorkflowJobInheritNodesMixin):
class Meta:
app_label = 'main'
ordering = ('id',)