VOL-665: ADTRAN ONU support for OpenOMCI

Change-Id: I65bb69b7bd5e769b7fff123947391ddf87d6e7ce
diff --git a/voltha/adapters/adtran_onu/adtran_onu_handler.py b/voltha/adapters/adtran_onu/adtran_onu_handler.py
index dea1e78..60b4c69 100644
--- a/voltha/adapters/adtran_onu/adtran_onu_handler.py
+++ b/voltha/adapters/adtran_onu/adtran_onu_handler.py
@@ -15,10 +15,9 @@
 #
 
 import arrow
+import structlog
 
 from voltha.adapters.adtran_olt.xpon.adtran_xpon import AdtranXPON
-from omci.omci_cc import OMCI_CC
-from omci.omci_entities import onu_custom_entity_classes
 from pon_port import PonPort
 from uni_port import UniPort
 from heartbeat import HeartBeat
@@ -34,14 +33,15 @@
 from voltha.protos import third_party
 from voltha.protos.common_pb2 import OperStatus, ConnectStatus
 from voltha.protos.device_pb2 import Image
-from voltha.extensions.omci.omci import *
 from common.utils.indexpool import IndexPool
+from voltha.extensions.omci.openomci_agent import OpenOMCIAgent
 
 _ = third_party
 _MAXIMUM_PORT = 128          # PON and UNI ports
 _ONU_REBOOT_MIN = 60
 _ONU_REBOOT_RETRY = 10
 
+
 class AdtranOnuHandler(AdtranXPON):
     def __init__(self, adapter, device_id):
         kwargs = dict()
@@ -60,12 +60,17 @@
         self._upstream_channel_speed = 0
 
         self._unis = dict()         # Port # -> UniPort
-        self._pons = dict()         # Port # -> PonPort
+        self._pon = None
         self._heartbeat = HeartBeat.create(self, device_id)
 
         self._deferred = None
         self._event_deferred = None
-        self._omci = None
+
+        # TODO: Remove next two lines if/when OpenOMCI is in the core or a container
+        #       in order to support multiple ONUs per instance
+        self._omci_agent = OpenOMCIAgent(self.adapter_agent.core)
+        self._omci_agent.start()
+
         self._port_number_pool = IndexPool(_MAXIMUM_PORT, 1)
 
         self._olt_created = False   # True if deprecated method of OLT creating DA is used
@@ -128,8 +133,14 @@
         return self._olt_created    # ONU was created with deprecated 'child_device_detected' call
 
     @property
+    def omci_agent(self):
+        return self._omci_agent
+
+    @property
     def omci(self):
-        return self._omci
+        # TODO: Decrement access to Communications channel at this point?  What about current PM stuff?
+        _onu_omci_device = self._pon.onu_omci_device
+        return _onu_omci_device.omci_cc if _onu_omci_device is not None else None
 
     @property
     def heartbeat(self):
@@ -148,11 +159,8 @@
         return self._unis.get(port_no_or_name)
 
     @property
-    def pon_ports(self):
-        return self._pons.values()
-
-    def pon_port(self, port_no):
-        return self._pons.get(port_no)
+    def pon_port(self):
+        return self._pon
 
     @property
     def _next_port_number(self):
@@ -163,17 +171,10 @@
 
     def start(self):
         assert self._enabled, 'Start should only be called if enabled'
-        #
-        # TODO: Perform common startup tasks here
-        #
+
         self._cancel_deferred()
 
-        self._omci = OMCI_CC(self.adapter_agent,
-                             self.device_id,
-                             custom_me_entries=onu_custom_entity_classes)
-        self._omci.enabled = True
-
-        # Handle received ONU event messages
+        # Handle received ONU event messages   TODO: Deprecate this....
         self._event_messages = DeferredQueue()
         self._event_deferred = reactor.callLater(0, self._handle_onu_events)
 
@@ -181,10 +182,10 @@
         self.adapter_agent.register_for_inter_adapter_messages()
 
         # Port startup
-        for port in self.uni_ports:
-            port.enabled = True
+        if self._pon is not None:
+            self._pon.enabled = True
 
-        for port in self.pon_ports:
+        for port in self.uni_ports:
             port.enabled = True
 
         # Heartbeat
@@ -203,16 +204,16 @@
         # Heartbeat
         self._heartbeat.stop()
 
+        # OMCI Communications
+        # if self._onu_omci_device is not None:
+        #     self._onu_omci_device.stop()
+
         # Port shutdown
         for port in self.uni_ports:
             port.enabled = False
 
-        for port in self.pon_ports:
-            port.enabled = False
-
-        omci, self._omci = self._omci, None
-        if omci is not None:
-            omci.enabled = False
+        if self._pon is not None:
+            self._pon.enabled = False
 
         queue, self._event_deferred = self._event_deferred, None
         if queue is not None:
@@ -220,8 +221,8 @@
                 _ = yield queue.get()
 
     def receive_message(self, msg):
-        if self._omci is not None and self.enabled:
-            self._omci.receive_message(msg)
+        if self.omci is not None and self.enabled:
+            self.omci.receive_message(msg)
 
     def activate(self, device):
         self.log.info('activating')
@@ -271,28 +272,26 @@
         # Need to query ONU for number of supported uni ports
         # For now, temporarily set number of ports to 1 - port #2
 
-        # Register physical ports.  Should have at least one of each
-
-        pon_port = PonPort.create(self, self._next_port_number)
-
-        self._pons[pon_port.port_number] = pon_port
-        self.adapter_agent.add_port(device.id, pon_port.get_port())
-
         parent_device = self.adapter_agent.get_device(device.parent_id)
         self.logical_device_id = parent_device.parent_id
         assert self.logical_device_id, 'Invalid logical device ID'
 
+        # Register physical ports.  Should have at least one of each
+
+        self._pon = PonPort.create(self, self._next_port_number)
+        self.adapter_agent.add_port(device.id, self._pon.get_port())
+
         if self._olt_created:
             # vlan non-zero if created via legacy method (not xPON). Also
             # Set a random serial number since not xPON based
 
-            uni_port = UniPort.create(self, self._next_port_number,
-                                      'deprecated', device.vlan)
+            uni_port = UniPort.create(self, self._next_port_number, device.vlan,
+                                      'deprecated', device.vlan, None)
             self._unis[uni_port.port_number] = uni_port
             self.adapter_agent.add_port(device.id, uni_port.get_port())
 
             device.serial_number = uuid4().hex
-            uni_port.add_logical_port(device.vlan, control_vlan=device.vlan)
+            uni_port.add_logical_port(device.vlan, subscriber_vlan=device.vlan)
 
             # Start things up for this ONU Handler.
             self.enabled = True
@@ -376,7 +375,7 @@
         # self.log.info('bulk-flow-update', device_id=device.id, flows=flows)
 
         import voltha.core.flow_decomposer as fd
-        from voltha.protos.openflow_13_pb2 import OFPXMC_OPENFLOW_BASIC, ofp_port
+        from voltha.protos.openflow_13_pb2 import OFPXMC_OPENFLOW_BASIC
 
         def is_downstream(port):
             return port == 100  # Need a better way
@@ -384,7 +383,7 @@
         def is_upstream(port):
             return not is_downstream(port)
 
-        omci = self._omci
+        omci = self.omci
 
         for flow in flows:
             _type = None
@@ -482,7 +481,7 @@
 
                     elif action.type == fd.PUSH_VLAN:
                         _push_tpid = action.push.ethertype
-                        log.info('action-type-push-vlan',
+                        self.log.info('action-type-push-vlan',
                                  push_tpid=_push_tpid, in_port=_in_port)
                         if action.push.ethertype != 0x8100:
                             self.log.error('unhandled-tpid',
@@ -501,9 +500,8 @@
                             self.log.error('unsupported-action-set-field-type',
                                            field_type=_field.type)
                     else:
-                        log.error('unsupported-action-type',
-                                  action_type=action.type, in_port=_in_port)
-
+                        self.log.error('unsupported-action-type',
+                                       action_type=action.type, in_port=_in_port)
                 #
                 # All flows created from ONU adapter should be OMCI based
                 #
@@ -541,7 +539,7 @@
                     '''
 
             except Exception as e:
-                log.exception('failed-to-install-flow', e=e, flow=flow)
+                self.log.exception('failed-to-install-flow', e=e, flow=flow)
 
     @inlineCallbacks
     def reboot(self):
@@ -627,7 +625,6 @@
 
         self.log.info('reboot-complete', device_id=self.device_id)
 
-
     def self_test_device(self, device):
         """
         This is called to Self a device based on a NBI call.
@@ -673,9 +670,9 @@
                               portid=port_id)
 
         # Remove pon port from parent
-        for port in self.pon_ports:
+        if self._pon is not None:
             self.adapter_agent.delete_port_reference_from_parent(self.device_id,
-                                                                 port.get_port())
+                                                                 self._pon.get_port())
 
         # Just updating the port status may be an option as well
         # port.ofp_port.config = OFPPC_NO_RECV
@@ -715,10 +712,10 @@
             # self.uni_port = self._get_uni_port()   deprecated
 
             # Add the pon port reference to the parent
-            for port in self.pon_ports:
+            if self._pon is not None:
                 # TODO: Send 'enable' to PonPort?
                 self.adapter_agent.add_port_reference_to_parent(device.id,
-                                                                port.get_port())
+                                                                self._pon.get_port())
 
             # Update the connect status to REACHABLE
             device.connect_status = ConnectStatus.REACHABLE
@@ -732,7 +729,7 @@
             if self.olt_created:
                 # vlan non-zero if created via legacy method (not xPON)
                 self.uni_port('deprecated').add_logical_port(device.vlan, device.vlan,
-                                                             control_vlan=device.vlan)
+                                                             subscriber_vlan=device.vlan)
 
             device = self.adapter_agent.get_device(device.id)
             device.oper_status = OperStatus.ACTIVE
@@ -747,6 +744,12 @@
 
     def delete(self):
         self.log.info('deleting', device_id=self.device_id)
+
+        # OpenOMCI cleanup
+        if self._omci_agent is not None:
+            self._omci_agent.remove_device(self.device_id, cleanup=True)
+            #self._onu_omci_device = None
+            self._omci_agent = None
         #
         # handling needed here
         # self.enabled = False
@@ -783,7 +786,7 @@
     def _check_for_mock_config(self, data):
         # Check for MOCK configuration
         description = data.get('description')
-        if description is not None and description.lower() == 'mock':
+        if description is not None and 'mock' in description.lower():
             self._is_mock = True
 
     def on_ont_ani_create(self, ont_ani):
@@ -919,16 +922,22 @@
             # Set a random serial number since not xPON based
 
             device = self.adapter_agent.get_device(self.device_id)
-            ofp_port_no, cntl_vlan = UniPort.decode_openflow_port_and_control_vlan(self, venet)
+            ofp_port_no, subscriber_vlan, untagged_vlan = UniPort.decode_venet(venet)
 
             uni_port = UniPort.create(self, venet['name'],
                                       self._next_port_number,
-                                      cntl_vlan)
+                                      ofp_port_no,
+                                      subscriber_vlan,
+                                      untagged_vlan)
 
             self._unis[uni_port.port_number] = uni_port
             self.adapter_agent.add_port(device.id, uni_port.get_port())
 
-            uni_port.add_logical_port(ofp_port_no, control_vlan=cntl_vlan)
+            # If the PON has already synchronized, add the logical port now
+            # since we know we have been activated
+
+            if self._pon is not None and self._pon.connected:
+                uni_port.add_logical_port(ofp_port_no, subscriber_vlan=subscriber_vlan)
 
         # TODO: Next is just for debugging to see what this call returns after
         #       we add a UNI
@@ -987,11 +996,8 @@
         tcont['object'] = OnuTCont.create(self, tcont, traffic_descriptor,
                                           is_mock=self.is_mock)
 
-        # Look up any PON port  # TODO: Add the vont-ani 'name' to the PON Port and look up that way
-        pon_port = self.pon_ports[0]
-
-        if pon_port is not None:
-            pon_port.add_tcont(tcont['object'])
+        if self._pon is not None:
+            self._pon.add_tcont(tcont['object'])
 
         return tcont
 
@@ -1007,25 +1013,20 @@
 
         update['object'] = tc
 
-        # Look up any PON port  # TODO: Add the vont-ani 'name' to the PON Port and look up that way
-        pon_port = self.pon_ports[0]
-
-        if pon_port is not None:
+        if self._pon is not None:
             keys = [k for k in diffs.keys() if k in valid_keys]
 
             for k in keys:
                 if k == 'td-ref':
                     td = self.traffic_descriptors.get(update['td-ref'])
                     if td is not None:
-                        pon_port.update_tcont_td(tcont['alloc-id'], td)
+                        self._pon.update_tcont_td(tcont['alloc-id'], td)
 
         return update
 
     def on_tcont_delete(self, tcont):
-        pon_port = self.pon_ports[0]        # Look up any PON port  # TODO: Add the vont-ani 'name' to the PON Port and look up that way
-
-        if pon_port is not None:
-            pon_port.remove_tcont(tcont['alloc-id'])
+        if self._pon is not None:
+            self._pon.remove_tcont(tcont['alloc-id'])
 
         return None
 
@@ -1056,10 +1057,8 @@
                   if val['td-ref'] == td_name and td_name is not None}
 
         for tcont in tconts.itervalues():
-            pon_port = self.pon_ports[0]        # Look up any PON port  # TODO: Add the vont-ani 'name' to the PON Port and look up that way
-
-            if pon_port is not None:
-                pon_port.update_tcont_td(tcont['alloc-id'], update['object'])
+            if self._pon is not None:
+                self._pon.update_tcont_td(tcont['alloc-id'], update['object'])
 
         return update
 
@@ -1074,13 +1073,12 @@
 
     def on_gemport_create(self, gem_port):
         from onu_gem_port import OnuGemPort
+        assert self._pon is not None, 'No PON port'
 
-        gem_port['object'] = OnuGemPort.create(self, gem_port, is_mock=self.is_mock)
-        # Look up any PON port  # TODO: Add the vont-ani 'name' to the PON Port and look up that way
-        pon_port = self.pon_ports[0]
-        if pon_port is not None:
-            pon_port.add_gem_port(gem_port['object'])
-
+        gem_port['object'] = OnuGemPort.create(self, gem_port,
+                                               self._pon.next_gem_entity_id,
+                                               is_mock=self.is_mock)
+        self._pon.add_gem_port(gem_port['object'])
         return gem_port
 
     def on_gemport_modify(self, gem_port, update, diffs):
@@ -1106,9 +1104,8 @@
         return update
 
     def on_gemport_delete(self, gem_port):
-        pon_port = self.pon_ports[0]        # Look up any PON port  # TODO: Add the vont-ani 'name' to the PON Port and look up that way
-        if pon_port is not None:
-            pon_port.remove_gem_id(gem_port['gemport-id'])
+        if self._pon is not None:
+            self._pon.remove_gem_id(gem_port['gemport-id'])
 
         return None