Added OMCI message parsing tests

Change-Id: Ic864446241c3507dcfb389c7e774d0aa79de3b55
diff --git a/common/frameio/frameio.py b/common/frameio/frameio.py
index 0a222ad..4611c14 100644
--- a/common/frameio/frameio.py
+++ b/common/frameio/frameio.py
@@ -53,7 +53,7 @@
     """
     Return a hexadecimal string encoding of input buffer
     """
-    return ' '.join('%02x' % ord(c) for c in buffer)
+    return ''.join('%02x' % ord(c) for c in buffer)
 
 
 class _SelectWakerDescriptor(object):
diff --git a/maple_experiments/chat.py b/maple_experiments/chat.py
new file mode 100755
index 0000000..358b728
--- /dev/null
+++ b/maple_experiments/chat.py
@@ -0,0 +1,160 @@
+#!/usr/bin/env python
+
+import sys
+from time import sleep
+
+from scapy.packet import Packet
+from twisted.spread import pb
+from twisted.internet import reactor
+from twisted.internet.defer import inlineCallbacks, returnValue, DeferredQueue
+from twisted.python import util
+
+from common.frameio.frameio import hexify
+from common.utils.asleep import asleep
+from voltha.extensions.omci.omci import *
+
+
+class OmciProxy(pb.Root):
+
+    def __init__(self):
+        reactor.listenTCP(24497, pb.PBServerFactory(self))
+        self.remote = None
+        self.response_queue = DeferredQueue()
+
+    @inlineCallbacks
+    def connect(self):
+        factory = pb.PBClientFactory()
+        reactor.connectTCP("10.111.101.206", 24498, factory)
+        self.remote = yield factory.getRootObject()
+        print 'connected'
+        yield self.remote.callRemote("setRemote", port=24496)
+
+    def remote_echo(self, pkt_type, pon, onu, port, crc, size, data):
+        print "Packet Type:", pkt_type
+        print "PON:", pon
+        print "ONU ID:", onu
+        print "Port:", port
+        print "CRC OK:", crc
+        print "Packet Size:", size
+        print "received:", hexify(data)
+        self.response_queue.put(data)
+
+    @inlineCallbacks
+    def send_omci(self, msg):
+        if isinstance(msg, Packet):
+            msg = str(msg)
+        try:
+            print ' sending:', msg
+            yield self.remote.callRemote("send_omci", 0, 0, 1, msg)
+            print 'msg sent'
+
+        except Exception, e:
+            print >> sys.stderr, 'Blew up:', str(e)
+
+    def receive(self):
+        return self.response_queue.get()
+
+
+@inlineCallbacks
+def chat():
+    proxy = OmciProxy()
+    yield proxy.connect()
+
+    tx_id = [0]
+    def get_tx_id():
+        tx_id[0] += 1
+        return tx_id[0]
+
+    if 0:
+        # MIB RESET
+        frame = OmciFrame(
+            transaction_id=get_tx_id(),
+            message_type=OmciMibReset.message_id,
+            omci_message=OmciMibReset(
+                entity_class=OntData.class_id
+            )
+        )
+        yield proxy.send_omci(hexify(str(frame)))
+
+        # MIB RESET RESPONSE
+        response = yield proxy.receive()
+        resp = OmciFrame(response)
+        resp.show()
+
+    if 0:
+        # GET ALL ALARMS
+        frame = OmciFrame(
+            transaction_id=get_tx_id(),
+            message_type=OmciGetAllAlarms.message_id,
+            omci_message=OmciGetAllAlarms(
+                entity_class=OntData.class_id,
+                entity_id=0
+            )
+        )
+        yield proxy.send_omci(hexify(str(frame)))
+
+        # MIB UPLOAD RESPONSE
+        response = yield proxy.receive()
+        resp = OmciFrame(response)
+        resp.show()
+
+    if 0:
+        # MIB UPLOAD
+        frame = OmciFrame(
+            transaction_id=get_tx_id(),
+            message_type=OmciMibUpload.message_id,
+            omci_message=OmciMibUpload(
+                entity_class=OntData.class_id
+            )
+        )
+        yield proxy.send_omci(hexify(str(frame)))
+
+        # MIB UPLOAD RESPONSE
+        response = yield proxy.receive()
+        resp = OmciFrame(response)
+        resp.show()
+
+        n_commands = resp.omci_message.number_of_commands
+        for seq_num in range(n_commands):
+            print 'seq_num', seq_num
+            frame = OmciFrame(
+                transaction_id=get_tx_id(),
+                message_type=OmciMibUploadNext.message_id,
+                omci_message=OmciMibUploadNext(
+                    entity_class=OntData.class_id,
+                    command_sequence_number=seq_num
+                )
+            )
+            yield proxy.send_omci(hexify(str(frame)))
+
+            response = yield proxy.receive()
+            print hexify(response)
+            # resp = OmciFrame(response)
+            # resp.show()
+
+
+    if 1:
+        # GET CIRCUIT PACK
+        frame = OmciFrame(
+            transaction_id=get_tx_id(),
+            message_type=OmciGet.message_id,
+            omci_message=OmciGet(
+                entity_class=CircuitPack.class_id,
+                entity_id=0x101,
+                attributes_mask=CircuitPack.mask_for('vendor_id')
+            )
+        )
+        yield proxy.send_omci(hexify(str(frame)))
+
+        # MIB UPLOAD RESPONSE
+        response = yield proxy.receive()
+        resp = OmciFrame(response)
+        resp.show()
+
+    yield asleep(1)
+    reactor.stop()
+
+
+if __name__ == '__main__':
+    reactor.callLater(0, chat)
+    reactor.run()
diff --git a/tests/utests/voltha/extensions/omci/test_omci.py b/tests/utests/voltha/extensions/omci/test_omci.py
index 1900508..2fcb75b 100644
--- a/tests/utests/voltha/extensions/omci/test_omci.py
+++ b/tests/utests/voltha/extensions/omci/test_omci.py
@@ -529,6 +529,44 @@
         frame2 = OmciFrame(hex2raw(ref))
         self.assertEqual(frame2, frame)
 
+    def test_mib_upload(self):
+        ref = '00304D0A000200000000000000000000' \
+              '00000000000000000000000000000000' \
+              '000000000000000000000028'
+        frame = OmciFrame(
+            transaction_id=48,
+            message_type=OmciMibUpload.message_id,
+            omci_message=OmciMibUpload(
+                entity_class=OntData.class_id
+            )
+        )
+        self.assertGeneratedFrameEquals(frame, ref)
+
+    def test_parsing_mib_upload_next_responses(self):
+        refs = [
+            '00042e0a000200000002000080000000000000000000000000000000000000000000000000000000000000283e0c62ee',
+            '00052e0a0002000000050101f0002f2f0520202020202020202020202020202020202020200000000000002808523170',
+            '00062e0a00020000000501010f80202020202020202020202020202020202020202000000000000000000028922568e4',
+            '00072e0a0002000000050104f00030300120202020202020202020202020202020202020200000000000002812bfa77d',
+            '00082e0a00020000000501040f802020202020202020202020202020202020202020000000000000000000282b03fcee',
+            '00092e0a0002000000050180f000f8f80120202020202020202020202020202020202020200000000000002881e385a2',
+            '000a2e0a00020000000501800f8020202020202020202020202020202020202020200000000000000000002888c5dbc2',
+            '000b2e0a0002000000060101f0002f054252434d12345678000000000000000000000000000c00000000002895471f4a',
+            '000c2e0a00020000000601010f004252434d0000000000000000000000000000000000000000000000000028742cbaea',
+            '000d2e0a000200000006010100f820202020202020202020202020202020202020200000000000000000002846978475',
+            '000e2e0a00020000000601010004000000000000000000000000000000000000000000000000000000000028a8403aea',
+            '000f2e0a0002000000060104f00030014252434d12345678000000000000000000000000000c000000000028723cf2ae',
+            '00102e0a00020000000601040f004252434d0000000000000000000000000000000000000000000000000028a958ebf8',
+            '00112e0a000200000006010400f8202020202020202020202020202020202020202000000800000000000028424cc847',
+            '00122e0a000200000006010400040000000000000000000000000000000000000000000000000000000000282bb79708',
+            '00132e0a0002000000060180f000f8014252434d12345678000000000000000000000000000c0000000000287834e722',
+            '00142e0a00020000000601800f004252434d000000000000000000000000000000000000000000000000002833d78834',
+        ]
+        for i, data in enumerate(refs):
+            frame = OmciFrame(hex2raw(data))
+            print 'Response', i
+            frame.show()
+
 
 if __name__ == '__main__':
     main()
diff --git a/voltha/extensions/omci/omci_entities.py b/voltha/extensions/omci/omci_entities.py
index cbeeec5..6c082ad 100644
--- a/voltha/extensions/omci/omci_entities.py
+++ b/voltha/extensions/omci/omci_entities.py
@@ -133,7 +133,28 @@
     mandatory_operations = {OP.Get, OP.Set,
                             OP.GetAllAlarms, OP.GetAllAlarmsNext,
                             OP.MibReset, OP.MibUpload, OP.MibUploadNext}
-    optional_operations = {}
+
+
+class Cardholder(EntityClass):
+    class_id = 5
+    attributes = [
+        ECA(ShortField("managed_entity_id", None), {AA.R}),
+        ECA(ByteField("actual_plugin_unit_type", None), {AA.R}),
+        ECA(ByteField("expected_plugin_unit_type", None), {AA.R, AA.W}),
+        ECA(ByteField("expected_port_count", None), {AA.R, AA.W},
+            optional=True),
+        ECA(StrFixedLenField("expected_equipment_id", None, 20), {AA.R, AA.W},
+            optional=True),
+        ECA(StrFixedLenField("actual_equipment_id", None, 20), {AA.R},
+            optional=True),
+        ECA(ByteField("protection_profile_pointer", None), {AA.R},
+            optional=True),
+        ECA(ByteField("invoke_protection_switch", None), {AA.R, AA.W},
+            optional=True),
+        ECA(ByteField("arc", None), {AA.R, AA.W}),
+        ECA(ByteField("arc_interval", None), {AA.R, AA.W}),
+    ]
+    mandatory_operations = {OP.Get, OP.Set}
 
 
 class CircuitPack(EntityClass):
diff --git a/voltha/extensions/omci/omci_frame.py b/voltha/extensions/omci/omci_frame.py
index 5c8dd20..a762ad8 100644
--- a/voltha/extensions/omci/omci_frame.py
+++ b/voltha/extensions/omci/omci_frame.py
@@ -73,13 +73,21 @@
                 "omci_message", None, OmciGetAllAlarmsNextResponse), align=36),
                 lambda pkt:
                 pkt.message_type == OmciGetAllAlarmsNextResponse.message_id),
+
         ConditionalField(FixedLenField(
             PacketField("omci_message", None, OmciMibUpload), align=36),
+            lambda pkt: pkt.message_type == OmciMibUpload.message_id),
+        ConditionalField(FixedLenField(
+            PacketField("omci_message", None, OmciMibUploadResponse), align=36),
             lambda pkt: pkt.message_type == OmciMibUploadResponse.message_id),
         ConditionalField(FixedLenField(
             PacketField("omci_message", None, OmciMibUploadNext), align=36),
             lambda pkt:
-                pkt.message_type == OmciMibUploadNextResponse.message_id),
+                pkt.message_type == OmciMibUploadNext.message_id),
+        ConditionalField(FixedLenField(
+            PacketField("omci_message", None, OmciMibUploadNextResponse), align=36),
+            lambda pkt: pkt.message_type == OmciMibUploadNextResponse.message_id),
+
         ConditionalField(FixedLenField(
             PacketField("omci_message", None, OmciMibReset), align=36),
             lambda pkt: pkt.message_type == OmciMibReset.message_id),
diff --git a/voltha/extensions/omci/omci_messages.py b/voltha/extensions/omci/omci_messages.py
index 3710702..12956dc 100644
--- a/voltha/extensions/omci/omci_messages.py
+++ b/voltha/extensions/omci/omci_messages.py
@@ -86,7 +86,10 @@
         indices = entity_class.attribute_indices_from_mask(attribute_mask)
         data = {}
         for index in indices:
-            fld = entity_class.attributes[index]._fld
+            try:
+                fld = entity_class.attributes[index]._fld
+            except IndexError, e:
+                raise
             s, value = fld.getfield(pkt, s)
             data[fld.name] = value
         return  s, data
@@ -264,7 +267,8 @@
         ShortField("object_entity_class", None),
         ShortField("object_entity_id", 0),
         ShortField("object_attributes_mask", None),
-        OmciMaskedData("object_data")
+        OmciMaskedData("object_data", entity_class='object_entity_class',
+                       attributes_mask='object_attributes_mask')
     ]