def _generate_vni(self): """ Method to get unused VxLAN Network Identifier (VNI) Args: None Returns: VNI """ # find unused VLAN ID for vlanID_range in self.vni_range: try: start_vni, end_vni = map( int, vlanID_range.replace(" ", "").split("-")) for i in range(start_vni, end_vni + 1): vni = random.randrange(start_vni, end_vni, 1) if vni not in self.used_vni: return vni except Exception as exp: raise SdnConnectorError( "Exception {} occurred while searching a free VNI.".format( exp)) else: raise SdnConnectorError( "Unable to create the virtual network." " All VNI in VNI range {} are in use.".format(self.vni_range))
def edit_connectivity_service(self, service_uuid, conn_info=None, connection_points=None, **kwargs): self.logger.info( "Editing connectivity service id: {}".format(service_uuid)) data = {"timeout-millis": 10000, "acceptable": ["DORMANT"]} try: self.__post(self.__ACTIONS_MAP.get("RESET"), "/service/" + service_uuid, get_response=False) response = self.__post(self.__ACTIONS_MAP.get("CHECK"), "/service/" + service_uuid, data) if "status" in response: self.logger.debug( "Connectivity service {} reset".format(service_uuid)) else: raise SdnConnectorError( "Invalid status check response (could be an issue with the DPB)", 500) except Exception as e: raise SdnConnectorError( "Failed to reset service | text: {}".format(e), 500) try: data = {"segment": []} for point in connection_points: data["segment"].append({ "terminal-name": point.get("service_endpoint_id"), "label": int((point.get("service_endpoint_encapsulation_info") ).get("vlan")), "ingress-bw": 10.0, "egress-bw": 10.0 }) # "ingress-bw": (bandwidth.get(point.get("service_endpoint_id"))).get("ingress"), # "egress-bw": (bandwidth.get(point.get("service_endpoint_id"))).get("egress")} self.__post(self.__ACTIONS_MAP.get("DEFINE"), "/service/" + str(service_uuid), data, get_response=False) self.__post(self.__ACTIONS_MAP.get("ACTIVATE"), "/service/" + str(service_uuid), get_response=False) except Exception as e: raise SdnConnectorError( "Failed to edit connectivity service | text: {}".format(e), 500) self.logger.debug( "Edited connectivity service {}".format(service_uuid)) return conn_info
def post(self, function, url_params="", data=None, get_response=True): url = self.__base_url + url_params + \ "/" + function[self.__FUNCTION_MAP_POS] try: self.logger.info(data) response = requests.post(url, json=data) if response.status_code != 200: raise SdnConnectorError( "REST request failed (status code: {})".format( response.status_code)) if get_response: return response.json() except Exception as e: raise SdnConnectorError("REST request failed | text: {}".format(e), 500)
def __build_private_key_obj(self): try: with open(self.__auth_data.get("key_file"), 'r') as key_file: if self.__auth_data.get("key_type") == "RSA": return paramiko.RSAKey.from_private_key( key_file, password=self.__auth_data.get("key_pass", None)) elif self.__auth_data.get("key_type") == "ECDSA": return paramiko.ECDSAKey.from_private_key( key_file, password=self.__auth_data.get("key_pass", None)) else: raise SdnConnectorError("Key type not supported", 400) except Exception as e: raise SdnConnectorError( "Could not load private SSH key | text: {}".format(e), 500)
def get(self, function, url_params=""): url = self.__base_url + url_params + function[self.__FUNCTION_MAP_POS] try: return requests.get(url) except Exception as e: raise SdnConnectorError("REST request failed | text: {}".format(e), 500)
def __init__(self, wim, wim_account, config): self.logger = logging.getLogger(self.__LOGGER_NAME) self.__wim = wim self.__account = wim_account self.__config = config self.__cli_config = self.__account.pop("config", None) self.__url = self.__wim.get("wim_url", "") self.__password = self.__account.get("passwd", "") self.__username = self.__account.get("user", "") self.__network = self.__cli_config.get("network", "") self.__connection_type = self.__cli_config.get("connection_type", "REST") self.__port = self.__cli_config.get( "port", (80 if self.__connection_type == "REST" else 22)) self.__ssh_auth = self.__cli_config.get("ssh_auth", None) if self.__connection_type == "SSH": interface = DpbSshInterface(self.__username, self.__password, self.__url, self.__port, self.__network, self.__ssh_auth, self.__LOGGER_NAME) elif self.__connection_type == "REST": interface = DpbRestInterface(self.__url, self.__port, self.__network, self.__LOGGER_NAME) else: raise SdnConnectorError( "Connection type not supported (must be SSH or REST)", 400) self.__post = interface.post self.__get = interface.get self.logger.info("DPB WimConn Init OK")
def create_virtual_network(self, name, vni): self.logger.debug("create vname, name: {}, vni: {}".format(name, vni)) routetarget = '{}:{}'.format(self.asn, vni) vnet_dict = { "virtual-network": { "virtual_network_properties": { "vxlan_network_identifier": vni, }, "parent_type": "project", "fq_name": [self.domain, self.project, name], "route_target_list": { "route_target": ["target:" + routetarget] } } } endpoint = self.controller_url + 'virtual-networks' resp = self.http.post_cmd(url=endpoint, headers=self.http_header, post_fields_dict=vnet_dict) if not resp: raise SdnConnectorError( 'Error creating virtual network: empty response') vnet_info = json.loads(resp) self.logger.debug("created vnet, vnet_info: {}".format(vnet_info)) return vnet_info.get("virtual-network").get('uuid'), vnet_info.get( "virtual-network")
def delete_connectivity_service(self, service_uuid, conn_info=None): self.logger.debug( "delete_connectivity_service uuid: {}".format(service_uuid)) conn_info = conn_info or {} created_ifs = conn_info.get("interfaces", []) # Obtain current config onos_config = self._get_onos_netconfig() try: # Removes ports used by network from onos config for vpls in onos_config.get('apps', {}).get( 'org.onosproject.vpls', {}).get('vpls', {}).get('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._pop_last_update_time(onos_config) self._post_onos_netconfig(onos_config) self.logger.debug( "deleted connectivity service uuid: {}".format(service_uuid)) except SdnConnectorError: raise except Exception as e: self.logger.error('Exception delete connection_service: %s', e, exc_info=True) raise SdnConnectorError( "Exception delete connectivity service: {}".format(str(e)))
def check_credentials(self): """Check if the connector itself can access the SDN/WIM with the provided url (wim.wim_url), user (wim_account.user), and password (wim_account.password) Raises: SdnConnectorError: Issues regarding authorization, access to external URLs, etc are detected. """ self.logger.debug("") try: resp = self.underlay_api.check_auth() if not resp: raise SdnConnectorError('Empty response') except Exception as e: self.logger.error('Error checking credentials') raise SdnConnectorError('Error checking credentials: {}'.format( str(e)))
def __exception(self, x, **kwargs): http_code = kwargs.get("http_code") if hasattr(x, "value"): error = x.value else: error = x self.logger.error(error) raise SdnConnectorError(error, http_code=http_code)
def create_vmi(self, switch_id, switch_port, network, vlan): self.logger.debug( "create vmi, switch_id: {}, switch_port: {}, network: {}, vlan: {}" .format(switch_id, switch_port, network, vlan)) vmi_name = self.get_vmi_name(switch_id, switch_port, vlan) vpg_name = self.get_vpg_name(switch_id, switch_port) profile_dict = { "local_link_information": [{ "port_id": switch_port.replace(":", "_"), "switch_id": switch_port.replace(":", "_"), "switch_info": switch_id, "fabric": self.fabric }] } vmi_dict = { "virtual-machine-interface": { "parent_type": "project", "fq_name": [self.domain, self.project, vmi_name], "virtual_network_refs": [{ "to": [self.domain, self.project, network] }], "virtual_machine_interface_properties": { "sub_interface_vlan_tag": vlan }, "virtual_machine_interface_bindings": { "key_value_pair": [{ "key": "vnic_type", "value": "baremetal" }, { "key": "vif_type", "value": "vrouter" }, { "key": "vpg", "value": vpg_name }, { "key": "profile", "value": json.dumps(profile_dict) }] } } } endpoint = self.controller_url + 'virtual-machine-interfaces' self.logger.debug("vmi_dict: {}".format(vmi_dict)) resp = self.http.post_cmd(url=endpoint, headers=self.http_header, post_fields_dict=vmi_dict) if not resp: raise SdnConnectorError('Error creating vmi: empty response') vmi_info = json.loads(resp) self.logger.debug("created vmi, info: {}".format(vmi_info)) return vmi_info.get("virtual-machine-interface").get( 'uuid'), vmi_info.get("virtual-machine-interface")
def get_connectivity_service_status(self, service_uuid, conn_info=None): self.logger.info( "Checking connectivity service status id:{}".format(service_uuid)) data = {"timeout-millis": 10000, "acceptable": ["ACTIVE", "FAILED"]} try: response = self.__post(self.__ACTIONS_MAP.get("CHECK"), "/service/" + service_uuid, data) if "status" in response: status = response.get("status", None) self.logger.info("CHECKED CONNECTIVITY SERVICE STATUS") return {"wim_status": self.__STATUS_MAP.get(status)} else: raise SdnConnectorError( "Invalid status check response (could be an issue with the DPB)", 500) except Exception as e: raise SdnConnectorError( "Failed to check service status | text: {}".format(e), 500)
def delete_connectivity_service(self, service_uuid, conn_info=None): """ Disconnect multi-site endpoints previously connected :param service_uuid: The one returned by create_connectivity_service :param conn_info: The one returned by last call to 'create_connectivity_service' or 'edit_connectivity_service' if they do not return None :return: None :raises: SdnConnectorException: In case of error. The parameter http_code must be filled """ self.logger.info( "delete_connectivity_service vnet_name: {}, connection_points: {}". format(service_uuid, conn_info)) try: vnet_uuid = service_uuid # vnet_name = conn_info["vnet"]["name"] # always should exist as the network is the first thing created work_cps = conn_info["connection_points"] # 1: For each connection point delete vlan from vpg and it is is the # last one, delete vpg for cp in work_cps.values(): self._delete_port(cp.get("switch_dpid"), cp.get("switch_port"), cp.get("vlan")) # 2: Delete vnet self.underlay_api.delete_virtual_network(vnet_uuid) self.logger.info( "deleted connectivity_service vnet_uuid: {}, connection_points: {}" .format(service_uuid, conn_info)) except SdnConnectorError: raise except HttpException as e: self.logger.error( "Error deleting connectivity service: {}".format(e)) raise SdnConnectorError( "Exception deleting connectivity service: {}".format(str(e))) except Exception as e: self.logger.error( "Error deleting connectivity service: {}".format(e), exc_info=True) raise SdnConnectorError( "Exception deleting connectivity service: {}".format(str(e)))
def create_connectivity_service(self, service_type, connection_points, **kwargs): self.logger.info("Creating a connectivity service") try: response = self.__post(self.__ACTIONS_MAP.get("CREATE")) if "service-id" in response: service_id = int(response.get("service-id")) self.logger.debug("created service id {}".format(service_id)) else: raise SdnConnectorError( "Invalid create service response (could be an issue with the DPB)", 500) data = {"segment": []} for point in connection_points: data["segment"].append({ "terminal-name": point.get("service_endpoint_id"), "label": int((point.get("service_endpoint_encapsulation_info") ).get("vlan")), "ingress-bw": 10.0, "egress-bw": 10.0 }) # "ingress-bw": (bandwidth.get(point.get("service_endpoint_id"))).get("ingress"), # "egress-bw": (bandwidth.get(point.get("service_endpoint_id"))).get("egress")} self.__post(self.__ACTIONS_MAP.get("DEFINE"), "/service/" + str(service_id), data, get_response=False) self.__post(self.__ACTIONS_MAP.get("ACTIVATE"), "/service/" + str(service_id), get_response=False) self.logger.debug( "Created connectivity service id:{}".format(service_id)) return (str(service_id), None) except Exception as e: raise SdnConnectorError( "Connectivity service could not be made | text: {}".format(e), 500)
def delete_connectivity_service(self, service_uuid, conn_info=None): """Disconnect multi-site endpoints previously connected """ self.logger.debug( "delete_connectivity_service: service_uuid='{}' conn_info='{}'". format(service_uuid, conn_info)) if service_uuid not in self.connections: raise SdnConnectorError( "connectivity {} not found".format(service_uuid), http_code=HTTPStatus.NOT_FOUND.value) self.connections.pop(service_uuid, None) return None
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 _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 __init__(self, wim, wim_account, config=None, logger=None): 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.get("wim_url") if not url: 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" self.logger.info("ONOS VPLS Connector Initialized.")
def delete_connectivity_service(self, service_uuid, conn_info=None): self.logger.info( "Deleting connectivity service id: {}".format(service_uuid)) try: self.__post(self.__ACTIONS_MAP.get("RELEASE"), "/service/" + service_uuid, get_response=False) except Exception as e: raise SdnConnectorError( "Could not delete service id:{} (could be an issue with the DPB): {}" .format(service_uuid, e), 500) self.logger.debug( "Deleted connectivity service id:{}".format(service_uuid)) return None
def check_credentials(self): status_code = 503 onos_config_req = None try: onos_config_req = requests.get(self.url, auth=HTTPBasicAuth( self.user, self.password)) onos_config_req.raise_for_status() except Exception as e: if onos_config_req: status_code = onos_config_req.status_code self.logger.exception('Error checking credentials: {}'.format(e)) raise SdnConnectorError('Error checking credentials: {}'.format(e), http_code=status_code)
def post(self, function, url_params="", data=None, get_response=True): """post request to dpb via ssh notes: - session_id need only be unique per ssh session, thus is currently safe if ro is restarted """ self._check_connection() if data is None: data = {} url_ext_info = url_params.split('/') for i in range(0, len(url_ext_info)): if url_ext_info[i] == "service": data["service-id"] = int(url_ext_info[i + 1]) data["type"] = function[self.__FUNCTION_MAP_POS] data = {"session": self.__session_id, "content": data} self.__session_id += 1 try: data = json.dumps(data).encode("utf-8") data_packed = struct.pack(">I" + str(len(data)) + "s", len(data), data) self.__stdin.write(data_packed) self.logger.debug("Data sent to DPB via SSH") except Exception as e: raise SdnConnectorError( "Failed to write via SSH | text: {}".format(e), 500) try: data_len = struct.unpack(">I", self.__stdout.read(4))[0] data = struct.unpack( str(data_len) + "s", self.__stdout.read(data_len))[0] return json.loads(data).get("content", {}) except Exception as e: raise SdnConnectorError( "Could not get response from WIM | text: {}".format(e), 500)
def edit_connectivity_service(self, service_uuid, conn_info=None, connection_points=None, **kwargs): """Change an existing connectivity service. This method's arguments and return value follow the same convention as :meth:`~.create_connectivity_service`. """ self.logger.debug( "edit_connectivity_service: service_uuid='{}' conn_info='{}', connection_points='{}'" "kwargs='{}'".format(service_uuid, conn_info, connection_points, kwargs)) if service_uuid not in self.connections: raise SdnConnectorError( "connectivity {} not found".format(service_uuid), http_code=HTTPStatus.NOT_FOUND.value) self.connections[service_uuid] = connection_points.copy() return None
def create_vpg(self, switch_id, switch_port): self.logger.debug("create vpg, switch_id: {}, switch_port: {}".format( switch_id, switch_port)) vpg_name = self.get_vpg_name(switch_id, switch_port) vpg_dict = { "virtual-port-group": { "parent_type": "fabric", "fq_name": ["default-global-system-config", self.fabric, vpg_name] } } endpoint = self.controller_url + 'virtual-port-groups' resp = self.http.post_cmd(url=endpoint, headers=self.http_header, post_fields_dict=vpg_dict) if not resp: raise SdnConnectorError( 'Error creating virtual port group: empty response') vpg_info = json.loads(resp) self.logger.debug("created vpg, vpg_info: {}".format(vpg_info)) return vpg_info.get("virtual-port-group").get('uuid'), vpg_info.get( "virtual-port-group")
def __connect(self): private_key = None password = None if self.__auth_data.get("auth_type", "PASS") == "KEY": private_key = self.__build_private_key_obj() if self.__auth_data.get("auth_type", "PASS") == "PASS": password = self.__password try: self.__ssh_client.connect(hostname=self.__url, port=self.__port, username=self.__username, password=password, pkey=private_key, look_for_keys=False, compress=False) stdin, stdout, stderr = self.__ssh_client.exec_command( command=self.__network) except paramiko.BadHostKeyException as e: raise SdnConnectorError( "Could not add SSH host key | text: {}".format(e), 500) except paramiko.AuthenticationException as e: raise SdnConnectorError( "Could not authorize SSH connection | text: {}".format(e), 400) except paramiko.SSHException as e: raise SdnConnectorError( "Could not establish the SSH connection | text: {}".format(e), 500) except Exception as e: raise SdnConnectorError( "Unknown error occurred when connecting via SSH | text: {}". format(e), 500) try: data_len = struct.unpack(">I", stdout.read(4))[0] data = json.loads( struct.unpack(str(data_len) + "s", stdout.read(data_len))[0]) except Exception as e: raise SdnConnectorError( "Failed to get response from DPB | text: {}".format(e), 500) if "error" in data: raise SdnConnectorError( data.get("msg", data.get("error", "ERROR")), 500) self.logger.info("SSH connection to DPB established OK") return stdin, stdout
def get_all_active_connectivity_services(self): raise SdnConnectorError('Impossible to use WIM: {}'.format(self.error_msg))
def edit_connectivity_service(self, service_uuid, *args, **kwargs): raise SdnConnectorError('Impossible to change connection {}: {}' .format(service_uuid, self.error_msg))
def delete_connectivity_service(self, service_uuid, _conn_info=None): raise SdnConnectorError('Impossible to delete {}: {}' .format(service_uuid, self.error_msg))
def create_connectivity_service(self, service_uuid, *args, **kwargs): raise SdnConnectorError('Impossible to create connectivity: {}' .format(self.error_msg))
def get_connectivity_service_status(self, service_uuid, _conn_info=None): raise SdnConnectorError('Impossible to retrieve status for {}: {}' .format(service_uuid, self.error_msg))
def check_credentials(self): raise SdnConnectorError('Impossible to use WIM:\n' + self.error_msg)