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