def connection_made(self, transport): """Handle new client connection, passing it to the controller. Build a new Kytos `Connection` and send a ``kytos/core.connection.new`` KytosEvent through the app buffer. """ self.transport = transport addr, port = transport.get_extra_info('peername') _, server_port = transport.get_extra_info('sockname') socket = transport.get_extra_info('socket') LOG.info("New connection from %s:%s", addr, port) self.connection = Connection(addr, port, socket) # ASYNC TODO: # if self.server.protocol_name: # self.known_ports[server_port] = self.server.protocol_name 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 # ASYNC TODO: # self.request.settimeout(70) event_name = 'kytos/core.connection.new' # f'kytos/core.{self.connection.protocol.name}.connection.new' event = KytosEvent(name=event_name, content={'source': self.connection}) self._loop.create_task(self.server.controller.buffers.raw.aput(event))
def setUp(self): """Instantiate a Connection.""" socket = MagicMock() switch = MagicMock() self.connection = Connection('addr', 123, socket, switch) switch.connection = self.connection
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 connection_made(self, transport): """Handle new client connection, passing it to the controller. Build a new Kytos `Connection` and send a ``kytos/core.connection.new`` KytosEvent through the app buffer. """ self.transport = transport addr, port = transport.get_extra_info('peername') _, server_port = transport.get_extra_info('sockname') socket = transport.get_extra_info('socket') LOG.info("New connection from %s:%s", addr, port) self.connection = Connection(addr, port, socket) # This allows someone to inherit from KytosServer and start a server # on another port to handle a different protocol. if self.server.protocol_name: self.known_ports[server_port] = self.server.protocol_name 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 event_name = f'kytos/core.{protocol_name}.connection.new' event = KytosEvent(name=event_name, content={'source': self.connection}) self._loop.create_task(self.server.controller.buffers.raw.aput(event))
def get_connection_mock(of_version, target_switch, state=ConnectionState.NEW): """Return a connection mock.""" connection = Connection(Mock(), Mock(), Mock()) connection.switch = target_switch connection.state = state connection.protocol.version = of_version connection.protocol.unpack = unpack return connection
def get_switch_mock(of_version, connection_state=ConnectionState.NEW, dpid="00:00:00:00:00:00:00:01"): """Return a switch mock.""" switch = Switch(dpid) address = Mock() port = Mock() socket = Mock() switch.connection = Connection(address, port, socket) switch.connection.protocol.version = of_version switch.connection.state = connection_state return switch
class KytosServerProtocol(asyncio.Protocol): """Kytos' main request handler. 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 KytosEvent (``kytos/core.connection.closed``) on the controller. """ known_ports = {6633: 'openflow', 6653: 'openflow'} def __init__(self): """Initialize protocol and check if server attribute was set.""" self._loop = asyncio.get_event_loop() self.connection = None self.transport = None self._rest = b'' # server attribute is set outside this class, in KytosServer.init() # Here we initialize it to None to avoid pylint warnings if not getattr(self, 'server'): self.server = None # Then we check if it was really set if not self.server: raise ValueError("server instance must be assigned before init") def connection_made(self, transport): """Handle new client connection, passing it to the controller. Build a new Kytos `Connection` and send a ``kytos/core.connection.new`` KytosEvent through the app buffer. """ self.transport = transport addr, port = transport.get_extra_info('peername') _, server_port = transport.get_extra_info('sockname') socket = transport.get_extra_info('socket') LOG.info("New connection from %s:%s", addr, port) self.connection = Connection(addr, port, socket) # ASYNC TODO: # if self.server.protocol_name: # self.known_ports[server_port] = self.server.protocol_name 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 # ASYNC TODO: # self.request.settimeout(70) event_name = 'kytos/core.connection.new' # f'kytos/core.{self.connection.protocol.name}.connection.new' event = KytosEvent(name=event_name, content={'source': self.connection}) self._loop.create_task(self.server.controller.buffers.raw.aput(event)) def data_received(self, data): """Handle each request and place its data in the raw event buffer. Sends the received binary data in a ``kytos/core.{protocol}.raw.in`` event on the raw buffer. """ data = self._rest + data LOG.debug("New data from %s:%s (%s bytes)", self.connection.address, self.connection.port, len(data)) # LOG.debug("New data from %s:%s (%s bytes): %s", self.addr, self.port, # len(data), binascii.hexlify(data)) content = {'source': self.connection, 'new_data': data} event_name = f'kytos/core.{self.connection.protocol.name}.raw.in' event = KytosEvent(name=event_name, content=content) self._loop.create_task(self.server.controller.buffers.raw.aput(event)) def connection_lost(self, exc): """Close the connection socket and generate connection lost event. Emits a ``kytos/core.connection.lost`` event through the App buffer. """ LOG.info("Connection lost with client %s:%s. Reason: %s", self.connection.address, self.connection.port, exc) self.connection.close() content = {'source': self.connection} if exc: content['exception'] = exc event_name = \ f'kytos/core.{self.connection.protocol.name}.connection.lost' event = KytosEvent(name=event_name, content=content) self._loop.create_task(self.server.controller.buffers.app.aput(event))
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)
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)
class KytosServerProtocol(asyncio.Protocol): """Kytos' main request handler. 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 KytosEvent (``kytos/core.connection.closed``) on the controller. """ known_ports = { 6633: 'openflow', 6653: 'openflow' } def __init__(self): """Initialize protocol and check if server attribute was set.""" self._loop = asyncio.get_event_loop() self.connection = None self.transport = None self._rest = b'' # server attribute is set outside this class, in KytosServer.init() # Here we initialize it to None to avoid pylint warnings if not getattr(self, 'server'): self.server = None # Then we check if it was really set if not self.server: raise ValueError("server instance must be assigned before init") def connection_made(self, transport): """Handle new client connection, passing it to the controller. Build a new Kytos `Connection` and send a ``kytos/core.connection.new`` KytosEvent through the app buffer. """ self.transport = transport addr, port = transport.get_extra_info('peername') _, server_port = transport.get_extra_info('sockname') socket = transport.get_extra_info('socket') LOG.info("New connection from %s:%s", addr, port) self.connection = Connection(addr, port, socket) # This allows someone to inherit from KytosServer and start a server # on another port to handle a different protocol. if self.server.protocol_name: self.known_ports[server_port] = self.server.protocol_name 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 event_name = f'kytos/core.{protocol_name}.connection.new' event = KytosEvent(name=event_name, content={'source': self.connection}) self._loop.create_task(self.server.controller.buffers.raw.aput(event)) def data_received(self, data): """Handle each request and place its data in the raw event buffer. Sends the received binary data in a ``kytos/core.{protocol}.raw.in`` event on the raw buffer. """ # max_size = 2**16 # new_data = self.request.recv(max_size) data = self._rest + data LOG.debug("New data from %s:%s (%s bytes)", self.connection.address, self.connection.port, len(data)) # LOG.debug("New data from %s:%s (%s bytes): %s", self.addr, self.port, # len(data), binascii.hexlify(data)) content = {'source': self.connection, 'new_data': data} event_name = f'kytos/core.{self.connection.protocol.name}.raw.in' event = KytosEvent(name=event_name, content=content) self._loop.create_task(self.server.controller.buffers.raw.aput(event)) def connection_lost(self, exc): """Close the connection socket and generate connection lost event. Emits a ``kytos/core.{protocol}.connection.lost`` event through the App buffer. """ reason = exc or "Request closed by client" LOG.info("Connection lost with client %s:%s. Reason: %s", self.connection.address, self.connection.port, reason) self.connection.close() content = {'source': self.connection} if exc: content['exception'] = exc event_name = \ f'kytos/core.{self.connection.protocol.name}.connection.lost' event = KytosEvent(name=event_name, content=content) self._loop.create_task(self.server.controller.buffers.app.aput(event))