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