1 # -*- coding: utf-8 -*-
3 # Copyright 2018 Whitestack, LLC
4 # *************************************************************
6 # This file is part of OSM RO module
7 # All Rights Reserved to Whitestack, LLC
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
13 # http://www.apache.org/licenses/LICENSE-2.0
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
20 # For those usages not covered by the Apache License, Version 2.0 please
21 # contact: bdiaz@whitestack.com or glavado@whitestack.com
28 from requests
.auth
import HTTPBasicAuth
30 from osm_ro
.wim
.sdnconn
import SdnConnectorBase
, SdnConnectorError
33 class OnosVpls(SdnConnectorBase
):
35 https://wiki.onosproject.org/display/ONOS/VPLS+User+Guide
37 _WIM_LOGGER
= "sdn.assist.onos.vpls"
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")
46 raise SdnConnectorError("'url' must be provided")
47 if not url
.startswith("http"):
49 if not url
.endswith("/"):
51 self
.url
= url
+ "onos/v1/network/configuration"
52 self
.logger
.info("ONOS VPLS Connector Initialized.")
54 def check_credentials(self
):
56 onos_config_req
= None
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
:
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
)
66 def get_connectivity_service_status(self
, service_uuid
, conn_info
=None):
69 onos_config
= self
._get
_onos
_netconfig
()
70 vpls_config
= onos_config
.get('apps', {}).get('org.onosproject.vpls')
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
}
76 return {'sdn_status': 'ERROR', 'sdn_info': 'not found'}
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
)}
82 def _get_onos_netconfig(self
):
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()
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
))
99 def _post_onos_netconfig(self
, onos_config
):
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
))
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.')
120 # FIXME ¿must check number of connection_points?
121 service_uuid
= str(uuid
.uuid4())
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
)
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
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'] = {
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
148 self
._pop
_last
_update
_time
(onos_config
)
150 onos_config
['apps'] = {
151 'org.onosproject.vpls': {
155 'name': service_uuid
,
156 'interfaces': interfaces
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
)
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
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
)
178 if isinstance(e
, SdnConnectorError
):
181 raise SdnConnectorError("Exception create_connectivity_service: {}".format(e
))
183 def _set_encapsulation(self
, service_uuid
, connection_points
, onos_config
):
185 # check if encapsulation is vlan, check just one connection point
187 for connection_point
in connection_points
:
188 if connection_point
.get("service_endpoint_encapsulation_type") == "dot1q":
189 encapsulation
= "VLAN"
191 # if encapsulation is defined, assign
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
198 def edit_connectivity_service(self
, service_uuid
,
199 conn_info
, connection_points
,
201 raise SdnConnectorError('Not supported', http_code
=501)
203 def delete_connectivity_service(self
, service_uuid
, conn_info
=None):
204 self
.logger
.debug("delete_connectivity_service uuid: {}".format(service_uuid
))
206 conn_info
= conn_info
or []
207 # Obtain current config
208 onos_config
= self
._get
_onos
_netconfig
()
212 created_ifs
= [item
[1] for item
in conn_info
]
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
)
229 raise SdnConnectorError("service uuid: {} does not exist".format(service_uuid
))
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
):
237 self
.logger
.error('Exception delete connection_service: %s', e
, exc_info
=True)
238 raise SdnConnectorError("Exception create_connectivity_service: {}".format(str(e
)))
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
)
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
)
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()
259 def _pop_last_update_time(self
, onos_config
):
260 onos_config
['apps']['org.onosproject.vpls']['vpls'].pop('lastUpdateTime', None)
262 def _create_missing_interfaces(self
,connection_points
, onos_config
):
264 for port
in connection_points
:
265 created_ifz
= self
._append
_port
_to
_onos
_config
(port
, onos_config
)
267 created_ifs
.append(created_ifz
)
270 def _append_port_to_onos_config(self
, port
, onos_config
):
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)
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'])
291 #self.logger.debug("create port and interface")
292 onos_config
['ports'][port_name
] = {
293 'interfaces': [interface_config
]
295 created_item
= (port_name
, port
['service_endpoint_id'])
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"
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")
312 service_type
= 'ELAN'
314 "service_endpoint_id": "switch1_ifz2",
315 "service_endpoint_encapsulation_type": "dot1q",
316 "service_endpoint_encapsulation_info": {
317 "switch_dpid": "0000000000000011",
323 "service_endpoint_id": "switch3_ifz2",
324 "service_endpoint_encapsulation_type": "dot1q",
325 "service_endpoint_encapsulation_info": {
326 "switch_dpid": "0000000000000031",
331 connection_points
= [conn_point_0
, conn_point_1
]
332 #service_uuid, created_items = onos_vpls.create_connectivity_service(service_type, connection_points)
334 #print(created_items)
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
)