1165b3de9bbb548931f38342c332241a1edd5a7b
[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 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)
208
209 # print(yaml.safe_dump(ns))
210 try:
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,
220 postfields_dict=ns)
221 # print('HTTP CODE: {}'.format(http_code))
222 # print('RESP: {}'.format(resp))
223 if http_code in (200, 201, 202, 204):
224 if resp:
225 resp = json.loads(resp)
226 if not resp or 'id' not in resp:
227 raise ClientException('unexpected response from server - {} '.format(
228 resp))
229 if wait:
230 # Wait for status for NS instance creation
231 self._wait(resp.get('nslcmop_id'))
232 return resp['id']
233 else:
234 msg = ""
235 if resp:
236 try:
237 msg = json.loads(resp)
238 except ValueError:
239 msg = resp
240 raise ClientException(msg)
241 except ClientException as exc:
242 message="failed to create ns: {} nsd: {}\nerror:\n{}".format(
243 nsr_name,
244 nsd_name,
245 str(exc))
246 raise ClientException(message)
247
248 def list_op(self, name, filter=None):
249 """Returns the list of operations of a NS
250 """
251 ns = self.get(name)
252 try:
253 self._apiResource = '/ns_lcm_op_occs'
254 self._apiBase = '{}{}{}'.format(self._apiName,
255 self._apiVersion, self._apiResource)
256 filter_string = ''
257 if filter:
258 filter_string = '&{}'.format(filter)
259 http_code, resp = self._http.get2_cmd('{}?nsInstanceId={}'.format(
260 self._apiBase, ns['_id'],
261 filter_string) )
262 #print('HTTP CODE: {}'.format(http_code))
263 #print('RESP: {}'.format(resp))
264 if http_code == 200:
265 if resp:
266 resp = json.loads(resp)
267 return resp
268 else:
269 raise ClientException('unexpected response from server')
270 else:
271 msg = ""
272 if resp:
273 try:
274 resp = json.loads(resp)
275 msg = resp['detail']
276 except ValueError:
277 msg = resp
278 raise ClientException(msg)
279 except ClientException as exc:
280 message="failed to get operation list of NS {}:\nerror:\n{}".format(
281 name,
282 str(exc))
283 raise ClientException(message)
284
285 def get_op(self, operationId):
286 """Returns the status of an operation
287 """
288 self._client.get_token()
289 try:
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))
296 if http_code == 200:
297 if resp:
298 resp = json.loads(resp)
299 return resp
300 else:
301 raise ClientException('unexpected response from server')
302 else:
303 msg = ""
304 if resp:
305 try:
306 resp = json.loads(resp)
307 msg = resp['detail']
308 except ValueError:
309 msg = resp
310 raise ClientException(msg)
311 except ClientException as exc:
312 message="failed to get status of operation {}:\nerror:\n{}".format(
313 operationId,
314 str(exc))
315 raise ClientException(message)
316
317 def exec_op(self, name, op_name, op_data=None, wait=False, ):
318 """Executes an operation on a NS
319 """
320 try:
321 ns = self.get(name)
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):
332 if resp:
333 resp = json.loads(resp)
334 if not resp or 'id' not in resp:
335 raise ClientException('unexpected response from server - {}'.format(
336 resp))
337 if wait:
338 # Wait for status for NS instance action
339 # For the 'action' operation, 'id' is used
340 self._wait(resp.get('id'))
341 return resp['id']
342 else:
343 msg = ""
344 if resp:
345 try:
346 msg = json.loads(resp)
347 except ValueError:
348 msg = resp
349 raise ClientException(msg)
350 except ClientException as exc:
351 message="failed to exec operation {}:\nerror:\n{}".format(
352 name,
353 str(exc))
354 raise ClientException(message)
355
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
358 """
359 self._client.get_token()
360 try:
361 op_data={}
362 op_data["scaleType"] = "SCALE_VNF"
363 op_data["scaleVnfData"] = {}
364 if scale_in:
365 op_data["scaleVnfData"]["scaleVnfType"] = "SCALE_IN"
366 else:
367 op_data["scaleVnfData"]["scaleVnfType"] = "SCALE_OUT"
368 op_data["scaleVnfData"]["scaleByStepData"] = {
369 "member-vnf-index": vnf_name,
370 "scaling-group-descriptor": scaling_group,
371 }
372 op_id = self.exec_op(ns_name, op_name='scale', op_data=op_data, wait=wait)
373 print(str(op_id))
374 except ClientException as exc:
375 message="failed to scale vnf {} of ns {}:\nerror:\n{}".format(
376 vnf_name, ns_name, str(exc))
377 raise ClientException(message)
378
379 def create_alarm(self, alarm):
380 self._client.get_token()
381 data = {}
382 data["create_alarm_request"] = {}
383 data["create_alarm_request"]["alarm_create_request"] = alarm
384 try:
385 http_code, resp = self._http.post_cmd(endpoint='/test/message/alarm_request',
386 postfields_dict=data)
387 #print('HTTP CODE: {}'.format(http_code))
388 #print('RESP: {}'.format(resp))
389 if http_code in (200, 201, 202, 204):
390 #resp = json.loads(resp)
391 print('Alarm created')
392 else:
393 msg = ""
394 if resp:
395 try:
396 msg = json.loads(resp)
397 except ValueError:
398 msg = resp
399 raise ClientException('error: code: {}, resp: {}'.format(
400 http_code, msg))
401 except ClientException as exc:
402 message="failed to create alarm: alarm {}\n{}".format(
403 alarm,
404 str(exc))
405 raise ClientException(message)
406
407 def delete_alarm(self, name):
408 self._client.get_token()
409 data = {}
410 data["delete_alarm_request"] = {}
411 data["delete_alarm_request"]["alarm_delete_request"] = {}
412 data["delete_alarm_request"]["alarm_delete_request"]["alarm_uuid"] = name
413 try:
414 http_code, resp = self._http.post_cmd(endpoint='/test/message/alarm_request',
415 postfields_dict=data)
416 #print('HTTP CODE: {}'.format(http_code))
417 #print('RESP: {}'.format(resp))
418 if http_code in (200, 201, 202, 204):
419 #resp = json.loads(resp)
420 print('Alarm deleted')
421 else:
422 msg = ""
423 if resp:
424 try:
425 msg = json.loads(resp)
426 except ValueError:
427 msg = resp
428 raise ClientException('error: code: {}, resp: {}'.format(
429 http_code, msg))
430 except ClientException as exc:
431 message="failed to delete alarm: alarm {}\n{}".format(
432 name,
433 str(exc))
434 raise ClientException(message)
435
436 def export_metric(self, metric):
437 self._client.get_token()
438 data = {}
439 data["read_metric_data_request"] = metric
440 try:
441 http_code, resp = self._http.post_cmd(endpoint='/test/message/metric_request',
442 postfields_dict=data)
443 #print('HTTP CODE: {}'.format(http_code))
444 #print('RESP: {}'.format(resp))
445 if http_code in (200, 201, 202, 204):
446 #resp = json.loads(resp)
447 return 'Metric exported'
448 else:
449 msg = ""
450 if resp:
451 try:
452 msg = json.loads(resp)
453 except ValueError:
454 msg = resp
455 raise ClientException('error: code: {}, resp: {}'.format(
456 http_code, msg))
457 except ClientException as exc:
458 message="failed to export metric: metric {}\n{}".format(
459 metric,
460 str(exc))
461 raise ClientException(message)
462
463 def get_field(self, ns_name, field):
464 nsr = self.get(ns_name)
465 print(yaml.safe_dump(nsr))
466 if nsr is None:
467 raise NotFound("failed to retrieve ns {}".format(ns_name))
468
469 if field in nsr:
470 return nsr[field]
471
472 raise NotFound("failed to find {} in ns {}".format(field, ns_name))