Ejemplo n.º 1
0
 def __init__(self, bridge, test_vlans, trunk_iface, event_queue_append):
     self._ovs_helper = OvsHelper()
     self._logger = get_logger('device_discovery')
     self._bridge = bridge
     self._test_vlans = test_vlans
     self._polling_timer = None
     self._fdb_snapshot = None
     self._trunk_iface = trunk_iface
     self._trunk_ofport = None
     self._event_queue_append = event_queue_append
Ejemplo n.º 2
0
 def __init__(self, target, tunnel_ip, ovs_bridge):
     self._logger = get_logger('daqclient')
     self._logger.info('Using target %s', target)
     self._channel = grpc.insecure_channel(target)
     self._stub = None
     self._mac_sessions = {}
     self._lock = threading.Lock()
     self._tunnel_ip = tunnel_ip
     self._endpoint_handler = OvsHelper()
     self._ovs_bridge = ovs_bridge
     # Assigned VLAN is always set to 0 for non-FOT DAQ Client
     self._assigned_vlan = 0
Ejemplo n.º 3
0
class NetworkHelper():
    """Network setup helper for device coupler"""
    def __init__(self, trunk_iface, bridge, test_vlans):
        self._ovs_helper = OvsHelper()
        self._logger = get_logger('networkhelper')
        self._trunk_iface = trunk_iface
        self._bridge = bridge
        self._test_vlans = list(range(test_vlans[0], test_vlans[1] + 1))

    def setup(self):
        """Setup n/w"""
        self._logger.info('Setting up device coupler network.')
        self._setup_ovs_bridge()

    def cleanup(self):
        """Clean up n/w"""
        self._delete_ovs_bridge()
        self._logger.info('Cleaned up device coupler network.')

    def _setup_ovs_bridge(self):
        self._ovs_helper.create_ovs_bridge(self._bridge)
        self._ovs_helper.add_iface_to_bridge(self._bridge, self._trunk_iface)
        self._ovs_helper.set_trunk_vlan(self._trunk_iface, self._test_vlans)

    def _delete_ovs_bridge(self):
        self._ovs_helper.delete_ovs_bridge(self._bridge)

    def get_ovs_bridge(self):
        return self._bridge
Ejemplo n.º 4
0
    def setup(self):
        """Setup device coupler"""
        self._ovs_helper = OvsHelper()

        self._network_helper = NetworkHelper(self._trunk_iface,
                                             self._OVS_BRIDGE,
                                             self._test_vlans)
        self._network_helper.setup()

        self._event_queue = Queue()
        self._workers_executor = ThreadPoolExecutor(
            max_workers=self._WORKER_COUNT)
        self._device_discovery = DeviceDiscovery(self._OVS_BRIDGE,
                                                 self._test_vlans,
                                                 self._trunk_iface,
                                                 self.add_event_to_queue)

        self._source_ip = self._ovs_helper.get_interface_ip()
        target_str = '%s:%s' % (self._daq_grpc_ip, self._daq_grpc_port)
        self._daq_client = DAQClient(target_str, self._source_ip,
                                     self._OVS_BRIDGE)
Ejemplo n.º 5
0
 def __init__(self, trunk_iface, bridge, test_vlans):
     self._ovs_helper = OvsHelper()
     self._logger = get_logger('networkhelper')
     self._trunk_iface = trunk_iface
     self._bridge = bridge
     self._test_vlans = list(range(test_vlans[0], test_vlans[1] + 1))
Ejemplo n.º 6
0
class DeviceCoupler():
    """Main for device coupler"""

    _WORKER_TIMEOUT = 10
    _WORKER_COUNT = 3
    _OVS_BRIDGE = "dev_br0"

    def __init__(self, config):
        self._logger = get_logger('device_coupler')
        self._test_vlans = (config.run_trigger.vlan_start,
                            config.run_trigger.vlan_end)
        self._trunk_iface = config.switch_setup.data_intf
        self._daq_grpc_ip = config.run_trigger.runner_service_ip
        self._daq_grpc_port = config.device_reporting.server_port
        self._network_helper = None
        self._device_discovery = None
        self._daq_client = None
        self._event_queue = None
        self._workers_executor = None
        self._source_ip = None
        self._running = None

    def setup(self):
        """Setup device coupler"""
        self._ovs_helper = OvsHelper()

        self._network_helper = NetworkHelper(self._trunk_iface,
                                             self._OVS_BRIDGE,
                                             self._test_vlans)
        self._network_helper.setup()

        self._event_queue = Queue()
        self._workers_executor = ThreadPoolExecutor(
            max_workers=self._WORKER_COUNT)
        self._device_discovery = DeviceDiscovery(self._OVS_BRIDGE,
                                                 self._test_vlans,
                                                 self._trunk_iface,
                                                 self.add_event_to_queue)

        self._source_ip = self._ovs_helper.get_interface_ip()
        target_str = '%s:%s' % (self._daq_grpc_ip, self._daq_grpc_port)
        self._daq_client = DAQClient(target_str, self._source_ip,
                                     self._OVS_BRIDGE)

    def start(self):
        """Start device coupler"""
        self._running = True
        self._device_discovery.start()
        self._daq_client.start()
        self._logger.info('Starting %s workers', self._WORKER_COUNT)
        for index in range(self._WORKER_COUNT):
            self._workers_executor.submit(self._process_event_queue)

    def cleanup(self):
        """Clean up device coupler"""
        self._running = False
        self._workers_executor.shutdown()
        self._daq_client.stop()
        self._device_discovery.cleanup()
        self._network_helper.cleanup()
        self._logger.info('Cleanup complete')

    def add_event_to_queue(self, event):
        """Add event to queue for processing"""
        self._event_queue.put(event)

    def _process_event_queue(self):
        while self._running:
            try:
                event = self._event_queue.get(timeout=self._WORKER_TIMEOUT)
                self._logger.info(event)
                if event.event_type == DiscoveryEventType.DISCOVERY:
                    port = self._get_device_port(event.vlan)
                    self._daq_client.process_device_discovery(
                        event.mac, event.vlan, port)
                else:
                    self._daq_client.process_device_expiry(event.mac)
            except Empty:
                # Worker timeout. Do nothing
                pass

    def _get_device_port(self, vlan):
        """Mapping for VLAN to ports uses first VLAN of accepted test VLAN range."""
        # TODO: Change once device_coupler can reliably access switch port number
        return vlan - self._test_vlans[0] + 1
Ejemplo n.º 7
0
 def test_ovs_bridge_vlan(self):
     """Test adding interfaces to OVS bridge with VLANs"""
     ovs = OvsHelper()
     bridge = 'test_br'
     ovs.delete_ovs_bridge(bridge)
     ovs.create_ovs_bridge(bridge)
     for index in range(1, 5):
         self._create_netns_with_veth_pair(index)
         iface = 'dev%s' % index
         tag = 200 + index % 2
         ovs.add_iface_to_bridge(bridge, iface)
         ovs.set_native_vlan(iface, tag)
     retcode, _, _ = ovs._run_shell_no_raise(
         'sudo ip netns exec vnet1 ping -c 3 10.1.1.3')
     self.assertEqual(retcode, 0)
     retcode, _, _ = ovs._run_shell_no_raise(
         'sudo ip netns exec vnet1 ping -c 3 10.1.1.4')
     self.assertEqual(retcode, 1)
     for index in range(1, 5):
         vnet = 'vnet%s' % index
         self._delete_netns(vnet)
     ovs.delete_ovs_bridge(bridge)
Ejemplo n.º 8
0
 def _delete_netns(self, vnet):
     ovs = OvsHelper()
     ovs._run_shell('sudo ip netns del %s' % vnet)
Ejemplo n.º 9
0
 def _create_netns_with_veth_pair(self, index):
     ovs = OvsHelper()
     iface1 = 'dev%s' % index
     iface2 = 'netns0'
     vnet = 'vnet%s' % index
     ovs.create_veth_pair(iface1, iface2)
     ovs._run_shell('sudo ip netns add %s' % vnet)
     ovs._run_shell('sudo ip link set %s netns %s' % (iface2, vnet))
     ovs._run_shell('sudo ip -n %s addr add 10.1.1.%s/24 dev %s' %
                    (vnet, index, iface2))
     ovs._run_shell('sudo ip -n %s link set %s up' % (vnet, iface2))
Ejemplo n.º 10
0
class DeviceDiscovery():
    """Device discovery helper for device coupler"""

    _POLLING_INTERVAL = 3

    def __init__(self, bridge, test_vlans, trunk_iface, event_queue_append):
        self._ovs_helper = OvsHelper()
        self._logger = get_logger('device_discovery')
        self._bridge = bridge
        self._test_vlans = test_vlans
        self._polling_timer = None
        self._fdb_snapshot = None
        self._trunk_iface = trunk_iface
        self._trunk_ofport = None
        self._event_queue_append = event_queue_append

    def start(self):
        """Setup device discovery"""
        self._trunk_ofport = self._ovs_helper.get_interface_ofport(
            self._trunk_iface)
        self._fdb_snapshot = set()
        self._polling_timer = HeartbeatScheduler(self._POLLING_INTERVAL)
        self._polling_timer.add_callback(self.poll_forwarding_table)
        self._polling_timer.start()

    def cleanup(self):
        """Clean up device discovery"""
        if self._polling_timer:
            self._polling_timer.stop()
        self._logger.info('Clean up complete.')

    def _process_entry(self, event):
        # Only process events for test vlans in config and on trunk port
        vlan_in_range = event.vlan >= self._test_vlans[
            0] and event.vlan <= self._test_vlans[1]
        if event.port == self._trunk_ofport and vlan_in_range:
            self._logger.info('Processing event: %s', event)
            self._event_queue_append(event)

    def poll_forwarding_table(self):
        """Poll forwarding table and determine devices learnt/expired"""
        fdb_table = set(self._ovs_helper.get_forwarding_table(self._bridge))
        self._logger.info('Polling fdb on %s:\n %s', self._bridge, fdb_table)
        expired = self._fdb_snapshot - fdb_table
        discovered = fdb_table - self._fdb_snapshot
        for entry in expired:
            event = self._build_device_discovery_event(entry, expire=True)
            self._process_entry(event)
        for entry in discovered:
            event = self._build_device_discovery_event(entry)
            self._process_entry(event)
        self._fdb_snapshot = fdb_table

    def _build_device_discovery_event(self, entry, expire=False):
        port, vlan, mac = entry
        event = DeviceDiscoveryEvent()
        event.port = int(port)
        event.vlan = int(vlan)
        event.mac = mac
        event.event_type = DiscoveryEventType.EXPIRY if expire else DiscoveryEventType.DISCOVERY
        return event
Ejemplo n.º 11
0
class DAQClient():
    """gRPC client to send device result"""
    def __init__(self, target, tunnel_ip, ovs_bridge):
        self._logger = get_logger('daqclient')
        self._logger.info('Using target %s', target)
        self._channel = grpc.insecure_channel(target)
        self._stub = None
        self._mac_sessions = {}
        self._lock = threading.Lock()
        self._tunnel_ip = tunnel_ip
        self._endpoint_handler = OvsHelper()
        self._ovs_bridge = ovs_bridge
        # Assigned VLAN is always set to 0 for non-FOT DAQ Client
        self._assigned_vlan = 0

    def start(self):
        """Start the client handler"""
        grpc.channel_ready_future(
            self._channel).result(timeout=CONNECT_TIMEOUT_SEC)
        self._stub = SessionServerStub(self._channel)

    def stop(self):
        """Stop client handler"""
        for mac in self._mac_sessions.keys():
            self._disconnect(mac)

    def _connect(self, mac, vlan, port):
        self._logger.info('Connecting %s with VLAN %s', mac, vlan)
        session_params = SessionParams()
        session_params.device_mac = mac
        session_params.device_vlan = vlan
        session_params.device_port = port
        session_params.assigned_vlan = self._assigned_vlan
        session_params.endpoint.ip = self._tunnel_ip or DEFAULT_SERVER_ADDRESS
        session = self._stub.StartSession(session_params)
        thread = threading.Thread(
            target=lambda: self._run_test_session(mac, session))
        thread.start()
        self._logger.info('Connection of %s with VLAN %s succeeded', mac, vlan)
        return session

    def _disconnect(self, mac):
        with self._lock:
            session = self._mac_sessions.get(mac, {}).get('session')
            if session:
                session.cancel()
                mac_session = self._mac_sessions.pop(mac)
                index = mac_session['index']
                interface = "vxlan%s" % index
                self._endpoint_handler.remove_vxlan_endpoint(
                    interface, self._ovs_bridge)
                self._logger.info('Session terminated for %s', mac)
            else:
                self._logger.warning(
                    'Attempt to disconnect unconnected device %s', mac)

    def _is_session_running(self, mac, progress):
        result = self._process_session_progress(mac, progress)
        return result not in ('PASSED', 'FAILED', 'ERROR')

    def _process_session_progress(self, mac, progress):
        endpoint = progress.endpoint
        result_code = progress.result.code
        assert not (endpoint.ip and
                    result_code), 'both endpoint.ip and result.code defined'
        if result_code:
            result_name = SessionResult.ResultCode.Name(result_code)
            self._logger.info('Device report %s as %s', mac, result_name)
            return result_name
        if endpoint.ip:
            self._logger.info('Device report %s endpoint ip: %s)', mac,
                              endpoint.ip)
            # TODO: Change the way indexes work. Check for VXLAN port being sent
            index = endpoint.vni
            device = self._mac_sessions[mac]
            device['index'] = index
            interface = "vxlan%s" % index
            self._endpoint_handler.remove_vxlan_endpoint(
                interface, self._ovs_bridge)
            self._endpoint_handler.create_vxlan_endpoint(
                interface, endpoint.ip, index)
            self._endpoint_handler.add_iface_to_bridge(
                self._ovs_bridge, interface, tag=device['device_vlan'])
        return None

    def _run_test_session(self, mac, session):
        try:
            for progress in session:
                if not self._is_session_running(mac, progress):
                    break
            self._logger.info('Progress complete for %s', mac)
        except Exception as e:
            self._logger.error('Progress exception: %s', e)
            self._logger.error('Traceback: %s', traceback.format_exc())
        self._disconnect(mac)

    def _initiate_test_session(self, mac, device_vlan, port):
        if mac in self._mac_sessions:
            vlan_in_session = (
                device_vlan == self._mac_sessions[mac]['device_vlan'])
            port_in_session = (port == self._mac_sessions[mac]['device_port'])
            if vlan_in_session and port_in_session:
                self._logger.info(
                    'Test session for %s already exists. Ignoring.', mac)
                return
            self._logger.info(
                'MAC learned on VLAN %s. Terminating current session.',
                device_vlan)
            self._disconnect(mac)

        self._logger.info('Initiating test session for %s on VLAN %s', mac,
                          device_vlan)

        if device_vlan:
            self._mac_sessions[mac] = {}
            self._mac_sessions[mac]['device_vlan'] = device_vlan
            self._mac_sessions[mac]['device_port'] = port
            self._mac_sessions[mac]['session'] = self._connect(
                mac, device_vlan, port)
        self._logger.info('Initiated test session %s', self._mac_sessions[mac])

    def process_device_discovery(self, mac, device_vlan, port):
        """Process discovery of device to be tested"""
        # TODO: End existing test session and start new one if discovered on another vlan
        with self._lock:
            self._initiate_test_session(mac, device_vlan, port)

    def process_device_expiry(self, mac):
        """Process expiry of device"""
        self._logger.info('Terminating session for %s', mac)
        self._disconnect(mac)