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"]
198 ns
["nsdId"] = nsd
["_id"]
199 ns
["nsName"] = nsr_name
200 ns
["nsDescription"] = description
201 ns
["vimAccountId"] = get_vim_account_id(account
)
202 # ns['userdata'] = {}
203 # ns['userdata']['key1']='value1'
204 # ns['userdata']['key2']='value2'
206 if ssh_keys
is not None:
208 for pubkeyfile
in ssh_keys
.split(","):
209 with
open(pubkeyfile
, "r") as f
:
210 ns
["ssh_keys"].append(f
.read())
212 ns_config
= yaml
.safe_load(config
)
213 if "vim-network-name" in ns_config
:
214 ns_config
["vld"] = ns_config
.pop("vim-network-name")
215 if "vld" in ns_config
:
216 if not isinstance(ns_config
["vld"], list):
217 raise ClientException(
218 "Error at --config 'vld' must be a list of dictionaries"
220 for vld
in ns_config
["vld"]:
221 if not isinstance(vld
, dict):
222 raise ClientException(
223 "Error at --config 'vld' must be a list of dictionaries"
225 if vld
.get("vim-network-name"):
226 if isinstance(vld
["vim-network-name"], dict):
227 vim_network_name_dict
= {}
228 for vim_account
, vim_net
in vld
["vim-network-name"].items():
229 vim_network_name_dict
[
230 get_vim_account_id(vim_account
)
232 vld
["vim-network-name"] = vim_network_name_dict
233 if "wim_account" in vld
and vld
["wim_account"] is not None:
234 vld
["wimAccountId"] = get_wim_account_id(vld
.pop("wim_account"))
235 if "vnf" in ns_config
:
236 for vnf
in ns_config
["vnf"]:
237 if vnf
.get("vim_account"):
238 vnf
["vimAccountId"] = get_vim_account_id(vnf
.pop("vim_account"))
240 if "additionalParamsForNs" in ns_config
:
241 if not isinstance(ns_config
["additionalParamsForNs"], dict):
242 raise ClientException(
243 "Error at --config 'additionalParamsForNs' must be a dictionary"
245 if "additionalParamsForVnf" in ns_config
:
246 if not isinstance(ns_config
["additionalParamsForVnf"], list):
247 raise ClientException(
248 "Error at --config 'additionalParamsForVnf' must be a list"
250 for additional_param_vnf
in ns_config
["additionalParamsForVnf"]:
251 if not isinstance(additional_param_vnf
, dict):
252 raise ClientException(
253 "Error at --config 'additionalParamsForVnf' items must be dictionaries"
255 if not additional_param_vnf
.get("member-vnf-index"):
256 raise ClientException(
257 "Error at --config 'additionalParamsForVnf' items must contain "
260 if "wim_account" in ns_config
:
261 wim_account
= ns_config
.pop("wim_account")
262 if wim_account
is not None:
263 ns
["wimAccountId"] = get_wim_account_id(wim_account
)
264 # rest of parameters without any transformation or checking
265 # "timeout_ns_deploy"
269 # print(yaml.safe_dump(ns))
271 self
._apiResource
= "/ns_instances_content"
272 self
._apiBase
= "{}{}{}".format(
273 self
._apiName
, self
._apiVersion
, self
._apiResource
275 headers
= self
._client
._headers
276 headers
["Content-Type"] = "application/yaml"
278 "{}: {}".format(key
, val
) for (key
, val
) in list(headers
.items())
280 self
._http
.set_http_header(http_header
)
281 http_code
, resp
= self
._http
.post_cmd(
282 endpoint
=self
._apiBase
, postfields_dict
=ns
284 # print('HTTP CODE: {}'.format(http_code))
285 # print('RESP: {}'.format(resp))
286 # if http_code in (200, 201, 202, 204):
288 resp
= json
.loads(resp
)
289 if not resp
or "id" not in resp
:
290 raise ClientException(
291 "unexpected response from server - {} ".format(resp
)
294 # Wait for status for NS instance creation
295 self
._wait
(resp
.get("nslcmop_id"), wait
)
302 # msg = json.loads(resp)
305 # raise ClientException(msg)
306 except ClientException
as exc
:
307 message
= "failed to create ns: {} nsd: {}\nerror:\n{}".format(
308 nsr_name
, nsd_name
, str(exc
)
310 raise ClientException(message
)
312 def list_op(self
, name
, filter=None):
313 """Returns the list of operations of a NS"""
314 self
._logger
.debug("")
317 self
._apiResource
= "/ns_lcm_op_occs"
318 self
._apiBase
= "{}{}{}".format(
319 self
._apiName
, self
._apiVersion
, self
._apiResource
323 filter_string
= "&{}".format(filter)
324 http_code
, resp
= self
._http
.get2_cmd(
325 "{}?nsInstanceId={}{}".format(self
._apiBase
, ns
["_id"], filter_string
)
327 # print('HTTP CODE: {}'.format(http_code))
328 # print('RESP: {}'.format(resp))
331 resp
= json
.loads(resp
)
334 raise ClientException("unexpected response from server")
339 # resp = json.loads(resp)
340 # msg = resp['detail']
343 raise ClientException(msg
)
344 except ClientException
as exc
:
345 message
= "failed to get operation list of NS {}:\nerror:\n{}".format(
348 raise ClientException(message
)
350 def get_op(self
, operationId
):
351 """Returns the status of an operation"""
352 self
._logger
.debug("")
353 self
._client
.get_token()
355 self
._apiResource
= "/ns_lcm_op_occs"
356 self
._apiBase
= "{}{}{}".format(
357 self
._apiName
, self
._apiVersion
, self
._apiResource
359 http_code
, resp
= self
._http
.get2_cmd(
360 "{}/{}".format(self
._apiBase
, operationId
)
362 # print('HTTP CODE: {}'.format(http_code))
363 # print('RESP: {}'.format(resp))
366 resp
= json
.loads(resp
)
369 raise ClientException("unexpected response from server")
374 # resp = json.loads(resp)
375 # msg = resp['detail']
378 raise ClientException(msg
)
379 except ClientException
as exc
:
380 message
= "failed to get status of operation {}:\nerror:\n{}".format(
381 operationId
, str(exc
)
383 raise ClientException(message
)
392 """Executes an operation on a NS"""
393 self
._logger
.debug("")
397 self
._apiResource
= "/ns_instances"
398 self
._apiBase
= "{}{}{}".format(
399 self
._apiName
, self
._apiVersion
, self
._apiResource
401 endpoint
= "{}/{}/{}".format(self
._apiBase
, ns
["_id"], op_name
)
402 # print('OP_NAME: {}'.format(op_name))
403 # print('OP_DATA: {}'.format(json.dumps(op_data)))
404 http_code
, resp
= self
._http
.post_cmd(
405 endpoint
=endpoint
, postfields_dict
=op_data
407 # print('HTTP CODE: {}'.format(http_code))
408 # print('RESP: {}'.format(resp))
409 # if http_code in (200, 201, 202, 204):
411 resp
= json
.loads(resp
)
412 if not resp
or "id" not in resp
:
413 raise ClientException(
414 "unexpected response from server - {}".format(resp
)
417 # Wait for status for NS instance action
418 # For the 'action' operation, 'id' is used
419 self
._wait
(resp
.get("id"), wait
)
425 # msg = json.loads(resp)
428 # raise ClientException(msg)
429 except ClientException
as exc
:
430 message
= "failed to exec operation {}:\nerror:\n{}".format(name
, str(exc
))
431 raise ClientException(message
)
443 """Scales a VNF by adding/removing VDUs"""
444 self
._logger
.debug("")
445 self
._client
.get_token()
448 op_data
["scaleType"] = "SCALE_VNF"
449 op_data
["scaleVnfData"] = {}
450 if scale_in
and not scale_out
:
451 op_data
["scaleVnfData"]["scaleVnfType"] = "SCALE_IN"
452 elif not scale_in
and scale_out
:
453 op_data
["scaleVnfData"]["scaleVnfType"] = "SCALE_OUT"
455 raise ClientException("you must set either 'scale_in' or 'scale_out'")
456 op_data
["scaleVnfData"]["scaleByStepData"] = {
457 "member-vnf-index": vnf_name
,
458 "scaling-group-descriptor": scaling_group
,
461 op_data
["timeout_ns_scale"] = timeout
462 op_id
= self
.exec_op(ns_name
, op_name
="scale", op_data
=op_data
, wait
=wait
)
464 except ClientException
as exc
:
465 message
= "failed to scale vnf {} of ns {}:\nerror:\n{}".format(
466 vnf_name
, ns_name
, str(exc
)
468 raise ClientException(message
)
470 def create_alarm(self
, alarm
):
471 self
._logger
.debug("")
472 self
._client
.get_token()
474 data
["create_alarm_request"] = {}
475 data
["create_alarm_request"]["alarm_create_request"] = alarm
477 http_code
, resp
= self
._http
.post_cmd(
478 endpoint
="/test/message/alarm_request", postfields_dict
=data
480 # print('HTTP CODE: {}'.format(http_code))
481 # print('RESP: {}'.format(resp))
482 # if http_code in (200, 201, 202, 204):
483 # resp = json.loads(resp)
484 print("Alarm created")
489 # msg = json.loads(resp)
492 # raise ClientException('error: code: {}, resp: {}'.format(
494 except ClientException
as exc
:
495 message
= "failed to create alarm: alarm {}\n{}".format(alarm
, str(exc
))
496 raise ClientException(message
)
498 def delete_alarm(self
, name
):
499 self
._logger
.debug("")
500 self
._client
.get_token()
502 data
["delete_alarm_request"] = {}
503 data
["delete_alarm_request"]["alarm_delete_request"] = {}
504 data
["delete_alarm_request"]["alarm_delete_request"]["alarm_uuid"] = name
506 http_code
, resp
= self
._http
.post_cmd(
507 endpoint
="/test/message/alarm_request", postfields_dict
=data
509 # print('HTTP CODE: {}'.format(http_code))
510 # print('RESP: {}'.format(resp))
511 # if http_code in (200, 201, 202, 204):
512 # resp = json.loads(resp)
513 print("Alarm deleted")
518 # msg = json.loads(resp)
521 # raise ClientException('error: code: {}, resp: {}'.format(
523 except ClientException
as exc
:
524 message
= "failed to delete alarm: alarm {}\n{}".format(name
, str(exc
))
525 raise ClientException(message
)
527 def export_metric(self
, metric
):
528 self
._logger
.debug("")
529 self
._client
.get_token()
531 data
["read_metric_data_request"] = metric
533 http_code
, resp
= self
._http
.post_cmd(
534 endpoint
="/test/message/metric_request", postfields_dict
=data
536 # print('HTTP CODE: {}'.format(http_code))
537 # print('RESP: {}'.format(resp))
538 # if http_code in (200, 201, 202, 204):
539 # resp = json.loads(resp)
540 return "Metric exported"
545 # msg = json.loads(resp)
548 # raise ClientException('error: code: {}, resp: {}'.format(
550 except ClientException
as exc
:
551 message
= "failed to export metric: metric {}\n{}".format(metric
, str(exc
))
552 raise ClientException(message
)
554 def get_field(self
, ns_name
, field
):
555 self
._logger
.debug("")
556 nsr
= self
.get(ns_name
)
557 print(yaml
.safe_dump(nsr
))
559 raise NotFound("failed to retrieve ns {}".format(ns_name
))
564 raise NotFound("failed to find {} in ns {}".format(field
, ns_name
))