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
58 filter_string
= '?{}'.format(filter)
59 resp
= self
._http
.get_cmd('{}{}'.format(self
._apiBase
,filter_string
))
65 """Returns an NS based on name or id
67 if utils
.validate_uuid4(name
):
68 for ns
in self
.list():
72 for ns
in self
.list():
73 if name
== ns
['name']:
75 raise NotFound("ns {} not found".format(name
))
77 def get_individual(self
, name
):
79 if not utils
.validate_uuid4(name
):
80 for ns
in self
.list():
81 if name
== ns
['name']:
84 resp
= self
._http
.get_cmd('{}/{}'.format(self
._apiBase
, ns_id
))
85 #resp = self._http.get_cmd('{}/{}/nsd_content'.format(self._apiBase, ns_id))
86 #print yaml.safe_dump(resp)
89 raise NotFound("ns {} not found".format(name
))
91 def delete(self
, name
, force
=False, wait
=False):
95 querystring
= '?FORCE=True'
96 http_code
, resp
= self
._http
.delete_cmd('{}/{}{}'.format(self
._apiBase
,
97 ns
['_id'], querystring
))
98 # print 'HTTP CODE: {}'.format(http_code)
99 # print 'RESP: {}'.format(resp)
102 resp
= json
.loads(resp
)
103 # For the 'delete' operation, '_id' is used
104 self
._wait
(resp
.get('_id'), deleteFlag
=True)
106 print('Deletion in progress')
107 elif http_code
== 204:
113 msg
= json
.loads(resp
)
116 raise ClientException("failed to delete ns {} - {}".format(name
, msg
))
118 def create(self
, nsd_name
, nsr_name
, account
, config
=None,
119 ssh_keys
=None, description
='default description',
120 admin_status
='ENABLED', wait
=False):
122 nsd
= self
._client
.nsd
.get(nsd_name
)
127 def get_vim_account_id(vim_account
):
128 if vim_account_id
.get(vim_account
):
129 return vim_account_id
[vim_account
]
131 vim
= self
._client
.vim
.get(vim_account
)
133 raise NotFound("cannot find vim account '{}'".format(vim_account
))
134 vim_account_id
[vim_account
] = vim
['_id']
137 def get_wim_account_id(wim_account
):
138 if not isinstance(wim_account
, str):
140 if wim_account_id
.get(wim_account
):
141 return wim_account_id
[wim_account
]
143 wim
= self
._client
.wim
.get(wim_account
)
145 raise NotFound("cannot find wim account '{}'".format(wim_account
))
146 wim_account_id
[wim_account
] = wim
['_id']
150 ns
['nsdId'] = nsd
['_id']
151 ns
['nsName'] = nsr_name
152 ns
['nsDescription'] = description
153 ns
['vimAccountId'] = get_vim_account_id(account
)
155 #ns['userdata']['key1']='value1'
156 #ns['userdata']['key2']='value2'
158 if ssh_keys
is not None:
160 for pubkeyfile
in ssh_keys
.split(','):
161 with
open(pubkeyfile
, 'r') as f
:
162 ns
['ssh_keys'].append(f
.read())
164 ns_config
= yaml
.load(config
)
165 if "vim-network-name" in ns_config
:
166 ns_config
["vld"] = ns_config
.pop("vim-network-name")
167 if "vld" in ns_config
:
168 for vld
in ns_config
["vld"]:
169 if vld
.get("vim-network-name"):
170 if isinstance(vld
["vim-network-name"], dict):
171 vim_network_name_dict
= {}
172 for vim_account
, vim_net
in list(vld
["vim-network-name"].items()):
173 vim_network_name_dict
[get_vim_account_id(vim_account
)] = vim_net
174 vld
["vim-network-name"] = vim_network_name_dict
175 if "wim_account" in vld
and vld
["wim_account"] is not None:
176 vld
["wimAccountId"] = get_wim_account_id(vld
.pop("wim_account"))
177 ns
["vld"] = ns_config
["vld"]
178 if "vnf" in ns_config
:
179 for vnf
in ns_config
["vnf"]:
180 if vnf
.get("vim_account"):
181 vnf
["vimAccountId"] = get_vim_account_id(vnf
.pop("vim_account"))
182 ns
["vnf"] = ns_config
["vnf"]
184 if "additionalParamsForNs" in ns_config
:
185 ns
["additionalParamsForNs"] = ns_config
.pop("additionalParamsForNs")
186 if not isinstance(ns
["additionalParamsForNs"], dict):
187 raise ValueError("Error at --config 'additionalParamsForNs' must be a dictionary")
188 if "additionalParamsForVnf" in ns_config
:
189 ns
["additionalParamsForVnf"] = ns_config
.pop("additionalParamsForVnf")
190 if not isinstance(ns
["additionalParamsForVnf"], list):
191 raise ValueError("Error at --config 'additionalParamsForVnf' must be a list")
192 for additional_param_vnf
in ns
["additionalParamsForVnf"]:
193 if not isinstance(additional_param_vnf
, dict):
194 raise ValueError("Error at --config 'additionalParamsForVnf' items must be dictionaries")
195 if not additional_param_vnf
.get("member-vnf-index"):
196 raise ValueError("Error at --config 'additionalParamsForVnf' items must contain "
197 "'member-vnf-index'")
198 if not additional_param_vnf
.get("additionalParams"):
199 raise ValueError("Error at --config 'additionalParamsForVnf' items must contain "
200 "'additionalParams'")
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
286 self
._apiResource
= '/ns_lcm_op_occs'
287 self
._apiBase
= '{}{}{}'.format(self
._apiName
,
288 self
._apiVersion
, self
._apiResource
)
289 http_code
, resp
= self
._http
.get2_cmd('{}/{}'.format(self
._apiBase
, operationId
))
290 #print 'HTTP CODE: {}'.format(http_code)
291 #print 'RESP: {}'.format(resp)
294 resp
= json
.loads(resp
)
297 raise ClientException('unexpected response from server')
302 resp
= json
.loads(resp
)
306 raise ClientException(msg
)
307 except ClientException
as exc
:
308 message
="failed to get status of operation {}:\nerror:\n{}".format(
311 raise ClientException(message
)
313 def exec_op(self
, name
, op_name
, op_data
=None, wait
=False):
314 """Executes an operation on a NS
318 self
._apiResource
= '/ns_instances'
319 self
._apiBase
= '{}{}{}'.format(self
._apiName
,
320 self
._apiVersion
, self
._apiResource
)
321 endpoint
= '{}/{}/{}'.format(self
._apiBase
, ns
['_id'], op_name
)
322 #print 'OP_NAME: {}'.format(op_name)
323 #print 'OP_DATA: {}'.format(json.dumps(op_data))
324 http_code
, resp
= self
._http
.post_cmd(endpoint
=endpoint
, postfields_dict
=op_data
)
325 #print 'HTTP CODE: {}'.format(http_code)
326 #print 'RESP: {}'.format(resp)
327 if http_code
in (200, 201, 202, 204):
329 resp
= json
.loads(resp
)
330 if not resp
or 'id' not in resp
:
331 raise ClientException('unexpected response from server - {}'.format(
334 # Wait for status for NS instance action
335 # For the 'action' operation, 'id' is used
336 self
._wait
(resp
.get('id'))
342 msg
= json
.loads(resp
)
345 raise ClientException(msg
)
346 except ClientException
as exc
:
347 message
="failed to exec operation {}:\nerror:\n{}".format(
350 raise ClientException(message
)
352 def scale_vnf(self
, ns_name
, vnf_name
, scaling_group
, scale_in
, scale_out
, wait
=False):
353 """Scales a VNF by adding/removing VDUs
357 op_data
["scaleType"] = "SCALE_VNF"
358 op_data
["scaleVnfData"] = {}
360 op_data
["scaleVnfData"]["scaleVnfType"] = "SCALE_IN"
362 op_data
["scaleVnfData"]["scaleVnfType"] = "SCALE_OUT"
363 op_data
["scaleVnfData"]["scaleByStepData"] = {
364 "member-vnf-index": vnf_name
,
365 "scaling-group-descriptor": scaling_group
,
367 self
.exec_op(ns_name
, op_name
='scale', op_data
=op_data
, wait
=wait
)
368 except ClientException
as exc
:
369 message
="failed to scale vnf {} of ns {}:\nerror:\n{}".format(
370 vnf_name
, ns_name
, exc
.message
)
371 raise ClientException(message
)
373 def create_alarm(self
, alarm
):
375 data
["create_alarm_request"] = {}
376 data
["create_alarm_request"]["alarm_create_request"] = alarm
378 http_code
, resp
= self
._http
.post_cmd(endpoint
='/test/message/alarm_request',
379 postfields_dict
=data
)
380 #print 'HTTP CODE: {}'.format(http_code)
381 #print 'RESP: {}'.format(resp)
382 if http_code
in (200, 201, 202, 204):
383 #resp = json.loads(resp)
384 print('Alarm created')
389 msg
= json
.loads(resp
)
392 raise ClientException('error: code: {}, resp: {}'.format(
394 except ClientException
as exc
:
395 message
="failed to create alarm: alarm {}\n{}".format(
398 raise ClientException(message
)
400 def delete_alarm(self
, name
):
402 data
["delete_alarm_request"] = {}
403 data
["delete_alarm_request"]["alarm_delete_request"] = {}
404 data
["delete_alarm_request"]["alarm_delete_request"]["alarm_uuid"] = name
406 http_code
, resp
= self
._http
.post_cmd(endpoint
='/test/message/alarm_request',
407 postfields_dict
=data
)
408 #print 'HTTP CODE: {}'.format(http_code)
409 #print 'RESP: {}'.format(resp)
410 if http_code
in (200, 201, 202, 204):
411 #resp = json.loads(resp)
412 print('Alarm deleted')
417 msg
= json
.loads(resp
)
420 raise ClientException('error: code: {}, resp: {}'.format(
422 except ClientException
as exc
:
423 message
="failed to delete alarm: alarm {}\n{}".format(
426 raise ClientException(message
)
428 def export_metric(self
, metric
):
430 data
["read_metric_data_request"] = metric
432 http_code
, resp
= self
._http
.post_cmd(endpoint
='/test/message/metric_request',
433 postfields_dict
=data
)
434 #print 'HTTP CODE: {}'.format(http_code)
435 #print 'RESP: {}'.format(resp)
436 if http_code
in (200, 201, 202, 204):
437 #resp = json.loads(resp)
438 return 'Metric exported'
443 msg
= json
.loads(resp
)
446 raise ClientException('error: code: {}, resp: {}'.format(
448 except ClientException
as exc
:
449 message
="failed to export metric: metric {}\n{}".format(
452 raise ClientException(message
)
454 def get_field(self
, ns_name
, field
):
455 nsr
= self
.get(ns_name
)
456 print(yaml
.safe_dump(nsr
))
458 raise NotFound("failed to retrieve ns {}".format(ns_name
))
463 raise NotFound("failed to find {} in ns {}".format(field
, ns_name
))