예제 #1
0
class TestConnection(TestCase):
    """Connection tests."""
    def setUp(self):
        """Instantiate a Connection."""
        socket = MagicMock()
        switch = MagicMock()
        self.connection = Connection('addr', 123, socket, switch)

        switch.connection = self.connection

    def test__str__(self):
        """Test __str__ method."""
        self.assertEqual(str(self.connection), "Connection('addr', 123)")

    def test__repr__(self):
        """Test __repr__ method."""
        self.connection.socket = 'socket'
        self.connection.switch = 'switch'

        expected = "Connection('addr', 123, 'socket', 'switch', " + \
                   "<ConnectionState.NEW: 0>)"
        self.assertEqual(repr(self.connection), expected)

    def test_state(self):
        """Test state property."""
        self.assertEqual(self.connection.state.value, 0)

        self.connection.state = ConnectionState.FINISHED
        self.assertEqual(self.connection.state.value, 4)

    def test_state__error(self):
        """Test state property to error case."""
        with self.assertRaises(Exception):
            self.connection.state = 1000

    def test_id(self):
        """Test id property."""
        self.assertEqual(self.connection.id, ('addr', 123))

    def test_send(self):
        """Test send method."""
        self.connection.send(b'data')

        self.connection.socket.sendall.assert_called_with(b'data')

    def test_send__error(self):
        """Test send method to error case."""
        self.connection.socket.sendall.side_effect = SocketError

        self.connection.send(b'data')

        self.assertIsNone(self.connection.socket)

    def test_close(self):
        """Test close method."""
        self.connection.close()

        self.assertIsNone(self.connection.socket)

    def test_close__os_error(self):
        """Test close method to OSError case."""
        self.connection.socket.shutdown.side_effect = OSError

        with self.assertRaises(OSError):
            self.connection.close()

        self.assertIsNotNone(self.connection.socket)

    def test_close__attribute_error(self):
        """Test close method to AttributeError case."""
        self.connection.socket = None

        self.connection.close()

        self.assertIsNone(self.connection.socket)

    def test_is_alive(self):
        """Test is_alive method to True and False returns."""
        self.assertTrue(self.connection.is_alive())

        self.connection.state = ConnectionState.FINISHED
        self.assertFalse(self.connection.is_alive())

    def test_is_new(self):
        """Test is_new method."""
        self.assertTrue(self.connection.is_new())

    def test_established_state(self):
        """Test set_established_state and is_established methods."""
        self.connection.set_established_state()
        self.assertTrue(self.connection.is_established())

    def test_setup_state(self):
        """Test set_setup_state and is_during_setup methods."""
        self.connection.set_setup_state()
        self.assertTrue(self.connection.is_during_setup())

    def test_update_switch(self):
        """Test update_switch method."""
        switch = MagicMock()
        self.connection.update_switch(switch)

        self.assertEqual(self.connection.switch, switch)
        self.assertEqual(switch.connection, self.connection)
예제 #2
0
class KytosRequestHandler(BaseRequestHandler):
    """The socket/request handler class for our controller.

    It is instantiated once per connection between each switch and the
    controller.
    The setup method will dispatch a KytosEvent (``kytos/core.connection.new``)
    on the controller, that will be processed by a Core App.
    The finish method will close the connection and dispatch a KytonEvents
    (``kytos/core.connection.closed``) on the controller.
    """

    known_ports = {6633: 'openflow'}

    def __init__(self, request, client_address, server):
        """Contructor takes the parameters below.

        Args:
            request (socket.socket):
                Request sent by client.
            client_address (tuple):
                Client address, tuple with host and port.
            server (socketserver.BaseServer):
                Server used to send messages to client.
        """
        super().__init__(request, client_address, server)
        self.connection = None

    def setup(self):
        """Method used to setup the new connection.

        This method builds a new controller Connection, and places a
        ``kytos/core.connection.new`` KytosEvent in the app buffer.
        """
        self.ip = self.client_address[0]
        self.port = self.client_address[1]

        log.info("New connection from %s:%s", self.ip, self.port)

        self.connection = Connection(self.ip, self.port, self.request)  # noqa
        server_port = self.server.server_address[1]
        if server_port in self.known_ports:
            protocol_name = self.known_ports[server_port]
        else:
            protocol_name = f'{server_port:04d}'
        self.connection.protocol.name = protocol_name

        self.request.settimeout(30)
        self.exception = None

        event_name = \
            f'kytos/core.{self.connection.protocol.name}.connection.new'
        event = KytosEvent(name=event_name,
                           content={'source': self.connection})

        self.server.controller.buffers.app.put(event)

    def handle(self):
        """Handle each request and places its data in the raw event buffer.

        This method loops reading the binary data from the connection socket,
        and placing a ``kytos/core.messages.new`` KytosEvent in the raw event
        buffer.
        """
        curr_thread = current_thread()
        MAX_SIZE = 2**16
        while True:
            try:
                new_data = self.request.recv(MAX_SIZE)
            except (SocketError, OSError, InterruptedError,
                    ConnectionResetError) as exception:
                self.exception = exception
                log.debug('Socket handler exception while reading: %s',
                          exception)
                break
            if new_data == b'':
                self.exception = 'Request closed by client.'
                break

            if not self.connection.is_alive():
                continue

            log.debug("New data from %s:%s at thread %s", self.ip, self.port,
                      curr_thread.name)

            content = {'source': self.connection, 'new_data': new_data}
            event_name = \
                f'kytos/core.{self.connection.protocol.name}.raw.in'
            event = KytosEvent(name=event_name, content=content)

            self.server.controller.buffers.raw.put(event)

    def finish(self):
        """Method is called when the client connection is finished.

        This method closes the connection socket and generates a
        ``kytos/core.connection.lost`` KytosEvent in the App buffer.
        """
        log.info("Connection lost with Client %s:%s. Reason: %s", self.ip,
                 self.port, self.exception)
        self.connection.state = CONNECTION_STATE.FINISHED
        self.connection.close()
        content = {'source': self.connection}
        if self.exception:
            content['exception'] = self.exception
        event_name = \
            f'kytos/core.{self.connection.protocol.name}.connection.lost'
        event = KytosEvent(name=event_name, content=content)
        self.server.controller.buffers.app.put(event)