kytos.core.connection   A
last analyzed

Complexity

Total Complexity 24

Size/Duplication

Total Lines 150
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
eloc 84
dl 0
loc 150
rs 10
c 0
b 0
f 0
ccs 77
cts 77
cp 1
wmc 24

15 Methods

Rating   Name   Duplication   Size   Complexity  
A Connection.state() 0 4 2
A Connection.__init__() 0 16 1
A ConnectionProtocol.__init__() 0 5 1
A Connection.id() 0 9 1
A Connection.set_setup_state() 0 3 1
A Connection.__repr__() 0 3 1
A Connection.is_during_setup() 0 3 1
A Connection.is_new() 0 3 1
A Connection.__str__() 0 2 1
A Connection.set_established_state() 0 3 1
A Connection.is_alive() 0 4 1
A Connection.is_established() 0 3 1
A Connection.update_switch() 0 8 1
A Connection.send() 0 12 3
B Connection.close() 0 18 6
1
"""Module with main classes related to Connections."""
2 1
import logging
3 1
from enum import Enum
4 1
from errno import EBADF, ENOTCONN
5 1
from socket import SHUT_RDWR
6 1
from socket import error as SocketError
7
8 1
__all__ = ('Connection', 'ConnectionProtocol', 'ConnectionState')
9
10 1
LOG = logging.getLogger(__name__)
11
12
13 1
class ConnectionState(Enum):
14
    """Enum of possible general connections states."""
15
16 1
    NEW = 0
17 1
    SETUP = 1
18 1
    ESTABLISHED = 2
19 1
    FAILED = 3
20 1
    FINISHED = 4
21
22
23 1
class ConnectionProtocol:
24
    """Class to hold simple protocol information for the connection."""
25
26 1
    def __init__(self, name=None, version=None, state=None):
27
        """Assign parameters to instance variables."""
28 1
        self.name = name
29 1
        self.version = version
30 1
        self.state = state
31
32
33 1
class Connection:
34
    """Connection class to abstract a network connections."""
35
36 1
    def __init__(self, address, port, socket, switch=None):
37
        """Assign parameters to instance variables.
38
39
        Args:
40
            address (|hw_address|): Source address.
41
            port (int): Port number.
42
            socket (socket): socket.
43
            switch (:class:`~.Switch`): switch with this connection.
44
        """
45 1
        self.address = address
46 1
        self.port = port
47 1
        self.socket = socket
48 1
        self.switch = switch
49 1
        self.state = ConnectionState.NEW
50 1
        self.protocol = ConnectionProtocol()
51 1
        self.remaining_data = b''
52
53 1
    def __str__(self):
54 1
        return f"Connection({self.address!r}, {self.port!r})"
55
56 1
    def __repr__(self):
57 1
        return f"Connection({self.address!r}, {self.port!r}," + \
58
               f" {self.socket!r}, {self.switch!r}, {self.state!r})"
59
60 1
    @property
61
    def state(self):
62
        """Return the state of the connection."""
63 1
        return self._state
64
65 1
    @state.setter
66
    def state(self, new_state):
67 1
        if new_state not in ConnectionState:
68 1
            raise Exception('Unknown State', new_state)
69
        # pylint: disable=attribute-defined-outside-init
70 1
        self._state = new_state
71
        # pylint: enable=attribute-defined-outside-init
72 1
        LOG.debug('Connection %s changed state: %s',
73
                  self.id, self.state)
74
75 1
    @property
76
    def id(self):  # pylint: disable=invalid-name
77
        """Return id from Connection instance.
78
79
        Returns:
80
            string: Connection id.
81
82
        """
83 1
        return (self.address, self.port)
84
85 1
    def send(self, buffer):
86
        """Send a buffer message using the socket from the connection instance.
87
88
        Args:
89
            buffer (bytes): Message buffer that will be sent.
90
        """
91 1
        try:
92 1
            if self.is_alive():
93 1
                self.socket.sendall(buffer)
94 1
        except (OSError, SocketError) as exception:
95 1
            LOG.debug('Could not send packet. Exception: %s', exception)
96 1
            self.close()
97
98 1
    def close(self):
99
        """Close the socket from connection instance."""
100 1
        self.state = ConnectionState.FINISHED
101 1
        if self.switch and self.switch.connection is self:
102 1
            self.switch.connection = None
103
104 1
        LOG.debug('Shutting down Connection %s', self.id)
105
106 1
        try:
107 1
            self.socket.shutdown(SHUT_RDWR)
108 1
            self.socket.close()
109 1
            self.socket = None
110 1
            LOG.debug('Connection Closed: %s', self.id)
111 1
        except OSError as exception:
112 1
            if exception.errno not in (ENOTCONN, EBADF):
113 1
                raise exception
114 1
        except AttributeError as exception:
115 1
            LOG.debug('Socket Already Closed: %s', self.id)
116
117 1
    def is_alive(self):
118
        """Return True if the connection socket is alive. False otherwise."""
119 1
        return self.socket is not None and self.state not in (
120
            ConnectionState.FINISHED, ConnectionState.FAILED)
121
122 1
    def is_new(self):
123
        """Return True if the connection is new. False otherwise."""
124 1
        return self.state == ConnectionState.NEW
125
126 1
    def is_established(self):
127
        """Return True if the connection is established. False otherwise."""
128 1
        return self.state == ConnectionState.ESTABLISHED
129
130 1
    def is_during_setup(self):
131
        """Return True if the connection is in setup state. False otherwise."""
132 1
        return self.state == ConnectionState.SETUP
133
134 1
    def set_established_state(self):
135
        """Set the connection state to Established."""
136 1
        self.state = ConnectionState.ESTABLISHED
137
138 1
    def set_setup_state(self):
139
        """Set the connection state to Setup."""
140 1
        self.state = ConnectionState.SETUP
141
142 1
    def update_switch(self, switch):
143
        """Update switch with this instance of Connection.
144
145
        Args:
146
          switch (:class:`~.Switch`): switch instance.
147
        """
148 1
        self.switch = switch
149
        self.switch.connection = self
150