VOL-927: ADTRAN-OLT: Fix for flow setup race condition

Change-Id: I9367bd01cf41617252e211e9353ad10b8f57e719
diff --git a/voltha/adapters/adtran_olt/adtran_olt.py b/voltha/adapters/adtran_olt/adtran_olt.py
index 82015b8..9b846f0 100644
--- a/voltha/adapters/adtran_olt/adtran_olt.py
+++ b/voltha/adapters/adtran_olt/adtran_olt.py
@@ -51,7 +51,7 @@
         self.descriptor = Adapter(
             id=self.name,
             vendor='Adtran, Inc.',
-            version='0.15',
+            version='0.16',
             config=AdapterConfig(log_level=LogLevel.INFO)
         )
         log.debug('adtran_olt.__init__', adapter_agent=adapter_agent)
diff --git a/voltha/adapters/adtran_olt/flow/evc.py b/voltha/adapters/adtran_olt/flow/evc.py
index 4260c66..0a762fc 100644
--- a/voltha/adapters/adtran_olt/flow/evc.py
+++ b/voltha/adapters/adtran_olt/flow/evc.py
@@ -209,7 +209,7 @@
         Get all EVC Maps that reference this EVC
         :return: list of EVCMap
         """
-        return list(self._evc_maps.values())
+        return list(self._evc_maps.values()) if self._evc_maps is not None else []
 
     @property
     def evc_map_names(self):
@@ -217,11 +217,12 @@
         Get all EVC Map names that reference this EVC
         :return: list of EVCMap names
         """
-        return list(self._evc_maps.keys())
+        return list(self._evc_maps.keys()) if self._evc_maps is not None else []
 
     def add_evc_map(self, evc_map):
-        if self._evc_maps is not None:
-            self._evc_maps[evc_map.name] = evc_map
+        if self._evc_maps is None:
+            self._evc_maps = {}
+        self._evc_maps[evc_map.name] = evc_map
 
     def remove_evc_map(self, evc_map):
         if self._evc_maps is not None and evc_map.name in self._evc_maps:
diff --git a/voltha/adapters/adtran_olt/flow/evc_map.py b/voltha/adapters/adtran_olt/flow/evc_map.py
index 40892aa..239113e 100644
--- a/voltha/adapters/adtran_olt/flow/evc_map.py
+++ b/voltha/adapters/adtran_olt/flow/evc_map.py
@@ -231,7 +231,7 @@
         # self._udp_src = None
         return xml
 
-    def _ingress_install_xml(self, onu_s_gem_ids_and_vid, acl_list):
+    def _ingress_install_xml(self, onu_s_gem_ids_and_vid, acl_list, create):
         from ..onu import Onu
 
         if len(acl_list):
@@ -247,7 +247,7 @@
                 else onu_or_vlan_id
 
             for gem_id in gem_ids_and_vid[0]:
-                xml += '<evc-map>'
+                xml += '<evc-map{}>'.format('' if not create else ' xc:operation="create"')
                 xml += '<name>{}.{}.{}</name>'.format(self.name, ident, gem_id)
                 xml += '<ce-vlan-id>{}</ce-vlan-id>'.format(Onu.gem_id_to_gvid(gem_id))
 
@@ -326,12 +326,12 @@
             if not self._installed or self._needs_update:
                 try:
                     self._cancel_deferred()
-                    map_xml = self._ingress_install_xml(self._gem_ids_and_vid, work_acls.values()) \
+                    map_xml = self._ingress_install_xml(self._gem_ids_and_vid, work_acls.values(),
+                                                        not self._installed) \
                         if self._is_ingress_map else self._egress_install_xml()
 
                     log.debug('install', xml=map_xml, name=self.name)
                     results = yield self._handler.netconf_client.edit_config(map_xml)
-                    was_installed = self._installed
                     self._installed = results.ok
                     self._needs_update = results.ok
                     self.status = '' if results.ok else results.error
@@ -466,6 +466,7 @@
 
         log.debug('find-matching-ingress-flow', logical_port=flow.logical_port, flow=flow.output)
         candidate_flows = [f for f in upstream_flow_table.itervalues() if
+                           f.in_port == flow.in_port and
                            f.logical_port == flow.logical_port and
                            f.output == flow.output and
                            f.evc_map is not None]        # This weeds out this flow
diff --git a/voltha/adapters/adtran_olt/pon_port.py b/voltha/adapters/adtran_olt/pon_port.py
index dd2c3df..d0281ca 100644
--- a/voltha/adapters/adtran_olt/pon_port.py
+++ b/voltha/adapters/adtran_olt/pon_port.py
@@ -293,7 +293,7 @@
         Update the port status and state in the core
         """
         self.log.debug('update-adapter-agent', admin_state=self._admin_state,
-            oper_status=self._oper_status)
+                       oper_status=self._oper_status)
 
         # because the core does not provide methods for updating admin
         # and oper status per port, we need to copy any existing port
@@ -305,7 +305,7 @@
 
             # copy current Port info
             if agent_port is not None:
-                self._port = agent_port;
+                self._port = agent_port
 
         # set new states
         self._port.admin_state = self._admin_state
@@ -836,8 +836,8 @@
             # Hold off ONU activation until at least one GEM Port is defined.
             self.log.debug('onu-info', gem_ports=gem_ports)
 
-            return onu_info
-            # return onu_info if len(gem_ports) > 0 else None
+            # return onu_info
+            return onu_info if len(gem_ports) > 0 and venet is not None else None
 
         except Exception as e:
             self.log.exception('get-onu-info', e=e)
diff --git a/voltha/adapters/adtran_onu/adtran_onu.py b/voltha/adapters/adtran_onu/adtran_onu.py
index b9e8c2f..1f76dfb 100755
--- a/voltha/adapters/adtran_onu/adtran_onu.py
+++ b/voltha/adapters/adtran_onu/adtran_onu.py
@@ -22,13 +22,15 @@
 from voltha.adapters.iadapter import OnuAdapter
 from voltha.protos import third_party
 from adtran_onu_handler import AdtranOnuHandler
+from voltha.extensions.omci.openomci_agent import OpenOMCIAgent, OpenOmciAgentDefaults
+from voltha.extensions.omci.database.mib_db_dict import MibDbVolatileDict
 from twisted.internet import reactor
+from copy import deepcopy
 
 _ = third_party
 
 
 class AdtranOnuAdapter(OnuAdapter):
-
     def __init__(self, adapter_agent, config):
         self.log = structlog.get_logger()
         super(AdtranOnuAdapter, self).__init__(adapter_agent=adapter_agent,
@@ -36,9 +38,31 @@
                                                device_handler_class=AdtranOnuHandler,
                                                name='adtran_onu',
                                                vendor='Adtran, Inc.',
-                                               version='0.7',
+                                               version='0.9',
                                                device_type='adtran_onu',
                                                vendor_id='ADTN')
+        # Customize OpenOMCI for Adtran ONUs
+        self.adtran_omci = deepcopy(OpenOmciAgentDefaults)
+
+        # TODO: Continue to customize adtran_omci here as needed
+
+        self._omci_agent = OpenOMCIAgent(self.adapter_agent.core,
+                                         support_classes=self.adtran_omci)
+
+    @property
+    def omci_agent(self):
+        return self._omci_agent
+
+    def start(self):
+        super(AdtranOnuAdapter, self).start()
+        self._omci_agent.start()
+
+    def stop(self):
+        omci, self._omci_agent = self._omci_agent, None
+        if omci is not None:
+            omci.stop()
+
+        super(AdtranOnuAdapter, self).stop()
 
     def suppress_alarm(self, filter):
         raise NotImplementedError()
diff --git a/voltha/adapters/adtran_onu/adtran_onu_handler.py b/voltha/adapters/adtran_onu/adtran_onu_handler.py
index 86a70d5..bba4872 100644
--- a/voltha/adapters/adtran_onu/adtran_onu_handler.py
+++ b/voltha/adapters/adtran_onu/adtran_onu_handler.py
@@ -32,9 +32,7 @@
 
 from voltha.protos import third_party
 from voltha.protos.common_pb2 import OperStatus, ConnectStatus
-from voltha.protos.device_pb2 import Image
 from common.utils.indexpool import IndexPool
-from voltha.extensions.omci.openomci_agent import OpenOMCIAgent
 from voltha.extensions.omci.omci_me import *
 
 _ = third_party
@@ -67,11 +65,6 @@
         self._deferred = None
         self._event_deferred = 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
@@ -135,7 +128,7 @@
 
     @property
     def omci_agent(self):
-        return self._omci_agent
+        return self.adapter.omci_agent
 
     @property
     def omci(self):
@@ -589,6 +582,9 @@
         device.reason = 'reboot in progress'
         self.adapter_agent.update_device(device)
 
+        # Disable OpenOMCI
+        self.pon_port.enabled = False
+
         self._deferred = reactor.callLater(_ONU_REBOOT_MIN,
                                            self._finish_reboot,
                                            previous_oper_status,
@@ -609,6 +605,10 @@
         # real OLT the operational state should be the state the device is
         # after a reboot.
         # Get the latest device reference
+
+        # Restart OpenOMCI
+        self.pon_port.enabled = True
+
         device = self.adapter_agent.get_device(self.device_id)
 
         device.oper_status = previous_oper_status
@@ -782,9 +782,8 @@
         self._pon.delete()
 
         # OpenOMCI cleanup
-        if self._omci_agent is not None:
-            self._omci_agent.remove_device(self.device_id, cleanup=True)
-            self._omci_agent = None
+        if self.omci_agent is not None:
+            self.omci_agent.remove_device(self.device_id, cleanup=True)
 
     def _check_for_mock_config(self, data):
         # Check for MOCK configuration
diff --git a/voltha/adapters/adtran_onu/onu_gem_port.py b/voltha/adapters/adtran_onu/onu_gem_port.py
index 9d5bc61..0cff8ac 100644
--- a/voltha/adapters/adtran_onu/onu_gem_port.py
+++ b/voltha/adapters/adtran_onu/onu_gem_port.py
@@ -15,7 +15,7 @@
 
 import structlog
 from voltha.adapters.adtran_olt.xpon.gem_port import GemPort
-from twisted.internet.defer import inlineCallbacks, returnValue, succeed
+from twisted.internet.defer import inlineCallbacks, returnValue
 from voltha.extensions.omci.omci_me import GemPortNetworkCtpFrame, GemInterworkingTpFrame
 
 
@@ -31,7 +31,6 @@
                  traffic_class=None,
                  intf_ref=None,
                  untagged=False,
-                 exception=False,  # FIXED_ONU
                  name=None,
                  handler=None,
                  is_mock=False):
@@ -43,7 +42,6 @@
                                          traffic_class=traffic_class,
                                          intf_ref=intf_ref,
                                          untagged=untagged,
-                                         exception=exception,
                                          name=name,
                                          handler=handler)
         self._is_mock = is_mock
diff --git a/voltha/adapters/adtran_onu/onu_pm_metrics.py b/voltha/adapters/adtran_onu/onu_pm_metrics.py
index 52e1fc7..d4aa5f6 100644
--- a/voltha/adapters/adtran_onu/onu_pm_metrics.py
+++ b/voltha/adapters/adtran_onu/onu_pm_metrics.py
@@ -32,6 +32,7 @@
             ('rx_alarm_overflow', PmConfig.COUNTER),    # Autonomous ONU generated alarm message overflows
             ('rx_avc_overflow', PmConfig.COUNTER),      # Autonomous ONU generated AVC message overflows
             ('rx_onu_discards', PmConfig.COUNTER),      # Autonomous ONU message unknown type discards
+            ('rx_unknown_me', PmConfig.COUNTER),        # Managed Entities without a decode definition
             ('rx_timeouts', PmConfig.COUNTER),
             ('consecutive_errors', PmConfig.COUNTER),
             ('reply_min', PmConfig.GUAGE),      # Milliseconds
diff --git a/voltha/adapters/adtran_onu/pon_port.py b/voltha/adapters/adtran_onu/pon_port.py
index 40dee83..63c410e 100644
--- a/voltha/adapters/adtran_onu/pon_port.py
+++ b/voltha/adapters/adtran_onu/pon_port.py
@@ -58,7 +58,8 @@
 
         self._onu_omci_device = handler.omci_agent.add_device(handler.device_id,
                                                               handler.adapter_agent,
-                                                              onu_custom_me_entities())
+                                                              onu_custom_me_entities(),
+                                                              support_classes=handler.adapter.adtran_omci)
         # TODO: Add stats, alarm reference, ...
 
     def __str__(self):
@@ -97,6 +98,8 @@
         self._admin_state = AdminState.DISABLED
         self._oper_status = OperStatus.UNKNOWN
         self._update_adapter_agent()
+
+        self._dev_info_loaded = False
         # TODO: stop h/w sync
         pass
 
@@ -495,13 +498,12 @@
                 #            -
                 # TODO: All p-bits currently go to the one and only GEMPORT ID for now
 
-                gem_entity_ids = []
-                for gem_port in self._gem_ports.itervalues():
-                    gem_entity_ids.append(gem_port.entity_id)
+                gem_entity_ids = [gem_port.entity_id for _, gem_port in self._gem_ports.items()] \
+                    if len(self._gem_ports) else [OmciNullPointer]
 
                 frame = Ieee8021pMapperServiceProfileFrame(
-                    ieee_mapper_service_profile_entity_id,      # 802.1p mapper Service Mapper Profile ID
-                    interwork_tp_pointers=gem_entity_ids  # Interworking TP IDs  BP: oldvalue self.gemid
+                    ieee_mapper_service_profile_entity_id,   # 802.1p mapper Service Mapper Profile ID
+                    interwork_tp_pointers=gem_entity_ids     # Interworking TP IDs  BP: oldvalue self.gemid
                 ).set()
                 results = yield omci.send(frame)
 
@@ -811,7 +813,7 @@
                     in_sync = msg[IN_SYNC_KEY]
 
                     if in_sync:
-                        # Only call this once as well
+                        # Only call this once as well (after PON enable)
                         bus = self._onu_omci_device.event_bus
                         bus.unsubscribe(self._in_sync_subscription)
                         self._in_sync_subscription = None