Add vfc-ztevnfmdriver notify auto-swagger
[vfc/nfvo/driver/vnfm/svnfm.git] / zte / vmanager / driver / interfaces / views.py
1 # Copyright 2016-2017 ZTE Corporation.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License");
4 # you may not use this file except in compliance with the License.
5 # You may obtain a copy of the License at
6 #
7 #         http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS,
11 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 # See the License for the specific language governing permissions and
13 # limitations under the License.
14
15 import inspect
16 import json
17 import logging
18 import os
19 import traceback
20
21 from drf_yasg.utils import swagger_auto_schema
22 from rest_framework import status
23 from rest_framework.decorators import api_view
24 from rest_framework.response import Response
25 from rest_framework.views import APIView
26
27 from driver.interfaces.serializers import HealReqSerializer, InstScaleHealRespSerializer, ScaleReqSerializer, \
28     NotifyReqSerializer
29 from driver.pub.config.config import VNF_FTP
30 from driver.pub.utils import restcall
31 from driver.pub.utils.restcall import req_by_msb
32
33 logger = logging.getLogger(__name__)
34
35
36 def load_json_file(file_name):
37     json_file = os.path.join(os.path.dirname(__file__), "data/" + file_name)
38     f = open(json_file)
39     json_data = json.JSONDecoder().decode(f.read())
40     f.close()
41     return json_data
42
43
44 def fun_name():
45     return "=================%s==================" % inspect.stack()[1][3]
46
47
48 def ignorcase_get(args, key):
49     if not key:
50         return ""
51     if not args:
52         return ""
53     if key in args:
54         return args[key]
55     for old_key in args:
56         if old_key.upper() == key.upper():
57             return args[old_key]
58     return ""
59
60
61 def mapping_conv(keyword_map, rest_return):
62     resp_data = {}
63     for param in keyword_map:
64         if keyword_map[param]:
65             resp_data[keyword_map[param]] = ignorcase_get(rest_return, param)
66     return resp_data
67
68
69 # Query vnfm_info from nslcm
70 def get_vnfminfo_from_nslcm(vnfmid):
71     ret = req_by_msb("api/nslcm/v1/vnfms/%s" % vnfmid, "GET")
72     return ret
73
74
75 # Query vnfd_info from nslcm
76 def vnfd_get(vnfpackageid):
77     ret = req_by_msb("api/nslcm/v1/vnfpackage/%s" % vnfpackageid, "GET")
78     return ret
79
80
81 # Query vnfpackage_info from nslcm
82 def vnfpackage_get(csarid):
83     ret = req_by_msb("api/nslcm/v1/vnfpackage/%s" % csarid, "GET")
84     return ret
85
86
87 @api_view(http_method_names=['POST'])
88 def instantiate_vnf(request, *args, **kwargs):
89     try:
90         logger.debug("[%s] request.data=%s", fun_name(), request.data)
91         vnfm_id = ignorcase_get(kwargs, "vnfmid")
92         ret = get_vnfminfo_from_nslcm(vnfm_id)
93         if ret[0] != 0:
94             return Response(data={'error': ret[1]}, status=ret[2])
95         vnfm_info = json.JSONDecoder().decode(ret[1])
96         logger.debug("[%s] vnfm_info=%s", fun_name(), vnfm_info)
97         vnf_package_id = ignorcase_get(request.data, "vnfPackageId")
98         ret = vnfd_get(vnf_package_id)
99         if ret[0] != 0:
100             return Response(data={'error': ret[1]}, status=ret[2])
101         vnfd_info = json.JSONDecoder().decode(ret[1])
102         logger.debug("[%s] vnfd_info=%s", fun_name(), vnfd_info)
103         csar_id = ignorcase_get(vnfd_info, "csarId")
104         ret = vnfpackage_get(csar_id)
105         if ret[0] != 0:
106             return Response(data={'error': ret[1]}, status=ret[2])
107         vnf_package_info = json.JSONDecoder().decode(ret[1])
108         packageInfo = ignorcase_get(vnf_package_info, "packageInfo")
109         logger.debug("[%s] packageInfo=%s", fun_name(), packageInfo)
110         data = {}
111         data["NFVOID"] = 1
112         data["VNFMID"] = vnfm_id
113         vnfdModel = json.loads(ignorcase_get(packageInfo, "vnfdModel"))
114         metadata = ignorcase_get(vnfdModel, "metadata")
115         vnfd_name = ignorcase_get(metadata, "name")
116         # TODO  convert sdc vnf package to vnf vender package
117         inputs = []
118         if "SPGW" in vnfd_name.upper():
119             data["VNFD"] = VNF_FTP + "SPGW"
120             inputs = load_json_file("SPGW" + "_inputs.json")
121         elif "MME" in vnfd_name.upper():
122             data["VNFD"] = VNF_FTP + "MME"
123             inputs = load_json_file("MME" + "_inputs.json")
124         else:
125             data["VNFD"] = ignorcase_get(packageInfo, "downloadUri")
126
127         data["VNFURL"] = data["VNFD"]
128
129         data["extension"] = {}
130         for name, value in ignorcase_get(ignorcase_get(request.data, "additionalParam"), "inputs").items():
131             inputs.append({"name": name, "value": value})
132
133         data["extension"]["inputs"] = json.dumps(inputs)
134         additionalParam = ignorcase_get(request.data, "additionalParam")
135         data["extension"]["extVirtualLinks"] = ignorcase_get(additionalParam, "extVirtualLinks")
136         data["extension"]["vnfinstancename"] = ignorcase_get(request.data, "vnfInstanceName")
137         data["extension"]["vnfid"] = data["VNFD"]
138         data["extension"]["multivim"] = 0
139         logger.debug("[%s] call_req data=%s", fun_name(), data)
140
141         ret = restcall.call_req(
142             base_url=ignorcase_get(vnfm_info, "url"),
143             user=ignorcase_get(vnfm_info, "userName"),
144             passwd=ignorcase_get(vnfm_info, "password"),
145             auth_type=restcall.rest_no_auth,
146             resource="v1/vnfs",
147             method='post',
148             content=json.JSONEncoder().encode(data))
149
150         logger.debug("[%s] call_req ret=%s", fun_name(), ret)
151         if ret[0] != 0:
152             return Response(data={'error': ret[1]}, status=ret[2])
153         resp = json.JSONDecoder().decode(ret[1])
154         resp_data = {
155             "vnfInstanceId": ignorcase_get(resp, "VNFInstanceID"),
156             "jobId": ignorcase_get(resp, "JobId")
157         }
158         logger.info("[%s]resp_data=%s", fun_name(), resp_data)
159     except Exception as e:
160         logger.error("Error occurred when instantiating VNF")
161         raise e
162     return Response(data=resp_data, status=ret[2])
163
164
165 @api_view(http_method_names=['POST'])
166 def terminate_vnf(request, *args, **kwargs):
167     try:
168         logger.debug("[%s] request.data=%s", fun_name(), request.data)
169         vnfm_id = ignorcase_get(kwargs, "vnfmid")
170         ret = get_vnfminfo_from_nslcm(vnfm_id)
171         if ret[0] != 0:
172             return Response(data={'error': ret[1]}, status=ret[2])
173         vnfm_info = json.JSONDecoder().decode(ret[1])
174         logger.debug("[%s] vnfm_info=%s", fun_name(), vnfm_info)
175         ret = restcall.call_req(
176             base_url=ignorcase_get(vnfm_info, "url"),
177             user=ignorcase_get(vnfm_info, "userName"),
178             passwd=ignorcase_get(vnfm_info, "password"),
179             auth_type=restcall.rest_no_auth,
180             resource="v1/vnfs/%s" % (ignorcase_get(kwargs, "vnfInstanceID")),
181             method='delete',
182             content=json.JSONEncoder().encode(request.data))
183         if ret[0] != 0:
184             return Response(data={'error': ret[1]}, status=ret[2])
185         resp = json.JSONDecoder().decode(ret[1])
186         resp_data = {
187             "vnfInstanceId": ignorcase_get(resp, "VNFInstanceID"),
188             "jobId": ignorcase_get(resp, "JobId")
189         }
190         logger.debug("[%s]resp_data=%s", fun_name(), resp_data)
191     except Exception as e:
192         logger.error("Error occurred when terminating VNF")
193         raise e
194     return Response(data=resp_data, status=ret[2])
195
196
197 @api_view(http_method_names=['GET'])
198 def query_vnf(request, *args, **kwargs):
199     try:
200         logger.debug("[%s] request.data=%s", fun_name(), request.data)
201         vnfm_id = ignorcase_get(kwargs, "vnfmid")
202         ret = get_vnfminfo_from_nslcm(vnfm_id)
203         if ret[0] != 0:
204             return Response(data={'error': ret[1]}, status=ret[2])
205         vnfm_info = json.JSONDecoder().decode(ret[1])
206         logger.debug("[%s] vnfm_info=%s", fun_name(), vnfm_info)
207         ret = restcall.call_req(
208             base_url=ignorcase_get(vnfm_info, "url"),
209             user=ignorcase_get(vnfm_info, "userName"),
210             passwd=ignorcase_get(vnfm_info, "password"),
211             auth_type=restcall.rest_no_auth,
212             resource="v1/vnfs/%s" % (ignorcase_get(kwargs, "vnfInstanceID")),
213             method='get',
214             content=json.JSONEncoder().encode({}))
215         if ret[0] != 0:
216             return Response(data={'error': ret[1]}, status=ret[2])
217         resp = json.JSONDecoder().decode(ret[1])
218         vnf_status = ignorcase_get(resp, "vnfinstancestatus")
219         resp_data = {"vnfInfo": {"vnfStatus": vnf_status}}
220         logger.debug("[%s]resp_data=%s", fun_name(), resp_data)
221     except Exception as e:
222         logger.error("Error occurred when querying VNF information.")
223         raise e
224     return Response(data=resp_data, status=ret[2])
225
226
227 @api_view(http_method_names=['GET'])
228 def operation_status(request, *args, **kwargs):
229     data = {}
230     try:
231         logger.debug("[%s] request.data=%s", fun_name(), request.data)
232         vnfm_id = ignorcase_get(kwargs, "vnfmid")
233         ret = get_vnfminfo_from_nslcm(vnfm_id)
234         if ret[0] != 0:
235             return Response(data={'error': ret[1]}, status=ret[2])
236         vnfm_info = json.JSONDecoder().decode(ret[1])
237         logger.debug("[%s] vnfm_info=%s", fun_name(), vnfm_info)
238         operation_status_url = '/v1/jobs/{jobId}?NFVOID={nfvoId}&VNFMID={vnfmId}&ResponseID={responseId}'
239         ret = restcall.call_req(
240             base_url=ignorcase_get(vnfm_info, 'url'),
241             user=ignorcase_get(vnfm_info, 'userName'),
242             passwd=ignorcase_get(vnfm_info, 'password'),
243             auth_type=restcall.rest_no_auth,
244             resource=operation_status_url.format(jobId=ignorcase_get(kwargs, 'jobid'), nfvoId=1,
245                                                  vnfmId=ignorcase_get(kwargs, 'vnfmid'),
246                                                  responseId=ignorcase_get(request.GET, 'responseId')),
247             method='get',
248             content=json.JSONEncoder().encode(data))
249
250         if ret[0] != 0:
251             return Response(data={'error': ret[1]}, status=ret[2])
252         resp_data = json.JSONDecoder().decode(ret[1])
253         logger.info("[%s]resp_data=%s", fun_name(), resp_data)
254     except Exception as e:
255         logger.error("Error occurred when getting operation status information.")
256         raise e
257     return Response(data=resp_data, status=ret[2])
258
259
260 @api_view(http_method_names=['PUT'])
261 def grantvnf(request, *args, **kwargs):
262     logger.info("=====grantvnf=====")
263     try:
264         logger.info("req_data = %s", request.data)
265         grant_vnf_param_map = {
266             "VNFMID": "",
267             "NFVOID": "",
268             "VIMID": "",
269             "ExVIMIDList": "",
270             "ExVIMID": "",
271             "Tenant": "",
272             "VNFInstanceID": "vnfInstanceId",
273             "OperationRight": "",
274             "VMList": "",
275             "VMFlavor": "",
276             "VMNumber": ""
277         }
278         data = mapping_conv(grant_vnf_param_map, request.data)
279         data["vnfDescriptorId"] = ""
280         if ignorcase_get(request.data, "operationright") == 0:
281             data["lifecycleOperation"] = "Instantiate"
282             data["addresource"] = []
283             for vm in ignorcase_get(request.data, "vmlist"):
284                 for i in range(int(ignorcase_get(vm, "vmnumber"))):
285                     data["addresource"].append(
286                         {
287                             "type": "vdu",
288                             "resourceDefinitionId": i,
289                             "vdu": ignorcase_get(vm, "vmflavor"),
290                             "vimid": ignorcase_get(vm, "vimid"),
291                             "tenant": ignorcase_get(vm, "tenant")})
292
293         data["additionalparam"] = {}
294         data["additionalparam"]["vnfmid"] = ignorcase_get(request.data, "vnfmid")
295         data["additionalparam"]["vimid"] = ignorcase_get(request.data, "vimid")
296         data["additionalparam"]["tenant"] = ignorcase_get(request.data, "tenant")
297
298         ret = req_by_msb('api/nslcm/v1/ns/grantvnf', "POST", content=json.JSONEncoder().encode(data))
299         logger.info("ret = %s", ret)
300         if ret[0] != 0:
301             return Response(data={'error': ret[1]}, status=ret[2])
302         resp = json.JSONDecoder().decode(ret[1])
303         resp_data = {
304             'vimid': ignorcase_get(resp['vim'], 'vimid'),
305             'tenant': ignorcase_get(ignorcase_get(resp['vim'], 'accessinfo'), 'tenant')
306         }
307         logger.info("[%s]resp_data=%s", fun_name(), resp_data)
308     except Exception as e:
309         logger.error("Error occurred in Grant VNF.")
310         raise e
311     return Response(data=resp_data, status=ret[2])
312
313
314 class Notify(APIView):
315     @swagger_auto_schema(
316         request_body=NotifyReqSerializer()
317     )
318     def post(self, request):
319         try:
320             logger.info("[%s]req_data = %s", fun_name(), request.data)
321             notify_param_map = {
322                 "NFVOID": "",
323                 "VNFMID": "VNFMID",
324                 "VIMID": "vimid",
325                 "VNFInstanceID": "vnfInstanceId",
326                 "TimeStamp": "",
327                 "EventType": "operation",
328                 "VMList": "",
329                 "VMFlavor": "",
330                 "VMNumber": "",
331                 "VMIDlist": "",
332                 "VMUUID": ""
333             }
334             data = mapping_conv(notify_param_map, request.data)
335             logger.info("[%s]data = %s", fun_name(), data)
336
337             data["status"] = "result"
338             data["jobId"] = "notMust"
339             data["affectedVnfc"] = []
340             data["affectedVl"] = []
341             data["affectedVirtualStorage"] = []
342             data["affectedCp"] = []
343
344             extension = ignorcase_get(request.data, "extension")
345             openo_notification = ignorcase_get(extension, "openo_notification")
346             if openo_notification:
347                 affectedvnfcs = ignorcase_get(openo_notification, "affectedVnfc")
348                 affectedvls = ignorcase_get(openo_notification, "affectedvirtuallink")
349                 affectedcps = ignorcase_get(openo_notification, "affectedCp")
350                 vnfdmodule = ignorcase_get(openo_notification, "vnfdmodule")
351             else:
352                 affectedvnfcs = ignorcase_get(ignorcase_get(request.data, "extension"), "affectedvnfc")
353                 affectedvls = ignorcase_get(ignorcase_get(request.data, "extension"), "affectedvl")
354                 affectedcps = ignorcase_get(ignorcase_get(request.data, "extension"), "affectedcp")
355                 vnfdmodule = ignorcase_get(ignorcase_get(request.data, "extension"), "vnfdmodule")
356
357             data["vnfdmodule"] = vnfdmodule
358
359             for affectedvnfc in affectedvnfcs:
360                 data["affectedVnfc"].append({
361                     "vnfcInstanceId": ignorcase_get(affectedvnfc, "vnfcInstanceId"),
362                     "vduId": ignorcase_get(affectedvnfc, "vduId"),
363                     "changeType": ignorcase_get(affectedvnfc, "changeType"),
364                     "vimId": ignorcase_get(ignorcase_get(affectedvnfc, "computeResource"), "vimId"),
365                     "vmId": ignorcase_get(ignorcase_get(affectedvnfc, "computeResource"), "resourceId"),
366                     "vmName": ignorcase_get(ignorcase_get(affectedvnfc, "computeResource"), "resourceName")
367                 })
368
369             for affectedvl in affectedvls:
370                 data["affectedVl"].append({
371                     "vlInstanceId": ignorcase_get(affectedvl, "virtualLinkInstanceId"),
372                     "changeType": ignorcase_get(affectedvl, "changeType"),
373                     "vimId": ignorcase_get(ignorcase_get(affectedvl, "networkResource"), "vimId"),
374                     "vldId": ignorcase_get(affectedvl, "virtuallinkdescid"),
375                     "networkResource": {
376                         "resourceType": "network",
377                         "resourceId": ignorcase_get(ignorcase_get(affectedvl, "networkresource"), "resourceid"),
378                         "resourceName": ignorcase_get(ignorcase_get(affectedvl, "networkresource"), "resourcename")
379                     }
380                 })
381
382             for affectedcp in affectedcps:
383                 data["affectedCp"].append(affectedcp)
384
385             notify_url = 'api/nslcm/v1/ns/{vnfmid}/vnfs/{vnfInstanceId}/Notify'
386             ret = req_by_msb(notify_url.format(vnfmid=ignorcase_get(data, 'VNFMID'),
387                                                vnfInstanceId=ignorcase_get(data, 'vnfinstanceid')),
388                              "POST", content=json.JSONEncoder().encode(data))
389
390             logger.info("[%s]data = %s", fun_name(), ret)
391             if ret[0] != 0:
392                 return Response(data={'error': ret[1]}, status=ret[2])
393             return Response(data=None, status=ret[2])
394         except Exception as e:
395             logger.error("Error occurred in LCM notification.")
396             logger.error(traceback.format_exc())
397             raise e
398
399
400 class Scale(APIView):
401     @swagger_auto_schema(
402         request_body=ScaleReqSerializer(),
403         responses={
404             status.HTTP_202_ACCEPTED: InstScaleHealRespSerializer(),
405             status.HTTP_500_INTERNAL_SERVER_ERROR: "Internal error"
406         }
407     )
408     def post(self, request, vnfmid, vnfInstanceId):
409         logger.info("====scale_vnf===")
410         try:
411             logger.info("request.data = %s", request.data)
412             logger.info("requested_url = %s", request.get_full_path())
413             scaleReqSerializer = ScaleReqSerializer(data=request.data)
414             if not scaleReqSerializer.is_valid():
415                 raise Exception(scaleReqSerializer.errors)
416
417             ret = get_vnfminfo_from_nslcm(vnfmid)
418             if ret[0] != 0:
419                 raise Exception(ret[1])
420
421             vnfm_info = json.JSONDecoder().decode(ret[1])
422             scale_type = ignorcase_get(scaleReqSerializer.data, "type")
423             aspect_id = ignorcase_get(scaleReqSerializer.data, "aspectId")
424             number_of_steps = ignorcase_get(scaleReqSerializer.data, "numberOfSteps")
425             data = {
426                 'vnfmid': vnfmid,
427                 'nfvoid': 1,
428                 'scaletype': '0' if scale_type == 'SCALE_OUT' else '1',
429                 'vmlist': [{'VMNumber': number_of_steps, 'VMFlavor': aspect_id}],
430                 'extension': ''
431             }
432
433             logger.info("data = %s", data)
434             ret = restcall.call_req(
435                 base_url=ignorcase_get(vnfm_info, "url"),
436                 user=ignorcase_get(vnfm_info, "userName"),
437                 passwd=ignorcase_get(vnfm_info, "password"),
438                 auth_type=restcall.rest_no_auth,
439                 resource='/v1/vnfs/{vnfInstanceID}/scale'.format(vnfInstanceID=vnfInstanceId),
440                 method='put',  # POST
441                 content=json.JSONEncoder().encode(data))
442             logger.info("ret=%s", ret)
443             if ret[0] != 0:
444                 raise Exception('scale error')
445
446             resp_data = json.JSONDecoder().decode(ret[1])
447             logger.info("resp_data=%s", resp_data)
448             scaleRespSerializer = InstScaleHealRespSerializer(data=resp_data)
449             if not scaleRespSerializer.is_valid():
450                 raise Exception(scaleRespSerializer.errors)
451
452             return Response(data=scaleRespSerializer.data, status=status.HTTP_202_ACCEPTED)
453         except Exception as e:
454             logger.error("Error occurred when scaling VNF,error:%s", e.message)
455             logger.error(traceback.format_exc())
456             return Response(data={'error': 'scale expection'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
457
458
459 class Heal(APIView):
460     @swagger_auto_schema(
461         request_body=HealReqSerializer(),
462         responses={
463             status.HTTP_202_ACCEPTED: InstScaleHealRespSerializer(),
464             status.HTTP_500_INTERNAL_SERVER_ERROR: "Internal error"
465         }
466     )
467     def post(self, request, vnfmid, vnfInstanceId):
468         logger.info("====heal_vnf===")
469         try:
470             logger.info("request.data = %s", request.data)
471             logger.info("requested_url = %s", request.get_full_path())
472             healReqSerializer = HealReqSerializer(data=request.data)
473             if not healReqSerializer.is_valid():
474                 raise Exception(healReqSerializer.errors)
475
476             ret = get_vnfminfo_from_nslcm(vnfmid)
477             if ret[0] != 0:
478                 raise Exception(ret[1])
479
480             vnfm_info = json.JSONDecoder().decode(ret[1])
481             data = {}
482             data['action'] = ignorcase_get(healReqSerializer.data, 'action')
483             affectedvm = ignorcase_get(healReqSerializer.data, 'affectedvm')
484             data['affectedvm'] = []
485             if isinstance(affectedvm, list):
486                 data['affectedvm'] = affectedvm
487             else:
488                 data['affectedvm'].append(affectedvm)
489             data['lifecycleoperation'] = 'operate'
490             data['isgrace'] = 'force'
491
492             logger.info("data = %s", data)
493             ret = restcall.call_req(
494                 base_url=ignorcase_get(vnfm_info, "url"),
495                 user=ignorcase_get(vnfm_info, "userName"),
496                 passwd=ignorcase_get(vnfm_info, "password"),
497                 auth_type=restcall.rest_no_auth,
498                 resource='/api/v1/nf_m_i/nfs/{vnfInstanceID}/vms/operation'.format(vnfInstanceID=vnfInstanceId),
499                 method='post',
500                 content=json.JSONEncoder().encode(data))
501             logger.info("ret=%s", ret)
502             if ret[0] != 0:
503                 raise Exception('heal error')
504             resp_data = json.JSONDecoder().decode(ret[1])
505             logger.info("resp_data=%s", resp_data)
506             healRespSerializer = InstScaleHealRespSerializer(data=resp_data)
507             if not healRespSerializer.is_valid():
508                 raise Exception(healRespSerializer.errors)
509
510             return Response(data=healRespSerializer.data, status=status.HTTP_202_ACCEPTED)
511         except Exception as e:
512             logger.error("Error occurred when healing VNF,error:%s", e.message)
513             logger.error(traceback.format_exc())
514             return Response(data={'error': 'heal expection'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
515
516
517 def get_vdus(nf_model, aspect_id):
518     associated_group = ''
519     members = []
520     vnf_flavours = nf_model['vnf_flavours']
521     for vnf_flaour in vnf_flavours:
522         scaling_aspects = vnf_flaour['scaling_aspects']
523         for aspect in scaling_aspects:
524             if aspect_id == aspect['id']:
525                 associated_group = aspect['associated_group']
526                 break
527     if not associated_group:
528         logger.error('Cannot find the corresponding element group')
529         raise Exception('Cannot find the corresponding element group')
530     for element_group in nf_model['element_groups']:
531         if element_group['group_id'] == associated_group:
532             members = element_group['members']
533     if not members:
534         logger.error('Cannot find the corresponding members')
535         raise Exception('Cannot find the corresponding members')
536     return members
537
538
539 @api_view(http_method_names=['GET'])
540 def samples(request, *args, **kwargs):
541     return Response(data={"status": "ok"})