(bug 650) adding WIM account instantiation parameter.
[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, wim_account=None, 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 if wim_account: # at this point is a string or None
136 ns['wimAccountId'] = get_wim_account_id(yaml.load(wim_account))
137 #ns['userdata'] = {}
138 #ns['userdata']['key1']='value1'
139 #ns['userdata']['key2']='value2'
140
141 if ssh_keys is not None:
142 ns['ssh_keys'] = []
143 for pubkeyfile in ssh_keys.split(','):
144 with open(pubkeyfile, 'r') as f:
145 ns['ssh_keys'].append(f.read())
146 if config:
147 ns_config = yaml.load(config)
148 if "vim-network-name" in ns_config:
149 ns_config["vld"] = ns_config.pop("vim-network-name")
150 if "vld" in ns_config:
151 for vld in ns_config["vld"]:
152 if vld.get("vim-network-name"):
153 if isinstance(vld["vim-network-name"], dict):
154 vim_network_name_dict = {}
155 for vim_account, vim_net in list(vld["vim-network-name"].items()):
156 vim_network_name_dict[get_vim_account_id(vim_account)] = vim_net
157 vld["vim-network-name"] = vim_network_name_dict
158 if "wim_account" in vld and vld["wim_account"] is not None:
159 vld["wimAccountId"] = get_wim_account_id(vld.pop("wim_account"))
160 ns["vld"] = ns_config["vld"]
161 if "vnf" in ns_config:
162 for vnf in ns_config["vnf"]:
163 if vnf.get("vim_account"):
164 vnf["vimAccountId"] = get_vim_account_id(vnf.pop("vim_account"))
165 ns["vnf"] = ns_config["vnf"]
166
167 if "additionalParamsForNs" in ns_config:
168 ns["additionalParamsForNs"] = ns_config.pop("additionalParamsForNs")
169 if not isinstance(ns["additionalParamsForNs"], dict):
170 raise ValueError("Error at --config 'additionalParamsForNs' must be a dictionary")
171 if "additionalParamsForVnf" in ns_config:
172 ns["additionalParamsForVnf"] = ns_config.pop("additionalParamsForVnf")
173 if not isinstance(ns["additionalParamsForVnf"], list):
174 raise ValueError("Error at --config 'additionalParamsForVnf' must be a list")
175 for additional_param_vnf in ns["additionalParamsForVnf"]:
176 if not isinstance(additional_param_vnf, dict):
177 raise ValueError("Error at --config 'additionalParamsForVnf' items must be dictionaries")
178 if not additional_param_vnf.get("member-vnf-index"):
179 raise ValueError("Error at --config 'additionalParamsForVnf' items must contain "
180 "'member-vnf-index'")
181 if not additional_param_vnf.get("additionalParams"):
182 raise ValueError("Error at --config 'additionalParamsForVnf' items must contain "
183 "'additionalParams'")
184
185 # print yaml.safe_dump(ns)
186 try:
187 self._apiResource = '/ns_instances_content'
188 self._apiBase = '{}{}{}'.format(self._apiName,
189 self._apiVersion, self._apiResource)
190 headers = self._client._headers
191 headers['Content-Type'] = 'application/yaml'
192 http_header = ['{}: {}'.format(key,val)
193 for (key,val) in list(headers.items())]
194 self._http.set_http_header(http_header)
195 http_code, resp = self._http.post_cmd(endpoint=self._apiBase,
196 postfields_dict=ns)
197 #print 'HTTP CODE: {}'.format(http_code)
198 #print 'RESP: {}'.format(resp)
199 if http_code in (200, 201, 202, 204):
200 if resp:
201 resp = json.loads(resp)
202 if not resp or 'id' not in resp:
203 raise ClientException('unexpected response from server - {} '.format(
204 resp))
205 return resp['id']
206 else:
207 msg = ""
208 if resp:
209 try:
210 msg = json.loads(resp)
211 except ValueError:
212 msg = resp
213 raise ClientException(msg)
214 except ClientException as exc:
215 message="failed to create ns: {} nsd: {}\nerror:\n{}".format(
216 nsr_name,
217 nsd_name,
218 exc.message)
219 raise ClientException(message)
220
221 def list_op(self, name, filter=None):
222 """Returns the list of operations of a NS
223 """
224 ns = self.get(name)
225 try:
226 self._apiResource = '/ns_lcm_op_occs'
227 self._apiBase = '{}{}{}'.format(self._apiName,
228 self._apiVersion, self._apiResource)
229 filter_string = ''
230 if filter:
231 filter_string = '&{}'.format(filter)
232 http_code, resp = self._http.get2_cmd('{}?nsInstanceId={}'.format(
233 self._apiBase, ns['_id'],
234 filter_string) )
235 #print 'HTTP CODE: {}'.format(http_code)
236 #print 'RESP: {}'.format(resp)
237 if http_code == 200:
238 if resp:
239 resp = json.loads(resp)
240 return resp
241 else:
242 raise ClientException('unexpected response from server')
243 else:
244 msg = ""
245 if resp:
246 try:
247 resp = json.loads(resp)
248 msg = resp['detail']
249 except ValueError:
250 msg = resp
251 raise ClientException(msg)
252 except ClientException as exc:
253 message="failed to get operation list of NS {}:\nerror:\n{}".format(
254 name,
255 exc.message)
256 raise ClientException(message)
257
258 def get_op(self, operationId):
259 """Returns the status of an operation
260 """
261 try:
262 self._apiResource = '/ns_lcm_op_occs'
263 self._apiBase = '{}{}{}'.format(self._apiName,
264 self._apiVersion, self._apiResource)
265 http_code, resp = self._http.get2_cmd('{}/{}'.format(self._apiBase, operationId))
266 #print 'HTTP CODE: {}'.format(http_code)
267 #print 'RESP: {}'.format(resp)
268 if http_code == 200:
269 if resp:
270 resp = json.loads(resp)
271 return resp
272 else:
273 raise ClientException('unexpected response from server')
274 else:
275 msg = ""
276 if resp:
277 try:
278 resp = json.loads(resp)
279 msg = resp['detail']
280 except ValueError:
281 msg = resp
282 raise ClientException(msg)
283 except ClientException as exc:
284 message="failed to get status of operation {}:\nerror:\n{}".format(
285 operationId,
286 exc.message)
287 raise ClientException(message)
288
289 def exec_op(self, name, op_name, op_data=None):
290 """Executes an operation on a NS
291 """
292 ns = self.get(name)
293 try:
294 self._apiResource = '/ns_instances'
295 self._apiBase = '{}{}{}'.format(self._apiName,
296 self._apiVersion, self._apiResource)
297 endpoint = '{}/{}/{}'.format(self._apiBase, ns['_id'], op_name)
298 #print 'OP_NAME: {}'.format(op_name)
299 #print 'OP_DATA: {}'.format(json.dumps(op_data))
300 http_code, resp = self._http.post_cmd(endpoint=endpoint, postfields_dict=op_data)
301 #print 'HTTP CODE: {}'.format(http_code)
302 #print 'RESP: {}'.format(resp)
303 if http_code in (200, 201, 202, 204):
304 if resp:
305 resp = json.loads(resp)
306 if not resp or 'id' not in resp:
307 raise ClientException('unexpected response from server - {}'.format(
308 resp))
309 print(resp['id'])
310 else:
311 msg = ""
312 if resp:
313 try:
314 msg = json.loads(resp)
315 except ValueError:
316 msg = resp
317 raise ClientException(msg)
318 except ClientException as exc:
319 message="failed to exec operation {}:\nerror:\n{}".format(
320 name,
321 exc.message)
322 raise ClientException(message)
323
324 def scale_vnf(self, ns_name, vnf_name, scaling_group, scale_in, scale_out):
325 """Scales a VNF by adding/removing VDUs
326 """
327 try:
328 op_data={}
329 op_data["scaleType"] = "SCALE_VNF"
330 op_data["scaleVnfData"] = {}
331 if scale_in:
332 op_data["scaleVnfData"]["scaleVnfType"] = "SCALE_IN"
333 else:
334 op_data["scaleVnfData"]["scaleVnfType"] = "SCALE_OUT"
335 op_data["scaleVnfData"]["scaleByStepData"] = {
336 "member-vnf-index": vnf_name,
337 "scaling-group-descriptor": scaling_group,
338 }
339 self.exec_op(ns_name, op_name='scale', op_data=op_data)
340 except ClientException as exc:
341 message="failed to scale vnf {} of ns {}:\nerror:\n{}".format(
342 vnf_name, ns_name, exc.message)
343 raise ClientException(message)
344
345 def create_alarm(self, alarm):
346 data = {}
347 data["create_alarm_request"] = {}
348 data["create_alarm_request"]["alarm_create_request"] = alarm
349 try:
350 http_code, resp = self._http.post_cmd(endpoint='/test/message/alarm_request',
351 postfields_dict=data)
352 #print 'HTTP CODE: {}'.format(http_code)
353 #print 'RESP: {}'.format(resp)
354 if http_code in (200, 201, 202, 204):
355 #resp = json.loads(resp)
356 print('Alarm created')
357 else:
358 msg = ""
359 if resp:
360 try:
361 msg = json.loads(resp)
362 except ValueError:
363 msg = resp
364 raise ClientException('error: code: {}, resp: {}'.format(
365 http_code, msg))
366 except ClientException as exc:
367 message="failed to create alarm: alarm {}\n{}".format(
368 alarm,
369 exc.message)
370 raise ClientException(message)
371
372 def delete_alarm(self, name):
373 data = {}
374 data["delete_alarm_request"] = {}
375 data["delete_alarm_request"]["alarm_delete_request"] = {}
376 data["delete_alarm_request"]["alarm_delete_request"]["alarm_uuid"] = name
377 try:
378 http_code, resp = self._http.post_cmd(endpoint='/test/message/alarm_request',
379 postfields_dict=data)
380 #print 'HTTP CODE: {}'.format(http_code)
381 #print 'RESP: {}'.format(resp)
382 if http_code in (200, 201, 202, 204):
383 #resp = json.loads(resp)
384 print('Alarm deleted')
385 else:
386 msg = ""
387 if resp:
388 try:
389 msg = json.loads(resp)
390 except ValueError:
391 msg = resp
392 raise ClientException('error: code: {}, resp: {}'.format(
393 http_code, msg))
394 except ClientException as exc:
395 message="failed to delete alarm: alarm {}\n{}".format(
396 name,
397 exc.message)
398 raise ClientException(message)
399
400 def export_metric(self, metric):
401 data = {}
402 data["read_metric_data_request"] = metric
403 try:
404 http_code, resp = self._http.post_cmd(endpoint='/test/message/metric_request',
405 postfields_dict=data)
406 #print 'HTTP CODE: {}'.format(http_code)
407 #print 'RESP: {}'.format(resp)
408 if http_code in (200, 201, 202, 204):
409 #resp = json.loads(resp)
410 return 'Metric exported'
411 else:
412 msg = ""
413 if resp:
414 try:
415 msg = json.loads(resp)
416 except ValueError:
417 msg = resp
418 raise ClientException('error: code: {}, resp: {}'.format(
419 http_code, msg))
420 except ClientException as exc:
421 message="failed to export metric: metric {}\n{}".format(
422 metric,
423 exc.message)
424 raise ClientException(message)
425
426 def get_field(self, ns_name, field):
427 nsr = self.get(ns_name)
428 if nsr is None:
429 raise NotFound("failed to retrieve ns {}".format(ns_name))
430
431 if field in nsr:
432 return nsr[field]
433
434 raise NotFound("failed to find {} in ns {}".format(field, ns_name))