aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorfujinhua <fu.jinhua@zte.com.cn>2017-07-06 09:14:50 +0800
committerfujinhua <fu.jinhua@zte.com.cn>2017-07-06 09:14:50 +0800
commitbcf49a37cdf5da54fba35e65bdffb9d95babfc4c (patch)
tree6437dc7eed90bdcb8d3546900a755f3c225ebd30
parentfbbe35d470bc4aea03f3bb3e2d75eff89283bcb3 (diff)
Add seed codes of nfvo
Change-Id: Ib788826e983b75b1800ea9fc7715dc8b8044d38d Issue-Id: VFC-52 Signed-off-by: fujinhua <fu.jinhua@zte.com.cn>
-rw-r--r--.gitignore2
-rw-r--r--README.md15
-rw-r--r--assembly.xml56
-rw-r--r--initialize.sh16
-rw-r--r--lcm/__init__.py13
-rw-r--r--lcm/jobs/__init__.py13
-rw-r--r--lcm/jobs/job_get.py46
-rw-r--r--lcm/jobs/tests/__init__.py13
-rw-r--r--lcm/jobs/tests/tests.py32
-rw-r--r--lcm/jobs/urls.py23
-rw-r--r--lcm/jobs/views.py44
-rw-r--r--lcm/ns/__init__.py13
-rw-r--r--lcm/ns/const.py19
-rw-r--r--lcm/ns/data/file.json821
-rw-r--r--lcm/ns/data/init/deploy.xml39
-rw-r--r--lcm/ns/data/init/init.bpel1698
-rw-r--r--lcm/ns/data/init/init.wsdl149
-rw-r--r--lcm/ns/data/init/invoker.wsdl143
-rw-r--r--lcm/ns/data/init/invoker.xsd136
-rw-r--r--lcm/ns/data/scalemapping.json112
-rw-r--r--lcm/ns/ns_create.py59
-rw-r--r--lcm/ns/ns_get.py127
-rw-r--r--lcm/ns/ns_instant.py185
-rw-r--r--lcm/ns/ns_manual_scale.py129
-rw-r--r--lcm/ns/ns_terminate.py279
-rw-r--r--lcm/ns/sfcs/__init__.py13
-rw-r--r--lcm/ns/sfcs/create_flowcla.py92
-rw-r--r--lcm/ns/sfcs/create_port_chain.py95
-rw-r--r--lcm/ns/sfcs/create_portpairgp.py277
-rw-r--r--lcm/ns/sfcs/create_sfc_worker.py68
-rw-r--r--lcm/ns/sfcs/delete_sfcs.py91
-rw-r--r--lcm/ns/sfcs/detail_views.py41
-rw-r--r--lcm/ns/sfcs/get_sfcs.py23
-rw-r--r--lcm/ns/sfcs/sfc_instance.py94
-rw-r--r--lcm/ns/sfcs/urls.py29
-rw-r--r--lcm/ns/sfcs/utils.py54
-rw-r--r--lcm/ns/sfcs/views.py108
-rw-r--r--lcm/ns/swagger.json1046
-rw-r--r--lcm/ns/tests/__init__.py13
-rw-r--r--lcm/ns/tests/sfcs/__init__.py13
-rw-r--r--lcm/ns/tests/sfcs/test_create_flow_classifier.py56
-rw-r--r--lcm/ns/tests/sfcs/test_create_port_chain.py57
-rw-r--r--lcm/ns/tests/sfcs/test_create_port_pair_group.py81
-rw-r--r--lcm/ns/tests/sfcs/test_data.py1411
-rw-r--r--lcm/ns/tests/sfcs/test_sfc.py1652
-rw-r--r--lcm/ns/tests/sfcs/test_sfc_instance.py47
-rw-r--r--lcm/ns/tests/sfcs/test_sfcdetailview.py86
-rw-r--r--lcm/ns/tests/sfcs/tests.py77
-rw-r--r--lcm/ns/tests/test_ns_create.py39
-rw-r--r--lcm/ns/tests/test_ns_instant.py65
-rw-r--r--lcm/ns/tests/test_ns_manual_scale.py93
-rw-r--r--lcm/ns/tests/tests_ns_terminate.py98
-rw-r--r--lcm/ns/tests/vls/__init__.py13
-rw-r--r--lcm/ns/tests/vls/tests.py159
-rw-r--r--lcm/ns/tests/vnfs/__init__.py13
-rw-r--r--lcm/ns/tests/vnfs/tests.py637
-rw-r--r--lcm/ns/tests/vnfs/verify_test.py29
-rw-r--r--lcm/ns/urls.py34
-rw-r--r--lcm/ns/views.py160
-rw-r--r--lcm/ns/vls/__init__.py13
-rw-r--r--lcm/ns/vls/create_vls.py181
-rw-r--r--lcm/ns/vls/delete_vls.py85
-rw-r--r--lcm/ns/vls/get_vls.py23
-rw-r--r--lcm/ns/vls/urls.py25
-rw-r--r--lcm/ns/vls/views.py48
-rw-r--r--lcm/ns/vnfs/__init__.py13
-rw-r--r--lcm/ns/vnfs/const.py24
-rw-r--r--lcm/ns/vnfs/create_vnfs.py267
-rw-r--r--lcm/ns/vnfs/get_vnfs.py23
-rw-r--r--lcm/ns/vnfs/grant_vnfs.py125
-rw-r--r--lcm/ns/vnfs/notify_lcm.py172
-rw-r--r--lcm/ns/vnfs/scale_vnfs.py105
-rw-r--r--lcm/ns/vnfs/terminate_nfs.py125
-rw-r--r--lcm/ns/vnfs/urls.py30
-rw-r--r--lcm/ns/vnfs/verify_vnfs.py218
-rw-r--r--lcm/ns/vnfs/verify_vnfs_config.json13
-rw-r--r--lcm/ns/vnfs/views.py124
-rw-r--r--lcm/ns/vnfs/wait_job.py81
-rw-r--r--lcm/packages/__init__.py13
-rw-r--r--lcm/packages/nf_package.py439
-rw-r--r--lcm/packages/ns_package.py188
-rw-r--r--lcm/packages/tests/__init__.py13
-rw-r--r--lcm/packages/tests/test_nf.py471
-rw-r--r--lcm/packages/tests/test_ns.py418
-rw-r--r--lcm/packages/urls.py31
-rw-r--r--lcm/packages/views.py123
-rw-r--r--lcm/pub/__init__.py13
-rw-r--r--lcm/pub/config/__init__.py13
-rw-r--r--lcm/pub/config/config.py53
-rw-r--r--lcm/pub/database/__init__.py13
-rw-r--r--lcm/pub/database/models.py303
-rw-r--r--lcm/pub/exceptions.py17
-rw-r--r--lcm/pub/msapi/__init__.py13
-rw-r--r--lcm/pub/msapi/catalog.py104
-rw-r--r--lcm/pub/msapi/extsys.py67
-rw-r--r--lcm/pub/msapi/nslcm.py36
-rw-r--r--lcm/pub/msapi/resmgr.py122
-rw-r--r--lcm/pub/msapi/sdncdriver.py85
-rw-r--r--lcm/pub/msapi/tosca.py31
-rw-r--r--lcm/pub/msapi/vnfmdriver.py69
-rw-r--r--lcm/pub/msapi/wso2bpel.py25
-rw-r--r--lcm/pub/nfvi/__init__.py13
-rw-r--r--lcm/pub/nfvi/vim/__init__.py13
-rw-r--r--lcm/pub/nfvi/vim/api/__init__.py10
-rw-r--r--lcm/pub/nfvi/vim/api/multivim/__init__.py10
-rw-r--r--lcm/pub/nfvi/vim/api/multivim/api.py321
-rw-r--r--lcm/pub/nfvi/vim/api/openstack/__init__.py13
-rw-r--r--lcm/pub/nfvi/vim/api/openstack/api.py60
-rw-r--r--lcm/pub/nfvi/vim/api/openstack/auth.py46
-rw-r--r--lcm/pub/nfvi/vim/api/openstack/glancebase.py43
-rw-r--r--lcm/pub/nfvi/vim/api/openstack/image.py106
-rw-r--r--lcm/pub/nfvi/vim/api/openstack/network.py423
-rw-r--r--lcm/pub/nfvi/vim/api/openstack/neutronbase.py46
-rw-r--r--lcm/pub/nfvi/vim/api/openstack/project.py27
-rw-r--r--lcm/pub/nfvi/vim/const.py26
-rw-r--r--lcm/pub/nfvi/vim/lib/__init__.py13
-rw-r--r--lcm/pub/nfvi/vim/lib/syscomm.py20
-rw-r--r--lcm/pub/nfvi/vim/lib/vimexception.py17
-rw-r--r--lcm/pub/nfvi/vim/test/__init__.py13
-rw-r--r--lcm/pub/nfvi/vim/test/openstack/__init__.py13
-rw-r--r--lcm/pub/nfvi/vim/test/openstack/pub.py23
-rw-r--r--lcm/pub/nfvi/vim/test/openstack/test_image.py91
-rw-r--r--lcm/pub/nfvi/vim/test/openstack/test_network.py245
-rw-r--r--lcm/pub/nfvi/vim/test/openstack/testdata/cirros.qcow2bin0 -> 11075584 bytes
-rw-r--r--lcm/pub/nfvi/vim/vimadaptor.py120
-rw-r--r--lcm/pub/utils/__init__.py13
-rw-r--r--lcm/pub/utils/enumutil.py17
-rw-r--r--lcm/pub/utils/fileutil.py52
-rw-r--r--lcm/pub/utils/idutil.py20
-rw-r--r--lcm/pub/utils/jobutil.py142
-rw-r--r--lcm/pub/utils/restcall.py95
-rw-r--r--lcm/pub/utils/scaleaspect.py140
-rw-r--r--lcm/pub/utils/share_lock.py80
-rw-r--r--lcm/pub/utils/syscomm.py19
-rw-r--r--lcm/pub/utils/timeutil.py19
-rw-r--r--lcm/pub/utils/toscautil.py2632
-rw-r--r--lcm/pub/utils/toscautil_new.py1458
-rw-r--r--lcm/pub/utils/values.py25
-rw-r--r--lcm/samples/__init__.py13
-rw-r--r--lcm/samples/tests.py32
-rw-r--r--lcm/samples/urls.py20
-rw-r--r--lcm/samples/views.py65
-rw-r--r--lcm/settings.py144
-rw-r--r--lcm/urls.py32
-rw-r--r--lcm/wsgi.py22
-rw-r--r--logs/empty.txt0
-rw-r--r--manage.py22
-rw-r--r--pom.xml52
-rw-r--r--requirements.txt26
-rw-r--r--run.sh16
-rw-r--r--stop.sh16
-rw-r--r--tox.ini10
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&lt;=$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&lt;=$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&lt;=$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&lt;=$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&lt;=$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&lt;=$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
new file mode 100644
index 00000000..afe3ca37
--- /dev/null
+++ b/lcm/pub/nfvi/vim/test/openstack/testdata/cirros.qcow2
Binary files differ
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
diff --git a/run.sh b/run.sh
new file mode 100644
index 00000000..e94f1bba
--- /dev/null
+++ b/run.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
+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