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 not additional_param_vnf
.get("additionalParams"):
202 raise ValueError("Error at --config 'additionalParamsForVnf' items must contain "
203 "'additionalParams'")
204 if "wim_account" in ns_config
:
205 wim_account
= ns_config
.pop("wim_account")
206 if wim_account
is not None:
207 ns
['wimAccountId'] = get_wim_account_id(wim_account
)
209 # print(yaml.safe_dump(ns))
211 self
._apiResource
= '/ns_instances_content'
212 self
._apiBase
= '{}{}{}'.format(self
._apiName
,
213 self
._apiVersion
, self
._apiResource
)
214 headers
= self
._client
._headers
215 headers
['Content-Type'] = 'application/yaml'
216 http_header
= ['{}: {}'.format(key
,val
)
217 for (key
,val
) in list(headers
.items())]
218 self
._http
.set_http_header(http_header
)
219 http_code
, resp
= self
._http
.post_cmd(endpoint
=self
._apiBase
,
221 # print('HTTP CODE: {}'.format(http_code))
222 # print('RESP: {}'.format(resp))
223 if http_code
in (200, 201, 202, 204):
225 resp
= json
.loads(resp
)
226 if not resp
or 'id' not in resp
:
227 raise ClientException('unexpected response from server - {} '.format(
230 # Wait for status for NS instance creation
231 self
._wait
(resp
.get('nslcmop_id'))
237 msg
= json
.loads(resp
)
240 raise ClientException(msg
)
241 except ClientException
as exc
:
242 message
="failed to create ns: {} nsd: {}\nerror:\n{}".format(
246 raise ClientException(message
)
248 def list_op(self
, name
, filter=None):
249 """Returns the list of operations of a NS
253 self
._apiResource
= '/ns_lcm_op_occs'
254 self
._apiBase
= '{}{}{}'.format(self
._apiName
,
255 self
._apiVersion
, self
._apiResource
)
258 filter_string
= '&{}'.format(filter)
259 http_code
, resp
= self
._http
.get2_cmd('{}?nsInstanceId={}'.format(
260 self
._apiBase
, ns
['_id'],
262 #print('HTTP CODE: {}'.format(http_code))
263 #print('RESP: {}'.format(resp))
266 resp
= json
.loads(resp
)
269 raise ClientException('unexpected response from server')
274 resp
= json
.loads(resp
)
278 raise ClientException(msg
)
279 except ClientException
as exc
:
280 message
="failed to get operation list of NS {}:\nerror:\n{}".format(
283 raise ClientException(message
)
285 def get_op(self
, operationId
):
286 """Returns the status of an operation
288 self
._client
.get_token()
290 self
._apiResource
= '/ns_lcm_op_occs'
291 self
._apiBase
= '{}{}{}'.format(self
._apiName
,
292 self
._apiVersion
, self
._apiResource
)
293 http_code
, resp
= self
._http
.get2_cmd('{}/{}'.format(self
._apiBase
, operationId
))
294 #print('HTTP CODE: {}'.format(http_code))
295 #print('RESP: {}'.format(resp))
298 resp
= json
.loads(resp
)
301 raise ClientException('unexpected response from server')
306 resp
= json
.loads(resp
)
310 raise ClientException(msg
)
311 except ClientException
as exc
:
312 message
="failed to get status of operation {}:\nerror:\n{}".format(
315 raise ClientException(message
)
317 def exec_op(self
, name
, op_name
, op_data
=None, wait
=False):
318 """Executes an operation on a NS
322 self
._apiResource
= '/ns_instances'
323 self
._apiBase
= '{}{}{}'.format(self
._apiName
,
324 self
._apiVersion
, self
._apiResource
)
325 endpoint
= '{}/{}/{}'.format(self
._apiBase
, ns
['_id'], op_name
)
326 #print('OP_NAME: {}'.format(op_name))
327 #print('OP_DATA: {}'.format(json.dumps(op_data)))
328 http_code
, resp
= self
._http
.post_cmd(endpoint
=endpoint
, postfields_dict
=op_data
)
329 #print('HTTP CODE: {}'.format(http_code))
330 #print('RESP: {}'.format(resp))
331 if http_code
in (200, 201, 202, 204):
333 resp
= json
.loads(resp
)
334 if not resp
or 'id' not in resp
:
335 raise ClientException('unexpected response from server - {}'.format(
338 # Wait for status for NS instance action
339 # For the 'action' operation, 'id' is used
340 self
._wait
(resp
.get('id'))
346 msg
= json
.loads(resp
)
349 raise ClientException(msg
)
350 except ClientException
as exc
:
351 message
="failed to exec operation {}:\nerror:\n{}".format(
354 raise ClientException(message
)
356 def scale_vnf(self
, ns_name
, vnf_name
, scaling_group
, scale_in
, scale_out
, wait
=False):
357 """Scales a VNF by adding/removing VDUs
359 self
._client
.get_token()
362 op_data
["scaleType"] = "SCALE_VNF"
363 op_data
["scaleVnfData"] = {}
365 op_data
["scaleVnfData"]["scaleVnfType"] = "SCALE_IN"
367 op_data
["scaleVnfData"]["scaleVnfType"] = "SCALE_OUT"
368 op_data
["scaleVnfData"]["scaleByStepData"] = {
369 "member-vnf-index": vnf_name
,
370 "scaling-group-descriptor": scaling_group
,
372 self
.exec_op(ns_name
, op_name
='scale', op_data
=op_data
, wait
=wait
)
373 except ClientException
as exc
:
374 message
="failed to scale vnf {} of ns {}:\nerror:\n{}".format(
375 vnf_name
, ns_name
, exc
.message
)
376 raise ClientException(message
)
378 def create_alarm(self
, alarm
):
379 self
._client
.get_token()
381 data
["create_alarm_request"] = {}
382 data
["create_alarm_request"]["alarm_create_request"] = alarm
384 http_code
, resp
= self
._http
.post_cmd(endpoint
='/test/message/alarm_request',
385 postfields_dict
=data
)
386 #print('HTTP CODE: {}'.format(http_code))
387 #print('RESP: {}'.format(resp))
388 if http_code
in (200, 201, 202, 204):
389 #resp = json.loads(resp)
390 print('Alarm created')
395 msg
= json
.loads(resp
)
398 raise ClientException('error: code: {}, resp: {}'.format(
400 except ClientException
as exc
:
401 message
="failed to create alarm: alarm {}\n{}".format(
404 raise ClientException(message
)
406 def delete_alarm(self
, name
):
407 self
._client
.get_token()
409 data
["delete_alarm_request"] = {}
410 data
["delete_alarm_request"]["alarm_delete_request"] = {}
411 data
["delete_alarm_request"]["alarm_delete_request"]["alarm_uuid"] = name
413 http_code
, resp
= self
._http
.post_cmd(endpoint
='/test/message/alarm_request',
414 postfields_dict
=data
)
415 #print('HTTP CODE: {}'.format(http_code))
416 #print('RESP: {}'.format(resp))
417 if http_code
in (200, 201, 202, 204):
418 #resp = json.loads(resp)
419 print('Alarm deleted')
424 msg
= json
.loads(resp
)
427 raise ClientException('error: code: {}, resp: {}'.format(
429 except ClientException
as exc
:
430 message
="failed to delete alarm: alarm {}\n{}".format(
433 raise ClientException(message
)
435 def export_metric(self
, metric
):
436 self
._client
.get_token()
438 data
["read_metric_data_request"] = metric
440 http_code
, resp
= self
._http
.post_cmd(endpoint
='/test/message/metric_request',
441 postfields_dict
=data
)
442 #print('HTTP CODE: {}'.format(http_code))
443 #print('RESP: {}'.format(resp))
444 if http_code
in (200, 201, 202, 204):
445 #resp = json.loads(resp)
446 return 'Metric exported'
451 msg
= json
.loads(resp
)
454 raise ClientException('error: code: {}, resp: {}'.format(
456 except ClientException
as exc
:
457 message
="failed to export metric: metric {}\n{}".format(
460 raise ClientException(message
)
462 def get_field(self
, ns_name
, field
):
463 nsr
= self
.get(ns_name
)
464 print(yaml
.safe_dump(nsr
))
466 raise NotFound("failed to retrieve ns {}".format(ns_name
))
471 raise NotFound("failed to find {} in ns {}".format(field
, ns_name
))