class TestFlowFactory(TestCase): """Test the FlowFactory class.""" def setUp(self): """Execute steps before each tests. Set the server_name_url from kytos/of_core """ self.switch_v0x01 = get_switch_mock("00:00:00:00:00:00:00:01", 0x01) self.switch_v0x04 = get_switch_mock("00:00:00:00:00:00:00:02", 0x04) self.switch_v0x01.connection = get_connection_mock( 0x01, get_switch_mock("00:00:00:00:00:00:00:03")) self.switch_v0x04.connection = get_connection_mock( 0x04, get_switch_mock("00:00:00:00:00:00:00:04")) patch('kytos.core.helpers.run_on_thread', lambda x: x).start() # pylint: disable=import-outside-toplevel from napps.kytos.of_core.flow import FlowFactory self.addCleanup(patch.stopall) self.napp = FlowFactory() @patch('napps.kytos.of_core.flow.v0x01') @patch('napps.kytos.of_core.flow.v0x04') def test_from_of_flow_stats(self, *args): """Test from_of_flow_stats.""" (mock_flow_v0x04, mock_flow_v0x01) = args mock_stats = MagicMock() self.napp.from_of_flow_stats(mock_stats, self.switch_v0x01) mock_flow_v0x01.flow.Flow.from_of_flow_stats.assert_called() self.napp.from_of_flow_stats(mock_stats, self.switch_v0x04) mock_flow_v0x04.flow.Flow.from_of_flow_stats.assert_called()
def listen(self, switch, flows_stats): """Receive flow stats.""" flow_class = FlowFactory.get_class(switch) for flow_stat in flows_stats: flow = flow_class.from_of_flow_stats(flow_stat, switch) # Update controller's flow controller_flow = switch.get_flow_by_id(flow.id) if controller_flow: controller_flow.stats = flow.stats # Save packet_count using kytos/kronos namespace = f'kytos.kronos.{switch.id}.flow_id.{flow.id}' content = {'namespace': namespace, 'value': {'packet_count': flow.stats.packet_count}, 'callback': self._save_event_callback, 'timestamp': time.time()} event = KytosEvent(name='kytos.kronos.save', content=content) self._app_buffer.put(event) # Save byte_count using kytos/kronos namespace = f'kytos.kronos.{switch.id}.flow_id.{flow.id}' content = {'namespace': namespace, 'value': {'byte_count': flow.stats.byte_count}, 'callback': self._save_event_callback, 'timestamp': time.time()} event = KytosEvent(name='kytos.kronos.save', content=content) self._app_buffer.put(event)
def check_switch_consistency(self, switch): """Check consistency of installed flows for a specific switch.""" dpid = switch.dpid # Flows stored in storehouse stored_flows = self.stored_flows[dpid]['flow_list'] serializer = FlowFactory.get_class(switch) for stored_flow in stored_flows: command = stored_flow['command'] stored_flow_obj = serializer.from_dict(stored_flow['flow'], switch) flow = {'flows': [stored_flow['flow']]} if stored_flow_obj not in switch.flows: if command == 'add': log.info('A consistency problem was detected in ' f'switch {dpid}.') self._install_flows(command, flow, [switch]) log.info(f'Flow forwarded to switch {dpid} to be ' 'installed.') elif command == 'delete': log.info('A consistency problem was detected in ' f'switch {dpid}.') self._install_flows(command, flow, [switch]) log.info(f'Flow forwarded to switch {dpid} to be deleted.')
def check_storehouse_consistency(self, switch): """Check consistency of installed flows for a specific switch.""" dpid = switch.dpid for installed_flow in switch.flows: if dpid not in self.stored_flows: log.info('A consistency problem was detected in ' f'switch {dpid}.') flow = {'flows': [installed_flow.as_dict()]} command = 'delete' self._install_flows(command, flow, [switch]) log.info(f'Flow forwarded to switch {dpid} to be deleted.') else: serializer = FlowFactory.get_class(switch) stored_flows = self.stored_flows[dpid]['flow_list'] stored_flows_list = [ serializer.from_dict(stored_flow['flow'], switch) for stored_flow in stored_flows ] if installed_flow not in stored_flows_list: log.info('A consistency problem was detected in ' f'switch {dpid}.') flow = {'flows': [installed_flow.as_dict()]} command = 'delete' self._install_flows(command, flow, [switch]) log.info(f'Flow forwarded to switch {dpid} to be deleted.')
def _install_flows(self, command, flows_dict, switches=[], save=True): """Execute all procedures to install flows in the switches. Args: command: Flow command to be installed flows_dict: Dictionary with flows to be installed in the switches. switches: A list of switches save: A boolean to save flows in the storehouse (True) or not """ for switch in switches: serializer = FlowFactory.get_class(switch) flows = flows_dict.get('flows', []) for flow_dict in flows: flow = serializer.from_dict(flow_dict, switch) if command == "delete": flow_mod = flow.as_of_delete_flow_mod() elif command == "delete_strict": flow_mod = flow.as_of_strict_delete_flow_mod() elif command == "add": flow_mod = flow.as_of_add_flow_mod() else: raise InvalidCommandError self._send_flow_mod(flow.switch, flow_mod) self._add_flow_mod_sent(flow_mod.header.xid, flow, command) self._send_napp_event(switch, flow, command) if save: with self._storehouse_lock: self._store_changed_flows(command, flow_dict, switch)
def setUp(self): """Execute steps before each tests. Set the server_name_url from kytos/of_core """ self.switch_v0x01 = get_switch_mock("00:00:00:00:00:00:00:01", 0x01) self.switch_v0x04 = get_switch_mock("00:00:00:00:00:00:00:02", 0x04) self.switch_v0x01.connection = get_connection_mock( 0x01, get_switch_mock("00:00:00:00:00:00:00:03")) self.switch_v0x04.connection = get_connection_mock( 0x04, get_switch_mock("00:00:00:00:00:00:00:04")) patch('kytos.core.helpers.run_on_thread', lambda x: x).start() # pylint: disable=import-outside-toplevel from napps.kytos.of_core.flow import FlowFactory self.addCleanup(patch.stopall) self.napp = FlowFactory()
def _store_changed_flows(self, command, flow, switch): """Store changed flows. Args: command: Flow command to be installed flow: Flows to be stored switch: Switch target """ stored_flows_box = self.stored_flows.copy() # if the flow has a destination dpid it can be stored. if not switch: log.info('The Flow cannot be stored, the destination switch ' f'have not been specified: {switch}') return installed_flow = {} flow_list = [] installed_flow['command'] = command installed_flow['flow'] = flow serializer = FlowFactory.get_class(switch) installed_flow_obj = serializer.from_dict(flow, switch) if switch.id not in stored_flows_box: # Switch not stored, add to box. flow_list.append(installed_flow) stored_flows_box[switch.id] = {'flow_list': flow_list} else: stored_flows = stored_flows_box[switch.id].get('flow_list', []) # Check if flow already stored for stored_flow in stored_flows: stored_flow_obj = serializer.from_dict(stored_flow['flow'], switch) if installed_flow_obj == stored_flow_obj: if stored_flow['command'] == installed_flow['command']: log.debug('Data already stored.') return # Flow with inconsistency in "command" fields : Remove the # old instruction. This happens when there is a stored # instruction to install the flow, but the new instruction # is to remove it. In this case, the old instruction is # removed and the new one is stored. stored_flow['command'] = installed_flow.get('command') stored_flows.remove(stored_flow) break stored_flows.append(installed_flow) stored_flows_box[switch.id]['flow_list'] = stored_flows stored_flows_box['id'] = 'flow_persistence' self.storehouse.save_flow(stored_flows_box) del stored_flows_box['id'] self.stored_flows = stored_flows_box.copy()
def listen(cls, switch, flows_stats): """Receive flow stats.""" flow_class = FlowFactory.get_class(switch) for flow_stat in flows_stats: flow = flow_class.from_of_flow_stats(flow_stat, switch) # Update controller's flow controller_flow = switch.get_flow_by_id(flow.id) if controller_flow: controller_flow.stats = flow.stats # Update RRD database cls.rrd.update((switch.id, flow.id), packet_count=flow.stats.packet_count, byte_count=flow.stats.byte_count)
def _install_flows(self, command, flows_dict, switches=[]): """Execute all procedures to install flows in the switches. Args: command: Flow command to be installed flows_dict: Dictionary with flows to be installed in the switches. switches: A list of switches """ for switch in switches: serializer = FlowFactory.get_class(switch) flows = flows_dict.get('flows', []) for flow_dict in flows: flow = serializer.from_dict(flow_dict, switch) if command == "delete": flow_mod = flow.as_of_delete_flow_mod() elif command == "add": flow_mod = flow.as_of_add_flow_mod() else: raise InvalidCommandError self._send_flow_mod(flow.switch, flow_mod) self._add_flow_mod_sent(flow_mod.header.xid, flow) self._send_napp_event(switch, flow, command)