f0877fcd8aeeebf90ca75e649eb8869dfda2c6e7
[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 import logging
28
29
30 class Ns(object):
31
32 def __init__(self, http=None, client=None):
33 self._http = http
34 self._client = client
35 self._logger = logging.getLogger('osmclient')
36 self._apiName = '/nslcm'
37 self._apiVersion = '/v1'
38 self._apiResource = '/ns_instances_content'
39 self._apiBase = '{}{}{}'.format(self._apiName,
40 self._apiVersion, self._apiResource)
41
42 # NS '--wait' option
43 def _wait(self, id, deleteFlag=False):
44 self._logger.debug("")
45 # Endpoint to get operation status
46 apiUrlStatus = '{}{}{}'.format(self._apiName, self._apiVersion, '/ns_lcm_op_occs')
47 # Wait for status for NS instance creation/update/deletion
48 WaitForStatus.wait_for_status(
49 'NS',
50 str(id),
51 WaitForStatus.TIMEOUT_NS_OPERATION,
52 apiUrlStatus,
53 self._http.get2_cmd,
54 deleteFlag=deleteFlag)
55
56 def list(self, filter=None):
57 """Returns a list of NS
58 """
59 self._logger.debug("")
60 self._client.get_token()
61 filter_string = ''
62 if filter:
63 filter_string = '?{}'.format(filter)
64 _, resp = self._http.get2_cmd('{}{}'.format(self._apiBase,filter_string))
65 if resp:
66 return json.loads(resp)
67 return list()
68
69 def get(self, name):
70 """Returns an NS based on name or id
71 """
72 self._logger.debug("")
73 self._client.get_token()
74 if utils.validate_uuid4(name):
75 for ns in self.list():
76 if name == ns['_id']:
77 return ns
78 else:
79 for ns in self.list():
80 if name == ns['name']:
81 return ns
82 raise NotFound("ns '{}' not found".format(name))
83
84 def get_individual(self, name):
85 self._logger.debug("")
86 self._client.get_token()
87 ns_id = name
88 if not utils.validate_uuid4(name):
89 for ns in self.list():
90 if name == ns['name']:
91 ns_id = ns['_id']
92 break
93 try:
94 _, resp = self._http.get2_cmd('{}/{}'.format(self._apiBase, ns_id))
95 #resp = self._http.get_cmd('{}/{}/nsd_content'.format(self._apiBase, ns_id))
96 #print(yaml.safe_dump(resp))
97 if resp:
98 return json.loads(resp)
99 except NotFound:
100 raise NotFound("ns '{}' not found".format(name))
101 raise NotFound("ns '{}' not found".format(name))
102
103 def delete(self, name, force=False, wait=False):
104 self._logger.debug("")
105 ns = self.get(name)
106 querystring = ''
107 if force:
108 querystring = '?FORCE=True'
109 http_code, resp = self._http.delete_cmd('{}/{}{}'.format(self._apiBase,
110 ns['_id'], querystring))
111 # print('HTTP CODE: {}'.format(http_code))
112 # print('RESP: {}'.format(resp))
113 if http_code == 202:
114 if wait and resp:
115 resp = json.loads(resp)
116 # For the 'delete' operation, '_id' is used
117 self._wait(resp.get('_id'), deleteFlag=True)
118 else:
119 print('Deletion in progress')
120 elif http_code == 204:
121 print('Deleted')
122 else:
123 msg = resp or ""
124 # if resp:
125 # try:
126 # msg = json.loads(resp)
127 # except ValueError:
128 # msg = resp
129 raise ClientException("failed to delete ns {} - {}".format(name, msg))
130
131 def create(self, nsd_name, nsr_name, account, config=None,
132 ssh_keys=None, description='default description',
133 admin_status='ENABLED', wait=False):
134 self._logger.debug("")
135 self._client.get_token()
136 nsd = self._client.nsd.get(nsd_name)
137
138 vim_account_id = {}
139 wim_account_id = {}
140
141 def get_vim_account_id(vim_account):
142 self._logger.debug("")
143 if vim_account_id.get(vim_account):
144 return vim_account_id[vim_account]
145 vim = self._client.vim.get(vim_account)
146 if vim is None:
147 raise NotFound("cannot find vim account '{}'".format(vim_account))
148 vim_account_id[vim_account] = vim['_id']
149 return vim['_id']
150
151 def get_wim_account_id(wim_account):
152 self._logger.debug("")
153 # wim_account can be False (boolean) to indicate not use wim account
154 if not isinstance(wim_account, str):
155 return wim_account
156 if wim_account_id.get(wim_account):
157 return wim_account_id[wim_account]
158 wim = self._client.wim.get(wim_account)
159 if wim is None:
160 raise NotFound("cannot find wim account '{}'".format(wim_account))
161 wim_account_id[wim_account] = wim['_id']
162 return wim['_id']
163
164 ns = {}
165 ns['nsdId'] = nsd['_id']
166 ns['nsName'] = nsr_name
167 ns['nsDescription'] = description
168 ns['vimAccountId'] = get_vim_account_id(account)
169 #ns['userdata'] = {}
170 #ns['userdata']['key1']='value1'
171 #ns['userdata']['key2']='value2'
172
173 if ssh_keys is not None:
174 ns['ssh_keys'] = []
175 for pubkeyfile in ssh_keys.split(','):
176 with open(pubkeyfile, 'r') as f:
177 ns['ssh_keys'].append(f.read())
178 if config:
179 ns_config = yaml.safe_load(config)
180 if "vim-network-name" in ns_config:
181 ns_config["vld"] = ns_config.pop("vim-network-name")
182 if "vld" in ns_config:
183 for vld in ns_config["vld"]:
184 if vld.get("vim-network-name"):
185 if isinstance(vld["vim-network-name"], dict):
186 vim_network_name_dict = {}
187 for vim_account, vim_net in list(vld["vim-network-name"].items()):
188 vim_network_name_dict[get_vim_account_id(vim_account)] = vim_net
189 vld["vim-network-name"] = vim_network_name_dict
190 if "wim_account" in vld and vld["wim_account"] is not None:
191 vld["wimAccountId"] = get_wim_account_id(vld.pop("wim_account"))
192 ns["vld"] = ns_config["vld"]
193 if "vnf" in ns_config:
194 for vnf in ns_config["vnf"]:
195 if vnf.get("vim_account"):
196 vnf["vimAccountId"] = get_vim_account_id(vnf.pop("vim_account"))
197 ns["vnf"] = ns_config["vnf"]
198
199 if "additionalParamsForNs" in ns_config:
200 ns["additionalParamsForNs"] = ns_config.pop("additionalParamsForNs")
201 if not isinstance(ns["additionalParamsForNs"], dict):
202 raise ValueError("Error at --config 'additionalParamsForNs' must be a dictionary")
203 if "additionalParamsForVnf" in ns_config:
204 ns["additionalParamsForVnf"] = ns_config.pop("additionalParamsForVnf")
205 if not isinstance(ns["additionalParamsForVnf"], list):
206 raise ValueError("Error at --config 'additionalParamsForVnf' must be a list")
207 for additional_param_vnf in ns["additionalParamsForVnf"]:
208 if not isinstance(additional_param_vnf, dict):
209 raise ValueError("Error at --config 'additionalParamsForVnf' items must be dictionaries")
210 if not additional_param_vnf.get("member-vnf-index"):
211 raise ValueError("Error at --config 'additionalParamsForVnf' items must contain "
212 "'member-vnf-index'")
213 if "wim_account" in ns_config:
214 wim_account = ns_config.pop("wim_account")
215 if wim_account is not None:
216 ns['wimAccountId'] = get_wim_account_id(wim_account)
217 if "timeout_ns_deploy" in ns_config:
218 ns["timeout_ns_deploy"] = ns_config.pop("timeout_ns_deploy")
219
220 # print(yaml.safe_dump(ns))
221 try:
222 self._apiResource = '/ns_instances_content'
223 self._apiBase = '{}{}{}'.format(self._apiName,
224 self._apiVersion, self._apiResource)
225 headers = self._client._headers
226 headers['Content-Type'] = 'application/yaml'
227 http_header = ['{}: {}'.format(key,val)
228 for (key,val) in list(headers.items())]
229 self._http.set_http_header(http_header)
230 http_code, resp = self._http.post_cmd(endpoint=self._apiBase,
231 postfields_dict=ns)
232 # print('HTTP CODE: {}'.format(http_code))
233 # print('RESP: {}'.format(resp))
234 #if http_code in (200, 201, 202, 204):
235 if resp:
236 resp = json.loads(resp)
237 if not resp or 'id' not in resp:
238 raise ClientException('unexpected response from server - {} '.format(
239 resp))
240 if wait:
241 # Wait for status for NS instance creation
242 self._wait(resp.get('nslcmop_id'))
243 print(resp['id'])
244 return resp['id']
245 #else:
246 # msg = ""
247 # if resp:
248 # try:
249 # msg = json.loads(resp)
250 # except ValueError:
251 # msg = resp
252 # raise ClientException(msg)
253 except ClientException as exc:
254 message="failed to create ns: {} nsd: {}\nerror:\n{}".format(
255 nsr_name,
256 nsd_name,
257 str(exc))
258 raise ClientException(message)
259
260 def list_op(self, name, filter=None):
261 """Returns the list of operations of a NS
262 """
263 self._logger.debug("")
264 ns = self.get(name)
265 try:
266 self._apiResource = '/ns_lcm_op_occs'
267 self._apiBase = '{}{}{}'.format(self._apiName,
268 self._apiVersion, self._apiResource)
269 filter_string = ''
270 if filter:
271 filter_string = '&{}'.format(filter)
272 http_code, resp = self._http.get2_cmd('{}?nsInstanceId={}'.format(
273 self._apiBase, ns['_id'],
274 filter_string) )
275 #print('HTTP CODE: {}'.format(http_code))
276 #print('RESP: {}'.format(resp))
277 if http_code == 200:
278 if resp:
279 resp = json.loads(resp)
280 return resp
281 else:
282 raise ClientException('unexpected response from server')
283 else:
284 msg = resp or ""
285 # if resp:
286 # try:
287 # resp = json.loads(resp)
288 # msg = resp['detail']
289 # except ValueError:
290 # msg = resp
291 raise ClientException(msg)
292 except ClientException as exc:
293 message="failed to get operation list of NS {}:\nerror:\n{}".format(
294 name,
295 str(exc))
296 raise ClientException(message)
297
298 def get_op(self, operationId):
299 """Returns the status of an operation
300 """
301 self._logger.debug("")
302 self._client.get_token()
303 try:
304 self._apiResource = '/ns_lcm_op_occs'
305 self._apiBase = '{}{}{}'.format(self._apiName,
306 self._apiVersion, self._apiResource)
307 http_code, resp = self._http.get2_cmd('{}/{}'.format(self._apiBase, operationId))
308 #print('HTTP CODE: {}'.format(http_code))
309 #print('RESP: {}'.format(resp))
310 if http_code == 200:
311 if resp:
312 resp = json.loads(resp)
313 return resp
314 else:
315 raise ClientException('unexpected response from server')
316 else:
317 msg = resp or ""
318 # if resp:
319 # try:
320 # resp = json.loads(resp)
321 # msg = resp['detail']
322 # except ValueError:
323 # msg = resp
324 raise ClientException(msg)
325 except ClientException as exc:
326 message="failed to get status of operation {}:\nerror:\n{}".format(
327 operationId,
328 str(exc))
329 raise ClientException(message)
330
331 def exec_op(self, name, op_name, op_data=None, wait=False, ):
332 """Executes an operation on a NS
333 """
334 self._logger.debug("")
335 ns = self.get(name)
336 try:
337 ns = self.get(name)
338 self._apiResource = '/ns_instances'
339 self._apiBase = '{}{}{}'.format(self._apiName,
340 self._apiVersion, self._apiResource)
341 endpoint = '{}/{}/{}'.format(self._apiBase, ns['_id'], op_name)
342 #print('OP_NAME: {}'.format(op_name))
343 #print('OP_DATA: {}'.format(json.dumps(op_data)))
344 http_code, resp = self._http.post_cmd(endpoint=endpoint, postfields_dict=op_data)
345 #print('HTTP CODE: {}'.format(http_code))
346 #print('RESP: {}'.format(resp))
347 #if http_code in (200, 201, 202, 204):
348 if resp:
349 resp = json.loads(resp)
350 if not resp or 'id' not in resp:
351 raise ClientException('unexpected response from server - {}'.format(
352 resp))
353 if wait:
354 # Wait for status for NS instance action
355 # For the 'action' operation, 'id' is used
356 self._wait(resp.get('id'))
357 return resp['id']
358 #else:
359 # msg = ""
360 # if resp:
361 # try:
362 # msg = json.loads(resp)
363 # except ValueError:
364 # msg = resp
365 # raise ClientException(msg)
366 except ClientException as exc:
367 message="failed to exec operation {}:\nerror:\n{}".format(
368 name,
369 str(exc))
370 raise ClientException(message)
371
372 def scale_vnf(self, ns_name, vnf_name, scaling_group, scale_in, scale_out, wait=False):
373 """Scales a VNF by adding/removing VDUs
374 """
375 self._logger.debug("")
376 self._client.get_token()
377 try:
378 op_data={}
379 op_data["scaleType"] = "SCALE_VNF"
380 op_data["scaleVnfData"] = {}
381 if scale_in:
382 op_data["scaleVnfData"]["scaleVnfType"] = "SCALE_IN"
383 else:
384 op_data["scaleVnfData"]["scaleVnfType"] = "SCALE_OUT"
385 op_data["scaleVnfData"]["scaleByStepData"] = {
386 "member-vnf-index": vnf_name,
387 "scaling-group-descriptor": scaling_group,
388 }
389 op_id = self.exec_op(ns_name, op_name='scale', op_data=op_data, wait=wait)
390 print(str(op_id))
391 except ClientException as exc:
392 message="failed to scale vnf {} of ns {}:\nerror:\n{}".format(
393 vnf_name, ns_name, str(exc))
394 raise ClientException(message)
395
396 def create_alarm(self, alarm):
397 self._logger.debug("")
398 self._client.get_token()
399 data = {}
400 data["create_alarm_request"] = {}
401 data["create_alarm_request"]["alarm_create_request"] = alarm
402 try:
403 http_code, resp = self._http.post_cmd(endpoint='/test/message/alarm_request',
404 postfields_dict=data)
405 #print('HTTP CODE: {}'.format(http_code))
406 #print('RESP: {}'.format(resp))
407 # if http_code in (200, 201, 202, 204):
408 # resp = json.loads(resp)
409 print('Alarm created')
410 #else:
411 # msg = ""
412 # if resp:
413 # try:
414 # msg = json.loads(resp)
415 # except ValueError:
416 # msg = resp
417 # raise ClientException('error: code: {}, resp: {}'.format(
418 # http_code, msg))
419 except ClientException as exc:
420 message="failed to create alarm: alarm {}\n{}".format(
421 alarm,
422 str(exc))
423 raise ClientException(message)
424
425 def delete_alarm(self, name):
426 self._logger.debug("")
427 self._client.get_token()
428 data = {}
429 data["delete_alarm_request"] = {}
430 data["delete_alarm_request"]["alarm_delete_request"] = {}
431 data["delete_alarm_request"]["alarm_delete_request"]["alarm_uuid"] = name
432 try:
433 http_code, resp = self._http.post_cmd(endpoint='/test/message/alarm_request',
434 postfields_dict=data)
435 #print('HTTP CODE: {}'.format(http_code))
436 #print('RESP: {}'.format(resp))
437 # if http_code in (200, 201, 202, 204):
438 # resp = json.loads(resp)
439 print('Alarm deleted')
440 #else:
441 # msg = ""
442 # if resp:
443 # try:
444 # msg = json.loads(resp)
445 # except ValueError:
446 # msg = resp
447 # raise ClientException('error: code: {}, resp: {}'.format(
448 # http_code, msg))
449 except ClientException as exc:
450 message="failed to delete alarm: alarm {}\n{}".format(
451 name,
452 str(exc))
453 raise ClientException(message)
454
455 def export_metric(self, metric):
456 self._logger.debug("")
457 self._client.get_token()
458 data = {}
459 data["read_metric_data_request"] = metric
460 try:
461 http_code, resp = self._http.post_cmd(endpoint='/test/message/metric_request',
462 postfields_dict=data)
463 #print('HTTP CODE: {}'.format(http_code))
464 #print('RESP: {}'.format(resp))
465 # if http_code in (200, 201, 202, 204):
466 # resp = json.loads(resp)
467 return 'Metric exported'
468 #else:
469 # msg = ""
470 # if resp:
471 # try:
472 # msg = json.loads(resp)
473 # except ValueError:
474 # msg = resp
475 # raise ClientException('error: code: {}, resp: {}'.format(
476 # http_code, msg))
477 except ClientException as exc:
478 message="failed to export metric: metric {}\n{}".format(
479 metric,
480 str(exc))
481 raise ClientException(message)
482
483 def get_field(self, ns_name, field):
484 self._logger.debug("")
485 nsr = self.get(ns_name)
486 print(yaml.safe_dump(nsr))
487 if nsr is None:
488 raise NotFound("failed to retrieve ns {}".format(ns_name))
489
490 if field in nsr:
491 return nsr[field]
492
493 raise NotFound("failed to find {} in ns {}".format(field, ns_name))
494