X-Git-Url: https://osm.etsi.org/gitweb/?p=osm%2FRO.git;a=blobdiff_plain;f=RO-SDN-onos_openflow%2Fosm_rosdn_onosof%2Fonos_of.py;h=cba505cb9855cbcc77dd61ea63dacf95bbe4ddbe;hp=a29b40825101e435b451279daaa2462d1d758e5c;hb=80135b928ab442c38898750b4751480205b4affc;hpb=e493e9b91720e5116e00b4c06cf66c767bccce2f diff --git a/RO-SDN-onos_openflow/osm_rosdn_onosof/onos_of.py b/RO-SDN-onos_openflow/osm_rosdn_onosof/onos_of.py index a29b4082..cba505cb 100644 --- a/RO-SDN-onos_openflow/osm_rosdn_onosof/onos_of.py +++ b/RO-SDN-onos_openflow/osm_rosdn_onosof/onos_of.py @@ -32,7 +32,12 @@ import json import requests import base64 import logging -from osm_ro_plugin.openflow_conn import OpenflowConn, OpenflowConnConnectionException, OpenflowConnUnexpectedResponse +from osm_ro_plugin.openflow_conn import ( + OpenflowConn, + OpenflowConnConnectionException, + OpenflowConnUnexpectedResponse, +) + # OpenflowConnException, OpenflowConnAuthException, OpenflowConnNotFoundException, \ # OpenflowConnConflictException, OpenflowConnNotSupportedException, OpenflowConnNotImplemented @@ -44,49 +49,58 @@ class OfConnOnos(OpenflowConn): """ ONOS connector. No MAC learning is used """ + def __init__(self, params): - """ Constructor. - :param params: dictionary with the following keys: - of_dpid: DPID to use for this controller ?? Does a controller have a dpid? - of_url: must be [http://HOST:PORT/] - of_user: user credentials, can be missing or None - of_password: password credentials - of_debug: debug level for logging. Default to ERROR - other keys are ignored - Raise an exception if same parameter is missing or wrong + """Constructor. + :param params: dictionary with the following keys: + of_dpid: DPID to use for this controller ?? Does a controller have a dpid? + of_url: must be [http://HOST:PORT/] + of_user: user credentials, can be missing or None + of_password: password credentials + of_debug: debug level for logging. Default to ERROR + other keys are ignored + Raise an exception if same parameter is missing or wrong """ - OpenflowConn.__init__(self, params) # check params url = params.get("of_url") + if not url: raise ValueError("'url' must be provided") + if not url.startswith("http"): url = "http://" + url + if not url.endswith("/"): url = url + "/" + self.url = url + "onos/v1/" # internal variables self.name = "onosof" - self.headers = {'content-type': 'application/json', 'accept': 'application/json'} + self.headers = { + "content-type": "application/json", + "accept": "application/json", + } self.auth = "None" self.pp2ofi = {} # From Physical Port to OpenFlow Index self.ofi2pp = {} # From OpenFlow Index to Physical Port self.dpid = str(params["of_dpid"]) - self.id = 'of:'+str(self.dpid.replace(':', '')) + self.id = "of:" + str(self.dpid.replace(":", "")) # TODO This may not be straightforward if params.get("of_user"): of_password = params.get("of_password", "") - self.auth = base64.b64encode(bytes(params["of_user"] + ":" + of_password, "utf-8")) + self.auth = base64.b64encode( + bytes(params["of_user"] + ":" + of_password, "utf-8") + ) self.auth = self.auth.decode() - self.headers['authorization'] = 'Basic ' + self.auth + self.headers["authorization"] = "Basic " + self.auth - self.logger = logging.getLogger('ro.sdn.onosof') + self.logger = logging.getLogger("ro.sdn.onosof") # self.logger.setLevel( getattr(logging, params.get("of_debug", "ERROR")) ) self.logger.debug("onosof plugin initialized") self.ip_address = None @@ -98,60 +112,89 @@ class OfConnOnos(OpenflowConn): Raise a openflowconnUnexpectedResponse expection in case of failure """ try: - self.headers['content-type'] = 'text/plain' + self.headers["content-type"] = "text/plain" of_response = requests.get(self.url + "devices", headers=self.headers) - error_text = "Openflow response %d: %s" % (of_response.status_code, of_response.text) + error_text = "Openflow response %d: %s" % ( + of_response.status_code, + of_response.text, + ) + if of_response.status_code != 200: self.logger.warning("get_of_switches " + error_text) + raise OpenflowConnUnexpectedResponse(error_text) self.logger.debug("get_of_switches " + error_text) info = of_response.json() if type(info) != dict: - self.logger.error("get_of_switches. Unexpected response, not a dict: %s", str(info)) - raise OpenflowConnUnexpectedResponse("Unexpected response, not a dict. Wrong version?") + self.logger.error( + "get_of_switches. Unexpected response, not a dict: %s", str(info) + ) + + raise OpenflowConnUnexpectedResponse( + "Unexpected response, not a dict. Wrong version?" + ) - node_list = info.get('devices') + node_list = info.get("devices") if type(node_list) is not list: self.logger.error( "get_of_switches. Unexpected response, at 'devices', not found or not a list: %s", - str(type(node_list))) - raise OpenflowConnUnexpectedResponse("Unexpected response, at 'devices', not found " - "or not a list. Wrong version?") + str(type(node_list)), + ) + + raise OpenflowConnUnexpectedResponse( + "Unexpected response, at 'devices', not found " + "or not a list. Wrong version?" + ) switch_list = [] for node in node_list: - node_id = node.get('id') + node_id = node.get("id") if node_id is None: - self.logger.error("get_of_switches. Unexpected response at 'device':'id', not found: %s", - str(node)) - raise OpenflowConnUnexpectedResponse("Unexpected response at 'device':'id', " - "not found . Wrong version?") + self.logger.error( + "get_of_switches. Unexpected response at 'device':'id', not found: %s", + str(node), + ) - node_ip_address = node.get('annotations').get('managementAddress') + raise OpenflowConnUnexpectedResponse( + "Unexpected response at 'device':'id', " + "not found . Wrong version?" + ) + + node_ip_address = node.get("annotations").get("managementAddress") if node_ip_address is None: self.logger.error( "get_of_switches. Unexpected response at 'device':'managementAddress', not found: %s", - str(node)) - raise OpenflowConnUnexpectedResponse( - "Unexpected response at 'device':'managementAddress', not found. Wrong version?") + str(node), + ) - node_id_hex = hex(int(node_id.split(':')[1])).split('x')[1].zfill(16) + raise OpenflowConnUnexpectedResponse( + "Unexpected response at 'device':'managementAddress', not found. Wrong version?" + ) + node_id_hex = hex(int(node_id.split(":")[1])).split("x")[1].zfill(16) switch_list.append( - (':'.join(a + b for a, b in zip(node_id_hex[::2], node_id_hex[1::2])), node_ip_address)) - return switch_list + ( + ":".join( + a + b for a, b in zip(node_id_hex[::2], node_id_hex[1::2]) + ), + node_ip_address, + ) + ) + return switch_list except requests.exceptions.RequestException as e: error_text = type(e).__name__ + ": " + str(e) self.logger.error("get_of_switches " + error_text) + raise OpenflowConnConnectionException(error_text) except ValueError as e: # ValueError in the case that JSON can not be decoded error_text = type(e).__name__ + ": " + str(e) self.logger.error("get_of_switches " + error_text) + raise OpenflowConnUnexpectedResponse(error_text) def obtain_port_correspondence(self): @@ -161,36 +204,55 @@ class OfConnOnos(OpenflowConn): Raise a openflowconnUnexpectedResponse expection in case of failure """ try: - self.headers['content-type'] = 'text/plain' - of_response = requests.get(self.url + "devices/" + self.id + "/ports", headers=self.headers) - error_text = "Openflow response {}: {}".format(of_response.status_code, of_response.text) + self.headers["content-type"] = "text/plain" + of_response = requests.get( + self.url + "devices/" + self.id + "/ports", headers=self.headers + ) + error_text = "Openflow response {}: {}".format( + of_response.status_code, of_response.text + ) + if of_response.status_code != 200: self.logger.warning("obtain_port_correspondence " + error_text) + raise OpenflowConnUnexpectedResponse(error_text) self.logger.debug("obtain_port_correspondence " + error_text) info = of_response.json() - node_connector_list = info.get('ports') + node_connector_list = info.get("ports") if type(node_connector_list) is not list: self.logger.error( "obtain_port_correspondence. Unexpected response at 'ports', not found or not a list: %s", - str(node_connector_list)) - raise OpenflowConnUnexpectedResponse("Unexpected response at 'ports', not found or not " - "a list. Wrong version?") + str(node_connector_list), + ) - for node_connector in node_connector_list: - if node_connector['port'] != "local": - self.pp2ofi[str(node_connector['annotations']['portName'])] = str(node_connector['port']) - self.ofi2pp[str(node_connector['port'])] = str(node_connector['annotations']['portName']) + raise OpenflowConnUnexpectedResponse( + "Unexpected response at 'ports', not found or not " + "a list. Wrong version?" + ) - node_ip_address = info['annotations']['managementAddress'] + for node_connector in node_connector_list: + if node_connector["port"] != "local": + self.pp2ofi[str(node_connector["annotations"]["portName"])] = str( + node_connector["port"] + ) + self.ofi2pp[str(node_connector["port"])] = str( + node_connector["annotations"]["portName"] + ) + + node_ip_address = info["annotations"]["managementAddress"] if node_ip_address is None: self.logger.error( "obtain_port_correspondence. Unexpected response at 'managementAddress', not found: %s", - str(self.id)) - raise OpenflowConnUnexpectedResponse("Unexpected response at 'managementAddress', " - "not found. Wrong version?") + str(self.id), + ) + + raise OpenflowConnUnexpectedResponse( + "Unexpected response at 'managementAddress', " + "not found. Wrong version?" + ) + self.ip_address = node_ip_address # print self.name, ": obtain_port_correspondence ports:", self.pp2ofi @@ -198,11 +260,13 @@ class OfConnOnos(OpenflowConn): except requests.exceptions.RequestException as e: error_text = type(e).__name__ + ": " + str(e) self.logger.error("obtain_port_correspondence " + error_text) + raise OpenflowConnConnectionException(error_text) except ValueError as e: # ValueError in the case that JSON can not be decoded error_text = type(e).__name__ + ": " + str(e) self.logger.error("obtain_port_correspondence " + error_text) + raise OpenflowConnUnexpectedResponse(error_text) def get_of_rules(self, translate_of_ports=True): @@ -221,106 +285,138 @@ class OfConnOnos(OpenflowConn): switch: DPID, all Raise a openflowconnUnexpectedResponse exception in case of failure """ - try: - if len(self.ofi2pp) == 0: self.obtain_port_correspondence() # get rules - self.headers['content-type'] = 'text/plain' - of_response = requests.get(self.url + "flows/" + self.id, headers=self.headers) - error_text = "Openflow response %d: %s" % (of_response.status_code, of_response.text) + self.headers["content-type"] = "text/plain" + of_response = requests.get( + self.url + "flows/" + self.id, headers=self.headers + ) + error_text = "Openflow response %d: %s" % ( + of_response.status_code, + of_response.text, + ) # The configured page does not exist if there are no rules installed. In that case we return an empty dict if of_response.status_code == 404: return [] - elif of_response.status_code != 200: self.logger.warning("get_of_rules " + error_text) + raise OpenflowConnUnexpectedResponse(error_text) + self.logger.debug("get_of_rules " + error_text) info = of_response.json() if type(info) != dict: - self.logger.error("get_of_rules. Unexpected response, not a dict: %s", str(info)) - raise OpenflowConnUnexpectedResponse("Unexpected openflow response, not a dict. " - "Wrong version?") + self.logger.error( + "get_of_rules. Unexpected response, not a dict: %s", + str(info), + ) - flow_list = info.get('flows') + raise OpenflowConnUnexpectedResponse( + "Unexpected openflow response, not a dict. Wrong version?" + ) + + flow_list = info.get("flows") if flow_list is None: return [] + if type(flow_list) is not list: self.logger.error( "get_of_rules. Unexpected response at 'flows', not a list: %s", - str(type(flow_list))) - raise OpenflowConnUnexpectedResponse("Unexpected response at 'flows', not a list. " - "Wrong version?") + str(type(flow_list)), + ) + + raise OpenflowConnUnexpectedResponse( + "Unexpected response at 'flows', not a list. Wrong version?" + ) rules = [] # Response list for flow in flow_list: - if not ('id' in flow and 'selector' in flow and 'treatment' in flow and - 'instructions' in flow['treatment'] and 'criteria' in - flow['selector']): - raise OpenflowConnUnexpectedResponse("unexpected openflow response, one or more " - "elements are missing. Wrong version?") + if not ( + "id" in flow + and "selector" in flow + and "treatment" in flow + and "instructions" in flow["treatment"] + and "criteria" in flow["selector"] + ): + raise OpenflowConnUnexpectedResponse( + "unexpected openflow response, one or more " + "elements are missing. Wrong version?" + ) rule = dict() - rule['switch'] = self.dpid - rule['priority'] = flow.get('priority') - rule['name'] = flow['id'] + rule["switch"] = self.dpid + rule["priority"] = flow.get("priority") + rule["name"] = flow["id"] - for criteria in flow['selector']['criteria']: - if criteria['type'] == 'IN_PORT': - in_port = str(criteria['port']) + for criteria in flow["selector"]["criteria"]: + if criteria["type"] == "IN_PORT": + in_port = str(criteria["port"]) if in_port != "CONTROLLER": if in_port not in self.ofi2pp: - raise OpenflowConnUnexpectedResponse("Error: Ingress port {} is not " - "in switch port list".format(in_port)) + raise OpenflowConnUnexpectedResponse( + "Error: Ingress port {} is not " + "in switch port list".format(in_port) + ) + if translate_of_ports: in_port = self.ofi2pp[in_port] - rule['ingress_port'] = in_port - - elif criteria['type'] == 'VLAN_VID': - rule['vlan_id'] = criteria['vlanId'] - elif criteria['type'] == 'ETH_DST': - rule['dst_mac'] = str(criteria['mac']).lower() + rule["ingress_port"] = in_port + elif criteria["type"] == "VLAN_VID": + rule["vlan_id"] = criteria["vlanId"] + elif criteria["type"] == "ETH_DST": + rule["dst_mac"] = str(criteria["mac"]).lower() actions = [] - for instruction in flow['treatment']['instructions']: - if instruction['type'] == "OUTPUT": - out_port = str(instruction['port']) + for instruction in flow["treatment"]["instructions"]: + if instruction["type"] == "OUTPUT": + out_port = str(instruction["port"]) if out_port != "CONTROLLER": if out_port not in self.ofi2pp: - raise OpenflowConnUnexpectedResponse("Error: Output port {} is not in " - "switch port list".format(out_port)) + raise OpenflowConnUnexpectedResponse( + "Error: Output port {} is not in " + "switch port list".format(out_port) + ) if translate_of_ports: out_port = self.ofi2pp[out_port] - actions.append(('out', out_port)) + actions.append(("out", out_port)) - if instruction['type'] == "L2MODIFICATION" and instruction['subtype'] == "VLAN_POP": - actions.append(('vlan', 'None')) - if instruction['type'] == "L2MODIFICATION" and instruction['subtype'] == "VLAN_ID": - actions.append(('vlan', instruction['vlanId'])) + if ( + instruction["type"] == "L2MODIFICATION" + and instruction["subtype"] == "VLAN_POP" + ): + actions.append(("vlan", "None")) - rule['actions'] = actions + if ( + instruction["type"] == "L2MODIFICATION" + and instruction["subtype"] == "VLAN_ID" + ): + actions.append(("vlan", instruction["vlanId"])) + + rule["actions"] = actions rules.append(rule) - return rules + return rules except requests.exceptions.RequestException as e: # ValueError in the case that JSON can not be decoded error_text = type(e).__name__ + ": " + str(e) self.logger.error("get_of_rules " + error_text) + raise OpenflowConnConnectionException(error_text) except ValueError as e: # ValueError in the case that JSON can not be decoded error_text = type(e).__name__ + ": " + str(e) self.logger.error("get_of_rules " + error_text) + raise OpenflowConnUnexpectedResponse(error_text) def del_flow(self, flow_name): @@ -329,23 +425,28 @@ class OfConnOnos(OpenflowConn): :param flow_name: :return: Raise a openflowconnUnexpectedResponse expection in case of failure """ - try: self.logger.debug("del_flow: delete flow name {}".format(flow_name)) - self.headers['content-type'] = None - of_response = requests.delete(self.url + "flows/" + self.id + "/" + flow_name, headers=self.headers) - error_text = "Openflow response {}: {}".format(of_response.status_code, of_response.text) + self.headers["content-type"] = None + of_response = requests.delete( + self.url + "flows/" + self.id + "/" + flow_name, headers=self.headers + ) + error_text = "Openflow response {}: {}".format( + of_response.status_code, of_response.text + ) if of_response.status_code != 204: self.logger.warning("del_flow " + error_text) + raise OpenflowConnUnexpectedResponse(error_text) self.logger.debug("del_flow: {} OK,: {} ".format(flow_name, error_text)) - return None + return None except requests.exceptions.RequestException as e: error_text = type(e).__name__ + ": " + str(e) self.logger.error("del_flow " + error_text) + raise OpenflowConnConnectionException(error_text) def new_flow(self, data): @@ -370,85 +471,102 @@ class OfConnOnos(OpenflowConn): # Build the dictionary with the flow rule information for ONOS flow = dict() - # flow['id'] = data['name'] - flow['tableId'] = 0 - flow['priority'] = data.get('priority') - flow['timeout'] = 0 - flow['isPermanent'] = "true" - flow['appId'] = 10 # FIXME We should create an appId for OSM - flow['selector'] = dict() - flow['selector']['criteria'] = list() + # flow["id"] = data["name"] + flow["tableId"] = 0 + flow["priority"] = data.get("priority") + flow["timeout"] = 0 + flow["isPermanent"] = "true" + flow["appId"] = 10 # FIXME We should create an appId for OSM + flow["selector"] = dict() + flow["selector"]["criteria"] = list() # Flow rule matching criteria - if not data['ingress_port'] in self.pp2ofi: - error_text = 'Error. Port ' + data['ingress_port'] + ' is not present in the switch' + if not data["ingress_port"] in self.pp2ofi: + error_text = ( + "Error. Port " + + data["ingress_port"] + + " is not present in the switch" + ) self.logger.warning("new_flow " + error_text) + raise OpenflowConnUnexpectedResponse(error_text) ingress_port_criteria = dict() - ingress_port_criteria['type'] = "IN_PORT" - ingress_port_criteria['port'] = self.pp2ofi[data['ingress_port']] - flow['selector']['criteria'].append(ingress_port_criteria) + ingress_port_criteria["type"] = "IN_PORT" + ingress_port_criteria["port"] = self.pp2ofi[data["ingress_port"]] + flow["selector"]["criteria"].append(ingress_port_criteria) - if 'dst_mac' in data: + if "dst_mac" in data: dst_mac_criteria = dict() dst_mac_criteria["type"] = "ETH_DST" - dst_mac_criteria["mac"] = data['dst_mac'] - flow['selector']['criteria'].append(dst_mac_criteria) + dst_mac_criteria["mac"] = data["dst_mac"] + flow["selector"]["criteria"].append(dst_mac_criteria) - if data.get('vlan_id'): + if data.get("vlan_id"): vlan_criteria = dict() vlan_criteria["type"] = "VLAN_VID" - vlan_criteria["vlanId"] = int(data['vlan_id']) - flow['selector']['criteria'].append(vlan_criteria) + vlan_criteria["vlanId"] = int(data["vlan_id"]) + flow["selector"]["criteria"].append(vlan_criteria) # Flow rule treatment - flow['treatment'] = dict() - flow['treatment']['instructions'] = list() - flow['treatment']['deferred'] = list() + flow["treatment"] = dict() + flow["treatment"]["instructions"] = list() + flow["treatment"]["deferred"] = list() - for action in data['actions']: + for action in data["actions"]: new_action = dict() if action[0] == "vlan": - new_action['type'] = "L2MODIFICATION" + new_action["type"] = "L2MODIFICATION" + if action[1] is None: - new_action['subtype'] = "VLAN_POP" + new_action["subtype"] = "VLAN_POP" else: - new_action['subtype'] = "VLAN_ID" - new_action['vlanId'] = int(action[1]) - elif action[0] == 'out': - new_action['type'] = "OUTPUT" + new_action["subtype"] = "VLAN_ID" + new_action["vlanId"] = int(action[1]) + elif action[0] == "out": + new_action["type"] = "OUTPUT" + if not action[1] in self.pp2ofi: - error_msj = 'Port ' + action[1] + ' is not present in the switch' + error_msj = ( + "Port " + action[1] + " is not present in the switch" + ) + raise OpenflowConnUnexpectedResponse(error_msj) - new_action['port'] = self.pp2ofi[action[1]] + + new_action["port"] = self.pp2ofi[action[1]] else: error_msj = "Unknown item '%s' in action list" % action[0] self.logger.error("new_flow " + error_msj) + raise OpenflowConnUnexpectedResponse(error_msj) - flow['treatment']['instructions'].append(new_action) + flow["treatment"]["instructions"].append(new_action) - self.headers['content-type'] = 'application/json' + self.headers["content-type"] = "application/json" path = self.url + "flows/" + self.id self.logger.debug("new_flow post: {}".format(flow)) - of_response = requests.post(path, headers=self.headers, data=json.dumps(flow)) + of_response = requests.post( + path, headers=self.headers, data=json.dumps(flow) + ) - error_text = "Openflow response {}: {}".format(of_response.status_code, of_response.text) + error_text = "Openflow response {}: {}".format( + of_response.status_code, of_response.text + ) if of_response.status_code != 201: self.logger.warning("new_flow " + error_text) - raise OpenflowConnUnexpectedResponse(error_text) - flowId = of_response.headers['location'][path.__len__() + 1:] + raise OpenflowConnUnexpectedResponse(error_text) - data['name'] = flowId + flowId = of_response.headers["location"][path.__len__() + 1 :] + data["name"] = flowId self.logger.debug("new_flow id: {},: {} ".format(flowId, error_text)) - return None + return None except requests.exceptions.RequestException as e: error_text = type(e).__name__ + ": " + str(e) self.logger.error("new_flow " + error_text) + raise OpenflowConnConnectionException(error_text) def clear_all_flows(self): @@ -463,9 +581,10 @@ class OfConnOnos(OpenflowConn): self.del_flow(rule) self.logger.debug("clear_all_flows OK ") - return None + return None except requests.exceptions.RequestException as e: error_text = type(e).__name__ + ": " + str(e) self.logger.error("clear_all_flows " + error_text) + raise OpenflowConnConnectionException(error_text)