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
32 def __init__(self
, http
=None, client
=None):
35 self
._logger
= logging
.getLogger('osmclient')
36 self
._apiName
= '/nslcm'
37 self
._apiVersion
= '/v1'
38 self
._apiResource
= '/ns_instances_content'
39 self
._apiBase
= '{}{}{}'.format(self
._apiName
,
40 self
._apiVersion
, self
._apiResource
)
43 def _wait(self
, id, deleteFlag
=False):
44 self
._logger
.debug("")
45 # Endpoint to get operation status
46 apiUrlStatus
= '{}{}{}'.format(self
._apiName
, self
._apiVersion
, '/ns_lcm_op_occs')
47 # Wait for status for NS instance creation/update/deletion
48 WaitForStatus
.wait_for_status(
51 WaitForStatus
.TIMEOUT_NS_OPERATION
,
54 deleteFlag
=deleteFlag
)
56 def list(self
, filter=None):
57 """Returns a list of NS
59 self
._logger
.debug("")
60 self
._client
.get_token()
63 filter_string
= '?{}'.format(filter)
64 _
, resp
= self
._http
.get2_cmd('{}{}'.format(self
._apiBase
,filter_string
))
66 return json
.loads(resp
)
70 """Returns an NS based on name or id
72 self
._logger
.debug("")
73 self
._client
.get_token()
74 if utils
.validate_uuid4(name
):
75 for ns
in self
.list():
79 for ns
in self
.list():
80 if name
== ns
['name']:
82 raise NotFound("ns '{}' not found".format(name
))
84 def get_individual(self
, name
):
85 self
._logger
.debug("")
86 self
._client
.get_token()
88 if not utils
.validate_uuid4(name
):
89 for ns
in self
.list():
90 if name
== ns
['name']:
94 _
, resp
= self
._http
.get2_cmd('{}/{}'.format(self
._apiBase
, ns_id
))
95 #resp = self._http.get_cmd('{}/{}/nsd_content'.format(self._apiBase, ns_id))
96 #print(yaml.safe_dump(resp))
98 return json
.loads(resp
)
100 raise NotFound("ns '{}' not found".format(name
))
101 raise NotFound("ns '{}' not found".format(name
))
103 def delete(self
, name
, force
=False, wait
=False):
104 self
._logger
.debug("")
108 querystring
= '?FORCE=True'
109 http_code
, resp
= self
._http
.delete_cmd('{}/{}{}'.format(self
._apiBase
,
110 ns
['_id'], querystring
))
111 # print('HTTP CODE: {}'.format(http_code))
112 # print('RESP: {}'.format(resp))
115 resp
= json
.loads(resp
)
116 # For the 'delete' operation, '_id' is used
117 self
._wait
(resp
.get('_id'), deleteFlag
=True)
119 print('Deletion in progress')
120 elif http_code
== 204:
126 # msg = json.loads(resp)
129 raise ClientException("failed to delete ns {} - {}".format(name
, msg
))
131 def create(self
, nsd_name
, nsr_name
, account
, config
=None,
132 ssh_keys
=None, description
='default description',
133 admin_status
='ENABLED', wait
=False):
134 self
._logger
.debug("")
135 self
._client
.get_token()
136 nsd
= self
._client
.nsd
.get(nsd_name
)
141 def get_vim_account_id(vim_account
):
142 self
._logger
.debug("")
143 if vim_account_id
.get(vim_account
):
144 return vim_account_id
[vim_account
]
145 vim
= self
._client
.vim
.get(vim_account
)
147 raise NotFound("cannot find vim account '{}'".format(vim_account
))
148 vim_account_id
[vim_account
] = vim
['_id']
151 def get_wim_account_id(wim_account
):
152 self
._logger
.debug("")
153 # wim_account can be False (boolean) to indicate not use wim account
154 if not isinstance(wim_account
, str):
156 if wim_account_id
.get(wim_account
):
157 return wim_account_id
[wim_account
]
158 wim
= self
._client
.wim
.get(wim_account
)
160 raise NotFound("cannot find wim account '{}'".format(wim_account
))
161 wim_account_id
[wim_account
] = wim
['_id']
165 ns
['nsdId'] = nsd
['_id']
166 ns
['nsName'] = nsr_name
167 ns
['nsDescription'] = description
168 ns
['vimAccountId'] = get_vim_account_id(account
)
170 #ns['userdata']['key1']='value1'
171 #ns['userdata']['key2']='value2'
173 if ssh_keys
is not None:
175 for pubkeyfile
in ssh_keys
.split(','):
176 with
open(pubkeyfile
, 'r') as f
:
177 ns
['ssh_keys'].append(f
.read())
179 ns_config
= yaml
.safe_load(config
)
180 if "vim-network-name" in ns_config
:
181 ns_config
["vld"] = ns_config
.pop("vim-network-name")
182 if "vld" in ns_config
:
183 if not isinstance(ns_config
["vld"], list):
184 raise ValueError("Error at --config 'vld' must be a list of dictionaries")
185 for vld
in ns_config
["vld"]:
186 if not isinstance(vld
, dict):
187 raise ValueError("Error at --config 'vld' must be a list of dictionaries")
188 if vld
.get("vim-network-name"):
189 if isinstance(vld
["vim-network-name"], dict):
190 vim_network_name_dict
= {}
191 for vim_account
, vim_net
in vld
["vim-network-name"].items():
192 vim_network_name_dict
[get_vim_account_id(vim_account
)] = vim_net
193 vld
["vim-network-name"] = vim_network_name_dict
194 if "wim_account" in vld
and vld
["wim_account"] is not None:
195 vld
["wimAccountId"] = get_wim_account_id(vld
.pop("wim_account"))
196 if "vnf" in ns_config
:
197 for vnf
in ns_config
["vnf"]:
198 if vnf
.get("vim_account"):
199 vnf
["vimAccountId"] = get_vim_account_id(vnf
.pop("vim_account"))
201 if "additionalParamsForNs" in ns_config
:
202 if not isinstance(ns_config
["additionalParamsForNs"], dict):
203 raise ValueError("Error at --config 'additionalParamsForNs' must be a dictionary")
204 if "additionalParamsForVnf" in ns_config
:
205 if not isinstance(ns_config
["additionalParamsForVnf"], list):
206 raise ValueError("Error at --config 'additionalParamsForVnf' must be a list")
207 for additional_param_vnf
in ns_config
["additionalParamsForVnf"]:
208 if not isinstance(additional_param_vnf
, dict):
209 raise ValueError("Error at --config 'additionalParamsForVnf' items must be dictionaries")
210 if not additional_param_vnf
.get("member-vnf-index"):
211 raise ValueError("Error at --config 'additionalParamsForVnf' items must contain "
212 "'member-vnf-index'")
213 if "wim_account" in ns_config
:
214 wim_account
= ns_config
.pop("wim_account")
215 if wim_account
is not None:
216 ns
['wimAccountId'] = get_wim_account_id(wim_account
)
217 # rest of parameters without any transformation or checking
218 # "timeout_ns_deploy"
222 # print(yaml.safe_dump(ns))
224 self
._apiResource
= '/ns_instances_content'
225 self
._apiBase
= '{}{}{}'.format(self
._apiName
,
226 self
._apiVersion
, self
._apiResource
)
227 headers
= self
._client
._headers
228 headers
['Content-Type'] = 'application/yaml'
229 http_header
= ['{}: {}'.format(key
,val
)
230 for (key
,val
) in list(headers
.items())]
231 self
._http
.set_http_header(http_header
)
232 http_code
, resp
= self
._http
.post_cmd(endpoint
=self
._apiBase
,
234 # print('HTTP CODE: {}'.format(http_code))
235 # print('RESP: {}'.format(resp))
236 #if http_code in (200, 201, 202, 204):
238 resp
= json
.loads(resp
)
239 if not resp
or 'id' not in resp
:
240 raise ClientException('unexpected response from server - {} '.format(
243 # Wait for status for NS instance creation
244 self
._wait
(resp
.get('nslcmop_id'))
251 # msg = json.loads(resp)
254 # raise ClientException(msg)
255 except ClientException
as exc
:
256 message
="failed to create ns: {} nsd: {}\nerror:\n{}".format(
260 raise ClientException(message
)
262 def list_op(self
, name
, filter=None):
263 """Returns the list of operations of a NS
265 self
._logger
.debug("")
268 self
._apiResource
= '/ns_lcm_op_occs'
269 self
._apiBase
= '{}{}{}'.format(self
._apiName
,
270 self
._apiVersion
, self
._apiResource
)
273 filter_string
= '&{}'.format(filter)
274 http_code
, resp
= self
._http
.get2_cmd('{}?nsInstanceId={}'.format(
275 self
._apiBase
, ns
['_id'],
277 #print('HTTP CODE: {}'.format(http_code))
278 #print('RESP: {}'.format(resp))
281 resp
= json
.loads(resp
)
284 raise ClientException('unexpected response from server')
289 # resp = json.loads(resp)
290 # msg = resp['detail']
293 raise ClientException(msg
)
294 except ClientException
as exc
:
295 message
="failed to get operation list of NS {}:\nerror:\n{}".format(
298 raise ClientException(message
)
300 def get_op(self
, operationId
):
301 """Returns the status of an operation
303 self
._logger
.debug("")
304 self
._client
.get_token()
306 self
._apiResource
= '/ns_lcm_op_occs'
307 self
._apiBase
= '{}{}{}'.format(self
._apiName
,
308 self
._apiVersion
, self
._apiResource
)
309 http_code
, resp
= self
._http
.get2_cmd('{}/{}'.format(self
._apiBase
, operationId
))
310 #print('HTTP CODE: {}'.format(http_code))
311 #print('RESP: {}'.format(resp))
314 resp
= json
.loads(resp
)
317 raise ClientException('unexpected response from server')
322 # resp = json.loads(resp)
323 # msg = resp['detail']
326 raise ClientException(msg
)
327 except ClientException
as exc
:
328 message
="failed to get status of operation {}:\nerror:\n{}".format(
331 raise ClientException(message
)
333 def exec_op(self
, name
, op_name
, op_data
=None, wait
=False, ):
334 """Executes an operation on a NS
336 self
._logger
.debug("")
340 self
._apiResource
= '/ns_instances'
341 self
._apiBase
= '{}{}{}'.format(self
._apiName
,
342 self
._apiVersion
, self
._apiResource
)
343 endpoint
= '{}/{}/{}'.format(self
._apiBase
, ns
['_id'], op_name
)
344 #print('OP_NAME: {}'.format(op_name))
345 #print('OP_DATA: {}'.format(json.dumps(op_data)))
346 http_code
, resp
= self
._http
.post_cmd(endpoint
=endpoint
, postfields_dict
=op_data
)
347 #print('HTTP CODE: {}'.format(http_code))
348 #print('RESP: {}'.format(resp))
349 #if http_code in (200, 201, 202, 204):
351 resp
= json
.loads(resp
)
352 if not resp
or 'id' not in resp
:
353 raise ClientException('unexpected response from server - {}'.format(
356 # Wait for status for NS instance action
357 # For the 'action' operation, 'id' is used
358 self
._wait
(resp
.get('id'))
364 # msg = json.loads(resp)
367 # raise ClientException(msg)
368 except ClientException
as exc
:
369 message
="failed to exec operation {}:\nerror:\n{}".format(
372 raise ClientException(message
)
374 def scale_vnf(self
, ns_name
, vnf_name
, scaling_group
, scale_in
, scale_out
, wait
=False):
375 """Scales a VNF by adding/removing VDUs
377 self
._logger
.debug("")
378 self
._client
.get_token()
381 op_data
["scaleType"] = "SCALE_VNF"
382 op_data
["scaleVnfData"] = {}
384 op_data
["scaleVnfData"]["scaleVnfType"] = "SCALE_IN"
386 op_data
["scaleVnfData"]["scaleVnfType"] = "SCALE_OUT"
387 op_data
["scaleVnfData"]["scaleByStepData"] = {
388 "member-vnf-index": vnf_name
,
389 "scaling-group-descriptor": scaling_group
,
391 op_id
= self
.exec_op(ns_name
, op_name
='scale', op_data
=op_data
, wait
=wait
)
393 except ClientException
as exc
:
394 message
="failed to scale vnf {} of ns {}:\nerror:\n{}".format(
395 vnf_name
, ns_name
, str(exc
))
396 raise ClientException(message
)
398 def create_alarm(self
, alarm
):
399 self
._logger
.debug("")
400 self
._client
.get_token()
402 data
["create_alarm_request"] = {}
403 data
["create_alarm_request"]["alarm_create_request"] = alarm
405 http_code
, resp
= self
._http
.post_cmd(endpoint
='/test/message/alarm_request',
406 postfields_dict
=data
)
407 #print('HTTP CODE: {}'.format(http_code))
408 #print('RESP: {}'.format(resp))
409 # if http_code in (200, 201, 202, 204):
410 # resp = json.loads(resp)
411 print('Alarm created')
416 # msg = json.loads(resp)
419 # raise ClientException('error: code: {}, resp: {}'.format(
421 except ClientException
as exc
:
422 message
="failed to create alarm: alarm {}\n{}".format(
425 raise ClientException(message
)
427 def delete_alarm(self
, name
):
428 self
._logger
.debug("")
429 self
._client
.get_token()
431 data
["delete_alarm_request"] = {}
432 data
["delete_alarm_request"]["alarm_delete_request"] = {}
433 data
["delete_alarm_request"]["alarm_delete_request"]["alarm_uuid"] = name
435 http_code
, resp
= self
._http
.post_cmd(endpoint
='/test/message/alarm_request',
436 postfields_dict
=data
)
437 #print('HTTP CODE: {}'.format(http_code))
438 #print('RESP: {}'.format(resp))
439 # if http_code in (200, 201, 202, 204):
440 # resp = json.loads(resp)
441 print('Alarm deleted')
446 # msg = json.loads(resp)
449 # raise ClientException('error: code: {}, resp: {}'.format(
451 except ClientException
as exc
:
452 message
="failed to delete alarm: alarm {}\n{}".format(
455 raise ClientException(message
)
457 def export_metric(self
, metric
):
458 self
._logger
.debug("")
459 self
._client
.get_token()
461 data
["read_metric_data_request"] = metric
463 http_code
, resp
= self
._http
.post_cmd(endpoint
='/test/message/metric_request',
464 postfields_dict
=data
)
465 #print('HTTP CODE: {}'.format(http_code))
466 #print('RESP: {}'.format(resp))
467 # if http_code in (200, 201, 202, 204):
468 # resp = json.loads(resp)
469 return 'Metric exported'
474 # msg = json.loads(resp)
477 # raise ClientException('error: code: {}, resp: {}'.format(
479 except ClientException
as exc
:
480 message
="failed to export metric: metric {}\n{}".format(
483 raise ClientException(message
)
485 def get_field(self
, ns_name
, field
):
486 self
._logger
.debug("")
487 nsr
= self
.get(ns_name
)
488 print(yaml
.safe_dump(nsr
))
490 raise NotFound("failed to retrieve ns {}".format(ns_name
))
495 raise NotFound("failed to find {} in ns {}".format(field
, ns_name
))