diff options
-rw-r--r-- | catalog/pub/utils/toscaparser/__init__.py | 3 | ||||
-rw-r--r-- | catalog/pub/utils/toscaparser/basemodel.py | 34 | ||||
-rw-r--r-- | catalog/pub/utils/toscaparser/graph.py | 74 | ||||
-rw-r--r-- | catalog/pub/utils/toscaparser/nsdmodel.py | 6 | ||||
-rw-r--r-- | catalog/pub/utils/toscaparser/servicemodel.py | 19 | ||||
-rw-r--r-- | catalog/pub/utils/toscaparser/testdata/ns/ran.csar | bin | 2804 -> 3067 bytes | |||
-rw-r--r-- | catalog/pub/utils/toscaparser/testdata/ns/service-vIMS.csar | bin | 0 -> 54472 bytes | |||
-rw-r--r-- | catalog/pub/utils/toscaparser/tests.py | 37 | ||||
-rw-r--r-- | catalog/pub/utils/toscaparser/vnfdmodel.py | 4 |
9 files changed, 150 insertions, 27 deletions
diff --git a/catalog/pub/utils/toscaparser/__init__.py b/catalog/pub/utils/toscaparser/__init__.py index 12df6e89..b94ff4df 100644 --- a/catalog/pub/utils/toscaparser/__init__.py +++ b/catalog/pub/utils/toscaparser/__init__.py @@ -25,7 +25,6 @@ def parse_nsd(path, input_parameters=[], isETSI=True): tosca_obj = EtsiNsdInfoModel(path, input_parameters) else: tosca_obj = SdcServiceModel(path, input_parameters) - strResponse = json.dumps(tosca_obj, default=lambda obj: obj.__dict__) strResponse = strResponse.replace(': null', ': ""') return strResponse @@ -35,7 +34,6 @@ def parse_vnfd(path, input_parameters=[], isETSI=True): if isETSI: tosca_obj = EtsiVnfdInfoModel(path, input_parameters) else: - # SDC VF Model TBD tosca_obj = {} strResponse = json.dumps(tosca_obj, default=lambda obj: obj.__dict__) strResponse = strResponse.replace(': null', ': ""') @@ -46,7 +44,6 @@ def parse_pnfd(path, input_parameters=[], isETSI=True): if isETSI: tosca_obj = PnfdInfoModel(path, input_parameters) else: - # SDC PNF Model TBD tosca_obj = {} strResponse = json.dumps(tosca_obj, default=lambda obj: obj.__dict__) strResponse = strResponse.replace(': null', ': ""') diff --git a/catalog/pub/utils/toscaparser/basemodel.py b/catalog/pub/utils/toscaparser/basemodel.py index f45f7469..7df59ff8 100644 --- a/catalog/pub/utils/toscaparser/basemodel.py +++ b/catalog/pub/utils/toscaparser/basemodel.py @@ -24,6 +24,7 @@ import paramiko from toscaparser.tosca_template import ToscaTemplate from toscaparser.properties import Property from toscaparser.functions import Function, Concat, GetInput, get_function, function_mappings +from catalog.pub.utils.toscaparser.graph import Graph from catalog.pub.utils.toscaparser.dataentityext import DataEntityExt @@ -58,7 +59,8 @@ class BaseInfoModel(object): pass def buildInputs(self, tosca): - return tosca.tpl.get(TOPOLOGY_TEMPLATE, '').get(INPUTS, {}) + topo = tosca.tpl.get(TOPOLOGY_TEMPLATE, None) + return topo.get(INPUTS, {}) if topo else {} def buildToscaTemplate(self, path, params): file_name = None @@ -455,9 +457,37 @@ class BaseInfoModel(object): return False return True - def setTargetValues(dict_target, target_keys, dict_source, source_keys): + def setTargetValues(self, dict_target, target_keys, dict_source, source_keys): i = 0 for item in source_keys: dict_target[target_keys[i]] = dict_source.get(item, "") i += 1 return dict_target + + def get_deploy_graph(self, tosca, relations): + nodes = tosca.graph.nodetemplates + graph = Graph() + for node in nodes: + self._build_deploy_path(node, [], graph, relations) + return graph.to_dict() + + def _build_deploy_path(self, node, node_parent, graph, relations): + graph.add_node(node.name, node_parent) + type_require_set = {} + type_requires = node.type_definition.requirements + for type_require in type_requires: + type_require_set.update(type_require) + for requirement in node.requirements: + for k in requirement.keys(): + if type_require_set[k].get('relationship', None) in relations[0] or type_require_set[k].get('capability', None) in relations[0]: + if isinstance(requirement[k], dict): + next_node = requirement[k].get('node', None) + else: + next_node = requirement[k] + graph.add_node(next_node, [node.name]) + if type_require_set[k].get('relationship', None) in relations[1]: + if isinstance(requirement[k], dict): + next_node = requirement[k].get('node', None) + else: + next_node = requirement[k] + graph.add_node(next_node, [node.name]) diff --git a/catalog/pub/utils/toscaparser/graph.py b/catalog/pub/utils/toscaparser/graph.py new file mode 100644 index 00000000..6d38d12f --- /dev/null +++ b/catalog/pub/utils/toscaparser/graph.py @@ -0,0 +1,74 @@ +# Copyright 2018 ZTE Corporation. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +from collections import deque +from collections import OrderedDict + + +class Graph(object): + + def __init__(self, graph_dict=None): + self.graph = OrderedDict() + if graph_dict: + for node, dep_nodes in graph_dict.iteritems(): + self.add_node(node, dep_nodes) + + def add_node(self, node, dep_nodes): + if node not in self.graph: + self.graph[node] = set() + if isinstance(dep_nodes, list): + for dep_node in dep_nodes: + if dep_node not in self.graph: + self.graph[dep_node] = set() + if dep_node not in self.graph[node]: + self.graph[node].add(dep_node) + + def get_pre_nodes(self, node): + return [k for k in self.graph if node in self.graph[k]] + + def topo_sort(self): + degree = {} + for node in self.graph: + degree[node] = 0 + + for node in self.graph: + for dependent in self.graph[node]: + degree[dependent] += 1 + + queue = deque() + for node in degree: + if degree[node] == 0: + queue.appendleft(node) + + sort_list = [] + while queue: + node = queue.pop() + sort_list.append(node) + for dependent in self.graph[node]: + degree[dependent] -= 1 + if degree[dependent] == 0: + queue.appendleft(dependent) + + if len(sort_list) == len(self.graph): + return sort_list + else: + return None + + def to_dict(self): + dict = {} + for node, dependents in self.graph.iteritems(): + dict[node] = [] + for dep in dependents: + dict[node].append(dep) + return dict diff --git a/catalog/pub/utils/toscaparser/nsdmodel.py b/catalog/pub/utils/toscaparser/nsdmodel.py index 9d72642f..22a75b94 100644 --- a/catalog/pub/utils/toscaparser/nsdmodel.py +++ b/catalog/pub/utils/toscaparser/nsdmodel.py @@ -15,6 +15,7 @@ import functools import logging from catalog.pub.utils.toscaparser.basemodel import BaseInfoModel + logger = logging.getLogger(__name__) SECTIONS = (NS_TYPE, NS_VNF_TYPE, NS_VL_TYPE, NS_PNF_TYPE, NS_NFP_TYPE, NS_VNFFG_TYPE) = \ @@ -25,6 +26,8 @@ SECTIONS = (NS_TYPE, NS_VNF_TYPE, NS_VL_TYPE, NS_PNF_TYPE, NS_NFP_TYPE, NS_VNFFG 'tosca.nodes.nfv.NFP', 'tosca.nodes.nfv.VNFFG') +NFV_NS_RELATIONSHIPS = [["tosca.relationships.nfv.VirtualLinksTo", "tosca.relationships.DependsOn"], []] + class EtsiNsdInfoModel(BaseInfoModel): @@ -44,6 +47,7 @@ class EtsiNsdInfoModel(BaseInfoModel): self.vnffgs = self._get_all_vnffg(tosca.topology_template.groups, types) self.ns_exposed = self._get_all_endpoint_exposed(tosca.topology_template) self.nested_ns = self._get_all_nested_ns(nodeTemplates, types) + self.graph = self.get_deploy_graph(tosca, NFV_NS_RELATIONSHIPS) def _get_all_vnf(self, nodeTemplates, node_types): vnfs = [] @@ -159,7 +163,7 @@ class EtsiNsdInfoModel(BaseInfoModel): ns['ns_id'] = node['name'] ns['description'] = node['description'] ns['properties'] = node['properties'] - ns['networks'] = self._get_networks(node) + ns['networks'] = self._get_networks(node, node_types) nss.append(ns) return nss diff --git a/catalog/pub/utils/toscaparser/servicemodel.py b/catalog/pub/utils/toscaparser/servicemodel.py index 249710d9..bccd417d 100644 --- a/catalog/pub/utils/toscaparser/servicemodel.py +++ b/catalog/pub/utils/toscaparser/servicemodel.py @@ -40,6 +40,8 @@ SDC_PNF_METADATA_SECTIONS = (SDC_PNF_UUID, SDC_PNF_INVARIANTUUID, SDC_PNF_NAME, SDC_PNF_SECTIONS = (SDC_PNF_ID, SDC_PNF_METADATA, SDC_PNF_PROPERTIES, SDC_PNF_DESCRIPTION) = \ ("name", "metadata", "properties", "description") +SERVICE_RELATIONSHIPS = [["tosca.relationships.network.LinksTo", "tosca.relationships.nfv.VirtualLinksTo", "tosca.capabilities.nfv.VirtualLinkable", "tosca.relationships.DependsOn"], []] + class SdcServiceModel(BaseInfoModel): @@ -49,12 +51,14 @@ class SdcServiceModel(BaseInfoModel): def parseModel(self, tosca): self.metadata = self._buildServiceMetadata(tosca) self.inputs = self.buildInputs(tosca) - nodeTemplates = map(functools.partial(self.buildNode, tosca=tosca), tosca.nodetemplates) - types = tosca.topology_template.custom_defs - self.basepath = self.get_base_path(tosca) - self.vnfs = self._get_all_vnf(nodeTemplates, types) - self.pnfs = self._get_all_pnf(nodeTemplates, types) - self.vls = self._get_all_vl(nodeTemplates, types) + if hasattr(tosca, 'nodetemplates'): + nodeTemplates = map(functools.partial(self.buildNode, tosca=tosca), tosca.nodetemplates) + types = tosca.topology_template.custom_defs + self.basepath = self.get_base_path(tosca) + self.vnfs = self._get_all_vnf(nodeTemplates, types) + self.pnfs = self._get_all_pnf(nodeTemplates, types) + self.vls = self._get_all_vl(nodeTemplates, types) + self.graph = self.get_deploy_graph(tosca, SERVICE_RELATIONSHIPS) def _buildServiceMetadata(self, tosca): """ SDC service Meta Format @@ -71,7 +75,8 @@ class SdcServiceModel(BaseInfoModel): namingPolicy: '' """ metadata_temp = self.buildMetadata(tosca) - self.setTargetValues(self.metadata, NS_METADATA_SECTIONS, metadata_temp, SDC_SERVICE_METADATA_SECTIONS) + metadata = {} + return self.setTargetValues(metadata, NS_METADATA_SECTIONS, metadata_temp, SDC_SERVICE_METADATA_SECTIONS) def _get_all_vnf(self, nodeTemplates, node_types): """ SDC Resource Metadata diff --git a/catalog/pub/utils/toscaparser/testdata/ns/ran.csar b/catalog/pub/utils/toscaparser/testdata/ns/ran.csar Binary files differindex acd49093..ad9c7d96 100644 --- a/catalog/pub/utils/toscaparser/testdata/ns/ran.csar +++ b/catalog/pub/utils/toscaparser/testdata/ns/ran.csar diff --git a/catalog/pub/utils/toscaparser/testdata/ns/service-vIMS.csar b/catalog/pub/utils/toscaparser/testdata/ns/service-vIMS.csar Binary files differnew file mode 100644 index 00000000..754dfbc3 --- /dev/null +++ b/catalog/pub/utils/toscaparser/testdata/ns/service-vIMS.csar diff --git a/catalog/pub/utils/toscaparser/tests.py b/catalog/pub/utils/toscaparser/tests.py index cc99991f..0d90e581 100644 --- a/catalog/pub/utils/toscaparser/tests.py +++ b/catalog/pub/utils/toscaparser/tests.py @@ -20,6 +20,7 @@ import shutil from django.test import TestCase from catalog.pub.utils.toscaparser import parse_vnfd, parse_pnfd, parse_nsd +from catalog.pub.utils.toscaparser.graph import Graph logger = logging.getLogger(__name__) @@ -31,11 +32,11 @@ class TestToscaparser(TestCase): def tearDown(self): pass - def test_vnf_parse(self): + def test_vnfd_parse(self): self.remove_temp_dir() csar_path = os.path.dirname(os.path.abspath(__file__)) + "/testdata/vnf" input_parameters = [{"value": "222222", "key": "sdncontroller"}] - vcpe = ["infra", "vbng", "vbrgemu", "vgmux", "vgw"] + vcpe = ["vgw"] for vcpe_part in vcpe: csar_file = ("%s/%s.csar" % (csar_path, vcpe_part)) logger.debug("csar_file:%s", csar_file) @@ -53,20 +54,17 @@ class TestToscaparser(TestCase): def test_nsd_parse(self): self.remove_temp_dir() - pnf_csar = os.path.dirname(os.path.abspath(__file__)) + "/testdata/pnf/ran-du.csar" - nsd_json = parse_nsd(pnf_csar) + ran_csar = os.path.dirname(os.path.abspath(__file__)) + "/testdata/ns/ran.csar" + nsd_json = parse_nsd(ran_csar) metadata = json.loads(nsd_json).get("metadata") - self.assertNotEqual("RAN-NS", metadata.get("template_name", "")) + self.assertEqual("RAN-NS", metadata.get("template_name", "")) - pnf_csar = os.path.dirname(os.path.abspath(__file__)) + "/testdata/vnf/vgw.csar" - nsd_json = parse_nsd(pnf_csar) - metadata = json.loads(nsd_json).get("metadata") - self.assertNotEqual("RAN-NS", metadata.get("template_name", "")) - - # ran_csar = os.path.dirname(os.path.abspath(__file__)) + "/testdata/ns/ran.csar" - # nsd_json = parse_nsd(ran_csar) - # metadata = json.loads(nsd_json).get("metadata") - # self.assertEqual("RAN-NS", metadata.get("template_name", "")) + def test_service_descriptor_parse(self): + self.remove_temp_dir() + service_test_csar = os.path.dirname(os.path.abspath(__file__)) + "/testdata/ns/service-vIMS.csar" + test_json = parse_nsd(service_test_csar, [], False) + metadata = json.loads(test_json).get("metadata") + self.assertEqual("vIMS_v2", metadata.get("name", "")) def remove_temp_dir(self): tempdir = tempfile.gettempdir() @@ -75,3 +73,14 @@ class TestToscaparser(TestCase): path = tempfile.tempdir + "/" + dir if (not os.path.isfile(path)) and os.path.exists(path): shutil.rmtree(tempfile.tempdir + "/" + dir) + + def test_graph(self): + data = { + "cucp": [], + "du": [], + "vl_flat_net": ["cucp", "cuup"], + "vl_ext_net": ["cucp", "cuup"], + "cuup": [] + } + graph = Graph(data) + self.assertEqual(['vl_ext_net', 'vl_flat_net'].sort(), graph.get_pre_nodes("cucp").sort()) diff --git a/catalog/pub/utils/toscaparser/vnfdmodel.py b/catalog/pub/utils/toscaparser/vnfdmodel.py index de29d605..0b6609b8 100644 --- a/catalog/pub/utils/toscaparser/vnfdmodel.py +++ b/catalog/pub/utils/toscaparser/vnfdmodel.py @@ -21,6 +21,9 @@ logger = logging.getLogger(__name__) SECTIONS = (VDU_COMPUTE_TYPE, VNF_VL_TYPE, VDU_CP_TYPE, VDU_STORAGE_TYPE) = \ ('tosca.nodes.nfv.Vdu.Compute', 'tosca.nodes.nfv.VnfVirtualLink', 'tosca.nodes.nfv.VduCp', 'tosca.nodes.nfv.Vdu.VirtualStorage') +NFV_VNF_RELATIONSHIPS = [["tosca.relationships.nfv.VirtualLinksTo", "tosca.relationships.nfv.VduAttachesTo", "tosca.relationships.nfv.AttachesTo", "tosca.relationships.nfv.Vdu.AttachedTo", "tosca.relationships.DependsOn"], + ["tosca.nodes.relationships.VirtualBindsTo", "tosca.relationships.nfv.VirtualBindsTo"]] + class EtsiVnfdInfoModel(BaseInfoModel): @@ -39,6 +42,7 @@ class EtsiVnfdInfoModel(BaseInfoModel): self.vls = self._get_all_vl(nodeTemplates, node_types) self.cps = self._get_all_cp(nodeTemplates, node_types) self.vnf_exposed = self._get_all_endpoint_exposed(tosca.topology_template) + self.graph = self.get_deploy_graph(tosca, NFV_VNF_RELATIONSHIPS) def _get_all_volume_storage(self, nodeTemplates, node_types): rets = [] |