Passed
Pull Request — master (#1267)
by
unknown
08:50
created

kytos.core.connection.Connection.close()   A

Complexity

Conditions 4

Size

Total Lines 15
Code Lines 13

Duplication

Lines 0
Ratio 0 %

Importance

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