Initial auto onu discovery changes.

Change-Id: I30ec5aea0b0399183325efe97c51484b4b77db32
diff --git a/voltha/adapters/broadcom_onu/broadcom_onu.py b/voltha/adapters/broadcom_onu/broadcom_onu.py
index afcbba3..59fd987 100644
--- a/voltha/adapters/broadcom_onu/broadcom_onu.py
+++ b/voltha/adapters/broadcom_onu/broadcom_onu.py
@@ -159,7 +159,7 @@
         # first we verify that we got parent reference and proxy info
         assert device.parent_id
         assert device.proxy_address.device_id
-        assert device.proxy_address.channel_id
+        #assert device.proxy_address.channel_id      # c-vid
 
         # register for proxied messages right away
         self.proxy_address = device.proxy_address
@@ -172,21 +172,11 @@
         device.hardware_version = 'to be filled'
         device.firmware_version = 'to be filled'
         device.software_version = 'to be filled'
-        device.serial_number = uuid4().hex
         device.connect_status = ConnectStatus.REACHABLE
         self.adapter_agent.update_device(device)
 
-        # register physical ports
-        uni_port = Port(
-            port_no=2,
-            label='UNI facing Ethernet port',
-            type=Port.ETHERNET_UNI,
-            admin_state=AdminState.ENABLED,
-            oper_status=OperStatus.ACTIVE
-        )
-        self.adapter_agent.add_port(device.id, uni_port)
         self.adapter_agent.add_port(device.id, Port(
-            port_no=1,
+            port_no=100,
             label='PON port',
             type=Port.PON_ONU,
             admin_state=AdminState.ENABLED,
@@ -199,31 +189,54 @@
             ]
         ))
 
-        # add uni port to logical device
         parent_device = self.adapter_agent.get_device(device.parent_id)
         logical_device_id = parent_device.parent_id
         assert logical_device_id
-        port_no = device.proxy_address.channel_id
-        cap = OFPPF_1GB_FD | OFPPF_FIBER
-        self.adapter_agent.add_logical_port(logical_device_id, LogicalPort(
-            id='uni-{}'.format(port_no),
-            ofp_port=ofp_port(
-                port_no=port_no,
-                hw_addr=mac_str_to_tuple('00:00:00:00:%02x:%02x' % ((port_no >> 8) & 0xff, port_no & 0xff)),
-                name='uni-{}'.format(port_no),
-                config=0,
-                state=OFPPS_LIVE,
-                curr=cap,
-                advertised=cap,
-                peer=cap,
-                curr_speed=OFPPF_1GB_FD,
-                max_speed=OFPPF_1GB_FD
-            ),
-            device_id=device.id,
-            device_port_no=uni_port.port_no
-        ))
 
-        reactor.callLater(10, self.message_exchange)
+        # query ONU for number of supported uni ports
+        # temporarily set number of ports to 1 - port #2
+        uni_ports = (2,)
+
+        for uni in uni_ports:
+            # register physical ports
+            uni_port = Port(
+                port_no=uni,
+                label='UNI facing Ethernet port '+str(uni),
+                type=Port.ETHERNET_UNI,
+                admin_state=AdminState.ENABLED,
+                oper_status=OperStatus.ACTIVE
+            )
+            self.adapter_agent.add_port(device.id, uni_port)
+
+            # add uni port to logical device
+            port_no = device.proxy_address.channel_id + uni
+            cap = OFPPF_1GB_FD | OFPPF_FIBER
+            self.adapter_agent.add_logical_port(logical_device_id, LogicalPort(
+                id='uni-{}'.format(port_no),
+                ofp_port=ofp_port(
+                    port_no=port_no,
+                    hw_addr=mac_str_to_tuple('00:00:00:%02x:%02x:%02x' %
+                                             (device.proxy_address.onu_id & 0xff,
+                                              (port_no >> 8) & 0xff,
+                                              port_no & 0xff)),
+                    name='uni-{}'.format(port_no),
+                    config=0,
+                    state=OFPPS_LIVE,
+                    curr=cap,
+                    advertised=cap,
+                    peer=cap,
+                    curr_speed=OFPPF_1GB_FD,
+                    max_speed=OFPPF_1GB_FD
+                ),
+                device_id=device.id,
+                device_port_no=uni_port.port_no
+            ))
+
+            reactor.callLater(10,
+                              self.message_exchange,
+                              self.proxy_address.onu_id,
+                              self.proxy_address.onu_session_id,
+                              port_no)
 
         device = self.adapter_agent.get_device(device.id)
         device.oper_status = OperStatus.ACTIVE
@@ -238,12 +251,24 @@
         self.log.info('bulk-flow-update', device_id=device.id, flows=flows)
 
         def is_downstream(port):
-            return port == 2  # Need a better way
+            return port == 100  # Need a better way
 
         def is_upstream(port):
             return not is_downstream(port)
 
         for flow in flows:
+            _type = None
+            _port = None
+            _vlan_vid = None
+            _udp_dst = None
+            _udp_src = None
+            _ipv4_dst = None
+            _ipv4_src = None
+            _metadata = None
+            _output = None
+            _push_tpid = None
+            _field = None
+            _set_vlan_vid = None
             try:
                 _in_port = fd.get_in_port(flow)
                 assert _in_port is not None
@@ -305,7 +330,7 @@
                                       ipv4_dst=_ipv4_src)
 
                     elif field.type == fd.METADATA:
-                        _metadata = field.metadata
+                        _metadata = field.table_metadata
                         self.log.info('field-type-metadata',
                                       metadata=_metadata)
 
@@ -339,8 +364,8 @@
                         self.log.info('action-type-set-field',
                                       field=_field, in_port=_in_port)
                         if _field.type == fd.VLAN_VID:
-                            self.log.info('set-field-type-valn-vid',
-                                          vlan_vid=_field.vlan_vid & 0xfff)
+                            _set_vlan_vid = _field.vlan_vid & 0xfff
+                            self.log.info('set-field-type-valn-vid', _set_vlan_vid)
                         else:
                             self.log.error('unsupported-action-set-field-type',
                                            field_type=_field.type)
@@ -351,6 +376,19 @@
                 #
                 # All flows created from ONU adapter should be OMCI based
                 #
+                if _vlan_vid == 0:
+                    # allow priority tagged packets
+                    # Set AR - ExtendedVlanTaggingOperationConfigData
+                    #          514 - RxVlanTaggingOperationTable - add VLAN <cvid> to priority tagged pkts - c-vid
+                    self.send_set_extended_vlan_tagging_operation_vlan_configuration_data_single_tag(0x202, 8, 0, 0,
+                                                                                                     1, 8, _in_port)
+                    yield self.wait_for_response()
+
+                    # Set AR - ExtendedVlanTaggingOperationConfigData
+                    #          514 - RxVlanTaggingOperationTable - add VLAN <cvid> to priority tagged pkts - c-vid
+                    self.send_set_extended_vlan_tagging_operation_vlan_configuration_data_single_tag(0x205, 8, 0, 0,
+                                                                                                     1, 8, _in_port)
+                    yield self.wait_for_response()
 
             except Exception as e:
                 log.exception('failed-to-install-flow', e=e, flow=flow)
@@ -436,6 +474,13 @@
                 data=dict(
                     tp_pointer=OmciNullPointer,
                     interwork_tp_pointer_for_p_bit_priority_0=OmciNullPointer,
+                    interwork_tp_pointer_for_p_bit_priority_1=OmciNullPointer,
+                    interwork_tp_pointer_for_p_bit_priority_2=OmciNullPointer,
+                    interwork_tp_pointer_for_p_bit_priority_3=OmciNullPointer,
+                    interwork_tp_pointer_for_p_bit_priority_4=OmciNullPointer,
+                    interwork_tp_pointer_for_p_bit_priority_5=OmciNullPointer,
+                    interwork_tp_pointer_for_p_bit_priority_6=OmciNullPointer,
+                    interwork_tp_pointer_for_p_bit_priority_7=OmciNullPointer
                 )
             )
         )
@@ -534,7 +579,14 @@
                                               entity_id,
                                               interwork_tp_id):
         data = dict(
-            interwork_tp_pointer_for_p_bit_priority_0 = interwork_tp_id
+            interwork_tp_pointer_for_p_bit_priority_0=interwork_tp_id,
+            interwork_tp_pointer_for_p_bit_priority_1=interwork_tp_id,
+            interwork_tp_pointer_for_p_bit_priority_2=interwork_tp_id,
+            interwork_tp_pointer_for_p_bit_priority_3=interwork_tp_id,
+            interwork_tp_pointer_for_p_bit_priority_4=interwork_tp_id,
+            interwork_tp_pointer_for_p_bit_priority_5=interwork_tp_id,
+            interwork_tp_pointer_for_p_bit_priority_6=interwork_tp_id,
+            interwork_tp_pointer_for_p_bit_priority_7=interwork_tp_id
         )
         frame = OmciFrame(
             transaction_id=self.get_tx_id(),
@@ -953,12 +1005,14 @@
             self.log.info('wait-for-response-exception', exc=str(e))
 
     @inlineCallbacks
-    def message_exchange(self):
-        log.info('message_exchange')
+    def message_exchange(self, onu, gem, cvid):
+        log.info('message_exchange', onu=onu, gem=gem, cvid=cvid)
         # reset incoming message queue
         while self.incoming_messages.pending:
             _ = yield self.incoming_messages.get()
 
+        tcont = gem
+
         # construct message
         # MIB Reset - OntData - 0
         self.send_mib_reset()
@@ -968,134 +1022,60 @@
         self.send_create_gal_ethernet_profile(1, 48)
         yield self.wait_for_response()
 
-        # Set AR - TCont - 32768 - 1024
-        self.send_set_tcont(0x8000, 0x400)
+        # TCONT config
+        # Set AR - TCont - 32769 - (1025 or 1026)
+        self.send_set_tcont(0x8001, tcont)
         yield self.wait_for_response()
 
-        # Set AR - TCont - 32769 - 1025
-        self.send_set_tcont(0x8001, 0x401)
-        yield self.wait_for_response()
-
-        # Set AR - TCont - 32770 - 1026
-        self.send_set_tcont(0x8002, 0x402)
-        yield self.wait_for_response()
-
-        # Set AR - TCont - 32771 - 1027
-        self.send_set_tcont(0x8003, 0x403)
-        yield self.wait_for_response()
-
-        # Create AR - 802.1pMapperServiceProfile - 32768
-        self.send_create_8021p_mapper_service_profile(0x8000)
-        yield self.wait_for_response()
-
+        # Mapper Service config
         # Create AR - 802.1pMapperServiceProfile - 32769
         self.send_create_8021p_mapper_service_profile(0x8001)
         yield self.wait_for_response()
 
-        # Create AR - 802.1pMapperServiceProfile - 32770
-        self.send_create_8021p_mapper_service_profile(0x8002)
-        yield self.wait_for_response()
-
-        # Create AR - 802.1pMapperServiceProfile - 32771
-        self.send_create_8021p_mapper_service_profile(0x8003)
-        yield self.wait_for_response()
-
+        # MAC Bridge Service config
         # Create AR - MacBridgeServiceProfile - 513
         self.send_create_mac_bridge_service_profile(0x201)
         yield self.wait_for_response()
 
-        # Create AR - GemPortNetworkCtp - 256 - 1024 - 32768
-        self.send_create_gem_port_network_ctp(0x100, 0x400, 0x8000, "bi-directional", 0x100)
-        yield self.wait_for_response()
-
-        # Create AR - GemPortNetworkCtp - 257 - 1025 - 32769
-        self.send_create_gem_port_network_ctp(0x101, 0x401, 0x8001, "bi-directional", 0x100)
-        yield self.wait_for_response()
-
-        # Create AR - GemPortNetworkCtp - 258 - 1026 - 32770
-        self.send_create_gem_port_network_ctp(0x102, 0x402, 0x8002, "bi-directional", 0x100)
-        yield self.wait_for_response()
-
-        # Create AR - GemPortNetworkCtp - 259 - 1027 - 32771
-        self.send_create_gem_port_network_ctp(0x103, 0x403, 0x8003, "bi-directional", 0x100)
+        # GEM Port Network CTP config
+        # Create AR - GemPortNetworkCtp - 257 - <gem> - 32769
+        self.send_create_gem_port_network_ctp(0x101, gem, 0x8001, "bi-directional", 0x100)
         yield self.wait_for_response()
 
         # Create AR - GemPortNetworkCtp - 260 - 4000 - 0
         self.send_create_gem_port_network_ctp(0x104, 0x0FA0, 0, "downstream", 0)
         yield self.wait_for_response()
 
+        # Multicast GEM Interworking config
         # Create AR - MulticastGemInterworkingTp - 6 - 260
         self.send_create_multicast_gem_interworking_tp(0x6, 0x104)
         yield self.wait_for_response()
 
-        # Create AR - GemInterworkingTp - 32769 - 256 -32768 - 1
-        self.send_create_gem_inteworking_tp(0x8001, 0x100, 0x8000)
-        yield self.wait_for_response()
-
+        # GEM Interworking config
         # Create AR - GemInterworkingTp - 32770 - 257 -32769 - 1
         self.send_create_gem_inteworking_tp(0x8002, 0x101, 0x8001)
         yield self.wait_for_response()
 
-        # Create AR - GemInterworkingTp - 32771 - 258 -32770 - 1
-        self.send_create_gem_inteworking_tp(0x8003, 0x102, 0x8002)
-        yield self.wait_for_response()
-
-        # Create AR - GemInterworkingTp - 32772 - 259 -32771 - 1
-        self.send_create_gem_inteworking_tp(0x8004, 0x103, 0x8003)
-        yield self.wait_for_response()
-
-        # Set AR - 802.1pMapperServiceProfile - 32768 - 32769
-        self.send_set_8021p_mapper_service_profile(0x8000, 0x8001)
-        yield self.wait_for_response()
-
+        # Mapper Service Profile config
         # Set AR - 802.1pMapperServiceProfile - 32769 - 32770
         self.send_set_8021p_mapper_service_profile(0x8001, 0x8002)
         yield self.wait_for_response()
 
-        # Set AR - 802.1pMapperServiceProfile - 32770 - 32771
-        self.send_set_8021p_mapper_service_profile(0x8002, 0x8003)
-        yield self.wait_for_response()
-
-        # Set AR - 802.1pMapperServiceProfile - 32771 - 32772
-        self.send_set_8021p_mapper_service_profile(0x8003, 0x8004)
-        yield self.wait_for_response()
-
-        # Create AR - MacBridgePortConfigData - 8449 - 513 - 2 - 3 - 32768
-        self.send_create_mac_bridge_port_configuration_data(0x2101, 0x201, 2, 3, 0x8000)
-        yield self.wait_for_response()
-
+        # MAC Bridge Port config
         # Create AR - MacBridgePortConfigData - 8450 - 513 - 3 - 3 - 32769
         self.send_create_mac_bridge_port_configuration_data(0x2102, 0x201, 3, 3, 0x8001)
         yield self.wait_for_response()
 
-        # Create AR - MacBridgePortConfigData - 8451 - 513 - 4 - 3 - 32770
-        self.send_create_mac_bridge_port_configuration_data(0x2103, 0x201, 4, 3, 0x8002)
-        yield self.wait_for_response()
-
-        # Create AR - MacBridgePortConfigData - 8452 - 513 - 5 - 3 - 32771
-        self.send_create_mac_bridge_port_configuration_data(0x2104, 0x201, 5, 3, 0x8003)
-        yield self.wait_for_response()
-
         # Create AR - MacBridgePortConfigData - 9000 - 513 - 6 - 6 - 6
         self.send_create_mac_bridge_port_configuration_data(0x2328, 0x201, 6, 6, 6)
         yield self.wait_for_response()
 
-        # Create AR - VlanTaggingFilterData - 8449 - 040000000000000000000000000000000000000000000000
-        self.send_create_vlan_tagging_filter_data(0x2101, 0x0400)
+        # VLAN Tagging Filter config
+        # Create AR - VlanTaggingFilterData - 8450 - c-vid
+        self.send_create_vlan_tagging_filter_data(0x2102, cvid)
         yield self.wait_for_response()
 
-        # Create AR - VlanTaggingFilterData - 8450 - 040100000000000000000000000000000000000000000000
-        self.send_create_vlan_tagging_filter_data(0x2102, 0x0401)
-        yield self.wait_for_response()
-
-        # Create AR - VlanTaggingFilterData - 8451 - 040200000000000000000000000000000000000000000000
-        self.send_create_vlan_tagging_filter_data(0x2103, 0x0402)
-        yield self.wait_for_response()
-
-        # Create AR - VlanTaggingFilterData - 8452 - 040300000000000000000000000000000000000000000000
-        self.send_create_vlan_tagging_filter_data(0x2104, 0x0403)
-        yield self.wait_for_response()
-
+        # Multicast Operation Profile config
         # Create AR - MulticastOperationsProfile
         self.send_create_multicast_operations_profile(0x201, 3)
         yield self.wait_for_response()
@@ -1111,15 +1091,20 @@
                                                             '239.255.255.255')
         yield self.wait_for_response()
 
+        # Multicast Subscriber config
         # Create AR - MulticastSubscriberConfigInfo
         self.send_create_multicast_subscriber_config_info(0x201, 0, 0x201)
         yield self.wait_for_response()
 
+        # Multicast Operation Profile config
         # Set AR - MulticastOperationsProfile - Downstream IGMP Multicast TCI
-        self.send_set_multicast_operations_profile_ds_igmp_mcast_tci(0x201, 3, 0x401)
+        self.send_set_multicast_operations_profile_ds_igmp_mcast_tci(0x201, 4, cvid)
         yield self.wait_for_response()
 
-        # Create AR - ExtendedVlanTaggingOperationConfigData - 514 - 2 - 0x105
+        # Port 2
+        # Extended VLAN Tagging Operation config
+        # Create AR - ExtendedVlanTaggingOperationConfigData - 514 - 2 - 0x102
+        # TODO: add entry here for additional UNI interfaces
         self.send_create_extended_vlan_tagging_operation_configuration_data(0x202, 2, 0x102)
         yield self.wait_for_response()
 
@@ -1128,15 +1113,44 @@
         yield self.wait_for_response()
 
         # Set AR - ExtendedVlanTaggingOperationConfigData
-        #          514 - RxVlanTaggingOperationTable - add VLAN 1025 to untagged pkts
-        #self.send_set_extended_vlan_tagging_operation_vlan_configuration_data_untagged(0x202, 0x1000, 0x401)
+        #          514 - RxVlanTaggingOperationTable - add VLAN <cvid> to priority tagged pkts - c-vid
+        #self.send_set_extended_vlan_tagging_operation_vlan_configuration_data_single_tag(0x202, 8, 0, 0, 1, 8, cvid)
         #yield self.wait_for_response()
 
         # Set AR - ExtendedVlanTaggingOperationConfigData
-        #          514 - RxVlanTaggingOperationTable - add VLAN 1025 to priority tagged pkts
-        self.send_set_extended_vlan_tagging_operation_vlan_configuration_data_single_tag(0x202, 0, 0, 0, 1, 8, 0x401)
+        #          514 - RxVlanTaggingOperationTable - add VLAN <cvid> to untagged pkts - c-vid
+        self.send_set_extended_vlan_tagging_operation_vlan_configuration_data_untagged(0x202, 0x1000, cvid)
         yield self.wait_for_response()
 
-        # Create AR - MacBridgePortConfigData - 513 - 513 - 1 - 1 - 0x105
-        self.send_create_mac_bridge_port_configuration_data(0x201, 0x201, 1, 1, 0x102)
+        # MAC Bridge Port config
+        # Create AR - MacBridgePortConfigData - 513 - 513 - 1 - 1 - 0x102
+        # TODO: add more entries here for other UNI ports
+        self.send_create_mac_bridge_port_configuration_data(0x201, 0x201, 2, 1, 0x102)
+        yield self.wait_for_response()
+
+        # Port 5
+        # Extended VLAN Tagging Operation config
+        # Create AR - ExtendedVlanTaggingOperationConfigData - 514 - 2 - 0x102
+        # TODO: add entry here for additional UNI interfaces
+        self.send_create_extended_vlan_tagging_operation_configuration_data(0x205, 2, 0x105)
+        yield self.wait_for_response()
+
+        # Set AR - ExtendedVlanTaggingOperationConfigData - 514 - 8100 - 8100
+        self.send_set_extended_vlan_tagging_operation_tpid_configuration_data(0x205, 0x8100, 0x8100)
+        yield self.wait_for_response()
+
+        # Set AR - ExtendedVlanTaggingOperationConfigData
+        #          514 - RxVlanTaggingOperationTable - add VLAN <cvid> to priority tagged pkts - c-vid
+        #self.send_set_extended_vlan_tagging_operation_vlan_configuration_data_single_tag(0x205, 8, 0, 0, 1, 8, cvid)
+        #yield self.wait_for_response()
+
+        # Set AR - ExtendedVlanTaggingOperationConfigData
+        #          514 - RxVlanTaggingOperationTable - add VLAN <cvid> to untagged pkts - c-vid
+        self.send_set_extended_vlan_tagging_operation_vlan_configuration_data_untagged(0x205, 0x1000, cvid)
+        yield self.wait_for_response()
+
+        # MAC Bridge Port config
+        # Create AR - MacBridgePortConfigData - 513 - 513 - 1 - 1 - 0x102
+        # TODO: add more entries here for other UNI ports
+        self.send_create_mac_bridge_port_configuration_data(0x205, 0x201, 5, 1, 0x105)
         yield self.wait_for_response()
diff --git a/voltha/adapters/maple_olt/maple_olt.py b/voltha/adapters/maple_olt/maple_olt.py
index 7d08414..926e8d9 100644
--- a/voltha/adapters/maple_olt/maple_olt.py
+++ b/voltha/adapters/maple_olt/maple_olt.py
@@ -20,7 +20,7 @@
 from uuid import uuid4
 
 import arrow
-import structlog
+import binascii
 from scapy.layers.l2 import Ether, Dot1Q
 from twisted.internet import reactor
 from twisted.internet.protocol import ReconnectingClientFactory
@@ -153,10 +153,11 @@
         return pm_config
 
 
-
 class MapleOltRxHandler(pb.Root):
-    def __init__(self, device_id, adapter):
+    def __init__(self, device_id, adapter, onu_queue):
         self.device_id = device_id
+        self.adapter = adapter
+        self.onu_discovered_queue = onu_queue
         self.adapter_agent = adapter.adapter_agent
         self.adapter_name = adapter.name
         # registry('main').get_args().external_host_address
@@ -192,9 +193,9 @@
     def receive_omci_msg(self):
         return self.omci_rx_queue.get()
 
-    def remote_report_stats(self, object, key, stats_data):
+    def remote_report_stats(self, _object, key, stats_data):
         log.info('received-stats-msg',
-                 object=object,
+                 object=_object,
                  key=key,
                  stats=stats_data)
 
@@ -218,23 +219,29 @@
         except Exception as e:
             log.exception('failed-to-submit-kpis', e=e)
 
-    def remote_report_event(self, object, key, event, event_data=None):
+    def remote_report_event(self, _object, key, event, event_data=None):
+        def _convert_serial_data(data):
+            b = bytearray()
+            b.extend(data)
+
+            return binascii.hexlify(b)
+
         log.info('received-event-msg',
-                 object=object,
+                 object=_object,
                  key=key,
                  event_str=event,
                  event_data=event_data)
 
-        if object == 'device':
+        if _object == 'device':
             # key: {'device_id': <int>}
             # event: 'state-changed'
             #     event_data: {'state_change_successful': <False|True>,
             #                  'new_state': <str> ('active-working'|'inactive')}
             pass
-        elif object == 'nni':
+        elif _object == 'nni':
             # key: {'device_id': <int>, 'nni': <int>}
             pass
-        elif object == 'pon_ni':
+        elif _object == 'pon_ni':
             # key: {'device_id': <int>, 'pon_ni': <int>}
             # event: 'state-changed'
             #     event_data: {'state_change_successful': <False|True>,
@@ -252,9 +259,19 @@
             #                  'step_tuning_time': <int>
             #                  'attenuation': <int>
             #                  'power_levelling_caps': <int>}
-            pass
-        elif object == 'onu':
-            # key: {'device_id': <int>, 'pon_ni': <int>}, 'onu_id': <int>}
+            if 'onu-discovered' == event and event_data is not None:
+                event_data['_device_id'] = key['device_id'] if 'device_id' in key else None
+                event_data['_pon_id'] = key['pon_id'] if 'pon_id' in key else None
+                event_data['_vendor_id'] = _convert_serial_data(event_data['serial_num_vendor_id']) \
+                    if 'serial_num_vendor_id' in event_data else None
+                event_data['_vendor_specific'] = _convert_serial_data(event_data['serial_num_vendor_specific']) \
+                    if 'serial_num_vendor_specific' in event_data else None
+
+                self.onu_discovered_queue.put(event_data)
+                log.info('onu-discovered-event-added-to-queue', event_data=event_data)
+
+        elif _object == 'onu':
+            # key: {'device_id': <int>, 'pon_ni': <int>, 'onu_id': <int>}
             # event: 'activation-completed'
             #     event_data: {'activation_successful': <False|True>,
             #                  act_fail_reason': <str>}
@@ -277,31 +294,31 @@
             #     event_data: {'serial_num-vendor_id': <str>
             #                  'serial_num-vendor_specific: <str>}
             pass
-        elif object == 'alloc_id':
-            # key: {'device_id': <int>, 'pon_ni': <int>}, 'onu_id': <int>, 'alloc_id': ,<int>}
+        elif _object == 'alloc_id':
+            # key: {'device_id': <int>, 'pon_ni': <int>, 'onu_id': <int>, 'alloc_id': ,<int>}
             pass
-        elif object == 'gem_port':
-            # key: {'device_id': <int>, 'pon_ni': <int>}, 'onu_id': <int>, 'gem_port': ,<int>}
+        elif _object == 'gem_port':
+            # key: {'device_id': <int>, 'pon_ni': <int>, 'onu_id': <int>, 'gem_port': ,<int>}
             pass
-        elif object == 'trx':
+        elif _object == 'trx':
             # key: {'device_id': <int>, 'pon_ni': <int>}
             pass
-        elif object == 'flow_map':
+        elif _object == 'flow_map':
             # key: {'device_id': <int>, 'pon_ni': <int>}
             pass
 
-    def remote_report_alarm(self, object, key, alarm, status, priority,
+    def remote_report_alarm(self, _object, key, alarm, status, priority,
                             alarm_data=None):
         log.info('received-alarm-msg',
-                 object=object,
+                 object=_object,
                  key=key,
                  alarm=alarm,
                  status=status,
                  priority=priority,
                  alarm_data=alarm_data)
 
-        id = 'voltha.{}.{}.{}'.format(self.adapter_name, self.device_id, object)
-        description = '{} Alarm - {} - {}'.format(object.upper(), alarm.upper(),
+        id = 'voltha.{}.{}.{}'.format(self.adapter_name, self.device_id, _object)
+        description = '{} Alarm - {} - {}'.format(_object.upper(), alarm.upper(),
                                                   'Raised' if status else 'Cleared')
 
         if priority == 'low':
@@ -322,8 +339,7 @@
                 type=AlarmEventType.EQUIPMENT,
                 category=AlarmEventCategory.PON,
                 severity=severity,
-                state=AlarmEventState.RAISED if status else \
-                      AlarmEventState.CLEARED,
+                state=AlarmEventState.RAISED if status else AlarmEventState.CLEARED,
                 description=description,
                 context=alarm_data,
                 raised_ts = ts)
@@ -340,12 +356,11 @@
             # status: <False|True>
             pass
         elif object == 'onu':
-            # key: {'device_id': <int>, 'pon_ni': <int>}, 'onu_id': <int>}
+            # key: {'device_id': <int>, 'pon_ni': <int>, 'onu_id': <int>}
             # alarm: <'los'|'lob'|'lopc_miss'|'los_mic_err'|'dow'|'sf'|'sd'|'suf'|'df'|'tiw'|'looc'|'dg'>
             # status: <False|True>
             pass
 
-
 @implementer(IAdapterInterface)
 class MapleOltAdapter(object):
     name = 'maple_olt'
@@ -396,6 +411,7 @@
         handler.update_pm_metrics(device, pm_config)
 
     def adopt_device(self, device):
+        log.info("adopt-device", device=device)
         self.devices_handlers[device.id] = MapleOltHandler(self, device.id)
         reactor.callLater(0, self.devices_handlers[device.id].activate, device)
         return device
@@ -507,7 +523,8 @@
         self.pbc_factory = MaplePBClientFactory()
         self.pbc_port = 24498
         self.tx_id = 0
-        self.rx_handler = MapleOltRxHandler(self.device_id, self.adapter)
+        self.onu_discovered_queue = DeferredQueue()
+        self.rx_handler = MapleOltRxHandler(self.device_id, self.adapter, self.onu_discovered_queue)
         self.heartbeat_count = 0
         self.heartbeat_miss = 0
         self.heartbeat_interval = 1
@@ -523,6 +540,18 @@
     def get_channel(self):
         return self.pbc_factory.getChannel()
 
+    def get_proxy_channel_id_from_onu(self, onu_id):
+        return onu_id << 4
+
+    def get_onu_from_channel_id(self, channel_id):
+        return channel_id >> 4
+
+    def get_tunnel_tag_from_onu(self, onu):
+        return 1024 + (onu * 16)
+
+    def get_onu_from_tunnel_tag(self, tunnel_tag):
+        return (tunnel_tag - 1024) / 16
+
     def get_new_onu_id(self, vendor, vendor_specific):
         onu_id = None
         for i in range(0, 63):
@@ -563,14 +592,6 @@
                       onus=self.onus)
         return None
 
-    def get_vlan_from_onu(self, onu):
-        vlan = onu + 1024
-        return vlan
-
-    def get_onu_from_vlan(self, vlan):
-        onu = vlan - 1024
-        return onu
-
     @inlineCallbacks
     def send_set_remote(self):
         srv_ip = self.rx_handler.get_ip()
@@ -847,8 +868,84 @@
         reactor.callLater(self.heartbeat_interval, self.heartbeat, device_id)
 
     @inlineCallbacks
+    def arrive_onu(self):
+        self.log.info('arrive-onu waiting')
+        _data = yield self.onu_discovered_queue.get()
+
+        ok_to_arrive = False
+        olt_id = _data['_device_id']
+        pon_id = _data['_pon_id']
+        onu_id = self.onu_serial_exists(_data['_vendor_id'], _data['_vendor_specific'])
+        self.log.info('arrive-onu-detected', olt_id=olt_id, pon_ni=pon_id, onu_data=_data, onus=self.onus)
+
+        if _data['onu_id'] == 65535:
+            if onu_id is not None:
+                self.log.info('onu-activation-already-in-progress',
+                              vendor=_data['_vendor_id'],
+                              vendor_specific=_data['_vendor_specific'],
+                              onus=self.onus)
+            else:
+                onu_id = self.get_new_onu_id(_data['_vendor_id'],
+                                             _data['_vendor_specific'])
+                self.log.info('assigned-onu-id',
+                              onu_id=onu_id,
+                              vendor=_data['_vendor_id'],
+                              vendor_specific=_data['_vendor_specific'],
+                              onus=self.onus)
+                ok_to_arrive = True
+        else:
+            vendor_id, vendor_specific = self.onu_exists(_data['onu_id'])
+            if vendor_id is not None and vendor_id == _data['_vendor_id'] and \
+               vendor_specific is not None and vendor_specific == _data['_vendor_specific']:
+                onu_id = _data['onu_id']
+                self.log.info('re-discovered-existing-onu',
+                              onu_id=onu_id,
+                              vendor=_data['_vendor_id'],
+                              vendor_specific=_data['_vendor_specific'])
+                ok_to_arrive = True
+            else:
+                self.log.info('onu-id-serial-number-mismatch-detected',
+                              onu_id=onu_id,
+                              vendor_id=vendor_id,
+                              new_vendor_id=_data['_vendor_id'],
+                              vendor_specific=vendor_specific,
+                              new_vendor_specific=_data['_vendor_specific'])
+
+        if onu_id is not None and ok_to_arrive:
+            self.log.info('arriving-onu', onu_id=onu_id)
+            tunnel_tag = self.get_tunnel_tag_from_onu(onu_id)
+            yield self.send_create_onu(pon_id,
+                                       onu_id,
+                                       _data['_vendor_id'],
+                                       _data['_vendor_specific'])
+            yield self.send_configure_alloc_id(pon_id, onu_id, tunnel_tag)
+            yield self.send_configure_unicast_gem(pon_id, onu_id, tunnel_tag)
+            yield self.send_configure_multicast_gem(pon_id, onu_id, 4000)
+            yield self.send_activate_onu(pon_id, onu_id)
+
+            self.adapter_agent.child_device_detected(
+                parent_device_id=self.device_id,
+                parent_port_no=100,
+                child_device_type='broadcom_onu',
+                proxy_address=Device.ProxyAddress(
+                    device_id=self.device_id,
+                    channel_id=self.get_proxy_channel_id_from_onu(onu_id),  # c-vid
+                    onu_id=onu_id,
+                    onu_session_id=tunnel_tag  # tunnel_tag/gem_port, alloc_id
+                ),
+                vlan=tunnel_tag,
+                serial_number=_data['_vendor_specific']
+            )
+
+        reactor.callLater(1, self.arrive_onu)
+
+    @inlineCallbacks
     def activate(self, device):
         self.log.info('activating-olt', device=device)
+
+        while self.onu_discovered_queue.pending:
+            _ = yield self.onu_discovered_queue.get()
+
         if self.logical_device_id is None:
             if not device.ipv4_address:
                 device.oper_status = OperStatus.FAILED
@@ -863,7 +960,7 @@
             self.adapter_agent.update_device(device)
 
             nni_port = Port(
-                port_no=2,
+                port_no=1,
                 label='NNI facing Ethernet port',
                 type=Port.ETHERNET_NNI,
                 admin_state=AdminState.ENABLED,
@@ -871,7 +968,7 @@
             )
             self.adapter_agent.add_port(device.id, nni_port)
             self.adapter_agent.add_port(device.id, Port(
-                port_no=1,
+                port_no=100,
                 label='PON port',
                 type=Port.PON_OLT,
                 admin_state=AdminState.ENABLED,
@@ -960,25 +1057,6 @@
         yield self.send_set_remote()
         yield self.send_connect_olt(0)
         yield self.send_activate_olt(0)
-        # register ONUS per uni port until done asynchronously
-        for onu_no in [1]:
-            vlan_id = self.get_vlan_from_onu(onu_no)
-            yield self.send_create_onu(0, onu_no, '4252434d', '12345678')
-            yield self.send_configure_alloc_id(0, onu_no, vlan_id)
-            yield self.send_configure_unicast_gem(0,onu_no, vlan_id)
-            yield self.send_configure_multicast_gem(0, onu_no, 4000)
-            yield self.send_activate_onu(0, onu_no)
-
-            self.adapter_agent.child_device_detected(
-                parent_device_id=device.id,
-                parent_port_no=1,
-                child_device_type='broadcom_onu',
-                proxy_address=Device.ProxyAddress(
-                    device_id=device.id,
-                    channel_id=vlan_id
-                ),
-                vlan=vlan_id
-            )
 
         # Open the frameio port to receive in-band packet_in messages
         self.log.info('registering-frameio')
@@ -986,6 +1064,7 @@
             self.interface, self.rcv_io, is_inband_frame)
 
         # Finally set the initial PM configuration for this device
+        # TODO: if arrive_onu not working, the following PM stuff was commented out during testing
         self.pm_metrics=MapleOltPmMetrics(device)
         pm_config = self.pm_metrics.make_proto()
         log.info("initial-pm-config", pm_config=pm_config)
@@ -994,6 +1073,8 @@
         # Apply the PM configuration
         self.update_pm_metrics(device, pm_config)
 
+        reactor.callLater(1, self.arrive_onu)
+
         self.log.info('olt-activated', device=device)
 
     def rcv_io(self, port, frame):
@@ -1023,10 +1104,11 @@
         self.log.info('bulk-flow-update', device_id=device.id, flows=flows)
 
         def is_downstream(port):
-            return port == 2  # Need a better way
+            return not is_upstream(port)
 
         def is_upstream(port):
-            return not is_downstream(port)
+            return port == 100  # Need a better way
+
 
         for flow in flows:
             _type = None
@@ -1104,7 +1186,7 @@
                                       ipv4_dst=_ipv4_src)
 
                     elif field.type == fd.METADATA:
-                        _metadata = field.metadata
+                        _metadata = field.table_metadata
                         self.log.info('field-type-metadata',
                                       metadata=_metadata)
 
@@ -1147,16 +1229,19 @@
                         log.error('unsupported-action-type',
                                   action_type=action.type, in_port=_in_port)
 
-                if is_upstream(_in_port):
-                    yield self.send_config_classifier(0, _type, _ip_proto,
-                                                      _udp_dst)
-                    yield self.send_config_acflow(0, _in_port, _type, _ip_proto,
-                                                  _udp_dst)
+                if is_upstream(_in_port) and \
+                        (_type == 0x888e or
+                        (_type == 0x800 and (_ip_proto == 2 or _ip_proto == 17))):
+                    yield self.send_config_classifier(0, _type, _ip_proto, _udp_dst)
+                    yield self.send_config_acflow(0, _in_port, _type, _ip_proto, _udp_dst)
+
+
 
             except Exception as e:
                 log.exception('failed-to-install-flow', e=e, flow=flow)
 
 
+
     @inlineCallbacks
     def send_proxied_message(self, proxy_address, msg):
         if isinstance(msg, Packet):
@@ -1171,7 +1256,7 @@
             yield remote.callRemote("send_omci",
                                     0,
                                     0,
-                                    self.get_onu_from_vlan(proxy_address.channel_id),
+                                    self.get_onu_from_channel_id(proxy_address.channel_id),
                                     msg)
             onu, rmsg = yield self.rx_handler.receive_omci_msg()
             self.adapter_agent.receive_proxied_message(proxy_address, rmsg)