987130b52178b50f1850a545f432d94fe3d4f178
[osm/RO.git] / RO-SDN-onos_vpls / osm_rosdn_onos_vpls / sdn_assist_onos_vpls.py
1 # -*- coding: utf-8 -*-
2
3 # Copyright 2018 Whitestack, LLC
4 # *************************************************************
5
6 # This file is part of OSM RO module
7 # All Rights Reserved to Whitestack, LLC
8
9 # Licensed under the Apache License, Version 2.0 (the "License"); you may
10 # not use this file except in compliance with the License. You may obtain
11 # a copy of the License at
12
13 # http://www.apache.org/licenses/LICENSE-2.0
14
15 # Unless required by applicable law or agreed to in writing, software
16 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
17 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
18 # License for the specific language governing permissions and limitations
19 # under the License.
20 # For those usages not covered by the Apache License, Version 2.0 please
21 # contact: bdiaz@whitestack.com or glavado@whitestack.com
22 ##
23 import logging
24 import uuid
25 import copy
26
27 import requests
28 from requests.auth import HTTPBasicAuth
29
30 from osm_ro.wim.sdnconn import SdnConnectorBase, SdnConnectorError
31
32
33 class OnosVpls(SdnConnectorBase):
34 """
35 https://wiki.onosproject.org/display/ONOS/VPLS+User+Guide
36 """
37 _WIM_LOGGER = "sdn.assist.onos.vpls"
38
39 def __init__(self, wim, wim_account, config=None, logger=None):
40 self.logger = logger or logging.getLogger(self._WIM_LOGGER)
41 super().__init__(wim, wim_account, config, logger)
42 self.user = wim_account.get("user")
43 self.password = wim_account.get("password")
44 url = wim.get("wim_url")
45 if not url:
46 raise SdnConnectorError("'url' must be provided")
47 if not url.startswith("http"):
48 url = "http://" + url
49 if not url.endswith("/"):
50 url = url + "/"
51 self.url = url + "onos/v1/network/configuration"
52 self.logger.info("ONOS VPLS Connector Initialized.")
53
54 def check_credentials(self):
55 status_code = 503
56 onos_config_req = None
57 try:
58 onos_config_req = requests.get(self.url, auth=HTTPBasicAuth(self.user, self.password))
59 onos_config_req.raise_for_status()
60 except Exception as e:
61 if onos_config_req:
62 status_code = onos_config_req.status_code
63 self.logger.exception('Error checking credentials')
64 raise SdnConnectorError('Error checking credentials', http_code=status_code)
65
66 def get_connectivity_service_status(self, service_uuid, conn_info=None):
67 try:
68 result = None
69 onos_config = self._get_onos_netconfig()
70 vpls_config = onos_config.get('apps', {}).get('org.onosproject.vpls')
71 if vpls_config:
72 for vpls in vpls_config.get('vpls', {}).get('vplsList'):
73 if vpls.get('name') == service_uuid:
74 return {'sdn_status': 'ACTIVE', 'sdn_info': vpls}
75
76 return {'sdn_status': 'ERROR', 'sdn_info': 'not found'}
77
78 except Exception as e:
79 self.logger.error('Exception getting connectivity service info: %s', e)
80 return {'sdn_status': 'ERROR', 'error_msg': str(e)}
81
82 def _get_onos_netconfig(self):
83 try:
84 onos_config_req = requests.get(self.url, auth=HTTPBasicAuth(self.user, self.password))
85 status_code = onos_config_req.status_code
86 if status_code == requests.codes.ok:
87 return onos_config_req.json()
88 else:
89 self.logger.info("Error obtaining network config, status code: {}".format(status_code))
90 raise SdnConnectorError("Error obtaining network config status code: {}".format(status_code),
91 http_code=status_code)
92 except requests.exceptions.ConnectionError as e:
93 self.logger.info('Exception connecting to onos: %s', e)
94 raise SdnConnectorError("Error connecting to onos: {}".format(e))
95 except Exception as e:
96 self.logger.error('Exception getting onos network config: %s', e)
97 raise SdnConnectorError("Exception getting onos network config: {}".format(e))
98
99 def _post_onos_netconfig(self, onos_config):
100 try:
101 onos_config_resp = requests.post(self.url, json=onos_config, auth=HTTPBasicAuth(self.user, self.password))
102 status_code = onos_config_resp.status_code
103 if status_code != requests.codes.ok:
104 self.logger.info("Error updating network config, status code: {}".format(status_code))
105 raise SdnConnectorError("Error obtaining network config status code: {}".format(status_code),
106 http_code=status_code)
107 except requests.exceptions.ConnectionError as e:
108 self.logger.info('Exception connecting to onos: %s', e)
109 raise SdnConnectorError("Error connecting to onos: {}".format(e))
110 except Exception as e:
111 self.logger.info('Exception posting onos network config: %s', e)
112 raise SdnConnectorError("Exception posting onos network config: {}".format(e))
113
114 def create_connectivity_service(self, service_type, connection_points):
115 self.logger.debug("create_connectivity_service, service_type: {}, connection_points: {}".
116 format(service_type, connection_points))
117 if service_type.lower() != 'elan':
118 raise SdnConnectorError('Only ELAN network type is supported by ONOS VPLS.')
119
120 # FIXME ¿must check number of connection_points?
121 service_uuid = str(uuid.uuid4())
122
123 # Obtain current configuration
124 onos_config_orig = self._get_onos_netconfig()
125 #self.logger.debug("onos config: %s", onos_config_orig)
126 onos_config = copy.deepcopy(onos_config_orig)
127
128 try:
129 # Create missing interfaces
130 created_ifs = self._create_missing_interfaces(connection_points, onos_config)
131 self._post_onos_netconfig(onos_config)
132 created_items = created_ifs
133
134 # Add vpls service to config
135 interfaces = [port.get("service_endpoint_id") for port in connection_points]
136 if 'org.onosproject.vpls' in onos_config['apps']:
137 if 'vpls' not in onos_config['apps']['org.onosproject.vpls']:
138 onos_config['apps']['org.onosproject.vpls']['vpls'] = {
139 'vplsList': []
140 }
141 for vpls in onos_config['apps']['org.onosproject.vpls']['vpls']['vplsList']:
142 if vpls['name'] == service_uuid:
143 raise SdnConnectorError('Network {} already exists.'.format(service_uuid))
144 onos_config['apps']['org.onosproject.vpls']['vpls']['vplsList'].append({
145 'name': service_uuid,
146 'interfaces': interfaces
147 })
148 self._pop_last_update_time(onos_config)
149 else:
150 onos_config['apps'] = {
151 'org.onosproject.vpls': {
152 'vpls': {
153 "vplsList": [
154 {
155 'name': service_uuid,
156 'interfaces': interfaces
157 }
158 ]
159 }
160 }
161 }
162 self._set_encapsulation(service_uuid, connection_points, onos_config)
163 #self.logger.debug("original config: %s", onos_config_orig)
164 #self.logger.debug("original config: %s", onos_config)
165 self._post_onos_netconfig(onos_config)
166
167 self.logger.debug("created connectivity_service, service_uuid: {}, created_items: {}".
168 format(service_uuid, created_items))
169 return service_uuid, created_items
170 except Exception as e:
171 self.logger.error('Exception add connection_service: %s', e)
172 # try to rollback push original config
173 try:
174 self._post_onos_netconfig(onos_config_orig)
175 except Exception as e:
176 self.logger.error('Exception rolling back to original config: %s', e)
177 # raise exception
178 if isinstance(e, SdnConnectorError):
179 raise
180 else:
181 raise SdnConnectorError("Exception create_connectivity_service: {}".format(e))
182
183 def _set_encapsulation(self, service_uuid, connection_points, onos_config):
184
185 # check if encapsulation is vlan, check just one connection point
186 encapsulation = None
187 for connection_point in connection_points:
188 if connection_point.get("service_endpoint_encapsulation_type") == "dot1q":
189 encapsulation = "VLAN"
190 break
191 # if encapsulation is defined, assign
192 if encapsulation:
193 #self.logger.debug("assign encapsulation")
194 for vpls in onos_config['apps']['org.onosproject.vpls']['vpls']['vplsList']:
195 if vpls['name'] == service_uuid:
196 vpls['encapsulation'] = encapsulation
197
198 def edit_connectivity_service(self, service_uuid,
199 conn_info, connection_points,
200 **kwargs):
201 raise SdnConnectorError('Not supported', http_code=501)
202
203 def delete_connectivity_service(self, service_uuid, conn_info=None):
204 self.logger.debug("delete_connectivity_service uuid: {}".format(service_uuid))
205
206 conn_info = conn_info or []
207 # Obtain current config
208 onos_config = self._get_onos_netconfig()
209
210 try:
211 # created_interfaces
212 created_ifs = [item[1] for item in conn_info]
213
214 # Removes ports used by network from onos config
215 for vpls in onos_config['apps']['org.onosproject.vpls']['vpls']['vplsList']:
216 if vpls['name'] == service_uuid:
217 # iterate interfaces to check if must delete them
218 for interface in vpls['interfaces']:
219 for port in onos_config['ports'].values():
220 for port_interface in port['interfaces']:
221 if port_interface['name'] == interface:
222 # Delete only created ifzs
223 if port_interface['name'] in created_ifs:
224 self.logger.debug("Delete ifz: {}".format(port_interface['name']))
225 port['interfaces'].remove(port_interface)
226 onos_config['apps']['org.onosproject.vpls']['vpls']['vplsList'].remove(vpls)
227 break
228 else:
229 raise SdnConnectorError("service uuid: {} does not exist".format(service_uuid))
230
231 self._post_onos_netconfig(onos_config)
232 self.logger.debug("deleted connectivity service uuid: {}".format(service_uuid))
233 except Exception as e:
234 if isinstance(e, SdnConnectorError):
235 raise
236 else:
237 self.logger.error('Exception delete connection_service: %s', e, exc_info=True)
238 raise SdnConnectorError("Exception create_connectivity_service: {}".format(str(e)))
239
240 def _delete_network_port(self, net_id, port):
241 onos_config_req = requests.get(self.url, auth=HTTPBasicAuth(self.user, self.password))
242 onos_config_req.raise_for_status()
243 onos_config = onos_config_req.json()
244 for vpls in onos_config['apps']['org.onosproject.vpls']['vpls']['vplsList']:
245 if vpls['name'] == net_id:
246 for interface in vpls['interfaces']:
247 if interface == port['service_endpoint_id']:
248 vpls['interfaces'].remove(interface)
249 break
250 for onos_port in onos_config['ports'].values():
251 for port_interface in onos_port['interfaces']:
252 if port_interface['name'] == port['service_endpoint_id']:
253 onos_port['interfaces'].remove(port_interface)
254 break
255 self._pop_last_update_time(onos_config)
256 response = requests.post(self.url, json=onos_config, auth=HTTPBasicAuth(self.user, self.password))
257 response.raise_for_status()
258
259 def _pop_last_update_time(self, onos_config):
260 onos_config['apps']['org.onosproject.vpls']['vpls'].pop('lastUpdateTime', None)
261
262 def _create_missing_interfaces(self,connection_points, onos_config):
263 created_ifs = []
264 for port in connection_points:
265 created_ifz = self._append_port_to_onos_config(port, onos_config)
266 if created_ifz:
267 created_ifs.append(created_ifz)
268 return created_ifs
269
270 def _append_port_to_onos_config(self, port, onos_config):
271 created_item = None
272 port_name = 'of:{}/{}'.format(port['service_endpoint_encapsulation_info']['switch_dpid'],
273 port['service_endpoint_encapsulation_info']['switch_port'])
274 interface_config = {'name': port['service_endpoint_id']}
275 if 'vlan' in port['service_endpoint_encapsulation_info'] \
276 and port['service_endpoint_encapsulation_info']['vlan']:
277 interface_config['vlan'] = port['service_endpoint_encapsulation_info']['vlan']
278 if port_name in onos_config['ports'] and 'interfaces' in onos_config['ports'][port_name]:
279 for interface in onos_config['ports'][port_name]['interfaces']:
280 if interface['name'] == port['service_endpoint_id']:
281 #self.logger.debug("interface with same name and port exits")
282 # interface already exists TODO ¿check vlan? ¿delete and recreate?
283 # by the moment use and do not touch
284 #onos_config['ports'][port_name]['interfaces'].remove(interface)
285 break
286 else:
287 #self.logger.debug("port with same name exits but not interface")
288 onos_config['ports'][port_name]['interfaces'].append(interface_config)
289 created_item = (port_name, port['service_endpoint_id'])
290 else:
291 #self.logger.debug("create port and interface")
292 onos_config['ports'][port_name] = {
293 'interfaces': [interface_config]
294 }
295 created_item = (port_name, port['service_endpoint_id'])
296 return created_item
297
298
299 if __name__ == '__main__':
300 logger = logging.getLogger('openmano.sdn.onos_vpls')
301 logging.basicConfig()
302 logger.setLevel(getattr(logging, "DEBUG"))
303 # wim_url = "http://10.95.172.251:8181"
304 wim_url = "http://192.168.56.106:8181"
305 user = "karaf"
306 password = "karaf"
307 wim = {'wim_url': wim_url}
308 wim_account = {'user': user, 'password': password}
309 onos_vpls = OnosVpls(wim=wim, wim_account=wim_account, logger=logger)
310 conn_service = onos_vpls.get_connectivity_service_status("4e1f4c8a-a874-425d-a9b5-955cb77178f8")
311 print(conn_service)
312 service_type = 'ELAN'
313 conn_point_0 = {
314 "service_endpoint_id": "switch1_ifz2",
315 "service_endpoint_encapsulation_type": "dot1q",
316 "service_endpoint_encapsulation_info": {
317 "switch_dpid": "0000000000000011",
318 "switch_port": "1",
319 "vlan": "600"
320 }
321 }
322 conn_point_1 = {
323 "service_endpoint_id": "switch3_ifz2",
324 "service_endpoint_encapsulation_type": "dot1q",
325 "service_endpoint_encapsulation_info": {
326 "switch_dpid": "0000000000000031",
327 "switch_port": "3",
328 "vlan": "600"
329 }
330 }
331 connection_points = [conn_point_0, conn_point_1]
332 #service_uuid, created_items = onos_vpls.create_connectivity_service(service_type, connection_points)
333 #print(service_uuid)
334 #print(created_items)
335
336 #conn_info = None
337 conn_info = [('of:0000000000000011/1', 'switch1_ifz2'), ('of:0000000000000031/3', 'switch3_ifz2')]
338 onos_vpls.delete_connectivity_service("3a6a752e-8153-4b89-8b43-a7cebe0f0628", conn_info)