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