add sdc service csar related to PNF 60/69560/6
authormaopengzhang <zhang.maopeng1@zte.com.cn>
Sun, 30 Sep 2018 06:18:16 +0000 (14:18 +0800)
committermaopengzhang <zhang.maopeng1@zte.com.cn>
Sun, 30 Sep 2018 08:17:52 +0000 (16:17 +0800)
add sdc service csar related to PNF

Change-Id: I2586fd2397ad9799e2b9a229fcb6117135bf8a94
Issue-ID: VFC-1102
Signed-off-by: maopengzhang <zhang.maopeng1@zte.com.cn>
catalog/pub/utils/toscaparser/__init__.py
catalog/pub/utils/toscaparser/basemodel.py
catalog/pub/utils/toscaparser/graph.py [new file with mode: 0644]
catalog/pub/utils/toscaparser/nsdmodel.py
catalog/pub/utils/toscaparser/servicemodel.py
catalog/pub/utils/toscaparser/testdata/ns/ran.csar
catalog/pub/utils/toscaparser/testdata/ns/service-vIMS.csar [new file with mode: 0644]
catalog/pub/utils/toscaparser/tests.py
catalog/pub/utils/toscaparser/vnfdmodel.py

index 12df6e8..b94ff4d 100644 (file)
@@ -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', ': ""')
index f45f746..7df59ff 100644 (file)
@@ -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 (file)
index 0000000..6d38d12
--- /dev/null
@@ -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
index 9d72642..22a75b9 100644 (file)
@@ -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
 
index 249710d..bccd417 100644 (file)
@@ -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
index acd4909..ad9c7d9 100644 (file)
Binary files a/catalog/pub/utils/toscaparser/testdata/ns/ran.csar and b/catalog/pub/utils/toscaparser/testdata/ns/ran.csar differ
diff --git a/catalog/pub/utils/toscaparser/testdata/ns/service-vIMS.csar b/catalog/pub/utils/toscaparser/testdata/ns/service-vIMS.csar
new file mode 100644 (file)
index 0000000..754dfbc
Binary files /dev/null and b/catalog/pub/utils/toscaparser/testdata/ns/service-vIMS.csar differ
index cc99991..0d90e58 100644 (file)
@@ -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())
index de29d60..0b6609b 100644 (file)
@@ -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 = []