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