Code Coverage

Cobertura Coverage Report > osmclient.sol005 >

ns.py

Trend

Classes100%
 
Lines8%
   
Conditionals100%
 

File Coverage summary

NameClassesLinesConditionals
ns.py
100%
1/1
8%
26/335
100%
0/0

Coverage Breakdown by Class

NameLinesConditionals
ns.py
8%
26/335
N/A

Source

osmclient/sol005/ns.py
1 # Copyright 2018 Telefonica
2 #
3 # All Rights Reserved.
4 #
5 #    Licensed under the Apache License, Version 2.0 (the "License"); you may
6 #    not use this file except in compliance with the License. You may obtain
7 #    a copy of the License at
8 #
9 #         http://www.apache.org/licenses/LICENSE-2.0
10 #
11 #    Unless required by applicable law or agreed to in writing, software
12 #    distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13 #    WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14 #    License for the specific language governing permissions and limitations
15 #    under the License.
16
17 1 """
18 OSM ns API handling
19 """
20
21 1 from osmclient.common import utils
22 1 from osmclient.common import wait as WaitForStatus
23 1 from osmclient.common.exceptions import ClientException
24 1 from osmclient.common.exceptions import NotFound
25 1 import yaml
26 1 import json
27 1 import logging
28
29
30 1 class Ns(object):
31 1     def __init__(self, http=None, client=None):
32 0         self._http = http
33 0         self._client = client
34 0         self._logger = logging.getLogger("osmclient")
35 0         self._apiName = "/nslcm"
36 0         self._apiVersion = "/v1"
37 0         self._apiResource = "/ns_instances_content"
38 0         self._apiBase = "{}{}{}".format(
39             self._apiName, self._apiVersion, self._apiResource
40         )
41
42     # NS '--wait' option
43 1     def _wait(self, id, wait_time, deleteFlag=False):
44 0         self._logger.debug("")
45         # Endpoint to get operation status
46 0         apiUrlStatus = "{}{}{}".format(
47             self._apiName, self._apiVersion, "/ns_lcm_op_occs"
48         )
49         # Wait for status for NS instance creation/update/deletion
50 0         if isinstance(wait_time, bool):
51 0             wait_time = WaitForStatus.TIMEOUT_NS_OPERATION
52 0         WaitForStatus.wait_for_status(
53             "NS",
54             str(id),
55             wait_time,
56             apiUrlStatus,
57             self._http.get2_cmd,
58             deleteFlag=deleteFlag,
59         )
60
61 1     def list(self, filter=None):
62         """Returns a list of NS"""
63 0         self._logger.debug("")
64 0         self._client.get_token()
65 0         filter_string = ""
66 0         if filter:
67 0             filter_string = "?{}".format(filter)
68 0         _, resp = self._http.get2_cmd("{}{}".format(self._apiBase, filter_string))
69 0         if resp:
70 0             return json.loads(resp)
71 0         return list()
72
73 1     def get(self, name):
74         """Returns an NS based on name or id"""
75 0         self._logger.debug("")
76 0         self._client.get_token()
77 0         if utils.validate_uuid4(name):
78 0             for ns in self.list():
79 0                 if name == ns["_id"]:
80 0                     return ns
81         else:
82 0             for ns in self.list():
83 0                 if name == ns["name"]:
84 0                     return ns
85 0         raise NotFound("ns '{}' not found".format(name))
86
87 1     def get_individual(self, name):
88 0         self._logger.debug("")
89 0         self._client.get_token()
90 0         ns_id = name
91 0         if not utils.validate_uuid4(name):
92 0             for ns in self.list():
93 0                 if name == ns["name"]:
94 0                     ns_id = ns["_id"]
95 0                     break
96 0         try:
97 0             _, resp = self._http.get2_cmd("{}/{}".format(self._apiBase, ns_id))
98             # resp = self._http.get_cmd('{}/{}/nsd_content'.format(self._apiBase, ns_id))
99             # print(yaml.safe_dump(resp))
100 0             if resp:
101 0                 return json.loads(resp)
102 0         except NotFound:
103 0             raise NotFound("ns '{}' not found".format(name))
104 0         raise NotFound("ns '{}' not found".format(name))
105
106 1     def delete(self, name, force=False, config=None, wait=False):
107         """
108         Deletes a Network Service (NS)
109         :param name: name of network service
110         :param force: set force. Direct deletion without cleaning at VIM
111         :param config: parameters of deletion, as:
112              autoremove: Bool (default True)
113              timeout_ns_terminate: int
114              skip_terminate_primitives: Bool (default False) to not exec the terminate primitives
115         :param wait: Make synchronous. Wait until deletion is completed:
116             False to not wait (by default), True to wait a standard time, or int (time to wait)
117         :return: None. Exception if fail
118         """
119 0         self._logger.debug("")
120 0         ns = self.get(name)
121 0         querystring_list = []
122 0         querystring = ""
123 0         if config:
124 0             ns_config = yaml.safe_load(config)
125 0             querystring_list += ["{}={}".format(k, v) for k, v in ns_config.items()]
126 0         if force:
127 0             querystring_list.append("FORCE=True")
128 0         if querystring_list:
129 0             querystring = "?" + "&".join(querystring_list)
130 0         http_code, resp = self._http.delete_cmd(
131             "{}/{}{}".format(self._apiBase, ns["_id"], querystring)
132         )
133         # TODO change to use a POST self._http.post_cmd('{}/{}/terminate{}'.format(_apiBase, ns['_id'], querystring),
134         #                                               postfields_dict=ns_config)
135         # seting autoremove as True by default
136         # print('HTTP CODE: {}'.format(http_code))
137         # print('RESP: {}'.format(resp))
138 0         if http_code == 202:
139 0             if wait and resp:
140 0                 resp = json.loads(resp)
141                 # For the 'delete' operation, '_id' is used
142 0                 self._wait(resp.get("_id"), wait, deleteFlag=True)
143             else:
144 0                 print("Deletion in progress")
145 0         elif http_code == 204:
146 0             print("Deleted")
147         else:
148 0             msg = resp or ""
149             # if resp:
150             #     try:
151             #         msg = json.loads(resp)
152             #     except ValueError:
153             #         msg = resp
154 0             raise ClientException("failed to delete ns {} - {}".format(name, msg))
155
156 1     def create(
157         self,
158         nsd_name,
159         nsr_name,
160         account,
161         config=None,
162         ssh_keys=None,
163         description="default description",
164         admin_status="ENABLED",
165         wait=False,
166     ):
167 0         self._logger.debug("")
168 0         self._client.get_token()
169 0         nsd = self._client.nsd.get(nsd_name)
170
171 0         vim_account_id = {}
172 0         wim_account_id = {}
173
174 0         def get_vim_account_id(vim_account):
175 0             self._logger.debug("")
176 0             if vim_account_id.get(vim_account):
177 0                 return vim_account_id[vim_account]
178 0             vim = self._client.vim.get(vim_account)
179 0             if vim is None:
180 0                 raise NotFound("cannot find vim account '{}'".format(vim_account))
181 0             vim_account_id[vim_account] = vim["_id"]
182 0             return vim["_id"]
183
184 0         def get_wim_account_id(wim_account):
185 0             self._logger.debug("")
186             # wim_account can be False (boolean) to indicate not use wim account
187 0             if not isinstance(wim_account, str):
188 0                 return wim_account
189 0             if wim_account_id.get(wim_account):
190 0                 return wim_account_id[wim_account]
191 0             wim = self._client.wim.get(wim_account)
192 0             if wim is None:
193 0                 raise NotFound("cannot find wim account '{}'".format(wim_account))
194 0             wim_account_id[wim_account] = wim["_id"]
195 0             return wim["_id"]
196
197 0         vim_id = get_vim_account_id(account)
198 0         ns = {}
199 0         ns["nsdId"] = nsd["_id"]
200 0         ns["nsName"] = nsr_name
201 0         ns["nsDescription"] = description
202 0         ns["vimAccountId"] = vim_id
203         # ns['userdata'] = {}
204         # ns['userdata']['key1']='value1'
205         # ns['userdata']['key2']='value2'
206
207 0         if ssh_keys is not None:
208 0             ns["ssh_keys"] = []
209 0             for pubkeyfile in ssh_keys.split(","):
210 0                 with open(pubkeyfile, "r") as f:
211 0                     ns["ssh_keys"].append(f.read())
212 0         if config:
213 0             ns_config = yaml.safe_load(config)
214 0             if "vim-network-name" in ns_config:
215 0                 ns_config["vld"] = ns_config.pop("vim-network-name")
216 0             if "vld" in ns_config:
217 0                 if not isinstance(ns_config["vld"], list):
218 0                     raise ClientException(
219                         "Error at --config 'vld' must be a list of dictionaries"
220                     )
221 0                 for vld in ns_config["vld"]:
222 0                     if not isinstance(vld, dict):
223 0                         raise ClientException(
224                             "Error at --config 'vld' must be a list of dictionaries"
225                         )
226 0                     if vld.get("vim-network-name"):
227 0                         if isinstance(vld["vim-network-name"], dict):
228 0                             vim_network_name_dict = {}
229 0                             for vim_account, vim_net in vld["vim-network-name"].items():
230 0                                 vim_network_name_dict[
231                                     get_vim_account_id(vim_account)
232                                 ] = vim_net
233 0                             vld["vim-network-name"] = vim_network_name_dict
234 0                     if "wim_account" in vld and vld["wim_account"] is not None:
235 0                         vld["wimAccountId"] = get_wim_account_id(vld.pop("wim_account"))
236 0             if "vnf" in ns_config:
237 0                 for vnf in ns_config["vnf"]:
238 0                     if vnf.get("vim_account"):
239 0                         vnf["vimAccountId"] = get_vim_account_id(vnf.pop("vim_account"))
240
241 0             if "additionalParamsForNs" in ns_config:
242 0                 if not isinstance(ns_config["additionalParamsForNs"], dict):
243 0                     raise ClientException(
244                         "Error at --config 'additionalParamsForNs' must be a dictionary"
245                     )
246 0             if "additionalParamsForVnf" in ns_config:
247 0                 if not isinstance(ns_config["additionalParamsForVnf"], list):
248 0                     raise ClientException(
249                         "Error at --config 'additionalParamsForVnf' must be a list"
250                     )
251 0                 for additional_param_vnf in ns_config["additionalParamsForVnf"]:
252 0                     if not isinstance(additional_param_vnf, dict):
253 0                         raise ClientException(
254                             "Error at --config 'additionalParamsForVnf' items must be dictionaries"
255                         )
256 0                     if not additional_param_vnf.get("member-vnf-index"):
257 0                         raise ClientException(
258                             "Error at --config 'additionalParamsForVnf' items must contain "
259                             "'member-vnf-index'"
260                         )
261 0             if "wim_account" in ns_config:
262 0                 wim_account = ns_config.pop("wim_account")
263 0                 if wim_account is not None:
264 0                     ns["wimAccountId"] = get_wim_account_id(wim_account)
265             # rest of parameters without any transformation or checking
266             # "timeout_ns_deploy"
267             # "placement-engine"
268 0             ns.update(ns_config)
269
270         # print(yaml.safe_dump(ns))
271 0         try:
272 0             self._apiResource = "/ns_instances_content"
273 0             self._apiBase = "{}{}{}".format(
274                 self._apiName, self._apiVersion, self._apiResource
275             )
276 0             headers = self._client._headers
277 0             headers["Content-Type"] = "application/yaml"
278 0             http_header = [
279                 "{}: {}".format(key, val) for (key, val) in list(headers.items())
280             ]
281 0             self._http.set_http_header(http_header)
282 0             http_code, resp = self._http.post_cmd(
283                 endpoint=self._apiBase, postfields_dict=ns
284             )
285             # print('HTTP CODE: {}'.format(http_code))
286             # print('RESP: {}'.format(resp))
287             # if http_code in (200, 201, 202, 204):
288 0             if resp:
289 0                 resp = json.loads(resp)
290 0             if not resp or "id" not in resp:
291 0                 raise ClientException(
292                     "unexpected response from server - {} ".format(resp)
293                 )
294 0             if wait:
295                 # Wait for status for NS instance creation
296 0                 self._wait(resp.get("nslcmop_id"), wait)
297 0             print(resp["id"])
298 0             return resp["id"]
299             # else:
300             #    msg = ""
301             #    if resp:
302             #        try:
303             #            msg = json.loads(resp)
304             #        except ValueError:
305             #            msg = resp
306             #    raise ClientException(msg)
307 0         except ClientException as exc:
308 0             message = "failed to create ns: {} nsd: {}\nerror:\n{}".format(
309                 nsr_name, nsd_name, str(exc)
310             )
311 0             raise ClientException(message)
312
313 1     def list_op(self, name, filter=None):
314         """Returns the list of operations of a NS"""
315 0         self._logger.debug("")
316 0         ns = self.get(name)
317 0         try:
318 0             self._apiResource = "/ns_lcm_op_occs"
319 0             self._apiBase = "{}{}{}".format(
320                 self._apiName, self._apiVersion, self._apiResource
321             )
322 0             filter_string = ""
323 0             if filter:
324 0                 filter_string = "&{}".format(filter)
325 0             http_code, resp = self._http.get2_cmd(
326                 "{}?nsInstanceId={}{}".format(self._apiBase, ns["_id"], filter_string)
327             )
328             # print('HTTP CODE: {}'.format(http_code))
329             # print('RESP: {}'.format(resp))
330 0             if http_code == 200:
331 0                 if resp:
332 0                     resp = json.loads(resp)
333 0                     return resp
334                 else:
335 0                     raise ClientException("unexpected response from server")
336             else:
337 0                 msg = resp or ""
338                 #    if resp:
339                 #        try:
340                 #            resp = json.loads(resp)
341                 #            msg = resp['detail']
342                 #        except ValueError:
343                 #            msg = resp
344 0                 raise ClientException(msg)
345 0         except ClientException as exc:
346 0             message = "failed to get operation list of NS {}:\nerror:\n{}".format(
347                 name, str(exc)
348             )
349 0             raise ClientException(message)
350
351 1     def get_op(self, operationId):
352         """Returns the status of an operation"""
353 0         self._logger.debug("")
354 0         self._client.get_token()
355 0         try:
356 0             self._apiResource = "/ns_lcm_op_occs"
357 0             self._apiBase = "{}{}{}".format(
358                 self._apiName, self._apiVersion, self._apiResource
359             )
360 0             http_code, resp = self._http.get2_cmd(
361                 "{}/{}".format(self._apiBase, operationId)
362             )
363             # print('HTTP CODE: {}'.format(http_code))
364             # print('RESP: {}'.format(resp))
365 0             if http_code == 200:
366 0                 if resp:
367 0                     resp = json.loads(resp)
368 0                     return resp
369                 else:
370 0                     raise ClientException("unexpected response from server")
371             else:
372 0                 msg = resp or ""
373                 #    if resp:
374                 #        try:
375                 #            resp = json.loads(resp)
376                 #            msg = resp['detail']
377                 #        except ValueError:
378                 #            msg = resp
379 0                 raise ClientException(msg)
380 0         except ClientException as exc:
381 0             message = "failed to get status of operation {}:\nerror:\n{}".format(
382                 operationId, str(exc)
383             )
384 0             raise ClientException(message)
385
386 1     def exec_op(
387         self,
388         name,
389         op_name,
390         op_data=None,
391         wait=False,
392     ):
393         """Executes an operation on a NS"""
394 0         self._logger.debug("")
395 0         ns = self.get(name)
396 0         try:
397 0             ns = self.get(name)
398 0             self._apiResource = "/ns_instances"
399 0             self._apiBase = "{}{}{}".format(
400                 self._apiName, self._apiVersion, self._apiResource
401             )
402 0             endpoint = "{}/{}/{}".format(self._apiBase, ns["_id"], op_name)
403             # print('OP_NAME: {}'.format(op_name))
404             # print('OP_DATA: {}'.format(json.dumps(op_data)))
405 0             http_code, resp = self._http.post_cmd(
406                 endpoint=endpoint, postfields_dict=op_data
407             )
408             # print('HTTP CODE: {}'.format(http_code))
409             # print('RESP: {}'.format(resp))
410             # if http_code in (200, 201, 202, 204):
411 0             if resp:
412 0                 resp = json.loads(resp)
413 0             if not resp or "id" not in resp:
414 0                 raise ClientException(
415                     "unexpected response from server - {}".format(resp)
416                 )
417 0             if wait:
418                 # Wait for status for NS instance action
419                 # For the 'action' operation, 'id' is used
420 0                 self._wait(resp.get("id"), wait)
421 0             return resp["id"]
422             # else:
423             #    msg = ""
424             #    if resp:
425             #        try:
426             #            msg = json.loads(resp)
427             #        except ValueError:
428             #            msg = resp
429             #    raise ClientException(msg)
430 0         except ClientException as exc:
431 0             message = "failed to exec operation {}:\nerror:\n{}".format(name, str(exc))
432 0             raise ClientException(message)
433
434 1     def scale_vnf(
435         self,
436         ns_name,
437         vnf_name,
438         scaling_group,
439         scale_in,
440         scale_out,
441         wait=False,
442         timeout=None,
443     ):
444         """Scales a VNF by adding/removing VDUs"""
445 0         self._logger.debug("")
446 0         self._client.get_token()
447 0         try:
448 0             op_data = {}
449 0             op_data["scaleType"] = "SCALE_VNF"
450 0             op_data["scaleVnfData"] = {}
451 0             if scale_in and not scale_out:
452 0                 op_data["scaleVnfData"]["scaleVnfType"] = "SCALE_IN"
453 0             elif not scale_in and scale_out:
454 0                 op_data["scaleVnfData"]["scaleVnfType"] = "SCALE_OUT"
455             else:
456 0                 raise ClientException("you must set either 'scale_in' or 'scale_out'")
457 0             op_data["scaleVnfData"]["scaleByStepData"] = {
458                 "member-vnf-index": vnf_name,
459                 "scaling-group-descriptor": scaling_group,
460             }
461 0             if timeout:
462 0                 op_data["timeout_ns_scale"] = timeout
463 0             op_id = self.exec_op(ns_name, op_name="scale", op_data=op_data, wait=wait)
464 0             print(str(op_id))
465 0         except ClientException as exc:
466 0             message = "failed to scale vnf {} of ns {}:\nerror:\n{}".format(
467                 vnf_name, ns_name, str(exc)
468             )
469 0             raise ClientException(message)
470
471 1     def create_alarm(self, alarm):
472 0         self._logger.debug("")
473 0         self._client.get_token()
474 0         data = {}
475 0         data["create_alarm_request"] = {}
476 0         data["create_alarm_request"]["alarm_create_request"] = alarm
477 0         try:
478 0             http_code, resp = self._http.post_cmd(
479                 endpoint="/test/message/alarm_request", postfields_dict=data
480             )
481             # print('HTTP CODE: {}'.format(http_code))
482             # print('RESP: {}'.format(resp))
483             # if http_code in (200, 201, 202, 204):
484             #     resp = json.loads(resp)
485 0             print("Alarm created")
486             # else:
487             #    msg = ""
488             #    if resp:
489             #        try:
490             #            msg = json.loads(resp)
491             #        except ValueError:
492             #            msg = resp
493             #    raise ClientException('error: code: {}, resp: {}'.format(
494             #                          http_code, msg))
495 0         except ClientException as exc:
496 0             message = "failed to create alarm: alarm {}\n{}".format(alarm, str(exc))
497 0             raise ClientException(message)
498
499 1     def delete_alarm(self, name):
500 0         self._logger.debug("")
501 0         self._client.get_token()
502 0         data = {}
503 0         data["delete_alarm_request"] = {}
504 0         data["delete_alarm_request"]["alarm_delete_request"] = {}
505 0         data["delete_alarm_request"]["alarm_delete_request"]["alarm_uuid"] = name
506 0         try:
507 0             http_code, resp = self._http.post_cmd(
508                 endpoint="/test/message/alarm_request", postfields_dict=data
509             )
510             # print('HTTP CODE: {}'.format(http_code))
511             # print('RESP: {}'.format(resp))
512             # if http_code in (200, 201, 202, 204):
513             #    resp = json.loads(resp)
514 0             print("Alarm deleted")
515             # else:
516             #    msg = ""
517             #    if resp:
518             #        try:
519             #            msg = json.loads(resp)
520             #        except ValueError:
521             #            msg = resp
522             #    raise ClientException('error: code: {}, resp: {}'.format(
523             #                          http_code, msg))
524 0         except ClientException as exc:
525 0             message = "failed to delete alarm: alarm {}\n{}".format(name, str(exc))
526 0             raise ClientException(message)
527
528 1     def get_alarm(self, project_name=None, ns_id=None, uuid=None):
529 0         self._client.get_token()
530 0         try:
531 0             self._apiName = "/nsfm"
532 0             self._apiResource = "/alarms"
533 0             self._apiBase = "{}{}{}".format(
534                 self._apiName, self._apiVersion, self._apiResource
535             )
536 0             if uuid:
537                 # if request is for any uuid
538 0                 http_code, resp = self._http.get2_cmd(
539                     "{}/{}".format(self._apiBase, uuid)
540                 )
541 0             if not uuid:
542 0                 http_code, resp = self._http.get2_cmd(
543                     "{}/{}/{}/{}".format(self._apiBase, uuid, project_name, ns_id)
544                 )
545 0             if http_code == 200:
546 0                 if resp:
547 0                     resp = json.loads(resp)
548 0                     return resp
549                 else:
550 0                     raise ClientException("unexpected response from server")
551             else:
552 0                 msg = resp or ""
553 0                 raise ClientException(msg)
554 0         except ClientException as exc:
555 0             message = "failed to get alarm :\nerror:\n{}".format(str(exc))
556 0             raise ClientException(message)
557
558 1     def update_alarm(self, uuid, threshold=None, is_enable=None, wait=None):
559 0         self._client.get_token()
560 0         try:
561 0             op_data = {}
562 0             op_data["uuid"] = uuid
563 0             op_data["threshold"] = threshold
564 0             op_data["is_enable"] = is_enable
565 0             self._apiName = "/nsfm"
566 0             self._apiResource = "/alarms"
567 0             self._apiBase = "{}{}{}".format(
568                 self._apiName, self._apiVersion, self._apiResource
569             )
570 0             http_code, resp = self._http.patch_cmd(
571                 endpoint="{}".format(self._apiBase), postfields_dict=op_data
572             )
573 0             if resp:
574 0                 resp = json.loads(resp)
575 0             print(resp)
576 0             return resp
577 0         except ClientException as exc:
578 0             message = "failed to update alarm :\nerror:\n{}".format(str(exc))
579 0             raise ClientException(message)
580
581 1     def export_metric(self, metric):
582 0         self._logger.debug("")
583 0         self._client.get_token()
584 0         data = {}
585 0         data["read_metric_data_request"] = metric
586 0         try:
587 0             http_code, resp = self._http.post_cmd(
588                 endpoint="/test/message/metric_request", postfields_dict=data
589             )
590             # print('HTTP CODE: {}'.format(http_code))
591             # print('RESP: {}'.format(resp))
592             # if http_code in (200, 201, 202, 204):
593             #    resp = json.loads(resp)
594 0             return "Metric exported"
595             # else:
596             #    msg = ""
597             #    if resp:
598             #        try:
599             #            msg = json.loads(resp)
600             #        except ValueError:
601             #            msg = resp
602             #    raise ClientException('error: code: {}, resp: {}'.format(
603             #                          http_code, msg))
604 0         except ClientException as exc:
605 0             message = "failed to export metric: metric {}\n{}".format(metric, str(exc))
606 0             raise ClientException(message)
607
608 1     def get_field(self, ns_name, field):
609 0         self._logger.debug("")
610 0         nsr = self.get(ns_name)
611 0         print(yaml.safe_dump(nsr))
612 0         if nsr is None:
613 0             raise NotFound("failed to retrieve ns {}".format(ns_name))
614
615 0         if field in nsr:
616 0             return nsr[field]
617
618 0         raise NotFound("failed to find {} in ns {}".format(field, ns_name))