1 # Copyright 2018 Telefonica
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
9 # http://www.apache.org/licenses/LICENSE-2.0
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
18 OSM NSI (Network Slice Instance) API handling
21 from osmclient
.common
import utils
22 from osmclient
.common
.exceptions
import ClientException
23 from osmclient
.common
.exceptions
import NotFound
30 def __init__(self
, http
=None, client
=None):
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
)
39 def list(self
, filter=None):
40 """Returns a list of NSI
44 filter_string
= '?{}'.format(filter)
45 resp
= self
._http
.get_cmd('{}{}'.format(self
._apiBase
,filter_string
))
51 """Returns an NSI based on name or id
53 if utils
.validate_uuid4(name
):
54 for nsi
in self
.list():
55 if name
== nsi
['_id']:
58 for nsi
in self
.list():
59 if name
== nsi
['name']:
61 raise NotFound("nsi {} not found".format(name
))
63 def get_individual(self
, name
):
65 if not utils
.validate_uuid4(name
):
66 for nsi
in self
.list():
67 if name
== nsi
['name']:
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)
75 raise NotFound("nsi {} not found".format(name
))
77 def delete(self
, name
, force
=False):
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)
87 print('Deletion in progress')
88 elif http_code
== 204:
94 msg
= json
.loads(resp
)
97 raise ClientException("failed to delete nsi {} - {}".format(name
, msg
))
99 def create(self
, nst_name
, nsi_name
, account
, config
=None,
100 ssh_keys
=None, description
='default description',
101 admin_status
='ENABLED'):
103 nst
= self
._client
.nst
.get(nst_name
)
107 def get_vim_account_id(vim_account
):
108 if vim_account_id
.get(vim_account
):
109 return vim_account_id
[vim_account
]
111 vim
= self
._client
.vim
.get(vim_account
)
113 raise NotFound("cannot find vim account '{}'".format(vim_account
))
114 vim_account_id
[vim_account
] = vim
['_id']
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'
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})
132 # ns['ssh-authorized-key'] = ssh_keys_format
134 for pubkeyfile
in ssh_keys
.split(','):
135 with
open(pubkeyfile
, 'r') as f
:
136 nsi
['ssh_keys'].append(f
.read())
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"]
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'")
182 # print yaml.safe_dump(nsi)
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
,
194 #print 'HTTP CODE: {}'.format(http_code)
195 #print 'RESP: {}'.format(resp)
196 if http_code
in (200, 201, 202, 204):
198 resp
= json
.loads(resp
)
199 if not resp
or 'id' not in resp
:
200 raise ClientException('unexpected response from server - {} '.format(
207 msg
= json
.loads(resp
)
210 raise ClientException(msg
)
211 except ClientException
as exc
:
212 message
="failed to create nsi: {} nst: {}\nerror:\n{}".format(
216 raise ClientException(message
)
218 def list_op(self
, name
, filter=None):
219 """Returns the list of operations of a NSI
223 self
._apiResource
= '/nsi_lcm_op_occs'
224 self
._apiBase
= '{}{}{}'.format(self
._apiName
,
225 self
._apiVersion
, self
._apiResource
)
228 filter_string
= '&{}'.format(filter)
229 http_code
, resp
= self
._http
.get2_cmd('{}?nsiInstanceId={}'.format(
230 self
._apiBase
, nsi
['_id'],
232 #print 'HTTP CODE: {}'.format(http_code)
233 #print 'RESP: {}'.format(resp)
236 resp
= json
.loads(resp
)
239 raise ClientException('unexpected response from server')
244 resp
= json
.loads(resp
)
248 raise ClientException(msg
)
249 except ClientException
as exc
:
250 message
="failed to get operation list of NSI {}:\nerror:\n{}".format(
253 raise ClientException(message
)
255 def get_op(self
, operationId
):
256 """Returns the status of an operation
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)
267 resp
= json
.loads(resp
)
270 raise ClientException('unexpected response from server')
275 resp
= json
.loads(resp
)
279 raise ClientException(msg
)
280 except ClientException
as exc
:
281 message
="failed to get status of operation {}:\nerror:\n{}".format(
284 raise ClientException(message
)
286 def exec_op(self
, name
, op_name
, op_data
=None):
287 """Executes an operation on a NSI
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):
302 resp
= json
.loads(resp
)
303 if not resp
or 'id' not in resp
:
304 raise ClientException('unexpected response from server - {}'.format(
311 msg
= json
.loads(resp
)
314 raise ClientException(msg
)
315 except ClientException
as exc
:
316 message
="failed to exec operation {}:\nerror:\n{}".format(
319 raise ClientException(message
)