ADTRAN OLT: Support ONU removal on LOS

Change-Id: If4b5d2de7b3b340fc2101b64e9775e942eb8e442
diff --git a/voltha/adapters/adtran_olt/adtran_olt.py b/voltha/adapters/adtran_olt/adtran_olt.py
index 21ce715..b38843b 100644
--- a/voltha/adapters/adtran_olt/adtran_olt.py
+++ b/voltha/adapters/adtran_olt/adtran_olt.py
@@ -52,7 +52,7 @@
         self.descriptor = Adapter(
             id=self.name,
             vendor='ADTRAN, Inc.',
-            version='1.35',
+            version='1.36',
             config=AdapterConfig(log_level=LogLevel.INFO)
         )
         log.debug('adtran_olt.__init__', adapter_agent=adapter_agent)
diff --git a/voltha/adapters/adtran_olt/adtran_olt_handler.py b/voltha/adapters/adtran_olt/adtran_olt_handler.py
index f1228a8..8a88ea1 100644
--- a/voltha/adapters/adtran_olt/adtran_olt_handler.py
+++ b/voltha/adapters/adtran_olt/adtran_olt_handler.py
@@ -672,17 +672,6 @@
 
         super(AdtranOltHandler, self).delete()
 
-    def delete_child_device(self, proxy_address):
-        super(AdtranOltHandler, self).delete_child_device(proxy_address)
-
-        # TODO: Verify that ONU object cleanup of ONU will also clean
-        #       up logical id and physical port
-        # pon_intf_id_onu_id = (proxy_address.channel_id,
-        #                       proxy_address.onu_id)
-
-        # Free any PON resources that were reserved for the ONU
-        # TODO: Done in onu delete now -> self.resource_mgr.free_pon_resources_for_onu(pon_intf_id_onu_id)
-
     def rx_pa_packet(self, packets):
         if self._pon_agent is not None:
             for packet in packets:
@@ -1296,8 +1285,10 @@
         try:
             from voltha.protos.voltha_pb2 import Device
             # NOTE - channel_id of onu is set to pon_id
+            pon_port = self.pon_id_to_port_number(pon_id)
             proxy_address = Device.ProxyAddress(device_id=self.device_id,
-                                                channel_id=pon_id, onu_id=onu_id,
+                                                channel_id=pon_port,
+                                                onu_id=onu_id,
                                                 onu_session_id=onu_id)
 
             self.log.debug("added-onu", port_no=pon_id,
@@ -1306,7 +1297,7 @@
 
             self.adapter_agent.add_onu_device(
                 parent_device_id=self.device_id,
-                parent_port_no=pon_id,
+                parent_port_no=pon_port,
                 vendor_id=serial_number[:4],
                 proxy_address=proxy_address,
                 root=True,
diff --git a/voltha/adapters/adtran_olt/flow/evc_map.py b/voltha/adapters/adtran_olt/flow/evc_map.py
index ee19df9..5f0b227 100644
--- a/voltha/adapters/adtran_olt/flow/evc_map.py
+++ b/voltha/adapters/adtran_olt/flow/evc_map.py
@@ -821,9 +821,9 @@
                 for pb_tcont in pb_tconts:
                     from ..xpon.olt_tcont import OltTCont
                     tcont = OltTCont.create(pb_tcont,
-                                            self._tech_profile_id,
                                             self.pon_id,
                                             self.onu_id,
+                                            self._tech_profile_id,
                                             uni_id,
                                             ofp_port_no)
                     if tcont is not None:
@@ -846,8 +846,6 @@
                                                  ofp_port_no)
                     if gem_port is not None:
                         onu.add_gem_port(gem_port)
-                #
-                #
 
                 self._gem_ids_and_vid = {onu.onu_id: (onu_gems, flow.vlan_id)}
 
diff --git a/voltha/adapters/adtran_olt/onu.py b/voltha/adapters/adtran_olt/onu.py
index a1c3314..91f87a6 100644
--- a/voltha/adapters/adtran_olt/onu.py
+++ b/voltha/adapters/adtran_olt/onu.py
@@ -19,6 +19,7 @@
 from twisted.internet import reactor, defer
 from twisted.internet.defer import inlineCallbacks, returnValue, succeed
 from common.tech_profile.tech_profile import DEFAULT_TECH_PROFILE_TABLE_ID
+from voltha.protos.device_pb2 import Device
 
 from adtran_olt_handler import AdtranOltHandler
 from net.adtran_rest import RestInvalidResponseCode
@@ -50,9 +51,11 @@
         self._serial_number_string = onu_info['serial-number']
         self._device_id = onu_info['device-id']
         self._password = onu_info['password']
-
         self._created = False
-        self._proxy_address = None
+        self._proxy_address = Device.ProxyAddress(device_id=self.olt.device_id,
+                                                  channel_id=self.olt.pon_id_to_port_number(self._pon_id),
+                                                  onu_id=self._onu_id,
+                                                  onu_session_id=self._onu_id)
         self._sync_tick = _HW_SYNC_SECS
         self._expedite_sync = False
         self._expedite_count = 0
@@ -66,8 +69,6 @@
         # Provisionable items
         self._enabled = onu_info['enabled']
         self._upstream_fec_enable = onu_info.get('upstream-fec')
-        self._upstream_channel_speed = onu_info['upstream-channel-speed']
-        # TODO: how do we want to enforce upstream channel speed (if at all)?
 
         # KPI related items
         self._rssi = -9999
@@ -134,16 +135,6 @@
         self.pon.upstream_fec_enable = self.pon.any_upstream_fec_enabled
 
     @property
-    def upstream_channel_speed(self):
-        return self._upstream_channel_speed
-
-    @upstream_channel_speed.setter
-    def upstream_channel_speed(self, value):
-        assert isinstance(value, (int, float)), 'upstream speed is a numeric value'
-        if self._upstream_channel_speed != value:
-            self._upstream_channel_speed = value
-
-    @property
     def password(self):
         """
         Get password.  Base 64 format
@@ -206,14 +197,6 @@
 
     @property
     def proxy_address(self):
-        if self._proxy_address is None:
-            from voltha.protos.device_pb2 import Device
-
-            device_id = self.olt.device_id
-            self._proxy_address = Device.ProxyAddress(device_id=device_id,
-                                                      channel_id=self.pon.port_no,
-                                                      onu_id=self.onu_id,
-                                                      onu_session_id=self.onu_id)
         return self._proxy_address
 
     @property
@@ -311,7 +294,7 @@
                     if len(results) == 1 and results[0].get('serial-number', '') != self._serial_number_base64:
                         self._created = True
 
-                except Exception as e:
+                except Exception as _e:
                     self.log.warn('onu-exists-check', pon_id=self.pon_id, onu_id=self.onu_id,
                                   serial_number=self.serial_number)
 
@@ -354,13 +337,13 @@
 
         self._gem_ports.clear()
         self._tconts.clear()
+        olt, self._olt = self._olt, None
 
         uri = AdtranOltHandler.GPON_ONU_CONFIG_URI.format(self._pon_id, self._onu_id)
         name = 'onu-delete-{}-{}-{}: {}'.format(self._pon_id, self._onu_id,
                                                 self._serial_number_base64, self._enabled)
         try:
-            yield self.olt.rest_client.request('DELETE', uri, name=name)
-            self._olt = None
+            yield olt.rest_client.request('DELETE', uri, name=name)
 
         except RestInvalidResponseCode as e:
             if e.code != 404:
@@ -371,7 +354,7 @@
 
         # Release resource manager resources for this ONU
         pon_intf_id_onu_id = (self.pon_id, self.onu_id)
-        self._olt.resource_mgr.free_pon_resources_for_onu(pon_intf_id_onu_id)
+        olt.resource_mgr.free_pon_resources_for_onu(pon_intf_id_onu_id)
 
         returnValue('deleted')
 
@@ -679,12 +662,6 @@
         if not reflow and gem_port.gem_id in self._gem_ports:
             returnValue('nop')
 
-        gem_port.pon_id = self.pon_id
-        gem_port.onu_id = self.onu_id if self.onu_id is not None else -1
-        gem_port.intf_id = self.intf_id
-
-        # TODO: Currently only support a single UNI. Need to support multiple and track their GEM Ports
-        #       Probably best done by having a UNI-Port class (keep it simple)
         self.log.info('add', gem_port=gem_port, reflow=reflow)
         self._gem_ports[gem_port.gem_id] = gem_port
 
diff --git a/voltha/adapters/adtran_olt/pon_port.py b/voltha/adapters/adtran_olt/pon_port.py
index c9e18c8..bf7541b 100644
--- a/voltha/adapters/adtran_olt/pon_port.py
+++ b/voltha/adapters/adtran_olt/pon_port.py
@@ -576,7 +576,7 @@
                         # be handle by ONU H/w sync logic.
                         for onu in [self._onu_by_id[onu_id] for onu_id in my_onu_ids - hw_onu_ids
                                     if self._onu_by_id.get(onu_id) is not None]:
-                            dl.append(onu.create(dict(), dict(), reflow=True))
+                            dl.append(onu.create(reflow=True))
 
                     return defer.gatherResults(dl, consumeErrors=True)
 
@@ -729,7 +729,6 @@
             elif self.activation_method == "autoactivate":
                 onu_id = self.get_next_onu_id
                 enabled = True
-                channel_speed = 10000000000
                 upstream_fec_enabled = True
 
             else:
@@ -742,7 +741,6 @@
                 'pon': self,
                 'onu-id': onu_id,
                 'enabled': enabled,
-                'upstream-channel-speed': channel_speed,
                 'upstream-fec': upstream_fec_enabled,
                 'password': Onu.DEFAULT_PASSWORD,
             }
@@ -797,13 +795,13 @@
         reactor.callLater(0, alarm.raise_alarm)
 
         # Have the core create the ONU device
-        self._parent.add_onu_device(self._port_no, onu_id, serial_number)
-
-        onu = Onu(onu_info)
-        self._onus[serial_number_64] = onu
-        self._onu_by_id[onu.onu_id] = onu
+        self._parent.add_onu_device(self.pon_id, onu_id, serial_number)
 
         try:
+            onu = Onu(onu_info)
+            self._onus[serial_number_64] = onu
+            self._onu_by_id[onu.onu_id] = onu
+
             # Add Multicast to PON on a per-ONU basis
             #
             # for id_or_vid, gem_port in gem_ports.iteritems():
@@ -853,10 +851,9 @@
         if onu_id in self._onu_by_id:
             del self._onu_by_id[onu_id]
 
-        for sn_64 in [onu.serial_number_64 for onu in self.onus if onu.onu_id == onu_id]:
-            del self._onus[sn_64]
-
         if onu is not None:
+            if onu.serial_number_64 in self._onus:
+                del self._onus[onu.serial_number_64]
             try:
                 proxy_address = onu.proxy_address
                 onu.delete()                            # Remove from hardware
@@ -867,7 +864,6 @@
 
             except Exception as e:
                 self.log.exception('onu-delete', serial_number=onu.serial_number, e=e)
-
         else:
             try:
                 yield self._remove_from_hardware(onu_id)
@@ -875,7 +871,7 @@
             except Exception as e:
                 self.log.debug('onu-remove', serial_number=onu.serial_number, e=e)
 
-        # Remove from LOS list if needed
+        # Remove from LOS list if needed  TODO: Should a 'clear' alarm be sent as well ?
         if onu is not None and onu.id in self._active_los_alarms:
             self._active_los_alarms.remove(onu.id)
 
diff --git a/voltha/adapters/adtran_olt/resources/adtran_resource_manager.py b/voltha/adapters/adtran_olt/resources/adtran_resource_manager.py
index db56616..14efbf7 100644
--- a/voltha/adapters/adtran_olt/resources/adtran_resource_manager.py
+++ b/voltha/adapters/adtran_olt/resources/adtran_resource_manager.py
@@ -23,7 +23,6 @@
 from bitstring import BitArray
 import json
 from common.pon_resource_manager.resource_manager import PONResourceManager
-from adtran_tech_profile import AdtnTechProfile
 import adtranolt_platform as platform
 
 
diff --git a/voltha/adapters/adtran_olt/xpon/gem_port.py b/voltha/adapters/adtran_olt/xpon/gem_port.py
index d14b4f2..14dccb1 100644
--- a/voltha/adapters/adtran_olt/xpon/gem_port.py
+++ b/voltha/adapters/adtran_olt/xpon/gem_port.py
@@ -17,33 +17,22 @@
     """
     Class to wrap TCont capabilities
     """
-    def __init__(self, gem_id, alloc_id, tech_profile_id,
+    def __init__(self, gem_id, alloc_id, uni_id, tech_profile_id,
                  encryption=False,
-                 omci_transport=False,
                  multicast=False,
-                 tcont_ref=None,
                  traffic_class=None,
                  handler=None,
                  is_mock=False):
 
         self.gem_id = gem_id
         self._alloc_id = alloc_id
+        self.uni_id = uni_id
         self.tech_profile_id = tech_profile_id
-        self.tcont_ref = tcont_ref
         self.traffic_class = traffic_class
         self._encryption = encryption
-        self._omci_transport = omci_transport
         self.multicast = multicast
         self._handler = handler
         self._is_mock = is_mock
-
-        # TODO: Make this a base class and derive OLT and ONU specific classes from it
-        #       The primary thing to change is the PON ID is OLT specific and the add/remove
-        #       from hardware methods
-        self._pon_id = None
-        self._onu_id = None
-        self._intf_id = None
-
         self.tech_profile_id = None     # TODO: Make property and clean up object once tech profiles fully supported
 
         # Statistics
@@ -53,63 +42,22 @@
         self.tx_bytes = 0
 
     def __str__(self):
-        return "GemPort: alloc-id: {}, gem-id: {}".format(self.alloc_id,self.gem_id)
-
-    @property
-    def pon_id(self):
-        return self._pon_id
-
-    @pon_id.setter
-    def pon_id(self, pon_id):
-        assert self._pon_id is None or self._pon_id == pon_id, 'PON-ID can only be set once'
-        self._pon_id = pon_id
-
-    @property
-    def onu_id(self):
-        return self._onu_id
-
-    @onu_id.setter
-    def onu_id(self, onu_id):
-        assert self._onu_id is None or self._onu_id == onu_id, 'ONU-ID can only be set once'
-        self._onu_id = onu_id
-
-    @property
-    def intf_id(self):
-        return self._intf_id
-
-    @intf_id.setter
-    def intf_id(self, intf_id):
-        assert self._intf_id is None or self._intf_id == intf_id, 'Port Number can only be set once'
-        self._intf_id = intf_id
+        return "GemPort: alloc-id: {}, gem-id: {}, uni-id: {}".format(self.alloc_id,
+                                                                      self.gem_id,
+                                                                      self.uni_id)
 
     @property
     def alloc_id(self):
-        if self._alloc_id is None and self._handler is not None:
-            try:
-                self._alloc_id = self._handler.tconts.get(self.tcont_ref).get('alloc-id')
-
-            except Exception:
-                pass
-
         return self._alloc_id
 
     @property
-    def tcont(self):
-        tcont_item = self._handler.tconts.get(self.tcont_ref)
-        return tcont_item.get('object') if tcont_item is not None else None
-
-    @property
     def encryption(self):
         return self._encryption
 
-    @property
-    def omci_transport(self):
-        return self._omci_transport
-
     def to_dict(self):
         return {
             'port-id': self.gem_id,
             'alloc-id': self.alloc_id,
             'encryption': self._encryption,
-            'omci-transport': self.omci_transport
+            'omci-transport': False
         }
diff --git a/voltha/adapters/adtran_olt/xpon/olt_gem_port.py b/voltha/adapters/adtran_olt/xpon/olt_gem_port.py
index 3d2210d..272fb07 100644
--- a/voltha/adapters/adtran_olt/xpon/olt_gem_port.py
+++ b/voltha/adapters/adtran_olt/xpon/olt_gem_port.py
@@ -27,41 +27,46 @@
     """
     Adtran OLT specific implementation
     """
-    def __init__(self, gem_id, alloc_id, tech_profile_id, pon_id, onu_id,
+    def __init__(self, gem_id, alloc_id, tech_profile_id, pon_id, onu_id, uni_id,
                  encryption=False,
-                 omci_transport=False,
                  multicast=False,
-                 tcont_ref=None,
                  traffic_class=None,
                  handler=None,
                  is_mock=False):
-        super(OltGemPort, self).__init__(gem_id, alloc_id, tech_profile_id,
+        super(OltGemPort, self).__init__(gem_id, alloc_id, uni_id, tech_profile_id,
                                          encryption=encryption,
-                                         omci_transport=omci_transport,
                                          multicast=multicast,
-                                         tcont_ref=tcont_ref,
                                          traffic_class=traffic_class,
                                          handler=handler,
                                          is_mock=is_mock)
         self._timestamp = None
-        self.pon_id = pon_id
-        self.onu_id = onu_id
+        self._pon_id = pon_id
+        self._onu_id = onu_id       # None if this is a multicast GEM Port
 
     def __str__(self):
-        return "GemPort: {}/{}, alloc-id: {}, gem-id: {}".format(self.pon_id, self.onu_id,
-                                                                 self.alloc_id, self.gem_id)
+        return "GemPort: {}/{}/{}, alloc-id: {}, gem-id: {}".format(self.pon_id, self.onu_id,
+                                                                    self.uni_id, self.alloc_id,
+                                                                    self.gem_id)
 
     @staticmethod
-    def create(handler, gem, alloc_id, tech_profile_id, pon_id, onu_id, _uni_id, _ofp_port_no):
+    def create(handler, gem, alloc_id, tech_profile_id, pon_id, onu_id, uni_id, _ofp_port_no):
         return OltGemPort(gem.gemport_id,
                           alloc_id,
                           tech_profile_id,
-                          pon_id, onu_id,
+                          pon_id, onu_id, uni_id,
                           encryption=gem.aes_encryption.lower() == 'true',
                           handler=handler,
                           multicast=False)
 
     @property
+    def pon_id(self):
+        return self._pon_id
+
+    @property
+    def onu_id(self):
+        return self._onu_id
+
+    @property
     def timestamp(self):
         return self._timestamp
 
diff --git a/voltha/adapters/adtran_olt/xpon/olt_tcont.py b/voltha/adapters/adtran_olt/xpon/olt_tcont.py
index d678bc3..10fed00 100644
--- a/voltha/adapters/adtran_olt/xpon/olt_tcont.py
+++ b/voltha/adapters/adtran_olt/xpon/olt_tcont.py
@@ -27,13 +27,14 @@
     """
     Adtran OLT specific implementation
     """
-    def __init__(self, alloc_id, tech_profile_id, traffic_descriptor, pon_id, onu_id, is_mock=False):
-        super(OltTCont, self).__init__(alloc_id, tech_profile_id, traffic_descriptor, is_mock=is_mock)
+    def __init__(self, alloc_id, tech_profile_id, traffic_descriptor, pon_id, onu_id, uni_id, is_mock=False):
+        super(OltTCont, self).__init__(alloc_id, tech_profile_id, traffic_descriptor, uni_id, is_mock=is_mock)
         self.pon_id = pon_id
         self.onu_id = onu_id
 
     def __str__(self):
-        return "TCont: {}/{}, alloc-id: {}".format(self.pon_id, self.onu_id, self.alloc_id)
+        return "TCont: {}/{}/{}, alloc-id: {}".format(self.pon_id, self.onu_id,
+                                                      self.uni_id, self.alloc_id)
 
     @staticmethod
     def create(tcont, pon_id, onu_id, tech_profile_id, uni_id, ofp_port_no):
@@ -42,7 +43,7 @@
             return None
 
         td = OltTrafficDescriptor.create(tcont, pon_id, onu_id, uni_id, ofp_port_no)
-        return OltTCont(tcont.alloc_id, tech_profile_id, td, pon_id, onu_id)
+        return OltTCont(tcont.alloc_id, tech_profile_id, td, pon_id, onu_id, uni_id)
 
     @inlineCallbacks
     def add_to_hardware(self, session):
diff --git a/voltha/adapters/adtran_olt/xpon/tcont.py b/voltha/adapters/adtran_olt/xpon/tcont.py
index b5e8eee..79d94fa 100644
--- a/voltha/adapters/adtran_olt/xpon/tcont.py
+++ b/voltha/adapters/adtran_olt/xpon/tcont.py
@@ -17,14 +17,13 @@
     """
     Class to wrap TCont capabilities
     """
-    def __init__(self, alloc_id, tech_profile_id, traffic_descriptor, is_mock=False):
+    def __init__(self, alloc_id, tech_profile_id, traffic_descriptor, uni_id, is_mock=False):
         self.alloc_id = alloc_id
         self.traffic_descriptor = traffic_descriptor
         self._is_mock = is_mock
         self.tech_profile_id = tech_profile_id
-
-        # TODO: Make this a base class and derive OLT and ONU specific classes from it
-        #       The primary thing difference is the add/remove from hardware methods
+        self.uni_id = uni_id
 
     def __str__(self):
-        return "TCont: alloc-id: {}".format(self.alloc_id)
+        return "TCont: alloc-id: {}, uni-id: {}".format(self.alloc_id,
+                                                        self.uni_id)