def _preprocess_nodes(workflow, transformed_root, workflow_definition_root, nodes, fs=None): """ preprocess nodes Resolve start name and subworkflow dependencies. Looks at path and interrogates all workflows until the proper deployment path is found. If the proper deployment path is never found, then """ for full_node in nodes: if full_node.node_type is "start": full_node.name = "start" elif full_node.node_type is "subworkflow": app_path = None for action_el in workflow_definition_root: if "name" in action_el.attrib and action_el.attrib["name"] == full_node.name: for subworkflow_el in action_el: if xml_tag(subworkflow_el) == "sub-workflow": for property_el in subworkflow_el: if xml_tag(property_el) == "app-path": app_path = property_el.text if app_path is None: raise RuntimeError(_("Could not find app-path for subworkflow %s") % full_node.name) subworkflow = _resolve_subworkflow_from_deployment_dir(fs, workflow, app_path) if subworkflow: full_node.sub_workflow = subworkflow else: raise RuntimeError(_("Could not find subworkflow with deployment directory: %s") % app_path)
def _node_relationships(workflow, parent, child_el): """ Resolves node links. Will use 'start' link type for fork nodes and 'to' link type for all other nodes. Error links will automatically resolve to a single kill node. """ for el in child_el: # Skip special nodes (like comments). if not isinstance(el.tag, basestring): continue # Links name = xml_tag(el) if name in LINKS: if name == "path": if "start" not in el.attrib: raise RuntimeError(_("Node %s has a link that is missing 'start' attribute.") % parent.name) to = el.attrib["start"] name = "start" else: if "to" not in el.attrib: raise RuntimeError(_("Node %s has a link that is missing 'to' attribute.") % parent.name) to = el.attrib["to"] try: child = Node.objects.get(workflow=workflow, name=to) except Node.DoesNotExist, e: if name == "error": child, create = Kill.objects.get_or_create(name="kill", workflow=workflow, node_type=Kill.node_type) else: raise RuntimeError(_("Node %s has not been defined") % to) obj = Link.objects.create(name=name, parent=parent, child=child) obj.save()
def _decision_relationships(workflow, parent, child_el): """ Resolves the switch statement like nature of decision nodes. Will use 'to' link type, except for default case. """ for switch in child_el: # Skip special nodes (like comments). if not isinstance(switch.tag, basestring): continue for case in switch: # Skip special nodes (like comments). if not isinstance(case.tag, basestring): continue if "to" not in case.attrib: raise RuntimeError(_("Node %s has a link that is missing 'to' attribute.") % parent.name) to = case.attrib["to"] try: child = Node.objects.get(workflow=workflow, name=to) except Node.DoesNotExist, e: raise RuntimeError(_("Node %s has not been defined.") % to) if xml_tag(case) == "default": name = "default" obj = Link.objects.create(name=name, parent=parent, child=child) else: name = "start" comment = case.text.strip() obj = Link.objects.create(name=name, parent=parent, child=child, comment=comment) obj.save()
def _node_relationships(workflow, parent, child_el): """ Resolves node links. Will use 'start' link type for fork nodes and 'to' link type for all other nodes. """ for el in child_el: # Skip special nodes (like comments). if not isinstance(el.tag, basestring): continue # Links name = xml_tag(el) if name in LINKS: if name == 'path': if 'start' not in el.attrib: raise RuntimeError(_("Node %s has a link that is missing 'start' attribute.") % parent.name) to = el.attrib['start'] name = 'start' else: if 'to' not in el.attrib: raise RuntimeError(_("Node %s has a link that is missing 'to' attribute.") % parent.name) to = el.attrib['to'] try: child = Node.objects.get(workflow=workflow, name=to) except Node.DoesNotExist, e: raise RuntimeError("Node %s has not been defined" % to) obj = Link.objects.create(name=name, parent=parent, child=child) obj.save()
def _save_links(workflow, root): """ Iterates over all links in the passed XML doc and creates links. First non-META links are resolved and created, then META links. Link name is chosen with the following logic: If node is start, then use 'to'. Else If node is Join, then use 'to'. Else If node is Decision, then If tag is 'default', then use 'default' Else use 'start' Else If tag is 'path', use 'start' Else use tag as name ('ok' or 'error') This strategy has the following resolution: - Fork and Decision nodes have Links named 'start'. - Decision nodes have a 'default' link. - Decision nodes may have a 'related' link that is there end. - Fork nodes always have a 'related' node that is there end join node. - Start and Join nodes have links named 'to'. - All action nodes have 'ok' and 'error' links. Note: The nodes that these links point to should exist already. Note: Nodes are looked up by workflow and name. """ # Iterate over nodes for child_el in root: # Skip special nodes (like comments). if not isinstance(child_el.tag, basestring): continue # Skip kill nodes. if child_el.tag.endswith('kill'): continue # Iterate over node members # Join nodes have attributes which point to the next node # Start node has attribute which points to first node parent = Node.objects.get(workflow=workflow, name=child_el.attrib.get('name', xml_tag(child_el))).get_full_node() if isinstance(parent, Start): _start_relationships(workflow, parent, child_el) elif isinstance(parent, Join): _join_relationships(workflow, parent, child_el) elif isinstance(parent, Decision): _decision_relationships(workflow, parent, child_el) else: _node_relationships(workflow, parent, child_el) workflow.end = End.objects.get(workflow=workflow).get_full_node() workflow.save() _resolve_start_relationships(workflow) _resolve_fork_relationships(workflow) _resolve_decision_relationships(workflow)
def _save_links(workflow, root): """ Iterates over all links in the passed XML doc and creates links. First non-META links are resolved and created, then META links. Link name is chosen with the following logic: If node is start, then use 'to'. Else If node is Join, then use 'to'. Else If node is Decision, then If tag is 'default', then use 'default' Else use 'start' Else If tag is 'path', use 'start' Else use tag as name ('ok' or 'error') This strategy has the following resolution: - Fork and Decision nodes have Links named 'start'. - Decision nodes have a 'default' link. - Decision nodes may have a 'related' link that is there end. - Fork nodes always have a 'related' node that is there end join node. - Start and Join nodes have links named 'to'. - All action nodes have 'ok' and 'error' links. Note: The nodes that these links point to should exist already. Note: Nodes are looked up by workflow and name. Note: Skip global configuration explicitly. Unknown knows should throw an error. """ LOG.debug("Start resolving links for workflow %s" % smart_str(workflow.name)) # Iterate over nodes for child_el in root: # Skip special nodes (like comments). if not isinstance(child_el.tag, basestring): continue # Skip kill nodes. if child_el.tag.endswith("kill"): continue # Skip global configuration. if child_el.tag.endswith("global"): continue tag = xml_tag(child_el) name = child_el.attrib.get("name", tag) LOG.debug( "Getting node with data - XML TAG: %(tag)s\tLINK NAME: %(node_name)s\tWORKFLOW NAME: %(workflow_name)s" % {"tag": smart_str(tag), "node_name": smart_str(name), "workflow_name": smart_str(workflow.name)} ) # Iterate over node members # Join nodes have attributes which point to the next node # Start node has attribute which points to first node try: parent = Node.objects.get(name=name, workflow=workflow).get_full_node() except Node.DoesNotExist, e: raise RuntimeError(_("Node with name %s for workflow %s does not exist.") % (name, workflow.name)) if isinstance(parent, Start): _start_relationships(workflow, parent, child_el) elif isinstance(parent, Join): _join_relationships(workflow, parent, child_el) elif isinstance(parent, Decision): _decision_relationships(workflow, parent, child_el) else: _node_relationships(workflow, parent, child_el)
def _save_links(workflow, root): """ Iterates over all links in the passed XML doc and creates links. First non-META links are resolved and created, then META links. Link name is chosen with the following logic: If node is start, then use 'to'. Else If node is Join, then use 'to'. Else If node is Decision, then If tag is 'default', then use 'default' Else use 'start' Else If tag is 'path', use 'start' Else use tag as name ('ok' or 'error') This strategy has the following resolution: - Fork and Decision nodes have Links named 'start'. - Decision nodes have a 'default' link. - Decision nodes may have a 'related' link that is there end. - Fork nodes always have a 'related' node that is there end join node. - Start and Join nodes have links named 'to'. - All action nodes have 'ok' and 'error' links. Note: The nodes that these links point to should exist already. Note: Nodes are looked up by workflow and name. """ # Iterate over nodes for node in root: # Iterate over node members # Join nodes have attributes which point to the next node # Start node has attribute which points to first node parent = Node.objects.get(workflow=workflow, name=node.attrib.get("name", xml_tag(node))).get_full_node() if isinstance(parent, Start): workflow.start = parent to = node.attrib["to"] child = Node.objects.get(workflow=workflow, name=to) obj = Link.objects.create(name="to", parent=parent, child=child) obj.save() elif isinstance(parent, Join): to = node.attrib["to"] child = Node.objects.get(workflow=workflow, name=to) obj = Link.objects.create(name="to", parent=parent, child=child) obj.save() elif isinstance(parent, Decision): for switch in node: for case in switch: to = case.attrib["to"] child = Node.objects.get(workflow=workflow, name=to) if xml_tag(case) == "default": name = "default" obj = Link.objects.create(name=name, parent=parent, child=child) else: name = "start" comment = case.text.strip() obj = Link.objects.create(name=name, parent=parent, child=child, comment=comment) obj.save() else: for el in node: # Links name = xml_tag(el) if name in LINKS: if name == "path": to = el.attrib["start"] name = "start" else: to = el.attrib["to"] child = Node.objects.get(workflow=workflow, name=to) obj = Link.objects.create(name=name, parent=parent, child=child) obj.save() workflow.end = End.objects.get(workflow=workflow).get_full_node() workflow.save() _resolve_fork_relationships(workflow) _resolve_decision_relationships(workflow)