Replace deprecated yaml.load by yaml.safe_load
[osm/osmclient.git] / osmclient / sol005 / ns.py
1 # Copyright 2018 Telefonica
2 #
3 # All Rights Reserved.
4 #
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
8 #
9 # http://www.apache.org/licenses/LICENSE-2.0
10 #
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
15 # under the License.
16
17 """
18 OSM ns API handling
19 """
20
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 import yaml
26 import json
27
28
29 class Ns(object):
30
31 def __init__(self, http=None, client=None):
32 self._http = http
33 self._client = client
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)
39
40 # NS '--wait' option
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(
46 'NS',
47 str(id),
48 WaitForStatus.TIMEOUT_NS_OPERATION,
49 apiUrlStatus,
50 self._http.get2_cmd,
51 deleteFlag=deleteFlag)
52
53 def list(self, filter=None):
54 """Returns a list of NS
55 """
56 filter_string = ''
57 if filter:
58 filter_string = '?{}'.format(filter)
59 resp = self._http.get_cmd('{}{}'.format(self._apiBase,filter_string))
60 if resp:
61 return resp
62 return list()
63
64 def get(self, name):
65 """Returns an NS based on name or id
66 """
67 if utils.validate_uuid4(name):
68 for ns in self.list():
69 if name == ns['_id']:
70 return ns
71 else:
72 for ns in self.list():
73 if name == ns['name']:
74 return ns
75 raise NotFound("ns {} not found".format(name))
76
77 def get_individual(self, name):
78 ns_id = name
79 if not utils.validate_uuid4(name):
80 for ns in self.list():
81 if name == ns['name']:
82 ns_id = ns['_id']
83 break
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))
87 if resp:
88 return resp
89 raise NotFound("ns {} not found".format(name))
90
91 def delete(self, name, force=False, wait=False):
92 ns = self.get(name)
93 querystring = ''
94 if force:
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))
100 if http_code == 202:
101 if wait and resp:
102 resp = json.loads(resp)
103 # For the 'delete' operation, '_id' is used
104 self._wait(resp.get('_id'), deleteFlag=True)
105 else:
106 print('Deletion in progress')
107 elif http_code == 204:
108 print('Deleted')
109 else:
110 msg = ""
111 if resp:
112 try:
113 msg = json.loads(resp)
114 except ValueError:
115 msg = resp
116 raise ClientException("failed to delete ns {} - {}".format(name, msg))
117
118 def create(self, nsd_name, nsr_name, account, config=None,
119 ssh_keys=None, description='default description',
120 admin_status='ENABLED', wait=False):
121
122 nsd = self._client.nsd.get(nsd_name)
123
124 vim_account_id = {}
125 wim_account_id = {}
126
127 def get_vim_account_id(vim_account):
128 if vim_account_id.get(vim_account):
129 return vim_account_id[vim_account]
130
131 vim = self._client.vim.get(vim_account)
132 if vim is None:
133 raise NotFound("cannot find vim account '{}'".format(vim_account))
134 vim_account_id[vim_account] = vim['_id']
135 return vim['_id']
136
137 def get_wim_account_id(wim_account):
138 if not isinstance(wim_account, str):
139 return wim_account
140 if wim_account_id.get(wim_account):
141 return wim_account_id[wim_account]
142
143 wim = self._client.wim.get(wim_account)
144 if wim is None:
145 raise NotFound("cannot find wim account '{}'".format(wim_account))
146 wim_account_id[wim_account] = wim['_id']
147 return wim['_id']
148
149 ns = {}
150 ns['nsdId'] = nsd['_id']
151 ns['nsName'] = nsr_name
152 ns['nsDescription'] = description
153 ns['vimAccountId'] = get_vim_account_id(account)
154 #ns['userdata'] = {}
155 #ns['userdata']['key1']='value1'
156 #ns['userdata']['key2']='value2'
157
158 if ssh_keys is not None:
159 ns['ssh_keys'] = []
160 for pubkeyfile in ssh_keys.split(','):
161 with open(pubkeyfile, 'r') as f:
162 ns['ssh_keys'].append(f.read())
163 if config:
164 ns_config = yaml.safe_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"]
183
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)
205
206 # print(yaml.safe_dump(ns))
207 try:
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,
217 postfields_dict=ns)
218 # print('HTTP CODE: {}'.format(http_code))
219 # print('RESP: {}'.format(resp))
220 if http_code in (200, 201, 202, 204):
221 if resp:
222 resp = json.loads(resp)
223 if not resp or 'id' not in resp:
224 raise ClientException('unexpected response from server - {} '.format(
225 resp))
226 if wait:
227 # Wait for status for NS instance creation
228 self._wait(resp.get('nslcmop_id'))
229 return resp['id']
230 else:
231 msg = ""
232 if resp:
233 try:
234 msg = json.loads(resp)
235 except ValueError:
236 msg = resp
237 raise ClientException(msg)
238 except ClientException as exc:
239 message="failed to create ns: {} nsd: {}\nerror:\n{}".format(
240 nsr_name,
241 nsd_name,
242 exc.message)
243 raise ClientException(message)
244
245 def list_op(self, name, filter=None):
246 """Returns the list of operations of a NS
247 """
248 ns = self.get(name)
249 try:
250 self._apiResource = '/ns_lcm_op_occs'
251 self._apiBase = '{}{}{}'.format(self._apiName,
252 self._apiVersion, self._apiResource)
253 filter_string = ''
254 if filter:
255 filter_string = '&{}'.format(filter)
256 http_code, resp = self._http.get2_cmd('{}?nsInstanceId={}'.format(
257 self._apiBase, ns['_id'],
258 filter_string) )
259 #print('HTTP CODE: {}'.format(http_code))
260 #print('RESP: {}'.format(resp))
261 if http_code == 200:
262 if resp:
263 resp = json.loads(resp)
264 return resp
265 else:
266 raise ClientException('unexpected response from server')
267 else:
268 msg = ""
269 if resp:
270 try:
271 resp = json.loads(resp)
272 msg = resp['detail']
273 except ValueError:
274 msg = resp
275 raise ClientException(msg)
276 except ClientException as exc:
277 message="failed to get operation list of NS {}:\nerror:\n{}".format(
278 name,
279 exc.message)
280 raise ClientException(message)
281
282 def get_op(self, operationId):
283 """Returns the status of an operation
284 """
285 try:
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))
292 if http_code == 200:
293 if resp:
294 resp = json.loads(resp)
295 return resp
296 else:
297 raise ClientException('unexpected response from server')
298 else:
299 msg = ""
300 if resp:
301 try:
302 resp = json.loads(resp)
303 msg = resp['detail']
304 except ValueError:
305 msg = resp
306 raise ClientException(msg)
307 except ClientException as exc:
308 message="failed to get status of operation {}:\nerror:\n{}".format(
309 operationId,
310 exc.message)
311 raise ClientException(message)
312
313 def exec_op(self, name, op_name, op_data=None, wait=False):
314 """Executes an operation on a NS
315 """
316 ns = self.get(name)
317 try:
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):
328 if resp:
329 resp = json.loads(resp)
330 if not resp or 'id' not in resp:
331 raise ClientException('unexpected response from server - {}'.format(
332 resp))
333 if wait:
334 # Wait for status for NS instance action
335 # For the 'action' operation, 'id' is used
336 self._wait(resp.get('id'))
337 print(resp['id'])
338 else:
339 msg = ""
340 if resp:
341 try:
342 msg = json.loads(resp)
343 except ValueError:
344 msg = resp
345 raise ClientException(msg)
346 except ClientException as exc:
347 message="failed to exec operation {}:\nerror:\n{}".format(
348 name,
349 exc.message)
350 raise ClientException(message)
351
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
354 """
355 try:
356 op_data={}
357 op_data["scaleType"] = "SCALE_VNF"
358 op_data["scaleVnfData"] = {}
359 if scale_in:
360 op_data["scaleVnfData"]["scaleVnfType"] = "SCALE_IN"
361 else:
362 op_data["scaleVnfData"]["scaleVnfType"] = "SCALE_OUT"
363 op_data["scaleVnfData"]["scaleByStepData"] = {
364 "member-vnf-index": vnf_name,
365 "scaling-group-descriptor": scaling_group,
366 }
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)
372
373 def create_alarm(self, alarm):
374 data = {}
375 data["create_alarm_request"] = {}
376 data["create_alarm_request"]["alarm_create_request"] = alarm
377 try:
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')
385 else:
386 msg = ""
387 if resp:
388 try:
389 msg = json.loads(resp)
390 except ValueError:
391 msg = resp
392 raise ClientException('error: code: {}, resp: {}'.format(
393 http_code, msg))
394 except ClientException as exc:
395 message="failed to create alarm: alarm {}\n{}".format(
396 alarm,
397 exc.message)
398 raise ClientException(message)
399
400 def delete_alarm(self, name):
401 data = {}
402 data["delete_alarm_request"] = {}
403 data["delete_alarm_request"]["alarm_delete_request"] = {}
404 data["delete_alarm_request"]["alarm_delete_request"]["alarm_uuid"] = name
405 try:
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')
413 else:
414 msg = ""
415 if resp:
416 try:
417 msg = json.loads(resp)
418 except ValueError:
419 msg = resp
420 raise ClientException('error: code: {}, resp: {}'.format(
421 http_code, msg))
422 except ClientException as exc:
423 message="failed to delete alarm: alarm {}\n{}".format(
424 name,
425 exc.message)
426 raise ClientException(message)
427
428 def export_metric(self, metric):
429 data = {}
430 data["read_metric_data_request"] = metric
431 try:
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'
439 else:
440 msg = ""
441 if resp:
442 try:
443 msg = json.loads(resp)
444 except ValueError:
445 msg = resp
446 raise ClientException('error: code: {}, resp: {}'.format(
447 http_code, msg))
448 except ClientException as exc:
449 message="failed to export metric: metric {}\n{}".format(
450 metric,
451 exc.message)
452 raise ClientException(message)
453
454 def get_field(self, ns_name, field):
455 nsr = self.get(ns_name)
456 print(yaml.safe_dump(nsr))
457 if nsr is None:
458 raise NotFound("failed to retrieve ns {}".format(ns_name))
459
460 if field in nsr:
461 return nsr[field]
462
463 raise NotFound("failed to find {} in ns {}".format(field, ns_name))