392d1abcd93354896d27450294f75025fd9c66e8
[osm/osmclient.git] / osmclient / sol005 / nsi.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 NSI (Network Slice Instance) 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
28
29 class Nsi(object):
30
31 def __init__(self, http=None, client=None):
32 self._http = http
33 self._client = client
34 self._apiName = '/nsilcm'
35 self._apiVersion = '/v1'
36 self._apiResource = '/netslice_instances_content'
37 self._apiBase = '{}{}{}'.format(self._apiName,
38 self._apiVersion, self._apiResource)
39
40 # NSI '--wait' option
41 def _wait(self, id, deleteFlag=False):
42 # Endpoint to get operation status
43 apiUrlStatus = '{}{}{}'.format(self._apiName, self._apiVersion, '/nsi_lcm_op_occs')
44 # Wait for status for NSI instance creation/update/deletion
45 WaitForStatus.wait_for_status(
46 'NSI',
47 str(id),
48 WaitForStatus.TIMEOUT_NSI_OPERATION,
49 apiUrlStatus,
50 self._http.get2_cmd,
51 deleteFlag=deleteFlag)
52
53 def list(self, filter=None):
54 """Returns a list of NSI
55 """
56 filter_string = ''
57 if filter:
58 filter_string = '?{}'.format(filter)
59 resp = self._http.get_cmd('{}{}'.format(self._apiBase,filter_string))
60 if resp:
61 return resp
62 return list()
63
64 def get(self, name):
65 """Returns an NSI based on name or id
66 """
67 if utils.validate_uuid4(name):
68 for nsi in self.list():
69 if name == nsi['_id']:
70 return nsi
71 else:
72 for nsi in self.list():
73 if name == nsi['name']:
74 return nsi
75 raise NotFound("nsi {} not found".format(name))
76
77 def get_individual(self, name):
78 nsi_id = name
79 if not utils.validate_uuid4(name):
80 for nsi in self.list():
81 if name == nsi['name']:
82 nsi_id = nsi['_id']
83 break
84 resp = self._http.get_cmd('{}/{}'.format(self._apiBase, nsi_id))
85 #resp = self._http.get_cmd('{}/{}/nsd_content'.format(self._apiBase, nsi_id))
86 #print yaml.safe_dump(resp)
87 if resp:
88 return resp
89 raise NotFound("nsi {} not found".format(name))
90
91 def delete(self, name, force=False, wait=False):
92 nsi = self.get(name)
93 querystring = ''
94 if force:
95 querystring = '?FORCE=True'
96 http_code, resp = self._http.delete_cmd('{}/{}{}'.format(self._apiBase,
97 nsi['_id'], querystring))
98 # print 'HTTP CODE: {}'.format(http_code)
99 # print 'RESP: {}'.format(resp)
100 if http_code == 202:
101 if wait and resp:
102 resp = json.loads(resp)
103 # Wait for status for NSI instance deletion
104 # For the 'delete' operation, '_id' is used
105 self._wait(resp.get('_id'), deleteFlag=True)
106 else:
107 print('Deletion in progress')
108 elif http_code == 204:
109 print('Deleted')
110 else:
111 msg = ""
112 if resp:
113 try:
114 msg = json.loads(resp)
115 except ValueError:
116 msg = resp
117 raise ClientException("failed to delete nsi {} - {}".format(name, msg))
118
119 def create(self, nst_name, nsi_name, account, config=None,
120 ssh_keys=None, description='default description',
121 admin_status='ENABLED', wait=False):
122
123 nst = self._client.nst.get(nst_name)
124
125 vim_account_id = {}
126
127 def get_vim_account_id(vim_account):
128 if vim_account_id.get(vim_account):
129 return vim_account_id[vim_account]
130
131 vim = self._client.vim.get(vim_account)
132 if vim is None:
133 raise NotFound("cannot find vim account '{}'".format(vim_account))
134 vim_account_id[vim_account] = vim['_id']
135 return vim['_id']
136
137 nsi = {}
138 nsi['nstId'] = nst['_id']
139 nsi['nsiName'] = nsi_name
140 nsi['nsiDescription'] = description
141 nsi['vimAccountId'] = get_vim_account_id(account)
142 #nsi['userdata'] = {}
143 #nsi['userdata']['key1']='value1'
144 #nsi['userdata']['key2']='value2'
145
146 if ssh_keys is not None:
147 # ssh_keys is comma separate list
148 # ssh_keys_format = []
149 # for key in ssh_keys.split(','):
150 # ssh_keys_format.append({'key-pair-ref': key})
151 #
152 # ns['ssh-authorized-key'] = ssh_keys_format
153 nsi['ssh_keys'] = []
154 for pubkeyfile in ssh_keys.split(','):
155 with open(pubkeyfile, 'r') as f:
156 nsi['ssh_keys'].append(f.read())
157 if config:
158 nsi_config = yaml.load(config)
159 if "netslice-vld" in nsi_config:
160 for vld in nsi_config["netslice-vld"]:
161 if vld.get("vim-network-name"):
162 if isinstance(vld["vim-network-name"], dict):
163 vim_network_name_dict = {}
164 for vim_account, vim_net in list(vld["vim-network-name"].items()):
165 vim_network_name_dict[get_vim_account_id(vim_account)] = vim_net
166 vld["vim-network-name"] = vim_network_name_dict
167 nsi["netslice-vld"] = nsi_config["netslice-vld"]
168 if "netslice-subnet" in nsi_config:
169 for nssubnet in nsi_config["netslice-subnet"]:
170 if "vld" in nssubnet:
171 for vld in nssubnet["vld"]:
172 if vld.get("vim-network-name"):
173 if isinstance(vld["vim-network-name"], dict):
174 vim_network_name_dict = {}
175 for vim_account, vim_net in list(vld["vim-network-name"].items()):
176 vim_network_name_dict[get_vim_account_id(vim_account)] = vim_net
177 vld["vim-network-name"] = vim_network_name_dict
178 if "vnf" in nssubnet:
179 for vnf in nsi_config["vnf"]:
180 if vnf.get("vim_account"):
181 vnf["vimAccountId"] = get_vim_account_id(vnf.pop("vim_account"))
182 nsi["netslice-subnet"] = nsi_config["netslice-subnet"]
183
184 if "additionalParamsForNsi" in nsi_config:
185 nsi["additionalParamsForNsi"] = nsi_config.pop("additionalParamsForNsi")
186 if not isinstance(nsi["additionalParamsForNsi"], dict):
187 raise ValueError("Error at --config 'additionalParamsForNsi' must be a dictionary")
188 if "additionalParamsForSubnet" in nsi_config:
189 nsi["additionalParamsForSubnet"] = nsi_config.pop("additionalParamsForSubnet")
190 if not isinstance(nsi["additionalParamsForSubnet"], list):
191 raise ValueError("Error at --config 'additionalParamsForSubnet' must be a list")
192 for additional_param_subnet in nsi["additionalParamsForSubnet"]:
193 if not isinstance(additional_param_subnet, dict):
194 raise ValueError("Error at --config 'additionalParamsForSubnet' items must be dictionaries")
195 if not additional_param_subnet.get("id"):
196 raise ValueError("Error at --config 'additionalParamsForSubnet' items must contain subnet 'id'")
197 if not additional_param_subnet.get("additionalParamsForNs") and\
198 not additional_param_subnet.get("additionalParamsForVnf"):
199 raise ValueError("Error at --config 'additionalParamsForSubnet' items must contain "
200 "'additionalParamsForNs' and/or 'additionalParamsForVnf'")
201
202 # print yaml.safe_dump(nsi)
203 try:
204 self._apiResource = '/netslice_instances_content'
205 self._apiBase = '{}{}{}'.format(self._apiName,
206 self._apiVersion, self._apiResource)
207 headers = self._client._headers
208 headers['Content-Type'] = 'application/yaml'
209 http_header = ['{}: {}'.format(key,val)
210 for (key,val) in list(headers.items())]
211 self._http.set_http_header(http_header)
212 http_code, resp = self._http.post_cmd(endpoint=self._apiBase,
213 postfields_dict=nsi)
214 #print 'HTTP CODE: {}'.format(http_code)
215 #print 'RESP: {}'.format(resp)
216 if http_code in (200, 201, 202, 204):
217 if resp:
218 resp = json.loads(resp)
219 if not resp or 'id' not in resp:
220 raise ClientException('unexpected response from server - {} '.format(
221 resp))
222 if wait:
223 # Wait for status for NSI instance creation
224 self._wait(resp.get('nsilcmop_id'))
225 print(resp['id'])
226 else:
227 msg = ""
228 if resp:
229 try:
230 msg = json.loads(resp)
231 except ValueError:
232 msg = resp
233 raise ClientException(msg)
234 except ClientException as exc:
235 message="failed to create nsi: {} nst: {}\nerror:\n{}".format(
236 nsi_name,
237 nst_name,
238 exc.message)
239 raise ClientException(message)
240
241 def list_op(self, name, filter=None):
242 """Returns the list of operations of a NSI
243 """
244 nsi = self.get(name)
245 try:
246 self._apiResource = '/nsi_lcm_op_occs'
247 self._apiBase = '{}{}{}'.format(self._apiName,
248 self._apiVersion, self._apiResource)
249 filter_string = ''
250 if filter:
251 filter_string = '&{}'.format(filter)
252 http_code, resp = self._http.get2_cmd('{}?nsiInstanceId={}'.format(
253 self._apiBase, nsi['_id'],
254 filter_string) )
255 #print 'HTTP CODE: {}'.format(http_code)
256 #print 'RESP: {}'.format(resp)
257 if http_code == 200:
258 if resp:
259 resp = json.loads(resp)
260 return resp
261 else:
262 raise ClientException('unexpected response from server')
263 else:
264 msg = ""
265 if resp:
266 try:
267 resp = json.loads(resp)
268 msg = resp['detail']
269 except ValueError:
270 msg = resp
271 raise ClientException(msg)
272 except ClientException as exc:
273 message="failed to get operation list of NSI {}:\nerror:\n{}".format(
274 name,
275 exc.message)
276 raise ClientException(message)
277
278 def get_op(self, operationId):
279 """Returns the status of an operation
280 """
281 try:
282 self._apiResource = '/nsi_lcm_op_occs'
283 self._apiBase = '{}{}{}'.format(self._apiName,
284 self._apiVersion, self._apiResource)
285 http_code, resp = self._http.get2_cmd('{}/{}'.format(self._apiBase, operationId))
286 #print 'HTTP CODE: {}'.format(http_code)
287 #print 'RESP: {}'.format(resp)
288 if http_code == 200:
289 if resp:
290 resp = json.loads(resp)
291 return resp
292 else:
293 raise ClientException('unexpected response from server')
294 else:
295 msg = ""
296 if resp:
297 try:
298 resp = json.loads(resp)
299 msg = resp['detail']
300 except ValueError:
301 msg = resp
302 raise ClientException(msg)
303 except ClientException as exc:
304 message="failed to get status of operation {}:\nerror:\n{}".format(
305 operationId,
306 exc.message)
307 raise ClientException(message)
308
309 def exec_op(self, name, op_name, op_data=None):
310 """Executes an operation on a NSI
311 """
312 nsi = self.get(name)
313 try:
314 self._apiResource = '/netslice_instances'
315 self._apiBase = '{}{}{}'.format(self._apiName,
316 self._apiVersion, self._apiResource)
317 endpoint = '{}/{}/{}'.format(self._apiBase, nsi['_id'], op_name)
318 #print 'OP_NAME: {}'.format(op_name)
319 #print 'OP_DATA: {}'.format(json.dumps(op_data))
320 http_code, resp = self._http.post_cmd(endpoint=endpoint, postfields_dict=op_data)
321 #print 'HTTP CODE: {}'.format(http_code)
322 #print 'RESP: {}'.format(resp)
323 if http_code in (200, 201, 202, 204):
324 if resp:
325 resp = json.loads(resp)
326 if not resp or 'id' not in resp:
327 raise ClientException('unexpected response from server - {}'.format(
328 resp))
329 print(resp['id'])
330 else:
331 msg = ""
332 if resp:
333 try:
334 msg = json.loads(resp)
335 except ValueError:
336 msg = resp
337 raise ClientException(msg)
338 except ClientException as exc:
339 message="failed to exec operation {}:\nerror:\n{}".format(
340 name,
341 exc.message)
342 raise ClientException(message)
343