From eaa4344e10032f059124fb4db16fe729983adbc1 Mon Sep 17 00:00:00 2001 From: lloretgalleg Date: Tue, 17 Dec 2019 12:28:39 +0100 Subject: [PATCH] Improved error management, rollback behaviour Change-Id: I69fd31c6fc2b9dada1fb8bc0d53ea400cad6e9d7 Signed-off-by: lloretgalleg --- .../sdn_assist_onos_vpls.py | 339 ++++++++++++------ 1 file changed, 229 insertions(+), 110 deletions(-) diff --git a/RO-SDN-onos_vpls/osm_rosdn_onos_vpls/sdn_assist_onos_vpls.py b/RO-SDN-onos_vpls/osm_rosdn_onos_vpls/sdn_assist_onos_vpls.py index 8531444e..987130b5 100644 --- a/RO-SDN-onos_vpls/osm_rosdn_onos_vpls/sdn_assist_onos_vpls.py +++ b/RO-SDN-onos_vpls/osm_rosdn_onos_vpls/sdn_assist_onos_vpls.py @@ -22,34 +22,34 @@ ## import logging import uuid +import copy import requests from requests.auth import HTTPBasicAuth from osm_ro.wim.sdnconn import SdnConnectorBase, SdnConnectorError -log = logging.getLogger(__name__) - class OnosVpls(SdnConnectorBase): """ https://wiki.onosproject.org/display/ONOS/VPLS+User+Guide """ + _WIM_LOGGER = "sdn.assist.onos.vpls" def __init__(self, wim, wim_account, config=None, logger=None): - - super().__init__(wim, wim_account, config, log) + self.logger = logger or logging.getLogger(self._WIM_LOGGER) + super().__init__(wim, wim_account, config, logger) self.user = wim_account.get("user") self.password = wim_account.get("password") - url = wim_account.get("wim_url") + url = wim.get("wim_url") if not url: - raise ValueError("'url' must be provided") + raise SdnConnectorError("'url' must be provided") if not url.startswith("http"): url = "http://" + url if not url.endswith("/"): url = url + "/" self.url = url + "onos/v1/network/configuration" - log.info("ONOS VPLS Connector Initialized.") + self.logger.info("ONOS VPLS Connector Initialized.") def check_credentials(self): status_code = 503 @@ -60,58 +60,140 @@ class OnosVpls(SdnConnectorBase): except Exception as e: if onos_config_req: status_code = onos_config_req.status_code - log.exception('Error checking credentials') + self.logger.exception('Error checking credentials') raise SdnConnectorError('Error checking credentials', http_code=status_code) def get_connectivity_service_status(self, service_uuid, conn_info=None): - onos_config_req = requests.get(self.url, auth=HTTPBasicAuth(self.user, self.password)) - onos_config_req.raise_for_status() - onos_config = onos_config_req.json() - for vpls in onos_config['apps']['org.onosproject.vpls']['vpls']['vplsList']: - if vpls['name'] == service_uuid: - return vpls - raise SdnConnectorError('VPLS {} not found'.format(service_uuid), http_code=404) + try: + result = None + onos_config = self._get_onos_netconfig() + vpls_config = onos_config.get('apps', {}).get('org.onosproject.vpls') + if vpls_config: + for vpls in vpls_config.get('vpls', {}).get('vplsList'): + if vpls.get('name') == service_uuid: + return {'sdn_status': 'ACTIVE', 'sdn_info': vpls} + + return {'sdn_status': 'ERROR', 'sdn_info': 'not found'} + + except Exception as e: + self.logger.error('Exception getting connectivity service info: %s', e) + return {'sdn_status': 'ERROR', 'error_msg': str(e)} + + def _get_onos_netconfig(self): + try: + onos_config_req = requests.get(self.url, auth=HTTPBasicAuth(self.user, self.password)) + status_code = onos_config_req.status_code + if status_code == requests.codes.ok: + return onos_config_req.json() + else: + self.logger.info("Error obtaining network config, status code: {}".format(status_code)) + raise SdnConnectorError("Error obtaining network config status code: {}".format(status_code), + http_code=status_code) + except requests.exceptions.ConnectionError as e: + self.logger.info('Exception connecting to onos: %s', e) + raise SdnConnectorError("Error connecting to onos: {}".format(e)) + except Exception as e: + self.logger.error('Exception getting onos network config: %s', e) + raise SdnConnectorError("Exception getting onos network config: {}".format(e)) + + def _post_onos_netconfig(self, onos_config): + try: + onos_config_resp = requests.post(self.url, json=onos_config, auth=HTTPBasicAuth(self.user, self.password)) + status_code = onos_config_resp.status_code + if status_code != requests.codes.ok: + self.logger.info("Error updating network config, status code: {}".format(status_code)) + raise SdnConnectorError("Error obtaining network config status code: {}".format(status_code), + http_code=status_code) + except requests.exceptions.ConnectionError as e: + self.logger.info('Exception connecting to onos: %s', e) + raise SdnConnectorError("Error connecting to onos: {}".format(e)) + except Exception as e: + self.logger.info('Exception posting onos network config: %s', e) + raise SdnConnectorError("Exception posting onos network config: {}".format(e)) def create_connectivity_service(self, service_type, connection_points): + self.logger.debug("create_connectivity_service, service_type: {}, connection_points: {}". + format(service_type, connection_points)) if service_type.lower() != 'elan': raise SdnConnectorError('Only ELAN network type is supported by ONOS VPLS.') - onos_config_req = requests.get(self.url, auth=HTTPBasicAuth(self.user, self.password)) - onos_config_req.raise_for_status() - onos_config = onos_config_req.json() - service_uuid = uuid.uuid4() - if 'org.onosproject.vpls' in onos_config['apps']: - if 'vpls' not in onos_config['apps']['org.onosproject.vpls']: - onos_config['apps']['org.onosproject.vpls']['vpls'] = { - 'vplsList': [] - } - for vpls in onos_config['apps']['org.onosproject.vpls']['vpls']['vplsList']: - if vpls['name'] == service_uuid: - raise SdnConnectorError('Network {} already exists.'.format(service_uuid)) - onos_config['apps']['org.onosproject.vpls']['vpls']['vplsList'].append({ - 'name': service_uuid, - 'interfaces': [] - }) - self._pop_last_update_time(onos_config) - else: - onos_config['apps'] = { - 'org.onosproject.vpls': { - 'vpls': { - "vplsList": [ - { - 'name': service_uuid, - 'interfaces': [] - } - ] + # FIXME ¿must check number of connection_points? + service_uuid = str(uuid.uuid4()) + + # Obtain current configuration + onos_config_orig = self._get_onos_netconfig() + #self.logger.debug("onos config: %s", onos_config_orig) + onos_config = copy.deepcopy(onos_config_orig) + + try: + # Create missing interfaces + created_ifs = self._create_missing_interfaces(connection_points, onos_config) + self._post_onos_netconfig(onos_config) + created_items = created_ifs + + # Add vpls service to config + interfaces = [port.get("service_endpoint_id") for port in connection_points] + if 'org.onosproject.vpls' in onos_config['apps']: + if 'vpls' not in onos_config['apps']['org.onosproject.vpls']: + onos_config['apps']['org.onosproject.vpls']['vpls'] = { + 'vplsList': [] + } + for vpls in onos_config['apps']['org.onosproject.vpls']['vpls']['vplsList']: + if vpls['name'] == service_uuid: + raise SdnConnectorError('Network {} already exists.'.format(service_uuid)) + onos_config['apps']['org.onosproject.vpls']['vpls']['vplsList'].append({ + 'name': service_uuid, + 'interfaces': interfaces + }) + self._pop_last_update_time(onos_config) + else: + onos_config['apps'] = { + 'org.onosproject.vpls': { + 'vpls': { + "vplsList": [ + { + 'name': service_uuid, + 'interfaces': interfaces + } + ] + } } } - } - response = requests.post(self.url, json=onos_config, auth=HTTPBasicAuth(self.user, self.password)) - log.info(onos_config) - response.raise_for_status() + self._set_encapsulation(service_uuid, connection_points, onos_config) + #self.logger.debug("original config: %s", onos_config_orig) + #self.logger.debug("original config: %s", onos_config) + self._post_onos_netconfig(onos_config) + + self.logger.debug("created connectivity_service, service_uuid: {}, created_items: {}". + format(service_uuid, created_items)) + return service_uuid, created_items + except Exception as e: + self.logger.error('Exception add connection_service: %s', e) + # try to rollback push original config + try: + self._post_onos_netconfig(onos_config_orig) + except Exception as e: + self.logger.error('Exception rolling back to original config: %s', e) + # raise exception + if isinstance(e, SdnConnectorError): + raise + else: + raise SdnConnectorError("Exception create_connectivity_service: {}".format(e)) + + def _set_encapsulation(self, service_uuid, connection_points, onos_config): + + # check if encapsulation is vlan, check just one connection point + encapsulation = None for connection_point in connection_points: - self._add_network_port(service_uuid, connection_point) - return service_uuid, onos_config + if connection_point.get("service_endpoint_encapsulation_type") == "dot1q": + encapsulation = "VLAN" + break + # if encapsulation is defined, assign + if encapsulation: + #self.logger.debug("assign encapsulation") + for vpls in onos_config['apps']['org.onosproject.vpls']['vpls']['vplsList']: + if vpls['name'] == service_uuid: + vpls['encapsulation'] = encapsulation def edit_connectivity_service(self, service_uuid, conn_info, connection_points, @@ -119,22 +201,41 @@ class OnosVpls(SdnConnectorBase): raise SdnConnectorError('Not supported', http_code=501) def delete_connectivity_service(self, service_uuid, conn_info=None): - onos_config_req = requests.get(self.url, auth=HTTPBasicAuth(self.user, self.password)) - onos_config_req.raise_for_status() - onos_config = onos_config_req.json() - # Removes ports used by network from onos config - for vpls in onos_config['apps']['org.onosproject.vpls']['vpls']['vplsList']: - if vpls['name'] == service_uuid: - for interface in vpls['interfaces']: - for port in onos_config['ports'].values(): - for port_interface in port['interfaces']: - if port_interface['name'] == interface: - port['interfaces'].remove(port_interface) - onos_config['apps']['org.onosproject.vpls']['vpls']['vplsList'].remove(vpls) - break - self._pop_last_update_time(onos_config) - response = requests.post(self.url, json=onos_config, auth=HTTPBasicAuth(self.user, self.password)) - response.raise_for_status() + self.logger.debug("delete_connectivity_service uuid: {}".format(service_uuid)) + + conn_info = conn_info or [] + # Obtain current config + onos_config = self._get_onos_netconfig() + + try: + # created_interfaces + created_ifs = [item[1] for item in conn_info] + + # Removes ports used by network from onos config + for vpls in onos_config['apps']['org.onosproject.vpls']['vpls']['vplsList']: + if vpls['name'] == service_uuid: + # iterate interfaces to check if must delete them + for interface in vpls['interfaces']: + for port in onos_config['ports'].values(): + for port_interface in port['interfaces']: + if port_interface['name'] == interface: + # Delete only created ifzs + if port_interface['name'] in created_ifs: + self.logger.debug("Delete ifz: {}".format(port_interface['name'])) + port['interfaces'].remove(port_interface) + onos_config['apps']['org.onosproject.vpls']['vpls']['vplsList'].remove(vpls) + break + else: + raise SdnConnectorError("service uuid: {} does not exist".format(service_uuid)) + + self._post_onos_netconfig(onos_config) + self.logger.debug("deleted connectivity service uuid: {}".format(service_uuid)) + except Exception as e: + if isinstance(e, SdnConnectorError): + raise + else: + self.logger.error('Exception delete connection_service: %s', e, exc_info=True) + raise SdnConnectorError("Exception create_connectivity_service: {}".format(str(e))) def _delete_network_port(self, net_id, port): onos_config_req = requests.get(self.url, auth=HTTPBasicAuth(self.user, self.password)) @@ -155,65 +256,83 @@ class OnosVpls(SdnConnectorBase): response = requests.post(self.url, json=onos_config, auth=HTTPBasicAuth(self.user, self.password)) response.raise_for_status() - def _add_network_port(self, net_id, port): - onos_config_req = requests.get(self.url, auth=HTTPBasicAuth(self.user, self.password)) - onos_config_req.raise_for_status() - onos_config = onos_config_req.json() - self._append_port_to_onos_config(port, onos_config) - # Interfaces need to be registered before adding them to VPLS - response = requests.post(self.url, json=onos_config, auth=HTTPBasicAuth(self.user, self.password)) - response.raise_for_status() - for vpls in onos_config['apps']['org.onosproject.vpls']['vpls']['vplsList']: - if vpls['name'] == net_id: - vpls['interfaces'].append(port['service_endpoint_id']) - break - self._pop_last_update_time(onos_config) - response = requests.post(self.url, json=onos_config, auth=HTTPBasicAuth(self.user, self.password)) - response.raise_for_status() - def _pop_last_update_time(self, onos_config): - if 'lastUpdateTime' in onos_config['apps']['org.onosproject.vpls']['vpls']: - onos_config['apps']['org.onosproject.vpls']['vpls'].pop('lastUpdateTime') + onos_config['apps']['org.onosproject.vpls']['vpls'].pop('lastUpdateTime', None) + + def _create_missing_interfaces(self,connection_points, onos_config): + created_ifs = [] + for port in connection_points: + created_ifz = self._append_port_to_onos_config(port, onos_config) + if created_ifz: + created_ifs.append(created_ifz) + return created_ifs def _append_port_to_onos_config(self, port, onos_config): + created_item = None port_name = 'of:{}/{}'.format(port['service_endpoint_encapsulation_info']['switch_dpid'], - port['service_endpoint_encapsulation_info']['switch_port']) + port['service_endpoint_encapsulation_info']['switch_port']) interface_config = {'name': port['service_endpoint_id']} - if 'vlan' in port['service_endpoint_encapsulation_info'] and port['service_endpoint_encapsulation_info'][ - 'vlan']: + if 'vlan' in port['service_endpoint_encapsulation_info'] \ + and port['service_endpoint_encapsulation_info']['vlan']: interface_config['vlan'] = port['service_endpoint_encapsulation_info']['vlan'] if port_name in onos_config['ports'] and 'interfaces' in onos_config['ports'][port_name]: for interface in onos_config['ports'][port_name]['interfaces']: if interface['name'] == port['service_endpoint_id']: - onos_config['ports'][port_name]['interfaces'].remove(interface) - onos_config['ports'][port_name]['interfaces'].append(interface_config) + #self.logger.debug("interface with same name and port exits") + # interface already exists TODO ¿check vlan? ¿delete and recreate? + # by the moment use and do not touch + #onos_config['ports'][port_name]['interfaces'].remove(interface) + break + else: + #self.logger.debug("port with same name exits but not interface") + onos_config['ports'][port_name]['interfaces'].append(interface_config) + created_item = (port_name, port['service_endpoint_id']) else: + #self.logger.debug("create port and interface") onos_config['ports'][port_name] = { 'interfaces': [interface_config] } + created_item = (port_name, port['service_endpoint_id']) + return created_item if __name__ == '__main__': - pass - # host = '198.204.228.85' - # port = 8181 - # onos_vpls = OnosVpls(host, port, 'onos', 'rocks') - # ports = [ - # { - # 'uuid': '0a43961d', - # 'switch_dpid': '0000000000000001', - # 'switch_port': '1', - # 'vlan': 100 - # }, - # { - # 'uuid': 'ade3eefc', - # 'switch_dpid': '0000000000000003', - # 'switch_port': '1', - # 'vlan': 100 - # } - # ] - # onos_vpls.create_network('94979b37-3875-4f77-b620-01ff78f9c4fa', 'data') - # onos_vpls.add_network_port('94979b37-3875-4f77-b620-01ff78f9c4fa', ports[0]) - # onos_vpls.add_network_port('94979b37-3875-4f77-b620-01ff78f9c4fa', ports[1]) - # onos_vpls.delete_network_port('94979b37-3875-4f77-b620-01ff78f9c4fa', ports[1]) - # onos_vpls.delete_network('94979b37-3875-4f77-b620-01ff78f9c4fa') + logger = logging.getLogger('openmano.sdn.onos_vpls') + logging.basicConfig() + logger.setLevel(getattr(logging, "DEBUG")) + # wim_url = "http://10.95.172.251:8181" + wim_url = "http://192.168.56.106:8181" + user = "karaf" + password = "karaf" + wim = {'wim_url': wim_url} + wim_account = {'user': user, 'password': password} + onos_vpls = OnosVpls(wim=wim, wim_account=wim_account, logger=logger) + conn_service = onos_vpls.get_connectivity_service_status("4e1f4c8a-a874-425d-a9b5-955cb77178f8") + print(conn_service) + service_type = 'ELAN' + conn_point_0 = { + "service_endpoint_id": "switch1_ifz2", + "service_endpoint_encapsulation_type": "dot1q", + "service_endpoint_encapsulation_info": { + "switch_dpid": "0000000000000011", + "switch_port": "1", + "vlan": "600" + } + } + conn_point_1 = { + "service_endpoint_id": "switch3_ifz2", + "service_endpoint_encapsulation_type": "dot1q", + "service_endpoint_encapsulation_info": { + "switch_dpid": "0000000000000031", + "switch_port": "3", + "vlan": "600" + } + } + connection_points = [conn_point_0, conn_point_1] + #service_uuid, created_items = onos_vpls.create_connectivity_service(service_type, connection_points) + #print(service_uuid) + #print(created_items) + + #conn_info = None + conn_info = [('of:0000000000000011/1', 'switch1_ifz2'), ('of:0000000000000031/3', 'switch3_ifz2')] + onos_vpls.delete_connectivity_service("3a6a752e-8153-4b89-8b43-a7cebe0f0628", conn_info) -- 2.25.1