def clear_all_flows(self): """ Delete all existing rules :return: None if ok Raise an openflowconnUnexpectedResponse exception if fails with text_error """ try: # autodiscover version if self.version == None: sw_list = self.get_of_switches() if len(sw_list) == 0: # empty return None url = self.url + "/wm/%s/clear/%s/json" % ( self.ver_names["URLmodifier"], self.dpid) of_response = requests.get(url) error_text = "Openflow response %d: %s" % (of_response.status_code, of_response.text) if of_response.status_code < 200 or of_response.status_code >= 300: self.logger.warning("clear_all_flows " + error_text) raise openflow_conn.OpenflowconnUnexpectedResponse(error_text) self.logger.debug("clear_all_flows OK " + error_text) return None except requests.exceptions.RequestException as e: error_text = type(e).__name__ + ": " + str(e) self.logger.error("clear_all_flows " + error_text) raise openflow_conn.OpenflowconnConnectionException(error_text) except Exception as e: # ValueError in the case that JSON can not be decoded error_text = type(e).__name__ + ": " + str(e) self.logger.error("clear_all_flows " + error_text) raise openflow_conn.OpenflowconnUnexpectedResponse(error_text)
def obtain_port_correspondence(self): """ Obtain the correspondence between physical and openflow port names :return: dictionary with physical name as key, openflow name as value 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 %d: %s" % (of_response.status_code, of_response.text) if of_response.status_code != 200: self.logger.warning("obtain_port_correspondence " + error_text) raise openflow_conn.OpenflowconnUnexpectedResponse(error_text) self.logger.debug("obtain_port_correspondence " + error_text) info = of_response.json() 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 openflow_conn.OpenflowconnUnexpectedResponse( "Unexpected response at 'ports', not found or not " "a list. Wrong version?") 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 openflow_conn.OpenflowconnUnexpectedResponse( "Unexpected response at 'managementAddress', " "not found. Wrong version?") self.ip_address = node_ip_address # print self.name, ": obtain_port_correspondence ports:", self.pp2ofi return self.pp2ofi except requests.exceptions.RequestException as e: error_text = type(e).__name__ + ": " + str(e) self.logger.error("obtain_port_correspondence " + error_text) raise openflow_conn.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 openflow_conn.OpenflowconnUnexpectedResponse(error_text)
def get_of_switches(self): """ Obtain a a list of switches or DPID detected by this controller :return: list where each element a tuple pair (DPID, IP address) Raise an OpenflowconnConnectionException or OpenflowconnConnectionException exception if same parameter is missing or wrong """ try: of_response = requests.get(self.url + "/wm/core/controller/switches/json", headers=self.headers) 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 openflow_conn.OpenflowconnUnexpectedResponse(error_text) self.logger.debug("get_of_switches " + error_text) info = of_response.json() if type(info) != list and type(info) != tuple: self.logger.error( "get_of_switches. Unexpected response not a list %s", str(type(info))) raise openflow_conn.OpenflowconnUnexpectedResponse( "Unexpected response, not a list. Wrong version?") if len(info) == 0: return info # autodiscover version if self.version == None: if 'dpid' in info[0] and 'inetAddress' in info[0]: self._set_version("0.9") # elif 'switchDPID' in info[0] and 'inetAddress' in info[0]: # self._set_version("1.X") else: self.logger.error( "get_of_switches. Unexpected response, not found 'dpid' or 'switchDPID' field: %s", str(info[0])) raise openflow_conn.OpenflowconnUnexpectedResponse( "Unexpected response, not found 'dpid' or " "'switchDPID' field. Wrong version?") switch_list = [] for switch in info: switch_list.append( (switch[self.ver_names["dpid"]], switch['inetAddress'])) 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 openflow_conn.OpenflowconnConnectionException(error_text) except Exception 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 openflow_conn.OpenflowconnUnexpectedResponse(error_text)
def del_flow(self, flow_name): """ Delete an existing rule :param flow_name: this is the rule name :return: None if ok Raise an openflowconnUnexpectedResponse exception if fails with text_error """ try: # Raise an openflowconnUnexpectedResponse exception if fails with text_error # autodiscover version if self.version == None: self.get_of_switches() of_response = requests.delete(self.url + "/wm/%s/json" % self.ver_names["URLmodifier"], headers=self.headers, data='{"switch":"%s","name":"%s"}' % (self.dpid, flow_name) ) error_text = "Openflow response %d: %s" % (of_response.status_code, of_response.text) if of_response.status_code != 200: self.logger.warning("del_flow " + error_text) raise openflow_conn.OpenflowconnUnexpectedResponse(error_text) self.logger.debug("del_flow OK " + error_text) return None except requests.exceptions.RequestException as e: error_text = type(e).__name__ + ": " + str(e) self.logger.error("del_flow " + error_text) raise openflow_conn.OpenflowconnConnectionException(error_text)
def del_flow(self, flow_name): """ Delete an existing rule :param flow_name: :return: Raise a openflowconnUnexpectedResponse expection in case of failure """ try: self.headers['content-type'] = None of_response = requests.delete(self.url + "flows/" + self.id + "/" + flow_name, headers=self.headers) error_text = "Openflow response %d: %s" % (of_response.status_code, of_response.text) if of_response.status_code != 204: self.logger.warning("del_flow " + error_text) raise openflow_conn.OpenflowconnUnexpectedResponse(error_text) self.logger.debug("del_flow OK " + error_text) return None except requests.exceptions.RequestException as e: error_text = type(e).__name__ + ": " + str(e) self.logger.error("del_flow " + error_text) raise openflow_conn.OpenflowconnConnectionException(error_text)
def del_flow(self, flow_name): """ Delete an existing rule :param flow_name: flow_name, this is the rule name :return: Raise a OpenflowconnConnectionException expection in case of failure """ try: of_response = requests.delete( self.url + "/restconf/config/opendaylight-inventory:nodes/node/" + self.id + "/table/0/flow/" + flow_name, headers=self.headers) error_text = "Openflow response %d: %s" % (of_response.status_code, of_response.text) if of_response.status_code != 200: self.logger.warning("del_flow " + error_text) raise openflow_conn.OpenflowconnUnexpectedResponse(error_text) self.logger.debug("del_flow OK " + error_text) return None except requests.exceptions.RequestException as e: # raise an exception in case of contection error error_text = type(e).__name__ + ": " + str(e) self.logger.error("del_flow " + error_text) raise openflow_conn.OpenflowconnConnectionException(error_text)
def clear_all_flows(self): """ Delete all existing rules :return: Raise a OpenflowconnConnectionException expection in case of failure """ try: of_response = requests.delete( self.url + "/restconf/config/opendaylight-inventory:nodes/node/" + self.id + "/table/0", headers=self.headers) error_text = "Openflow response %d: %s" % (of_response.status_code, of_response.text) if of_response.status_code != 200 and of_response.status_code != 404: #HTTP_Not_Found self.logger.warning("clear_all_flows " + error_text) raise openflow_conn.OpenflowconnUnexpectedResponse(error_text) self.logger.debug("clear_all_flows OK " + error_text) except requests.exceptions.RequestException as e: error_text = type(e).__name__ + ": " + str(e) self.logger.error("clear_all_flows " + error_text) raise openflow_conn.OpenflowconnConnectionException(error_text)
def get_of_switches(self): """ Obtain a a list of switches or DPID detected by this controller :return: list length, and a list where each element a tuple pair (DPID, IP address) Raise an OpenflowconnConnectionException exception if fails with text_error """ try: of_response = requests.get( self.url + "/restconf/operational/opendaylight-inventory:nodes", headers=self.headers) 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 openflow_conn.OpenflowconnUnexpectedResponse( "Error get_of_switches " + 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 openflow_conn.OpenflowconnUnexpectedResponse( "Unexpected response, not a dict. Wrong version?") nodes = info.get('nodes') if type(nodes) is not dict: self.logger.error( "get_of_switches. Unexpected response at 'nodes', not found or not a dict: %s", str(type(info))) raise openflow_conn.OpenflowconnUnexpectedResponse( "Unexpected response at 'nodes', not found or " "not a dict. Wrong version?") node_list = nodes.get('node') if type(node_list) is not list: self.logger.error( "get_of_switches. Unexpected response, at 'nodes':'node', " "not found or not a list: %s", str(type(node_list))) raise openflow_conn.OpenflowconnUnexpectedResponse( "Unexpected response, at 'nodes':'node', not found " "or not a list. Wrong version?") switch_list = [] for node in node_list: node_id = node.get('id') if node_id is None: self.logger.error( "get_of_switches. Unexpected response at 'nodes':'node'[]:'id', not found: %s", str(node)) raise openflow_conn.OpenflowconnUnexpectedResponse( "Unexpected response at 'nodes':'node'[]:'id', " "not found . Wrong version?") if node_id == 'controller-config': continue node_ip_address = node.get('flow-node-inventory:ip-address') if node_ip_address is None: self.logger.error( "get_of_switches. Unexpected response at 'nodes':'node'[]:'flow-node-inventory:" "ip-address', not found: %s", str(node)) raise openflow_conn.OpenflowconnUnexpectedResponse( "Unexpected response at 'nodes':'node'[]:" "'flow-node-inventory:ip-address', " "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 len(switch_list), switch_list except requests.exceptions.RequestException as e: error_text = type(e).__name__ + ": " + str(e) self.logger.error("get_of_switches " + error_text) raise openflow_conn.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 openflow_conn.OpenflowconnUnexpectedResponse(error_text)
def new_flow(self, data): """ Insert a new static rule :param data: dictionary with the following content: priority: rule priority name: rule name ingress_port: match input port of the rule dst_mac: match destination mac address of the rule, missing or None if not apply vlan_id: match vlan tag of the rule, missing or None if not apply actions: list of actions, composed by a pair tuples with these posibilities: ('vlan', None/int): for stripping/setting a vlan tag ('out', port): send to this port :return: Raise a OpenflowconnConnectionException expection in case of failure """ try: if len(self.pp2ofi) == 0: self.obtain_port_correspondence() # We have to build the data for the opendaylight call from the generic data sdata = dict() sdata['flow-node-inventory:flow'] = list() sdata['flow-node-inventory:flow'].append(dict()) flow = sdata['flow-node-inventory:flow'][0] flow['id'] = data['name'] flow['flow-name'] = data['name'] flow['idle-timeout'] = 0 flow['hard-timeout'] = 0 flow['table_id'] = 0 flow['priority'] = data.get('priority') flow['match'] = dict() 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 openflow_conn.OpenflowconnUnexpectedResponse(error_text) flow['match']['in-port'] = self.pp2ofi[data['ingress_port']] if 'dst_mac' in data: flow['match']['ethernet-match'] = dict() flow['match']['ethernet-match']['ethernet-destination'] = dict( ) flow['match']['ethernet-match']['ethernet-destination'][ 'address'] = data['dst_mac'] if data.get('vlan_id'): flow['match']['vlan-match'] = dict() flow['match']['vlan-match']['vlan-id'] = dict() flow['match']['vlan-match']['vlan-id'][ 'vlan-id-present'] = True flow['match']['vlan-match']['vlan-id']['vlan-id'] = int( data['vlan_id']) flow['instructions'] = dict() flow['instructions']['instruction'] = list() flow['instructions']['instruction'].append(dict()) flow['instructions']['instruction'][0]['order'] = 1 flow['instructions']['instruction'][0]['apply-actions'] = dict() flow['instructions']['instruction'][0]['apply-actions'][ 'action'] = list() actions = flow['instructions']['instruction'][0]['apply-actions'][ 'action'] order = 0 for action in data['actions']: new_action = {'order': order} if action[0] == "vlan": if action[1] == None: # strip vlan new_action['strip-vlan-action'] = dict() else: new_action['set-field'] = dict() new_action['set-field']['vlan-match'] = dict() new_action['set-field']['vlan-match'][ 'vlan-id'] = dict() new_action['set-field']['vlan-match']['vlan-id'][ 'vlan-id-present'] = True new_action['set-field']['vlan-match']['vlan-id'][ 'vlan-id'] = int(action[1]) elif action[0] == 'out': new_action['output-action'] = dict() if not action[1] in self.pp2ofi: error_msj = 'Port ' + action[ 1] + ' is not present in the switch' raise openflow_conn.OpenflowconnUnexpectedResponse( error_msj) new_action['output-action'][ 'output-node-connector'] = self.pp2ofi[action[1]] else: error_msj = "Unknown item '%s' in action list" % action[0] self.logger.error("new_flow " + error_msj) raise openflow_conn.OpenflowconnUnexpectedResponse( error_msj) actions.append(new_action) order += 1 # print json.dumps(sdata) of_response = requests.put( self.url + "/restconf/config/opendaylight-inventory:nodes/node/" + self.id + "/table/0/flow/" + data['name'], headers=self.headers, data=json.dumps(sdata)) error_text = "Openflow response %d: %s" % (of_response.status_code, of_response.text) if of_response.status_code != 200: self.logger.warning("new_flow " + error_text) raise openflow_conn.OpenflowconnUnexpectedResponse(error_text) self.logger.debug("new_flow OK " + error_text) return None except requests.exceptions.RequestException as e: # raise an exception in case of contection error error_text = type(e).__name__ + ": " + str(e) self.logger.error("new_flow " + error_text) raise openflow_conn.OpenflowconnConnectionException(error_text)
def get_of_rules(self, translate_of_ports=True): """ Obtain the rules inserted at openflow controller :param translate_of_ports: :return: dict if ok: with the rule name as key and value is another dictionary with the following content: priority: rule priority name: rule name (present also as the master dict key) ingress_port: match input port of the rule dst_mac: match destination mac address of the rule, can be missing or None if not apply vlan_id: match vlan tag of the rule, can be missing or None if not apply actions: list of actions, composed by a pair tuples: (vlan, None/int): for stripping/setting a vlan tag (out, port): send to this port switch: DPID, all Raise a OpenflowconnConnectionException expection in case of failure """ try: # get rules if len(self.ofi2pp) == 0: self.obtain_port_correspondence() of_response = requests.get( self.url + "/restconf/config/opendaylight-inventory:nodes/node/" + self.id + "/table/0", 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 openflow_conn.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 openflow_conn.OpenflowconnUnexpectedResponse( "Unexpected openflow response, not a dict. " "Wrong version?") table = info.get('flow-node-inventory:table') if type(table) is not list: self.logger.error( "get_of_rules. Unexpected response at 'flow-node-inventory:table', " "not a list: %s", str(type(table))) raise openflow_conn.OpenflowconnUnexpectedResponse( "Unexpected response at 'flow-node-inventory:table'," " not a list. Wrong version?") flow_list = table[0].get('flow') if flow_list is None: return {} if type(flow_list) is not list: self.logger.error( "get_of_rules. Unexpected response at 'flow-node-inventory:table'[0]:'flow', not a list: %s", str(type(flow_list))) raise openflow_conn.OpenflowconnUnexpectedResponse( "Unexpected response at 'flow-node-inventory:" "table'[0]:'flow', not a list. Wrong version?") # TODO translate ports according to translate_of_ports parameter rules = dict() for flow in flow_list: if not ('id' in flow and 'match' in flow and 'instructions' in flow and 'instruction' in flow['instructions'] and 'apply-actions' in flow['instructions']['instruction'][0] and 'action' in flow['instructions']['instruction'][0] ['apply-actions']): raise openflow_conn.OpenflowconnUnexpectedResponse( "unexpected openflow response, one or more " "elements are missing. Wrong version?") flow['instructions']['instruction'][0]['apply-actions'][ 'action'] rule = dict() rule['switch'] = self.dpid rule['priority'] = flow.get('priority') # rule['name'] = flow['id'] # rule['cookie'] = flow['cookie'] if 'in-port' in flow['match']: in_port = flow['match']['in-port'] if not in_port in self.ofi2pp: raise openflow_conn.OpenflowconnUnexpectedResponse( "Error: Ingress port " + in_port + " is not in switch port list") if translate_of_ports: in_port = self.ofi2pp[in_port] rule['ingress_port'] = in_port if 'vlan-match' in flow['match'] and 'vlan-id' in flow['match']['vlan-match'] and \ 'vlan-id' in flow['match']['vlan-match']['vlan-id'] and \ 'vlan-id-present' in flow['match']['vlan-match']['vlan-id'] and \ flow['match']['vlan-match']['vlan-id']['vlan-id-present'] == True: rule['vlan_id'] = flow['match']['vlan-match'][ 'vlan-id']['vlan-id'] if 'ethernet-match' in flow['match'] and 'ethernet-destination' in flow['match']['ethernet-match'] and \ 'address' in flow['match']['ethernet-match']['ethernet-destination']: rule['dst_mac'] = flow['match']['ethernet-match'][ 'ethernet-destination']['address'] instructions = flow['instructions']['instruction'][0][ 'apply-actions']['action'] max_index = 0 for instruction in instructions: if instruction['order'] > max_index: max_index = instruction['order'] actions = [None] * (max_index + 1) for instruction in instructions: if 'output-action' in instruction: if not 'output-node-connector' in instruction[ 'output-action']: raise openflow_conn.OpenflowconnUnexpectedResponse( "unexpected openflow response, one or " "more elementa are missing. " "Wrong version?") out_port = instruction['output-action'][ 'output-node-connector'] if not out_port in self.ofi2pp: raise openflow_conn.OpenflowconnUnexpectedResponse( "Error: Output port " + out_port + " is not in switch port list") if translate_of_ports: out_port = self.ofi2pp[out_port] actions[instruction['order']] = ('out', out_port) elif 'strip-vlan-action' in instruction: actions[instruction['order']] = ('vlan', None) elif 'set-field' in instruction: if not ('vlan-match' in instruction['set-field'] and 'vlan-id' in instruction['set-field']['vlan-match'] and 'vlan-id' in instruction['set-field'] ['vlan-match']['vlan-id']): raise openflow_conn.OpenflowconnUnexpectedResponse( "unexpected openflow response, one or " "more elements are missing. " "Wrong version?") actions[instruction['order']] = ( 'vlan', instruction['set-field']['vlan-match'] ['vlan-id']['vlan-id']) actions = [x for x in actions if x != None] rule['actions'] = list(actions) rules[flow['id']] = dict(rule) #flow['id'] #flow['priority'] #flow['cookie'] #flow['match']['in-port'] #flow['match']['vlan-match']['vlan-id']['vlan-id'] # match -> in-port # -> vlan-match -> vlan-id -> vlan-id #flow['match']['vlan-match']['vlan-id']['vlan-id-present'] #TODO we asume that is not using rules with vlan-id-present:false #instructions -> instruction -> apply-actions -> action #instructions=flow['instructions']['instruction'][0]['apply-actions']['action'] #Es una lista. Posibles elementos: #max_index=0 #for instruction in instructions: # if instruction['order'] > max_index: # max_index = instruction['order'] #actions=[None]*(max_index+1) #for instruction in instructions: # if 'output-action' in instruction: # actions[instruction['order']] = ('out',instruction['output-action']['output-node-connector']) # elif 'strip-vlan-action' in instruction: # actions[instruction['order']] = ('vlan', None) # elif 'set-field' in instruction: # actions[instruction['order']] = ('vlan', instruction['set-field']['vlan-match']['vlan-id']['vlan-id']) # #actions = [x for x in actions if x != None] # -> output-action -> output-node-connector # -> pop-vlan-action return rules except requests.exceptions.RequestException as e: error_text = type(e).__name__ + ": " + str(e) self.logger.error("get_of_rules " + error_text) raise openflow_conn.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 openflow_conn.OpenflowconnUnexpectedResponse(error_text)
def get_of_switches(self): """ Obtain a a list of switches or DPID detected by this controller :return: list where each element a tuple pair (DPID, IP address) Raise a openflowconnUnexpectedResponse expection in case of failure """ try: 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) if of_response.status_code != 200: self.logger.warning("get_of_switches " + error_text) raise openflow_conn.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 openflow_conn.OpenflowconnUnexpectedResponse( "Unexpected response, not a dict. Wrong version?") 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 openflow_conn.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') if node_id is None: self.logger.error( "get_of_switches. Unexpected response at 'device':'id', not found: %s", str(node)) raise openflow_conn.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 openflow_conn.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)) raise switch_list except requests.exceptions.RequestException as e: error_text = type(e).__name__ + ": " + str(e) self.logger.error("get_of_switches " + error_text) raise openflow_conn.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 openflow_conn.OpenflowconnUnexpectedResponse(error_text)
def obtain_port_correspondence(self): """ Obtain the correspondence between physical and openflow port names :return: dictionary: with physical name as key, openflow name as value, Raise a OpenflowconnConnectionException expection in case of failure """ try: of_response = requests.get( self.url + "/restconf/operational/opendaylight-inventory:nodes", headers=self.headers) error_text = "Openflow response %d: %s" % (of_response.status_code, of_response.text) if of_response.status_code != 200: self.logger.warning("obtain_port_correspondence " + error_text) raise openflow_conn.OpenflowconnUnexpectedResponse(error_text) self.logger.debug("obtain_port_correspondence " + error_text) info = of_response.json() if type(info) != dict: self.logger.error( "obtain_port_correspondence. Unexpected response not a dict: %s", str(info)) raise openflow_conn.OpenflowconnUnexpectedResponse( "Unexpected openflow response, not a dict. " "Wrong version?") nodes = info.get('nodes') if type(nodes) is not dict: self.logger.error( "obtain_port_correspondence. Unexpected response at 'nodes', " "not found or not a dict: %s", str(type(nodes))) raise openflow_conn.OpenflowconnUnexpectedResponse( "Unexpected response at 'nodes',not found or not a dict. Wrong version?" ) node_list = nodes.get('node') if type(node_list) is not list: self.logger.error( "obtain_port_correspondence. Unexpected response, at 'nodes':'node', " "not found or not a list: %s", str(type(node_list))) raise openflow_conn.OpenflowconnUnexpectedResponse( "Unexpected response, at 'nodes':'node', " "not found or not a list. Wrong version?") for node in node_list: node_id = node.get('id') if node_id is None: self.logger.error( "obtain_port_correspondence. Unexpected response at 'nodes':'node'[]:'id', " "not found: %s", str(node)) raise openflow_conn.OpenflowconnUnexpectedResponse( "Unexpected response at 'nodes':'node'[]:'id', " "not found . Wrong version?") if node_id == 'controller-config': continue # Figure out if this is the appropriate switch. The 'id' is 'openflow:' plus the decimal value # of the dpid # In case this is not the desired switch, continue if self.id != node_id: continue node_connector_list = node.get('node-connector') if type(node_connector_list) is not list: self.logger.error( "obtain_port_correspondence. Unexpected response at " "'nodes':'node'[]:'node-connector', not found or not a list: %s", str(node)) raise openflow_conn.OpenflowconnUnexpectedResponse( "Unexpected response at 'nodes':'node'[]:" "'node-connector', not found or not a list. " "Wrong version?") for node_connector in node_connector_list: self.pp2ofi[str( node_connector['flow-node-inventory:name'])] = str( node_connector['id']) self.ofi2pp[node_connector['id']] = str( node_connector['flow-node-inventory:name']) node_ip_address = node.get('flow-node-inventory:ip-address') if node_ip_address is None: self.logger.error( "obtain_port_correspondence. Unexpected response at 'nodes':'node'[]:" "'flow-node-inventory:ip-address', not found: %s", str(node)) raise openflow_conn.OpenflowconnUnexpectedResponse( "Unexpected response at 'nodes':'node'[]:" "'flow-node-inventory:ip-address', not found. Wrong version?" ) self.ip_address = node_ip_address # If we found the appropriate dpid no need to continue in the for loop break # print self.name, ": obtain_port_correspondence ports:", self.pp2ofi return self.pp2ofi except requests.exceptions.RequestException as e: error_text = type(e).__name__ + ": " + str(e) self.logger.error("obtain_port_correspondence " + error_text) raise openflow_conn.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 openflow_conn.OpenflowconnUnexpectedResponse(error_text)
def new_flow(self, data): """ Insert a new static rule :param data: dictionary with the following content: priority: rule priority name: rule name ingress_port: match input port of the rule dst_mac: match destination mac address of the rule, missing or None if not apply vlan_id: match vlan tag of the rule, missing or None if not apply actions: list of actions, composed by a pair tuples with these posibilities: ('vlan', None/int): for stripping/setting a vlan tag ('out', port): send to this port :return: Raise a openflowconnUnexpectedResponse expection in case of failure """ try: if len(self.pp2ofi) == 0: self.obtain_port_correspondence() # 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 rule matching criteria 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 openflow_conn.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) 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) 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) # Flow rule treatment flow['treatment'] = dict() flow['treatment']['instructions'] = list() flow['treatment']['deferred'] = list() for action in data['actions']: new_action = dict() if action[0] == "vlan": new_action['type'] = "L2MODIFICATION" if action[1] == None: 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" if not action[1] in self.pp2ofi: error_msj = 'Port ' + action[ 1] + ' is not present in the switch' raise openflow_conn.OpenflowconnUnexpectedResponse( error_msj) 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 openflow_conn.OpenflowconnUnexpectedResponse( error_msj) flow['treatment']['instructions'].append(new_action) self.headers['content-type'] = 'application/json' path = self.url + "flows/" + self.id of_response = requests.post(path, headers=self.headers, data=json.dumps(flow)) error_text = "Openflow response %d: %s" % (of_response.status_code, of_response.text) if of_response.status_code != 201: self.logger.warning("new_flow " + error_text) raise openflow_conn.OpenflowconnUnexpectedResponse(error_text) flowId = of_response.headers['location'][path.__len__() + 1:] data['name'] = flowId self.logger.debug("new_flow OK " + error_text) return None except requests.exceptions.RequestException as e: error_text = type(e).__name__ + ": " + str(e) self.logger.error("new_flow " + error_text) raise openflow_conn.OpenflowconnConnectionException(error_text)
def get_of_rules(self, translate_of_ports=True): """ Obtain the rules inserted at openflow controller :param translate_of_ports: if True it translates ports from openflow index to physical switch name :return: dict if ok: with the rule name as key and value is another dictionary with the following content: priority: rule priority name: rule name (present also as the master dict key) ingress_port: match input port of the rule dst_mac: match destination mac address of the rule, can be missing or None if not apply vlan_id: match vlan tag of the rule, can be missing or None if not apply actions: list of actions, composed by a pair tuples: (vlan, None/int): for stripping/setting a vlan tag (out, port): send to this port switch: DPID, all Raise an openflowconnUnexpectedResponse exception if fails with text_error """ try: # get translation, autodiscover version if len(self.ofi2pp) == 0: self.obtain_port_correspondence() of_response = requests.get(self.url + "/wm/%s/list/%s/json" % (self.ver_names["URLmodifier"], self.dpid), headers=self.headers) error_text = "Openflow response %d: %s" % (of_response.status_code, of_response.text) if of_response.status_code != 200: self.logger.warning("get_of_rules " + error_text) raise openflow_conn.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(type(info))) raise openflow_conn.OpenflowconnUnexpectedResponse("Unexpected response, not a dict. Wrong version?") rule_dict = {} for switch, switch_info in info.iteritems(): if switch_info == None: continue if str(switch) != self.dpid: continue for name, details in switch_info.iteritems(): rule = {} rule["switch"] = str(switch) # rule["active"] = "true" rule["priority"] = int(details["priority"]) if self.version[0] == "0": if translate_of_ports: rule["ingress_port"] = self.ofi2pp[details["match"]["inputPort"]] else: rule["ingress_port"] = str(details["match"]["inputPort"]) dst_mac = details["match"]["dataLayerDestination"] if dst_mac != "00:00:00:00:00:00": rule["dst_mac"] = dst_mac vlan = details["match"]["dataLayerVirtualLan"] if vlan != -1: rule["vlan_id"] = vlan actionlist = [] for action in details["actions"]: if action["type"] == "OUTPUT": if translate_of_ports: port = self.ofi2pp[action["port"]] else: port = action["port"] actionlist.append(("out", port)) elif action["type"] == "STRIP_VLAN": actionlist.append(("vlan", None)) elif action["type"] == "SET_VLAN_ID": actionlist.append(("vlan", action["virtualLanIdentifier"])) else: actionlist.append((action["type"], str(action))) self.logger.warning("get_of_rules() Unknown action in rule %s: %s", rule["name"], str(action)) rule["actions"] = actionlist elif self.version[0] == "1": if translate_of_ports: rule["ingress_port"] = self.ofi2pp[details["match"]["in_port"]] else: rule["ingress_port"] = details["match"]["in_port"] if "eth_dst" in details["match"]: dst_mac = details["match"]["eth_dst"] if dst_mac != "00:00:00:00:00:00": rule["dst_mac"] = dst_mac if "eth_vlan_vid" in details["match"]: vlan = int(details["match"]["eth_vlan_vid"], 16) & 0xFFF rule["vlan_id"] = str(vlan) actionlist = [] for action in details["instructions"]["instruction_apply_actions"]: if action == "output": if translate_of_ports: port = self.ofi2pp[details["instructions"]["instruction_apply_actions"]["output"]] else: port = details["instructions"]["instruction_apply_actions"]["output"] actionlist.append(("out", port)) elif action == "strip_vlan": actionlist.append(("vlan", None)) elif action == "set_vlan_vid": actionlist.append( ("vlan", details["instructions"]["instruction_apply_actions"]["set_vlan_vid"])) else: self.logger.error("get_of_rules Unknown action in rule %s: %s", rule["name"], str(action)) # actionlist.append( (action, str(details["instructions"]["instruction_apply_actions"]) )) rule_dict[str(name)] = rule return rule_dict except requests.exceptions.RequestException as e: error_text = type(e).__name__ + ": " + str(e) self.logger.error("get_of_rules " + error_text) raise openflow_conn.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 openflow_conn.OpenflowconnUnexpectedResponse(error_text)
def get_of_rules(self, translate_of_ports=True): """ Obtain the rules inserted at openflow controller :param translate_of_ports: if True it translates ports from openflow index to physical switch name :return: dict if ok: with the rule name as key and value is another dictionary with the following content: priority: rule priority name: rule name (present also as the master dict key) ingress_port: match input port of the rule dst_mac: match destination mac address of the rule, can be missing or None if not apply vlan_id: match vlan tag of the rule, can be missing or None if not apply actions: list of actions, composed by a pair tuples: (vlan, None/int): for stripping/setting a vlan tag (out, port): send to this port switch: DPID, all Raise a openflowconnUnexpectedResponse expection 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) # 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 openflow_conn.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 openflow_conn.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 openflow_conn.OpenflowconnUnexpectedResponse( "Unexpected response at 'flows', not a list. " "Wrong version?") rules = dict() # Response dictionary 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 openflow_conn.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'] for criteria in flow['selector']['criteria']: if criteria['type'] == 'IN_PORT': in_port = str(criteria['port']) if in_port != "CONTROLLER": if not in_port in self.ofi2pp: raise openflow_conn.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() actions = [] for instruction in flow['treatment']['instructions']: if instruction['type'] == "OUTPUT": out_port = str(instruction['port']) if out_port != "CONTROLLER": if not out_port in self.ofi2pp: raise openflow_conn.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)) 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'])) rule['actions'] = actions rules[flow['id']] = dict(rule) 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 openflow_conn.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 openflow_conn.OpenflowconnUnexpectedResponse(error_text)
def obtain_port_correspondence(self): """ Obtain the correspondence between physical and openflow port names :return: dictionary: with physical name as key, openflow name as value Raise an openflowconnUnexpectedResponse exception if fails with text_error """ try: of_response = requests.get(self.url + "/wm/core/controller/switches/json", headers=self.headers) # print vim_response.status_code error_text = "Openflow response %d: %s" % (of_response.status_code, of_response.text) if of_response.status_code != 200: self.logger.warning("obtain_port_correspondence " + error_text) raise openflow_conn.OpenflowconnUnexpectedResponse(error_text) self.logger.debug("obtain_port_correspondence " + error_text) info = of_response.json() if type(info) != list and type(info) != tuple: raise openflow_conn.OpenflowconnUnexpectedResponse("unexpected openflow response, not a list. " "Wrong version?") index = -1 if len(info) > 0: # autodiscover version if self.version == None: if 'dpid' in info[0] and 'ports' in info[0]: self._set_version("0.9") elif 'switchDPID' in info[0]: self._set_version("1.X") else: raise openflow_conn.OpenflowconnUnexpectedResponse("unexpected openflow response, " "Wrong version?") for i in range(0, len(info)): if info[i][self.ver_names["dpid"]] == self.dpid: index = i break if index == -1: text = "DPID '" + self.dpid + "' not present in controller " + self.url # print self.name, ": get_of_controller_info ERROR", text raise openflow_conn.OpenflowconnUnexpectedResponse(text) else: if self.version[0] == "0": ports = info[index]["ports"] else: # version 1.X of_response = requests.get(self.url + "/wm/core/switch/%s/port-desc/json" % self.dpid, headers=self.headers) # print vim_response.status_code error_text = "Openflow response %d: %s" % (of_response.status_code, of_response.text) if of_response.status_code != 200: self.logger.warning("obtain_port_correspondence " + error_text) raise openflow_conn.OpenflowconnUnexpectedResponse(error_text) self.logger.debug("obtain_port_correspondence " + error_text) info = of_response.json() if type(info) != dict: raise openflow_conn.OpenflowconnUnexpectedResponse("unexpected openflow port-desc response, " "not a dict. Wrong version?") if "portDesc" not in info: raise openflow_conn.OpenflowconnUnexpectedResponse("unexpected openflow port-desc response, " "'portDesc' not found. Wrong version?") if type(info["portDesc"]) != list and type(info["portDesc"]) != tuple: raise openflow_conn.OpenflowconnUnexpectedResponse("unexpected openflow port-desc response at " "'portDesc', not a list. Wrong version?") ports = info["portDesc"] for port in ports: self.pp2ofi[str(port["name"])] = str(port["portNumber"]) self.ofi2pp[port["portNumber"]] = str(port["name"]) # print self.name, ": get_of_controller_info ports:", self.pp2ofi return self.pp2ofi except requests.exceptions.RequestException as e: error_text = type(e).__name__ + ": " + str(e) self.logger.error("obtain_port_correspondence " + error_text) raise openflow_conn.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 openflow_conn.OpenflowconnUnexpectedResponse(error_text)
def new_flow(self, data): """ Insert a new static rule :param data: dictionary with the following content: priority: rule priority name: rule name ingress_port: match input port of the rule dst_mac: match destination mac address of the rule, missing or None if not apply vlan_id: match vlan tag of the rule, missing or None if not apply actions: list of actions, composed by a pair tuples with these posibilities: ('vlan', None/int): for stripping/setting a vlan tag ('out', port): send to this port :return: None if ok Raise an openflowconnUnexpectedResponse exception if fails with text_error """ # get translation, autodiscover version if len(self.pp2ofi) == 0: self.obtain_port_correspondence() try: # We have to build the data for the floodlight call from the generic data sdata = {'active': "true", "name": data["name"]} if data.get("priority"): sdata["priority"] = str(data["priority"]) if data.get("vlan_id"): sdata[self.ver_names["vlanid"]] = data["vlan_id"] if data.get("dst_mac"): sdata[self.ver_names["destmac"]] = data["dst_mac"] sdata['switch'] = self.dpid 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 openflow_conn.OpenflowconnUnexpectedResponse(error_text) sdata[self.ver_names["inport"]] = self.pp2ofi[data['ingress_port']] sdata['actions'] = "" for action in data['actions']: if len(sdata['actions']) > 0: sdata['actions'] += ',' if action[0] == "vlan": if action[1] == None: sdata['actions'] += self.ver_names["stripvlan"] else: sdata['actions'] += self.ver_names["setvlan"] + "=" + str(action[1]) elif action[0] == 'out': sdata['actions'] += "output=" + self.pp2ofi[action[1]] of_response = requests.post(self.url + "/wm/%s/json" % self.ver_names["URLmodifier"], headers=self.headers, data=json.dumps(sdata)) error_text = "Openflow response %d: %s" % (of_response.status_code, of_response.text) if of_response.status_code != 200: self.logger.warning("new_flow " + error_text) raise openflow_conn.OpenflowconnUnexpectedResponse(error_text) self.logger.debug("new_flow OK" + error_text) return None except requests.exceptions.RequestException as e: error_text = type(e).__name__ + ": " + str(e) self.logger.error("new_flow " + error_text) raise openflow_conn.OpenflowconnConnectionException(error_text)