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