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