mirror of
https://github.com/ansible/awx.git
synced 2026-05-08 09:57:35 -02:30
Refactor for better performance.
This commit is contained in:
@@ -370,7 +370,7 @@ class SubListCreateAttachDetachAPIView(SubListCreateAPIView):
|
|||||||
# Base class for a sublist view that allows for creating subobjects and
|
# Base class for a sublist view that allows for creating subobjects and
|
||||||
# attaching/detaching them from the parent.
|
# attaching/detaching them from the parent.
|
||||||
|
|
||||||
def is_valid_relation(self, parent, sub):
|
def is_valid_relation(self, parent, sub, created=False):
|
||||||
return None
|
return None
|
||||||
|
|
||||||
def get_description_context(self):
|
def get_description_context(self):
|
||||||
@@ -410,7 +410,7 @@ class SubListCreateAttachDetachAPIView(SubListCreateAPIView):
|
|||||||
raise PermissionDenied()
|
raise PermissionDenied()
|
||||||
|
|
||||||
# Verify that the relationship to be added is valid.
|
# Verify that the relationship to be added is valid.
|
||||||
attach_errors = self.is_valid_relation(parent, sub)
|
attach_errors = self.is_valid_relation(parent, sub, created=created)
|
||||||
if attach_errors is not None:
|
if attach_errors is not None:
|
||||||
if created:
|
if created:
|
||||||
sub.delete()
|
sub.delete()
|
||||||
|
|||||||
@@ -69,7 +69,6 @@ from awx.api.renderers import * # noqa
|
|||||||
from awx.api.serializers import * # noqa
|
from awx.api.serializers import * # noqa
|
||||||
from awx.api.metadata import RoleMetadata
|
from awx.api.metadata import RoleMetadata
|
||||||
from awx.main.consumers import emit_channel_notification
|
from awx.main.consumers import emit_channel_notification
|
||||||
from awx.main.scheduler.dag_simple import SimpleDAG
|
|
||||||
|
|
||||||
logger = logging.getLogger('awx.api.views')
|
logger = logging.getLogger('awx.api.views')
|
||||||
|
|
||||||
@@ -2657,31 +2656,36 @@ class WorkflowJobTemplateNodeChildrenBaseList(EnforceParentRelationshipMixin, Su
|
|||||||
self.check_parent_access(parent)
|
self.check_parent_access(parent)
|
||||||
return getattr(parent, self.relationship).all()
|
return getattr(parent, self.relationship).all()
|
||||||
|
|
||||||
def is_valid_relation(self, parent, sub):
|
def is_valid_relation(self, parent, sub, created=False):
|
||||||
workflow_nodes = parent.workflow_job_template.workflow_job_template_nodes.all()
|
if created:
|
||||||
graph = SimpleDAG()
|
return None
|
||||||
|
|
||||||
|
workflow_nodes = parent.workflow_job_template.workflow_job_template_nodes.all().\
|
||||||
|
prefetch_related('success_nodes', 'failure_nodes', 'always_nodes')
|
||||||
|
graph = {}
|
||||||
for workflow_node in workflow_nodes:
|
for workflow_node in workflow_nodes:
|
||||||
graph.add_node(workflow_node)
|
graph[workflow_node.pk] = dict(node_object=workflow_node, metadata={'parent': None})
|
||||||
|
|
||||||
find = False
|
find = False
|
||||||
for node_type in ['success_nodes', 'failure_nodes', 'always_nodes']:
|
for node_type in ['success_nodes', 'failure_nodes', 'always_nodes']:
|
||||||
for workflow_node in workflow_nodes:
|
for workflow_node in workflow_nodes:
|
||||||
related_nodes = getattr(workflow_node, node_type).all()
|
parent_node = graph[workflow_node.pk]
|
||||||
|
related_nodes = getattr(parent_node['node_object'], node_type).all()
|
||||||
for related_node in related_nodes:
|
for related_node in related_nodes:
|
||||||
graph.add_edge(workflow_node, related_node, node_type)
|
sub_node = graph[related_node.pk]
|
||||||
if (not find and
|
sub_node['metadata']['parent'] = parent_node
|
||||||
parent == workflow_node and
|
if not find and parent == workflow_node and sub == related_node and self.relationship == node_type:
|
||||||
sub == related_node and
|
|
||||||
self.relationship == node_type):
|
|
||||||
find = True
|
find = True
|
||||||
if not find:
|
if not find:
|
||||||
graph.add_edge(parent, sub, self.relationship)
|
sub_node = graph[sub.pk]
|
||||||
|
parent_node = graph[parent.pk]
|
||||||
if graph.cycle_detected():
|
if sub_node['metadata']['parent'] is not None:
|
||||||
return {"Error": "Cycle detected!"}
|
return {"Error": "Multiple ancestor detected!"}
|
||||||
|
iter_node = parent_node
|
||||||
if graph.multi_ancestor_detected():
|
while iter_node is not None:
|
||||||
return {"Error": "Multiple ancestor detected!"}
|
if iter_node == sub_node:
|
||||||
|
return {"Error": "Cycle detected!"}
|
||||||
|
iter_node = iter_node['metadata']['parent']
|
||||||
|
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|||||||
@@ -137,53 +137,3 @@ class SimpleDAG(object):
|
|||||||
if len(self.get_dependents(n['node_object'])) < 1:
|
if len(self.get_dependents(n['node_object'])) < 1:
|
||||||
roots.append(n)
|
roots.append(n)
|
||||||
return roots
|
return roots
|
||||||
|
|
||||||
def _find_cycle(self, node):
|
|
||||||
stack = [node]
|
|
||||||
node['metadata']['color'] = 'gray'
|
|
||||||
|
|
||||||
while len(stack) > 0:
|
|
||||||
if stack[-1]['metadata']['count'] == len(stack[-1]['metadata']['adj_list']):
|
|
||||||
stack[-1]['metadata']['color'] = 'black'
|
|
||||||
stack.pop()
|
|
||||||
else:
|
|
||||||
to_push = stack[-1]['metadata']['adj_list'][stack[-1]['metadata']['count']]
|
|
||||||
stack[-1]['metadata']['count'] += 1
|
|
||||||
if to_push['metadata']['color'] == 'gray':
|
|
||||||
return True
|
|
||||||
elif to_push['metadata']['color'] == 'white':
|
|
||||||
to_push['metadata']['color'] = 'gray'
|
|
||||||
stack.append(to_push)
|
|
||||||
|
|
||||||
return False
|
|
||||||
|
|
||||||
def _clean_meta(self):
|
|
||||||
for node in self.nodes:
|
|
||||||
node['metadata'] = None
|
|
||||||
|
|
||||||
def cycle_detected(self):
|
|
||||||
for node in self.nodes:
|
|
||||||
node['metadata'] = {"adj_list": [], "color": "white", "count": 0}
|
|
||||||
for edge in self.edges:
|
|
||||||
self.nodes[edge[0]]['metadata']['adj_list'].append(self.nodes[edge[1]])
|
|
||||||
|
|
||||||
for node in self.nodes:
|
|
||||||
if node['metadata']['color'] == 'white' and self._find_cycle(node):
|
|
||||||
self._clean_meta()
|
|
||||||
return True
|
|
||||||
|
|
||||||
self._clean_meta()
|
|
||||||
return False
|
|
||||||
|
|
||||||
def multi_ancestor_detected(self):
|
|
||||||
for node in self.nodes:
|
|
||||||
node['metadata'] = {"ancestor": None}
|
|
||||||
for edge in self.edges:
|
|
||||||
if self.nodes[edge[1]]['metadata']['ancestor'] is None:
|
|
||||||
self.nodes[edge[1]]['metadata']['ancestor'] = self.nodes[edge[0]]
|
|
||||||
else:
|
|
||||||
self._clean_meta()
|
|
||||||
return True
|
|
||||||
|
|
||||||
self._clean_meta()
|
|
||||||
return False
|
|
||||||
|
|||||||
Reference in New Issue
Block a user