Skip to content
This repository was archived by the owner on Apr 22, 2024. It is now read-only.

Commit f4b4631

Browse files
authored
Merge pull request #323 from macartur/adding_multipart_pack_unpack
Adding multipart pack unpack
2 parents 1820155 + 4680331 commit f4b4631

9 files changed

Lines changed: 207 additions & 141 deletions

File tree

pyof/v0x04/controller2switch/multipart_reply.py

Lines changed: 38 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,9 @@
1010
from pyof.foundation.constants import DESC_STR_LEN, SERIAL_NUM_LEN
1111
from pyof.v0x04.common.flow_match import Match
1212
from pyof.v0x04.common.header import Header, Type
13+
from pyof.v0x04.common.port import Port
1314
from pyof.v0x04.controller2switch.common import (Bucket, BucketCounter,
15+
ExperimenterMultipartHeader,
1416
MultipartTypes, TableFeatures)
1517
from pyof.v0x04.controller2switch.meter_mod import (ListOfMeterBandHeader,
1618
MeterBandType, MeterFlags)
@@ -93,8 +95,15 @@ def pack(self, value=None):
9395
buff = self.body
9496
if not value:
9597
value = self.body
96-
if value and hasattr(value, 'pack'):
97-
self.body = BinaryData(value.pack())
98+
99+
if value:
100+
if isinstance(value, (list, FixedTypeList)):
101+
obj = self._get_body_instance()
102+
obj.extend(value)
103+
elif hasattr(value, 'pack'):
104+
obj = value
105+
106+
self.body = obj.pack()
98107

99108
multiparty_packed = super().pack()
100109
self.body = buff
@@ -127,19 +136,37 @@ def _unpack_body(self):
127136

128137
def _get_body_instance(self):
129138
"""Method used to return the body instance."""
130-
pyof_class = self._get_body_class()
131-
if pyof_class is None:
132-
return BinaryData(b'')
133-
else:
134-
return FixedTypeList(pyof_class=pyof_class)
139+
exp_header = ExperimenterMultipartHeader
140+
simple_body = {MultipartTypes.OFPMP_DESC: Desc,
141+
MultipartTypes.OFPMP_GROUP_FEATURES: GroupFeatures,
142+
MultipartTypes.OFPMP_METER_FEATURES: MeterFeatures,
143+
MultipartTypes.OFPMP_EXPERIMENTER: exp_header}
144+
145+
array_of_bodies = {MultipartTypes.OFPMP_FLOW: FlowStats,
146+
MultipartTypes.OFPMP_AGGREGATE: AggregateStatsReply,
147+
MultipartTypes.OFPMP_TABLE: TableStats,
148+
MultipartTypes.OFPMP_PORT_STATS: PortStats,
149+
MultipartTypes.OFPMP_QUEUE: QueueStats,
150+
MultipartTypes.OFPMP_GROUP: GroupStats,
151+
MultipartTypes.OFPMP_GROUP_DESC: GroupDescStats,
152+
MultipartTypes.OFPMP_METER: MeterStats,
153+
MultipartTypes.OFPMP_METER_CONFIG: MeterConfig,
154+
MultipartTypes.OFPMP_TABLE_FEATURES: TableFeatures,
155+
MultipartTypes.OFPMP_PORT_DESC: Port}
135156

136-
def _get_body_class(self):
137-
"""Method used to return the body class using the multipart_type."""
138157
if isinstance(self.multipart_type, (int, UBInt16)):
139158
self.multipart_type = self.multipart_type.enum_ref(
140159
self.multipart_type.value)
141-
if self.multipart_type.value == 12:
142-
return TableFeatures
160+
161+
pyof_class = simple_body.get(self.multipart_type, None)
162+
if pyof_class:
163+
return pyof_class()
164+
165+
array_of_class = array_of_bodies.get(self.multipart_type, None)
166+
if array_of_class:
167+
return FixedTypeList(pyof_class=pyof_class)
168+
169+
return BinaryData(b'')
143170

144171

145172
# MultipartReply Body

pyof/v0x04/controller2switch/multipart_request.py

Lines changed: 87 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,18 +5,20 @@
55

66
# Local source tree imports
77
from pyof.foundation.base import GenericMessage, GenericStruct
8-
from pyof.foundation.basic_types import (BinaryData, Pad,
8+
from pyof.foundation.basic_types import (BinaryData, FixedTypeList, Pad,
99
UBInt8, UBInt16, UBInt32, UBInt64)
1010
from pyof.v0x04.common.flow_match import Match
1111
from pyof.v0x04.common.header import Header, Type
12-
from pyof.v0x04.controller2switch.common import MultipartTypes
12+
from pyof.v0x04.controller2switch.common import (ExperimenterMultipartHeader,
13+
MultipartTypes, TableFeatures)
1314

1415
# Third-party imports
1516

16-
1717
__all__ = ('MultipartRequest', 'MultipartRequestFlags',
18-
'AggregateStatsRequest', 'FlowStatsRequest', 'PortStatsRequest',
19-
'QueueStatsRequest', 'GroupStatsRequest', 'MeterMultipartRequest')
18+
'AggregateStatsRequest', 'FlowStatsRequest',
19+
'PortStatsRequest', 'QueueStatsRequest',
20+
'GroupStatsRequest', 'MeterMultipartRequest',
21+
'TableFeatures')
2022

2123
# Enum
2224

@@ -63,8 +65,87 @@ def __init__(self, xid=None, multipart_type=None, flags=None, body=b''):
6365
self.flags = flags
6466
self.body = body
6567

68+
def pack(self, value=None):
69+
"""Pack a MultipartRequest using the object's attributes.
70+
71+
This method will pack the attribute body and multipart_type before pack
72+
the MultipartRequest object, then will return this struct as a
73+
binary data.
74+
75+
Returns:
76+
multiparty_packed (bytes): Binary data with MultipartRequest
77+
packed.
78+
"""
79+
buff = self.body
80+
if not value:
81+
value = self.body
82+
83+
if value:
84+
if isinstance(value, (list, FixedTypeList)):
85+
obj = self._get_body_instance()
86+
obj.extend(value)
87+
elif hasattr(value, 'pack'):
88+
obj = value
89+
90+
self.body = obj.pack()
91+
92+
multiparty_packed = super().pack()
93+
self.body = buff
94+
95+
return multiparty_packed
96+
97+
def unpack(self, buff, offset=0):
98+
"""Unpack a binary message into this object's attributes.
99+
100+
Unpack the binary value *buff* and update this object attributes based
101+
on the results. It is an inplace method and it receives the binary data
102+
of the message **without the header**.
103+
104+
This class' unpack method is like the :meth:`.GenericMessage.unpack`
105+
one, except for the ``body`` attribute which has its type determined
106+
by the ``multipart_type`` attribute.
107+
108+
Args:
109+
buff (bytes): Binary data package to be unpacked, without the
110+
header.
111+
"""
112+
super().unpack(buff[offset:])
113+
self._unpack_body()
114+
115+
def _unpack_body(self):
116+
"""Unpack `body` replace it by the result."""
117+
obj = self._get_body_instance()
118+
obj.unpack(self.body.value)
119+
self.body = obj
120+
121+
def _get_body_instance(self):
122+
"""Method used to return the body instance."""
123+
simple_body = {
124+
MultipartTypes.OFPMP_FLOW: FlowStatsRequest,
125+
MultipartTypes.OFPMP_AGGREGATE: AggregateStatsRequest,
126+
MultipartTypes.OFPMP_PORT_STATS: PortStatsRequest,
127+
MultipartTypes.OFPMP_QUEUE: QueueStatsRequest,
128+
MultipartTypes.OFPMP_GROUP: GroupStatsRequest,
129+
MultipartTypes.OFPMP_METER: MeterMultipartRequest,
130+
MultipartTypes.OFPMP_EXPERIMENTER: ExperimenterMultipartHeader
131+
}
132+
133+
array_of_bodies = {MultipartTypes.OFPMP_TABLE_FEATURES: TableFeatures}
134+
135+
if isinstance(self.multipart_type, (int, UBInt16)):
136+
self.multipart_type = self.multipart_type.enum_ref(
137+
self.multipart_type.value)
138+
139+
pyof_class = simple_body.get(self.multipart_type, None)
140+
if pyof_class:
141+
return pyof_class()
142+
143+
array_of_class = array_of_bodies.get(self.multipart_type, None)
144+
if array_of_class:
145+
return FixedTypeList(pyof_class=array_of_class)
146+
147+
return BinaryData(b'')
66148

67-
# MultipartRequest body
68149

69150
class AggregateStatsRequest(GenericStruct):
70151
"""Body for ofp_stats_request of type OFPST_AGGREGATE."""

setup.cfg

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,6 @@ ignore = D105,D203,D213,I0011
1010
# Conflicts with D211: No blank lines allowed before class docstring
1111
# D213: Should be ignored by default, but sometimes it is not
1212
# I0011: I0011 Locally disabling no-member (E1101) [pylint]
13+
14+
[isort]
15+
known_first_party = pyof

tests/v0x04/test_common/test_table_feature.py

Lines changed: 0 additions & 41 deletions
This file was deleted.

tests/v0x04/test_controller2switch/test_meter_features.py

Lines changed: 0 additions & 30 deletions
This file was deleted.

tests/v0x04/test_controller2switch/test_meter_stats.py

Lines changed: 0 additions & 39 deletions
This file was deleted.
Lines changed: 23 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,13 @@
11
"""MultipartReply message test."""
22

33
from pyof.v0x04.controller2switch.common import MultipartTypes
4-
from pyof.v0x04.controller2switch.multipart_reply import (MultipartReply,
4+
from pyof.v0x04.controller2switch.multipart_reply import (Desc, MultipartReply,
55
MultipartReplyFlags)
6-
76
from tests.v0x04.test_struct import TestStruct
87

98

10-
class TestTableFeatures(TestStruct):
11-
"""Test TableFeatures."""
9+
class TestMultipartReply(TestStruct):
10+
"""Test MultipartReply."""
1211

1312
@classmethod
1413
def setUpClass(cls):
@@ -17,5 +16,24 @@ def setUpClass(cls):
1716
super().set_message(MultipartReply, xid=16,
1817
multipart_type=MultipartTypes.OFPMP_METER_CONFIG,
1918
flags=MultipartReplyFlags.OFPMPF_REPLY_MORE,
20-
body=b'this is a test')
19+
body='')
2120
super().set_minimum_size(16)
21+
22+
@staticmethod
23+
def get_attributes(multipart_type=MultipartTypes.OFPMP_DESC,
24+
flags=MultipartReplyFlags.OFPMPF_REPLY_MORE,
25+
body=''):
26+
"""Method used to return a dict with instance paramenters."""
27+
return {'xid': 32, 'multipart_type': multipart_type, 'flags': flags,
28+
'body': body}
29+
30+
def test_pack_unpack_desc(self):
31+
"""Testing multipart_type with OFPMP_DESC."""
32+
instances = Desc(mfr_desc="MANUFACTURER DESCRIPTION",
33+
hw_desc="HARDWARE DESCRIPTION",
34+
sw_desc="SOFTWARE DESCRIPTION",
35+
serial_num="SERIAL NUMBER",
36+
dp_desc="DATAPATH DESCRIPTION")
37+
options = TestMultipartReply.get_attributes(
38+
multipart_type=MultipartTypes.OFPMP_DESC, body=instances)
39+
self._test_pack_unpack(**options)

0 commit comments

Comments
 (0)