diff options
author | 2017-07-06 09:14:50 +0800 | |
---|---|---|
committer | 2017-07-06 09:14:50 +0800 | |
commit | bcf49a37cdf5da54fba35e65bdffb9d95babfc4c (patch) | |
tree | 6437dc7eed90bdcb8d3546900a755f3c225ebd30 | |
parent | fbbe35d470bc4aea03f3bb3e2d75eff89283bcb3 (diff) |
Add seed codes of nfvo
Change-Id: Ib788826e983b75b1800ea9fc7715dc8b8044d38d
Issue-Id: VFC-52
Signed-off-by: fujinhua <fu.jinhua@zte.com.cn>
152 files changed, 22295 insertions, 0 deletions
diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..821f62a6 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +logs/*.log +*.pyc
\ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 00000000..7b59118f --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +# Copyright 2016 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. + +# Micro service of network service life cycle management. diff --git a/assembly.xml b/assembly.xml new file mode 100644 index 00000000..07de6794 --- /dev/null +++ b/assembly.xml @@ -0,0 +1,56 @@ +<!-- + Copyright 2016 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. +--> +<assembly xmlns="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/plugins/maven-assembly-plugin/assembly/1.1.0 http://maven.apache.org/xsd/assembly-1.1.0.xsd"> + <id>lcm</id> + <formats> + <format>zip</format> + </formats> + <fileSets> + <fileSet> + <directory>lcm</directory> + <outputDirectory>/lcm</outputDirectory> + <includes> + <include>**/*.py</include> + <include>**/*.json</include> + <include>**/*.xml</include> + <include>**/*.wsdl</include> + <include>**/*.xsd</include> + <include>**/*.bpel</include> + </includes> + </fileSet> + <fileSet> + <directory>logs</directory> + <outputDirectory>/logs</outputDirectory> + <includes> + <include>*.txt</include> + </includes> + </fileSet> + <fileSet> + <directory>.</directory> + <outputDirectory>/</outputDirectory> + <includes> + <include>*.py</include> + <include>*.txt</include> + <include>*.sh</include> + <include>*.ini</include> + <include>*.md</include> + </includes> + </fileSet> + </fileSets> + <baseDirectory>nfvo/lcm</baseDirectory> +</assembly> diff --git a/initialize.sh b/initialize.sh new file mode 100644 index 00000000..8f6a0003 --- /dev/null +++ b/initialize.sh @@ -0,0 +1,16 @@ +#!/bin/bash +# Copyright 2016 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. + +pip install -r requirements.txt diff --git a/lcm/__init__.py b/lcm/__init__.py new file mode 100644 index 00000000..5580cc3d --- /dev/null +++ b/lcm/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2016 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/jobs/__init__.py b/lcm/jobs/__init__.py new file mode 100644 index 00000000..5580cc3d --- /dev/null +++ b/lcm/jobs/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2016 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/jobs/job_get.py b/lcm/jobs/job_get.py new file mode 100644 index 00000000..a85bf8f1 --- /dev/null +++ b/lcm/jobs/job_get.py @@ -0,0 +1,46 @@ +# Copyright 2016 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 + +from lcm.pub.utils.jobutil import JobUtil + +logger = logging.getLogger(__name__) + + +class GetJobInfoService(object): + def __init__(self, job_id, response_id=0): + self.job_id = job_id + self.response_id = response_id if response_id else 0 + + def do_biz(self): + #logger.info("get job info, job_id=:%s, response_id=:%s" % (self.job_id, self.response_id)) + jobs = JobUtil.query_job_status(self.job_id, self.response_id) + if not jobs: + return {"jobId": self.job_id} + ret = { + "jobId": self.job_id, + "responseDescriptor": { + "status": jobs[0].status, + "progress": jobs[0].progress, + "statusDescription": jobs[0].descp, + "errorCode": jobs[0].errcode, + "responseId": jobs[0].indexid, + "responseHistoryList": [ + { + "status": job.status, + "progress": job.progress, + "statusDescription": job.descp, + "errorCode": job.errcode, + "responseId": job.indexid} for job in jobs[1:]]}} + return ret diff --git a/lcm/jobs/tests/__init__.py b/lcm/jobs/tests/__init__.py new file mode 100644 index 00000000..5580cc3d --- /dev/null +++ b/lcm/jobs/tests/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2016 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/jobs/tests/tests.py b/lcm/jobs/tests/tests.py new file mode 100644 index 00000000..76f9a8d5 --- /dev/null +++ b/lcm/jobs/tests/tests.py @@ -0,0 +1,32 @@ +# Copyright 2016 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.test import TestCase, Client +from rest_framework import status + +from lcm.pub.database.models import JobModel, JobStatusModel + + +class JobsViewTest(TestCase): + def setUp(self): + self.job_id = 'test_job_id' + self.client = Client() + + def tearDown(self): + JobModel.objects.all().delete() + + def test_job(self): + JobModel(jobid=self.job_id, jobtype='VNF', jobaction='INST', resid='1').save() + JobStatusModel(indexid=1, jobid=self.job_id, status='inst', progress=20, descp='inst').save() + response = self.client.get("/openoapi/nslcm/v1/jobs/%s" % self.job_id) + self.failUnlessEqual(status.HTTP_200_OK, response.status_code) diff --git a/lcm/jobs/urls.py b/lcm/jobs/urls.py new file mode 100644 index 00000000..2a163db9 --- /dev/null +++ b/lcm/jobs/urls.py @@ -0,0 +1,23 @@ +# Copyright 2016 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 patterns, url +from rest_framework.urlpatterns import format_suffix_patterns + +from lcm.jobs.views import JobView + +urlpatterns = patterns('', + url(r'^openoapi/nslcm/v1/jobs/(?P<job_id>[0-9a-zA-Z_-]+)$', JobView.as_view()), + ) + +urlpatterns = format_suffix_patterns(urlpatterns) diff --git a/lcm/jobs/views.py b/lcm/jobs/views.py new file mode 100644 index 00000000..2c971d49 --- /dev/null +++ b/lcm/jobs/views.py @@ -0,0 +1,44 @@ +# Copyright 2016 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 + +from rest_framework.response import Response +from rest_framework.views import APIView + +from lcm.jobs.job_get import GetJobInfoService +from lcm.pub.utils.jobutil import JobUtil +from lcm.pub.utils.values import ignore_case_get + +logger = logging.getLogger(__name__) + +class JobView(APIView): + def get(self, request, job_id): + response_id = ignore_case_get(request.META, 'responseId') + ret = GetJobInfoService(job_id, response_id).do_biz() + return Response(data=ret) + + def post(self, request, job_id): + try: + logger.debug("Enter JobView:post, %s, %s ", job_id, request.data) + jobs = JobUtil.query_job_status(job_id) + if len(jobs) > 0 and jobs[-1].errcode == '255': + return Response(data={'result': 'ok'}) + progress = request.data.get('progress') + desc = request.data.get('desc', '%s' % progress) + errcode = '0' if request.data.get('errcode') in ('true', 'active') else '255' + logger.debug("errcode=%s", errcode) + JobUtil.add_job_status(job_id, progress, desc, error_code=errcode) + return Response(data={'result': 'ok'}) + except Exception as e: + return Response(data={'result': 'error', 'msg': e.message}) diff --git a/lcm/ns/__init__.py b/lcm/ns/__init__.py new file mode 100644 index 00000000..5580cc3d --- /dev/null +++ b/lcm/ns/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2016 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/ns/const.py b/lcm/ns/const.py new file mode 100644 index 00000000..a0e9973d --- /dev/null +++ b/lcm/ns/const.py @@ -0,0 +1,19 @@ +# Copyright 2016-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 lcm.pub.utils.enumutil import enum + +OWNER_TYPE = enum(VNF=0, VNFM=1, NS=2) + +NS_INST_STATUS = enum(EMPTY='empty', INSTANTIATING='instantiating', TERMINATING='terminating', + ACTIVE='active', FAILED='failed', INACTIVE='inactive', UPDATING='updating', SCALING='scaling') diff --git a/lcm/ns/data/file.json b/lcm/ns/data/file.json new file mode 100644 index 00000000..520f334a --- /dev/null +++ b/lcm/ns/data/file.json @@ -0,0 +1,821 @@ +[ + { + "connections": [], + "type": "", + "id": "element4" + }, + { + "name": "createVL", + "position": { + "left": 141.5, + "top": 157 + }, + "type": "RestTask", + "microservice": "nslcm", + "connections": [ + "element169" + ], + "id": "element7", + "publicInterface": "vls Resource.2", + "method": "POST", + "accept": "application/json", + "contentType": "application/json", + "url": "http://127.0.0.1:80/openoapi/nslcm/v1/ns/vls", + "path": {}, + "query": {}, + "body": { + "vnfInstanceData": { + "type": "string", + "value": "" + }, + "additionalParamForVnf": { + "type": "plan", + "value": "StartEvent.object_additionalParamForVnf" + }, + "nsInstanceId": { + "type": "plan", + "value": "StartEvent.nsInstanceId" + }, + "flavourId": { + "type": "string", + "value": "" + }, + "pnfInfo": { + "type": "string", + "value": "" + }, + "extNSVirtualLink": { + "type": "string", + "value": "" + }, + "additionalParamForNs": { + "type": "plan", + "value": "StartEvent.object_additionalParamForNs" + }, + "context": { + "type": "plan", + "value": "StartEvent.object_context" + }, + "sapData": { + "type": "string", + "value": "" + }, + "nestedNsInstanceId": { + "type": "string", + "value": "" + }, + "jobId": { + "type": "string", + "value": "" + }, + "locationConstraints": { + "type": "string", + "value": "" + }, + "vlIndex": { + "type": "plan", + "value": "StartEvent.vl_index" + } + }, + "output": { + "vlId": { + "type": "topology", + "value": "" + }, + "result": { + "type": "plan", + "value": "StartEvent.vl_status" + }, + "detail": { + "type": "topology", + "value": "" + } + } + }, + { + "name": "StartEvent", + "position": { + "left": 64, + "top": 74 + }, + "type": "StartEvent", + "connections": [ + "element173" + ], + "id": "element27", + "output": { + "vlCount": { + "type": "string", + "value": "" + }, + "vnfCount": { + "type": "string", + "value": "" + }, + "sfcCount": { + "type": "string", + "value": "" + }, + "object_context": { + "type": "string", + "value": "" + }, + "nsInstanceId": { + "type": "string", + "value": "" + }, + "object_additionalParamForNs": { + "type": "string", + "value": "" + }, + "object_additionalParamForVnf": { + "type": "string", + "value": "" + }, + "jobId": { + "type": "string", + "value": "" + }, + "sdnControllerId": { + "type": "string", + "value": "" + } + }, + "variable": { + "vl_index": { + "type": "string", + "value": "1" + }, + "vnf_index": { + "type": "string", + "value": "1" + }, + "sfc_index": { + "type": "string", + "value": "1" + }, + "vnf_status": { + "type": "string", + "value": "active" + }, + "sfc_status": { + "type": "string", + "value": "active" + }, + "vl_status": { + "type": "string", + "value": "active" + }, + "exec_status": { + "type": "string", + "value": "active" + } + } + }, + { + "name": "EndEvent", + "position": { + "left": 872.5, + "top": 183 + }, + "type": "EndEvent", + "connections": [], + "id": "element31" + }, + { + "name": "Variable", + "position": { + "left": 321, + "top": 112 + }, + "type": "Variable", + "connections": [], + "id": "element166" + }, + { + "name": "Assign_vl_index", + "position": { + "left": 124.5, + "top": 257 + }, + "type": "Assign", + "connections": [ + "element177" + ], + "id": "element169", + "params": { + "StartEvent.vl_index": { + "type": "expression", + "value": "$vl_index+1", + "extension": "" + }, + "StartEvent.vl_status": { + "type": "expression", + "value": "concat(translate($vl_status,'0',''), 'active')", + "extension": "" + } + } + }, + { + "name": "While", + "position": { + "left": 164.5, + "top": 74 + }, + "type": "While", + "connections": [ + "element7" + ], + "id": "element173", + "condition": "$vl_index<=$vlCount and $vl_status='active'" + }, + { + "name": "End", + "position": { + "left": 164.5, + "top": 337 + }, + "type": "Loop", + "connections": [ + "element150" + ], + "id": "element177" + }, + { + "name": "While", + "position": { + "left": 273.5, + "top": 110 + }, + "type": "While", + "connections": [ + "element23" + ], + "id": "element19", + "condition": "$vnf_index<=$vnfCount and $vl_status='active' and $vnf_status='active'" + }, + { + "name": "createVNF", + "position": { + "left": 358.5, + "top": 106 + }, + "type": "RestTask", + "microservice": "nslcm", + "connections": [ + "element45" + ], + "id": "element23", + "publicInterface": "vnfs Resource.1", + "method": "POST", + "accept": "application/json", + "contentType": "application/json", + "url": "http://127.0.0.1:80/openoapi/nslcm/v1/ns/vnfs", + "path": {}, + "query": {}, + "body": { + "vnfInstanceData": { + "type": "string", + "value": "" + }, + "vnfIndex": { + "type": "plan", + "value": "StartEvent.vnf_index" + }, + "additionalParamForVnf": { + "type": "plan", + "value": "StartEvent.object_additionalParamForVnf" + }, + "nsInstanceId": { + "type": "plan", + "value": "StartEvent.nsInstanceId" + }, + "flavourId": { + "type": "string", + "value": "" + }, + "pnfInfo": { + "type": "string", + "value": "" + }, + "extNSVirtualLink": { + "type": "string", + "value": "" + }, + "additionalParamForNs": { + "type": "plan", + "value": "StartEvent.object_additionalParamForNs" + }, + "context": { + "type": "plan", + "value": "StartEvent.object_context" + }, + "sapData": { + "type": "string", + "value": "" + }, + "nestedNsInstanceId": { + "type": "string", + "value": "" + }, + "jobId": { + "type": "string", + "value": "" + }, + "locationConstraints": { + "type": "string", + "value": "" + } + }, + "output": { + "vnfInstId": { + "type": "topology", + "value": "" + }, + "jobId": { + "type": "topology", + "value": "" + } + } + }, + { + "name": "Assign_vnf_index", + "position": { + "left": 342, + "top": 389.5 + }, + "type": "Assign", + "connections": [ + "element53" + ], + "id": "element39", + "params": { + "StartEvent.vnf_index": { + "type": "expression", + "value": "$vnf_index+1", + "extension": "" + } + } + }, + { + "name": "RepeatUntil", + "position": { + "left": 385, + "top": 183 + }, + "type": "RepeatUntil", + "connections": [ + "element57" + ], + "id": "element45", + "condition": "$vnf_status='active' or $vnf_status='failed'" + }, + { + "name": "End", + "position": { + "left": 385, + "top": 337 + }, + "type": "Loop", + "connections": [ + "element39" + ], + "id": "element49" + }, + { + "name": "End", + "position": { + "left": 385, + "top": 447 + }, + "type": "Loop", + "connections": [ + "element318" + ], + "id": "element53" + }, + { + "name": "query_vnf nslcm", + "position": { + "left": 345, + "top": 257 + }, + "type": "RestTask", + "microservice": "nslcm", + "connections": [ + "element49" + ], + "id": "element57", + "publicInterface": "vnfs Resource.3", + "method": "GET", + "accept": "application/json", + "contentType": "application/json", + "url": "http://127.0.0.1:80/openoapi/nslcm/v1/ns/vnfs/{vnfInstId}", + "path": { + "vnfInstId": { + "type": "plan", + "value": "createVNF.vnfInstId" + } + }, + "query": {}, + "body": {}, + "output": { + "vnfInstId": { + "type": "topology", + "value": "" + }, + "vnfName": { + "type": "topology", + "value": "" + }, + "vnfStatus": { + "type": "plan", + "value": "StartEvent.vnf_status" + } + } + }, + { + "name": "While", + "position": { + "left": 510.5, + "top": 110 + }, + "type": "While", + "connections": [ + "element160" + ], + "id": "element156", + "condition": "$sfc_index<=$sfcCount and $vl_status='active' and $vnf_status='active' and $sfc_status='active'" + }, + { + "name": "createSFC", + "position": { + "left": 600.5, + "top": 106 + }, + "type": "RestTask", + "microservice": "nslcm", + "connections": [ + "element187" + ], + "id": "element160", + "publicInterface": "sfcs Resource.1", + "method": "POST", + "accept": "application/json", + "contentType": "application/json", + "url": "http://127.0.0.1:80/openoapi/nslcm/v1/ns/sfcs", + "path": {}, + "query": {}, + "body": { + "sdnControllerId": { + "type": "plan", + "value": "StartEvent.sdnControllerId" + }, + "vnfInstanceData": { + "type": "string", + "value": "" + }, + "additionalParamForVnf": { + "type": "plan", + "value": "StartEvent.object_additionalParamForVnf" + }, + "nsInstanceId": { + "type": "plan", + "value": "StartEvent.nsInstanceId" + }, + "additionalParamForNs": { + "type": "plan", + "value": "StartEvent.object_additionalParamForNs" + }, + "context": { + "type": "plan", + "value": "StartEvent.object_context" + }, + "sapData": { + "type": "string", + "value": "" + }, + "fpindex": { + "type": "plan", + "value": "StartEvent.sfc_index" + } + }, + "output": { + "sfcInstId": { + "type": "topology", + "value": "" + }, + "jobId": { + "type": "topology", + "value": "" + } + } + }, + { + "name": "RepeatUntil", + "position": { + "left": 627, + "top": 183 + }, + "type": "RepeatUntil", + "connections": [ + "element191" + ], + "id": "element187", + "condition": "$sfc_status='active' or $sfc_status='failed'" + }, + { + "name": "querySFC", + "position": { + "left": 602, + "top": 257 + }, + "type": "RestTask", + "microservice": "nslcm", + "connections": [ + "element201" + ], + "id": "element191", + "publicInterface": "sfcs Resource.0", + "method": "GET", + "accept": "application/json", + "contentType": "application/json", + "url": "http://127.0.0.1:80/openoapi/nslcm/v1/ns/sfcs/{sfcInstId}", + "path": { + "sfcInstId": { + "type": "plan", + "value": "createSFC.sfcInstId" + } + }, + "query": {}, + "body": {}, + "output": { + "sfcName": { + "type": "topology", + "value": "" + }, + "sfcInstId": { + "type": "topology", + "value": "" + }, + "sfcStatus": { + "type": "plan", + "value": "StartEvent.sfc_status" + } + } + }, + { + "name": "End", + "position": { + "left": 627, + "top": 315 + }, + "type": "Loop", + "connections": [ + "element205" + ], + "id": "element201" + }, + { + "name": "Assign_sfc_index", + "position": { + "left": 585, + "top": 389.5 + }, + "type": "Assign", + "connections": [ + "element228" + ], + "id": "element205", + "params": { + "StartEvent.sfc_index": { + "type": "expression", + "value": "$sfc_index+1", + "extension": "" + } + } + }, + { + "name": "End", + "position": { + "left": 627, + "top": 447 + }, + "type": "Loop", + "connections": [ + "element356" + ], + "id": "element228" + }, + { + "name": "jobstatus", + "position": { + "left": 251, + "top": 333 + }, + "type": "RestTask", + "microservice": "nslcm", + "connections": [ + "element19" + ], + "id": "element150", + "publicInterface": "jobstatus.0", + "method": "POST", + "accept": "application/json", + "contentType": "application/json", + "url": "http://127.0.0.1:80/openoapi/nslcm/v1/jobs/{jobId}", + "path": { + "jobId": { + "type": "plan", + "value": "StartEvent.jobId" + } + }, + "query": {}, + "body": { + "progress": { + "type": "string", + "value": "20" + }, + "errcode": { + "type": "plan", + "value": "StartEvent.vl_status" + }, + "desc": { + "type": "string", + "value": "" + } + }, + "output": {} + }, + { + "name": "jobstatus", + "position": { + "left": 488, + "top": 443 + }, + "type": "RestTask", + "microservice": "nslcm", + "connections": [ + "element156" + ], + "id": "element318", + "publicInterface": "jobstatus.0", + "method": "POST", + "accept": "application/json", + "contentType": "application/json", + "url": "http://127.0.0.1:80/openoapi/nslcm/v1/jobs/{jobId}", + "path": { + "jobId": { + "type": "plan", + "value": "StartEvent.jobId" + } + }, + "query": {}, + "body": { + "progress": { + "type": "string", + "value": "60" + }, + "errcode": { + "type": "plan", + "value": "StartEvent.vnf_status" + }, + "desc": { + "type": "string", + "value": "" + } + }, + "output": {} + }, + { + "name": "jobstatus", + "position": { + "left": 604.5, + "top": 512.5 + }, + "type": "RestTask", + "microservice": "nslcm", + "connections": [ + "element386" + ], + "id": "element356", + "publicInterface": "jobstatus.0", + "method": "POST", + "accept": "application/json", + "contentType": "application/json", + "url": "http://127.0.0.1:80/openoapi/nslcm/v1/jobs/{jobId}", + "path": { + "jobId": { + "type": "plan", + "value": "StartEvent.jobId" + } + }, + "query": {}, + "body": { + "progress": { + "type": "string", + "value": "80" + }, + "errcode": { + "type": "plan", + "value": "StartEvent.sfc_status" + }, + "desc": { + "type": "string", + "value": "" + } + }, + "output": {} + }, + { + "name": "post_do", + "position": { + "left": 731.5, + "top": 106 + }, + "type": "RestTask", + "microservice": "nslcm", + "connections": [ + "element369" + ], + "id": "element360", + "publicInterface": "ns postdeal.0", + "method": "POST", + "accept": "application/json", + "contentType": "application/json", + "url": "http://127.0.0.1:80/openoapi/nslcm/v1/ns/{nsInstanceId}/postdeal", + "path": { + "nsInstanceId": { + "type": "plan", + "value": "StartEvent.nsInstanceId" + } + }, + "query": {}, + "body": { + "status": { + "type": "plan", + "value": "StartEvent.exec_status" + } + }, + "output": {} + }, + { + "name": "jobstatus", + "position": { + "left": 850, + "top": 106 + }, + "type": "RestTask", + "microservice": "nslcm", + "connections": [ + "element31" + ], + "id": "element369", + "publicInterface": "jobstatus.0", + "method": "POST", + "accept": "application/json", + "contentType": "application/json", + "url": "http://127.0.0.1:80/openoapi/nslcm/v1/jobs/{jobId}", + "path": { + "jobId": { + "type": "plan", + "value": "StartEvent.jobId" + } + }, + "query": {}, + "body": { + "progress": { + "type": "string", + "value": "100" + }, + "errcode": { + "type": "plan", + "value": "StartEvent.exec_status" + }, + "desc": { + "type": "string", + "value": "" + } + }, + "output": {} + }, + { + "name": "Assign_all_stauts", + "position": { + "left": 710, + "top": 512.5 + }, + "type": "Assign", + "connections": [ + "element360" + ], + "id": "element386", + "params": { + "StartEvent.exec_status": { + "type": "expression", + "value": "starts-with($vl_status,'active') and contains($vnf_status,'active') and contains($sfc_status,'active')", + "extension": "" + } + } + } +]
\ No newline at end of file diff --git a/lcm/ns/data/init/deploy.xml b/lcm/ns/data/init/deploy.xml new file mode 100644 index 00000000..688d88a3 --- /dev/null +++ b/lcm/ns/data/init/deploy.xml @@ -0,0 +1,39 @@ +<?xml version="1.0" encoding="UTF-8" standalone="yes"?> +<!-- + + Copyright 2016 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. + +--> +<deploy xmlns="http://www.apache.org/ode/schemas/dd/2007/03" + xmlns:tns="http://www.zte.com.cn/tosca/nfv/ns" + xmlns:si="http://siserver.org/wsdl"> + <process name="tns:init"> + <active>true</active> + <retired>false</retired> + <process-events generate="all" /> + <provide partnerLink="client"> + <service name="tns:initService" port="initPort" /> + </provide> + <provide partnerLink="serviceInvokerPL"> + <service name="tns:initSICallback" port="initSICallbackPort" /> + </provide> + <invoke partnerLink="client"> + <service name="tns:initClientService" port="initClientPort" /> + </invoke> + <invoke partnerLink="serviceInvokerPL"> + <service name="si:InvokerService" port="InvokePort" /> + </invoke> + </process> +</deploy> diff --git a/lcm/ns/data/init/init.bpel b/lcm/ns/data/init/init.bpel new file mode 100644 index 00000000..781b1326 --- /dev/null +++ b/lcm/ns/data/init/init.bpel @@ -0,0 +1,1698 @@ +<?xml version="1.0" encoding="UTF-8" standalone="no"?> +<!-- + + Copyright 2016 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. + +--> +<process xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="init" + xmlns:tns="http://www.zte.com.cn/tosca/nfv/ns" + targetNamespace="http://www.zte.com.cn/tosca/nfv/ns" + xmlns="http://docs.oasis-open.org/wsbpel/2.0/process/executable" + xmlns:bpel4RestLight="http://iaas.uni-stuttgart.de/bpel/extensions/bpel4restlight" + xmlns:si="http://siserver.org/wsdl" + xmlns:sischema="http://siserver.org/schema" + xmlns:xlink="http://www.w3.org/1999/xlink" + xmlns:ode="http://www.apache.org/ode/type/extension"> + + <import namespace="http://siserver.org/wsdl" location="invoker.wsdl" importType="http://schemas.xmlsoap.org/wsdl/"></import> + <import importType="http://schemas.xmlsoap.org/wsdl/" location="init.wsdl" + namespace="http://www.zte.com.cn/tosca/nfv/ns" /> <!-- Todo place_holder wsdl and namespace --> + + <extensions> + <extension mustUnderstand="yes" + namespace="http://iaas.uni-stuttgart.de/bpel/extensions/bpel4restlight" /> + </extensions> + + <partnerLinks> + <partnerLink name="client" initializePartnerRole="yes" + partnerLinkType="tns:initPLT" myRole="initProvider" + partnerRole="initClient" /> + + <partnerLink name="serviceInvokerPL" + initializePartnerRole="yes" partnerLinkType="tns:OpenTOSCAServiceInvokerPLT" + myRole="ServiceInvokerClient" partnerRole="ServiceInvoker" /> + </partnerLinks> + + <variables> + <variable name="input" messageType="tns:planInputMessage" /> + <variable name="output" messageType="tns:planOutputMessage" /> + + <variable name="createVLResponse" type="xsd:string" /> + <variable name="vl_index" type="xsd:string" /> + <variable name="vnf_index" type="xsd:string" /> + <variable name="sfc_index" type="xsd:string" /> + <variable name="vnf_status" type="xsd:string" /> + <variable name="sfc_status" type="xsd:string" /> + <variable name="vl_status" type="xsd:string" /> + <variable name="exec_status" type="xsd:string" /> + <variable name="createVNFResponse" type="xsd:string" /> + <variable name="query_vnf_nslcmResponse" type="xsd:string" /> + <variable name="createSFCResponse" type="xsd:string" /> + <variable name="querySFCResponse" type="xsd:string" /> + <variable name="jobId" type="xsd:string" /> + <variable name="nsInstanceId" type="xsd:string" /> + <variable name="object_context" type="xsd:string" /> + <variable name="vlCount" type="xsd:string" /> + <variable name="sfcCount" type="xsd:string" /> + <variable name="vnfCount" type="xsd:string" /> + <variable name="object_additionalParamForVnf" type="xsd:string" /> + <variable name="sdnControllerId" type="xsd:string" /> + <variable name="object_additionalParamForNs" type="xsd:string" /> + </variables> + + <correlationSets> + <correlationSet name="ServiceInvokerCS" + properties="tns:ServiceInvokerRequestProperty" /> + </correlationSets> + + + + <sequence> + <receive createInstance="yes" name="initiate" operation="initiatePlan" + partnerLink="client" portType="tns:initPT" variable="input" /> + + <!-- Get values for variables 'instanceDataAPIUrl', 'csarId', 'serviceTemplateId', + 'serviceInstanceId' from input message These values are required to read/write + properties of the service instance the plan is working on --> + <assign name="initFromInputMsg" validate="no"> + <copy> + <from variable="input" part="payload"> + <query queryLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath2.0"><![CDATA[tns:jobId]]></query> + </from> + <to variable="jobId" /> + </copy> + <copy> + <from variable="input" part="payload"> + <query queryLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath2.0"><![CDATA[tns:nsInstanceId]]></query> + </from> + <to variable="nsInstanceId" /> + </copy> + <copy> + <from variable="input" part="payload"> + <query queryLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath2.0"><![CDATA[tns:object_context]]></query> + </from> + <to variable="object_context" /> + </copy> + <copy> + <from variable="input" part="payload"> + <query queryLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath2.0"><![CDATA[tns:vlCount]]></query> + </from> + <to variable="vlCount" /> + </copy> + <copy> + <from variable="input" part="payload"> + <query queryLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath2.0"><![CDATA[tns:sfcCount]]></query> + </from> + <to variable="sfcCount" /> + </copy> + <copy> + <from variable="input" part="payload"> + <query queryLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath2.0"><![CDATA[tns:vnfCount]]></query> + </from> + <to variable="vnfCount" /> + </copy> + <copy> + <from variable="input" part="payload"> + <query queryLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath2.0"><![CDATA[tns:object_additionalParamForVnf]]></query> + </from> + <to variable="object_additionalParamForVnf" /> + </copy> + <copy> + <from variable="input" part="payload"> + <query queryLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath2.0"><![CDATA[tns:sdnControllerId]]></query> + </from> + <to variable="sdnControllerId" /> + </copy> + <copy> + <from variable="input" part="payload"> + <query queryLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath2.0"><![CDATA[tns:object_additionalParamForNs]]></query> + </from> + <to variable="object_additionalParamForNs" /> + </copy> + </assign> + + <assign name="initFromVarMsg" validate="no"> + <copy> + <from> + <literal></literal> + </from> + <to variable="createVLResponse" /> + </copy> + <copy> + <from> + <literal>1</literal> + </from> + <to variable="vl_index" /> + </copy> + <copy> + <from> + <literal>1</literal> + </from> + <to variable="vnf_index" /> + </copy> + <copy> + <from> + <literal>1</literal> + </from> + <to variable="sfc_index" /> + </copy> + <copy> + <from> + <literal>active</literal> + </from> + <to variable="vnf_status" /> + </copy> + <copy> + <from> + <literal>active</literal> + </from> + <to variable="sfc_status" /> + </copy> + <copy> + <from> + <literal>active</literal> + </from> + <to variable="vl_status" /> + </copy> + <copy> + <from> + <literal>active</literal> + </from> + <to variable="exec_status" /> + </copy> + <copy> + <from> + <literal></literal> + </from> + <to variable="createVNFResponse" /> + </copy> + <copy> + <from> + <literal></literal> + </from> + <to variable="query_vnf_nslcmResponse" /> + </copy> + <copy> + <from> + <literal></literal> + </from> + <to variable="createSFCResponse" /> + </copy> + <copy> + <from> + <literal></literal> + </from> + <to variable="querySFCResponse" /> + </copy> + </assign> + + <!-- + Copyright 2016 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. + +--> + +<scope name="While_WhileTask" xmlns:pp="http://opentosca.org/api/pp" + xmlns:xlink="http://www.w3.org/1999/xlink"> + <while name="While"> + <condition expressionLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + $vl_index<=$vlCount and $vl_status='active' + </condition> + <sequence name="While_WhileBranch_Sequence"> + <!-- + Copyright 2016 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. + +--> + +<scope name="createVL" xmlns:pp="http://opentosca.org/api/pp" + xmlns:xlink="http://www.w3.org/1999/xlink"> + <!-- add by qinlihan add rest Components --> + <variables> + <variable name="request" type="xsd:string" /> + <variable name="url" type="xsd:string" /> + <variable name="temp" type="xsd:string" /> + </variables> + + <sequence name="createVL_Sequence"> + <!-- build url start --> + <assign name="createVL_URL"> + <copy> + <from> + <literal>http://127.0.0.1:80/openoapi/nslcm/v1/ns/vls</literal> + </from> + <to variable="url"></to> + </copy> + + + </assign> + <!-- build url end --> + + <!-- build request start --> + <assign name="createVL_Request"> + <copy> + <from> + <literal>{</literal> + </from> + <to variable="request"></to> + </copy> + + <copy> + <from variable="nsInstanceId"> + </from> + <to variable="temp"/> + </copy> + + <copy> + <from expressionLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + <![CDATA[concat($request,'"nsInstanceId":"',$temp,'"',',')]]> + </from> + <to variable="request"></to> + </copy> + <copy> + <from variable="object_context"> + </from> + <to variable="temp"/> + </copy> + + <copy> + <from expressionLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + <![CDATA[concat($request,'"context":',$temp,',')]]> + </from> + <to variable="request"></to> + </copy> + <copy> + <from variable="object_additionalParamForVnf"> + </from> + <to variable="temp"/> + </copy> + + <copy> + <from expressionLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + <![CDATA[concat($request,'"additionalParamForVnf":',$temp,',')]]> + </from> + <to variable="request"></to> + </copy> + <copy> + <from variable="object_additionalParamForNs"> + </from> + <to variable="temp"/> + </copy> + + <copy> + <from expressionLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + <![CDATA[concat($request,'"additionalParamForNs":',$temp,',')]]> + </from> + <to variable="request"></to> + </copy> + <copy> + <from variable="vl_index"> + </from> + <to variable="temp"/> + </copy> + + <copy> + <from expressionLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + <![CDATA[concat($request,'"vlIndex":"',$temp,'"')]]> + </from> + <to variable="request"></to> + </copy> + <copy> + <from expressionLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + <![CDATA[concat($request,'}')]]> + </from> + <to variable="request"></to> + </copy> + </assign> + <!-- build request end --> + + <extensionActivity> + <bpel4RestLight:POST contentType="application/json" accept="application/json" request='request' response='createVLResponse' uri="$bpelvar[url]"></bpel4RestLight:POST> + </extensionActivity> + + <assign name="createVL_Response_result"> + <copy> + <from variable="createVLResponse"> + <query queryLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + <![CDATA[//*[local-name()="result"]/text()]]> + </query> + </from> + <to variable="vl_status"/> + </copy> + </assign> + </sequence> +</scope> + + <!-- + Copyright 2016 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. + +--> + +<scope name="Assign_vl_index_AssignTask" xmlns:pp="http://opentosca.org/api/pp" + xmlns:xlink="http://www.w3.org/1999/xlink"> + <assign name="Assign_vl_index_Assign"> + <copy> + <from expressionLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + <![CDATA[$vl_index+1]]> + </from> + <to variable="vl_index"/> + </copy> + <copy> + <from expressionLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + <![CDATA[concat(translate($vl_status,'0',''), 'active')]]> + </from> + <to variable="vl_status"/> + </copy> + </assign> +</scope> + + <wait name="Wait"> + <for><![CDATA['PT01M30.0S']]></for> + </wait> + </sequence> + </while> +</scope> + + <!--"/src/main/resources/templates/bpel_management_activity_scope_template.xml"--> + <!-- + Copyright 2016 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. + +--> + +<!-- do nothing --> + + <!--"/src/main/resources/templates/bpel_management_activity_scope_template.xml"--> + <!-- + Copyright 2016 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. + +--> + +<scope name="jobstatus" xmlns:pp="http://opentosca.org/api/pp" + xmlns:xlink="http://www.w3.org/1999/xlink"> + <!-- add by qinlihan add rest Components --> + <variables> + <variable name="request" type="xsd:string" /> + <variable name="url" type="xsd:string" /> + <variable name="temp" type="xsd:string" /> + <!--temp var for response--> + <variable name="jobstatusResponse" type="xsd:string" /> + </variables> + + <sequence name="jobstatus_Sequence"> + <!-- build url start --> + <assign name="jobstatus_URL"> + <copy> + <from> + <literal>http://127.0.0.1:80/openoapi/nslcm/v1/jobs/{jobId}</literal> + </from> + <to variable="url"></to> + </copy> + + <copy> + <from variable="jobId"> + </from> + <to variable="temp"/> + </copy> + + <copy> + <from expressionLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + <![CDATA[concat(substring-before($url,'{jobId}'), $temp, substring-after($url,'{jobId}'))]]> + </from> + <to variable="url"></to> + </copy> + + </assign> + <!-- build url end --> + + <!-- build request start --> + <assign name="jobstatus_Request"> + <copy> + <from> + <literal>{</literal> + </from> + <to variable="request"></to> + </copy> + + <copy> + <from variable="vl_status"> + </from> + <to variable="temp"/> + </copy> + + <copy> + <from expressionLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + <![CDATA[concat($request,'"errcode":"',$temp,'"',',')]]> + </from> + <to variable="request"></to> + </copy> + <copy> + <from> + <literal>20</literal> + </from> + <to variable="temp"/> + </copy> + + <copy> + <from expressionLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + <![CDATA[concat($request,'"progress":"',$temp,'"')]]> + </from> + <to variable="request"></to> + </copy> + <copy> + <from expressionLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + <![CDATA[concat($request,'}')]]> + </from> + <to variable="request"></to> + </copy> + </assign> + <!-- build request end --> + + <extensionActivity> + <bpel4RestLight:POST contentType="application/json" accept="application/json" request='request' response='jobstatusResponse' uri="$bpelvar[url]"></bpel4RestLight:POST> + </extensionActivity> + + </sequence> +</scope> + + <!--"/src/main/resources/templates/bpel_management_activity_scope_template.xml"--> + <!-- + Copyright 2016 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. + +--> + +<scope name="While_WhileTask" xmlns:pp="http://opentosca.org/api/pp" + xmlns:xlink="http://www.w3.org/1999/xlink"> + <while name="While"> + <condition expressionLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + $vnf_index<=$vnfCount and $vl_status='active' and $vnf_status='active' + </condition> + <sequence name="While_WhileBranch_Sequence"> + <!-- + Copyright 2016 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. + +--> + +<scope name="createVNF" xmlns:pp="http://opentosca.org/api/pp" + xmlns:xlink="http://www.w3.org/1999/xlink"> + <!-- add by qinlihan add rest Components --> + <variables> + <variable name="request" type="xsd:string" /> + <variable name="url" type="xsd:string" /> + <variable name="temp" type="xsd:string" /> + </variables> + + <sequence name="createVNF_Sequence"> + <!-- build url start --> + <assign name="createVNF_URL"> + <copy> + <from> + <literal>http://127.0.0.1:80/openoapi/nslcm/v1/ns/vnfs</literal> + </from> + <to variable="url"></to> + </copy> + + + </assign> + <!-- build url end --> + + <!-- build request start --> + <assign name="createVNF_Request"> + <copy> + <from> + <literal>{</literal> + </from> + <to variable="request"></to> + </copy> + + <copy> + <from variable="nsInstanceId"> + </from> + <to variable="temp"/> + </copy> + + <copy> + <from expressionLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + <![CDATA[concat($request,'"nsInstanceId":"',$temp,'"',',')]]> + </from> + <to variable="request"></to> + </copy> + <copy> + <from variable="object_context"> + </from> + <to variable="temp"/> + </copy> + + <copy> + <from expressionLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + <![CDATA[concat($request,'"context":',$temp,',')]]> + </from> + <to variable="request"></to> + </copy> + <copy> + <from variable="object_additionalParamForVnf"> + </from> + <to variable="temp"/> + </copy> + + <copy> + <from expressionLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + <![CDATA[concat($request,'"additionalParamForVnf":',$temp,',')]]> + </from> + <to variable="request"></to> + </copy> + <copy> + <from variable="object_additionalParamForNs"> + </from> + <to variable="temp"/> + </copy> + + <copy> + <from expressionLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + <![CDATA[concat($request,'"additionalParamForNs":',$temp)]]> + </from> + <to variable="request"></to> + </copy> + <copy> + <from expressionLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + <![CDATA[concat($request,'}')]]> + </from> + <to variable="request"></to> + </copy> + </assign> + <!-- build request end --> + + <extensionActivity> + <bpel4RestLight:POST contentType="application/json" accept="application/json" request='request' response='createVNFResponse' uri="$bpelvar[url]"></bpel4RestLight:POST> + </extensionActivity> + + </sequence> +</scope> + + <!-- + Copyright 2016 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. + +--> + +<scope name="RepeatUntil_RepeatTask" xmlns:pp="http://opentosca.org/api/pp" + xmlns:xlink="http://www.w3.org/1999/xlink"> + <repeatUntil name="RepeatUntil"> + <sequence name="RepeatUntil_RepeatBranch_Sequence"> + <!-- + Copyright 2016 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. + +--> + +<scope name="query_vnf_nslcm" xmlns:pp="http://opentosca.org/api/pp" + xmlns:xlink="http://www.w3.org/1999/xlink"> + <!-- add by qinlihan add rest Components --> + <variables> + <variable name="request" type="xsd:string" /> + <variable name="url" type="xsd:string" /> + <variable name="temp" type="xsd:string" /> + </variables> + + <sequence name="query_vnf_nslcm_Sequence"> + <!-- build url start --> + <assign name="query_vnf_nslcm_URL"> + <copy> + <from> + <literal>http://127.0.0.1:80/openoapi/nslcm/v1/ns/vnfs/{vnfInstId}</literal> + </from> + <to variable="url"></to> + </copy> + + <copy> + <from variable="createVNFResponse"> + <query queryLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + <![CDATA[//*[local-name()="vnfInstId"]/text()]]> + </query> + </from> + <to variable="temp"/> + </copy> + + <copy> + <from expressionLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + <![CDATA[concat(substring-before($url,'{vnfInstId}'), $temp, substring-after($url,'{vnfInstId}'))]]> + </from> + <to variable="url"></to> + </copy> + + </assign> + <!-- build url end --> + + <!-- build request start --> + <assign name="query_vnf_nslcm_Request"> + <copy> + <from> + <literal>{</literal> + </from> + <to variable="request"></to> + </copy> + + <copy> + <from expressionLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + <![CDATA[concat($request,'}')]]> + </from> + <to variable="request"></to> + </copy> + </assign> + <!-- build request end --> + + <extensionActivity> + <bpel4RestLight:GET contentType="application/json" accept="application/json" uri="$bpelvar[url]" response='query_vnf_nslcmResponse'></bpel4RestLight:GET> + </extensionActivity> + + <assign name="query_vnf_nslcm_Response_vnfStatus"> + <copy> + <from variable="query_vnf_nslcmResponse"> + <query queryLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + <![CDATA[//*[local-name()="vnfStatus"]/text()]]> + </query> + </from> + <to variable="vnf_status"/> + </copy> + </assign> + </sequence> +</scope> + + <wait name="Wait"> + <for><![CDATA['PT01M30.0S']]></for> + </wait> + </sequence> + <condition expressionLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + $vnf_status='active' or $vnf_status='failed' + </condition> + </repeatUntil> +</scope> + + <!-- + Copyright 2016 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. + +--> + +<!-- do nothing --> + + <!-- + Copyright 2016 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. + +--> + +<scope name="Assign_vnf_index_AssignTask" xmlns:pp="http://opentosca.org/api/pp" + xmlns:xlink="http://www.w3.org/1999/xlink"> + <assign name="Assign_vnf_index_Assign"> + <copy> + <from expressionLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + <![CDATA[$vnf_index+1]]> + </from> + <to variable="vnf_index"/> + </copy> + </assign> +</scope> + + <wait name="Wait"> + <for><![CDATA['PT01M30.0S']]></for> + </wait> + </sequence> + </while> +</scope> + + <!--"/src/main/resources/templates/bpel_management_activity_scope_template.xml"--> + <!-- + Copyright 2016 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. + +--> + +<!-- do nothing --> + + <!--"/src/main/resources/templates/bpel_management_activity_scope_template.xml"--> + <!-- + Copyright 2016 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. + +--> + +<scope name="jobstatus" xmlns:pp="http://opentosca.org/api/pp" + xmlns:xlink="http://www.w3.org/1999/xlink"> + <!-- add by qinlihan add rest Components --> + <variables> + <variable name="request" type="xsd:string" /> + <variable name="url" type="xsd:string" /> + <variable name="temp" type="xsd:string" /> + <!--temp var for response--> + <variable name="jobstatusResponse" type="xsd:string" /> + </variables> + + <sequence name="jobstatus_Sequence"> + <!-- build url start --> + <assign name="jobstatus_URL"> + <copy> + <from> + <literal>http://127.0.0.1:80/openoapi/nslcm/v1/jobs/{jobId}</literal> + </from> + <to variable="url"></to> + </copy> + + <copy> + <from variable="jobId"> + </from> + <to variable="temp"/> + </copy> + + <copy> + <from expressionLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + <![CDATA[concat(substring-before($url,'{jobId}'), $temp, substring-after($url,'{jobId}'))]]> + </from> + <to variable="url"></to> + </copy> + + </assign> + <!-- build url end --> + + <!-- build request start --> + <assign name="jobstatus_Request"> + <copy> + <from> + <literal>{</literal> + </from> + <to variable="request"></to> + </copy> + + <copy> + <from variable="vnf_status"> + </from> + <to variable="temp"/> + </copy> + + <copy> + <from expressionLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + <![CDATA[concat($request,'"errcode":"',$temp,'"',',')]]> + </from> + <to variable="request"></to> + </copy> + <copy> + <from> + <literal>60</literal> + </from> + <to variable="temp"/> + </copy> + + <copy> + <from expressionLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + <![CDATA[concat($request,'"progress":"',$temp,'"')]]> + </from> + <to variable="request"></to> + </copy> + <copy> + <from expressionLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + <![CDATA[concat($request,'}')]]> + </from> + <to variable="request"></to> + </copy> + </assign> + <!-- build request end --> + + <extensionActivity> + <bpel4RestLight:POST contentType="application/json" accept="application/json" request='request' response='jobstatusResponse' uri="$bpelvar[url]"></bpel4RestLight:POST> + </extensionActivity> + + </sequence> +</scope> + + <!--"/src/main/resources/templates/bpel_management_activity_scope_template.xml"--> + <!-- + Copyright 2016 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. + +--> + +<scope name="While_WhileTask" xmlns:pp="http://opentosca.org/api/pp" + xmlns:xlink="http://www.w3.org/1999/xlink"> + <while name="While"> + <condition expressionLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + $sfc_index<=$sfcCount and $vl_status='active' and $vnf_status='active' and $sfc_status='active' + </condition> + <sequence name="While_WhileBranch_Sequence"> + <!-- + Copyright 2016 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. + +--> + +<scope name="createSFC" xmlns:pp="http://opentosca.org/api/pp" + xmlns:xlink="http://www.w3.org/1999/xlink"> + <!-- add by qinlihan add rest Components --> + <variables> + <variable name="request" type="xsd:string" /> + <variable name="url" type="xsd:string" /> + <variable name="temp" type="xsd:string" /> + </variables> + + <sequence name="createSFC_Sequence"> + <!-- build url start --> + <assign name="createSFC_URL"> + <copy> + <from> + <literal>http://127.0.0.1:80/openoapi/nslcm/v1/ns/sfcs</literal> + </from> + <to variable="url"></to> + </copy> + + + </assign> + <!-- build url end --> + + <!-- build request start --> + <assign name="createSFC_Request"> + <copy> + <from> + <literal>{</literal> + </from> + <to variable="request"></to> + </copy> + + <copy> + <from variable="nsInstanceId"> + </from> + <to variable="temp"/> + </copy> + + <copy> + <from expressionLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + <![CDATA[concat($request,'"nsInstanceId":"',$temp,'"',',')]]> + </from> + <to variable="request"></to> + </copy> + <copy> + <from variable="object_context"> + </from> + <to variable="temp"/> + </copy> + + <copy> + <from expressionLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + <![CDATA[concat($request,'"context":',$temp,',')]]> + </from> + <to variable="request"></to> + </copy> + <copy> + <from variable="sdnControllerId"> + </from> + <to variable="temp"/> + </copy> + + <copy> + <from expressionLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + <![CDATA[concat($request,'"sdnControllerId":"',$temp,'"',',')]]> + </from> + <to variable="request"></to> + </copy> + <copy> + <from variable="object_additionalParamForVnf"> + </from> + <to variable="temp"/> + </copy> + + <copy> + <from expressionLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + <![CDATA[concat($request,'"additionalParamForVnf":',$temp,',')]]> + </from> + <to variable="request"></to> + </copy> + <copy> + <from variable="object_additionalParamForNs"> + </from> + <to variable="temp"/> + </copy> + + <copy> + <from expressionLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + <![CDATA[concat($request,'"additionalParamForNs":',$temp,',')]]> + </from> + <to variable="request"></to> + </copy> + <copy> + <from variable="sfc_index"> + </from> + <to variable="temp"/> + </copy> + + <copy> + <from expressionLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + <![CDATA[concat($request,'"fpindex":"',$temp,'"')]]> + </from> + <to variable="request"></to> + </copy> + <copy> + <from expressionLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + <![CDATA[concat($request,'}')]]> + </from> + <to variable="request"></to> + </copy> + </assign> + <!-- build request end --> + + <extensionActivity> + <bpel4RestLight:POST contentType="application/json" accept="application/json" request='request' response='createSFCResponse' uri="$bpelvar[url]"></bpel4RestLight:POST> + </extensionActivity> + + </sequence> +</scope> + + <!-- + Copyright 2016 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. + +--> + +<scope name="RepeatUntil_RepeatTask" xmlns:pp="http://opentosca.org/api/pp" + xmlns:xlink="http://www.w3.org/1999/xlink"> + <repeatUntil name="RepeatUntil"> + <sequence name="RepeatUntil_RepeatBranch_Sequence"> + <!-- + Copyright 2016 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. + +--> + +<scope name="querySFC" xmlns:pp="http://opentosca.org/api/pp" + xmlns:xlink="http://www.w3.org/1999/xlink"> + <!-- add by qinlihan add rest Components --> + <variables> + <variable name="request" type="xsd:string" /> + <variable name="url" type="xsd:string" /> + <variable name="temp" type="xsd:string" /> + </variables> + + <sequence name="querySFC_Sequence"> + <!-- build url start --> + <assign name="querySFC_URL"> + <copy> + <from> + <literal>http://127.0.0.1:80/openoapi/nslcm/v1/ns/sfcs/{sfcInstId}</literal> + </from> + <to variable="url"></to> + </copy> + + <copy> + <from variable="createSFCResponse"> + <query queryLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + <![CDATA[//*[local-name()="sfcInstId"]/text()]]> + </query> + </from> + <to variable="temp"/> + </copy> + + <copy> + <from expressionLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + <![CDATA[concat(substring-before($url,'{sfcInstId}'), $temp, substring-after($url,'{sfcInstId}'))]]> + </from> + <to variable="url"></to> + </copy> + + </assign> + <!-- build url end --> + + <!-- build request start --> + <assign name="querySFC_Request"> + <copy> + <from> + <literal>{</literal> + </from> + <to variable="request"></to> + </copy> + + <copy> + <from expressionLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + <![CDATA[concat($request,'}')]]> + </from> + <to variable="request"></to> + </copy> + </assign> + <!-- build request end --> + + <extensionActivity> + <bpel4RestLight:GET contentType="application/json" accept="application/json" uri="$bpelvar[url]" response='querySFCResponse'></bpel4RestLight:GET> + </extensionActivity> + + <assign name="querySFC_Response_sfcStatus"> + <copy> + <from variable="querySFCResponse"> + <query queryLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + <![CDATA[//*[local-name()="sfcStatus"]/text()]]> + </query> + </from> + <to variable="sfc_status"/> + </copy> + </assign> + </sequence> +</scope> + + <wait name="Wait"> + <for><![CDATA['PT01M30.0S']]></for> + </wait> + </sequence> + <condition expressionLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + $sfc_status='active' or $sfc_status='failed' + </condition> + </repeatUntil> +</scope> + + <!-- + Copyright 2016 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. + +--> + +<!-- do nothing --> + + <!-- + Copyright 2016 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. + +--> + +<scope name="Assign_sfc_index_AssignTask" xmlns:pp="http://opentosca.org/api/pp" + xmlns:xlink="http://www.w3.org/1999/xlink"> + <assign name="Assign_sfc_index_Assign"> + <copy> + <from expressionLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + <![CDATA[$sfc_index+1]]> + </from> + <to variable="sfc_index"/> + </copy> + </assign> +</scope> + + <wait name="Wait"> + <for><![CDATA['PT01M30.0S']]></for> + </wait> + </sequence> + </while> +</scope> + + <!--"/src/main/resources/templates/bpel_management_activity_scope_template.xml"--> + <!-- + Copyright 2016 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. + +--> + +<!-- do nothing --> + + <!--"/src/main/resources/templates/bpel_management_activity_scope_template.xml"--> + <!-- + Copyright 2016 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. + +--> + +<scope name="jobstatus" xmlns:pp="http://opentosca.org/api/pp" + xmlns:xlink="http://www.w3.org/1999/xlink"> + <!-- add by qinlihan add rest Components --> + <variables> + <variable name="request" type="xsd:string" /> + <variable name="url" type="xsd:string" /> + <variable name="temp" type="xsd:string" /> + <!--temp var for response--> + <variable name="jobstatusResponse" type="xsd:string" /> + </variables> + + <sequence name="jobstatus_Sequence"> + <!-- build url start --> + <assign name="jobstatus_URL"> + <copy> + <from> + <literal>http://127.0.0.1:80/openoapi/nslcm/v1/jobs/{jobId}</literal> + </from> + <to variable="url"></to> + </copy> + + <copy> + <from variable="jobId"> + </from> + <to variable="temp"/> + </copy> + + <copy> + <from expressionLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + <![CDATA[concat(substring-before($url,'{jobId}'), $temp, substring-after($url,'{jobId}'))]]> + </from> + <to variable="url"></to> + </copy> + + </assign> + <!-- build url end --> + + <!-- build request start --> + <assign name="jobstatus_Request"> + <copy> + <from> + <literal>{</literal> + </from> + <to variable="request"></to> + </copy> + + <copy> + <from variable="sfc_status"> + </from> + <to variable="temp"/> + </copy> + + <copy> + <from expressionLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + <![CDATA[concat($request,'"errcode":"',$temp,'"',',')]]> + </from> + <to variable="request"></to> + </copy> + <copy> + <from> + <literal>80</literal> + </from> + <to variable="temp"/> + </copy> + + <copy> + <from expressionLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + <![CDATA[concat($request,'"progress":"',$temp,'"')]]> + </from> + <to variable="request"></to> + </copy> + <copy> + <from expressionLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + <![CDATA[concat($request,'}')]]> + </from> + <to variable="request"></to> + </copy> + </assign> + <!-- build request end --> + + <extensionActivity> + <bpel4RestLight:POST contentType="application/json" accept="application/json" request='request' response='jobstatusResponse' uri="$bpelvar[url]"></bpel4RestLight:POST> + </extensionActivity> + + </sequence> +</scope> + + <!--"/src/main/resources/templates/bpel_management_activity_scope_template.xml"--> + <!-- + Copyright 2016 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. + +--> + +<scope name="Assign_all_stauts_AssignTask" xmlns:pp="http://opentosca.org/api/pp" + xmlns:xlink="http://www.w3.org/1999/xlink"> + <assign name="Assign_all_stauts_Assign"> + <copy> + <from expressionLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + <![CDATA[starts-with($vl_status,'active') and contains($vnf_status,'active') and contains($sfc_status,'active')]]> + </from> + <to variable="exec_status"/> + </copy> + </assign> +</scope> + + <!--"/src/main/resources/templates/bpel_management_activity_scope_template.xml"--> + <!-- + Copyright 2016 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. + +--> + +<scope name="post_do" xmlns:pp="http://opentosca.org/api/pp" + xmlns:xlink="http://www.w3.org/1999/xlink"> + <!-- add by qinlihan add rest Components --> + <variables> + <variable name="request" type="xsd:string" /> + <variable name="url" type="xsd:string" /> + <variable name="temp" type="xsd:string" /> + <!--temp var for response--> + <variable name="post_doResponse" type="xsd:string" /> + </variables> + + <sequence name="post_do_Sequence"> + <!-- build url start --> + <assign name="post_do_URL"> + <copy> + <from> + <literal>http://127.0.0.1:80/openoapi/nslcm/v1/ns/{nsInstanceId}/postdeal</literal> + </from> + <to variable="url"></to> + </copy> + + <copy> + <from variable="nsInstanceId"> + </from> + <to variable="temp"/> + </copy> + + <copy> + <from expressionLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + <![CDATA[concat(substring-before($url,'{nsInstanceId}'), $temp, substring-after($url,'{nsInstanceId}'))]]> + </from> + <to variable="url"></to> + </copy> + + </assign> + <!-- build url end --> + + <!-- build request start --> + <assign name="post_do_Request"> + <copy> + <from> + <literal>{</literal> + </from> + <to variable="request"></to> + </copy> + + <copy> + <from variable="exec_status"> + </from> + <to variable="temp"/> + </copy> + + <copy> + <from expressionLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + <![CDATA[concat($request,'"status":"',$temp,'"')]]> + </from> + <to variable="request"></to> + </copy> + <copy> + <from expressionLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + <![CDATA[concat($request,'}')]]> + </from> + <to variable="request"></to> + </copy> + </assign> + <!-- build request end --> + + <extensionActivity> + <bpel4RestLight:POST contentType="application/json" accept="application/json" request='request' response='post_doResponse' uri="$bpelvar[url]"></bpel4RestLight:POST> + </extensionActivity> + + </sequence> +</scope> + + <!--"/src/main/resources/templates/bpel_management_activity_scope_template.xml"--> + <!-- + Copyright 2016 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. + +--> + +<scope name="jobstatus" xmlns:pp="http://opentosca.org/api/pp" + xmlns:xlink="http://www.w3.org/1999/xlink"> + <!-- add by qinlihan add rest Components --> + <variables> + <variable name="request" type="xsd:string" /> + <variable name="url" type="xsd:string" /> + <variable name="temp" type="xsd:string" /> + <!--temp var for response--> + <variable name="jobstatusResponse" type="xsd:string" /> + </variables> + + <sequence name="jobstatus_Sequence"> + <!-- build url start --> + <assign name="jobstatus_URL"> + <copy> + <from> + <literal>http://127.0.0.1:80/openoapi/nslcm/v1/jobs/{jobId}</literal> + </from> + <to variable="url"></to> + </copy> + + <copy> + <from variable="jobId"> + </from> + <to variable="temp"/> + </copy> + + <copy> + <from expressionLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + <![CDATA[concat(substring-before($url,'{jobId}'), $temp, substring-after($url,'{jobId}'))]]> + </from> + <to variable="url"></to> + </copy> + + </assign> + <!-- build url end --> + + <!-- build request start --> + <assign name="jobstatus_Request"> + <copy> + <from> + <literal>{</literal> + </from> + <to variable="request"></to> + </copy> + + <copy> + <from variable="exec_status"> + </from> + <to variable="temp"/> + </copy> + + <copy> + <from expressionLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + <![CDATA[concat($request,'"errcode":"',$temp,'"',',')]]> + </from> + <to variable="request"></to> + </copy> + <copy> + <from> + <literal>100</literal> + </from> + <to variable="temp"/> + </copy> + + <copy> + <from expressionLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + <![CDATA[concat($request,'"progress":"',$temp,'"')]]> + </from> + <to variable="request"></to> + </copy> + <copy> + <from expressionLanguage="urn:oasis:names:tc:wsbpel:2.0:sublang:xpath1.0"> + <![CDATA[concat($request,'}')]]> + </from> + <to variable="request"></to> + </copy> + </assign> + <!-- build request end --> + + <extensionActivity> + <bpel4RestLight:POST contentType="application/json" accept="application/json" request='request' response='jobstatusResponse' uri="$bpelvar[url]"></bpel4RestLight:POST> + </extensionActivity> + + </sequence> +</scope> + + <!--"/src/main/resources/templates/bpel_management_activity_scope_template.xml"--> + <!-- + Copyright 2016 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. + +--> + +<!-- do nothing --> + + <!--"/src/main/resources/templates/bpel_management_activity_scope_template.xml"--> + </sequence> +</process> diff --git a/lcm/ns/data/init/init.wsdl b/lcm/ns/data/init/init.wsdl new file mode 100644 index 00000000..ff375587 --- /dev/null +++ b/lcm/ns/data/init/init.wsdl @@ -0,0 +1,149 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + Copyright 2016 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. + +--> +<definitions name="init" + targetNamespace="http://www.zte.com.cn/tosca/nfv/ns" + xmlns:tns="http://www.zte.com.cn/tosca/nfv/ns" + xmlns="http://schemas.xmlsoap.org/wsdl/" xmlns:plnk="http://docs.oasis-open.org/wsbpel/2.0/plnktype" + xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:vprop="http://docs.oasis-open.org/wsbpel/2.0/varprop" + xmlns:si="http://siserver.org/wsdl" xmlns:xsd="http://www.w3.org/2001/XMLSchema" + > + + <import namespace="http://siserver.org/wsdl" location="invoker.wsdl" /> + <types> + <schema attributeFormDefault="unqualified" elementFormDefault="qualified" + targetNamespace="http://www.zte.com.cn/tosca/nfv/ns" + xmlns="http://www.w3.org/2001/XMLSchema"> + <element name="planInput"> + <complexType> + <sequence> + <element name="jobId" type="string" /> + <element name="nsInstanceId" type="string" /> + <element name="object_context" type="string" /> + <element name="vlCount" type="string" /> + <element name="sfcCount" type="string" /> + <element name="vnfCount" type="string" /> + <element name="object_additionalParamForVnf" type="string" /> + <element name="sdnControllerId" type="string" /> + <element name="object_additionalParamForNs" type="string" /> + </sequence> + </complexType> + </element> + <element name="planOutput"> + <complexType> + <sequence> + <element name="correlationId" type="string" /> + <!-- Relevant if build plan was executed that created a new service + instance --> + <element name="serviceInstanceId" type="string" /> + + </sequence> + </complexType> + </element> + </schema> + </types> + + <message name="planInputMessage"> + <part name="payload" element="tns:planInput" /> + </message> + <message name="planOutputMessage"> + <part name="payload" element="tns:planOutput" /> + </message> + + + <plnk:partnerLinkType name="initPLT"> + <plnk:role name="initProvider" portType="tns:initPT" /> + <plnk:role name="initClient" portType="tns:initClientCallbackPT" /> + </plnk:partnerLinkType> + <plnk:partnerLinkType name="OpenTOSCAServiceInvokerPLT"> + <plnk:role name="ServiceInvokerClient" portType="si:CallbackPortType" /> + <plnk:role name="ServiceInvoker" portType="si:InvokePortType" /> + </plnk:partnerLinkType> + + <vprop:property name="ServiceInvokerRequestProperty" + type="xsd:string" /> + + <vprop:propertyAlias messageType="si:invokeOperationAsyncMessage" + part="invokeOperationAsync" propertyName="tns:ServiceInvokerRequestProperty"> + <vprop:query><![CDATA[//*[local-name()="MessageID" and namespace-uri()="http://siserver.org/schema"]]]></vprop:query> + </vprop:propertyAlias> + + <vprop:propertyAlias messageType="si:invokeResponse" + part="invokeResponse" propertyName="tns:ServiceInvokerRequestProperty"> + <vprop:query><![CDATA[//*[local-name()="MessageID" and namespace-uri()="http://siserver.org/schema"]]]></vprop:query> + </vprop:propertyAlias> + + <!-- Port type provides the operation to the client for starting the plan + plan --> + <portType name="initPT"> + <operation name="initiatePlan"> + <input message="tns:planInputMessage" /> + </operation> + </portType> + + <portType name="initClientCallbackPT"> + <operation name="onResult"> + <input message="tns:planOutputMessage" /> + </operation> + </portType> + + + + + <binding name="initPTBinding" type="tns:initPT"> + <soap:binding style="document" + transport="http://schemas.xmlsoap.org/soap/http" /> + <operation name="initiatePlan"> + <soap:operation + soapAction="http://www.zte.com.cn/tosca/nfv/ns/initiate" /> + <input> + <soap:body use="literal" /> + </input> + </operation> + </binding> + <binding name="initClientPTBinding" type="tns:initClientCallbackPT"> + <soap:binding style="document" + transport="http://schemas.xmlsoap.org/soap/http" /> + <operation name="onResult"> + <soap:operation + soapAction="http://www.zte.com.cn/tosca/nfv/ns/onResult" /> + <input> + <soap:body use="literal" /> + </input> + </operation> + </binding> + + + <service name="initService"> + <port name="initPort" binding="tns:initPTBinding"> + <soap:address location="http://localhost:8080/init" /> + </port> + </service> + + <service name="initClientService"> + <port name="initClientPort" binding="tns:initClientPTBinding"> + <soap:address location="http://localhost:8080/initClient" /> + </port> + </service> + + <service name="initSICallback"> + <port binding="si:CallbackBinding" name="initSICallbackPort"> + <soap:address location="http://localhost:9763/services/initSICallback/"/> + </port> + </service> +</definitions>
\ No newline at end of file diff --git a/lcm/ns/data/init/invoker.wsdl b/lcm/ns/data/init/invoker.wsdl new file mode 100644 index 00000000..ff6ed847 --- /dev/null +++ b/lcm/ns/data/init/invoker.wsdl @@ -0,0 +1,143 @@ +<?xml version="1.0" encoding="UTF-8"?> +<wsdl:definitions + xmlns:xsd="http://www.w3.org/2001/XMLSchema" + xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/" + xmlns:tns="http://siserver.org/wsdl" + xmlns:ns="http://siserver.org/schema" + xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" + name="SIServerImplService" + targetNamespace="http://siserver.org/wsdl"> + + <wsdl:types> + <xsd:schema xmlns="http://www.w3.org/2001/XMLSchema"> + <xsd:import + namespace="http://siserver.org/schema" + schemaLocation="invoker.xsd" /> + </xsd:schema> + </wsdl:types> + <wsdl:message name="invokeOperationMessage"> + <wsdl:part + element="ns:invokeOperation" + name="invokeOperation"> + </wsdl:part> + </wsdl:message> + <wsdl:message name="invokeOperationAsyncMessage"> + <wsdl:part + element="ns:invokeOperationAsync" + name="invokeOperationAsync"> + </wsdl:part> + </wsdl:message> + <wsdl:message name="invokeOperationSyncMessage"> + <wsdl:part + element="ns:invokeOperationSync" + name="invokeOperationSync"> + </wsdl:part> + </wsdl:message> + <wsdl:message name="invokePlanMessage"> + <wsdl:part + element="ns:invokePlan" + name="invokePlan"> + </wsdl:part> + </wsdl:message> + <wsdl:message name="invokeResponse"> + <wsdl:part + element="ns:invokeResponse" + name="invokeResponse"> + </wsdl:part> + </wsdl:message> + <wsdl:portType name="InvokePortType"> + <wsdl:operation name="invokeOperation"> + <wsdl:input message="tns:invokeOperationMessage"> + </wsdl:input> + </wsdl:operation> + <wsdl:operation name="invokeOperationAsync"> + <wsdl:input message="tns:invokeOperationAsyncMessage"> + </wsdl:input> + </wsdl:operation> + <wsdl:operation name="invokeOperationSync"> + <wsdl:input message="tns:invokeOperationSyncMessage"> + </wsdl:input> + <wsdl:output message="tns:invokeResponse"> + </wsdl:output> + </wsdl:operation> + <wsdl:operation name="invokePlan"> + <wsdl:input message="tns:invokePlanMessage"> + </wsdl:input> + </wsdl:operation> + </wsdl:portType> + <wsdl:portType name="CallbackPortType"> + <wsdl:operation name="callback"> + <wsdl:input message="tns:invokeResponse"> + </wsdl:input> + </wsdl:operation> + </wsdl:portType> + <wsdl:binding + name="InvokeBinding" + type="tns:InvokePortType"> + <soap:binding + style="document" + transport="http://schemas.xmlsoap.org/soap/http" /> + <wsdl:operation name="invokeOperation"> + <soap:operation + soapAction="http://siserver.org/invokeOperation" + style="document" /> + <wsdl:input> + <soap:body use="literal" /> + </wsdl:input> + </wsdl:operation> + <wsdl:operation name="invokeOperationAsync"> + <soap:operation + soapAction="http://siserver.org/invokeOperationAsync" + style="document" /> + <wsdl:input> + <soap:body use="literal" /> + </wsdl:input> + </wsdl:operation> + <wsdl:operation name="invokeOperationSync"> + <soap:operation + soapAction="http://siserver.org/invokeOperationSync" + style="document" /> + <wsdl:input> + <soap:body use="literal" /> + </wsdl:input> + <wsdl:output> + <soap:body use="literal" /> + </wsdl:output> + </wsdl:operation> + <wsdl:operation name="invokePlan"> + <soap:operation + soapAction="http://siserver.org/invokePlan" + style="document" /> + <wsdl:input> + <soap:body use="literal" /> + </wsdl:input> + </wsdl:operation> + </wsdl:binding> + <wsdl:binding + name="CallbackBinding" + type="tns:CallbackPortType"> + <soap:binding + style="document" + transport="http://schemas.xmlsoap.org/soap/http" /> + <wsdl:operation name="callback"> + <wsdl:input> + <soap:body use="literal" /> + </wsdl:input> + </wsdl:operation> + </wsdl:binding> + <!-- Note, in original invoker.wsdl the service element was hosting InvokerPort + and CallbackPort. This resulted in crashes of the Apache ODE (WSO2 BPS) as + it only distinguishes bey service name --> + <wsdl:service name="InvokerService"> + <wsdl:port + name="CallbackPort" + binding="tns:CallbackBinding"> + <soap:address location="http://localhost:8088/callback" /> + </wsdl:port> + <wsdl:port + name="InvokePort" + binding="tns:InvokeBinding"> + <soap:address location="http://0.0.0.0:8081/invoker" /> + </wsdl:port> + </wsdl:service> +</wsdl:definitions>
\ No newline at end of file diff --git a/lcm/ns/data/init/invoker.xsd b/lcm/ns/data/init/invoker.xsd new file mode 100644 index 00000000..6874551a --- /dev/null +++ b/lcm/ns/data/init/invoker.xsd @@ -0,0 +1,136 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + Copyright 2016 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. + +--> +<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema" + xmlns:ns="http://siserver.org/schema" attributeFormDefault="qualified" + elementFormDefault="qualified" targetNamespace="http://siserver.org/schema"> + <xs:complexType name="ParamsMapItemType"> + <xs:sequence> + <xs:element name="key" type="xs:string" /> + <xs:element name="value" type="xs:string" /> + </xs:sequence> + </xs:complexType> + <xs:complexType name="ParamsMap"> + <xs:sequence> + <xs:element minOccurs="0" maxOccurs="unbounded" name="Param" + type="ns:ParamsMapItemType" /> + </xs:sequence> + </xs:complexType> + <xs:complexType name="Doc"> + <xs:sequence> + <xs:any minOccurs="0" maxOccurs="1" processContents="skip" /> + </xs:sequence> + </xs:complexType> + <xs:element name="invokeOperation" type="ns:invokeOperationAsync" /> + <xs:element name="invokeOperationAsync" type="ns:invokeOperationAsync" /> + <xs:complexType name="invokeOperationAsync"> + <xs:sequence> + <xs:element minOccurs="1" maxOccurs="1" name="CsarID" + type="xs:string" /> + <xs:element minOccurs="0" maxOccurs="1" name="ServiceInstanceID" + type="xs:string" /> + <xs:element minOccurs="0" maxOccurs="1" name="NodeInstanceID" + type="xs:string" /> + <xs:element minOccurs="1" maxOccurs="1" + name="ServiceTemplateIDNamespaceURI" type="xs:string" /> + <xs:element minOccurs="1" maxOccurs="1" + name="ServiceTemplateIDLocalPart" type="xs:string" /> + <xs:choice> + <xs:element minOccurs="1" maxOccurs="1" name="NodeTemplateID" + type="xs:string" /> + <xs:element minOccurs="1" maxOccurs="1" + name="RelationshipTemplateID" type="xs:string" /> + </xs:choice> + <xs:element minOccurs="0" maxOccurs="1" name="InterfaceName" + type="xs:string" /> + <xs:element minOccurs="1" maxOccurs="1" name="OperationName" + type="xs:string" /> + <xs:element minOccurs="1" maxOccurs="1" name="ReplyTo" + type="xs:string" /> + <xs:element minOccurs="1" maxOccurs="1" name="MessageID" + type="xs:string" /> + <xs:choice> + <xs:element minOccurs="0" name="Params" type="ns:ParamsMap" /> + <xs:element minOccurs="0" name="Doc" type="ns:Doc" /> + </xs:choice> + </xs:sequence> + </xs:complexType> + <xs:element name="invokeOperationSync" type="ns:invokeOperationSync" /> + <xs:complexType name="invokeOperationSync"> + <xs:sequence> + <xs:element minOccurs="1" maxOccurs="1" name="CsarID" + type="xs:string" /> + <xs:element minOccurs="0" maxOccurs="1" name="ServiceInstanceID" + type="xs:string" /> + <xs:element minOccurs="0" maxOccurs="1" name="NodeInstanceID" + type="xs:string" /> + <xs:element minOccurs="1" maxOccurs="1" + name="ServiceTemplateIDNamespaceURI" type="xs:string" /> + <xs:element minOccurs="1" maxOccurs="1" + name="ServiceTemplateIDLocalPart" type="xs:string" /> + <xs:choice> + <xs:element minOccurs="1" maxOccurs="1" name="NodeTemplateID" + type="xs:string" /> + <xs:element minOccurs="1" maxOccurs="1" + name="RelationshipTemplateID" type="xs:string" /> + </xs:choice> + <xs:element minOccurs="0" maxOccurs="1" name="InterfaceName" + type="xs:string" /> + <xs:element minOccurs="1" maxOccurs="1" name="OperationName" + type="xs:string" /> + <xs:choice> + <xs:element minOccurs="0" name="Params" type="ns:ParamsMap" /> + <xs:element minOccurs="0" name="Doc" type="ns:Doc" /> + </xs:choice> + </xs:sequence> + </xs:complexType> + <xs:element name="invokePlan" type="ns:invokePlan" /> + <xs:complexType name="invokePlan"> + <xs:sequence> + <xs:element minOccurs="1" maxOccurs="1" name="CsarID" + type="xs:string" /> + <xs:element minOccurs="0" maxOccurs="1" name="ServiceInstanceID" + type="xs:string" /> + <xs:element minOccurs="1" maxOccurs="1" name="PlanIDNamespaceURI" + type="xs:string" /> + <xs:element minOccurs="1" maxOccurs="1" name="PlanIDLocalPart" + type="xs:string" /> + <xs:element minOccurs="1" maxOccurs="1" name="OperationName" + type="xs:string" /> + <xs:element minOccurs="1" maxOccurs="1" name="ReplyTo" + type="xs:string" /> + <xs:element minOccurs="1" maxOccurs="1" name="MessageID" + type="xs:string" /> + <xs:choice> + <xs:element minOccurs="0" name="Params" type="ns:ParamsMap" /> + <xs:element minOccurs="0" name="Doc" type="ns:Doc" /> + </xs:choice> + </xs:sequence> + </xs:complexType> + <xs:element name="invokeResponse" type="ns:invokeResponse" /> + <xs:complexType name="invokeResponse"> + <xs:sequence> + <xs:element minOccurs="0" maxOccurs="1" name="MessageID" + type="xs:string" /> + <xs:choice> + <xs:element minOccurs="0" name="Params" type="ns:ParamsMap" /> + <xs:element minOccurs="0" name="Doc" type="ns:Doc" /> + </xs:choice> + </xs:sequence> + </xs:complexType> +</xs:schema>
\ No newline at end of file diff --git a/lcm/ns/data/scalemapping.json b/lcm/ns/data/scalemapping.json new file mode 100644 index 00000000..410332c9 --- /dev/null +++ b/lcm/ns/data/scalemapping.json @@ -0,0 +1,112 @@ +{ + "scale_options": [ + { + "ns_instanceId":"23", + "ns_scale_aspect": "TIC_EDGE_IMS", + "ns_scale_info_list": [ + { + "step": "1", + "vnf_scale_list":[ + { + "vnfInstanceId":"nf_zte_cscf", + "vnf_scaleAspectId": "gpu", + "numberOfSteps": "1" + }, + { + "vnfInstanceId":"nf_zte_hss", + "vnf_scaleAspectId": "gpu", + "numberOfSteps": "1" + } + ] + }, + { + "step": "2", + "vnf_scale_list":[ + { + "vnfInstanceId":"nf_zte_cscf", + "vnf_scaleAspectId": "mpu", + "numberOfSteps": "1" + }, + { + "vnfInstanceId":"nf_zte_hss", + "vnf_scaleAspectId": "mpu", + "numberOfSteps": "1" + } + ] + } + ] + }, + { + "ns_instanceId":"23", + "ns_scale_aspect": "TIC_EDGE_HW", + "ns_scale_info_list": [ + { + "step": "4", + "vnf_scale_list":[ + { + "vnfInstanceId":"nf_hw_cscf", + "vnf_scaleAspectId": "gpu", + "numberOfSteps": "1" + }, + { + "vnfInstanceId":"nf_hw_hss", + "vnf_scaleAspectId": "gpu", + "numberOfSteps": "1" + } + ] + }, + { + "step": "6", + "vnf_scale_list":[ + { + "vnfInstanceId":"nf_HW_cscf", + "vnf_scaleAspectId": "gpu", + "numberOfSteps": "1" + }, + { + "vnfInstanceId":"nf_HW_hss", + "vnf_scaleAspectId": "gpu", + "numberOfSteps": "1" + } + ] + } + ] + }, + { + "ns_instanceId":"235", + "ns_scale_aspect": "TIC_EDGE_HW", + "ns_scale_info_list": [ + { + "step": "4", + "vnf_scale_list":[ + { + "vnfInstanceId":"nf_hw_cscf", + "vnf_scaleAspectId": "gpu", + "numberOfSteps": "123" + }, + { + "vnfInstanceId":"nf_hw_hss", + "vnf_scaleAspectId": "gpu", + "numberOfSteps": "456" + } + ] + }, + { + "step": "6", + "vnf_scale_list":[ + { + "vnfInstanceId":"nf_HW_cscf", + "vnf_scaleAspectId": "gpu", + "numberOfSteps": "1" + }, + { + "vnfInstanceId":"nf_HW_hss", + "vnf_scaleAspectId": "gpu", + "numberOfSteps": "1" + } + ] + } + ] + } + ] +}
\ No newline at end of file diff --git a/lcm/ns/ns_create.py b/lcm/ns/ns_create.py new file mode 100644 index 00000000..69d7ae5a --- /dev/null +++ b/lcm/ns/ns_create.py @@ -0,0 +1,59 @@ +# Copyright 2016 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 uuid + +from lcm.pub.database.models import NSDModel, NSInstModel +from lcm.pub.exceptions import NSLCMException +from lcm.pub.utils.timeutil import now_time + +logger = logging.getLogger(__name__) + + +class CreateNSService(object): + def __init__(self, nsd_id, ns_name, description): + self.nsd_id = nsd_id + self.ns_name = ns_name + self.description = description + self.ns_inst_id = '' + self.ns_package_id = '' + + def do_biz(self): + self.check_nsd_valid() + self.check_ns_inst_name_exist() + self.create_ns_inst() + logger.debug("CreateNSService::do_biz::ns_inst_id=%s" % self.ns_inst_id) + return self.ns_inst_id + + def check_nsd_valid(self): + logger.debug("CreateNSService::check_nsd_valid::nsd_id=%s" % self.nsd_id) + ns_package_info = NSDModel.objects.filter(nsd_id=self.nsd_id) + if not ns_package_info: + raise NSLCMException("nsd(%s) not exists." % self.nsd_id) + self.ns_package_id = ns_package_info[0].id + logger.debug("CreateNSService::check_nsd_valid::ns_package_id=%s" % self.ns_package_id) + + def check_ns_inst_name_exist(self): + is_exist = NSInstModel.objects.filter(name=self.ns_name).exists() + logger.debug("CreateNSService::check_ns_inst_name_exist::is_exist=%s" % is_exist) + if is_exist: + raise NSLCMException("ns(%s) already existed." % self.ns_name) + + def create_ns_inst(self): + self.ns_inst_id = str(uuid.uuid4()) + logger.debug("CreateNSService::create_ns_inst::ns_inst_id=%s" % self.ns_inst_id) + NSInstModel(id=self.ns_inst_id, name=self.ns_name, nspackage_id=self.ns_package_id, + nsd_id=self.nsd_id, description=self.description, status='empty', + lastuptime=now_time()).save() + diff --git a/lcm/ns/ns_get.py b/lcm/ns/ns_get.py new file mode 100644 index 00000000..15f20e15 --- /dev/null +++ b/lcm/ns/ns_get.py @@ -0,0 +1,127 @@ +# Copyright 2016 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 json +import logging +import traceback + +from lcm.ns.const import OWNER_TYPE +from lcm.pub.database.models import NSInstModel, NfInstModel, VLInstModel, CPInstModel, VNFFGInstModel + +logger = logging.getLogger(__name__) + + +class GetNSInfoService(object): + def __init__(self, ns_inst_id=None): + self.ns_inst_id = ns_inst_id + + def get_ns_info(self): + try: + if self.ns_inst_id: + return self.get_single_ns_info(self.ns_inst_id) + else: + return self.get_total_ns_info() + except: + logger.error(traceback.format_exc()) + return None if self.ns_inst_id else [] + + def get_total_ns_info(self): + ns_inst_infos = NSInstModel.objects.all() + ns_info_list = [] + for info in ns_inst_infos: + ret = self.get_single_ns_info(info.id) + if not ret: + continue + ns_info_list.append(ret) + return ns_info_list + + def get_single_ns_info(self, ns_inst_id): + ns_insts = NSInstModel.objects.filter(id=ns_inst_id) + if not ns_insts: + return None + ns_inst_info = ns_insts[0] + ret = { + 'nsInstanceId': ns_inst_info.id, + 'nsName': ns_inst_info.name, + 'description': ns_inst_info.description, + 'nsdId': ns_inst_info.nsd_id, + 'vnfInfoId': self.get_vnf_infos(ns_inst_id), + 'vlInfo': self.get_vl_infos(ns_inst_id), + 'vnffgInfo': self.get_vnffg_infos(ns_inst_id, ns_inst_info.nsd_model), + 'nsState': ns_inst_info.status} + return ret + + @staticmethod + def get_vnf_infos(ns_inst_id): + ns_inst_infos = NfInstModel.objects.filter(ns_inst_id=ns_inst_id) + vnf_info_list = [] + for info in ns_inst_infos: + vnf_info = { + 'vnfInstanceId': info.nfinstid, + 'vnfInstanceName': info.nf_name, + 'vnfProfileId': info.vnf_id} + vnf_info_list.append(vnf_info) + return vnf_info_list + + def get_vl_infos(self, ns_inst_id): + vl_inst_infos = VLInstModel.objects.filter(ownertype=OWNER_TYPE.NS, ownerid=ns_inst_id) + vl_info_list = [] + for info in vl_inst_infos: + vl_info = { + 'vlInstanceId': info.vlinstanceid, + 'vlInstanceName': info.vlinstancename, + 'vldId': info.vldid, + 'relatedCpInstanceId': self.get_cp_infos(info.vlinstanceid)} + vl_info_list.append(vl_info) + return vl_info_list + + @staticmethod + def get_cp_infos(vl_inst_id): + cp_inst_infos = CPInstModel.objects.filter(relatedvl__icontains=vl_inst_id) + cp_info_list = [] + for info in cp_inst_infos: + cp_info = { + 'cpInstanceId': info.cpinstanceid, + 'cpInstanceName': info.cpname, + 'cpdId': info.cpdid} + cp_info_list.append(cp_info) + return cp_info_list + + def get_vnffg_infos(self, ns_inst_id, nsd_model): + vnffg_inst_infos = VNFFGInstModel.objects.filter(nsinstid=ns_inst_id) + vnffg_info_list = [] + for info in vnffg_inst_infos: + vnffg_info = { + 'vnffgInstanceId': info.vnffginstid, + 'vnfId': self.convert_string_to_list(info.vnflist), + 'pnfId': self.get_pnf_infos(nsd_model), + 'virtualLinkId': self.convert_string_to_list(info.vllist), + 'cpId': self.convert_string_to_list(info.cplist), + 'nfp': self.convert_string_to_list(info.fplist)} + vnffg_info_list.append(vnffg_info) + return vnffg_info_list + + @staticmethod + def get_pnf_infos(nsd_model): + context = json.loads(nsd_model) + pnfs = context['pnfs'] + pnf_list = [] + for pnf in pnfs: + pnf_list.append(pnf['pnf_id']) + return pnf_list + + @staticmethod + def convert_string_to_list(detail_id_string): + if not detail_id_string: + return None + return detail_id_string.split(',') diff --git a/lcm/ns/ns_instant.py b/lcm/ns/ns_instant.py new file mode 100644 index 00000000..8f0f1c65 --- /dev/null +++ b/lcm/ns/ns_instant.py @@ -0,0 +1,185 @@ +# Copyright 2016-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 json +import logging +import traceback +import time +import uuid + +from rest_framework import status + +from lcm.pub.database.models import DefPkgMappingModel, ServiceBaseInfoModel, InputParamMappingModel +from lcm.pub.database.models import NSInstModel, NfPackageModel, VNFFGInstModel +from lcm.pub.msapi.catalog import get_process_id, get_download_url_from_catalog +from lcm.pub.msapi.catalog import query_rawdata_from_catalog, get_servicetemplate_id, get_servicetemplate +from lcm.pub.msapi.wso2bpel import workflow_run +from lcm.pub.msapi.extsys import select_vnfm +from lcm.pub.utils.jobutil import JobUtil +from lcm.pub.utils import toscautil +from lcm.pub.utils.values import ignore_case_get +from lcm.pub.exceptions import NSLCMException + +logger = logging.getLogger(__name__) + + +class InstantNSService(object): + def __init__(self, ns_inst_id, plan_content): + self.ns_inst_id = ns_inst_id + self.req_data = plan_content + + def do_biz(self): + try: + job_id = JobUtil.create_job("NS", "NS_INST", self.ns_inst_id) + logger.debug('ns-instant(%s) workflow starting...' % self.ns_inst_id) + logger.debug('req_data=%s' % self.req_data) + ns_inst = NSInstModel.objects.get(id=self.ns_inst_id) + + input_parameters = [] + for key, val in self.req_data['additionalParamForNs'].items(): + input_parameters.append({"key": key, "value": val}) + + vim_id = '' + if 'location' in self.req_data['additionalParamForNs']: + vim_id = self.req_data['additionalParamForNs']['location'] + location_constraints = [] + if 'locationConstraints' in self.req_data: + location_constraints = self.req_data['locationConstraints'] + + JobUtil.add_job_status(job_id, 5, 'Start query nsd(%s)' % ns_inst.nspackage_id) + src_plan = query_rawdata_from_catalog(ns_inst.nspackage_id, input_parameters) + dst_plan = toscautil.convert_nsd_model(src_plan["rawData"]) + logger.debug('tosca plan dest:%s' % dst_plan) + NSInstModel.objects.filter(id=self.ns_inst_id).update(nsd_model=dst_plan) + + params_json = json.JSONEncoder().encode(self.req_data["additionalParamForNs"]) + # start + params_vnf = [] + plan_dict = json.JSONDecoder().decode(dst_plan) + for vnf in ignore_case_get(plan_dict, "vnfs"): + vnfd_id = vnf['properties']['id'] + vnfd = NfPackageModel.objects.get(vnfdid=vnfd_id) + vnfd_model = json.JSONDecoder().decode(vnfd.vnfdmodel) + vnfm_type = vnfd_model["metadata"].get("vnfmType", "ztevmanagerdriver") + vimid = self.get_vnf_vim_id(vim_id, location_constraints, vnfd_id) + vnfm_info = select_vnfm(vnfm_type=vnfm_type, vim_id=vimid) + params_vnf.append({ + "vnfProfileId": vnf["vnf_id"], + "additionalParam": { + "vimId": vimid, + "vnfmInstanceId": vnfm_info["vnfmId"], + "vnfmType": vnfm_type, + "inputs": params_json + } + }) + # end + + self.set_vl_vim_id(vim_id, location_constraints, plan_dict) + dst_plan = json.JSONEncoder().encode(plan_dict) + logger.debug('tosca plan dest add vimid:%s' % dst_plan) + NSInstModel.objects.filter(id=self.ns_inst_id).update(nsd_model=dst_plan) + + vnf_params_json = json.JSONEncoder().encode(params_vnf) + plan_input = {'jobId': job_id, + 'nsInstanceId': self.req_data["nsInstanceId"], + 'object_context': dst_plan, + 'object_additionalParamForNs': params_json, + 'object_additionalParamForVnf': vnf_params_json} + plan_input.update(**self.get_model_count(dst_plan)) + plan_input["sdnControllerId"] = ignore_case_get( + self.req_data['additionalParamForNs'], "sdncontroller") + + ServiceBaseInfoModel(service_id=self.ns_inst_id, + service_name=ns_inst.name, + service_type='NFVO', + description=ns_inst.description, + active_status='--', + status=ns_inst.status, + creator='--', + create_time=int(time.time()*1000)).save() + + service_tpl = get_servicetemplate(ns_inst.nsd_id) + DefPkgMappingModel(service_id=self.ns_inst_id, + service_def_id=service_tpl['csarId'], + template_name=service_tpl['templateName'], + template_id=service_tpl['serviceTemplateId']).save() + + for key, val in self.req_data['additionalParamForNs'].items(): + InputParamMappingModel(service_id=self.ns_inst_id, + input_key=key, input_value=val).save() + + for vnffg in ignore_case_get(plan_dict, "vnffgs"): + VNFFGInstModel(vnffgdid=vnffg["vnffg_id"], + vnffginstid=str(uuid.uuid4()), + nsinstid=self.ns_inst_id, + endpointnumber=0).save() + + servicetemplate_id = get_servicetemplate_id(ns_inst.nsd_id) + process_id = get_process_id('init', servicetemplate_id) + data = {"processId": process_id, "params": {"planInput": plan_input}} + logger.debug('ns-instant(%s) workflow data:%s' % (self.ns_inst_id, data)) + + ret = workflow_run(data) + logger.info("ns-instant(%s) workflow result:%s" % (self.ns_inst_id, ret)) + JobUtil.add_job_status(job_id, 10, 'NS inst(%s) workflow started: %s' % ( + self.ns_inst_id, ret.get('status'))) + if ret.get('status') == 1: + return dict(data={'jobId': job_id}, status=status.HTTP_200_OK) + return dict(data={'error': ret['message']}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + except Exception as e: + logger.error(traceback.format_exc()) + logger.error("ns-instant(%s) workflow error:%s" % (self.ns_inst_id, e.message)) + JobUtil.add_job_status(job_id, 255, 'NS instantiation failed: %s' % e.message) + return dict(data={'error': e.message}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + + def get_vnf_vim_id(self, vim_id, location_constraints, vnfdid): + for location in location_constraints: + if "vnfProfileId" in location and vnfdid == location["vnfProfileId"]: + return location["locationConstraints"]["vimId"] + if vim_id: + return vim_id + raise NSLCMException("No Vim info is found for vnf(%s)." % vnfdid) + + def set_vl_vim_id(self, vim_id, location_constraints, plan_dict): + if "vls" not in plan_dict: + logger.debug("No vl is found in nsd.") + return + vl_vnf = {} + for vnf in ignore_case_get(plan_dict, "vnfs"): + if "dependencies" in vnf: + for depend in vnf["dependencies"]: + vl_vnf[depend["vl_id"]] = vnf['properties']['id'] + vnf_vim = {} + for location in location_constraints: + if "vnfProfileId" in location: + vnfd_id = location["vnfProfileId"] + vnf_vim[vnfd_id] = location["locationConstraints"]["vimId"] + for vl in plan_dict["vls"]: + vnfdid = ignore_case_get(vl_vnf, vl["vl_id"]) + vimid = ignore_case_get(vnf_vim, vnfdid) + if not vimid: + vimid = vim_id + if not vimid: + raise NSLCMException("No Vim info for vl(%s) of vnf(%s)." % (vl["vl_id"], vnfdid)) + if "location_info" not in vl["properties"]: + vl["properties"]["location_info"] = {} + vl["properties"]["location_info"]["vimid"] = vimid + + @staticmethod + def get_model_count(context): + data = json.JSONDecoder().decode(context) + vls = len(data.get('vls', [])) + sfcs = len(data.get('fps', [])) + vnfs = len(data.get('vnfs', [])) + return {'vlCount': str(vls), 'sfcCount': str(sfcs), 'vnfCount': str(vnfs)} diff --git a/lcm/ns/ns_manual_scale.py b/lcm/ns/ns_manual_scale.py new file mode 100644 index 00000000..dc2d1b4a --- /dev/null +++ b/lcm/ns/ns_manual_scale.py @@ -0,0 +1,129 @@ +# 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 datetime +import logging +import threading +import time +import traceback + +from lcm.ns.const import NS_INST_STATUS +from lcm.ns.vnfs.scale_vnfs import NFManualScaleService +from lcm.pub.database.models import JobModel, NSInstModel +from lcm.pub.exceptions import NSLCMException +from lcm.pub.utils.jobutil import JobUtil, JOB_MODEL_STATUS +from lcm.pub.utils.values import ignore_case_get +from lcm.pub.utils.scaleaspect import get_scale_vnf_data + +JOB_ERROR = 255 +SCALE_TYPE = ("SCALE_NS", "SCALE_VNF") +logger = logging.getLogger(__name__) + + +class NSManualScaleService(threading.Thread): + def __init__(self, ns_instance_id, request_data, job_id): + super(NSManualScaleService, self).__init__() + self.ns_instance_id = ns_instance_id + self.request_data = request_data + self.job_id = job_id + self.scale_type = '' + self.scale_vnf_data = '' + self.scale_ns_data = '' + + def run(self): + try: + self.do_biz() + except NSLCMException as e: + JobUtil.add_job_status(self.job_id, JOB_ERROR, e.message) + except: + logger.error(traceback.format_exc()) + JobUtil.add_job_status(self.job_id, JOB_ERROR, 'ns scale fail') + finally: + self.update_ns_status(NS_INST_STATUS.ACTIVE) + + def do_biz(self): + self.update_job(1, desc='ns scale start') + self.update_ns_status(NS_INST_STATUS.SCALING) + self.get_and_check_params() + self.do_vnfs_scale() + self.update_job(100, desc='ns scale success') + + def get_and_check_params(self): + self.scale_type = ignore_case_get(self.request_data, 'scaleType') + if not self.scale_type or self.scale_type != SCALE_TYPE[0]: + logger.error('scaleType parameter does not exist or value is incorrect. It must be SCALE_NS.') + raise NSLCMException('scaleType parameter does not exist or value incorrect. It must be SCALE_NS.') + + # Get data if SCALE_NS + self.scale_ns_data = ignore_case_get(self.request_data, 'scaleNsData') + self.scale_vnf_data = get_scale_vnf_data(self.scale_ns_data,self.ns_instance_id) + logger.debug('scale_vnf_data = %s' % self.scale_vnf_data) + # Get data if SCALE_VNF + #self.scale_vnf_data = ignore_case_get(self.request_data, 'scaleVnfData') + if not self.scale_vnf_data: + logger.error('scaleVnfData parameter does not exist or value incorrect') + raise NSLCMException('scaleVnfData parameter does not exist or value incorrect') + + def do_vnfs_scale(self): + for i in range(len(self.scale_vnf_data)): + vnf_scale_params = self.prepare_vnf_scale_params(self.scale_vnf_data[i]) + count = len(self.scale_vnf_data) + progress_range = [11 + 80 / count * i, 10 + 80 / count * (i + 1)] + status = self.do_vnf_scale(vnf_scale_params, progress_range) + if status is JOB_MODEL_STATUS.FINISHED: + logger.info('nf[%s] scale handle end' % vnf_scale_params.get('vnfInstanceId')) + self.update_job(progress_range[1], + desc='nf[%s] scale handle end' % vnf_scale_params.get('vnfInstanceId')) + else: + logger.error('nf scale failed') + raise NSLCMException('nf scale failed') + + def prepare_vnf_scale_params(self, vnf_data): + vnf_instance_id = ignore_case_get(vnf_data, 'vnfInstanceId') + scale_by_step_data = ignore_case_get(vnf_data, 'scaleByStepData') + result = { + "vnfInstanceId": vnf_instance_id, + "scaleByStepData": scale_by_step_data, + "nsInstanceId": self.ns_instance_id + } + return result + + def do_vnf_scale(self, vnf_scale_params, progress_range): + nf_inst_id = vnf_scale_params.get('vnfInstanceId') + nf_service = NFManualScaleService(nf_inst_id, vnf_scale_params) + nf_service.start() + self.update_job(progress_range[0], desc='nf[%s] scale handle start' % nf_inst_id) + status = self.wait_job_finish(nf_service.job_id) + return status + + @staticmethod + def wait_job_finish(sub_job_id, timeout=3600): + query_interval = 2 + start_time = end_time = datetime.datetime.now() + while (end_time - start_time).seconds < timeout: + job_result = JobModel.objects.get(jobid=sub_job_id) + time.sleep(query_interval) + end_time = datetime.datetime.now() + if job_result.progress == 100: + return JOB_MODEL_STATUS.FINISHED + elif job_result.progress > 100: + return JOB_MODEL_STATUS.ERROR + else: + continue + return JOB_MODEL_STATUS.TIMEOUT + + def update_job(self, progress, desc=''): + JobUtil.add_job_status(self.job_id, progress, desc) + + def update_ns_status(self, status): + NSInstModel.objects.filter(id=self.ns_instance_id).update(status=status) diff --git a/lcm/ns/ns_terminate.py b/lcm/ns/ns_terminate.py new file mode 100644 index 00000000..95806573 --- /dev/null +++ b/lcm/ns/ns_terminate.py @@ -0,0 +1,279 @@ +# Copyright 2016 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 math +import traceback +import logging +import json +import threading +import time +from lcm.ns.vnfs.wait_job import wait_job_finish +from lcm.pub.database.models import NSInstModel, VLInstModel, FPInstModel, NfInstModel +from lcm.pub.database.models import DefPkgMappingModel, InputParamMappingModel, ServiceBaseInfoModel +from lcm.pub.utils.jobutil import JOB_MODEL_STATUS, JobUtil +from lcm.pub.exceptions import NSLCMException +from lcm.pub.msapi.nslcm import call_from_ns_cancel_resource +from lcm.pub.utils.values import ignore_case_get + +JOB_ERROR = 255 +# [delete vnf try times] + +logger = logging.getLogger(__name__) + + +class TerminateNsService(threading.Thread): + def __init__(self, ns_inst_id, terminate_type, terminate_timeout, job_id): + threading.Thread.__init__(self) + self.ns_inst_id = ns_inst_id + self.terminate_type = terminate_type + self.terminate_timeout = terminate_timeout + self.job_id = job_id + self.vnfm_inst_id = '' + + def run(self): + try: + self.do_biz() + except NSLCMException as e: + JobUtil.add_job_status(self.job_id, JOB_ERROR, e.message) + except: + logger.error(traceback.format_exc()) + JobUtil.add_job_status(self.job_id, JOB_ERROR, "ns terminate fail.", '') + + def do_biz(self): + if not self.check_data(): + JobUtil.add_job_status(self.job_id, 100, "Need not terminate.", '') + return + + self.cancel_sfc_list() + self.cancel_vnf_list() + time.sleep(4) + self.cancel_vl_list() + + self.finaldata() + + def check_data(self): + JobUtil.add_job_status(self.job_id, 0, "TERMINATING...", '') + ns_inst = NSInstModel.objects.filter(id=self.ns_inst_id) + if not ns_inst.exists(): + logger.warn('ns instance [%s] does not exist.' % self.ns_inst_id) + return False + JobUtil.add_job_status(self.job_id, 10, "Ns cancel: check ns_inst_id success", '') + return True + + # delete VLINST + def cancel_vl_list(self): + array_vlinst = VLInstModel.objects.filter(ownertype='2', ownerid=self.ns_inst_id) + if not array_vlinst: + logger.error("[cancel_vl_list] no vlinst attatch to ns_inst_id:%s" % self.ns_inst_id) + return + step_progress = 20 / len(array_vlinst) + cur_progress = 70 + for vlinst in array_vlinst: + tmp_msg = vlinst.vlinstanceid + try: + ret = self.delete_vl(tmp_msg) + if ret[0] == 0: + cur_progress += step_progress + result = json.JSONDecoder().decode(ret[1]).get("result", "") + if str(result) == '0': + JobUtil.add_job_status(self.job_id, cur_progress, "Delete vlinst:[%s] success." % tmp_msg, '') + else: + JobUtil.add_job_status(self.job_id, cur_progress, "Delete vlinst:[%s] failed." % tmp_msg, '') + return 'false' + else: + NSInstModel.objects.filter(id=self.ns_inst_id).update(status='FAILED') + return 'false' + except Exception as e: + logger.error("[cancel_vl_list] error[%s]!" % e.message) + logger.error(traceback.format_exc()) + JobUtil.add_job_status(self.job_id, cur_progress, "Delete vlinst:[%s] Failed." % tmp_msg, '') + return 'false' + return 'true' + + # delete SFC + def cancel_sfc_list(self): + array_sfcinst = FPInstModel.objects.filter(nsinstid=self.ns_inst_id) + if not array_sfcinst: + logger.error("[cancel_sfc_list] no sfcinst attatch to ns_inst_id:%s" % self.ns_inst_id) + return + step_progress = 20 / len(array_sfcinst) + cur_progress = 30 + for sfcinst in array_sfcinst: + tmp_msg = sfcinst.sfcid + try: + ret = self.delete_sfc(tmp_msg) + if ret[0] == 0: + cur_progress += step_progress + result = json.JSONDecoder().decode(ret[1]).get("result", "") + if str(result) == '0': + JobUtil.add_job_status(self.job_id, cur_progress, "Delete sfcinst:[%s] success." % tmp_msg, '') + else: + JobUtil.add_job_status(self.job_id, cur_progress, "Delete sfcinst:[%s] failed." % tmp_msg, '') + return 'false' + else: + NSInstModel.objects.filter(id=self.ns_inst_id).update(status='FAILED') + return 'false' + except Exception as e: + logger.error("[cancel_sfc_list] error[%s]!" % e.message) + logger.error(traceback.format_exc()) + JobUtil.add_job_status(self.job_id, cur_progress, "Delete sfcinst:[%s] Failed." % tmp_msg, '') + return 'false' + return 'true' + + # delete Vnf + def cancel_vnf_list(self): + array_vnfinst = NfInstModel.objects.filter(ns_inst_id=self.ns_inst_id) + if not array_vnfinst: + logger.error("[cancel_vnf_list] no vnfinst attatch to ns_inst_id:%s" % self.ns_inst_id) + return + step_progress = 20 / len(array_vnfinst) + cur_progress = 50 + for vnfinst in array_vnfinst: + tmp_msg = vnfinst.nfinstid + try: + self.delete_vnf(tmp_msg) + cur_progress += step_progress + JobUtil.add_job_status(self.job_id, cur_progress, "Delete vnfinst:[%s] success." % tmp_msg, '') + except Exception as e: + logger.error("[cancel_vnf_list] error[%s]!" % e.message) + logger.error(traceback.format_exc()) + JobUtil.add_job_status(self.job_id, cur_progress, "Delete vnfinst:[%s] Failed." % tmp_msg, '') + return 'false' + return 'true' + + def delete_vnf(self, nf_instid): + ret = call_from_ns_cancel_resource('vnf', nf_instid) + self.delete_resource(ret) + + def delete_sfc(self, sfc_instid): + ret = call_from_ns_cancel_resource('sfc', sfc_instid) + return ret + + def delete_vl(self, vl_instid): + ret = call_from_ns_cancel_resource('vl', vl_instid) + return ret + + def delete_resource(self, result): + logger.debug("terminate_type=%s, result=%s", self.terminate_type, result) + if result[0] == 0: + job_info = json.JSONDecoder().decode(result[1]) + vnfm_job_id = ignore_case_get(job_info, "jobid") + self.add_progress(5, "SEND_TERMINATE_REQ_SUCCESS") + if self.terminate_type == 'forceful': + ret = wait_job_finish(self.vnfm_inst_id, self.job_id, vnfm_job_id, + progress_range=[10, 50], + timeout=self.terminate_timeout, + job_callback=TerminateNsService.wait_job_mode_callback, mode='1') + if ret != JOB_MODEL_STATUS.FINISHED: + logger.error('[NS terminate] VNFM terminate ns failed') + NSInstModel.objects.filter(id=self.ns_inst_id).update(status='FAILED') + raise NSLCMException("DELETE_NS_RESOURCE_FAILED") + else: + logger.error('[NS terminate] VNFM terminate ns failed') + NSInstModel.objects.filter(id=self.ns_inst_id).update(status='FAILED') + raise NSLCMException("DELETE_NS_RESOURCE_FAILED") + + def exception(self): + NSInstModel.objects.filter(id=self.ns_inst_id).update(status='FAILED') + raise NSLCMException("DELETE_NS_RESOURCE_FAILED") + + def finaldata(self): + NSInstModel.objects.filter(id=self.ns_inst_id).update(status='null') + JobUtil.add_job_status(self.job_id, 100, "ns terminate ends.", '') + + # @staticmethod + # def call_vnfm_to_cancel_resource(res_type, instid): + # ret = call_from_ns_cancel_resource(res_type, instid) + # return ret + + def add_progress(self, progress, status_decs, error_code=""): + JobUtil.add_job_status(self.job_id, progress, status_decs, error_code) + + @staticmethod + def wait_job_mode_callback(vnfo_job_id, vnfm_job_id, job_status, jobs, progress_range, **kwargs): + for job in jobs: + progress = TerminateNsService.calc_progress_over_100(job['progress'], progress_range) + if 255 == progress and '1' == kwargs['mode']: + break + JobUtil.add_job_status(vnfo_job_id, progress, job.get('statusdescription', ''), job.get('errorcode', '')) + + latest_progress = TerminateNsService.calc_progress_over_100(job_status['progress'], progress_range) + if 255 == latest_progress and '1' == kwargs['mode']: + JobUtil.add_job_status(vnfo_job_id, progress_range[1], job_status.get('statusdescription', ''), + job_status.get('errorcode', '')) + else: + JobUtil.add_job_status(vnfo_job_id, latest_progress, job_status.get('statusdescription', ''), + job_status.get('errorcode', '')) + if job_status['status'] in ('error', 'finished'): + return True, job_status['status'] + return False, 'processing' + + @staticmethod + def wait_job_finish_common_call_back(vnfo_job_id, vnfm_job_id, job_status, jobs, progress_range, **kwargs): + error_254 = False + for job in jobs: + progress = TerminateNsService.calc_progress_over_100(job['progress'], progress_range) + if 254 == progress: + logger.debug("=========254==============") + progress = 255 + error_254 = True + JobUtil.add_job_status(vnfo_job_id, progress, job.get('statusdescription', ""), job.get('errorcode', "")) + latest_progress = TerminateNsService.calc_progress_over_100(job_status['progress'], progress_range) + if 254 == latest_progress: + logger.debug("=========254==============") + latest_progress = 255 + error_254 = True + JobUtil.add_job_status(vnfo_job_id, latest_progress, job_status.get('statusdescription', ""), + job_status.get('errorcode', "")) + # return error_254 + if error_254: + logger.debug("return 254") + return True, 'error_254' + if job_status['status'] in ('error', 'finished'): + return True, job_status['status'] + return False, 'processing' + + @staticmethod + def calc_progress_over_100(vnfm_progress, target_range=None): + if target_range is None: + target_range = [0, 100] + progress = int(vnfm_progress) + if progress > 100: + return progress + floor_progress = int(math.floor(float(target_range[1] - target_range[0]) / 100 * progress)) + target_range = floor_progress + target_range[0] + return target_range + + +class DeleteNsService(object): + def __init__(self, ns_inst_id): + self.ns_inst_id = ns_inst_id + + def do_biz(self): + try: + self.delete_ns() + except: + logger.error(traceback.format_exc()) + + def delete_ns(self): + logger.debug("delele NSInstModel(%s)", self.ns_inst_id) + NSInstModel.objects.filter(id=self.ns_inst_id).delete() + + logger.debug("delele InputParamMappingModel(%s)", self.ns_inst_id) + InputParamMappingModel.objects.filter(service_id=self.ns_inst_id).delete() + + logger.debug("delele DefPkgMappingModel(%s)", self.ns_inst_id) + DefPkgMappingModel.objects.filter(service_id=self.ns_inst_id).delete() + + logger.debug("delele ServiceBaseInfoModel(%s)", self.ns_inst_id) + ServiceBaseInfoModel.objects.filter(service_id=self.ns_inst_id).delete() diff --git a/lcm/ns/sfcs/__init__.py b/lcm/ns/sfcs/__init__.py new file mode 100644 index 00000000..b847fa86 --- /dev/null +++ b/lcm/ns/sfcs/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2016 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.
\ No newline at end of file diff --git a/lcm/ns/sfcs/create_flowcla.py b/lcm/ns/sfcs/create_flowcla.py new file mode 100644 index 00000000..346a8bcf --- /dev/null +++ b/lcm/ns/sfcs/create_flowcla.py @@ -0,0 +1,92 @@ +# Copyright 2016 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 + +from lcm.ns.sfcs.utils import get_fp_model_by_fp_inst_id +from lcm.pub.database.models import FPInstModel +from lcm.pub.msapi import extsys +from lcm.pub.msapi import sdncdriver + +logger = logging.getLogger(__name__) + + +class CreateFlowClassifier(object): + def __init__(self, data): + self.ns_model_data = data["ns_model_data"] + self.fp_inst_id = data["fpinstid"] + self.flow_classifiers_model = get_fp_model_by_fp_inst_id(data["ns_model_data"], self.fp_inst_id)["properties"][ + "policy"] + self.sdnControllerId = "" + self.url = "" + self.dscp = "" + self.ip_proto = "" + self.source_port_range = "" + self.dest_port_range = "" + self.source_ip_range = "" + self.dest_ip_range = "" + self.flow_classfier_id = "" + + def do_biz(self): + logger.info("CreateFlowClassifier start:") + self.init_data(self.flow_classifiers_model) + self.create_flow_classfier() + self.update_fp_inst() + logger.info("CreateFlowClassifier end:") + + def init_data(self, flow_classifiers_model): + fp_database_info = FPInstModel.objects.filter(fpinstid=self.fp_inst_id).get() + self.sdnControllerId = fp_database_info.sdncontrollerid + self.url = extsys.get_sdn_controller_by_id(self.sdnControllerId)["url"] + self.dscp = flow_classifiers_model["criteria"]["dscp"] + self.ip_proto = flow_classifiers_model["criteria"]["ip_protocol"] + self.source_port_range = flow_classifiers_model["criteria"]["source_port_range"] + self.dest_port_range = flow_classifiers_model["criteria"]["dest_port_range"] + self.dest_ip_range = flow_classifiers_model["criteria"]["dest_ip_range"] + self.source_ip_range = flow_classifiers_model["criteria"]["source_ip_range"] + + def update_fp_inst(self): + fp_inst_info = FPInstModel.objects.filter(fpinstid=self.fp_inst_id).get() + fp_inst_info.flowclassifiers = self.flow_classfier_id + FPInstModel.objects.filter(fpinstid=self.fp_inst_id).update(flowclassifiers=fp_inst_info.flowclassifiers) + + def create_flow_classfier(self): + data = { + "sdnControllerId": self.sdnControllerId, + "url": self.url, + "name": "", + "description": "", + "dscp": self.dscp, + "ip_proto": self.ip_proto, + "source_port_range": self.source_port_range, + "dest_port_range": self.dest_port_range, + "source_ip_range": self.concat_str(self.source_ip_range), + "dest_ip_range": self.concat_str(self.dest_ip_range) + } + # req_param = json.JSONEncoder().encoding(data) + # url = "/openoapi/sdncdriver/v1.0/createflowclassfier" + # ret = req_by_msb(url,"POST", data) + # if ret[0] > 0: + # logger.error('Send Flow Classifier request to Driver failed.') + # utils.sfc_inst_failed_handle(self.fp_inst_id, "Send Flow Classifier request to Driver failed.") + # raise NSLCMException('Send Flow Classifier request to Driver failed.') + # resp_body = json.loads(ret[1]) + self.flow_classfier_id = sdncdriver.create_flow_classfier(data) + + def concat_str(self, str_list): + final_str = "" + for str in str_list: + final_str += str + "," + return final_str[:-1] diff --git a/lcm/ns/sfcs/create_port_chain.py b/lcm/ns/sfcs/create_port_chain.py new file mode 100644 index 00000000..a3e98e09 --- /dev/null +++ b/lcm/ns/sfcs/create_port_chain.py @@ -0,0 +1,95 @@ +# Copyright 2016 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 json +import logging + +from lcm.pub.database.models import FPInstModel +from lcm.pub.msapi import extsys +from lcm.pub.msapi import sdncdriver + +logger = logging.getLogger(__name__) + + +class CreatePortChain(object): + def __init__(self, data): + self.fp_inst_id = data["fpinstid"] + self.ns_model_info = data["ns_model_data"] + self.sdnControllerId = "" + self.symmetric = "" + self.port_pair_groups_ids = [] + self.flow_classifier_ids = [] + + def do_biz(self): + logger.info("CreatePortChain start:") + self.init_data() + self.create_sfc() + logger.info("CreatePortChain end:") + + def init_data(self): + fp_inst_info = FPInstModel.objects.filter(fpinstid=self.fp_inst_id).get() + self.sdnControllerId = fp_inst_info.sdncontrollerid + self.symmetric = "true" if fp_inst_info.symmetric == 1 else "false" + flow_classfier_str = fp_inst_info.flowclassifiers + self.flow_classifier_ids = [flow_classfier_str] + portpairgroup_ids = [] + for portpairgroup in json.loads(fp_inst_info.portpairgroups): + portpairgroup_ids.append(portpairgroup["groupid"]) + self.port_pair_groups_ids = portpairgroup_ids + + def create_sfc(self): + data = { + "sdnControllerId": self.sdnControllerId, + "url": extsys.get_sdn_controller_by_id(self.sdnControllerId)["url"], + "flowClassifiers": self.flow_classifier_ids, + "portPairGroups": self.port_pair_groups_ids, + "symmetric": self.symmetric + } + + # url = "/openoapi/sdncdriver/v1.0/createchain" + # req_param = json.JSONEncoder.encoding(data) + # ret = req_by_msb(url, "POST", req_param) + # ret = req_by_msb("OPENAPI_CREATE_SERVICE_PORT_CHAIN",data) + # if ret[0] > 0: + # logger.error('Send SFC Create request to Driver failed.') + # sfc_inst_failed_handle( "Send SFC Create request to Driver failed.") + # raise NSLCMException('Send SFC Create request to Driver failed.') + # resp_body = json.loads(ret[1]) + # sfc_id = resp_body["id"] + sfc_id = sdncdriver.create_port_chain(data) + FPInstModel.objects.filter(fpinstid=self.fp_inst_id).update(sfcid=sfc_id) + + # def get_url_by_sdncontrollerid(self): + # try: + # logger.warn("query sdncontroller by id begins:") + # + # url = "/openoapi/extsys/v1/sdncontrollers/%s" % (self.sdnControllerId) + # ret = req_by_msb(url, "GET") + # if ret[0] > 0: + # logger.error('query sdncontroller failed.') + # raise VnfoException('query sdncontroller failed.') + # resp_body = json.JSONDecoder().decode(ret[1]) + # logger.warn("query sdncontroller by id ends:") + # except: + # if ret[0] > 0: + # logger.error('Send Flow Classifier request to Driver failed.') + # self.sfc_inst_failed_handle(self.fp_inst_id, "Send Flow Classifier request to Driver failed.") + # raise VnfoException('Send Flow Classifier request to Driver failed.') + # + # return resp_body('url') + + # def sfc_inst_failed_handle(fp_inst_id, error_msg): + # logger.error('create sfc failed, detail message: %s' % error_msg) + # FPInstModel.objects.filter(fpid=fp_inst_id).update(status="disabled").get() diff --git a/lcm/ns/sfcs/create_portpairgp.py b/lcm/ns/sfcs/create_portpairgp.py new file mode 100644 index 00000000..701adab9 --- /dev/null +++ b/lcm/ns/sfcs/create_portpairgp.py @@ -0,0 +1,277 @@ +# Copyright 2016 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 json +import logging + +from lcm.ns.sfcs.utils import get_fp_model_by_fp_inst_id +from lcm.pub.database.models import FPInstModel, NfInstModel, CPInstModel, PortInstModel, VNFCInstModel +from lcm.pub.msapi import extsys +from lcm.pub.msapi import sdncdriver + +logger = logging.getLogger(__name__) + + +class CreatePortPairGroup(object): + def __init__(self, data): + self.fp_inst_id = data["fpinstid"] + self.ns_model_data = data["ns_model_data"] + self.ns_inst_id = data["nsinstid"] + self.fp_model = get_fp_model_by_fp_inst_id(self.ns_model_data, + self.fp_inst_id) + self.port_pair_groups = [] + self.port_pair_group_db_infos = [] + self.sdncontrollerid = "" + + def do_biz(self): + logger.info("CreatePortPairGroup start") + self.init_port_pair_group(self.fp_model) + self.create_port_pair_and_groups() + self.save_port_pair_group_info_2_db() + logger.info("CreatePortPairGroup end") + + def create_port_pair_and_groups(self): + for port_pair_group_info in self.port_pair_groups: + port_pairs_info = port_pair_group_info.get("portpair") + port_pair_ids = [] + for port_pair_info in port_pairs_info: + port_pair_ids.append(self.create_port_pair(port_pair_info)) + self.create_port_pair_and_group(port_pair_ids) + + def save_port_pair_group_info_2_db(self): + logger.info("portpairgroups in fpinstmodel:%s" % self.port_pair_group_db_infos) + FPInstModel.objects.filter(fpinstid=self.fp_inst_id).update( + portpairgroups=json.JSONEncoder().encode(self.port_pair_group_db_infos)) + + def create_port_pair_and_group(self, port_pair_ids): + # url = const.port_pair_group_url + port_pair_group = { + "sdnControllerId": self.sdncontrollerid, + "url": extsys.get_sdn_controller_by_id(self.sdncontrollerid)["url"], + "portPairs": port_pair_ids + } + port_pair_group_id = sdncdriver.create_port_pair_group(port_pair_group) + port_pair_group_info = { + "groupid": port_pair_group_id, + "portpair": port_pair_ids} + + self.port_pair_group_db_infos.append(port_pair_group_info) + + def create_port_pair(self, port_pair_info): + # url = const.port_pair_url + port_pair_info.update({ + "sdnControllerId": self.sdncontrollerid, + "url": extsys.get_sdn_controller_by_id(self.sdncontrollerid)["url"]}) + return sdncdriver.create_port_pair(port_pair_info) + + def init_port_pair_group(self, fp_model): + forwarder_list = fp_model["forwarder_list"] + self.sdncontrollerid = FPInstModel.objects.filter(fpinstid=self.fp_inst_id).get().sdncontrollerid + index = 0 + while index < len(forwarder_list): + if (forwarder_list[index]["type"] == "cp"): + index += self.generate_port_pair_group_cp(index, forwarder_list) + else: + index += self.generate_port_pair_group_vnf(index, forwarder_list) + index += 1 + + logger.info("port pair group: %s" % self.port_pair_groups) + + def generate_port_pair_group_cp(self, index, forwarder_list): + cur_forwarder = forwarder_list[index]["node_name"] + cur_cp_model_info = self.get_cp_model_info_by_cpid(cur_forwarder) + if (index < len(forwarder_list) - 1 and forwarder_list[index + 1]["type"] == "cp"): + next_forward = forwarder_list[index + 1]["node_name"] + next_cp_model_info = self.get_cp_model_info_by_cpid(next_forward) + if (cur_cp_model_info["pnf_id"] == next_cp_model_info["pnf_id"]): # same port pair group + self.generate_port_pair_group_type_cp(cur_cp_model_info, next_cp_model_info) + return 1 + self.generate_port_pair_group_type_cp(cur_cp_model_info, cur_cp_model_info) + return 0 + + def generate_port_pair_group_vnf(self, index, forwarder_list): + incre = 0 + cur_vnf_id = forwarder_list[index]["node_name"] + cur_forwarder = forwarder_list[index]["capability"] + self.vnf_model_in_ns_info = self.get_vnf_model_info_by_vnf_id(cur_vnf_id) + vnf_inst_database_info = NfInstModel.objects.filter(vnf_id=self.vnf_model_in_ns_info["vnf_id"], + ns_inst_id=self.ns_inst_id).get() + self.vnf_inst_database_info = vnf_inst_database_info + logger.info("VNFD MODEL : %s" %vnf_inst_database_info.vnfd_model) + vnfd_model_info = json.loads(vnf_inst_database_info.vnfd_model) + # vnfd_model_info = json.dumps(vnf_inst_database_info.vnfd_model) + # vnfd_model_info = json.dumps(self.vnf_inst_database_info.vnfd_model) + self.vnfd_model_info = vnfd_model_info + logger.info("forward list: %s" % forwarder_list) + logger.info("current fowarder : %s" % cur_forwarder) + cpd_id = self.get_cpdid_info_forwarder(vnfd_model_info, cur_forwarder) + self.cp_model_in_vnf = self.get_cp_from_vnfd_model(cpd_id) + vnfc_inst_infos = VNFCInstModel.objects.filter(nfinstid=vnf_inst_database_info.nfinstid) + if not vnfc_inst_infos: + logger.error("VNFCInstModel is None") + return 0 + logger.info("vnfc instance id: %s" % vnfc_inst_infos.get().vnfcinstanceid) + logger.info("cpd id: %s" % cpd_id) + cp_inst_infos = [] + for vnfc_inst_info in vnfc_inst_infos: + cp_db_info = CPInstModel.objects.filter(cpdid=cpd_id, + # ownertype=OWNER_TYPE.VNF, + ownertype=3, + ownerid=vnfc_inst_info.vnfcinstanceid).get() + if cp_db_info: + cp_inst_infos.append(cp_db_info) + if not cp_inst_infos: + logger.error("CPInstModel is None") + return 0 + logger.info("cp info : %s" % cp_inst_infos) + port_pairs = [] + if (index < len(forwarder_list) - 1 and forwarder_list[index + 1]["type"] == "vnf"): + next_vnf_id = forwarder_list[index + 1]["node_name"] + if (cur_vnf_id != next_vnf_id): + for cp_inst_info in cp_inst_infos: + port_pairs.append(self.generate_port_pair_group(cp_inst_info, cp_inst_info)) + else: + next_forwarder = forwarder_list[index]["capability"] + next_forwarder_cpd_id = self.get_cpdid_info_forwarder(vnfd_model_info, next_forwarder) + + vnfc_inst_infos = VNFCInstModel.objects.filter(nfinstid=vnf_inst_database_info.nfinstid) + + if not vnfc_inst_infos: + logger.error("VNFCInstModel is None") + return 0 + next_cp_inst_infos = [] + for vnfc_inst_info in vnfc_inst_infos: + next_cp_db_info = CPInstModel.objects.filter(cpdid=next_forwarder_cpd_id, + # ownertype=OWNER_TYPE.VNF, + ownertype=3, + ownerid=vnfc_inst_info.vnfcinstanceid).get() + if next_cp_db_info: + next_cp_inst_infos.append(next_cp_db_info) + + port_pairs.append(self.generate_port_pair_group(cp_inst_infos[0], next_cp_inst_infos[0])) + incre = 1 + + else: + for cp_inst_info in cp_inst_infos: + port_pairs.append(self.generate_port_pair_group(cp_inst_info, cp_inst_info)) + self.port_pair_groups.append( + { + "portpair": port_pairs + } + ) + return incre + + def generate_port_pair_group_type_cp(self, ingress_cp_model_info, egress_cp_model_info): + sf_type = "" + request_reclassification = False + nsh_aware = True + sf_param = {} + if ingress_cp_model_info["pnf_id"] == "": + pass + else: + pnf_model_info = self.get_pnf_model_info_by_cpid(ingress_cp_model_info["pnf_id"]) + sf_type = "pnf_" + pnf_model_info["properties"]["pnf_type"] + nsh_aware = pnf_model_info["properties"]["nsh_aware"] + request_reclassification = pnf_model_info["properties"]["request_reclassification"] + sf_param = pnf_model_info["properties"] + sf_param["mng-address"] = pnf_model_info["properties"]["management_address"] + port_pair_info = { + "sfType": sf_type, + "nshAware": nsh_aware, + "requestReclassification": request_reclassification, + "ingress": { + "encapsulation": ingress_cp_model_info["properties"]["sfc_encapsulation"], + "ip": ingress_cp_model_info["properties"]["ip_address"], + "mac": ingress_cp_model_info["properties"]["mac_address"], + "portName": ingress_cp_model_info["properties"].get("interface_name") + }, + "egress": { + "encapsulation": egress_cp_model_info["properties"]["sfc_encapsulation"], + "ip": egress_cp_model_info["properties"]["ip_address"], + "mac": egress_cp_model_info["properties"]["mac_address"], + "portName": egress_cp_model_info["properties"].get("interface_name") + }, + "sfParam": sf_param + } + self.port_pair_groups.append( + { + "portpair": [port_pair_info] + } + ) + + def generate_port_pair_group(self, ingress_cp_inst_info, egress_cp_inst_info): + if (ingress_cp_inst_info.relatedport != ""): + ingress_port = ingress_cp_inst_info.relatedport + else: + ingress_port = CPInstModel.objects.filter(cpinstanceid=ingress_cp_inst_info.relatedcp, + ownertype="vnf", + ownerid=self.vnf_inst_database_info.nfinstid).get().relatedport + if (egress_cp_inst_info.relatedport != ""): + egress_port = egress_cp_inst_info.relatedport + else: + egress_port = CPInstModel.objects.filter(cpinstanceid=egress_cp_inst_info.relatedcp, + ownertype="vnf", + ownerid=self.vnf_inst_database_info.nfinstid).get().relatedport + ingress_port_inst_info = PortInstModel.objects.filter(portid=ingress_port).get() + egress_port_inst_info = PortInstModel.objects.filter(portid=egress_port).get() + + port_pair_info = { + "sfType": "vnf_" + self.vnf_model_in_ns_info["properties"]["vnf_type"], + "nshAware": self.vnf_model_in_ns_info["properties"]["nsh_aware"], + "requestReclassification": self.vnf_model_in_ns_info["properties"]["request_reclassification"], + "ingress": { + "encapsulation": ingress_port_inst_info.sfcencapsulation, + "ip": ingress_port_inst_info.ipaddress, + "mac": ingress_port_inst_info.macaddress + }, + "egress": { + "encapsulation": egress_port_inst_info.sfcencapsulation, + "ip": egress_port_inst_info.ipaddress, + "mac": egress_port_inst_info.macaddress + }, + "sfParam": self.vnf_model_in_ns_info["properties"] + } + return port_pair_info + + def get_cp_from_vnfd_model(self, cpdid): + for cp_model in self.vnfd_model_info["cps"]: + if cp_model["cp_id"] == cpdid: + return cp_model + + def get_pnf_model_info_by_cpid(self, pnfid): + for pnf_model_info in self.ns_model_data["pnfs"]: + if (pnf_model_info["pnf_id"] == pnfid): + return pnf_model_info + + def get_cp_model_info_by_cpid(self, cpid): + for cp_model_info in self.ns_model_data["cps"]: + if (cp_model_info["cp_id"] == cpid): + return cp_model_info + + def get_vnf_model_info_by_vnf_id(self, vnfid): + for vnf_model_info in self.ns_model_data["vnfs"]: + if (vnf_model_info["vnf_id"] == vnfid): + return vnf_model_info + + def get_cpdid_info_forwarder(self, vnf_model, forwarder): + logger.info("vnf_exposed %s" % type(vnf_model)) + # logger.info("vnf_exposed %s" % json.loads(vnf_model)) + # vnf_model = json.loads(vnf_model) + logger.info("vnf_exposed %s" % type(vnf_model["vnf_exposed"])) + logger.info("vnf_exposed %s" % vnf_model["vnf_exposed"]) + logger.info("foward_cps %s" % vnf_model["vnf_exposed"]["forward_cps"]) + for forwarder_info in vnf_model["vnf_exposed"]["forward_cps"]: + if (forwarder_info["key_name"] == forwarder): + return forwarder_info["cp_id"] diff --git a/lcm/ns/sfcs/create_sfc_worker.py b/lcm/ns/sfcs/create_sfc_worker.py new file mode 100644 index 00000000..380fd593 --- /dev/null +++ b/lcm/ns/sfcs/create_sfc_worker.py @@ -0,0 +1,68 @@ +# Copyright 2016 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 threading import Thread + +from lcm.ns.sfcs.create_flowcla import CreateFlowClassifier +from lcm.ns.sfcs.create_port_chain import CreatePortChain +from lcm.ns.sfcs.create_portpairgp import CreatePortPairGroup +from lcm.ns.sfcs.sfc_instance import SfcInstance +from lcm.ns.sfcs.utils import update_fp_status +from lcm.pub.exceptions import NSLCMException +from lcm.pub.utils.jobutil import JobUtil + +logger = logging.getLogger(__name__) + + +class CreateSfcWorker(Thread): + def __init__(self, data): + super(CreateSfcWorker, self).__init__() + self.ns_inst_id = data["nsinstid"] + self.ns_model_data = data["ns_model_data"] + self.fp_id = data["fpindex"] + self.sdnControllerId = data["sdncontrollerid"] + self.fp_inst_id = data["fpinstid"] + self.data = data + self.job_id = "" + + def init_data(self): + self.job_id = JobUtil.create_job("SFC", "sfc_init", self.ns_inst_id + "_" + self.fp_id) + return self.job_id + + def run(self): + try: + logger.info("Service Function Chain Worker start : ") + + CreateFlowClassifier(self.data).do_biz() + JobUtil.add_job_status(self.job_id, 50, "create flow classifer successfully!", "") + CreatePortPairGroup(self.data).do_biz() + JobUtil.add_job_status(self.job_id, 75, "create port pair group successfully!", "") + CreatePortChain(self.data).do_biz() + update_fp_status(self.fp_inst_id, "active") + JobUtil.add_job_status(self.job_id, 100, "create port chain successful!", "") + logger.info("Service Function Chain Worker end : ") + except NSLCMException as e: + self.handle_exception(e) + except Exception as e: + self.handle_exception(e) + + def handle_exception(self, e): + detail = "sfc instantiation failed, detail message: %s" % e.message + JobUtil.add_job_status(self.job_id, 255, "create sfc failed!", "") + logger.error(traceback.format_exc()) + logger.error(detail) + update_fp_status(self.fp_inst_id, "failed") diff --git a/lcm/ns/sfcs/delete_sfcs.py b/lcm/ns/sfcs/delete_sfcs.py new file mode 100644 index 00000000..23fed4c2 --- /dev/null +++ b/lcm/ns/sfcs/delete_sfcs.py @@ -0,0 +1,91 @@ +# Copyright 2016 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 json +import logging +import traceback + +from lcm.pub.database.models import FPInstModel, VNFFGInstModel +from lcm.pub.exceptions import NSLCMException +from lcm.pub.msapi import extsys, resmgr, sdncdriver + +logger = logging.getLogger(__name__) + + +class DeleteSfcs(object): + def __init__(self, sfc_inst_id): + self.sfc_inst_id = sfc_inst_id + self.ns_inst_id = "" + + def do(self): + try: + sfc_inst_info = FPInstModel.objects.filter(fpinstid=self.sfc_inst_id) + if not sfc_inst_info: + logger.warn("sfc inst id(%s) is not exist or has been already deleted" % self.sfc_inst_id) + return {"result": 0, "detail": "sfc is not exist or has been already deleted"} + self.ns_inst_id = sfc_inst_info[0].nsinstid + self.delete_sfc_from_driver(sfc_inst_info[0]) + self.delete_sfc_from_resmgr() + self.delete_sfc_from_db(sfc_inst_info) + return {"result": 0, "detail": "delete sfc success"} + except NSLCMException as e: + return self.exception_handle(e) + except Exception as e: + logger.error(traceback.format_exc()) + return self.exception_handle(e) + + def exception_handle(self, e): + detail = 'sfc delete failed, detail message: %s' % e.message + logger.error(detail) + return {"result": 1, "detail": detail} + + def delete_sfc_from_driver(self, sfc_inst_info): + sdn_controller_id = sfc_inst_info.sdncontrollerid + sdn_controller_url = extsys.get_sdn_controller_by_id(sdn_controller_id)["url"] + sfc_id = sfc_inst_info.sfcid + flow_classifiers = sfc_inst_info.flowclassifiers + port_pair_groups = sfc_inst_info.portpairgroups + if sfc_id: + req_param = {"sdnControllerId": sdn_controller_id, "url": sdn_controller_url, "id": sfc_id} + sdncdriver.delete_port_chain(req_param) + if flow_classifiers: + for flow_id in flow_classifiers.split(","): + req_param = {"sdnControllerId": sdn_controller_id, "url": sdn_controller_url, "id": flow_id} + sdncdriver.delete_flow_classifier(req_param) + if port_pair_groups: + for group in json.JSONDecoder().decode(port_pair_groups): + group_id = group["groupid"] + req_param = {"sdnControllerId": sdn_controller_id, "url": sdn_controller_url, "id": group_id} + sdncdriver.delete_port_pair_group(req_param) + port_pair = group["portpair"] + for port_pair_id in port_pair: + req_param = {"sdnControllerId": sdn_controller_id, "url": sdn_controller_url, "id": port_pair_id} + sdncdriver.delete_port_pair(req_param) + + def delete_sfc_from_db(self, sfc_inst_info): + # do_biz_with_share_lock("delete-sfclist-in-vnffg-%s" % self.ns_inst_id, self.delete_sfc_inst_id_in_vnffg) + self.delete_sfc_inst_id_in_vnffg() + sfc_inst_info.delete() + + def delete_sfc_from_resmgr(self): + resmgr.delete_sfc(self.sfc_inst_id) + + def delete_sfc_inst_id_in_vnffg(self): + for vnffg_info in VNFFGInstModel.objects.filter(nsinstid=self.ns_inst_id): + new_sfc_id_list = "" + for old_sfc_id in vnffg_info.fplist.split(","): + if old_sfc_id != self.sfc_inst_id: + new_sfc_id_list += old_sfc_id + "," + new_sfc_id_list = new_sfc_id_list[:-1] + VNFFGInstModel.objects.filter(vnffginstid=vnffg_info.vnffginstid).update(fplist=new_sfc_id_list) diff --git a/lcm/ns/sfcs/detail_views.py b/lcm/ns/sfcs/detail_views.py new file mode 100644 index 00000000..b5646f5f --- /dev/null +++ b/lcm/ns/sfcs/detail_views.py @@ -0,0 +1,41 @@ +# Copyright 2016 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 rest_framework import status +from rest_framework.response import Response +from rest_framework.views import APIView + +from lcm.ns.sfcs.delete_sfcs import DeleteSfcs +from lcm.ns.sfcs.get_sfcs import GetSfcs + + +class SfcDetailView(APIView): + def get(self, request, sfc_inst_id): + print "SfcCreateView--get::> %s" % sfc_inst_id + sfc_inst_info = GetSfcs(sfc_inst_id).do() + if not sfc_inst_info: + return Response(status=status.HTTP_404_NOT_FOUND) + return Response(status=status.HTTP_200_OK, data={'sfcInstId': sfc_inst_id, + 'sfcName': "xxx", + 'sfcStatus': sfc_inst_info[0].status}) + + def delete(self, request_paras, sfc_inst_id): + resp = DeleteSfcs(sfc_inst_id).do() + return Response(data=resp, status=status.HTTP_202_ACCEPTED) + + +class SfcCreateView(APIView): + def post(self, request): + print "SfcCreateView--post::> %s" % request.stream.body + return Response(data={"jobId": "1234", "sfcInstId": "1"}, status=status.HTTP_201_CREATED) diff --git a/lcm/ns/sfcs/get_sfcs.py b/lcm/ns/sfcs/get_sfcs.py new file mode 100644 index 00000000..653f8f68 --- /dev/null +++ b/lcm/ns/sfcs/get_sfcs.py @@ -0,0 +1,23 @@ +# Copyright 2016 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 lcm.pub.database.models import FPInstModel + + +class GetSfcs(object): + def __init__(self, sfc_inst_id): + self.sfc_inst_id = sfc_inst_id + + def do(self): + return FPInstModel.objects.filter(fpinstid=self.sfc_inst_id) diff --git a/lcm/ns/sfcs/sfc_instance.py b/lcm/ns/sfcs/sfc_instance.py new file mode 100644 index 00000000..cb68f8c5 --- /dev/null +++ b/lcm/ns/sfcs/sfc_instance.py @@ -0,0 +1,94 @@ +# Copyright 2016 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 + +from lcm.pub.database.models import VNFFGInstModel, FPInstModel +from lcm.pub.exceptions import NSLCMException +from lcm.pub.utils.share_lock import do_biz_with_share_lock + +logger = logging.getLogger(__name__) + + +class SfcInstance(object): + def __init__(self, data): + self.ns_inst_id = data["nsinstid"] + self.ns_model_data = data["ns_model_data"] + self.fp_id = data["fpindex"] + self.fp_inst_id = data["fpinstid"] + self.sdnControllerId = data["sdncontrollerid"] + + def do_biz(self): + self.init_data() + return self.save() + + def init_data(self): + self.fp_model = self.get_fp_model_by_fp_id() + logger.info("fp_model.properties:%s, fp_id:%s" % (self.fp_model["properties"], self.fp_id)) + if not self.fp_model: + return + logger.info("sfc_inst_symmetric %s" % self.fp_model["properties"].get("symmetric")) + self.symmetric = self.fp_model["properties"].get("symmetric") + logger.info("sfc_inst_symmetric %s" % self.symmetric) + self.policyinfo = self.fp_model["properties"].get("policy") + self.status = "processing" + vnffg_database_info = VNFFGInstModel.objects.filter(vnffgdid=self.get_vnffgdid_by_fp_id(), + nsinstid=self.ns_inst_id).get() + self.vnffg_inst_id = vnffg_database_info.vnffginstid + + def get_fp_model_by_fp_id(self): + fps_model = self.ns_model_data["fps"] + for fp_model in fps_model: + if fp_model["fp_id"] == self.fp_id: + return fp_model + return None + + def get_vnffgdid_by_fp_id(self): + vnffgs_model = self.ns_model_data["vnffgs"] + for vnffg_model in vnffgs_model: + fp_ids = vnffg_model["members"] + for fp_id in fp_ids: + if fp_id == self.fp_id: + return vnffg_model["vnffg_id"] + + def save(self): + try: + logger.info("Sfc Instanciate save2db start : ") + FPInstModel(fpid=self.fp_id, + fpinstid=self.fp_inst_id, + nsinstid=self.ns_inst_id, + vnffginstid=self.vnffg_inst_id, + symmetric=1 if self.symmetric else 0, + policyinfo=self.policyinfo, + status=self.status, + sdncontrollerid=self.sdnControllerId + ).save() + + do_biz_with_share_lock("update-sfclist-in-vnffg-%s" % self.ns_inst_id, self.update_vnfffg_info) + logger.info("Sfc Instanciate save2db end : ") + + except: + logger.error('SFC instantiation failed') + raise NSLCMException('SFC instantiation failed.') + return { + "fpinstid": self.fp_inst_id + } + + + def update_vnfffg_info(self): + vnffg_database_info = VNFFGInstModel.objects.filter(vnffginstid=self.vnffg_inst_id).get() + fp_inst_list = vnffg_database_info.fplist + fp_inst_list = fp_inst_list + ',' + self.fp_inst_id if fp_inst_list else self.fp_inst_id + VNFFGInstModel.objects.filter(vnffginstid=self.vnffg_inst_id).update(fplist=fp_inst_list) diff --git a/lcm/ns/sfcs/urls.py b/lcm/ns/sfcs/urls.py new file mode 100644 index 00000000..a47f840a --- /dev/null +++ b/lcm/ns/sfcs/urls.py @@ -0,0 +1,29 @@ +# Copyright 2016 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 patterns, url +from rest_framework.urlpatterns import format_suffix_patterns + +from lcm.ns.sfcs.detail_views import SfcDetailView +from lcm.ns.sfcs.views import SfcView, SfcInstanceView, PortPairGpView, FlowClaView, PortChainView + +urlpatterns = patterns('', + url(r'^openoapi/nslcm/v1/ns/sfcs$', SfcView.as_view()), + url(r'^openoapi/nslcm/v1/ns/sfcs/(?P<sfc_inst_id>[0-9a-zA-Z_-]+)$', SfcDetailView.as_view()), + url(r'^openoapi/nslcm/v1/ns/sfc_instance$', SfcInstanceView.as_view()), + url(r'^openoapi/nslcm/v1/ns/create_port_pair_group$', PortPairGpView.as_view()), + url(r'^openoapi/nslcm/v1/ns/create_flow_classifier$', FlowClaView.as_view()), + url(r'^openoapi/nslcm/v1/ns/create_port_chain$', PortChainView.as_view()), + ) + +urlpatterns = format_suffix_patterns(urlpatterns) diff --git a/lcm/ns/sfcs/utils.py b/lcm/ns/sfcs/utils.py new file mode 100644 index 00000000..b6d68cd2 --- /dev/null +++ b/lcm/ns/sfcs/utils.py @@ -0,0 +1,54 @@ +# Copyright 2016 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 + +from lcm.pub.database.models import FPInstModel + +logger = logging.getLogger(__name__) + + +def sfc_inst_failed_handle(fp_inst_id, error_msg): + logger.error('create sfc failed, detail message: %s' % error_msg) + FPInstModel.objects.filter(fpid=fp_inst_id).update(status="disabled").get() + + +def ignorcase_get(args, key): + if not key: + return "" + if key in args: + return args[key] + for oldkey in args: + if oldkey.upper() == key.upper(): + return args[oldkey] + return "" + +def ignor_dot(str): + index = str.find('.') + if index == -1: + return str; + return str[0:index] + +def get_fp_id(fpindex,ns_model): + index = int(int(float(fpindex))-1) + return ns_model['fps'][index].get("fp_id") + +def update_fp_status(fp_inst_id,status_info): + FPInstModel.objects.filter(fpinstid=fp_inst_id).update(status=status_info) + +def get_fp_model_by_fp_inst_id(ns_model_data, fp_inst_id): + fp_databas_info = FPInstModel.objects.filter(fpinstid=fp_inst_id).get() + fps_model = ns_model_data["fps"] + for fp_model in fps_model: + if fp_model["fp_id"] == fp_databas_info.fpid: + return fp_model diff --git a/lcm/ns/sfcs/views.py b/lcm/ns/sfcs/views.py new file mode 100644 index 00000000..1e3f98ff --- /dev/null +++ b/lcm/ns/sfcs/views.py @@ -0,0 +1,108 @@ +# Copyright 2016 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 json +import logging +import traceback +import uuid + +import time +from rest_framework import status +from rest_framework.response import Response +from rest_framework.views import APIView + +from lcm.ns.sfcs.create_flowcla import CreateFlowClassifier +from lcm.ns.sfcs.create_port_chain import CreatePortChain +from lcm.ns.sfcs.create_portpairgp import CreatePortPairGroup +from lcm.ns.sfcs.create_sfc_worker import CreateSfcWorker +from lcm.ns.sfcs.sfc_instance import SfcInstance +from lcm.ns.sfcs.utils import get_fp_id, ignorcase_get + +logger = logging.getLogger(__name__) + + +class SfcInstanceView(APIView): + def post(self, request): + data = { + 'nsinstid': request.data['nsinstanceid'], + "ns_model_data": json.loads(request.data['context']), + 'fpindex': request.data['fpindex'], + 'fpinstid': str(uuid.uuid4()), + 'sdncontrollerid': request.data["sdncontrollerid"]} + rsp = SfcInstance(data).do_biz() + return Response(data=rsp, status=status.HTTP_200_OK) + + +class PortPairGpView(APIView): + def post(self, request): + data = { + 'fpinstid': request.data["fpinstid"], + "ns_model_data": json.loads(request.data['context']), + 'nsinstid': request.data["nsinstanceid"]} + CreatePortPairGroup(data).do_biz() + return Response(status=status.HTTP_200_OK) + + +class FlowClaView(APIView): + def post(self, request): + data = { + 'fpinstid': request.data["fpinstid"], + "ns_model_data": json.loads(request.data['context'])} + CreateFlowClassifier(data).do_biz() + return Response(status=status.HTTP_200_OK) + + +class PortChainView(APIView): + def post(self, request): + data = { + 'fpinstid': request.data["fpinstid"], + "ns_model_data": json.loads(request.data['context'])} + CreatePortChain(data).do_biz() + return Response(status=status.HTTP_200_OK) + + +class SfcView(APIView): + def post(self, request): + try: + logger.info("Create Service Function Chain start") + logger.info("service_function_chain_request: %s" % json.dumps(request.data)) + logger.info("service_function_chain_context : %s" % json.dumps(request.data['context'])) + logger.info("service_function_chain_context : %s" % request.data['context']) + logger.info("service_function_chain_instanceid : %s" % ignorcase_get(request.data, 'nsinstanceid')) + logger.info("service_function_chain_sdncontrollerid : %s" % ignorcase_get(request.data, 'sdncontrollerid')) + logger.info("service_function_chain_fpindex : %s" % ignorcase_get(request.data, 'fpindex')) + ns_model_data = request.data['context'] + except Exception as e: + logger.error(traceback.format_exc()) + data = { + 'nsinstid': ignorcase_get(request.data, 'nsinstanceid'), + "ns_model_data": ns_model_data, + 'fpindex': get_fp_id(ignorcase_get(request.data, 'fpindex'), ns_model_data), + 'fpinstid': str(uuid.uuid4()), + 'sdncontrollerid': ignorcase_get(request.data, 'sdncontrollerid') + } + logger.info("Save FPInstModel start: ") + SfcInstance(data).do_biz() + logger.info("Save FPInstModel end: ") + worker = CreateSfcWorker(data) + job_id = worker.init_data() + worker.start() + logger.info("Service Function Chain Thread Sleep start : %s" %time.ctime()) + time.sleep(2) + logger.info("Service Function Chain Thread Sleep end: %s" % time.ctime()) + logger.info("Create Service Function Chain end") + return Response(data={"jobId": job_id, + "sfcInstId": data["fpinstid"]}, + status=status.HTTP_200_OK) diff --git a/lcm/ns/swagger.json b/lcm/ns/swagger.json new file mode 100644 index 00000000..2664027e --- /dev/null +++ b/lcm/ns/swagger.json @@ -0,0 +1,1046 @@ +{ + "swagger": "2.0", + "info": { + "version": "1.0.0", + "title": "ZTE vManager Service rest API" + }, + "basePath": "/openoapi/nslcm/v1", + "tags": [ + { + "name": "lcm Resource" + } + ], + "paths": { + "/ns/vls": { + "post": { + "tags": [ + "vls Resource" + ], + "summary": "vl create", + "description": "", + "operationId": "create_vl", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "instantiate request param", + "required": true, + "schema": { + "$ref": "#/definitions/VlPostRequest" + } + } + ], + "responses": { + "201": { + "description": "", + "schema": { + "$ref": "#/definitions/VlPostResponse" + } + } + } + } + }, + "/ns/vls/{vlId}": { + "get": { + "tags": [ + "vls Resource" + ], + "summary": "query the specified vl info", + "description": "", + "operationId": "query_vl", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "vlId", + "in": "path", + "description": "vl instance id", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/VlInfo" + } + }, + "404": { + "description": "the vl instance id is wrong" + }, + "500": { + "description": "the url is invalid" + } + } + }, + "delete": { + "tags": [ + "vls Resource" + ], + "summary": "delete vl", + "description": "", + "operationId": "delete_vl", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "required": true, + "type": "string", + "description": "", + "name": "vlId", + "in": "path" + } + ], + "responses": { + "204": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/DeleteVlResponse" + } + }, + "404": { + "description": "the vl instance id is wrong" + }, + "500": { + "description": "the url is invalid" + } + } + } + }, + "/ns/vnfs": { + "post": { + "tags": [ + "vnfs Resource" + ], + "summary": "vnf create", + "description": "", + "operationId": "create_vnf", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "instantiate request param", + "required": true, + "schema": { + "$ref": "#/definitions/VnfPostRequest" + } + } + ], + "responses": { + "201": { + "description": "", + "schema": { + "$ref": "#/definitions/VnfPostResponse" + } + } + } + } + }, + "/ns/vnfs/{vnfInstId}": { + "get": { + "tags": [ + "vnfs Resource" + ], + "summary": "query the specified vnf info", + "description": "", + "operationId": "query_vnf", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "vnfInstId", + "in": "path", + "description": "vnf instance id", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/VnfInfo" + } + }, + "404": { + "description": "the vnf instance id is wrong" + }, + "500": { + "description": "the url is invalid" + } + } + }, + "delete": { + "tags": [ + "vnfs Resource" + ], + "summary": "delete vnf", + "description": "", + "operationId": "delete_vnf", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "required": true, + "type": "string", + "description": "", + "name": "vnfInstId", + "in": "path" + } + ], + "responses": { + "204": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/DeleteResponse" + } + }, + "404": { + "description": "the vl instance id is wrong" + }, + "500": { + "description": "the url is invalid" + } + } + } + }, + "/ns/sfcs": { + "post": { + "tags": [ + "sfcs Resource" + ], + "summary": "sfc create", + "description": "", + "operationId": "create_sfc", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "in": "body", + "name": "body", + "description": "request param", + "required": true, + "schema": { + "$ref": "#/definitions/SfcPostRequest" + } + } + ], + "responses": { + "201": { + "description": "", + "schema": { + "$ref": "#/definitions/SfcPostResponse" + } + } + } + } + }, + "/ns/sfcs/{sfcInstId}": { + "get": { + "tags": [ + "sfcs Resource" + ], + "summary": "query the specified sfc info", + "description": "", + "operationId": "query_sfc", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "name": "sfcInstId", + "in": "path", + "description": "sfc instance id", + "required": true, + "type": "string" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/SfcInfo" + } + }, + "404": { + "description": "the sfc instance id is wrong" + }, + "500": { + "description": "the url is invalid" + } + } + }, + "delete": { + "tags": [ + "vnfs Resource" + ], + "summary": "delete sfc", + "description": "", + "operationId": "delete_sfc", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "required": true, + "type": "string", + "description": "", + "name": "sfcInstId", + "in": "path" + } + ], + "responses": { + "204": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/DeleteResponse" + } + }, + "404": { + "description": "the sfc instance id is wrong" + }, + "500": { + "description": "the url is invalid" + } + } + } + }, + "/ns/{nsInstanceId}/postdeal": { + "post": { + "tags": [ + "ns postdeal" + ], + "summary": "ns postdeal", + "description": "", + "operationId": "ns_postdeal", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "required": true, + "type": "string", + "description": "", + "name": "nsInstanceId", + "in": "path" + }, + { + "in": "body", + "name": "body", + "description": "request param", + "required": true, + "schema": { + "$ref": "#/definitions/NSInstPostDetailRequest" + } + } + ], + "responses": { + "202": { + "description": "" + } + } + } + }, + "/ns/{nsInstanceId}/scale": { + "post": { + "tags": [ + "ns scale" + ], + "summary": "ns scale", + "description": "ns scale", + "operationId": "ns_scale", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "required": true, + "type": "string", + "description": "", + "name": "nsInstanceId", + "in": "path" + }, + { + "required": true, + "type": "Enum", + "description": "", + "name": "scaleType", + "in": "body" + }, + { + "in": "body", + "name": "scaleNsData", + "description": "scaleNsData", + "required": true, + "schema": { + "$ref": "#/definitions/ScaleNsData" + } + } + ], + "responses": { + "200": { + "description": "", + "schema": { + "$ref": "#/definitions/NsScaleResponse" + } + }, + "201": { + "description": "Invalid Request" + } + } + } + }, + "/ns/{ns_instance_id}": { + "delete": { + "tags": [ + "ns lcm" + ], + "summary": "ns delete", + "description": "ns delete", + "operationId": "ns_delete", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "required": true, + "type": "string", + "description": "Identifier of the NS instance.", + "name": "ns_instance_id", + "in": "path" + } + ], + "responses": { + "204": { + "description": "The NS instance resource and the associated NS identifier were deleted successfully." + } + } + } + }, + "/ns/{ns_instance_id}/terminate": { + "post": { + "tags": [ + "ns lcm" + ], + "summary": "ns terminate", + "description": "ns terminate", + "operationId": "ns_terminate", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "required": true, + "type": "string", + "description": "Identifier of the NS instance.", + "name": "ns_instance_id", + "in": "path" + }, + { + "in": "body", + "name": "NsTerminateRequest", + "description": "NsTerminateRequest", + "required": true, + "schema": { + "$ref": "#/definitions/NsTerminateRequest" + } + } + ], + "responses": { + "202": { + "description": "", + "schema": { + "$ref": "#/definitions/NsTerminateResponse" + } + }, + "500": { + "description": "the url is invalid" + } + } + } + }, + "/mandb/{modelName}": { + "get": { + "tags": [ + "ns lcm" + ], + "summary": "query ns table info", + "description": "query ns table info", + "operationId": "query_ns_table", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "required": true, + "type": "string", + "description": "model Name.", + "name": "modelName", + "in": "path" + } + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/TableInfo" + } + }, + "500": { + "description": "the url is invalid" + } + } + }, + "delete": { + "tags": [ + "ns lcm" + ], + "summary": "ns table delete", + "description": "ns table delete", + "operationId": "ns_table_delete", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "required": true, + "type": "string", + "description": "model Name.", + "name": "modelName", + "in": "path" + } + ], + "responses": { + "204": { + "description": "The tables were deleted successfully." + } + } + } + }, + "/ns": { + "get": { + "tags": [ + "ns lcm" + ], + "summary": "ns get", + "description": "ns get", + "operationId": "ns_get", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + + ], + "responses": { + "200": { + "description": "successful operation", + "schema": { + "$ref": "#/definitions/NsInfo" + } + } + } + } + }, + "/jobs/{jobId}":{ + "post": { + "tags": [ + "jobstatus" + ], + "summary": "jobstatus", + "description": "", + "operationId": "jobstatus", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "parameters": [ + { + "required": true, + "type": "string", + "description": "", + "name": "jobId", + "in": "path" + }, + { + "in": "body", + "name": "body", + "description": "request param", + "required": true, + "schema": { + "$ref": "#/definitions/JobProgressRequest" + } + } + ], + "responses": { + "202": { + "description": "" + } + } + } + } + }, + "definitions": { + "VlPostRequest": { + "type": "object", + "properties": { + "jobId": { + "type": "string" + }, + "nsInstanceId": { + "type": "string" + }, + "flavourId": { + "type": "string" + }, + "sapData": { + "type": "array", + "items": { + "type": "object" + } + }, + "pnfInfo": { + "type": "array", + "items": { + "type": "object" + } + }, + "vnfInstanceData": { + "type": "array", + "items": { + "type": "object" + } + }, + "nestedNsInstanceId": { + "type": "array", + "items": { + "type": "object" + } + }, + "locationConstraints": { + "type": "array", + "items": { + "type": "object" + } + }, + "additionalParamForNs": { + "type": "array", + "items": { + "type": "object" + } + }, + "additionalParamForVnf": { + "type": "array", + "items": { + "type": "object" + } + }, + "extNSVirtualLink": { + "type": "array", + "items": { + "type": "object" + } + }, + "context": { + "type": "string" + }, + "vlIndex": { + "type": "string" + } + } + }, + "VlPostResponse": { + "type": "object", + "properties": { + "result": { + "type": "integer", + "enum": [ + 0, + 1 + ] + }, + "detail": { + "type": "string" + }, + "vlId": { + "type": "string" + } + } + }, + "VnfPostRequest": { + "type": "object", + "properties": { + "jobId": { + "type": "string" + }, + "nsInstanceId": { + "type": "string" + }, + "flavourId": { + "type": "string" + }, + "sapData": { + "type": "array", + "items": { + "type": "object" + } + }, + "pnfInfo": { + "type": "array", + "items": { + "type": "object" + } + }, + "vnfInstanceData": { + "type": "array", + "items": { + "type": "object" + } + }, + "nestedNsInstanceId": { + "type": "array", + "items": { + "type": "object" + } + }, + "locationConstraints": { + "type": "array", + "items": { + "type": "object" + } + }, + "additionalParamForNs": { + "type": "array", + "items": { + "type": "object" + } + }, + "additionalParamForVnf": { + "type": "array", + "items": { + "type": "object" + } + }, + "extNSVirtualLink": { + "type": "array", + "items": { + "type": "object" + } + }, + "context": { + "type": "string" + }, + "vnfIndex": { + "type": "string" + } + } + }, + "VnfPostResponse": { + "type": "object", + "properties": { + "vnfInstId": { + "type": "string" + }, + "jobId": { + "type": "string" + } + } + }, + "SfcPostRequest": { + "type": "object", + "properties": { + "jobId": { + "type": "string" + }, + "nsInstanceId": { + "type": "string" + }, + "sapData": { + "type": "array", + "items": { + "type": "object" + } + }, + "vnfInstanceData": { + "type": "array", + "items": { + "type": "object" + } + }, + "additionalParamForNs": { + "type": "array", + "items": { + "type": "object" + } + }, + "additionalParamForVnf": { + "type": "array", + "items": { + "type": "object" + } + }, + "sdnControllerId": { + "type": "string" + }, + "context": { + "type": "string" + }, + "fpindex": { + "type": "string" + } + } + }, + "NSInstPostDetailRequest": { + "type": "object", + "properties": { + "status": { + "type": "string" + } + } + }, + "JobProgressRequest": { + "type": "object", + "properties": { + "progress": { + "type": "string" + }, + "desc": { + "type": "string" + }, + "errcode": { + "type": "string" + } + } + }, + "SfcPostResponse": { + "type": "object", + "properties": { + "sfcInstId": { + "type": "string" + }, + "jobId": { + "type": "string" + } + } + }, + "VlInfo": { + "type": "object", + "properties": { + "vlId": { + "type": "string" + }, + "vlName": { + "type": "string" + }, + "vlStatus": { + "type": "string" + } + } + }, + "VnfInfo": { + "type": "object", + "properties": { + "vnfInstId": { + "type": "string" + }, + "vnfName": { + "type": "string" + }, + "vnfStatus": { + "type": "string" + } + } + }, + "SfcInfo": { + "type": "object", + "properties": { + "sfcInstId": { + "type": "string" + }, + "sfcName": { + "type": "string" + }, + "sfcStatus": { + "type": "string" + } + } + }, + "DeleteVlResponse": { + "type": "object", + "properties": { + "result": { + "type": "integer", + "enum": [ + 0, + 1 + ] + }, + "detail": { + "type": "string" + } + } + }, + "DeleteResponse": { + "type": "object", + "properties": { + "result": { + "type": "integer", + "enum": [ + 0, + 1 + ] + }, + "detail": { + "type": "string" + } + } + }, + "ScaleNsData": { + "type": "object", + "properties": { + "scaleNsByStepsData": { + "$ref": "#/definitions/ScaleNsByStepsData" + } + } + }, + "ScaleNsByStepsData": { + "type": "object", + "properties": { + "scalingDirection": { + "type": "Enum" + }, + "aspectId": { + "type": "string" + }, + "numberOfSteps": { + "type": "integer" + } + } + }, + "NsScaleResponse": { + "type": "object", + "properties": { + "jobID": { + "type": "string" + } + } + }, + "NsTerminateRequest": { + "type": "object", + "properties": { + "terminationType": { + "type": "string" + }, + "gracefulTerminationTimeout": { + "type": "string" + } + } + }, + "NsTerminateResponse": { + "type": "object", + "properties": { + "jobID": { + "type": "string" + } + } + }, + "TableInfo": { + "type": "object", + "properties": { + "count": { + "type": "string" + } + } + }, + "NsInfo": { + "type": "object", + "properties": { + "vnfInfoId": { + "type": "string" + }, + "vlInfo": { + "type": "string" + }, + "nsState": { + "type": "string" + }, + "description": { + "type": "string" + }, + "nsName": { + "type": "string" + }, + "nsInstanceId": { + "type": "string" + }, + "nsdId": { + "type": "string" + }, + "vnffgInfo": { + "type": "string" + } + } + } + } +}
\ No newline at end of file diff --git a/lcm/ns/tests/__init__.py b/lcm/ns/tests/__init__.py new file mode 100644 index 00000000..5580cc3d --- /dev/null +++ b/lcm/ns/tests/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2016 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/ns/tests/sfcs/__init__.py b/lcm/ns/tests/sfcs/__init__.py new file mode 100644 index 00000000..5580cc3d --- /dev/null +++ b/lcm/ns/tests/sfcs/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2016 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/ns/tests/sfcs/test_create_flow_classifier.py b/lcm/ns/tests/sfcs/test_create_flow_classifier.py new file mode 100644 index 00000000..eb149dca --- /dev/null +++ b/lcm/ns/tests/sfcs/test_create_flow_classifier.py @@ -0,0 +1,56 @@ +# Copyright 2016 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 mock +# import json +# from test_data import nsd_model +# from rest_framework import status +# from lcm.pub.utils import restcall +# from lcm.pub.database.models import FPInstModel +# from django.test import Client +# from django.test import TestCase +# +# +# class TestSfc(TestCase): +# def setUp(self): +# self.client = Client() +# FPInstModel.objects.all().delete() +# FPInstModel(fpinstid="fp_inst_1", fpid="fpd_1", sdncontrollerid="test").save() +# +# def tearDown(self): +# FPInstModel.objects.all().delete() +# +# @mock.patch.object(restcall, 'call_req') +# def test_create_flow_classifier_success(self, mock_call_req): +# data = { +# "fpinstid": "fp_inst_1", +# "context": json.dumps(nsd_model) +# } +# mock_vals = { +# "/openoapi/extsys/v1/sdncontrollers/test": +# [0, json.JSONEncoder().encode({"url": "url_1"}), '200'], +# "/openoapi/sdncdriver/v1.0/createflowclassfier": +# [0, json.JSONEncoder().encode({"id": "test_id_1"}), '200'], +# "/openoapi/microservices/v1/services": +# [0, None, '200'] +# +# } +# +# def side_effect(*args): +# return mock_vals[args[4]] +# +# mock_call_req.side_effect = side_effect +# resp = self.client.post("/openoapi/nslcm/v1/ns/create_flow_classifier", data) +# ret = FPInstModel.objects.get(fpinstid="fp_inst_1") +# self.assertEqual(resp.status_code, status.HTTP_200_OK) +# self.assertEqual("test_id_1", ret.flowclassifiers) diff --git a/lcm/ns/tests/sfcs/test_create_port_chain.py b/lcm/ns/tests/sfcs/test_create_port_chain.py new file mode 100644 index 00000000..ab68681b --- /dev/null +++ b/lcm/ns/tests/sfcs/test_create_port_chain.py @@ -0,0 +1,57 @@ +# Copyright 2016 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 mock +# import json +# from test_data import nsd_model +# from rest_framework import status +# from lcm.pub.utils import restcall +# from lcm.pub.database.models import FPInstModel +# from django.test import Client +# from django.test import TestCase +# +# +# class TestSfc(TestCase): +# def setUp(self): +# self.client = Client() +# FPInstModel.objects.all().delete() +# FPInstModel(fpinstid="fp_inst_1", sdncontrollerid="test_sdncontrollerid", +# symmetric=1, flowclassifiers="test_flowclassifiers", +# portpairgroups=json.JSONEncoder().encode([{"groupid": "1"}])).save() +# +# def tearDown(self): +# FPInstModel.objects.all().delete() +# +# @mock.patch.object(restcall, 'call_req') +# def test_create_port_chain_success(self, mock_call_req): +# data = { +# "fpinstid": "fp_inst_1", +# "context": json.dumps(nsd_model) +# } +# mock_vals = { +# "/openoapi/extsys/v1/sdncontrollers/test_sdncontrollerid": +# [0, json.JSONEncoder().encode({"url": "url_1"}), '200'], +# "/openoapi/sdncdriver/v1.0/createportchain": +# [0, json.JSONEncoder().encode({"id": "test_id_1"}), '200'], +# "/openoapi/microservices/v1/services": +# [0, None, '200'] +# } +# +# def side_effect(*args): +# return mock_vals[args[4]] +# +# mock_call_req.side_effect = side_effect +# resp = self.client.post("/openoapi/nslcm/v1/ns/create_port_chain", data) +# ret = FPInstModel.objects.get(fpinstid="fp_inst_1") +# self.assertEqual(resp.status_code, status.HTTP_200_OK) +# self.assertEqual("test_id_1", ret.sfcid) diff --git a/lcm/ns/tests/sfcs/test_create_port_pair_group.py b/lcm/ns/tests/sfcs/test_create_port_pair_group.py new file mode 100644 index 00000000..a63b6307 --- /dev/null +++ b/lcm/ns/tests/sfcs/test_create_port_pair_group.py @@ -0,0 +1,81 @@ +# Copyright 2016 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 mock +# import json +# from test_data import nsd_model, vnfd_model_dict1, vnfd_model_dict2 +# from rest_framework import status +# from lcm.pub.utils import restcall +# from lcm.pub.database.models import FPInstModel, NfInstModel +# from django.test import Client +# from django.test import TestCase +# +# +# class TestSfc(TestCase): +# def setUp(self): +# self.client = Client() +# FPInstModel.objects.all().delete() +# NfInstModel.objects.all().delete() +# NfInstModel( +# nfinstid="vnf_inst_1", ns_inst_id="ns_inst_1", vnf_id="vnf_1", +# vnfd_model=json.dumps(vnfd_model_dict1)).save() +# NfInstModel( +# nfinstid="vnf_inst_2", vnf_id="vnf_2", ns_inst_id="ns_inst_1", +# vnfd_model=json.dumps(vnfd_model_dict2)).save() +# FPInstModel( +# fpid="fpd_1", fpinstid="fp_inst_1", nsinstid="ns_inst_1", vnffginstid="vnffg_inst_1", +# policyinfo=[{ +# "type": "ACL", +# "criteria": { +# "dest_port_range": [80, 1024], +# "source_port_range": [80, 1024], +# "ip_protocol": "tcp", +# "dest_ip_range": ["192.168.1.2", "192.168.1.100"], +# "source_ip_range": ["192.168.1.2", "192.168.1.100"], +# "dscp": 100, +# } +# }], +# status="enabled", +# sdncontrollerid="sdn_controller_1" +# ).save() +# +# def tearDown(self): +# FPInstModel.objects.all().delete() +# NfInstModel.objects.all().delete() +# +# @mock.patch.object(restcall, 'call_req') +# def test_create_port_pair_group_success(self, mock_call_req): +# data = { +# "nsinstanceid": "ns_inst_1", +# "fpinstid": "fp_inst_1", +# "context": json.dumps(nsd_model) +# } +# mock_vals = { +# "/openoapi/extsys/v1/sdncontrollers/sdn_controller_1": +# [0, json.JSONEncoder().encode({"url": "url_1"}), '200'], +# "/openoapi/sdncdriver/v1.0/createportpair": +# [0, json.JSONEncoder().encode({"id": "createportpair_id"}), '200'], +# "/openoapi/sdncdriver/v1.0/createportpairgroup": +# [0, json.JSONEncoder().encode({"id": "createportpairgroup_id"}), '200'], +# "/openoapi/microservices/v1/services": +# [0, None, '200'] +# } +# +# def side_effect(*args): +# return mock_vals[args[4]] +# +# mock_call_req.side_effect = side_effect +# resp = self.client.post("/openoapi/nslcm/v1/ns/create_port_pair_group", data) +# rest = json.loads(FPInstModel.objects.get(fpinstid="fp_inst_1").portpairgroups)[0] +# self.assertEqual(resp.status_code, status.HTTP_200_OK) +# self.assertEqual("createportpairgroup_id", rest["groupid"]) diff --git a/lcm/ns/tests/sfcs/test_data.py b/lcm/ns/tests/sfcs/test_data.py new file mode 100644 index 00000000..e8734648 --- /dev/null +++ b/lcm/ns/tests/sfcs/test_data.py @@ -0,0 +1,1411 @@ +# Copyright 2016 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. + +nsd_model = { + "metadata": { + "id": "nsd_demo", + "vendor": "zte", + "version": "1.1.0", + "name": "testNSD", + "description": "demo nsd", + }, + + "inputs": { + "param1": "11", + "param2": "22", + }, + + "vnfs": [ + { + "type": "tosca.nodes.nfv.ext.VNF.FireWall", + "vnf_id": "vnf_1", + "description": "", + "properties": { + "id": "vnfd_1", + "vendor": "zte", + "version": "1.2.0", + "vnfd_version": "1.1.0", + "vnf_type": "vnf1", + "domain_type": "CN", + "name": "vnf1", + "is_shared": False, + "cross_dc": False, + "request_reclassification": False, + "nsh_aware": False, + "custom_properties": { + "key1": "value1", + "keyN": "valueN", + }, + }, + "dependencies": [ + "vnf_id1", "vnf_id2" + ], + "networks": [ + { + "key_name": "virtualLink1", + "vl_id": "vl_id1", + }, + ], + }, + { + "type": "tosca.nodes.nfv.ext.VNF.FireWall", + "vnf_id": "vnf_2", + "description": "", + "properties": { + "id": "vnfd_2", + "vendor": "zte", + "version": "1.2.0", + "vnfd_version": "1.1.0", + "vnf_type": "vnf2", + "domain_type": "CN", + "name": "vnf1", + "is_shared": False, + "cross_dc": False, + "request_reclassification": False, + "nsh_aware": False, + "custom_properties": { + "key1": "value1", + "keyN": "valueN", + }, + }, + "dependencies": [ + "vnf_id1", "vnf_id2" + ], + "networks": [ + { + "key_name": "virtualLink1", + "vl_id": "vl_id1", + }, + ], + } + ], + + "pnfs": [ + { + "pnf_id": "pnf1", + "description": "", + "properties": { + "id": "pnf1", + "vendor": "zte", + "version": "1.1.0", + "pnf_type": "TTGW", + "request_reclassification": False, + "nsh_aware": False, + }, + "cps": [ + "cpd_1", "cpd_22", + ] + } + ], + + "nested_ns": [ + { + "ns_id": "ns2", + "description": "", + "properties": { + "id": "ns2_demo", + "vendor": "zte", + "version": "1.1.0", + "name": "NSD2", + }, + "networks": [ + { + "key_name": "virtualLink1", + "vl_id": "vl_id1", + }, + ], + } + ], + + "vls": [ + { + "vl_id": "vldId1", + "description": "", + "properties": { + "name": "umac_241_control", + "network_id": "fgdhsj434hfjdfd", + "network_name": "umac_control", + "vendor": "zte", + "mtu": 1500, + "network_type": "vlan", + "physical_network": "phynet01", + "segmentation_id": "30", + "vlan_transparent": False, + "vds_name": "vds1", + "cidr": "192.168.199.0/24", + "ip_version": 4, + "gateway_ip": "192.168.199.1", + "dhcp_enabled": False, + "dns_nameservers": ["192.168.0.4", "192.168.0.10"], + "start_ip": "192.168.199.2", + "end_ip": "192.168.199.254", + "host_routes": [ + { + "destination": "10.43.26.0/24", + "nexthop": "10.41.23.1", + }, + ], + "location_info": { + "vimId": "vimid", + "tenant": "tenantname", + }, + "vlan_transparent": False, + }, + }, + ], + + "cps": [ + { + "cp_id": "cpd_1", + "description": "", + "properties": { + "mac_address": "00:d9:00:82:11:e1", + "ip_address": "192.168.1.21", + "ip_range_start": "192.168.1.20", + "ip_range_end": "192.168.1.29", + "floating_ip_address": { + "external_network": "extnet01", + "ip_address": "10.43.53.23", + }, + "service_ip_address": "192.168.1.23", + "order": 1, + "bandwidth": 1000, + "vnic_type": "normal", + "allowed_address_pairs": [ + { + "ip": "192.168.1.13", + "mac": "00:f3:43:20:a2:a3" + }, + ], + "bond": "none", + "macbond": "00:d9:00:82:11:d1", + "sfc_encapsulation": "", + "direction": "", + }, + "vl_id": "vlid1", + "pnf_id": "pnf1", + }, + + { + "cp_id": "forwarder_brasDP_dcPort", + "description": "", + "properties": { + "mac_address": "00:d9:00:82:14:e1", + "ip_address": "192.168.1.24", + "ip_range_start": "192.168.1.20", + "ip_range_end": "192.168.1.29", + "floating_ip_address": { + "external_network": "extnet01", + "ip_address": "10.43.53.23", + }, + "service_ip_address": "192.168.1.23", + "order": 1, + "bandwidth": 1000, + "vnic_type": "normal", + "allowed_address_pairs": [ + { + "ip": "192.168.1.13", + "mac": "00:f3:43:20:a2:a3" + }, + ], + "bond": "none", + "macbond": "00:d9:00:82:11:d1", + "sfc_encapsulation": "", + "direction": "", + }, + "vl_id": "vlid1", + "pnf_id": "pnf1", + }, + { + "cp_id": "forwarder_brasDP_internetPort", + "description": "", + "properties": { + "mac_address": "00:d9:00:82:15:e1", + "ip_address": "192.168.1.25", + "ip_range_start": "192.168.1.20", + "ip_range_end": "192.168.1.29", + "floating_ip_address": { + "external_network": "extnet01", + "ip_address": "10.43.53.23", + }, + "service_ip_address": "192.168.1.23", + "order": 1, + "bandwidth": 1000, + "vnic_type": "normal", + "allowed_address_pairs": [ + { + "ip": "192.168.1.13", + "mac": "00:f3:43:20:a2:a3" + }, + ], + "bond": "none", + "macbond": "00:d9:00:82:11:d1", + "sfc_encapsulation": "", + "direction": "", + }, + "vl_id": "vlid1", + "pnf_id": "pnf1", + }, + + ], + + "fps": [ + { + "fp_id": "fpd_1", + "description": "", + "properties": { + "policy": { + "type": "ACL", + "criteria": { + "dest_port_range": [80, 1024], + "source_port_range": [80, 1024], + "ip_protocol": "tcp", + "dest_ip_range": ["192.168.1.2", "192.168.1.100"], + "source_ip_range": ["192.168.1.2", "192.168.1.100"], + "dscp": 100, + }, + }, + "symmetric": True, + }, + "forwarder_list": [ + { + "type": "cp", + "node_name": "cpd_1", + "capability": "", + }, + { + "type": "cp", + "node_name": "forwarder_brasDP_dcPort", + "capability": "", + }, + { + "type": "vnf", + "node_name": "vnf_1", + "capability": "forwarder1", + }, + { + "type": "vnf", + "node_name": "vnf_2", + "capability": "forwarder2", + }, + { + "type": "cp", + "node_name": "forwarder_brasDP_dcPort", + "capability": "", + }, + { + "type": "cp", + "node_name": "forwarder_brasDP_internetPort", + "capability": "", + }, + ], + }, + + { + "fp_id": "fpd_2", + "description": "", + "properties": { + "policy": { + "type": "ACL", + "criteria": { + "dest_port_range": [80, 1024], + "source_port_range": [80, 1024], + "ip_protocol": "tcp", + "dest_ip_range": ["192.168.1.2", "192.168.1.100"], + "source_ip_range": ["192.168.1.2", "192.168.1.100"], + "dscp": 100, + }, + }, + "symmetric": True, + }, + "forwarder_list": [ + + { + "type": "cp", + "node_name": "forwarder_brasDP_internetPort", + "capability": "", + }, + { + "type": "cp", + "node_name": "forwarder_brasDP_dcPort", + "capability": "", + }, + { + "type": "vnf", + "node_name": "vnf_2", + "capability": "forwarder2", + }, + + ], + }, + ], + + "vnffgs": [ + { + "vnffg_id": "vnffg_id1", + "description": "", + "properties": { + "vendor": "zte", + "version": "1.1.2", + "number_of_endpoints": 7, + "dependent_virtual_link": ["vldId1"], + "connection_point": ["CP01", "CP02"], + "constituent_vnfs": ["vnf_id1", "vnf_id2"], + "constituent_pnfs": ["pnf1", "pnf2"], + }, + "members": ["fpd_1", "fpd_2"], + } + ], + + "server_groups": [ + { + "group_id": "", + "description": "", + "properties": { + "name": "server_group1", + "affinity_antiaffinity": "anti-affinity", + "scope": "host", + }, + "members": ["vnf1", "vnf2"], + }, + ], + + "ns_exposed": { + "external_cps": [ + { + "key_name": "virtualLink1", + "cp_id": "cp1", + }, + ], + "forward_cps": [ + { + "key_name": "forwarder_brasDP_userPort", + "cp_id": "cpd_1", + }, + { + "key_name": "forwarder_brasDP_internetPort", + "cp_id": "cpd_4", + }, + { + "key_name": "forwarder_brasDP_dcPort", + "cp_id": "cpd_5", + }, + + ], + }, + + "policies": [ + { + "scaling": [ + { + "policy_id": "id1", + "description": "", + "properties": { + "policy_file": "Policies/ns1-policy.xml", + }, + "targets": ['pfu_vm'], + }, + ], + }, + ], + + "ns_flavours": [ + { + "flavour_id": "flavour1", + "description": "", + "vnf_profiles": [ + { + "vnf_id": "vnf1", + "flavour_id": "flavour1", + "instances_minimum_number": 1, + "instances_maximum_number": 4, + "local_affinity_antiaffinity_rule": [ + { + "affinity_antiaffinity": "affinity", + "scope": "node", + } + ] + }, + ], + "pnf_profiles": [ + { + "pnf_id": "pnf1", + }, + ], + "vl_profiles": [ + { + "vl_id": "vlid1", + "bitrate_requirements": { + "root": 1000, + "leaf": 100 + }, + "qos": { + "maximum_latency": "1 ms", + "maximum_jitter": "10 ms", + "maximum_packet_loss_ratio": 0.5 + }, + } + ], + "instantiation_levels": [ + { + "id": "instLevel1", + "description": "", + "vnf_levels": [ + { + "vnf_id": "", + "vnf_instantiation_level": "small", + "instances_number": 1 + }, + ], + "scale_level_id": "scaleLevel1", + } + ], + "default_instantiation_level": "instLevel1", + "scale_levels": [ + { + "id": "scaleLevel1", + "order": 1, + "vnf_levels": [ + { + "vnf_id": "", + "vnf_instantiation_level": "small", + "instances_number": 1 + }, + ], + }, + ], + "supported_operations": ["Scale", "Heal"], + "affinity_antiaffinity_groups": [ + { + "group_id": "group1Id", + "name": "groupName", + "affinity_antiaffinity": "affinity", + "scope": "node", + "members": [ + "vnfId1", "vnfIdN", + ], + }, + ], + }, + ], +} +vnfd_model_dict1 = { + + 'vdus': [ + { + 'volumn_storages': [ + + ], + 'nfv_compute': { + 'mem_size': '', + 'num_cpus': u'2' + }, + 'local_storages': [ + + ], + 'vdu_id': u'vdu_omm.001', + 'image_file': u'opencos_sss_omm_img_release_20150723-1-disk1', + 'dependencies': [ + + ], + 'vls': [ + + ], + 'cps': [ + + ], + 'properties': { + 'key_vdu': '', + 'support_scaling': False, + 'vdu_type': '', + 'name': '', + 'storage_policy': '', + 'location_info': { + 'vimId': '', + 'availability_zone': '', + 'region': '', + 'dc': '', + 'host': '', + 'tenant': '' + }, + 'inject_data_list': [ + + ], + 'watchdog': { + 'action': '', + 'enabledelay': '' + }, + 'local_affinity_antiaffinity_rule': { + + }, + 'template_id': u'omm.001', + 'manual_scale_select_vim': False + }, + 'description': u'singleommvm' + }, + { + 'volumn_storages': [ + + ], + 'nfv_compute': { + 'mem_size': '', + 'num_cpus': u'4' + }, + 'local_storages': [ + + ], + 'vdu_id': u'vdu_1', + 'image_file': u'sss', + 'dependencies': [ + + ], + 'vls': [ + + ], + 'cps': [ + + ], + 'properties': { + 'key_vdu': '', + 'support_scaling': False, + 'vdu_type': '', + 'name': '', + 'storage_policy': '', + 'location_info': { + 'vimId': '', + 'availability_zone': '', + 'region': '', + 'dc': '', + 'host': '', + 'tenant': '' + }, + 'inject_data_list': [ + + ], + 'watchdog': { + 'action': '', + 'enabledelay': '' + }, + 'local_affinity_antiaffinity_rule': { + + }, + 'template_id': u'1', + 'manual_scale_select_vim': False + }, + 'description': u'ompvm' + }, + { + 'volumn_storages': [ + + ], + 'nfv_compute': { + 'mem_size': '', + 'num_cpus': u'14' + }, + 'local_storages': [ + + ], + 'vdu_id': u'vdu_2', + 'image_file': u'sss', + 'dependencies': [ + + ], + 'vls': [ + + ], + 'cps': [ + + ], + 'properties': { + 'key_vdu': '', + 'support_scaling': False, + 'vdu_type': '', + 'name': '', + 'storage_policy': '', + 'location_info': { + 'vimId': '', + 'availability_zone': '', + 'region': '', + 'dc': '', + 'host': '', + 'tenant': '' + }, + 'inject_data_list': [ + + ], + 'watchdog': { + 'action': '', + 'enabledelay': '' + }, + 'local_affinity_antiaffinity_rule': { + + }, + 'template_id': u'2', + 'manual_scale_select_vim': False + }, + 'description': u'ompvm' + }, + { + 'volumn_storages': [ + + ], + 'nfv_compute': { + 'mem_size': '', + 'num_cpus': u'14' + }, + 'local_storages': [ + + ], + 'vdu_id': u'vdu_3', + 'image_file': u'sss', + 'dependencies': [ + + ], + 'vls': [ + + ], + 'cps': [ + + ], + 'properties': { + 'key_vdu': '', + 'support_scaling': False, + 'vdu_type': '', + 'name': '', + 'storage_policy': '', + 'location_info': { + 'vimId': '', + 'availability_zone': '', + 'region': '', + 'dc': '', + 'host': '', + 'tenant': '' + }, + 'inject_data_list': [ + + ], + 'watchdog': { + 'action': '', + 'enabledelay': '' + }, + 'local_affinity_antiaffinity_rule': { + + }, + 'template_id': u'3', + 'manual_scale_select_vim': False + }, + 'description': u'ompvm' + }, + { + 'volumn_storages': [ + + ], + 'nfv_compute': { + 'mem_size': '', + 'num_cpus': u'4' + }, + 'local_storages': [ + + ], + 'vdu_id': u'vdu_10', + 'image_file': u'sss', + 'dependencies': [ + + ], + 'vls': [ + + ], + 'cps': [ + + ], + 'properties': { + 'key_vdu': '', + 'support_scaling': False, + 'vdu_type': '', + 'name': '', + 'storage_policy': '', + 'location_info': { + 'vimId': '', + 'availability_zone': '', + 'region': '', + 'dc': '', + 'host': '', + 'tenant': '' + }, + 'inject_data_list': [ + + ], + 'watchdog': { + 'action': '', + 'enabledelay': '' + }, + 'local_affinity_antiaffinity_rule': { + + }, + 'template_id': u'10', + 'manual_scale_select_vim': False + }, + 'description': u'ppvm' + }, + { + 'volumn_storages': [ + + ], + 'nfv_compute': { + 'mem_size': '', + 'num_cpus': u'14' + }, + 'local_storages': [ + + ], + 'vdu_id': u'vdu_11', + 'image_file': u'sss', + 'dependencies': [ + + ], + 'vls': [ + + ], + 'cps': [ + + ], + 'properties': { + 'key_vdu': '', + 'support_scaling': False, + 'vdu_type': '', + 'name': '', + 'storage_policy': '', + 'location_info': { + 'vimId': '', + 'availability_zone': '', + 'region': '', + 'dc': '', + 'host': '', + 'tenant': '' + }, + 'inject_data_list': [ + + ], + 'watchdog': { + 'action': '', + 'enabledelay': '' + }, + 'local_affinity_antiaffinity_rule': { + + }, + 'template_id': u'11', + 'manual_scale_select_vim': False + }, + 'description': u'ppvm' + }, + { + 'volumn_storages': [ + + ], + 'nfv_compute': { + 'mem_size': '', + 'num_cpus': u'14' + }, + 'local_storages': [ + + ], + 'vdu_id': u'vdu_12', + 'image_file': u'sss', + 'dependencies': [ + + ], + 'vls': [ + + ], + 'cps': [ + + ], + 'properties': { + 'key_vdu': '', + 'support_scaling': False, + 'vdu_type': '', + 'name': '', + 'storage_policy': '', + 'location_info': { + 'vimId': '', + 'availability_zone': '', + 'region': '', + 'dc': '', + 'host': '', + 'tenant': '' + }, + 'inject_data_list': [ + + ], + 'watchdog': { + 'action': '', + 'enabledelay': '' + }, + 'local_affinity_antiaffinity_rule': { + + }, + 'template_id': u'12', + 'manual_scale_select_vim': False + }, + 'description': u'ppvm' + } + ], + 'volumn_storages': [ + + ], + 'policies': { + 'scaling': { + 'targets': { + + }, + 'policy_id': u'policy_scale_sss-vnf-template', + 'properties': { + 'policy_file': '*-vnfd.zip/*-vnf-policy.xml' + }, + 'description': '' + } + }, + 'image_files': [ + { + 'description': '', + 'properties': { + 'name': u'opencos_sss_omm_img_release_20150723-1-disk1.vmdk', + 'checksum': '', + 'disk_format': u'VMDK', + 'file_url': u'./zte-cn-sss-main-image/OMM/opencos_sss_omm_img_release_20150723-1-disk1.vmdk', + 'container_type': 'vm', + 'version': '', + 'hypervisor_type': 'kvm' + }, + 'image_file_id': u'opencos_sss_omm_img_release_20150723-1-disk1' + }, + { + 'description': '', + 'properties': { + 'name': u'sss.vmdk', + 'checksum': '', + 'disk_format': u'VMDK', + 'file_url': u'./zte-cn-sss-main-image/NE/sss.vmdk', + 'container_type': 'vm', + 'version': '', + 'hypervisor_type': 'kvm' + }, + 'image_file_id': u'sss' + } + ], + 'vls': [ + + ], + 'cps': [ + {'cp_id': 'cpd_1', + "description": "", + "properties": { + "mac_address": "00:d9:00:82:11:e1", + "ip_address": "10.43.25.2", + "ip_range_start": "192.168.1.20", + "ip_range_end": "192.168.1.29", + "sfc_encapsulation": "" + } + }, + ], + 'metadata': { + 'vendor': u'zte', + 'is_shared': False, + 'description': '', + 'domain_type': u'CN', + 'version': u'v4.14.10', + 'vmnumber_overquota_alarm': False, + 'cross_dc': False, + 'vnf_type': u'SSS', + 'vnfd_version': u'V00000001', + 'id': u'vnfd_2', + 'name': u'sss-vnf-template' + }, + + 'vnf_exposed': { + "external_cps": [ + { + "key_name": "virtualLink1", + "cp_id": "cp1", + }, + ], + "forward_cps": [ + { + "key_name": "forwarder1", + "cp_id": "cpd_1", + }, + { + "key_name": "forwarder2", + "cp_id": "cpd_2", + }, + ], + } +} +vnfd_model_dict2 = { + 'local_storages': [ + + ], + 'vdus': [ + { + 'volumn_storages': [ + + ], + 'nfv_compute': { + 'mem_size': '', + 'num_cpus': u'2' + }, + 'local_storages': [ + + ], + 'vdu_id': u'vdu_omm.001', + 'image_file': u'opencos_sss_omm_img_release_20150723-1-disk1', + 'dependencies': [ + + ], + 'vls': [ + + ], + 'cps': [ + + ], + 'properties': { + 'key_vdu': '', + 'support_scaling': False, + 'vdu_type': '', + 'name': '', + 'storage_policy': '', + 'location_info': { + 'vimId': '', + 'availability_zone': '', + 'region': '', + 'dc': '', + 'host': '', + 'tenant': '' + }, + 'inject_data_list': [ + + ], + 'watchdog': { + 'action': '', + 'enabledelay': '' + }, + 'local_affinity_antiaffinity_rule': { + + }, + 'template_id': u'omm.001', + 'manual_scale_select_vim': False + }, + 'description': u'singleommvm' + }, + { + 'volumn_storages': [ + + ], + 'nfv_compute': { + 'mem_size': '', + 'num_cpus': u'4' + }, + 'local_storages': [ + + ], + 'vdu_id': u'vdu_1', + 'image_file': u'sss', + 'dependencies': [ + + ], + 'vls': [ + + ], + 'cps': [ + + ], + 'properties': { + 'key_vdu': '', + 'support_scaling': False, + 'vdu_type': '', + 'name': '', + 'storage_policy': '', + 'location_info': { + 'vimId': '', + 'availability_zone': '', + 'region': '', + 'dc': '', + 'host': '', + 'tenant': '' + }, + 'inject_data_list': [ + + ], + 'watchdog': { + 'action': '', + 'enabledelay': '' + }, + 'local_affinity_antiaffinity_rule': { + + }, + 'template_id': u'1', + 'manual_scale_select_vim': False + }, + 'description': u'ompvm' + }, + { + 'volumn_storages': [ + + ], + 'nfv_compute': { + 'mem_size': '', + 'num_cpus': u'14' + }, + 'local_storages': [ + + ], + 'vdu_id': u'vdu_2', + 'image_file': u'sss', + 'dependencies': [ + + ], + 'vls': [ + + ], + 'cps': [ + + ], + 'properties': { + 'key_vdu': '', + 'support_scaling': False, + 'vdu_type': '', + 'name': '', + 'storage_policy': '', + 'location_info': { + 'vimId': '', + 'availability_zone': '', + 'region': '', + 'dc': '', + 'host': '', + 'tenant': '' + }, + 'inject_data_list': [ + + ], + 'watchdog': { + 'action': '', + 'enabledelay': '' + }, + 'local_affinity_antiaffinity_rule': { + + }, + 'template_id': u'2', + 'manual_scale_select_vim': False + }, + 'description': u'ompvm' + }, + { + 'volumn_storages': [ + + ], + 'nfv_compute': { + 'mem_size': '', + 'num_cpus': u'14' + }, + 'local_storages': [ + + ], + 'vdu_id': u'vdu_3', + 'image_file': u'sss', + 'dependencies': [ + + ], + 'vls': [ + + ], + 'cps': [ + + ], + 'properties': { + 'key_vdu': '', + 'support_scaling': False, + 'vdu_type': '', + 'name': '', + 'storage_policy': '', + 'location_info': { + 'vimId': '', + 'availability_zone': '', + 'region': '', + 'dc': '', + 'host': '', + 'tenant': '' + }, + 'inject_data_list': [ + + ], + 'watchdog': { + 'action': '', + 'enabledelay': '' + }, + 'local_affinity_antiaffinity_rule': { + + }, + 'template_id': u'3', + 'manual_scale_select_vim': False + }, + 'description': u'ompvm' + }, + { + 'volumn_storages': [ + + ], + 'nfv_compute': { + 'mem_size': '', + 'num_cpus': u'4' + }, + 'local_storages': [ + + ], + 'vdu_id': u'vdu_10', + 'image_file': u'sss', + 'dependencies': [ + + ], + 'vls': [ + + ], + 'cps': [ + + ], + 'properties': { + 'key_vdu': '', + 'support_scaling': False, + 'vdu_type': '', + 'name': '', + 'storage_policy': '', + 'location_info': { + 'vimId': '', + 'availability_zone': '', + 'region': '', + 'dc': '', + 'host': '', + 'tenant': '' + }, + 'inject_data_list': [ + + ], + 'watchdog': { + 'action': '', + 'enabledelay': '' + }, + 'local_affinity_antiaffinity_rule': { + + }, + 'template_id': u'10', + 'manual_scale_select_vim': False + }, + 'description': u'ppvm' + }, + { + 'volumn_storages': [ + + ], + 'nfv_compute': { + 'mem_size': '', + 'num_cpus': u'14' + }, + 'local_storages': [ + + ], + 'vdu_id': u'vdu_11', + 'image_file': u'sss', + 'dependencies': [ + + ], + 'vls': [ + + ], + 'cps': [ + + ], + 'properties': { + 'key_vdu': '', + 'support_scaling': False, + 'vdu_type': '', + 'name': '', + 'storage_policy': '', + 'location_info': { + 'vimId': '', + 'availability_zone': '', + 'region': '', + 'dc': '', + 'host': '', + 'tenant': '' + }, + 'inject_data_list': [ + + ], + 'watchdog': { + 'action': '', + 'enabledelay': '' + }, + 'local_affinity_antiaffinity_rule': { + + }, + 'template_id': u'11', + 'manual_scale_select_vim': False + }, + 'description': u'ppvm' + }, + { + 'volumn_storages': [ + + ], + 'nfv_compute': { + 'mem_size': '', + 'num_cpus': u'14' + }, + 'local_storages': [ + + ], + 'vdu_id': u'vdu_12', + 'image_file': u'sss', + 'dependencies': [ + + ], + 'vls': [ + + ], + 'cps': [ + + ], + 'properties': { + 'key_vdu': '', + 'support_scaling': False, + 'vdu_type': '', + 'name': '', + 'storage_policy': '', + 'location_info': { + 'vimId': '', + 'availability_zone': '', + 'region': '', + 'dc': '', + 'host': '', + 'tenant': '' + }, + 'inject_data_list': [ + + ], + 'watchdog': { + 'action': '', + 'enabledelay': '' + }, + 'local_affinity_antiaffinity_rule': { + + }, + 'template_id': u'12', + 'manual_scale_select_vim': False + }, + 'description': u'ppvm' + } + ], + 'volumn_storages': [ + + ], + 'policies': { + 'scaling': { + 'targets': { + + }, + 'policy_id': u'policy_scale_sss-vnf-template', + 'properties': { + 'policy_file': '*-vnfd.zip/*-vnf-policy.xml' + }, + 'description': '' + } + }, + 'image_files': [ + { + 'description': '', + 'properties': { + 'name': u'opencos_sss_omm_img_release_20150723-1-disk1.vmdk', + 'checksum': '', + 'disk_format': u'VMDK', + 'file_url': u'./zte-cn-sss-main-image/OMM/opencos_sss_omm_img_release_20150723-1-disk1.vmdk', + 'container_type': 'vm', + 'version': '', + 'hypervisor_type': 'kvm' + }, + 'image_file_id': u'opencos_sss_omm_img_release_20150723-1-disk1' + }, + { + 'description': '', + 'properties': { + 'name': u'sss.vmdk', + 'checksum': '', + 'disk_format': u'VMDK', + 'file_url': u'./zte-cn-sss-main-image/NE/sss.vmdk', + 'container_type': 'vm', + 'version': '', + 'hypervisor_type': 'kvm' + }, + 'image_file_id': u'sss' + } + ], + 'vls': [ + + ], + 'cps': [ + {'cp_id': 'cpd_2', + "description": "", + "properties": { + "mac_address": "00:d9:00:82:11:e2", + "ip_address": "10.43.25.3", + "ip_range_start": "192.168.1.20", + "ip_range_end": "192.168.1.29", + "sfc_encapsulation": "" + } + }, + ], + 'metadata': { + 'vendor': u'zte', + 'is_shared': False, + 'description': '', + 'domain_type': u'CN', + 'version': u'v4.14.10', + 'vmnumber_overquota_alarm': False, + 'cross_dc': False, + 'vnf_type': u'SSS', + 'vnfd_version': u'V00000001', + 'id': u'sss-vnf-template', + 'name': u'vnfd_2' + }, + 'vnf_exposed': { + "external_cps": [ + { + "key_name": "virtualLink1", + "cp_id": "cp1", + }, + ], + "forward_cps": [ + { + "key_name": "forwarder2", + "cp_id": "cpd_2", + }, + { + "key_name": "forwarder3", + "cp_id": "cpd_2", + }, + ], + } +} + + + + diff --git a/lcm/ns/tests/sfcs/test_sfc.py b/lcm/ns/tests/sfcs/test_sfc.py new file mode 100644 index 00000000..cabead00 --- /dev/null +++ b/lcm/ns/tests/sfcs/test_sfc.py @@ -0,0 +1,1652 @@ +# Copyright 2016 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 json +# +# import mock +# from django.test import Client +# from django.test import TestCase +# from rest_framework import status +# +# from lcm.pub.database.models import FPInstModel, CPInstModel, PortInstModel, NfInstModel +# from lcm.pub.database.models import VNFFGInstModel +# from lcm.pub.msapi import extsys +# from lcm.pub.msapi import sdncdriver +# from lcm.pub.utils import restcall +# +# +# class TestSfc(TestCase): +# def setUp(self): +# self.client = Client() +# FPInstModel.objects.filter().delete() +# VNFFGInstModel.objects.filter().delete() +# CPInstModel.objects.filter().delete() +# PortInstModel.objects.filter().delete() +# NfInstModel.objects.filter().delete() +# +# self.save_vnffg_inst_data() +# self.save_vnf_inst_data() +# self.save_cp_inst_data() +# self.save_port_inst_data() +# self.save_fp_inst_data() +# +# def tearDown(self): +# pass +# +# @mock.patch.object(restcall, 'call_req') +# def test_sfc_instanciate(self, mock_call_req): +# data = { +# "nsinstanceid": "ns_inst_1", +# "context": json.dumps(nsd_model), +# "fpindex": "fpd_1", +# "sdncontrollerid": "sdnControllerId_1" +# } +# +# resp = self.client.post("/openoapi/nslcm/v1/ns/sfc_instance", data, format='json') +# self.assertEqual(resp.status_code, status.HTTP_200_OK) +# +# @mock.patch.object(extsys, "get_sdn_controller_by_id") +# @mock.patch.object(sdncdriver, "create_flow_classfier") +# @mock.patch.object(restcall, 'call_req') +# def test_create_flow_classfier(self, mock_call_req, mock_create_flow_classfier, mock_get_sdn_controller_by_id): +# data = { +# "fpinstid": "fp_inst_1", +# "context": json.dumps(nsd_model) +# } +# mock_create_flow_classfier.return_value = [0, json.dumps({'id': '1'})] +# mock_get_sdn_controller_by_id.return_value = json.loads('{"test":"test_name","url":"url_add"}') +# resp = self.client.post("/openoapi/nslcm/v1/ns/create_flow_classifier", data) +# self.assertEqual(resp.status_code, status.HTTP_200_OK) +# +# @mock.patch.object(extsys, "get_sdn_controller_by_id") +# @mock.patch.object(sdncdriver, 'create_port_pair_group') +# @mock.patch.object(sdncdriver, 'create_port_pair') +# @mock.patch.object(restcall, 'call_req') +# def test_create_port_pair_group(self, mock_call_req, mock_create_port_pair, mock_create_port_pair_group +# , mock_get_sdn_controller_by_id): +# data = { +# "nsinstanceid": "ns_inst_1", +# "fpinstid": "fp_inst_1", +# "context": json.dumps(nsd_model) +# } +# mock_create_port_pair.return_value = [0, json.dumps({'id': '1'})] +# mock_create_port_pair_group.return_value = [0, json.dumps({'id': '1'})] +# mock_get_sdn_controller_by_id.return_value = json.loads('{"test":"test_name","url":"url_add"}') +# resp = self.client.post("/openoapi/nslcm/v1/ns/create_port_pair_group", data) +# self.assertEqual(resp.status_code, status.HTTP_200_OK) +# +# @mock.patch.object(extsys, "get_sdn_controller_by_id") +# @mock.patch.object(sdncdriver, 'create_port_chain') +# @mock.patch.object(restcall, 'call_req') +# def test_create_port_chain(self, mock_call_req, mock_create_port_chain +# , mock_get_sdn_controller_by_id): +# data = { +# "nsinstanceid": "ns_inst_1", +# "fpinstid": "fp_inst_1", +# "context": json.dumps(nsd_model) +# } +# self.update_fp_inst_data() +# mock_create_port_chain.return_value = [0, json.dumps({'id': '1'})] +# mock_get_sdn_controller_by_id.return_value = json.loads('{"test":"test_name","url":"url_add"}') +# resp = self.client.post("/openoapi/nslcm/v1/ns/create_port_chain", data) +# self.assertEqual(resp.status_code, status.HTTP_200_OK) +# +# # @mock.patch.object(restcall, 'call_req') +# # def test_create_sfc(self, mock_call_req): +# # data = { +# # "nsinstanceid": "ns_inst_1", +# # "context": json.dumps(nsd_model), +# # "fpindex": "fpd_1", +# # 'fpinstid': str(uuid.uuid4()), +# # "sdncontrollerid": "sdnControllerId_1" +# # } +# # +# # resp = self.client.post("/openoapi/nslcm/v1/ns/sfc", data, format='json') +# # self.assertEqual(resp.status_code, status.HTTP_200_OK) +# +# def update_fp_inst_data(self): +# FPInstModel.objects.filter(fpinstid="fp_inst_1").update(flowclassifiers="1", +# portpairgroups=json.JSONEncoder().encode([{ +# "groupid": "1", +# "portpair": ["2"] +# }])) +# +# def save_vnffg_inst_data(self): +# VNFFGInstModel( +# vnffgdid="vnffg_id1", +# vnffginstid="vnffg_inst_1", +# nsinstid="ns_inst_1", +# endpointnumber=2, +# vllist="vlinst1", +# cplist="cp1", +# vnflist="vnf1,vnf2" +# ).save() +# +# def save_cp_inst_data(self): +# CPInstModel( +# cpinstanceid="cp_inst_1", +# cpdid="cpd_1", +# ownertype=0, +# ownerid="vnf_inst_1", +# relatedtype=1, +# relatedport="port_inst_1" +# ).save() +# +# CPInstModel( +# cpinstanceid="cp_inst_2", +# cpdid="cpd_2", +# ownertype=0, +# ownerid="vnf_inst_2", +# relatedtype=1, +# relatedport="port_inst_2" +# ).save() +# +# def save_fp_inst_data(self): +# FPInstModel( +# fpid="fpd_1", +# fpinstid="fp_inst_1", +# nsinstid="ns_inst_1", +# vnffginstid="vnffg_inst_1", +# policyinfo=[{ +# "type": "ACL", +# "criteria": { +# "dest_port_range": [80, 1024], +# "source_port_range": [80, 1024], +# "ip_protocol": "tcp", +# "dest_ip_range": ["192.168.1.2", "192.168.1.100"], +# "source_ip_range": ["192.168.1.2", "192.168.1.100"], +# "dscp": 100, +# } +# }], +# status="enabled", +# sdncontrollerid="sdn_controller_1" +# +# ).save() +# +# FPInstModel( +# fpid="fpd_2", +# fpinstid="fp_inst_2", +# nsinstid="ns_inst_1", +# vnffginstid="vnffg_inst_1", +# policyinfo=[{ +# "type": "ACL", +# "criteria": { +# "dest_port_range": [80, 1024], +# "source_port_range": [80, 1024], +# "ip_protocol": "tcp", +# "dest_ip_range": ["192.168.1.2", "192.168.1.100"], +# "source_ip_range": ["192.168.1.2", "192.168.1.100"], +# "dscp": 100, +# } +# }], +# status="enabled", +# sdncontrollerid="sdn_controller_1" +# +# ).save() +# +# # +# # def save_sdnc_inst_data(self): +# # SDNCModel( +# # uuid="uuid_111", +# # sdncontrollerid="sdn_controller_1", +# # name="111", +# # type="vnf", +# # url="192.168.0.1:8080", +# # username="admin", +# # pwd="admin", +# # ver="ver", +# # longitude="longitude", +# # latitude="latitude" +# # +# # ).save() +# +# +# +# +# def save_port_inst_data(self): +# PortInstModel( +# portid="port_inst_1", +# networkid="network_inst_1", +# subnetworkid="subnetwork_inst_1", +# vimid="vim_1", +# # insttype="2", +# resourceid="res_1", +# ipaddress="10.43.25.2", +# macaddress="EC-F4-BB-20-43-F1" +# ).save() +# +# PortInstModel( +# portid="port_inst_2", +# networkid="network_inst_1", +# subnetworkid="subnetwork_inst_1", +# vimid="vim_1", +# # insttype="2", +# resourceid="res_1", +# ipaddress="10.43.25.3", +# macaddress="EC-F4-BB-20-43-F2" +# ).save() +# +# def save_vnf_inst_data(self): +# NfInstModel( +# nfinstid="vnf_inst_1", +# ns_inst_id="ns_inst_1", +# vnf_id="vnf_1", +# vnfd_model=json.dumps(vnfd_model_dict1) +# +# ).save() +# +# NfInstModel( +# nfinstid="vnf_inst_2", +# vnf_id="vnf_2", +# ns_inst_id="ns_inst_1", +# vnfd_model=json.dumps(vnfd_model_dict2) +# +# ).save() +# +# +# vnfd_model_dict1 = { +# +# 'vdus': [ +# { +# 'volumn_storages': [ +# +# ], +# 'nfv_compute': { +# 'mem_size': '', +# 'num_cpus': u'2' +# }, +# 'local_storages': [ +# +# ], +# 'vdu_id': u'vdu_omm.001', +# 'image_file': u'opencos_sss_omm_img_release_20150723-1-disk1', +# 'dependencies': [ +# +# ], +# 'vls': [ +# +# ], +# 'cps': [ +# +# ], +# 'properties': { +# 'key_vdu': '', +# 'support_scaling': False, +# 'vdu_type': '', +# 'name': '', +# 'storage_policy': '', +# 'location_info': { +# 'vimId': '', +# 'availability_zone': '', +# 'region': '', +# 'dc': '', +# 'host': '', +# 'tenant': '' +# }, +# 'inject_data_list': [ +# +# ], +# 'watchdog': { +# 'action': '', +# 'enabledelay': '' +# }, +# 'local_affinity_antiaffinity_rule': { +# +# }, +# 'template_id': u'omm.001', +# 'manual_scale_select_vim': False +# }, +# 'description': u'singleommvm' +# }, +# { +# 'volumn_storages': [ +# +# ], +# 'nfv_compute': { +# 'mem_size': '', +# 'num_cpus': u'4' +# }, +# 'local_storages': [ +# +# ], +# 'vdu_id': u'vdu_1', +# 'image_file': u'sss', +# 'dependencies': [ +# +# ], +# 'vls': [ +# +# ], +# 'cps': [ +# +# ], +# 'properties': { +# 'key_vdu': '', +# 'support_scaling': False, +# 'vdu_type': '', +# 'name': '', +# 'storage_policy': '', +# 'location_info': { +# 'vimId': '', +# 'availability_zone': '', +# 'region': '', +# 'dc': '', +# 'host': '', +# 'tenant': '' +# }, +# 'inject_data_list': [ +# +# ], +# 'watchdog': { +# 'action': '', +# 'enabledelay': '' +# }, +# 'local_affinity_antiaffinity_rule': { +# +# }, +# 'template_id': u'1', +# 'manual_scale_select_vim': False +# }, +# 'description': u'ompvm' +# }, +# { +# 'volumn_storages': [ +# +# ], +# 'nfv_compute': { +# 'mem_size': '', +# 'num_cpus': u'14' +# }, +# 'local_storages': [ +# +# ], +# 'vdu_id': u'vdu_2', +# 'image_file': u'sss', +# 'dependencies': [ +# +# ], +# 'vls': [ +# +# ], +# 'cps': [ +# +# ], +# 'properties': { +# 'key_vdu': '', +# 'support_scaling': False, +# 'vdu_type': '', +# 'name': '', +# 'storage_policy': '', +# 'location_info': { +# 'vimId': '', +# 'availability_zone': '', +# 'region': '', +# 'dc': '', +# 'host': '', +# 'tenant': '' +# }, +# 'inject_data_list': [ +# +# ], +# 'watchdog': { +# 'action': '', +# 'enabledelay': '' +# }, +# 'local_affinity_antiaffinity_rule': { +# +# }, +# 'template_id': u'2', +# 'manual_scale_select_vim': False +# }, +# 'description': u'ompvm' +# }, +# { +# 'volumn_storages': [ +# +# ], +# 'nfv_compute': { +# 'mem_size': '', +# 'num_cpus': u'14' +# }, +# 'local_storages': [ +# +# ], +# 'vdu_id': u'vdu_3', +# 'image_file': u'sss', +# 'dependencies': [ +# +# ], +# 'vls': [ +# +# ], +# 'cps': [ +# +# ], +# 'properties': { +# 'key_vdu': '', +# 'support_scaling': False, +# 'vdu_type': '', +# 'name': '', +# 'storage_policy': '', +# 'location_info': { +# 'vimId': '', +# 'availability_zone': '', +# 'region': '', +# 'dc': '', +# 'host': '', +# 'tenant': '' +# }, +# 'inject_data_list': [ +# +# ], +# 'watchdog': { +# 'action': '', +# 'enabledelay': '' +# }, +# 'local_affinity_antiaffinity_rule': { +# +# }, +# 'template_id': u'3', +# 'manual_scale_select_vim': False +# }, +# 'description': u'ompvm' +# }, +# { +# 'volumn_storages': [ +# +# ], +# 'nfv_compute': { +# 'mem_size': '', +# 'num_cpus': u'4' +# }, +# 'local_storages': [ +# +# ], +# 'vdu_id': u'vdu_10', +# 'image_file': u'sss', +# 'dependencies': [ +# +# ], +# 'vls': [ +# +# ], +# 'cps': [ +# +# ], +# 'properties': { +# 'key_vdu': '', +# 'support_scaling': False, +# 'vdu_type': '', +# 'name': '', +# 'storage_policy': '', +# 'location_info': { +# 'vimId': '', +# 'availability_zone': '', +# 'region': '', +# 'dc': '', +# 'host': '', +# 'tenant': '' +# }, +# 'inject_data_list': [ +# +# ], +# 'watchdog': { +# 'action': '', +# 'enabledelay': '' +# }, +# 'local_affinity_antiaffinity_rule': { +# +# }, +# 'template_id': u'10', +# 'manual_scale_select_vim': False +# }, +# 'description': u'ppvm' +# }, +# { +# 'volumn_storages': [ +# +# ], +# 'nfv_compute': { +# 'mem_size': '', +# 'num_cpus': u'14' +# }, +# 'local_storages': [ +# +# ], +# 'vdu_id': u'vdu_11', +# 'image_file': u'sss', +# 'dependencies': [ +# +# ], +# 'vls': [ +# +# ], +# 'cps': [ +# +# ], +# 'properties': { +# 'key_vdu': '', +# 'support_scaling': False, +# 'vdu_type': '', +# 'name': '', +# 'storage_policy': '', +# 'location_info': { +# 'vimId': '', +# 'availability_zone': '', +# 'region': '', +# 'dc': '', +# 'host': '', +# 'tenant': '' +# }, +# 'inject_data_list': [ +# +# ], +# 'watchdog': { +# 'action': '', +# 'enabledelay': '' +# }, +# 'local_affinity_antiaffinity_rule': { +# +# }, +# 'template_id': u'11', +# 'manual_scale_select_vim': False +# }, +# 'description': u'ppvm' +# }, +# { +# 'volumn_storages': [ +# +# ], +# 'nfv_compute': { +# 'mem_size': '', +# 'num_cpus': u'14' +# }, +# 'local_storages': [ +# +# ], +# 'vdu_id': u'vdu_12', +# 'image_file': u'sss', +# 'dependencies': [ +# +# ], +# 'vls': [ +# +# ], +# 'cps': [ +# +# ], +# 'properties': { +# 'key_vdu': '', +# 'support_scaling': False, +# 'vdu_type': '', +# 'name': '', +# 'storage_policy': '', +# 'location_info': { +# 'vimId': '', +# 'availability_zone': '', +# 'region': '', +# 'dc': '', +# 'host': '', +# 'tenant': '' +# }, +# 'inject_data_list': [ +# +# ], +# 'watchdog': { +# 'action': '', +# 'enabledelay': '' +# }, +# 'local_affinity_antiaffinity_rule': { +# +# }, +# 'template_id': u'12', +# 'manual_scale_select_vim': False +# }, +# 'description': u'ppvm' +# } +# ], +# 'volumn_storages': [ +# +# ], +# 'policies': { +# 'scaling': { +# 'targets': { +# +# }, +# 'policy_id': u'policy_scale_sss-vnf-template', +# 'properties': { +# 'policy_file': '*-vnfd.zip/*-vnf-policy.xml' +# }, +# 'description': '' +# } +# }, +# 'image_files': [ +# { +# 'description': '', +# 'properties': { +# 'name': u'opencos_sss_omm_img_release_20150723-1-disk1.vmdk', +# 'checksum': '', +# 'disk_format': u'VMDK', +# 'file_url': u'./zte-cn-sss-main-image/OMM/opencos_sss_omm_img_release_20150723-1-disk1.vmdk', +# 'container_type': 'vm', +# 'version': '', +# 'hypervisor_type': 'kvm' +# }, +# 'image_file_id': u'opencos_sss_omm_img_release_20150723-1-disk1' +# }, +# { +# 'description': '', +# 'properties': { +# 'name': u'sss.vmdk', +# 'checksum': '', +# 'disk_format': u'VMDK', +# 'file_url': u'./zte-cn-sss-main-image/NE/sss.vmdk', +# 'container_type': 'vm', +# 'version': '', +# 'hypervisor_type': 'kvm' +# }, +# 'image_file_id': u'sss' +# } +# ], +# 'vls': [ +# +# ], +# 'cps': [ +# {'cp_id': 'cpd_1', +# "description": "", +# "properties": { +# "mac_address": "00:d9:00:82:11:e1", +# "ip_address": "10.43.25.2", +# "ip_range_start": "192.168.1.20", +# "ip_range_end": "192.168.1.29", +# "sfc_encapsulation": "" +# } +# }, +# ], +# 'metadata': { +# 'vendor': u'zte', +# 'is_shared': False, +# 'description': '', +# 'domain_type': u'CN', +# 'version': u'v4.14.10', +# 'vmnumber_overquota_alarm': False, +# 'cross_dc': False, +# 'vnf_type': u'SSS', +# 'vnfd_version': u'V00000001', +# 'id': u'vnfd_2', +# 'name': u'sss-vnf-template' +# }, +# +# 'vnf_exposed': { +# "external_cps": [ +# { +# "key_name": "virtualLink1", +# "cp_id": "cp1", +# }, +# ], +# "forward_cps": [ +# { +# "key_name": "forwarder1", +# "cp_id": "cpd_1", +# }, +# { +# "key_name": "forwarder2", +# "cp_id": "cpd_2", +# }, +# ], +# } +# } +# +# vnfd_model_dict2 = { +# 'local_storages': [ +# +# ], +# 'vdus': [ +# { +# 'volumn_storages': [ +# +# ], +# 'nfv_compute': { +# 'mem_size': '', +# 'num_cpus': u'2' +# }, +# 'local_storages': [ +# +# ], +# 'vdu_id': u'vdu_omm.001', +# 'image_file': u'opencos_sss_omm_img_release_20150723-1-disk1', +# 'dependencies': [ +# +# ], +# 'vls': [ +# +# ], +# 'cps': [ +# +# ], +# 'properties': { +# 'key_vdu': '', +# 'support_scaling': False, +# 'vdu_type': '', +# 'name': '', +# 'storage_policy': '', +# 'location_info': { +# 'vimId': '', +# 'availability_zone': '', +# 'region': '', +# 'dc': '', +# 'host': '', +# 'tenant': '' +# }, +# 'inject_data_list': [ +# +# ], +# 'watchdog': { +# 'action': '', +# 'enabledelay': '' +# }, +# 'local_affinity_antiaffinity_rule': { +# +# }, +# 'template_id': u'omm.001', +# 'manual_scale_select_vim': False +# }, +# 'description': u'singleommvm' +# }, +# { +# 'volumn_storages': [ +# +# ], +# 'nfv_compute': { +# 'mem_size': '', +# 'num_cpus': u'4' +# }, +# 'local_storages': [ +# +# ], +# 'vdu_id': u'vdu_1', +# 'image_file': u'sss', +# 'dependencies': [ +# +# ], +# 'vls': [ +# +# ], +# 'cps': [ +# +# ], +# 'properties': { +# 'key_vdu': '', +# 'support_scaling': False, +# 'vdu_type': '', +# 'name': '', +# 'storage_policy': '', +# 'location_info': { +# 'vimId': '', +# 'availability_zone': '', +# 'region': '', +# 'dc': '', +# 'host': '', +# 'tenant': '' +# }, +# 'inject_data_list': [ +# +# ], +# 'watchdog': { +# 'action': '', +# 'enabledelay': '' +# }, +# 'local_affinity_antiaffinity_rule': { +# +# }, +# 'template_id': u'1', +# 'manual_scale_select_vim': False +# }, +# 'description': u'ompvm' +# }, +# { +# 'volumn_storages': [ +# +# ], +# 'nfv_compute': { +# 'mem_size': '', +# 'num_cpus': u'14' +# }, +# 'local_storages': [ +# +# ], +# 'vdu_id': u'vdu_2', +# 'image_file': u'sss', +# 'dependencies': [ +# +# ], +# 'vls': [ +# +# ], +# 'cps': [ +# +# ], +# 'properties': { +# 'key_vdu': '', +# 'support_scaling': False, +# 'vdu_type': '', +# 'name': '', +# 'storage_policy': '', +# 'location_info': { +# 'vimId': '', +# 'availability_zone': '', +# 'region': '', +# 'dc': '', +# 'host': '', +# 'tenant': '' +# }, +# 'inject_data_list': [ +# +# ], +# 'watchdog': { +# 'action': '', +# 'enabledelay': '' +# }, +# 'local_affinity_antiaffinity_rule': { +# +# }, +# 'template_id': u'2', +# 'manual_scale_select_vim': False +# }, +# 'description': u'ompvm' +# }, +# { +# 'volumn_storages': [ +# +# ], +# 'nfv_compute': { +# 'mem_size': '', +# 'num_cpus': u'14' +# }, +# 'local_storages': [ +# +# ], +# 'vdu_id': u'vdu_3', +# 'image_file': u'sss', +# 'dependencies': [ +# +# ], +# 'vls': [ +# +# ], +# 'cps': [ +# +# ], +# 'properties': { +# 'key_vdu': '', +# 'support_scaling': False, +# 'vdu_type': '', +# 'name': '', +# 'storage_policy': '', +# 'location_info': { +# 'vimId': '', +# 'availability_zone': '', +# 'region': '', +# 'dc': '', +# 'host': '', +# 'tenant': '' +# }, +# 'inject_data_list': [ +# +# ], +# 'watchdog': { +# 'action': '', +# 'enabledelay': '' +# }, +# 'local_affinity_antiaffinity_rule': { +# +# }, +# 'template_id': u'3', +# 'manual_scale_select_vim': False +# }, +# 'description': u'ompvm' +# }, +# { +# 'volumn_storages': [ +# +# ], +# 'nfv_compute': { +# 'mem_size': '', +# 'num_cpus': u'4' +# }, +# 'local_storages': [ +# +# ], +# 'vdu_id': u'vdu_10', +# 'image_file': u'sss', +# 'dependencies': [ +# +# ], +# 'vls': [ +# +# ], +# 'cps': [ +# +# ], +# 'properties': { +# 'key_vdu': '', +# 'support_scaling': False, +# 'vdu_type': '', +# 'name': '', +# 'storage_policy': '', +# 'location_info': { +# 'vimId': '', +# 'availability_zone': '', +# 'region': '', +# 'dc': '', +# 'host': '', +# 'tenant': '' +# }, +# 'inject_data_list': [ +# +# ], +# 'watchdog': { +# 'action': '', +# 'enabledelay': '' +# }, +# 'local_affinity_antiaffinity_rule': { +# +# }, +# 'template_id': u'10', +# 'manual_scale_select_vim': False +# }, +# 'description': u'ppvm' +# }, +# { +# 'volumn_storages': [ +# +# ], +# 'nfv_compute': { +# 'mem_size': '', +# 'num_cpus': u'14' +# }, +# 'local_storages': [ +# +# ], +# 'vdu_id': u'vdu_11', +# 'image_file': u'sss', +# 'dependencies': [ +# +# ], +# 'vls': [ +# +# ], +# 'cps': [ +# +# ], +# 'properties': { +# 'key_vdu': '', +# 'support_scaling': False, +# 'vdu_type': '', +# 'name': '', +# 'storage_policy': '', +# 'location_info': { +# 'vimId': '', +# 'availability_zone': '', +# 'region': '', +# 'dc': '', +# 'host': '', +# 'tenant': '' +# }, +# 'inject_data_list': [ +# +# ], +# 'watchdog': { +# 'action': '', +# 'enabledelay': '' +# }, +# 'local_affinity_antiaffinity_rule': { +# +# }, +# 'template_id': u'11', +# 'manual_scale_select_vim': False +# }, +# 'description': u'ppvm' +# }, +# { +# 'volumn_storages': [ +# +# ], +# 'nfv_compute': { +# 'mem_size': '', +# 'num_cpus': u'14' +# }, +# 'local_storages': [ +# +# ], +# 'vdu_id': u'vdu_12', +# 'image_file': u'sss', +# 'dependencies': [ +# +# ], +# 'vls': [ +# +# ], +# 'cps': [ +# +# ], +# 'properties': { +# 'key_vdu': '', +# 'support_scaling': False, +# 'vdu_type': '', +# 'name': '', +# 'storage_policy': '', +# 'location_info': { +# 'vimId': '', +# 'availability_zone': '', +# 'region': '', +# 'dc': '', +# 'host': '', +# 'tenant': '' +# }, +# 'inject_data_list': [ +# +# ], +# 'watchdog': { +# 'action': '', +# 'enabledelay': '' +# }, +# 'local_affinity_antiaffinity_rule': { +# +# }, +# 'template_id': u'12', +# 'manual_scale_select_vim': False +# }, +# 'description': u'ppvm' +# } +# ], +# 'volumn_storages': [ +# +# ], +# 'policies': { +# 'scaling': { +# 'targets': { +# +# }, +# 'policy_id': u'policy_scale_sss-vnf-template', +# 'properties': { +# 'policy_file': '*-vnfd.zip/*-vnf-policy.xml' +# }, +# 'description': '' +# } +# }, +# 'image_files': [ +# { +# 'description': '', +# 'properties': { +# 'name': u'opencos_sss_omm_img_release_20150723-1-disk1.vmdk', +# 'checksum': '', +# 'disk_format': u'VMDK', +# 'file_url': u'./zte-cn-sss-main-image/OMM/opencos_sss_omm_img_release_20150723-1-disk1.vmdk', +# 'container_type': 'vm', +# 'version': '', +# 'hypervisor_type': 'kvm' +# }, +# 'image_file_id': u'opencos_sss_omm_img_release_20150723-1-disk1' +# }, +# { +# 'description': '', +# 'properties': { +# 'name': u'sss.vmdk', +# 'checksum': '', +# 'disk_format': u'VMDK', +# 'file_url': u'./zte-cn-sss-main-image/NE/sss.vmdk', +# 'container_type': 'vm', +# 'version': '', +# 'hypervisor_type': 'kvm' +# }, +# 'image_file_id': u'sss' +# } +# ], +# 'vls': [ +# +# ], +# 'cps': [ +# {'cp_id': 'cpd_2', +# "description": "", +# "properties": { +# "mac_address": "00:d9:00:82:11:e2", +# "ip_address": "10.43.25.3", +# "ip_range_start": "192.168.1.20", +# "ip_range_end": "192.168.1.29", +# "sfc_encapsulation": "" +# } +# }, +# ], +# 'metadata': { +# 'vendor': u'zte', +# 'is_shared': False, +# 'description': '', +# 'domain_type': u'CN', +# 'version': u'v4.14.10', +# 'vmnumber_overquota_alarm': False, +# 'cross_dc': False, +# 'vnf_type': u'SSS', +# 'vnfd_version': u'V00000001', +# 'id': u'sss-vnf-template', +# 'name': u'vnfd_2' +# }, +# 'vnf_exposed': { +# "external_cps": [ +# { +# "key_name": "virtualLink1", +# "cp_id": "cp1", +# }, +# ], +# "forward_cps": [ +# { +# "key_name": "forwarder2", +# "cp_id": "cpd_2", +# }, +# { +# "key_name": "forwarder3", +# "cp_id": "cpd_2", +# }, +# ], +# } +# } +# +# nsd_model = { +# "metadata": { +# "id": "nsd_demo", +# "vendor": "zte", +# "version": "1.1.0", +# "name": "testNSD", +# "description": "demo nsd", +# }, +# +# "inputs": { +# "param1": "11", +# "param2": "22", +# }, +# +# "vnfs": [ +# { +# "type": "tosca.nodes.nfv.ext.VNF.FireWall", +# "vnf_id": "vnf_1", +# "description": "", +# "properties": { +# "id": "vnfd_1", +# "vendor": "zte", +# "version": "1.2.0", +# "vnfd_version": "1.1.0", +# "vnf_type": "vnf1", +# "domain_type": "CN", +# "name": "vnf1", +# "is_shared": False, +# "cross_dc": False, +# "request_reclassification": False, +# "nsh_aware": False, +# "custom_properties": { +# "key1": "value1", +# "keyN": "valueN", +# }, +# }, +# "dependencies": [ +# "vnf_id1", "vnf_id2" +# ], +# "networks": [ +# { +# "key_name": "virtualLink1", +# "vl_id": "vl_id1", +# }, +# ], +# }, +# { +# "type": "tosca.nodes.nfv.ext.VNF.FireWall", +# "vnf_id": "vnf_2", +# "description": "", +# "properties": { +# "id": "vnfd_2", +# "vendor": "zte", +# "version": "1.2.0", +# "vnfd_version": "1.1.0", +# "vnf_type": "vnf2", +# "domain_type": "CN", +# "name": "vnf1", +# "is_shared": False, +# "cross_dc": False, +# "request_reclassification": False, +# "nsh_aware": False, +# "custom_properties": { +# "key1": "value1", +# "keyN": "valueN", +# }, +# }, +# "dependencies": [ +# "vnf_id1", "vnf_id2" +# ], +# "networks": [ +# { +# "key_name": "virtualLink1", +# "vl_id": "vl_id1", +# }, +# ], +# } +# ], +# +# "pnfs": [ +# { +# "pnf_id": "pnf1", +# "description": "", +# "properties": { +# "id": "pnf1", +# "vendor": "zte", +# "version": "1.1.0", +# "pnf_type": "TTGW", +# "request_reclassification": False, +# "nsh_aware": False, +# }, +# "cps": [ +# "cpd_1", "cpd_22", +# ] +# } +# ], +# +# "nested_ns": [ +# { +# "ns_id": "ns2", +# "description": "", +# "properties": { +# "id": "ns2_demo", +# "vendor": "zte", +# "version": "1.1.0", +# "name": "NSD2", +# }, +# "networks": [ +# { +# "key_name": "virtualLink1", +# "vl_id": "vl_id1", +# }, +# ], +# } +# ], +# +# "vls": [ +# { +# "vl_id": "vldId1", +# "description": "", +# "properties": { +# "name": "umac_241_control", +# "network_id": "fgdhsj434hfjdfd", +# "network_name": "umac_control", +# "vendor": "zte", +# "mtu": 1500, +# "network_type": "vlan", +# "physical_network": "phynet01", +# "segmentation_id": "30", +# "vlan_transparent": False, +# "vds_name": "vds1", +# "cidr": "192.168.199.0/24", +# "ip_version": 4, +# "gateway_ip": "192.168.199.1", +# "dhcp_enabled": False, +# "dns_nameservers": ["192.168.0.4", "192.168.0.10"], +# "start_ip": "192.168.199.2", +# "end_ip": "192.168.199.254", +# "host_routes": [ +# { +# "destination": "10.43.26.0/24", +# "nexthop": "10.41.23.1", +# }, +# ], +# "location_info": { +# "vimId": "vimid", +# "tenant": "tenantname", +# }, +# "vlan_transparent": False, +# }, +# }, +# ], +# +# "cps": [ +# { +# "cp_id": "cpd_1", +# "description": "", +# "properties": { +# "mac_address": "00:d9:00:82:11:e1", +# "ip_address": "192.168.1.21", +# "ip_range_start": "192.168.1.20", +# "ip_range_end": "192.168.1.29", +# "floating_ip_address": { +# "external_network": "extnet01", +# "ip_address": "10.43.53.23", +# }, +# "service_ip_address": "192.168.1.23", +# "order": 1, +# "bandwidth": 1000, +# "vnic_type": "normal", +# "allowed_address_pairs": [ +# { +# "ip": "192.168.1.13", +# "mac": "00:f3:43:20:a2:a3" +# }, +# ], +# "bond": "none", +# "macbond": "00:d9:00:82:11:d1", +# "sfc_encapsulation": "", +# "direction": "", +# }, +# "vl_id": "vlid1", +# "pnf_id": "pnf1", +# }, +# +# { +# "cp_id": "forwarder_brasDP_dcPort", +# "description": "", +# "properties": { +# "mac_address": "00:d9:00:82:14:e1", +# "ip_address": "192.168.1.24", +# "ip_range_start": "192.168.1.20", +# "ip_range_end": "192.168.1.29", +# "floating_ip_address": { +# "external_network": "extnet01", +# "ip_address": "10.43.53.23", +# }, +# "service_ip_address": "192.168.1.23", +# "order": 1, +# "bandwidth": 1000, +# "vnic_type": "normal", +# "allowed_address_pairs": [ +# { +# "ip": "192.168.1.13", +# "mac": "00:f3:43:20:a2:a3" +# }, +# ], +# "bond": "none", +# "macbond": "00:d9:00:82:11:d1", +# "sfc_encapsulation": "", +# "direction": "", +# }, +# "vl_id": "vlid1", +# "pnf_id": "pnf1", +# }, +# { +# "cp_id": "forwarder_brasDP_internetPort", +# "description": "", +# "properties": { +# "mac_address": "00:d9:00:82:15:e1", +# "ip_address": "192.168.1.25", +# "ip_range_start": "192.168.1.20", +# "ip_range_end": "192.168.1.29", +# "floating_ip_address": { +# "external_network": "extnet01", +# "ip_address": "10.43.53.23", +# }, +# "service_ip_address": "192.168.1.23", +# "order": 1, +# "bandwidth": 1000, +# "vnic_type": "normal", +# "allowed_address_pairs": [ +# { +# "ip": "192.168.1.13", +# "mac": "00:f3:43:20:a2:a3" +# }, +# ], +# "bond": "none", +# "macbond": "00:d9:00:82:11:d1", +# "sfc_encapsulation": "", +# "direction": "", +# }, +# "vl_id": "vlid1", +# "pnf_id": "pnf1", +# }, +# +# ], +# +# "fps": [ +# { +# "fp_id": "fpd_1", +# "description": "", +# "properties": { +# "policy": { +# "type": "ACL", +# "criteria": { +# "dest_port_range": [80, 1024], +# "source_port_range": [80, 1024], +# "ip_protocol": "tcp", +# "dest_ip_range": ["192.168.1.2", "192.168.1.100"], +# "source_ip_range": ["192.168.1.2", "192.168.1.100"], +# "dscp": 100, +# }, +# }, +# "symmetric": True, +# }, +# "forwarder_list": [ +# { +# "type": "cp", +# "node_name": "cpd_1", +# "capability": "", +# }, +# { +# "type": "cp", +# "node_name": "forwarder_brasDP_dcPort", +# "capability": "", +# }, +# { +# "type": "vnf", +# "node_name": "vnf_1", +# "capability": "forwarder1", +# }, +# { +# "type": "vnf", +# "node_name": "vnf_2", +# "capability": "forwarder2", +# }, +# { +# "type": "cp", +# "node_name": "forwarder_brasDP_dcPort", +# "capability": "", +# }, +# { +# "type": "cp", +# "node_name": "forwarder_brasDP_internetPort", +# "capability": "", +# }, +# ], +# }, +# +# { +# "fp_id": "fpd_2", +# "description": "", +# "properties": { +# "policy": { +# "type": "ACL", +# "criteria": { +# "dest_port_range": [80, 1024], +# "source_port_range": [80, 1024], +# "ip_protocol": "tcp", +# "dest_ip_range": ["192.168.1.2", "192.168.1.100"], +# "source_ip_range": ["192.168.1.2", "192.168.1.100"], +# "dscp": 100, +# }, +# }, +# "symmetric": True, +# }, +# "forwarder_list": [ +# +# { +# "type": "cp", +# "node_name": "forwarder_brasDP_internetPort", +# "capability": "", +# }, +# { +# "type": "cp", +# "node_name": "forwarder_brasDP_dcPort", +# "capability": "", +# }, +# { +# "type": "vnf", +# "node_name": "vnf_2", +# "capability": "forwarder2", +# }, +# +# ], +# }, +# ], +# +# "vnffgs": [ +# { +# "vnffg_id": "vnffg_id1", +# "description": "", +# "properties": { +# "vendor": "zte", +# "version": "1.1.2", +# "number_of_endpoints": 7, +# "dependent_virtual_link": ["vldId1"], +# "connection_point": ["CP01", "CP02"], +# "constituent_vnfs": ["vnf_id1", "vnf_id2"], +# "constituent_pnfs": ["pnf1", "pnf2"], +# }, +# "members": ["fpd_1", "fpd_2"], +# } +# ], +# +# "server_groups": [ +# { +# "group_id": "", +# "description": "", +# "properties": { +# "name": "server_group1", +# "affinity_antiaffinity": "anti-affinity", +# "scope": "host", +# }, +# "members": ["vnf1", "vnf2"], +# }, +# ], +# +# "ns_exposed": { +# "external_cps": [ +# { +# "key_name": "virtualLink1", +# "cp_id": "cp1", +# }, +# ], +# "forward_cps": [ +# { +# "key_name": "forwarder_brasDP_userPort", +# "cp_id": "cpd_1", +# }, +# { +# "key_name": "forwarder_brasDP_internetPort", +# "cp_id": "cpd_4", +# }, +# { +# "key_name": "forwarder_brasDP_dcPort", +# "cp_id": "cpd_5", +# }, +# +# ], +# }, +# +# "policies": [ +# { +# "scaling": [ +# { +# "policy_id": "id1", +# "description": "", +# "properties": { +# "policy_file": "Policies/ns1-policy.xml", +# }, +# "targets": ['pfu_vm'], +# }, +# ], +# }, +# ], +# +# "ns_flavours": [ +# { +# "flavour_id": "flavour1", +# "description": "", +# "vnf_profiles": [ +# { +# "vnf_id": "vnf1", +# "flavour_id": "flavour1", +# "instances_minimum_number": 1, +# "instances_maximum_number": 4, +# "local_affinity_antiaffinity_rule": [ +# { +# "affinity_antiaffinity": "affinity", +# "scope": "node", +# } +# ] +# }, +# ], +# "pnf_profiles": [ +# { +# "pnf_id": "pnf1", +# }, +# ], +# "vl_profiles": [ +# { +# "vl_id": "vlid1", +# "bitrate_requirements": { +# "root": 1000, +# "leaf": 100 +# }, +# "qos": { +# "maximum_latency": "1 ms", +# "maximum_jitter": "10 ms", +# "maximum_packet_loss_ratio": 0.5 +# }, +# } +# ], +# "instantiation_levels": [ +# { +# "id": "instLevel1", +# "description": "", +# "vnf_levels": [ +# { +# "vnf_id": "", +# "vnf_instantiation_level": "small", +# "instances_number": 1 +# }, +# ], +# "scale_level_id": "scaleLevel1", +# } +# ], +# "default_instantiation_level": "instLevel1", +# "scale_levels": [ +# { +# "id": "scaleLevel1", +# "order": 1, +# "vnf_levels": [ +# { +# "vnf_id": "", +# "vnf_instantiation_level": "small", +# "instances_number": 1 +# }, +# ], +# }, +# ], +# "supported_operations": ["Scale", "Heal"], +# "affinity_antiaffinity_groups": [ +# { +# "group_id": "group1Id", +# "name": "groupName", +# "affinity_antiaffinity": "affinity", +# "scope": "node", +# "members": [ +# "vnfId1", "vnfIdN", +# ], +# }, +# ], +# }, +# ], +# } diff --git a/lcm/ns/tests/sfcs/test_sfc_instance.py b/lcm/ns/tests/sfcs/test_sfc_instance.py new file mode 100644 index 00000000..f0f90648 --- /dev/null +++ b/lcm/ns/tests/sfcs/test_sfc_instance.py @@ -0,0 +1,47 @@ +# Copyright 2016 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 json +# from rest_framework import status +# from test_data import nsd_model +# from django.test import Client +# from django.test import TestCase +# from lcm.pub.database.models import FPInstModel, VNFFGInstModel +# +# +# class TestSfc(TestCase): +# def setUp(self): +# self.client = Client() +# VNFFGInstModel.objects.all().delete() +# FPInstModel.objects.all().delete() +# VNFFGInstModel(vnffgdid="vnffg_id1", vnffginstid="vnffg_inst_1", nsinstid="ns_inst_1", endpointnumber=2, +# vllist="vlinst1", cplist="cp1", vnflist="vnf1,vnf2").save() +# +# def tearDown(self): +# VNFFGInstModel.objects.all().delete() +# FPInstModel.objects.all().delete() +# +# def test_sfc_instance_success(self): +# data = { +# "nsinstanceid": "ns_inst_1", +# "context": json.dumps(nsd_model), +# "fpindex": "fpd_1", +# "sdncontrollerid": "sdnControllerId_1" +# } +# resp = self.client.post("/openoapi/nslcm/v1/ns/sfc_instance", data, format='json') +# +# vnffg = VNFFGInstModel.objects.get(vnffginstid="vnffg_inst_1") +# ret = FPInstModel.objects.get(fpinstid=resp.data["fpinstid"]) +# self.assertEqual(resp.status_code, status.HTTP_200_OK) +# self.assertEqual(vnffg.fplist, resp.data["fpinstid"]) +# self.assertIsNotNone(ret) diff --git a/lcm/ns/tests/sfcs/test_sfcdetailview.py b/lcm/ns/tests/sfcs/test_sfcdetailview.py new file mode 100644 index 00000000..d82f224d --- /dev/null +++ b/lcm/ns/tests/sfcs/test_sfcdetailview.py @@ -0,0 +1,86 @@ +# Copyright 2016 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 json +import uuid +from lcm.pub.msapi import extsys +import mock +from django.test import TestCase, Client +from rest_framework import status +from lcm.pub.msapi import sdncdriver +from lcm.pub.database.models import FPInstModel, VNFFGInstModel, NfInstModel +from lcm.pub.utils import restcall +from lcm.pub.msapi import resmgr +class TestSfcDetailViews(TestCase): + def setUp(self): + self.client = Client() + self.ns_inst_id = str(uuid.uuid4()) + self.sfc_inst_id = str(uuid.uuid4()) + self.status = "active" + self.sdn_controler_id = str(uuid.uuid4()) + sfc_id = str(uuid.uuid4()) + flow_classifiers = "flow1,flow2" + port_pair_groups = json.JSONEncoder().encode( + [{"groupid": "group1", "portpair": [str(uuid.uuid4()), str(uuid.uuid4())]}, + {"groupid": "group2", "portpair": [str(uuid.uuid4()), str(uuid.uuid4())]}]) + + + def tearDown(self): + pass + + + def test_sfc_delete_failed(self): + response = self.client.delete("/openoapi/nslcm/v1/ns/sfcs/%s" % "notExist") + expect_resp_data = {"result": 0, "detail": "sfc is not exist or has been already deleted"} + self.assertEqual(status.HTTP_202_ACCEPTED, response.status_code) + self.assertEqual(expect_resp_data, response.data) + + @mock.patch.object(extsys, "get_sdn_controller_by_id") + @mock.patch.object(sdncdriver, "delete_port_chain") + @mock.patch.object(sdncdriver, "delete_flow_classifier") + @mock.patch.object(sdncdriver, "delete_port_pair_group") + @mock.patch.object(sdncdriver, "delete_port_pair") + @mock.patch.object(resmgr, "delete_sfc") + def test_sfc_delete_success(self, mock_delete_sfc, mock_delete_port_pair, mock_delete_port_pair_group, mock_delete_flow_classifier, mock_delete_port_chain, mock_get_sdn_controller_by_id): + mock_delete_port_chain.return_value=None + mock_delete_flow_classifier.return_value=None + mock_delete_port_pair_group.return_value=None + mock_delete_port_pair.return_value=None + mock_delete_sfc.return_value=None + mock_get_sdn_controller_by_id.return_value = json.loads('{"test":"test_name","url":"url_add"}') + sfc_inst_id = "10" + + FPInstModel(fpid="1", fpinstid="10", fpname="2", nsinstid="3", vnffginstid="4", + symmetric="5", policyinfo="6", forworderpaths="7", status="8", sdncontrollerid="9", + sfcid="10", flowclassifiers="11", + portpairgroups=json.JSONEncoder().encode([{"groupid":"98","portpair":"99"}]) + ).save() + response = self.client.delete("/openoapi/nslcm/v1/ns/sfcs/%s" % sfc_inst_id) + expect_resp_data = {"result": 0, "detail": "delete sfc success"} + self.assertEqual(expect_resp_data, response.data) + + def test_sfc_get_failed(self): + sfc_inst_id="10" + response = self.client.get("/openoapi/nslcm/v1/ns/sfcs/%s" % sfc_inst_id) + self.assertEqual(status.HTTP_404_NOT_FOUND, response.status_code) + + def test_sfc_get_success(self): + sfc_inst_id ="10" + FPInstModel(fpid="1", fpinstid="10", fpname="2", nsinstid="3", vnffginstid="4", + symmetric="5", policyinfo="6", forworderpaths="7", status="8", sdncontrollerid="9", + sfcid="10", flowclassifiers="11", + portpairgroups="12").save() + response = self.client.get("/openoapi/nslcm/v1/ns/sfcs/%s" % sfc_inst_id) + expect_resp_data={'sfcName': 'xxx', 'sfcInstId': '10', 'sfcStatus': '8'} + self.assertEqual(expect_resp_data, response.data) + diff --git a/lcm/ns/tests/sfcs/tests.py b/lcm/ns/tests/sfcs/tests.py new file mode 100644 index 00000000..f93bc4c5 --- /dev/null +++ b/lcm/ns/tests/sfcs/tests.py @@ -0,0 +1,77 @@ +# Copyright 2016 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 json +import uuid + +import mock +from django.test import TestCase, Client +from rest_framework import status + +from lcm.pub.database.models import FPInstModel, VNFFGInstModel +from lcm.pub.utils import restcall + + +class TestSfcDetailViews(TestCase): + def setUp(self): + self.client = Client() + self.ns_inst_id = str(uuid.uuid4()) + self.sfc_inst_id = str(uuid.uuid4()) + self.status = "active" + self.sdn_controler_id = str(uuid.uuid4()) + sfc_id = str(uuid.uuid4()) + flow_classifiers = "flow1,flow2" + port_pair_groups = json.JSONEncoder().encode( + [{"groupid": "group1", "portpair": [str(uuid.uuid4()), str(uuid.uuid4())]}, + {"groupid": "group2", "portpair": [str(uuid.uuid4()), str(uuid.uuid4())]}]) + FPInstModel(fpid="", fpinstid=self.sfc_inst_id, nsinstid=self.ns_inst_id, vnffginstid="", policyinfo="", + status=self.status, sdncontrollerid=self.sdn_controler_id, sfcid=sfc_id, + flowclassifiers=flow_classifiers, + portpairgroups=port_pair_groups).save() + VNFFGInstModel(vnffgdid="", vnffginstid="", nsinstid=self.ns_inst_id, + fplist="test1," + self.sfc_inst_id + ",test2,test3", endpointnumber=0, cplist="", vnflist="", + vllist="", status="").save() + + def tearDown(self): + FPInstModel.objects.all().delete() + VNFFGInstModel.objects.all().delete() + + @mock.patch.object(restcall, "call_req") + def test_delete_sfc(self, mock_req_by_rest): + mock_req_by_rest.return_value = [0, '{"test":"test_name","url":"url_add"}'] + response = self.client.delete("/openoapi/nslcm/v1/ns/sfcs/%s" % self.sfc_inst_id) + self.assertEqual(status.HTTP_202_ACCEPTED, response.status_code) + expect_resp_data = {"result": 0, "detail": "delete sfc success"} + self.assertEqual(expect_resp_data, response.data) + + for vnffg_info in VNFFGInstModel.objects.filter(nsinstid=self.ns_inst_id): + self.assertEqual(vnffg_info.fplist, "test1,test2,test3") + if FPInstModel.objects.filter(fpinstid=self.sfc_inst_id): + self.fail() + + response = self.client.delete("/openoapi/nslcm/v1/ns/sfcs/%s" % "notExist") + self.assertEqual(status.HTTP_202_ACCEPTED, response.status_code) + expect_resp_data = {"result": 0, "detail": "sfc is not exist or has been already deleted"} + self.assertEqual(expect_resp_data, response.data) + + def test_query_sfc(self): + response = self.client.get("/openoapi/nslcm/v1/ns/sfcs/%s" % self.sfc_inst_id) + self.assertEqual(status.HTTP_200_OK, response.status_code) + expect_resp_data = {'sfcInstId': self.sfc_inst_id, + 'sfcStatus': self.status, + 'sfcName': "xxx"} + self.assertEqual(expect_resp_data, response.data) + + response = self.client.get("/openoapi/nslcm/v1/ns/sfcs/%s" % "notExist") + self.assertEqual(status.HTTP_404_NOT_FOUND, response.status_code) diff --git a/lcm/ns/tests/test_ns_create.py b/lcm/ns/tests/test_ns_create.py new file mode 100644 index 00000000..1182184a --- /dev/null +++ b/lcm/ns/tests/test_ns_create.py @@ -0,0 +1,39 @@ +# Copyright 2016 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 uuid + +from django.test import TestCase, Client +from rest_framework import status + +from lcm.pub.database.models import NSInstModel, NSDModel + + +class TestNsInstantiate(TestCase): + def setUp(self): + self.client = Client() + self.nsd_id = str(uuid.uuid4()) + self.ns_package_id = str(uuid.uuid4()) + NSDModel(id=self.ns_package_id, nsd_id=self.nsd_id, name='name').save() + + def tearDown(self): + NSDModel.objects.all().delete() + NSInstModel.objects.all().delete() + + def test_create_ns(self): + data = { + 'nsdid': self.nsd_id, + 'nsname': 'ns', + 'description': 'description'} + response = self.client.post("/openoapi/nslcm/v1/ns", data=data) + self.failUnlessEqual(status.HTTP_201_CREATED, response.status_code) diff --git a/lcm/ns/tests/test_ns_instant.py b/lcm/ns/tests/test_ns_instant.py new file mode 100644 index 00000000..ed20c052 --- /dev/null +++ b/lcm/ns/tests/test_ns_instant.py @@ -0,0 +1,65 @@ +# Copyright 2016 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 mock +from rest_framework import status +from django.test import TestCase +from django.test import Client + +from lcm.pub.database.models import NSInstModel +from lcm.pub.utils import restcall +from lcm.pub.utils import toscautil + + +class TestNsInstant(TestCase): + def setUp(self): + self.client = Client() + NSInstModel.objects.filter().delete() + self.context = '{"vnfs": ["a", "b"], "sfcs": ["c"], "vls": ["d", "e", "f"]}' + NSInstModel(id="123", nspackage_id="7", nsd_id="2").save() + + def tearDown(self): + pass + + """ + @mock.patch.object(restcall, 'call_req') + @mock.patch.object(toscautil, 'convert_nsd_model') + def test_ns_instant_ok(self, mock_convert_nsd_model, mock_call_req): + mock_convert_nsd_model.return_value = self.context + mock_vals = { + "/openoapi/catalog/v1/csars/7/files?relativePath=abc.yaml": + [0, '{"downloadUri":"http://test.yaml", "localPath":"./test.yaml"}', '200'], + "/openoapi/tosca/v1/indirect/plan": + [0, '{"description":"", "metadata":{}, "nodes":[]}', '200'], + "/openoapi/catalog/v1/servicetemplates/2/operations": + [0, '[{"name":"LCM", "processId":"{http://www.open-o.org/tosca/nfv/2015/12}init-16"}]', '200'], + "/openoapi/wso2bpel/v1/process/instance": + [0, '{"status": 1}', '200']} + + def side_effect(*args): + return mock_vals[args[4]] + + mock_call_req.side_effect = side_effect + + data = {'iaUrl': "", 'vnfmId': "", 'context': "{\"e\":{\"f\":\"4\"}}", 'statusUrl': "", + 'serviceTemplateId': "", 'roUrl': "", 'containerapiUrl': "", 'flavor': "", + 'nsInstanceId': "123", 'instanceId': "234", 'resourceUrl': "", 'callbackId': "", + 'additionalParamForVnf': "[{\"b\":1},{\"c\":{\"d\":\"2\"}}]", + 'additionalParamForNs': "[{\"a\":3},{\"e\":{\"f\":\"4\"}}]", 'flavorParams': ""} + resp = self.client.post("/openoapi/nslcm/v1/ns/123/instantiate", data, format='json') + self.assertEqual(resp.status_code, status.HTTP_200_OK) + """ + + def test_swagger_ok(self): + resp = self.client.get("/openoapi/nslcm/v1/swagger.json", format='json') + self.assertEqual(resp.status_code, status.HTTP_200_OK) diff --git a/lcm/ns/tests/test_ns_manual_scale.py b/lcm/ns/tests/test_ns_manual_scale.py new file mode 100644 index 00000000..cd7ccc01 --- /dev/null +++ b/lcm/ns/tests/test_ns_manual_scale.py @@ -0,0 +1,93 @@ +# 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 mock +import uuid +from rest_framework import status +from django.test import TestCase +from django.test import Client +from lcm.pub.database.models import NSDModel, NSInstModel +from lcm.pub.utils.jobutil import JobUtil, JOB_MODEL_STATUS, JOB_TYPE +from lcm.ns.const import NS_INST_STATUS +from lcm.pub.utils import restcall +from lcm.pub.utils import toscautil +from lcm.ns.ns_manual_scale import NSManualScaleService + +class TestNsManualScale(TestCase): + def setUp(self): + self.nsd_id = str(uuid.uuid4()) + self.ns_package_id = str(uuid.uuid4()) + self.ns_inst_id = str(uuid.uuid4()) + self.job_id = JobUtil.create_job("NS", JOB_TYPE.MANUAL_SCALE_VNF, self.ns_inst_id) + NSDModel(id=self.ns_package_id, nsd_id=self.nsd_id, name='name').save() + + self.client = Client() + self.context = '{"vnfs": ["a", "b"], "sfcs": ["c"], "vls": ["d", "e", "f"]}' + NSInstModel(id=self.ns_inst_id, name="abc",nspackage_id="7", nsd_id="111").save() + + def tearDown(self): + NSInstModel.objects.filter().delete() + + + """ + @mock.patch.object(restcall, 'call_req') + @mock.patch.object(toscautil, 'convert_nsd_model') + def test_ns_instant_ok(self, mock_convert_nsd_model, mock_call_req): + mock_convert_nsd_model.return_value = self.context + mock_vals = { + "/openoapi/catalog/v1/csars/7/files?relativePath=abc.yaml": + [0, '{"downloadUri":"http://test.yaml", "localPath":"./test.yaml"}', '200'], + "/openoapi/tosca/v1/indirect/plan": + [0, '{"description":"", "metadata":{}, "nodes":[]}', '200'], + "/openoapi/catalog/v1/servicetemplates/2/operations": + [0, '[{"name":"LCM", "processId":"{http://www.open-o.org/tosca/nfv/2015/12}init-16"}]', '200'], + "/openoapi/wso2bpel/v1/process/instance": + [0, '{"status": 1}', '200']} + + def side_effect(*args): + return mock_vals[args[4]] + + mock_call_req.side_effect = side_effect + + data = {'iaUrl': "", 'vnfmId': "", 'context': "{\"e\":{\"f\":\"4\"}}", 'statusUrl': "", + 'serviceTemplateId': "", 'roUrl': "", 'containerapiUrl': "", 'flavor': "", + 'nsInstanceId': "123", 'instanceId': "234", 'resourceUrl': "", 'callbackId': "", + 'additionalParamForVnf': "[{\"b\":1},{\"c\":{\"d\":\"2\"}}]", + 'additionalParamForNs': "[{\"a\":3},{\"e\":{\"f\":\"4\"}}]", 'flavorParams': ""} + resp = self.client.post("/openoapi/nslcm/v1/ns/123/instantiate", data, format='json') + self.assertEqual(resp.status_code, status.HTTP_200_OK) + """ + @mock.patch.object(NSManualScaleService, 'run') + def test_ns_manual_scale(self, mock_run): + data = { + 'nsdid': self.nsd_id, + 'nsname': 'ns', + 'description': 'description'} + response = self.client.post("/openoapi/nslcm/v1/ns/%s/scale"%self.nsd_id, data=data) + self.failUnlessEqual(status.HTTP_202_ACCEPTED, response.status_code) + + @mock.patch.object(restcall, 'call_req') + def test_ns_manual_scale_thread(self, mock_call): + + data = { + 'nsdid': self.nsd_id, + 'nsname': 'ns', + 'description': 'description'} + NSManualScaleService(self.ns_inst_id, data, self.job_id).run() + self.assertTrue(NSInstModel.objects.get(id=self.ns_inst_id).status, NS_INST_STATUS.ACTIVE) + + def test_swagger_ok(self): + resp = self.client.get("/openoapi/nslcm/v1/swagger.json", format='json') + self.assertEqual(resp.status_code, status.HTTP_200_OK) diff --git a/lcm/ns/tests/tests_ns_terminate.py b/lcm/ns/tests/tests_ns_terminate.py new file mode 100644 index 00000000..2ea1fa45 --- /dev/null +++ b/lcm/ns/tests/tests_ns_terminate.py @@ -0,0 +1,98 @@ +# Copyright 2016-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 json +import uuid + +import mock +from django.test import TestCase, Client +from rest_framework import status + +from lcm.pub.database.models import NfInstModel, NSInstModel +from lcm.pub.utils import restcall +from lcm.pub.utils.jobutil import JOB_MODEL_STATUS +from lcm.ns.ns_terminate import TerminateNsService +from lcm.pub.utils.jobutil import JobUtil, JOB_TYPE + + +class TestTerminateNsViews(TestCase): + def setUp(self): + self.client = Client() + self.ns_inst_id = '1' + self.nf_inst_id = '1' + self.vnffg_id = str(uuid.uuid4()) + self.vim_id = str(uuid.uuid4()) + self.job_id = str(uuid.uuid4()) + self.nf_uuid = '1-1-1' + self.tenant = "tenantname" + model = '{"metadata": {"vnfdId": "1","vnfdName": "PGW001","vnfProvider": "zte","vnfdVersion": "V00001",' \ + '"vnfVersion": "V5.10.20","productType": "CN","vnfType": "PGW",' \ + '"description": "PGW VNFD description","isShared":true,"vnfExtendType":"driver"}}' + NSInstModel(id=self.ns_inst_id, name="ns_name", status='null').save() + NfInstModel.objects.create(nfinstid=self.nf_inst_id, nf_name='name_1', vnf_id='1', + vnfm_inst_id='1', ns_inst_id='1-1-1,2-2-2', + max_cpu='14', max_ram='12296', max_hd='101', max_shd="20", max_net=10, + status='null', mnfinstid=self.nf_uuid, package_id='pkg1', + vnfd_model=model) + + def tearDown(self): + NSInstModel.objects.all().delete() + NfInstModel.objects.all().delete() + + @mock.patch.object(TerminateNsService, 'do_biz') + def test_terminate_vnf_url(self, mock_run): + req_data = { + "terminationType": "forceful", + "gracefulTerminationTimeout": "600"} + response = self.client.post("/openoapi/nslcm/v1/ns/%s/terminate" % self.ns_inst_id, data=req_data) + self.failUnlessEqual(status.HTTP_202_ACCEPTED, response.status_code) + + response = self.client.delete("/openoapi/nslcm/v1/ns/%s" % self.ns_inst_id) + self.failUnlessEqual(status.HTTP_204_NO_CONTENT, response.status_code) + + @mock.patch.object(restcall, "call_req") + def test_terminate_vnf(self, mock_call_req): + job_id = JobUtil.create_job("VNF", JOB_TYPE.TERMINATE_VNF, self.nf_inst_id) + + mock_vals = { + "/openoapi/nslcm/v1/ns/vls/1": + [0, json.JSONEncoder().encode({"jobId": self.job_id}), '200'], + "/openoapi/nslcm/v1/ns/sfcs/1": + [0, json.JSONEncoder().encode({"jobId": self.job_id}), '200'], + "/openoapi/nslcm/v1/ns/vnfs/1": + [0, json.JSONEncoder().encode({}), '200'], + "/openoapi/ztevmanagerdriver/v1/jobs/" + self.job_id + "&responseId=0": + [0, json.JSONEncoder().encode({"jobid": self.job_id, + "responsedescriptor": {"progress": "100", + "status": JOB_MODEL_STATUS.FINISHED, + "responseid": "3", + "statusdescription": "creating", + "errorcode": "0", + "responsehistorylist": [ + {"progress": "0", + "status": JOB_MODEL_STATUS.PROCESSING, + "responseid": "2", + "statusdescription": "creating", + "errorcode": "0"}]}}), '200']} + + def side_effect(*args): + return mock_vals[args[4]] + + mock_call_req.side_effect = side_effect + + TerminateNsService(self.nf_inst_id, "forceful", "600", job_id).start() + nsinst = NSInstModel.objects.get(id=self.ns_inst_id) + if nsinst: + self.assertTrue(1, 0) + else: + self.assertTrue(1, 1) diff --git a/lcm/ns/tests/vls/__init__.py b/lcm/ns/tests/vls/__init__.py new file mode 100644 index 00000000..5580cc3d --- /dev/null +++ b/lcm/ns/tests/vls/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2016 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/ns/tests/vls/tests.py b/lcm/ns/tests/vls/tests.py new file mode 100644 index 00000000..30062984 --- /dev/null +++ b/lcm/ns/tests/vls/tests.py @@ -0,0 +1,159 @@ +# Copyright 2016 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 json +import uuid + +import mock +from django.test import TestCase, Client +from rest_framework import status + +from lcm.pub.database.models import VLInstModel, NSInstModel, VNFFGInstModel +from lcm.pub.nfvi.vim import vimadaptor +from lcm.pub.utils import restcall + + +class TestVlViews(TestCase): + def setUp(self): + self.client = Client() + self.ns_inst_id = str(uuid.uuid4()) + self.vnffg_id = str(uuid.uuid4()) + self.vl_id_1 = 1 + self.vl_id_2 = 1 + self.vim_id = str(uuid.uuid4()) + self.tenant = "tenantname" + properties = {"network_type": "vlan", "name": "externalMNetworkName", "dhcp_enabled": False, + "location_info": {"host": True, "vimid": self.vim_id, "region": True, "tenant": self.tenant}, + "end_ip": "190.168.100.100", "gateway_ip": "190.168.100.1", "start_ip": "190.168.100.2", + "cidr": "190.168.100.0/24", "mtu": 1500, "network_name": "sub_mnet", "ip_version": 4} + self.context = { + "vls": [{"vl_id": self.vl_id_1, "description": "", "properties": properties, "route_external": False}, + {"vl_id": self.vl_id_2, "description": "", "properties": properties, "route_external": False}], + "vnffgs": [{"vnffg_id": self.vnffg_id, "description": "", + "properties": {"vendor": "zte", "version": "1.1.2", "number_of_endpoints": 7, + "dependent_virtual_link": [self.vl_id_2, self.vl_id_1], + "connection_point": ["CP01", "CP02"], + "constituent_vnfs": ["VNF1", "VNF2", "VNF3"], + "constituent_pnfs": ["pnf1", "pnf2"]}, + "members": ["forwarding_path1", "forwarding_path2"]}]} + NSInstModel(id=self.ns_inst_id, name="ns_name").save() + VNFFGInstModel(vnffgdid=self.vnffg_id, vnffginstid="", nsinstid=self.ns_inst_id, endpointnumber=0, vllist="", + cplist="", vnflist="", fplist="", status="").save() + + def tearDown(self): + VLInstModel.objects.all().delete() + NSInstModel.objects.all().delete() + VNFFGInstModel.objects.all().delete() + + @mock.patch.object(restcall, "call_req") + @mock.patch.object(vimadaptor.VimAdaptor, "create_network") + def test_create_vl(self, mock_create_network, mock_req_by_rest): + network_id = str(uuid.uuid4()) + subnetwork_id = str(uuid.uuid4()) + mock_create_network.return_value = [0, + {"status": "ACTIVE", "id": network_id, "name": "net1", + "provider:segmentation_id": 204, "provider:network_type": "vlan", + "res_type": 1, + "subnet_list": [ + {"id": subnetwork_id, "name": "subnet1", "res_type": 1}]}] + mock_req_by_rest.return_value = [0, + '{"test":"test_name","name":"vim_name","type":"type_name","url":"url_add"' + ',"userName":"user_name","password":"password","tenant":"tenant"}'] + + self.create_vl(self.vl_id_1) + self.create_vl(self.vl_id_2) + vl_from_vl_1 = VLInstModel.objects.filter(vldid=self.vl_id_1, ownerid=self.ns_inst_id) + self.assertEqual(network_id, vl_from_vl_1[0].relatednetworkid) + self.assertEqual(subnetwork_id, vl_from_vl_1[0].relatedsubnetworkid) + #self.assertEqual(self.vim_id, vl_from_vl_1[0].vimid) + self.assertEqual(self.tenant, vl_from_vl_1[0].tenant) + vl_from_vl_2 = VLInstModel.objects.filter(vldid=self.vl_id_2, ownerid=self.ns_inst_id) + self.assertEqual(VNFFGInstModel.objects.filter(vnffgdid=self.vnffg_id, nsinstid=self.ns_inst_id)[0].vllist, + vl_from_vl_2[0].vlinstanceid + "," + vl_from_vl_1[0].vlinstanceid) + + def create_vl(self, vl_id): + req_data = { + "nsInstanceId": self.ns_inst_id, + "context": json.JSONEncoder().encode(self.context), + "vlindex": vl_id} + response = self.client.post("/openoapi/nslcm/v1/ns/vls", data=req_data) + self.assertEqual(status.HTTP_201_CREATED, response.status_code) + self.assertEqual(0, response.data["result"]) + + @mock.patch.object(restcall, "call_req") + @mock.patch.object(vimadaptor.VimAdaptor, "create_network") + @mock.patch.object(uuid, "uuid4") + def test_create_network_fail_when_send_to_vim(self, mock_uuid4, mock_create_network, mock_req_by_rest): + req_data = { + "nsInstanceId": self.ns_inst_id, + "context": json.JSONEncoder().encode(self.context), + "vlindex": self.vl_id_1} + mock_uuid4.return_value = '999' + mock_req_by_rest.return_value = [0, + '{"test":"test_name","name":"vim_name","type":"type_name","url":"url_add"' + ',"userName":"user_name","password":"password","tenant":"tenant"}'] + mock_create_network.return_value = [1, (1)] + response = self.client.post("/openoapi/nslcm/v1/ns/vls", data=req_data) + retinfo = {"detail": "vl instantiation failed, detail message: Send post vl request to vim failed."} + self.assertEqual(retinfo["detail"], response.data["detail"]) + + +class TestVlDetailViews(TestCase): + def setUp(self): + self.client = Client() + self.vl_inst_id = str(uuid.uuid4()) + self.vl_name = str(uuid.uuid4()) + self.ns_inst_id = str(uuid.uuid4()) + VLInstModel(vlinstanceid=self.vl_inst_id, vldid="", vlinstancename=self.vl_name, ownertype=1, + ownerid=self.ns_inst_id, relatednetworkid="network1", relatedsubnetworkid="subnet1,subnet2", + vimid="", + tenant="").save() + VNFFGInstModel(vnffgdid="", vnffginstid="", nsinstid=self.ns_inst_id, + vllist="test1," + self.vl_inst_id + ",test2,test3", endpointnumber=0, cplist="", vnflist="", + fplist="", status="").save() + + def tearDown(self): + VLInstModel.objects.all().delete() + VNFFGInstModel.objects.all().delete() + + @mock.patch.object(restcall, "call_req") + @mock.patch.object(vimadaptor.VimAdaptor, "delete_network") + @mock.patch.object(vimadaptor.VimAdaptor, "delete_subnet") + def test_delete_vl(self, mock_delete_subnet, mock_delete_network, mock_req_by_rest): + mock_req_by_rest.return_value = [0, + '{"test":"test_name","name":"vim_name","type":"type_name","url":"url_add"' + ',"userName":"user_name","password":"password","tenant":"tenant"}'] + response = self.client.delete("/openoapi/nslcm/v1/ns/vls/%s" % self.vl_inst_id) + self.assertEqual(status.HTTP_202_ACCEPTED, response.status_code) + expect_resp_data = {"result": 0, "detail": "delete vl success"} + self.assertEqual(expect_resp_data, response.data) + + for vnffg_info in VNFFGInstModel.objects.filter(nsinstid=self.ns_inst_id): + self.assertEqual(vnffg_info.vllist, "test1,test2,test3") + if VLInstModel.objects.filter(vlinstanceid=self.vl_inst_id): + self.fail() + + response = self.client.delete("/openoapi/nslcm/v1/ns/vls/%s" % "notExist") + self.assertEqual(status.HTTP_202_ACCEPTED, response.status_code) + expect_resp_data = {"result": 0, "detail": "vl is not exist or has been already deleted"} + self.assertEqual(expect_resp_data, response.data) + + def test_query_vl(self): + response = self.client.get("/openoapi/nslcm/v1/ns/vls/%s" % self.vl_inst_id) + self.assertEqual(status.HTTP_200_OK, response.status_code) + expect_resp_data = {'vlId': self.vl_inst_id, 'vlName': self.vl_name, 'vlStatus': "active"} + self.assertEqual(expect_resp_data, response.data) + + response = self.client.get("/openoapi/nslcm/v1/ns/vls/%s" % "notExist") + self.assertEqual(status.HTTP_404_NOT_FOUND, response.status_code) diff --git a/lcm/ns/tests/vnfs/__init__.py b/lcm/ns/tests/vnfs/__init__.py new file mode 100644 index 00000000..5580cc3d --- /dev/null +++ b/lcm/ns/tests/vnfs/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2016 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/ns/tests/vnfs/tests.py b/lcm/ns/tests/vnfs/tests.py new file mode 100644 index 00000000..ffd0eea3 --- /dev/null +++ b/lcm/ns/tests/vnfs/tests.py @@ -0,0 +1,637 @@ +# Copyright 2016-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 json +import uuid + +import mock +from django.test import TestCase, Client +from rest_framework import status + +from lcm.ns.vnfs import create_vnfs +from lcm.ns.vnfs.const import VNF_STATUS +from lcm.ns.vnfs.create_vnfs import CreateVnfs +from lcm.pub.database.models import NfInstModel, JobModel, NfPackageModel, NSInstModel +from lcm.pub.utils import restcall +from lcm.pub.utils.jobutil import JOB_MODEL_STATUS +from lcm.pub.utils.timeutil import now_time +from lcm.pub.utils.values import ignore_case_get +from lcm.ns.vnfs.terminate_nfs import TerminateVnfs +from lcm.ns.vnfs.scale_vnfs import NFManualScaleService +from lcm.pub.utils.jobutil import JobUtil, JOB_TYPE + + +class TestGetVnfViews(TestCase): + def setUp(self): + self.client = Client() + self.nf_inst_id = str(uuid.uuid4()) + NfInstModel(nfinstid=self.nf_inst_id, nf_name='vnf1', vnfm_inst_id='1', vnf_id='vnf_id1', + status=VNF_STATUS.ACTIVE, create_time=now_time(), lastuptime=now_time()).save() + + def tearDown(self): + NfInstModel.objects.all().delete() + + def test_get_vnf(self): + response = self.client.get("/openoapi/nslcm/v1/ns/vnfs/%s" % self.nf_inst_id) + self.failUnlessEqual(status.HTTP_200_OK, response.status_code) + context = json.loads(response.content) + self.failUnlessEqual(self.nf_inst_id, context['vnfInstId']) + + +class TestCreateVnfViews(TestCase): + def setUp(self): + self.ns_inst_id = str(uuid.uuid4()) + self.job_id = str(uuid.uuid4()) + self.data = { + 'nsInstanceId': self.ns_inst_id, + 'additionalParamForNs': {"inputs": json.dumps({})}, + 'additionalParamForVnf': [{ + 'vnfprofileid': 'VBras', + 'additionalparam': { + 'inputs': json.dumps({'vnf_param1': '11', 'vnf_param2': '22'}), + 'vnfminstanceid': "1"}}], + 'vnfIndex': '1'} + self.client = Client() + NfPackageModel(uuid=str(uuid.uuid4()), nfpackageid='package_id1', vnfdid='zte_vbras', vendor='zte', + vnfdversion='1.0.0', vnfversion='1.0.0', vnfdmodel=json.dumps(vnfd_model_dict)).save() + NSInstModel(id=self.ns_inst_id, name='ns', nspackage_id='1', nsd_id='nsd_id', description='description', + status='instantiating', nsd_model=json.dumps(nsd_model_dict), create_time=now_time(), + lastuptime=now_time()).save() + + def tearDown(self): + NfInstModel.objects.all().delete() + JobModel.objects.all().delete() + + @mock.patch.object(CreateVnfs, 'run') + def test_create_vnf(self, mock_run): + response = self.client.post("/openoapi/nslcm/v1/ns/vnfs", data=self.data) + self.failUnlessEqual(status.HTTP_202_ACCEPTED, response.status_code) + context = json.loads(response.content) + self.assertTrue(NfInstModel.objects.filter(nfinstid=context['vnfInstId']).exists()) + + @mock.patch.object(restcall, 'call_req') + def test_create_vnf_thread(self, mock_call_req): + mock_vals = { + "/openoapi/ztevmanagerdriver/v1/1/vnfs": + [0, json.JSONEncoder().encode({"jobId": self.job_id, "vnfInstanceId": 3}), '200'], + "/openoapi/extsys/v1/vnfms/1": + [0, json.JSONEncoder().encode({"name": 'vnfm1'}), '200'], + "/openoapi/resmgr/v1/vnf": + [0, json.JSONEncoder().encode({}), '200'], + "/openoapi/resmgr/v1/vnfinfo": + [0, json.JSONEncoder().encode({}), '200'], + "/openoapi/ztevmanagerdriver/v1/jobs/" + self.job_id + "&responseId=0": + [0, json.JSONEncoder().encode({"jobid": self.job_id, + "responsedescriptor": {"progress": "100", + "status": JOB_MODEL_STATUS.FINISHED, + "responseid": "3", + "statusdescription": "creating", + "errorcode": "0", + "responsehistorylist": [ + {"progress": "0", + "status": JOB_MODEL_STATUS.PROCESSING, + "responseid": "2", + "statusdescription": "creating", + "errorcode": "0"}]}}), '200']} + + def side_effect(*args): + return mock_vals[args[4]] + mock_call_req.side_effect = side_effect + data = {'ns_instance_id': ignore_case_get(self.data, 'nsInstanceId'), + 'additional_param_for_ns': ignore_case_get(self.data, 'additionalParamForNs'), + 'additional_param_for_vnf': ignore_case_get(self.data, 'additionalParamForVnf'), + 'vnf_index': ignore_case_get(self.data, 'vnfIndex')} + nf_inst_id, job_id = create_vnfs.prepare_create_params() + CreateVnfs(data, nf_inst_id, job_id).run() + self.assertTrue(NfInstModel.objects.get(nfinstid=nf_inst_id).status, VNF_STATUS.ACTIVE) + + +class TestTerminateVnfViews(TestCase): + def setUp(self): + self.client = Client() + self.ns_inst_id = str(uuid.uuid4()) + self.nf_inst_id = '1' + self.vnffg_id = str(uuid.uuid4()) + self.vim_id = str(uuid.uuid4()) + self.job_id = str(uuid.uuid4()) + self.nf_uuid = '111' + self.tenant = "tenantname" + NSInstModel.objects.all().delete() + NfInstModel.objects.all().delete() + NSInstModel(id=self.ns_inst_id, name="ns_name").save() + NfInstModel.objects.create(nfinstid=self.nf_inst_id, nf_name='name_1', vnf_id='1', + vnfm_inst_id='1', ns_inst_id='111,2-2-2', + max_cpu='14', max_ram='12296', max_hd='101', max_shd="20", max_net=10, + status='active', mnfinstid=self.nf_uuid, package_id='pkg1', + vnfd_model='{"metadata": {"vnfdId": "1","vnfdName": "PGW001",' + '"vnfProvider": "zte","vnfdVersion": "V00001","vnfVersion": "V5.10.20",' + '"productType": "CN","vnfType": "PGW",' + '"description": "PGW VNFD description",' + '"isShared":true,"vnfExtendType":"driver"}}') + + def tearDown(self): + NSInstModel.objects.all().delete() + NfInstModel.objects.all().delete() + + @mock.patch.object(TerminateVnfs, 'run') + def test_terminate_vnf_url(self, mock_run): + req_data = { + "terminationType": "forceful", + "gracefulTerminationTimeout": "600"} + + response = self.client.post("/openoapi/nslcm/v1/ns/vnfs/%s" % self.nf_inst_id, data=req_data) + self.failUnlessEqual(status.HTTP_201_CREATED, response.status_code) + + @mock.patch.object(restcall, "call_req") + def test_terminate_vnf(self, mock_call_req): + job_id = JobUtil.create_job("VNF", JOB_TYPE.TERMINATE_VNF, self.nf_inst_id) + + nfinst = NfInstModel.objects.filter(nfinstid=self.nf_inst_id) + if nfinst: + self.failUnlessEqual(1, 1) + else: + self.failUnlessEqual(1, 0) + + mock_vals = { + "/openoapi/ztevmanagerdriver/v1/1/vnfs/111/terminate": + [0, json.JSONEncoder().encode({"jobId": job_id}), '200'], + "/openoapi/extsys/v1/vnfms/1": + [0, json.JSONEncoder().encode({"name": 'vnfm1', "type": 'ztevmanagerdriver'}), '200'], + "/openoapi/resmgr/v1/vnf/1": + [0, json.JSONEncoder().encode({"jobId": job_id}), '200'], + "/openoapi/ztevmanagerdriver/v1/1/jobs/" + job_id + "?responseId=0": + [0, json.JSONEncoder().encode({"jobId": job_id, + "responsedescriptor": {"progress": "100", + "status": JOB_MODEL_STATUS.FINISHED, + "responseid": "3", + "statusdescription": "creating", + "errorcode": "0", + "responsehistorylist": [ + {"progress": "0", + "status": JOB_MODEL_STATUS.PROCESSING, + "responseid": "2", + "statusdescription": "creating", + "errorcode": "0"}]}}), '200']} + + req_data = { + "terminationType": "forceful", + "gracefulTerminationTimeout": "600"} + + def side_effect(*args): + return mock_vals[args[4]] + mock_call_req.side_effect = side_effect + + TerminateVnfs(req_data, self.nf_inst_id, job_id).run() + nfinst = NfInstModel.objects.filter(nfinstid=self.nf_inst_id) + if nfinst: + self.failUnlessEqual(1, 0) + else: + self.failUnlessEqual(1, 1) + +class TestScaleVnfViews(TestCase): + def setUp(self): + self.client = Client() + self.ns_inst_id = str(uuid.uuid4()) + self.nf_inst_id = str(uuid.uuid4()) + self.vnffg_id = str(uuid.uuid4()) + self.vim_id = str(uuid.uuid4()) + self.job_id = str(uuid.uuid4()) + self.nf_uuid = '111' + self.tenant = "tenantname" + NSInstModel(id=self.ns_inst_id, name="ns_name").save() + NfInstModel.objects.create(nfinstid=self.nf_inst_id, nf_name='name_1', vnf_id='1', + vnfm_inst_id='1', ns_inst_id='111,2-2-2', + max_cpu='14', max_ram='12296', max_hd='101', max_shd="20", max_net=10, + status='active', mnfinstid=self.nf_uuid, package_id='pkg1', + vnfd_model='{"metadata": {"vnfdId": "1","vnfdName": "PGW001",' + '"vnfProvider": "zte","vnfdVersion": "V00001","vnfVersion": "V5.10.20",' + '"productType": "CN","vnfType": "PGW",' + '"description": "PGW VNFD description",' + '"isShared":true,"vnfExtendType":"driver"}}') + + def tearDown(self): + NSInstModel.objects.all().delete() + NfInstModel.objects.all().delete() + + @mock.patch.object(restcall, "call_req") + def test_scale_vnf(self, mock_call_req): + job_id = JobUtil.create_job("VNF", JOB_TYPE.TERMINATE_VNF, self.nf_inst_id) + + vnfd_info = { + "vnf_flavours":[ + { + "flavour_id":"flavour1", + "description":"", + "vdu_profiles":[ + { + "vdu_id":"vdu1Id", + "instances_minimum_number": 1, + "instances_maximum_number": 4, + "local_affinity_antiaffinity_rule":[ + { + "affinity_antiaffinity":"affinity", + "scope":"node", + } + ] + } + ], + "scaling_aspects":[ + { + "id": "demo_aspect", + "name": "demo_aspect", + "description": "demo_aspect", + "associated_group": "elementGroup1", + "max_scale_level": 5 + } + ] + } + ], + "element_groups": [ + { + "group_id": "elementGroup1", + "description": "", + "properties":{ + "name": "elementGroup1", + }, + "members": ["gsu_vm","pfu_vm"], + } + ] + } + + req_data = { + "scaleVnfData": [ + { + "type":"SCALE_OUT", + "aspectId":"demo_aspect1", + "numberOfSteps":1, + "additionalParam":vnfd_info + }, + { + "type":"SCALE_OUT", + "aspectId":"demo_aspect2", + "numberOfSteps":1, + "additionalParam":vnfd_info + } + ] + } + + + mock_vals = { + "/openoapi/ztevmanagerdriver/v1/1/vnfs/111/terminate": + [0, json.JSONEncoder().encode({"jobId": job_id}), '200'], + "/openoapi/ztevmanagerdriver/v1/1/vnfs/111/terminate": + [0, json.JSONEncoder().encode({"jobId": job_id}), '200'] + } + NFManualScaleService(self.nf_inst_id, req_data).run() + nsIns = NfInstModel.objects.filter(nfinstid=self.nf_inst_id) + if nsIns: + self.failUnlessEqual(1, 1) + else: + self.failUnlessEqual(1, 0) + +vnfd_model_dict = { + 'local_storages': [], + 'vdus': [ + { + 'volumn_storages': [], + 'nfv_compute': { + 'mem_size': '', + 'num_cpus': u'2'}, + 'local_storages': [], + 'vdu_id': u'vdu_omm.001', + 'image_file': u'opencos_sss_omm_img_release_20150723-1-disk1', + 'dependencies': [], + 'vls': [], + 'cps': [], + 'properties': { + 'key_vdu': '', + 'support_scaling': False, + 'vdu_type': '', + 'name': '', + 'storage_policy': '', + 'location_info': { + 'vimId': '', + 'availability_zone': '', + 'region': '', + 'dc': '', + 'host': '', + 'tenant': ''}, + 'inject_data_list': [], + 'watchdog': { + 'action': '', + 'enabledelay': ''}, + 'local_affinity_antiaffinity_rule': {}, + 'template_id': u'omm.001', + 'manual_scale_select_vim': False}, + 'description': u'singleommvm'}, + { + 'volumn_storages': [], + 'nfv_compute': { + 'mem_size': '', + 'num_cpus': u'4'}, + 'local_storages': [], + 'vdu_id': u'vdu_1', + 'image_file': u'sss', + 'dependencies': [], + 'vls': [], + 'cps': [], + 'properties': { + 'key_vdu': '', + 'support_scaling': False, + 'vdu_type': '', + 'name': '', + 'storage_policy': '', + 'location_info': { + 'vimId': '', + 'availability_zone': '', + 'region': '', + 'dc': '', + 'host': '', + 'tenant': ''}, + 'inject_data_list': [], + 'watchdog': { + 'action': '', + 'enabledelay': ''}, + 'local_affinity_antiaffinity_rule': {}, + 'template_id': u'1', + 'manual_scale_select_vim': False}, + 'description': u'ompvm'}, + { + 'volumn_storages': [], + 'nfv_compute': { + 'mem_size': '', + 'num_cpus': u'14'}, + 'local_storages': [], + 'vdu_id': u'vdu_2', + 'image_file': u'sss', + 'dependencies': [], + 'vls': [], + 'cps': [], + 'properties': { + 'key_vdu': '', + 'support_scaling': False, + 'vdu_type': '', + 'name': '', + 'storage_policy': '', + 'location_info': { + 'vimId': '', + 'availability_zone': '', + 'region': '', + 'dc': '', + 'host': '', + 'tenant': ''}, + 'inject_data_list': [], + 'watchdog': { + 'action': '', + 'enabledelay': ''}, + 'local_affinity_antiaffinity_rule': {}, + 'template_id': u'2', + 'manual_scale_select_vim': False}, + 'description': u'ompvm'}, + { + 'volumn_storages': [], + 'nfv_compute': { + 'mem_size': '', + 'num_cpus': u'14'}, + 'local_storages': [], + 'vdu_id': u'vdu_3', + 'image_file': u'sss', + 'dependencies': [], + 'vls': [], + 'cps': [], + 'properties': { + 'key_vdu': '', + 'support_scaling': False, + 'vdu_type': '', + 'name': '', + 'storage_policy': '', + 'location_info': { + 'vimId': '', + 'availability_zone': '', + 'region': '', + 'dc': '', + 'host': '', + 'tenant': ''}, + 'inject_data_list': [], + 'watchdog': { + 'action': '', + 'enabledelay': ''}, + 'local_affinity_antiaffinity_rule': {}, + 'template_id': u'3', + 'manual_scale_select_vim': False}, + 'description': u'ompvm'}, + { + 'volumn_storages': [], + 'nfv_compute': { + 'mem_size': '', + 'num_cpus': u'4'}, + 'local_storages': [], + 'vdu_id': u'vdu_10', + 'image_file': u'sss', + 'dependencies': [], + 'vls': [], + 'cps': [], + 'properties': { + 'key_vdu': '', + 'support_scaling': False, + 'vdu_type': '', + 'name': '', + 'storage_policy': '', + 'location_info': { + 'vimId': '', + 'availability_zone': '', + 'region': '', + 'dc': '', + 'host': '', + 'tenant': ''}, + 'inject_data_list': [], + 'watchdog': { + 'action': '', + 'enabledelay': ''}, + 'local_affinity_antiaffinity_rule': {}, + 'template_id': u'10', + 'manual_scale_select_vim': False}, + 'description': u'ppvm'}, + { + 'volumn_storages': [], + 'nfv_compute': { + 'mem_size': '', + 'num_cpus': u'14'}, + 'local_storages': [], + 'vdu_id': u'vdu_11', + 'image_file': u'sss', + 'dependencies': [], + 'vls': [], + 'cps': [], + 'properties': { + 'key_vdu': '', + 'support_scaling': False, + 'vdu_type': '', + 'name': '', + 'storage_policy': '', + 'location_info': { + 'vimId': '', + 'availability_zone': '', + 'region': '', + 'dc': '', + 'host': '', + 'tenant': ''}, + 'inject_data_list': [], + 'watchdog': { + 'action': '', + 'enabledelay': ''}, + 'local_affinity_antiaffinity_rule': {}, + 'template_id': u'11', + 'manual_scale_select_vim': False}, + 'description': u'ppvm'}, + { + 'volumn_storages': [], + 'nfv_compute': { + 'mem_size': '', + 'num_cpus': u'14'}, + 'local_storages': [], + 'vdu_id': u'vdu_12', + 'image_file': u'sss', + 'dependencies': [], + 'vls': [], + 'cps': [], + 'properties': { + 'key_vdu': '', + 'support_scaling': False, + 'vdu_type': '', + 'name': '', + 'storage_policy': '', + 'location_info': { + 'vimId': '', + 'availability_zone': '', + 'region': '', + 'dc': '', + 'host': '', + 'tenant': ''}, + 'inject_data_list': [], + 'watchdog': { + 'action': '', + 'enabledelay': ''}, + 'local_affinity_antiaffinity_rule': {}, + 'template_id': u'12', + 'manual_scale_select_vim': False}, + 'description': u'ppvm'}], + 'volumn_storages': [], + 'policies': { + 'scaling': { + 'targets': {}, + 'policy_id': u'policy_scale_sss-vnf-template', + 'properties': { + 'policy_file': '*-vnfd.zip/*-vnf-policy.xml'}, + 'description': ''}}, + 'image_files': [ + { + 'description': '', + 'properties': { + 'name': u'opencos_sss_omm_img_release_20150723-1-disk1.vmdk', + 'checksum': '', + 'disk_format': u'VMDK', + 'file_url': u'./zte-cn-sss-main-image/OMM/opencos_sss_omm_img_release_20150723-1-disk1.vmdk', + 'container_type': 'vm', + 'version': '', + 'hypervisor_type': 'kvm'}, + 'image_file_id': u'opencos_sss_omm_img_release_20150723-1-disk1'}, + { + 'description': '', + 'properties': { + 'name': u'sss.vmdk', + 'checksum': '', + 'disk_format': u'VMDK', + 'file_url': u'./zte-cn-sss-main-image/NE/sss.vmdk', + 'container_type': 'vm', + 'version': '', + 'hypervisor_type': 'kvm'}, + 'image_file_id': u'sss'}], + 'vls': [], + 'cps': [], + 'metadata': { + 'vendor': u'zte', + 'is_shared': False, + 'description': '', + 'domain_type': u'CN', + 'version': u'v4.14.10', + 'vmnumber_overquota_alarm': False, + 'cross_dc': False, + 'vnf_type': u'SSS', + 'vnfd_version': u'V00000001', + 'id': u'sss-vnf-template', + 'name': u'sss-vnf-template'}} + +nsd_model_dict = { + "vnffgs": [], + "inputs": { + "externalDataNetworkName": { + "default": "", + "type": "string", + "description": ""}}, + "pnfs": [], + "fps": [], + "server_groups": [], + "ns_flavours": [], + "vnfs": [ + { + "dependency": [], + "properties": { + "plugin_info": "vbrasplugin_1.0", + "vendor": "zte", + "is_shared": "False", + "request_reclassification": "False", + "vnfd_version": "1.0.0", + "version": "1.0", + "nsh_aware": "True", + "cross_dc": "False", + "externalDataNetworkName": { + "get_input": "externalDataNetworkName"}, + "id": "zte_vbras", + "name": "vbras"}, + "vnf_id": "VBras", + "networks": [], + "description": ""}], + "ns_exposed": { + "external_cps": [], + "forward_cps": []}, + "vls": [ + { + "vl_id": "ext_mnet_network", + "description": "", + "properties": { + "network_type": "vlan", + "name": "externalMNetworkName", + "dhcp_enabled": False, + "location_info": { + "host": True, + "vimid": 2, + "region": True, + "tenant": "admin", + "dc": ""}, + "end_ip": "190.168.100.100", + "gateway_ip": "190.168.100.1", + "start_ip": "190.168.100.2", + "cidr": "190.168.100.0/24", + "mtu": 1500, + "network_name": "sub_mnet", + "ip_version": 4}}], + "cps": [], + "policies": [], + "metadata": { + "invariant_id": "vbras_ns", + "description": "vbras_ns", + "version": 1, + "vendor": "zte", + "id": "vbras_ns", + "name": "vbras_ns"}} diff --git a/lcm/ns/tests/vnfs/verify_test.py b/lcm/ns/tests/vnfs/verify_test.py new file mode 100644 index 00000000..59cdbea9 --- /dev/null +++ b/lcm/ns/tests/vnfs/verify_test.py @@ -0,0 +1,29 @@ +# 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.test import TestCase, Client +from rest_framework import status + +class TestGetVnfViews(TestCase): + def setUp(self): + self.client = Client() + self.data = {"PackageID":"1223334"} + pass + + def tearDown(self): + pass + + def test_verify_vnfs(self): + response = self.client.post("/openoapi/nslcm/v1/vnfonboarding", data=self.data) + self.failUnlessEqual(status.HTTP_202_ACCEPTED, response.status_code)
\ No newline at end of file diff --git a/lcm/ns/urls.py b/lcm/ns/urls.py new file mode 100644 index 00000000..1efd963e --- /dev/null +++ b/lcm/ns/urls.py @@ -0,0 +1,34 @@ +# Copyright 2016-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 patterns, url +from rest_framework.urlpatterns import format_suffix_patterns + +from lcm.ns.views import CreateNSView, NSInstView, TerminateNSView, NSDetailView, SwaggerJsonView, NSInstPostDealView, \ + NSManualScaleView + +urlpatterns = patterns('', + url(r'^openoapi/nslcm/v1/ns$', CreateNSView.as_view()), + url(r'^openoapi/nslcm/v1/swagger.json$', SwaggerJsonView.as_view()), + url(r'^openoapi/nslcm/v1/ns/(?P<ns_instance_id>[0-9a-zA-Z_-]+)/instantiate$', + NSInstView.as_view()), + url(r'^openoapi/nslcm/v1/ns/(?P<ns_instance_id>[0-9a-zA-Z_-]+)/terminate$', + TerminateNSView.as_view()), + url(r'^openoapi/nslcm/v1/ns/(?P<ns_instance_id>[0-9a-zA-Z_-]+)$', NSDetailView.as_view()), + url(r'^openoapi/nslcm/v1/ns/(?P<ns_instance_id>[0-9a-zA-Z_-]+)/postdeal$', + NSInstPostDealView.as_view()), + url(r'^openoapi/nslcm/v1/ns/(?P<ns_instance_id>[0-9a-zA-Z_-]+)/scale$', + NSManualScaleView.as_view()), + ) + +urlpatterns = format_suffix_patterns(urlpatterns) diff --git a/lcm/ns/views.py b/lcm/ns/views.py new file mode 100644 index 00000000..ffe35a49 --- /dev/null +++ b/lcm/ns/views.py @@ -0,0 +1,160 @@ +# Copyright 2016-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 json +import logging +import os +import traceback + +from rest_framework import status +from rest_framework.response import Response +from rest_framework.views import APIView + +from lcm.ns.ns_create import CreateNSService +from lcm.ns.ns_get import GetNSInfoService +from lcm.ns.ns_instant import InstantNSService +from lcm.ns.ns_manual_scale import NSManualScaleService +from lcm.ns.ns_terminate import TerminateNsService, DeleteNsService +from lcm.pub.database.models import NSInstModel, ServiceBaseInfoModel +from lcm.pub.utils.jobutil import JobUtil, JOB_TYPE +from lcm.pub.utils.values import ignore_case_get +from lcm.pub.utils.restcall import req_by_msb +from lcm.pub.exceptions import NSLCMException + +logger = logging.getLogger(__name__) + + +class CreateNSView(APIView): + def get(self, request): + logger.debug("CreateNSView::get") + ret = GetNSInfoService().get_ns_info() + logger.debug("CreateNSView::get::ret=%s", ret) + return Response(data=ret, status=status.HTTP_200_OK) + + def post(self, request): + logger.debug("Enter CreateNS: %s", request.data) + nsd_id = ignore_case_get(request.data, 'nsdId') + ns_name = ignore_case_get(request.data, 'nsName') + description = ignore_case_get(request.data, 'description') + try: + ns_inst_id = CreateNSService(nsd_id, ns_name, description).do_biz() + except Exception as e: + logger.error("Exception in CreateNS: %s", e.message) + return Response(data={'error': e.message}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + logger.debug("CreateNSView::post::ret={'nsInstanceId':%s}", ns_inst_id) + return Response(data={'nsInstanceId': ns_inst_id}, status=status.HTTP_201_CREATED) + + +class NSInstView(APIView): + def post(self, request, ns_instance_id): + ack = InstantNSService(ns_instance_id, request.data).do_biz() + logger.debug("Leave NSInstView::post::ack=%s", ack) + return Response(data=ack['data'], status=ack['status']) + + +class TerminateNSView(APIView): + def post(self, request, ns_instance_id): + logger.debug("Enter TerminateNSView::post %s", request.data) + termination_type = ignore_case_get(request.data, 'terminationType') + graceful_termination_timeout = ignore_case_get(request.data, 'gracefulTerminationTimeout') + job_id = JobUtil.create_job("VNF", JOB_TYPE.TERMINATE_VNF, ns_instance_id) + try: + TerminateNsService(ns_instance_id, termination_type, graceful_termination_timeout, job_id).start() + except Exception as e: + logger.error("Exception in CreateNS: %s", e.message) + return Response(data={'error': e.message}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + ret = {'jobId': job_id} + logger.debug("Leave TerminateNSView::post ret=%s", ret) + return Response(data=ret, status=status.HTTP_202_ACCEPTED) + + +class NSDetailView(APIView): + def get(self, request, ns_instance_id): + logger.debug("Enter NSDetailView::get ns(%s)", ns_instance_id) + ret = GetNSInfoService(ns_instance_id).get_ns_info() + if not ret: + return Response(status=status.HTTP_404_NOT_FOUND) + logger.debug("Leave NSDetailView::get::ret=%s", ret) + return Response(data=ret, status=status.HTTP_200_OK) + + def delete(self, request, ns_instance_id): + logger.debug("Enter NSDetailView::delete ns(%s)", ns_instance_id) + DeleteNsService(ns_instance_id).do_biz() + return Response(data={}, status=status.HTTP_204_NO_CONTENT) + + +class SwaggerJsonView(APIView): + def get(self, request): + json_file = os.path.join(os.path.dirname(__file__), 'swagger.json') + f = open(json_file) + json_data = json.JSONDecoder().decode(f.read()) + f.close() + return Response(json_data) + + +class NSInstPostDealView(APIView): + + + def post(self, request, ns_instance_id): + logger.debug("Enter NSInstPostDealView::post %s, %s", request.data, ns_instance_id) + ns_post_status = ignore_case_get(request.data, 'status') + ns_status = 'ACTIVE' if ns_post_status == 'true' else 'FAILED' + ns_opr_status = 'success' if ns_post_status == 'true' else 'failed' + try: + NSInstModel.objects.filter(id=ns_instance_id).update(status=ns_status) + ServiceBaseInfoModel.objects.filter(service_id=ns_instance_id).update( + active_status=ns_status, status=ns_opr_status) + nsd_info = NSInstModel.objects.filter(id=ns_instance_id) + nsd_id = nsd_info[0].nsd_id + nsd_model = json.loads(nsd_info[0].nsd_model) + if "policies" in nsd_model and nsd_model["policies"]: + policy = nsd_model["policies"][0] + if "properties" in policy and policy["properties"]: + file_url = ignore_case_get(policy["properties"][0], "drl_file_url") + else: + file_url = "" + self.send_policy_request(ns_instance_id, nsd_id, file_url) + except: + logger.error(traceback.format_exc()) + return Response(data={'error': 'Failed to update status of NS(%s)' % ns_instance_id}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR) + logger.debug("*****NS INST %s, %s******", ns_status, ns_opr_status) + return Response(data={'success': 'Update status of NS(%s) to %s' % (ns_instance_id, ns_status)}, + status=status.HTTP_202_ACCEPTED) + + def send_policy_request(self,ns_instance_id, nsd_id, file_url): + input_data = { + "nsid": ns_instance_id, + "nsdid": nsd_id, + "fileUri":file_url + } + req_param = json.JSONEncoder().encode(input_data) + policy_engine_url = 'openoapi/polengine/v1/policyinfo' + ret = req_by_msb(policy_engine_url, "POST", req_param) + if ret[0] != 0: + logger.error("Failed to send ns policy req") + #raise NSLCMException('Failed to send ns policy req)') + + +class NSManualScaleView(APIView): + def post(self, request, ns_instance_id): + logger.debug("Enter NSManualScaleView::post %s, %s", request.data, ns_instance_id) + job_id = JobUtil.create_job("NS", JOB_TYPE.MANUAL_SCALE_VNF, ns_instance_id) + try: + NSManualScaleService(ns_instance_id, request.data, job_id).start() + except Exception as e: + logger.error(traceback.format_exc()) + JobUtil.add_job_status(job_id, 255, 'NS scale failed: %s' % e.message) + return Response(data={'error': 'NS scale failed: %s' % ns_instance_id}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR) + return Response(data={'jobId': job_id}, status=status.HTTP_202_ACCEPTED)
\ No newline at end of file diff --git a/lcm/ns/vls/__init__.py b/lcm/ns/vls/__init__.py new file mode 100644 index 00000000..5580cc3d --- /dev/null +++ b/lcm/ns/vls/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2016 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/ns/vls/create_vls.py b/lcm/ns/vls/create_vls.py new file mode 100644 index 00000000..0065ff68 --- /dev/null +++ b/lcm/ns/vls/create_vls.py @@ -0,0 +1,181 @@ +# Copyright 2016-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 json +import logging +import traceback +import uuid + +from lcm.ns.const import OWNER_TYPE +from lcm.pub.database.models import VLInstModel, NSInstModel, VNFFGInstModel +from lcm.pub.exceptions import NSLCMException +from lcm.pub.msapi import extsys, resmgr +from lcm.pub.nfvi.vim import const +from lcm.pub.nfvi.vim import vimadaptor +from lcm.pub.utils.values import ignore_case_get + +logger = logging.getLogger(__name__) + + +class CreateVls(object): + def __init__(self, data): + self.owner_id = ignore_case_get(data, "nsInstanceId") + self.index = int(float(ignore_case_get(data, "vlIndex"))) + self.context = ignore_case_get(data, "context") + self.additionalParam = ignore_case_get(data, "additionalParamForNs") + self.vl_inst_id = str(uuid.uuid4()) + self.owner_type = OWNER_TYPE.NS + self.vld_id = "" + self.vl_properties = "" + self.vl_inst_name = "" + self.related_network_id = "" + self.related_subnetwork_id = "" + self.vim_id = "" + self.vim_name = "" + self.tenant = "" + self.description = "" + self.route_external = "" + self.ns_name = "" + + def do(self): + try: + self.get_data() + self.create_vl_to_vim() + self.create_vl_to_resmgr() + self.save_vl_to_db() + return {"result": 0, "detail": "instantiation vl success", "vlId": self.vl_inst_id} + except NSLCMException as e: + return self.exception_handle(e) + except Exception as e: + logger.error(traceback.format_exc()) + return self.exception_handle(e) + + def exception_handle(self, e): + detail = "vl instantiation failed, detail message: %s" % e.message + logger.error(detail) + return {"result": 1, "detail": detail, "vlId": self.vl_inst_id} + + def get_data(self): + if isinstance(self.context, (unicode, str)): + self.context = json.JSONDecoder().decode(self.context) + vl_info = self.get_vl_info(ignore_case_get(self.context, "vls")) + self.vld_id = ignore_case_get(vl_info, "vl_id") + self.description = ignore_case_get(vl_info, "description") + self.vl_properties = ignore_case_get(vl_info, "properties") + self.vl_inst_name = ignore_case_get(self.vl_properties, "name") + self.route_external = ignore_case_get(vl_info, "route_external") + ns_info = NSInstModel.objects.filter(id=self.owner_id) + self.ns_name = ns_info[0].name if ns_info else "" + + def get_vl_info(self, vl_all_info): + return vl_all_info[self.index - 1] + + def create_vl_to_vim(self): + self.vim_id = self.vl_properties["location_info"]["vimid"] + if not self.vim_id: + self.vim_id = ignore_case_get(self.additionalParam, "location") + self.tenant = ignore_case_get(self.vl_properties["location_info"], "tenant") + network_data = { + "tenant": self.tenant, + "network_name": self.vl_properties.get("network_name", ""), + "shared": const.SHARED_NET, + "network_type": self.vl_properties.get("network_type", ""), + "segmentation_id": self.vl_properties.get("segmentation_id", ""), + "physical_network": self.vl_properties.get("physical_network", ""), + "mtu": self.vl_properties.get("mtu", const.DEFAULT_MTU), + "vlan_transparent": self.vl_properties.get("vlan_transparent", False), + "subnet_list": [{ + "subnet_name": self.vl_properties.get("name", ""), + "cidr": self.vl_properties.get("cidr", "192.168.0.0/24"), + "ip_version": self.vl_properties.get("ip_version", const.IPV4), + "enable_dhcp": self.vl_properties.get("dhcp_enabled", False), + "gateway_ip": self.vl_properties.get("gateway_ip", ""), + "dns_nameservers": self.vl_properties.get("dns_nameservers", ""), + "host_routes": self.vl_properties.get("host_routes", "")}]} + startip = self.vl_properties.get("start_ip", "") + endip = self.vl_properties.get("end_ip", "") + if startip and endip: + network_data["subnet_list"][0]["allocation_pools"] = [ + {"start": startip, "end": endip}] + + vl_resp = self.create_network_to_vim(network_data) + self.related_network_id = vl_resp["id"] + self.related_subnetwork_id = vl_resp["subnet_list"][0]["id"] if vl_resp["subnet_list"] else "" + + def create_network_to_vim(self, network_data): + vim_resp_body = extsys.get_vim_by_id(self.vim_id) + self.vim_name = vim_resp_body["name"] + data = { + "vimid": self.vim_id, + "vimtype": vim_resp_body["type"], + "url": vim_resp_body["url"], + "user": vim_resp_body["userName"], + "passwd": vim_resp_body["password"], + "tenant": vim_resp_body["tenant"]} + vim_api = vimadaptor.VimAdaptor(data) + if not network_data["tenant"]: + network_data["tenant"] = vim_resp_body["tenant"] + vl_ret = vim_api.create_network(network_data) + if vl_ret[0] != 0: + logger.error("Send post vl request to vim failed, detail is %s" % vl_ret[1]) + raise NSLCMException("Send post vl request to vim failed.") + return vl_ret[1] + + def save_vl_to_db(self): + VLInstModel(vlinstanceid=self.vl_inst_id, vldid=self.vld_id, vlinstancename=self.vl_inst_name, + ownertype=self.owner_type, ownerid=self.owner_id, relatednetworkid=self.related_network_id, + relatedsubnetworkid=self.related_subnetwork_id, vimid=self.vim_id, tenant=self.tenant).save() + # do_biz_with_share_lock("create-vllist-in-vnffg-%s" % self.owner_id, self.create_vl_inst_id_in_vnffg) + self.create_vl_inst_id_in_vnffg() + + def create_vl_to_resmgr(self): + req_param = { + "vlInstanceId": self.vl_inst_id, + "name": self.vl_properties.get("network_name", ""), + "backendId": str(self.related_network_id), + "isPublic": "True", + "dcName": "", + "vimId": str(self.vim_id), + "vimName": self.vim_name, + "physicialNet": self.vl_properties.get("physical_network", ""), + "nsId": self.owner_id, + "nsName": self.ns_name, + "description": self.description, + "networkType": self.vl_properties.get("network_type", ""), + "segmentation": str(self.vl_properties.get("segmentation_id", "")), + "mtu": str(self.vl_properties.get("mtu", "")), + "vlanTransparent": str(self.vl_properties.get("vlan_transparent", "")), + "routerExternal": self.route_external, + "resourceProviderType": "", + "resourceProviderId": ""} + resmgr.create_vl(req_param) + + def create_vl_inst_id_in_vnffg(self): + if "vnffgs" in self.context: + for vnffg_info in self.context["vnffgs"]: + vl_id_list = vnffg_info.get("properties", {}).get("dependent_virtual_link", "") + if vl_id_list: + vl_inst_id_list = [] + for vl_id in vl_id_list: + vl_inst_info = VLInstModel.objects.filter(vldid=vl_id) + if vl_inst_info: + vl_inst_id_list.append(vl_inst_info[0].vlinstanceid) + else: + vl_inst_id_list.append("") + vl_inst_id_str = "" + for vl_inst_id in vl_inst_id_list: + vl_inst_id_str += vl_inst_id + "," + vl_inst_id_str = vl_inst_id_str[:-1] + VNFFGInstModel.objects.filter(vnffgdid=vnffg_info["vnffg_id"], nsinstid=self.owner_id).update( + vllist=vl_inst_id_str) diff --git a/lcm/ns/vls/delete_vls.py b/lcm/ns/vls/delete_vls.py new file mode 100644 index 00000000..d2d8640d --- /dev/null +++ b/lcm/ns/vls/delete_vls.py @@ -0,0 +1,85 @@ +# Copyright 2016 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 lcm.pub.database.models import VLInstModel, VNFFGInstModel +from lcm.pub.exceptions import NSLCMException +from lcm.pub.msapi import resmgr, extsys +from lcm.pub.nfvi.vim import vimadaptor + +logger = logging.getLogger(__name__) + + +class DeleteVls(object): + def __init__(self, vl_inst_id): + self.vl_inst_id = vl_inst_id + self.ns_inst_id = "" + + def do(self): + try: + vl_inst_info = VLInstModel.objects.filter(vlinstanceid=self.vl_inst_id) + if not vl_inst_info: + logger.info("vl inst id(%s) is not exist or has been already deleted" % self.vl_inst_id) + return {"result": 0, "detail": "vl is not exist or has been already deleted"} + self.ns_inst_id = vl_inst_info[0].ownerid + vim_id = vl_inst_info[0].vimid + subnetwork_id_list = vl_inst_info[0].relatedsubnetworkid.split(",") + network_id = vl_inst_info[0].relatednetworkid + self.delete_vl_from_vim(vim_id, subnetwork_id_list, network_id) + self.delete_vl_from_resmgr() + self.delete_vl_from_db(vl_inst_info) + return {"result": 0, "detail": "delete vl success"} + except NSLCMException as e: + return self.exception_handle(e) + except Exception as e: + logger.error(traceback.format_exc()) + return self.exception_handle(e) + + def exception_handle(self, e): + detail = "vl delete failed, detail message: %s" % e.message + logger.error(detail) + return {"result": 0, "detail": detail} + + def delete_vl_from_vim(self, vim_id, subnetwork_id_list, network_id): + vim_resp_body = extsys.get_vim_by_id(vim_id) + data = { + "vimid": vim_id, + "vimtype": vim_resp_body["type"], + "url": vim_resp_body["url"], + "user": vim_resp_body["userName"], + "passwd": vim_resp_body["password"], + "tenant": vim_resp_body["tenant"]} + vim_api = vimadaptor.VimAdaptor(data) + for subnetwork_id in subnetwork_id_list: + vim_api.delete_subnet(subnet_id=subnetwork_id) + vim_api.delete_network(network_id=network_id) + + def delete_vl_from_db(self, vl_inst_info): + # do_biz_with_share_lock("delete-vllist-in-vnffg-%s" % self.ns_inst_id, self.delete_vl_inst_id_in_vnffg) + self.delete_vl_inst_id_in_vnffg() + vl_inst_info.delete() + + def delete_vl_from_resmgr(self): + resmgr.delete_vl(self.vl_inst_id) + + def delete_vl_inst_id_in_vnffg(self): + for vnffg_info in VNFFGInstModel.objects.filter(nsinstid=self.ns_inst_id): + new_vl_id_list = "" + for old_vl_id in vnffg_info.vllist.split(","): + if old_vl_id != self.vl_inst_id: + new_vl_id_list += old_vl_id + "," + new_vl_id_list = new_vl_id_list[:-1] + VNFFGInstModel.objects.filter(vnffginstid=vnffg_info.vnffginstid).update(vllist=new_vl_id_list) diff --git a/lcm/ns/vls/get_vls.py b/lcm/ns/vls/get_vls.py new file mode 100644 index 00000000..37692c5a --- /dev/null +++ b/lcm/ns/vls/get_vls.py @@ -0,0 +1,23 @@ +# Copyright 2016 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 lcm.pub.database.models import VLInstModel + + +class GetVls(object): + def __init__(self, vl_inst_id): + self.vl_inst_id = vl_inst_id + + def do(self): + return VLInstModel.objects.filter(vlinstanceid=self.vl_inst_id) diff --git a/lcm/ns/vls/urls.py b/lcm/ns/vls/urls.py new file mode 100644 index 00000000..cad8ba34 --- /dev/null +++ b/lcm/ns/vls/urls.py @@ -0,0 +1,25 @@ +# Copyright 2016 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 patterns, url +from rest_framework.urlpatterns import format_suffix_patterns + +from lcm.ns.vls.views import VlView, VlDetailView + +urlpatterns = patterns('', + url(r'^openoapi/nslcm/v1/ns/vls$', VlView.as_view()), + url(r'^openoapi/nslcm/v1/ns/vls/(?P<vl_inst_id>[0-9a-zA-Z_-]+)$', VlDetailView.as_view()), + ) + +urlpatterns = format_suffix_patterns(urlpatterns) diff --git a/lcm/ns/vls/views.py b/lcm/ns/vls/views.py new file mode 100644 index 00000000..94ae5e21 --- /dev/null +++ b/lcm/ns/vls/views.py @@ -0,0 +1,48 @@ +# Copyright 2016 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 rest_framework import status +from rest_framework.response import Response +from rest_framework.views import APIView + +from lcm.ns.vls.create_vls import CreateVls +from lcm.ns.vls.delete_vls import DeleteVls +from lcm.ns.vls.get_vls import GetVls + +import logging + +logger = logging.getLogger(__name__) + + +class VlView(APIView): + def post(self, request): + logger.debug("VlsCreateView--post::> %s" % request.data) + resp = CreateVls(request.data).do() + return Response(data=resp, status=status.HTTP_201_CREATED) + + +class VlDetailView(APIView): + def get(self, request, vl_inst_id): + logger.debug("VlDetailView--get::> %s" % vl_inst_id) + vl_inst_info = GetVls(vl_inst_id).do() + if not vl_inst_info: + return Response(status=status.HTTP_404_NOT_FOUND) + return Response(status=status.HTTP_200_OK, data={'vlId': vl_inst_id, + 'vlName': vl_inst_info[0].vlinstancename, + 'vlStatus': "active"}) + + def delete(self, request_paras, vl_inst_id): + logger.debug("VlDetailView--delete::> %s" % vl_inst_id) + resp = DeleteVls(vl_inst_id).do() + return Response(data=resp, status=status.HTTP_202_ACCEPTED) diff --git a/lcm/ns/vnfs/__init__.py b/lcm/ns/vnfs/__init__.py new file mode 100644 index 00000000..5580cc3d --- /dev/null +++ b/lcm/ns/vnfs/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2016 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/ns/vnfs/const.py b/lcm/ns/vnfs/const.py new file mode 100644 index 00000000..0bef8fe5 --- /dev/null +++ b/lcm/ns/vnfs/const.py @@ -0,0 +1,24 @@ +# Copyright 2016 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 lcm.pub.utils.enumutil import enum + +VNF_STATUS = enum(NULL='null', INSTANTIATING="instantiating", INACTIVE='inactive', ACTIVE="active", FAILED="failed", + TERMINATING="terminating", SCALING="scaling") +INST_TYPE = enum(VNF=0, VNFM=1) +INST_TYPE_NAME = enum(VNF='VNF', VNFM='VNFM') +PACKAGE_TYPE = enum(VNFD='VNFD', NSD='NSD') + +NFVO_VNF_INST_TIMEOUT_SECOND = 1800 + +DATETIME_FORMAT = '%Y-%m-%d %H:%M:%S' diff --git a/lcm/ns/vnfs/create_vnfs.py b/lcm/ns/vnfs/create_vnfs.py new file mode 100644 index 00000000..83bb4443 --- /dev/null +++ b/lcm/ns/vnfs/create_vnfs.py @@ -0,0 +1,267 @@ +# Copyright 2016 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 json +import logging +import traceback +import uuid +from threading import Thread +from lcm.ns.const import OWNER_TYPE + +from lcm.ns.vnfs.const import VNF_STATUS, NFVO_VNF_INST_TIMEOUT_SECOND, INST_TYPE, INST_TYPE_NAME +from lcm.ns.vnfs.wait_job import wait_job_finish +from lcm.pub.database.models import NfPackageModel, NfInstModel, NSInstModel, VmInstModel, VNFFGInstModel, VLInstModel +from lcm.pub.exceptions import NSLCMException +from lcm.pub.msapi.extsys import get_vnfm_by_id +from lcm.pub.msapi.resmgr import create_vnf, create_vnf_creation_info +from lcm.pub.msapi.vnfmdriver import send_nf_init_request +from lcm.pub.utils.jobutil import JOB_MODEL_STATUS, JobUtil, JOB_TYPE +from lcm.pub.utils.share_lock import do_biz_with_share_lock +from lcm.pub.utils.timeutil import now_time +from lcm.pub.utils.values import ignore_case_get + +logger = logging.getLogger(__name__) + + +def prepare_create_params(): + nf_inst_id = str(uuid.uuid4()) + NfInstModel(nfinstid=nf_inst_id, status=VNF_STATUS.INSTANTIATING, create_time=now_time(), + lastuptime=now_time()).save() + job_id = JobUtil.create_job(INST_TYPE_NAME.VNF, JOB_TYPE.CREATE_VNF, nf_inst_id) + JobUtil.add_job_status(job_id, 0, 'create vnf record in database.', 0) + return nf_inst_id, job_id + + +class CreateVnfs(Thread): + def __init__(self, data, nf_inst_id, job_id): + super(CreateVnfs, self).__init__() + self.data = data + self.nf_inst_id = nf_inst_id + self.job_id = job_id + self.ns_inst_id = '' + self.vnf_id = '' + self.vnfd_id = '' + self.ns_inst_name = '' + self.nsd_model = '' + self.vnfd_model = '' + self.vnf_inst_name = '' + self.vnfm_inst_id = '' + self.inputs = '' + self.nf_package_info = '' + self.vnfm_nf_inst_id = '' + self.vnfm_job_id = '' + self.vnfm_inst_name = '' + self.vim_id = '' + + def run(self): + try: + self.get_params() + self.check_nf_name_exist() + self.get_vnfd_id() + self.check_nf_package_valid() + self.send_nf_init_request_to_vnfm() + self.send_get_vnfm_request_to_extsys() + self.send_create_vnf_request_to_resmgr() + self.wait_vnfm_job_finish() + self.write_vnf_creation_info() + self.save_info_to_db() + except NSLCMException as e: + self.vnf_inst_failed_handle(e.message) + except Exception: + logger.error(traceback.format_exc()) + self.vnf_inst_failed_handle('unexpected exception') + + def get_params(self): + self.ns_inst_id = self.data['ns_instance_id'] + vnf_index = int(float(self.data['vnf_index'])) - 1 + additional_vnf_info = self.data['additional_param_for_vnf'][vnf_index] + self.vnf_id = ignore_case_get(additional_vnf_info, 'vnfProfileId') + additional_param = ignore_case_get(additional_vnf_info, 'additionalParam') + self.vnfm_inst_id = ignore_case_get(additional_param, 'vnfmInstanceId') + para = ignore_case_get(additional_param, 'inputs') + self.inputs = json.loads(para) if isinstance(para, (str, unicode)) else para + self.vim_id = ignore_case_get(additional_param, 'vimId') + self.vnfd_id = ignore_case_get(additional_param, 'vnfdId') + + def check_nf_name_exist(self): + is_exist = NfInstModel.objects.filter(nf_name=self.vnf_inst_name).exists() + if is_exist: + logger.error('The name of NF instance already exists.') + raise NSLCMException('The name of NF instance already exists.') + + def get_vnfd_id(self): + if self.vnfd_id: + logger.debug("need not get vnfd_id") + self.nsd_model={'vnfs': [], 'vls': [], 'vnffgs': []} + self.vnf_inst_name = self.vnfd_id + str(uuid.uuid4()) + self.vnf_inst_name = self.vnf_inst_name[:30] + return + ns_inst_info = NSInstModel.objects.get(id=self.ns_inst_id) + self.ns_inst_name = ns_inst_info.name + self.nsd_model = json.loads(ns_inst_info.nsd_model) + for vnf_info in self.nsd_model['vnfs']: + if self.vnf_id == vnf_info['vnf_id']: + self.vnfd_id = vnf_info['properties']['id'] + if 'name' not in vnf_info['properties']: + self.vnf_inst_name = self.vnfd_id + str(uuid.uuid4()) + else: + self.vnf_inst_name = vnf_info['properties']['name'] + str(uuid.uuid4()) + self.vnf_inst_name = self.vnf_inst_name[:30] + return + logger.error('Can not found vnf in nsd model') + raise NSLCMException('Can not found vnf in nsd model') + + def check_nf_package_valid(self): + nf_package_info = NfPackageModel.objects.filter(vnfdid=self.vnfd_id) + if not nf_package_info: + logger.info('NF package not exist.') + raise NSLCMException('NF package not exist.') + self.nf_package_info = nf_package_info[0] + self.vnfd_model = json.loads(self.nf_package_info.vnfdmodel) + + def get_virtual_link_info(self, vnf_id): + virtual_link_list, ext_virtual_link = [], [] + for vnf_info in self.nsd_model['vnfs']: + if vnf_info['vnf_id'] != vnf_id: + continue + for network_info in vnf_info['networks']: + vl_instance = VLInstModel.objects.get( + vldid=network_info['vl_id'], + ownertype=OWNER_TYPE.NS, + ownerid=self.ns_inst_id) + vl_instance_id = vl_instance.vlinstanceid + network_name, subnet_name = self.get_network_info_of_vl(network_info['vl_id']) + virtual_link_list.append({ + 'network_name': network_name, + 'key_name': network_info['key_name'], + 'subnetwork_name': subnet_name, + 'vl_instance_id': vl_instance_id + }) + ext_virtual_link.append({ + "vlInstanceId": vl_instance_id, + "resourceId": vl_instance.relatednetworkid, + "resourceSubnetId": vl_instance.relatedsubnetworkid, + "cpdId": self.get_cpd_id_of_vl(network_info['key_name']), + "vim": { + "vimid": vl_instance.vimid + } + }) + return virtual_link_list, ext_virtual_link + + def get_cpd_id_of_vl(self, vl_key): + for cpd in self.vnfd_model["vnf_exposed"]["external_cps"]: + if vl_key == cpd["key_name"]: + return cpd["cpd_id"] + return "" + + def get_network_info_of_vl(self, vl_id): + for vnf_info in self.nsd_model['vls']: + if vnf_info['vl_id'] == vl_id: + return vnf_info['properties']['network_name'], vnf_info['properties']['name'] + return '', '' + + def send_nf_init_request_to_vnfm(self): + virtual_link_list, ext_virtual_link = self.get_virtual_link_info(self.vnf_id) + req_param = json.JSONEncoder().encode({ + 'vnfInstanceName': self.vnf_inst_name, + 'vnfPackageId': self.nf_package_info.nfpackageid, + 'vnfDescriptorId': self.vnfd_id, + 'extVirtualLink': ext_virtual_link, + 'additionalParam': {"inputs": self.inputs, + "vimId": self.vim_id, + "extVirtualLinks": virtual_link_list}}) + rsp = send_nf_init_request(self.vnfm_inst_id, req_param) + self.vnfm_job_id = ignore_case_get(rsp, 'jobId') + self.vnfm_nf_inst_id = ignore_case_get(rsp, 'vnfInstanceId') + + NfInstModel.objects.filter(nfinstid=self.nf_inst_id).update( + mnfinstid=self.vnfm_nf_inst_id, + nf_name=self.vnf_inst_name, + vnf_id=self.vnf_id, + package_id=self.nf_package_info.nfpackageid, + vnfm_inst_id=self.vnfm_inst_id, + ns_inst_id=self.ns_inst_id, + version=self.nf_package_info.vnfversion, + vendor=self.nf_package_info.vendor, + vnfd_model=self.vnfd_model, + input_params=json.JSONEncoder().encode(self.inputs), + lastuptime=now_time()) + + def send_get_vnfm_request_to_extsys(self): + resp_body = get_vnfm_by_id(self.vnfm_inst_id) + self.vnfm_inst_name = ignore_case_get(resp_body, 'name') + + def send_create_vnf_request_to_resmgr(self): + pkg_vnfd = json.loads(self.nf_package_info.vnfdmodel) + data = { + 'nf_inst_id': self.nf_inst_id, + 'vnfm_nf_inst_id': self.vnfm_nf_inst_id, + 'vnf_inst_name': self.vnf_inst_name, + 'ns_inst_id': self.ns_inst_id, + 'ns_inst_name': self.ns_inst_name, + 'nf_inst_name': self.vnf_inst_name, + 'vnfm_inst_id': self.vnfm_inst_id, + 'vnfm_inst_name': self.vnfm_inst_name, + 'vnfd_name': pkg_vnfd['metadata'].get('name', 'undefined'), + 'vnfd_id': self.vnfd_id, + 'job_id': self.job_id, + 'nf_inst_status': VNF_STATUS.INSTANTIATING, + 'vnf_type': pkg_vnfd['metadata'].get('vnf_type', 'undefined'), + 'nf_package_id': self.nf_package_info.nfpackageid} + create_vnf(data) + + def wait_vnfm_job_finish(self): + ret = wait_job_finish(vnfm_id=self.vnfm_inst_id, vnfo_job_id=self.job_id, + vnfm_job_id=self.vnfm_job_id, progress_range=[10, 90], + timeout=NFVO_VNF_INST_TIMEOUT_SECOND) + + if ret != JOB_MODEL_STATUS.FINISHED: + logger.error('VNF instantiation failed on VNFM side.') + raise NSLCMException('VNF instantiation failed on VNFM side.') + + def write_vnf_creation_info(self): + logger.debug("write_vnf_creation_info start") + vm_inst_infos = VmInstModel.objects.filter(insttype=INST_TYPE.VNF, instid=self.nf_inst_id) + data = { + 'nf_inst_id': self.nf_inst_id, + 'ns_inst_id': self.ns_inst_id, + 'vnfm_inst_id': self.vnfm_inst_id, + 'vms': [{'vmId': vm_inst_info.resouceid, 'vmName': vm_inst_info.vmname, 'vmStatus': 'ACTIVE'} for + vm_inst_info in vm_inst_infos]} + create_vnf_creation_info(data) + logger.debug("write_vnf_creation_info end") + + def save_info_to_db(self): + logger.debug("save_info_to_db start") + do_biz_with_share_lock("set-vnflist-in-vnffginst-%s" % self.ns_inst_id, self.save_vnf_inst_id_in_vnffg) + NfInstModel.objects.filter(nfinstid=self.nf_inst_id).update(status=VNF_STATUS.ACTIVE, lastuptime=now_time()) + JobUtil.add_job_status(self.job_id, 100, 'vnf instantiation success', 0) + logger.debug("save_info_to_db end") + + def vnf_inst_failed_handle(self, error_msg): + logger.error('VNF instantiation failed, detail message: %s' % error_msg) + NfInstModel.objects.filter(nfinstid=self.nf_inst_id).update(status=VNF_STATUS.FAILED, + lastuptime=now_time()) + JobUtil.add_job_status(self.job_id, 255, 'VNF instantiation failed, detail message: %s' % error_msg, 0) + + def save_vnf_inst_id_in_vnffg(self): + vnffgs = self.nsd_model['vnffgs'] + for vnffg in vnffgs: + if self.vnf_id not in vnffg['members']: + continue + vnffg_inst_infos = VNFFGInstModel.objects.filter(vnffgdid=vnffg['vnffg_Id'], nsinstid=self.ns_inst_id) + if not vnffg_inst_infos: + logger.error('Vnffg instance not exist.') + raise NSLCMException('Vnffg instance not exist.') + vnf_list = vnffg_inst_infos[0].vnflist + vnffg_inst_infos.update(vnf_list=vnf_list + ',' + self.nf_inst_id if vnf_list else self.nf_inst_id) diff --git a/lcm/ns/vnfs/get_vnfs.py b/lcm/ns/vnfs/get_vnfs.py new file mode 100644 index 00000000..0ca8e66e --- /dev/null +++ b/lcm/ns/vnfs/get_vnfs.py @@ -0,0 +1,23 @@ +# Copyright 2016 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 lcm.pub.database.models import NfInstModel + + +class GetVnf(object): + def __init__(self, nf_inst_id): + self.nf_inst_id = nf_inst_id + + def do_biz(self): + nf_inst_info = NfInstModel.objects.filter(nfinstid=self.nf_inst_id) + return nf_inst_info diff --git a/lcm/ns/vnfs/grant_vnfs.py b/lcm/ns/vnfs/grant_vnfs.py new file mode 100644 index 00000000..c93647f6 --- /dev/null +++ b/lcm/ns/vnfs/grant_vnfs.py @@ -0,0 +1,125 @@ +# Copyright 2016 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 json + +from lcm.pub.msapi import resmgr +from lcm.pub.database.models import NfPackageModel, NfInstModel +from lcm.pub.exceptions import NSLCMException +from lcm.pub.utils.values import ignore_case_get + +logger = logging.getLogger(__name__) + + +class GrantVnfs(object): + def __init__(self, data, job_id): + self.job_id = job_id + self.vnfm_inst_id = '' + self.vnf_uuid = '' + self.vnfm_job_id = '' + self.data = data + + def send_grant_vnf_to_resMgr(self): + logger.debug("grant data from vnfm:%s", self.data) + if isinstance(self.data, (unicode, str)): + self.data = json.JSONDecoder().decode(self.data) + has_res_tpl = False + grant_type = None + if ignore_case_get(self.data, "addResource"): + grant_type = "addResource" + elif ignore_case_get(self.data, "removeResource"): + grant_type = "removeResource" + else: + #raise NSLCMException("No grant resource is found.") + has_res_tpl = True + + for res in ignore_case_get(self.data, grant_type): + if "resourceTemplate" in res: + has_res_tpl = True + break + + if not has_res_tpl: + m_vnf_inst_id = ignore_case_get(self.data, "vnfInstanceId") + additional_param = ignore_case_get(self.data, "additionalparam") + vnfm_inst_id = ignore_case_get(additional_param, "vnfmid") + vim_id = ignore_case_get(additional_param, "vimid") + + vnfinsts = NfInstModel.objects.filter( + mnfinstid=m_vnf_inst_id, vnfm_inst_id=vnfm_inst_id) + if not vnfinsts: + raise NSLCMException("Vnfinst(%s) is not found in vnfm(%s)" % ( + m_vnf_inst_id, vnfm_inst_id)) + + vnf_pkg_id = vnfinsts[0].package_id + vnf_pkgs = NfPackageModel.objects.filter(nfpackageid=vnf_pkg_id) + if not vnf_pkgs: + raise NSLCMException("vnfpkg(%s) is not found" % vnf_pkg_id) + + vnfd = json.JSONDecoder().decode(vnf_pkgs[0].vnfdmodel) + + req_param = { + "vnfInstanceId": m_vnf_inst_id, + "vimId": vim_id, + "additionalParam": additional_param, + grant_type: [] + } + for res in ignore_case_get(self.data, grant_type): + vdu_name = ignore_case_get(res, "vdu") + grant_res = { + "resourceDefinitionId": ignore_case_get(res, "resourceDefinitionId"), + "type": ignore_case_get(res,"type"), + "vdu": vdu_name + } + for vdu in vnfd["vdus"]: + if vdu_name in (vdu["vdu_id"], vdu["properties"].get("name", "")): + grant_res["resourceTemplate"] = self.get_res_tpl(vdu, vnfd) + break + req_param[grant_type].append(grant_res) + self.data = req_param + return resmgr.grant_vnf(self.data) + + def get_res_tpl(self, vdu, vnfd): + storage_size = 0 + for storage_id in vdu["local_storages"]: + storage_size = storage_size + self.get_storage_size(storage_id, vnfd) + resourceTemplate = { + "virtualComputeDescriptor": { + "virtualCpu": { + "numVirtualCpu": int(vdu["nfv_compute"]["num_cpus"]) + }, + "virtualMemory": { + "virtualMemSize": int(vdu["nfv_compute"]["mem_size"]) + } + }, + "virtualStorageDescriptor": { + "typeOfStorage": "", + "sizeOfStorage": storage_size, + "swImageDescriptor": "" + } + } + return resourceTemplate + + def get_storage_size(self, storage_id, vnfd): + for storage in vnfd["local_storages"]: + if storage_id == storage["local_storage_id"]: + return int(storage["properties"]["size"]) + return 0 + + + + + + + diff --git a/lcm/ns/vnfs/notify_lcm.py b/lcm/ns/vnfs/notify_lcm.py new file mode 100644 index 00000000..6342f7a6 --- /dev/null +++ b/lcm/ns/vnfs/notify_lcm.py @@ -0,0 +1,172 @@ +# Copyright 2016 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 uuid +import logging +import traceback + +from rest_framework import status +from rest_framework.response import Response +from lcm.ns.vnfs.const import INST_TYPE +from lcm.pub.exceptions import NSLCMException +from lcm.pub.database.models import VNFCInstModel, VLInstModel, NfInstModel, PortInstModel, CPInstModel, VmInstModel +from lcm.pub.utils.values import ignore_case_get + + +logger = logging.getLogger(__name__) + + +class NotifyLcm(object): + def __init__(self, vnfmid, vnfInstanceId, data): + logger.debug("[Notify LCM] vnfmid=%s, vnfInstanceId=%s, data=%s" % (vnfmid, vnfInstanceId, data)) + self.vnf_instid = '' + self.vnfmid = vnfmid + self.m_vnfInstanceId = vnfInstanceId + self.status = ignore_case_get(data, 'status') + self.operation = ignore_case_get(data, 'operation') + self.lcm_jobid = ignore_case_get(data, 'jobId') + self.vnfdmodule = ignore_case_get(data, 'vnfdmodule') + self.affectedVnfc = ignore_case_get(data, 'affectedVnfc') + self.affectedVl = ignore_case_get(data, 'affectedVl') + self.affectedCp = ignore_case_get(data, 'affectedCp') + self.affectedVirtualStorage = ignore_case_get(data, 'affectedVirtualStorage') + + def do_biz(self): + try: + self.vnf_instid = self.get_vnfinstid(self.m_vnfInstanceId, self.vnfmid) + self.update_Vnfc() + self.update_Vl() + self.update_Cp() + self.update_Storage() + #self.update_vnf_by_vnfdmodule() + logger.debug("notify lcm end") + except NSLCMException as e: + self.exception(e.message) + except Exception: + logger.error(traceback.format_exc()) + self.exception('unexpected exception') + + def get_vnfinstid(self, mnfinstid, vnfm_inst_id): + nfinst = NfInstModel.objects.filter(mnfinstid=mnfinstid, vnfm_inst_id=vnfm_inst_id).first() + if nfinst: + return nfinst.nfinstid + else: + self.exception('vnfinstid not exist') + + def exception(self, error_msg): + logger.error('Notify Lcm failed, detail message: %s' % error_msg) + return Response(data={'error': '%s' % error_msg}, status=status.HTTP_409_CONFLICT) + + def update_Vnfc(self): + for vnfc in self.affectedVnfc: + vnfcInstanceId = ignore_case_get(vnfc, 'vnfcInstanceId') + vduId = ignore_case_get(vnfc, 'vduId') + changeType = ignore_case_get(vnfc, 'changeType') + vimId = ignore_case_get(vnfc, 'vimid') + vmId = ignore_case_get(vnfc, 'vmid') + vmName = ignore_case_get(vnfc, 'vmname') + # resourceType = ignore_case_get(vmResource, 'resourceType') + # resourceId = ignore_case_get(vmId, 'resourceId') + + + # if resourceType != 'vm': + # self.exception('affectedVnfc struct error: resourceType not euqal vm') + + if changeType == 'added': + VNFCInstModel(vnfcinstanceid=vnfcInstanceId, vduid=vduId, + nfinstid=self.vnf_instid, vmid=vmId).save() + VmInstModel(vmid=vmId, vimid=vimId, resouceid=vmId, insttype=INST_TYPE.VNF, + instid=self.vnf_instid, vmname=vmName, hostid='1').save() + elif changeType == 'removed': + VNFCInstModel.objects.filter(vnfcinstanceid=vnfcInstanceId).delete() + elif changeType == 'modified': + VNFCInstModel.objects.filter(vnfcinstanceid=vnfcInstanceId).update(vduid=vduId, + nfinstid=self.vnf_instid, + vmid=vmId) + else: + self.exception('affectedVnfc struct error: changeType not in {added,removed,modified}') + + def update_Vl(self): + for vl in self.affectedVl: + vlInstanceId = ignore_case_get(vl, 'vlInstanceId') + vldid = ignore_case_get(vl, 'vldid') + changeType = ignore_case_get(vl, 'changeType') + networkResource = ignore_case_get(vl, 'networkResource') + resourceType = ignore_case_get(networkResource, 'resourceType') + resourceId = ignore_case_get(networkResource, 'resourceId') + + if resourceType != 'network': + self.exception('affectedVl struct error: resourceType not euqal network') + + ownerId = self.vnf_instid + ownerId = self.get_vnfinstid(self.vnf_instid, self.vnfmid) + + if changeType == 'added': + VLInstModel(vlInstanceId=vlInstanceId, vldId=vldid, ownerType=0, ownerId=ownerId, + relatedNetworkId=resourceId, vlType=0).save() + elif changeType == 'removed': + VLInstModel.objects.filter(vlInstanceId=vlInstanceId).delete() + elif changeType == 'modified': + VLInstModel.objects.filter(vlInstanceId=vlInstanceId)\ + .update(vldId=vldid, ownerType=0, ownerId=ownerId, relatedNetworkId=resourceId, vlType=0) + else: + self.exception('affectedVl struct error: changeType not in {added,removed,modified}') + + def update_Cp(self): + for cp in self.affectedCp: + virtualLinkInstanceId = ignore_case_get(cp, 'virtualLinkInstanceId') + #ownerid = ignore_case_get(cp, 'ownerid') + ownertype = ignore_case_get(cp, 'ownertype') + if not ownertype: + ownertype = 0 + ownerid = self.vnf_instid if str(ownertype) == "0" else ignore_case_get(cp, 'ownerid') + cpInstanceId = ignore_case_get(cp, 'cpinstanceid') + cpdId = ignore_case_get(cp, 'cpdid') + changeType = ignore_case_get(cp, 'changetype') + relatedportId = '' + portResource = ignore_case_get(cp, 'portResource') + if portResource: + vimId = ignore_case_get(portResource, 'vimid') + resourceId = ignore_case_get(portResource, 'resourceid') + resourceName = ignore_case_get(portResource, 'resourceName') + tenant = ignore_case_get(portResource, 'tenant') + ipAddress = ignore_case_get(portResource, 'ipAddress') + macAddress = ignore_case_get(portResource, 'macAddress') + sfcEncapsulation = ignore_case_get(portResource, 'sfcEncapsulation') + instId = ignore_case_get(portResource, 'instId') + portid = str(uuid.uuid4()) + PortInstModel(portid=portid, networkid='unknown', subnetworkid='unknown', vimid=vimId, + resourceid=resourceId, name=resourceName, instid=instId, cpinstanceid=cpInstanceId, + bandwidth='unknown', operationalstate='active', ipaddress=ipAddress, macaddress=macAddress, + floatipaddress='unknown', serviceipaddress='unknown', typevirtualnic='unknown', + sfcencapsulation='gre', direction='unknown', tenant=tenant).save() + relatedportId = portid + + if changeType == 'added': + CPInstModel(cpinstanceid=cpInstanceId, cpdid=cpdId, ownertype=ownertype, ownerid=ownerid, + relatedtype=2, relatedport=relatedportId, status='active').save() + elif changeType == 'removed': + CPInstModel.objects.filter(cpinstanceid=cpInstanceId).delete() + elif changeType == 'changed': + CPInstModel.objects.filter(cpinstanceid=cpInstanceId).update(cpdid=cpdId, ownertype=ownertype, + ownerid=ownerid, + vlinstanceid=virtualLinkInstanceId, + relatedtype=2, relatedport=relatedportId) + else: + self.exception('affectedVl struct error: changeType not in {added,removed,modified}') + + def update_Storage(self): + pass + + def update_vnf_by_vnfdmodule(self): + NfInstModel.objects.filter(nfinstid=self.vnf_instid).update(vnfd_model=self.vnfdmodule) diff --git a/lcm/ns/vnfs/scale_vnfs.py b/lcm/ns/vnfs/scale_vnfs.py new file mode 100644 index 00000000..fb4f2bee --- /dev/null +++ b/lcm/ns/vnfs/scale_vnfs.py @@ -0,0 +1,105 @@ +# 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 json +import logging +import threading +import traceback + +from lcm.ns.vnfs.const import VNF_STATUS +from lcm.ns.vnfs.wait_job import wait_job_finish +from lcm.pub.database.models import NfInstModel +from lcm.pub.exceptions import NSLCMException +from lcm.pub.msapi.vnfmdriver import send_nf_scaling_request +from lcm.pub.utils.jobutil import JobUtil, JOB_TYPE, JOB_MODEL_STATUS +from lcm.pub.utils.values import ignore_case_get + +JOB_ERROR = 255 +SCALE_TYPE = ("SCALE_OUT", "SCALE_IN") +logger = logging.getLogger(__name__) + + +class NFManualScaleService(threading.Thread): + def __init__(self, vnf_instance_id, data): + super(NFManualScaleService, self).__init__() + self.vnf_instance_id = vnf_instance_id + self.data = data + self.job_id = JobUtil.create_job("NF", JOB_TYPE.MANUAL_SCALE_VNF, vnf_instance_id) + self.scale_vnf_data = '' + self.nf_model = {} + self.nf_scale_params = [] + self.m_nf_inst_id = '' + self.vnfm_inst_id = '' + + def run(self): + try: + self.do_biz() + except NSLCMException as e: + JobUtil.add_job_status(self.job_id, JOB_ERROR, e.message) + except: + logger.error(traceback.format_exc()) + JobUtil.add_job_status(self.job_id, JOB_ERROR, 'nf scale fail') + finally: + self.update_nf_status() + + def do_biz(self): + self.update_job(1, desc='nf scale start') + self.update_nf_status(VNF_STATUS.SCALING) + self.get_and_check_params() + self.send_nf_scaling_requests() + self.update_job(100, desc='nf scale success') + + def get_and_check_params(self): + nf_info = NfInstModel.objects.filter(nfinstid=self.vnf_instance_id) + if not nf_info: + logger.error('NF instance[id=%s] does not exist' % self.vnf_instance_id) + raise NSLCMException('NF instance[id=%s] does not exist' % self.vnf_instance_id) + logger.debug('vnfd_model = %s, vnf_instance_id = %s' % (nf_info[0].vnfd_model, self.vnf_instance_id)) + self.nf_model = json.loads(nf_info[0].vnfd_model) + self.m_nf_inst_id = nf_info[0].mnfinstid + self.vnfm_inst_id = nf_info[0].vnfm_inst_id + self.scale_vnf_data = ignore_case_get(self.data, 'scaleVnfData') + if not self.scale_vnf_data: + logger.error('scaleVnfData parameter does not exist or value incorrect') + raise NSLCMException('scaleVnfData parameter does not exist or value incorrect') + for vnf_data in self.scale_vnf_data: + scale_type = ignore_case_get(vnf_data, 'type') + aspect_id = ignore_case_get(vnf_data, 'aspectId') + number_of_steps = ignore_case_get(vnf_data, 'numberOfSteps') + self.nf_scale_params.append({ + 'type': scale_type, + 'aspectId': aspect_id, + 'numberOfSteps': number_of_steps, + 'additionalParam': {'vnfdModel': self.nf_model} + }) + + def send_nf_scaling_requests(self): + for i in range(len(self.nf_scale_params)): + progress_range = [10 + 80 / len(self.nf_scale_params) * i, 10 + 80 / len(self.nf_scale_params) * (i + 1)] + self.send_nf_scaling_request(self.nf_scale_params[i], progress_range) + + def send_nf_scaling_request(self, scale_param, progress_range): + req_param = json.JSONEncoder().encode(scale_param) + rsp = send_nf_scaling_request(self.vnfm_inst_id, self.m_nf_inst_id, req_param) + vnfm_job_id = ignore_case_get(rsp, 'jobId') + ret = wait_job_finish(self.vnfm_inst_id, self.job_id, vnfm_job_id, progress_range=progress_range, timeout=1200, + mode='1') + if ret != JOB_MODEL_STATUS.FINISHED: + logger.error('[NF scale] nf scale failed') + raise NSLCMException("nf scale failed") + + def update_nf_status(self, status=VNF_STATUS.ACTIVE): + NfInstModel.objects.filter(nfinstid=self.vnf_instance_id).update(status=status) + + def update_job(self, progress, desc=''): + JobUtil.add_job_status(self.job_id, progress, desc)
\ No newline at end of file diff --git a/lcm/ns/vnfs/terminate_nfs.py b/lcm/ns/vnfs/terminate_nfs.py new file mode 100644 index 00000000..7b0b21b3 --- /dev/null +++ b/lcm/ns/vnfs/terminate_nfs.py @@ -0,0 +1,125 @@ +# Copyright 2016 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 +import json + +import threading + +from lcm.ns.vnfs.wait_job import wait_job_finish +from lcm.pub.database.models import NfInstModel +from lcm.ns.vnfs.const import VNF_STATUS, NFVO_VNF_INST_TIMEOUT_SECOND +from lcm.pub.utils.values import ignore_case_get +from lcm.pub.utils.jobutil import JOB_MODEL_STATUS, JobUtil +from lcm.pub.exceptions import NSLCMException +from lcm.pub.msapi.vnfmdriver import send_nf_terminate_request +from lcm.pub.msapi import resmgr + +logger = logging.getLogger(__name__) + + +class TerminateVnfs(threading.Thread): + def __init__(self, data, vnf_inst_id, job_id): + threading.Thread.__init__(self) + self.vnf_inst_id = vnf_inst_id + self.job_id = job_id + self.vnfm_inst_id = '' + self.vnf_uuid = '' + self.vnfm_job_id = '' + self.terminationType = data['terminationType'] + self.gracefulTerminationTimeout = data['gracefulTerminationTimeout'] + self.initdata() + + def run(self): + try: + self.check_nf_valid() + self.send_nf_terminate_to_vnfmDriver() + self.wait_vnfm_job_finish() + self.send_terminate_vnf_to_resMgr() + self.delete_data_from_db() + except NSLCMException as e: + self.exception(e.message) + except Exception: + logger.error(traceback.format_exc()) + self.exception('unexpected exception') + + def set_vnf_status(self, vnf_inst_info): + vnf_status = vnf_inst_info.status + if (vnf_status == VNF_STATUS.TERMINATING): + logger.info('[VNF terminate] VNF is dealing by other application,try again later.') + raise NSLCMException('[VNF terminate] VNF is dealing by other application,try again later.') + else: + vnf_inst_info.status = VNF_STATUS.TERMINATING + vnf_inst_info.save() + + def check_vnf_is_exist(self): + vnf_inst = NfInstModel.objects.filter(nfinstid=self.vnf_inst_id) + if not vnf_inst.exists(): + logger.warning('[VNF terminate] Vnf terminate [%s] is not exist.' % self.vnf_inst_id) + return None + return vnf_inst[0] + + def add_progress(self, progress, status_decs, error_code=""): + JobUtil.add_job_status(self.job_id, progress, status_decs, error_code) + + def initdata(self): + vnf_inst_info = self.check_vnf_is_exist() + if not vnf_inst_info: + self.add_progress(100, "TERM_VNF_NOT_EXIST_SUCCESS", "finished") + self.add_progress(2, "GET_VNF_INST_SUCCESS") + self.vnfm_inst_id = vnf_inst_info.vnfm_inst_id + self.vnf_uuid = vnf_inst_info.mnfinstid + if not self.vnf_uuid: + self.add_progress(100, "TERM_VNF_NOT_EXIST_SUCCESS", "finished") + + def check_nf_valid(self): + vnf_inst = NfInstModel.objects.filter(nfinstid=self.vnf_inst_id) + if not vnf_inst.exists(): + logger.warning('[VNF terminate] Vnf instance [%s] is not exist.' % self.vnf_inst_id) + raise NSLCMException('[VNF terminate] Vnf instance is not exist.') + if not vnf_inst: + self.add_progress(100, "TERM_VNF_NOT_EXIST_SUCCESS", "finished") + raise NSLCMException('[VNF terminate] Vnf instance is not exist.') + self.set_vnf_status(vnf_inst[0]) + + def exception(self, error_msg): + logger.error('VNF Terminate failed, detail message: %s' % error_msg) + NfInstModel.objects.filter(nfinstid=self.vnf_inst_id).update(status=VNF_STATUS.FAILED) + JobUtil.add_job_status(self.job_id, 255, 'VNF Terminate failed, detail message: %s' % error_msg, 0) + + def send_nf_terminate_to_vnfmDriver(self): + req_param = json.JSONEncoder().encode({ + 'terminationType': self.terminationType, + 'gracefulTerminationTimeout': self.gracefulTerminationTimeout}) + rsp = send_nf_terminate_request(self.vnfm_inst_id, self.vnf_uuid, req_param) + self.vnfm_job_id = ignore_case_get(rsp, 'jobId') + + def send_terminate_vnf_to_resMgr(self): + resmgr.terminate_vnf(self.vnf_inst_id) + + def wait_vnfm_job_finish(self): + if not self.vnfm_job_id: + logger.warn("No Job, need not wait") + return + ret = wait_job_finish(vnfm_id=self.vnfm_inst_id, vnfo_job_id=self.job_id, + vnfm_job_id=self.vnfm_job_id, progress_range=[10, 90], + timeout=NFVO_VNF_INST_TIMEOUT_SECOND) + + if ret != JOB_MODEL_STATUS.FINISHED: + logger.error('VNF terminate failed on VNFM side.') + raise NSLCMException('VNF terminate failed on VNFM side.') + + def delete_data_from_db(self): + NfInstModel.objects.filter(nfinstid=self.vnf_inst_id).delete() + JobUtil.add_job_status(self.job_id, 100, 'vnf terminate success', 0) diff --git a/lcm/ns/vnfs/urls.py b/lcm/ns/vnfs/urls.py new file mode 100644 index 00000000..4ab427b9 --- /dev/null +++ b/lcm/ns/vnfs/urls.py @@ -0,0 +1,30 @@ +# Copyright 2016-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 patterns, url +from rest_framework.urlpatterns import format_suffix_patterns + +from lcm.ns.vnfs.views import NfView, NfDetailView, NfGrant, LcmNotify, NfScaleView, NfVerifyView + +urlpatterns = patterns('', + url(r'^openoapi/nslcm/v1/ns/vnfs$', NfView.as_view()), + url(r'^openoapi/nslcm/v1/ns/vnfs/(?P<vnfinstid>[0-9a-zA-Z_-]+)$', NfDetailView.as_view()), + url(r'^openoapi/nslcm/v1/ns/grantvnf$', NfGrant.as_view()), + url(r'^openoapi/nslcm/v1/ns/(?P<vnfmid>[0-9a-zA-Z_-]+)' + r'/vnfs/(?P<vnfInstanceId>[0-9a-zA-Z_-]+)/Notify$', + LcmNotify.as_view()), + url(r'^openoapi/nslcm/v1/ns/vnfs/(?P<vnfinstid>[0-9a-zA-Z_-]+)/scaling$', NfScaleView.as_view()), + url(r'^openoapi/nslcm/v1/vnfonboarding$', NfVerifyView.as_view()), + ) + +urlpatterns = format_suffix_patterns(urlpatterns) diff --git a/lcm/ns/vnfs/verify_vnfs.py b/lcm/ns/vnfs/verify_vnfs.py new file mode 100644 index 00000000..e40e27a3 --- /dev/null +++ b/lcm/ns/vnfs/verify_vnfs.py @@ -0,0 +1,218 @@ +# 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 json +import logging +import os +import threading +import traceback +import time + +from lcm.pub.exceptions import NSLCMException +from lcm.pub.utils.jobutil import JobUtil, JOB_TYPE, JOB_MODEL_STATUS +from lcm.pub.utils.values import ignore_case_get +from lcm.pub.utils.restcall import req_by_msb + +logger = logging.getLogger(__name__) + +JOB_ERROR = 255 + +class VerifyVnfs(threading.Thread): + def __init__(self, data, job_id): + super(VerifyVnfs, self).__init__() + self.data = data + self.job_id = job_id + self.vnf_inst_id = '' + self.verify_ok = False + self.verify_config = '' + + def run(self): + try: + self.verify_config = self.load_config() + JobUtil.create_job("VNF", JOB_TYPE.CREATE_VNF, self.job_id, 'vnfsdk', self.job_id) + self.do_on_boarding() + self.do_inst_vnf() + self.do_func_test() + self.verify_ok = True + except NSLCMException as e: + self.update_job(JOB_ERROR, e.message) + except: + logger.error(traceback.format_exc()) + self.update_job(JOB_ERROR, 'Unknown error in vnf verify.') + finally: + logger.warn("Ignore terminate vnf operation") + if self.verify_ok: + self.update_job(100, "Ignore terminate vnf operation.") + #self.do_term_vnf() + + def do_on_boarding(self): + self.update_job(10, "Start vnf on boarding.") + onboarding_data = { + "csarId":self.data["PackageID"], + "labVimId":ignore_case_get(self.verify_config, "labVimId") + } + ret = req_by_msb("/openoapi/nslcm/v1/vnfpackage", "POST", json.JSONEncoder().encode(onboarding_data)) + if ret[0] != 0: + raise NSLCMException("Failed to call vnf onboarding: %s" % ret[1]) + rsp_data = json.JSONDecoder().decode(ret[1]) + if not self.wait_until_job_done(rsp_data["jobId"], 15): + raise NSLCMException("Vnf onboarding failed") + self.update_job(20, "Vnf on boarding success.") + + def do_inst_vnf(self): + self.update_job(30, "Start inst vnf.") + vnf_param = ignore_case_get(self.verify_config, "additionalParamForVnf") + if vnf_param and "additionalParam" in vnf_param[0]: + vnf_param[0]["additionalParam"]["vimId"] = ignore_case_get(self.verify_config, "lab_vim_id") + inst_data = { + "nsInstanceId": "", + "additionalParamForVnf": vnf_param, + "vnfIndex": "1" + } + ret = req_by_msb("/openoapi/nslcm/v1/ns/vnfs", "POST", json.JSONEncoder().encode(inst_data)) + if ret[0] != 0: + raise NSLCMException("Failed to call inst vnf: %s" % ret[1]) + rsp_data = json.JSONDecoder().decode(ret[1]) + self.vnf_inst_id = rsp_data["vnfInstId"] + if not self.wait_until_job_done(rsp_data["jobId"], 40): + raise NSLCMException("Vnf(%s) inst failed" % self.vnf_inst_id) + self.update_job(50, "Inst vnf success.") + + def do_func_test(self): + self.update_job(60, "Start vnf function test.") + func_data = {"PackageID":self.data["PackageID"]} + ret = req_by_msb("/openapi/vnfsdk/v1/functest/taskmanager/testtasks", "POST", json.JSONEncoder().encode(func_data)) + if ret[0] != 0: + raise NSLCMException("Failed to call func test: %s" % ret[1]) + rsp_data = json.JSONDecoder().decode(ret[1]) + + if not self.wait_func_test_job_done(rsp_data["TaskID"], 40): + raise NSLCMException("Func test failed") + logger.info("Query(%s) job success.", rsp_data["TaskID"]) + + ret = req_by_msb("/openapi/vnfsdk/v1/functest/taskmanager/testtasks/%s/result" % rsp_data["TaskID"], "GET") + if ret[0] != 0: + raise NSLCMException("Failed to get func test result: %s" % ret[1]) + rsp_result_data = json.JSONDecoder().decode(ret[1]) + logger.info("Func test(%s) result: %s", rsp_result_data) + self.update_job(80, "Vnf function test success.") + + def do_term_vnf(self): + if not self.vnf_inst_id: + return + self.update_job(90, "Start term vnf.") + term_data = { + "terminationType": "forceful", + "gracefulTerminationTimeout": "600" + } + ret = req_by_msb("/openoapi/nslcm/v1/ns/vnfs/%s" % self.vnf_inst_id, "POST", json.JSONEncoder().encode(term_data)) + if ret[0] != 0: + raise NSLCMException("Failed to call term vnf: %s" % ret[1]) + rsp_data = json.JSONDecoder().decode(ret[1]) + end_progress = 100 if self.verify_ok else JOB_ERROR + term_progress = 95 if self.verify_ok else JOB_ERROR + if not self.wait_until_job_done(rsp_data["jobId"], term_progress): + logger.error("Vnf(%s) term failed", self.vnf_inst_id) + end_progress = JOB_ERROR + self.update_job(end_progress, "Term vnf end.") + + def update_job(self, progress, desc=''): + JobUtil.add_job_status(self.job_id, progress, desc) + + def wait_until_job_done(self, job_id, global_progress, retry_count=60, interval_second=3): + count = 0 + response_id, new_response_id = 0, 0 + job_end_normal, job_timeout = False, True + while count < retry_count: + count = count + 1 + time.sleep(interval_second) + ret = req_by_msb("/openoapi/nslcm/v1/jobs/%s?responseId=%s" % (job_id, response_id), "GET") + if ret[0] != 0: + logger.error("Failed to query job: %s:%s", ret[2], ret[1]) + continue + job_result = json.JSONDecoder().decode(ret[1]) + if "responseDescriptor" not in job_result: + logger.error("Job(%s) does not exist.", job_id) + continue + progress = job_result["responseDescriptor"]["progress"] + new_response_id = job_result["responseDescriptor"]["responseId"] + job_desc = job_result["responseDescriptor"]["statusDescription"] + if new_response_id != response_id: + self.update_job(global_progress, job_desc) + logger.debug("%s:%s:%s", progress, new_response_id, job_desc) + response_id = new_response_id + count = 0 + if progress == JOB_ERROR: + if 'already onBoarded' in job_desc: + logger.warn("%s:%s", job_id, job_desc) + job_end_normal, job_timeout = True, False + logger.info("Job(%s) ended", job_id) + break + job_timeout = False + logger.error("Job(%s) failed: %s", job_id, job_desc) + break + elif progress == 100: + job_end_normal, job_timeout = True, False + logger.info("Job(%s) ended normally", job_id) + break + if job_timeout: + logger.error("Job(%s) timeout", job_id) + return job_end_normal + + def wait_func_test_job_done(self, job_id, global_progress, retry_count=60, interval_second=3): + count = 0 + response_id, new_response_id = 0, 0 + job_end_normal, job_timeout = False, True + while count < retry_count: + count = count + 1 + time.sleep(interval_second) + ret = req_by_msb("/openapi/vnfsdk/v1/functest/taskmanager/testtasks/%s?responseId=%s" % + (job_id, response_id), "GET") + if ret[0] != 0: + logger.error("Failed to query job: %s:%s", ret[2], ret[1]) + continue + job_result = json.JSONDecoder().decode(ret[1]) + if "responseDescriptor" not in job_result: + logger.error("Job(%s) does not exist.", job_id) + continue + progress = job_result["responseDescriptor"]["progress"] + new_response_id = job_result["responseDescriptor"]["responseId"] + job_desc = job_result["responseDescriptor"]["statusDescription"] + if new_response_id != response_id: + self.update_job(global_progress, job_desc) + logger.debug("%s:%s:%s", progress, new_response_id, job_desc) + response_id = new_response_id + count = 0 + if progress == JOB_ERROR: + if 'already onBoarded' in job_desc: + logger.warn("%s:%s", job_id, job_desc) + job_end_normal, job_timeout = True, False + logger.info("Job(%s) ended", job_id) + break + job_timeout = False + logger.error("Job(%s) failed: %s", job_id, job_desc) + break + elif progress == 100: + job_end_normal, job_timeout = True, False + logger.info("Job(%s) ended normally", job_id) + break + if job_timeout: + logger.error("Job(%s) timeout", job_id) + return job_end_normal + + def load_config(self): + json_file = os.path.join(os.path.dirname(__file__), 'verify_vnfs_config.json') + f = open(json_file) + json_data = json.JSONDecoder().decode(f.read()) + f.close() + return json_data diff --git a/lcm/ns/vnfs/verify_vnfs_config.json b/lcm/ns/vnfs/verify_vnfs_config.json new file mode 100644 index 00000000..712a070f --- /dev/null +++ b/lcm/ns/vnfs/verify_vnfs_config.json @@ -0,0 +1,13 @@ +{ + "labVimId":"1111", + "additionalParamForVnf":[ + { + "vnfProfileId":"2222", + "additionalParam":{ + "vnfmInstanceId":"vnfmid_4", + "inputs":{"vnf_param1": "inputs_11", "vnf_param2": "inputs_22"}, + "vnfdId":"777" + } + } + ] +}
\ No newline at end of file diff --git a/lcm/ns/vnfs/views.py b/lcm/ns/vnfs/views.py new file mode 100644 index 00000000..5c925697 --- /dev/null +++ b/lcm/ns/vnfs/views.py @@ -0,0 +1,124 @@ +# Copyright 2016-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 +import uuid + +from rest_framework import status +from rest_framework.response import Response +from rest_framework.views import APIView + +from lcm.ns.vnfs import create_vnfs +from lcm.ns.vnfs.create_vnfs import CreateVnfs +from lcm.ns.vnfs.verify_vnfs import VerifyVnfs +from lcm.ns.vnfs.get_vnfs import GetVnf +from lcm.ns.vnfs.scale_vnfs import NFManualScaleService +from lcm.ns.vnfs.terminate_nfs import TerminateVnfs +from lcm.ns.vnfs.grant_vnfs import GrantVnfs +from lcm.ns.vnfs.notify_lcm import NotifyLcm +from lcm.pub.utils.jobutil import JobUtil, JOB_TYPE +from lcm.pub.utils.values import ignore_case_get + +logger = logging.getLogger(__name__) + + +class NfView(APIView): + def post(self, request): + logger.debug("VnfCreateView--post::> %s" % request.data) + data = {'ns_instance_id': ignore_case_get(request.data, 'nsInstanceId'), + 'additional_param_for_ns': ignore_case_get(request.data, 'additionalParamForVnf'), + 'additional_param_for_vnf': ignore_case_get(request.data, 'additionalParamForVnf'), + 'vnf_index': ignore_case_get(request.data, 'vnfIndex')} + nf_inst_id, job_id = create_vnfs.prepare_create_params() + CreateVnfs(data, nf_inst_id, job_id).start() + rsp = { + "vnfInstId": nf_inst_id, + "jobId": job_id} + return Response(data=rsp, status=status.HTTP_202_ACCEPTED) + + +class NfDetailView(APIView): + def get(self, request, vnfinstid): + logger.debug("VnfQueryView--get::> %s" % vnfinstid) + nf_inst_info = GetVnf(vnfinstid).do_biz() + if not nf_inst_info: + return Response(status=status.HTTP_404_NOT_FOUND) + return Response(status=status.HTTP_200_OK, + data={'vnfInstId': nf_inst_info[0].nfinstid, 'vnfName': nf_inst_info[0].nf_name, + 'vnfStatus': nf_inst_info[0].status}) + + def post(self, request_paras, vnfinstid): + logger.debug("VnfTerminateView--post::> %s, %s", vnfinstid, request_paras.data) + vnf_inst_id = vnfinstid + terminationType = ignore_case_get(request_paras.data, 'terminationType') + gracefulTerminationTimeout = ignore_case_get(request_paras.data, 'gracefulTerminationTimeout') + job_id = JobUtil.create_job("VNF", JOB_TYPE.TERMINATE_VNF, vnf_inst_id) + data = {'terminationType': terminationType, 'gracefulTerminationTimeout': gracefulTerminationTimeout} + logger.debug("data=%s", data) + try: + TerminateVnfs(data, vnf_inst_id, job_id).start() + except Exception as e: + return Response(data={'error': '%s' % e.message}, status=status.HTTP_409_CONFLICT) + rsp = {'jobId': job_id} + return Response(data=rsp, status=status.HTTP_201_CREATED) + + +class NfGrant(APIView): + def post(self, request): + logger.debug("NfGrant--post::> %s" % request.data) + try: + vnf_inst_id = ignore_case_get(request.data, 'vnfInstanceId') + job_id = JobUtil.create_job("VNF", JOB_TYPE.GRANT_VNF, vnf_inst_id) + rsp = GrantVnfs(request.data, job_id).send_grant_vnf_to_resMgr() + """ + rsp = { + "vim": { + "vimid": ignore_case_get(ignore_case_get(request.data, 'additionalparam'), 'vimid'), + "accessinfo": { + "tenant": "admin" + } + } + } + """ + return Response(data=rsp, status=status.HTTP_201_CREATED) + except Exception as e: + logger.error(traceback.format_exc()) + return Response(data={'error': '%s' % e.message}, status=status.HTTP_409_CONFLICT) + + +class LcmNotify(APIView): + def post(self, request_paras, vnfmid, vnfInstanceId): + logger.debug("LcmNotify--post::> %s" % request_paras.data) + try: + NotifyLcm(vnfmid, vnfInstanceId, request_paras.data).do_biz() + return Response(data={}, status=status.HTTP_201_CREATED) + except Exception as e: + return Response(data={'error': '%s' % e.message}, status=status.HTTP_409_CONFLICT) + + +class NfScaleView(APIView): + def post(self, request_paras, vnfinstid): + logger.debug("NfScaleView--post::> %s" % request_paras.data) + try: + NFManualScaleService(vnfinstid, request_paras.data).start() + return Response(data={}, status=status.HTTP_202_ACCEPTED) + except Exception as e: + return Response(data={'error': '%s' % e.message}, status=status.HTTP_409_CONFLICT) + +class NfVerifyView(APIView): + def post(self, request): + job_id = "VNFSDK_" + str(uuid.uuid4()) + logger.debug("NfVerifyView--post::%s> %s", job_id, request.data) + VerifyVnfs(request.data, job_id).start() + return Response(data={"jobId": job_id}, status=status.HTTP_202_ACCEPTED)
\ No newline at end of file diff --git a/lcm/ns/vnfs/wait_job.py b/lcm/ns/vnfs/wait_job.py new file mode 100644 index 00000000..39a9555b --- /dev/null +++ b/lcm/ns/vnfs/wait_job.py @@ -0,0 +1,81 @@ +# Copyright 2016 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 time +import datetime +import logging + +import math + +from lcm.pub.utils.jobutil import JobUtil, JOB_MODEL_STATUS +from lcm.pub.msapi.vnfmdriver import query_vnfm_job +from lcm.pub.utils.values import ignore_case_get + +logger = logging.getLogger(__name__) + + +def calc_progress(vnfm_progress, target_range=None): + target_range = [0, 100] if not target_range else target_range + progress = int(vnfm_progress) if vnfm_progress else 0 + if progress > 100: + return progress + floor_progress = int(math.floor(float(target_range[1] - target_range[0]) / 100 * progress)) + target_range = floor_progress + target_range[0] + return target_range + + +def default_callback(vnfo_job_id, vnfm_job_id, job_status, jobs, progress_range, **kwargs): + for job in jobs: + progress = calc_progress( + ignore_case_get(job, 'progress'), + progress_range) + JobUtil.add_job_status(vnfo_job_id, progress, + ignore_case_get(job, 'statusdescription'), + ignore_case_get(job, 'errorcode')) + latest_progress = calc_progress( + ignore_case_get(job_status, 'progress'), + progress_range) + JobUtil.add_job_status(vnfo_job_id, latest_progress, + ignore_case_get(job_status, 'statusdescription'), + ignore_case_get(job_status, 'errorcode')) + jobstatus = ignore_case_get(job_status, 'status') + if jobstatus in (JOB_MODEL_STATUS.ERROR, JOB_MODEL_STATUS.FINISHED): + return True, jobstatus + return False, JOB_MODEL_STATUS.PROCESSING + + +def wait_job_finish(vnfm_id, vnfo_job_id, vnfm_job_id, progress_range=None, timeout=600, + job_callback=default_callback, **kwargs): + progress_range = [0, 100] if not progress_range else progress_range + response_id = 0 + query_interval = 2 + start_time = end_time = datetime.datetime.now() + while (end_time - start_time).seconds < timeout: + query_status, result = query_vnfm_job(vnfm_id, vnfm_job_id, response_id) + time.sleep(query_interval) + end_time = datetime.datetime.now() + if not query_status: + continue + job_status = ignore_case_get(result, 'responsedescriptor') + response_id_new = ignore_case_get(job_status, 'responseid') + if response_id_new == response_id: + continue + response_id = response_id_new + jobs = ignore_case_get(job_status, 'responsehistorylist', []) + if jobs: + jobs.reverse() + is_end, status = job_callback(vnfo_job_id, vnfm_job_id, job_status, + jobs, progress_range, **kwargs) + if is_end: + return status + return JOB_MODEL_STATUS.TIMEOUT diff --git a/lcm/packages/__init__.py b/lcm/packages/__init__.py new file mode 100644 index 00000000..5580cc3d --- /dev/null +++ b/lcm/packages/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2016 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/packages/nf_package.py b/lcm/packages/nf_package.py new file mode 100644 index 00000000..6c845d55 --- /dev/null +++ b/lcm/packages/nf_package.py @@ -0,0 +1,439 @@ +# Copyright 2016-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 json +import logging +import uuid +import os +import time +import threading +import traceback +import sys + +from lcm.pub.database.models import NfPackageModel, VnfPackageFileModel, NfInstModel +from lcm.pub.utils.values import ignore_case_get +from lcm.pub.utils import fileutil +from lcm.pub.msapi.catalog import STATUS_ONBOARDED, P_STATUS_ENABLED +from lcm.pub.msapi.catalog import P_STATUS_DELETEFAILED, P_STATUS_DELETING +from lcm.pub.msapi.catalog import P_STATUS_NORMAL, P_STATUS_ONBOARDING, P_STATUS_ONBOARDFAILED +from lcm.pub.msapi.catalog import query_csar_from_catalog, set_csar_state +from lcm.pub.msapi.catalog import query_rawdata_from_catalog, delete_csar_from_catalog +from lcm.pub.msapi.catalog import get_download_url_from_catalog +from lcm.pub.exceptions import NSLCMException +from lcm.pub.msapi.extsys import get_vims +from lcm.pub.config.config import IMAGE_ROOT_PATH, IGNORE_DEL_IMG_WEHN_DEL_CSAR +from lcm.pub.nfvi.vim.vimadaptor import VimAdaptor +from lcm.pub.nfvi.vim import const +from lcm.pub.utils.jobutil import JobUtil +from lcm.pub.utils import toscautil + +logger = logging.getLogger(__name__) + +SUPPORT_MULTI_VIM, UNSUPPORT_MULTI_VIM = 1, 0 +ZTE_PRIVATE = 0 +DEPLOY_TYPE_IAAS = 1 +IMAGE_FILE = 2 +IMAGE_STATUS_ENABLE = "Enable" +MAX_RETRY_TIMES = 300 +SLEEP_INTERVAL_SECONDS = 2 +IMAGE_ACTIVE = 'active' +JOB_ERROR = 255 + + +class NfOnBoardingThread(threading.Thread): + """ + NF package onBoarding + """ + + def __init__(self, csar_id, vim_ids, lab_vim_id, job_id): + threading.Thread.__init__(self) + self.csar_id = csar_id + self.vim_ids = vim_ids + self.lab_vim_id = lab_vim_id + self.job_id = job_id + + self.csar_info = None + self.nfd = None + self.nfd_id = None + self.img_save_path = os.path.join(IMAGE_ROOT_PATH, self.job_id) + + self.need_rollback_when_failed = False + + def run(self): + try: + self.on_boarding() + except NSLCMException as e: + self.rollback_on_boarding() + JobUtil.add_job_status(self.job_id, JOB_ERROR, e.message) + except: + logger.error(traceback.format_exc()) + logger.error(str(sys.exc_info())) + self.rollback_on_boarding() + JobUtil.add_job_status(self.job_id, JOB_ERROR, "Failed to onBoarding CSAR(%s)" % self.csar_id) + + def on_boarding(self): + JobUtil.create_job( + inst_type='nf', + jobaction='on_boarding', + inst_id=self.csar_id, + job_id=self.job_id) + JobUtil.add_job_status(self.job_id, 5, "Start CSAR(%s) onBoarding." % self.csar_id) + self.on_boarding_pre_deal() + self.nf_package_save() + self.need_rollback_when_failed = True + nf_images = self.download_nf_images() + self.upload_nf_images(nf_images) + set_csar_state(self.csar_id, "onBoardState", STATUS_ONBOARDED) + set_csar_state(self.csar_id, "processState", P_STATUS_NORMAL) + set_csar_state(self.csar_id, "operationalState", P_STATUS_ENABLED) + JobUtil.add_job_status(self.job_id, 100, "CSAR(%s) onBoarding successfully." % self.csar_id) + + def on_boarding_pre_deal(self): + JobUtil.add_job_status(self.job_id, 10, "Check status of CSAR(%s) from catalog." % self.csar_id) + + self.csar_info = query_csar_from_catalog(self.csar_id) + + on_board_state = ignore_case_get(self.csar_info, "onBoardState") + if on_board_state == STATUS_ONBOARDED: + raise NSLCMException("CSAR(%s) already onBoarded." % self.csar_id) + + process_state = ignore_case_get(self.csar_info, "processState") + if process_state == P_STATUS_ONBOARDING: + raise NSLCMException("CSAR(%s) is onBoarding now." % self.csar_id) + + JobUtil.add_job_status(self.job_id, 20, "Get model of CSAR(%s) from catalog." % self.csar_id) + + raw_data = query_rawdata_from_catalog(self.csar_id) + self.nfd = toscautil.convert_vnfd_model(raw_data["rawData"]) # convert to inner json + self.nfd = json.JSONDecoder().decode(self.nfd) + self.nfd_id = self.nfd["metadata"]["id"] + if NfPackageModel.objects.filter(vnfdid=self.nfd_id): + raise NSLCMException("NFD(%s) already exists." % self.nfd_id) + + def nf_package_save(self): + JobUtil.add_job_status(self.job_id, 30, "Save CSAR(%s) to database." % self.csar_id) + vnfd_ver = self.nfd["metadata"].get("vnfd_version") + if not vnfd_ver: + vnfd_ver = self.nfd["metadata"].get("vnfdVersion") + NfPackageModel( + uuid=str(uuid.uuid4()), + nfpackageid=self.csar_id, + vnfdid=self.nfd_id, + vendor=self.nfd["metadata"].get("vendor", "undefined"), + vnfdversion=vnfd_ver, + vnfversion=self.nfd["metadata"].get("version", "undefined"), + vnfdmodel=json.JSONEncoder().encode(self.nfd) + ).save() + + def download_nf_images(self): + nf_images = [] + for image_file in self.nfd["image_files"]: + img_name = image_file["properties"]["name"] + img_relative_path = image_file["properties"]["file_url"] + img_type = image_file["properties"]["disk_format"] + img_desc = image_file.get("description", "") + img_url, img_local_path = get_download_url_from_catalog(self.csar_id, img_relative_path) + JobUtil.add_job_status(self.job_id, 50, "Start to download Image(%s)." % img_name) + is_download_ok, img_save_full_path = fileutil.download_file_from_http(img_url, + self.img_save_path, img_name) + if not is_download_ok: + raise NSLCMException("Failed to download image from %s" % img_url) + logger.debug("Download Image(%s) to %s successfully.", img_name, img_save_full_path) + nf_images.append({ + "image_url": img_url, + "img_name": img_name, + "img_save_full_path": img_save_full_path, + "img_type": img_type, + "img_desc": img_desc}) + return nf_images + + def upload_nf_images(self, nf_images): + vims = get_vims() + if self.lab_vim_id and (not self.vim_ids): + self.vim_ids = [self.lab_vim_id] + for vim_id in self.vim_ids: + sel_vim = [vim for vim in vims if vim["vimId"] == vim_id] + if not sel_vim: + logger.warn("VIMID(%s) does not exist.", vim_id) + continue + vim_api = VimAdaptor({ + "vimid": vim_id, + "vimtype": sel_vim[0]["type"], + "url": sel_vim[0]["url"], + "user": sel_vim[0]["userName"], + "passwd": sel_vim[0]["password"], + "tenant": sel_vim[0]["tenant"]}) + for nf_image in nf_images: + self.upload_one_nf_image(vim_api, nf_image, vim_id, sel_vim) + fileutil.delete_dirs(self.img_save_path) + + def upload_one_nf_image(self, vim_api, nf_image, vim_id, sel_vim): + JobUtil.add_job_status(self.job_id, 80, "Start to upload Image(%s) to VIM(%s)." % + (nf_image["img_name"], vim_id)) + ret = vim_api.create_image({ + "image_url": nf_image["image_url"], + "image_name": nf_image["img_name"], + "image_path": nf_image["img_save_full_path"], + "image_type": nf_image["img_type"]}) + if ret[0] != 0: + raise NSLCMException("Failed to create image:%s" % ret[1]) + image_id = ret[1]["id"] + + self.wait_until_upload_done(vim_api, image_id) + + VnfPackageFileModel( + vnfpid=self.csar_id, + filename=nf_image["img_name"], + filetype=IMAGE_FILE, + imageid=image_id, + vimid=vim_id, + vimuser=sel_vim[0]["userName"], + tenant=sel_vim[0]["tenant"], + purpose=nf_image["img_desc"], + status=IMAGE_STATUS_ENABLE).save() + + def wait_until_upload_done(self, vim_api, image_id): + retry_times = 0 + image_create_success = False + + while retry_times < MAX_RETRY_TIMES: + retry_times += 1 + ret = vim_api.get_image(image_id=image_id) + if ret[0] != 0: + logging.warn("Failed to query image:%s", ret[1]) + continue + if ret[1]["status"] == IMAGE_ACTIVE: + image_create_success = True + break + time.sleep(SLEEP_INTERVAL_SECONDS) + + if not image_create_success: + timeout_seconds = MAX_RETRY_TIMES * SLEEP_INTERVAL_SECONDS + raise NSLCMException("Failed to create image:timeout(%s seconds.)" % timeout_seconds) + + def rollback_on_boarding(self): + if not self.need_rollback_when_failed: + return + try: + set_csar_state(self.csar_id, "processState", P_STATUS_ONBOARDFAILED) + NfPackageModel.objects.filter(nfpackageid=self.csar_id).delete() + VnfPackageFileModel.objects.filter(vnfpid=self.csar_id).delete() + fileutil.delete_dirs(self.img_save_path) + except: + logger.error(traceback.format_exc()) + logger.error(str(sys.exc_info())) + + +###################################################################################################################### + + +class NfPkgDeleteThread(threading.Thread): + """ + NF Package Deleting + """ + + def __init__(self, csar_id, job_id): + threading.Thread.__init__(self) + self.csar_id = csar_id + self.job_id = job_id + + def run(self): + try: + self.delete_csar() + except NSLCMException as e: + set_csar_state(self.csar_id, "processState", P_STATUS_DELETEFAILED) + JobUtil.add_job_status(self.job_id, JOB_ERROR, e.message) + except: + logger.error(traceback.format_exc()) + logger.error(str(sys.exc_info())) + set_csar_state(self.csar_id, "processState", P_STATUS_DELETEFAILED) + JobUtil.add_job_status(self.job_id, JOB_ERROR, "Failed to delete CSAR(%s)" % self.csar_id) + + def delete_csar(self): + JobUtil.create_job( + inst_type='nf', + jobaction='delete', + inst_id=self.csar_id, + job_id=self.job_id) + JobUtil.add_job_status(self.job_id, 5, "Start to delete CSAR(%s)." % self.csar_id) + if query_csar_from_catalog(self.csar_id, "processState") == P_STATUS_DELETING: + JobUtil.add_job_status(self.job_id, 100, "CSAR(%s) is deleting now." % self.csar_id) + return + + if NfInstModel.objects.filter(package_id=self.csar_id): + ret = set_csar_state(self.csar_id, "deletionPending", True) + JobUtil.add_job_status(self.job_id, 100, ret[1]) + return + + NfPackage().delete_csar(self.csar_id, self.job_id) + + +class NfPkgDeletePendingThread(threading.Thread): + """ + NF Package Delete Pending + """ + + def __init__(self, csar_id, job_id): + threading.Thread.__init__(self) + self.csar_id = csar_id + self.job_id = job_id + + def run(self): + try: + self.delete_pending_csar() + except NSLCMException as e: + set_csar_state(self.csar_id, "processState", P_STATUS_DELETEFAILED) + JobUtil.add_job_status(self.job_id, JOB_ERROR, e.message) + except: + logger.error(traceback.format_exc()) + logger.error(str(sys.exc_info())) + set_csar_state(self.csar_id, "processState", P_STATUS_DELETEFAILED) + JobUtil.add_job_status(self.job_id, JOB_ERROR, "Failed to delete CSAR(%s)" % self.csar_id) + + def delete_pending_csar(self): + JobUtil.create_job( + inst_type='nf', + jobaction='delete_pending', + inst_id=self.csar_id, + job_id=self.job_id) + JobUtil.add_job_status(self.job_id, 5, "Start to delete pending CSAR(%s)." % self.csar_id) + + if not NfPackageModel.objects.filter(nfpackageid=self.csar_id): + JobUtil.add_job_status(self.job_id, 100, "Delete pending CSAR(%s) successfully." % self.csar_id) + return + + csar_info = query_csar_from_catalog(self.csar_id) + + process_state = ignore_case_get(csar_info, "processState") + if process_state == P_STATUS_DELETING: + JobUtil.add_job_status(self.job_id, 100, "CSAR(%s) is deleting now." % self.csar_id) + return + + deletion_pending = ignore_case_get(csar_info, "deletionPending") + if deletion_pending.lower() == "false": + JobUtil.add_job_status(self.job_id, 100, "CSAR(%s) need not to be deleted." % self.csar_id) + return + + if NfInstModel.objects.filter(package_id=self.csar_id): + JobUtil.add_job_status(self.job_id, 100, "CSAR(%s) is in using, cannot be deleted." % self.csar_id) + return + + NfPackage().delete_csar(self.csar_id, self.job_id) + + +#################################################################################################################### +class NfPackage(object): + """ + Actions for nf package. + """ + + def __init__(self): + pass + + def get_csars(self): + ret = {"csars": []} + nf_pkgs = NfPackageModel.objects.filter() + for nf_pkg in nf_pkgs: + ret["csars"].append({ + "csarId": nf_pkg.nfpackageid, + "vnfdId": nf_pkg.vnfdid + }) + return ret + + def get_csar(self, csar_id): + pkg_info = {} + nf_pkg = NfPackageModel.objects.filter(nfpackageid=csar_id) + if nf_pkg: + pkg_info["vnfdId"] = nf_pkg[0].vnfdid + pkg_info["vnfdProvider"] = nf_pkg[0].vendor + pkg_info["vnfdVersion"] = nf_pkg[0].vnfdversion + pkg_info["vnfVersion"] = nf_pkg[0].vnfversion + + casrinfo = query_csar_from_catalog(csar_id) + props_of_catalog = [ + "name", "provider", "version", "operationalState", "usageState", + "onBoardState", "processState", "deletionPending", "downloadUri", + "createTime", "modifyTime", "format", "size"] + for prop in props_of_catalog: + pkg_info[prop] = ignore_case_get(casrinfo, prop) + + nf_pkg_files = VnfPackageFileModel.objects.filter(vnfpid=csar_id) + img_info = [{ + "index": str(i), + "fileName": nf_pkg_files[i].filename, + "imageId": nf_pkg_files[i].imageid, + "vimId": nf_pkg_files[i].vimid, + "vimUser": nf_pkg_files[i].vimuser, + "tenant": nf_pkg_files[i].tenant, + "status": nf_pkg_files[i].status} + for i in range(len(nf_pkg_files))] + + vnf_insts = NfInstModel.objects.filter(package_id=csar_id) + vnf_inst_info = [{"vnfInstanceId": vnf_inst.nfinstid, + "vnfInstanceName": vnf_inst.nf_name} for vnf_inst in vnf_insts] + + return [0, {"csarId": csar_id, + "packageInfo": pkg_info, + "imageInfo": img_info, + "vnfInstanceInfo": vnf_inst_info}] + + def delete_csar(self, csar_id, job_id): + JobUtil.add_job_status(job_id, 10, "Set processState of CSAR(%s)." % csar_id) + set_csar_state(csar_id, "processState", P_STATUS_DELETING) + + JobUtil.add_job_status(job_id, 20, "Get package files of CSAR(%s)." % csar_id) + all_nf_pkg_files = VnfPackageFileModel.objects.all() + nf_pkg_files = VnfPackageFileModel.objects.filter(vnfpid=csar_id) + vims = get_vims() + + for pkg_file in nf_pkg_files: + if IGNORE_DEL_IMG_WEHN_DEL_CSAR: + logger.warn("ignore delete image(%s)" % pkg_file.filename) + continue + JobUtil.add_job_status(job_id, 50, "Delete image(%s) of CSAR(%s)." % + (pkg_file.filename, csar_id)) + if self.is_image_refed_by_other_nf_pkg(all_nf_pkg_files, pkg_file.imageid, csar_id, pkg_file.vimid): + logger.warn("Image(%s) is refered by CSAR(%s).", pkg_file.filename, csar_id) + continue + sel_vim = [vim for vim in vims if vim["vimId"] == pkg_file.vimid] + if not sel_vim: + logger.warn("Vim(%s) does not exist.", pkg_file.vimid) + continue + vim_api = VimAdaptor({ + "vimid": pkg_file.vimid, + "vimtype": sel_vim[0]["type"], + "url": sel_vim[0]["url"], + "user": sel_vim[0]["userName"], + "passwd": sel_vim[0]["password"], + "tenant": sel_vim[0]["tenant"]}) + ret = vim_api.delete_image(pkg_file.imageid) + if ret[0] != 0: + logger.error("Failed to delete image(%s) from vim(%s)", pkg_file.filename, pkg_file.vimid) + + JobUtil.add_job_status(job_id, 70, "Delete CSAR(%s) from catalog." % csar_id) + ret = delete_csar_from_catalog(csar_id) + if ret[0] != 0: + raise NSLCMException(ret[1]) + + JobUtil.add_job_status(job_id, 90, "Delete CSAR(%s) from database." % csar_id) + VnfPackageFileModel.objects.filter(vnfpid=csar_id).delete() + NfPackageModel.objects.filter(nfpackageid=csar_id).delete() + + JobUtil.add_job_status(job_id, 100, "Delete CSAR(%s) successfully." % csar_id) + + def is_image_refed_by_other_nf_pkg(self, nf_pkg_files, imageid, csar_id, vim_id): + for f in nf_pkg_files: + if f.imageid == imageid and f.vimid == vim_id and f.vnfpid != csar_id: + return True + return False diff --git a/lcm/packages/ns_package.py b/lcm/packages/ns_package.py new file mode 100644 index 00000000..4a7fb303 --- /dev/null +++ b/lcm/packages/ns_package.py @@ -0,0 +1,188 @@ +# Copyright 2016 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 json +import logging + +import traceback +import sys + +from lcm.pub.database.models import NSDModel, NSInstModel, NfPackageModel +from lcm.pub.utils.values import ignore_case_get +from lcm.pub.msapi.catalog import STATUS_ONBOARDED, P_STATUS_ENABLED +from lcm.pub.msapi.catalog import query_csar_from_catalog, set_csar_state +from lcm.pub.msapi.catalog import query_rawdata_from_catalog, delete_csar_from_catalog +from lcm.pub.exceptions import NSLCMException +from lcm.pub.utils import toscautil + +logger = logging.getLogger(__name__) + +STATUS_SUCCESS, STATUS_FAILED = "success", "failed" + + +def fmt_ns_pkg_rsp(status, desc, error_code="500"): + return [0, {"status": status, "statusDescription": desc, "errorCode": error_code}] + + +def ns_common_call(fun, csar_id, operation=""): + ret = None + try: + if operation == "": + ret = fun(csar_id) + else: + ret = fun(csar_id, operation) + + if ret[0] != 0: + return fmt_ns_pkg_rsp(STATUS_FAILED, ret[1]) + except NSLCMException as e: + return fmt_ns_pkg_rsp(STATUS_FAILED, e.message) + except: + logger.error(traceback.format_exc()) + return fmt_ns_pkg_rsp(STATUS_FAILED, str(sys.exc_info())) + return fmt_ns_pkg_rsp(STATUS_SUCCESS, ret[1], "") + +def ns_on_boarding(csar_id): + return ns_common_call(NsPackage().on_boarding, csar_id) + + +def ns_delete_csar(csar_id): + return ns_common_call(NsPackage().delete_csar, csar_id) + + +def ns_delete_pending_csar(csar_id): + return ns_common_call(NsPackage().delete_pending_csar, csar_id) + + +def ns_set_state_csar(csar_id, operation): + return ns_common_call(NsPackage().set_state_csar, csar_id, operation) + + +def ns_get_csar(csar_id): + ret = None + try: + ret = NsPackage().get_csar(csar_id) + except NSLCMException as e: + return [1, e.message] + except: + logger.error(traceback.format_exc()) + return [1, str(sys.exc_info())] + return ret + + +############################################################################################################### + + +class NsPackage(object): + """ + Actions for ns package. + """ + + def __init__(self): + pass + + def on_boarding(self, csar_id): + csar = query_csar_from_catalog(csar_id) + if ignore_case_get(csar, "onBoardState") == STATUS_ONBOARDED: + raise NSLCMException("CSAR(%s) already onBoarded." % csar_id) + + raw_data = query_rawdata_from_catalog(csar_id) + nsd = toscautil.convert_nsd_model(raw_data["rawData"]) # convert to inner json + nsd = json.JSONDecoder().decode(nsd) + nsd_id = nsd["metadata"]["id"] + if NSDModel.objects.filter(nsd_id=nsd_id): + raise NSLCMException("NSD(%s) already exists." % nsd_id) + + for vnf in nsd["vnfs"]: + vnfd_id = vnf["properties"]["id"] + pkg = NfPackageModel.objects.filter(vnfdid=vnfd_id) + if not pkg: + raise NSLCMException("VNF package(%s) is not onBoarded." % vnfd_id) + pkg_id = pkg[0].nfpackageid + if query_csar_from_catalog(pkg_id, "onBoardState") != STATUS_ONBOARDED: + raise NSLCMException("VNF package(%s) is not onBoarded on catalog." % pkg_id) + + NSDModel( + id=csar_id, + nsd_id=nsd_id, + name=nsd["metadata"].get("name", nsd_id), + vendor=nsd["metadata"].get("vendor", "undefined"), + description=nsd["metadata"].get("description", ""), + version=nsd["metadata"].get("version", "undefined"), + nsd_model=json.JSONEncoder().encode(nsd)).save() + + set_csar_state(csar_id, "onBoardState", STATUS_ONBOARDED) + set_csar_state(csar_id, "operationalState", P_STATUS_ENABLED) + + return [0, "CSAR(%s) onBoarded successfully." % csar_id] + + def delete_csar(self, csar_id): + if not NSDModel.objects.filter(id=csar_id): + return delete_csar_from_catalog(csar_id) + + if NSInstModel.objects.filter(nspackage_id=csar_id): + return set_csar_state(csar_id, "deletionPending", True) + + ret = delete_csar_from_catalog(csar_id) + if ret[0] == 0: + NSDModel.objects.filter(id=csar_id).delete() + return ret + + def delete_pending_csar(self, csar_id): + if not NSDModel.objects.filter(id=csar_id): + return [0, "Delete pending CSAR(%s) successfully." % csar_id] + + pending = query_csar_from_catalog(csar_id, "deletionPending") + + if pending.lower() == "false": + return [1, "CSAR(%s) need not to be deleted." % csar_id] + + if NSInstModel.objects.filter(nspackage_id=csar_id): + return [1, "CSAR(%s) is in using, cannot be deleted." % csar_id] + + ret = delete_csar_from_catalog(csar_id) + if ret[0] == 0: + NSDModel.objects.filter(id=csar_id).delete() + return ret + + def get_csar(self, csar_id): + package_info = {} + + csar_in_catalog = query_csar_from_catalog(csar_id) + props_of_catalog = [ + "name", "provider", "version", "operationalState", "usageState", + "onBoardState", "processState", "deletionPending", "downloadUri", + "createTime", "modifyTime", "format", "size"] + for prop in props_of_catalog: + package_info[prop] = ignore_case_get(csar_in_catalog, prop) + + csars = NSDModel.objects.filter(id=csar_id) + if csars: + package_info["nsdId"] = csars[0].nsd_id + package_info["nsdProvider"] = csars[0].vendor + package_info["nsdVersion"] = csars[0].version + + nss = NSInstModel.objects.filter(nspackage_id=csar_id) + ns_instance_info = [{"nsInstanceId": ns.id, "nsInstanceName": ns.name} for ns in nss] + + return [0, {"csarId": csar_id, "packageInfo": package_info, "nsInstanceInfo": ns_instance_info}] + + + def set_state_csar(self, csar_id, operation): + if not NSDModel.objects.filter(id=csar_id): + raise NSLCMException("CSAR(%s) does not exist." % csar_id) + + csar = query_csar_from_catalog(csar_id) + if ignore_case_get(csar, "operationalState") == operation.capitalize(): + raise NSLCMException("CSAR(%s) already %s." % (csar_id, operation)) + return set_csar_state(csar_id, 'operationState', operation.capitalize()) diff --git a/lcm/packages/tests/__init__.py b/lcm/packages/tests/__init__.py new file mode 100644 index 00000000..5580cc3d --- /dev/null +++ b/lcm/packages/tests/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2016 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/packages/tests/test_nf.py b/lcm/packages/tests/test_nf.py new file mode 100644 index 00000000..fad4cbe6 --- /dev/null +++ b/lcm/packages/tests/test_nf.py @@ -0,0 +1,471 @@ +# Copyright 2016-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 json +import mock +from rest_framework import status +from django.test import TestCase +from django.test import Client + +from lcm.pub.utils import restcall +from lcm.pub.utils import fileutil +from lcm.pub.nfvi.vim.vimadaptor import VimAdaptor +from lcm.pub.database.models import NfPackageModel, VnfPackageFileModel, NfInstModel +from lcm.pub.database.models import JobStatusModel, JobModel +from lcm.packages.nf_package import NfOnBoardingThread, NfPkgDeletePendingThread +from lcm.packages.nf_package import NfPkgDeleteThread +from lcm.packages import nf_package +from lcm.pub.nfvi.vim.const import VIM_OPENSTACK + + +class TestNfPackage(TestCase): + def setUp(self): + self.client = Client() + NfPackageModel.objects.filter().delete() + VnfPackageFileModel.objects.filter().delete() + NfInstModel.objects.filter().delete() + JobModel.objects.filter().delete() + JobStatusModel.objects.filter().delete() + self.vnfd_raw_data = { + "rawData":{ + "instance":{ + "metadata":{ + "is_shared":False, + "plugin_info":"vbrasplugin_1.0", + "vendor":"zte", + "request_reclassification":False, + "name":"vbras", + "version":1, + "vnf_type":"vbras", + "cross_dc":False, + "vnfd_version":"1.0.0", + "id":"zte_vbras_1.0", + "nsh_aware":True + }, + "nodes":[ + { + "id":"aaa_dnet_cp_0xu2j5sbigxc8h1ega3if0ld1", + "type_name":"tosca.nodes.nfv.ext.zte.CP", + "template_name":"aaa_dnet_cp", + "properties":{ + "bandwidth":{ + "type_name":"integer", + "value":0 + }, + "direction":{ + "type_name":"string", + "value":"bidirectional" + }, + "vnic_type":{ + "type_name":"string", + "value":"normal" + }, + "sfc_encapsulation":{ + "type_name":"string", + "value":"mac" + }, + "order":{ + "type_name":"integer", + "value":2 + } + }, + "relationships":[ + { + "name":"guest_os", + "source_requirement_index":0, + "target_node_id":"AAA_image_d8aseebr120nbm7bo1ohkj194", + "target_capability_name":"feature" + } + ] + }, + { + "id":"LB_Image_oj5l2ay8l2g6vcq6fsswzduha", + "type_name":"tosca.nodes.nfv.ext.ImageFile", + "template_name":"LB_Image", + "properties":{ + "disk_format":{ + "type_name":"string", + "value":"qcow2" + }, + "file_url":{ + "type_name":"string", + "value":"/SoftwareImages/image-lb" + }, + "name":{ + "type_name":"string", + "value":"image-lb" + } + } + } + ] + }, + "model":{ + "metadata":{ + "is_shared":False, + "plugin_info":"vbrasplugin_1.0", + "vendor":"zte", + "request_reclassification":False, + "name":"vbras", + "version":1, + "vnf_type":"vbras", + "cross_dc":False, + "vnfd_version":"1.0.0", + "id":"zte_vbras_1.0", + "nsh_aware":True + }, + "node_templates":[ + { + "name":"aaa_dnet_cp", + "type_name":"tosca.nodes.nfv.ext.zte.CP", + "default_instances":1, + "min_instances":0, + "properties":{ + "bandwidth":{ + "type_name":"integer", + "value":0 + } + }, + "requirement_templates":[ + { + "name":"virtualbinding", + "target_node_template_name":"AAA", + "target_capability_name":"virtualbinding" + } + ] + } + ] + } + } + } + + def tearDown(self): + pass + + def assert_job_result(self, job_id, job_progress, job_detail): + jobs = JobStatusModel.objects.filter( + jobid=job_id, + progress=job_progress, + descp=job_detail) + self.assertEqual(1, len(jobs)) + + @mock.patch.object(NfOnBoardingThread, 'run') + def test_nf_pkg_on_boarding_normal(self, mock_run): + resp = self.client.post("/openoapi/nslcm/v1/vnfpackage", { + "csarId": "1", + "vimIds": ["1"] + }, format='json') + self.assertEqual(resp.status_code, status.HTTP_202_ACCEPTED) + + @mock.patch.object(restcall, 'call_req') + def test_nf_pkg_on_boarding_when_on_boarded(self, mock_call_req): + mock_call_req.return_value = [0, json.JSONEncoder().encode({"onBoardState": "onBoarded"}), '200'] + NfOnBoardingThread(csar_id="1", + vim_ids=["1"], + lab_vim_id="", + job_id="2").run() + self.assert_job_result("2", 255, "CSAR(1) already onBoarded.") + + @mock.patch.object(restcall, 'call_req') + def test_nf_pkg_on_boarding_when_on_boarding(self, mock_call_req): + mock_call_req.return_value = [0, json.JSONEncoder().encode({ + "onBoardState": "non-onBoarded", + "processState": "onBoarding" + }), '200'] + NfOnBoardingThread(csar_id="2", + vim_ids=["1"], + lab_vim_id="", + job_id="3").run() + self.assert_job_result("3", 255, "CSAR(2) is onBoarding now.") + + @mock.patch.object(restcall, 'call_req') + def test_nf_on_boarding_when_nfd_already_exists(self, mock_call_req): + mock_vals = { + "/openoapi/catalog/v1/csars/2": + [0, json.JSONEncoder().encode({ + "onBoardState": "onBoardFailed", "processState": "deleteFailed"}), '200'], + "/openoapi/catalog/v1/servicetemplates/queryingrawdata": + [0, json.JSONEncoder().encode(self.vnfd_raw_data), '200']} + + def side_effect(*args): + return mock_vals[args[4]] + + mock_call_req.side_effect = side_effect + NfPackageModel(uuid="1", nfpackageid="2", vnfdid="zte_vbras_1.0").save() + NfOnBoardingThread(csar_id="2", vim_ids=["1"], lab_vim_id="", job_id="4").run() + self.assert_job_result("4", 255, "NFD(zte_vbras_1.0) already exists.") + + @mock.patch.object(restcall, 'call_req') + @mock.patch.object(fileutil, 'download_file_from_http') + @mock.patch.object(VimAdaptor, '__init__') + @mock.patch.object(VimAdaptor, 'create_image') + @mock.patch.object(VimAdaptor, 'get_image') + def test_nf_on_boarding_when_successfully(self, mock_get_image, mock_create_image, + mock__init__, mock_download_file_from_http, mock_call_req): + mock_download_file_from_http.return_value = True, "/root/package" + mock_vals = { + "/openoapi/catalog/v1/csars/2": + [0, json.JSONEncoder().encode({ + "onBoardState": "onBoardFailed", "processState": "deleteFailed"}), '200'], + "/openoapi/catalog/v1/servicetemplates/queryingrawdata": + [0, json.JSONEncoder().encode(self.vnfd_raw_data), '200'], + "/openoapi/catalog/v1/csars/2/files?relativePath=/SoftwareImages/image-lb": + [0, json.JSONEncoder().encode({ + "csar_file_info": [{"downloadUri": "8"}, {"localPath": "9"}]}), '200'], + "/openoapi/extsys/v1/vims": + [0, json.JSONEncoder().encode([{ + "vimId": "1", "type": VIM_OPENSTACK, + "url": "/root/package", "userName": "tom", + "password": "tom", "tenant": "10"}]), '200'], + "/openoapi/catalog/v1/csars/2?onBoardState=onBoarded": [0, '{}', 200], + "/openoapi/catalog/v1/csars/2?operationalState=Enabled": [0, '{}', 200], + "/openoapi/catalog/v1/csars/2?processState=normal": [0, '{}', 200]} + mock_create_image.return_value = [0, {"id": "30", "name": "jerry", "res_type": 0}] + mock__init__.return_value = None + mock_get_image.return_value = [0, {"id": "30", "name": "jerry", "size": "60", "status": "active"}] + + def side_effect(*args): + return mock_vals[args[4]] + mock_call_req.side_effect = side_effect + + NfOnBoardingThread(csar_id="2", vim_ids=["1"], lab_vim_id="", job_id="4").run() + self.assert_job_result("4", 100, "CSAR(2) onBoarding successfully.") + + @mock.patch.object(restcall, 'call_req') + @mock.patch.object(fileutil, 'download_file_from_http') + @mock.patch.object(VimAdaptor, '__init__') + @mock.patch.object(VimAdaptor, 'create_image') + @mock.patch.object(VimAdaptor, 'get_image') + def test_nf_on_boarding_when_timeout(self, mock_get_image, mock_create_image, + mock__init__, mock_download_file_from_http, mock_call_req): + nf_package.MAX_RETRY_TIMES = 2 + nf_package.SLEEP_INTERVAL_SECONDS = 1 + mock_download_file_from_http.return_value = True, "/root/package" + mock_vals = { + "/openoapi/catalog/v1/csars/3": + [0, json.JSONEncoder().encode({"onBoardState": "onBoardFailed", + "processState": "deleteFailed"}), '200'], + "/openoapi/catalog/v1/servicetemplates/queryingrawdata": + [0, json.JSONEncoder().encode(self.vnfd_raw_data), '200'], + "/openoapi/catalog/v1/csars/3/files?relativePath=/SoftwareImages/image-lb": + [0, json.JSONEncoder().encode({ + "csar_file_info": [{"downloadUri": "8"}, {"localPath": "9"}]}), '200'], + "/openoapi/catalog/v1/csars/3?processState=onBoardFailed": [0, '{}', 200], + "/openoapi/extsys/v1/vims": + [0, json.JSONEncoder().encode([{ + "vimId": "1", "type": VIM_OPENSTACK, + "url": "/root/package", "userName": "tom", + "password": "tom", "tenant": "10"}]), 200]} + mock_create_image.return_value = [0, {"id": "30", "name": "jerry", "res_type": 0}] + mock__init__.return_value = None + mock_get_image.return_value = [0, {"id": "30", "name": "jerry", "size": "60", "status": "0"}] + + def side_effect(*args): + return mock_vals[args[4]] + + mock_call_req.side_effect = side_effect + NfOnBoardingThread(csar_id="3", vim_ids=["1"], lab_vim_id="", job_id="6").run() + self.assert_job_result("6", 255, "Failed to create image:timeout(2 seconds.)") + + @mock.patch.object(restcall, 'call_req') + @mock.patch.object(fileutil, 'download_file_from_http') + @mock.patch.object(VimAdaptor, '__init__') + @mock.patch.object(VimAdaptor, 'create_image') + def test_nf_on_boarding_when_failed_to_create_image(self, mock_create_image, + mock__init__, mock_download_file_from_http, mock_call_req): + mock_download_file_from_http.return_value = True, "/root/package" + mock_vals = { + "/openoapi/catalog/v1/csars/5": + [0, json.JSONEncoder().encode({ + "onBoardState": "onBoardFailed", "processState": "deleteFailed"}), '200'], + "/openoapi/catalog/v1/servicetemplates/queryingrawdata": + [0, json.JSONEncoder().encode(self.vnfd_raw_data), '200'], + "/openoapi/catalog/v1/csars/5/files?relativePath=/SoftwareImages/image-lb": + [0, json.JSONEncoder().encode({ + "csar_file_info": [{"downloadUri": "8"}, {"localPath": "9"}]}), '200'], + "/openoapi/catalog/v1/csars/5?processState=onBoardFailed": [0, '{}', 200], + "/openoapi/extsys/v1/vims": + [0, json.JSONEncoder().encode([{ + "vimId": "1", "type": VIM_OPENSTACK, + "url": "/root/package", "userName": "tom", + "password": "tom", "tenant": "10"}]), '200']} + mock_create_image.return_value = [1, 'Unsupported image format.'] + mock__init__.return_value = None + + def side_effect(*args): + return mock_vals[args[4]] + mock_call_req.side_effect = side_effect + NfOnBoardingThread(csar_id="5", vim_ids=["1"], lab_vim_id="", job_id="8").run() + self.assert_job_result("8", 255, "Failed to create image:Unsupported image format.") + + ######################################################################### + @mock.patch.object(restcall, 'call_req') + def test_get_csar_successfully(self, mock_call_req): + mock_call_req.return_value = [0, json.JSONEncoder().encode({ + "name": "1", "provider": "2", "version": "3", "operationalState": "4", + "usageState": "5", "onBoardState": "6", "processState": "7", + "deletionPending": "8", "downloadUri": "9", "createTime": "10", + "modifyTime": "11", "format": "12", "size": "13" + }), '200'] + NfPackageModel(uuid="1", vnfdid="001", vendor="vendor", + vnfdversion="1.2.0", vnfversion="1.1.0", nfpackageid="13").save() + VnfPackageFileModel(id="1", filename="filename", imageid="00001", + vimid="1", vimuser="001", tenant="12", status="1", vnfpid="13").save() + NfInstModel(nfinstid="1", mnfinstid="001", nf_name="name", package_id="13").save() + resp = self.client.get("/openoapi/nslcm/v1/vnfpackage/13") + self.assertEqual(resp.status_code, status.HTTP_200_OK) + expect_data = { + "csarId": '13', + "packageInfo": { + "vnfdId": "001", + "vnfdProvider": "vendor", + "vnfdVersion": "1.2.0", + "vnfVersion": "1.1.0", + "name": "1", + "provider": "2", + "version": "3", + "operationalState": "4", + "usageState": "5", + "onBoardState": "6", + "processState": "7", + "deletionPending": "8", + "downloadUri": "9", + "createTime": "10", + "modifyTime": "11", + "format": "12", + "size": "13"}, + "imageInfo": [{ + "index": "0", + "fileName": "filename", + "imageId": "00001", + "vimId": "1", + "vimUser": "001", + "tenant": "12", + "status": "1"}], + "vnfInstanceInfo": [{ + "vnfInstanceId": "1", + "vnfInstanceName": "name"}]} + self.assertEqual(expect_data, resp.data) + + ######################################################################### + @mock.patch.object(restcall, 'call_req') + def test_delete_pending_csar_when_successfully(self, mock_call_req): + mock_call_req.return_value = [0, json.JSONEncoder().encode({ + "processState": "deleting"}), "200"] + NfPkgDeletePendingThread(csar_id="1", job_id='2').run() + self.assert_job_result("2", 100, "Delete pending CSAR(1) successfully.") + + @mock.patch.object(restcall, 'call_req') + def test_delete_pending_csar_when_deleting(self, mock_call_req): + NfPackageModel(uuid="01", nfpackageid="1").save() + mock_call_req.return_value = [0, json.JSONEncoder().encode({ + "processState": "deleting"}), "200"] + NfPkgDeletePendingThread(csar_id="1", job_id='2').run() + self.assert_job_result("2", 100, "CSAR(1) is deleting now.") + + @mock.patch.object(restcall, 'call_req') + def test_delete_pending_csar_when_not_deletion_pending(self, mock_call_req): + NfPackageModel(uuid="01", nfpackageid="1").save() + mock_call_req.return_value = [0, json.JSONEncoder().encode({ + "deletionPending": "false"}), "200"] + NfPkgDeletePendingThread(csar_id="1", job_id='2').run() + self.assert_job_result("2", 100, "CSAR(1) need not to be deleted.") + + @mock.patch.object(restcall, 'call_req') + def test_delete_pending_csar_when_in_using(self, mock_call_req): + mock_call_req.return_value = [0, json.JSONEncoder().encode({ + "processState": "normal"}), "200"] + NfPackageModel(uuid="01", nfpackageid="1").save() + NfInstModel(nfinstid="01", package_id="1").save() + NfPkgDeletePendingThread(csar_id="1", job_id='2').run() + self.assert_job_result("2", 100, "CSAR(1) is in using, cannot be deleted.") + + @mock.patch.object(VimAdaptor, '__init__') + @mock.patch.object(VimAdaptor, 'delete_image') + @mock.patch.object(restcall, 'call_req') + def test_delete_csarr_when_exception(self, mock_call_req, mock_delete_image, mock_init_): + mock_vals = { + ("/openoapi/catalog/v1/csars/1", "DELETE"): + [1, "{}", "400"], + ("/openoapi/catalog/v1/csars/1?processState=deleting", "PUT"): + [0, "{}", "200"], + ("/openoapi/catalog/v1/csars/1?processState=deleteFailed", "PUT"): + [0, "{}", "200"], + ("/openoapi/catalog/v1/csars/1", "GET"): + [0, json.JSONEncoder().encode({"processState": "normal"}), "200"], + ("/openoapi/extsys/v1/vims", "GET"): + [0, json.JSONEncoder().encode([{"vimId": "002", + "url": "url_test", + "userName": "test01", + "password": "123456", + "tenant": "test"}]), "200"]} + mock_delete_image.return_value = [0, "", '200'] + + def side_effect(*args): + return mock_vals[(args[4], args[5])] + + mock_call_req.side_effect = side_effect + mock_init_.return_value = None + VnfPackageFileModel(vnfpid="1", imageid="001", vimid="002").save() + NfPackageModel(uuid="01", nfpackageid="1").save() + NfPkgDeletePendingThread(csar_id="1", job_id='2').run() + self.assert_job_result("2", 255, "Failed to delete CSAR(1) from catalog.") + + @mock.patch.object(VimAdaptor, '__init__') + @mock.patch.object(VimAdaptor, 'delete_image') + @mock.patch.object(restcall, 'call_req') + def test_delete_csar_when_successfully(self, mock_call_req, mock_delete_image, mock_init_): + mock_vals = { + ("/openoapi/catalog/v1/csars/1", "DELETE"): + [0, json.JSONEncoder().encode({"successfully": "successfully"}), "200"], + ("/openoapi/catalog/v1/csars/1?processState=deleting", "PUT"): + [0, json.JSONEncoder().encode({"successfully": "successfully"}), "200"], + ("/openoapi/catalog/v1/csars/1?processState=deleteFailed", "PUT"): + [0, json.JSONEncoder().encode({"successfully": "successfully"}), "200"], + ("/openoapi/catalog/v1/csars/1", "GET"): + [0, json.JSONEncoder().encode({"notProcessState": "notProcessState"}), "200"], + ("/openoapi/extsys/v1/vims", "GET"): + [0, json.JSONEncoder().encode([{ + "vimId": "002", + "url": "url_test", + "userName": "test01", + "password": "123456", + "tenant": "test"}]), "200"]} + mock_delete_image.return_value = [0, json.JSONEncoder().encode({"test": "test"}), '200'] + + def side_effect(*args): + return mock_vals[(args[4], args[5])] + + mock_call_req.side_effect = side_effect + mock_init_.return_value = None + VnfPackageFileModel(vnfpid="1", imageid="001", vimid="002").save() + NfPackageModel(uuid="01", nfpackageid="1").save() + NfPkgDeletePendingThread(csar_id="1", job_id='2').run() + self.assert_job_result("2", 100, "Delete CSAR(1) successfully.") + + ######################################################################### + @mock.patch.object(restcall, 'call_req') + def test_delete_nf_pkg_when_deleting(self, mock_call_req): + mock_call_req.return_value = [0, json.JSONEncoder().encode({"processState": "deleting"}), '200'] + NfPkgDeleteThread(csar_id="1", job_id="2").run() + self.assert_job_result("2", 100, "CSAR(1) is deleting now.") + + + def test_get_nf_csars_normal(self): + NfPackageModel(uuid="01", nfpackageid="1", vnfdid="2").save() + resp = self.client.get("/openoapi/nslcm/v1/vnfpackage") + self.assertEqual(resp.status_code, status.HTTP_200_OK) + self.assertEqual(1, len(resp.data["csars"])) + self.assertEqual("1", resp.data["csars"][0]["csarId"]) + self.assertEqual("2", resp.data["csars"][0]["vnfdId"]) + + diff --git a/lcm/packages/tests/test_ns.py b/lcm/packages/tests/test_ns.py new file mode 100644 index 00000000..391297df --- /dev/null +++ b/lcm/packages/tests/test_ns.py @@ -0,0 +1,418 @@ +# Copyright 2016 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 json +import mock +from rest_framework import status +from django.test import TestCase +from django.test import Client + +from lcm.pub.utils import restcall +from lcm.pub.database.models import NSDModel, NSInstModel, NfPackageModel + + +class TestNsPackage(TestCase): + def setUp(self): + self.client = Client() + NSDModel.objects.filter().delete() + NSInstModel.objects.filter().delete() + NfPackageModel.objects.filter().delete() + self.nsd_raw_data = { + "rawData":{ + "instance":{ + "metadata":{ + "vendor":"ZTE", + "name":"VBRAS_NS", + "csarVersion":1, + "csarType":"NSAR", + "csarProvider":"ZTE", + "version":1, + "invariant_id":"VBRAS_NS_NO_SFC", + "id":"VBRAS_NS_ZTE_1.0", + "description":"VBRAS_ZTE_NS" + }, + "nodes":[ + { + "id":"VBras_yfye7lsgi73p8j4p2a6vbguzd", + "type_name":"tosca.nodes.nfv.ext.zte.VNF.VBras", + "template_name":"VBras", + "properties":{ + "vendor":{ + "type_name":"string", + "value":"zte" + }, + "name":{ + "type_name":"string", + "value":"vbras" + }, + "version":{ + "type_name":"string", + "value":"1.0" + }, + "vnf_type":{ + "type_name":"string", + "value":"vbras" + }, + "vnfd_version":{ + "type_name":"string", + "value":"1.0.0" + }, + "id":{ + "type_name":"string", + "value":"zte_vbras_1.0" + } + } + } + ] + }, + "model":{ + "metadata":{ + "vendor":"ZTE", + "name":"VBRAS_NS", + "csarVersion":1, + "csarType":"NSAR", + "csarProvider":"ZTE", + "version":1, + "invariant_id":"VBRAS_NS_NO_SFC", + "id":"VBRAS_NS_ZTE_1.0", + "description":"VBRAS_ZTE_NS" + }, + "node_templates":[ + { + "name":"VBras", + "type_name":"tosca.nodes.nfv.ext.zte.VNF.VBras", + "default_instances":1, + "min_instances":0, + "properties":{ + "vendor":{ + "type_name":"string", + "value":"zte" + }, + "name":{ + "type_name":"string", + "value":"vbras" + }, + "version":{ + "type_name":"string", + "value":"1.0" + }, + "vnf_type":{ + "type_name":"string", + "value":"vbras" + }, + "vnfd_version":{ + "type_name":"string", + "value":"1.0.0" + }, + "id":{ + "type_name":"string", + "value":"zte_vbras_1.0" + } + }, + "requirement_templates":[ + { + "name":"lb_mnet_vl_cp", + "target_node_template_name":"ext_mnet_net", + "target_capability_name":"virtual_linkable" + } + ] + } + ] + } + } + } + + def tearDown(self): + pass + + def set_nsd_metadata(self, key, val): + self.nsd_raw_data["rawData"]["instance"]["metadata"][key] = val + + def set_nsd_vnf_id(self, val): + self.nsd_raw_data["rawData"]["instance"]["nodes"][0]["properties"]["id"]["value"] = val + + @mock.patch.object(restcall, 'call_req') + def test_ns_pkg_on_boarding_when_on_boarded(self, mock_call_req): + mock_call_req.return_value = [0, json.JSONEncoder().encode({"onBoardState": "onBoarded"}), '200'] + resp = self.client.post("/openoapi/nslcm/v1/nspackage", {"csarId": "1"}, format='json') + self.assertEqual(resp.status_code, status.HTTP_202_ACCEPTED) + self.assertEqual("failed", resp.data["status"]) + self.assertEqual("CSAR(1) already onBoarded.", resp.data["statusDescription"]) + + @mock.patch.object(restcall, 'call_req') + def test_ns_pkg_on_boarding_when_nsd_already_exists(self, mock_call_req): + self.set_nsd_metadata(key="id", val="2") + mock_vals = { + "/openoapi/catalog/v1/csars/2": + [0, json.JSONEncoder().encode({"onBoardState": "non-onBoarded"}), '200'], + "/openoapi/catalog/v1/servicetemplates/queryingrawdata": + [0, json.JSONEncoder().encode(self.nsd_raw_data), '200']} + + def side_effect(*args): + return mock_vals[args[4]] + mock_call_req.side_effect = side_effect + + NSDModel(id="1", nsd_id="2").save() + resp = self.client.post("/openoapi/nslcm/v1/nspackage", {"csarId": "2"}, format='json') + self.assertEqual(resp.status_code, status.HTTP_202_ACCEPTED) + self.assertEqual("failed", resp.data["status"]) + self.assertEqual("NSD(2) already exists.", resp.data["statusDescription"]) + + @mock.patch.object(restcall, 'call_req') + def test_ns_pkg_on_boarding_when_vnf_pkg_not_on_boarded(self, mock_call_req): + self.set_nsd_metadata(key="id", val="2") + self.set_nsd_vnf_id(val="3") + mock_vals = { + "/openoapi/catalog/v1/csars/3": + [0, json.JSONEncoder().encode({"onBoardState": "non-onBoarded"}), '200'], + "/openoapi/catalog/v1/servicetemplates/queryingrawdata": + [0, json.JSONEncoder().encode(self.nsd_raw_data), '200']} + + def side_effect(*args): + return mock_vals[args[4]] + mock_call_req.side_effect = side_effect + + resp = self.client.post("/openoapi/nslcm/v1/nspackage", {"csarId": "3"}, format='json') + self.assertEqual(resp.status_code, status.HTTP_202_ACCEPTED) + self.assertEqual("failed", resp.data["status"]) + self.assertEqual("VNF package(3) is not onBoarded.", resp.data["statusDescription"]) + + @mock.patch.object(restcall, 'call_req') + def test_ns_pkg_on_boarding_when_vnf_pkg_not_on_boarded_on_catalog(self, mock_call_req): + self.set_nsd_metadata(key="id", val="2") + self.set_nsd_vnf_id(val="6") + mock_vals = { + "/openoapi/catalog/v1/csars/4": + [0, json.JSONEncoder().encode({"onBoardState": "non-onBoarded"}), '200'], + "/openoapi/catalog/v1/servicetemplates/queryingrawdata": + [0, json.JSONEncoder().encode(self.nsd_raw_data), '200'], + "/openoapi/catalog/v1/csars/5": + [0, json.JSONEncoder().encode({"onBoardState": "non-onBoarded"}), '200'], } + + def side_effect(*args): + return mock_vals[args[4]] + mock_call_req.side_effect = side_effect + + NfPackageModel(uuid="1", nfpackageid="5", vnfdid="6").save() + resp = self.client.post("/openoapi/nslcm/v1/nspackage", {"csarId": "4"}, format='json') + self.assertEqual(resp.status_code, status.HTTP_202_ACCEPTED) + self.assertEqual("failed", resp.data["status"]) + self.assertEqual("VNF package(5) is not onBoarded on catalog.", resp.data["statusDescription"]) + + @mock.patch.object(restcall, 'call_req') + def test_ns_pkg_on_boarding_when_on_board_success(self, mock_call_req): + self.set_nsd_metadata(key="id", val="2") + self.set_nsd_metadata(key="name", val="3") + self.set_nsd_metadata(key="vendor", val="4") + self.set_nsd_metadata(key="description", val="5") + self.set_nsd_metadata(key="version", val="6") + self.set_nsd_vnf_id(val="7") + mock_vals = { + "/openoapi/catalog/v1/csars/5": + [0, json.JSONEncoder().encode({ + "onBoardState": "non-onBoarded", + "createTime": "2016-05-15 12:30:34", + "modifyTime": "2016-05-15 12:30:34"}), '200'], + "/openoapi/catalog/v1/servicetemplates/queryingrawdata": + [0, json.JSONEncoder().encode(self.nsd_raw_data), '200'], + "/openoapi/catalog/v1/csars/6": + [0, json.JSONEncoder().encode({"onBoardState": "onBoarded"}), '200'], + "/openoapi/catalog/v1/csars/5?operationalState=Enabled": [0, '{}', 200], + "/openoapi/catalog/v1/csars/5?onBoardState=onBoarded": [0, "OK", '200']} + + def side_effect(*args): + return mock_vals[args[4]] + + mock_call_req.side_effect = side_effect + + NfPackageModel(uuid="1", nfpackageid="6", vnfdid="7").save() + resp = self.client.post("/openoapi/nslcm/v1/nspackage", {"csarId": "5"}, format='json') + self.assertEqual(resp.status_code, status.HTTP_202_ACCEPTED) + self.assertEqual("success", resp.data["status"]) + self.assertEqual("CSAR(5) onBoarded successfully.", resp.data["statusDescription"]) + nsds = NSDModel.objects.filter(id="5") + self.assertEqual(1, len(nsds)) + self.assertEqual("2", nsds[0].nsd_id) + + ############################################################################################################### + @mock.patch.object(restcall, 'call_req') + def test_delete_csar_when_id_not_exist(self, mock_call_req): + mock_call_req.return_value = [0, "", '204'] + resp = self.client.delete("/openoapi/nslcm/v1/nspackage/6") + self.assertEqual(resp.status_code, status.HTTP_202_ACCEPTED) + self.assertEqual("success", resp.data["status"]) + self.assertEqual("Delete CSAR(6) successfully.", resp.data["statusDescription"]) + + @mock.patch.object(restcall, 'call_req') + def test_delete_csar_when_ref_by_ns_inst(self, mock_call_req): + mock_call_req.return_value = [0, "OK", '200'] + + NSDModel(id="7", nsd_id="2").save() + NSInstModel(id="1", nspackage_id="7").save() + + resp = self.client.delete("/openoapi/nslcm/v1/nspackage/7") + self.assertEqual(resp.status_code, status.HTTP_202_ACCEPTED) + self.assertEqual("success", resp.data["status"]) + self.assertEqual("Set deletionPending to True of CSAR(7) successfully.", + resp.data["statusDescription"]) + + @mock.patch.object(restcall, 'call_req') + def test_delete_csar_when_delete_success(self, mock_call_req): + mock_call_req.return_value = [0, "OK", '204'] + + NSDModel(id="8", nsd_id="2").save() + + resp = self.client.delete("/openoapi/nslcm/v1/nspackage/8") + self.assertEqual(resp.status_code, status.HTTP_202_ACCEPTED) + self.assertEqual("success", resp.data["status"]) + self.assertEqual("Delete CSAR(8) successfully.", resp.data["statusDescription"]) + + ############################################################################################################### + def test_delete_pending_csar_when_id_not_exist(self): + resp = self.client.delete("/openoapi/nslcm/v1/nspackage/9/deletionpending") + self.assertEqual(resp.status_code, status.HTTP_202_ACCEPTED) + self.assertEqual("success", resp.data["status"]) + self.assertEqual("Delete pending CSAR(9) successfully.", resp.data["statusDescription"]) + + @mock.patch.object(restcall, 'call_req') + def test_delete_pending_csar_when_pending_is_false(self, mock_call_req): + mock_call_req.return_value = [0, '{"deletionPending": "false"}', '200'] + NSDModel(id="10", nsd_id="2").save() + resp = self.client.delete("/openoapi/nslcm/v1/nspackage/10/deletionpending") + self.assertEqual(resp.status_code, status.HTTP_202_ACCEPTED) + self.assertEqual("failed", resp.data["status"]) + self.assertEqual("CSAR(10) need not to be deleted.", resp.data["statusDescription"]) + + @mock.patch.object(restcall, 'call_req') + def test_delete_pending_csar_when_refed_by_ns(self, mock_call_req): + mock_call_req.return_value = [0, '{"deletionPending": "true"}', '200'] + NSDModel(id="11", nsd_id="2").save() + NSInstModel(id="1", nspackage_id="11").save() + resp = self.client.delete("/openoapi/nslcm/v1/nspackage/11/deletionpending") + self.assertEqual(resp.status_code, status.HTTP_202_ACCEPTED) + self.assertEqual("failed", resp.data["status"]) + self.assertEqual("CSAR(11) is in using, cannot be deleted.", resp.data["statusDescription"]) + + @mock.patch.object(restcall, 'call_req') + def test_delete_pending_csar_when_delete_success(self, mock_call_req): + mock_call_req.side_effect = [ + [0, '{"deletionPending": "true"}', '200'], + [0, "OK", '204']] + NSDModel(id="12", nsd_id="2").save() + resp = self.client.delete("/openoapi/nslcm/v1/nspackage/12/deletionpending") + self.assertEqual(resp.status_code, status.HTTP_202_ACCEPTED) + self.assertEqual("success", resp.data["status"]) + self.assertEqual("Delete CSAR(12) successfully.", resp.data["statusDescription"]) + + ############################################################################################################### + @mock.patch.object(restcall, 'call_req') + def test_get_csar_successfully(self, mock_call_req): + mock_call_req.return_value = [0, json.JSONEncoder().encode({ + "name": "1", + "provider": "2", + "version": "3", + "operationalState": "4", + "usageState": "5", + "onBoardState": "6", + "processState": "7", + "deletionPending": "8", + "downloadUri": "9", + "createTime": "10", + "modifyTime": "11", + "format": "12", + "size": "13" + }), '200'] + + NSDModel(id="13", nsd_id="2", vendor="3", version="4").save() + NSInstModel(id="1", nspackage_id="13", name="11").save() + NSInstModel(id="2", nspackage_id="13", name="22").save() + + resp = self.client.get("/openoapi/nslcm/v1/nspackage/13") + self.assertEqual(resp.status_code, status.HTTP_200_OK) + expect_data = {"nsInstanceInfo": [{"nsInstanceId": "1", "nsInstanceName": "11"}, + {"nsInstanceId": "2", "nsInstanceName": "22"}], "csarId": "13", + "packageInfo": {"nsdProvider": "3", "usageState": "5", + "onBoardState": "6", "name": "1", "format": "12", + "modifyTime": "11", "nsdId": "2", "nsdVersion": "4", + "deletionPending": "8", "version": "3", "downloadUri": "9", + "processState": "7", "provider": "2", "operationalState": "4", + "createTime": "10", "size": "13"}} + self.assertEqual(expect_data, resp.data) + + ############################################################################################################### + def test_disable_csar_when_id_not_exist_table(self): + resp = self.client.put("/openoapi/nslcm/v1/nspackage/14/disabled") + self.assertEqual(resp.status_code, status.HTTP_202_ACCEPTED) + self.assertEqual("failed", resp.data["status"]) + self.assertEqual("CSAR(14) does not exist.", resp.data["statusDescription"]) + + @mock.patch.object(restcall, 'call_req') + def test_disable_csar_when_csar_is_disabled(self, mock_call_req): + NSDModel(id="15", nsd_id="2").save() + mock_call_req.return_value = [0, json.JSONEncoder().encode({"operationalState": "Disabled"}), '200'] + resp = self.client.put("/openoapi/nslcm/v1/nspackage/15/disabled") + self.assertEqual(resp.status_code, status.HTTP_202_ACCEPTED) + self.assertEqual("failed", resp.data["status"]) + self.assertEqual("CSAR(15) already disabled.", resp.data["statusDescription"]) + + @mock.patch.object(restcall, 'call_req') + def test_disable_csar_successfully(self, mock_call_req): + NSDModel(id="16", nsd_id="2").save() + mock_vals = { + "/openoapi/catalog/v1/csars/16": + [0, json.JSONEncoder().encode({"operationalState": "Enabled"}), '200'], + "/openoapi/catalog/v1/csars/16?operationState=Disabled": + [0, "OK", '200']} + + def side_effect(*args): + return mock_vals[args[4]] + mock_call_req.side_effect = side_effect + + resp = self.client.put("/openoapi/nslcm/v1/nspackage/16/disabled") + self.assertEqual(resp.status_code, status.HTTP_202_ACCEPTED) + self.assertEqual("success", resp.data["status"]) + self.assertEqual("Set operationState to Disabled of CSAR(16) successfully.", resp.data["statusDescription"]) + + ############################################################################################################### + def test_enable_csar_when_id_not_exist_table(self): + resp = self.client.put("/openoapi/nslcm/v1/nspackage/17/enabled") + self.assertEqual(resp.status_code, status.HTTP_202_ACCEPTED) + self.assertEqual("failed", resp.data["status"]) + self.assertEqual("CSAR(17) does not exist.", resp.data["statusDescription"]) + + @mock.patch.object(restcall, 'call_req') + def test_enable_csar_when_csar_is_enabled(self, mock_call_req): + NSDModel(id="18", nsd_id="2").save() + mock_call_req.return_value = [0, json.JSONEncoder().encode({"operationalState": "Enabled"}), '200'] + resp = self.client.put("/openoapi/nslcm/v1/nspackage/18/enabled") + self.assertEqual(resp.status_code, status.HTTP_202_ACCEPTED) + self.assertEqual("failed", resp.data["status"]) + self.assertEqual("CSAR(18) already enabled.", resp.data["statusDescription"]) + + @mock.patch.object(restcall, 'call_req') + def test_enable_csar_successfully(self, mock_call_req): + NSDModel(id="19", nsd_id="2").save() + mock_vals = { + "/openoapi/catalog/v1/csars/19": + [0, json.JSONEncoder().encode({"operationalState": "Disabled"}), '200'], + "/openoapi/catalog/v1/csars/19?operationState=Enabled": + [0, "OK", '200']} + + def side_effect(*args): + return mock_vals[args[4]] + mock_call_req.side_effect = side_effect + + resp = self.client.put("/openoapi/nslcm/v1/nspackage/19/enabled") + self.assertEqual(resp.status_code, status.HTTP_202_ACCEPTED) + self.assertEqual("success", resp.data["status"]) + self.assertEqual("Set operationState to Enabled of CSAR(19) successfully.", resp.data["statusDescription"])
\ No newline at end of file diff --git a/lcm/packages/urls.py b/lcm/packages/urls.py new file mode 100644 index 00000000..89432c9b --- /dev/null +++ b/lcm/packages/urls.py @@ -0,0 +1,31 @@ +# Copyright 2016 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.packages import views + +urlpatterns = [ + url(r'^openoapi/nslcm/v1/nspackage/(?P<csarId>[0-9a-zA-Z\-\_]+)$', views.ns_access_csar, name='ns_access_csar'), + url(r'^openoapi/nslcm/v1/nspackage$', views.ns_on_boarding, name='ns_on_boarding'), + url(r'^openoapi/nslcm/v1/nspackage/(?P<csarId>[0-9a-zA-Z\-\_]+)/deletionpending$', + views.ns_delete_pending_csar, name='ns_delete_pending_csar'), + url(r'^openoapi/nslcm/v1/nspackage/(?P<csarId>[0-9a-zA-Z\-\_]+)/(?P<operation>(disabled|enabled))$', + views.ns_set_state_csar, name='ns_set_state_csar'), + url(r'^openoapi/nslcm/v1/vnfpackage/(?P<csarId>[0-9a-zA-Z\-\_]+)$', views.nf_access_csar, name='nf_access_csar'), + url(r'^openoapi/nslcm/v1/vnfpackage$', views.nf_on_boarding, name='nf_on_boarding'), + url(r'^openoapi/nslcm/v1/vnfpackage/(?P<csarId>[0-9a-zA-Z\-\_]+)/deletionpending$', + views.nf_delete_pending_csar, name='nf_delete_pending_csar'), ] + +urlpatterns = format_suffix_patterns(urlpatterns) diff --git a/lcm/packages/views.py b/lcm/packages/views.py new file mode 100644 index 00000000..c50c7416 --- /dev/null +++ b/lcm/packages/views.py @@ -0,0 +1,123 @@ +# Copyright 2016-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 uuid + +from rest_framework import status +from rest_framework.decorators import api_view +from rest_framework.response import Response + +from lcm.pub.utils.values import ignore_case_get +from lcm.pub.utils.syscomm import fun_name +from lcm.packages import ns_package, nf_package + +logger = logging.getLogger(__name__) + + +@api_view(http_method_names=['POST']) +def ns_on_boarding(request, *args, **kwargs): + csar_id = ignore_case_get(request.data, "csarId") + logger.info("Enter %s, method is %s, csar_id is %s", fun_name(), request.method, csar_id) + ret = ns_package.ns_on_boarding(csar_id) + logger.info("Leave %s, Return value is %s", fun_name(), str(ret)) + if ret[0] != 0: + return Response(data={'error': ret[1]}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + return Response(data=ret[1], status=status.HTTP_202_ACCEPTED) + + +@api_view(http_method_names=['GET', 'DELETE']) +def ns_access_csar(request, *args, **kwargs): + csar_id = ignore_case_get(kwargs, "csarId") + logger.info("Enter %s, method is %s, csar_id is %s", fun_name(), request.method, csar_id) + ret, normal_status = None, None + if request.method == 'GET': + ret = ns_package.ns_get_csar(csar_id) + normal_status = status.HTTP_200_OK + else: + ret = ns_package.ns_delete_csar(csar_id) + normal_status = status.HTTP_202_ACCEPTED + logger.info("Leave %s, Return value is %s", fun_name(), str(ret)) + if ret[0] != 0: + return Response(data={'error': ret[1]}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + return Response(data=ret[1], status=normal_status) + + +@api_view(http_method_names=['DELETE']) +def ns_delete_pending_csar(request, *args, **kwargs): + csar_id = ignore_case_get(kwargs, "csarId") + logger.info("Enter %s, method is %s, csar_id is %s", fun_name(), request.method, csar_id) + ret = ns_package.ns_delete_pending_csar(csar_id) + logger.info("Leave %s, Return value is %s", fun_name(), str(ret)) + if ret[0] != 0: + return Response(data={'error': ret[1]}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + return Response(data=ret[1], status=status.HTTP_202_ACCEPTED) + + +@api_view(http_method_names=['PUT']) +def ns_set_state_csar(request, *args, **kwargs): + csar_id = ignore_case_get(kwargs, "csarId") + operation = ignore_case_get(kwargs, "operation") + logger.info("Enter %s, method is %s, csar_id is %s, operation is %s", fun_name(), request.method, csar_id, operation) + ret = ns_package.ns_set_state_csar(csar_id, operation) + logger.info("Leave %s, Return value is %s", fun_name(), str(ret)) + if ret[0] != 0: + return Response(data={'error': ret[1]}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + return Response(data=ret[1], status=status.HTTP_202_ACCEPTED) + +################################################################################################################# +@api_view(http_method_names=['POST', 'GET']) +def nf_on_boarding(request, *args, **kwargs): + logger.info("Enter %s%s, method is %s", fun_name(), request.data, request.method) + if request.method == 'GET': + ret = nf_package.NfPackage().get_csars() + logger.debug("csars=%s", str(ret)) + return Response(data=ret, status=status.HTTP_200_OK) + csar_id = ignore_case_get(request.data, "csarId") + vim_ids = ignore_case_get(request.data, "vimIds") + lab_vim_id = ignore_case_get(request.data, "labVimId") + job_id = str(uuid.uuid4()) + nf_package.NfOnBoardingThread(csar_id, vim_ids, lab_vim_id, job_id).start() + ret = {"jobId": job_id} + logger.info("Leave %s, Return value is %s", fun_name(), str(ret)) + return Response(data=ret, status=status.HTTP_202_ACCEPTED) + + +@api_view(http_method_names=['GET', 'DELETE']) +def nf_access_csar(request, *args, **kwargs): + logger.info("Enter %s%s, method is %s", fun_name(), args, request.method) + csar_id = ignore_case_get(kwargs, "csarId") + if request.method == 'GET': + ret = nf_package.NfPackage().get_csar(csar_id) + logger.info("Leave %s, Return value is %s", fun_name(), str(ret)) + if ret[0] != 0: + return Response(data={'error': ret[1]}, status=status.HTTP_500_INTERNAL_SERVER_ERROR) + return Response(data=ret[1], status=status.HTTP_200_OK) + # NF package deleting + job_id = str(uuid.uuid4()) + nf_package.NfPkgDeleteThread(csar_id, job_id).start() + ret = {"jobId": job_id} + logger.info("Leave %s, Return value is %s", fun_name(), str(ret)) + return Response(data=ret, status=status.HTTP_202_ACCEPTED) + + +@api_view(http_method_names=['DELETE']) +def nf_delete_pending_csar(request, *args, **kwargs): + logger.info("Enter %s%s, method is %s", fun_name(), args, request.method) + csar_id = ignore_case_get(kwargs, "csarId") + job_id = str(uuid.uuid4()) + nf_package.NfPkgDeletePendingThread(csar_id, job_id).start() + ret = {"jobId": job_id} + logger.info("Leave %s, Return value is %s", fun_name(), str(ret)) + return Response(data=ret, status=status.HTTP_202_ACCEPTED)
\ No newline at end of file diff --git a/lcm/pub/__init__.py b/lcm/pub/__init__.py new file mode 100644 index 00000000..5580cc3d --- /dev/null +++ b/lcm/pub/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2016 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/pub/config/__init__.py b/lcm/pub/config/__init__.py new file mode 100644 index 00000000..5580cc3d --- /dev/null +++ b/lcm/pub/config/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2016 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/pub/config/config.py b/lcm/pub/config/config.py new file mode 100644 index 00000000..462d1ba0 --- /dev/null +++ b/lcm/pub/config/config.py @@ -0,0 +1,53 @@ +# Copyright 2016 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 os + +# [MSB] +MSB_SERVICE_IP = '127.0.0.1' +MSB_SERVICE_PORT = '80' + +# [IMAGE LOCAL PATH] +ROOT_PATH = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) +IMAGE_ROOT_PATH = os.path.join(ROOT_PATH, "/VmNfvo/VnfProduct") + +# [REDIS] +REDIS_HOST = '127.0.0.1' +REDIS_PORT = '6379' +REDIS_PASSWD = '' + +# [mysql] +DB_IP = "127.0.0.1" +DB_PORT = 3306 +DB_NAME = "inventory" +DB_USER = "inventory" +DB_PASSWD = "inventory" + +# [register] +REG_TO_MSB_WHEN_START = True +REG_TO_MSB_REG_URL = "/openoapi/microservices/v1/services" +REG_TO_MSB_REG_PARAM = { + "serviceName": "nslcm", + "version": "v1", + "url": "/openoapi/nslcm/v1", + "protocol": "REST", + "visualRange": "1", + "nodes": [{ + "ip": "127.0.0.1", + "port": "8403", + "ttl": 0 + }] +} + +# delete image from vim option when delete csar +IGNORE_DEL_IMG_WEHN_DEL_CSAR = True diff --git a/lcm/pub/database/__init__.py b/lcm/pub/database/__init__.py new file mode 100644 index 00000000..5580cc3d --- /dev/null +++ b/lcm/pub/database/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2016 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/pub/database/models.py b/lcm/pub/database/models.py new file mode 100644 index 00000000..cb40c08d --- /dev/null +++ b/lcm/pub/database/models.py @@ -0,0 +1,303 @@ +# Copyright 2016 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.db import models + + +class NSDModel(models.Model): + class Meta: + db_table = 'NFVO_NSPACKAGE' + + id = models.CharField(db_column='ID', primary_key=True, max_length=200) + nsd_id = models.CharField(db_column='NSDID', max_length=200) + name = models.CharField(db_column='NAME', max_length=200) + vendor = models.CharField(db_column='VENDOR', max_length=200, null=True, blank=True) + description = models.CharField(db_column='DESCRIPTION', max_length=200, null=True, blank=True) + version = models.CharField(db_column='VERSION', max_length=200, null=True, blank=True) + nsd_model = models.TextField(db_column='NSDMODEL', max_length=65535, null=True, blank=True) + + +class NSInstModel(models.Model): + class Meta: + db_table = 'NFVO_NSINST' + + id = models.CharField(db_column='ID', primary_key=True, max_length=200) + name = models.CharField(db_column='NAME', max_length=200) + nspackage_id = models.CharField(db_column='NSPACKAGEID', max_length=200, null=True, blank=True) + nsd_id = models.CharField(db_column='NSDID', max_length=200) + description = models.CharField(db_column='DESCRIPTION', max_length=255, null=True, blank=True) + sdncontroller_id = models.CharField(db_column='SDNCONTROLLERID', max_length=200, null=True, blank=True) + flavour_id = models.CharField(db_column='FLAVOURID', max_length=200, null=True, blank=True) + ns_level = models.CharField(db_column='NSLEVEL', max_length=200, null=True, blank=True) + status = models.CharField(db_column='STATUS', max_length=200, null=True, blank=True) + nsd_model = models.TextField(db_column='NSDMODEL', max_length=20000, null=True, blank=True) + input_params = models.TextField(db_column='INPUTPARAMS', max_length=2000, null=True, blank=True) + scale_params = models.TextField(db_column='SCALEPARAMS', max_length=2000, null=True, blank=True) + create_time = models.CharField(db_column='CREATETIME', max_length=200, null=True, blank=True) + lastuptime = models.CharField(db_column='LASTUPTIME', max_length=200, null=True, blank=True) + + +class NfPackageModel(models.Model): + uuid = models.CharField(db_column='UUID', primary_key=True, max_length=255) + nfpackageid = models.CharField(db_column='NFPACKAGEID', max_length=200) + vnfdid = models.CharField(db_column='VNFDID', max_length=255) + vendor = models.CharField(db_column='VENDOR', max_length=255) + vnfdversion = models.CharField(db_column='VNFDVERSION', max_length=255) + vnfversion = models.CharField(db_column='VNFVERSION', max_length=255) + vnfdmodel = models.TextField(db_column='VNFDMODEL', max_length=65535, blank=True, null=True) + + class Meta: + db_table = 'NFVO_NFPACKAGE' + + +class VnfPackageFileModel(models.Model): + id = models.AutoField(db_column='ID', primary_key=True) + vnfpid = models.CharField(db_column='NFPACKAGEID', max_length=50) + filename = models.CharField(db_column='FILENAME', max_length=100) + filetype = models.CharField(db_column='FILETYPE', max_length=2) + imageid = models.CharField(db_column='IMAGEID', max_length=50) + vimid = models.CharField(db_column='VIMID', max_length=50) + vimuser = models.CharField(db_column='VIMUSER', max_length=50) + tenant = models.CharField(db_column='TENANT', max_length=50) + purpose = models.CharField(db_column='PURPOSE', max_length=1000) + status = models.CharField(db_column='STATUS', max_length=10) + + class Meta: + db_table = 'NFVO_NFPACKAGEFILE' + + +class FPInstModel(models.Model): + class Meta: + db_table = 'NFVO_FPINST' + + fpid = models.CharField(db_column='FPID', max_length=255) + fpinstid = models.CharField(db_column='FPINSTID', max_length=255, primary_key=True) + fpname = models.CharField(db_column='FPNAME', max_length=255) + nsinstid = models.CharField(db_column='NSINSTID', max_length=255) + vnffginstid = models.CharField(db_column='VNFFGINSTID', max_length=255) + symmetric = models.IntegerField(db_column='SYMMETRIC', null=True) + policyinfo = models.TextField(db_column='POLICYINFO', max_length=65535) + forworderpaths = models.CharField(db_column='FORWORDERPATHS', max_length=255, null=True, blank=True) + status = models.CharField(db_column='STATUS', max_length=255) + sdncontrollerid = models.CharField(db_column='SDNCONTROLLERID', max_length=255) + sfcid = models.CharField(db_column='SFCID', max_length=255) + flowclassifiers = models.CharField(db_column='FLOWCLASSIFIERS', max_length=255) + portpairgroups = models.TextField(db_column='PORTPAIRGROUPS', max_length=65535) + + +class VNFFGInstModel(models.Model): + class Meta: + db_table = 'NFVO_VNFFGINST' + + vnffgdid = models.CharField(db_column='VNFFGDID', max_length=255) + vnffginstid = models.CharField(db_column='VNFFGINSTID', max_length=255, primary_key=True) + nsinstid = models.CharField(db_column='NSINSTID', max_length=255) + desc = models.CharField(db_column='DESC', max_length=255, blank=True, null=True) + vendor = models.CharField(db_column='VENDOR', max_length=255, blank=True, null=True) + version = models.CharField(db_column='VERSION', max_length=255, blank=True, null=True) + endpointnumber = models.IntegerField(db_column='ENDPOINTNUMBER') + vllist = models.CharField(db_column='VLLIST', max_length=1024) + cplist = models.CharField(db_column='CPLIST', max_length=1024) + vnflist = models.CharField(db_column='VNFLIST', max_length=1024) + fplist = models.CharField(db_column='FPLIST', max_length=1024) + status = models.CharField(db_column='STATUS', max_length=255) + + +class NfInstModel(models.Model): + class Meta: + db_table = 'NFVO_NFINST' + + nfinstid = models.CharField(db_column='NFINSTID', max_length=200, primary_key=True) + mnfinstid = models.CharField(db_column='M_NFINSTID', max_length=200, blank=True, null=True) + nf_name = models.CharField(db_column='NFNAME', max_length=100, blank=True, null=True) + template_id = models.CharField(db_column='TEMPLATEID', max_length=200, blank=True, null=True) + vnf_id = models.CharField(db_column='VNFID', max_length=200, blank=True, null=True) + package_id = models.CharField(db_column='PACKAGEID', max_length=200, blank=True, null=True) + vnfm_inst_id = models.CharField(db_column='VNFMINSTID', max_length=200, blank=True, null=True) + ns_inst_id = models.CharField(db_column='NSINSTID', max_length=200, blank=True, null=True) + status = models.CharField(db_column='STATUS', max_length=20, blank=True, null=True) + flavour_id = models.CharField(db_column='FLAVOURID', max_length=200, blank=True, null=True) + vnf_level = models.CharField(db_column='VNFLEVEL', max_length=200, blank=True, null=True) + location = models.CharField(db_column='LOCATION', max_length=200, blank=True, null=True) + max_vm = models.IntegerField(db_column='MAXVM', null=True) + max_cpu = models.IntegerField(db_column='MAXCPU', null=True) + max_ram = models.IntegerField(db_column='MAXRAM', null=True) + max_hd = models.IntegerField(db_column='MAXHD', null=True) + max_shd = models.IntegerField(db_column='MAXSHD', null=True) + max_net = models.IntegerField(db_column='MAXNET', null=True) + version = models.CharField(db_column='VERSION', max_length=255, null=True) + vendor = models.CharField(db_column='VENDOR', max_length=255, null=True, blank=True) + vnfd_model = models.TextField(db_column='VNFDMODEL', max_length=20000, blank=True, null=True) + input_params = models.TextField(db_column='INPUTPARAMS', max_length=2000, blank=True, null=True) + scale_params = models.TextField(db_column='SCALEPARAMS', max_length=2000, null=True, blank=True) + create_time = models.CharField(db_column='CREATETIME', max_length=200, null=True, blank=True) + lastuptime = models.CharField(db_column='LASTUPTIME', max_length=200, blank=True, null=True) + extension = models.TextField(db_column='EXTENSION', max_length=65535, blank=True, null=True) + + +class VmInstModel(models.Model): + class Meta: + db_table = 'NFVO_VMINST' + + vmid = models.CharField(db_column='VMID', primary_key=True, max_length=255) + vimid = models.CharField(db_column='VIMID', max_length=255) + resouceid = models.CharField(db_column='RESOURCEID', max_length=255) + insttype = models.IntegerField(db_column='INSTTYPE', null=True) + instid = models.CharField(db_column='INSTID', max_length=255, null=True) + vmname = models.CharField(db_column='VMNAME', max_length=255) + operationalstate = models.IntegerField(db_column='OPERATIONALSTATE', default=1) + zoneid = models.CharField(db_column='ZONEID', max_length=255, null=True) + tenant = models.CharField(db_column='TENANT', max_length=255, null=True) + hostid = models.CharField(db_column='HOSTID', max_length=255) + detailinfo = models.CharField(db_column='DETAILINFO', max_length=255, null=True) + + +class VNFCInstModel(models.Model): + class Meta: + db_table = 'NFVO_VNFCINST' + + vnfcinstanceid = models.CharField(db_column='VNFCINSTANCEID', max_length=255, primary_key=True) + vduid = models.CharField(db_column='VDUID', max_length=255) + nfinstid = models.CharField(db_column='NFINSTID', max_length=255) + vmid = models.CharField(db_column='VMID', max_length=255) + status = models.CharField(db_column='STATUS', max_length=255) + + +class CPInstModel(models.Model): + class Meta: + db_table = 'NFVO_CPINST' + + cpinstanceid = models.CharField(db_column='CPINSTANCEID', max_length=255, primary_key=True) + cpdid = models.CharField(db_column='CPDID', max_length=255) + cpinstancename = models.CharField(db_column='CPINSTANCENAME', max_length=255) + ownertype = models.IntegerField(db_column='OWNERTYPE') + ownerid = models.CharField(db_column='OWNERID', max_length=255) + relatedtype = models.IntegerField(db_column='RELATEDTYPE') + relatedvl = models.CharField(db_column='RELATEDVL', max_length=255, blank=True, null=True) + relatedcp = models.CharField(db_column='RELATEDCP', max_length=255, blank=True, null=True) + relatedport = models.CharField(db_column='RELATEDPORT', max_length=255, blank=True, null=True) + status = models.CharField(db_column='STATUS', max_length=255) + + +class VLInstModel(models.Model): + class Meta: + db_table = 'NFVO_VLINST' + + vlinstanceid = models.CharField(db_column='VLINSTANCEID', max_length=255, primary_key=True) + vldid = models.CharField(db_column='VLDID', max_length=255) + vlinstancename = models.CharField(db_column='VLINSTANCENAME', max_length=255, blank=True, null=True) + ownertype = models.IntegerField(db_column='OWNERTYPE') + ownerid = models.CharField(db_column='OWNERID', max_length=255) + relatednetworkid = models.CharField(db_column='RELATEDNETWORKID', max_length=255, blank=True, null=True) + relatedsubnetworkid = models.CharField(db_column='RELATEDSUBNETWORKID', max_length=255, blank=True, null=True) + vltype = models.IntegerField(db_column='VLTYPE', default=0) + vimid = models.CharField(db_column='VIMID', max_length=255) + tenant = models.CharField(db_column='TENANT', max_length=255) + status = models.CharField(db_column='STATUS', max_length=255) + + +class PortInstModel(models.Model): + class Meta: + db_table = 'NFVO_PORTINST' + + portid = models.CharField(db_column='PORTID', max_length=255, primary_key=True) + networkid = models.CharField(db_column='NETWORKID', max_length=255) + subnetworkid = models.CharField(db_column='SUBNETWORKID', max_length=255) + vimid = models.CharField(db_column='VIMID', max_length=255) + resourceid = models.CharField(db_column='RESOURCEID', max_length=255) + name = models.CharField(db_column='NAME', max_length=255) + instid = models.CharField(db_column='INSTID', max_length=255) + cpinstanceid = models.CharField(db_column='CPINSTANCEID', max_length=255) + bandwidth = models.CharField(db_column='BANDWIDTH', max_length=255) + operationalstate = models.CharField(db_column='OPERATIONALSTATE', max_length=255) + ipaddress = models.CharField(db_column='IPADDRESS', max_length=255) + macaddress = models.CharField(db_column='MACADDRESS', max_length=255) + floatipaddress = models.CharField(db_column='FLOATIPADDRESS', max_length=255) + serviceipaddress = models.CharField(db_column='SERVICEIPADDRESS', max_length=255) + typevirtualnic = models.CharField(db_column='TYPEVIRTUALNIC', max_length=255) + sfcencapsulation = models.CharField(db_column='SFCENCAPSULATION', max_length=255) + direction = models.CharField(db_column='DIRECTION', max_length=255) + tenant = models.CharField(db_column='TENANT', max_length=255) + + +class JobModel(models.Model): + class Meta: + db_table = 'NFVO_JOB' + + jobid = models.CharField(db_column='JOBID', primary_key=True, max_length=255) + jobtype = models.CharField(db_column='JOBTYPE', max_length=255) + jobaction = models.CharField(db_column='JOBACTION', max_length=255) + resid = models.CharField(db_column='RESID', max_length=255) + status = models.IntegerField(db_column='STATUS', null=True, blank=True) + starttime = models.CharField(db_column='STARTTIME', max_length=255, null=True, blank=True) + endtime = models.CharField(db_column='ENDTIME', max_length=255, null=True, blank=True) + progress = models.IntegerField(db_column='PROGRESS', null=True, blank=True) + user = models.CharField(db_column='USER', max_length=255, null=True, blank=True) + parentjobid = models.CharField(db_column='PARENTJOBID', max_length=255, null=True, blank=True) + resname = models.CharField(db_column='RESNAME', max_length=255, null=True, blank=True) + + def toJSON(self): + import json + return json.dumps(dict([(attr, getattr(self, attr)) for attr in [f.name for f in self._meta.fields]])) + + +class JobStatusModel(models.Model): + class Meta: + db_table = 'NFVO_JOB_STATUS' + + indexid = models.IntegerField(db_column='INDEXID') + jobid = models.CharField(db_column='JOBID', max_length=255) + status = models.CharField(db_column='STATUS', max_length=255) + progress = models.IntegerField(db_column='PROGRESS', null=True, blank=True) + descp = models.TextField(db_column='DESCP', max_length=65535) + errcode = models.CharField(db_column='ERRCODE', max_length=255, null=True, blank=True) + addtime = models.CharField(db_column='ADDTIME', max_length=255, null=True, blank=True) + + def toJSON(self): + import json + return json.dumps(dict([(attr, getattr(self, attr)) for attr in [f.name for f in self._meta.fields]])) + + +class DefPkgMappingModel(models.Model): + class Meta: + db_table = 't_lcm_defPackage_mapping' + + service_id = models.CharField(db_column='serviceId', max_length=255, primary_key=True) + service_def_id = models.CharField(db_column='serviceDefId', max_length=255) + template_id = models.CharField(db_column='templateId', max_length=255) + template_name = models.CharField(db_column='templateName', max_length=255) + + +class InputParamMappingModel(models.Model): + class Meta: + db_table = 't_lcm_inputParam_mapping' + + service_id = models.CharField(db_column='serviceId', max_length=255) + input_key = models.CharField(db_column='inputKey', max_length=255) + input_value = models.CharField(db_column='inputValue', max_length=255, null=True, blank=True) + + +class ServiceBaseInfoModel(models.Model): + class Meta: + db_table = 't_lcm_servicebaseinfo' + + service_id = models.CharField(db_column='serviceId', max_length=255, primary_key=True) + service_name = models.CharField(db_column='serviceName', max_length=255) + service_type = models.CharField(db_column='serviceType', max_length=20) + description = models.CharField(db_column='description', max_length=255, null=True, blank=True) + active_status = models.CharField(db_column='activeStatus', max_length=20) + status = models.CharField(db_column='status', max_length=20) + creator = models.CharField(db_column='creator', max_length=50) + create_time = models.BigIntegerField(db_column='createTime', max_length=20) + +
\ No newline at end of file diff --git a/lcm/pub/exceptions.py b/lcm/pub/exceptions.py new file mode 100644 index 00000000..2c3f2cae --- /dev/null +++ b/lcm/pub/exceptions.py @@ -0,0 +1,17 @@ +# Copyright 2016 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. + + +class NSLCMException(Exception): + pass diff --git a/lcm/pub/msapi/__init__.py b/lcm/pub/msapi/__init__.py new file mode 100644 index 00000000..5580cc3d --- /dev/null +++ b/lcm/pub/msapi/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2016 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/pub/msapi/catalog.py b/lcm/pub/msapi/catalog.py new file mode 100644 index 00000000..24444657 --- /dev/null +++ b/lcm/pub/msapi/catalog.py @@ -0,0 +1,104 @@ +# Copyright 2016 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 json +import logging + +from lcm.pub.utils.restcall import req_by_msb +from lcm.pub.utils.values import ignore_case_get +from lcm.pub.exceptions import NSLCMException + +logger = logging.getLogger(__name__) + +STATUS_ONBOARDED, STATUS_NON_ONBOARDED = "onBoarded", "non-onBoarded" +P_STATUS_ENABLED, P_STATUS_DISABLED = "Enabled", "Disabled" +P_STATUS_NORMAL, P_STATUS_ONBOARDING, P_STATUS_ONBOARDFAILED = "normal", "onBoarding", "onBoardFailed" +P_STATUS_DELETING, P_STATUS_DELETEFAILED = "deleting", "deleteFailed" + + +def query_csar_from_catalog(csar_id, key=''): + ret = req_by_msb("/openoapi/catalog/v1/csars/%s" % csar_id, "GET") + if ret[0] != 0: + logger.error("Status code is %s, detail is %s.", ret[2], ret[1]) + if ret[2] == '404': + raise NSLCMException("CSAR(%s) does not exist." % csar_id) + raise NSLCMException("Failed to query CSAR(%s) from catalog." % csar_id) + csar_info = json.JSONDecoder().decode(ret[1]) + return ignore_case_get(csar_info, key) if key else csar_info + + +def query_rawdata_from_catalog(csar_id, input_parameters=[]): + req_param = json.JSONEncoder().encode({"csarId": csar_id, "inputParameters": input_parameters}) + ret = req_by_msb("/openoapi/catalog/v1/servicetemplates/queryingrawdata", "POST", req_param) + if ret[0] != 0: + logger.error("Status code is %s, detail is %s.", ret[2], ret[1]) + raise NSLCMException("Failed to query rawdata of CSAR(%s) from catalog." % csar_id) + return json.JSONDecoder().decode(ret[1]) + + +def set_csar_state(csar_id, prop, val): + ret = req_by_msb("/openoapi/catalog/v1/csars/%s?%s=%s" % (csar_id, prop, val), "PUT") + if ret[0] != 0: + logger.error("Status code is %s, detail is %s.", ret[2], ret[1]) + return [1, "Failed to set %s to %s of CSAR(%s)." % (prop, val, csar_id)] + return [0, "Set %s to %s of CSAR(%s) successfully." % (prop, val, csar_id)] + + +def delete_csar_from_catalog(csar_id): + ret = req_by_msb("/openoapi/catalog/v1/csars/%s" % csar_id, "DELETE") + if ret[0] != 0 and ret[2] != '404': + logger.error("Status code is %s, detail is %s.", ret[2], ret[1]) + set_csar_state(csar_id, "processState", P_STATUS_DELETEFAILED) + return [1, "Failed to delete CSAR(%s) from catalog." % csar_id] + return [0, "Delete CSAR(%s) successfully." % csar_id] + + +def get_download_url_from_catalog(csar_id, relative_path): + ret = req_by_msb("/openoapi/catalog/v1/csars/%s/files?relativePath=%s" % (csar_id, relative_path), "GET") + if ret[0] != 0: + logger.error("Status code is %s, detail is %s.", ret[2], ret[1]) + raise NSLCMException("Failed to get download url of CSAR(%s)." % csar_id) + csar_file_info = json.JSONDecoder().decode(ret[1]) + return ignore_case_get(csar_file_info, "downloadUri"), ignore_case_get(csar_file_info, "localPath") + + +def get_process_id(name, srv_template_id): + ret = req_by_msb('/openoapi/catalog/v1/servicetemplates/%s/operations' % srv_template_id, 'GET') + if ret[0] != 0: + raise NSLCMException('Failed to get service[%s,%s] process id' % (name, srv_template_id)) + items = json.JSONDecoder().decode(ret[1]) + for item in items: + if name in item['name']: + return item['processId'] + raise NSLCMException('service[%s,%s] process id not exist' % (name, srv_template_id)) + +def get_servicetemplate_id(nsd_id): + ret = req_by_msb('/openoapi/catalog/v1/servicetemplates', 'GET') + if ret[0] != 0: + raise NSLCMException('Failed to get servicetemplates info') + stpls = json.JSONDecoder().decode(ret[1]) + for stpl in stpls: + if stpl.get("id", "") == nsd_id: + return stpl["serviceTemplateId"] + raise NSLCMException('servicetemplate(%s) does not exist.' % nsd_id) + +def get_servicetemplate(nsd_id): + ret = req_by_msb('/openoapi/catalog/v1/servicetemplates', 'GET') + if ret[0] != 0: + raise NSLCMException('Failed to get servicetemplates info') + stpls = json.JSONDecoder().decode(ret[1]) + for stpl in stpls: + if stpl.get("id", "") == nsd_id: + return stpl + return NSLCMException('servicetemplate(%s) does not exist.' % nsd_id) diff --git a/lcm/pub/msapi/extsys.py b/lcm/pub/msapi/extsys.py new file mode 100644 index 00000000..0b766630 --- /dev/null +++ b/lcm/pub/msapi/extsys.py @@ -0,0 +1,67 @@ +# Copyright 2016 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 json +import logging + +from lcm.pub.exceptions import NSLCMException +from lcm.pub.utils.restcall import req_by_msb + +logger = logging.getLogger(__name__) + + +def get_vims(): + ret = req_by_msb("/openoapi/extsys/v1/vims", "GET") + if ret[0] != 0: + logger.error("Status code is %s, detail is %s.", ret[2], ret[1]) + raise NSLCMException("Failed to query vims from extsys.") + return json.JSONDecoder().decode(ret[1]) + + +def get_vim_by_id(vim_id): + ret = req_by_msb("/openoapi/extsys/v1/vims/%s" % vim_id, "GET") + if ret[0] != 0: + logger.error("Status code is %s, detail is %s.", ret[2], ret[1]) + raise NSLCMException("Failed to query vim(%s) from extsys." % vim_id) + return json.JSONDecoder().decode(ret[1]) + + +def get_sdn_controller_by_id(sdn_ontroller_id): + ret = req_by_msb("/openoapi/extsys/v1/sdncontrollers/%s" % sdn_ontroller_id, "GET") + if ret[0] != 0: + logger.error("Failed to query sdn ontroller(%s) from extsys. detail is %s.", sdn_ontroller_id, ret[1]) + raise NSLCMException("Failed to query sdn ontroller(%s) from extsys." % sdn_ontroller_id) + return json.JSONDecoder().decode(ret[1]) + + +def get_vnfm_by_id(vnfm_inst_id): + uri = '/openoapi/extsys/v1/vnfms/%s' % vnfm_inst_id + ret = req_by_msb(uri, "GET") + if ret[0] > 0: + logger.error('Send get VNFM information request to extsys failed.') + raise NSLCMException('Send get VNFM information request to extsys failed.') + return json.JSONDecoder().decode(ret[1]) + +def select_vnfm(vnfm_type, vim_id): + uri = '/openoapi/extsys/v1/vnfms' + ret = req_by_msb(uri, "GET") + if ret[0] > 0: + logger.error("Failed to call %s: %s", uri, ret[1]) + raise NSLCMException('Failed to get vnfms from extsys.') + vnfms = json.JSONDecoder().decode(ret[1]) + for vnfm in vnfms: + if vnfm["type"] == vnfm_type and vnfm["vimId"] == vim_id: + return vnfm + raise NSLCMException('No vnfm found with %s in vim(%s)' % (vnfm_type, vim_id)) + diff --git a/lcm/pub/msapi/nslcm.py b/lcm/pub/msapi/nslcm.py new file mode 100644 index 00000000..e62ad4ca --- /dev/null +++ b/lcm/pub/msapi/nslcm.py @@ -0,0 +1,36 @@ +# Copyright 2016 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 json +import logging + +from lcm.pub.utils.restcall import req_by_msb + +logger = logging.getLogger(__name__) + +def call_from_ns_cancel_resource(res_type, instid): + method = "DELETE" + if res_type == 'vl': + uri = '/openoapi/nslcm/v1/ns/vls/%s' % instid + + elif res_type == 'sfc': + uri = '/openoapi/nslcm/v1/ns/sfcs/%s' % instid + else: + # vnf + method = "POST" + uri = '/openoapi/nslcm/v1/ns/vnfs/%s' % instid + req_param = {} + ret = req_by_msb(uri, method, json.dumps(req_param)) + logger.info("[NS terminate] call vnfm [%s] result:%s" % (res_type, ret)) + return ret
\ No newline at end of file diff --git a/lcm/pub/msapi/resmgr.py b/lcm/pub/msapi/resmgr.py new file mode 100644 index 00000000..d1fcc69d --- /dev/null +++ b/lcm/pub/msapi/resmgr.py @@ -0,0 +1,122 @@ +# Copyright 2016 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 json +import logging + +from lcm.pub.exceptions import NSLCMException +from lcm.pub.utils.restcall import req_by_msb + +logger = logging.getLogger(__name__) + + +def create_vl(req_param): + ret = req_by_msb("/openoapi/resmgr/v1/vl", "POST", json.JSONEncoder().encode(req_param)) + if ret[0] != 0: + logger.error("Failed to create vl to resmgr. detail is %s.", ret[1]) + #raise NSLCMException('Failed to create vl to resmgr.') + #return json.JSONDecoder().decode(ret[1]) + + +def delete_vl(vl_inst_id): + ret = req_by_msb("/openoapi/resmgr/v1/vl/%s" % vl_inst_id, "DELETE") + if ret[0] != 0: + logger.error("Failed to delete vl(%s) to resmgr. detail is %s.", vl_inst_id, ret[1]) + #raise NSLCMException("Failed to delete vl(%s) to resmgr." % vl_inst_id) + + +def delete_sfc(sfc_inst_id): + ret = req_by_msb("/openoapi/resmgr/v1/sfc/%s" % sfc_inst_id, "DELETE") + if ret[0] != 0: + logger.error("Failed to delete sfc(%s) to resmgr. detail is %s.", sfc_inst_id, ret[1]) + #raise NSLCMException("Failed to delete sfc(%s) to resmgr." % sfc_inst_id) + + +def grant_vnf(req_param): + grant_data = json.JSONEncoder().encode(req_param) + ret = req_by_msb("/openoapi/resmgr/v1/resource/grant", "PUT", grant_data) + if ret[0] != 0: + logger.error("Failed to grant vnf to resmgr. detail is %s.", ret[1]) + #raise NSLCMException('Failed to grant vnf to resmgr.') + vim_id = "" + if "vimId" in req_param: + vim_id = req_param["vimId"] + elif "additionalparam" in req_param and "vimid" in req_param["additionalparam"]: + vim_id = req_param["additionalparam"]["vimid"] + try: + from lcm.pub.msapi import extsys + vim = extsys.get_vim_by_id(vim_id) + if isinstance(vim, list): + vim = vim[0] + vim_id = vim["vimId"] + grant_rsp = { + "vim": { + "vimid": vim_id, + "accessinfo": { + "tenant": vim["tenant"] + } + } + } + logger.debug("grant_rsp=%s" % grant_rsp) + return grant_rsp + except: + raise NSLCMException('Failed to grant vnf to resmgr.') + return json.JSONDecoder().decode(ret[1]) + + +def create_vnf(data): + uri = '/openoapi/resmgr/v1/vnf' + req_param = json.JSONEncoder().encode({ + 'orchVnfInstanceId': data['nf_inst_id'], + 'vnfInstanceId': data['vnfm_nf_inst_id'], + 'vnfInstanceName': data['vnf_inst_name'], + 'nsId': data['ns_inst_id'], + 'nsName': data['ns_inst_name'], + 'vnfmId': data['vnfm_inst_id'], + 'vnfmName': data['vnfm_inst_name'], + 'vnfPackageName': data['vnfd_name'], + 'vnfDescriptorName': data['vnfd_id'], + 'jobId': data['job_id'], + 'vnfStatus': data['nf_inst_status'], + 'vnfType': data['vnf_type'], + 'onboardingId': data['nf_package_id'], + 'onboardingName': data['vnfd_name']}) + + ret = req_by_msb(uri, "POST", req_param) + if ret[0] != 0: + logger.error('Send create VNF request to resmgr failed.') + #raise NSLCMException('Send create VNF request to resmgr failed.') + + +def create_vnf_creation_info(data): + uri = '/openoapi/resmgr/v1/vnfinfo' + req_param = json.JSONEncoder().encode({ + 'vnfInstanceId': data['nf_inst_id'], + 'nsId': data['ns_inst_id'], + 'vnfmId': data['vnfm_inst_id'], + 'vms': data['vms']}) + + ret = req_by_msb(uri, "POST", req_param) + if ret[0] > 0: + logger.error('Send write vnf creation information to resmgr failed.') + #raise NSLCMException('Send write vnf creation information to resmgr failed.') + + +def terminate_vnf(vnf_inst_id): + uri = '/openoapi/resmgr/v1/vnf/%s' % vnf_inst_id + req_param = {} + ret = req_by_msb(uri, "DELETE", json.dumps(req_param)) + if ret[0] > 0: + logger.error('Send terminate VNF request to resmgr failed.') + #raise NSLCMException('Send terminate VNF request to resmgr failed.')
\ No newline at end of file diff --git a/lcm/pub/msapi/sdncdriver.py b/lcm/pub/msapi/sdncdriver.py new file mode 100644 index 00000000..f2331ce3 --- /dev/null +++ b/lcm/pub/msapi/sdncdriver.py @@ -0,0 +1,85 @@ +# Copyright 2016 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 json +import logging + +from lcm.pub.exceptions import NSLCMException +from lcm.pub.utils.restcall import req_by_msb + +logger = logging.getLogger(__name__) + + +def delete_port_chain(req_param): + url = "/openoapi/sdncdriver/v1/delchain" + str = "delete port chain" + delete_func(req_param, url, str) + + +def delete_flow_classifier(req_param): + url = "/openoapi/sdncdriver/v1/delclassifier" + str = "delete flow classifier" + delete_func(req_param, url, str) + + +def delete_port_pair_group(req_param): + url = "/openoapi/sdncdriver/v1/delportpairgroup" + str = "delete port pair" + delete_func(req_param, url, str) + + +def delete_port_pair(req_param): + url = "/openoapi/sdncdriver/v1/delportpair" + str = "delete port pair" + delete_func(req_param, url, str) + + +def delete_func(req_param, url, str): + ret = req_by_msb(url, "DELETE", json.JSONEncoder().encode(req_param)) + if ret[0] != 0: + logger.error("Failed to %s to sdncdriver. detail is %s.", str, ret[1]) + raise NSLCMException('Failed to %s to sdncdriver.' % str) + + +def create_flow_classfier(data): + url = "/openoapi/ztesdncdriver/v1/createflowclassfier" + str = "create flow classfier" + return create(data, url, str) + + +def create_port_pair(data): + url = "/openoapi/ztesdncdriver/v1/createportpair" + str = "create port pair" + return create(data, url, str) + + +def create_port_pair_group(data): + url = "/openoapi/ztesdncdriver/v1/createportpairgroup" + str = "create port pair group" + return create(data, url, str) + + +def create_port_chain(data): + url = "/openoapi/ztesdncdriver/v1/createportchain" + str = "create port chain" + return create(data, url, str) + + +def create(req_param, url, str): + ret = req_by_msb(url, "POST", json.dumps(req_param)) + if ret[0] != 0: + logger.error("Failed to %s to sdncdriver. detail is %s.", str, ret[1]) + raise NSLCMException('Failed to %s to sdncdriver.' % str) + resp_body = json.loads(ret[1]) + return resp_body["id"] diff --git a/lcm/pub/msapi/tosca.py b/lcm/pub/msapi/tosca.py new file mode 100644 index 00000000..5db971dd --- /dev/null +++ b/lcm/pub/msapi/tosca.py @@ -0,0 +1,31 @@ +# Copyright 2016 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 json + +from lcm.pub.exceptions import NSLCMException +from lcm.pub.utils.restcall import req_by_msb + + +def tosca_plan(uri, inputs): + """ + https://wiki.open-o.org/view/Common_TOSCA_API_Documentation + """ + content = {"uri": uri, "inputs": inputs} + content_str = json.JSONEncoder().encode(content) + ret = req_by_msb("/openoapi/tosca/v1/indirect/instance", "POST", content_str) + if ret[0] != 0: + raise NSLCMException("status code is %s, detail is %s.", ret[2], ret[1]) + if ret[2] != '200': + raise NSLCMException("tosca error occur when call parser api failed: %s" % content_str) + return ret[1] diff --git a/lcm/pub/msapi/vnfmdriver.py b/lcm/pub/msapi/vnfmdriver.py new file mode 100644 index 00000000..5da08d27 --- /dev/null +++ b/lcm/pub/msapi/vnfmdriver.py @@ -0,0 +1,69 @@ +# Copyright 2016 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 json +import logging + +from lcm.pub.exceptions import NSLCMException +from lcm.pub.utils.restcall import req_by_msb +from lcm.pub.utils.values import ignore_case_get +from lcm.pub.msapi.extsys import get_vnfm_by_id + +logger = logging.getLogger(__name__) + + +def send_nf_init_request(vnfm_inst_id, req_param): + vnfm = get_vnfm_by_id(vnfm_inst_id) + uri = '/openoapi/%s/v1/%s/vnfs' % (vnfm["type"], vnfm_inst_id) + ret = req_by_msb(uri, "POST", req_param) + if ret[0] != 0: + logger.error("Failed to send nf init req:%s,%s", ret[2], ret[1]) + raise NSLCMException('Failed to send nf init request to VNFM(%s)' % vnfm_inst_id) + return json.JSONDecoder().decode(ret[1]) + +def send_nf_terminate_request(vnfm_inst_id, vnf_inst_id, req_param): + vnfm = get_vnfm_by_id(vnfm_inst_id) + uri = '/openoapi/%s/v1/%s/vnfs/%s/terminate' % (vnfm["type"], vnfm_inst_id, vnf_inst_id) + ret = req_by_msb(uri, "POST", req_param) + if ret[0] > 0: + logger.error("Failed to send nf terminate req:%s,%s", ret[2], ret[1]) + raise NSLCMException('Failed to send nf terminate request to VNFM(%s)' % vnfm_inst_id) + return json.JSONDecoder().decode(ret[1]) if ret[1] else {} + +def query_vnfm_job(vnfm_inst_id, job_id, response_id=0): + vnfm = get_vnfm_by_id(vnfm_inst_id) + retry_time = 3 + uri = '/openoapi/%s/v1/%s/jobs/%s?responseId=%s' % (vnfm["type"], + vnfm_inst_id, job_id, response_id) + while retry_time > 0: + rsp = req_by_msb(uri, "GET") + if str(rsp[2]) == '404': + return False, '' + if rsp[0] != 0: + logger.warning('retry_time=%s, detail message:%s' % (retry_time, rsp[1])) + retry_time -= 1 + else: + break + if retry_time <= 0: + logger.error(rsp[1]) + raise NSLCMException('Failed to query job from VNFM!') + return True, json.JSONDecoder().decode(rsp[1]) + +def send_nf_scaling_request(vnfm_inst_id, vnf_inst_id, req_param): + vnfm = get_vnfm_by_id(vnfm_inst_id) + uri = '/openoapi/%s/v1/%s/vnfs/%s/scale' % (vnfm["type"], vnfm_inst_id, vnf_inst_id) + ret = req_by_msb(uri, "POST", req_param) + if ret[0] > 0: + logger.error("Failed to send nf scale req:%s,%s", ret[2], ret[1]) + raise NSLCMException('Failed to send nf scale request to VNFM(%s)' % vnfm_inst_id) + return json.JSONDecoder().decode(ret[1]) diff --git a/lcm/pub/msapi/wso2bpel.py b/lcm/pub/msapi/wso2bpel.py new file mode 100644 index 00000000..afc1c407 --- /dev/null +++ b/lcm/pub/msapi/wso2bpel.py @@ -0,0 +1,25 @@ +# Copyright 2016 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 json + +from lcm.pub.exceptions import NSLCMException +from lcm.pub.utils.restcall import req_by_msb + + +def workflow_run(content): + content_str = json.JSONEncoder().encode(content) + ret = req_by_msb("/openoapi/wso2bpel/v1/process/instance", "POST", content_str) + if ret[0] != 0: + raise NSLCMException("Status code is %s, detail is %s.", ret[2], ret[1]) + return json.JSONDecoder().decode(ret[1]) diff --git a/lcm/pub/nfvi/__init__.py b/lcm/pub/nfvi/__init__.py new file mode 100644 index 00000000..5580cc3d --- /dev/null +++ b/lcm/pub/nfvi/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2016 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/pub/nfvi/vim/__init__.py b/lcm/pub/nfvi/vim/__init__.py new file mode 100644 index 00000000..5580cc3d --- /dev/null +++ b/lcm/pub/nfvi/vim/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2016 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/pub/nfvi/vim/api/__init__.py b/lcm/pub/nfvi/vim/api/__init__.py new file mode 100644 index 00000000..f5e43792 --- /dev/null +++ b/lcm/pub/nfvi/vim/api/__init__.py @@ -0,0 +1,10 @@ +# Copyright 2016 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/pub/nfvi/vim/api/multivim/__init__.py b/lcm/pub/nfvi/vim/api/multivim/__init__.py new file mode 100644 index 00000000..8d66b23f --- /dev/null +++ b/lcm/pub/nfvi/vim/api/multivim/__init__.py @@ -0,0 +1,10 @@ +# 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/pub/nfvi/vim/api/multivim/api.py b/lcm/pub/nfvi/vim/api/multivim/api.py new file mode 100644 index 00000000..f3bdd30a --- /dev/null +++ b/lcm/pub/nfvi/vim/api/multivim/api.py @@ -0,0 +1,321 @@ +# 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 json +import logging + +from lcm.pub.nfvi.vim.lib.vimexception import VimException +from lcm.pub.utils.restcall import req_by_msb +from lcm.pub.nfvi.vim import const + +logger = logging.getLogger(__name__) + +VIM_DRIVER_BASE_URL = "openoapi/multivim/v1" + +def call(vim_id, tenant_id, res, method, data=''): + if data and not isinstance(data, (str, unicode)): + data = json.JSONEncoder().encode(data) + url = "{base_url}/{vim_id}{tenant_id}/{res}".format( + base_url=VIM_DRIVER_BASE_URL, + vim_id=vim_id, + tenant_id="/" + tenant_id if tenant_id else "", + res=res) + ret = req_by_msb(url, method, data) + if ret[0] > 0: + raise VimException(ret[1], ret[2]) + return json.JSONDecoder().decode(ret[1]) if ret[1] else {} + +###################################################################### + +def create_image(vim_id, tenant_id, data): + return call(vim_id, tenant_id, "images", "POST", data) + +def delete_image(vim_id, tenant_id, image_id): + return call(vim_id, tenant_id, "images/%s" % image_id, "DELETE") + +def get_image(vim_id, tenant_id, image_id): + return call(vim_id, tenant_id, "images/%s" % image_id, "GET") + +def list_image(vim_id, tenant_id): + return call(vim_id, tenant_id, "images", "GET") + +###################################################################### + +def create_network(vim_id, tenant_id, data): + return call(vim_id, tenant_id, "networks", "POST", data) + +def delete_network(vim_id, tenant_id, network_id): + return call(vim_id, tenant_id, "networks/%s" % network_id, "DELETE") + +def get_network(vim_id, tenant_id, network_id): + return call(vim_id, tenant_id, "networks/%s" % network_id, "GET") + +def list_network(vim_id, tenant_id): + return call(vim_id, tenant_id, "networks", "GET") + +###################################################################### + +def create_subnet(vim_id, tenant_id, data): + return call(vim_id, tenant_id, "subnets", "POST", data) + +def delete_subnet(vim_id, tenant_id, subnet_id): + return call(vim_id, tenant_id, "subnets/%s" % subnet_id, "DELETE") + +def get_subnet(vim_id, tenant_id, subnet_id): + return call(vim_id, tenant_id, "subnets/%s" % subnet_id, "GET") + +def list_subnet(vim_id, tenant_id): + return call(vim_id, tenant_id, "subnets", "GET") + +###################################################################### + +def create_port(vim_id, tenant_id, data): + return call(vim_id, tenant_id, "ports", "POST", data) + +def delete_port(vim_id, tenant_id, port_id): + return call(vim_id, tenant_id, "ports/%s" % port_id, "DELETE") + +def get_port(vim_id, tenant_id, port_id): + return call(vim_id, tenant_id, "ports/%s" % port_id, "GET") + +def list_port(vim_id, tenant_id): + return call(vim_id, tenant_id, "ports", "GET") + +###################################################################### + +def create_flavor(vim_id, tenant_id, data): + return call(vim_id, tenant_id, "flavors", "POST", data) + +def delete_flavor(vim_id, tenant_id, flavor_id): + return call(vim_id, tenant_id, "flavors/%s" % flavor_id, "DELETE") + +def get_flavor(vim_id, tenant_id, flavor_id): + return call(vim_id, tenant_id, "flavors/%s" % flavor_id, "GET") + +def list_flavor(vim_id, tenant_id): + return call(vim_id, tenant_id, "flavors", "GET") + +###################################################################### + +def create_vm(vim_id, tenant_id, data): + return call(vim_id, tenant_id, "servers", "POST", data) + +def delete_vm(vim_id, tenant_id, vm_id): + return call(vim_id, tenant_id, "servers/%s" % vm_id, "DELETE") + +def get_vm(vim_id, tenant_id, vm_id): + return call(vim_id, tenant_id, "servers/%s" % vm_id, "GET") + +def list_vm(vim_id, tenant_id): + return call(vim_id, tenant_id, "servers", "GET") + +###################################################################### + +def create_volume(vim_id, tenant_id, data): + return call(vim_id, tenant_id, "volumes", "POST", data) + +def delete_volume(vim_id, tenant_id, volume_id): + return call(vim_id, tenant_id, "volumes/%s" % volume_id, "DELETE") + +def get_volume(vim_id, tenant_id, volume_id): + return call(vim_id, tenant_id, "volumes/%s" % volume_id, "GET") + +def list_volume(vim_id, tenant_id): + return call(vim_id, tenant_id, "volumes", "GET") + +###################################################################### + +def list_tenant(vim_id, tenant_name=""): + res = "tenants" + if tenant_name: + res = "%s?name=%s" % (res, tenant_name) + return call(vim_id, "", res, "GET") + +###################################################################### + + +class MultiVimApi: + + def login(self, connect_info): + self.vim_id = connect_info["vimid"] + self.tenant_name = connect_info["tenant"] + tenants = list_tenant(self.vim_id) + for tenant in tenants["tenants"]: + if self.tenant_name == tenant["name"]: + self.tenant_id = tenant["id"] + return [0, connect_info] + raise VimException(1, "Tenant(%s) not exist" % self.tenant_name) + + def query_net(self, auth_info, net_id): + net = get_network(self.vim_id, self.tenant_id, net_id) + return [0, { + "id": net.get("id", ""), + "name": net.get("name", ""), + "status": net.get("status", ""), + "admin_state_up": net.get("admin_state_up", True), + "network_type": net.get("networkType", ""), + "physical_network": net.get("physicalNetwork", ""), + "segmentation_id": net.get("segmentationId", ""), + "tenant_id": self.tenant_id, + "tenant_name": self.tenant_name, + "subnets": net.get("subnets", []), + "shared": net.get("shared", True), + "router_external": net.get("routerExternal", "") + }] + + def query_nets(self, auth_info): + nets = list_network(self.vim_id, self.tenant_id) + return [0, {"networks": [{ + "id": net.get("id", ""), + "name": net.get("name", ""), + "status": net.get("status", ""), + "admin_state_up": net.get("admin_state_up", True), + "network_type": net.get("networkType", ""), + "physical_network": net.get("physicalNetwork", ""), + "segmentation_id": net.get("segmentationId", ""), + "tenant_id": self.tenant_id, + "tenant_name": self.tenant_name, + "subnets": net.get("subnets", []), + "shared": net.get("shared", True), + "router_external": net.get("routerExternal", "") + } for net in nets["networks"]]}] + + def query_subnet(self, auth_info, subnet_id): + subnet_info = get_subnet(self.vim_id, self.tenant_id, subnet_id) + ret = [0, {}] + ret[1]["id"] = subnet_id + ret[1]["name"] = subnet_info.get("name", "") + ret[1]["status"] = "" + ret[1]["ip_version"] = subnet_info.get("ipVersion", 4) + ret[1]["cidr"] = subnet_info.get("cidr", "") + ret[1]["allocation_pools"] = subnet_info.get("allocationPools", []) + ret[1]["enable_dhcp"] = subnet_info.get("enableDhcp", False) + ret[1]["gateway_ip"] = subnet_info.get("gatewayIp", "") + ret[1]["host_routes"] = subnet_info.get("hostRoutes", []) + ret[1]["dns_nameservers"] = subnet_info.get("dnsNameservers", []) + return ret + + def query_port(self, auth_info, port_id): + port_info = get_port(self.vim_id, self.tenant_id, port_id) + ret = [0, {}] + ret[1]["id"] = port_id + ret[1]["name"] = port_info.get("name", "") + ret[1]["network_id"] = port_info.get("networkId", "") + ret[1]["tenant_id"] = self.tenant_id, + ret[1]["ip"] = port_info.get("ip", "") + ret[1]["subnet_id"] = port_info.get("subnetId", "") + ret[1]["mac_address"] = port_info.get("macAddress", "") + ret[1]["status"] = port_info.get("status", "") + ret[1]["admin_state_up"] = port_info.get("admin_state_up", True) + ret[1]["device_id"] = port_info.get("device_id", "") + return ret + + def create_port(self, auth_info, data): + return [0, data] + + def delete_port(self, auth_info, port_id): + return [0, ""] + + def create_image(self, auth_info, data): + image_data = { + "name": data["image_name"], + "imagePath": data["image_url"], + "imageType": data["image_type"], + "containerFormat": "bare", + "visibility": "public", + "properties": [] + } + image = create_image(self.vim_id, self.tenant_id, image_data) + return [0, { + "id": image["id"], + "name": image["name"], + const.RES_TYPE_KEY: image["returnCode"]}] + + def get_image(self, auth_info, image_id): + image = get_image(self.vim_id, self.tenant_id, image_id) + return [0, { + "id": image["id"], + "name": image["name"], + "size": image["size"], + "status": image["status"]}] + + def get_images(self, auth_info): + images = list_image(self.vim_id, self.tenant_id) + return [0, {"image_list": [{ + "id": img["id"], + "name": img["name"], + "size": img["size"], + "status": img["status"] + } for img in images["images"]]}] + + def delete_image(self, auth_info, image_id): + return [0, ""] + + def create_network(self, auth_info, data): + net_data = { + "name": data["network_name"], + "shared": True, + "networkType": data["network_type"] + } + if "physical_network" in data and data['physical_network']: + net_data["physicalNetwork"] = data['physical_network'] + if "vlan_transparent" in data and data["vlan_transparent"]: + net_data["vlanTransparent"] = data["vlan_transparent"] + if "segmentation_id" in data and data['segmentation_id']: + net_data["segmentationId"] = data["segmentation_id"] + if "routerExternal" in data and data['routerExternal']: + net_data["routerExternal"] = data["routerExternal"] + net = create_network(self.vim_id, self.tenant_id, net_data) + network_id = net["id"] + ret_net = { + "status": net.get("status", ""), + "id": network_id, + "name": net.get("name", ""), + "provider:segmentation_id": net.get("segmentationId", ""), + "provider:network_type": net.get("networkType", ""), + const.RES_TYPE_KEY: net["returnCode"], + "subnet_list": [] + } + if "subnet_list" in data and data["subnet_list"]: + subnet = data["subnet_list"][0] + subnet_data = { + "networkId": network_id, + "name": subnet["subnet_name"], + "cidr": subnet["cidr"], + "ipVersion": const.IPV4, + "enableDhcp": False + } + if "ip_version" in subnet and subnet["ip_version"]: + subnet_data["ipVersion"] = int(subnet["ip_version"]) + if "enable_dhcp" in subnet and subnet["enable_dhcp"]: + subnet_data["enableDhcp"] = int(subnet["enable_dhcp"]) == const.ENABLE_DHCP + if "gateway_ip" in subnet and subnet["gateway_ip"]: + subnet_data["gatewayIp"] = subnet["gateway_ip"] + if "dns_nameservers" in subnet and subnet["dns_nameservers"]: + subnet_data["dnsNameservers"] = subnet["dns_nameservers"] + if "allocation_pools" in subnet and subnet["allocation_pools"]: + subnet_data["allocationPools"] = subnet["allocation_pools"] + if "host_routes" in subnet and subnet["host_routes"]: + subnet_data["hostRoutes"] = subnet["host_routes"] + subnet_create = create_subnet(self.vim_id, self.tenant_id, subnet_data) + ret_net["subnet_list"].append({ + "id": subnet_create["id"], + "name": subnet_create["name"], + const.RES_TYPE_KEY: net["returnCode"]}) + return [0, ret_net] + + def delete_network(self, auth_info, network_id): + return delete_network(self.vim_id, self.tenant_id, network_id) + + def delete_subnet(self, auth_info, subnet_id): + return delete_subnet(self.vim_id, self.tenant_id, subnet_id) diff --git a/lcm/pub/nfvi/vim/api/openstack/__init__.py b/lcm/pub/nfvi/vim/api/openstack/__init__.py new file mode 100644 index 00000000..5580cc3d --- /dev/null +++ b/lcm/pub/nfvi/vim/api/openstack/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2016 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/pub/nfvi/vim/api/openstack/api.py b/lcm/pub/nfvi/vim/api/openstack/api.py new file mode 100644 index 00000000..f781e475 --- /dev/null +++ b/lcm/pub/nfvi/vim/api/openstack/api.py @@ -0,0 +1,60 @@ +# Copyright 2016 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 lcm.pub.nfvi.vim.api.openstack import auth, network, image + + +class OpenstackApi: + + def login(self, connect_info): + return auth.login(connect_info) + + def query_net(self, auth_info, net_id): + return network.query_net(auth_info, net_id) + + def query_nets(self, auth_info): + return network.query_nets(auth_info) + + def query_subnet(self, auth_info, subnet_id): + return network.query_subnet(auth_info, subnet_id) + + def query_port(self, auth_info, port_id): + return network.query_port(auth_info, port_id) + + def create_port(self, auth_info, data): + return network.create_port(auth_info, data) + + def delete_port(self, auth_info, port_id): + return network.delete_port(auth_info, port_id) + + def create_image(self, auth_info, data): + return image.create_image(auth_info, data) + + def get_image(self, auth_info, image_id): + return image.get_image(auth_info, image_id) + + def get_images(self, auth_info): + return image.get_images(auth_info) + + def delete_image(self, auth_info, image_id): + return image.delete_image(auth_info, image_id) + + def create_network(self, auth_info, data): + return network.create_network(auth_info, data) + + def delete_network(self, auth_info, network_id): + return network.delete_network(auth_info, network_id) + + def delete_subnet(self, auth_info, subnet_id): + return network.delete_subnet(auth_info, subnet_id) diff --git a/lcm/pub/nfvi/vim/api/openstack/auth.py b/lcm/pub/nfvi/vim/api/openstack/auth.py new file mode 100644 index 00000000..b662c51f --- /dev/null +++ b/lcm/pub/nfvi/vim/api/openstack/auth.py @@ -0,0 +1,46 @@ +# Copyright 2016 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 + +from keystoneclient.v2_0 import client + +from lcm.pub.nfvi.vim.lib.syscomm import fun_name + +logger = logging.getLogger(__name__) + +OPENSTACK_CA_CERT = '/etc/httpd/conf.d/cert/vim/ca.cert' +OPENSTACK_CLIENT_CERT = '/etc/httpd/conf.d/cert/vim/client.cert' +OPENSTACK_CLIENT_KEY = '/etc/httpd/conf.d/cert/vim/client.key' + + +def login(connect_info): + url, user = connect_info["url"], connect_info["user"] + passwd, tenant = connect_info["passwd"], connect_info["tenant"] + cacert, clientcert, clientkey = None, None, None + insecure = url.startswith('https') + logger.info( + "[%s]client.Client(auth_url='%s'," + "username='%s',password='%s'," + "tenant_name='%s',insecure=%s,cert='%s',key='%s',cacert='%s')" % + (fun_name(), url, user, passwd, tenant, insecure, clientcert, clientkey, cacert)) + connect_info["cacert"] = cacert + connect_info["clientcert"] = clientcert + connect_info["clientkey"] = clientkey + connect_info["insecure"] = insecure + connect_info["keystone"] = client.Client(auth_url=url, username=user, password=passwd, interface='public', + tenant_name=tenant, insecure=insecure, cert=clientcert, key=clientkey, + cacert=cacert, debug=True) + ret = [0, connect_info] + return ret diff --git a/lcm/pub/nfvi/vim/api/openstack/glancebase.py b/lcm/pub/nfvi/vim/api/openstack/glancebase.py new file mode 100644 index 00000000..cfcde4fd --- /dev/null +++ b/lcm/pub/nfvi/vim/api/openstack/glancebase.py @@ -0,0 +1,43 @@ +# Copyright 2016 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 + +logger = logging.getLogger(__name__) + + +def get_glance(funname, auth_info, ver='v2'): + import glanceclient.v1.client as glanceclient1 + import glanceclient.v2.client as glanceclient2 + keystone = auth_info["keystone"] + cacert = auth_info["cacert"] + clientcert = auth_info["clientcert"] + clientkey = auth_info["clientkey"] + insecure = auth_info["insecure"] + glance_endpoint = keystone.service_catalog.url_for(service_type='image') + logger.info("[%s]call glanceclient.Client('%s',token='%s',insecure=%s,cert_file='%s',key_file='%s',cacert='%s')" + % (funname, glance_endpoint, keystone.auth_token, insecure, clientcert, clientkey, cacert)) + if 'v1' == ver: + logger.info("return glanceclient1") + return glanceclient1.Client(glance_endpoint, + token=keystone.auth_token, + insecure=insecure, + cert_file=clientcert, + key_file=clientkey, + cacert=cacert) + else: + logger.info("return glanceclient2") + return glanceclient2.Client(glance_endpoint, + token=keystone.auth_token, + insecure=insecure, + cert_file=clientcert, + key_file=clientkey, + cacert=cacert) diff --git a/lcm/pub/nfvi/vim/api/openstack/image.py b/lcm/pub/nfvi/vim/api/openstack/image.py new file mode 100644 index 00000000..2a04cb9a --- /dev/null +++ b/lcm/pub/nfvi/vim/api/openstack/image.py @@ -0,0 +1,106 @@ +# Copyright 2016 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 sys +import traceback +import threading + +from lcm.pub.nfvi.vim.api.openstack import glancebase +from lcm.pub.nfvi.vim.lib.syscomm import fun_name +from lcm.pub.nfvi.vim import const + +logger = logging.getLogger(__name__) + + +class ImageUploadThread(threading.Thread): + def __init__(self, glance, image_id, image_path): + threading.Thread.__init__(self) + self.glance = glance + self.image_id = image_id + self.image_path = image_path + + def run(self): + try: + self.glance.images.upload(self.image_id, open(self.image_path, 'rb')) + except Exception as ex: + logger.error(traceback.format_exc()) + err_msg = ex.message if ex.message else str(sys.exc_info()) + logger.error("Failed to upload image(%s): %s", self.image_id, err_msg) + except: + logger.error(traceback.format_exc()) + logger.error("Failed to upload image(%s): [%s]", self.image_id, str(sys.exc_info())) + + +def create_image(auth_info, data): + ret = None + glance = glancebase.get_glance(fun_name(), auth_info) + + exist_img = [img for img in glance.images.list() if img.name == data["image_name"]] + if exist_img: + ret = [0, {"id": exist_img[0].id, "name": data["image_name"], const.RES_TYPE_KEY: const.RES_TYPE_EXIST}] + else: + img = glance.images.create( + name=data["image_name"], + disk_format=data["image_type"], + visibility='public', + container_format='bare') + ret = [0, {"id": img.id, "name": data["image_name"], const.RES_TYPE_KEY: const.RES_TYPE_NEW}] + try: + ImageUploadThread(glance, img.id, data["image_path"]).start() + except: + logger.error(traceback.format_exc()) + logger.error(str(sys.exc_info())) + return ret + + +def get_image(auth_info, image_id): + from glanceclient.exc import HTTPNotFound + glance = glancebase.get_glance(fun_name(), auth_info) + img = None + try: + img = glance.images.get(image_id) + except HTTPNotFound: + logger.warn("Exception: %s" % str(sys.exc_info())) + return [2, "Image(%s) does not exist" % image_id] + ret_img_info = get_single_image(img) + if 'status' in ret_img_info and 'deleted' == ret_img_info["status"]: + return [2, "Image(%s) is deleted" % image_id] + return [0, ret_img_info] + + +def delete_image(auth_info, image_id): + from glanceclient.exc import HTTPNotFound + glance = glancebase.get_glance(fun_name(), auth_info) + try: + glance.images.delete(image_id) + except HTTPNotFound: + logger.warn("Exception: %s" % str(sys.exc_info())) + return [0, "Image(%s) does not exist" % image_id] + return [0, "Image(%s) has been deleted" % image_id] + + +def get_images(auth_info): + glance = glancebase.get_glance(fun_name(), auth_info) + imgs = glance.images.list() + return [0, {"image_list": [get_single_image(img) for img in imgs]}] + + +def get_single_image(img): + img_size = 0 + try: + img_size = img.size / 1024 + except: + pass + return {"id": img.id, "name": img.name, "size": img_size, "status": img.status} diff --git a/lcm/pub/nfvi/vim/api/openstack/network.py b/lcm/pub/nfvi/vim/api/openstack/network.py new file mode 100644 index 00000000..ebfdf660 --- /dev/null +++ b/lcm/pub/nfvi/vim/api/openstack/network.py @@ -0,0 +1,423 @@ +# Copyright 2016 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 sys +import traceback + +from neutronclient.common.exceptions import NeutronClientException +from neutronclient.common.exceptions import NetworkNotFoundClient +from neutronclient.common.exceptions import NotFound as SubnetNotFound + +from lcm.pub.nfvi.vim.api.openstack import neutronbase +from lcm.pub.nfvi.vim.lib.syscomm import fun_name +from lcm.pub.nfvi.vim.api.openstack import project +from lcm.pub.nfvi.vim import const +from lcm.pub.nfvi.vim.lib.vimexception import VimException + +logger = logging.getLogger(__name__) + + +def query_net(auth_info, net_id): + neutron = neutronbase.get_neutron_default(fun_name(), auth_info) + net = None + try: + net = neutron.show_network(net_id)["network"] + keystone = auth_info["keystone"] + tenant = keystone.tenants.get(tenant_id=net["tenant_id"]) + except NetworkNotFoundClient as e: + logger.warn("NetworkNotFoundClient: %s", e.message) + return [2, e.message] + return [0, { + "id": net["id"], + "name": net["name"], + "status": net["status"], + "admin_state_up": net["admin_state_up"], + "network_type": net["provider:network_type"], + "physical_network": net["provider:physical_network"], + "segmentation_id": net["provider:segmentation_id"], + "tenant_id": net["tenant_id"], + "tenant_name": tenant.name, + "subnets": net["subnets"], + "shared": net["shared"], + "router_external": net["router:external"] + }] + + +def query_nets(auth_info): + neutron = neutronbase.get_neutron_default(fun_name(), auth_info) + keystone = auth_info["keystone"] + tenants_map = {} + tenants = keystone.tenants.list() + for tenant in tenants: + tenants_map[tenant.id] = tenant.name + + nets = neutron.list_networks() + return [0, {"networks": [{ + "id": net["id"], + "name": net["name"], + "status": net["status"], + "admin_state_up": net["admin_state_up"], + "network_type": net["provider:network_type"], + "physical_network": net["provider:physical_network"], + "segmentation_id": net["provider:segmentation_id"], + "tenant_id": net["tenant_id"], + "tenant_name": tenants_map[net["tenant_id"]] if net["tenant_id"] in tenants_map else "unknown", + "subnets": net["subnets"], + "shared": net["shared"], + "router_external": net["router:external"] + } for net in nets["networks"]]}] + + +def query_subnet(auth_info, subnet_id): + neutron = neutronbase.get_neutron_default(fun_name(), auth_info) + subnet_info = None + try: + subnet_info = neutron.show_subnet(subnet_id)["subnet"] + except SubnetNotFound as e: + logger.warn("SubnetNotFound: %s", e.message) + return [2, e.message] + ret = [0, {}] + ret[1]["id"] = subnet_id + ret[1]["name"] = subnet_info["name"] + ret[1]["status"] = "" + ret[1]["ip_version"] = subnet_info["ip_version"] + ret[1]["cidr"] = subnet_info["cidr"] + ret[1]["allocation_pools"] = subnet_info["allocation_pools"] + ret[1]["enable_dhcp"] = subnet_info["enable_dhcp"] + ret[1]["gateway_ip"] = subnet_info["gateway_ip"] + ret[1]["host_routes"] = subnet_info["host_routes"] + ret[1]["dns_nameservers"] = subnet_info["dns_nameservers"] + return ret + + +def query_port(auth_info, port_id): + neutron = neutronbase.get_neutron_default(fun_name(), auth_info) + port_info = None + try: + port_info = neutron.show_port(port_id)["port"] + except NeutronClientException as e: + logger.warn("NeutronClientException: %s", e.message) + return [2, e.message] + ret = [0, {}] + ret[1]["id"] = port_id + ret[1]["name"] = port_info["name"] + ret[1]["network_id"] = port_info["network_id"] + ret[1]["tenant_id"] = port_info["tenant_id"] + if "fixed_ips" in port_info and port_info["fixed_ips"]: + ret[1]["ip"] = port_info["fixed_ips"][0]["ip_address"] + ret[1]["subnet_id"] = port_info["fixed_ips"][0]["subnet_id"] + else: + ret[1]["ip"] = "" + ret[1]["subnet_id"] = "" + ret[1]["mac_address"] = port_info["mac_address"] + ret[1]["status"] = port_info["status"] + ret[1]["admin_state_up"] = port_info["admin_state_up"] + ret[1]["device_id"] = port_info["device_id"] + return ret + + +def get_subnet_id(neutron, data, network_id): + subnet_id = None + if "subnet_name" in data and data["subnet_name"]: + all_subnets = neutron.list_subnets() + filter_subnets = [subnet for subnet in all_subnets["subnets"] if subnet["name"] == data["subnet_name"] + and subnet["network_id"] == network_id] + count_filter_subnets = len(filter_subnets) + if 1 > count_filter_subnets: + logger.error("Subnet name(%s) does not exist" % data["subnet_name"]) + raise VimException("Subnet name(%s) does not exist" % data["subnet_name"]) + if 1 < count_filter_subnets: + for subnet in filter_subnets: + logger.error("subnet_id=%s", subnet["id"]) + raise VimException("%d subnet(%s) exist in network(%s)" + % (count_filter_subnets, data["subnet_name"], data["network_name"])) + subnet_id = filter_subnets[0]['id'] + else: + subnets = neutron.list_subnets() + filter_subnets = [subnet for subnet in subnets["subnets"] if subnet["network_id"] == network_id] + if filter_subnets: + subnet_id = filter_subnets[0]["id"] + return subnet_id + + +def create_port(auth_info, data): + tenant_id = project.get_tenant_id(fun_name(), auth_info, data["tenant_name"]) + + neutron_admin = neutronbase.get_neutron_default(fun_name(), auth_info) + all_nets = neutron_admin.list_networks() + filter_nets = [net for net in all_nets['networks'] if net['name'] == data["network_name"]] + sel_nets = [net for net in filter_nets if net['tenant_id'] == tenant_id or + (net['tenant_id'] != tenant_id and net['shared'])] + count_sel_nets = len(sel_nets) + if 1 > count_sel_nets: + logger.error("Network(%s) does not exist" % data["network_name"]) + raise VimException("Network(%s) does not exist" % data["network_name"]) + if 1 < count_sel_nets: + for net in sel_nets: + logger.error("net_id=%s", net["id"]) + raise VimException("%d networks(%s) exist in tenant(%s)" + % (count_sel_nets, data["network_name"], data["tenant_name"])) + network_id = sel_nets[0]['id'] + if tenant_id != sel_nets[0]['tenant_id']: + neutron = neutronbase.get_neutron_by_tenant_id(fun_name(), auth_info, sel_nets[0]['tenant_id']) + else: + neutron = neutronbase.get_neutron(fun_name(), auth_info, data["tenant_name"]) + + # get subnet id + subnet_id = get_subnet_id(neutron_admin, data, network_id) + + # check port + port_data = None + ports = neutron.list_ports() + sel_ports = [port for port in ports['ports'] if port['tenant_id'] == tenant_id + and port['network_id'] == network_id] + filter_ports = [] + for port in sel_ports: + if port['name'] == data["port_name"] and port['fixed_ips']: + for fixed_ip in port['fixed_ips']: + if fixed_ip['subnet_id'] == subnet_id: + filter_ports.append(port) + break + count_filter_ports = len(filter_ports) + if 1 < count_filter_ports: + for port in filter_ports: + logger.error("port_id=%s", port["id"]) + raise VimException("%d port(%s) exist in subnet(%s)" + % (count_filter_ports, data["port_name"], data["subnet_name"])) + if 1 == len(filter_ports): + logger.debug("Port(%s) is exist!" % data["port_name"]) + port_data = {'status': filter_ports[0]['status'], + 'id': filter_ports[0]['id'], + 'name': filter_ports[0]['name'], + 'network_id': filter_ports[0]['network_id'], + const.RES_TYPE_KEY: const.RES_TYPE_EXIST} + return [0, port_data] + + # create port + create_param = {'port': { + 'name': data["port_name"], + 'admin_state_up': True, + 'network_id': network_id, + 'security_groups': [], + 'tenant_id': tenant_id}} + if "mac_address" in data and data["mac_address"]: + create_param['port']['mac_address'] = data["mac_address"] + if "vnic_type" in data and data["vnic_type"]: + create_param['port']['binding:vnic_type'] = data["vnic_type"] + if "bandwidth" in data and data["bandwidth"]: + create_param['port']['bandwidth'] = int(data["bandwidth"]) + if "bond" in data and data["bond"]: + create_param['port']['bond'] = int(data["bond"]) + if "macbond" in data and data["macbond"]: + if 'mac_address' in create_param['port']: + create_param['port']['mac_address'] += (',' + data["macbond"]) + else: + create_param['port']['mac_address'] = data["macbond"] + if "ip" in data and data["ip"]: + if subnet_id: + create_param['port']['fixed_ips'] = [{"subnet_id": subnet_id, "ip_address": data["ip"]}] + + if "allowed_address_pairs" in data and data["allowed_address_pairs"]: + create_param['port']['allowed_address_pairs'] = data["allowed_address_pairs"] + logger.info("[%s]call neutron.create_port(%s)" % (fun_name(), str(create_param))) + port_created = None + try: + port_created = neutron.create_port(create_param) + except NeutronClientException as ex: + logger.info("create_port exception: %s, %s", str(sys.exc_info()), ex.message) + create_param['port'].pop('security_groups') + if 'allowed_address_pairs' in create_param['port']: + create_param['port'].pop('allowed_address_pairs') + logger.info("[%s]recall neutron.create_port(%s)" % (fun_name(), str(create_param))) + port_created = neutron.create_port(create_param) + if port_created: + port_data = {'status': port_created['port']['status'], + 'id': port_created['port']['id'], + 'name': port_created['port']['name'], + 'network_id': port_created['port']['network_id'], + const.RES_TYPE_KEY: const.RES_TYPE_NEW} + return [0, port_data] + + +def create_network(auth_info, data): + neutron = neutronbase.get_neutron(fun_name(), auth_info, data["tenant"]) + tenant_id = project.get_tenant_id(fun_name(), auth_info, data["tenant"]) + + neutron_admin = neutronbase.get_neutron_default(fun_name(), auth_info) + all_nets = neutron_admin.list_networks() + filter_nets = [net for net in all_nets['networks'] if net['name'] == data["network_name"]] + sel_nets = [net for net in filter_nets if net['tenant_id'] == tenant_id or + (net['tenant_id'] != tenant_id and net['shared'])] + count_sel_nets = len(sel_nets) + if 1 < count_sel_nets: + for sel_net in sel_nets: + logger.info("net_id=%s, net_tenant_id=%s", sel_net["id"], sel_net['tenant_id']) + raise VimException("Already %d networks are found with name %s" % (count_sel_nets, data["network_name"])) + + network_data = None + if sel_nets: + if sel_nets[0]['tenant_id'] != tenant_id: + neutron = neutronbase.get_neutron_by_tenant_id(fun_name(), auth_info, sel_nets[0]['tenant_id']) + all_subnets = neutron_admin.list_subnets() + filter_subnets = [subnet for subnet in all_subnets["subnets"] if subnet["network_id"] == sel_nets[0]["id"]] + network_data = {"status": sel_nets[0]["status"], + "id": sel_nets[0]["id"], + "name": data["network_name"], + "provider:segmentation_id": sel_nets[0]["provider:segmentation_id"], + "provider:network_type": sel_nets[0]["provider:network_type"], + const.RES_TYPE_KEY: const.RES_TYPE_EXIST, + "subnet_list": [{ + "id": subnet["id"], + "name": subnet["name"], + const.RES_TYPE_KEY: const.RES_TYPE_EXIST + } for subnet in filter_subnets]} + else: + create_params = { + 'network': { + 'name': data["network_name"], + 'admin_state_up': True, + 'tenant_id': tenant_id, + 'shared': "shared" in data and int(data["shared"]) == const.SHARED_NET}} + if "mtu" in data and int(data["mtu"]) != const.DEFAULT_MTU: + create_params['network']['mtu'] = int(data["mtu"]) + if "vlan_transparent" in data and int(data["vlan_transparent"]) == const.SUPPORT_VLAN_TRANSPARENT: + create_params['network']['vlan-transparent'] = True + if "network_type" in data and data['network_type']: + create_params['network']['provider:network_type'] = data['network_type'] + if "segmentation_id" in data and data['segmentation_id']: + create_params['network']['provider:segmentation_id'] = int(data['segmentation_id']) + if "physical_network" in data and data['physical_network']: + create_params['network']['provider:physical_network'] = data['physical_network'] + + logger.info("[%s]call neutron.create_network(%s)" % (fun_name(), str(create_params))) + network_created = neutron.create_network(create_params) + network_data = {"status": network_created['network']['status'], + "id": network_created['network']['id'], + "name": data["network_name"], + "provider:segmentation_id": network_created['network']['provider:segmentation_id'], + "provider:network_type": network_created['network']['provider:network_type'], + const.RES_TYPE_KEY: const.RES_TYPE_NEW, + "subnet_list": []} + + # subnet create + exist_subnet_names = [subnet["name"] for subnet in network_data["subnet_list"]] + need_rollback, ret_error = False, None + if "subnet_list" in data and data["subnet_list"]: + for subnet_data in data["subnet_list"]: + if subnet_data["subnet_name"] in exist_subnet_names: + continue + ret = create_subnet(neutron, network_data["id"], subnet_data) + if ret[0] != 0: + need_rollback, ret_error = True, ret + break + network_data["subnet_list"].append(ret[1]) + + # rollback when failed to create subnet + if need_rollback: + rollback(neutron_admin, network_data) + return ret_error + + return [0, network_data] + + +def create_subnet(neutron, network_id, data): + all_subnets = neutron.list_subnets() + filter_subnets = [subnet for subnet in all_subnets["subnets"] + if subnet["network_id"] == network_id and subnet["name"] == data["subnet_name"]] + if filter_subnets: + return [0, { + "id": filter_subnets[0]["id"], + "name": data["subnet_name"], + const.RES_TYPE_KEY: const.RES_TYPE_EXIST}] + try: + create_params = { + 'subnet': { + 'network_id': network_id, + 'name': data["subnet_name"], + 'cidr': data["cidr"], + 'ip_version': int(data["ip_version"]) if "ip_version" in data else const.IPV4, }} + create_params["subnet"]["enable_dhcp"] = ("enable_dhcp" in data + and int(data["enable_dhcp"]) == const.ENABLE_DHCP) + if "gateway_ip" in data and data["gateway_ip"]: + create_params["subnet"]["gateway_ip"] = data["gateway_ip"] + else: + create_params["subnet"]["gateway_ip"] = None + if "dns_nameservers" in data and data["dns_nameservers"]: + create_params["subnet"]["dns_nameservers"] = data["dns_nameservers"] + if "allocation_pools" in data and data["allocation_pools"]: + create_params["subnet"]["allocation_pools"] = data["allocation_pools"] + if "host_routes" in data and data["host_routes"]: + create_params["subnet"]["host_routes"] = data["host_routes"] + + logger.info("[%s]call neutron.create_subnet(%s)" % (fun_name(), str(create_params))) + subnet_created = neutron.create_subnet(create_params) + return [0, {"id": subnet_created["subnet"]["id"], + "name": data["subnet_name"], + const.RES_TYPE_KEY: const.RES_TYPE_NEW}] + except Exception as ex: + logger.error(traceback.format_exc()) + logger.error(str(sys.exc_info())) + return [1, ex.message if ex.message else str(sys.exc_info())] + + +def rollback(neutron, network_data): + for subnet_data in network_data["subnet_list"]: + if subnet_data[const.RES_TYPE_KEY] == const.RES_TYPE_NEW: + try: + logger.info("[%s]call neutron.delete_subnet(%s)" % (fun_name(), subnet_data["id"])) + neutron.delete_subnet(subnet_data["subnet_id"]) + except: + logger.error("[%s]%s", fun_name(), str(sys.exc_info())) + + if network_data and network_data[const.RES_TYPE_KEY] == const.RES_TYPE_NEW: + try: + logger.info("[%s]call neutron.delete_network(%s)" % (fun_name(), network_data["id"])) + neutron.delete_network(network_data["id"]) + except: + logger.error("[%s]%s", fun_name(), str(sys.exc_info())) + + +def delete_network(auth_info, network_id): + neutron = neutronbase.get_neutron_default(fun_name(), auth_info) + try: + neutron.delete_network(network_id) + except Exception as ex: + logger.error(traceback.format_exc()) + msg = ex.message if ex.message else str(sys.exc_info()) + logger.error(msg) + if 404 == ex.status_code: + return [0, ex.message] + return [1, "Vim exception."] + return [0, "Network(%s) is deleted" % network_id] + + +def delete_subnet(auth_info, subnet_id): + neutron = neutronbase.get_neutron_default(fun_name(), auth_info) + try: + neutron.delete_subnet(subnet_id) + except NeutronClientException as e: + logger.warn("[%s]NetworkNotFoundClient: %s", fun_name(), e.message) + return [0, e.message] + return [0, "Subnet(%s) is deleted" % subnet_id] + + +def delete_port(auth_info, port_id): + neutron = neutronbase.get_neutron_default(fun_name(), auth_info) + try: + neutron.delete_port(port_id) + except NeutronClientException as e: + logger.warn("[%s]NeutronClientException: %s", fun_name(), e.message) + return [0, e.message] + return [0, "Port(%s) is deleted" % port_id] diff --git a/lcm/pub/nfvi/vim/api/openstack/neutronbase.py b/lcm/pub/nfvi/vim/api/openstack/neutronbase.py new file mode 100644 index 00000000..1a755cc8 --- /dev/null +++ b/lcm/pub/nfvi/vim/api/openstack/neutronbase.py @@ -0,0 +1,46 @@ +# Copyright 2016 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 neutronclient.v2_0.client as neutronclient + +logger = logging.getLogger(__name__) + + +def get_neutron(funname, auth_info, tenant_name): + username = auth_info["user"] + passwd = auth_info["passwd"] + url = auth_info["url"] + cacert = auth_info["cacert"] + insecure = auth_info["insecure"] + logger.info("[%s]call neutronclient.Client(auth_url='%s'," + "username='%s',password='%s',tenant_name='%s',insecure=%s,ca_cert='%s')" + % (funname, url, username, passwd, tenant_name, insecure, cacert)) + return neutronclient.Client(username=username, password=passwd, tenant_name=tenant_name, + insecure=insecure, auth_url=url, ca_cert=cacert) + + +def get_neutron_by_tenant_id(funname, auth_info, tenant_id): + username = auth_info["user"] + passwd = auth_info["passwd"] + url = auth_info["url"] + cacert = auth_info["cacert"] + logger.info("[%s]call neutronclient.Client(auth_url='%s'," + "username='%s',password='%s',tenant_id='%s',ca_cert='%s')" + % (funname, url, username, passwd, tenant_id, cacert)) + return neutronclient.Client(username=username, password=passwd, tenant_id=tenant_id, auth_url=url, ca_cert=cacert) + + +def get_neutron_default(funname, auth_info): + return get_neutron(funname, auth_info, auth_info["tenant"]) diff --git a/lcm/pub/nfvi/vim/api/openstack/project.py b/lcm/pub/nfvi/vim/api/openstack/project.py new file mode 100644 index 00000000..b0c079ad --- /dev/null +++ b/lcm/pub/nfvi/vim/api/openstack/project.py @@ -0,0 +1,27 @@ +# Copyright 2016 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 + +from lcm.pub.nfvi.vim.lib.vimexception import VimException + + +logger = logging.getLogger(__name__) + + +def get_tenant_id(funname, auth_info, tenant): + logger.debug("[%s]call get_tenant_id(%s)", funname, tenant) + keystone = auth_info["keystone"] + tids = [t.id for t in keystone.tenants.list() if t.name == tenant] + if not tids: + raise VimException("Tenant(%s) does not exist." % tenant) + logger.debug("[%s]tenant_id=%s", funname, tids[0]) + return tids[0] diff --git a/lcm/pub/nfvi/vim/const.py b/lcm/pub/nfvi/vim/const.py new file mode 100644 index 00000000..e8798689 --- /dev/null +++ b/lcm/pub/nfvi/vim/const.py @@ -0,0 +1,26 @@ +# Copyright 2016 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. + + +VIM_OPENSTACK = "openstack" +VIM_VMWARE = "vmware" +RES_TYPE_KEY = "res_type" +RES_TYPE_NEW = 1 +RES_TYPE_EXIST = 0 +SHARED_NET = 1 +SUPPORT_VLAN_TRANSPARENT = 1 +DEFAULT_MTU = 1500 +IPV4 = 4 +IPV6 = 6 +ENABLE_DHCP = 1 diff --git a/lcm/pub/nfvi/vim/lib/__init__.py b/lcm/pub/nfvi/vim/lib/__init__.py new file mode 100644 index 00000000..5580cc3d --- /dev/null +++ b/lcm/pub/nfvi/vim/lib/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2016 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/pub/nfvi/vim/lib/syscomm.py b/lcm/pub/nfvi/vim/lib/syscomm.py new file mode 100644 index 00000000..447bf4ee --- /dev/null +++ b/lcm/pub/nfvi/vim/lib/syscomm.py @@ -0,0 +1,20 @@ +# Copyright 2016 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 inspect + + +def fun_name(): + return inspect.stack()[1][3] diff --git a/lcm/pub/nfvi/vim/lib/vimexception.py b/lcm/pub/nfvi/vim/lib/vimexception.py new file mode 100644 index 00000000..b2b09cc3 --- /dev/null +++ b/lcm/pub/nfvi/vim/lib/vimexception.py @@ -0,0 +1,17 @@ +# Copyright 2016 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. + + +class VimException(Exception): + pass diff --git a/lcm/pub/nfvi/vim/test/__init__.py b/lcm/pub/nfvi/vim/test/__init__.py new file mode 100644 index 00000000..5580cc3d --- /dev/null +++ b/lcm/pub/nfvi/vim/test/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2016 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/pub/nfvi/vim/test/openstack/__init__.py b/lcm/pub/nfvi/vim/test/openstack/__init__.py new file mode 100644 index 00000000..5580cc3d --- /dev/null +++ b/lcm/pub/nfvi/vim/test/openstack/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2016 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/pub/nfvi/vim/test/openstack/pub.py b/lcm/pub/nfvi/vim/test/openstack/pub.py new file mode 100644 index 00000000..6c36f43e --- /dev/null +++ b/lcm/pub/nfvi/vim/test/openstack/pub.py @@ -0,0 +1,23 @@ +# Copyright 2016 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 lcm.pub.nfvi.vim import const + +connect_info = { + "vimtype": const.VIM_OPENSTACK, + "url": "http://10.43.35.131:5000/v2.0", + "user": "admin", + "passwd": "keystone", + "tenant": "admin"} diff --git a/lcm/pub/nfvi/vim/test/openstack/test_image.py b/lcm/pub/nfvi/vim/test/openstack/test_image.py new file mode 100644 index 00000000..004d737b --- /dev/null +++ b/lcm/pub/nfvi/vim/test/openstack/test_image.py @@ -0,0 +1,91 @@ +# Copyright 2016 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 os +import time + +from lcm.pub.nfvi.vim import vimadaptor +from lcm.pub.nfvi.vim.test.openstack import pub +from lcm.pub.nfvi.vim import const + + +class TestImage(unittest.TestCase): + def setUp(self): + self.api = vimadaptor.VimAdaptor(pub.connect_info) + self.currentdir = os.path.dirname(os.path.abspath(__file__)) + def tearDown(self): + pass + def createImg(self, data): + return self.api.create_image(data) + + def test_image_all(self): + image_data = { + "image_name": "cirros", + "image_path": self.currentdir + "/testdata/cirros.qcow2", + "image_type": "qcow2" + } + + # create image + ret = self.createImg(image_data) + self.assertEqual(0, ret[0]) + if ret[1][const.RES_TYPE_KEY] == const.RES_TYPE_EXIST: + self.api.delete_image(image_id = ret[1]["id"]) + ret = self.createImg(image_data) + self.assertEqual(0, ret[0]) + imageid = ret[1]["id"] + retryTimes = 0 + while retryTimes < 10: + ret = self.api.get_image(image_id = imageid) + self.assertEqual(0, ret[0]) + if ret[1]["status"] == 'active': + break + time.sleep(2) + + # image is exist + ret = self.createImg(image_data) + self.assertEqual(0, ret[0]) + self.assertEqual(const.RES_TYPE_EXIST, ret[1][const.RES_TYPE_KEY]) + + # get all images + ret = self.api.get_images() + self.assertEqual(0, ret[0]) + flag = False + for image in ret[1]['image_list']: + if image_data["image_name"] == image["name"]: + flag = True + break + self.assertTrue(flag) + + # delete image + ret = self.api.delete_image(image_id = imageid) + self.assertEqual(0, ret[0]) + + # get_image except + ret = self.api.get_image(image_id = imageid) + self.assertEqual(2, ret[0]) + + # delete image except + ret = self.api.delete_image(image_id = imageid) + self.assertEqual(0, ret[0]) + + # Exception + image_data["image_path"] = "aaaa" + ret = self.createImg(image_data) + self.assertEqual(0, ret[0]) + + imageid = ret[1]["id"] + self.api.delete_image(image_id = imageid) +""" diff --git a/lcm/pub/nfvi/vim/test/openstack/test_network.py b/lcm/pub/nfvi/vim/test/openstack/test_network.py new file mode 100644 index 00000000..54ed68d6 --- /dev/null +++ b/lcm/pub/nfvi/vim/test/openstack/test_network.py @@ -0,0 +1,245 @@ +# Copyright 2016 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 +from lcm.pub.nfvi.vim import vimadaptor +from lcm.pub.nfvi.vim.api.openstack import neutronbase +from lcm.pub.nfvi.vim.lib.syscomm import fun_name +from lcm.pub.nfvi.vim.test.openstack import pub +from lcm.pub.nfvi.vim import const + +class TestNetwork(unittest.TestCase): + def setUp(self): + self.api = vimadaptor.VimAdaptor(pub.connect_info) + self.network_data = { + "tenant": "admin", + "network_name": "testnet1", + "shared": const.SHARED_NET, + "network_type": "", + "mtu": 1523, + "subnet_list": [{ + "subnet_name": "subnet1", + "cidr": "192.168.1.0/24", + "ip_version": const.IPV4, + "enable_dhcp": 0, + "gateway_ip": "192.168.1.1", + "dns_nameservers": [], + "allocation_pools":[], + "host_routes": [] + }] + } + + self.port_data = { + "port_name":"port_test", + "tenant_name":"test", + "network_name":"testnet1", + "mac_address":"fa:16:3e:c9:cb:f5", + "vnic_type":"normal", + "bandwidth":"100", + "bond":"0", + "macbond":"", + "ip":"192.168.1.10", + "subnet_name":"subnet1", + "allowed_address_pairs":[{'ip_address':'192.168.1.11','mac_address':'fa:16:3e:c9:cb:f6'}] + } + + self.port_data_no_subname = { + "port_name":"port_test_no_subname", + "tenant_name":"admin", + "network_name":"testnet1", + "mac_address":"fa:16:3e:c9:cb:f7", + "vnic_type":"normal", + "bandwidth":"100", + "bond":"0", + "macbond":"", + "ip":"192.168.1.12", + "allowed_address_pairs":[{'ip_address':'192.168.1.13','mac_address':'fa:16:3e:c9:cb:f8'}] + } + + self.network_data_rollback = { + "tenant": "admin", + "network_name": "testnet1", + "shared": const.SHARED_NET, + "network_type": "", + "mtu": 1523, + "subnet_list": [{ + "subnet_name": "subnet1", + "cidr": "192.168.1.0/24", + "ip_version": const.IPV4, + "enable_dhcp": 0, + "gateway_ip": "192.168.1.1", + "dns_nameservers": [], + "allocation_pools":[], + "host_routes": [] + }, + { + "subnet_name": "subnet2", + "cidr": "191.168.1.0/24", + "ip_version": const.IPV6, + "enable_dhcp": 0, + "gateway_ip": "191.168.1.1", + "dns_nameservers": [], + "allocation_pools":[], + "host_routes": [] + }] + } + def tearDown(self): + pass + + def test_network_all(self): + neutron = neutronbase.get_neutron_default(fun_name(), pub.connect_info) + + # create network + ret = self.api.create_network(self.network_data) + self.assertEqual(0, ret[0], ret[1]) + if ret[1][const.RES_TYPE_KEY] == const.RES_TYPE_EXIST: + for subnet in ret[1]["subnet_list"]: + ports = neutron.list_ports() + for port in ports['ports']: + for fixed_ip in port['fixed_ips']: + if fixed_ip['subnet_id'] == subnet["id"]: + self.api.delete_port(port['id']) + break + ret_del = self.api.delete_subnet(subnet_id = subnet["id"]) + self.assertEqual(0, ret_del[0]) + ret_del = self.api.delete_network(network_id = ret[1]["id"]) + self.assertEqual(0, ret_del[0]) + ret = self.api.create_network(self.network_data) + self.assertEqual(0, ret[0]) + + # network exist + ret = self.api.create_network(self.network_data) + self.assertEqual(0, ret[0]) + self.assertEqual(ret[1][const.RES_TYPE_KEY], const.RES_TYPE_EXIST) + + # query subnet + q_subnet_ret = self.api.query_subnet(ret[1]["subnet_list"][0]["id"]) + self.assertEqual(0, q_subnet_ret[0]) + + # query net + q_net_ret = self.api.query_net(ret[1]["id"]) + self.assertEqual(0, q_net_ret[0]) + + # query nets + q_nets_ret = self.api.query_nets() + self.assertEqual(0, q_nets_ret[0]) + flag = False + for network in q_nets_ret[1]['networks']: + if ret[1]["id"] == network["id"]: + flag = True + break + self.assertTrue(flag) + + # create port + create_port_ret = self.api.create_port(self.port_data) + self.assertEqual(0, create_port_ret[0], create_port_ret[1]) + + # port exist + create_port_ret = self.api.create_port(self.port_data) + self.assertEqual(0, create_port_ret[0]) + self.assertEqual(create_port_ret[1][const.RES_TYPE_KEY], const.RES_TYPE_EXIST) + + # create port no subname + ret_no_subname = self.api.create_port(self.port_data_no_subname) + self.assertEqual(0, ret_no_subname[0], ret_no_subname[1]) + self.api.delete_port(ret_no_subname[1]['id']) + + # create port except, networks not exist + network_name = self.port_data["network_name"] + self.port_data["network_name"] = "no_network" + create_port_except_ret = self.api.create_port(self.port_data) + self.assertEqual(1, create_port_except_ret[0]) + self.port_data["network_name"] = network_name + + # create port except, subnet not exist + subnet_name = self.port_data["subnet_name"] + self.port_data["subnet_name"] = "no_subnet" + create_port_except_ret = self.api.create_port(self.port_data) + self.assertEqual(1, create_port_except_ret[0]) + self.port_data["subnet_name"] = subnet_name + + # query port + q_port_ret = self.api.query_port(create_port_ret[1]['id']) + self.assertEqual(0, q_port_ret[0], q_port_ret[1]) + + # delete port + ret_port_del = self.api.delete_port(create_port_ret[1]['id']) + self.assertEqual(0, ret_port_del[0]) + + # delete network + for subnet in ret[1]["subnet_list"]: + ret_del = self.api.delete_subnet(subnet_id = subnet["id"]) + self.assertEqual(0, ret_del[0]) + ret_del = self.api.delete_network(network_id = ret[1]["id"]) + self.assertEqual(0, ret_del[0]) + + # query net except + q_net_ret = self.api.query_net(ret[1]["id"]) + self.assertEqual(2, q_net_ret[0]) + + # query subnet except + q_subnet_ret = self.api.query_subnet(ret[1]["subnet_list"][0]["id"]) + self.assertEqual(2, q_subnet_ret[0]) + + # rollback test + ret_roolback = self.api.create_network(self.network_data_rollback) + self.assertEqual(1, ret_roolback[0]) + + # delete except + ret_del = self.api.delete_subnet(subnet_id = "11111") + self.assertEqual(0, ret_del[0]) + + ret_del = self.api.delete_network(network_id = "11111") + self.assertEqual(0, ret_del[0]) + + ret_del = self.api.delete_port(port_id = "11111") + self.assertEqual(0, ret_del[0]) + + # query except + q_del = self.api.query_port(port_id = "11111") + self.assertEqual(2, q_del[0]) + + # multiple network except + tenant = self.network_data["tenant"] + network = self.network_data["network_name"] + self.network_data["tenant"] = "test" + self.network_data["network_name"] = "ut_test_network" + ret = self.api.create_network(self.network_data) + self.network_data["tenant"] = tenant + self.network_data["network_name"] = network + self.assertEqual(1, ret[0]) + + tenant = self.port_data["tenant_name"] + network = self.port_data["network_name"] + self.port_data["tenant_name"] = "test" + self.port_data["network_name"] = "ut_test_network" + ret = self.api.create_port(self.port_data) + self.port_data["tenant_name"] = tenant + self.port_data["network_name"] = network + self.assertEqual(1, ret[0]) + + # multiple subnet except + tenant = self.port_data["tenant_name"] + network = self.port_data["network_name"] + subnet = self.port_data["subnet_name"] + self.port_data["tenant_name"] = "test" + self.port_data["network_name"] = "ut_test_subnet_except" + self.port_data["subnet_name"] = "ut_test_same_subnet" + ret = self.api.create_port(self.port_data) + self.port_data["tenant_name"] = tenant + self.port_data["network_name"] = network + self.port_data["subnet_name"] = subnet + self.assertEqual(1, ret[0]) +""" diff --git a/lcm/pub/nfvi/vim/test/openstack/testdata/cirros.qcow2 b/lcm/pub/nfvi/vim/test/openstack/testdata/cirros.qcow2 Binary files differnew file mode 100644 index 00000000..afe3ca37 --- /dev/null +++ b/lcm/pub/nfvi/vim/test/openstack/testdata/cirros.qcow2 diff --git a/lcm/pub/nfvi/vim/vimadaptor.py b/lcm/pub/nfvi/vim/vimadaptor.py new file mode 100644 index 00000000..5f08d0b1 --- /dev/null +++ b/lcm/pub/nfvi/vim/vimadaptor.py @@ -0,0 +1,120 @@ +# Copyright 2016 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 sys +import traceback + +from requests import RequestException + +from lcm.pub.nfvi.vim.lib.syscomm import fun_name +from lcm.pub.nfvi.vim import const +from lcm.pub.nfvi.vim.lib.vimexception import VimException + +logger = logging.getLogger(__name__) + + +class VimAdaptor: + def __init__(self, connectInfo): + logger.info("[VimAdaptor]connectInfo=%s" % connectInfo) + self.apiImpl, self.authInfo = None, [1, "No auth info"] + self.create_api(connectInfo) + self.force_login(connectInfo) + + def create_api(self, connectInfo): + vimtype = connectInfo['vimtype'] if 'vimtype' in connectInfo else None + logger.info("call %s, vimtype=%s" % (fun_name(), vimtype)) + if vimtype == const.VIM_OPENSTACK: + from lcm.pub.nfvi.vim.api.openstack.api import OpenstackApi + self.apiImpl = OpenstackApi() + elif vimtype == const.VIM_VMWARE: + from lcm.pub.nfvi.vim.api.multivim.api import MultiVimApi + self.apiImpl = MultiVimApi() + else: + self.authInfo = [1, "Unsupported vimtype(%s)" % vimtype] + + def api_call(self, funname, fun, *args): + logger.info("call %s%s" % (funname, str(args))) + ret = None + try: + ret = fun(self.authInfo[1], *args) if self.authInfo[0] == 0 else self.authInfo + except VimException as e: + ret = [1, e.message] + except RequestException as e: + logger.error("request=%s, url=%s" % (e.request.headers._store, e.request.url)) + logger.error(traceback.format_exc()) + ret = [1, e.message if e.message else str(sys.exc_info())] + except Exception as ex: + logger.error(traceback.format_exc()) + ret = [1, ex.message if ex.message else str(sys.exc_info())] + except: + logger.error(traceback.format_exc()) + ret = [1, str(sys.exc_info())] + logger.info("[%s]ret=%s" % (funname, ret)) + return ret + + def force_login(self, connectInfo): + if self.apiImpl: + logger.info("call %s(%s)" % (fun_name(), connectInfo)) + try: + self.authInfo = self.apiImpl.login(connectInfo) + except VimException as e: + self.authInfo = [1, e.message] + except Exception as ex: + logger.error(traceback.format_exc()) + logger.error(str(sys.exc_info())) + self.authInfo = [1, ex.message if ex.message else str(sys.exc_info())] + except: + logger.error(traceback.format_exc()) + self.authInfo = [1, str(sys.exc_info())] + logger.info("self.authInfo=%s" % self.authInfo) + + def query_net(self, net_id): + return self.api_call(fun_name(), self.apiImpl.query_net, net_id) + + def query_nets(self): + return self.api_call(fun_name(), self.apiImpl.query_nets) + + def query_subnet(self, subnet_id): + return self.api_call(fun_name(), self.apiImpl.query_subnet, subnet_id) + + def query_port(self, port_id): + return self.api_call(fun_name(), self.apiImpl.query_port, port_id) + + def create_image(self, data): + return self.api_call(fun_name(), self.apiImpl.create_image, data) + + def get_image(self, image_id): + return self.api_call(fun_name(), self.apiImpl.get_image, image_id) + + def get_images(self): + return self.api_call(fun_name(), self.apiImpl.get_images) + + def delete_image(self, image_id): + return self.api_call(fun_name(), self.apiImpl.delete_image, image_id) + + def create_network(self, data): + return self.api_call(fun_name(), self.apiImpl.create_network, data) + + def delete_network(self, network_id): + return self.api_call(fun_name(), self.apiImpl.delete_network, network_id) + + def delete_subnet(self, subnet_id): + return self.api_call(fun_name(), self.apiImpl.delete_subnet, subnet_id) + + def create_port(self, data): + return self.api_call(fun_name(), self.apiImpl.create_port, data) + + def delete_port(self, port_id): + return self.api_call(fun_name(), self.apiImpl.delete_port, port_id) diff --git a/lcm/pub/utils/__init__.py b/lcm/pub/utils/__init__.py new file mode 100644 index 00000000..5580cc3d --- /dev/null +++ b/lcm/pub/utils/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2016 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/pub/utils/enumutil.py b/lcm/pub/utils/enumutil.py new file mode 100644 index 00000000..8b263db4 --- /dev/null +++ b/lcm/pub/utils/enumutil.py @@ -0,0 +1,17 @@ +# Copyright 2016 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. + + +def enum(**enums): + return type('Enum', (), enums) diff --git a/lcm/pub/utils/fileutil.py b/lcm/pub/utils/fileutil.py new file mode 100644 index 00000000..3568e5ee --- /dev/null +++ b/lcm/pub/utils/fileutil.py @@ -0,0 +1,52 @@ +# Copyright 2016 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 os +import shutil +import logging +import traceback +import urllib2 + +logger = logging.getLogger(__name__) + + +def make_dirs(path): + if not os.path.exists(path): + os.makedirs(path, 0777) + + +def delete_dirs(path): + try: + if os.path.exists(path): + shutil.rmtree(path) + except Exception as e: + logger.error(traceback.format_exc()) + logger.error("Failed to delete %s:%s", path, e.message) + + +def download_file_from_http(url, local_dir, file_name): + local_file_name = os.path.join(local_dir, file_name) + is_download_ok = False + try: + make_dirs(local_dir) + r = urllib2.Request(url) + req = urllib2.urlopen(r) + save_file = open(local_file_name, 'wb') + save_file.write(req.read()) + save_file.close() + req.close() + is_download_ok = True + except: + logger.error(traceback.format_exc()) + logger.error("Failed to download %s to %s.", url, local_file_name) + return is_download_ok, local_file_name diff --git a/lcm/pub/utils/idutil.py b/lcm/pub/utils/idutil.py new file mode 100644 index 00000000..85bebb83 --- /dev/null +++ b/lcm/pub/utils/idutil.py @@ -0,0 +1,20 @@ +# Copyright 2016 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 redisco import containers as cont + + +def get_auto_id(id_type, id_group="auto_id_hash"): + auto_id_hash = cont.Hash(id_group) + auto_id_hash.hincrby(id_type, 1) + return auto_id_hash.hget(id_type) diff --git a/lcm/pub/utils/jobutil.py b/lcm/pub/utils/jobutil.py new file mode 100644 index 00000000..650d2afc --- /dev/null +++ b/lcm/pub/utils/jobutil.py @@ -0,0 +1,142 @@ +# Copyright 2016-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 datetime +import logging +import uuid +import traceback + +from lcm.pub.database.models import JobStatusModel, JobModel +from lcm.pub.utils import idutil + +logger = logging.getLogger(__name__) + + +def enum(**enums): + return type('Enum', (), enums) + + +JOB_STATUS = enum(PROCESSING=0, FINISH=1) +JOB_MODEL_STATUS = enum(STARTED='started', PROCESSING='processing', FINISHED='finished', ERROR='error', + TIMEOUT='timeout') +JOB_TYPE = enum(CREATE_VNF="create vnf", TERMINATE_VNF="terminate vnf", GRANT_VNF="grant vnf", MANUAL_SCALE_VNF="manual scale vnf") + + +class JobUtil(object): + def __init__(self): + pass + + @staticmethod + def __gen_job_id(job_name): + return "%s-%s" % (job_name if job_name else "UnknownJob", uuid.uuid1()) + + @staticmethod + def query_job_status(job_id, index_id=-1): + #logger.info("Query job status, jobid =[%s], responseid [%d]" % (job_id, index_id)) + jobs = [] + if index_id < 0: + row = JobStatusModel.objects.filter(jobid=job_id).order_by("-indexid").first() + if row: + jobs.append(row) + else: + [jobs.append(job) for job in JobStatusModel.objects.filter(jobid=job_id).order_by("-indexid") + if job.indexid > index_id] + + #logger.info("Query job status, rows=%s" % str(jobs)) + return jobs + + @staticmethod + def is_job_exists(job_id): + jobs = JobModel.objects.filter(jobid=job_id) + return len(jobs) > 0 + + @staticmethod + def create_job(inst_type, jobaction, inst_id, user='', job_id=None, res_name=''): + if job_id is None: + job_id = JobUtil.__gen_job_id( + '%s-%s-%s' % (str(inst_type).replace(' ', '_'), str(jobaction).replace(' ', '_'), str(inst_id))) + job = JobModel() + job.jobid = job_id + job.jobtype = inst_type + job.jobaction = jobaction + job.resid = str(inst_id) + job.status = JOB_STATUS.PROCESSING + job.user = user + job.starttime = datetime.datetime.now().strftime('%Y-%m-%d %X') + job.progress = 0 + job.resname = res_name + logger.debug("create a new job, jobid=%s, jobtype=%s, jobaction=%s, resid=%s, status=%d" % + (job.jobid, job.jobtype, job.jobaction, job.resid, job.status)) + job.save() + return job_id + + @staticmethod + def clear_job(job_id): + [job.delete() for job in JobModel.objects.filter(jobid=job_id)] + logger.debug("Clear job, job_id=%s" % job_id) + + @staticmethod + def add_job_status(job_id, progress, status_decs, error_code=""): + jobs = JobModel.objects.filter(jobid=job_id) + if not jobs: + logger.error("Job[%s] is not exists, please create job first." % job_id) + raise Exception("Job[%s] is not exists." % job_id) + try: + int_progress = int(progress) + job_status = JobStatusModel() + job_status.indexid = int(idutil.get_auto_id(job_id)) + job_status.jobid = job_id + job_status.status = "processing" + job_status.progress = int_progress + + if job_status.progress == 0: + job_status.status = "started" + elif job_status.progress == 100: + job_status.status = "finished" + elif job_status.progress == 101: + job_status.status = "partly_finished" + elif job_status.progress > 101: + job_status.status = "error" + + if error_code == "255": + job_status.status = "error" + + job_status.descp = status_decs + job_status.errcode = error_code + job_status.addtime = datetime.datetime.now().strftime('%Y-%m-%d %X') + job_status.save() + logger.debug("Add a new job status, jobid=%s, indexid=%d," + " status=%s, description=%s, progress=%d, errcode=%s, addtime=%r" % + (job_status.jobid, job_status.indexid, job_status.status, job_status.descp, + job_status.progress, job_status.errcode, job_status.addtime)) + + job = jobs[0] + job.progress = int_progress + if job_status.progress >= 100: + job.status = JOB_STATUS.FINISH + job.endtime = datetime.datetime.now().strftime('%Y-%m-%d %X') + job.save() + logger.debug("update job, jobid=%s, progress=%d" % (job_status.jobid, int_progress)) + except: + logger.error(traceback.format_exc()) + + @staticmethod + def clear_job_status(job_id): + [job.delete() for job in JobStatusModel.objects.filter(jobid=job_id)] + logger.debug("Clear job status, job_id=%s" % job_id) + + @staticmethod + def get_unfinished_jobs(url_prefix, inst_id, inst_type): + jobs = JobModel.objects.filter(resid=inst_id, jobtype=inst_type, status=JOB_STATUS.PROCESSING) + progresses = reduce(lambda content, job: content + [url_prefix + "/" + job.jobid], jobs, []) + return progresses diff --git a/lcm/pub/utils/restcall.py b/lcm/pub/utils/restcall.py new file mode 100644 index 00000000..bfb02d6b --- /dev/null +++ b/lcm/pub/utils/restcall.py @@ -0,0 +1,95 @@ +# Copyright 2016 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 sys +import traceback +import logging +import urllib2 +import uuid +import httplib2 + +from lcm.pub.config.config import MSB_SERVICE_IP, MSB_SERVICE_PORT + +rest_no_auth, rest_oneway_auth, rest_bothway_auth = 0, 1, 2 +HTTP_200_OK, HTTP_201_CREATED, HTTP_204_NO_CONTENT, HTTP_202_ACCEPTED = '200', '201', '204', '202' +status_ok_list = [HTTP_200_OK, HTTP_201_CREATED, HTTP_204_NO_CONTENT, HTTP_202_ACCEPTED] +HTTP_404_NOTFOUND, HTTP_403_FORBIDDEN, HTTP_401_UNAUTHORIZED, HTTP_400_BADREQUEST = '404', '403', '401', '400' + +logger = logging.getLogger(__name__) + + +def call_req(base_url, user, passwd, auth_type, resource, method, content=''): + callid = str(uuid.uuid1()) + logger.debug("[%s]call_req('%s','%s','%s',%s,'%s','%s','%s')" % ( + callid, base_url, user, passwd, auth_type, resource, method, content)) + ret = None + resp_status = '' + try: + full_url = combine_url(base_url, resource) + headers = {'content-type': 'application/json', 'accept': 'application/json'} + if user: + headers['Authorization'] = 'Basic ' + ('%s:%s' % (user, passwd)).encode("base64") + ca_certs = None + for retry_times in range(3): + http = httplib2.Http(ca_certs=ca_certs, disable_ssl_certificate_validation=(auth_type == rest_no_auth)) + http.follow_all_redirects = True + try: + resp, resp_content = http.request(full_url, method=method.upper(), body=content, headers=headers) + resp_status, resp_body = resp['status'], resp_content.decode('UTF-8') + logger.debug("[%s][%d]status=%s,resp_body=%s)" % (callid, retry_times, resp_status, resp_body)) + if resp_status in status_ok_list: + ret = [0, resp_body, resp_status] + else: + ret = [1, resp_body, resp_status] + break + except Exception as ex: + if 'httplib.ResponseNotReady' in str(sys.exc_info()): + logger.debug("retry_times=%d", retry_times) + logger.error(traceback.format_exc()) + ret = [1, "Unable to connect to %s" % full_url, resp_status] + continue + raise ex + except urllib2.URLError as err: + ret = [2, str(err), resp_status] + except Exception as ex: + logger.error(traceback.format_exc()) + logger.error("[%s]ret=%s" % (callid, str(sys.exc_info()))) + res_info = str(sys.exc_info()) + if 'httplib.ResponseNotReady' in res_info: + res_info = "The URL[%s] request failed or is not responding." % full_url + ret = [3, res_info, resp_status] + except: + logger.error(traceback.format_exc()) + ret = [4, str(sys.exc_info()), resp_status] + + logger.debug("[%s]ret=%s" % (callid, str(ret))) + return ret + + +def req_by_msb(resource, method, content=''): + base_url = "http://%s:%s/" % (MSB_SERVICE_IP, MSB_SERVICE_PORT) + return call_req(base_url, "", "", rest_no_auth, resource, method, content) + + +def combine_url(base_url, resource): + full_url = None + if base_url.endswith('/') and resource.startswith('/'): + full_url = base_url[:-1] + resource + elif base_url.endswith('/') and not resource.startswith('/'): + full_url = base_url + resource + elif not base_url.endswith('/') and resource.startswith('/'): + full_url = base_url + resource + else: + full_url = base_url + '/' + resource + return full_url diff --git a/lcm/pub/utils/scaleaspect.py b/lcm/pub/utils/scaleaspect.py new file mode 100644 index 00000000..dda1797e --- /dev/null +++ b/lcm/pub/utils/scaleaspect.py @@ -0,0 +1,140 @@ +# 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 json +import os +import logging +from lcm.pub.exceptions import NSLCMException +from rest_framework import status +from rest_framework.response import Response +from rest_framework.views import APIView +from rest_framework import status +from rest_framework.response import Response + +logger = logging.getLogger(__name__) +SCALE_TYPE = ("SCALE_NS", "SCALE_VNF") + +scale_vnf_data_mapping = { + "vnfInstanceId":"", + "scaleByStepData":[ + { + "type":"", + "aspectId":"", + "numberOfSteps":"" + } + ] +} + +def ignorcase_get(args, key): + if not key: + return "" + if not args: + return "" + if key in args: + return args[key] + for old_key in args: + if old_key.upper() == key.upper(): + return args[old_key] + return "" + +def mapping_conv(keyword_map, rest_return): + resp_data = {} + for param in keyword_map: + if keyword_map[param]: + if isinstance(keyword_map[param], dict): + resp_data[param] = mapping_conv(keyword_map[param], ignorcase_get(rest_return, param)) + else: + resp_data[param] = ignorcase_get(rest_return, param) + return resp_data + +def get_vnf_scale_info(filename, ns_instanceId, aspect, step): + json_data = get_json_data(filename) + scale_options = ignorcase_get(json_data, "scale_options") + for i in range(scale_options.__len__()): + ns_scale_option = scale_options[i] + if (ignorcase_get(ns_scale_option, "ns_instanceId") == ns_instanceId) \ + and (ignorcase_get(ns_scale_option, "ns_scale_aspect") == aspect): + ns_scale_info_list = ignorcase_get(ns_scale_option, "ns_scale_info_list") + for j in range(ns_scale_info_list.__len__()): + ns_scale_info = ns_scale_info_list[j] + if ns_scale_info["step"] == step: + return ns_scale_info["vnf_scale_list"] + + return None + +def get_json_data(filename): + f = open(filename) + json_str = f.read() + data = json.JSONDecoder().decode(json_str) + f.close() + return data + +def check_scale_list(vnf_scale_list, ns_instanceId, aspect, step): + if vnf_scale_list is None: + logger.debug("The scaling option[ns=%s, aspect=%s, step=%s] does not exist. Pls check the config file." %(ns_instanceId, aspect, step)) + raise Exception("The scaling option[ns=%s, aspect=%s, step=%s] does not exist. Pls check the config file." %(ns_instanceId, aspect, step)) + else: + return vnf_scale_list + +def set_scaleVnfData_type(vnf_scale_list, scale_type): + logger.debug("vnf_scale_list = %s, type = %s" % (vnf_scale_list, scale_type)) + scaleVnfDataList = [] + if vnf_scale_list is not None: + for i in range(vnf_scale_list.__len__()): + scaleVnfData = scale_vnf_data_mapping + scaleVnfData["vnfInstanceId"] = get_vnfInstanceIdByName(vnf_scale_list[i]["vnfInstanceId"]) + scaleVnfData["scaleByStepData"][0]["type"] = scale_type + scaleVnfData["scaleByStepData"][0]["aspectId"] = vnf_scale_list[i]["vnf_scaleAspectId"] + scaleVnfData["scaleByStepData"][0]["numberOfSteps"] = vnf_scale_list[i]["numberOfSteps"] + scaleVnfDataList.append(scaleVnfData) + logger.debug("scaleVnfDataList = %s" % scaleVnfDataList) + return scaleVnfDataList + +def get_vnfInstanceIdByName(name): + return name + +def get_vnf_data(filename, ns_instanceId, aspect, step, scale_type): + + vnf_scale_list = get_vnf_scale_info(filename, ns_instanceId, aspect, step) + check_scale_list(vnf_scale_list, ns_instanceId, aspect, step) + scaleVnfDataList = set_scaleVnfData_type(vnf_scale_list,scale_type) + logger.debug("scaleVnfDataList = %s" % scaleVnfDataList) + return scaleVnfDataList + + #return Response(data={'error': e.message},status=status.HTTP_204_NO_CONTENT) + #return Response(data={'success': 'success'},status=status.HTTP_200_OK) + +def get_and_check_params(scaleNsData, ns_InstanceId): + + if scaleNsData is None: + pass + #raise NSLCMException("Error! scaleNsData in the request is Empty!") + + scaleNsByStepsData = scaleNsData[0]["scaleNsByStepsData"] + if scaleNsByStepsData is None: + pass + #raise NSLCMException("Error! scaleNsByStepsData in the request is Empty!") + + aspect = scaleNsByStepsData[0]["aspectId"] + numberOfSteps = scaleNsByStepsData[0]["numberOfSteps"] + scale_type = scaleNsByStepsData[0]["scalingDirection"] + + return ns_InstanceId,aspect,numberOfSteps,scale_type + +def get_scale_vnf_data(scaleNsData, ns_InstanceId): + curdir_path = os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))) + filename = curdir_path + "/ns/data/scalemapping.json" + logger.debug("filename = %s" % filename) + ns_InstanceId,aspect,numberOfSteps,scale_type = get_and_check_params(scaleNsData, ns_InstanceId) + return get_vnf_data(filename, ns_InstanceId,aspect,numberOfSteps,scale_type) diff --git a/lcm/pub/utils/share_lock.py b/lcm/pub/utils/share_lock.py new file mode 100644 index 00000000..1e5e555b --- /dev/null +++ b/lcm/pub/utils/share_lock.py @@ -0,0 +1,80 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# Copyright 2016 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 time + +import redis + +from lcm.pub.config.config import REDIS_HOST, REDIS_PORT, REDIS_PASSWD + + +class SharedLock: + def __init__(self, lock_key, host=REDIS_HOST, port=REDIS_PORT, password=REDIS_PASSWD, db=9, lock_timeout=5 * 60): + self.lock_key = lock_key + self.lock_timeout = lock_timeout + self.redis = redis.Redis(host=host, port=port, db=db, password=password) + self.acquire_time = -1 + + def acquire(self): + begin = now = int(time.time()) + while (now - begin) < self.lock_timeout: + + result = self.redis.setnx(self.lock_key, now + self.lock_timeout + 1) + if result == 1 or result is True: + self.acquire_time = now + return True + + current_lock_timestamp = self.redis.get(self.lock_key) + if not current_lock_timestamp: + time.sleep(1) + continue + + current_lock_timestamp = int(current_lock_timestamp) + + if now > current_lock_timestamp: + next_lock_timestamp = self.redis.getset(self.lock_key, now + self.lock_timeout + 1) + if not next_lock_timestamp: + time.sleep(1) + continue + next_lock_timestamp = int(next_lock_timestamp) + + if next_lock_timestamp == current_lock_timestamp: + self.acquire_time = now + return True + else: + time.sleep(1) + continue + return False + + def release(self): + now = int(time.time()) + if now > self.acquire_time + self.lock_timeout: + # key expired, do nothing and let other clients handle it + return + self.acquire_time = None + self.redis.delete(self.lock_key) + + +def do_biz_with_share_lock(lock_name, callback): + lock = SharedLock(lock_name) + try: + if not lock.acquire(): + raise Exception(lock_name + " timeout") + callback() + except Exception as e: + raise e + finally: + lock.release() diff --git a/lcm/pub/utils/syscomm.py b/lcm/pub/utils/syscomm.py new file mode 100644 index 00000000..90e5c344 --- /dev/null +++ b/lcm/pub/utils/syscomm.py @@ -0,0 +1,19 @@ +# Copyright 2016 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 inspect + + +def fun_name(): + return inspect.stack()[1][3] diff --git a/lcm/pub/utils/timeutil.py b/lcm/pub/utils/timeutil.py new file mode 100644 index 00000000..b703a3ca --- /dev/null +++ b/lcm/pub/utils/timeutil.py @@ -0,0 +1,19 @@ +# Copyright 2016 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 datetime + + +def now_time(fmt="%Y-%m-%d %H:%M:%S"): + return datetime.datetime.now().strftime(fmt) diff --git a/lcm/pub/utils/toscautil.py b/lcm/pub/utils/toscautil.py new file mode 100644 index 00000000..f9ac6c13 --- /dev/null +++ b/lcm/pub/utils/toscautil.py @@ -0,0 +1,2632 @@ +# Copyright 2016-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 json + +def safe_get(key_val, key): + return key_val[key] if key in key_val else "" + + +def find_node_name(node_id, node_list): + for node in node_list: + if node['id'] == node_id: + return node['template_name'] + raise Exception('can not find node(%s).' % node_id) + + +def find_node_type(node_id, node_list): + for node in node_list: + if node['id'] == node_id: + return node['type_name'] + raise Exception('can not find node(%s).' % node_id) + + +def find_related_node(node_id, src_json_model, requirement_name): + related_nodes = [] + for model_tpl in safe_get(src_json_model, "node_templates"): + for rt in safe_get(model_tpl, 'requirement_templates'): + if safe_get(rt, 'name') == requirement_name and \ + safe_get(rt, 'target_node_template_name') == node_id: + related_nodes.append(model_tpl['name']) + return related_nodes + + +def convert_props(src_node, dest_node): + if 'properties' in src_node and src_node['properties']: + for prop_name, prop_info in src_node['properties'].items(): + if 'value' in prop_info: + dest_node['properties'][prop_name] = prop_info['value'] + + +def convert_metadata(src_json): + return src_json['metadata'] if 'metadata' in src_json else {} + + +def convert_inputs(src_json): + inputs = {} + if 'inputs' in src_json: + src_inputs = src_json['inputs'] + for param_name, param_info in src_inputs.items(): + input_param = {} + if 'type_name' in param_info: + input_param['type'] = param_info['type_name'] + if 'description' in param_info: + input_param['description'] = param_info['description'] + if 'value' in param_info: + input_param['value'] = param_info['value'] + inputs[param_name] = input_param + return inputs + + +def convert_vnf_node(src_node, src_json_model): + vnf_node = {'type': src_node['type_name'], 'vnf_id': src_node['template_name'], + 'description': '', 'properties': {}, 'dependencies': [], 'networks': []} + convert_props(src_node, vnf_node) + for model_tpl in safe_get(src_json_model, "node_templates"): + if model_tpl['name'] != vnf_node['vnf_id']: + continue + vnf_node['dependencies'] = [{ + 'key_name': requirement['name'], + 'vl_id': requirement['target_node_template_name']} for \ + requirement in safe_get(model_tpl, 'requirement_templates') if \ + safe_get(requirement, 'target_capability_name') == 'virtual_linkable'] + vnf_node['networks'] = [requirement['target_node_template_name'] for \ + requirement in safe_get(model_tpl, 'requirement_templates') if \ + safe_get(requirement, 'name') == 'dependency'] + return vnf_node + + +def convert_pnf_node(src_node, src_json_model): + pnf_node = {'pnf_id': src_node['template_name'], 'description': '', 'properties': {}} + convert_props(src_node, pnf_node) + pnf_node['cps'] = find_related_node(src_node['id'], src_json_model, 'virtualbinding') + return pnf_node + + +def convert_vl_node(src_node, src_node_list): + vl_node = {'vl_id': src_node['template_name'], 'description': '', 'properties': {}} + convert_props(src_node, vl_node) + vl_node['route_id'] = '' + for relation in safe_get(src_node, 'relationships'): + if safe_get(relation, 'type_name').endswith('.VirtualLinksTo'): + vl_node['route_id'] = find_node_name(relation['target_node_id'], src_node_list) + break + vl_node['route_external'] = (src_node['type_name'].find('.RouteExternalVL') > 0) + return vl_node + + +def convert_cp_node(src_node, src_node_list, model_type='NSD'): + cp_node = {'cp_id': src_node['template_name'], 'description': '', 'properties': {}} + convert_props(src_node, cp_node) + src_relationships = src_node['relationships'] + for relation in src_relationships: + if safe_get(relation, 'name') == 'virtualLink': + cp_node['vl_id'] = find_node_name(relation['target_node_id'], src_node_list) + elif safe_get(relation, 'name') == 'virtualbinding': + node_key = 'pnf_id' if model_type == 'NSD' else 'vdu_id' + cp_node[node_key] = find_node_name(relation['target_node_id'], src_node_list) + return cp_node + + +def convert_router_node(src_node, src_node_list): + router_node = {'router_id': src_node['template_name'], 'description': '', 'properties': {}} + convert_props(src_node, router_node) + for relation in src_node['relationships']: + if safe_get(relation, 'name') != 'external_virtual_link': + continue + router_node['external_vl_id'] = find_node_name(relation['target_node_id'], src_node_list) + router_node['external_ip_addresses'] = [] + if 'properties' not in relation: + continue + for prop_name, prop_info in relation['properties'].items(): + if prop_name == 'router_ip_address': + router_node['external_ip_addresses'].append(prop_info['value']) + break + return router_node + + +def convert_fp_node(src_node, src_node_list, src_json_model): + fp_node = {'fp_id': src_node['template_name'], 'description': '', + 'properties': {}, 'forwarder_list': []} + convert_props(src_node, fp_node) + for relation in safe_get(src_node, 'relationships'): + if safe_get(relation, 'name') != 'forwarder': + continue + forwarder_point = {'type': 'vnf'} + target_node_type = find_node_type(relation['target_node_id'], src_node_list).upper() + if target_node_type.find('.CP.') >= 0 or target_node_type.endswith('.CP'): + forwarder_point['type'] = 'cp' + forwarder_point['node_name'] = find_node_name(relation['target_node_id'], src_node_list) + forwarder_point['capability'] = '' + if forwarder_point['type'] == 'vnf': + for node_tpl in src_json_model["node_templates"]: + if fp_node['fp_id'] != node_tpl["name"]: + continue + for r_tpl in safe_get(node_tpl, "requirement_templates"): + if safe_get(r_tpl, "target_node_template_name") != forwarder_point['node_name']: + continue + forwarder_point['capability'] = safe_get(r_tpl, "target_capability_name") + break + break + fp_node['forwarder_list'].append(forwarder_point) + return fp_node + + +def convert_vnffg_group(src_group, src_group_list, src_node_list): + vnffg = {'vnffg_id': src_group['template_name'], 'description': '', + 'properties': {}, 'members': []} + convert_props(src_group, vnffg) + for member_node_id in src_group['member_node_ids']: + vnffg['members'].append(find_node_name(member_node_id, src_node_list)) + return vnffg + + +def convert_imagefile_node(src_node, src_node_list): + image_node = {'image_file_id': src_node['template_name'], 'description': '', + 'properties': {}} + convert_props(src_node, image_node) + return image_node + + +def convert_localstorage_node(src_node, src_node_list): + localstorage_node = {'local_storage_id': src_node['template_name'], 'description': '', + 'properties': {}} + convert_props(src_node, localstorage_node) + return localstorage_node + + +def convert_vdu_node(src_node, src_node_list, src_json_model): + vdu_node = {'vdu_id': src_node['template_name'], 'description': '', 'properties': {}, + 'image_file': '', 'local_storages': [], 'dependencies': [], 'nfv_compute': {}, + 'vls': [], 'artifacts': []} + convert_props(src_node, vdu_node) + + for relation in src_node['relationships']: + r_id, r_name = safe_get(relation, 'target_node_id'), safe_get(relation, 'name') + if r_name == 'guest_os': + vdu_node['image_file'] = find_node_name(r_id, src_node_list) + elif r_name == 'local_storage': + vdu_node['local_storages'].append(find_node_name(r_id, src_node_list)) + elif r_name.endswith('.AttachesTo'): + nt = find_node_type(r_id, src_node_list) + if nt.endswith('.BlockStorage.Local') or nt.endswith('.LocalStorage'): + vdu_node['local_storages'].append(find_node_name(r_id, src_node_list)) + + for capability in src_node['capabilities']: + if capability['name'] != 'nfv_compute': + continue + for prop_name, prop_info in capability['properties'].items(): + if 'value' in prop_info: + vdu_node['nfv_compute'][prop_name] = prop_info['value'] + + vdu_node['cps'] = find_related_node(src_node['id'], src_json_model, 'virtualbinding') + + for cp_node in vdu_node['cps']: + for src_cp_node in src_node_list: + if src_cp_node['template_name'] != cp_node: + continue + for relation in safe_get(src_cp_node, 'relationships'): + if relation['name'] != 'virtualLink': + continue + vl_node_name = find_node_name(relation['target_node_id'], src_node_list) + if vl_node_name not in vdu_node['vls']: + vdu_node['vls'].append(vl_node_name) + + for item in safe_get(src_node, 'artifacts'): + artifact = {'artifact_name': item['name'], 'type': item['type_name'], + 'file': item['source_path']} + vdu_node['artifacts'].append(artifact) + + return vdu_node + + +def convert_exposed_node(src_json, src_nodes, exposed): + for item in safe_get(safe_get(src_json, 'substitution'), 'requirements'): + exposed['external_cps'].append({'key_name': item['mapped_name'], + "cp_id": find_node_name(item['node_id'], src_nodes)}) + for item in safe_get(safe_get(src_json, 'substitution'), 'capabilities'): + exposed['forward_cps'].append({'key_name': item['mapped_name'], + "cp_id": find_node_name(item['node_id'], src_nodes)}) + + +def convert_vnffgs(src_json_inst, src_nodes): + vnffgs = [] + src_groups = safe_get(src_json_inst, 'groups') + for group in src_groups: + type_name = group['type_name'].upper() + if type_name.find('.VNFFG.') >= 0 or type_name.endswith('.VNFFG'): + vnffgs.append(convert_vnffg_group(group, src_groups, src_nodes)) + return vnffgs + + +def convert_common(src_json, target_json): + if isinstance(src_json, (unicode, str)): + src_json_dict = json.loads(src_json) + else: + src_json_dict = src_json + src_json_inst = src_json_dict["instance"] + src_json_model = src_json_dict["model"] if "model" in src_json_dict else {} + + target_json['metadata'] = convert_metadata(src_json_inst) + target_json['inputs'] = convert_inputs(src_json_inst) + target_json['vls'] = [] + target_json['cps'] = [] + target_json['routers'] = [] + + return src_json_inst, src_json_model + +def convert_policy_node(src_json): + target_json = {'name': src_json['template_name'],'file_url': src_json['properties']['drl_file_url']['value']} + + return target_json + +def convert_nsd_model(src_json): + target_json = {'vnfs': [], 'pnfs': [], 'fps': [], 'policies': []} + src_json_inst, src_json_model = convert_common(src_json, target_json) + + src_nodes = src_json_inst['nodes'] + for node in src_nodes: + type_name = node['type_name'] + if type_name.find('.VNF.') > 0 or type_name.endswith('.VNF'): + target_json['vnfs'].append(convert_vnf_node(node, src_json_model)) + elif type_name.find('.PNF.') > 0 or type_name.endswith('.PNF'): + target_json['pnfs'].append(convert_pnf_node(node, src_json_model)) + elif type_name.find('.VL.') > 0 or type_name.endswith('.VL') \ + or node['type_name'].find('.RouteExternalVL') > 0: + target_json['vls'].append(convert_vl_node(node, src_nodes)) + elif type_name.find('.CP.') > 0 or type_name.endswith('.CP'): + target_json['cps'].append(convert_cp_node(node, src_nodes)) + elif type_name.find('.FP.') > 0 or type_name.endswith('.FP'): + target_json['fps'].append(convert_fp_node(node, src_nodes, src_json_model)) + elif type_name.endswith('.Router'): + target_json['routers'].append(convert_router_node(node, src_nodes)) + elif type_name.endswith('tosca.policies.Drools'): + target_json['policies'].append(convert_policy_node(node)) + + target_json['vnffgs'] = convert_vnffgs(src_json_inst, src_nodes) + + target_json['ns_exposed'] = {'external_cps': [], 'forward_cps': []} + convert_exposed_node(src_json_inst, src_nodes, target_json['ns_exposed']) + return json.dumps(target_json) + + +def convert_vnfd_model(src_json): + target_json = {'image_files': [], 'local_storages': [], 'vdus': []} + src_json_inst, src_json_model = convert_common(src_json, target_json) + if "vnfdVersion" in src_json_inst.get("metadata", {}): + from . import toscautil_new + return toscautil_new.convert_vnfd_model(src_json) + + src_nodes = src_json_inst['nodes'] + for node in src_nodes: + type_name = node['type_name'] + if type_name.endswith('.ImageFile'): + target_json['image_files'].append(convert_imagefile_node(node, src_nodes)) + elif type_name.endswith('.BlockStorage.Local') or type_name.endswith('.LocalStorage'): + target_json['local_storages'].append(convert_localstorage_node(node, src_nodes)) + elif type_name.find('.VDU.') > 0 or type_name.endswith('.VDU'): + target_json['vdus'].append(convert_vdu_node(node, src_nodes, src_json_model)) + elif type_name.find('.VL.') > 0 or type_name.endswith('.VL') \ + or node['type_name'].find('.RouteExternalVL') > 0: + target_json['vls'].append(convert_vl_node(node, src_nodes)) + elif type_name.find('.CP.') > 0 or type_name.endswith('.CP'): + target_json['cps'].append(convert_cp_node(node, src_nodes, 'VNFD')) + elif type_name.endswith('.Router'): + target_json['routers'].append(convert_router_node(node, src_nodes)) + + target_json['vnf_exposed'] = {'external_cps': [], 'forward_cps': []} + convert_exposed_node(src_json_inst, src_nodes, target_json['vnf_exposed']) + return json.dumps(target_json) + +if __name__ == '__main__': + src_json = json.dumps( + { + "instance":{ + "metadata":{ + "vendor":"ZTE", + "name":"VCPE_NS", + "csarVersion":"v1.0", + "csarType":"NSAR", + "csarProvider":"ZTE", + "version":1, + "invariant_id":"vcpe_ns_sff_1", + "id":"VCPE_NS", + "description":"vcpe_ns" + }, + "policies:":[ + { + "aaa:" : { + "type": "tosca.policies.Drools", + "properties": { + "drl_file_url":"policies/abc.drl" + } + } + } + ], + "nodes":[ + { + "id":"policies", + "type_name":"tosca.policies.Drools", + "template_name":"aaa", + "properties":{ + "drl_file_url":{ + "type_name":"string", + "value":"policies/abc.drl" + } + } + }, + { + "id":"path2_kgmfqr5ldqs9lj3oscrgxqefc", + "type_name":"tosca.nodes.nfv.ext.FP", + "template_name":"path2", + "properties":{ + "symmetric":{ + "type_name":"boolean", + "value":False + }, + "policy":{ + "type_name":"tosca.datatypes.nfv.ext.FPPolicy", + "value":{ + "type":"ACL", + "criteria":{ + "dest_port_range":"1-100", + "ip_protocol":"tcp", + "source_ip_range":[ + "119.1.1.1-119.1.1.10" + ], + "dest_ip_range":[ + {"get_input":"NatIpRange"} + ], + "dscp":0, + "source_port_range":"1-100" + } + } + } + }, + "interfaces":[ + { + "name":"Standard", + "description":"This lifecycle interface defines the essential, normative operations that TOSCA nodes may support.", + "type_name":"tosca.interfaces.node.lifecycle.Standard", + "operations":[ + { + "name":"create", + "description":"Standard lifecycle create operation." + }, + { + "name":"stop", + "description":"Standard lifecycle stop operation." + }, + { + "name":"start", + "description":"Standard lifecycle start operation." + }, + { + "name":"delete", + "description":"Standard lifecycle delete operation." + }, + { + "name":"configure", + "description":"Standard lifecycle configure operation." + } + ] + } + ], + "capabilities":[ + { + "name":"feature", + "type_name":"tosca.capabilities.Node" + } + ], + "relationships":[ + { + "name":"forwarder", + "source_requirement_index":0, + "target_node_id":"m6000_data_out_qeukdtf6g87cnparxi51fa8s6" + }, + { + "name":"forwarder", + "source_requirement_index":1, + "target_node_id":"m600_tunnel_cp_imwfk5l48ljz0g9knc6d68hv5" + }, + { + "name":"forwarder", + "source_requirement_index":2, + "target_node_id":"VNAT_cfdljtspvkp234irka59wgab0", + "target_capability_name":"feature" + } + ] + }, + { + "id":"path1_bv53fblv26hawr8dj4fxe2rsd", + "type_name":"tosca.nodes.nfv.ext.FP", + "template_name":"path1", + "properties":{ + "symmetric":{ + "type_name":"boolean", + "value":True + }, + "policy":{ + "type_name":"tosca.datatypes.nfv.ext.FPPolicy", + "value":{ + "type":"ACL", + "criteria":{ + "dest_port_range":"1-100", + "ip_protocol":"tcp", + "source_ip_range":[ + "1-100" + ], + "dest_ip_range":[ + "1-100" + ], + "dscp":4, + "source_port_range":"1-100" + } + } + } + }, + "interfaces":[ + { + "name":"Standard", + "description":"This lifecycle interface defines the essential, normative operations that TOSCA nodes may support.", + "type_name":"tosca.interfaces.node.lifecycle.Standard", + "operations":[ + { + "name":"create", + "description":"Standard lifecycle create operation." + }, + { + "name":"stop", + "description":"Standard lifecycle stop operation." + }, + { + "name":"start", + "description":"Standard lifecycle start operation." + }, + { + "name":"delete", + "description":"Standard lifecycle delete operation." + }, + { + "name":"configure", + "description":"Standard lifecycle configure operation." + } + ] + } + ], + "capabilities":[ + { + "name":"feature", + "type_name":"tosca.capabilities.Node" + } + ], + "relationships":[ + { + "name":"forwarder", + "source_requirement_index":0, + "target_node_id":"m6000_data_in_eldly5txw4frny3cc349uz3nc" + }, + { + "name":"forwarder", + "source_requirement_index":1, + "target_node_id":"m600_tunnel_cp_imwfk5l48ljz0g9knc6d68hv5" + }, + { + "name":"forwarder", + "source_requirement_index":2, + "target_node_id":"VFW_57z0ua89aiyl8ncvw7h7mjf34", + "target_capability_name":"feature" + }, + { + "name":"forwarder", + "source_requirement_index":3, + "target_node_id":"VNAT_cfdljtspvkp234irka59wgab0", + "target_capability_name":"feature" + }, + { + "name":"forwarder", + "source_requirement_index":4, + "target_node_id":"m600_tunnel_cp_imwfk5l48ljz0g9knc6d68hv5" + }, + { + "name":"forwarder", + "source_requirement_index":5, + "target_node_id":"m6000_data_out_qeukdtf6g87cnparxi51fa8s6" + } + ] + }, + { + "id":"m6000_data_out_qeukdtf6g87cnparxi51fa8s6", + "type_name":"tosca.nodes.nfv.ext.zte.CP", + "template_name":"m6000_data_out", + "properties":{ + "direction":{ + "type_name":"string", + "value":"bidirectional" + }, + "vnic_type":{ + "type_name":"string", + "value":"normal" + }, + "bandwidth":{ + "type_name":"integer", + "value":0 + }, + "mac_address":{ + "type_name":"string", + "value":"11-22-33-22-11-44" + }, + "interface_name":{ + "type_name":"string", + "value":"xgei-0/4/1/5" + }, + "ip_address":{ + "type_name":"string", + "value":"176.1.1.2" + }, + "order":{ + "type_name":"integer", + "value":0 + }, + "sfc_encapsulation":{ + "type_name":"string", + "value":"mac" + } + }, + "interfaces":[ + { + "name":"Standard", + "description":"This lifecycle interface defines the essential, normative operations that TOSCA nodes may support.", + "type_name":"tosca.interfaces.node.lifecycle.Standard", + "operations":[ + { + "name":"create", + "description":"Standard lifecycle create operation." + }, + { + "name":"stop", + "description":"Standard lifecycle stop operation." + }, + { + "name":"start", + "description":"Standard lifecycle start operation." + }, + { + "name":"delete", + "description":"Standard lifecycle delete operation." + }, + { + "name":"configure", + "description":"Standard lifecycle configure operation." + } + ] + } + ], + "capabilities":[ + { + "name":"feature", + "type_name":"tosca.capabilities.Node" + }, + { + "name":"forwarder", + "type_name":"tosca.capabilities.nfv.Forwarder" + } + ], + "relationships":[ + { + "name":"virtualbinding", + "source_requirement_index":0, + "target_node_id":"m6000_s_7qtzo5nuocyfmebc6kp9raq18", + "target_capability_name":"feature" + }, + { + "name":"virtualLink", + "source_requirement_index":1, + "target_node_id":"path2_kgmfqr5ldqs9lj3oscrgxqefc", + "target_capability_name":"feature" + }, + { + "name":"forwarder", + "source_requirement_index":2, + "target_node_id":"path2_kgmfqr5ldqs9lj3oscrgxqefc", + "target_capability_name":"feature" + } + ] + }, + { + "id":"VFW_57z0ua89aiyl8ncvw7h7mjf34", + "type_name":"tosca.nodes.nfv.ext.zte.VNF.VFW", + "template_name":"VFW", + "properties":{ + "is_shared":{ + "type_name":"boolean", + "value":False + }, + "plugin_info":{ + "type_name":"string", + "value":"vbrasplugin_1.0" + }, + "vendor":{ + "type_name":"string", + "value":"zte" + }, + "request_reclassification":{ + "type_name":"boolean", + "value":False + }, + "vnf_extend_type":{ + "type_name":"string", + "value":"driver" + }, + "name":{ + "type_name":"string", + "value":"VFW" + }, + "version":{ + "type_name":"string", + "value":"1.0" + }, + "cross_dc":{ + "type_name":"boolean", + "value":False + }, + "vnf_type":{ + "type_name":"string", + "value":"VFW" + }, + "vnfd_version":{ + "type_name":"string", + "value":"1.0.0" + }, + "id":{ + "type_name":"string", + "value":"vcpe_vfw_zte_1_0" + }, + "nsh_aware":{ + "type_name":"boolean", + "value":True + }, + "adjust_vnf_capacity":{ + "type_name":"boolean", + "value":True + }, + "vmnumber_overquota_alarm":{ + "type_name":"boolean", + "value":True + }, + "csarProvider":{ + "type_name":"string", + "value":"ZTE" + }, + "csarVersion":{ + "type_name":"string", + "value":"v1.0" + }, + "externalPluginManageNetworkName":{ + "type_name":"string", + "value":"vlan_4007_plugin_net" + }, + "csarType":{ + "type_name":"string", + "value":"NFAR" + } + }, + "interfaces":[ + { + "name":"Standard", + "description":"This lifecycle interface defines the essential, normative operations that TOSCA nodes may support.", + "type_name":"tosca.interfaces.node.lifecycle.Standard", + "operations":[ + { + "name":"create", + "description":"Standard lifecycle create operation." + }, + { + "name":"stop", + "description":"Standard lifecycle stop operation." + }, + { + "name":"start", + "description":"Standard lifecycle start operation." + }, + { + "name":"delete", + "description":"Standard lifecycle delete operation." + }, + { + "name":"configure", + "description":"Standard lifecycle configure operation." + } + ] + } + ], + "capabilities":[ + { + "name":"feature", + "type_name":"tosca.capabilities.Node" + }, + { + "name":"forwarder", + "type_name":"tosca.capabilities.nfv.Forwarder" + }, + { + "name":"vfw_fw_inout", + "type_name":"tosca.capabilities.nfv.Forwarder" + } + ], + "relationships":[ + { + "name":"vfw_ctrl_by_manager_cp", + "source_requirement_index":0, + "target_node_id":"ext_mnet_net_au2otee5mcy0dnpqykj487zr3", + "target_capability_name":"feature" + }, + { + "name":"vfw_data_cp", + "source_requirement_index":1, + "target_node_id":"sfc_data_network_vx3pc1oahn0k0pa5q722yafee", + "target_capability_name":"feature" + }, + { + "name":"virtualLink", + "source_requirement_index":2, + "target_node_id":"path2_kgmfqr5ldqs9lj3oscrgxqefc", + "target_capability_name":"feature" + }, + { + "name":"forwarder", + "source_requirement_index":3, + "target_node_id":"path2_kgmfqr5ldqs9lj3oscrgxqefc", + "target_capability_name":"feature" + } + ] + }, + { + "id":"m600_tunnel_cp_imwfk5l48ljz0g9knc6d68hv5", + "type_name":"tosca.nodes.nfv.ext.zte.CP", + "template_name":"m600_tunnel_cp", + "properties":{ + "direction":{ + "type_name":"string", + "value":"bidirectional" + }, + "vnic_type":{ + "type_name":"string", + "value":"normal" + }, + "bandwidth":{ + "type_name":"integer", + "value":0 + }, + "mac_address":{ + "type_name":"string", + "value":"00-11-00-22-33-00" + }, + "interface_name":{ + "type_name":"string", + "value":"gei-0/4/0/13" + }, + "ip_address":{ + "type_name":"string", + "value":"191.167.100.5" + }, + "order":{ + "type_name":"integer", + "value":0 + }, + "sfc_encapsulation":{ + "type_name":"string", + "value":"mac" + } + }, + "interfaces":[ + { + "name":"Standard", + "description":"This lifecycle interface defines the essential, normative operations that TOSCA nodes may support.", + "type_name":"tosca.interfaces.node.lifecycle.Standard", + "operations":[ + { + "name":"create", + "description":"Standard lifecycle create operation." + }, + { + "name":"stop", + "description":"Standard lifecycle stop operation." + }, + { + "name":"start", + "description":"Standard lifecycle start operation." + }, + { + "name":"delete", + "description":"Standard lifecycle delete operation." + }, + { + "name":"configure", + "description":"Standard lifecycle configure operation." + } + ] + } + ], + "capabilities":[ + { + "name":"feature", + "type_name":"tosca.capabilities.Node" + }, + { + "name":"forwarder", + "type_name":"tosca.capabilities.nfv.Forwarder" + } + ], + "relationships":[ + { + "name":"virtualLink", + "source_requirement_index":0, + "target_node_id":"ext_datanet_net_qtqzlx5dsthzs883hxjn6hyhd", + "target_capability_name":"feature" + }, + { + "name":"virtualbinding", + "source_requirement_index":1, + "target_node_id":"m6000_s_7qtzo5nuocyfmebc6kp9raq18", + "target_capability_name":"feature" + }, + { + "name":"forwarder", + "source_requirement_index":2, + "target_node_id":"path2_kgmfqr5ldqs9lj3oscrgxqefc", + "target_capability_name":"feature" + } + ] + }, + { + "id":"ext_mnet_net_au2otee5mcy0dnpqykj487zr3", + "type_name":"tosca.nodes.nfv.ext.VL.Vmware", + "template_name":"ext_mnet_net", + "properties":{ + "name":{ + "type_name":"string", + "value":"vlan_4008_mng_net" + }, + "dhcp_enabled":{ + "type_name":"boolean", + "value":True + }, + "location_info":{ + "type_name":"tosca.datatypes.nfv.ext.LocationInfo", + "value":{ + "tenant":"admin", + "vimid":2, + "availability_zone":"nova" + } + }, + "ip_version":{ + "type_name":"integer", + "value":4 + }, + "mtu":{ + "type_name":"integer", + "value":1500 + }, + "network_name":{ + "type_name":"string", + "value":"vlan_4008_mng_net" + }, + "network_type":{ + "type_name":"string", + "value":"vlan" + } + }, + "interfaces":[ + { + "name":"Standard", + "description":"This lifecycle interface defines the essential, normative operations that TOSCA nodes may support.", + "type_name":"tosca.interfaces.node.lifecycle.Standard", + "operations":[ + { + "name":"create", + "description":"Standard lifecycle create operation." + }, + { + "name":"stop", + "description":"Standard lifecycle stop operation." + }, + { + "name":"start", + "description":"Standard lifecycle start operation." + }, + { + "name":"delete", + "description":"Standard lifecycle delete operation." + }, + { + "name":"configure", + "description":"Standard lifecycle configure operation." + } + ] + } + ], + "capabilities":[ + { + "name":"feature", + "type_name":"tosca.capabilities.Node" + }, + { + "name":"virtual_linkable", + "type_name":"tosca.capabilities.nfv.VirtualLinkable" + } + ] + }, + { + "id":"m6000_data_in_eldly5txw4frny3cc349uz3nc", + "type_name":"tosca.nodes.nfv.ext.zte.CP", + "template_name":"m6000_data_in", + "properties":{ + "direction":{ + "type_name":"string", + "value":"bidirectional" + }, + "vnic_type":{ + "type_name":"string", + "value":"normal" + }, + "bandwidth":{ + "type_name":"integer", + "value":0 + }, + "mac_address":{ + "type_name":"string", + "value":"11-22-33-22-11-41" + }, + "interface_name":{ + "type_name":"string", + "value":"gei-0/4/0/7" + }, + "ip_address":{ + "type_name":"string", + "value":"1.1.1.1" + }, + "order":{ + "type_name":"integer", + "value":0 + }, + "sfc_encapsulation":{ + "type_name":"string", + "value":"mac" + }, + "bond":{ + "type_name":"string", + "value":"none" + } + }, + "interfaces":[ + { + "name":"Standard", + "description":"This lifecycle interface defines the essential, normative operations that TOSCA nodes may support.", + "type_name":"tosca.interfaces.node.lifecycle.Standard", + "operations":[ + { + "name":"create", + "description":"Standard lifecycle create operation." + }, + { + "name":"stop", + "description":"Standard lifecycle stop operation." + }, + { + "name":"start", + "description":"Standard lifecycle start operation." + }, + { + "name":"delete", + "description":"Standard lifecycle delete operation." + }, + { + "name":"configure", + "description":"Standard lifecycle configure operation." + } + ] + } + ], + "capabilities":[ + { + "name":"feature", + "type_name":"tosca.capabilities.Node" + }, + { + "name":"forwarder", + "type_name":"tosca.capabilities.nfv.Forwarder" + } + ], + "relationships":[ + { + "name":"virtualbinding", + "source_requirement_index":0, + "target_node_id":"m6000_s_7qtzo5nuocyfmebc6kp9raq18", + "target_capability_name":"feature" + }, + { + "name":"virtualLink", + "source_requirement_index":1, + "target_node_id":"path2_kgmfqr5ldqs9lj3oscrgxqefc", + "target_capability_name":"feature" + }, + { + "name":"forwarder", + "source_requirement_index":2, + "target_node_id":"path2_kgmfqr5ldqs9lj3oscrgxqefc", + "target_capability_name":"feature" + } + ] + }, + { + "id":"ext_datanet_net_qtqzlx5dsthzs883hxjn6hyhd", + "type_name":"tosca.nodes.nfv.ext.VL.Vmware", + "template_name":"ext_datanet_net", + "properties":{ + "name":{ + "type_name":"string", + "value":"vlan_4004_tunnel_net" + }, + "dhcp_enabled":{ + "type_name":"boolean", + "value":True + }, + "location_info":{ + "type_name":"tosca.datatypes.nfv.ext.LocationInfo", + "value":{ + "tenant":"admin", + "vimid":2, + "availability_zone":"nova" + } + }, + "ip_version":{ + "type_name":"integer", + "value":4 + }, + "mtu":{ + "type_name":"integer", + "value":1500 + }, + "network_name":{ + "type_name":"string", + "value":"vlan_4004_tunnel_net" + }, + "network_type":{ + "type_name":"string", + "value":"vlan" + } + }, + "interfaces":[ + { + "name":"Standard", + "description":"This lifecycle interface defines the essential, normative operations that TOSCA nodes may support.", + "type_name":"tosca.interfaces.node.lifecycle.Standard", + "operations":[ + { + "name":"create", + "description":"Standard lifecycle create operation." + }, + { + "name":"stop", + "description":"Standard lifecycle stop operation." + }, + { + "name":"start", + "description":"Standard lifecycle start operation." + }, + { + "name":"delete", + "description":"Standard lifecycle delete operation." + }, + { + "name":"configure", + "description":"Standard lifecycle configure operation." + } + ] + } + ], + "capabilities":[ + { + "name":"feature", + "type_name":"tosca.capabilities.Node" + }, + { + "name":"virtual_linkable", + "type_name":"tosca.capabilities.nfv.VirtualLinkable" + } + ] + }, + { + "id":"m600_mnt_cp_l3488y2a8ilyfdn0l89ni4os7", + "type_name":"tosca.nodes.nfv.ext.zte.CP", + "template_name":"m600_mnt_cp", + "properties":{ + "direction":{ + "type_name":"string", + "value":"bidirectional" + }, + "vnic_type":{ + "type_name":"string", + "value":"normal" + }, + "bandwidth":{ + "type_name":"integer", + "value":0 + }, + "mac_address":{ + "type_name":"string", + "value":"00-11-00-22-33-11" + }, + "interface_name":{ + "type_name":"string", + "value":"gei-0/4/0/1" + }, + "ip_address":{ + "type_name":"string", + "value":"10.46.244.51" + }, + "order":{ + "type_name":"integer", + "value":0 + }, + "sfc_encapsulation":{ + "type_name":"string", + "value":"mac" + }, + "bond":{ + "type_name":"string", + "value":"none" + } + }, + "interfaces":[ + { + "name":"Standard", + "description":"This lifecycle interface defines the essential, normative operations that TOSCA nodes may support.", + "type_name":"tosca.interfaces.node.lifecycle.Standard", + "operations":[ + { + "name":"create", + "description":"Standard lifecycle create operation." + }, + { + "name":"stop", + "description":"Standard lifecycle stop operation." + }, + { + "name":"start", + "description":"Standard lifecycle start operation." + }, + { + "name":"delete", + "description":"Standard lifecycle delete operation." + }, + { + "name":"configure", + "description":"Standard lifecycle configure operation." + } + ] + } + ], + "capabilities":[ + { + "name":"feature", + "type_name":"tosca.capabilities.Node" + }, + { + "name":"forwarder", + "type_name":"tosca.capabilities.nfv.Forwarder" + } + ], + "relationships":[ + { + "name":"virtualLink", + "source_requirement_index":0, + "target_node_id":"ext_mnet_net_au2otee5mcy0dnpqykj487zr3", + "target_capability_name":"feature" + }, + { + "name":"virtualbinding", + "source_requirement_index":1, + "target_node_id":"m6000_s_7qtzo5nuocyfmebc6kp9raq18", + "target_capability_name":"feature" + }, + { + "name":"forwarder", + "source_requirement_index":2, + "target_node_id":"path2_kgmfqr5ldqs9lj3oscrgxqefc", + "target_capability_name":"feature" + } + ] + }, + { + "id":"sfc_data_network_vx3pc1oahn0k0pa5q722yafee", + "type_name":"tosca.nodes.nfv.ext.zte.VL", + "template_name":"sfc_data_network", + "properties":{ + "name":{ + "type_name":"string", + "value":"sfc_data_network" + }, + "dhcp_enabled":{ + "type_name":"boolean", + "value":True + }, + "is_predefined":{ + "type_name":"boolean", + "value":False + }, + "location_info":{ + "type_name":"tosca.datatypes.nfv.ext.LocationInfo", + "value":{ + "tenant":"admin", + "vimid":2, + "availability_zone":"nova" + } + }, + "ip_version":{ + "type_name":"integer", + "value":4 + }, + "mtu":{ + "type_name":"integer", + "value":1500 + }, + "network_name":{ + "type_name":"string", + "value":"sfc_data_network" + }, + "network_type":{ + "type_name":"string", + "value":"vlan" + } + }, + "interfaces":[ + { + "name":"Standard", + "description":"This lifecycle interface defines the essential, normative operations that TOSCA nodes may support.", + "type_name":"tosca.interfaces.node.lifecycle.Standard", + "operations":[ + { + "name":"create", + "description":"Standard lifecycle create operation." + }, + { + "name":"stop", + "description":"Standard lifecycle stop operation." + }, + { + "name":"start", + "description":"Standard lifecycle start operation." + }, + { + "name":"delete", + "description":"Standard lifecycle delete operation." + }, + { + "name":"configure", + "description":"Standard lifecycle configure operation." + } + ] + } + ], + "capabilities":[ + { + "name":"feature", + "type_name":"tosca.capabilities.Node" + }, + { + "name":"virtual_linkable", + "type_name":"tosca.capabilities.nfv.VirtualLinkable" + } + ] + }, + { + "id":"m6000_s_7qtzo5nuocyfmebc6kp9raq18", + "type_name":"tosca.nodes.nfv.ext.PNF", + "template_name":"m6000_s", + "properties":{ + "vendor":{ + "type_name":"string", + "value":"zte" + }, + "request_reclassification":{ + "type_name":"boolean", + "value":False + }, + "pnf_type":{ + "type_name":"string", + "value":"m6000s" + }, + "version":{ + "type_name":"string", + "value":"1.0" + }, + "management_address":{ + "type_name":"string", + "value":"111111" + }, + "id":{ + "type_name":"string", + "value":"m6000_s" + }, + "nsh_aware":{ + "type_name":"boolean", + "value":False + } + }, + "interfaces":[ + { + "name":"Standard", + "description":"This lifecycle interface defines the essential, normative operations that TOSCA nodes may support.", + "type_name":"tosca.interfaces.node.lifecycle.Standard", + "operations":[ + { + "name":"create", + "description":"Standard lifecycle create operation." + }, + { + "name":"stop", + "description":"Standard lifecycle stop operation." + }, + { + "name":"start", + "description":"Standard lifecycle start operation." + }, + { + "name":"delete", + "description":"Standard lifecycle delete operation." + }, + { + "name":"configure", + "description":"Standard lifecycle configure operation." + } + ] + } + ], + "capabilities":[ + { + "name":"feature", + "type_name":"tosca.capabilities.Node" + }, + { + "name":"virtualBinding", + "type_name":"tosca.capabilities.nfv.VirtualBindable" + }, + { + "name":"forwarder", + "type_name":"tosca.capabilities.nfv.Forwarder" + } + ], + "relationships":[ + { + "name":"forwarder", + "source_requirement_index":0, + "target_node_id":"path2_kgmfqr5ldqs9lj3oscrgxqefc", + "target_capability_name":"feature" + } + ] + }, + { + "id":"VNAT_cfdljtspvkp234irka59wgab0", + "type_name":"tosca.nodes.nfv.ext.zte.VNF.VNAT", + "template_name":"VNAT", + "properties":{ + "is_shared":{ + "type_name":"boolean", + "value":False + }, + "plugin_info":{ + "type_name":"string", + "value":"vbrasplugin_1.0" + }, + "vendor":{ + "type_name":"string", + "value":"zte" + }, + "request_reclassification":{ + "type_name":"boolean", + "value":False + }, + "name":{ + "type_name":"string", + "value":"VNAT" + }, + "vnf_extend_type":{ + "type_name":"string", + "value":"driver" + }, + "externalPluginManageNetworkName":{ + "type_name":"string", + "value":"vlan_4007_plugin_net" + }, + "version":{ + "type_name":"string", + "value":"1.0" + }, + "cross_dc":{ + "type_name":"boolean", + "value":False + }, + "vnf_type":{ + "type_name":"string", + "value":"VNAT" + }, + "vnfd_version":{ + "type_name":"string", + "value":"1.0.0" + }, + "id":{ + "type_name":"string", + "value":"vcpe_vnat_zte_1" + }, + "nsh_aware":{ + "type_name":"boolean", + "value":True + }, + "adjust_vnf_capacity":{ + "type_name":"boolean", + "value":True + }, + "vmnumber_overquota_alarm":{ + "type_name":"boolean", + "value":True + }, + "csarProvider":{ + "type_name":"string", + "value":"ZTE" + }, + "NatIpRange":{ + "type_name":"string", + "value":"192.167.0.10-192.168.0.20" + }, + "csarVersion":{ + "type_name":"string", + "value":"v1.0" + }, + "csarType":{ + "type_name":"string", + "value":"NFAR" + } + }, + "interfaces":[ + { + "name":"Standard", + "description":"This lifecycle interface defines the essential, normative operations that TOSCA nodes may support.", + "type_name":"tosca.interfaces.node.lifecycle.Standard", + "operations":[ + { + "name":"create", + "description":"Standard lifecycle create operation." + }, + { + "name":"stop", + "description":"Standard lifecycle stop operation." + }, + { + "name":"start", + "description":"Standard lifecycle start operation." + }, + { + "name":"delete", + "description":"Standard lifecycle delete operation." + }, + { + "name":"configure", + "description":"Standard lifecycle configure operation." + } + ] + } + ], + "capabilities":[ + { + "name":"feature", + "type_name":"tosca.capabilities.Node" + }, + { + "name":"forwarder", + "type_name":"tosca.capabilities.nfv.Forwarder" + }, + { + "name":"vnat_fw_inout", + "type_name":"tosca.capabilities.nfv.Forwarder" + } + ], + "relationships":[ + { + "name":"vnat_ctrl_by_manager_cp", + "source_requirement_index":0, + "target_node_id":"ext_mnet_net_au2otee5mcy0dnpqykj487zr3", + "target_capability_name":"feature" + }, + { + "name":"vnat_data_cp", + "source_requirement_index":1, + "target_node_id":"sfc_data_network_vx3pc1oahn0k0pa5q722yafee", + "target_capability_name":"feature" + }, + { + "name":"virtualLink", + "source_requirement_index":2, + "target_node_id":"path2_kgmfqr5ldqs9lj3oscrgxqefc", + "target_capability_name":"feature" + }, + { + "name":"forwarder", + "source_requirement_index":3, + "target_node_id":"path2_kgmfqr5ldqs9lj3oscrgxqefc", + "target_capability_name":"feature" + } + ] + } + ], + "groups":[ + { + "id":"vnffg1_wk1aqhk6exoh5fmds2unu0uyc", + "type_name":"tosca.groups.nfv.VNFFG", + "template_name":"vnffg1", + "properties":{ + "vendor":{ + "type_name":"string", + "value":"zte" + }, + "connection_point":{ + "type_name":"list", + "value":[ + "m6000_data_in", + "m600_tunnel_cp", + "m6000_data_out" + ] + }, + "version":{ + "type_name":"string", + "value":"1.0" + }, + "constituent_vnfs":{ + "type_name":"list", + "value":[ + "VFW", + "VNAT" + ] + }, + "number_of_endpoints":{ + "type_name":"integer", + "value":3 + }, + "dependent_virtual_link":{ + "type_name":"list", + "value":[ + "sfc_data_network", + "ext_datanet_net", + "ext_mnet_net" + ] + } + }, + "interfaces":[ + { + "name":"standard", + "description":"This lifecycle interface defines the essential, normative operations that TOSCA nodes may support.", + "type_name":"tosca.interfaces.node.lifecycle.Standard", + "operations":[ + { + "name":"create", + "description":"Standard lifecycle create operation." + }, + { + "name":"stop", + "description":"Standard lifecycle stop operation." + }, + { + "name":"start", + "description":"Standard lifecycle start operation." + }, + { + "name":"delete", + "description":"Standard lifecycle delete operation." + }, + { + "name":"configure", + "description":"Standard lifecycle configure operation." + } + ] + } + ], + "member_node_ids":[ + "path1_bv53fblv26hawr8dj4fxe2rsd", + "path2_kgmfqr5ldqs9lj3oscrgxqefc" + ] + } + ], + "substitution":{ + "node_type_name":"tosca.nodes.nfv.NS.VCPE_NS" + }, + "inputs":{ + "externalDataNetworkName":{ + "type_name":"string", + "value":"vlan_4004_tunnel_net" + }, + "sfc_data_network":{ + "type_name":"string", + "value":"sfc_data_network" + }, + "NatIpRange":{ + "type_name":"string", + "value":"192.167.0.10-192.168.0.20" + }, + "externalManageNetworkName":{ + "type_name":"string", + "value":"vlan_4008_mng_net" + }, + "externalPluginManageNetworkName":{ + "type_name":"string", + "value":"vlan_4007_plugin_net" + } + } + }, + "model":{ + "metadata":{ + "vendor":"ZTE", + "name":"VCPE_NS", + "csarVersion":"v1.0", + "csarType":"NSAR", + "csarProvider":"ZTE", + "version":1, + "invariant_id":"vcpe_ns_sff_1", + "id":"VCPE_NS", + "description":"vcpe_ns" + }, + "node_templates":[ + { + "name":"path2", + "type_name":"tosca.nodes.nfv.ext.FP", + "default_instances":1, + "min_instances":0, + "properties":{ + "symmetric":{ + "type_name":"boolean", + "value":False + }, + "policy":{ + "type_name":"tosca.datatypes.nfv.ext.FPPolicy", + "value":{ + "type":"ACL", + "criteria":{ + "dest_port_range":"1-100", + "ip_protocol":"tcp", + "source_ip_range":[ + "119.1.1.1-119.1.1.10" + ], + "dest_ip_range":[ + {"get_input":"NatIpRange"} + ], + "dscp":0, + "source_port_range":"1-100" + } + } + } + }, + "interface_templates":[ + "<aria.modeling.model_elements.InterfaceTemplate object at 0x7f8ed0288a10>" + ], + "capability_templates":[ + { + "name":"feature", + "type_name":"tosca.capabilities.Node" + } + ], + "requirement_templates":[ + { + "name":"forwarder", + "target_node_template_name":"m6000_data_out" + }, + { + "name":"forwarder", + "target_node_template_name":"m600_tunnel_cp" + }, + { + "name":"forwarder", + "target_node_template_name":"VNAT", + "target_capability_name":"vnat_fw_inout" + } + ] + }, + { + "name":"path1", + "type_name":"tosca.nodes.nfv.ext.FP", + "default_instances":1, + "min_instances":0, + "properties":{ + "symmetric":{ + "type_name":"boolean", + "value":True + }, + "policy":{ + "type_name":"tosca.datatypes.nfv.ext.FPPolicy", + "value":{ + "type":"ACL", + "criteria":{ + "dest_port_range":"1-100", + "ip_protocol":"tcp", + "source_ip_range":[ + "1-100" + ], + "dest_ip_range":[ + "1-100" + ], + "dscp":4, + "source_port_range":"1-100" + } + } + } + }, + "interface_templates":[ + "<aria.modeling.model_elements.InterfaceTemplate object at 0x7f8ec81df090>" + ], + "capability_templates":[ + { + "name":"feature", + "type_name":"tosca.capabilities.Node" + } + ], + "requirement_templates":[ + { + "name":"forwarder", + "target_node_template_name":"m6000_data_in" + }, + { + "name":"forwarder", + "target_node_template_name":"m600_tunnel_cp" + }, + { + "name":"forwarder", + "target_node_template_name":"VFW", + "target_capability_name":"vfw_fw_inout" + }, + { + "name":"forwarder", + "target_node_template_name":"VNAT", + "target_capability_name":"vnat_fw_inout" + }, + { + "name":"forwarder", + "target_node_template_name":"m600_tunnel_cp" + }, + { + "name":"forwarder", + "target_node_template_name":"m6000_data_out" + } + ] + }, + { + "name":"m6000_data_out", + "type_name":"tosca.nodes.nfv.ext.zte.CP", + "default_instances":1, + "min_instances":0, + "properties":{ + "direction":{ + "type_name":"string", + "value":"bidirectional" + }, + "vnic_type":{ + "type_name":"string", + "value":"normal" + }, + "bandwidth":{ + "type_name":"integer", + "value":0 + }, + "mac_address":{ + "type_name":"string", + "value":"11-22-33-22-11-44" + }, + "interface_name":{ + "type_name":"string", + "value":"xgei-0/4/1/5" + }, + "ip_address":{ + "type_name":"string", + "value":"176.1.1.2" + }, + "order":{ + "type_name":"integer", + "value":0 + }, + "sfc_encapsulation":{ + "type_name":"string", + "value":"mac" + } + }, + "interface_templates":[ + "<aria.modeling.model_elements.InterfaceTemplate object at 0x7f8ec82c6610>" + ], + "capability_templates":[ + { + "name":"feature", + "type_name":"tosca.capabilities.Node" + }, + { + "name":"forwarder", + "type_name":"tosca.capabilities.nfv.Forwarder" + } + ], + "requirement_templates":[ + { + "name":"virtualbinding", + "target_node_template_name":"m6000_s", + "target_capability_name":"virtualBinding" + }, + { + "name":"virtualLink", + "target_node_type_name":"tosca.nodes.Root" + }, + { + "name":"forwarder", + "target_node_type_name":"tosca.nodes.Root" + } + ] + }, + { + "name":"VFW", + "type_name":"tosca.nodes.nfv.ext.zte.VNF.VFW", + "default_instances":1, + "min_instances":0, + "properties":{ + "is_shared":{ + "type_name":"boolean", + "value":False + }, + "plugin_info":{ + "type_name":"string", + "value":"vbrasplugin_1.0" + }, + "vendor":{ + "type_name":"string", + "value":"zte" + }, + "request_reclassification":{ + "type_name":"boolean", + "value":False + }, + "vnf_extend_type":{ + "type_name":"string", + "value":"driver" + }, + "name":{ + "type_name":"string", + "value":"VFW" + }, + "version":{ + "type_name":"string", + "value":"1.0" + }, + "cross_dc":{ + "type_name":"boolean", + "value":False + }, + "vnf_type":{ + "type_name":"string", + "value":"VFW" + }, + "vnfd_version":{ + "type_name":"string", + "value":"1.0.0" + }, + "id":{ + "type_name":"string", + "value":"vcpe_vfw_zte_1_0" + }, + "nsh_aware":{ + "type_name":"boolean", + "value":True + }, + "adjust_vnf_capacity":{ + "type_name":"boolean", + "value":True + }, + "vmnumber_overquota_alarm":{ + "type_name":"boolean", + "value":True + }, + "csarProvider":{ + "type_name":"string", + "value":"ZTE" + }, + "csarVersion":{ + "type_name":"string", + "value":"v1.0" + }, + "externalPluginManageNetworkName":{ + "type_name":"string", + "value":"vlan_4007_plugin_net" + }, + "csarType":{ + "type_name":"string", + "value":"NFAR" + } + }, + "interface_templates":[ + "<aria.modeling.model_elements.InterfaceTemplate object at 0x7f8ec8281950>" + ], + "capability_templates":[ + { + "name":"feature", + "type_name":"tosca.capabilities.Node" + }, + { + "name":"forwarder", + "type_name":"tosca.capabilities.nfv.Forwarder" + }, + { + "name":"vfw_fw_inout", + "type_name":"tosca.capabilities.nfv.Forwarder" + } + ], + "requirement_templates":[ + { + "name":"vfw_ctrl_by_manager_cp", + "target_node_template_name":"ext_mnet_net", + "target_capability_name":"virtual_linkable" + }, + { + "name":"vfw_data_cp", + "target_node_template_name":"sfc_data_network", + "target_capability_name":"virtual_linkable" + }, + { + "name":"virtualLink", + "target_node_type_name":"tosca.nodes.Root" + }, + { + "name":"forwarder", + "target_node_type_name":"tosca.nodes.Root" + } + ] + }, + { + "name":"m600_tunnel_cp", + "type_name":"tosca.nodes.nfv.ext.zte.CP", + "default_instances":1, + "min_instances":0, + "properties":{ + "direction":{ + "type_name":"string", + "value":"bidirectional" + }, + "vnic_type":{ + "type_name":"string", + "value":"normal" + }, + "bandwidth":{ + "type_name":"integer", + "value":0 + }, + "mac_address":{ + "type_name":"string", + "value":"00-11-00-22-33-00" + }, + "interface_name":{ + "type_name":"string", + "value":"gei-0/4/0/13" + }, + "ip_address":{ + "type_name":"string", + "value":"191.167.100.5" + }, + "order":{ + "type_name":"integer", + "value":0 + }, + "sfc_encapsulation":{ + "type_name":"string", + "value":"mac" + } + }, + "interface_templates":[ + "<aria.modeling.model_elements.InterfaceTemplate object at 0x1ae39d0>" + ], + "capability_templates":[ + { + "name":"feature", + "type_name":"tosca.capabilities.Node" + }, + { + "name":"forwarder", + "type_name":"tosca.capabilities.nfv.Forwarder" + } + ], + "requirement_templates":[ + { + "name":"virtualLink", + "target_node_template_name":"ext_datanet_net", + "target_capability_name":"virtual_linkable" + }, + { + "name":"virtualbinding", + "target_node_template_name":"m6000_s", + "target_capability_name":"virtualBinding" + }, + { + "name":"forwarder", + "target_node_type_name":"tosca.nodes.Root" + } + ] + }, + { + "name":"ext_mnet_net", + "type_name":"tosca.nodes.nfv.ext.VL.Vmware", + "default_instances":1, + "min_instances":0, + "properties":{ + "name":{ + "type_name":"string", + "value":"vlan_4008_mng_net" + }, + "dhcp_enabled":{ + "type_name":"boolean", + "value":True + }, + "location_info":{ + "type_name":"tosca.datatypes.nfv.ext.LocationInfo", + "value":{ + "tenant":"admin", + "vimid":2, + "availability_zone":"nova" + } + }, + "ip_version":{ + "type_name":"integer", + "value":4 + }, + "mtu":{ + "type_name":"integer", + "value":1500 + }, + "network_name":{ + "type_name":"string", + "value":"vlan_4008_mng_net" + }, + "network_type":{ + "type_name":"string", + "value":"vlan" + } + }, + "interface_templates":[ + "<aria.modeling.model_elements.InterfaceTemplate object at 0x7f8ed00f89d0>" + ], + "capability_templates":[ + { + "name":"feature", + "type_name":"tosca.capabilities.Node" + }, + { + "name":"virtual_linkable", + "type_name":"tosca.capabilities.nfv.VirtualLinkable" + } + ] + }, + { + "name":"m6000_data_in", + "type_name":"tosca.nodes.nfv.ext.zte.CP", + "default_instances":1, + "min_instances":0, + "properties":{ + "direction":{ + "type_name":"string", + "value":"bidirectional" + }, + "vnic_type":{ + "type_name":"string", + "value":"normal" + }, + "bandwidth":{ + "type_name":"integer", + "value":0 + }, + "mac_address":{ + "type_name":"string", + "value":"11-22-33-22-11-41" + }, + "interface_name":{ + "type_name":"string", + "value":"gei-0/4/0/7" + }, + "ip_address":{ + "type_name":"string", + "value":"1.1.1.1" + }, + "order":{ + "type_name":"integer", + "value":0 + }, + "sfc_encapsulation":{ + "type_name":"string", + "value":"mac" + }, + "bond":{ + "type_name":"string", + "value":"none" + } + }, + "interface_templates":[ + "<aria.modeling.model_elements.InterfaceTemplate object at 0x1745710>" + ], + "capability_templates":[ + { + "name":"feature", + "type_name":"tosca.capabilities.Node" + }, + { + "name":"forwarder", + "type_name":"tosca.capabilities.nfv.Forwarder" + } + ], + "requirement_templates":[ + { + "name":"virtualbinding", + "target_node_template_name":"m6000_s", + "target_capability_name":"virtualBinding" + }, + { + "name":"virtualLink", + "target_node_type_name":"tosca.nodes.Root" + }, + { + "name":"forwarder", + "target_node_type_name":"tosca.nodes.Root" + } + ] + }, + { + "name":"ext_datanet_net", + "type_name":"tosca.nodes.nfv.ext.VL.Vmware", + "default_instances":1, + "min_instances":0, + "properties":{ + "name":{ + "type_name":"string", + "value":"vlan_4004_tunnel_net" + }, + "dhcp_enabled":{ + "type_name":"boolean", + "value":True + }, + "location_info":{ + "type_name":"tosca.datatypes.nfv.ext.LocationInfo", + "value":{ + "tenant":"admin", + "vimid":2, + "availability_zone":"nova" + } + }, + "ip_version":{ + "type_name":"integer", + "value":4 + }, + "mtu":{ + "type_name":"integer", + "value":1500 + }, + "network_name":{ + "type_name":"string", + "value":"vlan_4004_tunnel_net" + }, + "network_type":{ + "type_name":"string", + "value":"vlan" + } + }, + "interface_templates":[ + "<aria.modeling.model_elements.InterfaceTemplate object at 0x7f8eac063990>" + ], + "capability_templates":[ + { + "name":"feature", + "type_name":"tosca.capabilities.Node" + }, + { + "name":"virtual_linkable", + "type_name":"tosca.capabilities.nfv.VirtualLinkable" + } + ] + }, + { + "name":"m600_mnt_cp", + "type_name":"tosca.nodes.nfv.ext.zte.CP", + "default_instances":1, + "min_instances":0, + "properties":{ + "direction":{ + "type_name":"string", + "value":"bidirectional" + }, + "vnic_type":{ + "type_name":"string", + "value":"normal" + }, + "bandwidth":{ + "type_name":"integer", + "value":0 + }, + "mac_address":{ + "type_name":"string", + "value":"00-11-00-22-33-11" + }, + "interface_name":{ + "type_name":"string", + "value":"gei-0/4/0/1" + }, + "ip_address":{ + "type_name":"string", + "value":"10.46.244.51" + }, + "order":{ + "type_name":"integer", + "value":0 + }, + "sfc_encapsulation":{ + "type_name":"string", + "value":"mac" + }, + "bond":{ + "type_name":"string", + "value":"none" + } + }, + "interface_templates":[ + "<aria.modeling.model_elements.InterfaceTemplate object at 0x7f8ec81264d0>" + ], + "capability_templates":[ + { + "name":"feature", + "type_name":"tosca.capabilities.Node" + }, + { + "name":"forwarder", + "type_name":"tosca.capabilities.nfv.Forwarder" + } + ], + "requirement_templates":[ + { + "name":"virtualLink", + "target_node_template_name":"ext_mnet_net", + "target_capability_name":"virtual_linkable" + }, + { + "name":"virtualbinding", + "target_node_template_name":"m6000_s", + "target_capability_name":"virtualBinding" + }, + { + "name":"forwarder", + "target_node_type_name":"tosca.nodes.Root" + } + ] + }, + { + "name":"sfc_data_network", + "type_name":"tosca.nodes.nfv.ext.zte.VL", + "default_instances":1, + "min_instances":0, + "properties":{ + "name":{ + "type_name":"string", + "value":"sfc_data_network" + }, + "dhcp_enabled":{ + "type_name":"boolean", + "value":True + }, + "is_predefined":{ + "type_name":"boolean", + "value":False + }, + "location_info":{ + "type_name":"tosca.datatypes.nfv.ext.LocationInfo", + "value":{ + "tenant":"admin", + "vimid":2, + "availability_zone":"nova" + } + }, + "ip_version":{ + "type_name":"integer", + "value":4 + }, + "mtu":{ + "type_name":"integer", + "value":1500 + }, + "network_name":{ + "type_name":"string", + "value":"sfc_data_network" + }, + "network_type":{ + "type_name":"string", + "value":"vlan" + } + }, + "interface_templates":[ + "<aria.modeling.model_elements.InterfaceTemplate object at 0x7f8ec813c6d0>" + ], + "capability_templates":[ + { + "name":"feature", + "type_name":"tosca.capabilities.Node" + }, + { + "name":"virtual_linkable", + "type_name":"tosca.capabilities.nfv.VirtualLinkable" + } + ] + }, + { + "name":"m6000_s", + "type_name":"tosca.nodes.nfv.ext.PNF", + "default_instances":1, + "min_instances":0, + "properties":{ + "vendor":{ + "type_name":"string", + "value":"zte" + }, + "request_reclassification":{ + "type_name":"boolean", + "value":False + }, + "pnf_type":{ + "type_name":"string", + "value":"m6000s" + }, + "version":{ + "type_name":"string", + "value":"1.0" + }, + "management_address":{ + "type_name":"string", + "value":"111111" + }, + "id":{ + "type_name":"string", + "value":"m6000_s" + }, + "nsh_aware":{ + "type_name":"boolean", + "value":False + } + }, + "interface_templates":[ + "<aria.modeling.model_elements.InterfaceTemplate object at 0x7f8ec8132490>" + ], + "capability_templates":[ + { + "name":"feature", + "type_name":"tosca.capabilities.Node" + }, + { + "name":"virtualBinding", + "type_name":"tosca.capabilities.nfv.VirtualBindable" + }, + { + "name":"forwarder", + "type_name":"tosca.capabilities.nfv.Forwarder" + } + ], + "requirement_templates":[ + { + "name":"forwarder", + "target_node_type_name":"tosca.nodes.Root" + } + ] + }, + { + "name":"VNAT", + "type_name":"tosca.nodes.nfv.ext.zte.VNF.VNAT", + "default_instances":1, + "min_instances":0, + "properties":{ + "is_shared":{ + "type_name":"boolean", + "value":False + }, + "plugin_info":{ + "type_name":"string", + "value":"vbrasplugin_1.0" + }, + "vendor":{ + "type_name":"string", + "value":"zte" + }, + "request_reclassification":{ + "type_name":"boolean", + "value":False + }, + "name":{ + "type_name":"string", + "value":"VNAT" + }, + "vnf_extend_type":{ + "type_name":"string", + "value":"driver" + }, + "externalPluginManageNetworkName":{ + "type_name":"string", + "value":"vlan_4007_plugin_net" + }, + "version":{ + "type_name":"string", + "value":"1.0" + }, + "cross_dc":{ + "type_name":"boolean", + "value":False + }, + "vnf_type":{ + "type_name":"string", + "value":"VNAT" + }, + "vnfd_version":{ + "type_name":"string", + "value":"1.0.0" + }, + "id":{ + "type_name":"string", + "value":"vcpe_vnat_zte_1" + }, + "nsh_aware":{ + "type_name":"boolean", + "value":True + }, + "adjust_vnf_capacity":{ + "type_name":"boolean", + "value":True + }, + "vmnumber_overquota_alarm":{ + "type_name":"boolean", + "value":True + }, + "csarProvider":{ + "type_name":"string", + "value":"ZTE" + }, + "NatIpRange":{ + "type_name":"string", + "value":"192.167.0.10-192.168.0.20" + }, + "csarVersion":{ + "type_name":"string", + "value":"v1.0" + }, + "csarType":{ + "type_name":"string", + "value":"NFAR" + } + }, + "interface_templates":[ + "<aria.modeling.model_elements.InterfaceTemplate object at 0x1bba810>" + ], + "capability_templates":[ + { + "name":"feature", + "type_name":"tosca.capabilities.Node" + }, + { + "name":"forwarder", + "type_name":"tosca.capabilities.nfv.Forwarder" + }, + { + "name":"vnat_fw_inout", + "type_name":"tosca.capabilities.nfv.Forwarder" + } + ], + "requirement_templates":[ + { + "name":"vnat_ctrl_by_manager_cp", + "target_node_template_name":"ext_mnet_net", + "target_capability_name":"virtual_linkable" + }, + { + "name":"vnat_data_cp", + "target_node_template_name":"sfc_data_network", + "target_capability_name":"virtual_linkable" + }, + { + "name":"virtualLink", + "target_node_type_name":"tosca.nodes.Root" + }, + { + "name":"forwarder", + "target_node_type_name":"tosca.nodes.Root" + } + ] + } + ], + "group_templates":[ + { + "name":"vnffg1", + "type_name":"tosca.groups.nfv.VNFFG", + "properties":{ + "vendor":{ + "type_name":"string", + "value":"zte" + }, + "connection_point":{ + "type_name":"list", + "value":[ + "m6000_data_in", + "m600_tunnel_cp", + "m6000_data_out" + ] + }, + "version":{ + "type_name":"string", + "value":"1.0" + }, + "constituent_vnfs":{ + "type_name":"list", + "value":[ + "VFW", + "VNAT" + ] + }, + "number_of_endpoints":{ + "type_name":"integer", + "value":3 + }, + "dependent_virtual_link":{ + "type_name":"list", + "value":[ + "sfc_data_network", + "ext_datanet_net", + "ext_mnet_net" + ] + } + }, + "interface_templates":[ + "<aria.modeling.model_elements.InterfaceTemplate object at 0x7f8ec811cd10>" + ], + "member_node_template_names":[ + "path1", + "path2" + ] + } + ], + "substitution_template":{ + "node_type_name":"tosca.nodes.nfv.NS.VCPE_NS" + }, + "inputs":{ + "externalDataNetworkName":{ + "type_name":"string", + "value":"vlan_4004_tunnel_net" + }, + "sfc_data_network":{ + "type_name":"string", + "value":"sfc_data_network" + }, + "NatIpRange":{ + "type_name":"string", + "value":"192.167.0.10-192.168.0.20" + }, + "externalManageNetworkName":{ + "type_name":"string", + "value":"vlan_4008_mng_net" + }, + "externalPluginManageNetworkName":{ + "type_name":"string", + "value":"vlan_4007_plugin_net" + } + } + } + } + ) + print convert_nsd_model(src_json) diff --git a/lcm/pub/utils/toscautil_new.py b/lcm/pub/utils/toscautil_new.py new file mode 100644 index 00000000..1b01989d --- /dev/null +++ b/lcm/pub/utils/toscautil_new.py @@ -0,0 +1,1458 @@ +# 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 json + +def safe_get(key_val, key): + return key_val[key] if key in key_val else "" + + +def find_node_name(node_id, node_list): + for node in node_list: + if node['id'] == node_id: + return node['template_name'] + raise Exception('can not find node(%s).' % node_id) + + +def find_node_type(node_id, node_list): + for node in node_list: + if node['id'] == node_id: + return node['type_name'] + raise Exception('can not find node(%s).' % node_id) + + +def find_related_node(node_id, src_json_model, requirement_name): + related_nodes = [] + for model_tpl in safe_get(src_json_model, "node_templates"): + for rt in safe_get(model_tpl, 'requirement_templates'): + if safe_get(rt, 'name') == requirement_name and \ + safe_get(rt, 'target_node_template_name') == node_id: + related_nodes.append(model_tpl['name']) + return related_nodes + + +def convert_props(src_node, dest_node): + if 'properties' in src_node and src_node['properties']: + for prop_name, prop_info in src_node['properties'].items(): + if 'value' in prop_info: + dest_node['properties'][prop_name] = prop_info['value'] + + +def convert_metadata(src_json): + return src_json['metadata'] if 'metadata' in src_json else {} + +def convert_factor_unit(value): + if isinstance(value, (str, unicode)): + return value + return "%s %s" % (value["factor"], value["unit"]) + +def convert_inputs(src_json): + inputs = {} + if 'inputs' in src_json: + src_inputs = src_json['inputs'] + for param_name, param_info in src_inputs.items(): + input_param = {} + if 'type_name' in param_info: + input_param['type'] = param_info['type_name'] + if 'description' in param_info: + input_param['description'] = param_info['description'] + if 'value' in param_info: + input_param['value'] = param_info['value'] + inputs[param_name] = input_param + return inputs + + +def convert_vnf_node(src_node, src_json_model): + vnf_node = {'type': src_node['type_name'], 'vnf_id': src_node['template_name'], + 'description': '', 'properties': {}, 'dependencies': [], 'networks': []} + convert_props(src_node, vnf_node) + for model_tpl in safe_get(src_json_model, "node_templates"): + if model_tpl['name'] != vnf_node['vnf_id']: + continue + vnf_node['dependencies'] = [{ + 'key_name': requirement['name'], + 'vl_id': requirement['target_node_template_name']} for \ + requirement in safe_get(model_tpl, 'requirement_templates') if \ + safe_get(requirement, 'target_capability_name') == 'virtual_linkable'] + vnf_node['networks'] = [requirement['target_node_template_name'] for \ + requirement in safe_get(model_tpl, 'requirement_templates') if \ + safe_get(requirement, 'name') == 'dependency'] + return vnf_node + + +def convert_pnf_node(src_node, src_json_model): + pnf_node = {'pnf_id': src_node['template_name'], 'description': '', 'properties': {}} + convert_props(src_node, pnf_node) + pnf_node['cps'] = find_related_node(src_node['id'], src_json_model, 'virtualbinding') + return pnf_node + + +def convert_vl_node(src_node, src_node_list): + vl_node = {'vl_id': src_node['template_name'], 'description': '', 'properties': {}} + convert_props(src_node, vl_node) + vl_node['route_id'] = '' + for relation in safe_get(src_node, 'relationships'): + if safe_get(relation, 'type_name').endswith('.VirtualLinksTo'): + vl_node['route_id'] = find_node_name(relation['target_node_id'], src_node_list) + break + vl_node['route_external'] = (src_node['type_name'].find('.RouteExternalVL') > 0) + return vl_node + + +def convert_cp_node(src_node, src_node_list, model_type='NSD'): + cp_node = {'cp_id': src_node['template_name'], 'description': '', 'properties': {}} + convert_props(src_node, cp_node) + src_relationships = src_node['relationships'] + for relation in src_relationships: + if safe_get(relation, 'name') in ('virtualLink', 'virtual_link'): + cp_node['vl_id'] = find_node_name(relation['target_node_id'], src_node_list) + elif safe_get(relation, 'name') in ('virtualbinding', 'virtual_binding'): + node_key = 'pnf_id' if model_type == 'NSD' else 'vdu_id' + cp_node[node_key] = find_node_name(relation['target_node_id'], src_node_list) + return cp_node + + +def convert_router_node(src_node, src_node_list): + router_node = {'router_id': src_node['template_name'], 'description': '', 'properties': {}} + convert_props(src_node, router_node) + for relation in src_node['relationships']: + if safe_get(relation, 'name') != 'external_virtual_link': + continue + router_node['external_vl_id'] = find_node_name(relation['target_node_id'], src_node_list) + router_node['external_ip_addresses'] = [] + if 'properties' not in relation: + continue + for prop_name, prop_info in relation['properties'].items(): + if prop_name == 'router_ip_address': + router_node['external_ip_addresses'].append(prop_info['value']) + break + return router_node + + +def convert_fp_node(src_node, src_node_list, src_json_model): + fp_node = {'fp_id': src_node['template_name'], 'description': '', + 'properties': {}, 'forwarder_list': []} + convert_props(src_node, fp_node) + for relation in safe_get(src_node, 'relationships'): + if safe_get(relation, 'name') != 'forwarder': + continue + forwarder_point = {'type': 'vnf'} + target_node_type = find_node_type(relation['target_node_id'], src_node_list).upper() + if target_node_type.find('.CP.') >= 0 or target_node_type.endswith('.CP'): + forwarder_point['type'] = 'cp' + forwarder_point['node_name'] = find_node_name(relation['target_node_id'], src_node_list) + forwarder_point['capability'] = '' + if forwarder_point['type'] == 'vnf': + for node_tpl in src_json_model["node_templates"]: + if fp_node['fp_id'] != node_tpl["name"]: + continue + for r_tpl in safe_get(node_tpl, "requirement_templates"): + if safe_get(r_tpl, "target_node_template_name") != forwarder_point['node_name']: + continue + forwarder_point['capability'] = safe_get(r_tpl, "target_capability_name") + break + break + fp_node['forwarder_list'].append(forwarder_point) + return fp_node + + +def convert_vnffg_group(src_group, src_group_list, src_node_list): + vnffg = {'vnffg_id': src_group['template_name'], 'description': '', + 'properties': {}, 'members': []} + convert_props(src_group, vnffg) + for member_node_id in src_group['member_node_ids']: + vnffg['members'].append(find_node_name(member_node_id, src_node_list)) + return vnffg + + +def convert_imagefile_node(src_node, src_node_list): + image_node = {'image_file_id': src_node['template_name'], 'description': '', + 'properties': {}} + convert_props(src_node, image_node) + return image_node + + +def convert_localstorage_node(src_node, src_node_list): + localstorage_node = {'local_storage_id': src_node['template_name'], 'description': '', + 'properties': {}} + convert_props(src_node, localstorage_node) + return localstorage_node + +def convert_volumestorage_node(src_node, src_node_list): + volumestorage_node = { + 'volume_storage_id': src_node['id'], + 'description': "", + 'properties': {}} + convert_props(src_node, volumestorage_node) + volumestorage_node["properties"]["size"] = convert_factor_unit( + volumestorage_node["properties"]["size_of_storage"]) + return volumestorage_node + +def convert_vdu_node(src_node, src_node_list, src_json_model): + vdu_node = {'vdu_id': src_node['template_name'], 'description': '', 'properties': {}, + 'image_file': '', 'local_storages': [], 'dependencies': [], 'nfv_compute': {}, + 'vls': [], 'artifacts': [], 'volume_storages': []} + convert_props(src_node, vdu_node) + + for relation in src_node.get('relationships', ''): + r_id, r_name = safe_get(relation, 'target_node_id'), safe_get(relation, 'name') + if r_name == 'guest_os': + vdu_node['image_file'] = find_node_name(r_id, src_node_list) + elif r_name == 'local_storage': + vdu_node['local_storages'].append(find_node_name(r_id, src_node_list)) + elif r_name == 'virtual_storage': + vdu_node['volume_storages'].append(r_id) + elif r_name.endswith('.AttachesTo'): + nt = find_node_type(r_id, src_node_list) + if nt.endswith('.BlockStorage.Local') or nt.endswith('.LocalStorage'): + vdu_node['local_storages'].append(find_node_name(r_id, src_node_list)) + + for capability in src_node['capabilities']: + if not capability['type_name'].endswith('.VirtualCompute'): + continue + vdu_node['nfv_compute']['flavor_extra_specs'] = {} + for prop_name, prop_info in capability['properties'].items(): + if prop_name == "virtual_cpu": + vdu_node['nfv_compute']['num_cpus'] = prop_info["value"]["num_virtual_cpu"] + if "virtual_cpu_clock" in prop_info["value"]: + vdu_node['nfv_compute']['cpu_frequency'] = convert_factor_unit( + prop_info["value"]["virtual_cpu_clock"]) + elif prop_name == "virtual_memory": + vdu_node['nfv_compute']['mem_size'] = convert_factor_unit( + prop_info["value"]["virtual_mem_size"]) + elif prop_name == "requested_additional_capabilities": + if prop_info and "value" in prop_info: + for key, val in prop_info["value"].items(): + vdu_node['nfv_compute']['flavor_extra_specs'].update( + val["target_performance_parameters"]) + + vdu_node['cps'] = find_related_node(src_node['id'], src_json_model, 'virtualbinding') + + for cp_node in vdu_node['cps']: + for src_cp_node in src_node_list: + if src_cp_node['template_name'] != cp_node: + continue + for relation in safe_get(src_cp_node, 'relationships'): + if relation['name'] != 'virtualLink': + continue + vl_node_name = find_node_name(relation['target_node_id'], src_node_list) + if vl_node_name not in vdu_node['vls']: + vdu_node['vls'].append(vl_node_name) + + for item in safe_get(src_node, 'artifacts'): + artifact = {'artifact_name': item['name'], 'type': item['type_name'], + 'file': item['source_path'], 'properties': {}} + convert_props(item, artifact) + for key in artifact['properties']: + if 'factor' in artifact['properties'][key] and 'unit' in artifact['properties'][key]: + artifact['properties'][key] = convert_factor_unit(artifact['properties'][key]) + vdu_node['artifacts'].append(artifact) + if artifact["type"].endswith(".SwImage"): + vdu_node['image_file'] = artifact["artifact_name"] + return vdu_node + + +def convert_exposed_node(src_json, src_nodes, exposed): + for item in safe_get(safe_get(src_json, 'substitution'), 'requirements'): + exposed['external_cps'].append({'key_name': item['mapped_name'], + "cp_id": find_node_name(item['node_id'], src_nodes)}) + for item in safe_get(safe_get(src_json, 'substitution'), 'capabilities'): + exposed['forward_cps'].append({'key_name': item['mapped_name'], + "cp_id": find_node_name(item['node_id'], src_nodes)}) + + +def convert_vnffgs(src_json_inst, src_nodes): + vnffgs = [] + src_groups = safe_get(src_json_inst, 'groups') + for group in src_groups: + type_name = group['type_name'].upper() + if type_name.find('.VNFFG.') >= 0 or type_name.endswith('.VNFFG'): + vnffgs.append(convert_vnffg_group(group, src_groups, src_nodes)) + return vnffgs + +def merge_imagefile_node(img_nodes, vdu_nodes): + for vdu_node in vdu_nodes: + for artifact in vdu_node.get("artifacts", []): + if not artifact["type"].endswith(".SwImage"): + continue + imgids = [img["image_file_id"] for img in img_nodes] + if artifact["artifact_name"] in imgids: + continue + img_nodes.append({ + "image_file_id": artifact["artifact_name"], + "description": "", + "properties": artifact["properties"] + }) + +def convert_common(src_json, target_json): + if isinstance(src_json, (unicode, str)): + src_json_dict = json.loads(src_json) + else: + src_json_dict = src_json + src_json_inst = src_json_dict["instance"] + src_json_model = src_json_dict["model"] if "model" in src_json_dict else {} + + target_json['metadata'] = convert_metadata(src_json_inst) + target_json['inputs'] = convert_inputs(src_json_inst) + target_json['vls'] = [] + target_json['cps'] = [] + target_json['routers'] = [] + + return src_json_inst, src_json_model + + +def convert_nsd_model(src_json): + target_json = {'vnfs': [], 'pnfs': [], 'fps': []} + src_json_inst, src_json_model = convert_common(src_json, target_json) + + src_nodes = src_json_inst['nodes'] + for node in src_nodes: + type_name = node['type_name'] + if type_name.find('.VNF.') > 0 or type_name.endswith('.VNF'): + target_json['vnfs'].append(convert_vnf_node(node, src_json_model)) + elif type_name.find('.PNF.') > 0 or type_name.endswith('.PNF'): + target_json['pnfs'].append(convert_pnf_node(node, src_json_model)) + elif type_name.find('.VL.') > 0 or type_name.endswith('.VL') \ + or node['type_name'].find('.RouteExternalVL') > 0: + target_json['vls'].append(convert_vl_node(node, src_nodes)) + elif type_name.find('.CP.') > 0 or type_name.endswith('.CP'): + target_json['cps'].append(convert_cp_node(node, src_nodes)) + elif type_name.find('.FP.') > 0 or type_name.endswith('.FP'): + target_json['fps'].append(convert_fp_node(node, src_nodes, src_json_model)) + elif type_name.endswith('.Router'): + target_json['routers'].append(convert_router_node(node, src_nodes)) + + target_json['vnffgs'] = convert_vnffgs(src_json_inst, src_nodes) + + target_json['ns_exposed'] = {'external_cps': [], 'forward_cps': []} + convert_exposed_node(src_json_inst, src_nodes, target_json['ns_exposed']) + return json.dumps(target_json) + + +def convert_vnfd_model(src_json): + target_json = {'image_files': [], 'local_storages': [], 'vdus': [], 'volume_storages': []} + src_json_inst, src_json_model = convert_common(src_json, target_json) + + src_nodes = src_json_inst['nodes'] + for node in src_nodes: + type_name = node['type_name'] + if type_name.endswith('.ImageFile'): + target_json['image_files'].append(convert_imagefile_node(node, src_nodes)) + elif type_name.endswith('.BlockStorage.Local') or type_name.endswith('.LocalStorage'): + target_json['local_storages'].append(convert_localstorage_node(node, src_nodes)) + elif type_name.endswith('VDU.VirtualStorage'): + target_json['volume_storages'].append(convert_volumestorage_node(node, src_nodes)) + elif type_name.endswith('VDU.Compute'): + target_json['vdus'].append(convert_vdu_node(node, src_nodes, src_json_model)) + elif type_name.find('.VL.') > 0 or type_name.endswith('.VL') \ + or type_name.endswith('.VnfVirtualLinkDesc') \ + or type_name.endswith('.RouteExternalVL'): + target_json['vls'].append(convert_vl_node(node, src_nodes)) + elif type_name.find('.CP.') > 0 or type_name.endswith('.CP') or type_name.endswith(".VduCpd"): + target_json['cps'].append(convert_cp_node(node, src_nodes, 'VNFD')) + elif type_name.endswith('.Router'): + target_json['routers'].append(convert_router_node(node, src_nodes)) + + target_json['vnf_exposed'] = {'external_cps': [], 'forward_cps': []} + convert_exposed_node(src_json_inst, src_nodes, target_json['vnf_exposed']) + merge_imagefile_node(target_json['image_files'], target_json['vdus']) + return json.dumps(target_json) + +if __name__ == '__main__': + src_json = json.dumps({ + "instance": { + "metadata": { + "vnfSoftwareVersion": "1.0.0", + "vnfProductName": "zte", + "localizationLanguage": [ + "english", + "chinese" + ], + "vnfProvider": "zte", + "vnfmInfo": "zte", + "defaultLocalizationLanguage": "english", + "vnfdId": "zte-hss-1.0", + "vnfProductInfoDescription": "hss", + "vnfdVersion": "1.0.0", + "vnfProductInfoName": "hss" + }, + "nodes": [ + { + "id": "vNAT_Storage_6wdgwzedlb6sq18uzrr41sof7", + "type_name": "tosca.nodes.nfv.VDU.VirtualStorage", + "template_name": "vNAT_Storage", + "properties": { + "size_of_storage": { + "type_name": "scalar-unit.size", + "value": { + "value": 10000000000, + "factor": 10, + "unit": "GB", + "unit_size": 1000000000 + } + }, + "type_of_storage": { + "type_name": "string", + "value": "volume" + }, + "rdma_enabled": { + "type_name": "boolean", + "value": False + } + }, + "interfaces": [ + { + "name": "Standard", + "description": "This lifecycle interface defines the essential, normative operations that TOSCA nodes may support.", + "type_name": "tosca.interfaces.node.lifecycle.Standard", + "operations": [ + { + "name": "create", + "description": "Standard lifecycle create operation." + }, + { + "name": "stop", + "description": "Standard lifecycle stop operation." + }, + { + "name": "start", + "description": "Standard lifecycle start operation." + }, + { + "name": "delete", + "description": "Standard lifecycle delete operation." + }, + { + "name": "configure", + "description": "Standard lifecycle configure operation." + } + ] + } + ], + "capabilities": [ + { + "name": "feature", + "type_name": "tosca.capabilities.Node" + }, + { + "name": "virtual_storage", + "type_name": "tosca.capabilities.nfv.VirtualStorage" + } + ] + }, + { + "id": "sriov_link_2610d7gund4e645wo39dvp238", + "type_name": "tosca.nodes.nfv.VnfVirtualLinkDesc", + "template_name": "sriov_link", + "properties": { + "vl_flavours": { + "type_name": "map", + "value": { + "vl_id": "aaaa" + } + }, + "connectivity_type": { + "type_name": "tosca.datatypes.nfv.ConnectivityType", + "value": { + "layer_protocol": "ipv4", + "flow_pattern": "flat" + } + }, + "description": { + "type_name": "string", + "value": "sriov_link" + }, + "test_access": { + "type_name": "list", + "value": [ + "test" + ] + } + }, + "interfaces": [ + { + "name": "Standard", + "description": "This lifecycle interface defines the essential, normative operations that TOSCA nodes may support.", + "type_name": "tosca.interfaces.node.lifecycle.Standard", + "operations": [ + { + "name": "create", + "description": "Standard lifecycle create operation." + }, + { + "name": "stop", + "description": "Standard lifecycle stop operation." + }, + { + "name": "start", + "description": "Standard lifecycle start operation." + }, + { + "name": "delete", + "description": "Standard lifecycle delete operation." + }, + { + "name": "configure", + "description": "Standard lifecycle configure operation." + } + ] + } + ], + "capabilities": [ + { + "name": "feature", + "type_name": "tosca.capabilities.Node" + }, + { + "name": "virtual_linkable", + "type_name": "tosca.capabilities.nfv.VirtualLinkable" + } + ] + }, + { + "id": "vdu_vNat_7ozwkcr86sa87fmd2nue2ww07", + "type_name": "tosca.nodes.nfv.VDU.Compute", + "template_name": "vdu_vNat", + "properties": { + "configurable_properties": { + "type_name": "map", + "value": { + "test": { + "additional_vnfc_configurable_properties": { + "aaa": "1", + "bbb": "2", + "ccc": "3" + } + } + } + }, + "boot_order": { + "type_name": "list", + "value": [ + "vNAT_Storage" + ] + }, + "name": { + "type_name": "string", + "value": "vNat" + }, + "nfvi_constraints": { + "type_name": "list", + "value": [ + "test" + ] + }, + "description": { + "type_name": "string", + "value": "the virtual machine of vNat" + } + }, + "interfaces": [ + { + "name": "Standard", + "description": "This lifecycle interface defines the essential, normative operations that TOSCA nodes may support.", + "type_name": "tosca.interfaces.node.lifecycle.Standard", + "operations": [ + { + "name": "create", + "description": "Standard lifecycle create operation." + }, + { + "name": "stop", + "description": "Standard lifecycle stop operation." + }, + { + "name": "start", + "description": "Standard lifecycle start operation." + }, + { + "name": "delete", + "description": "Standard lifecycle delete operation." + }, + { + "name": "configure", + "description": "Standard lifecycle configure operation." + } + ] + } + ], + "artifacts": [ + { + "name": "vNatVNFImage", + "type_name": "tosca.artifacts.nfv.SwImage", + "source_path": "/swimages/vRouterVNF_ControlPlane.qcow2", + "properties": { + "operating_system": { + "type_name": "string", + "value": "linux" + }, + "sw_image": { + "type_name": "string", + "value": "/swimages/vRouterVNF_ControlPlane.qcow2" + }, + "name": { + "type_name": "string", + "value": "vNatVNFImage" + }, + "checksum": { + "type_name": "string", + "value": "5000" + }, + "min_ram": { + "type_name": "scalar-unit.size", + "value": { + "value": 1000000000, + "factor": 1, + "unit": "GB", + "unit_size": 1000000000 + } + }, + "disk_format": { + "type_name": "string", + "value": "qcow2" + }, + "version": { + "type_name": "string", + "value": "1.0" + }, + "container_format": { + "type_name": "string", + "value": "bare" + }, + "min_disk": { + "type_name": "scalar-unit.size", + "value": { + "value": 10000000000, + "factor": 10, + "unit": "GB", + "unit_size": 1000000000 + } + }, + "supported_virtualisation_environments": { + "type_name": "list", + "value": [ + "test_0" + ] + }, + "size": { + "type_name": "scalar-unit.size", + "value": { + "value": 10000000000, + "factor": 10, + "unit": "GB", + "unit_size": 1000000000 + } + } + } + } + ], + "capabilities": [ + { + "name": "feature", + "type_name": "tosca.capabilities.Node" + }, + { + "name": "os", + "type_name": "tosca.capabilities.OperatingSystem", + "properties": { + "distribution": { + "type_name": "string", + "description": "The Operating System (OS) distribution. Examples of valid values for a \"type\" of \"Linux\" would include: debian, fedora, rhel and ubuntu." + }, + "version": { + "type_name": "version", + "description": "The Operating System version." + }, + "type": { + "type_name": "string", + "description": "The Operating System (OS) type. Examples of valid values include: linux, aix, mac, windows, etc." + }, + "architecture": { + "type_name": "string", + "description": "The Operating System (OS) architecture. Examples of valid values include: x86_32, x86_64, etc." + } + } + }, + { + "name": "host", + "type_name": "tosca.capabilities.Container", + "properties": { + "cpu_frequency": { + "type_name": "scalar-unit.frequency", + "description": "Specifies the operating frequency of CPU's core. This property expresses the expected frequency of one (1) CPU as provided by the property \"num_cpus\"." + }, + "mem_size": { + "type_name": "scalar-unit.size", + "description": "Size of memory available to applications running on the Compute node (default unit is MB)." + }, + "num_cpus": { + "type_name": "integer", + "description": "Number of (actual or virtual) CPUs associated with the Compute node." + }, + "disk_size": { + "type_name": "scalar-unit.size", + "description": "Size of the local disk available to applications running on the Compute node (default unit is MB)." + } + } + }, + { + "name": "binding", + "type_name": "tosca.capabilities.network.Bindable" + }, + { + "name": "scalable", + "type_name": "tosca.capabilities.Scalable", + "properties": { + "min_instances": { + "type_name": "integer", + "value": 1, + "description": "This property is used to indicate the minimum number of instances that should be created for the associated TOSCA Node Template by a TOSCA orchestrator." + }, + "default_instances": { + "type_name": "integer", + "description": "An optional property that indicates the requested default number of instances that should be the starting number of instances a TOSCA orchestrator should attempt to allocate. Note: The value for this property MUST be in the range between the values set for \"min_instances\" and \"max_instances\" properties." + }, + "max_instances": { + "type_name": "integer", + "value": 1, + "description": "This property is used to indicate the maximum number of instances that should be created for the associated TOSCA Node Template by a TOSCA orchestrator." + } + } + }, + { + "name": "virtual_compute", + "type_name": "tosca.capabilities.nfv.VirtualCompute", + "properties": { + "requested_additional_capabilities": { + "type_name": "map", + "value": { + "ovs_dpdk": { + "requested_additional_capability_name": "ovs_dpdk", + "min_requested_additional_capability_version": "1.0", + "support_mandatory": True, + "target_performance_parameters": { + "sw:ovs_dpdk": "true" + }, + "preferred_requested_additional_capability_version": "1.0" + }, + "hyper_threading": { + "requested_additional_capability_name": "hyper_threading", + "min_requested_additional_capability_version": "1.0", + "support_mandatory": True, + "target_performance_parameters": { + "hw:cpu_cores": "2", + "hw:cpu_threads": "2", + "hw:cpu_threads_policy": "isolate", + "hw:cpu_sockets": "2" + }, + "preferred_requested_additional_capability_version": "1.0" + }, + "numa": { + "requested_additional_capability_name": "numa", + "min_requested_additional_capability_version": "1.0", + "support_mandatory": True, + "target_performance_parameters": { + "hw:numa_cpus.0": "0,1", + "hw:numa_cpus.1": "2,3,4,5", + "hw:numa_nodes": "2", + "hw:numa_mem.1": "3072", + "hw:numa_mem.0": "1024" + }, + "preferred_requested_additional_capability_version": "1.0" + } + } + }, + "virtual_cpu": { + "type_name": "tosca.datatypes.nfv.VirtualCpu", + "value": { + "num_virtual_cpu": 2, + "virtual_cpu_clock": { + "value": 2400000000, + "factor": 2.4, + "unit": "GHz", + "unit_size": 1000000000 + }, + "cpu_architecture": "X86", + "virtual_cpu_oversubscription_policy": "test", + "virtual_cpu_pinning": { + "cpu_pinning_map": { + "cpu_pinning_0": "1" + }, + "cpu_pinning_policy": "static" + } + } + }, + "virtual_memory": { + "type_name": "tosca.datatypes.nfv.VirtualMemory", + "value": { + "virtual_mem_oversubscription_policy": "mem_policy_test", + "numa_enabled": True, + "virtual_mem_size": { + "value": 10000000000, + "factor": 10, + "unit": "GB", + "unit_size": 1000000000 + } + } + } + } + }, + { + "name": "virtual_binding", + "type_name": "tosca.capabilities.nfv.VirtualBindable" + } + ], + "relationships": [ + { + "name": "virtual_storage", + "source_requirement_index": 0, + "target_node_id": "vNAT_Storage_6wdgwzedlb6sq18uzrr41sof7", + "properties": { + "location": { + "type_name": "string", + "value": "/mnt/volume_0", + "description": "The relative location (e.g., path on the file system), which provides the root location to address an attached node. e.g., a mount point / path such as '/usr/data'. Note: The user must provide it and it cannot be \"root\"." + } + }, + "source_interfaces": [ + { + "name": "Configure", + "description": "The lifecycle interfaces define the essential, normative operations that each TOSCA Relationship Types may support.", + "type_name": "tosca.interfaces.relationship.Configure", + "operations": [ + { + "name": "add_source", + "description": "Operation to notify the target node of a source node which is now available via a relationship." + }, + { + "name": "pre_configure_target", + "description": "Operation to pre-configure the target endpoint." + }, + { + "name": "post_configure_source", + "description": "Operation to post-configure the source endpoint." + }, + { + "name": "target_changed", + "description": "Operation to notify source some property or attribute of the target changed" + }, + { + "name": "pre_configure_source", + "description": "Operation to pre-configure the source endpoint." + }, + { + "name": "post_configure_target", + "description": "Operation to post-configure the target endpoint." + }, + { + "name": "remove_target", + "description": "Operation to remove a target node." + }, + { + "name": "add_target", + "description": "Operation to notify the source node of a target node being added via a relationship." + } + ] + } + ] + } + ] + }, + { + "id": "SRIOV_Port_leu1j6rfdt4c8vta6aec1xe39", + "type_name": "tosca.nodes.nfv.VduCpd", + "template_name": "SRIOV_Port", + "properties": { + "address_data": { + "type_name": "list", + "value": [ + { + "address_type": "ip_address", + "l3_address_data": { + "ip_address_type": "ipv4", + "floating_ip_activated": False, + "number_of_ip_address": 1, + "ip_address_assignment": True + } + } + ] + }, + "description": { + "type_name": "string", + "value": "sriov port" + }, + "layer_protocol": { + "type_name": "string", + "value": "ipv4" + }, + "virtual_network_interface_requirements": { + "type_name": "list", + "value": [ + { + "requirement": { + "SRIOV": "true" + }, + "support_mandatory": False, + "name": "sriov", + "description": "sriov" + }, + { + "requirement": { + "SRIOV": "false" + }, + "support_mandatory": False, + "name": "normal", + "description": "normal" + } + ] + }, + "role": { + "type_name": "string", + "value": "root" + }, + "bitrate_requirement": { + "type_name": "integer", + "value": 10 + } + }, + "interfaces": [ + { + "name": "Standard", + "description": "This lifecycle interface defines the essential, normative operations that TOSCA nodes may support.", + "type_name": "tosca.interfaces.node.lifecycle.Standard", + "operations": [ + { + "name": "create", + "description": "Standard lifecycle create operation." + }, + { + "name": "stop", + "description": "Standard lifecycle stop operation." + }, + { + "name": "start", + "description": "Standard lifecycle start operation." + }, + { + "name": "delete", + "description": "Standard lifecycle delete operation." + }, + { + "name": "configure", + "description": "Standard lifecycle configure operation." + } + ] + } + ], + "capabilities": [ + { + "name": "feature", + "type_name": "tosca.capabilities.Node" + } + ], + "relationships": [ + { + "name": "virtual_binding", + "source_requirement_index": 0, + "target_node_id": "vdu_vNat_7ozwkcr86sa87fmd2nue2ww07", + "source_interfaces": [ + { + "name": "Configure", + "description": "The lifecycle interfaces define the essential, normative operations that each TOSCA Relationship Types may support.", + "type_name": "tosca.interfaces.relationship.Configure", + "operations": [ + { + "name": "add_source", + "description": "Operation to notify the target node of a source node which is now available via a relationship." + }, + { + "name": "pre_configure_target", + "description": "Operation to pre-configure the target endpoint." + }, + { + "name": "post_configure_source", + "description": "Operation to post-configure the source endpoint." + }, + { + "name": "target_changed", + "description": "Operation to notify source some property or attribute of the target changed" + }, + { + "name": "pre_configure_source", + "description": "Operation to pre-configure the source endpoint." + }, + { + "name": "post_configure_target", + "description": "Operation to post-configure the target endpoint." + }, + { + "name": "remove_target", + "description": "Operation to remove a target node." + }, + { + "name": "add_target", + "description": "Operation to notify the source node of a target node being added via a relationship." + } + ] + } + ] + }, + { + "name": "virtual_link", + "source_requirement_index": 1, + "target_node_id": "sriov_link_2610d7gund4e645wo39dvp238", + "target_capability_name": "feature", + "source_interfaces": [ + { + "name": "Configure", + "description": "The lifecycle interfaces define the essential, normative operations that each TOSCA Relationship Types may support.", + "type_name": "tosca.interfaces.relationship.Configure", + "operations": [ + { + "name": "add_source", + "description": "Operation to notify the target node of a source node which is now available via a relationship." + }, + { + "name": "pre_configure_target", + "description": "Operation to pre-configure the target endpoint." + }, + { + "name": "post_configure_source", + "description": "Operation to post-configure the source endpoint." + }, + { + "name": "target_changed", + "description": "Operation to notify source some property or attribute of the target changed" + }, + { + "name": "pre_configure_source", + "description": "Operation to pre-configure the source endpoint." + }, + { + "name": "post_configure_target", + "description": "Operation to post-configure the target endpoint." + }, + { + "name": "remove_target", + "description": "Operation to remove a target node." + }, + { + "name": "add_target", + "description": "Operation to notify the source node of a target node being added via a relationship." + } + ] + } + ] + } + ] + } + ], + "substitution": { + "node_type_name": "tosca.nodes.nfv.VNF.vOpenNAT", + "requirements": [ + { + "mapped_name": "sriov_plane", + "node_id": "SRIOV_Port_leu1j6rfdt4c8vta6aec1xe39", + "name": "virtual_link" + } + ] + } + }, + "model": { + "metadata": { + "vnfSoftwareVersion": "1.0.0", + "vnfProductName": "openNAT", + "localizationLanguage": [ + "english", + "chinese" + ], + "vnfProvider": "intel", + "vnfmInfo": "GVNFM", + "defaultLocalizationLanguage": "english", + "vnfdId": "openNAT-1.0", + "vnfProductInfoDescription": "openNAT", + "vnfdVersion": "1.0.0", + "vnfProductInfoName": "openNAT" + }, + "node_templates": [ + { + "name": "vNAT_Storage", + "type_name": "tosca.nodes.nfv.VDU.VirtualStorage", + "default_instances": 1, + "min_instances": 0, + "properties": { + "size_of_storage": { + "type_name": "scalar-unit.size", + "value": { + "value": 10000000000, + "factor": 10, + "unit": "GB", + "unit_size": 1000000000 + } + }, + "type_of_storage": { + "type_name": "string", + "value": "volume" + }, + "rdma_enabled": { + "type_name": "boolean", + "value": False + } + }, + "interface_templates": [ + "" + ], + "capability_templates": [ + { + "name": "feature", + "type_name": "tosca.capabilities.Node" + }, + { + "name": "virtual_storage", + "type_name": "tosca.capabilities.nfv.VirtualStorage" + } + ] + }, + { + "name": "vdu_vNat", + "type_name": "tosca.nodes.nfv.VDU.Compute", + "default_instances": 1, + "min_instances": 0, + "properties": { + "configurable_properties": { + "type_name": "map", + "value": { + "test": { + "additional_vnfc_configurable_properties": { + "aaa": "1", + "bbb": "2", + "ccc": "3" + } + } + } + }, + "boot_order": { + "type_name": "list", + "value": [ + "vNAT_Storage" + ] + }, + "name": { + "type_name": "string", + "value": "vNat" + }, + "nfvi_constraints": { + "type_name": "list", + "value": [ + "test" + ] + }, + "description": { + "type_name": "string", + "value": "the virtual machine of vNat" + } + }, + "interface_templates": [ + "" + ], + "artifact_templates": [ + "" + ], + "capability_templates": [ + { + "name": "feature", + "type_name": "tosca.capabilities.Node" + }, + { + "name": "os", + "type_name": "tosca.capabilities.OperatingSystem", + "properties": { + "distribution": { + "type_name": "string", + "description": "The Operating System (OS) distribution. Examples of valid values for a \"type\" of \"Linux\" would include: debian, fedora, rhel and ubuntu." + }, + "version": { + "type_name": "version", + "description": "The Operating System version." + }, + "type": { + "type_name": "string", + "description": "The Operating System (OS) type. Examples of valid values include: linux, aix, mac, windows, etc." + }, + "architecture": { + "type_name": "string", + "description": "The Operating System (OS) architecture. Examples of valid values include: x86_32, x86_64, etc." + } + } + }, + { + "name": "host", + "type_name": "tosca.capabilities.Container", + "valid_source_node_type_names": [ + "tosca.nodes.SoftwareComponent" + ], + "properties": { + "cpu_frequency": { + "type_name": "scalar-unit.frequency", + "description": "Specifies the operating frequency of CPU's core. This property expresses the expected frequency of one (1) CPU as provided by the property \"num_cpus\"." + }, + "mem_size": { + "type_name": "scalar-unit.size", + "description": "Size of memory available to applications running on the Compute node (default unit is MB)." + }, + "num_cpus": { + "type_name": "integer", + "description": "Number of (actual or virtual) CPUs associated with the Compute node." + }, + "disk_size": { + "type_name": "scalar-unit.size", + "description": "Size of the local disk available to applications running on the Compute node (default unit is MB)." + } + } + }, + { + "name": "binding", + "type_name": "tosca.capabilities.network.Bindable" + }, + { + "name": "scalable", + "type_name": "tosca.capabilities.Scalable", + "properties": { + "min_instances": { + "type_name": "integer", + "value": 1, + "description": "This property is used to indicate the minimum number of instances that should be created for the associated TOSCA Node Template by a TOSCA orchestrator." + }, + "default_instances": { + "type_name": "integer", + "description": "An optional property that indicates the requested default number of instances that should be the starting number of instances a TOSCA orchestrator should attempt to allocate. Note: The value for this property MUST be in the range between the values set for \"min_instances\" and \"max_instances\" properties." + }, + "max_instances": { + "type_name": "integer", + "value": 1, + "description": "This property is used to indicate the maximum number of instances that should be created for the associated TOSCA Node Template by a TOSCA orchestrator." + } + } + }, + { + "name": "virtual_compute", + "type_name": "tosca.capabilities.nfv.VirtualCompute", + "properties": { + "requested_additional_capabilities": { + "type_name": "map", + "value": { + "ovs_dpdk": { + "requested_additional_capability_name": "ovs_dpdk", + "min_requested_additional_capability_version": "1.0", + "support_mandatory": True, + "target_performance_parameters": { + "sw:ovs_dpdk": "true" + }, + "preferred_requested_additional_capability_version": "1.0" + }, + "hyper_threading": { + "requested_additional_capability_name": "hyper_threading", + "min_requested_additional_capability_version": "1.0", + "support_mandatory": True, + "target_performance_parameters": { + "hw:cpu_cores": "2", + "hw:cpu_threads": "2", + "hw:cpu_threads_policy": "isolate", + "hw:cpu_sockets": "2" + }, + "preferred_requested_additional_capability_version": "1.0" + }, + "numa": { + "requested_additional_capability_name": "numa", + "min_requested_additional_capability_version": "1.0", + "support_mandatory": True, + "target_performance_parameters": { + "hw:numa_cpus.0": "0,1", + "hw:numa_cpus.1": "2,3,4,5", + "hw:numa_nodes": "2", + "hw:numa_mem.1": "3072", + "hw:numa_mem.0": "1024" + }, + "preferred_requested_additional_capability_version": "1.0" + } + } + }, + "virtual_cpu": { + "type_name": "tosca.datatypes.nfv.VirtualCpu", + "value": { + "num_virtual_cpu": 2, + "virtual_cpu_clock": { + "value": 2400000000, + "factor": 2.4, + "unit": "GHz", + "unit_size": 1000000000 + }, + "cpu_architecture": "X86", + "virtual_cpu_oversubscription_policy": "test", + "virtual_cpu_pinning": { + "cpu_pinning_map": { + "cpu_pinning_0": "1" + }, + "cpu_pinning_policy": "static" + } + } + }, + "virtual_memory": { + "type_name": "tosca.datatypes.nfv.VirtualMemory", + "value": { + "virtual_mem_oversubscription_policy": "mem_policy_test", + "numa_enabled": True, + "virtual_mem_size": { + "value": 10000000000, + "factor": 10, + "unit": "GB", + "unit_size": 1000000000 + } + } + } + } + }, + { + "name": "virtual_binding", + "type_name": "tosca.capabilities.nfv.VirtualBindable" + } + ], + "requirement_templates": [ + { + "name": "virtual_storage", + "target_node_template_name": "vNAT_Storage", + "relationship_template": { + "type_name": "tosca.relationships.nfv.VDU.AttachedTo", + "properties": { + "location": { + "type_name": "string", + "value": "/mnt/volume_0", + "description": "The relative location (e.g., path on the file system), which provides the root location to address an attached node. e.g., a mount point / path such as '/usr/data'. Note: The user must provide it and it cannot be \"root\"." + } + }, + "source_interface_templates": [ + "" + ] + } + } + ] + }, + { + "name": "SRIOV_Port", + "type_name": "tosca.nodes.nfv.VduCpd", + "default_instances": 1, + "min_instances": 0, + "properties": { + "address_data": { + "type_name": "list", + "value": [ + { + "address_type": "ip_address", + "l3_address_data": { + "ip_address_type": "ipv4", + "floating_ip_activated": False, + "number_of_ip_address": 1, + "ip_address_assignment": True + } + } + ] + }, + "description": { + "type_name": "string", + "value": "sriov port" + }, + "layer_protocol": { + "type_name": "string", + "value": "ipv4" + }, + "virtual_network_interface_requirements": { + "type_name": "list", + "value": [ + { + "requirement": { + "SRIOV": "true" + }, + "support_mandatory": False, + "name": "sriov", + "description": "sriov" + }, + { + "requirement": { + "SRIOV": "false" + }, + "support_mandatory": False, + "name": "normal", + "description": "normal" + } + ] + }, + "role": { + "type_name": "string", + "value": "root" + }, + "bitrate_requirement": { + "type_name": "integer", + "value": 10 + } + }, + "interface_templates": [ + "" + ], + "capability_templates": [ + { + "name": "feature", + "type_name": "tosca.capabilities.Node" + } + ], + "requirement_templates": [ + { + "name": "virtual_binding", + "target_node_template_name": "vdu_vNat", + "relationship_template": { + "type_name": "tosca.relationships.nfv.VirtualBindsTo", + "source_interface_templates": [ + "" + ] + } + }, + { + "name": "virtual_link", + "target_node_type_name": "tosca.nodes.nfv.VnfVirtualLinkDesc", + "relationship_template": { + "type_name": "tosca.relationships.nfv.VirtualLinksTo", + "source_interface_templates": [ + "" + ] + } + } + ] + } + ], + "substitution_template": { + "node_type_name": "tosca.nodes.nfv.VNF.vOpenNAT", + "requirement_templates": [ + { + "mapped_name": "sriov_plane", + "node_template_name": "SRIOV_Port", + "name": "virtual_link" + } + ] + } + } + }) + print convert_vnfd_model(src_json) + + + + diff --git a/lcm/pub/utils/values.py b/lcm/pub/utils/values.py new file mode 100644 index 00000000..27d71a53 --- /dev/null +++ b/lcm/pub/utils/values.py @@ -0,0 +1,25 @@ +# Copyright 2016 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. + + +def ignore_case_get(args, key, def_val=""): + if not key: + return def_val + if key in args: + return args[key] + for old_key in args: + if old_key.upper() == key.upper(): + return args[old_key] + return def_val + diff --git a/lcm/samples/__init__.py b/lcm/samples/__init__.py new file mode 100644 index 00000000..5580cc3d --- /dev/null +++ b/lcm/samples/__init__.py @@ -0,0 +1,13 @@ +# Copyright 2016 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/samples/tests.py b/lcm/samples/tests.py new file mode 100644 index 00000000..79301f14 --- /dev/null +++ b/lcm/samples/tests.py @@ -0,0 +1,32 @@ +# Copyright 2016 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 SampleViewTest(unittest.TestCase): + def setUp(self): + self.client = Client() + + def tearDown(self): + pass + + def test_sample(self): + response = self.client.get("/samples/") + self.assertEqual(status.HTTP_200_OK, response.status_code, response.content) + resp_data = json.loads(response.content) + self.assertEqual({"status": "active"}, resp_data) diff --git a/lcm/samples/urls.py b/lcm/samples/urls.py new file mode 100644 index 00000000..41f7f380 --- /dev/null +++ b/lcm/samples/urls.py @@ -0,0 +1,20 @@ +# Copyright 2016 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 lcm.samples import views + +urlpatterns = [ + url(r'^openoapi/nslcm/v1/mandb/(?P<modelName>[a-zA-Z\-]+)$', views.TablesList.as_view()), + url(r'^samples/$', views.SampleList.as_view()), ] diff --git a/lcm/samples/views.py b/lcm/samples/views.py new file mode 100644 index 00000000..0e3c6acf --- /dev/null +++ b/lcm/samples/views.py @@ -0,0 +1,65 @@ +# Copyright 2016 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.views import APIView +from rest_framework.response import Response +from rest_framework import status +from lcm.pub.database import models + + +logger = logging.getLogger(__name__) + + +class SampleList(APIView): + """ + List all samples. + """ + def get(self, request, format=None): + logger.debug("get") + return Response({"status": "active"}) + +class TablesList(APIView): + def delete(self, request, modelName): + logger.debug("Start delete model %s", modelName) + try: + modelNames = modelName.split("-") + for name in modelNames: + model_obj = eval("models.%s.objects" % name) + model_obj.filter().delete() + logger.debug("End delete model %s", name) + except: + logger.error(traceback.format_exc()) + return Response(data={"error": "failed"}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR) + return Response(data={}, status=status.HTTP_204_NO_CONTENT) + + + def get(self, request, modelName): + logger.debug("Get model %s", modelName) + count = 0 + try: + model_obj = eval("models.%s.objects" % modelName) + count = len(model_obj.filter()) + except: + logger.error(traceback.format_exc()) + return Response(data={"error": "failed"}, + status=status.HTTP_500_INTERNAL_SERVER_ERROR) + return Response(data={"count": count}, status=status.HTTP_200_OK) + + + + diff --git a/lcm/settings.py b/lcm/settings.py new file mode 100644 index 00000000..45ebfee2 --- /dev/null +++ b/lcm/settings.py @@ -0,0 +1,144 @@ +# Copyright 2016 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 os +import sys + +import redisco + +from lcm.pub.config.config import REDIS_HOST, REDIS_PORT, REDIS_PASSWD +from lcm.pub.config.config import DB_NAME, DB_IP, DB_USER, DB_PASSWD, DB_PORT + +# Build paths inside the project like this: os.path.join(BASE_DIR, ...) +BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) + +# Quick-start development settings - unsuitable for production +# See https://docs.djangoproject.com/en/1.9/howto/deployment/checklist/ + +# SECURITY WARNING: keep the secret key used in production secret! +SECRET_KEY = '3o-wney!99y)^h3v)0$j16l9=fdjxcb+a8g+q3tfbahcnu2b0o' + +# SECURITY WARNING: don't run with debug turned on in production! +DEBUG = True + +ALLOWED_HOSTS = [] + +# Application definition + +INSTALLED_APPS = [ + 'django.contrib.auth', + 'django.contrib.contenttypes', + 'django.contrib.sessions', + 'django.contrib.messages', + 'django.contrib.staticfiles', + 'rest_framework', + 'lcm.pub.database', + 'lcm.samples' +] + +MIDDLEWARE_CLASSES = [ + 'django.middleware.security.SecurityMiddleware', + 'django.contrib.sessions.middleware.SessionMiddleware', + 'django.middleware.common.CommonMiddleware', + 'django.middleware.csrf.CsrfViewMiddleware', + 'django.contrib.auth.middleware.AuthenticationMiddleware', + 'django.contrib.auth.middleware.SessionAuthenticationMiddleware', + 'django.contrib.messages.middleware.MessageMiddleware', + 'django.middleware.clickjacking.XFrameOptionsMiddleware', +] + +ROOT_URLCONF = 'lcm.urls' + +WSGI_APPLICATION = 'lcm.wsgi.application' + +REST_FRAMEWORK = { + 'DEFAULT_RENDERER_CLASSES': ( + 'rest_framework.renderers.JSONRenderer', + ), + + 'DEFAULT_PARSER_CLASSES': ( + 'rest_framework.parsers.JSONParser', + 'rest_framework.parsers.MultiPartParser', + # 'rest_framework.parsers.FormParser', + # 'rest_framework.parsers.FileUploadParser', + ) +} + +DATABASES = { + 'default': { + 'ENGINE': 'django.db.backends.mysql', + 'NAME': DB_NAME, + 'HOST': DB_IP, + 'PORT': DB_PORT, + 'USER': DB_USER, + 'PASSWORD': DB_PASSWD, + }, +} + +redisco.connection_setup(host=REDIS_HOST, port=REDIS_PORT, password=REDIS_PASSWD, db=0) +# CACHE_BACKEND = 'redis_cache.cache://%s@%s:%s' % (REDIS_PASSWD, REDIS_HOST, REDIS_PORT) + +TIME_ZONE = 'UTC' + +# Static files (CSS, JavaScript, Images) +# https://docs.djangoproject.com/en/1.6/howto/static-files/ + +STATIC_URL = '/static/' + +LOGGING = { + 'version': 1, + 'disable_existing_loggers': True, + 'formatters': { + 'standard': { + 'format': '%(asctime)s:[%(name)s]:[%(filename)s]-[%(lineno)d] [%(levelname)s]:%(message)s', + }, + }, + 'filters': { + }, + 'handlers': { + 'lcm_handler': { + 'level': 'DEBUG', + 'class': 'logging.handlers.RotatingFileHandler', + 'filename': os.path.join(BASE_DIR, 'logs/runtime_lcm.log'), + 'formatter': 'standard', + 'maxBytes': 1024 * 1024 * 50, + 'backupCount': 5, + }, + }, + + 'loggers': { + 'lcm': { + 'handlers': ['lcm_handler'], + 'level': 'DEBUG', + 'propagate': False + }, + } +} + +if 'test' in sys.argv: + from lcm.pub.config import config + config.REG_TO_MSB_WHEN_START = False + DATABASES = {} + DATABASES['default'] = { + 'ENGINE': 'django.db.backends.sqlite3', + 'NAME': ':memory:', + } + REST_FRAMEWORK = {} + import platform + + if platform.system() == 'Linux': + TEST_RUNNER = 'xmlrunner.extra.djangotestrunner.XMLTestRunner' + TEST_OUTPUT_VERBOSE = True + TEST_OUTPUT_DESCRIPTIONS = True + TEST_OUTPUT_DIR = 'test-reports' diff --git a/lcm/urls.py b/lcm/urls.py new file mode 100644 index 00000000..c9a808b3 --- /dev/null +++ b/lcm/urls.py @@ -0,0 +1,32 @@ +# Copyright 2016 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 include, url +from lcm.pub.config.config import REG_TO_MSB_WHEN_START, REG_TO_MSB_REG_URL, REG_TO_MSB_REG_PARAM + +urlpatterns = [ + url(r'^', include('lcm.samples.urls')), + url(r'^', include('lcm.packages.urls')), + url(r'^', include('lcm.ns.vnfs.urls')), + url(r'^', include('lcm.ns.vls.urls')), + url(r'^', include('lcm.ns.sfcs.urls')), + url(r'^', include('lcm.ns.urls')), + url(r'^', include('lcm.jobs.urls')), +] + +# regist to MSB when startup +if REG_TO_MSB_WHEN_START: + import json + from lcm.pub.utils.restcall import req_by_msb + req_by_msb(REG_TO_MSB_REG_URL, "POST", json.JSONEncoder().encode(REG_TO_MSB_REG_PARAM)) diff --git a/lcm/wsgi.py b/lcm/wsgi.py new file mode 100644 index 00000000..f1c80c4a --- /dev/null +++ b/lcm/wsgi.py @@ -0,0 +1,22 @@ +# Copyright 2016 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 os + +from django.core.wsgi import get_wsgi_application + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "lcm.settings") + +application = get_wsgi_application() diff --git a/logs/empty.txt b/logs/empty.txt new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/logs/empty.txt diff --git a/manage.py b/manage.py new file mode 100644 index 00000000..5e924789 --- /dev/null +++ b/manage.py @@ -0,0 +1,22 @@ +# Copyright 2016 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 os +import sys + +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "lcm.settings") + +if __name__ == "__main__": + from django.core.management import execute_from_command_line + execute_from_command_line(sys.argv) diff --git a/pom.xml b/pom.xml new file mode 100644 index 00000000..1c5f788b --- /dev/null +++ b/pom.xml @@ -0,0 +1,52 @@ +<?xml version="1.0"?> +<!-- + Copyright 2016 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. +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <parent> + <groupId>org.openo.nfvo</groupId> + <artifactId>nfvo-root</artifactId> + <version>1.1.0-SNAPSHOT</version> + </parent> + <modelVersion>4.0.0</modelVersion> + <groupId>org.openo.nfvo</groupId> + <artifactId>nfvo-lcm</artifactId> + <version>1.1.0-SNAPSHOT</version> + <packaging>pom</packaging> + <name>nfvo/lcm</name> + <description>nfvo lcm</description> + <build> + <plugins> + <plugin> + <artifactId>maven-assembly-plugin</artifactId> + <configuration> + <appendAssemblyId>false</appendAssemblyId> + <descriptors> + <descriptor>assembly.xml</descriptor> + </descriptors> + </configuration> + <executions> + <execution> + <id>make-assembly</id> + <phase>package</phase> + <goals> + <goal>single</goal> + </goals> + </execution> + </executions> + </plugin> + </plugins> + </build> +</project> diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..25904796 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,26 @@ +# rest framework +Django==1.9.6 +djangorestframework==3.3.3 + +# for access MySQL +MySQL-python==1.2.5 + +# redis cache +redis==2.10.5 + +# for access redis cache +redisco==0.1.4 +django-redis-cache==0.13.1 + +# for call rest api +httplib2==0.9.2 + +# for call openstack api +python-keystoneclient==3.6.0 +python-glanceclient==2.5.0 +python-neutronclient==6.0.0 + +# for unit test +coverage==4.2 +mock==2.0.0 +unittest_xml_reporting==1.12.0 @@ -0,0 +1,16 @@ +#!/bin/bash +# Copyright 2016 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. +sip=127.0.0.1 +nohup python manage.py runserver $sip:8403 > /dev/null & diff --git a/stop.sh b/stop.sh new file mode 100644 index 00000000..5674b463 --- /dev/null +++ b/stop.sh @@ -0,0 +1,16 @@ +#!/bin/bash +# Copyright 2016 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. +sip=127.0.0.1 +ps auxww | grep "manage.py runserver $sip:8403" | awk '{print $2}' | xargs kill -9 diff --git a/tox.ini b/tox.ini new file mode 100644 index 00000000..baf2214a --- /dev/null +++ b/tox.ini @@ -0,0 +1,10 @@ +[tox] +envlist = py27 +skipsdist = true + +[tox:jenkins] +downloadcache = ~/cache/pip + +[testenv] +deps = -r{toxinidir}/requirements.txt +commands = coverage run --branch manage.py test lcm |