File: multipart_reply.py

package info (click to toggle)
python-openflow 2021.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 1,224 kB
  • sloc: python: 6,906; sh: 4; makefile: 4
file content (748 lines) | stat: -rw-r--r-- 25,403 bytes parent folder | download | duplicates (3)
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
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
"""Controller replying state from datapath."""

# System imports
from enum import Enum

# Local source tree imports
from pyof.foundation.base import GenericBitMask, GenericMessage, GenericStruct
from pyof.foundation.basic_types import (
    BinaryData, Char, FixedTypeList, Pad, UBInt8, UBInt16, UBInt32, UBInt64)
from pyof.foundation.constants import DESC_STR_LEN, SERIAL_NUM_LEN
from pyof.v0x04.common.flow_instructions import ListOfInstruction
from pyof.v0x04.common.flow_match import Match
from pyof.v0x04.common.header import Header, Type
from pyof.v0x04.common.port import Port
from pyof.v0x04.controller2switch.common import (
    Bucket, ExperimenterMultipartHeader, ListOfBucketCounter, MultipartType,
    TableFeatures)
from pyof.v0x04.controller2switch.meter_mod import (
    ListOfMeterBandHeader, MeterBandType, MeterFlags)

# Third-party imports


__all__ = ('MultipartReply', 'MultipartReplyFlags', 'AggregateStatsReply',
           'Desc', 'FlowStats', 'PortStats', 'QueueStats', 'GroupDescStats',
           'GroupFeatures', 'GroupStats', 'MeterConfig', 'MeterFeatures',
           'BandStats', 'ListOfBandStats', 'MeterStats', 'GroupCapabilities',
           'TableStats')

# Enum


class MultipartReplyFlags(Enum):
    """Flags for MultipartReply."""

    #: More replies to follow.
    OFPMPF_REPLY_MORE = 1 << 0


class GroupCapabilities(GenericBitMask):
    """Group configuration flags."""

    #: Support weight for select groups.
    OFPGFC_SELECT_WEIGHT = 1 << 0
    #: Support liveness for select groups.
    OFPGFC_SELECT_LIVENESS = 1 << 1
    #: Support chaining groups.
    OFPGFC_CHAINING = 1 << 2
    #: Chack chaining for loops and delete.
    OFPGFC_CHAINING_CHECKS = 1 << 3

# Classes


class MultipartReply(GenericMessage):
    """Reply datapath state.

    While the system is running, the controller may reply state from the
    datapath using the OFPT_MULTIPART_REPLY message.
    """

    #: Openflow :class:`~pyof.v0x04.common.header.Header`
    header = Header(message_type=Type.OFPT_MULTIPART_REPLY)
    #: One of the OFPMP_* constants.
    multipart_type = UBInt16(enum_ref=MultipartType)
    #: OFPMPF_REPLY_* flags.
    flags = UBInt16()
    #: Padding
    pad = Pad(4)
    #: Body of the reply
    body = BinaryData()

    def __init__(self, xid=None, multipart_type=None, flags=None, body=b''):
        """Create a MultipartReply with the optional parameters below.

        Args:
            xid (int): xid to the header.
            multipart_type (int): One of the OFPMP_* constants.
            flags (int): OFPMPF_REPLY_* flags.
            body (bytes): Body of the reply.

        """
        super().__init__(xid)
        self.multipart_type = multipart_type
        self.flags = flags
        self.body = body

    def pack(self, value=None):
        """Pack a StatsReply using the object's attributes.

        This method will pack the attribute body and multipart_type before pack
        the StatsReply object, then will return this struct as a binary data.

        Returns:
            stats_reply_packed (bytes): Binary data with StatsReply packed.

        """
        buff = self.body
        if not value:
            value = self.body

        if value:
            if isinstance(value, (list, FixedTypeList)):
                obj = self._get_body_instance()
                obj.extend(value)
            elif hasattr(value, 'pack'):
                obj = value

            self.body = obj.pack()

        multipart_packed = super().pack()
        self.body = buff

        return multipart_packed

    def unpack(self, buff, offset=0):
        """Unpack a binary message into this object's attributes.

        Unpack the binary value *buff* and update this object attributes based
        on the results. It is an inplace method and it receives the binary data
        of the message **without the header**.

        This class' unpack method is like the :meth:`.GenericMessage.unpack`
        one, except for the ``body`` attribute which has its type determined
        by the ``multipart_type`` attribute.

        Args:
            buff (bytes): Binary data package to be unpacked, without the
                header.

        """
        super().unpack(buff[offset:])
        self._unpack_body()

    def _unpack_body(self):
        """Unpack `body` replace it by the result."""
        obj = self._get_body_instance()
        obj.unpack(self.body.value)
        self.body = obj

    def _get_body_instance(self):
        """Return the body instance."""
        exp_header = ExperimenterMultipartHeader
        simple_body = {MultipartType.OFPMP_DESC: Desc,
                       MultipartType.OFPMP_GROUP_FEATURES: GroupFeatures,
                       MultipartType.OFPMP_METER_FEATURES: MeterFeatures,
                       MultipartType.OFPMP_EXPERIMENTER: exp_header}

        array_of_bodies = {MultipartType.OFPMP_FLOW: FlowStats,
                           MultipartType.OFPMP_AGGREGATE: AggregateStatsReply,
                           MultipartType.OFPMP_TABLE: TableStats,
                           MultipartType.OFPMP_PORT_STATS: PortStats,
                           MultipartType.OFPMP_QUEUE: QueueStats,
                           MultipartType.OFPMP_GROUP: GroupStats,
                           MultipartType.OFPMP_GROUP_DESC: GroupDescStats,
                           MultipartType.OFPMP_METER: MeterStats,
                           MultipartType.OFPMP_METER_CONFIG: MeterConfig,
                           MultipartType.OFPMP_TABLE_FEATURES: TableFeatures,
                           MultipartType.OFPMP_PORT_DESC: Port}

        if isinstance(self.multipart_type, UBInt16):
            self.multipart_type = self.multipart_type.enum_ref(
                self.multipart_type.value)

        pyof_class = simple_body.get(self.multipart_type, None)
        if pyof_class:
            return pyof_class()

        array_of_class = array_of_bodies.get(self.multipart_type, None)
        if array_of_class:
            return FixedTypeList(pyof_class=array_of_class)

        return BinaryData(b'')


# MultipartReply Body

class AggregateStatsReply(GenericStruct):
    """Body of reply to OFPMP_AGGREGATE request."""

    #: Number of packets in flows.
    packet_count = UBInt64()
    #: Number of bytes in flows.
    byte_count = UBInt64()
    #: Number of flows.
    flow_count = UBInt32()
    #: Align to 64 bits
    pad = Pad(4)

    def __init__(self, packet_count=None, byte_count=None, flow_count=None):
        """Create a AggregateStatsReply with the optional parameters below.

        Args:
            packet_count (int): Number of packets in flows
            byte_count (int):   Number of bytes in flows
            flow_count (int):   Number of flows

        """
        super().__init__()
        self.packet_count = packet_count
        self.byte_count = byte_count
        self.flow_count = flow_count


class Desc(GenericStruct):
    """Information available from the OFPST_DESC stats request.

    Information about the switch manufacturer, hardware revision, software
    revision, serial number and a description field.
    """

    #: Manufacturer description
    mfr_desc = Char(length=DESC_STR_LEN)
    #: Hardware description
    hw_desc = Char(length=DESC_STR_LEN)
    #: Software description
    sw_desc = Char(length=DESC_STR_LEN)
    #: Serial number
    serial_num = Char(length=SERIAL_NUM_LEN)
    #: Datapath description
    dp_desc = Char(length=DESC_STR_LEN)

    def __init__(self, mfr_desc=None, hw_desc=None, sw_desc=None,
                 serial_num=None, dp_desc=None):
        """Create a Desc with the optional parameters below.

        Args:
            mfr_desc (str): Manufacturer description
            hw_desc (str): Hardware description
            sw_desc (str): Software description
            serial_num (str): Serial number
            dp_desc (str): Datapath description

        """
        super().__init__()
        self.mfr_desc = mfr_desc
        self.hw_desc = hw_desc
        self.sw_desc = sw_desc
        self.serial_num = serial_num
        self.dp_desc = dp_desc


class FlowStats(GenericStruct):
    """Body of reply to OFPST_FLOW request."""

    length = UBInt16()
    table_id = UBInt8()
    #: Align to 32 bits.
    pad = Pad(1)
    duration_sec = UBInt32()
    duration_nsec = UBInt32()
    priority = UBInt16()
    idle_timeout = UBInt16()
    hard_timeout = UBInt16()
    flags = UBInt16()
    #: Align to 64-bits
    pad2 = Pad(4)
    cookie = UBInt64()
    packet_count = UBInt64()
    byte_count = UBInt64()
    match = Match()
    instructions = ListOfInstruction()

    def __init__(self, length=None, table_id=None, duration_sec=None,
                 duration_nsec=None, priority=None, idle_timeout=None,
                 hard_timeout=None, flags=None, cookie=None, packet_count=None,
                 byte_count=None, match=None, instructions=None):
        """Create a FlowStats with the optional parameters below.

        Args:
            length (int): Length of this entry.
            table_id (int): ID of table flow came from.
            duration_sec (int): Time flow has been alive in seconds.
            duration_nsec (int): Time flow has been alive in nanoseconds in
                addition to duration_sec.
            priority (int): Priority of the entry. Only meaningful when this
                is not an exact-match entry.
            idle_timeout (int): Number of seconds idle before expiration.
            hard_timeout (int): Number of seconds before expiration.
            cookie (int): Opaque controller-issued identifier.
            packet_count (int): Number of packets in flow.
            byte_count (int): Number of bytes in flow.
            match (~pyof.v0x04.common.flow_match.Match): Description of fields.

        """
        super().__init__()
        self.length = length
        self.table_id = table_id
        self.duration_sec = duration_sec
        self.duration_nsec = duration_nsec
        self.priority = priority
        self.idle_timeout = idle_timeout
        self.hard_timeout = hard_timeout
        self.flags = flags
        self.cookie = cookie
        self.packet_count = packet_count
        self.byte_count = byte_count
        self.match = match
        self.instructions = instructions or []

    def unpack(self, buff, offset=0):
        """Unpack a binary message into this object's attributes.

        Pass the correct length for list unpacking.

        Args:
            buff (bytes): Binary data package to be unpacked.
            offset (int): Where to begin unpacking.

        """
        unpack_length = UBInt16()
        unpack_length.unpack(buff, offset)
        super().unpack(buff[:offset+unpack_length], offset)


class PortStats(GenericStruct):
    """Body of reply to OFPST_PORT request.

    If a counter is unsupported, set the field to all ones.
    """

    port_no = UBInt32()
    #: Align to 64-bits.
    pad = Pad(4)
    rx_packets = UBInt64()
    tx_packets = UBInt64()
    rx_bytes = UBInt64()
    tx_bytes = UBInt64()
    rx_dropped = UBInt64()
    tx_dropped = UBInt64()
    rx_errors = UBInt64()
    tx_errors = UBInt64()
    rx_frame_err = UBInt64()
    rx_over_err = UBInt64()
    rx_crc_err = UBInt64()
    collisions = UBInt64()
    duration_sec = UBInt32()
    duration_nsec = UBInt32()

    def __init__(self, port_no=None, rx_packets=None,
                 tx_packets=None, rx_bytes=None, tx_bytes=None,
                 rx_dropped=None, tx_dropped=None, rx_errors=None,
                 tx_errors=None, rx_frame_err=None, rx_over_err=None,
                 rx_crc_err=None, collisions=None, duration_sec=None,
                 duration_nsec=None):
        """Create a PortStats with the optional parameters below.

        Args:
            port_no (:class:`int`, :class:`~pyof.v0x04.common.port.Port`):
                Port number.
            rx_packets (int): Number of received packets.
            tx_packets (int): Number of transmitted packets.
            rx_bytes (int): Number of received bytes.
            tx_bytes (int): Number of transmitted bytes.
            rx_dropped (int): Number of packets dropped by RX.
            tx_dropped (int): Number of packets dropped by TX.
            rx_errors (int): Number of receive errors. This is a super-set of
                more specific receive errors and should be greater than or
                equal to the sum of all rx_*_err values.
            tx_errors (int): Number of transmit errors.  This is a super-set of
                more specific transmit errors and should be greater than or
                equal to the sum of all tx_*_err values (none currently
                defined).
            rx_frame_err (int): Number of frame alignment errors.
            rx_over_err (int): Number of packets with RX overrun.
            rx_crc_err (int): Number of CRC errors.
            collisions (int): Number of collisions.
            duration_sec (int): Time port has been alive in seconds
            duration_nsec (int): Time port has been alive in nanoseconds beyond
                duration_sec

        """
        super().__init__()
        self.port_no = port_no
        self.rx_packets = rx_packets
        self.tx_packets = tx_packets
        self.rx_bytes = rx_bytes
        self.tx_bytes = tx_bytes
        self.rx_dropped = rx_dropped
        self.tx_dropped = tx_dropped
        self.rx_errors = rx_errors
        self.tx_errors = tx_errors
        self.rx_frame_err = rx_frame_err
        self.rx_over_err = rx_over_err
        self.rx_crc_err = rx_crc_err
        self.collisions = collisions
        self.duration_sec = duration_sec
        self.duration_nsec = duration_nsec


class QueueStats(GenericStruct):
    """Implements the reply body of a port_no."""

    port_no = UBInt32()
    queue_id = UBInt32()
    tx_bytes = UBInt64()
    tx_packets = UBInt64()
    tx_errors = UBInt64()
    duration_sec = UBInt32()
    duration_nsec = UBInt32()

    def __init__(self, port_no=None, queue_id=None, tx_bytes=None,
                 tx_packets=None, tx_errors=None, duration_sec=None,
                 duration_nsec=None):
        """Create a QueueStats with the optional parameters below.

        Args:
            port_no (:class:`int`, :class:`~pyof.v0x04.common.port.Port`):
                Port Number.
            queue_id (int): Queue ID.
            tx_bytes (int): Number of transmitted bytes.
            tx_packets (int): Number of transmitted packets.
            tx_errors (int): Number of packets dropped due to overrun.
            duration_sec (int): Time queue has been alive in seconds.
            duration_nsec (int): Time queue has been alive in nanoseconds
                beyond duration_sec.

        """
        super().__init__()
        self.port_no = port_no
        self.queue_id = queue_id
        self.tx_bytes = tx_bytes
        self.tx_packets = tx_packets
        self.tx_errors = tx_errors
        self.duration_sec = duration_sec
        self.duration_nsec = duration_nsec


class GroupDescStats(GenericStruct):
    """Body of reply to OFPMP_GROUP_DESC request."""

    length = UBInt16()
    group_type = UBInt8()
    #: Pad to 64 bits.
    pad = Pad(1)
    group_id = UBInt32()
    buckets = FixedTypeList(Bucket)

    def __init__(self, length=None, group_type=None, group_id=None,
                 buckets=None):
        """Create a GroupDescStats with the optional parameters below.

        Args:
            length (int): Length of this entry.
            group_type (|GroupType_v0x04|): One of OFPGT_*.
            group_id (int): Group identifier.
            buckets (|ListOfBuckets_v0x04|): List of buckets in group.

        """
        super().__init__()
        self.length = length
        self.group_type = group_type
        self.group_id = group_id
        self.buckets = buckets


class GroupFeatures(GenericStruct):
    """Body of reply to OFPMP_GROUP_FEATURES request.Group features."""

    types = UBInt32()
    capabilities = UBInt32(enum_ref=GroupCapabilities)
    max_groups1 = UBInt32()
    max_groups2 = UBInt32()
    max_groups3 = UBInt32()
    max_groups4 = UBInt32()
    actions1 = UBInt32()
    actions2 = UBInt32()
    actions3 = UBInt32()
    actions4 = UBInt32()

    def __init__(self, types=None, capabilities=None, max_groups1=None,
                 max_groups2=None, max_groups3=None, max_groups4=None,
                 actions1=None, actions2=None, actions3=None, actions4=None):
        """Create a GroupFeatures with the optional parameters below.

        Args:
            types: Bitmap of OFPGT_* values supported.
            capabilities: Bitmap of OFPGFC_* capability supported.
            max_groups: 4-position array; Maximum number of groups for each
                type.
            actions: 4-position array; Bitmaps of OFPAT_* that are supported.

        """
        super().__init__()
        self.types = types
        self.capabilities = capabilities
        self.max_groups1 = max_groups1
        self.max_groups2 = max_groups2
        self.max_groups3 = max_groups3
        self.max_groups4 = max_groups4
        self.actions1 = actions1
        self.actions2 = actions2
        self.actions3 = actions3
        self.actions4 = actions4


class GroupStats(GenericStruct):
    """Body of reply to OFPMP_GROUP request."""

    length = UBInt16()
    #: Align to 64 bits.
    pad = Pad(2)
    group_id = UBInt32()
    ref_count = UBInt32()
    #: Align to 64 bits.
    pad2 = Pad(4)
    packet_count = UBInt64()
    byte_count = UBInt64()
    duration_sec = UBInt32()
    duration_nsec = UBInt32()
    bucket_stats = ListOfBucketCounter()

    def __init__(self, length=None, group_id=None, ref_count=None,
                 packet_count=None, byte_count=None, duration_sec=None,
                 duration_nsec=None, bucket_stats=None):
        """Create a GroupStats with the optional parameters below.

        Args:
            length: Length of this entry
            group_id: Group identifier
            ref_count: Number of flows or groups that directly forward
                to this group.
            packet_count: Number of packets processed by group
            byte_count: Number of bytes processed by group
            duration_sec: Time group has been alive in seconds
            duration_nsec: Time group has been alive in nanoseconds
            bucket_stats: List of stats of group buckets

        """
        super().__init__()
        self.length = length
        self.group_id = group_id
        self.ref_count = ref_count
        self.packet_count = packet_count
        self.byte_count = byte_count
        self.duration_sec = duration_sec
        self.duration_nsec = duration_nsec
        self.bucket_stats = bucket_stats


class MeterConfig(GenericStruct):
    """MeterConfig is a class to represent ofp_meter_config structure.

    Body of reply to OFPMP_METER_CONFIG request.
    """

    # Length of this entry.
    length = UBInt16()
    # All OFPMC_* that apply.
    flags = UBInt16(enum_ref=MeterFlags)
    # Meter instance or OFPM_ALL .
    meter_id = UBInt32()
    # The bands length is inferred from the length field.
    bands = ListOfMeterBandHeader()

    def __init__(self, flags=MeterFlags.OFPMF_STATS, meter_id=None,
                 bands=None):
        """Create a MeterConfig with the optional parameters below.

        Args:
            flags (|MeterFlags_v0x04|):
                Meter configuration flags.The default value is
                MeterFlags.OFPMF_STATS
            meter_id (|Meter_v0x04|):
                Meter Indentify.The value Meter.OFPM_ALL is used to
                refer to all Meters on the switch.
            bands(list): List of MeterBandHeader instances.

        """
        super().__init__()
        self.flags = flags
        self.meter_id = meter_id
        self.bands = bands if bands else []


class MeterFeatures(GenericStruct):
    """Body of reply to OFPMP_METER_FEATURES request. Meter features."""

    max_meter = UBInt32()
    band_types = UBInt32(enum_ref=MeterBandType)
    capabilities = UBInt32(enum_ref=MeterFlags)
    max_bands = UBInt8()
    max_color = UBInt8()
    pad = Pad(2)

    def __init__(self, max_meter=None, band_types=None, capabilities=None,
                 max_bands=None, max_color=None):
        """Create a MeterFeatures with the optional parameters below.

        Args:
            max_meter(int): Maximum number of meters.
            band_types (|MeterBandType_v0x04|):
                Bitmaps of OFPMBT_* values supported.
            capabilities (|MeterFlags_v0x04|): Bitmaps of "ofp_meter_flags".
            max_bands(int): Maximum bands per meters
            max_color(int): Maximum color value

        """
        super().__init__()
        self.max_meter = max_meter
        self.band_types = band_types
        self.capabilities = capabilities
        self.max_bands = max_bands
        self.max_color = max_color


class BandStats(GenericStruct):
    """Band  Statistics.

    Statistics for each meter band.
    """

    packet_band_count = UBInt64()
    byte_band_count = UBInt64()

    def __init__(self, packet_band_count=None, byte_band_count=None):
        """Create a BandStats with the optional parameters below.

        Args:
            packet_band_count(int): Number of packets in band.
            byte_band_count(int):   Number of bytes in band.

        """
        super().__init__()
        self.packet_band_count = packet_band_count
        self.byte_band_count = byte_band_count


class ListOfBandStats(FixedTypeList):
    """List of BandStats.

    Represented by instances of BandStats.
    """

    def __init__(self, items=None):
        """Create a ListOfBandStats with the optional parameters below.

        Args:
            items (|BandStats_v0x04|): Instance or a list of instances.

        """
        super().__init__(pyof_class=BandStats, items=items)


class MeterStats(GenericStruct):
    """Meter Statistics.

    Body of reply to OFPMP_METER request.
    """

    meter_id = UBInt32()
    length = UBInt16()
    pad = Pad(6)
    flow_count = UBInt32()
    packet_in_count = UBInt64()
    byte_in_count = UBInt64()
    duration_sec = UBInt32()
    duration_nsec = UBInt32()
    band_stats = ListOfBandStats()

    def __init__(self, meter_id=None, flow_count=None,
                 packet_in_count=None, byte_in_count=None, duration_sec=None,
                 duration_nsec=None, band_stats=None):
        """Create a MeterStats with the optional parameters below.

        Args:
            meter_id (|Meter_v0x04|):  Meter instance.
            flow_count(int):      Number of flows bound to meter.
            packet_in_count(int): Number of packets in input.
            byte_in_count(int):   Number of bytes in input.
            duration_sec(int):    Time meter has been alive in seconds.
            duration_nsec(int):   Time meter has been alive in
                                  nanoseconds beyond duration_sec.
            band_stats(list):     Instances of BandStats

        """
        super().__init__()
        self.meter_id = meter_id
        self.flow_count = flow_count
        self.packet_in_count = packet_in_count
        self.byte_in_count = byte_in_count
        self.duration_sec = duration_sec
        self.duration_nsec = duration_nsec
        self.band_stats = band_stats if band_stats else []
        self.update_length()

    def update_length(self):
        """Update length attribute with current struct length."""
        self.length = self.get_size()

    def pack(self, value=None):
        """Pack method used to update the length of instance and packing.

        Args:
            value: Structure to be packed.

        """
        self.update_length()
        return super().pack(value)

    def unpack(self, buff=None, offset=0):
        """Unpack *buff* into this object.

        This method will convert a binary data into a readable value according
        to the attribute format.

        Args:
            buff (bytes): Binary buffer.
            offset (int): Where to begin unpacking.

        Raises:
            :exc:`~.exceptions.UnpackException`: If unpack fails.

        """
        length = UBInt16()
        length.unpack(buff, offset)

        length.unpack(buff, offset=offset+MeterStats.meter_id.get_size())
        super().unpack(buff[:offset+length.value], offset=offset)


class TableStats(GenericStruct):
    """Body of reply to OFPST_TABLE request."""

    table_id = UBInt8()
    #: Align to 32-bits.
    pad = Pad(3)
    active_count = UBInt32()
    lookup_count = UBInt64()
    matched_count = UBInt64()

    def __init__(self, table_id=None, active_count=None, lookup_count=None,
                 matched_count=None):
        """Create a TableStats with the optional parameters below.

        Args:
            table_id (int): Identifier of table.  Lower numbered tables are
                consulted first.
            active_count (int): Number of active entries.
            lookup_count (int): Number of packets looked up in table.
            matched_count (int): Number of packets that hit table.

        """
        super().__init__()
        self.table_id = table_id
        self.active_count = active_count
        self.lookup_count = lookup_count
        self.matched_count = matched_count