split Node in job and job template node

This commit is contained in:
Chris Meyers
2016-09-12 11:04:20 -04:00
parent e4025a7eff
commit cea5ebadb7
9 changed files with 232 additions and 111 deletions

View File

@@ -2228,31 +2228,51 @@ class WorkflowJobTemplateListSerializer(UnifiedJobTemplateSerializer):
class WorkflowJobTemplateSerializer(WorkflowJobTemplateListSerializer): class WorkflowJobTemplateSerializer(WorkflowJobTemplateListSerializer):
pass pass
class WorkflowNodeSerializer(BaseSerializer): class WorkflowNodeBaseSerializer(BaseSerializer):
#workflow_job_template = UnifiedJobTemplateSerializer() #workflow_job_template = UnifiedJobTemplateSerializer()
class Meta: class Meta:
model = WorkflowNode
# TODO: workflow_job and job read-only # TODO: workflow_job and job read-only
fields = ('id', 'url', 'related', 'workflow_job_template', 'unified_job_template', 'success_nodes', 'failure_nodes', 'always_nodes', 'job',) fields = ('id', 'url', 'related', 'success_nodes', 'failure_nodes', 'always_nodes',)
class WorkflowJobTemplateNodeSerializer(WorkflowNodeBaseSerializer):
class Meta:
model = WorkflowJobTemplateNode
fields = ('*', 'workflow_job_template', 'unified_job_template',)
def get_related(self, obj): def get_related(self, obj):
res = super(WorkflowNodeSerializer, self).get_related(obj) res = super(WorkflowJobTemplateNodeSerializer, self).get_related(obj)
res['success_nodes'] = reverse('api:workflow_job_template_node_success_nodes_list', args=(obj.pk,))
res['failure_nodes'] = reverse('api:workflow_job_template_node_failure_nodes_list', args=(obj.pk,))
res['always_nodes'] = reverse('api:workflow_job_template_node_always_nodes_list', args=(obj.pk,))
if obj.workflow_job_template: if obj.workflow_job_template:
res['workflow_job_template'] = reverse('api:workflow_job_template_detail', args=(obj.workflow_job_template.pk,)) res['workflow_job_template'] = reverse('api:workflow_job_template_detail', args=(obj.workflow_job_template.pk,))
if obj.unified_job_template: if obj.unified_job_template:
res['unified_job_template'] = obj.unified_job_template.get_absolute_url() res['unified_job_template'] = obj.unified_job_template.get_absolute_url()
return res
class WorkflowJobNodeSerializer(WorkflowNodeBaseSerializer):
class Meta:
model = WorkflowJobTemplateNode
fields = ('*', 'workflow_job_template', 'unified_job_template', 'job', 'workflow_job',)
def get_related(self, obj):
res = super(WorkflowJobNodeSerializer, self).get_related(obj)
res['success_nodes'] = reverse('api:workflow_job_node_success_nodes_list', args=(obj.pk,))
res['failure_nodes'] = reverse('api:workflow_job_node_failure_nodes_list', args=(obj.pk,))
res['always_nodes'] = reverse('api:workflow_job_node_always_nodes_list', args=(obj.pk,))
if obj.workflow_job_template:
res['workflow_job_template'] = reverse('api:workflow_job_template_detail', args=(obj.workflow_job_template.pk,))
if obj.job: if obj.job:
res['job'] = reverse('api:job_detail', args=(obj.job.pk,)) res['job'] = reverse('api:job_detail', args=(obj.job.pk,))
if obj.workflow_job: if obj.workflow_job:
res['workflow_job'] = reverse('api:workflow_job_detail', args=(obj.workflow_job.pk,)) res['workflow_job'] = reverse('api:workflow_job_detail', args=(obj.workflow_job.pk,))
res['success_nodes'] = reverse('api:workflow_node_success_nodes_list', args=(obj.pk,))
res['failure_nodes'] = reverse('api:workflow_node_failure_nodes_list', args=(obj.pk,))
res['always_nodes'] = reverse('api:workflow_node_always_nodes_list', args=(obj.pk,))
return res return res
class WorkflowNodeDetailSerializer(WorkflowNodeSerializer): class WorkflowJobNodeListSerializer(WorkflowJobNodeSerializer):
pass
class WorkflowJobTemplateNodeDetailSerializer(WorkflowJobTemplateNodeSerializer):
''' '''
Influence the api browser sample data to not include workflow_job_template Influence the api browser sample data to not include workflow_job_template
@@ -2262,14 +2282,13 @@ class WorkflowNodeDetailSerializer(WorkflowNodeSerializer):
Maybe something to do with workflow_job_template being a relational field? Maybe something to do with workflow_job_template being a relational field?
''' '''
def build_relational_field(self, field_name, relation_info): def build_relational_field(self, field_name, relation_info):
field_class, field_kwargs = super(WorkflowNodeDetailSerializer, self).build_relational_field(field_name, relation_info) field_class, field_kwargs = super(WorkflowJobTemplateNodeDetailSerializer, self).build_relational_field(field_name, relation_info)
if self.instance and field_name == 'workflow_job_template': if self.instance and field_name == 'workflow_job_template':
field_kwargs['read_only'] = True field_kwargs['read_only'] = True
field_kwargs.pop('queryset', None) field_kwargs.pop('queryset', None)
return field_class, field_kwargs return field_class, field_kwargs
class WorkflowJobTemplateNodeListSerializer(WorkflowJobTemplateNodeSerializer):
class WorkflowNodeListSerializer(WorkflowNodeSerializer):
pass pass
class JobListSerializer(JobSerializer, UnifiedJobListSerializer): class JobListSerializer(JobSerializer, UnifiedJobListSerializer):

View File

@@ -262,8 +262,8 @@ workflow_job_template_urls = patterns('awx.api.views',
url(r'^(?P<pk>[0-9]+)/launch/$', 'workflow_job_template_launch'), url(r'^(?P<pk>[0-9]+)/launch/$', 'workflow_job_template_launch'),
url(r'^(?P<pk>[0-9]+)/workflow_nodes/$', 'workflow_job_template_workflow_nodes_list'), url(r'^(?P<pk>[0-9]+)/workflow_nodes/$', 'workflow_job_template_workflow_nodes_list'),
# url(r'^(?P<pk>[0-9]+)/cancel/$', 'workflow_job_template_cancel'), # url(r'^(?P<pk>[0-9]+)/cancel/$', 'workflow_job_template_cancel'),
#url(r'^(?P<pk>[0-9]+)/nodes/$', 'workflow_job_template_node_list'),
) )
workflow_job_urls = patterns('awx.api.views', workflow_job_urls = patterns('awx.api.views',
url(r'^$', 'workflow_job_list'), url(r'^$', 'workflow_job_list'),
url(r'^(?P<pk>[0-9]+)/$', 'workflow_job_detail'), url(r'^(?P<pk>[0-9]+)/$', 'workflow_job_detail'),
@@ -290,12 +290,20 @@ label_urls = patterns('awx.api.views',
url(r'^(?P<pk>[0-9]+)/$', 'label_detail'), url(r'^(?P<pk>[0-9]+)/$', 'label_detail'),
) )
workflow_node_urls = patterns('awx.api.views', workflow_job_template_node_urls = patterns('awx.api.views',
url(r'^$', 'workflow_node_list'), url(r'^$', 'workflow_job_template_node_list'),
url(r'^(?P<pk>[0-9]+)/$', 'workflow_node_detail'), url(r'^(?P<pk>[0-9]+)/$', 'workflow_job_template_node_detail'),
url(r'^(?P<pk>[0-9]+)/success_nodes/$', 'workflow_node_success_nodes_list'), url(r'^(?P<pk>[0-9]+)/success_nodes/$', 'workflow_job_template_node_success_nodes_list'),
url(r'^(?P<pk>[0-9]+)/failure_nodes/$', 'workflow_node_failure_nodes_list'), url(r'^(?P<pk>[0-9]+)/failure_nodes/$', 'workflow_job_template_node_failure_nodes_list'),
url(r'^(?P<pk>[0-9]+)/always_nodes/$', 'workflow_node_always_nodes_list'), url(r'^(?P<pk>[0-9]+)/always_nodes/$', 'workflow_job_template_node_always_nodes_list'),
)
workflow_job_node_urls = patterns('awx.api.views',
url(r'^$', 'workflow_job_node_list'),
url(r'^(?P<pk>[0-9]+)/$', 'workflow_job_node_detail'),
url(r'^(?P<pk>[0-9]+)/success_nodes/$', 'workflow_job_node_success_nodes_list'),
url(r'^(?P<pk>[0-9]+)/failure_nodes/$', 'workflow_job_node_failure_nodes_list'),
url(r'^(?P<pk>[0-9]+)/always_nodes/$', 'workflow_job_node_always_nodes_list'),
) )
schedule_urls = patterns('awx.api.views', schedule_urls = patterns('awx.api.views',
@@ -350,7 +358,8 @@ v1_urls = patterns('awx.api.views',
url(r'^workflow_job_templates/',include(workflow_job_template_urls)), url(r'^workflow_job_templates/',include(workflow_job_template_urls)),
url(r'^workflow_jobs/' ,include(workflow_job_urls)), url(r'^workflow_jobs/' ,include(workflow_job_urls)),
url(r'^labels/', include(label_urls)), url(r'^labels/', include(label_urls)),
url(r'^workflow_nodes/', include(workflow_node_urls)), url(r'^workflow_job_template_nodes/', include(workflow_job_template_node_urls)),
#url(r'^workflow_job_nodes/', include(workflow_job_node_urls)),
url(r'^unified_job_templates/$','unified_job_template_list'), url(r'^unified_job_templates/$','unified_job_template_list'),
url(r'^unified_jobs/$', 'unified_job_list'), url(r'^unified_jobs/$', 'unified_job_list'),
url(r'^activity_stream/', include(activity_stream_urls)), url(r'^activity_stream/', include(activity_stream_urls)),

View File

@@ -2615,31 +2615,31 @@ class JobTemplateObjectRolesList(SubListAPIView):
return Role.objects.filter(content_type=content_type, object_id=po.pk) return Role.objects.filter(content_type=content_type, object_id=po.pk)
# TODO: # TODO:
class WorkflowNodeList(ListCreateAPIView): class WorkflowJobTemplateNodeList(ListCreateAPIView):
model = WorkflowNode model = WorkflowJobTemplateNode
serializer_class = WorkflowNodeSerializer serializer_class = WorkflowJobTemplateNodeListSerializer
new_in_310 = True new_in_310 = True
# TODO: # TODO:
class WorkflowNodeDetail(RetrieveUpdateDestroyAPIView): class WorkflowJobTemplateNodeDetail(RetrieveUpdateDestroyAPIView):
model = WorkflowNode model = WorkflowJobTemplateNode
serializer_class = WorkflowNodeDetailSerializer serializer_class = WorkflowJobTemplateNodeDetailSerializer
new_in_310 = True new_in_310 = True
class WorkflowNodeChildrenBaseList(EnforceParentRelationshipMixin, SubListCreateAttachDetachAPIView): class WorkflowJobTemplateNodeChildrenBaseList(EnforceParentRelationshipMixin, SubListCreateAttachDetachAPIView):
model = WorkflowNode model = WorkflowJobTemplateNode
serializer_class = WorkflowNodeListSerializer serializer_class = WorkflowJobTemplateNodeListSerializer
always_allow_superuser = True # TODO: RBAC always_allow_superuser = True # TODO: RBAC
parent_model = WorkflowNode parent_model = WorkflowJobTemplateNode
relationship = '' relationship = ''
enforce_parent_relationship = 'workflow_job_template' enforce_parent_relationship = 'workflow_job_template'
new_in_310 = True new_in_310 = True
''' '''
Limit the set of WorkflowNodes to the related nodes of specified by Limit the set of WorkflowJobTemplateNodes to the related nodes of specified by
'relationship' 'relationship'
''' '''
def get_queryset(self): def get_queryset(self):
@@ -2647,18 +2647,46 @@ class WorkflowNodeChildrenBaseList(EnforceParentRelationshipMixin, SubListCreate
self.check_parent_access(parent) self.check_parent_access(parent)
return getattr(parent, self.relationship).all() return getattr(parent, self.relationship).all()
class WorkflowNodeSuccessNodesList(WorkflowNodeChildrenBaseList): class WorkflowJobTemplateNodeSuccessNodesList(WorkflowJobTemplateNodeChildrenBaseList):
relationship = 'success_nodes' relationship = 'success_nodes'
class WorkflowNodeFailureNodesList(WorkflowNodeChildrenBaseList): class WorkflowJobTemplateNodeFailureNodesList(WorkflowJobTemplateNodeChildrenBaseList):
relationship = 'failure_nodes' relationship = 'failure_nodes'
class WorkflowNodeAlwaysNodesList(WorkflowNodeChildrenBaseList): class WorkflowJobTemplateNodeAlwaysNodesList(WorkflowJobTemplateNodeChildrenBaseList):
relationship = 'always_nodes' relationship = 'always_nodes'
'''
class WorkflowJobNodeChildrenBaseList(EnforceParentRelationshipMixin, SubListCreateAttachDetachAPIView):
model = WorkflowJobNode
serializer_class = WorkflowJobNodeListSerializer
always_allow_superuser = True # TODO: RBAC
parent_model = WorkflowJobTemplateNode
relationship = ''
enforce_parent_relationship = 'workflow_job_template'
new_in_310 = True
#
#Limit the set of WorkflowJobTemplateNodes to the related nodes of specified by
#'relationship'
#
def get_queryset(self):
parent = self.get_parent_object()
self.check_parent_access(parent)
return getattr(parent, self.relationship).all()
class WorkflowJobNodeSuccessNodesList(WorkflowJobNodeChildrenBaseList):
relationship = 'success_nodes'
class WorkflowJobNodeFailureNodesList(WorkflowJobNodeChildrenBaseList):
relationship = 'failure_nodes'
class WorkflowJobNodeAlwaysNodesList(WorkflowJobNodeChildrenBaseList):
relationship = 'always_nodes'
'''
# TODO: # TODO:
class WorkflowJobTemplateList(ListCreateAPIView): class WorkflowJobTemplateList(ListCreateAPIView):
@@ -2705,11 +2733,11 @@ class WorkflowJobTemplateLaunch(GenericAPIView):
# TODO: # TODO:
class WorkflowJobTemplateWorkflowNodesList(SubListCreateAPIView): class WorkflowJobTemplateWorkflowNodesList(SubListCreateAPIView):
model = WorkflowNode model = WorkflowJobTemplateNode
serializer_class = WorkflowNodeListSerializer serializer_class = WorkflowJobTemplateNodeListSerializer
always_allow_superuser = True # TODO: RBAC always_allow_superuser = True # TODO: RBAC
parent_model = WorkflowJobTemplate parent_model = WorkflowJobTemplate
relationship = 'workflow_nodes' relationship = 'workflow_job_template_nodes'
parent_key = 'workflow_job_template' parent_key = 'workflow_job_template'
# TODO: # TODO:
@@ -2740,14 +2768,13 @@ class WorkflowJobDetail(RetrieveDestroyAPIView):
class WorkflowJobWorkflowNodesList(SubListAPIView): class WorkflowJobWorkflowNodesList(SubListAPIView):
model = WorkflowNode model = WorkflowJobNode
serializer_class = WorkflowNodeListSerializer serializer_class = WorkflowJobNodeListSerializer
always_allow_superuser = True # TODO: RBAC always_allow_superuser = True # TODO: RBAC
parent_model = WorkflowJob parent_model = WorkflowJob
relationship = 'workflow_job_nodes' relationship = 'workflow_job_nodes'
parent_key = 'workflow_job' parent_key = 'workflow_job'
class SystemJobTemplateList(ListAPIView): class SystemJobTemplateList(ListAPIView):
model = SystemJobTemplate model = SystemJobTemplate

View File

@@ -1133,11 +1133,43 @@ class SystemJobAccess(BaseAccess):
model = SystemJob model = SystemJob
# TODO: # TODO:
class WorkflowNodeAccess(BaseAccess): class WorkflowJobTemplateNodeAccess(BaseAccess):
''' '''
I can see/use a WorkflowNode if I have permission to associated Workflow Job Template I can see/use a WorkflowJobTemplateNode if I have permission to associated Workflow Job Template
''' '''
model = WorkflowNode model = WorkflowJobTemplateNode
def get_queryset(self):
if self.user.is_superuser or self.user.is_system_auditor:
return self.model.objects.all()
@check_superuser
def can_read(self, obj):
return True
@check_superuser
def can_add(self, data):
if not data: # So the browseable API will work
return True
return True
@check_superuser
def can_change(self, obj, data):
if self.can_add(data) is False:
return False
return True
def can_delete(self, obj):
return self.can_change(obj, None)
# TODO:
class WorkflowJobNodeAccess(BaseAccess):
'''
I can see/use a WorkflowJobNode if I have permission to associated Workflow Job
'''
model = WorkflowJobNode
def get_queryset(self): def get_queryset(self):
if self.user.is_superuser or self.user.is_system_auditor: if self.user.is_superuser or self.user.is_system_auditor:
@@ -1863,6 +1895,7 @@ register_access(Role, RoleAccess)
register_access(NotificationTemplate, NotificationTemplateAccess) register_access(NotificationTemplate, NotificationTemplateAccess)
register_access(Notification, NotificationAccess) register_access(Notification, NotificationAccess)
register_access(Label, LabelAccess) register_access(Label, LabelAccess)
register_access(WorkflowNode, WorkflowNodeAccess) register_access(WorkflowJobTemplateNode, WorkflowJobTemplateNodeAccess)
register_access(WorkflowJobNode, WorkflowJobNodeAccess)
register_access(WorkflowJobTemplate, WorkflowJobTemplateAccess) register_access(WorkflowJobTemplate, WorkflowJobTemplateAccess)
register_access(WorkflowJob, WorkflowJobAccess) register_access(WorkflowJob, WorkflowJobAccess)

View File

@@ -26,6 +26,21 @@ class Migration(migrations.Migration):
}, },
bases=('main.unifiedjob', models.Model, awx.main.models.notifications.JobNotificationMixin, awx.main.models.workflow.WorkflowJobInheritNodesMixin), bases=('main.unifiedjob', models.Model, awx.main.models.notifications.JobNotificationMixin, awx.main.models.workflow.WorkflowJobInheritNodesMixin),
), ),
migrations.CreateModel(
name='WorkflowJobNode',
fields=[
('id', models.AutoField(verbose_name='ID', serialize=False, auto_created=True, primary_key=True)),
('created', models.DateTimeField(default=None, editable=False)),
('modified', models.DateTimeField(default=None, editable=False)),
('always_nodes', models.ManyToManyField(related_name='workflowjobnodes_always', to='main.WorkflowJobNode', blank=True)),
('failure_nodes', models.ManyToManyField(related_name='workflowjobnodes_failure', to='main.WorkflowJobNode', blank=True)),
('job', models.ForeignKey(related_name='unified_job_nodes', on_delete=django.db.models.deletion.SET_NULL, default=None, blank=True, to='main.UnifiedJob', null=True)),
('success_nodes', models.ManyToManyField(related_name='workflowjobnodes_success', to='main.WorkflowJobNode', blank=True)),
],
options={
'abstract': False,
},
),
migrations.CreateModel( migrations.CreateModel(
name='WorkflowJobTemplate', name='WorkflowJobTemplate',
fields=[ fields=[
@@ -36,19 +51,30 @@ class Migration(migrations.Migration):
bases=('main.unifiedjobtemplate', models.Model), bases=('main.unifiedjobtemplate', models.Model),
), ),
migrations.CreateModel( migrations.CreateModel(
name='WorkflowNode', name='WorkflowJobTemplateNode',
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)),
('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)),
('always_nodes', models.ManyToManyField(related_name='parent_always_nodes', to='main.WorkflowNode', blank=True)), ('always_nodes', models.ManyToManyField(related_name='workflowjobtemplatenodes_always', to='main.WorkflowJobTemplateNode', blank=True)),
('failure_nodes', models.ManyToManyField(related_name='parent_failure_nodes', to='main.WorkflowNode', blank=True)), ('failure_nodes', models.ManyToManyField(related_name='workflowjobtemplatenodes_failure', to='main.WorkflowJobTemplateNode', blank=True)),
('job', models.ForeignKey(related_name='unified_job_nodes', on_delete=django.db.models.deletion.SET_NULL, default=None, blank=True, to='main.UnifiedJob', null=True)), ('success_nodes', models.ManyToManyField(related_name='workflowjobtemplatenodes_success', to='main.WorkflowJobTemplateNode', blank=True)),
('success_nodes', models.ManyToManyField(related_name='parent_success_nodes', to='main.WorkflowNode', 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='unified_jt_workflow_nodes', 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', models.ForeignKey(related_name='workflow_job_nodes', on_delete=django.db.models.deletion.SET_NULL, default=None, blank=True, to='main.WorkflowJob', null=True)),
('workflow_job_template', models.ForeignKey(related_name='workflow_nodes', default=None, blank=True, to='main.WorkflowJobTemplate', null=True)),
], ],
options={
'abstract': False,
},
),
migrations.AddField(
model_name='workflowjobnode',
name='unified_job_template',
field=models.ForeignKey(related_name='workflowjobnodes', on_delete=django.db.models.deletion.SET_NULL, default=None, blank=True, to='main.UnifiedJobTemplate', null=True),
),
migrations.AddField(
model_name='workflowjobnode',
name='workflow_job',
field=models.ForeignKey(related_name='workflow_job_nodes', on_delete=django.db.models.deletion.SET_NULL, default=None, blank=True, to='main.WorkflowJob', null=True),
), ),
migrations.AddField( migrations.AddField(
model_name='workflowjob', model_name='workflowjob',
@@ -60,6 +86,11 @@ class Migration(migrations.Migration):
name='workflow_job', name='workflow_job',
field=models.ManyToManyField(to='main.WorkflowJob', blank=True), field=models.ManyToManyField(to='main.WorkflowJob', blank=True),
), ),
migrations.AddField(
model_name='activitystream',
name='workflow_job_node',
field=models.ManyToManyField(to='main.WorkflowJobNode', blank=True),
),
migrations.AddField( migrations.AddField(
model_name='activitystream', model_name='activitystream',
name='workflow_job_template', name='workflow_job_template',
@@ -67,7 +98,7 @@ class Migration(migrations.Migration):
), ),
migrations.AddField( migrations.AddField(
model_name='activitystream', model_name='activitystream',
name='workflow_node', name='workflow_job_template_node',
field=models.ManyToManyField(to='main.WorkflowNode', blank=True), field=models.ManyToManyField(to='main.WorkflowJobTemplateNode', blank=True),
), ),
] ]

View File

@@ -49,7 +49,8 @@ class ActivityStream(models.Model):
permission = models.ManyToManyField("Permission", blank=True) permission = models.ManyToManyField("Permission", blank=True)
job_template = models.ManyToManyField("JobTemplate", blank=True) job_template = models.ManyToManyField("JobTemplate", blank=True)
job = models.ManyToManyField("Job", blank=True) job = models.ManyToManyField("Job", blank=True)
workflow_node = models.ManyToManyField("WorkflowNode", blank=True) workflow_job_template_node = models.ManyToManyField("WorkflowJobTemplateNode", blank=True)
workflow_job_node = models.ManyToManyField("WorkflowJobNode", blank=True)
workflow_job_template = models.ManyToManyField("WorkflowJobTemplate", blank=True) workflow_job_template = models.ManyToManyField("WorkflowJobTemplate", blank=True)
workflow_job = models.ManyToManyField("WorkflowJob", blank=True) workflow_job = models.ManyToManyField("WorkflowJob", blank=True)
unified_job_template = models.ManyToManyField("UnifiedJobTemplate", blank=True, related_name='activity_stream_as_unified_job_template+') unified_job_template = models.ManyToManyField("UnifiedJobTemplate", blank=True, related_name='activity_stream_as_unified_job_template+')

View File

@@ -18,11 +18,11 @@ from awx.main.models.rbac import (
) )
from awx.main.fields import ImplicitRoleField from awx.main.fields import ImplicitRoleField
__all__ = ['WorkflowJobTemplate', 'WorkflowJob', 'WorkflowJobOptions', 'WorkflowNode'] __all__ = ['WorkflowJobTemplate', 'WorkflowJob', 'WorkflowJobOptions', 'WorkflowJobNode', 'WorkflowJobTemplateNode',]
class WorkflowNode(CreatedModifiedModel):
class WorkflowNodeBase(CreatedModifiedModel):
class Meta: class Meta:
abstract = True
app_label = 'main' app_label = 'main'
# TODO: RBAC # TODO: RBAC
@@ -31,41 +31,55 @@ class WorkflowNode(CreatedModifiedModel):
parent_role='workflow_job_template.admin_role', parent_role='workflow_job_template.admin_role',
) )
''' '''
success_nodes = models.ManyToManyField(
# TODO: Ensure the API forces workflow_job_template being set 'self',
workflow_job_template = models.ForeignKey(
'WorkflowJobTemplate',
related_name='workflow_nodes',
blank=True, blank=True,
null=True, symmetrical=False,
default=None, related_name='%(class)ss_success',
on_delete=models.CASCADE, )
failure_nodes = models.ManyToManyField(
'self',
blank=True,
symmetrical=False,
related_name='%(class)ss_failure',
)
always_nodes = models.ManyToManyField(
'self',
blank=True,
symmetrical=False,
related_name='%(class)ss_always',
) )
unified_job_template = models.ForeignKey( unified_job_template = models.ForeignKey(
'UnifiedJobTemplate', 'UnifiedJobTemplate',
related_name='unified_jt_workflow_nodes', related_name='%(class)ss',
blank=True, blank=True,
null=True, null=True,
default=None, default=None,
on_delete=models.SET_NULL, on_delete=models.SET_NULL,
) )
success_nodes = models.ManyToManyField(
'self', class WorkflowJobTemplateNode(WorkflowNodeBase):
related_name='parent_success_nodes', # TODO: Ensure the API forces workflow_job_template being set
workflow_job_template = models.ForeignKey(
'WorkflowJobTemplate',
related_name='workflow_job_template_nodes',
blank=True, blank=True,
symmetrical=False, null=True,
default=None,
on_delete=models.CASCADE,
) )
failure_nodes = models.ManyToManyField(
'self', def get_absolute_url(self):
related_name='parent_failure_nodes', return reverse('api:workflow_job_template_node_detail', args=(self.pk,))
class WorkflowJobNode(WorkflowNodeBase):
job = models.ForeignKey(
'UnifiedJob',
related_name='unified_job_nodes',
blank=True, blank=True,
symmetrical=False, null=True,
) default=None,
always_nodes = models.ManyToManyField( on_delete=models.SET_NULL,
'self',
related_name='parent_always_nodes',
blank=True,
symmetrical=False,
) )
workflow_job = models.ForeignKey( workflow_job = models.ForeignKey(
'WorkflowJob', 'WorkflowJob',
@@ -75,17 +89,9 @@ class WorkflowNode(CreatedModifiedModel):
default=None, default=None,
on_delete=models.SET_NULL, on_delete=models.SET_NULL,
) )
job = models.ForeignKey(
'UnifiedJob',
related_name='unified_job_nodes',
blank=True,
null=True,
default=None,
on_delete=models.SET_NULL,
)
def get_absolute_url(self): def get_absolute_url(self):
return reverse('api:workflow_node_detail', args=(self.pk,)) return reverse('api:workflow_job_node_detail', args=(self.pk,))
class WorkflowJobOptions(BaseModel): class WorkflowJobOptions(BaseModel):
class Meta: class Meta:
@@ -147,22 +153,17 @@ class WorkflowJobInheritNodesMixin(object):
for old_related_node in old_related_nodes: for old_related_node in old_related_nodes:
new_related_node_id = node_ids_map[old_related_node.id] new_related_node_id = node_ids_map[old_related_node.id]
new_related_node = WorkflowNode.objects.get(id=new_related_node_id) new_related_node = WorkflowJobNode.objects.get(id=new_related_node_id)
new_node_type_mgr.add(new_related_node) new_node_type_mgr.add(new_related_node)
def inherit_jt_workflow_nodes(self): def inherit_jt_workflow_nodes(self):
new_nodes = [] new_nodes = []
old_nodes = self.workflow_job_template.workflow_nodes.all() old_nodes = self.workflow_job_template.workflow_job_template_nodes.all()
node_ids_map = {} node_ids_map = {}
for old_node in old_nodes: for old_node in old_nodes:
new_node = WorkflowNode.objects.get(id=old_node.pk) new_node = WorkflowJobNode.objects.create(workflow_job=self, unified_job_template=old_node.unified_job_template)
new_node.workflow_job = self
new_node.job = None
new_node.workflow_job_template = None
new_node.pk = None
new_node.save()
new_nodes.append(new_node) new_nodes.append(new_node)
node_ids_map[old_node.id] = new_node.id node_ids_map[old_node.id] = new_node.id

16
awx/main/tests/manual/workflows/linear.py Normal file → Executable file
View File

@@ -1,6 +1,6 @@
# AWX # AWX
from awx.main.models import ( from awx.main.models import (
WorkflowNode, WorkflowJobTemplateNode,
WorkflowJobTemplate, WorkflowJobTemplate,
) )
from awx.main.models.jobs import JobTemplate from awx.main.models.jobs import JobTemplate
@@ -10,16 +10,16 @@ def do_init_workflow(job_template_success, job_template_fail, job_template_never
wfjt.delete() wfjt.delete()
wfjt, created = WorkflowJobTemplate.objects.get_or_create(name="linear workflow") wfjt, created = WorkflowJobTemplate.objects.get_or_create(name="linear workflow")
print(wfjt.id) print(wfjt.id)
WorkflowNode.objects.all().delete() WorkflowJobTemplateNode.objects.all().delete()
if created: if created:
nodes_success = [] nodes_success = []
nodes_fail = [] nodes_fail = []
nodes_never = [] nodes_never = []
for i in range(0, 2): for i in range(0, 2):
nodes_success.append(WorkflowNode.objects.create(workflow_job_template=wfjt, unified_job_template=job_template_success)) nodes_success.append(WorkflowJobTemplateNode.objects.create(workflow_job_template=wfjt, unified_job_template=job_template_success))
nodes_fail.append(WorkflowNode.objects.create(workflow_job_template=wfjt, unified_job_template=job_template_fail)) nodes_fail.append(WorkflowJobTemplateNode.objects.create(workflow_job_template=wfjt, unified_job_template=job_template_fail))
nodes_never.append(WorkflowNode.objects.create(workflow_job_template=wfjt, unified_job_template=job_template_never)) nodes_never.append(WorkflowJobTemplateNode.objects.create(workflow_job_template=wfjt, unified_job_template=job_template_never))
nodes_never.append(WorkflowNode.objects.create(workflow_job_template=wfjt, unified_job_template=job_template_never)) nodes_never.append(WorkflowJobTemplateNode.objects.create(workflow_job_template=wfjt, unified_job_template=job_template_never))
nodes_fail[1].delete() nodes_fail[1].delete()
nodes_success[0].success_nodes.add(nodes_fail[0]) nodes_success[0].success_nodes.add(nodes_fail[0])
@@ -32,8 +32,8 @@ def do_init_workflow(job_template_success, job_template_fail, job_template_never
def do_init(): def do_init():
jt_success = JobTemplate.objects.get(id=5) jt_success = JobTemplate.objects.get(id=5)
jt_fail= JobTemplate.objects.get(id=9) jt_fail= JobTemplate.objects.get(id=6)
jt_never= JobTemplate.objects.get(id=11) jt_never= JobTemplate.objects.get(id=7)
do_init_workflow(jt_success, jt_fail, jt_never) do_init_workflow(jt_success, jt_fail, jt_never)
if __name__ == "__main__": if __name__ == "__main__":

4
awx/main/tests/manual/workflows/parallel.py Normal file → Executable file
View File

@@ -32,8 +32,8 @@ def do_init_workflow(job_template_success, job_template_fail, job_template_never
def do_init(): def do_init():
jt_success = JobTemplate.objects.get(id=5) jt_success = JobTemplate.objects.get(id=5)
jt_fail= JobTemplate.objects.get(id=9) jt_fail= JobTemplate.objects.get(id=6)
jt_never= JobTemplate.objects.get(id=11) jt_never= JobTemplate.objects.get(id=7)
jt_parallel = [] jt_parallel = []
jt_parallel.append(JobTemplate.objects.get(id=16)) jt_parallel.append(JobTemplate.objects.get(id=16))