def generation(self):
        self.info("TOSCA diagram generation")

        topology_template = syntax.get_topology_template(
            self.tosca_service_template.get_yaml())
        # Generate only for TOSCA topology template.
        if topology_template is None:
            return

        # Generate the TOSCA diagram.
        self.open_file(".dot")

        self.generate("graph ToscaDiagram {")
        self.generate('  rankdir="LR"')

        target_capability_ids = {
        }  # map<requirement_assignment_id,capability_id>
        connected_capabilities = set()  # set<node_name.capability_name>
        connected_requirements = set()  # set<node_name.requirement_name>

        substitution_mappings = syntax.get_substitution_mappings(
            topology_template)
        if substitution_mappings is not None:
            for capability_name, capability_yaml in syntax.get_capabilities(
                    substitution_mappings).items():
                if capability_yaml:
                    if not isinstance(capability_yaml, list):
                        continue  # TODO something when capability_yaml is not a list
                    capability_name_id = "topology_template_substitution_mappings_capability_" + normalize_name(
                        capability_name)
                    self.generate(
                        "  ",
                        capability_name_id,
                        '[label="',
                        capability_name,
                        '" shape=cds style=filled fillcolor=orange]',
                        sep="",
                    )
                    self.generate(
                        "  ",
                        capability_name_id,
                        " -- ",
                        normalize_name(capability_yaml[0]),
                        "_capability_",
                        normalize_name(capability_yaml[1]),
                        "[style=dotted]",
                        sep="",
                    )
                    connected_capabilities.add(capability_yaml[0] + "." +
                                               capability_yaml[1])

            for (
                    requirement_name,
                    requirement_yaml,
            ) in syntax.get_substitution_mappings_requirements(
                    substitution_mappings).items():
                if requirement_yaml:
                    connected_requirements.add(requirement_yaml[0] + "." +
                                               requirement_yaml[1])

            substitution_mappings_node_type = syntax.get_node_type(
                substitution_mappings)
            self.generate("  subgraph clusterSubstitutionMappings {")
            self.generate('    label="',
                          substitution_mappings_node_type,
                          '"',
                          sep="")

        node_templates = syntax.get_node_templates(topology_template)

        for node_name, node_yaml in node_templates.items():
            node_type_requirements = syntax.get_requirements_dict(
                self.type_system.merge_type(syntax.get_type(node_yaml)))
            for requirement in syntax.get_requirements_list(node_yaml):
                for requirement_name, requirement_yaml in requirement.items():
                    # ACK for Alien4Cloud
                    requirement_name = syntax.get_type_requirement(
                        requirement_yaml, requirement_name)
                    if requirement_yaml:
                        requirement_capability = syntax.get_requirement_capability(
                            node_type_requirements.get(requirement_name))
                        if requirement_capability is None:
                            self.error(
                                requirement_name + ": capability undefined",
                                requirement_name,
                            )
                            continue
                        requirement_node = syntax.get_requirement_node_template(
                            requirement_yaml)
                        if requirement_node is None:
                            continue
                        capability_found = False
                        requirement_node_template = node_templates.get(
                            requirement_node)
                        if requirement_node_template is None:
                            self.error(
                                requirement_node + " node template undefined",
                                requirement_node,
                            )
                            continue
                        for capability_name, capability_yaml in syntax.get_capabilities(
                                self.type_system.merge_node_type(
                                    syntax.get_type(
                                        requirement_node_template))).items():
                            if self.type_system.is_derived_from(
                                    syntax.get_capability_type(
                                        capability_yaml),
                                    requirement_capability,
                            ):
                                capability_found = True
                                break
                        if capability_found:
                            target_capability_ids[id(requirement)] = (
                                self.get_node_name_id(requirement_node) +
                                "_capability_" +
                                normalize_name(capability_name))
                            connected_capabilities.add(requirement_node + "." +
                                                       capability_name)
                            connected_requirements.add(node_name + "." +
                                                       requirement_name)
                        else:
                            self.error(
                                ' capability of type "' +
                                requirement_capability + '" not found',
                                requirement_node_template,
                            )

        for node_name, node_yaml in node_templates.items():
            node_name_id = self.get_node_name_id(node_name)
            node_type = syntax.get_type(node_yaml)
            merged_node_type = self.type_system.merge_type(node_type)
            self.generate("    subgraph cluster", node_name_id, " {", sep="")
            self.generate("      color=white")
            self.generate('      label=""')
            self.generate(
                "      ",
                node_name_id,
                '[label="',
                node_name,
                ": ",
                short_type_name(node_type),
                '|\l\l\l\l" shape=record style=rounded]',
                sep="",
            )
            for capability_name, capability_yaml in syntax.get_capabilities(
                    merged_node_type).items():
                if (node_name + "." + capability_name in connected_capabilities
                        or node_yaml.get("capabilities",
                                         {}).get(capability_name) is not None):
                    self.generate(
                        "      ",
                        node_name_id,
                        "_capability_",
                        normalize_name(capability_name),
                        '[label="',
                        capability_name,
                        '" shape=cds style=filled fillcolor=orange]',
                        sep="",
                    )
                    self.generate(
                        "      ",
                        node_name_id,
                        "_capability_",
                        normalize_name(capability_name),
                        " -- ",
                        node_name_id,
                        sep="",
                    )
            for requirement_name, requirement_yaml in syntax.get_requirements_dict(
                    merged_node_type).items():
                if (node_name + "." + requirement_name
                        in connected_requirements
                        or requirement_yaml.get("occurrences", [1, 1])[0] > 0):
                    self.generate(
                        "      ",
                        node_name_id,
                        "_requirement_",
                        normalize_name(requirement_name),
                        '[label="',
                        requirement_name,
                        '" shape=cds style=filled fillcolor=turquoise]',
                        sep="",
                    )
                    self.generate(
                        "      ",
                        node_name_id,
                        " -- ",
                        node_name_id,
                        "_requirement_",
                        normalize_name(requirement_name),
                        sep="",
                    )
            self.generate("    }")

        for node_name, node_yaml in node_templates.items():
            node_name_id = self.get_node_name_id(node_name)
            for requirement in syntax.get_requirements_list(node_yaml):
                for requirement_name, requirement_yaml in requirement.items():
                    # ACK for Alien4Cloud
                    requirement_name = syntax.get_type_requirement(
                        requirement_yaml, requirement_name)
                    capability_id = target_capability_ids.get(id(requirement))
                    if capability_id is not None:
                        self.generate(
                            "    ",
                            node_name_id,
                            "_requirement_",
                            normalize_name(requirement_name),
                            " -- ",
                            capability_id,
                            "[style=dotted]",
                            sep="",
                        )

        if substitution_mappings is not None:
            self.generate("  }")
            for (
                    requirement_name,
                    requirement_yaml,
            ) in syntax.get_substitution_mappings_requirements(
                    substitution_mappings).items():
                if requirement_yaml:
                    requirement_name_id = "topology_template_substitution_mappings_requirement_" + normalize_name(
                        requirement_name)
                    self.generate(
                        "  ",
                        requirement_name_id,
                        '[label="',
                        requirement_name,
                        '" shape=cds style=filled fillcolor=turquoise]',
                        sep="",
                    )
                    self.generate(
                        "  ",
                        normalize_name(requirement_yaml[0]),
                        "_requirement_",
                        normalize_name(requirement_yaml[1]),
                        " -- ",
                        requirement_name_id,
                        "[style=dotted]",
                        sep="",
                    )

        self.generate("}")
        self.close_file()
    def generate_UML2_deployment_diagram(self, topology_template):
        self.generate("@startuml")
        self.generate("skinparam componentStyle uml2")
        self.generate()

        node_templates = get_dict(topology_template, NODE_TEMPLATES)

        non_containeds = list(node_templates.keys())
        containers = {}
        contained_containers = []

        # Iterate over all node templates to find containers.
        for node_template_name, node_template_yaml in node_templates.items():
            merged_node_template_type = self.type_system.merge_node_type(
                node_template_yaml.get(TYPE))
            # Iterate over all capabilities of the node template type.
            for capability_name, capability_yaml in get_dict(
                    merged_node_template_type, CAPABILITIES).items():
                capability_type = get_capability_type(capability_yaml)
                if self.type_system.is_derived_from(
                        capability_type, "tosca.capabilities.Container"):
                    containers[node_template_name] = containers.get(
                        node_template_name, dict())
                    try:
                        non_containeds.remove(node_template_name)
                    except ValueError:
                        pass

        # Iterate over all node templates to find containeds.
        for node_template_name, node_template_yaml in node_templates.items():
            merged_node_template_type = self.type_system.merge_node_type(
                node_template_yaml.get(TYPE))
            # Iterate over all requirements of the node template.
            for requirement in get_list(node_template_yaml, REQUIREMENTS):
                for requirement_name, requirement_yaml in requirement.items():
                    requirement_definition = get_dict(
                        merged_node_template_type,
                        REQUIREMENTS).get(requirement_name)
                    requirement_relationship = syntax.get_requirement_relationship(
                        requirement_definition)
                    requirement_relationship_type = syntax.get_relationship_type(
                        requirement_relationship)
                    if self.type_system.is_derived_from(
                            requirement_relationship_type,
                            "tosca.relationships.HostedOn"):
                        requirement_node = get_requirement_node_template(
                            requirement_yaml)
                        if requirement_node is not None:
                            try:
                                containers[requirement_node][
                                    node_template_name] = containers.get(
                                        node_template_name, dict())
                            except KeyError as e:
                                self.error(e)
                            contained_containers.append(node_template_name)
                            try:
                                non_containeds.remove(node_template_name)
                            except ValueError:
                                pass

        # TODO: Remove containers contained by other containers.
        for contained_container_name in contained_containers:
            if containers.get(contained_container_name) is not None:
                del containers[contained_container_name]

        # Iterate over all containers.

        def get_uml2_kind(tosca_type):
            uml2_kind = "component"
            for tt, kind in self.configuration.get(UML2, "kinds").items():
                if self.type_system.is_derived_from(tosca_type, tt):
                    uml2_kind = kind
                    break
            return uml2_kind

        def generate_container(self, container_name, containeds):
            node_template = node_templates.get(container_name)
            node_template_type = node_template.get(TYPE)
            uml2_kind = get_uml2_kind(node_template_type)
            node_template_artifacts = get_dict(node_template, ARTIFACTS)
            if len(containeds) == 0 and len(node_template_artifacts) == 0:
                self.generate(
                    uml2_kind,
                    ' "',
                    container_name,
                    ": ",
                    short_type_name(node_template_type),
                    '" as node_',
                    normalize_name(container_name),
                    sep="",
                )
            else:
                self.generate(
                    uml2_kind,
                    ' "',
                    container_name,
                    ": ",
                    short_type_name(node_template_type),
                    '" as node_',
                    normalize_name(container_name),
                    " {",
                    sep="",
                )
                for contained_name, contained_dict in containeds.items():
                    generate_container(self, contained_name, contained_dict)
                for artifact_name, artifact_yaml in node_template_artifacts.items(
                ):
                    artifact_type = syntax.get_artifact_type(artifact_yaml)
                    if artifact_type is None:
                        artifact_type = "Artifact"
                    self.generate(
                        'artifact "',
                        syntax.get_artifact_file(artifact_yaml),
                        '" <<',
                        artifact_type,
                        ">> as node_",
                        normalize_name(container_name),
                        "_artifact_",
                        normalize_name(artifact_name),
                        sep="",
                    )
                self.generate("}")

        substitution_mappings = topology_template.get(SUBSTITUTION_MAPPINGS)
        if substitution_mappings:
            # Create components connected to the capabilities of the substitition mapping.
            for capability_name, capability_yaml in get_dict(
                    substitution_mappings, CAPABILITIES).items():
                self.generate(
                    'component "a node" as substitution_mappings_capability_',
                    normalize_name(capability_name),
                    sep="",
                )

            substitution_mappings_node_type = substitution_mappings.get(
                NODE_TYPE)
            self.generate(
                get_uml2_kind(substitution_mappings_node_type),
                ' ": ',
                substitution_mappings_node_type,
                '" as substitution_mappings {',
                sep="",
            )

        for container_name, containeds in containers.items():
            generate_container(self, container_name, containeds)

        for node_template_name in non_containeds:
            generate_container(self, node_template_name, {})

        relationship_templates = get_dict(topology_template,
                                          RELATIONSHIP_TEMPLATES)

        # Iterate over all node templates to draw relationships.
        for node_template_name, node_template_yaml in node_templates.items():
            merged_node_template_type = self.type_system.merge_node_type(
                node_template_yaml.get(TYPE))
            # Iterate over all requirements of the node template.
            for requirement in get_list(node_template_yaml, REQUIREMENTS):
                for requirement_name, requirement_yaml in requirement.items():
                    requirement_relationship_type = None
                    if type(requirement_yaml) == dict:
                        requirement_relationship = syntax.get_requirement_relationship(
                            requirement_yaml)
                        if type(requirement_relationship) == dict:
                            requirement_relationship_type = syntax.get_relationship_type(
                                requirement_relationship)
                        else:
                            relationship_template = relationship_templates.get(
                                requirement_relationship)
                            if relationship_template:
                                requirement_relationship_type = relationship_template.get(
                                    TYPE)
                            else:
                                requirement_relationship_type = requirement_relationship
                    if requirement_relationship_type is None:
                        requirement = get_dict(merged_node_template_type,
                                               REQUIREMENTS).get(
                                                   requirement_name, {})
                        tmp = syntax.get_requirement_relationship(requirement)
                        requirement_relationship_type = syntax.get_relationship_type(
                            tmp)
                    if requirement_relationship_type is None:
                        continue

                    if not self.type_system.is_derived_from(
                            requirement_relationship_type,
                            "tosca.relationships.HostedOn"):
                        requirement_node = get_requirement_node_template(
                            requirement_yaml)
                        if requirement_node:
                            self.generate(
                                "node_",
                                normalize_name(node_template_name),
                                ' "',
                                requirement_name,
                                '" ..> node_',
                                normalize_name(requirement_node),
                                " : <<",
                                short_type_name(requirement_relationship_type),
                                ">>",
                                sep="",
                            )

        if substitution_mappings:
            self.generate("}")
            # Connect created components to the nodes exported by the capabilities of the substitition mapping.
            for capability_name, capability_yaml in get_dict(
                    substitution_mappings, CAPABILITIES).items():
                if type(capability_yaml) != list:
                    continue  # TODO
                target_node_name = capability_yaml[0]
                target_capability_name = capability_yaml[1]
                self.generate(
                    "substitution_mappings_capability_",
                    normalize_name(capability_name),
                    ' "',
                    capability_name,
                    '" ..> node_',
                    normalize_name(target_node_name),
                    sep="",
                )

            merged_substitution_mappings_node_type = self.type_system.merge_node_type(
                substitution_mappings_node_type)
            # Get all requirements of the node type of the substitution mapping.
            all_requirement_declarations = get_dict(
                merged_substitution_mappings_node_type, REQUIREMENTS)
            req_idx = 0
            # Iterate over all requirements of the substitution mapping.
            for (
                    requirement_name,
                    requirement_yaml,
            ) in syntax.get_substitution_mappings_requirements(
                    substitution_mappings).items():
                requirement_capability = syntax.get_requirement_capability(
                    all_requirement_declarations.get(requirement_name))
                if requirement_capability is None:
                    continue
                self.generate(
                    get_uml2_kind(requirement_capability),
                    ' ": ',
                    short_type_name(requirement_capability),
                    '" as substitution_mappings_requirement_',
                    req_idx,
                    sep="",
                )
                requirement_node = requirement_yaml[0]
                requirement_node_capability = requirement_yaml[1]
                self.generate(
                    "node_",
                    normalize_name(requirement_node),
                    ' "',
                    normalize_name(requirement_node_capability),
                    '" ..> "',
                    requirement_name,
                    '" substitution_mappings_requirement_',
                    req_idx,
                    sep="",
                )
                req_idx = req_idx + 1

        self.generate("@enduml")
Ejemplo n.º 3
0
    def generate_network_diagram(self, topology_template):
        # network ports
        # dict<port node name, array of bindable node templates>
        ports = {}

        def add_port_binding(port_name, node_name):
            port_bindings = ports.get(port_name)
            if port_bindings is None:
                port_bindings = []
                ports[port_name] = port_bindings
            port_bindings.append(node_name)

        # Networks
        # dict<network name, Network>
        networks = {}

        def get_network(network_name):
            network = networks.get(network_name)
            if network is None:
                network = Network()
                networks[network_name] = network
            return network

        # iterates over all the requirements of the substitution mapping
        # in order to create external networks
        substitution_mapping = topology_template.get(
            syntax.SUBSTITUTION_MAPPINGS)
        if substitution_mapping is not None:
            substitution_mapping_node_type = syntax.get_node_type(
                substitution_mapping)
            if substitution_mapping_node_type is not None:
                node_type = self.type_system.merge_type(
                    substitution_mapping_node_type)
                node_type_requirements = syntax.get_requirements_dict(
                    node_type)
                for (
                        requirement_name,
                        requirement_value,
                ) in syntax.get_substitution_mappings_requirements(
                        substitution_mapping).items():
                    requirement_definition = node_type_requirements.get(
                        requirement_name)
                    capability = syntax.get_requirement_capability(
                        requirement_definition)
                    if capability in self.configuration.get(
                            NWDIAG, "linkable_capability_types"):
                        # is a requirement with capability in linkable
                        # capability types
                        # create the Network associated to this external
                        # network requirement
                        network = get_network(requirement_name)
                        # the requirement node is in the external node
                        network.nodes[
                            requirement_value[0]] = requirement_value[0]

        # iterates over all the node templates
        node_templates = topology_template.get(syntax.NODE_TEMPLATES, {})
        for node_name, node_yaml in node_templates.items():
            # get the node type
            node_type = node_yaml.get(syntax.TYPE)
            node_type_type = self.type_system.merge_type(node_type)

            # Deal with Forwarding nodes which are both a port and a network
            if node_type in self.configuration.get(NWDIAG,
                                                   "forwarding_node_types"):
                # a Forwarding node is a node of its associated Forwarding
                # network
                get_network(node_name).nodes[node_name] = node_name

            # iterate over all the node capabilities
            # in order to identify node templates which are networks
            for cap_name, cap_def in node_type_type.get(
                    syntax.CAPABILITIES, {}).items():
                cap_def_type = (cap_def.get(syntax.TYPE) if isinstance(
                    cap_def, dict) else cap_def)
                if cap_def_type in self.configuration.get(
                        NWDIAG, "linkable_capability_types"):
                    # this node template is a network node,
                    # i.e. a node template with a linkable capability
                    get_network(node_name).network_node = node_yaml

            # iterate over all the requirements of the current node
            node_type_requirements = syntax.get_requirements_dict(
                node_type_type)
            for requirement in syntax.get_requirements_list(node_yaml):
                for requirement_name, requirement_yaml in requirement.items():
                    requirement_definition = node_type_requirements.get(
                        requirement_name)
                    capability = syntax.get_requirement_capability(
                        requirement_definition)
                    if capability in self.configuration.get(
                            NWDIAG, "linkable_capability_types"):
                        network_node = syntax.get_requirement_node_template(
                            requirement_yaml)
                        # current node template is a node of the network node
                        get_network(network_node).nodes[node_name] = node_name
                    elif capability in self.configuration.get(
                            NWDIAG, "bindable_capability_types"):
                        binding_node = syntax.get_requirement_node_template(
                            requirement_yaml)
                        add_port_binding(node_name, binding_node)

        # network diagram generation
        if len(networks) == 0:  # no network node template found
            self.info("No network diagram generated.")
        else:
            self.info("Network diagram generation ...")
            # network node templates found then
            # generate the network diagram file
            self.open_file(".nwdiag")
            self.generate("{")
            # iterate over all found networks
            for network_name, network in networks.items():
                self.generate('  network "%s" {' % network_name)
                network_node = network.network_node
                if network_node is None:
                    # this is an external network associated to a network
                    # requirement of the substitution mapping
                    network_repr = {}  # use default graphical attributes
                    network_label = network_name
                    network_address = ""  # no network address
                else:
                    # this a network node template
                    network_repr = self.get_representation(network_node)
                    network_label = self.resolve_attribute(
                        network_node, network_repr, "label", "\n")
                    network_label = (network_label
                                     if network_label != "" else network_name)
                    network_address = self.resolve_attribute(
                        network_node, network_repr, "address", "\n")  # '', ')
                # generate the graphical attributes of the network
                # TODO: add other graphical attributes font, etc.
                self.generate('    label = "%s"' % network_label)
                self.generate('    address = "%s"' % network_address)
                self.generate('    color = "%s"' %
                              network_repr.get("color", "lightblue"))
                self.generate('    textcolor = "%s"' % network_repr.get(
                    "textcolor",
                    "black",
                ))

                # generate all the ports connected to the network
                if len(network.nodes) == 0:  # no node found
                    # nwdiag tool requires that a network has nodes else an
                    # error is produced!
                    # so generate a graphical note
                    self.generate(
                        '    empty_network_%s[label="This network\nis empty!", shape=note];'
                        % network_name)
                else:
                    # iterates over of the nodes of the network
                    for port_name, node_name in network.nodes.items():
                        port_node = node_templates.get(port_name)
                        port_repr = self.get_representation(port_node)
                        port_address = self.resolve_attribute(
                            port_node, port_repr, "address", ", ")
                        bindings = ports.get(node_name, [node_name])
                        for binding in bindings:
                            bindings = ports.get(binding, [binding])
                            for binding in bindings:
                                binding_node = node_templates.get(binding)
                                binding_repr = self.get_representation(
                                    binding_node)
                                binding_label = self.resolve_attribute(
                                    binding_node, binding_repr, "label", "\n")
                                binding_label = (binding_label
                                                 if binding_label != "" else
                                                 binding)
                                # generate the graphical node
                                # TODO: add other graphical attributes font,
                                #       etc.
                                self.generate(
                                    '    "%s"[label="%s", address="%s", shape="%s", color="%s", textcolor="%s", style="%s"%s];'
                                    % (
                                        binding,
                                        binding_label,
                                        port_address,
                                        binding_repr.get("shape", "box"),
                                        binding_repr.get("color", "white"),
                                        binding_repr.get("textcolor", "black"),
                                        binding_repr.get("style", "solid"),
                                        self.get_icon_attribute(
                                            binding_node, binding_repr),
                                    ))
                self.generate("  }")
            self.generate("}")
            self.close_file()
    def generate_UML2_component_diagram(self, topology_template,
                                        with_relationships):
        self.generate("@startuml")
        self.generate("skinparam componentStyle uml2")
        if with_relationships:
            self.generate("skinparam component {")
            self.generate("  backgroundColor<<relationship>> White")
            self.generate("}")
        self.generate()

        substitution_mappings = topology_template.get(SUBSTITUTION_MAPPINGS)
        if substitution_mappings:
            substitution_mappings_uml_id = SUBSTITUTION_MAPPINGS
            substitution_mappings_node_type = substitution_mappings.get(
                NODE_TYPE)
            merged_substitution_mappings_type = self.type_system.merge_node_type(
                substitution_mappings_node_type)
            for capability_name, capability_yaml in get_dict(
                    merged_substitution_mappings_type, CAPABILITIES).items():
                capability_uml_id = (substitution_mappings_uml_id + "_" +
                                     normalize_name(capability_name))
                # Declare an UML interface for the substitution_mappings capability.
                self.generate('interface "',
                              capability_name,
                              '" as ',
                              capability_uml_id,
                              sep="")
            self.generate(
                'component ": ',
                substitution_mappings_node_type,
                '" <<node>> as ',
                substitution_mappings_uml_id,
                " {",
                sep="",
            )

        relationship_templates = get_dict(topology_template,
                                          RELATIONSHIP_TEMPLATES)

        already_generated_interfaces = {}

        # Iterate over all node templates.
        node_templates = get_dict(topology_template, NODE_TEMPLATES)
        for node_template_name, node_template_yaml in node_templates.items():
            node_template_type = node_template_yaml.get(TYPE)
            merged_node_template_type = self.type_system.merge_node_type(
                node_template_type)
            node_template_uml_id = "node_" + normalize_name(node_template_name)
            # Declare an UML component for the node template.
            self.generate(
                'component "',
                node_template_name,
                ": ",
                short_type_name(node_template_type),
                '" <<node>> as ',
                node_template_uml_id,
                sep="",
            )
            # Iterate over all capabilities of the node template.
            for capability_name, capability_yaml in get_dict(
                    merged_node_template_type, CAPABILITIES).items():
                if type(capability_yaml) == dict:
                    capability_occurrences = capability_yaml.get(OCCURRENCES)
                else:
                    capability_occurrences = None
                if with_relationships or (capability_occurrences
                                          and capability_occurrences[0] > 0):
                    capability_uml_id = (node_template_uml_id + "_" +
                                         normalize_name(capability_name))
                    # Declare an UML interface for the node template capability.
                    self.generate(
                        'interface "',
                        capability_name,
                        '" as ',
                        capability_uml_id,
                        sep="",
                    )
                    # Connect the capability UML interface to the node template UML component.
                    self.generate(capability_uml_id, "--",
                                  node_template_uml_id)
                    already_generated_interfaces[
                        capability_uml_id] = capability_uml_id
            if with_relationships:
                # Iterate over all requirements of the node template.
                index = 0
                for requirement in get_list(node_template_yaml, REQUIREMENTS):
                    for requirement_name, requirement_yaml in requirement.items(
                    ):
                        requirement_uml_id = (
                            node_template_uml_id + "_" +
                            normalize_name(requirement_name) +
                            "_relationship" + str(index))
                        index = index + 1

                        requirement_node = get_requirement_node_template(
                            requirement_yaml)
                        if requirement_node is None:
                            continue

                        relationship_component_name = ""  # No name.
                        relationship_component_type = None

                        if type(requirement_yaml) == dict:
                            requirement_relationship = syntax.get_requirement_relationship(
                                requirement_yaml)
                            if type(requirement_relationship) == dict:
                                relationship_component_type = syntax.get_relationship_type(
                                    requirement_relationship)
                            else:
                                relationship_template = relationship_templates.get(
                                    requirement_relationship)
                                if relationship_template:
                                    relationship_component_name = (
                                        requirement_relationship)
                                    relationship_component_type = relationship_template.get(
                                        TYPE)
                                else:
                                    relationship_component_type = (
                                        requirement_relationship)
                        if relationship_component_type is None:
                            requirement = get_dict(merged_node_template_type,
                                                   REQUIREMENTS).get(
                                                       requirement_name, {})
                            tmp = syntax.get_requirement_relationship(
                                requirement)
                            relationship_component_type = syntax.get_relationship_type(
                                tmp)
                        if relationship_component_type is None:
                            continue
                        # Declare an UML component for the node template requirement relationship.
                        self.generate(
                            'component "',
                            relationship_component_name,
                            ": ",
                            short_type_name(relationship_component_type),
                            '" <<relationship>> as ',
                            requirement_uml_id,
                            sep="",
                        )
                        # Declare an UML interface for the node template requirement relationship.
                        self.generate('interface " " as ',
                                      requirement_uml_id,
                                      "_source",
                                      sep="")
                        # Connect the UML interface to the relationship UML component.
                        self.generate(
                            requirement_uml_id,
                            "_source",
                            " -- ",
                            requirement_uml_id,
                            sep="",
                        )
                        # Connect the node template UML component to the relationship UML component.
                        self.generate(
                            node_template_uml_id,
                            " --( ",
                            requirement_uml_id,
                            "_source",
                            " : ",
                            requirement_name,
                            sep="",
                        )
            self.generate()

        # Iterate over all node templates.
        for node_template_name, node_template_yaml in node_templates.items():
            node_template_uml_id = "node_" + normalize_name(node_template_name)
            node_template_type = node_template_yaml.get(TYPE)
            merged_node_template_type = self.type_system.merge_node_type(
                node_template_type)
            # Iterate over all requirements of the node template.
            index = 0
            for requirement in get_list(node_template_yaml, REQUIREMENTS):
                for requirement_name, requirement_yaml in requirement.items():
                    source_uml_id = node_template_uml_id
                    if with_relationships:
                        source_uml_id = (source_uml_id + "_" +
                                         normalize_name(requirement_name) +
                                         "_relationship" + str(index))
                    index = index + 1
                    requirement_node = get_requirement_node_template(
                        requirement_yaml)
                    if requirement_node is None:
                        continue
                    requirement_node_template = node_templates.get(
                        requirement_node)
                    if requirement_node_template is None:
                        continue
                    requirement_node_type_name = requirement_node_template.get(
                        TYPE)
                    if requirement_node_type_name is None:
                        continue

                    requirement_capability = syntax.get_requirement_capability(
                        get_dict(merged_node_template_type,
                                 REQUIREMENTS).get(requirement_name))
                    capability_found = False
                    for (capability_name, capability_yaml) in get_dict(
                            self.type_system.merge_node_type(
                                requirement_node_type_name),
                            CAPABILITIES,
                    ).items():
                        if self.type_system.is_derived_from(
                                syntax.get_capability_type(capability_yaml),
                                requirement_capability,
                        ):
                            capability_found = True
                            break
                    if capability_found:
                        target_node_uml_id = "node_" + normalize_name(
                            requirement_node)
                        target_capability_uml_id = (
                            target_node_uml_id + "_" +
                            normalize_name(capability_name))
                        if with_relationships:
                            self.generate(source_uml_id,
                                          " --( ",
                                          target_capability_uml_id,
                                          sep="")
                        else:
                            if (already_generated_interfaces.get(
                                    target_capability_uml_id) is None):
                                self.generate(
                                    'interface "',
                                    capability_name,
                                    '" as ',
                                    target_capability_uml_id,
                                    sep="",
                                )
                                # Connect the capability UML interface to the node template UML component.
                                self.generate(target_capability_uml_id, "--",
                                              target_node_uml_id)
                                already_generated_interfaces[
                                    target_capability_uml_id] = target_capability_uml_id
                            self.generate(
                                source_uml_id,
                                " --( ",
                                target_capability_uml_id,
                                " : ",
                                requirement_name,
                                sep="",
                            )

        if substitution_mappings:
            capabilities = get_dict(substitution_mappings, CAPABILITIES)

            for capability_name, capability_yaml in get_dict(
                    merged_substitution_mappings_type, CAPABILITIES).items():
                capability = capabilities.get(capability_name)
                if capability is not None:
                    if type(capability) != list:
                        continue  # TODO when capability is not a list
                    target_node_uml_id = "node_" + capability[0]
                    target_uml_id = (target_node_uml_id + "_" +
                                     normalize_name(capability[1]))

                    if already_generated_interfaces.get(target_uml_id) is None:
                        self.generate(
                            'interface "',
                            capability_name,
                            '" as ',
                            target_uml_id,
                            sep="",
                        )
                        # Connect the capability UML interface to the node template UML component.
                        self.generate(target_uml_id, "--", target_node_uml_id)
                        already_generated_interfaces[
                            target_uml_id] = target_uml_id

            self.generate("}")

            for capability_name, capability_yaml in get_dict(
                    merged_substitution_mappings_type, CAPABILITIES).items():
                capability_uml_id = (substitution_mappings_uml_id + "_" +
                                     normalize_name(capability_name))
                # Connect the capability UML interface to the node template UML component.
                capability = capabilities.get(capability_name)
                if capability is not None:
                    if type(capability) != list:
                        continue  # TODO when capability is not a list
                    target_node_uml_id = "node_" + capability[0]
                    target_uml_id = (target_node_uml_id + "_" +
                                     normalize_name(capability[1]))
                    self.generate(capability_uml_id, "--(", target_uml_id)
                else:
                    self.generate(capability_uml_id, "--",
                                  substitution_mappings_uml_id)

            index = 0
            for (
                    requirement_name,
                    requirement_yaml,
            ) in syntax.get_substitution_mappings_requirements(
                    substitution_mappings).items():
                interface_uml_id = (substitution_mappings_uml_id + "_" +
                                    normalize_name(requirement_name) +
                                    str(index))
                index = index + 1
                self.generate('interface "', requirement_name, '" as ',
                              interface_uml_id)
                if requirement_yaml:
                    source_uml_id = "node_" + normalize_name(
                        requirement_yaml[0])
                    self.generate(
                        source_uml_id,
                        " --( ",
                        interface_uml_id,
                        " : ",
                        requirement_yaml[1],
                        sep="",
                    )
                else:
                    self.generate(
                        substitution_mappings_uml_id,
                        " -- ",
                        interface_uml_id,
                        " : ",
                        requirement_name,
                    )

        self.generate("@enduml")