summaryrefslogtreecommitdiffstats
path: root/gvnfmadapter
diff options
context:
space:
mode:
authorQuan-Zhong <quanzhong@huawei.com>2017-07-04 17:30:27 +0800
committerQuan-Zhong <quanzhong@huawei.com>2017-07-04 17:30:27 +0800
commit308af7411c9f9cdb065da07b6d77e32f5911e7a1 (patch)
tree37e42ceddc177a6dc1a69bac096c80f506ea8d22 /gvnfmadapter
parent381a045ffca7ee96cf63a919220c619b0aafe8ed (diff)
add gvnfm codes
NFVO-127 Change-Id: I447ff20ed2559d6b77041879f6abc15710acb501 Signed-off-by: Quan-Zhong <quanzhong@huawei.com>
Diffstat (limited to 'gvnfmadapter')
-rw-r--r--gvnfmadapter/assembly.xml51
-rw-r--r--gvnfmadapter/driver/__init__.py13
-rw-r--r--gvnfmadapter/driver/interfaces/__init__.py13
-rw-r--r--gvnfmadapter/driver/interfaces/tests.py300
-rw-r--r--gvnfmadapter/driver/interfaces/urls.py29
-rw-r--r--gvnfmadapter/driver/interfaces/views.py441
-rw-r--r--gvnfmadapter/driver/pub/__init__.py13
-rw-r--r--gvnfmadapter/driver/pub/config/__init__.py13
-rw-r--r--gvnfmadapter/driver/pub/config/config.py33
-rw-r--r--gvnfmadapter/driver/pub/database/__init__.py13
-rw-r--r--gvnfmadapter/driver/pub/database/models.py13
-rw-r--r--gvnfmadapter/driver/pub/utils/__init__.py13
-rw-r--r--gvnfmadapter/driver/pub/utils/restcall.py95
-rw-r--r--gvnfmadapter/driver/settings.py127
-rw-r--r--gvnfmadapter/driver/swagger/__init__.py13
-rw-r--r--gvnfmadapter/driver/swagger/swagger.json468
-rw-r--r--gvnfmadapter/driver/swagger/tests.py31
-rw-r--r--gvnfmadapter/driver/swagger/urls.py20
-rw-r--r--gvnfmadapter/driver/swagger/views.py29
-rw-r--r--gvnfmadapter/driver/urls.py26
-rw-r--r--gvnfmadapter/driver/wsgi.py22
-rw-r--r--gvnfmadapter/initialize.sh15
-rw-r--r--gvnfmadapter/logs/empty.txt0
-rw-r--r--gvnfmadapter/manage.py23
-rw-r--r--gvnfmadapter/pom.xml53
-rw-r--r--gvnfmadapter/requirements.txt11
-rw-r--r--gvnfmadapter/run.sh15
-rw-r--r--gvnfmadapter/stop.sh15
-rw-r--r--gvnfmadapter/tox.ini7
29 files changed, 1915 insertions, 0 deletions
diff --git a/gvnfmadapter/assembly.xml b/gvnfmadapter/assembly.xml
new file mode 100644
index 0000000..30a2649
--- /dev/null
+++ b/gvnfmadapter/assembly.xml
@@ -0,0 +1,51 @@
+<!--
+ 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.
+-->
+<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>drivers-vnfm-gvnfm-gvnfmadapter</id>
+ <formats>
+ <format>zip</format>
+ </formats>
+ <fileSets>
+ <fileSet>
+ <directory>driver</directory>
+ <outputDirectory>/driver</outputDirectory>
+ <includes>
+ <include>**/*.py</include>
+ <include>**/*.json</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>
+ </includes>
+ </fileSet>
+ </fileSets>
+ <baseDirectory>nfvo/drivers/vnfm/gvnfm/gvnfmadapter</baseDirectory>
+</assembly>
diff --git a/gvnfmadapter/driver/__init__.py b/gvnfmadapter/driver/__init__.py
new file mode 100644
index 0000000..c7b6818
--- /dev/null
+++ b/gvnfmadapter/driver/__init__.py
@@ -0,0 +1,13 @@
+# Copyright 2017 ZTE Corporation.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/gvnfmadapter/driver/interfaces/__init__.py b/gvnfmadapter/driver/interfaces/__init__.py
new file mode 100644
index 0000000..c7b6818
--- /dev/null
+++ b/gvnfmadapter/driver/interfaces/__init__.py
@@ -0,0 +1,13 @@
+# Copyright 2017 ZTE Corporation.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/gvnfmadapter/driver/interfaces/tests.py b/gvnfmadapter/driver/interfaces/tests.py
new file mode 100644
index 0000000..8b154af
--- /dev/null
+++ b/gvnfmadapter/driver/interfaces/tests.py
@@ -0,0 +1,300 @@
+# 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 mock
+from django.test import Client
+from django.test import TestCase
+from rest_framework import status
+from driver.pub.utils import restcall
+
+
+class InterfacesTest(TestCase):
+ def setUp(self):
+ self.client = Client()
+
+ def tearDown(self):
+ pass
+
+ @mock.patch.object(restcall, 'call_req')
+ def test_instantiate_vnf(self, mock_call_req):
+ vnfm_info = {
+ 'userName': 'admin',
+ 'vendor': 'ZTE',
+ 'name': 'ZTE_VNFM_237_62',
+ 'vimId': '516cee95-e8ca-4d26-9268-38e343c2e31e',
+ 'url': 'http://192.168.237.165:2324',
+ 'certificateUrl': '',
+ 'version': 'V1.0',
+ 'vnfmId': 'b0797c9b-3da9-459c-b25c-3813e9d8fd70',
+ 'password': 'admin',
+ 'type': 'ztevmanagerdriver',
+ 'createTime': '2016-10-31 11:08:39',
+ 'description': ''
+ }
+ job_info = {
+ "vnfInstanceId":"8",
+ "jobId":"NF-CREATE-8-b384535c-9f45-11e6-8749-fa163e91c2f9"
+ }
+ vnflcm_info = {
+ "vnfInstanceId":"8",
+ "vnfLcOpId":"NF-CREATE-8-b384535c-9f45-11e6-8749-fa163e91c2f9"
+ }
+
+ r1 = [0, json.JSONEncoder().encode(vnfm_info), "200"]
+ ret = [0, json.JSONEncoder().encode(job_info), '200']
+ ret2 = [0, json.JSONEncoder().encode(vnflcm_info), '200']
+ mock_call_req.side_effect = [r1, ret, r1, ret2]
+ req_data = {
+ 'vnfInstanceName': 'VFW_f88c0cb7-512a-44c4-bd09-891663f19367',
+ 'vnfPackageId': 'd852e1be-0aac-48f1-b1a4-cd825f6cdf9a',
+ 'vnfDescriptorId': 'vcpe_vfw_zte_1_0',
+ 'additionalParam': {
+ 'sdncontroller': 'e4d637f1-a4ec-4c59-8b20-4e8ab34daba9',
+ 'NatIpRange': '192.167.0.10-192.168.0.20',
+ 'm6000_mng_ip': '192.168.11.11',
+ 'externalPluginManageNetworkName': 'plugin_net_2014',
+ 'location': '516cee95-e8ca-4d26-9268-38e343c2e31e',
+ 'externalManageNetworkName': 'mng_net_2017',
+ 'sfc_data_network': 'sfc_data_net_2016',
+ 'externalDataNetworkName': 'Flow_out_net',
+ 'inputs':{}
+ }
+ }
+ response = self.client.post("/openoapi/ztevnfm/v1/1/vnfs",
+ data=json.dumps(req_data), content_type="application/json")
+ self.assertEqual(status.HTTP_201_CREATED, response.status_code)
+ print job_info
+ print response.data
+ self.assertEqual(job_info, response.data)
+
+
+ @mock.patch.object(restcall, 'call_req')
+ def test_terminate_vnf(self, mock_call_req):
+ vnfm_info = {
+ "vnfmId": "19ecbb3a-3242-4fa3-9926-8dfb7ddc29ee",
+ "name": "g_vnfm",
+ "type": "vnfm",
+ "vimId": "",
+ "vendor": "ZTE",
+ "version": "v1.0",
+ "description": "vnfm",
+ "certificateUrl": "",
+ "url": "http://10.74.44.11",
+ "userName": "admin",
+ "password": "admin",
+ "createTime": "2016-07-06 15:33:18"
+ }
+ job_info = {"vnfInstanceId": "1", "vnfLcOpId": "1"}
+ job_status_info = {"VnfLcOpResponseDescriptor":{"progress":"100"}}
+ r1 = [0, json.JSONEncoder().encode(vnfm_info), "200"]
+ r2 = [0, json.JSONEncoder().encode(job_info), "200"]
+ job_ret = [0, json.JSONEncoder().encode(job_status_info), "200"]
+ mock_call_req.side_effect = [r1, r2, r1, job_ret, r1, r2]
+ response = self.client.post("/openoapi/ztevnfm/v1/ztevnfmid/vnfs/2/terminate")
+ self.assertEqual(status.HTTP_204_NO_CONTENT, response.status_code)
+ self.assertEqual(job_info, response.data)
+
+
+ @mock.patch.object(restcall, 'call_req')
+ def test_query_vnf(self, mock_call_req):
+ vnfm_info = {
+ "vnfmId": "19ecbb3a-3242-4fa3-9926-8dfb7ddc29ee",
+ "name": "g_vnfm",
+ "type": "vnfm",
+ "vimId": "",
+ "vendor": "ZTE",
+ "version": "v1.0",
+ "description": "vnfm",
+ "certificateUrl": "",
+ "url": "http://10.74.44.11",
+ "userName": "admin",
+ "password": "admin",
+ "createTime": "2016-07-06 15:33:18"
+ }
+ job_info = {"ResponseInfo": {"vnfInstanceId":"88","instantiationState":"INSTANTIATED","vnfSoftwareVersion":"v1.2.3"}}
+ r1 = [0, json.JSONEncoder().encode(vnfm_info), "200"]
+ r2 = [0, json.JSONEncoder().encode(job_info), "200"]
+ mock_call_req.side_effect = [r1, r2]
+ response = self.client.get("/openoapi/ztevnfm/v1/19ecbb3a-3242-4fa3-9926-8dfb7ddc29ee/vnfs/88")
+ self.assertEqual(status.HTTP_200_OK, response.status_code)
+ expect_resp_data = {"vnfInfo": {"vnfInstanceId": "88", "vnfStatus": "ACTIVE","version":"v1.2.3"}}
+ self.assertEqual(expect_resp_data, response.data)
+
+
+ @mock.patch.object(restcall, 'call_req')
+ def test_operation_status(self, mock_call_req):
+ vnfm_info = {
+ 'userName': 'admin',
+ 'vendor': 'ZTE',
+ 'name': 'ZTE_VNFM_237_62',
+ 'vimId': '516cee95-e8ca-4d26-9268-38e343c2e31e',
+ 'url': 'http://192.168.237.165:2324',
+ 'certificateUrl': '',
+ 'version': 'V1.0',
+ 'vnfmId': 'b0797c9b-3da9-459c-b25c-3813e9d8fd70',
+ 'password': 'admin',
+ 'type': 'ztevmanagerdriver',
+ 'createTime': '2016-10-31 11:08:39',
+ 'description': ''
+ }
+ expected_body = {
+ "jobId": "NF-CREATE-11-ec6c2f2a-9f48-11e6-9405-fa163e91c2f9",
+ "responseDescriptor":{
+ "responseId": 3,
+ "progress": 40,
+ "status": "PROCESSING",
+ "statusDescription": "OMC VMs are decommissioned in VIM",
+ "errorCode": "null",
+ "responseHistoryList": [
+ {
+ "status": "error",
+ "progress": 255,
+ "errorcode": "",
+ "responseid": 20,
+ "statusdescription": "'JsonParser' object has no attribute 'parser_info'"
+ }
+ ]
+ }
+ }
+ resp_body = {
+ "ResponseInfo": {
+ "vnfLcOpId":"NF-CREATE-11-ec6c2f2a-9f48-11e6-9405-fa163e91c2f9",
+ "responseDescriptor":{
+ "responseId": 3,
+ "progress": 40,
+ "lcmOperationStatus": "PROCESSING",
+ "statusDescription": "OMC VMs are decommissioned in VIM",
+ "errorCode": "null",
+ "responseHistoryList": [
+ {"status": "error",
+ "progress": 255,
+ "errorcode": "",
+ "responseid": 20,
+ "statusdescription": "'JsonParser' object has no attribute 'parser_info'"}]
+ }
+ }
+ }
+ r1 = [0, json.JSONEncoder().encode(vnfm_info), '200']
+ r2 = [0, json.JSONEncoder().encode(resp_body), '200']
+ mock_call_req.side_effect = [r1, r2]
+ response = self.client.get("/openoapi/gvnfmadapter/v1/{vnfmid}/jobs/{jobid}?responseId={responseId}".
+ format(vnfmid=vnfm_info["vnfmId"],jobid=resp_body["ResponseInfo"]["vnfLcOpId"],
+ responseId=resp_body["ResponseInfo"]["responseDescriptor"]["responseId"]))
+ self.assertEqual(status.HTTP_200_OK, response.status_code)
+ print "========"
+ print response.data
+ self.assertDictEqual(expected_body, response.data)
+
+
+ @mock.patch.object(restcall, 'call_req')
+ def test_grantvnf(self, mock_call_req):
+ vim_info = {"vim":{"accessinfo":{"tenant":"admin"},"vimid":"516cee95-e8ca-4d26-9268-38e343c2e31e"}}
+ req_data = {
+ "vnfmid": "13232222",
+ "nfvoid": "03212234",
+ "vimid": "12345678",
+ "exvimidlist ":["exvimid"],
+ "tenant": " tenant1",
+ "vnfistanceid": "1234",
+ "operationright": "0",
+ "vmlist": [
+ {
+ "vmflavor": "SMP",
+ "vmnumber": "3"},
+ {
+ "vmflavor": "CMP",
+ "vmnumber": "3"}
+ ]
+ }
+ mock_call_req.return_value = [0, json.JSONEncoder().encode(vim_info), '201']
+ response = self.client.put("/openoapi/gvnfmadapter/v1/resource/grant",
+ data=json.dumps(req_data), content_type='application/json')
+ self.assertEqual(str(status.HTTP_201_CREATED), response.status_code)
+ expect_resp_data = {"vimid": "516cee95-e8ca-4d26-9268-38e343c2e31e", "tenant": "admin"}
+ self.assertDictEqual(expect_resp_data, response.data)
+
+ @mock.patch.object(restcall, 'call_req')
+ def test_notify(self, mock_call_req):
+ vim_info = {
+ "vim":{
+ "vimInfoId": "111111",
+ "vimId": "12345678",
+ "interfaceInfo": {
+ "vimType": "vnf",
+ "apiVersion": "v1",
+ "protocolType": "None"},
+ "accessInfo": {
+ "tenant": "tenant1",
+ "username": "admin",
+ "password": "password"},
+ "interfaceEndpoint": "http://127.0.0.1/api/v1"
+ },
+ "zone": "",
+ "addResource": {
+ "resourceDefinitionId": "xxxxx",
+ "vimId": "12345678",
+ "zoneId": "000"},
+ "removeResource": "",
+ "vimAssets": {
+ "computeResourceFlavour": {
+ "vimId": "12345678",
+ "vduId": "sdfasdf",
+ "vimFlavourId": "12"},
+ "softwareImage": {
+ "vimId": "12345678",
+ "imageName": "AAA",
+ "vimImageId": ""}},
+ "additionalParam": ""
+ }
+ r2 = [0, json.JSONEncoder().encode(vim_info), "200"]
+ mock_call_req.side_effect = [r2]
+ req_data = {
+ "nfvoid": "1",
+ "vnfmid": "876543211",
+ "vimid": "6543211",
+ "timestamp": "1234567890",
+ "vnfinstanceid": "1",
+ "eventtype": "0",
+ "vmlist":
+ [
+ {
+ "vmflavor": "SMP",
+ "vmnumber": "3",
+ "vmidlist ": ["vmuuid"]},
+ {
+ "vmflavor": "CMP",
+ "vmnumber": "3",
+ "vmidlist ": ["vmuuid"]}
+ ]
+ }
+ response = self.client.post("/openoapi/gvnfmadapter/v1/vnfs/lifecyclechangesnotification",
+ data=json.dumps(req_data), content_type='application/json')
+ self.assertEqual(str(status.HTTP_200_OK), response.status_code)
+ expect_resp_data = None
+ self.assertEqual(expect_resp_data, response.data)
+
+ @mock.patch.object(restcall, 'call_req')
+ def test_get_vnfpkgs(self, mock_call_req):
+ mock_call_req.return_value = [0, json.JSONEncoder().encode({
+ "csars": [{
+ "csarId": "1",
+ "vnfdId": "2"
+ }]
+ }), '200']
+ resp = self.client.get("/openoapi/gvnfmadapter/v1/vnfpackages")
+ self.assertEqual(status.HTTP_200_OK, resp.status_code)
+ 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/gvnfmadapter/driver/interfaces/urls.py b/gvnfmadapter/driver/interfaces/urls.py
new file mode 100644
index 0000000..8c1bfc7
--- /dev/null
+++ b/gvnfmadapter/driver/interfaces/urls.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.conf.urls import url
+from driver.interfaces import views
+
+urlpatterns = [
+ url(r'^openoapi/(?P<vnfmtype>[0-9a-zA-Z\-\_]+)/v1/(?P<vnfmid>[0-9a-zA-Z\-\_]+)/vnfs$', views.instantiate_vnf,
+ name='instantiate_vnf'),
+ url(r'^openoapi/(?P<vnfmtype>[0-9a-zA-Z\-\_]+)/v1/(?P<vnfmid>[0-9a-zA-Z\-\_]+)/vnfs/(?P<vnfInstanceId>'
+ r'[0-9a-zA-Z\-\_]+)/terminate$',views.terminate_vnf, name='terminate_vnf'),
+ url(r'^openoapi/(?P<vnfmtype>[0-9a-zA-Z\-\_]+)/v1/(?P<vnfmid>[0-9a-zA-Z\-\_]+)/vnfs/(?P<vnfInstanceId>'
+ r'[0-9a-zA-Z\-\_]+)$',views.query_vnf, name='query_vnf'),
+ url(r'^openoapi/(?P<vnfmtype>[0-9a-zA-Z\-\_]+)/v1/(?P<vnfmid>[0-9a-zA-Z\-\_]+)/jobs/(?P<jobid>[0-9a-zA-Z\-\_]+)$',
+ views.operation_status, name='operation_status'),
+ url(r'^openoapi/(?P<vnfmtype>[0-9a-zA-Z\-\_]+)/v1/vnfpackages$', views.get_vnfpkgs, name='get_vnfpkgs'),
+ url(r'^openoapi/(?P<vnfmtype>[0-9a-zA-Z\-\_]+)/v1/resource/grant$', views.grantvnf, name='grantvnf'),
+ url(r'^openoapi/(?P<vnfmtype>[0-9a-zA-Z\-\_]+)/v1/vnfs/lifecyclechangesnotification$', views.notify, name='notify'),]
diff --git a/gvnfmadapter/driver/interfaces/views.py b/gvnfmadapter/driver/interfaces/views.py
new file mode 100644
index 0000000..c903ab9
--- /dev/null
+++ b/gvnfmadapter/driver/interfaces/views.py
@@ -0,0 +1,441 @@
+# 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 inspect
+import json
+import logging
+import time
+from rest_framework.decorators import api_view
+from rest_framework.response import Response
+from driver.pub.utils import restcall
+from driver.pub.utils.restcall import req_by_msb
+from rest_framework import status
+
+# ==================================================
+vnf_create_url = "openoapi/vnflcm/v1/vnf_instances"
+vnf_inst_url = "openoapi/vnflcm/v1/vnf_instances/%s/instantiate"
+vnf_delete_url = "openoapi/vnflcm/v1/vnf_instances/%s"
+vnf_terminate_url = "openoapi/vnflcm/v1/vnf_instances/%s/terminate"
+operation_status_url = "openoapi/vnflcm/v1/vnf_lc_ops/%s?responseId=%s"
+vnf_detail_url = "openoapi/vnflcm/v1/vnf_instances/%s"
+EXTSYS_GET_VNFM = "openoapi/extsys/v1/vnfms/%s"
+vnf_query_url = "openoapi/vnflcm/v1/vnf_instances/%s"
+notify_url = 'openoapi/nslcm/v1/vnfs/{vnfInstanceId}/Notify'
+
+query_vnf_resp_mapping = {
+ "vnfInstanceId": "",
+ "vnfInstanceName": "",
+ "vnfInstanceDescription": "",
+ "vnfdId": "",
+ "vnfPackageId":"",
+ "version":"",
+ "vnfProvider":"",
+ "vnfType":"",
+ "vnfStatus":""
+}
+
+
+logger = logging.getLogger(__name__)
+
+
+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 fun_name():
+ return "=================%s==================" % inspect.stack()[1][3]
+
+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 set_createvnf_params(data):
+ input_data = {}
+ input_data["vnfdId"] = ignorcase_get(data,"vnfDescriptorId")
+ input_data["vnfInstanceName"] = ignorcase_get(data, "vnfInstanceName")
+ input_data["vnfInstanceDescription"] = ignorcase_get(data, "vnfInstanceDescription")
+
+ return input_data
+
+def set_instantvnf_params(data):
+ input_data = {}
+ input_data["flavourId"] = ignorcase_get(data, "flavourId")
+ input_data["extVirtualLinks"] = ignorcase_get(data, "extVirtualLink")
+ input_data["additionalParams"] = ignorcase_get(data,"additionalParams")
+ input_data["flavourId"] = ignorcase_get(data,"flavourId")
+
+ return input_data
+
+def set_terminatevnf_params(data):
+ input_data = {}
+ input_data["terminationType"] = ignorcase_get(data,"terminationType")
+ input_data["gracefulTerminationTimeout"] = ignorcase_get(data,"gracefulTerminationTimeout")
+
+ return input_data
+
+def set_deletevnf_params(data):
+ pass
+
+
+def get_inst_levelId(vnfdId):
+ inst_levelId = 0
+
+ return inst_levelId
+
+def get_vnfm_info(vnfm_id):
+ ret = req_by_msb((EXTSYS_GET_VNFM) % vnfm_id, "GET")
+ if ret[0] != 0:
+ return 255, Response(data={'error': ret[1]}, status=ret[2])
+ vnfm_info = json.JSONDecoder().decode(ret[1])
+ logger.debug("[%s] vnfm_info=%s", fun_name(), vnfm_info)
+ return 0, vnfm_info
+
+def call_vnfm_rest(vnfm_info, input_data, res_url, call_method = "post"):
+ ret = restcall.call_req(
+ base_url=ignorcase_get(vnfm_info, "url"),
+ user=ignorcase_get(vnfm_info, "userName"),
+ passwd=ignorcase_get(vnfm_info, "password"),
+ auth_type=restcall.rest_no_auth,
+ resource=res_url,
+ method=call_method,
+ content=json.JSONEncoder().encode(input_data))
+
+ return ret
+
+def call_vnfm_createvnf(vnfm_info, input_data):
+ return call_vnfm_rest(vnfm_info, input_data, vnf_create_url)
+
+def call_vnfm_instvnf(vnfm_info, input_data, vnfInstanceId):
+ return call_vnfm_rest(vnfm_info, input_data, vnf_inst_url % vnfInstanceId, "post")
+
+def call_vnfm_terminatevnf(vnfm_info, input_data, vnfInstanceId):
+ return call_vnfm_rest(vnfm_info, input_data, vnf_terminate_url % vnfInstanceId, "post")
+
+def call_vnfm_deletevnf(vnfm_info, vnfInstanceId):
+ return call_vnfm_rest(vnfm_info, None, vnf_delete_url % vnfInstanceId, "delete")
+
+def call_vnfm_queryvnf(vnfm_info,vnfInstanceId):
+ return call_vnfm_rest(vnfm_info, None, vnf_query_url % vnfInstanceId, "get")
+
+def call_vnfm_operation_status(vnfm_info, jobId, responseId = None):
+ return call_vnfm_rest(vnfm_info, None, operation_status_url % (jobId, responseId), "get")
+
+def wait4job(vnfm_id,jobId,gracefulTerminationTimeout):
+
+ begin_time = time.time()
+ try:
+ ret, vnfm_info = get_vnfm_info(vnfm_id)
+ if ret != 0:
+ return 255, Response(data={"error":"Fail to get VNFM!"}, status=status.HTTP_412_PRECONDITION_FAILED)
+
+ responseId = None
+ while ret == 0:
+ cur_time = time.time()
+ if gracefulTerminationTimeout and (cur_time - begin_time > gracefulTerminationTimeout):
+ return 255, Response(data={"error":"Fail to terminate VNF!"}, status=status.HTTP_408_REQUEST_TIMEOUT)
+
+ ret = call_vnfm_operation_status(vnfm_info,jobId,responseId)
+ if ret[0] != 0:
+ return 255, Response(data={"error":"Fail to get job status!"}, status=status.HTTP_412_PRECONDITION_FAILED)
+ if json.JSONDecoder().decode(ret[2]) != 200:
+ return 255, Response(data={"error":"Fail to get job status!"}, status=status.HTTP_412_PRECONDITION_FAILED)
+ job_info = json.JSONDecoder().decode(ret[1])
+ responseId = ignorcase_get(ignorcase_get(job_info, "VnfLcOpResponseDescriptor"), "responseId")
+ progress = ignorcase_get(ignorcase_get(job_info, "VnfLcOpResponseDescriptor"), "progress")
+ if progress == "100":
+ return 0, Response(data={"success":"success"}, status=status.HTTP_204_NO_CONTENT)
+ except Exception as e:
+ logger.error("Error occurred when do_createvnf")
+ return 255, Response(data={"error":"Exception caught! Fail to get job status!"}, status=status.HTTP_412_PRECONDITION_FAILED)
+
+
+def do_createvnf(request, data, vnfm_id):
+ logger.debug("[%s] request.data=%s", fun_name(), request.data)
+
+ try:
+ ret, vnfm_info = get_vnfm_info(vnfm_id)
+ if ret != 0:
+ return ret, vnfm_info
+
+ ret = call_vnfm_createvnf(vnfm_info, data)
+ logger.debug("[%s] call_req ret=%s", fun_name(), ret)
+ if ret[0] != 0:
+ return 255, Response(data={'error': ret[1]}, status=ret[2])
+ resp = json.JSONDecoder().decode(ret[1])
+ except Exception as e:
+ logger.error("Error occurred when do_createvnf")
+ raise e
+
+ return 0, resp
+
+def do_instvnf(vnfInstanceId, request, data, vnfm_id):
+ logger.debug("[%s] request.data=%s", fun_name(), request.data)
+
+ try:
+ ret, vnfm_info = get_vnfm_info(vnfm_id)
+ if ret != 0:
+ return ret, vnfm_info
+
+ ret = call_vnfm_instvnf(vnfm_info,data, vnfInstanceId)
+ logger.debug("[%s] call_req ret=%s", fun_name(), ret)
+ if ret[0] != 0:
+ return 255, Response(data={'error': ret[1]}, status=ret[2])
+ resp = json.JSONDecoder().decode(ret[1])
+ except Exception as e:
+ logger.error("Error occurred when do_instvnf")
+ raise e
+
+ return 0, resp
+
+def do_terminatevnf(request, data, vnfm_id, vnfInstanceId):
+ logger.debug("[%s] request.data=%s", fun_name(), request.data)
+ try:
+ ret, vnfm_info = get_vnfm_info(vnfm_id)
+ if ret != 0:
+ return ret,vnfm_info
+
+ ret = call_vnfm_terminatevnf(vnfm_info, data, vnfInstanceId)
+ if ret[0] != 0:
+ return 255, Response(data={'error': ret[1]}, status=ret[2])
+ resp_data = json.JSONDecoder().decode(ret[1])
+ logger.debug("[%s]resp_data=%s", fun_name(), resp_data)
+ except Exception as e:
+ logger.error("Error occurred when do_terminatevnf")
+ raise e
+
+ return 0, resp_data
+
+def do_deletevnf(request, vnfm_id, vnfInstanceId):
+ logger.debug("[%s] request.data=%s", fun_name(), request.data)
+ input_data = set_deletevnf_params(request.data)
+ try:
+ ret, vnfm_info = get_vnfm_info(vnfm_id)
+ if ret != 0:
+ return ret, vnfm_info
+
+ ret = call_vnfm_deletevnf(vnfm_info, vnfInstanceId)
+
+ if ret[0] != 0:
+ return 255, Response(data={'error': ret[1]}, status=ret[2])
+ resp_data = json.JSONDecoder().decode(ret[1])
+ logger.debug("[%s]resp_data=%s", fun_name(), resp_data)
+ except Exception as e:
+ logger.error("Error occurred when do_deletevnf")
+ raise e
+ return 0, resp_data
+
+def do_queryvnf(request, vnfm_id, vnfInstanceId):
+ logger.debug("[%s] request.data=%s", fun_name(), request.data)
+ try:
+ ret, vnfm_info = get_vnfm_info(vnfm_id)
+ if ret != 0:
+ return ret, vnfm_info
+
+ ret = call_vnfm_queryvnf(vnfm_info, vnfInstanceId)
+
+ if ret[0] != 0:
+ return 255, Response(data={'error': ret[1]}, status=ret[2])
+ resp_data = json.JSONDecoder().decode(ret[1])
+ logger.debug("[%s]resp_data=%s", fun_name(), resp_data)
+ except Exception as e:
+ logger.error("Error occurred when do_query vnf")
+ raise e
+ return 0, resp_data
+
+@api_view(http_method_names=['POST'])
+def instantiate_vnf(request, *args, **kwargs):
+ try:
+ input_data = set_createvnf_params(request.data)
+ vnfm_id = ignorcase_get(kwargs, "vnfmid")
+ ret, resp = do_createvnf(request, input_data, vnfm_id)
+ if ret != 0:
+ return resp
+
+ logger.info("[%s]resp_data=%s", fun_name(), resp)
+ vnfInstanceId = resp["vnfInstanceId"]
+ logger.info("[%s]vnfInstanceId=%s", fun_name(), vnfInstanceId)
+
+ input_data = set_instantvnf_params(request.data)
+ ret, resp = do_instvnf(vnfInstanceId, request, input_data, vnfm_id)
+ if ret != 0:
+ return resp
+
+ resp_data = {"jobId":"", "vnfInstanceId":""}
+ resp_data["vnfInstanceId"] = vnfInstanceId
+ resp_data["jobId"] = resp["vnfLcOpId"]
+ except Exception as e:
+ logger.error("Error occurred when instantiating VNF")
+ raise e
+
+ return Response(data=resp_data, status=status.HTTP_201_CREATED)
+
+
+@api_view(http_method_names=['POST'])
+def terminate_vnf(request, *args, **kwargs):
+ vnfm_id = ignorcase_get(kwargs, "vnfmid")
+ vnfInstanceId = ignorcase_get(kwargs, "vnfInstanceId")
+ try:
+ input_data = set_terminatevnf_params(request.data)
+ ret, resp = do_terminatevnf(request, input_data, vnfm_id, vnfInstanceId)
+ if ret != 0:
+ return resp
+
+ jobId = ignorcase_get(resp, "vnfLcOpId")
+ gracefulTerminationTimeout = ignorcase_get(request.data, "gracefulTerminationTimeout")
+ ret, response = wait4job(vnfm_id,jobId,gracefulTerminationTimeout)
+ if ret != 0:
+ return response
+
+ ret, resp = do_deletevnf(request, vnfm_id, vnfInstanceId)
+ if ret != 0:
+ return resp
+
+ except Exception as e:
+ logger.error("Error occurred when terminating VNF")
+ raise e
+
+ return Response(data=resp, status=status.HTTP_204_NO_CONTENT)
+
+@api_view(http_method_names=['GET'])
+def query_vnf(request, *args, **kwargs):
+ vnfm_id = ignorcase_get(kwargs, "vnfmid")
+ vnfInstanceId = ignorcase_get(kwargs, "vnfInstanceId")
+ try:
+ logger.debug("[%s] request.data=%s", fun_name(), request.data)
+ ret, resp = do_queryvnf(request, vnfm_id, vnfInstanceId)
+ if ret != 0:
+ return resp
+
+ resp_response_data = mapping_conv(query_vnf_resp_mapping, ignorcase_get(resp, "ResponseInfo"))
+ resp_data = {
+ "vnfInfo":resp_response_data
+ }
+ #Handle vnfSoftwareVersion and vnfStatus specially
+ resp_data["vnfInfo"]["version"] = ignorcase_get(ignorcase_get(resp, "ResponseInfo"), "vnfSoftwareVersion")
+ if ignorcase_get(ignorcase_get(resp, "ResponseInfo"), "instantiationState"):
+ if ignorcase_get(ignorcase_get(resp, "ResponseInfo"), "instantiationState") == "INSTANTIATED":
+ resp_data["vnfInfo"]["vnfStatus"] = "ACTIVE"
+ if ignorcase_get(ignorcase_get(resp, "ResponseInfo"), "vnfInstanceId"):
+ resp_data["vnfInfo"]["vnfInstanceId"] = ignorcase_get(ignorcase_get(resp, "ResponseInfo"), "vnfInstanceId")
+ logger.debug("[%s]resp_data=%s", fun_name(), resp_data)
+ except Exception as e:
+ logger.error("Error occurred when querying VNF information.")
+ raise e
+ return Response(data=resp_data, status=status.HTTP_200_OK)
+
+# ==================================================
+
+
+@api_view(http_method_names=['GET'])
+def operation_status(request, *args, **kwargs):
+ data = {}
+ try:
+ logger.debug("[%s] request.data=%s", fun_name(), request.data)
+ vnfm_id = ignorcase_get(kwargs, "vnfmid")
+ jobId = ignorcase_get(kwargs, "jobId")
+ responseId = ignorcase_get(kwargs, "responseId")
+
+ ret, vnfm_info = get_vnfm_info(vnfm_id)
+ if ret != 0:
+ return Response(data={'error': ret[1]}, status=ret[2])
+ logger.debug("[%s] vnfm_info=%s", fun_name(), vnfm_info)
+
+ ret = call_vnfm_operation_status(vnfm_info, jobId, responseId)
+
+ if ret[0] != 0:
+ return Response(data={'error': ret[1]}, status=ret[2])
+ resp_data = json.JSONDecoder().decode(ret[1])
+ logger.info("[%s]resp_data=%s", fun_name(), resp_data)
+ ResponseInfo = ignorcase_get(resp_data, "ResponseInfo")
+ operation_data = {}
+ operation_data["jobId"] = ignorcase_get(ResponseInfo, "vnfLcOpId")
+ operation_data["responseDescriptor"] = {}
+ operation_data["responseDescriptor"]["status"] = ignorcase_get(ignorcase_get(ResponseInfo, "responseDescriptor"),"lcmOperationStatus")
+ operation_data["responseDescriptor"]["progress"] = ignorcase_get(ignorcase_get(ResponseInfo, "responseDescriptor"),"progress")
+ operation_data["responseDescriptor"]["statusDescription"] = ignorcase_get(ignorcase_get(ResponseInfo, "responseDescriptor"),"statusDescription")
+ operation_data["responseDescriptor"]["errorCode"] = ignorcase_get(ignorcase_get(ResponseInfo, "responseDescriptor"),"errorCode")
+ operation_data["responseDescriptor"]["responseId"] = ignorcase_get(ignorcase_get(ResponseInfo, "responseDescriptor"),"responseId")
+ operation_data["responseDescriptor"]["responseHistoryList"] = ignorcase_get(ignorcase_get(ResponseInfo, "responseDescriptor"),"responseHistoryList")
+
+ except Exception as e:
+ logger.error("Error occurred when getting operation status information.")
+ raise e
+ return Response(data=operation_data, status=status.HTTP_200_OK)
+
+
+# ==================================================
+grant_vnf_url = 'openoapi/nslcm/v1/grantvnf'
+
+@api_view(http_method_names=['PUT'])
+def grantvnf(request, *args, **kwargs):
+ logger.info("=====grantvnf=====")
+ try:
+ resp_data = {}
+ logger.info("req_data = %s", request.data)
+ ret = req_by_msb(grant_vnf_url, "POST", content=json.JSONEncoder().encode(request.data))
+ logger.info("ret = %s", ret)
+ if ret[0] != 0:
+ return Response(data={'error': ret[1]}, status=ret[2])
+ resp = json.JSONDecoder().decode(ret[1])
+ resp_data['vimid'] = ignorcase_get(resp['vim'], 'vimid')
+ resp_data['tenant'] = ignorcase_get(ignorcase_get(resp['vim'], 'accessinfo'), 'tenant')
+ logger.info("[%s]resp_data=%s", fun_name(), resp_data)
+ except Exception as e:
+ logger.error("Error occurred in Grant VNF.")
+ raise e
+ return Response(data=resp_data, status=ret[2])
+
+
+# ==================================================
+
+
+@api_view(http_method_names=['POST'])
+def notify(request, *args, **kwargs):
+ try:
+ logger.info("[%s]req_data = %s", fun_name(), request.data)
+ ret = req_by_msb(notify_url.format(vnfmid=ignorcase_get(request.data, 'VNFMID'),
+ vnfInstanceId=ignorcase_get(request.data, 'vnfinstanceid')),
+ "POST", content=json.JSONEncoder().encode(request.data))
+ logger.info("[%s]data = %s", fun_name(), ret)
+ if ret[0] != 0:
+ return Response(data={'error': ret[1]}, status=ret[2])
+ except Exception as e:
+ logger.error("Error occurred in LCM notification.")
+ raise e
+ return Response(data=None, status=ret[2])
+
+@api_view(http_method_names=['GET'])
+def get_vnfpkgs(request, *args, **kwargs):
+ logger.info("Enter %s", fun_name())
+ ret = req_by_msb("openoapi/nslcm/v1/vnfpackage", "GET")
+ if ret[0] != 0:
+ return Response(data={'error': ret[1]}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
+ resp = json.JSONDecoder().decode(ret[1])
+ return Response(data=resp, status=status.HTTP_200_OK)
diff --git a/gvnfmadapter/driver/pub/__init__.py b/gvnfmadapter/driver/pub/__init__.py
new file mode 100644
index 0000000..c7b6818
--- /dev/null
+++ b/gvnfmadapter/driver/pub/__init__.py
@@ -0,0 +1,13 @@
+# Copyright 2017 ZTE Corporation.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/gvnfmadapter/driver/pub/config/__init__.py b/gvnfmadapter/driver/pub/config/__init__.py
new file mode 100644
index 0000000..c7b6818
--- /dev/null
+++ b/gvnfmadapter/driver/pub/config/__init__.py
@@ -0,0 +1,13 @@
+# Copyright 2017 ZTE Corporation.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/gvnfmadapter/driver/pub/config/config.py b/gvnfmadapter/driver/pub/config/config.py
new file mode 100644
index 0000000..d760979
--- /dev/null
+++ b/gvnfmadapter/driver/pub/config/config.py
@@ -0,0 +1,33 @@
+# 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.
+
+# [MSB]
+MSB_SERVICE_IP = '127.0.0.1'
+MSB_SERVICE_PORT = '80'
+
+# [register]
+REG_TO_MSB_WHEN_START = True
+REG_TO_MSB_REG_URL = "/openoapi/microservices/v1/services"
+REG_TO_MSB_REG_PARAM = {
+ "serviceName": "gvnfmdriver",
+ "version": "v1",
+ "url": "/openoapi/gvnfmdriver/v1",
+ "protocol": "REST",
+ "visualRange": "1",
+ "nodes": [{
+ "ip": "127.0.0.1",
+ "port": "8484",
+ "ttl": 0
+ }]
+}
diff --git a/gvnfmadapter/driver/pub/database/__init__.py b/gvnfmadapter/driver/pub/database/__init__.py
new file mode 100644
index 0000000..c7b6818
--- /dev/null
+++ b/gvnfmadapter/driver/pub/database/__init__.py
@@ -0,0 +1,13 @@
+# Copyright 2017 ZTE Corporation.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/gvnfmadapter/driver/pub/database/models.py b/gvnfmadapter/driver/pub/database/models.py
new file mode 100644
index 0000000..c7b6818
--- /dev/null
+++ b/gvnfmadapter/driver/pub/database/models.py
@@ -0,0 +1,13 @@
+# Copyright 2017 ZTE Corporation.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/gvnfmadapter/driver/pub/utils/__init__.py b/gvnfmadapter/driver/pub/utils/__init__.py
new file mode 100644
index 0000000..c7b6818
--- /dev/null
+++ b/gvnfmadapter/driver/pub/utils/__init__.py
@@ -0,0 +1,13 @@
+# Copyright 2017 ZTE Corporation.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/gvnfmadapter/driver/pub/utils/restcall.py b/gvnfmadapter/driver/pub/utils/restcall.py
new file mode 100644
index 0000000..08f4cf3
--- /dev/null
+++ b/gvnfmadapter/driver/pub/utils/restcall.py
@@ -0,0 +1,95 @@
+# 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 sys
+import traceback
+import logging
+import urllib2
+import uuid
+import httplib2
+
+from driver.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/gvnfmadapter/driver/settings.py b/gvnfmadapter/driver/settings.py
new file mode 100644
index 0000000..33da552
--- /dev/null
+++ b/gvnfmadapter/driver/settings.py
@@ -0,0 +1,127 @@
+# 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 os
+
+import sys
+
+# 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',
+ 'driver.pub.database',
+ 'driver.interfaces'
+ ]
+
+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 = 'driver.urls'
+
+WSGI_APPLICATION = 'driver.wsgi.application'
+
+
+REST_FRAMEWORK = {
+ 'DEFAULT_RENDERER_CLASSES': (
+ 'rest_framework.renderers.JSONRenderer',),
+
+ 'DEFAULT_PARSER_CLASSES': (
+ 'rest_framework.parsers.MultiPartParser',
+ 'rest_framework.parsers.JSONParser')}
+"""
+DATABASES = {
+ 'default': {
+ 'ENGINE': 'django.db.backends.mysql',
+ 'NAME': 'vmanager',
+ 'HOST': 'localhost',
+ 'USER': 'root',
+ 'PASSWORD':'password',
+ },
+}
+
+redis_client = redis.StrictRedis(host='127.0.0.1', port=6379, password='', db=1)
+"""
+DATABASES = {
+ 'default': {
+ 'ENGINE': 'django.db.backends.sqlite3',
+ 'NAME': os.path.join(BASE_DIR, 'db.sqlite3'), }}
+
+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': {
+ 'driver_handler': {
+ 'level': 'DEBUG',
+ 'class': 'logging.handlers.RotatingFileHandler',
+ 'filename': os.path.join(BASE_DIR, 'logs/runtime_driver.log'),
+ 'formatter': 'standard',
+ 'maxBytes': 1024 * 1024 * 50,
+ 'backupCount': 5, }, },
+
+ 'loggers': {
+ 'driver': {
+ 'handlers': ['driver_handler'],
+ 'level': 'DEBUG',
+ 'propagate': False}, }}
+
+if 'test' in sys.argv:
+ from driver.pub.config import config
+ config.REG_TO_MSB_WHEN_START = False
+
+ 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/gvnfmadapter/driver/swagger/__init__.py b/gvnfmadapter/driver/swagger/__init__.py
new file mode 100644
index 0000000..c7b6818
--- /dev/null
+++ b/gvnfmadapter/driver/swagger/__init__.py
@@ -0,0 +1,13 @@
+# Copyright 2017 ZTE Corporation.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
diff --git a/gvnfmadapter/driver/swagger/swagger.json b/gvnfmadapter/driver/swagger/swagger.json
new file mode 100644
index 0000000..760cc91
--- /dev/null
+++ b/gvnfmadapter/driver/swagger/swagger.json
@@ -0,0 +1,468 @@
+{
+ "swagger": "2.0",
+ "info": {
+ "version": "1.0.0",
+ "title": "GVNFM Driver Service rest API"
+ },
+ "basePath": "/openoapi/{vnfmtype}/v1",
+ "tags": [
+ {
+ "name": "gvnfmdriver"
+ }
+ ],
+ "paths": {
+ "/{vnfmid}/vnfs": {
+ "post": {
+ "tags": [
+ "vnf instantiate"
+ ],
+ "summary": "instantiate the vnf",
+ "description": "",
+ "operationId": "instantiate_vnf",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "parameters": [
+ {
+ "name": "vnfmid",
+ "in": "path",
+ "description": "vnfm instance id",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "name": "body",
+ "in": "body",
+ "description": "request parameters",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/VnfRequestParams"
+ }
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "successful instantiate",
+ "schema": {
+ "$ref": "#/definitions/VnfResult"
+ }
+ },
+ "404": {
+ "description": "the vnfm instance id is wrong"
+ },
+ "500": {
+ "description": "the url is invalid"
+ }
+ }
+ }
+ },
+ "/{vnfmid}/vnfs/{vnfInstanceId}/terminate": {
+ "post": {
+ "tags": [
+ "vnf terminate"
+ ],
+ "summary": "terminate the vnf",
+ "description": "",
+ "operationId": "terminate_vnf",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "parameters": [
+ {
+ "name": "vnfmid",
+ "in": "path",
+ "description": "vnfm instance id",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "name": "vnfInstanceId",
+ "in": "path",
+ "description": "vnf instance id",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "successful terminate",
+ "schema": {
+ "$ref": "#/definitions/VnfResult"
+ }
+ },
+ "404": {
+ "description": "the vnfmid and vnfInstanceId are wrong"
+ },
+ "500": {
+ "description": "the url is invalid"
+ }
+ }
+ }
+ },
+ "/{vnfmid}/vnfs/{vnfInstanceId}": {
+ "get": {
+ "tags": [
+ "query vnf"
+ ],
+ "summary": "query the vnf",
+ "description": "",
+ "operationId": "query_vnf",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "parameters": [
+ {
+ "name": "vnfmid",
+ "in": "path",
+ "description": "vnfm instance id",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "name": "vnfInstanceId",
+ "in": "path",
+ "description": "vnf instance id",
+ "required": true,
+ "type": "string"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "successful query",
+ "schema": {
+ "$ref": "#/definitions/returnVnfInfo"
+ }
+ },
+ "404": {
+ "description": "the vnfmid and vnfInstanceId are wrong"
+ },
+ "500": {
+ "description": "the url is invalid"
+ }
+ }
+ }
+ },
+ "/{vnfmid}/jobs/{jobid}": {
+ "get": {
+ "tags": [
+ "operation status"
+ ],
+ "summary": "operation status",
+ "description": "",
+ "operationId": "operation_status",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "parameters": [
+ {
+ "name": "vnfmid",
+ "in": "path",
+ "description": "vnfm instance id",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "name": "jobid",
+ "in": "path",
+ "description": "vnf job id",
+ "required": true,
+ "type": "string"
+ },
+ {
+ "name": "responseId",
+ "in": "path",
+ "description": "vnf response id",
+ "required": true,
+ "type": "integer"
+ }
+ ],
+ "responses": {
+ "200": {
+ "description": "successful operation",
+ "schema": {
+ "$ref": "#/definitions/OperationStatusInfo"
+ }
+ },
+ "404": {
+ "description": "the vnfmid ,jobid and responseId are wrong"
+ },
+ "500": {
+ "description": "the url is invalid"
+ }
+ }
+ }
+ },
+ "/resource/grant": {
+ "put": {
+ "tags": [
+ "grant vnf"
+ ],
+ "summary": "grant the vnf",
+ "description": "",
+ "operationId": "grant_vnf",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "parameters": [
+ {
+ "name": "body",
+ "in": "body",
+ "description": "request data for grant the vnf",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/RequestGrantParams"
+ }
+ }
+ ],
+ "responses": {
+ "201": {
+ "description": "successful grant",
+ "schema": {
+ "$ref": "#/definitions/responseGrantResult"
+ }
+ },
+ "404": {
+ "description": "the request body is wrong"
+ },
+ "500": {
+ "description": "the url is invalid"
+ }
+ }
+ }
+ },
+ "/vnfs/lifecyclechangesnotification": {
+ "post": {
+ "tags": [
+ "life cycle changes notification"
+ ],
+ "summary": "life cycle changes notification",
+ "description": "",
+ "operationId": "lifecyclechangesnotification",
+ "consumes": [
+ "application/json"
+ ],
+ "produces": [
+ "application/json"
+ ],
+ "parameters": [
+ {
+ "name": "body",
+ "in": "body",
+ "description": "request data for grant the vnf",
+ "required": true,
+ "schema": {
+ "$ref": "#/definitions/RequestNotifyParams"
+ }
+ }
+ ],
+ "responses": {
+ "201": {
+ "description": "successful Notify",
+ "schema": {
+ "$ref": "#/definitions/ResponseNotifyResult"
+ }
+ },
+ "404": {
+ "description": "the request body is wrong"
+ },
+ "500": {
+ "description": "the url is invalid"
+ }
+ }
+ }
+ }
+ },
+ "definitions": {
+ "VnfRequestParams": {
+ "type": "object",
+ "properties": {
+ "vnfInstanceName": {
+ "type": "string"
+ },
+ "vnfPackageId": {
+ "type": "string"
+ },
+ "vnfDescriptorId": {
+ "type": "string"
+ },
+ "additionalParam": {
+ "type": "object",
+ "properties": {
+ "sdncontroller": {
+ "type": "string"
+ },
+ "NatIpRange": {
+ "type": "string"
+ },
+ "m6000_mng_ip": {
+ "type": "string"
+ },
+ "externalPluginManageNetworkName": {
+ "type": "string"
+ },
+ "location": {
+ "type": "string"
+ },
+ "externalManageNetworkName": {
+ "type": "string"
+ },
+ "sfc_data_network": {
+ "type": "string"
+ },
+ "externalDataNetworkName": {
+ "type": "string"
+ },
+ "inputs": {
+ "type": "object"
+ }
+ }
+ }
+ }
+ },
+ "VnfResult": {
+ "type": "object",
+ "properties": {
+ "vnfInstanceId": {
+ "type": "string"
+ },
+ "jobId": {
+ "type": "string"
+ }
+ }
+ },
+ "returnVnfInfo": {
+ "type": "object",
+ "properties": {
+ "vnfInfo": {
+ "type": "object",
+ "properties": {
+ "nfInstanceId": {
+ "type": "string"
+ },
+ "vnfStatus": {
+ "type": "string"
+ },
+ "version": {
+ "type": "string"
+ }
+ }
+ }
+ }
+ },
+ "OperationStatusInfo": {
+ "type": "object",
+ "properties": {
+ "responsedescriptor": {
+ "type": "object",
+ "properties": {
+ "status": {
+ "type": "string"
+ },
+ "responsehistorylist": {
+ "type": "array"
+ },
+ "responseid": {
+ "type": "integer"
+ },
+ "errorcode": {
+ "type": "string"
+ },
+ "progress": {
+ "type": "integer"
+ },
+ "statusdescription": {
+ "type": "string"
+ }
+ }
+ },
+ "jobid": {
+ "type": "string"
+ }
+ }
+ },
+ "RequestGrantParams": {
+ "type": "object",
+ "properties": {
+ "vnfmid": {
+ "type": "string"
+ },
+ "nfvoid": {
+ "type": "string"
+ },
+ "vimid": {
+ "type": "string"
+ },
+ "exvimidlist": {
+ "type": "array"
+ },
+ "tenant": {
+ "type": "string"
+ },
+ "vnfistanceid": {
+ "type": "string"
+ },
+ "operationright": {
+ "type": "string"
+ },
+ "vmlist": {
+ "type": "array"
+ }
+ }
+ },
+ "responseGrantResult": {
+ "type": "object",
+ "properties": {
+ "vimid": {
+ "type": "string"
+ },
+ "tenant": {
+ "type": "string"
+ }
+ }
+ },
+ "RequestNotifyParams": {
+ "type": "object",
+ "properties": {
+ "nfvoid": {
+ "type": "string"
+ },
+ "vnfmid": {
+ "type": "string"
+ },
+ "vimid": {
+ "type": "string"
+ },
+ "timestamp": {
+ "type": "string"
+ },
+ "vnfinstanceid": {
+ "type": "string"
+ },
+ "eventtype": {
+ "type": "string"
+ },
+ "vmlist": {
+ "type": "array"
+ }
+ }
+ },
+ "ResponseNotifyResult": {
+ "type": "object"
+ }
+ }
+} \ No newline at end of file
diff --git a/gvnfmadapter/driver/swagger/tests.py b/gvnfmadapter/driver/swagger/tests.py
new file mode 100644
index 0000000..a2e0b9d
--- /dev/null
+++ b/gvnfmadapter/driver/swagger/tests.py
@@ -0,0 +1,31 @@
+# Copyright 2017 ZTE Corporation.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import unittest
+import json
+from django.test import Client
+from rest_framework import status
+
+
+class SwaggerViewTest(unittest.TestCase):
+ def setUp(self):
+ self.client = Client()
+
+ def tearDown(self):
+ pass
+
+ def test_sample(self):
+ response = self.client.get("/openoapi/gvnfmdriver/v1/swagger.json")
+ self.assertEqual(status.HTTP_200_OK, response.status_code, response.content)
+
diff --git a/gvnfmadapter/driver/swagger/urls.py b/gvnfmadapter/driver/swagger/urls.py
new file mode 100644
index 0000000..d482557
--- /dev/null
+++ b/gvnfmadapter/driver/swagger/urls.py
@@ -0,0 +1,20 @@
+# Copyright 2017 ZTE Corporation.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+from django.conf.urls import url
+from driver.swagger import views
+
+urlpatterns = [
+ url(r'^openoapi/gvnfmdriver/v1/swagger.json$', views.SwaggerView.as_view()),
+]
diff --git a/gvnfmadapter/driver/swagger/views.py b/gvnfmadapter/driver/swagger/views.py
new file mode 100644
index 0000000..e9c9604
--- /dev/null
+++ b/gvnfmadapter/driver/swagger/views.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.
+import os
+import json
+from rest_framework.views import APIView
+from rest_framework.response import Response
+
+
+class SwaggerView(APIView):
+ """
+ Show rest api swagger.
+ """
+ def get(self, request, format=None):
+ 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)
diff --git a/gvnfmadapter/driver/urls.py b/gvnfmadapter/driver/urls.py
new file mode 100644
index 0000000..86f1fc3
--- /dev/null
+++ b/gvnfmadapter/driver/urls.py
@@ -0,0 +1,26 @@
+# 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 driver.pub.config.config import REG_TO_MSB_WHEN_START, REG_TO_MSB_REG_URL, REG_TO_MSB_REG_PARAM
+from django.conf.urls import include, url
+urlpatterns = [
+ url(r'^', include('driver.interfaces.urls')),
+ url(r'^', include('driver.swagger.urls')),
+]
+
+# regist to MSB when startup
+if REG_TO_MSB_WHEN_START:
+ import json
+ from driver.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/gvnfmadapter/driver/wsgi.py b/gvnfmadapter/driver/wsgi.py
new file mode 100644
index 0000000..1008e32
--- /dev/null
+++ b/gvnfmadapter/driver/wsgi.py
@@ -0,0 +1,22 @@
+# 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 os
+
+from django.core.wsgi import get_wsgi_application
+
+os.environ.setdefault("DJANGO_SETTINGS_MODULE", "driver.settings")
+
+application = get_wsgi_application()
diff --git a/gvnfmadapter/initialize.sh b/gvnfmadapter/initialize.sh
new file mode 100644
index 0000000..7ace382
--- /dev/null
+++ b/gvnfmadapter/initialize.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+# 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.
+pip install -r requirements.txt
diff --git a/gvnfmadapter/logs/empty.txt b/gvnfmadapter/logs/empty.txt
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/gvnfmadapter/logs/empty.txt
diff --git a/gvnfmadapter/manage.py b/gvnfmadapter/manage.py
new file mode 100644
index 0000000..383c71a
--- /dev/null
+++ b/gvnfmadapter/manage.py
@@ -0,0 +1,23 @@
+# Copyright 2017 ZTE Corporation.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+import os
+import sys
+
+os.environ.setdefault("DJANGO_SETTINGS_MODULE", "driver.settings")
+
+# load initial configuration
+if __name__ == "__main__":
+ from django.core.management import execute_from_command_line
+ execute_from_command_line(sys.argv)
diff --git a/gvnfmadapter/pom.xml b/gvnfmadapter/pom.xml
new file mode 100644
index 0000000..6424504
--- /dev/null
+++ b/gvnfmadapter/pom.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0"?>
+<!--
+ 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.
+-->
+<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>
+ <relativePath>../../../../pom.xml</relativePath>
+ </parent>
+ <modelVersion>4.0.0</modelVersion>
+ <groupId>org.openo.nfvo</groupId>
+ <artifactId>drivers-vnfm-gvnfm-gvnfmadapter</artifactId>
+ <version>1.1.0-SNAPSHOT</version>
+ <packaging>pom</packaging>
+ <name>nfvo/drivers/vnfm/gvnfm/gvnfmadapter</name>
+ <description>nfvo drivers-vnfm-gvnfm-gvnfmadapter</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/gvnfmadapter/requirements.txt b/gvnfmadapter/requirements.txt
new file mode 100644
index 0000000..49f0e42
--- /dev/null
+++ b/gvnfmadapter/requirements.txt
@@ -0,0 +1,11 @@
+# rest framework
+Django==1.9.6
+djangorestframework==3.3.3
+
+# for call rest api
+httplib2==0.9.2
+
+# for unit test
+coverage==4.2
+mock==2.0.0
+unittest_xml_reporting==1.12.0
diff --git a/gvnfmadapter/run.sh b/gvnfmadapter/run.sh
new file mode 100644
index 0000000..7600abb
--- /dev/null
+++ b/gvnfmadapter/run.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+# 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.
+nohup python manage.py runserver 127.0.0.1:8484 > /dev/null &
diff --git a/gvnfmadapter/stop.sh b/gvnfmadapter/stop.sh
new file mode 100644
index 0000000..26efe30
--- /dev/null
+++ b/gvnfmadapter/stop.sh
@@ -0,0 +1,15 @@
+#!/bin/bash
+# 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.
+ps auxww | grep 'manage.py runserver 127.0.0.1:8484' | awk '{print $2}' | xargs kill -9
diff --git a/gvnfmadapter/tox.ini b/gvnfmadapter/tox.ini
new file mode 100644
index 0000000..4e62baf
--- /dev/null
+++ b/gvnfmadapter/tox.ini
@@ -0,0 +1,7 @@
+[tox]
+envlist = py27
+skipsdist = true
+
+[testenv]
+deps = -r{toxinidir}/requirements.txt
+commands = coverage run --branch manage.py test