adding SOL005 additionalParams to ns/nsi creation
[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.exceptions import ClientException
23 from osmclient.common.exceptions import NotFound
24 import yaml
25 import json
26
27
28 class Nsi(object):
29
30 def __init__(self, http=None, client=None):
31 self._http = http
32 self._client = client
33 self._apiName = '/nsilcm'
34 self._apiVersion = '/v1'
35 self._apiResource = '/netslice_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 NSI
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 NSI based on name or id
52 """
53 if utils.validate_uuid4(name):
54 for nsi in self.list():
55 if name == nsi['_id']:
56 return nsi
57 else:
58 for nsi in self.list():
59 if name == nsi['name']:
60 return nsi
61 raise NotFound("nsi {} not found".format(name))
62
63 def get_individual(self, name):
64 nsi_id = name
65 if not utils.validate_uuid4(name):
66 for nsi in self.list():
67 if name == nsi['name']:
68 nsi_id = nsi['_id']
69 break
70 resp = self._http.get_cmd('{}/{}'.format(self._apiBase, nsi_id))
71 #resp = self._http.get_cmd('{}/{}/nsd_content'.format(self._apiBase, nsi_id))
72 #print yaml.safe_dump(resp)
73 if resp:
74 return resp
75 raise NotFound("nsi {} not found".format(name))
76
77 def delete(self, name, force=False):
78 nsi = self.get(name)
79 querystring = ''
80 if force:
81 querystring = '?FORCE=True'
82 http_code, resp = self._http.delete_cmd('{}/{}{}'.format(self._apiBase,
83 nsi['_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 nsi {} - {}".format(name, msg))
98
99 def create(self, nst_name, nsi_name, account, config=None,
100 ssh_keys=None, description='default description',
101 admin_status='ENABLED'):
102
103 nst = self._client.nst.get(nst_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 nsi = {}
118 nsi['nstId'] = nst['_id']
119 nsi['nsiName'] = nsi_name
120 nsi['nsiDescription'] = description
121 nsi['vimAccountId'] = get_vim_account_id(account)
122 #nsi['userdata'] = {}
123 #nsi['userdata']['key1']='value1'
124 #nsi['userdata']['key2']='value2'
125
126 if ssh_keys is not None:
127 # ssh_keys is comma separate list
128 # ssh_keys_format = []
129 # for key in ssh_keys.split(','):
130 # ssh_keys_format.append({'key-pair-ref': key})
131 #
132 # ns['ssh-authorized-key'] = ssh_keys_format
133 nsi['ssh_keys'] = []
134 for pubkeyfile in ssh_keys.split(','):
135 with open(pubkeyfile, 'r') as f:
136 nsi['ssh_keys'].append(f.read())
137 if config:
138 nsi_config = yaml.load(config)
139 if "netslice-vld" in nsi_config:
140 for vld in nsi_config["netslice-vld"]:
141 if vld.get("vim-network-name"):
142 if isinstance(vld["vim-network-name"], dict):
143 vim_network_name_dict = {}
144 for vim_account, vim_net in list(vld["vim-network-name"].items()):
145 vim_network_name_dict[get_vim_account_id(vim_account)] = vim_net
146 vld["vim-network-name"] = vim_network_name_dict
147 nsi["netslice-vld"] = nsi_config["netslice-vld"]
148 if "netslice-subnet" in nsi_config:
149 for nssubnet in nsi_config["netslice-subnet"]:
150 if "vld" in nssubnet:
151 for vld in nssubnet["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 "vnf" in nssubnet:
159 for vnf in nsi_config["vnf"]:
160 if vnf.get("vim_account"):
161 vnf["vimAccountId"] = get_vim_account_id(vnf.pop("vim_account"))
162 nsi["netslice-subnet"] = nsi_config["netslice-subnet"]
163
164 if "additionalParamsForNsi" in nsi_config:
165 nsi["additionalParamsForNsi"] = nsi_config.pop("additionalParamsForNsi")
166 if not isinstance(nsi["additionalParamsForNsi"], dict):
167 raise ValueError("Error at --config 'additionalParamsForNsi' must be a dictionary")
168 if "additionalParamsForSubnet" in nsi_config:
169 nsi["additionalParamsForSubnet"] = nsi_config.pop("additionalParamsForSubnet")
170 if not isinstance(nsi["additionalParamsForSubnet"], list):
171 raise ValueError("Error at --config 'additionalParamsForSubnet' must be a list")
172 for additional_param_subnet in nsi["additionalParamsForSubnet"]:
173 if not isinstance(additional_param_subnet, dict):
174 raise ValueError("Error at --config 'additionalParamsForSubnet' items must be dictionaries")
175 if not additional_param_subnet.get("id"):
176 raise ValueError("Error at --config 'additionalParamsForSubnet' items must contain subnet 'id'")
177 if not additional_param_subnet.get("additionalParamsForNs") and\
178 not additional_param_subnet.get("additionalParamsForVnf"):
179 raise ValueError("Error at --config 'additionalParamsForSubnet' items must contain "
180 "'additionalParamsForNs' and/or 'additionalParamsForVnf'")
181
182 # print yaml.safe_dump(nsi)
183 try:
184 self._apiResource = '/netslice_instances_content'
185 self._apiBase = '{}{}{}'.format(self._apiName,
186 self._apiVersion, self._apiResource)
187 headers = self._client._headers
188 headers['Content-Type'] = 'application/yaml'
189 http_header = ['{}: {}'.format(key,val)
190 for (key,val) in list(headers.items())]
191 self._http.set_http_header(http_header)
192 http_code, resp = self._http.post_cmd(endpoint=self._apiBase,
193 postfields_dict=nsi)
194 #print 'HTTP CODE: {}'.format(http_code)
195 #print 'RESP: {}'.format(resp)
196 if http_code in (200, 201, 202, 204):
197 if resp:
198 resp = json.loads(resp)
199 if not resp or 'id' not in resp:
200 raise ClientException('unexpected response from server - {} '.format(
201 resp))
202 print(resp['id'])
203 else:
204 msg = ""
205 if resp:
206 try:
207 msg = json.loads(resp)
208 except ValueError:
209 msg = resp
210 raise ClientException(msg)
211 except ClientException as exc:
212 message="failed to create nsi: {} nst: {}\nerror:\n{}".format(
213 nsi_name,
214 nst_name,
215 exc.message)
216 raise ClientException(message)
217
218 def list_op(self, name, filter=None):
219 """Returns the list of operations of a NSI
220 """
221 nsi = self.get(name)
222 try:
223 self._apiResource = '/nsi_lcm_op_occs'
224 self._apiBase = '{}{}{}'.format(self._apiName,
225 self._apiVersion, self._apiResource)
226 filter_string = ''
227 if filter:
228 filter_string = '&{}'.format(filter)
229 http_code, resp = self._http.get2_cmd('{}?nsiInstanceId={}'.format(
230 self._apiBase, nsi['_id'],
231 filter_string) )
232 #print 'HTTP CODE: {}'.format(http_code)
233 #print 'RESP: {}'.format(resp)
234 if http_code == 200:
235 if resp:
236 resp = json.loads(resp)
237 return resp
238 else:
239 raise ClientException('unexpected response from server')
240 else:
241 msg = ""
242 if resp:
243 try:
244 resp = json.loads(resp)
245 msg = resp['detail']
246 except ValueError:
247 msg = resp
248 raise ClientException(msg)
249 except ClientException as exc:
250 message="failed to get operation list of NSI {}:\nerror:\n{}".format(
251 name,
252 exc.message)
253 raise ClientException(message)
254
255 def get_op(self, operationId):
256 """Returns the status of an operation
257 """
258 try:
259 self._apiResource = '/nsi_lcm_op_occs'
260 self._apiBase = '{}{}{}'.format(self._apiName,
261 self._apiVersion, self._apiResource)
262 http_code, resp = self._http.get2_cmd('{}/{}'.format(self._apiBase, operationId))
263 #print 'HTTP CODE: {}'.format(http_code)
264 #print 'RESP: {}'.format(resp)
265 if http_code == 200:
266 if resp:
267 resp = json.loads(resp)
268 return resp
269 else:
270 raise ClientException('unexpected response from server')
271 else:
272 msg = ""
273 if resp:
274 try:
275 resp = json.loads(resp)
276 msg = resp['detail']
277 except ValueError:
278 msg = resp
279 raise ClientException(msg)
280 except ClientException as exc:
281 message="failed to get status of operation {}:\nerror:\n{}".format(
282 operationId,
283 exc.message)
284 raise ClientException(message)
285
286 def exec_op(self, name, op_name, op_data=None):
287 """Executes an operation on a NSI
288 """
289 nsi = self.get(name)
290 try:
291 self._apiResource = '/netslice_instances'
292 self._apiBase = '{}{}{}'.format(self._apiName,
293 self._apiVersion, self._apiResource)
294 endpoint = '{}/{}/{}'.format(self._apiBase, nsi['_id'], op_name)
295 #print 'OP_NAME: {}'.format(op_name)
296 #print 'OP_DATA: {}'.format(json.dumps(op_data))
297 http_code, resp = self._http.post_cmd(endpoint=endpoint, postfields_dict=op_data)
298 #print 'HTTP CODE: {}'.format(http_code)
299 #print 'RESP: {}'.format(resp)
300 if http_code in (200, 201, 202, 204):
301 if resp:
302 resp = json.loads(resp)
303 if not resp or 'id' not in resp:
304 raise ClientException('unexpected response from server - {}'.format(
305 resp))
306 print(resp['id'])
307 else:
308 msg = ""
309 if resp:
310 try:
311 msg = json.loads(resp)
312 except ValueError:
313 msg = resp
314 raise ClientException(msg)
315 except ClientException as exc:
316 message="failed to exec operation {}:\nerror:\n{}".format(
317 name,
318 exc.message)
319 raise ClientException(message)
320