mirror of
https://github.com/ansible/awx.git
synced 2026-03-21 19:07:39 -02:30
split Node in job and job template node
This commit is contained in:
@@ -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):
|
||||||
|
|||||||
@@ -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)),
|
||||||
|
|||||||
@@ -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
|
||||||
|
|||||||
@@ -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)
|
||||||
|
|||||||
@@ -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),
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|||||||
@@ -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+')
|
||||||
|
|||||||
@@ -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
16
awx/main/tests/manual/workflows/linear.py
Normal file → Executable 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
4
awx/main/tests/manual/workflows/parallel.py
Normal file → Executable 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))
|
||||||
|
|||||||
Reference in New Issue
Block a user