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, config
=None, wait
=False):
104 self
._logger
.debug("")
106 querystring_list
= []
109 ns_config
= yaml
.safe_load(config
)
110 querystring_list
+= ["{}={}".format(k
, v
) for k
, v
in ns_config
.items()]
112 querystring_list
.append('FORCE=True')
114 querystring
= "?" + "&".join(querystring_list
)
115 http_code
, resp
= self
._http
.delete_cmd('{}/{}{}'.format(self
._apiBase
,
116 ns
['_id'], querystring
))
117 # TODO change to use a POST self._http.post_cmd('{}/{}/terminate{}'.format(_apiBase, ns['_id'], querystring),
118 # postfields_dict=ns_config)
119 # seting autoremove as True by default
120 # print('HTTP CODE: {}'.format(http_code))
121 # print('RESP: {}'.format(resp))
124 resp
= json
.loads(resp
)
125 # For the 'delete' operation, '_id' is used
126 self
._wait
(resp
.get('_id'), deleteFlag
=True)
128 print('Deletion in progress')
129 elif http_code
== 204:
135 # msg = json.loads(resp)
138 raise ClientException("failed to delete ns {} - {}".format(name
, msg
))
140 def create(self
, nsd_name
, nsr_name
, account
, config
=None,
141 ssh_keys
=None, description
='default description',
142 admin_status
='ENABLED', wait
=False):
143 self
._logger
.debug("")
144 self
._client
.get_token()
145 nsd
= self
._client
.nsd
.get(nsd_name
)
150 def get_vim_account_id(vim_account
):
151 self
._logger
.debug("")
152 if vim_account_id
.get(vim_account
):
153 return vim_account_id
[vim_account
]
154 vim
= self
._client
.vim
.get(vim_account
)
156 raise NotFound("cannot find vim account '{}'".format(vim_account
))
157 vim_account_id
[vim_account
] = vim
['_id']
160 def get_wim_account_id(wim_account
):
161 self
._logger
.debug("")
162 # wim_account can be False (boolean) to indicate not use wim account
163 if not isinstance(wim_account
, str):
165 if wim_account_id
.get(wim_account
):
166 return wim_account_id
[wim_account
]
167 wim
= self
._client
.wim
.get(wim_account
)
169 raise NotFound("cannot find wim account '{}'".format(wim_account
))
170 wim_account_id
[wim_account
] = wim
['_id']
174 ns
['nsdId'] = nsd
['_id']
175 ns
['nsName'] = nsr_name
176 ns
['nsDescription'] = description
177 ns
['vimAccountId'] = get_vim_account_id(account
)
179 #ns['userdata']['key1']='value1'
180 #ns['userdata']['key2']='value2'
182 if ssh_keys
is not None:
184 for pubkeyfile
in ssh_keys
.split(','):
185 with
open(pubkeyfile
, 'r') as f
:
186 ns
['ssh_keys'].append(f
.read())
188 ns_config
= yaml
.safe_load(config
)
189 if "vim-network-name" in ns_config
:
190 ns_config
["vld"] = ns_config
.pop("vim-network-name")
191 if "vld" in ns_config
:
192 if not isinstance(ns_config
["vld"], list):
193 raise ValueError("Error at --config 'vld' must be a list of dictionaries")
194 for vld
in ns_config
["vld"]:
195 if not isinstance(vld
, dict):
196 raise ValueError("Error at --config 'vld' must be a list of dictionaries")
197 if vld
.get("vim-network-name"):
198 if isinstance(vld
["vim-network-name"], dict):
199 vim_network_name_dict
= {}
200 for vim_account
, vim_net
in vld
["vim-network-name"].items():
201 vim_network_name_dict
[get_vim_account_id(vim_account
)] = vim_net
202 vld
["vim-network-name"] = vim_network_name_dict
203 if "wim_account" in vld
and vld
["wim_account"] is not None:
204 vld
["wimAccountId"] = get_wim_account_id(vld
.pop("wim_account"))
205 if "vnf" in ns_config
:
206 for vnf
in ns_config
["vnf"]:
207 if vnf
.get("vim_account"):
208 vnf
["vimAccountId"] = get_vim_account_id(vnf
.pop("vim_account"))
210 if "additionalParamsForNs" in ns_config
:
211 if not isinstance(ns_config
["additionalParamsForNs"], dict):
212 raise ValueError("Error at --config 'additionalParamsForNs' must be a dictionary")
213 if "additionalParamsForVnf" in ns_config
:
214 if not isinstance(ns_config
["additionalParamsForVnf"], list):
215 raise ValueError("Error at --config 'additionalParamsForVnf' must be a list")
216 for additional_param_vnf
in ns_config
["additionalParamsForVnf"]:
217 if not isinstance(additional_param_vnf
, dict):
218 raise ValueError("Error at --config 'additionalParamsForVnf' items must be dictionaries")
219 if not additional_param_vnf
.get("member-vnf-index"):
220 raise ValueError("Error at --config 'additionalParamsForVnf' items must contain "
221 "'member-vnf-index'")
222 if "wim_account" in ns_config
:
223 wim_account
= ns_config
.pop("wim_account")
224 if wim_account
is not None:
225 ns
['wimAccountId'] = get_wim_account_id(wim_account
)
226 # rest of parameters without any transformation or checking
227 # "timeout_ns_deploy"
231 # print(yaml.safe_dump(ns))
233 self
._apiResource
= '/ns_instances_content'
234 self
._apiBase
= '{}{}{}'.format(self
._apiName
,
235 self
._apiVersion
, self
._apiResource
)
236 headers
= self
._client
._headers
237 headers
['Content-Type'] = 'application/yaml'
238 http_header
= ['{}: {}'.format(key
,val
)
239 for (key
,val
) in list(headers
.items())]
240 self
._http
.set_http_header(http_header
)
241 http_code
, resp
= self
._http
.post_cmd(endpoint
=self
._apiBase
,
243 # print('HTTP CODE: {}'.format(http_code))
244 # print('RESP: {}'.format(resp))
245 #if http_code in (200, 201, 202, 204):
247 resp
= json
.loads(resp
)
248 if not resp
or 'id' not in resp
:
249 raise ClientException('unexpected response from server - {} '.format(
252 # Wait for status for NS instance creation
253 self
._wait
(resp
.get('nslcmop_id'))
260 # msg = json.loads(resp)
263 # raise ClientException(msg)
264 except ClientException
as exc
:
265 message
="failed to create ns: {} nsd: {}\nerror:\n{}".format(
269 raise ClientException(message
)
271 def list_op(self
, name
, filter=None):
272 """Returns the list of operations of a NS
274 self
._logger
.debug("")
277 self
._apiResource
= '/ns_lcm_op_occs'
278 self
._apiBase
= '{}{}{}'.format(self
._apiName
,
279 self
._apiVersion
, self
._apiResource
)
282 filter_string
= '&{}'.format(filter)
283 http_code
, resp
= self
._http
.get2_cmd('{}?nsInstanceId={}'.format(
284 self
._apiBase
, ns
['_id'],
286 #print('HTTP CODE: {}'.format(http_code))
287 #print('RESP: {}'.format(resp))
290 resp
= json
.loads(resp
)
293 raise ClientException('unexpected response from server')
298 # resp = json.loads(resp)
299 # msg = resp['detail']
302 raise ClientException(msg
)
303 except ClientException
as exc
:
304 message
="failed to get operation list of NS {}:\nerror:\n{}".format(
307 raise ClientException(message
)
309 def get_op(self
, operationId
):
310 """Returns the status of an operation
312 self
._logger
.debug("")
313 self
._client
.get_token()
315 self
._apiResource
= '/ns_lcm_op_occs'
316 self
._apiBase
= '{}{}{}'.format(self
._apiName
,
317 self
._apiVersion
, self
._apiResource
)
318 http_code
, resp
= self
._http
.get2_cmd('{}/{}'.format(self
._apiBase
, operationId
))
319 #print('HTTP CODE: {}'.format(http_code))
320 #print('RESP: {}'.format(resp))
323 resp
= json
.loads(resp
)
326 raise ClientException('unexpected response from server')
331 # resp = json.loads(resp)
332 # msg = resp['detail']
335 raise ClientException(msg
)
336 except ClientException
as exc
:
337 message
="failed to get status of operation {}:\nerror:\n{}".format(
340 raise ClientException(message
)
342 def exec_op(self
, name
, op_name
, op_data
=None, wait
=False, ):
343 """Executes an operation on a NS
345 self
._logger
.debug("")
349 self
._apiResource
= '/ns_instances'
350 self
._apiBase
= '{}{}{}'.format(self
._apiName
,
351 self
._apiVersion
, self
._apiResource
)
352 endpoint
= '{}/{}/{}'.format(self
._apiBase
, ns
['_id'], op_name
)
353 #print('OP_NAME: {}'.format(op_name))
354 #print('OP_DATA: {}'.format(json.dumps(op_data)))
355 http_code
, resp
= self
._http
.post_cmd(endpoint
=endpoint
, postfields_dict
=op_data
)
356 #print('HTTP CODE: {}'.format(http_code))
357 #print('RESP: {}'.format(resp))
358 #if http_code in (200, 201, 202, 204):
360 resp
= json
.loads(resp
)
361 if not resp
or 'id' not in resp
:
362 raise ClientException('unexpected response from server - {}'.format(
365 # Wait for status for NS instance action
366 # For the 'action' operation, 'id' is used
367 self
._wait
(resp
.get('id'))
373 # msg = json.loads(resp)
376 # raise ClientException(msg)
377 except ClientException
as exc
:
378 message
="failed to exec operation {}:\nerror:\n{}".format(
381 raise ClientException(message
)
383 def scale_vnf(self
, ns_name
, vnf_name
, scaling_group
, scale_in
, scale_out
, wait
=False):
384 """Scales a VNF by adding/removing VDUs
386 self
._logger
.debug("")
387 self
._client
.get_token()
390 op_data
["scaleType"] = "SCALE_VNF"
391 op_data
["scaleVnfData"] = {}
393 op_data
["scaleVnfData"]["scaleVnfType"] = "SCALE_IN"
395 op_data
["scaleVnfData"]["scaleVnfType"] = "SCALE_OUT"
396 op_data
["scaleVnfData"]["scaleByStepData"] = {
397 "member-vnf-index": vnf_name
,
398 "scaling-group-descriptor": scaling_group
,
400 op_id
= self
.exec_op(ns_name
, op_name
='scale', op_data
=op_data
, wait
=wait
)
402 except ClientException
as exc
:
403 message
="failed to scale vnf {} of ns {}:\nerror:\n{}".format(
404 vnf_name
, ns_name
, str(exc
))
405 raise ClientException(message
)
407 def create_alarm(self
, alarm
):
408 self
._logger
.debug("")
409 self
._client
.get_token()
411 data
["create_alarm_request"] = {}
412 data
["create_alarm_request"]["alarm_create_request"] = alarm
414 http_code
, resp
= self
._http
.post_cmd(endpoint
='/test/message/alarm_request',
415 postfields_dict
=data
)
416 #print('HTTP CODE: {}'.format(http_code))
417 #print('RESP: {}'.format(resp))
418 # if http_code in (200, 201, 202, 204):
419 # resp = json.loads(resp)
420 print('Alarm created')
425 # msg = json.loads(resp)
428 # raise ClientException('error: code: {}, resp: {}'.format(
430 except ClientException
as exc
:
431 message
="failed to create alarm: alarm {}\n{}".format(
434 raise ClientException(message
)
436 def delete_alarm(self
, name
):
437 self
._logger
.debug("")
438 self
._client
.get_token()
440 data
["delete_alarm_request"] = {}
441 data
["delete_alarm_request"]["alarm_delete_request"] = {}
442 data
["delete_alarm_request"]["alarm_delete_request"]["alarm_uuid"] = name
444 http_code
, resp
= self
._http
.post_cmd(endpoint
='/test/message/alarm_request',
445 postfields_dict
=data
)
446 #print('HTTP CODE: {}'.format(http_code))
447 #print('RESP: {}'.format(resp))
448 # if http_code in (200, 201, 202, 204):
449 # resp = json.loads(resp)
450 print('Alarm deleted')
455 # msg = json.loads(resp)
458 # raise ClientException('error: code: {}, resp: {}'.format(
460 except ClientException
as exc
:
461 message
="failed to delete alarm: alarm {}\n{}".format(
464 raise ClientException(message
)
466 def export_metric(self
, metric
):
467 self
._logger
.debug("")
468 self
._client
.get_token()
470 data
["read_metric_data_request"] = metric
472 http_code
, resp
= self
._http
.post_cmd(endpoint
='/test/message/metric_request',
473 postfields_dict
=data
)
474 #print('HTTP CODE: {}'.format(http_code))
475 #print('RESP: {}'.format(resp))
476 # if http_code in (200, 201, 202, 204):
477 # resp = json.loads(resp)
478 return 'Metric exported'
483 # msg = json.loads(resp)
486 # raise ClientException('error: code: {}, resp: {}'.format(
488 except ClientException
as exc
:
489 message
="failed to export metric: metric {}\n{}".format(
492 raise ClientException(message
)
494 def get_field(self
, ns_name
, field
):
495 self
._logger
.debug("")
496 nsr
= self
.get(ns_name
)
497 print(yaml
.safe_dump(nsr
))
499 raise NotFound("failed to retrieve ns {}".format(ns_name
))
504 raise NotFound("failed to find {} in ns {}".format(field
, ns_name
))