Support INVARIANTID in NSD 15/66115/7
authormaopengzhang <zhang.maopeng1@zte.com.cn>
Wed, 12 Sep 2018 13:00:41 +0000 (21:00 +0800)
committermaopengzhang <zhang.maopeng1@zte.com.cn>
Thu, 13 Sep 2018 14:53:28 +0000 (22:53 +0800)
Support NSD InvariantID in Catalog

Change-Id: Id59531454777d682d3b57ed7c05c25029bc8d8ce
Issue-ID: VFC-635
Signed-off-by: maopengzhang <zhang.maopeng1@zte.com.cn>
15 files changed:
catalog/packages/biz/ns_descriptor.py
catalog/packages/biz/sdc_ns_package.py [moved from catalog/packages/biz/nspackage.py with 74% similarity]
catalog/packages/biz/sdc_vnf_package.py [moved from catalog/packages/biz/vnfpackage.py with 100% similarity]
catalog/packages/tests/test_nspackage.py
catalog/packages/tests/test_vnfpackage.py
catalog/packages/views/catalog_views.py
catalog/pub/database/models.py
catalog/pub/utils/toscaparser/__init__.py
catalog/pub/utils/toscaparser/basemodel.py
catalog/pub/utils/toscaparser/const.py [new file with mode: 0644]
catalog/pub/utils/toscaparser/nsdmodel.py
catalog/pub/utils/toscaparser/pnfmodel.py
catalog/pub/utils/toscaparser/servicemodel.py [new file with mode: 0644]
catalog/pub/utils/toscaparser/tests.py
catalog/pub/utils/toscaparser/vnfdmodel.py

index d781e17..9a484b2 100644 (file)
@@ -25,20 +25,23 @@ from catalog.pub.database.models import NSPackageModel, PnfPackageModel, VnfPack
 from catalog.pub.exceptions import CatalogException, ResourceNotFoundException
 from catalog.pub.utils import fileutil, toscaparser
 from catalog.pub.utils.values import ignore_case_get
+from catalog.pub.utils.toscaparser.const import NS_UUID, NS_INVARIANTUUID, NS_NAME, NS_VERSION, NS_DESIGNER, NS_DESCRIPTION
 
 logger = logging.getLogger(__name__)
 
+METADATA = "metadata"
+
 
 class NsDescriptor(object):
 
     def __init__(self):
         pass
 
-    def create(self, data):
+    def create(self, data, id=None):
         logger.info('Start to create a NSD...')
         user_defined_data = ignore_case_get(data, 'userDefinedData')
         data = {
-            'id': str(uuid.uuid4()),
+            'id': id if id else str(uuid.uuid4()),
             'nsdOnboardingState': PKG_STATUS.CREATED,
             'nsdOperationalState': PKG_STATUS.DISABLED,
             'nsdUsageState': PKG_STATUS.NOT_IN_USE,
@@ -116,31 +119,35 @@ class NsDescriptor(object):
         logger.info('NSD(%s) has been downloaded.' % nsd_info_id)
         return read(local_file_path, start, end)
 
-    def parse_nsd_and_save(self, nsd_info_id, local_file_name):
+    def parse_nsd_and_save(self, nsd_info_id, local_file_name, isETSI=True):
         logger.info('Start to process NSD(%s)...' % nsd_info_id)
         ns_pkgs = NSPackageModel.objects.filter(nsPackageId=nsd_info_id)
         ns_pkgs.update(onboardingState=PKG_STATUS.PROCESSING)
-        nsd_json = toscaparser.parse_nsd(local_file_name)
+
+        nsd_json = toscaparser.parse_nsd(local_file_name, isETSI)
         nsd = json.JSONDecoder().decode(nsd_json)
 
-        nsd_id = nsd["metadata"]["id"]
-        if nsd_id and NSPackageModel.objects.filter(nsdId=nsd_id):
-            logger.info('NSD(%s) already exists.' % nsd_id)
+        nsd_id = nsd[METADATA].get(NS_UUID, "undefined")
+        if nsd_id == "undefined":
+            raise CatalogException("Service UUID(%s) does not exist in metadata." % nsd_id)
+        if NSPackageModel.objects.filter(nsdId=nsd_id):
             raise CatalogException("NSD(%s) already exists." % nsd_id)
 
         for vnf in nsd["vnfs"]:
             vnfd_id = vnf["properties"]["id"]
             pkg = VnfPackageModel.objects.filter(vnfdId=vnfd_id)
             if not pkg:
-                logger.error("VNFD is not distributed.")
+                vnfd_name = vnf.get("vnf_id", "undefined")
+                logger.error("[%s] is not distributed.", vnfd_name)
                 raise CatalogException("VNF package(%s) is not distributed." % vnfd_id)
 
         ns_pkgs.update(
-            nsdId=nsd_id,
-            nsdName=nsd["metadata"].get("name", nsd_id),
-            nsdDesginer=nsd["metadata"].get("vendor", "undefined"),
-            nsdDescription=nsd["metadata"].get("description", ""),
-            nsdVersion=nsd["metadata"].get("version", "undefined"),
+            nsdId=nsd[METADATA].get(NS_UUID, "undefined"),
+            nsdName=nsd[METADATA].get(NS_NAME, "undefined"),
+            nsdDesginer=nsd[METADATA].get(NS_DESIGNER, "undefined"),
+            nsdDescription=nsd[METADATA].get(NS_DESCRIPTION, ""),
+            nsdVersion=nsd[METADATA].get(NS_VERSION, "undefined"),
+            invariantId=nsd[METADATA].get(NS_INVARIANTUUID, "undefined"),
             onboardingState=PKG_STATUS.ONBOARDED,
             operationalState=PKG_STATUS.ENABLED,
             usageState=PKG_STATUS.NOT_IN_USE,
@@ -158,7 +165,7 @@ class NsDescriptor(object):
             'nsdName': ns_pkg.nsdName,
             'nsdVersion': ns_pkg.nsdVersion,
             'nsdDesigner': ns_pkg.nsdDesginer,
-            'nsdInvariantId': None,  # TODO
+            'nsdInvariantId': ns_pkg.invariantId,
             'vnfPkgIds': [],
             'pnfdInfoIds': [],  # TODO
             'nestedNsdInfoIds': [],  # TODO
similarity index 74%
rename from catalog/packages/biz/nspackage.py
rename to catalog/packages/biz/sdc_ns_package.py
index cf1f2cd..b079f3e 100644 (file)
@@ -12,7 +12,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import json
 import logging
 import os
 import sys
@@ -20,16 +19,18 @@ import traceback
 
 from catalog.pub.config.config import CATALOG_ROOT_PATH, CATALOG_URL_PATH, MSB_SERVICE_IP
 from catalog.pub.config.config import REG_TO_MSB_REG_PARAM
-from catalog.pub.database.models import NSPackageModel, VnfPackageModel
+from catalog.pub.database.models import NSPackageModel
 from catalog.pub.exceptions import CatalogException
 from catalog.pub.msapi import sdc
-from catalog.pub.utils import fileutil
 from catalog.pub.utils import toscaparser
+from catalog.packages.biz.ns_descriptor import NsDescriptor
 
 logger = logging.getLogger(__name__)
 
 STATUS_SUCCESS, STATUS_FAILED = "success", "failed"
 
+METADATA = "metadata"
+
 
 def fmt_ns_pkg_rsp(status, desc, error_code="500"):
     return [0, {"status": status, "statusDescription": desc, "errorCode": error_code}]
@@ -40,11 +41,11 @@ def ns_on_distribute(csar_id):
     try:
         ret = NsPackage().on_distribute(csar_id)
     except CatalogException as e:
-        NsPackage().delete_catalog(csar_id)
+        NsPackage().delete_csar(csar_id)
         return fmt_ns_pkg_rsp(STATUS_FAILED, e.message)
     except:
         logger.error(traceback.format_exc())
-        NsPackage().delete_catalog(csar_id)
+        NsPackage().delete_csar(csar_id)
         return fmt_ns_pkg_rsp(STATUS_FAILED, str(sys.exc_info()))
     if ret[0]:
         return fmt_ns_pkg_rsp(STATUS_FAILED, ret[1])
@@ -95,7 +96,7 @@ def parse_nsd(csar_id, inputs):
         if not ns_pkg:
             raise CatalogException("NS CSAR(%s) does not exist." % csar_id)
         csar_path = ns_pkg[0].localFilePath
-        ret = {"model": toscaparser.parse_nsd(csar_path, inputs)}
+        ret = {"model": toscaparser.parse_nsd(csar_path, inputs, False)}
     except CatalogException as e:
         return [1, e.message]
     except Exception as e:
@@ -122,39 +123,17 @@ class NsPackage(object):
         csar_name = "%s.csar" % artifact.get("name", csar_id)
         local_file_name = sdc.download_artifacts(artifact["toscaModelURL"], local_path, csar_name)
 
-        nsd_json = toscaparser.parse_nsd(local_file_name)
-        nsd = json.JSONDecoder().decode(nsd_json)
-
-        nsd_id = nsd["metadata"]["id"]
-        if NSPackageModel.objects.filter(nsdId=nsd_id):
-            raise CatalogException("NSD(%s) already exists." % nsd_id)
-
-        for vnf in nsd["vnfs"]:
-            vnfd_id = vnf["properties"]["id"]
-            pkg = VnfPackageModel.objects.filter(vnfdId=vnfd_id)
-            if not pkg:
-                vnfd_name = vnf.get("vnf_id", "undefined")
-                logger.error("[%s] is not distributed.", vnfd_name)
-                raise CatalogException("VNF package(%s) is not distributed." % vnfd_id)
-
-        NSPackageModel(
-            nsPackageId=csar_id,
-            nsdId=nsd_id,
-            nsdName=nsd["metadata"].get("name", nsd_id),
-            nsdDesginer=nsd["metadata"].get("vendor", "undefined"),
-            nsdDescription=nsd["metadata"].get("description", ""),
-            nsdVersion=nsd["metadata"].get("version", "undefined"),
-            nsPackageUri=csar_name,
-            sdcCsarId=csar_id,
-            localFilePath=local_file_name,
-            nsdModel=nsd_json
-        ).save()
-
+        data = {
+            'userDefinedData': ""
+        }
+        nsd = NsDescriptor()
+        nsd.create(data, csar_id)
+        nsd.parse_nsd_and_save(csar_id, local_file_name, False)
         return [0, "CSAR(%s) distributed successfully." % csar_id]
 
     def delete_csar(self, csar_id):
-        NSPackageModel.objects.filter(nsPackageId=csar_id).delete()
-        self.delete_catalog(csar_id)
+        nsd = NsDescriptor()
+        nsd.delete_single(csar_id)
         return [0, "Delete CSAR(%s) successfully." % csar_id]
 
     def get_csars(self):
@@ -175,6 +154,7 @@ class NsPackage(object):
             package_info["nsdVersion"] = csars[0].nsdVersion
             package_info["csarName"] = csars[0].nsPackageUri
             package_info["nsdModel"] = csars[0].nsdModel
+            package_info["nsdInvariantId"] = csars[0].invariantId
             package_info["downloadUrl"] = "http://%s:%s/%s/%s/%s" % (
                 MSB_SERVICE_IP,
                 REG_TO_MSB_REG_PARAM["nodes"][0]["port"],
@@ -185,7 +165,3 @@ class NsPackage(object):
             raise CatalogException("Ns package[%s] not Found." % csar_id)
 
         return [0, {"csarId": csar_id, "packageInfo": package_info}]
-
-    def delete_catalog(self, csar_id):
-        local_path = os.path.join(CATALOG_ROOT_PATH, csar_id)
-        fileutil.delete_dirs(local_path)
index 9af8231..80b4acb 100644 (file)
@@ -385,7 +385,9 @@ class TestNsPackage(TestCase):
                                         "nsPackageId": "13",
                                         "downloadUrl": "http://127.0.0.1:8806/static/catalog/13/13.csar",
                                         "nsdModel": "",
-                                        "nsdVersion": "2"}},
+                                        "nsdVersion": "2",
+                                        "nsdInvariantId": None
+                                        }},
                        {"csarId": "14",
                         "packageInfo": {"csarName": "14.csar",
                                         "nsdProvider": "3",
@@ -393,7 +395,8 @@ class TestNsPackage(TestCase):
                                         "nsPackageId": "14",
                                         "downloadUrl": "http://127.0.0.1:8806/static/catalog/14/14.csar",
                                         "nsdModel": "",
-                                        "nsdVersion": "3"}}]
+                                        "nsdVersion": "3",
+                                        "nsdInvariantId": None}}]
         self.assertEqual(expect_data, resp.data)
 
     def test_ns_pkg_get_one(self):
@@ -415,7 +418,8 @@ class TestNsPackage(TestCase):
                 "nsdVersion": "4",
                 "csarName": "14.csar",
                 "nsdModel": "",
-                "downloadUrl": "http://127.0.0.1:8806/static/catalog/14/14.csar"}}
+                "downloadUrl": "http://127.0.0.1:8806/static/catalog/14/14.csar",
+                "nsdInvariantId": None}}
         self.assertEqual(expect_data, resp.data)
 
     def test_ns_pkg_get_one_not_found(self):
index a3cbe94..0086129 100644 (file)
@@ -17,7 +17,7 @@ import mock
 from rest_framework.test import APIClient
 from django.test import TestCase
 from rest_framework import status
-from catalog.packages.biz.vnfpackage import NfDistributeThread, NfPkgDeleteThread
+from catalog.packages.biz.sdc_vnf_package import NfDistributeThread, NfPkgDeleteThread
 from catalog.pub.database.models import JobStatusModel, JobModel
 from catalog.pub.database.models import VnfPackageModel
 from catalog.pub.msapi import sdc
index 157af9f..fd7f5d4 100644 (file)
@@ -20,7 +20,7 @@ from drf_yasg.utils import no_body, swagger_auto_schema
 from rest_framework import status
 from rest_framework.decorators import api_view
 from rest_framework.response import Response
-from catalog.packages.biz import vnfpackage, nspackage
+from catalog.packages.biz import sdc_vnf_package, sdc_ns_package
 from catalog.packages.serializers.catalog_serializers import InternalErrorRequestSerializer
 from catalog.packages.serializers.catalog_serializers import NfPackageDistributeRequestSerializer
 from catalog.packages.serializers.catalog_serializers import NfPackageSerializer
@@ -59,7 +59,7 @@ def nspackages_rc(request, *args, **kwargs):
 
     if request.method == 'GET':
         # Gets ns package list
-        ret = nspackage.ns_get_csars()
+        ret = sdc_ns_package.ns_get_csars()
         normal_status = status.HTTP_200_OK
 
         if ret[0] == 0:
@@ -77,7 +77,7 @@ def nspackages_rc(request, *args, **kwargs):
 
         csar_id = ignore_case_get(request.data, "csarId")
         logger.debug("csar_id is %s", csar_id)
-        ret = nspackage.ns_on_distribute(csar_id)
+        ret = sdc_ns_package.ns_on_distribute(csar_id)
         normal_status = status.HTTP_202_ACCEPTED
 
     logger.debug("Leave %s, Return value is %s", fun_name(), ret)
@@ -113,7 +113,7 @@ def nfpackages_rc(request, *args, **kwargs):
         request.method)
     ret, normal_status, response_serializer, validation_error = None, None, None, None
     if request.method == 'GET':
-        ret = vnfpackage.nf_get_csars()
+        ret = sdc_vnf_package.nf_get_csars()
         normal_status = status.HTTP_200_OK
         response_serializer = NfPackagesSerializer(data=ret[1])
     elif request.method == 'POST':
@@ -128,7 +128,7 @@ def nfpackages_rc(request, *args, **kwargs):
         vim_ids = ignore_case_get(request_serivalizer.data, "vimIds")
         lab_vim_id = ignore_case_get(request_serivalizer.data, "labVimId")
         job_id = str(uuid.uuid4())
-        vnfpackage.NfDistributeThread(
+        sdc_vnf_package.NfDistributeThread(
             csar_id, vim_ids, lab_vim_id, job_id).start()
         ret = [0, {"jobId": job_id}]
         normal_status = status.HTTP_202_ACCEPTED
@@ -189,7 +189,7 @@ def ns_rd_csar(request, *args, **kwargs):
                 fun_name(), request.method, csar_id)
     ret, normal_status, response_serializer, validation_error = None, None, None, None
     if request.method == 'GET':
-        ret = nspackage.ns_get_csar(csar_id)
+        ret = sdc_ns_package.ns_get_csar(csar_id)
         normal_status = status.HTTP_200_OK
         if ret[0] == 0:
             response_serializer = NsPackageSerializer(data=ret[1])
@@ -197,7 +197,7 @@ def ns_rd_csar(request, *args, **kwargs):
             if validation_error:
                 return validation_error
     elif request.method == 'DELETE':
-        ret = nspackage.ns_delete_csar(csar_id)
+        ret = sdc_ns_package.ns_delete_csar(csar_id)
         normal_status = status.HTTP_200_OK
     logger.info("Leave %s, Return value is %s", fun_name(), ret)
     if ret[0] != 0:
@@ -248,13 +248,13 @@ def nf_rd_csar(request, *args, **kwargs):
     ret, normal_status, response_serializer, validation_error = None, None, None, None
 
     if request.method == 'GET':
-        ret = vnfpackage.nf_get_csar(csar_id)
+        ret = sdc_vnf_package.nf_get_csar(csar_id)
         normal_status = status.HTTP_200_OK
         response_serializer = NfPackageSerializer(data=ret[1])
 
     elif request.method == 'DELETE':
         job_id = str(uuid.uuid4())
-        vnfpackage.NfPkgDeleteThread(csar_id, job_id).start()
+        sdc_vnf_package.NfPkgDeleteThread(csar_id, job_id).start()
         ret = [0, {"jobId": job_id}]
         normal_status = status.HTTP_202_ACCEPTED
         response_serializer = PostJobResponseSerializer(data=ret[1])
@@ -290,7 +290,7 @@ def ns_model_parser(request, *args, **kwargs):
         fun_name(),
         csar_id,
         inputs)
-    ret = nspackage.parse_nsd(csar_id, inputs)
+    ret = sdc_ns_package.parse_nsd(csar_id, inputs)
     logger.info("Leave %s, Return value is %s", fun_name(), ret)
     if ret[0] != 0:
         return Response(
@@ -323,7 +323,7 @@ def vnf_model_parser(request, *args, **kwargs):
         fun_name(),
         csar_id,
         inputs)
-    ret = vnfpackage.parse_vnfd(csar_id, inputs)
+    ret = sdc_vnf_package.parse_vnfd(csar_id, inputs)
     logger.info("Leave %s, Return value is %s", fun_name(), ret)
     if ret[0] != 0:
         return Response(
index 7d215cc..d150386 100644 (file)
@@ -25,6 +25,7 @@ class NSPackageModel(models.Model):
     usageState = models.CharField(db_column='USAGESTATE', max_length=20, blank=True, null=True)  # usageState
     deletionPending = models.CharField(db_column='DELETIONPENDING', max_length=20, blank=True, null=True)  # deletionPending
     nsdId = models.CharField(db_column='NSDID', max_length=50, blank=True, null=True)
+    invariantId = models.CharField(db_column='INVARIANTID', max_length=50, blank=True, null=True)  # nsdInvariantId
     nsdName = models.CharField(db_column='NSDNAME', max_length=50, blank=True, null=True)
     nsdDesginer = models.CharField(db_column='NSDDESIGNER', max_length=50, null=True, blank=True)
     nsdDescription = models.CharField(db_column='NSDDESCRIPTION', max_length=100, null=True, blank=True)
index b172d67..12df6e8 100644 (file)
@@ -17,24 +17,37 @@ import json
 from catalog.pub.utils.toscaparser.nsdmodel import EtsiNsdInfoModel
 from catalog.pub.utils.toscaparser.vnfdmodel import EtsiVnfdInfoModel
 from catalog.pub.utils.toscaparser.pnfmodel import PnfdInfoModel
+from catalog.pub.utils.toscaparser.servicemodel import SdcServiceModel
 
 
-def parse_nsd(path, input_parameters=[]):
-    tosca_obj = EtsiNsdInfoModel(path, input_parameters)
+def parse_nsd(path, input_parameters=[], isETSI=True):
+    if isETSI:
+        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
 
 
-def parse_vnfd(path, input_parameters=[]):
-    tosca_obj = EtsiVnfdInfoModel(path, input_parameters)
+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', ': ""')
     return strResponse
 
 
-def parse_pnfd(path, input_parameters=[]):
-    tosca_obj = PnfdInfoModel(path, input_parameters)
+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', ': ""')
     return strResponse
index 54e1fd9..f45f746 100644 (file)
@@ -12,7 +12,6 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 
-import copy
 import ftplib
 import json
 import logging
@@ -30,6 +29,24 @@ from catalog.pub.utils.toscaparser.dataentityext import DataEntityExt
 
 logger = logging.getLogger(__name__)
 
+METADATA = "metadata"
+PROPERTIES = "properties"
+DESCRIPTION = "description"
+REQUIREMENTS = "requirements"
+INTERFACES = "interfaces"
+TOPOLOGY_TEMPLATE = "topology_template"
+INPUTS = "inputs"
+CAPABILITIES = "capabilities"
+ATTRIBUTES = "attributes"
+ARTIFACTS = "artifacts"
+DERIVED_FROM = "derived_from"
+
+NODE_NAME = "name"
+NODE_TYPE = "nodeType"
+NODE_ROOT = "tosca.nodes.Root"
+GROUP_TYPE = "groupType"
+GROUPS_ROOT = "tosca.groups.Root"
+
 
 class BaseInfoModel(object):
 
@@ -40,16 +57,8 @@ class BaseInfoModel(object):
     def parseModel(self, tosca):
         pass
 
-    def buildInputs(self, top_inputs):
-        ret = {}
-        for tmpinput in top_inputs:
-            tmp = {}
-            tmp['type'] = tmpinput.type
-            tmp['description'] = tmpinput.description
-            tmp['default'] = tmpinput.default
-
-            ret[tmpinput.name] = tmp
-        return ret
+    def buildInputs(self, tosca):
+        return tosca.tpl.get(TOPOLOGY_TEMPLATE, '').get(INPUTS, {})
 
     def buildToscaTemplate(self, path, params):
         file_name = None
@@ -177,34 +186,31 @@ class BaseInfoModel(object):
             if f is not None:
                 f.close()
 
-    def buidMetadata(self, tosca):
-        if 'metadata' in tosca.tpl:
-            self.metadata = copy.deepcopy(tosca.tpl['metadata'])
-            if tosca.tpl['metadata'].get('UUID', ''):
-                self.metadata['id'] = tosca.tpl['metadata']['UUID']
+    def buildMetadata(self, tosca):
+        return tosca.tpl.get(METADATA, {}) if tosca else {}
 
     def buildNode(self, nodeTemplate, tosca):
         inputs = tosca.inputs
         parsed_params = tosca.parsed_params
         ret = {}
-        ret['name'] = nodeTemplate.name
-        ret['nodeType'] = nodeTemplate.type
-        if 'description' in nodeTemplate.entity_tpl:
-            ret['description'] = nodeTemplate.entity_tpl['description']
+        ret[NODE_NAME] = nodeTemplate.name
+        ret[NODE_TYPE] = nodeTemplate.type
+        if DESCRIPTION in nodeTemplate.entity_tpl:
+            ret[DESCRIPTION] = nodeTemplate.entity_tpl[DESCRIPTION]
         else:
-            ret['description'] = ''
-        if 'metadata' in nodeTemplate.entity_tpl:
-            ret['metadata'] = nodeTemplate.entity_tpl['metadata']
+            ret[DESCRIPTION] = ''
+        if METADATA in nodeTemplate.entity_tpl:
+            ret[METADATA] = nodeTemplate.entity_tpl[METADATA]
         else:
-            ret['metadata'] = ''
+            ret[METADATA] = ''
         props = self.buildProperties_ex(nodeTemplate, tosca.topology_template)
-        ret['properties'] = self.verify_properties(props, inputs, parsed_params)
-        ret['requirements'] = self.build_requirements(nodeTemplate)
+        ret[PROPERTIES] = self.verify_properties(props, inputs, parsed_params)
+        ret[REQUIREMENTS] = self.build_requirements(nodeTemplate)
         self.buildCapabilities(nodeTemplate, inputs, ret)
         self.buildArtifacts(nodeTemplate, inputs, ret)
         interfaces = self.build_interfaces(nodeTemplate)
         if interfaces:
-            ret['interfaces'] = interfaces
+            ret[INTERFACES] = interfaces
         return ret
 
     def buildProperties(self, nodeTemplate, parsed_params):
@@ -219,8 +225,8 @@ class BaseInfoModel(object):
                     tmp = {}
                     tmp[item.value.name] = item.value.input_name
                     properties[k] = tmp
-        if 'attributes' in nodeTemplate.entity_tpl:
-            for k, item in nodeTemplate.entity_tpl['attributes'].items():
+        if ATTRIBUTES in nodeTemplate.entity_tpl:
+            for k, item in nodeTemplate.entity_tpl[ATTRIBUTES].items():
                 properties[k] = str(item)
         return properties
 
@@ -319,13 +325,13 @@ class BaseInfoModel(object):
         return rets
 
     def buildCapabilities(self, nodeTemplate, inputs, ret):
-        capabilities = json.dumps(nodeTemplate.entity_tpl.get('capabilities', None))
+        capabilities = json.dumps(nodeTemplate.entity_tpl.get(CAPABILITIES, None))
         match = re.findall(r'\{"get_input":\s*"([\w|\-]+)"\}', capabilities)
         for m in match:
             aa = [input_def for input_def in inputs if m == input_def.name][0]
             capabilities = re.sub(r'\{"get_input":\s*"([\w|\-]+)"\}', json.dumps(aa.default), capabilities, 1)
         if capabilities != 'null':
-            ret['capabilities'] = json.loads(capabilities)
+            ret[CAPABILITIES] = json.loads(capabilities)
 
     def buildArtifacts(self, nodeTemplate, inputs, ret):
         artifacts = json.dumps(nodeTemplate.entity_tpl.get('artifacts', None))
@@ -334,19 +340,19 @@ class BaseInfoModel(object):
             aa = [input_def for input_def in inputs if m == input_def.name][0]
             artifacts = re.sub(r'\{"get_input":\s*"([\w|\-]+)"\}', json.dumps(aa.default), artifacts, 1)
         if artifacts != 'null':
-            ret['artifacts'] = json.loads(artifacts)
+            ret[ARTIFACTS] = json.loads(artifacts)
 
     def build_interfaces(self, node_template):
-        if 'interfaces' in node_template.entity_tpl:
-            return node_template.entity_tpl['interfaces']
+        if INTERFACES in node_template.entity_tpl:
+            return node_template.entity_tpl[INTERFACES]
         return None
 
     def isNodeTypeX(self, node, nodeTypes, x):
-        node_type = node['nodeType']
+        node_type = node[NODE_TYPE]
         while node_type != x:
             node_type_derived = node_type
-            node_type = nodeTypes[node_type]['derived_from']
-            if node_type == "tosca.nodes.Root" or node_type == node_type_derived:
+            node_type = nodeTypes[node_type][DERIVED_FROM]
+            if node_type == NODE_ROOT or node_type == node_type_derived:
                 return False
         return True
 
@@ -355,7 +361,7 @@ class BaseInfoModel(object):
 
     def getRequirementByNodeName(self, nodeTemplates, storage_name, prop):
         for node in nodeTemplates:
-            if node['name'] == storage_name:
+            if node[NODE_NAME] == storage_name:
                 if prop in node:
                     return node[prop]
 
@@ -371,8 +377,8 @@ class BaseInfoModel(object):
 
     def getRequirementByName(self, node, requirementName):
         requirements = []
-        if 'requirements' in node:
-            for item in node['requirements']:
+        if REQUIREMENTS in node:
+            for item in node[REQUIREMENTS]:
                 for key, value in item.items():
                     if key == requirementName:
                         requirements.append(value)
@@ -410,13 +416,13 @@ class BaseInfoModel(object):
 
     def get_node_by_name(self, node_templates, name):
         for node in node_templates:
-            if node['name'] == name:
+            if node[NODE_NAME] == name:
                 return node
         return None
 
     def getCapabilityByName(self, node, capabilityName):
-        if 'capabilities' in node and capabilityName in node['capabilities']:
-            return node['capabilities'][capabilityName]
+        if CAPABILITIES in node and capabilityName in node[CAPABILITIES]:
+            return node[CAPABILITIES][capabilityName]
         return None
 
     def get_base_path(self, tosca):
@@ -425,22 +431,14 @@ class BaseInfoModel(object):
 
     def build_artifacts(self, node):
         rets = []
-        if 'artifacts' in node and len(node['artifacts']) > 0:
-            artifacts = node['artifacts']
+        if ARTIFACTS in node and len(node[ARTIFACTS]) > 0:
+            artifacts = node[ARTIFACTS]
             for name, value in artifacts.items():
                 ret = {}
+                ret['artifact_name'] = name
+                ret['file'] = value
                 if isinstance(value, dict):
-                    ret['artifact_name'] = name
-                    ret['type'] = value.get('type', '')
-                    ret['file'] = value.get('file', '')
-                    ret['repository'] = value.get('repository', '')
-                    ret['deploy_path'] = value.get('deploy_path', '')
-                else:
-                    ret['artifact_name'] = name
-                    ret['type'] = ''
-                    ret['file'] = value
-                    ret['repository'] = ''
-                    ret['deploy_path'] = ''
+                    ret.update(value)
                 rets.append(ret)
         return rets
 
@@ -449,10 +447,17 @@ class BaseInfoModel(object):
         return self.get_node_by_name(node_templates, req_node_name)
 
     def isGroupTypeX(self, group, groupTypes, x):
-        group_type = group['groupType']
+        group_type = group[GROUP_TYPE]
         while group_type != x:
             group_type_derived = group_type
-            group_type = groupTypes[group_type]['derived_from']
-            if group_type == "tosca.groups.Root" or group_type == group_type_derived:
+            group_type = groupTypes[group_type][DERIVED_FROM]
+            if group_type == GROUPS_ROOT or group_type == group_type_derived:
                 return False
         return True
+
+    def setTargetValues(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
diff --git a/catalog/pub/utils/toscaparser/const.py b/catalog/pub/utils/toscaparser/const.py
new file mode 100644 (file)
index 0000000..1631c12
--- /dev/null
@@ -0,0 +1,27 @@
+# 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.
+
+NS_METADATA_SECTIONS = (NS_UUID, NS_INVARIANTUUID, NS_NAME, NS_VERSION, NS_DESIGNER, NS_DESCRIPTION) =\
+    ("id", "invariant_id", "name", "version", "designer", "description")
+
+PNF_METADATA_SECTIONS = (PNF_UUID, PNF_INVARIANTUUID, PNF_NAME, PNF_METADATA_DESCRIPTION, PNF_VERSION, PNF_PROVIDER) = \
+    ("descriptor_id", "descriptor_invariant_id", "name", "description", "version", "provider")
+PNF_SECTIONS = (PNF_ID, PNF_METADATA, PNF_PROPERTIES, PNF_DESCRIPTION) = \
+    ("pnf_id", "metadata", "properties", "description")
+
+VNF_SECTIONS = (VNF_ID, VNF_METADATA, VNF_PROPERTIES, VNF_DESCRIPTION) = \
+    ("vnf_id", "metadata", "properties", "description")
+
+VL_SECTIONS = (VL_ID, VL_METADATA, VL_PROPERTIES, VL_DESCRIPTION) = \
+    ("vl_id", "metadata", "properties", "description")
index ae99e9b..9d72642 100644 (file)
@@ -32,9 +32,8 @@ class EtsiNsdInfoModel(BaseInfoModel):
         super(EtsiNsdInfoModel, self).__init__(path, params)
 
     def parseModel(self, tosca):
-        self.buidMetadata(tosca)
-        if hasattr(tosca, 'topology_template') and hasattr(tosca.topology_template, 'inputs'):
-            self.inputs = self.buildInputs(tosca.topology_template.inputs)
+        self.metadata = self.buildMetadata(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)
index 7dc99d8..1acf6d4 100644 (file)
@@ -24,10 +24,8 @@ class PnfdInfoModel(BaseInfoModel):
         super(PnfdInfoModel, self).__init__(path, params)
 
     def parseModel(self, tosca):
-        self.buidMetadata(tosca)
-        if hasattr(tosca, 'topology_template') and hasattr(tosca.topology_template, 'inputs'):
-            self.inputs = self.buildInputs(tosca.topology_template.inputs)
-
+        self.metadata = self.buildMetadata(tosca)
+        self.inputs = self.buildInputs(tosca)
         nodeTemplates = map(functools.partial(self.buildNode, tosca=tosca),
                             tosca.nodetemplates)
         self.basepath = self.get_base_path(tosca)
diff --git a/catalog/pub/utils/toscaparser/servicemodel.py b/catalog/pub/utils/toscaparser/servicemodel.py
new file mode 100644 (file)
index 0000000..249710d
--- /dev/null
@@ -0,0 +1,129 @@
+# 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.
+
+import functools
+import logging
+from catalog.pub.utils.toscaparser.const import NS_METADATA_SECTIONS, PNF_METADATA_SECTIONS, VNF_SECTIONS, PNF_SECTIONS, VL_SECTIONS
+from catalog.pub.utils.toscaparser.basemodel import BaseInfoModel
+logger = logging.getLogger(__name__)
+
+SDC_SERVICE_SECTIONS = (SERVICE_TYPE, SRV_DESCRIPTION) = (
+    'org.openecomp.resource.abstract.nodes.service', 'description')
+
+SDC_SERVICE_METADATA_SECTIONS = (SRV_UUID, SRV_INVARIANTUUID, SRV_NAME) = (
+    'UUID', 'invariantUUID', 'name')
+
+SDC_VL = (VL_TYPE) = ('nfv.ext.zte.VL')
+SDC_VL_SECTIONS = (VL_ID, VL_METADATA, VL_PROPERTIES, VL_DESCRIPTION) = \
+    ("name", "metadata", "properties", "description")
+
+SDC_VF = (VF_TYPE, VF_UUID) = \
+    ('org.openecomp.resource.abstract.nodes.VF', 'UUID')
+SDC_VF_SECTIONS = (VF_ID, VF_METADATA, VF_PROPERTIES, VF_DESCRIPTION) = \
+    ("name", "metadata", "properties", "description")
+
+SDC_PNF = (PNF_TYPE) = \
+    ('org.openecomp.resource.abstract.nodes.PNF')
+SDC_PNF_METADATA_SECTIONS = (SDC_PNF_UUID, SDC_PNF_INVARIANTUUID, SDC_PNF_NAME, SDC_PNF_METADATA_DESCRIPTION, SDC_PNF_VERSION) = \
+    ("UUID", "invariantUUID", "name", "description", "version")
+SDC_PNF_SECTIONS = (SDC_PNF_ID, SDC_PNF_METADATA, SDC_PNF_PROPERTIES, SDC_PNF_DESCRIPTION) = \
+    ("name", "metadata", "properties", "description")
+
+
+class SdcServiceModel(BaseInfoModel):
+
+    def __init__(self, path, params):
+        super(SdcServiceModel, self).__init__(path, params)
+
+    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)
+
+    def _buildServiceMetadata(self, tosca):
+        """ SDC service Meta Format
+         invariantUUID: e2618ee1 - a29a - 44c4 - a52a - b718fe1269f4
+         UUID: 2362d14a - 115f - 4a2b - b449 - e2f93c0b7c89
+         name: demoVLB
+         description: catalogservicedescription
+         type: Service
+         category: NetworkL1 - 3
+         serviceType: ''
+         serviceRole: ''
+         serviceEcompNaming: true
+         ecompGeneratedNaming: true
+         namingPolicy: ''
+        """
+        metadata_temp = self.buildMetadata(tosca)
+        self.setTargetValues(self.metadata, NS_METADATA_SECTIONS, metadata_temp, SDC_SERVICE_METADATA_SECTIONS)
+
+    def _get_all_vnf(self, nodeTemplates, node_types):
+        """  SDC Resource Metadata
+        invariantUUID: 9ed46ddc-8eb7-4cb0-a1b6-04136c921af4
+        UUID: b56ba35d-45fb-41e3-b6b8-b4f66917baa1
+        customizationUUID: af0a6e64-967b-476b-87bc-959dcf59c305
+        version: '1.0'
+        name: b7d2fceb-dd11-43cd-a3fa
+        description: vendor software product
+        type: VF
+        category: Generic
+        subcategory: Abstract
+        resourceVendor: b9d9f9f7-7994-4f0d-8104
+        resourceVendorRelease: '1.0'
+        resourceVendorModelNumber: ''
+        """
+        vnfs = []
+        for node in nodeTemplates:
+            if self.isNodeTypeX(node, node_types, VF_TYPE):
+                vnf = {}
+                self.setTargetValues(vnf, VNF_SECTIONS, node, SDC_VF_SECTIONS)
+                if node['metadata']:
+                    vnf['properties']['id'] = node['metadata'].get('UUID', 'undefined')
+                vnf['dependencies'] = self._get_networks(node, node_types)
+                vnf['networks'] = self._get_networks(node, node_types)
+                vnfs.append(vnf)
+        return vnfs
+
+    def _get_all_pnf(self, nodeTemplates, node_types):
+        pnfs = []
+        for node in nodeTemplates:
+            if self.isNodeTypeX(node, node_types, PNF_TYPE):
+                pnf = {}
+                self.setTargetValues(pnf, PNF_SECTIONS, node, SDC_PNF_SECTIONS)
+                self.setTargetValues(pnf['properties'], PNF_METADATA_SECTIONS, node['metadata'], SDC_PNF_METADATA_SECTIONS)
+                pnf['networks'] = self._get_networks(node, node_types)
+                pnfs.append(pnf)
+        return pnfs
+
+    def _get_all_vl(self, nodeTemplates, node_types):
+        vls = []
+        for node in nodeTemplates:
+            if self.isNodeTypeX(node, node_types, VL_TYPE):
+                vl = {}
+                self.setTargetValues(vl, VL_SECTIONS, node, SDC_VL_SECTIONS)
+                vls.append(vl)
+        return vls
+
+    def _get_networks(self, node, node_types):
+        rets = []
+        if 'requirements' in node and self.isNodeTypeX(node, node_types, VF_TYPE):
+            for item in node['requirements']:
+                for key, value in item.items():
+                    rets.append({"key_name": key, "vl_id": self.get_requirement_node_name(value)})
+        return rets
index 36ceb2a..39be7f6 100644 (file)
@@ -14,6 +14,8 @@
 import json
 import os
 import logging
+import tempfile
+import shutil
 
 from django.test import TestCase
 
@@ -30,6 +32,7 @@ class TestToscaparser(TestCase):
         pass
 
     def test_nsd_parse(self):
+        self.remove_temp_dir()
         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")
@@ -46,6 +49,7 @@ class TestToscaparser(TestCase):
         self.assertNotEqual("RAN-NS", metadata.get("template_name", ""))
 
     def test_vnf_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"]
@@ -58,7 +62,16 @@ class TestToscaparser(TestCase):
             self.assertEqual(("vCPE_%s" % vcpe_part), metadata.get("template_name", ""))
 
     def test_pnfd_parse(self):
+        self.remove_temp_dir()
         csar_path = os.path.dirname(os.path.abspath(__file__)) + "/testdata/pnf/ran-du.csar"
         pnfd_json = parse_pnfd(csar_path)
         metadata = json.loads(pnfd_json).get("metadata")
         self.assertEqual("RAN_DU", metadata.get("template_name", ""))
+
+    def remove_temp_dir(self):
+        tempdir = tempfile.gettempdir()
+        for dir in os.listdir(tempdir):
+            if dir.startswith("tmp"):
+                path = tempfile.tempdir + "/" + dir
+                if (not os.path.isfile(path)) and os.path.exists(path):
+                    shutil.rmtree(tempfile.tempdir + "/" + dir)
index 135963b..de29d60 100644 (file)
@@ -28,10 +28,8 @@ class EtsiVnfdInfoModel(BaseInfoModel):
         super(EtsiVnfdInfoModel, self).__init__(path, params)
 
     def parseModel(self, tosca):
-        self.buidMetadata(tosca)
-        if hasattr(tosca, 'topology_template') and hasattr(tosca.topology_template, 'inputs'):
-            self.inputs = self.buildInputs(tosca.topology_template.inputs)
-
+        self.metadata = self.buildMetadata(tosca)
+        self.inputs = self.buildInputs(tosca)
         nodeTemplates = map(functools.partial(self.buildNode, tosca=tosca),
                             tosca.nodetemplates)
         node_types = tosca.topology_template.custom_defs