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 for vld
in ns_config
["vld"]:
184 if vld
.get("vim-network-name"):
185 if isinstance(vld
["vim-network-name"], dict):
186 vim_network_name_dict
= {}
187 for vim_account
, vim_net
in list(vld
["vim-network-name"].items()):
188 vim_network_name_dict
[get_vim_account_id(vim_account
)] = vim_net
189 vld
["vim-network-name"] = vim_network_name_dict
190 if "wim_account" in vld
and vld
["wim_account"] is not None:
191 vld
["wimAccountId"] = get_wim_account_id(vld
.pop("wim_account"))
192 ns
["vld"] = ns_config
["vld"]
193 if "vnf" in ns_config
:
194 for vnf
in ns_config
["vnf"]:
195 if vnf
.get("vim_account"):
196 vnf
["vimAccountId"] = get_vim_account_id(vnf
.pop("vim_account"))
197 ns
["vnf"] = ns_config
["vnf"]
199 if "additionalParamsForNs" in ns_config
:
200 ns
["additionalParamsForNs"] = ns_config
.pop("additionalParamsForNs")
201 if not isinstance(ns
["additionalParamsForNs"], dict):
202 raise ValueError("Error at --config 'additionalParamsForNs' must be a dictionary")
203 if "additionalParamsForVnf" in ns_config
:
204 ns
["additionalParamsForVnf"] = ns_config
.pop("additionalParamsForVnf")
205 if not isinstance(ns
["additionalParamsForVnf"], list):
206 raise ValueError("Error at --config 'additionalParamsForVnf' must be a list")
207 for additional_param_vnf
in ns
["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 if "timeout_ns_deploy" in ns_config
:
218 ns
["timeout_ns_deploy"] = ns_config
.pop("timeout_ns_deploy")
220 # print(yaml.safe_dump(ns))
222 self
._apiResource
= '/ns_instances_content'
223 self
._apiBase
= '{}{}{}'.format(self
._apiName
,
224 self
._apiVersion
, self
._apiResource
)
225 headers
= self
._client
._headers
226 headers
['Content-Type'] = 'application/yaml'
227 http_header
= ['{}: {}'.format(key
,val
)
228 for (key
,val
) in list(headers
.items())]
229 self
._http
.set_http_header(http_header
)
230 http_code
, resp
= self
._http
.post_cmd(endpoint
=self
._apiBase
,
232 # print('HTTP CODE: {}'.format(http_code))
233 # print('RESP: {}'.format(resp))
234 #if http_code in (200, 201, 202, 204):
236 resp
= json
.loads(resp
)
237 if not resp
or 'id' not in resp
:
238 raise ClientException('unexpected response from server - {} '.format(
241 # Wait for status for NS instance creation
242 self
._wait
(resp
.get('nslcmop_id'))
249 # msg = json.loads(resp)
252 # raise ClientException(msg)
253 except ClientException
as exc
:
254 message
="failed to create ns: {} nsd: {}\nerror:\n{}".format(
258 raise ClientException(message
)
260 def list_op(self
, name
, filter=None):
261 """Returns the list of operations of a NS
263 self
._logger
.debug("")
266 self
._apiResource
= '/ns_lcm_op_occs'
267 self
._apiBase
= '{}{}{}'.format(self
._apiName
,
268 self
._apiVersion
, self
._apiResource
)
271 filter_string
= '&{}'.format(filter)
272 http_code
, resp
= self
._http
.get2_cmd('{}?nsInstanceId={}'.format(
273 self
._apiBase
, ns
['_id'],
275 #print('HTTP CODE: {}'.format(http_code))
276 #print('RESP: {}'.format(resp))
279 resp
= json
.loads(resp
)
282 raise ClientException('unexpected response from server')
287 # resp = json.loads(resp)
288 # msg = resp['detail']
291 raise ClientException(msg
)
292 except ClientException
as exc
:
293 message
="failed to get operation list of NS {}:\nerror:\n{}".format(
296 raise ClientException(message
)
298 def get_op(self
, operationId
):
299 """Returns the status of an operation
301 self
._logger
.debug("")
302 self
._client
.get_token()
304 self
._apiResource
= '/ns_lcm_op_occs'
305 self
._apiBase
= '{}{}{}'.format(self
._apiName
,
306 self
._apiVersion
, self
._apiResource
)
307 http_code
, resp
= self
._http
.get2_cmd('{}/{}'.format(self
._apiBase
, operationId
))
308 #print('HTTP CODE: {}'.format(http_code))
309 #print('RESP: {}'.format(resp))
312 resp
= json
.loads(resp
)
315 raise ClientException('unexpected response from server')
320 # resp = json.loads(resp)
321 # msg = resp['detail']
324 raise ClientException(msg
)
325 except ClientException
as exc
:
326 message
="failed to get status of operation {}:\nerror:\n{}".format(
329 raise ClientException(message
)
331 def exec_op(self
, name
, op_name
, op_data
=None, wait
=False, ):
332 """Executes an operation on a NS
334 self
._logger
.debug("")
338 self
._apiResource
= '/ns_instances'
339 self
._apiBase
= '{}{}{}'.format(self
._apiName
,
340 self
._apiVersion
, self
._apiResource
)
341 endpoint
= '{}/{}/{}'.format(self
._apiBase
, ns
['_id'], op_name
)
342 #print('OP_NAME: {}'.format(op_name))
343 #print('OP_DATA: {}'.format(json.dumps(op_data)))
344 http_code
, resp
= self
._http
.post_cmd(endpoint
=endpoint
, postfields_dict
=op_data
)
345 #print('HTTP CODE: {}'.format(http_code))
346 #print('RESP: {}'.format(resp))
347 #if http_code in (200, 201, 202, 204):
349 resp
= json
.loads(resp
)
350 if not resp
or 'id' not in resp
:
351 raise ClientException('unexpected response from server - {}'.format(
354 # Wait for status for NS instance action
355 # For the 'action' operation, 'id' is used
356 self
._wait
(resp
.get('id'))
362 # msg = json.loads(resp)
365 # raise ClientException(msg)
366 except ClientException
as exc
:
367 message
="failed to exec operation {}:\nerror:\n{}".format(
370 raise ClientException(message
)
372 def scale_vnf(self
, ns_name
, vnf_name
, scaling_group
, scale_in
, scale_out
, wait
=False):
373 """Scales a VNF by adding/removing VDUs
375 self
._logger
.debug("")
376 self
._client
.get_token()
379 op_data
["scaleType"] = "SCALE_VNF"
380 op_data
["scaleVnfData"] = {}
382 op_data
["scaleVnfData"]["scaleVnfType"] = "SCALE_IN"
384 op_data
["scaleVnfData"]["scaleVnfType"] = "SCALE_OUT"
385 op_data
["scaleVnfData"]["scaleByStepData"] = {
386 "member-vnf-index": vnf_name
,
387 "scaling-group-descriptor": scaling_group
,
389 op_id
= self
.exec_op(ns_name
, op_name
='scale', op_data
=op_data
, wait
=wait
)
391 except ClientException
as exc
:
392 message
="failed to scale vnf {} of ns {}:\nerror:\n{}".format(
393 vnf_name
, ns_name
, str(exc
))
394 raise ClientException(message
)
396 def create_alarm(self
, alarm
):
397 self
._logger
.debug("")
398 self
._client
.get_token()
400 data
["create_alarm_request"] = {}
401 data
["create_alarm_request"]["alarm_create_request"] = alarm
403 http_code
, resp
= self
._http
.post_cmd(endpoint
='/test/message/alarm_request',
404 postfields_dict
=data
)
405 #print('HTTP CODE: {}'.format(http_code))
406 #print('RESP: {}'.format(resp))
407 # if http_code in (200, 201, 202, 204):
408 # resp = json.loads(resp)
409 print('Alarm created')
414 # msg = json.loads(resp)
417 # raise ClientException('error: code: {}, resp: {}'.format(
419 except ClientException
as exc
:
420 message
="failed to create alarm: alarm {}\n{}".format(
423 raise ClientException(message
)
425 def delete_alarm(self
, name
):
426 self
._logger
.debug("")
427 self
._client
.get_token()
429 data
["delete_alarm_request"] = {}
430 data
["delete_alarm_request"]["alarm_delete_request"] = {}
431 data
["delete_alarm_request"]["alarm_delete_request"]["alarm_uuid"] = name
433 http_code
, resp
= self
._http
.post_cmd(endpoint
='/test/message/alarm_request',
434 postfields_dict
=data
)
435 #print('HTTP CODE: {}'.format(http_code))
436 #print('RESP: {}'.format(resp))
437 # if http_code in (200, 201, 202, 204):
438 # resp = json.loads(resp)
439 print('Alarm deleted')
444 # msg = json.loads(resp)
447 # raise ClientException('error: code: {}, resp: {}'.format(
449 except ClientException
as exc
:
450 message
="failed to delete alarm: alarm {}\n{}".format(
453 raise ClientException(message
)
455 def export_metric(self
, metric
):
456 self
._logger
.debug("")
457 self
._client
.get_token()
459 data
["read_metric_data_request"] = metric
461 http_code
, resp
= self
._http
.post_cmd(endpoint
='/test/message/metric_request',
462 postfields_dict
=data
)
463 #print('HTTP CODE: {}'.format(http_code))
464 #print('RESP: {}'.format(resp))
465 # if http_code in (200, 201, 202, 204):
466 # resp = json.loads(resp)
467 return 'Metric exported'
472 # msg = json.loads(resp)
475 # raise ClientException('error: code: {}, resp: {}'.format(
477 except ClientException
as exc
:
478 message
="failed to export metric: metric {}\n{}".format(
481 raise ClientException(message
)
483 def get_field(self
, ns_name
, field
):
484 self
._logger
.debug("")
485 nsr
= self
.get(ns_name
)
486 print(yaml
.safe_dump(nsr
))
488 raise NotFound("failed to retrieve ns {}".format(ns_name
))
493 raise NotFound("failed to find {} in ns {}".format(field
, ns_name
))