1 # Copyright 2018 Telefonica
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
9 # http://www.apache.org/licenses/LICENSE-2.0
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
21 from osmclient
.common
import utils
22 from osmclient
.common
import wait
as WaitForStatus
23 from osmclient
.common
.exceptions
import ClientException
24 from osmclient
.common
.exceptions
import NotFound
31 def __init__(self
, http
=None, client
=None):
34 self
._logger
= logging
.getLogger("osmclient")
35 self
._apiName
= "/nslcm"
36 self
._apiVersion
= "/v1"
37 self
._apiResource
= "/ns_instances_content"
38 self
._apiBase
= "{}{}{}".format(
39 self
._apiName
, self
._apiVersion
, self
._apiResource
43 def _wait(self
, id, wait_time
, deleteFlag
=False):
44 self
._logger
.debug("")
45 # Endpoint to get operation status
46 apiUrlStatus
= "{}{}{}".format(
47 self
._apiName
, self
._apiVersion
, "/ns_lcm_op_occs"
49 # Wait for status for NS instance creation/update/deletion
50 if isinstance(wait_time
, bool):
51 wait_time
= WaitForStatus
.TIMEOUT_NS_OPERATION
52 WaitForStatus
.wait_for_status(
58 deleteFlag
=deleteFlag
,
61 def list(self
, filter=None):
62 """Returns a list of NS"""
63 self
._logger
.debug("")
64 self
._client
.get_token()
67 filter_string
= "?{}".format(filter)
68 _
, resp
= self
._http
.get2_cmd("{}{}".format(self
._apiBase
, filter_string
))
70 return json
.loads(resp
)
74 """Returns an NS based on name or id"""
75 self
._logger
.debug("")
76 self
._client
.get_token()
77 if utils
.validate_uuid4(name
):
78 for ns
in self
.list():
82 for ns
in self
.list():
83 if name
== ns
["name"]:
85 raise NotFound("ns '{}' not found".format(name
))
87 def get_individual(self
, name
):
88 self
._logger
.debug("")
89 self
._client
.get_token()
91 if not utils
.validate_uuid4(name
):
92 for ns
in self
.list():
93 if name
== ns
["name"]:
97 _
, 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))
101 return json
.loads(resp
)
103 raise NotFound("ns '{}' not found".format(name
))
104 raise NotFound("ns '{}' not found".format(name
))
106 def delete(self
, name
, force
=False, config
=None, wait
=False):
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
119 self
._logger
.debug("")
121 querystring_list
= []
124 ns_config
= yaml
.safe_load(config
)
125 querystring_list
+= ["{}={}".format(k
, v
) for k
, v
in ns_config
.items()]
127 querystring_list
.append("FORCE=True")
129 querystring
= "?" + "&".join(querystring_list
)
130 http_code
, resp
= self
._http
.delete_cmd(
131 "{}/{}{}".format(self
._apiBase
, ns
["_id"], querystring
)
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))
140 resp
= json
.loads(resp
)
141 # For the 'delete' operation, '_id' is used
142 self
._wait
(resp
.get("_id"), wait
, deleteFlag
=True)
144 print("Deletion in progress")
145 elif http_code
== 204:
151 # msg = json.loads(resp)
154 raise ClientException("failed to delete ns {} - {}".format(name
, msg
))
163 description
="default description",
164 admin_status
="ENABLED",
167 self
._logger
.debug("")
168 self
._client
.get_token()
169 nsd
= self
._client
.nsd
.get(nsd_name
)
174 def get_vim_account_id(vim_account
):
175 self
._logger
.debug("")
176 if vim_account_id
.get(vim_account
):
177 return vim_account_id
[vim_account
]
178 vim
= self
._client
.vim
.get(vim_account
)
180 raise NotFound("cannot find vim account '{}'".format(vim_account
))
181 vim_account_id
[vim_account
] = vim
["_id"]
184 def get_wim_account_id(wim_account
):
185 self
._logger
.debug("")
186 # wim_account can be False (boolean) to indicate not use wim account
187 if not isinstance(wim_account
, str):
189 if wim_account_id
.get(wim_account
):
190 return wim_account_id
[wim_account
]
191 wim
= self
._client
.wim
.get(wim_account
)
193 raise NotFound("cannot find wim account '{}'".format(wim_account
))
194 wim_account_id
[wim_account
] = wim
["_id"]
197 vim_id
= get_vim_account_id(account
)
199 ns
["nsdId"] = nsd
["_id"]
200 ns
["nsName"] = nsr_name
201 ns
["nsDescription"] = description
202 ns
["vimAccountId"] = vim_id
203 # ns['userdata'] = {}
204 # ns['userdata']['key1']='value1'
205 # ns['userdata']['key2']='value2'
207 if ssh_keys
is not None:
209 for pubkeyfile
in ssh_keys
.split(","):
210 with
open(pubkeyfile
, "r") as f
:
211 ns
["ssh_keys"].append(f
.read())
213 ns_config
= yaml
.safe_load(config
)
214 if "vim-network-name" in ns_config
:
215 ns_config
["vld"] = ns_config
.pop("vim-network-name")
216 if "vld" in ns_config
:
217 if not isinstance(ns_config
["vld"], list):
218 raise ClientException(
219 "Error at --config 'vld' must be a list of dictionaries"
221 for vld
in ns_config
["vld"]:
222 if not isinstance(vld
, dict):
223 raise ClientException(
224 "Error at --config 'vld' must be a list of dictionaries"
226 if vld
.get("vim-network-name"):
227 if isinstance(vld
["vim-network-name"], dict):
228 vim_network_name_dict
= {}
229 for vim_account
, vim_net
in vld
["vim-network-name"].items():
230 vim_network_name_dict
[
231 get_vim_account_id(vim_account
)
233 vld
["vim-network-name"] = vim_network_name_dict
234 if "wim_account" in vld
and vld
["wim_account"] is not None:
235 vld
["wimAccountId"] = get_wim_account_id(vld
.pop("wim_account"))
236 if "vnf" in ns_config
:
237 for vnf
in ns_config
["vnf"]:
238 if vnf
.get("vim_account"):
239 vnf
["vimAccountId"] = get_vim_account_id(vnf
.pop("vim_account"))
241 if "additionalParamsForNs" in ns_config
:
242 if not isinstance(ns_config
["additionalParamsForNs"], dict):
243 raise ClientException(
244 "Error at --config 'additionalParamsForNs' must be a dictionary"
246 if "additionalParamsForVnf" in ns_config
:
247 if not isinstance(ns_config
["additionalParamsForVnf"], list):
248 raise ClientException(
249 "Error at --config 'additionalParamsForVnf' must be a list"
251 for additional_param_vnf
in ns_config
["additionalParamsForVnf"]:
252 if not isinstance(additional_param_vnf
, dict):
253 raise ClientException(
254 "Error at --config 'additionalParamsForVnf' items must be dictionaries"
256 if not additional_param_vnf
.get("member-vnf-index"):
257 raise ClientException(
258 "Error at --config 'additionalParamsForVnf' items must contain "
261 if "wim_account" in ns_config
:
262 wim_account
= ns_config
.pop("wim_account")
263 if wim_account
is not None:
264 ns
["wimAccountId"] = get_wim_account_id(wim_account
)
265 # rest of parameters without any transformation or checking
266 # "timeout_ns_deploy"
270 # print(yaml.safe_dump(ns))
272 self
._apiResource
= "/ns_instances_content"
273 self
._apiBase
= "{}{}{}".format(
274 self
._apiName
, self
._apiVersion
, self
._apiResource
276 headers
= self
._client
._headers
277 headers
["Content-Type"] = "application/yaml"
279 "{}: {}".format(key
, val
) for (key
, val
) in list(headers
.items())
281 self
._http
.set_http_header(http_header
)
282 http_code
, resp
= self
._http
.post_cmd(
283 endpoint
=self
._apiBase
, postfields_dict
=ns
285 # print('HTTP CODE: {}'.format(http_code))
286 # print('RESP: {}'.format(resp))
287 # if http_code in (200, 201, 202, 204):
289 resp
= json
.loads(resp
)
290 if not resp
or "id" not in resp
:
291 raise ClientException(
292 "unexpected response from server - {} ".format(resp
)
295 # Wait for status for NS instance creation
296 self
._wait
(resp
.get("nslcmop_id"), wait
)
303 # msg = json.loads(resp)
306 # raise ClientException(msg)
307 except ClientException
as exc
:
308 message
= "failed to create ns: {} nsd: {}\nerror:\n{}".format(
309 nsr_name
, nsd_name
, str(exc
)
311 raise ClientException(message
)
313 def list_op(self
, name
, filter=None):
314 """Returns the list of operations of a NS"""
315 self
._logger
.debug("")
318 self
._apiResource
= "/ns_lcm_op_occs"
319 self
._apiBase
= "{}{}{}".format(
320 self
._apiName
, self
._apiVersion
, self
._apiResource
324 filter_string
= "&{}".format(filter)
325 http_code
, resp
= self
._http
.get2_cmd(
326 "{}?nsInstanceId={}{}".format(self
._apiBase
, ns
["_id"], filter_string
)
328 # print('HTTP CODE: {}'.format(http_code))
329 # print('RESP: {}'.format(resp))
332 resp
= json
.loads(resp
)
335 raise ClientException("unexpected response from server")
340 # resp = json.loads(resp)
341 # msg = resp['detail']
344 raise ClientException(msg
)
345 except ClientException
as exc
:
346 message
= "failed to get operation list of NS {}:\nerror:\n{}".format(
349 raise ClientException(message
)
351 def get_op(self
, operationId
):
352 """Returns the status of an operation"""
353 self
._logger
.debug("")
354 self
._client
.get_token()
356 self
._apiResource
= "/ns_lcm_op_occs"
357 self
._apiBase
= "{}{}{}".format(
358 self
._apiName
, self
._apiVersion
, self
._apiResource
360 http_code
, resp
= self
._http
.get2_cmd(
361 "{}/{}".format(self
._apiBase
, operationId
)
363 # print('HTTP CODE: {}'.format(http_code))
364 # print('RESP: {}'.format(resp))
367 resp
= json
.loads(resp
)
370 raise ClientException("unexpected response from server")
375 # resp = json.loads(resp)
376 # msg = resp['detail']
379 raise ClientException(msg
)
380 except ClientException
as exc
:
381 message
= "failed to get status of operation {}:\nerror:\n{}".format(
382 operationId
, str(exc
)
384 raise ClientException(message
)
393 """Executes an operation on a NS"""
394 self
._logger
.debug("")
398 self
._apiResource
= "/ns_instances"
399 self
._apiBase
= "{}{}{}".format(
400 self
._apiName
, self
._apiVersion
, self
._apiResource
402 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 http_code
, resp
= self
._http
.post_cmd(
406 endpoint
=endpoint
, postfields_dict
=op_data
408 # print('HTTP CODE: {}'.format(http_code))
409 # print('RESP: {}'.format(resp))
410 # if http_code in (200, 201, 202, 204):
412 resp
= json
.loads(resp
)
413 if not resp
or "id" not in resp
:
414 raise ClientException(
415 "unexpected response from server - {}".format(resp
)
418 # Wait for status for NS instance action
419 # For the 'action' operation, 'id' is used
420 self
._wait
(resp
.get("id"), wait
)
426 # msg = json.loads(resp)
429 # raise ClientException(msg)
430 except ClientException
as exc
:
431 message
= "failed to exec operation {}:\nerror:\n{}".format(name
, str(exc
))
432 raise ClientException(message
)
444 """Scales a VNF by adding/removing VDUs"""
445 self
._logger
.debug("")
446 self
._client
.get_token()
449 op_data
["scaleType"] = "SCALE_VNF"
450 op_data
["scaleVnfData"] = {}
451 if scale_in
and not scale_out
:
452 op_data
["scaleVnfData"]["scaleVnfType"] = "SCALE_IN"
453 elif not scale_in
and scale_out
:
454 op_data
["scaleVnfData"]["scaleVnfType"] = "SCALE_OUT"
456 raise ClientException("you must set either 'scale_in' or 'scale_out'")
457 op_data
["scaleVnfData"]["scaleByStepData"] = {
458 "member-vnf-index": vnf_name
,
459 "scaling-group-descriptor": scaling_group
,
462 op_data
["timeout_ns_scale"] = timeout
463 op_id
= self
.exec_op(ns_name
, op_name
="scale", op_data
=op_data
, wait
=wait
)
465 except ClientException
as exc
:
466 message
= "failed to scale vnf {} of ns {}:\nerror:\n{}".format(
467 vnf_name
, ns_name
, str(exc
)
469 raise ClientException(message
)
471 def create_alarm(self
, alarm
):
472 self
._logger
.debug("")
473 self
._client
.get_token()
475 data
["create_alarm_request"] = {}
476 data
["create_alarm_request"]["alarm_create_request"] = alarm
478 http_code
, resp
= self
._http
.post_cmd(
479 endpoint
="/test/message/alarm_request", postfields_dict
=data
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 print("Alarm created")
490 # msg = json.loads(resp)
493 # raise ClientException('error: code: {}, resp: {}'.format(
495 except ClientException
as exc
:
496 message
= "failed to create alarm: alarm {}\n{}".format(alarm
, str(exc
))
497 raise ClientException(message
)
499 def delete_alarm(self
, name
):
500 self
._logger
.debug("")
501 self
._client
.get_token()
503 data
["delete_alarm_request"] = {}
504 data
["delete_alarm_request"]["alarm_delete_request"] = {}
505 data
["delete_alarm_request"]["alarm_delete_request"]["alarm_uuid"] = name
507 http_code
, resp
= self
._http
.post_cmd(
508 endpoint
="/test/message/alarm_request", postfields_dict
=data
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 print("Alarm deleted")
519 # msg = json.loads(resp)
522 # raise ClientException('error: code: {}, resp: {}'.format(
524 except ClientException
as exc
:
525 message
= "failed to delete alarm: alarm {}\n{}".format(name
, str(exc
))
526 raise ClientException(message
)
528 def get_alarm(self
, project_name
=None, ns_id
=None, uuid
=None):
529 self
._client
.get_token()
531 self
._apiName
= "/nsfm"
532 self
._apiResource
= "/alarms"
533 self
._apiBase
= "{}{}{}".format(
534 self
._apiName
, self
._apiVersion
, self
._apiResource
537 # if request is for any uuid
538 http_code
, resp
= self
._http
.get2_cmd(
539 "{}/{}".format(self
._apiBase
, uuid
)
542 http_code
, resp
= self
._http
.get2_cmd(
543 "{}/{}/{}/{}".format(self
._apiBase
, uuid
, project_name
, ns_id
)
547 resp
= json
.loads(resp
)
550 raise ClientException("unexpected response from server")
553 raise ClientException(msg
)
554 except ClientException
as exc
:
555 message
= "failed to get alarm :\nerror:\n{}".format(str(exc
))
556 raise ClientException(message
)
558 def update_alarm(self
, uuid
, threshold
=None, is_enable
=None, wait
=None):
559 self
._client
.get_token()
562 op_data
["uuid"] = uuid
563 op_data
["threshold"] = threshold
564 op_data
["is_enable"] = is_enable
565 self
._apiName
= "/nsfm"
566 self
._apiResource
= "/alarms"
567 self
._apiBase
= "{}{}{}".format(
568 self
._apiName
, self
._apiVersion
, self
._apiResource
570 http_code
, resp
= self
._http
.patch_cmd(
571 endpoint
="{}".format(self
._apiBase
), postfields_dict
=op_data
574 resp
= json
.loads(resp
)
577 except ClientException
as exc
:
578 message
= "failed to update alarm :\nerror:\n{}".format(str(exc
))
579 raise ClientException(message
)
581 def export_metric(self
, metric
):
582 self
._logger
.debug("")
583 self
._client
.get_token()
585 data
["read_metric_data_request"] = metric
587 http_code
, resp
= self
._http
.post_cmd(
588 endpoint
="/test/message/metric_request", postfields_dict
=data
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 return "Metric exported"
599 # msg = json.loads(resp)
602 # raise ClientException('error: code: {}, resp: {}'.format(
604 except ClientException
as exc
:
605 message
= "failed to export metric: metric {}\n{}".format(metric
, str(exc
))
606 raise ClientException(message
)
608 def get_field(self
, ns_name
, field
):
609 self
._logger
.debug("")
610 nsr
= self
.get(ns_name
)
611 print(yaml
.safe_dump(nsr
))
613 raise NotFound("failed to retrieve ns {}".format(ns_name
))
618 raise NotFound("failed to find {} in ns {}".format(field
, ns_name
))