1 | """Defines common structures and enums for controller2switch.""" |
||
2 | |||
3 | # System imports |
||
4 | 1 | from enum import IntEnum |
|
5 | |||
6 | 1 | from pyof.foundation.base import GenericMessage, GenericStruct |
|
7 | 1 | from pyof.foundation.basic_types import ( |
|
8 | BinaryData, Char, Pad, UBInt8, UBInt16, UBInt32, UBInt64) |
||
9 | 1 | from pyof.foundation.constants import ( |
|
10 | DESC_STR_LEN, OFP_MAX_TABLE_NAME_LEN, SERIAL_NUM_LEN) |
||
11 | # Local source tree imports |
||
12 | 1 | from pyof.v0x01.common.action import ListOfActions |
|
13 | 1 | from pyof.v0x01.common.flow_match import FlowWildCards, Match |
|
14 | 1 | from pyof.v0x01.common.header import Header |
|
15 | 1 | from pyof.v0x01.common.phy_port import Port |
|
16 | |||
17 | # Third-party imports |
||
18 | |||
19 | 1 | __all__ = ('ConfigFlags', 'StatsTypes', 'AggregateStatsReply', |
|
20 | 'AggregateStatsRequest', 'DescStats', 'FlowStats', |
||
21 | 'FlowStatsRequest', 'PortStats', 'PortStatsRequest', 'QueueStats', |
||
22 | 'QueueStatsRequest', 'TableStats', 'VendorStats', |
||
23 | 'VendorStatsRequest') |
||
24 | |||
25 | # Enums |
||
26 | |||
27 | |||
28 | 1 | class ConfigFlags(IntEnum): |
|
29 | """Configuration Flags. Handling of IP Fragments.""" |
||
30 | |||
31 | #: No special handling for fragments |
||
32 | 1 | OFPC_FRAG_NORMAL = 0 |
|
33 | #: Drop fragments |
||
34 | 1 | OFPC_FRAG_DROP = 1 |
|
35 | #: Reassemble (only if OFPC_IP_REASM set) |
||
36 | 1 | OFPC_FRAG_REASM = 2 |
|
37 | 1 | OFPC_FRAG_MASK = 3 |
|
38 | |||
39 | |||
40 | 1 | class StatsTypes(IntEnum): |
|
41 | """Type field to be used both in both request and reply. |
||
42 | |||
43 | It specifies the kind of information being passed and determines how the |
||
44 | body field is interpreted. |
||
45 | """ |
||
46 | |||
47 | #: Description of this OpenFlow switch. The request body is empty. |
||
48 | 1 | OFPST_DESC = 0 |
|
49 | #: Individual flow statistics. The request body is struct |
||
50 | #: ofp_flow_stats_request. |
||
51 | 1 | OFPST_FLOW = 1 |
|
52 | #: Aggregate flow statistics. The request body is struct |
||
53 | #: ofp_aggregate_stats_request. |
||
54 | 1 | OFPST_AGGREGATE = 2 |
|
55 | #: Flow table statistics. The request body is empty. |
||
56 | 1 | OFPST_TABLE = 3 |
|
57 | #: Physical port statistics. The request body is empty. |
||
58 | 1 | OFPST_PORT = 4 |
|
59 | #: Queue statistics for a port. The request body defines the port |
||
60 | 1 | OFPST_QUEUE = 5 |
|
61 | #: Vendor extension. The request and reply bodies begin with a 32-bit |
||
62 | #: vendor ID |
||
63 | 1 | OFPST_VENDOR = 0xffff |
|
64 | |||
65 | |||
66 | # Classes |
||
67 | |||
68 | |||
69 | 1 | class SwitchConfig(GenericMessage): |
|
70 | """Used as base class for SET_CONFIG and GET_CONFIG_REPLY messages.""" |
||
71 | |||
72 | 1 | header = Header() |
|
73 | 1 | flags = UBInt16(enum_ref=ConfigFlags) |
|
74 | 1 | miss_send_len = UBInt16() |
|
75 | |||
76 | 1 | def __init__(self, xid=None, flags=None, miss_send_len=None): |
|
77 | """Create a SwitchConfig with the optional parameters below. |
||
78 | |||
79 | Args: |
||
80 | xid (int): xid to be used on the message header. |
||
81 | flags (ConfigFlags): OFPC_* flags. |
||
82 | miss_send_len (int): UBInt16 max bytes of new flow that the |
||
83 | datapath should send to the controller. |
||
84 | """ |
||
85 | 1 | super().__init__(xid) |
|
86 | 1 | self.flags = flags |
|
87 | 1 | self.miss_send_len = miss_send_len |
|
88 | |||
89 | |||
90 | 1 | class AggregateStatsReply(GenericStruct): |
|
91 | """Body of reply to OFPST_AGGREGATE request.""" |
||
92 | |||
93 | 1 | packet_count = UBInt64() |
|
94 | 1 | byte_count = UBInt64() |
|
95 | 1 | flow_count = UBInt32() |
|
96 | #: Align to 64 bits |
||
97 | 1 | pad = Pad(4) |
|
98 | |||
99 | 1 | def __init__(self, packet_count=None, byte_count=None, flow_count=None): |
|
100 | """Create a AggregateStatsReply with the optional parameters below. |
||
101 | |||
102 | Args: |
||
103 | packet_count (int): Number of packets in flows |
||
104 | byte_count (int): Number of bytes in flows |
||
105 | flow_count (int): Number of flows |
||
106 | """ |
||
107 | 1 | super().__init__() |
|
108 | 1 | self.packet_count = packet_count |
|
109 | 1 | self.byte_count = byte_count |
|
110 | 1 | self.flow_count = flow_count |
|
111 | |||
112 | |||
113 | 1 | class AggregateStatsRequest(GenericStruct): |
|
114 | """Body for ofp_stats_request of type OFPST_AGGREGATE.""" |
||
115 | |||
116 | 1 | match = Match() |
|
117 | 1 | table_id = UBInt8() |
|
118 | #: Align to 32 bits |
||
119 | 1 | pad = Pad(1) |
|
120 | 1 | out_port = UBInt16() |
|
121 | |||
122 | 1 | def __init__(self, match=Match(), table_id=0xff, out_port=Port.OFPP_NONE): |
|
123 | """Create a AggregateStatsRequest with the optional parameters below. |
||
124 | |||
125 | Args: |
||
126 | match (~pyof.v0x01.common.flow_match.Match): Fields to match. |
||
127 | table_id (int): ID of table to read (from pyof_table_stats) 0xff |
||
128 | for all tables or 0xfe for emergency. |
||
129 | out_port (int): Require matching entries to include this as an |
||
130 | output port. A value of OFPP_NONE indicates no restriction. |
||
131 | """ |
||
132 | 1 | super().__init__() |
|
133 | 1 | self.match = match |
|
134 | 1 | self.table_id = table_id |
|
135 | 1 | self.out_port = out_port |
|
136 | |||
137 | |||
138 | 1 | View Code Duplication | class DescStats(GenericStruct): |
0 ignored issues
–
show
Duplication
introduced
by
![]() |
|||
139 | """Information available from the OFPST_DESC stats request. |
||
140 | |||
141 | Information about the switch manufacturer, hardware revision, software |
||
142 | revision, serial number and a description field. |
||
143 | """ |
||
144 | |||
145 | 1 | mfr_desc = Char(length=DESC_STR_LEN) |
|
146 | 1 | hw_desc = Char(length=DESC_STR_LEN) |
|
147 | 1 | sw_desc = Char(length=DESC_STR_LEN) |
|
148 | 1 | serial_num = Char(length=SERIAL_NUM_LEN) |
|
149 | 1 | dp_desc = Char(length=DESC_STR_LEN) |
|
150 | |||
151 | 1 | def __init__(self, mfr_desc=None, hw_desc=None, sw_desc=None, |
|
152 | serial_num=None, dp_desc=None): |
||
153 | """Create a DescStats with the optional parameters below. |
||
154 | |||
155 | Args: |
||
156 | mfr_desc (str): Manufacturer description |
||
157 | hw_desc (str): Hardware description |
||
158 | sw_desc (str): Software description |
||
159 | serial_num (str): Serial number |
||
160 | dp_desc (str): Human readable description of datapath |
||
161 | """ |
||
162 | 1 | super().__init__() |
|
163 | 1 | self.mfr_desc = mfr_desc |
|
164 | 1 | self.hw_desc = hw_desc |
|
165 | 1 | self.sw_desc = sw_desc |
|
166 | 1 | self.serial_num = serial_num |
|
167 | 1 | self.dp_desc = dp_desc |
|
168 | |||
169 | |||
170 | 1 | View Code Duplication | class FlowStats(GenericStruct): |
0 ignored issues
–
show
|
|||
171 | """Body of reply to OFPST_FLOW request.""" |
||
172 | |||
173 | 1 | length = UBInt16() |
|
174 | 1 | table_id = UBInt8() |
|
175 | #: Align to 32 bits. |
||
176 | 1 | pad = Pad(1) |
|
177 | 1 | match = Match() |
|
178 | 1 | duration_sec = UBInt32() |
|
179 | 1 | duration_nsec = UBInt32() |
|
180 | 1 | priority = UBInt16() |
|
181 | 1 | idle_timeout = UBInt16() |
|
182 | 1 | hard_timeout = UBInt16() |
|
183 | #: Align to 64-bits |
||
184 | 1 | pad2 = Pad(6) |
|
185 | 1 | cookie = UBInt64() |
|
186 | 1 | packet_count = UBInt64() |
|
187 | 1 | byte_count = UBInt64() |
|
188 | 1 | actions = ListOfActions() |
|
189 | |||
190 | 1 | def __init__(self, length=None, table_id=None, match=None, |
|
191 | duration_sec=None, duration_nsec=None, priority=None, |
||
192 | idle_timeout=None, hard_timeout=None, cookie=None, |
||
193 | packet_count=None, byte_count=None, actions=None): |
||
194 | """Create a FlowStats with the optional parameters below. |
||
195 | |||
196 | Args: |
||
197 | length (int): Length of this entry. |
||
198 | table_id (int): ID of table flow came from. |
||
199 | match (~pyof.v0x01.common.flow_match.Match): Description of fields. |
||
200 | duration_sec (int): Time flow has been alive in seconds. |
||
201 | duration_nsec (int): Time flow has been alive in nanoseconds in |
||
202 | addition to duration_sec. |
||
203 | priority (int): Priority of the entry. Only meaningful when this |
||
204 | is not an exact-match entry. |
||
205 | idle_timeout (int): Number of seconds idle before expiration. |
||
206 | hard_timeout (int): Number of seconds before expiration. |
||
207 | cookie (int): Opaque controller-issued identifier. |
||
208 | packet_count (int): Number of packets in flow. |
||
209 | byte_count (int): Number of bytes in flow. |
||
210 | actions (:class:`~pyof.v0x01.common.actions.ListOfActions`): |
||
211 | List of Actions. |
||
212 | """ |
||
213 | 1 | super().__init__() |
|
214 | 1 | self.length = length |
|
215 | 1 | self.table_id = table_id |
|
216 | 1 | self.match = match |
|
217 | 1 | self.duration_sec = duration_sec |
|
218 | 1 | self.duration_nsec = duration_nsec |
|
219 | 1 | self.priority = priority |
|
220 | 1 | self.idle_timeout = idle_timeout |
|
221 | 1 | self.hard_timeout = hard_timeout |
|
222 | 1 | self.cookie = cookie |
|
223 | 1 | self.packet_count = packet_count |
|
224 | 1 | self.byte_count = byte_count |
|
225 | 1 | self.actions = [] if actions is None else actions |
|
226 | |||
227 | 1 | def unpack(self, buff, offset=0): |
|
228 | """Unpack *buff* into this object. |
||
229 | |||
230 | Do nothing, since the _length is already defined and it is just a Pad. |
||
231 | Keep buff and offset just for compability with other unpack methods. |
||
232 | |||
233 | Args: |
||
234 | buff (bytes): Buffer where data is located. |
||
235 | offset (int): Where data stream begins. |
||
236 | """ |
||
237 | 1 | self.length = UBInt16() |
|
238 | 1 | self.length.unpack(buff, offset) |
|
239 | 1 | max_length = offset + self.length.value |
|
240 | 1 | super().unpack(buff[:max_length], offset) |
|
241 | |||
242 | |||
243 | 1 | class FlowStatsRequest(GenericStruct): |
|
244 | """Body for ofp_stats_request of type OFPST_FLOW.""" |
||
245 | |||
246 | 1 | match = Match() |
|
247 | 1 | table_id = UBInt8() |
|
248 | #: Align to 32 bits. |
||
249 | 1 | pad = Pad(1) |
|
250 | 1 | out_port = UBInt16() |
|
251 | |||
252 | 1 | def __init__(self, match=None, table_id=0xff, out_port=Port.OFPP_NONE): |
|
253 | """Create a FlowStatsRequest with the optional parameters below. |
||
254 | |||
255 | Args: |
||
256 | match (:class:`~pyof.v0x01.common.flow_match.Match`): |
||
257 | Fields to match. |
||
258 | table_id (int): ID of table to read (from pyof_table_stats) |
||
259 | 0xff for all tables or 0xfe for emergency. |
||
260 | out_port (:class:`int`, :class:`~pyof.v0x01.common.phy_port.Port`): |
||
261 | Require matching entries to include this as an output port. |
||
262 | A value of :attr:`.Port.OFPP_NONE` indicates no restriction. |
||
263 | """ |
||
264 | 1 | super().__init__() |
|
265 | 1 | self.match = Match() if match is None else match |
|
266 | 1 | self.table_id = table_id |
|
267 | 1 | self.out_port = out_port |
|
268 | |||
269 | |||
270 | 1 | class PortStats(GenericStruct): |
|
271 | """Body of reply to OFPST_PORT request. |
||
272 | |||
273 | If a counter is unsupported, set the field to all ones. |
||
274 | """ |
||
275 | |||
276 | 1 | port_no = UBInt16() |
|
277 | #: Align to 64-bits. |
||
278 | 1 | pad = Pad(6) |
|
279 | 1 | rx_packets = UBInt64() |
|
280 | 1 | tx_packets = UBInt64() |
|
281 | 1 | rx_bytes = UBInt64() |
|
282 | 1 | tx_bytes = UBInt64() |
|
283 | 1 | rx_dropped = UBInt64() |
|
284 | 1 | tx_dropped = UBInt64() |
|
285 | 1 | rx_errors = UBInt64() |
|
286 | 1 | tx_errors = UBInt64() |
|
287 | 1 | rx_frame_err = UBInt64() |
|
288 | 1 | rx_over_err = UBInt64() |
|
289 | 1 | rx_crc_err = UBInt64() |
|
290 | 1 | collisions = UBInt64() |
|
291 | |||
292 | 1 | View Code Duplication | def __init__(self, port_no=None, rx_packets=None, |
0 ignored issues
–
show
|
|||
293 | tx_packets=None, rx_bytes=None, tx_bytes=None, |
||
294 | rx_dropped=None, tx_dropped=None, rx_errors=None, |
||
295 | tx_errors=None, rx_frame_err=None, rx_over_err=None, |
||
296 | rx_crc_err=None, collisions=None): |
||
297 | """Create a PortStats with the optional parameters below. |
||
298 | |||
299 | Args: |
||
300 | port_no (:class:`int`, :class:`~pyof.v0x01.common.phy_port.Port`): |
||
301 | Port number. |
||
302 | rx_packets (int): Number of received packets. |
||
303 | tx_packets (int): Number of transmitted packets. |
||
304 | rx_bytes (int): Number of received bytes. |
||
305 | tx_bytes (int): Number of transmitted bytes. |
||
306 | rx_dropped (int): Number of packets dropped by RX. |
||
307 | tx_dropped (int): Number of packets dropped by TX. |
||
308 | rx_errors (int): Number of receive errors. This is a super-set of |
||
309 | more specific receive errors and should be greater than or |
||
310 | equal to the sum of all rx_*_err values. |
||
311 | tx_errors (int): Number of transmit errors. This is a super-set of |
||
312 | more specific transmit errors and should be greater than or |
||
313 | equal to the sum of all tx_*_err values (none currently |
||
314 | defined). |
||
315 | rx_frame_err (int): Number of frame alignment errors. |
||
316 | rx_over_err (int): Number of packets with RX overrun. |
||
317 | rx_crc_err (int): Number of CRC errors. |
||
318 | collisions (int): Number of collisions. |
||
319 | """ |
||
320 | 1 | super().__init__() |
|
321 | 1 | self.port_no = port_no |
|
322 | 1 | self.rx_packets = rx_packets |
|
323 | 1 | self.tx_packets = tx_packets |
|
324 | 1 | self.rx_bytes = rx_bytes |
|
325 | 1 | self.tx_bytes = tx_bytes |
|
326 | 1 | self.rx_dropped = rx_dropped |
|
327 | 1 | self.tx_dropped = tx_dropped |
|
328 | 1 | self.rx_errors = rx_errors |
|
329 | 1 | self.tx_errors = tx_errors |
|
330 | 1 | self.rx_frame_err = rx_frame_err |
|
331 | 1 | self.rx_over_err = rx_over_err |
|
332 | 1 | self.rx_crc_err = rx_crc_err |
|
333 | 1 | self.collisions = collisions |
|
334 | |||
335 | |||
336 | 1 | class PortStatsRequest(GenericStruct): |
|
337 | """Body for ofp_stats_request of type OFPST_PORT.""" |
||
338 | |||
339 | 1 | port_no = UBInt16() |
|
340 | #: Align to 64-bits. |
||
341 | 1 | pad = Pad(6) |
|
342 | |||
343 | 1 | def __init__(self, port_no=None): |
|
344 | """Create a PortStatsRequest with the optional parameters below. |
||
345 | |||
346 | Args: |
||
347 | port_no (:class:`int`, :class:`~pyof.v0x01.common.phy_port.Port`): |
||
348 | OFPST_PORT message must request statistics either for a single |
||
349 | port (specified in ``port_no``) or for all ports |
||
350 | (if ``port_no`` == :attr:`.Port.OFPP_NONE`). |
||
351 | """ |
||
352 | 1 | super().__init__() |
|
353 | 1 | self.port_no = port_no |
|
354 | |||
355 | |||
356 | 1 | class QueueStats(GenericStruct): |
|
357 | """Implements the reply body of a port_no.""" |
||
358 | |||
359 | 1 | port_no = UBInt16() |
|
360 | #: Align to 32-bits. |
||
361 | 1 | pad = Pad(2) |
|
362 | 1 | queue_id = UBInt32() |
|
363 | 1 | tx_bytes = UBInt64() |
|
364 | 1 | tx_packets = UBInt64() |
|
365 | 1 | tx_errors = UBInt64() |
|
366 | |||
367 | 1 | def __init__(self, port_no=None, queue_id=None, tx_bytes=None, |
|
368 | tx_packets=None, tx_errors=None): |
||
369 | """Create a QueueStats with the optional parameters below. |
||
370 | |||
371 | Args: |
||
372 | port_no (:class:`int`, :class:`~pyof.v0x01.common.phy_port.Port`): |
||
373 | Port Number. |
||
374 | queue_id (int): Queue ID. |
||
375 | tx_bytes (int): Number of transmitted bytes. |
||
376 | tx_packets (int): Number of transmitted packets. |
||
377 | tx_errors (int): Number of packets dropped due to overrun. |
||
378 | """ |
||
379 | 1 | super().__init__() |
|
380 | 1 | self.port_no = port_no |
|
381 | 1 | self.queue_id = queue_id |
|
382 | 1 | self.tx_bytes = tx_bytes |
|
383 | 1 | self.tx_packets = tx_packets |
|
384 | 1 | self.tx_errors = tx_errors |
|
385 | |||
386 | |||
387 | 1 | class QueueStatsRequest(GenericStruct): |
|
388 | """Implements the request body of a ``port_no``.""" |
||
389 | |||
390 | 1 | port_no = UBInt16() |
|
391 | #: Align to 32-bits |
||
392 | 1 | pad = Pad(2) |
|
393 | 1 | queue_id = UBInt32() |
|
394 | |||
395 | 1 | def __init__(self, port_no=None, queue_id=None): |
|
396 | """Create a QueueStatsRequest with the optional parameters below. |
||
397 | |||
398 | Args: |
||
399 | port_no (:class:`int`, :class:`~pyof.v0x01.common.phy_port.Port`): |
||
400 | All ports if :attr:`.Port.OFPP_ALL`. |
||
401 | queue_id (int): All queues if OFPQ_ALL (``0xfffffff``). |
||
402 | """ |
||
403 | 1 | super().__init__() |
|
404 | 1 | self.port_no = port_no |
|
405 | 1 | self.queue_id = queue_id |
|
406 | |||
407 | |||
408 | 1 | View Code Duplication | class TableStats(GenericStruct): |
0 ignored issues
–
show
|
|||
409 | """Body of reply to OFPST_TABLE request.""" |
||
410 | |||
411 | 1 | table_id = UBInt8() |
|
412 | #: Align to 32-bits. |
||
413 | 1 | pad = Pad(3) |
|
414 | 1 | name = Char(length=OFP_MAX_TABLE_NAME_LEN) |
|
415 | 1 | wildcards = UBInt32(enum_ref=FlowWildCards) |
|
416 | 1 | max_entries = UBInt32() |
|
417 | 1 | active_count = UBInt32() |
|
418 | 1 | count_lookup = UBInt64() |
|
419 | 1 | count_matched = UBInt64() |
|
420 | |||
421 | 1 | def __init__(self, table_id=None, name=None, wildcards=None, |
|
422 | max_entries=None, active_count=None, count_lookup=None, |
||
423 | count_matched=None): |
||
424 | """Create a TableStats with the optional parameters below. |
||
425 | |||
426 | Args: |
||
427 | table_id (int): Identifier of table. Lower numbered tables are |
||
428 | consulted first. |
||
429 | name (str): Table name. |
||
430 | wildcards (:class:`~pyof.v0x01.common.flow_match.FlowWildCards`): |
||
431 | Bitmap of OFPFW_* wildcards that are supported by the table. |
||
432 | max_entries (int): Max number of entries supported. |
||
433 | active_count (int): Number of active entries. |
||
434 | count_lookup (int): Number of packets looked up in table. |
||
435 | count_matched (int): Number of packets that hit table. |
||
436 | """ |
||
437 | 1 | super().__init__() |
|
438 | 1 | self.table_id = table_id |
|
439 | 1 | self.name = name |
|
440 | 1 | self.wildcards = wildcards |
|
441 | 1 | self.max_entries = max_entries |
|
442 | 1 | self.active_count = active_count |
|
443 | 1 | self.count_lookup = count_lookup |
|
444 | 1 | self.count_matched = count_matched |
|
445 | |||
446 | |||
447 | 1 | class VendorStats(GenericStruct): |
|
448 | """Vendor extension.""" |
||
449 | |||
450 | 1 | vendor = UBInt32() |
|
451 | 1 | body = BinaryData() |
|
452 | |||
453 | 1 | def __init__(self, vendor=None, body=b''): |
|
454 | """Create instance attributes. |
||
455 | |||
456 | Args: |
||
457 | vendor (int): 32-bit vendor ID. |
||
458 | body (bytes): Vendor-defined body |
||
459 | """ |
||
460 | 1 | super().__init__() |
|
461 | 1 | self.vendor = vendor |
|
462 | 1 | self.body = body |
|
463 | |||
464 | |||
465 | VendorStatsRequest = VendorStats |
||
466 |