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()
Ejemplo n.º 2
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()