Fix bug 946 - removed unnecessary check for additionalParams
[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 self._client.get_token()
57 filter_string = ''
58 if filter:
59 filter_string = '?{}'.format(filter)
60 resp = self._http.get_cmd('{}{}'.format(self._apiBase,filter_string))
61 if resp:
62 return resp
63 return list()
64
65 def get(self, name):
66 """Returns an NS based on name or id
67 """
68 self._client.get_token()
69 if utils.validate_uuid4(name):
70 for ns in self.list():
71 if name == ns['_id']:
72 return ns
73 else:
74 for ns in self.list():
75 if name == ns['name']:
76 return ns
77 raise NotFound("ns {} not found".format(name))
78
79 def get_individual(self, name):
80 self._client.get_token()
81 ns_id = name
82 if not utils.validate_uuid4(name):
83 for ns in self.list():
84 if name == ns['name']:
85 ns_id = ns['_id']
86 break
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))
90 if resp:
91 return resp
92 raise NotFound("ns {} not found".format(name))
93
94 def delete(self, name, force=False, wait=False):
95 ns = self.get(name)
96 querystring = ''
97 if force:
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))
103 if http_code == 202:
104 if wait and resp:
105 resp = json.loads(resp)
106 # For the 'delete' operation, '_id' is used
107 self._wait(resp.get('_id'), deleteFlag=True)
108 else:
109 print('Deletion in progress')
110 elif http_code == 204:
111 print('Deleted')
112 else:
113 msg = ""
114 if resp:
115 try:
116 msg = json.loads(resp)
117 except ValueError:
118 msg = resp
119 raise ClientException("failed to delete ns {} - {}".format(name, msg))
120
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)
126
127 vim_account_id = {}
128 wim_account_id = {}
129
130 def get_vim_account_id(vim_account):
131 if vim_account_id.get(vim_account):
132 return vim_account_id[vim_account]
133
134 vim = self._client.vim.get(vim_account)
135 if vim is None:
136 raise NotFound("cannot find vim account '{}'".format(vim_account))
137 vim_account_id[vim_account] = vim['_id']
138 return vim['_id']
139
140 def get_wim_account_id(wim_account):
141 if not isinstance(wim_account, str):
142 return wim_account
143 if wim_account_id.get(wim_account):
144 return wim_account_id[wim_account]
145
146 wim = self._client.wim.get(wim_account)
147 if wim is None:
148 raise NotFound("cannot find wim account '{}'".format(wim_account))
149 wim_account_id[wim_account] = wim['_id']
150 return wim['_id']
151
152 ns = {}
153 ns['nsdId'] = nsd['_id']
154 ns['nsName'] = nsr_name
155 ns['nsDescription'] = description
156 ns['vimAccountId'] = get_vim_account_id(account)
157 #ns['userdata'] = {}
158 #ns['userdata']['key1']='value1'
159 #ns['userdata']['key2']='value2'
160
161 if ssh_keys is not None:
162 ns['ssh_keys'] = []
163 for pubkeyfile in ssh_keys.split(','):
164 with open(pubkeyfile, 'r') as f:
165 ns['ssh_keys'].append(f.read())
166 if config:
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"]
186
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)
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 str(exc))
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 str(exc))
280 raise ClientException(message)
281
282 def get_op(self, operationId):
283 """Returns the status of an operation
284 """
285 self._client.get_token()
286 try:
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))
293 if http_code == 200:
294 if resp:
295 resp = json.loads(resp)
296 return resp
297 else:
298 raise ClientException('unexpected response from server')
299 else:
300 msg = ""
301 if resp:
302 try:
303 resp = json.loads(resp)
304 msg = resp['detail']
305 except ValueError:
306 msg = resp
307 raise ClientException(msg)
308 except ClientException as exc:
309 message="failed to get status of operation {}:\nerror:\n{}".format(
310 operationId,
311 str(exc))
312 raise ClientException(message)
313
314 def exec_op(self, name, op_name, op_data=None, wait=False, ):
315 """Executes an operation on a NS
316 """
317 try:
318 ns = self.get(name)
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):
329 if resp:
330 resp = json.loads(resp)
331 if not resp or 'id' not in resp:
332 raise ClientException('unexpected response from server - {}'.format(
333 resp))
334 if wait:
335 # Wait for status for NS instance action
336 # For the 'action' operation, 'id' is used
337 self._wait(resp.get('id'))
338 return resp['id']
339 else:
340 msg = ""
341 if resp:
342 try:
343 msg = json.loads(resp)
344 except ValueError:
345 msg = resp
346 raise ClientException(msg)
347 except ClientException as exc:
348 message="failed to exec operation {}:\nerror:\n{}".format(
349 name,
350 str(exc))
351 raise ClientException(message)
352
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
355 """
356 self._client.get_token()
357 try:
358 op_data={}
359 op_data["scaleType"] = "SCALE_VNF"
360 op_data["scaleVnfData"] = {}
361 if scale_in:
362 op_data["scaleVnfData"]["scaleVnfType"] = "SCALE_IN"
363 else:
364 op_data["scaleVnfData"]["scaleVnfType"] = "SCALE_OUT"
365 op_data["scaleVnfData"]["scaleByStepData"] = {
366 "member-vnf-index": vnf_name,
367 "scaling-group-descriptor": scaling_group,
368 }
369 op_id = self.exec_op(ns_name, op_name='scale', op_data=op_data, wait=wait)
370 print(str(op_id))
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)
375
376 def create_alarm(self, alarm):
377 self._client.get_token()
378 data = {}
379 data["create_alarm_request"] = {}
380 data["create_alarm_request"]["alarm_create_request"] = alarm
381 try:
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')
389 else:
390 msg = ""
391 if resp:
392 try:
393 msg = json.loads(resp)
394 except ValueError:
395 msg = resp
396 raise ClientException('error: code: {}, resp: {}'.format(
397 http_code, msg))
398 except ClientException as exc:
399 message="failed to create alarm: alarm {}\n{}".format(
400 alarm,
401 str(exc))
402 raise ClientException(message)
403
404 def delete_alarm(self, name):
405 self._client.get_token()
406 data = {}
407 data["delete_alarm_request"] = {}
408 data["delete_alarm_request"]["alarm_delete_request"] = {}
409 data["delete_alarm_request"]["alarm_delete_request"]["alarm_uuid"] = name
410 try:
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')
418 else:
419 msg = ""
420 if resp:
421 try:
422 msg = json.loads(resp)
423 except ValueError:
424 msg = resp
425 raise ClientException('error: code: {}, resp: {}'.format(
426 http_code, msg))
427 except ClientException as exc:
428 message="failed to delete alarm: alarm {}\n{}".format(
429 name,
430 str(exc))
431 raise ClientException(message)
432
433 def export_metric(self, metric):
434 self._client.get_token()
435 data = {}
436 data["read_metric_data_request"] = metric
437 try:
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'
445 else:
446 msg = ""
447 if resp:
448 try:
449 msg = json.loads(resp)
450 except ValueError:
451 msg = resp
452 raise ClientException('error: code: {}, resp: {}'.format(
453 http_code, msg))
454 except ClientException as exc:
455 message="failed to export metric: metric {}\n{}".format(
456 metric,
457 str(exc))
458 raise ClientException(message)
459
460 def get_field(self, ns_name, field):
461 nsr = self.get(ns_name)
462 print(yaml.safe_dump(nsr))
463 if nsr is None:
464 raise NotFound("failed to retrieve ns {}".format(ns_name))
465
466 if field in nsr:
467 return nsr[field]
468
469 raise NotFound("failed to find {} in ns {}".format(field, ns_name))