VOL-555 - Support Deletion of an ONU using the XPON configuration Interface in
ASFvOLT16 and Broadcom ONU adapter

Change-Id: I10672a3ffa8e480cbcb40be872cc65019ecb4d26
diff --git a/voltha/adapters/asfvolt16_olt/asfvolt16_device_handler.py b/voltha/adapters/asfvolt16_olt/asfvolt16_device_handler.py
index ce71710..8ce5c14 100644
--- a/voltha/adapters/asfvolt16_olt/asfvolt16_device_handler.py
+++ b/voltha/adapters/asfvolt16_olt/asfvolt16_device_handler.py
@@ -29,6 +29,7 @@
 from scapy.layers.l2 import Ether, Dot1Q
 from uuid import uuid4
 from common.frameio.frameio import BpfProgramFilter
+from common.utils.asleep import asleep
 from twisted.internet import reactor
 from common.frameio.frameio import hexify
 from scapy.packet import Packet
@@ -148,7 +149,7 @@
             self.pm_default_freq = pm_config.default_freq
 
         if pm_config.grouped is True:
-            log.error('pm-groups-are-not-supported')
+            self.log.error('pm-groups-are-not-supported')
         else:
             for m in pm_config.metrics:
                 self.pon_metrics[m.name].config.enabled = m.enabled
@@ -303,7 +304,7 @@
 
         uni = self.get_uni_port(onu_device.id)
         if uni is not None:
-           logical_port = (onu_device.proxy_address.channel_id + uni.port_no)
+           logical_port = uni.port_no
         return logical_port
 
     def activate(self, device):
@@ -723,6 +724,15 @@
         )
         self.adapter_agent.add_port(self.device_id, port)
 
+    def del_port(self, label, port_type=None, port_no=None):
+        self.log.info('deleting-port', port_no=port_no,
+                      port_type=port_type, label=label)
+        ports = self.adapter_agent.get_ports(self.device_id, port_type)
+        for port in ports:
+            if port.label == label:
+                break
+        self.adapter_agent.delete_port(self.device_id, port)
+
     def add_logical_device(self, device_id):
         self.log.info('adding-logical-device', device_id=device_id)
         ld = LogicalDevice(
@@ -847,6 +857,17 @@
             # ENABLED and operation state is in Failed or Unkown
             self.log.info('Not-Yet-handled', olt_id=self.olt_id,
                           pon_ni=ind_info['_pon_id'], onu_data=ind_info)
+        elif ind_info['_sub_group_type'] == 'sub_term_indication' and \
+             ind_info['activation_successful'] == True:
+            pon_id = ind_info['_pon_id']
+            self.log.info('handle_activated_onu', olt_id=self.olt_id,
+                          pon_ni=pon_id, onu_data=ind_info)
+            msg = {'proxy_address': child_device.proxy_address,
+                   'event': 'activation-completed', 'event_data': ind_info}
+
+            # Send the event message to the ONU adapter
+            self.adapter_agent.publish_inter_adapter_message(child_device.id,
+                                                             msg)
         else:
             self.log.info('Invalid-ONU-event', olt_id=self.olt_id,
                           pon_ni=ind_info['_pon_id'], onu_data=ind_info)
@@ -858,8 +879,14 @@
 
     def handle_activated_onu(self, child_device, ind_info):
         pon_id = ind_info['_pon_id']
-        self.log.info('Not-handled-Yet', olt_id=self.olt_id,
+        self.log.info('handle-activated-onu', olt_id=self.olt_id,
                       pon_ni=pon_id, onu_data=ind_info)
+        msg = {'proxy_address': child_device.proxy_address,
+               'event': 'activation-completed', 'event_data': ind_info}
+
+        # Send the event message to the ONU adapter
+        self.adapter_agent.publish_inter_adapter_message(child_device.id,
+                                                         msg)
 
     def handle_discovered_onu(self, child_device, ind_info):
         pon_id = ind_info['_pon_id']
@@ -969,6 +996,30 @@
                           onu_id=child_device.proxy_address.onu_id,
                           serial_number=serial_number)
 
+    def delete_v_ont_ani(self, data):
+        serial_number = data.data.expected_serial_number
+        child_device = self.adapter_agent.get_child_device(
+            self.device_id,
+            serial_number=serial_number)
+        if child_device is None:
+            self.log.info('Failed-to-find-ONU-Info',
+                          serial_number=serial_number)
+        elif child_device.admin_state == AdminState.ENABLED:
+            self.log.info('Deactivating ONU',
+                          serial_number=serial_number,
+                          onu_id=child_device.proxy_address.onu_id,
+                          pon_id=child_device.parent_port_no)
+            onu_info = dict()
+            onu_info['pon_id'] = child_device.parent_port_no
+            onu_info['onu_id'] = child_device.proxy_address.onu_id
+            onu_info['vendor'] = child_device.vendor_id
+            onu_info['vendor_specific'] = serial_number[4:]
+            self.bal.deactivate_onu(onu_info)
+        else:
+            self.log.info('Invalid-ONU-state-to-deactivate',
+                          onu_id=child_device.proxy_address.onu_id,
+                          serial_number=serial_number)
+
     def create_interface(self, data):
         try:
             if isinstance(data, ChannelgroupConfig):
@@ -1053,7 +1104,39 @@
         return
 
     def remove_interface(self, data):
-        self.log.info('Not-Implemented-yet')
+        try:
+            if isinstance(data, ChannelgroupConfig):
+                if data.name in self.channel_groups:
+                    del self.channel_groups[data.name]
+            if isinstance(data, ChannelpartitionConfig):
+                if data.name in self.channel_partitions:
+                    del self.channel_partitions[data.name]
+            if isinstance(data, ChannelpairConfig):
+                    del self.channel_pairs[data.name]
+            if isinstance(data, ChannelterminationConfig):
+                if data.name in self.channel_terminations:
+                    self.log.info('Deativating-PON-port-at-OLT',
+                                  pon_id=data.data.xgs_ponid)
+                    self.del_port(label=data.name,
+                                  port_type=Port.PON_OLT,
+                                  port_no=data.data.xgs_ponid)
+                    self.bal.deactivate_pon_port(self.olt_id, data.data.xgs_ponid)
+                    del self.channel_terminations[data.name]
+            if isinstance(data, VOntaniConfig):
+                if data.name in self.v_ont_anis:
+                    self.delete_v_ont_ani(data)
+                    del self.v_ont_anis[data.name]
+            if isinstance(data, VEnetConfig):
+                if data.name in self.v_enets:
+                    self.log.info("deleting-port-at-olt")
+                    self.del_port(label=data.interface.name,
+                                  port_type = Port.ETHERNET_UNI)
+                    del self.v_enets[data.name]
+            if isinstance(data, OntaniConfig):
+                if data.name in self.ont_anis:
+                    del self.ont_anis[data.name]
+        except Exception as e:
+            self.log.exception('', exc=str(e))
         return
 
     def create_tcont(self, tcont_data, traffic_descriptor_data):
@@ -1094,7 +1177,19 @@
         raise NotImplementedError()
 
     def remove_tcont(self, tcont_data, traffic_descriptor_data):
-        raise NotImplementedError()
+        if traffic_descriptor_data.name in self.traffic_descriptors:
+            del self.traffic_descriptors[traffic_descriptor_data.name]
+        if tcont_data.interface_reference in self.v_ont_anis:
+            v_ont_ani = self.v_ont_anis[tcont_data.interface_reference]
+            onu_device    = self.adapter_agent.get_child_device(
+                self.device_id,
+                onu_id=v_ont_ani.v_ont_ani.data.onu_id)
+            # To-Do: Right Now use alloc_id as schduler ID. Need to
+            # find way to generate uninqe number.
+            id = tcont_data.alloc_id
+            self.bal.delete_scheduler(id, 'upstream')
+            if tcont_data.name in v_ont_ani.tconts:
+                del v_ont_ani.tconts[tcont_data.name]
 
     def create_gemport(self, data):
         if data.itf_ref in self.v_enets:
@@ -1117,7 +1212,15 @@
         raise NotImplementedError()
 
     def remove_gemport(self, data):
-        raise NotImplementedError()
+        if data.itf_ref in self.v_enets:
+            v_enet = self.v_enets[data.itf_ref]
+            if data.name in v_enet.gem_ports:
+                self.del_all_flow(v_enet)
+                del v_enet.gem_ports[data.name]
+                #To-Do Need to know what to do with flows.
+        else:
+            self.log.info('VEnet-is-not-configured-yet.',
+                          gem_port_info=data)
 
     def disable(self):
         super(Asfvolt16Handler, self).disable()
@@ -1340,6 +1443,7 @@
     # upstreand and downstream flow, as broadcom devices
     # expects down stream flows to be added to handle
     # packet_out messge from controller.
+    @inlineCallbacks
     def divide_and_add_flow(self, v_enet, classifier, action):
         if 'ip_proto' in classifier:
             if classifier['ip_proto'] == 17:
@@ -1364,27 +1468,27 @@
         elif 'eth_type' in classifier:
             if classifier['eth_type'] == 0x888e:
                 # self.log.error('Addtion of EAPOL flows are defferd')
-                self.add_eapol_flow(classifier, action,
+                yield self.add_eapol_flow(classifier, action,
                                     v_enet, ASFVOLT_EAPOL_ID,
                                     ASFVOLT_DOWNLINK_EAPOL_ID,
                                     ASFVOLT16_DEFAULT_VLAN)
         elif 'push_vlan' in action:
 
-            self.prepare_and_add_dhcp_flow(classifier, action, v_enet,
+            yield self.prepare_and_add_dhcp_flow(classifier, action, v_enet,
                                            ASFVOLT_DHCP_TAGGED_ID,
                                            ASFVOLT_DOWNLINK_DHCP_TAGGED_ID)
 
             #self.del_flow(v_enet, ASFVOLT_EAPOL_ID, ASFVOLT_DOWNLINK_EAPOL_ID)
-            self.prepare_and_add_eapol_flow(classifier, action, v_enet,
+            yield self.prepare_and_add_eapol_flow(classifier, action, v_enet,
                                            ASFVOLT_EAPOL_ID_DATA_VLAN,
                                            ASFVOLT_DOWNLINK_EAPOL_ID_DATA_VLAN)
-            self.add_data_flow(classifier, action, v_enet)
+            yield self.add_data_flow(classifier, action, v_enet)
         else:
             self.log.info('Invalid-flow-type-to-handle',
                           classifier=classifier,
                           action=action)
 
-
+    @inlineCallbacks
     def prepare_and_add_eapol_flow(self, data_classifier, data_action,
                                   v_enet, eapol_id, downlink_eapol_id):
         eapol_classifier = dict()
@@ -1393,12 +1497,12 @@
         eapol_classifier['pkt_tag_type'] = 'single_tag'
         #eapol_classifier['vlan_vid'] = data_classifier['vlan_vid']
 
-        eapol_action['vlan_push'] = True
+        eapol_action['push_vlan'] = True
         eapol_action['vlan_vid'] = data_action['vlan_vid']
-        self.add_eapol_flow(eapol_classifier, eapol_action, v_enet,
+        yield self.add_eapol_flow(eapol_classifier, eapol_action, v_enet,
                            eapol_id, downlink_eapol_id, data_classifier['vlan_vid'])
 
-
+    @inlineCallbacks
     def add_eapol_flow(self, uplink_classifier, uplink_action,
                        v_enet, uplink_eapol_id, downlink_eapol_id, vlan_id):
         downlink_classifier = dict(uplink_classifier)
@@ -1448,7 +1552,7 @@
                           action=uplink_action, gem_port=gem_port,
                           flow_id=flow_id,
                           sched_info=tcont.alloc_id)
-            self.bal.add_flow(onu_device.proxy_address.onu_id,
+            yield self.bal.add_flow(onu_device.proxy_address.onu_id,
                               onu_device.proxy_address.channel_id,
                               flow_id, gem_port.gemport_id,
                               uplink_classifier, is_down_stream,
@@ -1456,8 +1560,8 @@
                               sched_id=tcont.alloc_id)
             # To-Do. While addition of one flow is in progress,
             # we cannot add an another flow. Right now use sleep
-            # of 5 sec, assuming that addtion of flow is successful.
-            time.sleep(0.1)
+            # of 0.1 sec, assuming that addtion of flow is successful.
+            yield asleep(0.1)
         except Exception as e:
             self.log.exception('failed-to-install-Upstream-EAPOL-flow', e=e,
                                classifier=uplink_classifier,
@@ -1480,14 +1584,14 @@
                           gem_port=gem_port,
                           flow_id=downlink_flow_id,
                           sched_info=tcont.alloc_id)
-            self.bal.add_flow(onu_device.proxy_address.onu_id,
+            yield self.bal.add_flow(onu_device.proxy_address.onu_id,
                               onu_device.proxy_address.channel_id,
                               downlink_flow_id, gem_port.gemport_id,
                               downlink_classifier, is_down_stream)
             # To-Do. While addition of one flow is in progress,
             # we cannot add an another flow. Right now use sleep
-            # of 5 sec, assuming that addtion of flow is successful.
-            time.sleep(0.1)
+            # of 0.1 sec, assuming that addtion of flow is successful.
+            yield asleep(0.1)
         except Exception as e:
             self.log.exception('failed-to-install-downstream-EAPOL-flow', e=e,
                                classifier=downlink_classifier,
@@ -1495,6 +1599,7 @@
                                onu_id=onu_device.proxy_address.onu_id,
                                intf_id=onu_device.proxy_address.channel_id)
 
+    @inlineCallbacks
     def prepare_and_add_dhcp_flow(self, data_classifier, data_action,
                                   v_enet, dhcp_id, downlink_dhcp_id):
         dhcp_classifier = dict()
@@ -1504,11 +1609,12 @@
         dhcp_classifier['udp_dst'] = 67
         dhcp_classifier['pkt_tag_type'] = 'single_tag'
         dhcp_classifier['vlan_vid'] = data_classifier['vlan_vid']
-        dhcp_action['vlan_push'] = True
+        dhcp_action['push_vlan'] = True
         dhcp_action['vlan_vid'] = data_action['vlan_vid']
-        self.add_dhcp_flow(dhcp_classifier, dhcp_action, v_enet,
+        yield self.add_dhcp_flow(dhcp_classifier, dhcp_action, v_enet,
                            dhcp_id, downlink_dhcp_id)
 
+    @inlineCallbacks
     def add_dhcp_flow(self, uplink_classifier, uplink_action,
                       v_enet, dhcp_id, downlink_dhcp_id):
         downlink_classifier = dict(uplink_classifier)
@@ -1519,7 +1625,7 @@
         gem_port = self.get_gem_port_info(v_enet, traffic_class=2)
         if gem_port is None:
             self.log.info('Failed-to-get-gemport')
-            self.store_flows(uplink_classifier, uplink_action, 
+            self.store_flows(uplink_classifier, uplink_action,
                              v_enet, traffic_class=2)
             return
         v_ont_ani = self.get_v_ont_ani(v_enet.v_enet.data.v_ontani_ref)
@@ -1552,7 +1658,7 @@
                           gem_port=gem_port,
                           flow_id=flow_id,
                           sched_info=tcont.alloc_id)
-            self.bal.add_flow(onu_device.proxy_address.onu_id,
+            yield self.bal.add_flow(onu_device.proxy_address.onu_id,
                               onu_device.proxy_address.channel_id,
                               flow_id, gem_port.gemport_id,
                               uplink_classifier, is_down_stream,
@@ -1560,8 +1666,8 @@
                               sched_id=tcont.alloc_id)
             # To-Do. While addition of one flow is in progress,
             # we cannot add an another flow. Right now use sleep
-            # of 5 sec, assuming that addtion of flow is successful.
-            time.sleep(0.1)
+            # of 0.1 sec, assuming that addtion of flow is successful.
+            yield asleep(0.1)
 
         except Exception as e:
             self.log.exception('failed-to-install-dhcp-upstream-flow', e=e,
@@ -1579,10 +1685,10 @@
             # Copy O_OVID
             downlink_classifier['vlan_vid'] = downlink_action['vlan_vid']
             # Copy I_OVID
-            #downlink_classifier['metadata'] = uplink_classifier['vlan_vid']
-            if 'push_vlan' in downlink_classifier:
+            downlink_classifier['metadata'] = uplink_classifier['vlan_vid']
+            if 'push_vlan' in downlink_action:
                 downlink_action.pop('push_vlan')
-            downlink_action['pop_vlan'] = True
+            downlink_action['trap_to_host'] = True
         else:
             downlink_classifier['pkt_tag_type'] =  'untagged'
             downlink_classifier.pop('vlan_vid')
@@ -1598,7 +1704,7 @@
                           action=downlink_action, gem_port=gem_port,
                           flow_id=downlink_flow_id,
                           sched_info=tcont.alloc_id)
-            self.bal.add_flow(onu_device.proxy_address.onu_id,
+            yield self.bal.add_flow(onu_device.proxy_address.onu_id,
                               onu_device.proxy_address.channel_id,
                               downlink_flow_id, gem_port.gemport_id,
                               downlink_classifier, is_down_stream,
@@ -1606,7 +1712,7 @@
             # To-Do. While addition of one flow is in progress,
             # we cannot add an another flow. Right now use sleep
             # of 5 sec, assuming that addtion of flow is successful.
-            time.sleep(0.1)
+            yield asleep(0.1)
         except Exception as e:
             self.log.exception('failed-to-install-dhcp-downstream-flow', e=e,
                                classifier=downlink_classifier,
@@ -1618,6 +1724,7 @@
         self.log.info('Not-Implemented-Yet')
         return
 
+    @inlineCallbacks
     def add_data_flow(self, uplink_classifier, uplink_action, v_enet):
 
         downlink_classifier = dict(uplink_classifier)
@@ -1633,11 +1740,12 @@
 
         # To-Do right now only one GEM port is supported, so below method
         # will take care of handling all the p bits.
-        # We need to revisit when mulitple gem port per bits is needed.
-        self.add_hsia_flow(uplink_classifier, uplink_action,
+        # We need to revisit when mulitple gem port per p bits is needed.
+        yield self.add_hsia_flow(uplink_classifier, uplink_action,
                           downlink_classifier, downlink_action,
                           v_enet, ASFVOLT_HSIA_ID, ASFVOLT_DOWNLINK_HSIA_ID)
 
+    @inlineCallbacks
     def add_hsia_flow(self, uplink_classifier, uplink_action,
                      downlink_classifier, downlink_action,
                      v_enet, hsia_id, downlink_hsia_id):
@@ -1647,7 +1755,7 @@
         gem_port = self.get_gem_port_info(v_enet, traffic_class=2)
         if gem_port is None:
             self.log.info('Failed-to-get-gemport')
-            self.store_flows(uplink_classifier, uplink_action, 
+            self.store_flows(uplink_classifier, uplink_action,
                              v_enet, traffic_class=2)
             return
         v_ont_ani = self.get_v_ont_ani(v_enet.v_enet.data.v_ontani_ref)
@@ -1678,7 +1786,7 @@
                           gem_port=gem_port,
                           flow_id=flow_id,
                           sched_info=tcont.alloc_id)
-            self.bal.add_flow(onu_device.proxy_address.onu_id,
+            yield self.bal.add_flow(onu_device.proxy_address.onu_id,
                               onu_device.proxy_address.channel_id,
                               flow_id, gem_port.gemport_id,
                               uplink_classifier, is_down_stream,
@@ -1686,8 +1794,8 @@
                               sched_id=tcont.alloc_id)
             # To-Do. While addition of one flow is in progress,
             # we cannot add an another flow. Right now use sleep
-            # of 5 sec, assuming that addtion of flow is successful.
-            time.sleep(0.1)
+            # of 0.1 sec, assuming that addtion of flow is successful.
+            yield asleep(0.1)
         except Exception as e:
             self.log.exception('failed-to-install-ARP-upstream-flow', e=e,
                                classifier=uplink_classifier,
@@ -1707,15 +1815,15 @@
                           action=downlink_action,
                           gem_port=gem_port,
                           flow_id=downlink_flow_id)
-            self.bal.add_flow(onu_device.proxy_address.onu_id,
+            yield self.bal.add_flow(onu_device.proxy_address.onu_id,
                               onu_device.proxy_address.channel_id,
                               downlink_flow_id, gem_port.gemport_id,
                               downlink_classifier, is_down_stream,
                               action_info=downlink_action)
             # To-Do. While addition of one flow is in progress,
             # we cannot add an another flow. Right now use sleep
-            # of 5 sec, assuming that addtion of flow is successful.
-            time.sleep(0.1)
+            # of 0.1 sec, assuming that addtion of flow is successful.
+            yield asleep(0.1)
         except Exception as e:
             self.log.exception('failed-to-install-ARP-downstream-flow', e=e,
                                classifier=downlink_classifier,
@@ -1723,7 +1831,16 @@
                                onu_id=onu_device.proxy_address.onu_id,
                                intf_id=onu_device.proxy_address.channel_id)
 
+    @inlineCallbacks
+    def del_all_flow(self, v_enet):
+        yield self.del_flow(v_enet, ASFVOLT_HSIA_ID, ASFVOLT_HSIA_ID)
+        yield self.del_flow(v_enet, ASFVOLT_DHCP_TAGGED_ID,
+                      ASFVOLT_DOWNLINK_DHCP_TAGGED_ID)
+        yield self.del_flow(v_enet, ASFVOLT_EAPOL_ID_DATA_VLAN,
+                      ASFVOLT_DOWNLINK_EAPOL_ID_DATA_VLAN)
+        yield self.del_flow(v_enet, ASFVOLT_EAPOL_ID, ASFVOLT_DOWNLINK_EAPOL_ID)
 
+    @inlineCallbacks
     def del_flow(self, v_enet, uplink_id, downlink_id):
         # To-Do For a time being hard code the traffic class value.
         # Need to know how to get the traffic class info from flows.
@@ -1739,43 +1856,43 @@
                           onu_id=v_ont_ani.v_ont_ani.data.onu_id)
             return
 
-        flow_id = self.get_flow_id(onu_device.proxy_address.onu_id,
-                                   onu_device.proxy_address.channel_id,
-                                   uplink_id)
-        try:
-            is_down_stream = False
-            self.log.info('deleting-Upstream-EAPOL-flow',
-                          flow_id=flow_id)
-            self.bal.delete_flow(onu_device.proxy_address.onu_id,
-                              onu_device.proxy_address.channel_id,
-                              flow_id, is_down_stream)
-            # To-Do. While deletion of one flow is in progress,
-            # we cannot delete an another flow. Right now use sleep
-            # of 5 sec, assuming that deletion of flow is successful.
-            time.sleep(0.1)
-        except Exception as e:
-            self.log.exception('failed-to-delete-Upstream-EAPOL-flow', e=e,
-                               flow_id=flow_id,
-                               onu_id=onu_device.proxy_address.onu_id,
-                               intf_id=onu_device.proxy_address.channel_id)
-
         downlink_flow_id = self.get_flow_id(onu_device.proxy_address.onu_id,
                                             onu_device.proxy_address.channel_id,
                                             downlink_id)
         is_down_stream = True
         try:
-            self.log.info('Deleting-Downstream-EAPOL-flow',
+            self.log.info('Deleting-Downstream-flow',
                           flow_id=downlink_flow_id)
 
-            self.bal.delete_flow(onu_device.proxy_address.onu_id,
+            yield self.bal.delete_flow(onu_device.proxy_address.onu_id,
                               onu_device.proxy_address.channel_id,
                               downlink_flow_id, is_down_stream)
-            # To-Do. While deletion of one flow is in progress,
+            # While deletion of one flow is in progress,
             # we cannot delete an another flow. Right now use sleep
-            # of 5 sec, assuming that deletion of flow is successful.
-            time.sleep(0.1)
+            # of 0.1 sec, assuming that deletion of flow is successful.
+            yield asleep(0.1)
         except Exception as e:
-            self.log.exception('failed-to-install-downstream-EAPOL-flow', e=e,
+            self.log.exception('failed-to-install-downstream-flow', e=e,
                                flow_id=flow_id,
                                onu_id=onu_device.proxy_address.onu_id,
                                intf_id=onu_device.proxy_address.channel_id)
+
+        uplink_flow_id = self.get_flow_id(onu_device.proxy_address.onu_id,
+                                   onu_device.proxy_address.channel_id,
+                                   uplink_id)
+        try:
+            is_down_stream = False
+            self.log.info('deleting-Upstream-flow',
+                          flow_id=uplink_flow_id)
+            yield self.bal.delete_flow(onu_device.proxy_address.onu_id,
+                              onu_device.proxy_address.channel_id,
+                              uplink_flow_id, is_down_stream)
+            # While deletion of one flow is in progress,
+            # we cannot delete an another flow. Right now use sleep
+            # of 0.1 sec, assuming that deletion of flow is successful.
+            yield asleep(0.1)
+        except Exception as e:
+            self.log.exception('failed-to-delete-Upstream-flow', e=e,
+                               flow_id=uplink_flow_id,
+                               onu_id=onu_device.proxy_address.onu_id,
+                               intf_id=onu_device.proxy_address.channel_id)
diff --git a/voltha/adapters/asfvolt16_olt/bal.py b/voltha/adapters/asfvolt16_olt/bal.py
index 736693a..43c3467 100644
--- a/voltha/adapters/asfvolt16_olt/bal.py
+++ b/voltha/adapters/asfvolt16_olt/bal.py
@@ -110,6 +110,26 @@
         return
 
     @inlineCallbacks
+    def deactivate_pon_port(self, olt_no, pon_port):
+        try:
+            obj = bal_pb2.BalCfg()
+            #            Fill Header details
+            obj.device_id = self.device_id.encode('ascii', 'ignore')
+            obj.hdr.obj_type = bal_model_ids_pb2.BAL_OBJ_ID_INTERFACE
+            #            Fill Access Terminal Details
+            obj.interface.key.intf_id = pon_port
+            obj.interface.key.intf_type = bal_model_types_pb2.BAL_INTF_TYPE_PON
+            obj.interface.data.admin_state = bal_model_types_pb2.BAL_STATE_DOWN
+            obj.interface.data.transceiver_type = \
+                bal_model_types_pb2.BAL_TRX_TYPE_XGPON_LTH_7226_PC
+            self.log.info('deactivating-pon-port-in-olt',
+                          olt=olt_no, pon_port=pon_port,
+                          pon_port_details=obj)
+        except Exception as e:
+            self.log.info('deactivating-pon-port in olt-exception', exc=str(e))
+        return
+
+    @inlineCallbacks
     def send_omci_request_message(self, proxy_address, msg):
         try:
             obj = bal_pb2.BalCfg()
@@ -397,6 +417,55 @@
                           exc=str(e))
         return
 
+
+    @inlineCallbacks
+    def deactivate_onu(self, onu_info):
+        try:
+            obj = bal_pb2.BalCfg()
+            # Fill Header details
+            obj.device_id = self.device_id.encode('ascii', 'ignore')
+            obj.hdr.obj_type = bal_model_ids_pb2.BAL_OBJ_ID_SUBSCRIBER_TERMINAL
+            # Fill Access Terminal Details
+            obj.terminal.key.intf_id = onu_info['pon_id']
+            obj.terminal.key.sub_term_id = onu_info['onu_id']
+            obj.terminal.data.admin_state = bal_model_types_pb2.BAL_STATE_DOWN
+            obj.terminal.data.serial_number.vendor_id = onu_info['vendor']
+            obj.terminal.data.serial_number.vendor_specific = \
+                onu_info['vendor_specific']
+            obj.terminal.data.registration_id = \
+                '202020202020202020202020202020202020202020202020202020202020202020202020'
+            self.log.info('deactivating-ONU-in-olt',
+                          onu_details=obj)
+            yield self.stub.BalCfgSet(obj)
+        except Exception as e:
+            self.log.info('deactivating-ONU-exception',
+                          onu_info['onu_id'], exc=str(e))
+        return
+
+    @inlineCallbacks
+    def delete_scheduler(self, id, direction):
+        try:
+            obj = bal_pb2.BalKey()
+            obj.hdr.obj_type = bal_model_ids_pb2.BAL_OBJ_ID_TM_SCHED
+            # Fill Access Terminal Details
+            if direction == 'downstream':
+                obj.tm_sched_key.dir =\
+                    bal_model_types_pb2.BAL_TM_SCHED_DIR_DS
+            else:
+                obj.tm_sched_key.dir = \
+                    bal_model_types_pb2.BAL_TM_SCHED_DIR_US
+            obj.tm_sched_key.id = id
+            self.log.info('Deleting Scheduler',
+                          scheduler_details=obj)
+            yield self.stub.BalCfgClear(obj)
+        except Exception as e:
+            self.log.info('creat-scheduler-exception',
+                          olt=self.olt.olt_id,
+                          sched_id=id,
+                          direction=direction,
+                          exc=str(e))
+        return
+
     @inlineCallbacks
     def get_bal_interface_stats(self, intf_id, interface_type):
         self.log.info('Fetching-Statistics')
diff --git a/voltha/adapters/broadcom_onu/broadcom_onu.py b/voltha/adapters/broadcom_onu/broadcom_onu.py
index 2ac33bf..f3a73d7 100644
--- a/voltha/adapters/broadcom_onu/broadcom_onu.py
+++ b/voltha/adapters/broadcom_onu/broadcom_onu.py
@@ -148,7 +148,13 @@
         raise NotImplementedError()
 
     def delete_device(self, device):
-        raise NotImplementedError()
+        log.info('delete-device', device_id=device.id)
+        if device.id in self.devices_handlers:
+            handler = self.devices_handlers[device.id]
+            if handler is not None:
+                handler.delete(device)
+            del self.devices_handlers[device.id]
+        return
 
     def get_device_details(self, device):
         raise NotImplementedError()
@@ -233,7 +239,11 @@
         raise NotImplementedError()
 
     def remove_tcont(self, device, tcont_data, traffic_descriptor_data):
-        raise NotImplementedError()
+        log.info('remove-tcont', device_id=device.id)
+        if device.id in self.devices_handlers:
+            handler = self.devices_handlers[device.id]
+            if handler is not None:
+                handler.remove_tcont(tcont_data, traffic_descriptor_data)
 
     def create_gemport(self, device, data):
         log.info('create-gemport', device_id=device.id)
@@ -246,7 +256,11 @@
         raise NotImplementedError()
 
     def remove_gemport(self, device, data):
-        raise NotImplementedError()
+        log.info('remove-gemport', device_id=device.id)
+        if device.id in self.devices_handlers:
+            handler = self.devices_handlers[device.id]
+            if handler is not None:
+                handler.remove_gemport(data)
 
     def create_multicast_gemport(self, device, data):
         log.info('create-multicast-gemport', device_id=device.id)
@@ -307,13 +321,13 @@
             if event_msg['event_data']['activation_successful'] == True:
                 for uni in self.uni_ports:
                     port_no = self.proxy_address.channel_id + uni
-                    reactor.callLater(1,
-                      self.message_exchange,
-                      self.proxy_address.onu_id,
-                      self.proxy_address.onu_session_id,
-                      port_no)
+                    yield self.message_exchange(
+                         self.proxy_address.onu_id,
+                         self.proxy_address.onu_session_id,
+                         port_no)
 
                 device = self.adapter_agent.get_device(self.device_id)
+                device.connect_status = ConnectStatus.REACHABLE
                 device.oper_status = OperStatus.ACTIVE
                 self.adapter_agent.update_device(device)
 
@@ -406,6 +420,24 @@
 
         log.info('reconciling-broadcom-onu-device-ends')
 
+    @inlineCallbacks
+    def delete(self, device):
+        self.log.info('delete-onu')
+
+        # construct message
+        # MIB Reset - OntData - 0
+        self.send_mib_reset()
+        yield self.wait_for_response()
+
+        self.proxy_address = device.proxy_address
+        self.adapter_agent.unregister_for_proxied_messages(device.proxy_address)
+
+        ports = self.adapter_agent.get_ports(self.device_id, Port.PON_ONU)
+        if ports is not None:
+            for port in ports:
+                if port.label == 'PON port':
+                    self.adapter_agent.delete_port(self.device_id, port)
+                    break
 
     @inlineCallbacks
     def update_flow_table(self, device, flows):
@@ -754,6 +786,19 @@
         )
         self.send_omci_message(frame)
 
+    def send_delete_omci_mesage(self,
+                                class_id,
+                                entity_id):
+        frame = OmciFrame(
+            transaction_id=self.get_tx_id(),
+            message_type=OmciDelete.message_id,
+            omci_message=OmciDelete(
+                entity_class=class_id,
+                entity_id=entity_id
+            )
+        )
+        self.send_omci_message(frame)
+
     def send_set_8021p_mapper_service_profile(self,
                                               entity_id,
                                               interwork_tp_id):
@@ -1398,6 +1443,26 @@
                 device_port_no=uni_port.port_no
             ))
 
+    def del_uni_port(self, device, parent_logical_device_id,
+                     name, parent_port_num=None):
+        self.log.info('del-uni-port', device_id=device.id,
+                      logical_device_id=parent_logical_device_id,
+                      name=name)
+        if parent_port_num is not None:
+            uni = parent_port_num
+            port_no = parent_port_num
+        else:
+            uni = self.uni_ports[0]
+            port_no = device.proxy_address.channel_id + uni
+            # register physical ports
+        ports = self.adapter_agent.get_ports(self.device_id, Port.ETHERNET_UNI)
+        for port in ports:
+            if port.label == 'UNI facing Ethernet port '+str(uni):
+                break
+        self.adapter_agent.delete_port(self.device_id, port)
+        self.adapter_agent.delete_logical_port_by_id(parent_logical_device_id,
+                                                     'uni-{}'.format(port_no))
+
     def create_interface(self, data):
         if isinstance(data, VEnetConfig):
             parent_port_num = None
@@ -1444,7 +1509,23 @@
         return
 
     def remove_interface(self, data):
-        self.log.info('Not Implemented yet')
+        if isinstance(data, VEnetConfig):
+            parent_port_num = None
+            onu_device = self.adapter_agent.get_device(self.device_id)
+            ports = self.adapter_agent.get_ports(onu_device.parent_id, Port.ETHERNET_UNI)
+            parent_port_num = None
+            for port in ports:
+                if port.label == data.interface.name:
+                    parent_port_num = port.port_no
+                    break
+
+            parent_device = self.adapter_agent.get_device(onu_device.parent_id)
+            logical_device_id = parent_device.parent_id
+            assert logical_device_id
+            self.del_uni_port(onu_device, logical_device_id,
+                              data.name, parent_port_num)
+        else:
+            self.log.info('Not handled Yet')
         return
 
     @inlineCallbacks
@@ -1478,6 +1559,24 @@
 
 
     @inlineCallbacks
+    def remove_gemport(self, data):
+        log.info('remove-gemport')
+        gem_port= GemportsConfigData()
+        gem_port.CopyFrom(data)
+        self.send_set_8021p_mapper_service_profile(0x8001, 0xFFFF)
+
+        yield self.wait_for_response()
+
+        self.send_delete_omci_mesage(GemInterworkingTp.class_id,
+                                     gem_port.gemport_id)
+        yield self.wait_for_response()
+
+        #To-Do Need to see how the valuse 0x8001 is derived
+        self.send_delete_omci_mesage(GemPortNetworkCtp.class_id,
+                                     gem_port.gemport_id)
+        yield self.wait_for_response()
+
+    @inlineCallbacks
     def create_tcont(self, tcont_data, traffic_descriptor_data):
         log.info('create-tcont')
 	tcont = TcontsConfigData()
@@ -1489,5 +1588,11 @@
 	else:
             self.log.info('Recevied NULL tcont Data', tcont= tcont.alloc_id)
 
+    @inlineCallbacks
+    def remove_tcont(self, tcont_data, traffic_descriptor_data):
+        log.info('remove-tcont')
+        self.send_set_tcont(0x8001, 0xFFFF)
+        yield self.wait_for_response()
+
     def create_multicast_gemport(self, data):
         self.log.info('Send relevant OMCI message')
diff --git a/voltha/adapters/iadapter.py b/voltha/adapters/iadapter.py
index e6e0d46..d8b6718 100644
--- a/voltha/adapters/iadapter.py
+++ b/voltha/adapters/iadapter.py
@@ -195,7 +195,11 @@
         raise NotImplementedError()
 
     def remove_tcont(self, device, tcont_data, traffic_descriptor_data):
-        raise NotImplementedError()
+        log.info('remove-tcont', device_id=device.id)
+        if device.id in self.devices_handlers:
+            handler = self.devices_handlers[device.id]
+            if handler is not None:
+                handler.remove_tcont(tcont_data, traffic_descriptor_data)
 
     def create_gemport(self, device, data):
         raise NotImplementedError()
@@ -204,7 +208,11 @@
         raise NotImplementedError()
 
     def remove_gemport(self, device, data):
-        raise NotImplementedError()
+        log.info('remove-gemport', device_id=device.id)
+        if device.id in self.devices_handlers:
+            handler = self.devices_handlers[device.id]
+            if handler is not None:
+                handler.remove_gemport(data)
 
     def create_multicast_gemport(self, device, data):
         raise NotImplementedError()
diff --git a/voltha/core/xpon_agent.py b/voltha/core/xpon_agent.py
index 5236999..32b7a95 100644
--- a/voltha/core/xpon_agent.py
+++ b/voltha/core/xpon_agent.py
@@ -468,12 +468,12 @@
                         'xpon-agent-removing-interface-at-onu-device:',
                         onu_device_id=onu_device.id, data=data)
                     self.remove_interface_in_device(onu_device, data)
-                if isinstance(data, VOntaniConfig):
-                    self.delete_onu_device(olt_device, onu_device)
                 log.info(
                     'xpon-agent-removing-interface-at-olt-device:',
                     olt_device_id=olt_device.id, data=data)
                 self.remove_interface_in_device(olt_device, data)
+                if isinstance(data, VOntaniConfig):
+                    self.delete_onu_device(olt_device, onu_device)
 
     def create_channel_termination(self, olt_device, data):
         channel_pair = self.get_parent_data(data)