diff options
-rw-r--r-- | .gitreview | 4 | ||||
-rw-r--r-- | lcm/pub/database/models.py | 2 | ||||
-rw-r--r-- | lcm/pub/utils/toscaparser/__init__.py | 4 | ||||
-rw-r--r-- | lcm/pub/utils/toscaparser/baseinfomodel.py | 105 | ||||
-rw-r--r-- | lcm/pub/utils/toscaparser/basemodel.py | 178 | ||||
-rw-r--r-- | lcm/pub/utils/toscaparser/etsinsdinfomodel.py | 12 | ||||
-rw-r--r-- | lcm/pub/utils/toscaparser/nsdmodel.py | 59 | ||||
-rw-r--r-- | lcm/pub/utils/toscaparser/vnfdmodel.py (renamed from lcm/pub/utils/toscaparser/etsivnfdinfomodel.py) | 0 | ||||
-rw-r--r-- | lcm/urls.py | 1 | ||||
-rw-r--r-- | lcm/workflows/__init__.py | 13 | ||||
-rw-r--r-- | lcm/workflows/tests.py | 31 | ||||
-rw-r--r-- | lcm/workflows/urls.py | 23 | ||||
-rw-r--r-- | lcm/workflows/views.py | 47 | ||||
-rw-r--r-- | resources/dbscripts/mysql/vfc-nfvo-lcm-createobj.sql | 2 |
14 files changed, 362 insertions, 119 deletions
diff --git a/.gitreview b/.gitreview new file mode 100644 index 00000000..136320da --- /dev/null +++ b/.gitreview @@ -0,0 +1,4 @@ +[gerrit] +host=gerrit.onap.org +port=29418 +project=vfc/nfvo/lcm
\ No newline at end of file diff --git a/lcm/pub/database/models.py b/lcm/pub/database/models.py index 9071c8e7..0f26c238 100644 --- a/lcm/pub/database/models.py +++ b/lcm/pub/database/models.py @@ -308,5 +308,7 @@ class WFPlanModel(models.Model): deployed_id = models.CharField(db_column='DEPLOYEDID', max_length=255, primary_key=True) process_id = models.CharField(db_column='PROCESSID', max_length=255) + status = models.CharField(db_column='STATUS', max_length=255) + message = models.CharField(db_column='MESSAGE', max_length=1024) plan_name = models.CharField(db_column='PLANNAME', max_length=255) diff --git a/lcm/pub/utils/toscaparser/__init__.py b/lcm/pub/utils/toscaparser/__init__.py index 4d9064ed..b224f4ef 100644 --- a/lcm/pub/utils/toscaparser/__init__.py +++ b/lcm/pub/utils/toscaparser/__init__.py @@ -14,8 +14,8 @@ import json from lcm.pub.utils.toscaparser.convert import convert_nsd, convert_vnfd -from lcm.pub.utils.toscaparser.etsinsdinfomodel import EtsiNsdInfoModel -from lcm.pub.utils.toscaparser.etsivnfdinfomodel import EtsiVnfdInfoModel +from lcm.pub.utils.toscaparser.nsdmodel import EtsiNsdInfoModel +from lcm.pub.utils.toscaparser.vnfdmodel import EtsiVnfdInfoModel from lcm.pub.utils.toscaparser.parser import parse_nsd_model, parse_vnfd_model diff --git a/lcm/pub/utils/toscaparser/baseinfomodel.py b/lcm/pub/utils/toscaparser/baseinfomodel.py deleted file mode 100644 index 0e186f60..00000000 --- a/lcm/pub/utils/toscaparser/baseinfomodel.py +++ /dev/null @@ -1,105 +0,0 @@ -import os -import urllib - -from toscaparser.tosca_template import ToscaTemplate - -from lcm.pub.utils.toscaparser.dataentityext import DataEntityExt - - -class BaseInfoModel(object): - def __init__(self, path, params): - pass - - def buildToscaTemplate(self, path, params): - file_name = None - try: - file_name = self._check_download_file(path) - valid_params = self._validate_input_params(file_name, params) - return self._create_tosca_template(file_name, valid_params) - finally: - if file_name != None and file_name != path and os.path.exists(file_name): - try: - os.remove(file_name) - except Exception, e: - pass - - def _validate_input_params(self, path, params): - valid_params = {} - if params and len(params) > 0: - tmp = self._create_tosca_template(path, None) - for key,value in params.items(): - if hasattr(tmp, 'inputs') and len(tmp.inputs) > 0: - for input_def in tmp.inputs: - if (input_def.name == key): - valid_params[key] = DataEntityExt.validate_datatype(input_def.type, value) - - return valid_params - - def _create_tosca_template(self, file_name, valid_params): - tosca_tpl = None - try: - tosca_tpl = ToscaTemplate(file_name, valid_params) - print "-----------------------------" - print '\n'.join(['%s:%s' % item for item in tosca_tpl.__dict__.items()]) - print "-----------------------------" - return tosca_tpl - finally: - pass - # if tosca_tpl != None and hasattr(tosca_tpl, "temp_dir") and os.path.exists(tosca_tpl.temp_dir): - # try: - # shutil.rmtree(tosca_tpl.temp_dir) - # except Exception, e: - # pass - # # if tosca_tpl != None and tosca_tpl.temp_dir != None and os.path.exists(tosca_tpl.temp_dir): - # # try: - # # shutil.rmtree(tosca_tpl.temp_dir) - # # except Exception, e: - # # pass - - def _check_download_file(self, path): - if (path.startswith("ftp") or path.startswith("sftp")): - return self.downloadFileFromFtpServer(path) - elif (path.startswith("http")): - return self.download_file_from_httpserver(path) - return path - - def download_file_from_httpserver(self, path): - path = path.encode("utf-8") - tmps = str.split(path, '/') - localFileName = tmps[len(tmps) - 1] - urllib.urlretrieve(path, localFileName) - return localFileName - - def downloadFileFromFtpServer(self, path): - path = path.encode("utf-8") - tmp = str.split(path, '://') - protocol = tmp[0] - tmp = str.split(tmp[1], ':') - if len(tmp) == 2: - userName = tmp[0] - tmp = str.split(tmp[1], '@') - userPwd = tmp[0] - index = tmp[1].index('/') - hostIp = tmp[1][0:index] - remoteFileName = tmp[1][index:len(tmp[1])] - if protocol.lower() == 'ftp': - hostPort = 21 - else: - hostPort = 22 - - if len(tmp) == 3: - userName = tmp[0] - userPwd = str.split(tmp[1], '@')[0] - hostIp = str.split(tmp[1], '@')[1] - index = tmp[2].index('/') - hostPort = tmp[2][0:index] - remoteFileName = tmp[2][index:len(tmp[2])] - - localFileName = str.split(remoteFileName, '/') - localFileName = localFileName[len(localFileName) - 1] - - if protocol.lower() == 'sftp': - self.sftp_get(userName, userPwd, hostIp, hostPort, remoteFileName, localFileName) - else: - self.ftp_get(userName, userPwd, hostIp, hostPort, remoteFileName, localFileName) - return localFileName diff --git a/lcm/pub/utils/toscaparser/basemodel.py b/lcm/pub/utils/toscaparser/basemodel.py new file mode 100644 index 00000000..143120e6 --- /dev/null +++ b/lcm/pub/utils/toscaparser/basemodel.py @@ -0,0 +1,178 @@ +import copy +import json +import os +import re +import shutil +import urllib + +from toscaparser.functions import GetInput +from toscaparser.tosca_template import ToscaTemplate + +from lcm.pub.utils.toscaparser.dataentityext import DataEntityExt + + +class BaseInfoModel(object): + + def buildToscaTemplate(self, path, params): + file_name = None + try: + file_name = self._check_download_file(path) + valid_params = self._validate_input_params(file_name, params) + return self._create_tosca_template(file_name, valid_params) + finally: + if file_name != None and file_name != path and os.path.exists(file_name): + try: + os.remove(file_name) + except Exception, e: + pass + + def _validate_input_params(self, path, params): + valid_params = {} + if params and len(params) > 0: + tmp = self._create_tosca_template(path, None) + for key,value in params.items(): + if hasattr(tmp, 'inputs') and len(tmp.inputs) > 0: + for input_def in tmp.inputs: + if (input_def.name == key): + valid_params[key] = DataEntityExt.validate_datatype(input_def.type, value) + + return valid_params + + def _create_tosca_template(self, file_name, valid_params): + tosca_tpl = None + try: + tosca_tpl = ToscaTemplate(file_name, valid_params) + print "-----------------------------" + print '\n'.join(['%s:%s' % item for item in tosca_tpl.__dict__.items()]) + print "-----------------------------" + return tosca_tpl + finally: + if tosca_tpl != None and hasattr(tosca_tpl, "temp_dir") and os.path.exists(tosca_tpl.temp_dir): + try: + shutil.rmtree(tosca_tpl.temp_dir) + except Exception, e: + pass + + def _check_download_file(self, path): + if (path.startswith("ftp") or path.startswith("sftp")): + return self.downloadFileFromFtpServer(path) + elif (path.startswith("http")): + return self.download_file_from_httpserver(path) + return path + + def download_file_from_httpserver(self, path): + path = path.encode("utf-8") + tmps = str.split(path, '/') + localFileName = tmps[len(tmps) - 1] + urllib.urlretrieve(path, localFileName) + return localFileName + + def downloadFileFromFtpServer(self, path): + path = path.encode("utf-8") + tmp = str.split(path, '://') + protocol = tmp[0] + tmp = str.split(tmp[1], ':') + if len(tmp) == 2: + userName = tmp[0] + tmp = str.split(tmp[1], '@') + userPwd = tmp[0] + index = tmp[1].index('/') + hostIp = tmp[1][0:index] + remoteFileName = tmp[1][index:len(tmp[1])] + if protocol.lower() == 'ftp': + hostPort = 21 + else: + hostPort = 22 + + if len(tmp) == 3: + userName = tmp[0] + userPwd = str.split(tmp[1], '@')[0] + hostIp = str.split(tmp[1], '@')[1] + index = tmp[2].index('/') + hostPort = tmp[2][0:index] + remoteFileName = tmp[2][index:len(tmp[2])] + + localFileName = str.split(remoteFileName, '/') + localFileName = localFileName[len(localFileName) - 1] + + if protocol.lower() == 'sftp': + self.sftp_get(userName, userPwd, hostIp, hostPort, remoteFileName, localFileName) + else: + self.ftp_get(userName, userPwd, hostIp, hostPort, remoteFileName, localFileName) + return localFileName + + def buidMetadata(self, tosca): + if 'metadata' in tosca.tpl: + self.metadata = copy.deepcopy(tosca.tpl['metadata']) + + def buildProperties(self, nodeTemplate, parsed_params): + properties = {} + isMappingParams = parsed_params and len(parsed_params) > 0 + for k, item in nodeTemplate.get_properties().items(): + properties[k] = item.value + if isinstance(item.value, GetInput): + if item.value.result() and isMappingParams: + properties[k] = DataEntityExt.validate_datatype(item.type, item.value.result()) + else: + 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(): + properties[k] = str(item) + return properties + + def buildProperties(self, nodeTemplate, parsed_params): + properties = {} + isMappingParams = parsed_params and len(parsed_params) > 0 + for k, item in nodeTemplate.get_properties().items(): + properties[k] = item.value + if isinstance(item.value, GetInput): + if item.value.result() and isMappingParams: + properties[k] = DataEntityExt.validate_datatype(item.type, item.value.result()) + else: + 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(): + properties[k] = str(item) + return properties + + + def verify_properties(self, props, inputs, parsed_params): + ret_props = {} + if (props and len(props) > 0): + for key, value in props.items(): + ret_props[key] = self._verify_value(value, inputs, parsed_params) + # if isinstance(value, str): + # ret_props[key] = self._verify_string(inputs, parsed_params, value); + # continue + # if isinstance(value, list): + # ret_props[key] = map(lambda x: self._verify_dict(inputs, parsed_params, x), value) + # continue + # if isinstance(value, dict): + # ret_props[key] = self._verify_map(inputs, parsed_params, value) + # continue + # ret_props[key] = value + return ret_props + + def build_requirements(self, node_template): + rets = [] + for req in node_template.requirements: + for req_name, req_value in req.items(): + if (isinstance(req_value, dict)): + if ('node' in req_value and req_value['node'] not in node_template.templates): + continue # No target requirement for aria parser, not add to result. + rets.append({req_name : req_value}) + return rets + + def buildCapabilities(self, nodeTemplate, inputs, ret): + 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)
\ No newline at end of file diff --git a/lcm/pub/utils/toscaparser/etsinsdinfomodel.py b/lcm/pub/utils/toscaparser/etsinsdinfomodel.py deleted file mode 100644 index 693a7fdd..00000000 --- a/lcm/pub/utils/toscaparser/etsinsdinfomodel.py +++ /dev/null @@ -1,12 +0,0 @@ -from lcm.pub.utils.toscaparser.baseinfomodel import BaseInfoModel - - -class EtsiNsdInfoModel(BaseInfoModel): - - def __init__(self, path, params): - tosca = self.buildToscaTemplate(path, params) - self.parseModel(tosca) - - - def parseModel(self, tosca): - pass
\ No newline at end of file diff --git a/lcm/pub/utils/toscaparser/nsdmodel.py b/lcm/pub/utils/toscaparser/nsdmodel.py new file mode 100644 index 00000000..f0512068 --- /dev/null +++ b/lcm/pub/utils/toscaparser/nsdmodel.py @@ -0,0 +1,59 @@ +import functools + +from lcm.pub.utils.toscaparser.basemodel import BaseInfoModel + + +class EtsiNsdInfoModel(BaseInfoModel): + + def __init__(self, path, params): + tosca = self.buildToscaTemplate(path, params) + self.parseModel(tosca) + + 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) + + nodeTemplates = map(functools.partial(self.buildNode, inputs=tosca.inputs, parsed_params=tosca.parsed_params), + tosca.nodetemplates) + + # self.vnfs = self._get_all_vnf(nodeTemplates) + # self.pnfs = self._get_all_pnf(nodeTemplates) + # self.vls = self.get_all_vl(nodeTemplates) + # self.cps = self.get_all_cp(nodeTemplates) + # self.routers = self.get_all_router(nodeTemplates) + # self.fps = self._get_all_fp(nodeTemplates) + # self.vnffgs = self._get_all_vnffg(tosca.topology_template.groups) + # self.server_groups = self.get_all_server_group(tosca.topology_template.groups) + # self.ns_exposed = self.get_all_endpoint_exposed(tosca.topology_template) + # self.policies = self._get_policies_scaling(tosca.topology_template.policies) + # self.ns_flavours = self.get_all_flavour(tosca.topology_template.groups) + # self.nested_ns = self.get_all_nested_ns(nodeTemplates) + + 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 buildNode(self, nodeTemplate, inputs, parsed_params): + ret ={} + ret['name'] = nodeTemplate.name + ret['nodeType'] = nodeTemplate.type + if 'description' in nodeTemplate.entity_tpl: + ret['description'] = nodeTemplate.entity_tpl['description'] + else: + ret['description'] = '' + props = self.buildProperties(nodeTemplate, parsed_params) + 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 + return ret diff --git a/lcm/pub/utils/toscaparser/etsivnfdinfomodel.py b/lcm/pub/utils/toscaparser/vnfdmodel.py index 39e9a18b..39e9a18b 100644 --- a/lcm/pub/utils/toscaparser/etsivnfdinfomodel.py +++ b/lcm/pub/utils/toscaparser/vnfdmodel.py diff --git a/lcm/urls.py b/lcm/urls.py index c9a808b3..9471def9 100644 --- a/lcm/urls.py +++ b/lcm/urls.py @@ -23,6 +23,7 @@ urlpatterns = [ url(r'^', include('lcm.ns.sfcs.urls')), url(r'^', include('lcm.ns.urls')), url(r'^', include('lcm.jobs.urls')), + url(r'^', include('lcm.workflows.urls')), ] # regist to MSB when startup diff --git a/lcm/workflows/__init__.py b/lcm/workflows/__init__.py new file mode 100644 index 00000000..c7b6818e --- /dev/null +++ b/lcm/workflows/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2017 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. diff --git a/lcm/workflows/tests.py b/lcm/workflows/tests.py new file mode 100644 index 00000000..31651624 --- /dev/null +++ b/lcm/workflows/tests.py @@ -0,0 +1,31 @@ +# Copyright 2017 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 unittest +import json +from django.test import Client +from rest_framework import status + + +class WorkflowViewTest(unittest.TestCase): + def setUp(self): + self.client = Client() + + def tearDown(self): + pass + + def test_deploy_workflow(self): + response = self.client.post("/api/nslcm/v1/workflow", + {"filePath": "/home/init.zip"}, format='json') + self.assertEqual(status.HTTP_202_ACCEPTED, response.status_code, response.content) diff --git a/lcm/workflows/urls.py b/lcm/workflows/urls.py new file mode 100644 index 00000000..2545ffba --- /dev/null +++ b/lcm/workflows/urls.py @@ -0,0 +1,23 @@ +# Copyright 2017 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 django.conf.urls import url +from rest_framework.urlpatterns import format_suffix_patterns +from lcm.workflows import views + +urlpatterns = [ + url(r'^api/nslcm/v1/workflow$', views.deploy_workflow, name='deploy_workflow'), +] + +urlpatterns = format_suffix_patterns(urlpatterns) diff --git a/lcm/workflows/views.py b/lcm/workflows/views.py new file mode 100644 index 00000000..92f029fb --- /dev/null +++ b/lcm/workflows/views.py @@ -0,0 +1,47 @@ +# Copyright 2017 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 logging +import traceback + +from rest_framework import status +from rest_framework.decorators import api_view +from rest_framework.response import Response + +from lcm.pub.database import models +from lcm.pub.utils.syscomm import fun_name +from lcm.pub.utils.values import ignore_case_get + + +logger = logging.getLogger(__name__) + + +@api_view(http_method_names=['POST']) +def deploy_workflow(request, *args, **kwargs): + logger.debug("Enter %s", fun_name()) + file_path = ignore_case_get(request.data, "filePath") + logger.debug("file_path is %s", file_path) + ret = None + try: + ret = [0, "TODO"] + except: + logger.error(traceback.format_exc()) + return Response(data={'error': str(sys.exc_info())}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + logger.debug("Leave %s, Return value is %s", fun_name(), ret) + return Response(data=ret[1], status=status.HTTP_202_ACCEPTED) + + + + + diff --git a/resources/dbscripts/mysql/vfc-nfvo-lcm-createobj.sql b/resources/dbscripts/mysql/vfc-nfvo-lcm-createobj.sql index 36c9ffb6..b2588f05 100644 --- a/resources/dbscripts/mysql/vfc-nfvo-lcm-createobj.sql +++ b/resources/dbscripts/mysql/vfc-nfvo-lcm-createobj.sql @@ -273,5 +273,7 @@ DROP TABLE IF EXISTS NFVO_WF_PLAN; CREATE TABLE NFVO_WF_PLAN ( `DEPLOYEDID` varchar(255) NOT NULL PRIMARY KEY, `PROCESSID` varchar(255) NOT NULL, + `STATUS` varchar(255) NOT NULL, + `MESSAGE` varchar(1024) NULL, `PLANNAME` varchar(255) NOT NULL ); |