diff options
-rw-r--r-- | catalog/packages/biz/ns_descriptor.py | 35 | ||||
-rw-r--r-- | catalog/packages/biz/sdc_ns_package.py (renamed from catalog/packages/biz/nspackage.py) | 56 | ||||
-rw-r--r-- | catalog/packages/biz/sdc_vnf_package.py (renamed from catalog/packages/biz/vnfpackage.py) | 0 | ||||
-rw-r--r-- | catalog/packages/tests/test_nspackage.py | 10 | ||||
-rw-r--r-- | catalog/packages/tests/test_vnfpackage.py | 2 | ||||
-rw-r--r-- | catalog/packages/views/catalog_views.py | 22 | ||||
-rw-r--r-- | catalog/pub/database/models.py | 1 | ||||
-rw-r--r-- | catalog/pub/utils/toscaparser/__init__.py | 25 | ||||
-rw-r--r-- | catalog/pub/utils/toscaparser/basemodel.py | 123 | ||||
-rw-r--r-- | catalog/pub/utils/toscaparser/const.py | 27 | ||||
-rw-r--r-- | catalog/pub/utils/toscaparser/nsdmodel.py | 5 | ||||
-rw-r--r-- | catalog/pub/utils/toscaparser/pnfmodel.py | 6 | ||||
-rw-r--r-- | catalog/pub/utils/toscaparser/servicemodel.py | 129 | ||||
-rw-r--r-- | catalog/pub/utils/toscaparser/tests.py | 13 | ||||
-rw-r--r-- | catalog/pub/utils/toscaparser/vnfdmodel.py | 6 |
15 files changed, 315 insertions, 145 deletions
diff --git a/catalog/packages/biz/ns_descriptor.py b/catalog/packages/biz/ns_descriptor.py index d781e17b..9a484b20 100644 --- a/catalog/packages/biz/ns_descriptor.py +++ b/catalog/packages/biz/ns_descriptor.py @@ -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 diff --git a/catalog/packages/biz/nspackage.py b/catalog/packages/biz/sdc_ns_package.py index cf1f2cd5..b079f3e3 100644 --- a/catalog/packages/biz/nspackage.py +++ b/catalog/packages/biz/sdc_ns_package.py @@ -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) diff --git a/catalog/packages/biz/vnfpackage.py b/catalog/packages/biz/sdc_vnf_package.py index 55de8096..55de8096 100644 --- a/catalog/packages/biz/vnfpackage.py +++ b/catalog/packages/biz/sdc_vnf_package.py diff --git a/catalog/packages/tests/test_nspackage.py b/catalog/packages/tests/test_nspackage.py index 9af82312..80b4acb4 100644 --- a/catalog/packages/tests/test_nspackage.py +++ b/catalog/packages/tests/test_nspackage.py @@ -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): diff --git a/catalog/packages/tests/test_vnfpackage.py b/catalog/packages/tests/test_vnfpackage.py index a3cbe94a..0086129d 100644 --- a/catalog/packages/tests/test_vnfpackage.py +++ b/catalog/packages/tests/test_vnfpackage.py @@ -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 diff --git a/catalog/packages/views/catalog_views.py b/catalog/packages/views/catalog_views.py index 157af9fd..fd7f5d43 100644 --- a/catalog/packages/views/catalog_views.py +++ b/catalog/packages/views/catalog_views.py @@ -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( diff --git a/catalog/pub/database/models.py b/catalog/pub/database/models.py index 7d215cc7..d150386d 100644 --- a/catalog/pub/database/models.py +++ b/catalog/pub/database/models.py @@ -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) diff --git a/catalog/pub/utils/toscaparser/__init__.py b/catalog/pub/utils/toscaparser/__init__.py index b172d678..12df6e89 100644 --- a/catalog/pub/utils/toscaparser/__init__.py +++ b/catalog/pub/utils/toscaparser/__init__.py @@ -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 diff --git a/catalog/pub/utils/toscaparser/basemodel.py b/catalog/pub/utils/toscaparser/basemodel.py index 54e1fd97..f45f7469 100644 --- a/catalog/pub/utils/toscaparser/basemodel.py +++ b/catalog/pub/utils/toscaparser/basemodel.py @@ -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 index 00000000..1631c128 --- /dev/null +++ b/catalog/pub/utils/toscaparser/const.py @@ -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") diff --git a/catalog/pub/utils/toscaparser/nsdmodel.py b/catalog/pub/utils/toscaparser/nsdmodel.py index ae99e9bb..9d72642f 100644 --- a/catalog/pub/utils/toscaparser/nsdmodel.py +++ b/catalog/pub/utils/toscaparser/nsdmodel.py @@ -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) diff --git a/catalog/pub/utils/toscaparser/pnfmodel.py b/catalog/pub/utils/toscaparser/pnfmodel.py index 7dc99d80..1acf6d4c 100644 --- a/catalog/pub/utils/toscaparser/pnfmodel.py +++ b/catalog/pub/utils/toscaparser/pnfmodel.py @@ -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 index 00000000..249710d9 --- /dev/null +++ b/catalog/pub/utils/toscaparser/servicemodel.py @@ -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 diff --git a/catalog/pub/utils/toscaparser/tests.py b/catalog/pub/utils/toscaparser/tests.py index 36ceb2a3..39be7f64 100644 --- a/catalog/pub/utils/toscaparser/tests.py +++ b/catalog/pub/utils/toscaparser/tests.py @@ -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) diff --git a/catalog/pub/utils/toscaparser/vnfdmodel.py b/catalog/pub/utils/toscaparser/vnfdmodel.py index 135963b2..de29d605 100644 --- a/catalog/pub/utils/toscaparser/vnfdmodel.py +++ b/catalog/pub/utils/toscaparser/vnfdmodel.py @@ -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 |