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