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
25 from osmclient
.common
.exceptions
import OsmHttpException
33 def __init__(self
, http
=None, client
=None):
36 self
._logger
= logging
.getLogger('osmclient')
37 self
._apiName
= '/nslcm'
38 self
._apiVersion
= '/v1'
39 self
._apiResource
= '/ns_instances_content'
40 self
._apiBase
= '{}{}{}'.format(self
._apiName
,
41 self
._apiVersion
, self
._apiResource
)
44 def _wait(self
, id, deleteFlag
=False):
45 self
._logger
.debug("")
46 # Endpoint to get operation status
47 apiUrlStatus
= '{}{}{}'.format(self
._apiName
, self
._apiVersion
, '/ns_lcm_op_occs')
48 # Wait for status for NS instance creation/update/deletion
49 WaitForStatus
.wait_for_status(
52 WaitForStatus
.TIMEOUT_NS_OPERATION
,
55 deleteFlag
=deleteFlag
)
57 def list(self
, filter=None):
58 """Returns a list of NS
60 self
._logger
.debug("")
61 self
._client
.get_token()
64 filter_string
= '?{}'.format(filter)
65 _
, resp
= self
._http
.get2_cmd('{}{}'.format(self
._apiBase
,filter_string
))
67 return json
.loads(resp
)
71 """Returns an NS based on name or id
73 self
._logger
.debug("")
74 self
._client
.get_token()
75 if utils
.validate_uuid4(name
):
76 for ns
in self
.list():
80 for ns
in self
.list():
81 if name
== ns
['name']:
83 raise NotFound("ns {} not found".format(name
))
85 def get_individual(self
, name
):
86 self
._logger
.debug("")
87 self
._client
.get_token()
89 if not utils
.validate_uuid4(name
):
90 for ns
in self
.list():
91 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
)
99 raise NotFound("ns {} not found".format(name
))
101 def delete(self
, name
, force
=False, wait
=False):
102 self
._logger
.debug("")
106 querystring
= '?FORCE=True'
107 http_code
, resp
= self
._http
.delete_cmd('{}/{}{}'.format(self
._apiBase
,
108 ns
['_id'], querystring
))
109 # print('HTTP CODE: {}'.format(http_code))
110 # print('RESP: {}'.format(resp))
113 resp
= json
.loads(resp
)
114 # For the 'delete' operation, '_id' is used
115 self
._wait
(resp
.get('_id'), deleteFlag
=True)
117 print('Deletion in progress')
118 elif http_code
== 204:
124 msg
= json
.loads(resp
)
127 raise OsmHttpException("failed to delete ns {} - {}".format(name
, msg
))
129 def create(self
, nsd_name
, nsr_name
, account
, config
=None,
130 ssh_keys
=None, description
='default description',
131 admin_status
='ENABLED', wait
=False):
132 self
._logger
.debug("")
133 self
._client
.get_token()
134 nsd
= self
._client
.nsd
.get(nsd_name
)
139 def get_vim_account_id(vim_account
):
140 self
._logger
.debug("")
141 if vim_account_id
.get(vim_account
):
142 return vim_account_id
[vim_account
]
144 vim
= self
._client
.vim
.get(vim_account
)
146 raise NotFound("cannot find vim account '{}'".format(vim_account
))
147 vim_account_id
[vim_account
] = vim
['_id']
150 def get_wim_account_id(wim_account
):
151 self
._logger
.debug("")
152 if not isinstance(wim_account
, str):
154 if wim_account_id
.get(wim_account
):
155 return wim_account_id
[wim_account
]
157 wim
= self
._client
.wim
.get(wim_account
)
159 raise NotFound("cannot find wim account '{}'".format(wim_account
))
160 wim_account_id
[wim_account
] = wim
['_id']
164 ns
['nsdId'] = nsd
['_id']
165 ns
['nsName'] = nsr_name
166 ns
['nsDescription'] = description
167 ns
['vimAccountId'] = get_vim_account_id(account
)
169 #ns['userdata']['key1']='value1'
170 #ns['userdata']['key2']='value2'
172 if ssh_keys
is not None:
174 for pubkeyfile
in ssh_keys
.split(','):
175 with
open(pubkeyfile
, 'r') as f
:
176 ns
['ssh_keys'].append(f
.read())
178 ns_config
= yaml
.safe_load(config
)
179 if "vim-network-name" in ns_config
:
180 ns_config
["vld"] = ns_config
.pop("vim-network-name")
181 if "vld" in ns_config
:
182 for vld
in ns_config
["vld"]:
183 if vld
.get("vim-network-name"):
184 if isinstance(vld
["vim-network-name"], dict):
185 vim_network_name_dict
= {}
186 for vim_account
, vim_net
in list(vld
["vim-network-name"].items()):
187 vim_network_name_dict
[get_vim_account_id(vim_account
)] = vim_net
188 vld
["vim-network-name"] = vim_network_name_dict
189 if "wim_account" in vld
and vld
["wim_account"] is not None:
190 vld
["wimAccountId"] = get_wim_account_id(vld
.pop("wim_account"))
191 ns
["vld"] = ns_config
["vld"]
192 if "vnf" in ns_config
:
193 for vnf
in ns_config
["vnf"]:
194 if vnf
.get("vim_account"):
195 vnf
["vimAccountId"] = get_vim_account_id(vnf
.pop("vim_account"))
196 ns
["vnf"] = ns_config
["vnf"]
198 if "additionalParamsForNs" in ns_config
:
199 ns
["additionalParamsForNs"] = ns_config
.pop("additionalParamsForNs")
200 if not isinstance(ns
["additionalParamsForNs"], dict):
201 raise ValueError("Error at --config 'additionalParamsForNs' must be a dictionary")
202 if "additionalParamsForVnf" in ns_config
:
203 ns
["additionalParamsForVnf"] = ns_config
.pop("additionalParamsForVnf")
204 if not isinstance(ns
["additionalParamsForVnf"], list):
205 raise ValueError("Error at --config 'additionalParamsForVnf' must be a list")
206 for additional_param_vnf
in ns
["additionalParamsForVnf"]:
207 if not isinstance(additional_param_vnf
, dict):
208 raise ValueError("Error at --config 'additionalParamsForVnf' items must be dictionaries")
209 if not additional_param_vnf
.get("member-vnf-index"):
210 raise ValueError("Error at --config 'additionalParamsForVnf' items must contain "
211 "'member-vnf-index'")
212 if "wim_account" in ns_config
:
213 wim_account
= ns_config
.pop("wim_account")
214 if wim_account
is not None:
215 ns
['wimAccountId'] = get_wim_account_id(wim_account
)
216 if "timeout_ns_deploy" in ns_config
:
217 ns
["timeout_ns_deploy"] = ns_config
.pop("timeout_ns_deploy")
219 # print(yaml.safe_dump(ns))
221 self
._apiResource
= '/ns_instances_content'
222 self
._apiBase
= '{}{}{}'.format(self
._apiName
,
223 self
._apiVersion
, self
._apiResource
)
224 headers
= self
._client
._headers
225 headers
['Content-Type'] = 'application/yaml'
226 http_header
= ['{}: {}'.format(key
,val
)
227 for (key
,val
) in list(headers
.items())]
228 self
._http
.set_http_header(http_header
)
229 http_code
, resp
= self
._http
.post_cmd(endpoint
=self
._apiBase
,
231 # print('HTTP CODE: {}'.format(http_code))
232 # print('RESP: {}'.format(resp))
233 #if http_code in (200, 201, 202, 204):
235 resp
= json
.loads(resp
)
236 if not resp
or 'id' not in resp
:
237 raise ClientException('unexpected response from server - {} '.format(
240 # Wait for status for NS instance creation
241 self
._wait
(resp
.get('nslcmop_id'))
247 # msg = json.loads(resp)
250 # raise ClientException(msg)
251 except ClientException
as exc
:
252 message
="failed to create ns: {} nsd: {}\nerror:\n{}".format(
256 raise OsmHttpException(message
)
258 def list_op(self
, name
, filter=None):
259 """Returns the list of operations of a NS
261 self
._logger
.debug("")
264 self
._apiResource
= '/ns_lcm_op_occs'
265 self
._apiBase
= '{}{}{}'.format(self
._apiName
,
266 self
._apiVersion
, self
._apiResource
)
269 filter_string
= '&{}'.format(filter)
270 http_code
, resp
= self
._http
.get2_cmd('{}?nsInstanceId={}'.format(
271 self
._apiBase
, ns
['_id'],
273 #print('HTTP CODE: {}'.format(http_code))
274 #print('RESP: {}'.format(resp))
275 #if http_code == 200:
277 resp
= json
.loads(resp
)
280 raise ClientException('unexpected response from server')
285 # resp = json.loads(resp)
286 # msg = resp['detail']
289 # raise ClientException(msg)
290 except ClientException
as exc
:
291 message
="failed to get operation list of NS {}:\nerror:\n{}".format(
294 raise OsmHttpException(message
)
296 def get_op(self
, operationId
):
297 """Returns the status of an operation
299 self
._logger
.debug("")
300 self
._client
.get_token()
302 self
._apiResource
= '/ns_lcm_op_occs'
303 self
._apiBase
= '{}{}{}'.format(self
._apiName
,
304 self
._apiVersion
, self
._apiResource
)
305 http_code
, resp
= self
._http
.get2_cmd('{}/{}'.format(self
._apiBase
, operationId
))
306 #print('HTTP CODE: {}'.format(http_code))
307 #print('RESP: {}'.format(resp))
308 #if http_code == 200:
310 resp
= json
.loads(resp
)
313 raise ClientException('unexpected response from server')
318 # resp = json.loads(resp)
319 # msg = resp['detail']
322 # raise ClientException(msg)
323 except ClientException
as exc
:
324 message
="failed to get status of operation {}:\nerror:\n{}".format(
327 raise OsmHttpException(message
)
329 def exec_op(self
, name
, op_name
, op_data
=None, wait
=False, ):
330 """Executes an operation on a NS
332 self
._logger
.debug("")
336 self
._apiResource
= '/ns_instances'
337 self
._apiBase
= '{}{}{}'.format(self
._apiName
,
338 self
._apiVersion
, self
._apiResource
)
339 endpoint
= '{}/{}/{}'.format(self
._apiBase
, ns
['_id'], op_name
)
340 #print('OP_NAME: {}'.format(op_name))
341 #print('OP_DATA: {}'.format(json.dumps(op_data)))
342 http_code
, resp
= self
._http
.post_cmd(endpoint
=endpoint
, postfields_dict
=op_data
)
343 #print('HTTP CODE: {}'.format(http_code))
344 #print('RESP: {}'.format(resp))
345 #if http_code in (200, 201, 202, 204):
347 resp
= json
.loads(resp
)
348 if not resp
or 'id' not in resp
:
349 raise ClientException('unexpected response from server - {}'.format(
352 # Wait for status for NS instance action
353 # For the 'action' operation, 'id' is used
354 self
._wait
(resp
.get('id'))
360 # msg = json.loads(resp)
363 # raise ClientException(msg)
364 except ClientException
as exc
:
365 message
="failed to exec operation {}:\nerror:\n{}".format(
368 raise OsmHttpException(message
)
370 def scale_vnf(self
, ns_name
, vnf_name
, scaling_group
, scale_in
, scale_out
, wait
=False):
371 """Scales a VNF by adding/removing VDUs
373 self
._logger
.debug("")
374 self
._client
.get_token()
377 op_data
["scaleType"] = "SCALE_VNF"
378 op_data
["scaleVnfData"] = {}
380 op_data
["scaleVnfData"]["scaleVnfType"] = "SCALE_IN"
382 op_data
["scaleVnfData"]["scaleVnfType"] = "SCALE_OUT"
383 op_data
["scaleVnfData"]["scaleByStepData"] = {
384 "member-vnf-index": vnf_name
,
385 "scaling-group-descriptor": scaling_group
,
387 op_id
= self
.exec_op(ns_name
, op_name
='scale', op_data
=op_data
, wait
=wait
)
389 except ClientException
as exc
:
390 message
="failed to scale vnf {} of ns {}:\nerror:\n{}".format(
391 vnf_name
, ns_name
, str(exc
))
392 raise ClientException(message
)
394 def create_alarm(self
, alarm
):
395 self
._logger
.debug("")
396 self
._client
.get_token()
398 data
["create_alarm_request"] = {}
399 data
["create_alarm_request"]["alarm_create_request"] = alarm
401 http_code
, resp
= self
._http
.post_cmd(endpoint
='/test/message/alarm_request',
402 postfields_dict
=data
)
403 #print('HTTP CODE: {}'.format(http_code))
404 #print('RESP: {}'.format(resp))
405 #if http_code in (200, 201, 202, 204):
406 #resp = json.loads(resp)
407 print('Alarm created')
412 # msg = json.loads(resp)
415 # raise ClientException('error: code: {}, resp: {}'.format(
417 #except ClientException as exc:
418 # message="failed to create alarm: alarm {}\n{}".format(
421 # raise ClientException(message)
423 def delete_alarm(self
, name
):
424 self
._logger
.debug("")
425 self
._client
.get_token()
427 data
["delete_alarm_request"] = {}
428 data
["delete_alarm_request"]["alarm_delete_request"] = {}
429 data
["delete_alarm_request"]["alarm_delete_request"]["alarm_uuid"] = name
431 http_code
, resp
= self
._http
.post_cmd(endpoint
='/test/message/alarm_request',
432 postfields_dict
=data
)
433 #print('HTTP CODE: {}'.format(http_code))
434 #print('RESP: {}'.format(resp))
435 #if http_code in (200, 201, 202, 204):
436 #resp = json.loads(resp)
437 print('Alarm deleted')
442 # msg = json.loads(resp)
445 # raise ClientException('error: code: {}, resp: {}'.format(
447 #except ClientException as exc:
448 # message="failed to delete alarm: alarm {}\n{}".format(
451 # raise ClientException(message)
453 def export_metric(self
, metric
):
454 self
._logger
.debug("")
455 self
._client
.get_token()
457 data
["read_metric_data_request"] = metric
459 http_code
, resp
= self
._http
.post_cmd(endpoint
='/test/message/metric_request',
460 postfields_dict
=data
)
461 #print('HTTP CODE: {}'.format(http_code))
462 #print('RESP: {}'.format(resp))
463 #if http_code in (200, 201, 202, 204):
464 #resp = json.loads(resp)
465 return 'Metric exported'
470 # msg = json.loads(resp)
473 # raise ClientException('error: code: {}, resp: {}'.format(
475 #except ClientException as exc:
476 # message="failed to export metric: metric {}\n{}".format(
479 # raise ClientException(message)
481 def get_field(self
, ns_name
, field
):
482 self
._logger
.debug("")
483 nsr
= self
.get(ns_name
)
484 print(yaml
.safe_dump(nsr
))
486 raise NotFound("failed to retrieve ns {}".format(ns_name
))
491 raise NotFound("failed to find {} in ns {}".format(field
, ns_name
))