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