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
._apiName
= '/nslcm'
35 self
._apiVersion
= '/v1'
36 self
._apiResource
= '/ns_instances_content'
37 self
._apiBase
= '{}{}{}'.format(self
._apiName
,
38 self
._apiVersion
, self
._apiResource
)
41 def _wait(self
, id, deleteFlag
=False):
42 # Endpoint to get operation status
43 apiUrlStatus
= '{}{}{}'.format(self
._apiName
, self
._apiVersion
, '/ns_lcm_op_occs')
44 # Wait for status for NS instance creation/update/deletion
45 WaitForStatus
.wait_for_status(
48 WaitForStatus
.TIMEOUT_NS_OPERATION
,
51 deleteFlag
=deleteFlag
)
53 def list(self
, filter=None):
54 """Returns a list of NS
56 self
._client
.get_token()
59 filter_string
= '?{}'.format(filter)
60 resp
= self
._http
.get_cmd('{}{}'.format(self
._apiBase
,filter_string
))
66 """Returns an NS based on name or id
68 self
._client
.get_token()
69 if utils
.validate_uuid4(name
):
70 for ns
in self
.list():
74 for ns
in self
.list():
75 if name
== ns
['name']:
77 raise NotFound("ns {} not found".format(name
))
79 def get_individual(self
, name
):
80 self
._client
.get_token()
82 if not utils
.validate_uuid4(name
):
83 for ns
in self
.list():
84 if name
== ns
['name']:
87 resp
= self
._http
.get_cmd('{}/{}'.format(self
._apiBase
, ns_id
))
88 #resp = self._http.get_cmd('{}/{}/nsd_content'.format(self._apiBase, ns_id))
89 #print(yaml.safe_dump(resp))
92 raise NotFound("ns {} not found".format(name
))
94 def delete(self
, name
, force
=False, wait
=False):
98 querystring
= '?FORCE=True'
99 http_code
, resp
= self
._http
.delete_cmd('{}/{}{}'.format(self
._apiBase
,
100 ns
['_id'], querystring
))
101 # print('HTTP CODE: {}'.format(http_code))
102 # print('RESP: {}'.format(resp))
105 resp
= json
.loads(resp
)
106 # For the 'delete' operation, '_id' is used
107 self
._wait
(resp
.get('_id'), deleteFlag
=True)
109 print('Deletion in progress')
110 elif http_code
== 204:
116 msg
= json
.loads(resp
)
119 raise ClientException("failed to delete ns {} - {}".format(name
, msg
))
121 def create(self
, nsd_name
, nsr_name
, account
, config
=None,
122 ssh_keys
=None, description
='default description',
123 admin_status
='ENABLED', wait
=False):
124 self
._client
.get_token()
125 nsd
= self
._client
.nsd
.get(nsd_name
)
130 def get_vim_account_id(vim_account
):
131 if vim_account_id
.get(vim_account
):
132 return vim_account_id
[vim_account
]
134 vim
= self
._client
.vim
.get(vim_account
)
136 raise NotFound("cannot find vim account '{}'".format(vim_account
))
137 vim_account_id
[vim_account
] = vim
['_id']
140 def get_wim_account_id(wim_account
):
141 if not isinstance(wim_account
, str):
143 if wim_account_id
.get(wim_account
):
144 return wim_account_id
[wim_account
]
146 wim
= self
._client
.wim
.get(wim_account
)
148 raise NotFound("cannot find wim account '{}'".format(wim_account
))
149 wim_account_id
[wim_account
] = wim
['_id']
153 ns
['nsdId'] = nsd
['_id']
154 ns
['nsName'] = nsr_name
155 ns
['nsDescription'] = description
156 ns
['vimAccountId'] = get_vim_account_id(account
)
158 #ns['userdata']['key1']='value1'
159 #ns['userdata']['key2']='value2'
161 if ssh_keys
is not None:
163 for pubkeyfile
in ssh_keys
.split(','):
164 with
open(pubkeyfile
, 'r') as f
:
165 ns
['ssh_keys'].append(f
.read())
167 ns_config
= yaml
.safe_load(config
)
168 if "vim-network-name" in ns_config
:
169 ns_config
["vld"] = ns_config
.pop("vim-network-name")
170 if "vld" in ns_config
:
171 for vld
in ns_config
["vld"]:
172 if vld
.get("vim-network-name"):
173 if isinstance(vld
["vim-network-name"], dict):
174 vim_network_name_dict
= {}
175 for vim_account
, vim_net
in list(vld
["vim-network-name"].items()):
176 vim_network_name_dict
[get_vim_account_id(vim_account
)] = vim_net
177 vld
["vim-network-name"] = vim_network_name_dict
178 if "wim_account" in vld
and vld
["wim_account"] is not None:
179 vld
["wimAccountId"] = get_wim_account_id(vld
.pop("wim_account"))
180 ns
["vld"] = ns_config
["vld"]
181 if "vnf" in ns_config
:
182 for vnf
in ns_config
["vnf"]:
183 if vnf
.get("vim_account"):
184 vnf
["vimAccountId"] = get_vim_account_id(vnf
.pop("vim_account"))
185 ns
["vnf"] = ns_config
["vnf"]
187 if "additionalParamsForNs" in ns_config
:
188 ns
["additionalParamsForNs"] = ns_config
.pop("additionalParamsForNs")
189 if not isinstance(ns
["additionalParamsForNs"], dict):
190 raise ValueError("Error at --config 'additionalParamsForNs' must be a dictionary")
191 if "additionalParamsForVnf" in ns_config
:
192 ns
["additionalParamsForVnf"] = ns_config
.pop("additionalParamsForVnf")
193 if not isinstance(ns
["additionalParamsForVnf"], list):
194 raise ValueError("Error at --config 'additionalParamsForVnf' must be a list")
195 for additional_param_vnf
in ns
["additionalParamsForVnf"]:
196 if not isinstance(additional_param_vnf
, dict):
197 raise ValueError("Error at --config 'additionalParamsForVnf' items must be dictionaries")
198 if not additional_param_vnf
.get("member-vnf-index"):
199 raise ValueError("Error at --config 'additionalParamsForVnf' items must contain "
200 "'member-vnf-index'")
201 if "wim_account" in ns_config
:
202 wim_account
= ns_config
.pop("wim_account")
203 if wim_account
is not None:
204 ns
['wimAccountId'] = get_wim_account_id(wim_account
)
206 # print(yaml.safe_dump(ns))
208 self
._apiResource
= '/ns_instances_content'
209 self
._apiBase
= '{}{}{}'.format(self
._apiName
,
210 self
._apiVersion
, self
._apiResource
)
211 headers
= self
._client
._headers
212 headers
['Content-Type'] = 'application/yaml'
213 http_header
= ['{}: {}'.format(key
,val
)
214 for (key
,val
) in list(headers
.items())]
215 self
._http
.set_http_header(http_header
)
216 http_code
, resp
= self
._http
.post_cmd(endpoint
=self
._apiBase
,
218 # print('HTTP CODE: {}'.format(http_code))
219 # print('RESP: {}'.format(resp))
220 if http_code
in (200, 201, 202, 204):
222 resp
= json
.loads(resp
)
223 if not resp
or 'id' not in resp
:
224 raise ClientException('unexpected response from server - {} '.format(
227 # Wait for status for NS instance creation
228 self
._wait
(resp
.get('nslcmop_id'))
234 msg
= json
.loads(resp
)
237 raise ClientException(msg
)
238 except ClientException
as exc
:
239 message
="failed to create ns: {} nsd: {}\nerror:\n{}".format(
243 raise ClientException(message
)
245 def list_op(self
, name
, filter=None):
246 """Returns the list of operations of a NS
250 self
._apiResource
= '/ns_lcm_op_occs'
251 self
._apiBase
= '{}{}{}'.format(self
._apiName
,
252 self
._apiVersion
, self
._apiResource
)
255 filter_string
= '&{}'.format(filter)
256 http_code
, resp
= self
._http
.get2_cmd('{}?nsInstanceId={}'.format(
257 self
._apiBase
, ns
['_id'],
259 #print('HTTP CODE: {}'.format(http_code))
260 #print('RESP: {}'.format(resp))
263 resp
= json
.loads(resp
)
266 raise ClientException('unexpected response from server')
271 resp
= json
.loads(resp
)
275 raise ClientException(msg
)
276 except ClientException
as exc
:
277 message
="failed to get operation list of NS {}:\nerror:\n{}".format(
280 raise ClientException(message
)
282 def get_op(self
, operationId
):
283 """Returns the status of an operation
285 self
._client
.get_token()
287 self
._apiResource
= '/ns_lcm_op_occs'
288 self
._apiBase
= '{}{}{}'.format(self
._apiName
,
289 self
._apiVersion
, self
._apiResource
)
290 http_code
, resp
= self
._http
.get2_cmd('{}/{}'.format(self
._apiBase
, operationId
))
291 #print('HTTP CODE: {}'.format(http_code))
292 #print('RESP: {}'.format(resp))
295 resp
= json
.loads(resp
)
298 raise ClientException('unexpected response from server')
303 resp
= json
.loads(resp
)
307 raise ClientException(msg
)
308 except ClientException
as exc
:
309 message
="failed to get status of operation {}:\nerror:\n{}".format(
312 raise ClientException(message
)
314 def exec_op(self
, name
, op_name
, op_data
=None, wait
=False, ):
315 """Executes an operation on a NS
319 self
._apiResource
= '/ns_instances'
320 self
._apiBase
= '{}{}{}'.format(self
._apiName
,
321 self
._apiVersion
, self
._apiResource
)
322 endpoint
= '{}/{}/{}'.format(self
._apiBase
, ns
['_id'], op_name
)
323 #print('OP_NAME: {}'.format(op_name))
324 #print('OP_DATA: {}'.format(json.dumps(op_data)))
325 http_code
, resp
= self
._http
.post_cmd(endpoint
=endpoint
, postfields_dict
=op_data
)
326 #print('HTTP CODE: {}'.format(http_code))
327 #print('RESP: {}'.format(resp))
328 if http_code
in (200, 201, 202, 204):
330 resp
= json
.loads(resp
)
331 if not resp
or 'id' not in resp
:
332 raise ClientException('unexpected response from server - {}'.format(
335 # Wait for status for NS instance action
336 # For the 'action' operation, 'id' is used
337 self
._wait
(resp
.get('id'))
343 msg
= json
.loads(resp
)
346 raise ClientException(msg
)
347 except ClientException
as exc
:
348 message
="failed to exec operation {}:\nerror:\n{}".format(
351 raise ClientException(message
)
353 def scale_vnf(self
, ns_name
, vnf_name
, scaling_group
, scale_in
, scale_out
, wait
=False):
354 """Scales a VNF by adding/removing VDUs
356 self
._client
.get_token()
359 op_data
["scaleType"] = "SCALE_VNF"
360 op_data
["scaleVnfData"] = {}
362 op_data
["scaleVnfData"]["scaleVnfType"] = "SCALE_IN"
364 op_data
["scaleVnfData"]["scaleVnfType"] = "SCALE_OUT"
365 op_data
["scaleVnfData"]["scaleByStepData"] = {
366 "member-vnf-index": vnf_name
,
367 "scaling-group-descriptor": scaling_group
,
369 op_id
= self
.exec_op(ns_name
, op_name
='scale', op_data
=op_data
, wait
=wait
)
371 except ClientException
as exc
:
372 message
="failed to scale vnf {} of ns {}:\nerror:\n{}".format(
373 vnf_name
, ns_name
, str(exc
))
374 raise ClientException(message
)
376 def create_alarm(self
, alarm
):
377 self
._client
.get_token()
379 data
["create_alarm_request"] = {}
380 data
["create_alarm_request"]["alarm_create_request"] = alarm
382 http_code
, resp
= self
._http
.post_cmd(endpoint
='/test/message/alarm_request',
383 postfields_dict
=data
)
384 #print('HTTP CODE: {}'.format(http_code))
385 #print('RESP: {}'.format(resp))
386 if http_code
in (200, 201, 202, 204):
387 #resp = json.loads(resp)
388 print('Alarm created')
393 msg
= json
.loads(resp
)
396 raise ClientException('error: code: {}, resp: {}'.format(
398 except ClientException
as exc
:
399 message
="failed to create alarm: alarm {}\n{}".format(
402 raise ClientException(message
)
404 def delete_alarm(self
, name
):
405 self
._client
.get_token()
407 data
["delete_alarm_request"] = {}
408 data
["delete_alarm_request"]["alarm_delete_request"] = {}
409 data
["delete_alarm_request"]["alarm_delete_request"]["alarm_uuid"] = name
411 http_code
, resp
= self
._http
.post_cmd(endpoint
='/test/message/alarm_request',
412 postfields_dict
=data
)
413 #print('HTTP CODE: {}'.format(http_code))
414 #print('RESP: {}'.format(resp))
415 if http_code
in (200, 201, 202, 204):
416 #resp = json.loads(resp)
417 print('Alarm deleted')
422 msg
= json
.loads(resp
)
425 raise ClientException('error: code: {}, resp: {}'.format(
427 except ClientException
as exc
:
428 message
="failed to delete alarm: alarm {}\n{}".format(
431 raise ClientException(message
)
433 def export_metric(self
, metric
):
434 self
._client
.get_token()
436 data
["read_metric_data_request"] = metric
438 http_code
, resp
= self
._http
.post_cmd(endpoint
='/test/message/metric_request',
439 postfields_dict
=data
)
440 #print('HTTP CODE: {}'.format(http_code))
441 #print('RESP: {}'.format(resp))
442 if http_code
in (200, 201, 202, 204):
443 #resp = json.loads(resp)
444 return 'Metric exported'
449 msg
= json
.loads(resp
)
452 raise ClientException('error: code: {}, resp: {}'.format(
454 except ClientException
as exc
:
455 message
="failed to export metric: metric {}\n{}".format(
458 raise ClientException(message
)
460 def get_field(self
, ns_name
, field
):
461 nsr
= self
.get(ns_name
)
462 print(yaml
.safe_dump(nsr
))
464 raise NotFound("failed to retrieve ns {}".format(ns_name
))
469 raise NotFound("failed to find {} in ns {}".format(field
, ns_name
))