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