summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorShobana Jothi <shobana.jothi@verizon.com>2018-09-07 19:52:31 +0530
committerShobana Jothi <shobana.jothi@verizon.com>2018-09-17 12:40:41 +0530
commit9fd7049657992304428d4bcd651c13312479917b (patch)
tree7193949a1809001a952c2c18c6194052717905ca
parentdd8d72e678369aaf122bd3aecbcd336acdca3dd6 (diff)
Operate API in Gvnfm Driver
Change-Id: I94551bac941ae155b8322a2fb6b38cca12d5872c Issue-ID: VFC-1051 Signed-off-by: Shobana Jothi<shobana.jothi@verizon.com>
-rw-r--r--gvnfmadapter/driver/interfaces/serializers/operate_request.py36
-rw-r--r--gvnfmadapter/driver/interfaces/serializers/response.py23
-rw-r--r--gvnfmadapter/driver/interfaces/tests.py90
-rw-r--r--gvnfmadapter/driver/interfaces/urls.py4
-rw-r--r--gvnfmadapter/driver/interfaces/views.py53
-rw-r--r--gvnfmadapter/driver/pub/utils/restcall.py14
6 files changed, 213 insertions, 7 deletions
diff --git a/gvnfmadapter/driver/interfaces/serializers/operate_request.py b/gvnfmadapter/driver/interfaces/serializers/operate_request.py
new file mode 100644
index 0000000..8bbaab8
--- /dev/null
+++ b/gvnfmadapter/driver/interfaces/serializers/operate_request.py
@@ -0,0 +1,36 @@
+# Copyright (C) 2018 Verizon. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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 serializers
+
+
+class VnfOperateRequestSerializer(serializers.Serializer):
+ changeStateTo = serializers.ChoiceField(
+ help_text="The desired operational state (i.e. started or stopped) to change the VNF to.",
+ choices=["STARTED", "STOPPED"],
+ required=True)
+ stopType = serializers.ChoiceField(
+ help_text="It signals whether forceful or graceful stop is requested.",
+ choices=["FORCEFUL", "GRACEFUL"],
+ required=False)
+ gracefulStopTimeout = serializers.IntegerField(
+ help_text="The time interval to wait for the VNF to be taken out of service during graceful stop.",
+ required=False)
+ additionalParams = serializers.DictField(
+ help_text="Additional input parameters for the operate process, \
+ specific to the VNF being operated, \
+ as declared in the VNFD as part of OperateVnfOpConfig.",
+ child=serializers.CharField(help_text="", allow_blank=True),
+ required=False,
+ allow_null=True)
diff --git a/gvnfmadapter/driver/interfaces/serializers/response.py b/gvnfmadapter/driver/interfaces/serializers/response.py
new file mode 100644
index 0000000..adcbaa0
--- /dev/null
+++ b/gvnfmadapter/driver/interfaces/serializers/response.py
@@ -0,0 +1,23 @@
+# Copyright (C) 2018 Verizon. All Rights Reserved
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT 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 serializers
+
+
+class ProblemDetailsSerializer(serializers.Serializer):
+ type = serializers.CharField(help_text="Type", required=False, allow_null=True)
+ title = serializers.CharField(help_text="Title", required=False, allow_null=True)
+ status = serializers.IntegerField(help_text="Status", required=True)
+ detail = serializers.CharField(help_text="Detail", required=True, allow_null=True)
+ instance = serializers.CharField(help_text="Instance", required=False, allow_null=True)
diff --git a/gvnfmadapter/driver/interfaces/tests.py b/gvnfmadapter/driver/interfaces/tests.py
index 84607a9..7196f3a 100644
--- a/gvnfmadapter/driver/interfaces/tests.py
+++ b/gvnfmadapter/driver/interfaces/tests.py
@@ -728,3 +728,93 @@ class InterfacesTest(TestCase):
content_type='application/json'
)
self.assertEqual(status.HTTP_500_INTERNAL_SERVER_ERROR, response.status_code)
+
+
+# Operate API Test case
+ @mock.patch.object(restcall, 'call_req')
+ def test_operate_vnf_404_NotFound(self, mock_call_req):
+ vnfm_info = {
+ "vnfmId": "19ecbb3a-3242-4fa3-9926-8dfb7ddc29ee",
+ "name": "g_vnfm",
+ "type": "gvnfmdriver",
+ "vimId": "",
+ "vendor": "vendor1",
+ "version": "v1.0",
+ "description": "vnfm",
+ "certificateUrl": "",
+ "url": "http://10.74.44.11",
+ "userName": "admin",
+ "password": "admin",
+ "createTime": "2016-07-06 15:33:18"
+ }
+ req_data = {
+ "changeStateTo": "STARTED"
+ }
+ probDetail = {"status": 404, "detail": "VNF Instance not found"}
+ r1 = [0, json.JSONEncoder().encode(vnfm_info), "200", ""]
+ r2 = [1, json.JSONEncoder().encode(probDetail), "404", ""]
+ mock_call_req.side_effect = [r1, r2]
+ response = self.client.post("/api/gvnfmdriver/v1/vnfmid/vnfs/2/operate",
+ data=json.dumps(req_data), content_type="application/json")
+ self.assertEqual(status.HTTP_404_NOT_FOUND, response.status_code)
+ self.assertEqual(probDetail, response.data)
+
+ @mock.patch.object(restcall, 'call_req')
+ def test_operate_vnf_409_Conflict(self, mock_call_req):
+ vnfm_info = {
+ "vnfmId": "19ecbb3a-3242-4fa3-9926-8dfb7ddc29ee",
+ "name": "g_vnfm",
+ "type": "gvnfmdriver",
+ "vimId": "",
+ "vendor": "vendor1",
+ "version": "v1.0",
+ "description": "vnfm",
+ "certificateUrl": "",
+ "url": "http://10.74.44.11",
+ "userName": "admin",
+ "password": "admin",
+ "createTime": "2016-07-06 15:33:18"
+ }
+ req_data = {
+ "changeStateTo": "STOPPED",
+ "stopType": "GRACEFUL",
+ "gracefulStopTimeout": 2
+ }
+ probDetail = {"status": 409, "detail": "VNF Instance not in Instantiated State"}
+ r1 = [0, json.JSONEncoder().encode(vnfm_info), "200", ""]
+ r2 = [1, json.JSONEncoder().encode(probDetail), "409", ""]
+ mock_call_req.side_effect = [r1, r2]
+ response = self.client.post("/api/gvnfmdriver/v1/vnfmid/vnfs/2/operate",
+ data=json.dumps(req_data), content_type="application/json")
+ self.assertEqual(status.HTTP_409_CONFLICT, response.status_code)
+ self.assertEqual(probDetail, response.data)
+
+ @mock.patch.object(restcall, 'call_req')
+ def test_operate_vnf_success(self, mock_call_req):
+ vnfm_info = {
+ "vnfmId": "19ecbb3a-3242-4fa3-9926-8dfb7ddc29ee",
+ "name": "g_vnfm",
+ "type": "gvnfmdriver",
+ "vimId": "",
+ "vendor": "vendor1",
+ "version": "v1.0",
+ "description": "vnfm",
+ "certificateUrl": "",
+ "url": "http://10.74.44.11",
+ "userName": "admin",
+ "password": "admin",
+ "createTime": "2016-07-06 15:33:18"
+ }
+ req_data = {
+ "changeStateTo": "STOPPED",
+ "stopType": "GRACEFUL",
+ "gracefulStopTimeout": 2
+ }
+ r1 = [0, json.JSONEncoder().encode(vnfm_info), "200", ""]
+ r2 = [0, json.JSONEncoder().encode(''), "202", "/vnf_lc_ops/NF-OPERATE-12-2a3be946-b01d-11e8-9302-08002705b121"]
+ mock_call_req.side_effect = [r1, r2]
+ response = self.client.post("/api/gvnfmdriver/v1/vnfmid/vnfs/2/operate",
+ data=json.dumps(req_data), content_type="application/json")
+ self.assertEqual(status.HTTP_202_ACCEPTED, response.status_code)
+ self.assertEqual(None, response.data)
+ self.assertEqual("/vnf_lc_ops/NF-OPERATE-12-2a3be946-b01d-11e8-9302-08002705b121", response['Location'])
diff --git a/gvnfmadapter/driver/interfaces/urls.py b/gvnfmadapter/driver/interfaces/urls.py
index ac39eff..b615d9b 100644
--- a/gvnfmadapter/driver/interfaces/urls.py
+++ b/gvnfmadapter/driver/interfaces/urls.py
@@ -15,7 +15,7 @@
from django.conf.urls import url
from driver.interfaces.views import VnfInstInfo, VnfTermInfo, VnfQueryInfo, VnfOperInfo
from driver.interfaces.views import Subscription
-from driver.interfaces.views import VnfPkgsInfo, VnfGrantInfo, VnfNotifyInfo, QuerySingleVnfLcmOpOcc
+from driver.interfaces.views import VnfPkgsInfo, VnfGrantInfo, VnfNotifyInfo, QuerySingleVnfLcmOpOcc, VnfOperateView
urlpatterns = [
url(r'^api/(?P<vnfmtype>[0-9a-zA-Z\-\_]+)/v1/(?P<vnfmid>[0-9a-zA-Z\-\_]+)/vnfs$', VnfInstInfo.as_view()),
@@ -28,4 +28,6 @@ urlpatterns = [
url(r'^api/(?P<vnfmtype>[0-9a-zA-Z\-\_]+)/v1/resource/grant$', VnfGrantInfo.as_view()),
url(r'^api/(?P<vnfmtype>[0-9a-zA-Z\-\_]+)/v1/vnfs/lifecyclechangesnotification$', VnfNotifyInfo.as_view()),
url(r'^api/(?P<vnfmtype>[0-9a-zA-Z\-\_]+)/v1/(?P<vnfmid>[0-9a-zA-Z\-\_]+)/vnf_lcm_op_occs/(?P<lcmopoccid>[0-9a-zA-Z_-]+)$', QuerySingleVnfLcmOpOcc.as_view()),
+ url(r'^api/(?P<vnfmtype>[0-9a-zA-Z\-\_]+)/v1/(?P<vnfmid>[0-9a-zA-Z\-\_]+)/vnfs/(?P<vnfInstanceId>'
+ r'[0-9a-zA-Z\-\_]+)/operate$', VnfOperateView.as_view()),
]
diff --git a/gvnfmadapter/driver/interfaces/views.py b/gvnfmadapter/driver/interfaces/views.py
index 32897c2..b32aa8c 100644
--- a/gvnfmadapter/driver/interfaces/views.py
+++ b/gvnfmadapter/driver/interfaces/views.py
@@ -34,6 +34,8 @@ from driver.interfaces.serializers.lccn_subscription_request import LccnSubscrip
from driver.pub.exceptions import GvnfmDriverException
from driver.pub.utils import restcall
from driver.pub.utils.restcall import req_by_msb
+from driver.interfaces.serializers.operate_request import VnfOperateRequestSerializer
+from driver.interfaces.serializers.response import ProblemDetailsSerializer
logger = logging.getLogger(__name__)
@@ -299,6 +301,46 @@ class VnfNotifyInfo(APIView):
return Response(data={'error': 'unexpected exception'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
+class VnfOperateView(APIView):
+ @swagger_auto_schema(
+ request_body=VnfOperateRequestSerializer(),
+ responses={
+ status.HTTP_202_ACCEPTED: "Success",
+ status.HTTP_404_NOT_FOUND: ProblemDetailsSerializer(),
+ status.HTTP_409_CONFLICT: ProblemDetailsSerializer(),
+ status.HTTP_500_INTERNAL_SERVER_ERROR: "Internal error"
+ }
+ )
+ def post(self, request, vnfmtype, vnfmid, vnfInstanceId):
+ logger.debug("operate_vnf--post::> %s" % request.data)
+ logger.debug("Operate vnf begin!")
+ try:
+ requestSerializer = VnfOperateRequestSerializer(data=request.data)
+ request_isValid = requestSerializer.is_valid()
+ if not request_isValid:
+ raise Exception(requestSerializer.errors)
+ logger.debug("Operate vnf start!")
+ logger.debug("do_operate: vnfmid=[%s],vnfInstanceId=[%s],request data=[%s]",
+ vnfmid, vnfInstanceId, request.data)
+ statusCode, resp, location = do_lcmVnf(vnfmid, vnfInstanceId, request.data, "operate")
+ logger.debug("do_operate: response data=[%s]", resp)
+ logger.debug("Operate vnf end!")
+ ret = int(statusCode)
+ if ret == status.HTTP_404_NOT_FOUND:
+ return Response(data=resp, status=status.HTTP_404_NOT_FOUND)
+ elif ret == status.HTTP_409_CONFLICT:
+ return Response(data=resp, status=status.HTTP_409_CONFLICT)
+ response = Response(data=None, status=status.HTTP_202_ACCEPTED)
+ response["Location"] = location
+ return response
+ except GvnfmDriverException as e:
+ logger.error('operate vnf failed, detail message: %s' % e.message)
+ return Response(data={'error': e.message}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
+ except:
+ logger.error(traceback.format_exc())
+ return Response(data={'error': 'unexpected exception'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
+
+
class VnfPkgsInfo(APIView):
def get(request, *args, **kwargs):
try:
@@ -518,6 +560,17 @@ def do_deletevnf(vnfm_id, vnfInstanceId, data):
return json.JSONDecoder().decode(ret[1])
+def do_lcmVnf(vnfm_id, vnfInstanceId, data, lcmType):
+ logger.debug("[%s] request.data=%s", fun_name(), data)
+ vnfm_info = get_vnfminfo_from_nslcm(vnfm_id)
+ logger.debug("[do_lcmVnf] vnfm_info=[%s]", vnfm_info)
+ ret = call_vnfm("api/vnflcm/v1/vnf_instances/%s/%s" % (vnfInstanceId, lcmType), "POST", vnfm_info, data)
+ if ret[0] != 0 and int(ret[2]) != status.HTTP_404_NOT_FOUND and int(ret[2]) != status.HTTP_409_CONFLICT:
+ logger.error("Status code is %s, detail is %s.", ret[2], ret[1])
+ raise GvnfmDriverException('Failed to Operate vnf.')
+ return (ret[2], json.JSONDecoder().decode(ret[1]), ret[3])
+
+
def do_queryvnf(data, vnfm_id, vnfInstanceId):
logger.debug("[%s] request.data=%s", fun_name(), data)
vnfm_info = get_vnfminfo_from_nslcm(vnfm_id)
diff --git a/gvnfmadapter/driver/pub/utils/restcall.py b/gvnfmadapter/driver/pub/utils/restcall.py
index 08f4cf3..46defd3 100644
--- a/gvnfmadapter/driver/pub/utils/restcall.py
+++ b/gvnfmadapter/driver/pub/utils/restcall.py
@@ -34,6 +34,7 @@ def call_req(base_url, user, passwd, auth_type, resource, method, content=''):
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_Location = ''
resp_status = ''
try:
full_url = combine_url(base_url, resource)
@@ -47,31 +48,32 @@ def call_req(base_url, user, passwd, auth_type, resource, method, content=''):
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')
+ resp_Location = resp.get('Location', "")
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]
+ ret = [0, resp_body, resp_status, resp_Location]
else:
- ret = [1, resp_body, resp_status]
+ ret = [1, resp_body, resp_status, resp_Location]
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]
+ ret = [1, "Unable to connect to %s" % full_url, resp_status, resp_Location]
continue
raise ex
except urllib2.URLError as err:
- ret = [2, str(err), resp_status]
+ ret = [2, str(err), resp_status, resp_Location]
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]
+ ret = [3, res_info, resp_status, resp_Location]
except:
logger.error(traceback.format_exc())
- ret = [4, str(sys.exc_info()), resp_status]
+ ret = [4, str(sys.exc_info()), resp_status, resp_Location]
logger.debug("[%s]ret=%s" % (callid, str(ret)))
return ret