VOL-1308 - OpenOMCI decode failure for Serial Number in Circuit Pack(#6) and ONU-G(#256)
Added a new StrCompoundField that supports a composite aggregation of 2 fields to decode the serial number of OMCI that is 4 ascii and 4 binary digits. Representation is a 12 digit value: AAAAxxxxxxxx
Change-Id: I4934cae4746883263453f98080f5fd767d366631
diff --git a/tests/utests/voltha/extensions/omci/test_omci.py b/tests/utests/voltha/extensions/omci/test_omci.py
index de851c3..4270399 100644
--- a/tests/utests/voltha/extensions/omci/test_omci.py
+++ b/tests/utests/voltha/extensions/omci/test_omci.py
@@ -74,7 +74,7 @@
e = CircuitPack(
number_of_ports=4,
- serial_number='123-123A',
+ serial_number='BCMX31323334', # serial number is 4 ascii + 4 hex. 8 octets on the wire
version='a1c12fba91de',
vendor_id='BCM',
total_tcont_buffer_number=128
@@ -82,11 +82,11 @@
# Full object
self.assertEqual(e.serialize(),
- '\x04123-123Aa1c12fba91de\x00\x00BCM\x00\x80')
+ '\x04BCMX1234a1c12fba91de\x00\x00BCM\x00\x80')
# Explicit mask with valid values
self.assertEqual(e.serialize(0x800), 'BCM\x00')
- self.assertEqual(e.serialize(0x6800), '\x04123-123ABCM\x00')
+ self.assertEqual(e.serialize(0x6800), '\x04BCMX1234BCM\x00')
# Referring to an unfilled field is regarded as error
self.assertRaises(OmciUninitializedFieldError, e.serialize, 0xc00)
diff --git a/tests/utests/voltha/extensions/omci/test_omci_cc.py b/tests/utests/voltha/extensions/omci/test_omci_cc.py
index 102e017..de80614 100644
--- a/tests/utests/voltha/extensions/omci/test_omci_cc.py
+++ b/tests/utests/voltha/extensions/omci/test_omci_cc.py
@@ -655,6 +655,24 @@
self.assertTrue(True, 'Truth is the truth')
+ def test_rx_decode_onu_g(self):
+ self.setup_one_of_each()
+
+ omci_cc = self.onu_handler.omci_cc
+ omci_cc.enabled = True
+ snapshot = self._snapshot_stats()
+
+ msg = '001e2e0a0002000001000000e000424657530000' \
+ '0000000000000000000000324246575300107496' \
+ '00000028e7fb4a91'
+
+ omci_cc.receive_message(hex2raw(msg))
+
+ # Note: No counter increments
+ self.assertEqual(omci_cc.rx_frames, snapshot['rx_frames'] + 1)
+ self.assertEqual(omci_cc.rx_unknown_me, snapshot['rx_unknown_me'])
+ self.assertEqual(omci_cc.rx_unknown_tid, snapshot['rx_unknown_tid'] + 1)
+ self.assertEqual(omci_cc.rx_onu_frames, snapshot['rx_onu_frames'])
if __name__ == '__main__':
main()
diff --git a/voltha/extensions/omci/omci_defs.py b/voltha/extensions/omci/omci_defs.py
index 5bd6841..a4f7363 100644
--- a/voltha/extensions/omci/omci_defs.py
+++ b/voltha/extensions/omci/omci_defs.py
@@ -14,9 +14,6 @@
# limitations under the License.
#
from enum import Enum, IntEnum
-from scapy.fields import PadField
-from scapy.packet import Raw
-
class OmciUninitializedFieldError(Exception):
pass
@@ -25,23 +22,6 @@
class OmciInvalidTypeError(Exception):
pass
-
-class FixedLenField(PadField):
- """
- This Pad field limits parsing of its content to its size
- """
- def __init__(self, fld, align, padwith='\x00'):
- super(FixedLenField, self).__init__(fld, align, padwith)
-
- def getfield(self, pkt, s):
- remain, val = self._fld.getfield(pkt, s[:self._align])
- if isinstance(val.payload, Raw) and \
- not val.payload.load.replace(self._padwith, ''):
- # raw payload is just padding
- val.remove_payload()
- return remain + s[self._align:], val
-
-
def bitpos_from_mask(mask, lsb_pos=0, increment=1):
"""
Turn a decimal value (bitmask) into a list of indices where each
diff --git a/voltha/extensions/omci/omci_entities.py b/voltha/extensions/omci/omci_entities.py
index bf47f8e..b523ebf 100644
--- a/voltha/extensions/omci/omci_entities.py
+++ b/voltha/extensions/omci/omci_entities.py
@@ -25,9 +25,9 @@
from voltha.extensions.omci.omci_defs import OmciUninitializedFieldError, \
AttributeAccess, OmciNullPointer, EntityOperations, OmciInvalidTypeError
+from voltha.extensions.omci.omci_fields import OmciSerialNumberField
from voltha.extensions.omci.omci_defs import bitpos_from_mask
-
class EntityClassAttribute(object):
def __init__(self, fld, access=set(), optional=False, range_check=None,
@@ -276,7 +276,7 @@
range_check=lambda x: 0 <= x < 255 or 256 <= x < 511),
ECA(ByteField("type", None), {AA.R, AA.SBC}),
ECA(ByteField("number_of_ports", None), {AA.R}, optional=True),
- ECA(StrFixedLenField("serial_number", None, 8), {AA.R}),
+ ECA(OmciSerialNumberField("serial_number"), {AA.R}),
ECA(StrFixedLenField("version", None, 14), {AA.R}),
ECA(StrFixedLenField("vendor_id", None, 4), {AA.R}),
ECA(ByteField("administrative_state", None), {AA.R, AA.W}),
@@ -648,7 +648,7 @@
range_check=lambda x: x == 0),
ECA(StrFixedLenField("vendor_id", None, 4), {AA.R}),
ECA(StrFixedLenField("version", None, 14), {AA.R}),
- ECA(StrFixedLenField("serial_number", None, 8), {AA.R}),
+ ECA(OmciSerialNumberField("serial_number"), {AA.R}),
ECA(ByteField("traffic_management_options", None), {AA.R},
range_check=lambda x: 0 <= x <= 2),
ECA(ByteField("vp_vc_cross_connection_option", 0), {AA.R},
diff --git a/voltha/extensions/omci/omci_fields.py b/voltha/extensions/omci/omci_fields.py
new file mode 100644
index 0000000..a6df815
--- /dev/null
+++ b/voltha/extensions/omci/omci_fields.py
@@ -0,0 +1,80 @@
+#
+# Copyright 2017 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+import binascii
+from scapy.fields import Field, StrFixedLenField, PadField
+from scapy.packet import Raw
+
+class FixedLenField(PadField):
+ """
+ This Pad field limits parsing of its content to its size
+ """
+ def __init__(self, fld, align, padwith='\x00'):
+ super(FixedLenField, self).__init__(fld, align, padwith)
+
+ def getfield(self, pkt, s):
+ remain, val = self._fld.getfield(pkt, s[:self._align])
+ if isinstance(val.payload, Raw) and \
+ not val.payload.load.replace(self._padwith, ''):
+ # raw payload is just padding
+ val.remove_payload()
+ return remain + s[self._align:], val
+
+class StrCompoundField(Field):
+ __slots__ = ['flds']
+
+ def __init__(self, name, flds):
+ super(StrCompoundField, self).__init__(name=name, default=None, fmt='s')
+ self.flds = flds
+ for fld in self.flds:
+ assert not fld.holds_packets, 'compound field cannot have packet field members'
+
+ def addfield(self, pkt, s, val):
+ for fld in self.flds:
+ # run though fake add/get to consume the relevant portion of the input value for this field
+ x, extracted = fld.getfield(pkt, fld.addfield(pkt, '', val))
+ l = len(extracted)
+ s = fld.addfield(pkt, s, val[0:l])
+ val = val[l:]
+ return s;
+
+ def getfield(self, pkt, s):
+ data = ''
+ for fld in self.flds:
+ s, value = fld.getfield(pkt, s)
+ if not isinstance(value, str):
+ value = fld.i2repr(pkt, value)
+ data += value
+ return s, data
+
+class XStrFixedLenField(StrFixedLenField):
+ """
+ XStrFixedLenField which value is printed as hexadecimal.
+ """
+ def i2m(self, pkt, x):
+ l = self.length_from(pkt) * 2
+ return None if x is None else binascii.a2b_hex(x)[0:l+1]
+
+ def m2i(self, pkt, x):
+ return None if x is None else binascii.b2a_hex(x)
+
+class OmciSerialNumberField(StrCompoundField):
+ def __init__(self, name, default=None):
+ assert default is None or (isinstance(default, str) and len(default) == 12), 'invalid default serial number'
+ vendor_default = default[0:4] if default is not None else None
+ vendor_serial_default = default[4:12] if default is not None else None
+ super(OmciSerialNumberField, self).__init__(name,
+ [StrFixedLenField('vendor_id', vendor_default, 4),
+ XStrFixedLenField('vendor_serial_number', vendor_serial_default, 4)])
diff --git a/voltha/extensions/omci/omci_frame.py b/voltha/extensions/omci/omci_frame.py
index 98faf42..05be99f 100644
--- a/voltha/extensions/omci/omci_frame.py
+++ b/voltha/extensions/omci/omci_frame.py
@@ -17,7 +17,7 @@
from scapy.fields import ShortField, ConditionalField
from scapy.packet import Packet
-from voltha.extensions.omci.omci_defs import FixedLenField
+from voltha.extensions.omci.omci_fields import FixedLenField
from voltha.extensions.omci.omci_messages import OmciCreate, OmciDelete, \
OmciDeleteResponse, OmciSet, OmciSetResponse, OmciGet, OmciGetResponse, \
OmciGetAllAlarms, OmciGetAllAlarmsResponse, OmciGetAllAlarmsNext, \