VOL-295: This commit addresses the following issues
1) Allow using same ONU IDs accross multiple PON ports
2) Support multiple PON ports on a OLT and mutliple OLTs
3) When ONU device is not reachable, do not configure
   OMCI messages to it

Change-Id: I43db74fe66808a8e7aac1fb7def461c8bc9c8794
diff --git a/voltha/adapters/asfvolt16_olt/asfvolt16_device_handler.py b/voltha/adapters/asfvolt16_olt/asfvolt16_device_handler.py
index 4075899..653d978 100644
--- a/voltha/adapters/asfvolt16_olt/asfvolt16_device_handler.py
+++ b/voltha/adapters/asfvolt16_olt/asfvolt16_device_handler.py
@@ -92,23 +92,15 @@
 ASFVOLT_DHCP_TAGGED_ID = 5
 ASFVOLT_DOWNLINK_DHCP_TAGGED_ID = 6
 
-ASFVOLT_IGMP_TAGGED_ID = 7
-ASFVOLT_DOWNLINK_IGMP_TAGGED_ID = 8
-
-ASFVOLT_FIRMWARE_ID = 9
-ASFVOLT_DOWNLINK_FIRMWARE_ID = 10
-
-ASFVOLT_ARP_ID = 11
-ASFVOLT_DOWNLINK_ARP_ID = 12
-
-ASFVOLT_HSIA_ID = 13
-ASFVOLT_DOWNLINK_HSIA_ID = 14
-
-ASFVOLT_DNS_ID = 15
-ASFVOLT_DOWNLINK_DNS_ID = 16
+ASFVOLT_HSIA_ID = 7
 
 RESERVED_VLAN_ID = 4095
 
+# get_flow_id api uses 7 bit of the ONU id and hence
+# limits the max ONUs per pon port to 128. This should
+# be sufficient for most practical uses cases
+MAX_ONU_ID_PER_PON_PORT = 127
+
 
 class FlowInfo(object):
 
@@ -140,7 +132,7 @@
             self.value = value
             # group PM config is not supported currently
 
-    def __init__(self,device):
+    def __init__(self,device, log):
         self.pm_names = {
              "rx_bytes", "rx_packets", "rx_ucast_packets", "rx_mcast_packets",
              "rx_bcast_packets", "rx_error_packets", "rx_unknown_protos",
@@ -148,6 +140,7 @@
              "tx_bcast_packets", "tx_error_packets", "rx_crc_errors", "bip_errors"
         }
         self.device = device
+        self.log = log
         self.id = device.id
         # To collect pm metrices for each 'pm_default_freq/10' secs
         self.pm_default_freq = 20
@@ -219,7 +212,11 @@
         self.transceiver_type = bal_model_types_pb2.BAL_TRX_TYPE_XGPON_LTH_7226_PC
         self.asfvolt_device_info = Asfvolt16DeviceInfo(self.bal,
                                                        self.log, self.device_id)
+        # We allow only one pon port enabling at a time.
         self.pon_port_config_resp = DeferredQueue(size=1)
+        self.reconcile_in_progress = False
+        # defaults to first NNI port. Overriden after reading from the device
+        self.nni_intf_id = 0
 
     def __del__(self):
         super(Asfvolt16Handler, self).__del__()
@@ -227,26 +224,6 @@
     def __str__(self):
         return "Asfvolt16Handler: {}".format(self.host_and_port)
 
-    def _get_next_uni_port(self):
-        uni_ports = self.adapter_agent.get_ports(self.device_id,
-                                                 Port.ETHERNET_UNI)
-        uni_port_nums = set([uni_port.port_no for uni_port in uni_ports])
-        # We need to start allocating port numbers from ONU_UNI_PORT_START_ID.
-        # Find the first unused port number.
-        next_port_num = next(ifilterfalse(uni_port_nums.__contains__,
-                                          count(ONU_UNI_PORT_START_ID)))
-        if next_port_num <= 65535:
-            return next_port_num
-        else:
-            raise ValueError("invalid-port-number-{}".format(next_port_num))
-
-    def _valid_nni_port(self, port):
-        if port < self.asfvolt_device_info.asfvolt16_device_topology.num_of_pon_ports or \
-            port >= (self.asfvolt_device_info.asfvolt16_device_topology.num_of_pon_ports +
-                     self.asfvolt_device_info.asfvolt16_device_topology.num_of_nni_ports):
-            return False
-        return True
-
     def get_venet(self, **kwargs):
         name = kwargs.pop('name', None)
         gem_port_id = kwargs.pop('gem_port_id', None)
@@ -263,14 +240,41 @@
     def get_v_ont_ani(self, **kwargs):
         name = kwargs.pop('name', None)
         onu_id = kwargs.pop('onu_id', None)
+        pon_port = kwargs.pop('pon_port', None)
+
+        if name is None and (onu_id is None or pon_port is None):
+            self.log.error("onu-id-or-pon-port-missing",
+                           onu_id=onu_id, pon_port=pon_port)
+            return None
+
         if name is not None:
             for key, v_ont_ani in self.v_ont_anis.items():
                 if key == name:
                     return v_ont_ani
-        if onu_id is not None:
-            for key, v_ont_ani in self.v_ont_anis.items():
-                if onu_id == v_ont_ani.v_ont_ani.data.onu_id:
-                    return v_ont_ani
+
+        # First fetch the channel_termination whose pon_port matches
+        # the passed pon_port.
+        # Then fetch the v_ont_ani whose onu_id and channel_termination
+        # reference matches the right channel_termination.
+        # Note: preferred_chanpair is the channel_termination name.
+        chan_term = None
+        for channel_termination in \
+                self.channel_terminations.itervalues():
+            if pon_port == channel_termination.data.xgs_ponid:
+                chan_term = channel_termination
+                break
+
+        if chan_term is None:
+            self.log.error("no-channel-termination-matching-pon-port",
+                           pon_port=pon_port)
+            return None
+
+        for v_ont_ani in self.v_ont_anis.itervalues():
+            if v_ont_ani.v_ont_ani.data.preferred_chanpair == \
+                    chan_term.name and \
+                    v_ont_ani.v_ont_ani.data.onu_id == onu_id:
+                return v_ont_ani
+
         return None
 
     def get_gem_port_info(self, v_enet, **kwargs):
@@ -305,11 +309,14 @@
         return None
 
     def get_flow_id(self, onu_id, intf_id, id):
-        # Tp-Do Need to generate unique flow ID using
-        # OnuID, IntfId, id
-        # BAL accepts flow_id till 16384. So we are
-        # using only onu_id and id to generate flow ID.
-        return ((onu_id << 5) | id)
+        # BAL accepts flow_id till 16384 (14 bits).
+        # ++++++++++++++++++++++++++++++++++++++++++++++
+        # + 7 bits onu_id | 4 bits intf_id | 3 bits id +
+        # ++++++++++++++++++++++++++++++++++++++++++++++
+        # Note: Theoritical limit of onu_id is 255, but
+        # practically we have upto 64 or 128 ONUs per pon port.
+        # Hence limiting onu_id to 7 bits
+        return ((onu_id << 7) | (intf_id << 3) | (id))
 
     def get_uni_port(self, device_id):
         ports = self.adapter_agent.get_ports(device_id, Port.ETHERNET_UNI)
@@ -389,8 +396,11 @@
                           v_ont_ani=v_enet.v_enet.data.v_ontani_ref)
             return
 
+        pon_port = self._get_pon_port_from_pref_chanpair_ref(\
+                        v_ont_ani.v_ont_ani.data.preferred_chanpair)
         onu_device = self.adapter_agent.get_child_device(
-            self.device_id, onu_id=v_ont_ani.v_ont_ani.data.onu_id)
+            self.device_id, onu_id=v_ont_ani.v_ont_ani.data.onu_id,
+            parent_port_no=pon_port)
         if onu_device is None:
             self.log.info('Failed-to-get-onu-device',
                           onu_id=v_ont_ani.v_ont_ani.data.onu_id)
@@ -452,6 +462,7 @@
 
     def reconcile(self, device):
         self.log.info('reconciling-asfvolt16-starts',device=device)
+        self.reconcile_in_progress = True
 
         if not device.host_and_port:
             device.oper_status = OperStatus.FAILED
@@ -461,6 +472,7 @@
 
         try:
             # Establishing connection towards OLT
+            self.host_and_port = device.host_and_port
             self.bal.connect_olt(device.host_and_port, self.device_id, is_init=False)
             reactor.callInThread(self.bal.get_indication_info, self.device_id)
         except Exception as e:
@@ -475,7 +487,7 @@
             self.start_heartbeat()
 
             # Now set the initial PM configuration for this device
-            self.pm_metrics=Asfvolt16OltPmMetrics(device)
+            self.pm_metrics=Asfvolt16OltPmMetrics(device, self.log)
             pm_config = self.pm_metrics.make_proto()
             self.log.info("initial-pm-config", pm_config=pm_config)
             self.adapter_agent.update_device_pm_config(pm_config,init=True)
@@ -503,6 +515,7 @@
         device.oper_status = OperStatus.ACTIVE
         self.adapter_agent.update_device(device)
 
+        self.reconcile_in_progress = False
         self.log.info('reconciling-asfvolt16-device-ends',device=device)
 
     @inlineCallbacks
@@ -559,24 +572,17 @@
                 if d.is_reboot == bal_pb2.BAL_OLT_UP_AFTER_REBOOT:
                     self.log.info('activating-olt-again-after-reboot')
 
-                    for port in self.asfvolt_device_info.sfp_device_presence_map.iterkeys():
-                        # ignore any sfp other than the NNI.
-                        if not self._valid_nni_port(port):
-                            continue
-
-                        # Since OLT is reachable after reboot, OLT should configurable with
-                        # all the old existing flows. NNI ports should be mark it as down for
-                        # ONOS to push the old flows
-                        nni_intf_id = (port -
-                                       self.asfvolt_device_info.
-                                       asfvolt16_device_topology.num_of_pon_ports)
-                        self.update_logical_port(nni_intf_id + MIN_ASFVOLT_NNI_LOGICAL_PORT_NUM,
-                                                 Port.ETHERNET_NNI,
-                                                 OFPPS_LINK_DOWN)
+                    self.update_logical_port(self.nni_intf_id + MIN_ASFVOLT_NNI_LOGICAL_PORT_NUM,
+                                             Port.ETHERNET_NNI,
+                                             OFPPS_LINK_DOWN)
 
                     for key, v_ont_ani in self.v_ont_anis.items():
+
+                        pon_port = self._get_pon_port_from_pref_chanpair_ref(\
+                                      v_ont_ani.v_ont_ani.data.preferred_chanpair)
                         child_device = self.adapter_agent.get_child_device(
-                           self.device_id, onu_id=v_ont_ani.v_ont_ani.data.onu_id)
+                           self.device_id, onu_id=v_ont_ani.v_ont_ani.data.onu_id,
+                           parent_port_no=pon_port)
                         if child_device:
                             msg = {'proxy_address': child_device.proxy_address,
                                    'event': 'deactivate-onu', 'event_data': "olt-reboot"}
@@ -621,13 +627,7 @@
         self.log.info('Reboot-Status', err_status = err_status)
 
     def _handle_pm_counter_req_towards_device(self, device):
-        for port in self.asfvolt_device_info.sfp_device_presence_map.iterkeys():
-            if not self._valid_nni_port(port):
-                continue
-            nni_intf_id = (port -
-                            self.asfvolt_device_info.
-                            asfvolt16_device_topology.num_of_pon_ports)
-            self._handle_nni_pm_counter_req_towards_device(device, nni_intf_id)
+        self._handle_nni_pm_counter_req_towards_device(device, self.nni_intf_id)
         for value in self.channel_terminations.itervalues():
             self._handle_pon_pm_counter_req_towards_device(device,
                                                            value.data.xgs_ponid)
@@ -786,13 +786,15 @@
                             onu_id, dgi_status, balSubTermDgi_data,\
                             ind_info):
         self.log.info('Subscriber-terminal-dying-gasp')
+
         self.handle_alarms(device_id,"onu",\
                            intf_id,\
                            "dgi_indication",dgi_status,"medium",\
                            balSubTermDgi_data)
         if dgi_status == 1:
             child_device = self.adapter_agent.get_child_device(
-                           device_id, onu_id=onu_id)
+                           device_id, onu_id=onu_id,
+                           parent_port_no=intf_id)
             if child_device is None:
                self.log.info('Onu-is-not-configured', onu_id=onu_id)
                return
@@ -978,20 +980,21 @@
             for port in self.asfvolt_device_info.sfp_device_presence_map.iterkeys():
                 if not self._valid_nni_port(port):
                     continue
-
-                nni_intf_id = (port -
-                                self.asfvolt_device_info.
-                                asfvolt16_device_topology.num_of_pon_ports)
-                self.add_port(port_no=nni_intf_id +
-                              MIN_ASFVOLT_NNI_LOGICAL_PORT_NUM,
-                              port_type=Port.ETHERNET_NNI,
-                              label='NNI facing Ethernet port')
-                self.add_logical_port(port_no=nni_intf_id + \
-                                      MIN_ASFVOLT_NNI_LOGICAL_PORT_NUM,
-                                      port_type=Port.ETHERNET_NNI,
-                                      device_id=device.id,
-                                      logical_device_id=self.logical_device_id,
-                                      port_state=OFPPS_LIVE)
+                # We support only one NNI port.
+                self.nni_intf_id = (port -
+                                    self.asfvolt_device_info.
+                                    asfvolt16_device_topology.num_of_pon_ports)
+                break
+            self.add_port(port_no=self.nni_intf_id +
+                          MIN_ASFVOLT_NNI_LOGICAL_PORT_NUM,
+                          port_type=Port.ETHERNET_NNI,
+                          label='NNI facing Ethernet port')
+            self.add_logical_port(port_no=self.nni_intf_id + \
+                                  MIN_ASFVOLT_NNI_LOGICAL_PORT_NUM,
+                                  port_type=Port.ETHERNET_NNI,
+                                  device_id=device.id,
+                                  logical_device_id=self.logical_device_id,
+                                  port_state=OFPPS_LIVE)
 
             self.log.info('OLT-activation-complete')
 
@@ -1000,7 +1003,7 @@
                 self.log.info('Heart-beat-is-not-yet-started-starting-now')
                 self.start_heartbeat()
 
-                self.pm_metrics=Asfvolt16OltPmMetrics(device)
+                self.pm_metrics=Asfvolt16OltPmMetrics(device, self.log)
                 pm_config = self.pm_metrics.make_proto()
                 self.log.info("initial-pm-config", pm_config=pm_config)
                 self.adapter_agent.update_device_pm_config(pm_config,init=True)
@@ -1022,8 +1025,9 @@
             device.reason = 'device deactivated successfully'
             self.adapter_agent.update_device(device)
         else:
-            device.oper_status = OperStatus.UNKNOWN
-            device.reason = 'operation status unknown'
+            device.oper_status = OperStatus.FAILED
+            device.reason = 'Failed to Intialize OLT'
+            self.adapter_agent.update_device(device)
             reactor.callLater(15, self.activate, device)
         return
 
@@ -1162,7 +1166,8 @@
     def handle_omci_ind(self, ind_info):
         child_device = self.adapter_agent.get_child_device(
             self.device_id,
-            onu_id=ind_info['onu_id'])
+            onu_id=ind_info['onu_id'],
+            parent_port_no=ind_info['intf_id'])
         if child_device is None:
             self.log.info('Onu-is-not-configured', onu_id=ind_info['onu_id'])
             return
@@ -1278,25 +1283,34 @@
                         ("pon_id-%u-is-greater-than-%u"
                          % (data.data.xgs_ponid, max_pon_ports))
 
-                self.log.info('Activating-PON-port-at-OLT',
-                              pon_id=data.data.xgs_ponid)
+                # When reconcile is in progress, we just want to
+                # re-build the information in our local cache and
+                # not really go and configure the device.
+                # Because, the entire configuration is replayed on
+                # reconfiguration, we check and configure the device
+                # only when we are not reconciling (after VOLTHA restart.)
+                if not self.reconcile_in_progress:
+                    self.log.info('Activating-PON-port-at-OLT',
+                                  pon_id=data.data.xgs_ponid)
 
-                # We put the transaction in a deferredQueue
-                # Only one transaction can exist in the queue at a given time.
-                # We do this we enable the pon ports sequentially.
-                while True:
-                    try:
-                        self.pon_port_config_resp.put(data)
-                        break
-                    except QueueOverflow:
-                        self.log.info('another-pon-port-enable-pending')
-                        yield asleep(0.3)
+                    # We put the transaction in a deferredQueue
+                    # Only one transaction can exist in the queue
+                    # at a given time. We do this we enable the pon
+                    # ports sequentially.
+                    while True:
+                        try:
+                            self.pon_port_config_resp.put(data)
+                            break
+                        except QueueOverflow:
+                            self.log.info('another-pon-port-enable-pending')
+                            yield asleep(0.3)
 
-                self.add_port(port_no=data.data.xgs_ponid,
-                              port_type=Port.PON_OLT,
-                              label=data.name)
-                self.bal.activate_pon_port(self.olt_id, data.data.xgs_ponid,
-                                           self.transceiver_type)
+                    self.add_port(port_no=data.data.xgs_ponid,
+                                  port_type=Port.PON_OLT,
+                                  label=data.name)
+                    self.bal.activate_pon_port(self.olt_id, data.data.xgs_ponid,
+                                               self.transceiver_type)
+
                 if data.name in self.channel_terminations:
                     self.log.info('Channel-termination-already-present',
                                   channel_termination=data)
@@ -1308,7 +1322,20 @@
                     self.log.info('channel-termnination-data',
                                    data=data,chan_term=self.channel_terminations)
             if isinstance(data, VOntaniConfig):
-                self.handle_v_ont_ani_config(data)
+                if data.data.onu_id > MAX_ONU_ID_PER_PON_PORT:
+                    self.log.error("invalid-onu-id", onu_id=data.data.onu_id)
+                    raise Exception("onu-id-greater-than-{}-not-supported".
+                                    format(data.data.onu_id))
+
+                # When reconcile is in progress, we just want to
+                # re-build the information in our local cache and
+                # not really go and configure the device.
+                # Because, the entire configuration is replayed on
+                # reconfiguration, we check and configure the device
+                # only when we are not reconciling (after VOLTHA restart.)
+                if not self.reconcile_in_progress:
+                    self.handle_v_ont_ani_config(data)
+
                 if data.name in self.v_ont_anis:
                     self.log.info('v_ont_ani-already-present',
                                   v_ont_ani=data)
@@ -1405,9 +1432,12 @@
                 traffic_descriptor
         if tcont_data.interface_reference in self.v_ont_anis:
             v_ont_ani = self.v_ont_anis[tcont_data.interface_reference]
+            pon_port = self._get_pon_port_from_pref_chanpair_ref(\
+                          v_ont_ani.v_ont_ani.data.preferred_chanpair)
             onu_device = self.adapter_agent.get_child_device(
                 self.device_id,
-                onu_id=v_ont_ani.v_ont_ani.data.onu_id)
+                onu_id=v_ont_ani.v_ont_ani.data.onu_id,
+                parent_port_no=pon_port)
             if (onu_device is not None and
                         onu_device.oper_status == OperStatus.ACTIVE):
                 owner_info = dict()
@@ -1439,9 +1469,12 @@
             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(
+            pon_port = self._get_pon_port_from_pref_chanpair_ref(\
+                          v_ont_ani.v_ont_ani.data.preferred_chanpair)
+            onu_device = self.adapter_agent.get_child_device(
                 self.device_id,
-                onu_id=v_ont_ani.v_ont_ani.data.onu_id)
+                onu_id=v_ont_ani.v_ont_ani.data.onu_id,
+                parent_port_no=pon_port)
             # To-Do: Right Now use alloc_id as schduler ID. Need to
             # find way to generate uninqe number.
             id = tcont_data.alloc_id
@@ -1511,36 +1544,28 @@
 
         # deactivate olt
         yield self.bal.deactivate_olt()
+
+        # disable all ports on the device
+        self.adapter_agent.disable_all_ports(self.device_id)
+
         device.admin_state = AdminState.DISABLED
         device.oper_status = OperStatus.FAILED
         device.connect_status = ConnectStatus.UNREACHABLE
         self.adapter_agent.update_device(device)
         # deactivate nni port
-        for port in self.asfvolt_device_info.sfp_device_presence_map.iterkeys():
-            # ignore any sfp other than the NNI.
-            if not self._valid_nni_port(port):
-                continue
+        self.update_logical_port(self.nni_intf_id + MIN_ASFVOLT_NNI_LOGICAL_PORT_NUM,
+                                 Port.ETHERNET_NNI,
+                                 OFPPS_LINK_DOWN)
 
-            # Since OLT is reachable after reboot, OLT should configurable with
-            # all the old existing flows. NNI ports should be mark it as down for
-            # ONOS to push the old flows
-            nni_intf_id = (port -
-                           self.asfvolt_device_info.
-                           asfvolt16_device_topology.num_of_pon_ports)
-            self.update_logical_port(nni_intf_id + MIN_ASFVOLT_NNI_LOGICAL_PORT_NUM,
-                                     Port.ETHERNET_NNI,
-                                     OFPPS_LINK_DOWN)
-
-        # send event to child devices(onus)
+        # disable child devices(onus)
         for child_device in self.adapter_agent.\
                 get_child_devices(self.device_id):
-           msg = {'proxy_address': child_device.proxy_address,
-                  'event': 'olt-disabled'}
-           # Send evnt to such child devices which were REACHABLE,
-           # so that those devices can be marked UNREACHABLE
-           if child_device.connect_status == ConnectStatus.REACHABLE:
-               self.adapter_agent.publish_inter_adapter_message(child_device.id,
-                                                                msg)
+            msg = {'proxy_address': child_device.proxy_address,
+                   'event': 'olt-disabled'}
+            if child_device.oper_status == OperStatus.ACTIVE and \
+               child_device.connect_status == ConnectStatus.REACHABLE:
+                self.adapter_agent.publish_inter_adapter_message(child_device.id,
+                                                                 msg)
 
     @inlineCallbacks
     def reenable(self):
@@ -1568,32 +1593,24 @@
         device.connect_status = ConnectStatus.REACHABLE
         self.adapter_agent.update_device(device)
 
+        # enable all ports on the device
+        self.adapter_agent.enable_all_ports(self.device_id)
+
         # activate nni port
-        for port in self.asfvolt_device_info.sfp_device_presence_map.iterkeys():
-            # ignore any sfp other than the NNI.
-            if not self._valid_nni_port(port):
-                continue
+        self.update_logical_port(self.nni_intf_id + MIN_ASFVOLT_NNI_LOGICAL_PORT_NUM,
+                                 Port.ETHERNET_NNI,
+                                 OFPPS_LIVE)
 
-            # Since OLT is reachable after reboot, OLT should configurable with
-            # all the old existing flows. NNI ports should be mark it as down for
-            # ONOS to push the old flows
-            nni_intf_id = (port -
-                           self.asfvolt_device_info.
-                           asfvolt16_device_topology.num_of_pon_ports)
-            self.update_logical_port(nni_intf_id + MIN_ASFVOLT_NNI_LOGICAL_PORT_NUM,
-                                     Port.ETHERNET_NNI,
-                                     OFPPS_LIVE)
-
-        # send event to child devices(onus)
+        # enable child devices(onus)
         for child_device in self.adapter_agent.\
                 get_child_devices(self.device_id):
-           msg = {'proxy_address': child_device.proxy_address,
-                  'event': 'olt-enabled'}
-           # Send evnt to such child devices which were UNREACHABLE,
-           # so that those devices can be marked REACHABLE
-           if child_device.connect_status == ConnectStatus.UNREACHABLE:
-               self.adapter_agent.publish_inter_adapter_message(child_device.id,
-                                                                msg)
+            msg = {'proxy_address': child_device.proxy_address,
+                   'event': 'olt-enabled'}
+            if child_device.oper_status == OperStatus.ACTIVE and \
+               child_device.connect_status == ConnectStatus.UNREACHABLE:
+                # Send the event message to the ONU adapter
+                self.adapter_agent.publish_inter_adapter_message(child_device.id,
+                                                                 msg)
 
     def delete(self):
         super(Asfvolt16Handler, self).delete()
@@ -1894,8 +1911,11 @@
             self.log.info('Failed-to-get-tcont-info',
                           tcont=gem_port.tcont_ref)
             return
+        pon_port = self._get_pon_port_from_pref_chanpair_ref(\
+                      v_ont_ani.v_ont_ani.data.preferred_chanpair)
         onu_device = self.adapter_agent.get_child_device(
-            self.device_id, onu_id=v_ont_ani.v_ont_ani.data.onu_id)
+            self.device_id, onu_id=v_ont_ani.v_ont_ani.data.onu_id,
+            parent_port_no=pon_port)
         if onu_device is None:
             self.log.info('Failed-to-get-onu-device',
                           onu_id=v_ont_ani.v_ont_ani.data.onu_id)
@@ -1918,7 +1938,7 @@
                           flow_id=flow_id,
                           sched_info=tcont.alloc_id)
             yield self.bal.add_flow(onu_device.proxy_address.onu_id,
-                              onu_device.proxy_address.channel_id,
+                              onu_device.proxy_address.channel_id, self.nni_intf_id,
                               flow_id, gem_port.gemport_id,
                               uplink_classifier, is_down_stream,
                               action_info=uplink_action,
@@ -1945,12 +1965,12 @@
         try:
             self.log.info('Adding-Downstream-EAPOL-flow',
                           classifier=downlink_classifier,
-                          action=downlink_action,
+                          action_info=downlink_action,
                           gem_port=gem_port,
                           flow_id=downlink_flow_id,
                           sched_info=tcont.alloc_id)
             yield self.bal.add_flow(onu_device.proxy_address.onu_id,
-                              onu_device.proxy_address.channel_id,
+                              onu_device.proxy_address.channel_id, self.nni_intf_id,
                               downlink_flow_id, gem_port.gemport_id,
                               downlink_classifier, is_down_stream)
             # To-Do. While addition of one flow is in progress,
@@ -2000,8 +2020,11 @@
             self.log.error('Failed-to-get-tcont-info',
                            tcont=gem_port.tcont_ref)
             return
+        pon_port = self._get_pon_port_from_pref_chanpair_ref(\
+                      v_ont_ani.v_ont_ani.data.preferred_chanpair)
         onu_device = self.adapter_agent.get_child_device(
-            self.device_id, onu_id=v_ont_ani.v_ont_ani.data.onu_id)
+            self.device_id, onu_id=v_ont_ani.v_ont_ani.data.onu_id,
+            parent_port_no=pon_port)
         if onu_device is None:
             self.log.error('Failed-to-get-onu-device',
                            onu_id=v_ont_ani.v_ont_ani.data.onu_id)
@@ -2021,7 +2044,7 @@
                           flow_id=flow_id,
                           sched_info=tcont.alloc_id)
             yield self.bal.add_flow(onu_device.proxy_address.onu_id,
-                              onu_device.proxy_address.channel_id,
+                              onu_device.proxy_address.channel_id, self.nni_intf_id,
                               flow_id, gem_port.gemport_id,
                               uplink_classifier, is_down_stream,
                               action_info=uplink_action,
@@ -2069,7 +2092,7 @@
                           flow_id=downlink_flow_id,
                           sched_info=tcont.alloc_id)
             yield self.bal.add_flow(onu_device.proxy_address.onu_id,
-                              onu_device.proxy_address.channel_id,
+                              onu_device.proxy_address.channel_id, self.nni_intf_id,
                               downlink_flow_id, gem_port.gemport_id,
                               downlink_classifier, is_down_stream,
                               action_info=downlink_action)
@@ -2114,12 +2137,12 @@
         # 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)
+                          v_enet, ASFVOLT_HSIA_ID)
 
     @inlineCallbacks
     def add_hsia_flow(self, uplink_classifier, uplink_action,
                      downlink_classifier, downlink_action,
-                     v_enet, hsia_id, downlink_hsia_id):
+                     v_enet, hsia_id):
         # Add Upstream Firmware Flow.
         # To-Do For a time being hard code the traffic class value.
         # Need to know how to get the traffic class info from flows.
@@ -2139,8 +2162,11 @@
             self.log.info('Failed-to-get-tcont-info',
                           tcont=gem_port.tcont_ref)
             return
+        pon_port = self._get_pon_port_from_pref_chanpair_ref(\
+                      v_ont_ani.v_ont_ani.data.preferred_chanpair)
         onu_device = self.adapter_agent.get_child_device(
-            self.device_id, onu_id=v_ont_ani.v_ont_ani.data.onu_id)
+            self.device_id, onu_id=v_ont_ani.v_ont_ani.data.onu_id,
+            parent_port_no=pon_port)
         if onu_device is None:
             self.log.info('Failed-to-get-onu-device',
                           onu_id=v_ont_ani.v_ont_ani.data.onu_id)
@@ -2158,7 +2184,7 @@
                           flow_id=flow_id,
                           sched_info=tcont.alloc_id)
             yield self.bal.add_flow(onu_device.proxy_address.onu_id,
-                              onu_device.proxy_address.channel_id,
+                              onu_device.proxy_address.channel_id, self.nni_intf_id,
                               flow_id, gem_port.gemport_id,
                               uplink_classifier, is_down_stream,
                               action_info=uplink_action,
@@ -2173,13 +2199,13 @@
                                action=uplink_action,
                                onu_id=onu_device.proxy_address.onu_id,
                                intf_id=onu_device.proxy_address.channel_id)
+            return
         is_down_stream = True
         # To-Do: For Now hard code the p-bit values.
         #downlink_classifier['vlan_pcp'] = 7
         downlink_flow_id = self.get_flow_id(onu_device.proxy_address.onu_id,
                                             onu_device.proxy_address.channel_id,
                                             hsia_id)
-                                            #downlink_hsia_id)
         try:
             self.log.info('Adding-ARP-downstream-flow',
                           classifier=downlink_classifier,
@@ -2187,7 +2213,7 @@
                           gem_port=gem_port,
                           flow_id=downlink_flow_id)
             yield self.bal.add_flow(onu_device.proxy_address.onu_id,
-                              onu_device.proxy_address.channel_id,
+                              onu_device.proxy_address.channel_id, self.nni_intf_id,
                               downlink_flow_id, gem_port.gemport_id,
                               downlink_classifier, is_down_stream,
                               action_info=downlink_action)
@@ -2201,6 +2227,7 @@
                                action=downlink_action,
                                onu_id=onu_device.proxy_address.onu_id,
                                intf_id=onu_device.proxy_address.channel_id)
+            return
 
     @inlineCallbacks
     def del_all_flow(self, v_enet):
@@ -2220,8 +2247,11 @@
             self.log.info('Failed-to-get-v_ont_ani',
                           v_ont_ani=v_enet.v_enet.data.v_ontani_ref)
             return
+        pon_port = self._get_pon_port_from_pref_chanpair_ref(\
+                      v_ont_ani.v_ont_ani.data.preferred_chanpair)
         onu_device = self.adapter_agent.get_child_device(
-            self.device_id, onu_id=v_ont_ani.v_ont_ani.data.onu_id)
+            self.device_id, onu_id=v_ont_ani.v_ont_ani.data.onu_id,
+            parent_port_no=pon_port)
         if onu_device is None:
             self.log.info('Failed-to-get-onu-device',
                           onu_id=v_ont_ani.v_ont_ani.data.onu_id)
@@ -2302,3 +2332,39 @@
 
         except Exception as e:
             raise Exception('option-parsing-error: {}'.format(e.message))
+
+    def _get_next_uni_port(self):
+        uni_ports = self.adapter_agent.get_ports(self.device_id,
+                                                 Port.ETHERNET_UNI)
+        uni_port_nums = set([uni_port.port_no for uni_port in uni_ports])
+        # We need to start allocating port numbers from ONU_UNI_PORT_START_ID.
+        # Find the first unused port number.
+        next_port_num = next(ifilterfalse(uni_port_nums.__contains__,
+                                          count(ONU_UNI_PORT_START_ID)))
+        if next_port_num <= 65535:
+            return next_port_num
+        else:
+            raise ValueError("invalid-port-number-{}".format(next_port_num))
+
+    def _valid_nni_port(self, port):
+        if port < self.asfvolt_device_info.asfvolt16_device_topology.num_of_pon_ports or \
+            port >= (self.asfvolt_device_info.asfvolt16_device_topology.num_of_pon_ports +
+                     self.asfvolt_device_info.asfvolt16_device_topology.num_of_nni_ports):
+            return False
+        return True
+
+    def _get_pon_port_from_pref_chanpair_ref(self, chanpair_ref):
+        pon_id = -1
+        # return the pon port corresponding to the channel_termination
+        # whose chanpair_ref mathes the passed channelpair_ref
+        for channel_termination in self.channel_terminations.itervalues():
+            if channel_termination.data.channelpair_ref == chanpair_ref:
+                self.log.debug("channel-termination-entry-found",
+                                pon_id=channel_termination.data.xgs_ponid,
+                                chanpair_ref=chanpair_ref)
+                return channel_termination.data.xgs_ponid
+
+        if pon_id < 0:
+            raise Exception("pon-id-not-found-for-chanpair-ref {}".\
+                             format(chanpair_ref))
+
diff --git a/voltha/adapters/asfvolt16_olt/asfvolt16_ind_handler.py b/voltha/adapters/asfvolt16_olt/asfvolt16_ind_handler.py
index 0369528..ea6269d 100644
--- a/voltha/adapters/asfvolt16_olt/asfvolt16_ind_handler.py
+++ b/voltha/adapters/asfvolt16_olt/asfvolt16_ind_handler.py
@@ -304,6 +304,8 @@
         ind_info = dict()
         ind_info['_object_type'] = 'packet_in_indication'
         ind_info['_sub_group_type'] = 'omci_message'
+        ind_info['intf_id'] = \
+        indication.balOmciResp.key.packet_send_dest.itu_omci_channel.intf_id
         packet_data = indication.balOmciResp.key.packet_send_dest
         ind_info['onu_id'] = packet_data.itu_omci_channel.sub_term_id
         ind_info['packet'] = indication.balOmciResp.data.pkt
diff --git a/voltha/adapters/asfvolt16_olt/bal.py b/voltha/adapters/asfvolt16_olt/bal.py
index 409c223..43cdef1 100644
--- a/voltha/adapters/asfvolt16_olt/bal.py
+++ b/voltha/adapters/asfvolt16_olt/bal.py
@@ -15,6 +15,8 @@
 #
 
 from twisted.internet.defer import inlineCallbacks, returnValue
+from twisted.internet import reactor
+
 from voltha.adapters.asfvolt16_olt.protos import bal_pb2, \
     bal_model_types_pb2, bal_model_ids_pb2, bal_indications_pb2, asfvolt_pb2
 from voltha.adapters.asfvolt16_olt.grpc_client import GrpcClient
@@ -25,12 +27,11 @@
 import time
 import os
 
-"""
-ASFVOLT Adapter port is 60001
-"""
+# ASFVOLT Adapter port is 60001
 ADAPTER_PORT = 60001
 
 GRPC_TIMEOUT = 5
+GRPC_HEARTBEAT_TIMEOUT = 2
 
 
 class Bal(object):
@@ -39,6 +40,7 @@
         self.grpc_client = GrpcClient(self.log)
         self.stub = None
         self.ind_stub = None
+        self.asfvolt_stub = None
         self.device_id = None
         self.olt = olt
         self.interval = 0.05
@@ -56,7 +58,7 @@
         # Right now Bi-Directional GRPC support is not there in grpc-c.
         # This code may be needed when bidirectional supported added
         # in GRPC-C
-        if is_init == True:
+        if is_init is True:
             init = bal_pb2.BalInit()
             try:
                 os.environ["SERVICE_HOST_IP"]
@@ -69,7 +71,7 @@
             ip_port.append(str(adapter_ip))
             ip_port.append(":")
             ip_port.append(str(ADAPTER_PORT))
-            init.voltha_adapter_ip_port ="".join(ip_port)
+            init.voltha_adapter_ip_port = "".join(ip_port)
             self.log.info('Adapter-port-IP', init.voltha_adapter_ip_port)
             self.log.info('connecting-olt', host_and_port=host_and_port,
                           init_details=init)
@@ -223,8 +225,9 @@
         yield self.stub.BalCfgSet(obj, timeout=GRPC_TIMEOUT)
 
     @inlineCallbacks
-    def add_flow(self, onu_id, intf_id, flow_id, gem_port,
-                 classifier_info, is_downstream,
+    def add_flow(self, onu_id=None, intf_id=None, network_int_id=None,
+                 flow_id=None, gem_port=None,
+                 classifier_info=None, is_downstream=False,
                  action_info=None, sched_id=None):
         try:
             obj = bal_pb2.BalCfg()
@@ -243,11 +246,19 @@
                     bal_model_types_pb2.BAL_FLOW_TYPE_DOWNSTREAM
 
             obj.flow.data.admin_state = bal_model_types_pb2.BAL_STATE_UP
-            obj.flow.data.access_int_id = intf_id
-            # obj.flow.data.network_int_id = intf_id
-            obj.flow.data.sub_term_id = onu_id
-            obj.flow.data.svc_port_id = gem_port
+            if intf_id is not None:
+                obj.flow.data.access_int_id = intf_id
+            if network_int_id is not None:
+                obj.flow.data.network_int_id = network_int_id
+            if onu_id:
+                obj.flow.data.sub_term_id = onu_id
+            if gem_port:
+                obj.flow.data.svc_port_id = gem_port
             obj.flow.data.classifier.presence_mask = 0
+
+            if classifier_info is None:
+                classifier_info = dict()
+
             if 'eth_type' in classifier_info:
                 obj.flow.data.classifier.ether_type = \
                     classifier_info['eth_type']
@@ -309,6 +320,9 @@
                 obj.flow.data.classifier.presence_mask |= \
                     bal_model_types_pb2.BAL_CLASSIFIER_ID_PKT_TAG_TYPE
 
+            # Action field is not mandatory in Downstream
+            # If the packet matches the classifiers, the packet is put
+            # on the gem port specified.
             if action_info is not None:
                 obj.flow.data.action.presence_mask = 0
                 obj.flow.data.action.cmds_bitmask = 0
@@ -334,8 +348,9 @@
                     obj.flow.data.action.presence_mask |= \
                         bal_model_types_pb2.BAL_ACTION_ID_CMDS_BITMASK
                 else:
-                    self.log.info('Invalid-action-field')
+                    self.log.info('invalid-action',action_info=action_info)
                     return
+
             self.log.info('adding-flow-to-OLT-Device',
                           flow_details=obj)
             yield self.stub.BalCfgSet(obj, timeout=GRPC_TIMEOUT)
@@ -508,7 +523,8 @@
         try:
             obj = bal_pb2.BalHeartbeat()
             obj.device_id = device_id
-            rebootStatus = yield self.stub.BalApiHeartbeat(obj, timeout=GRPC_TIMEOUT)
+            rebootStatus = yield self.stub.BalApiHeartbeat(
+                                 obj, timeout=GRPC_HEARTBEAT_TIMEOUT)
             self.log.info('OLT-HeartBeat-Response-Received-from',
                           device=device_id, rebootStatus=rebootStatus)
             returnValue(rebootStatus)
@@ -553,10 +569,12 @@
                 obj.device_id = str(device_id)
                 bal_ind = self.ind_stub.BalGetIndFromDevice(obj, timeout=GRPC_TIMEOUT)
                 if bal_ind.ind_present == True:
-                    self.log.info('Indication-received',
-                                  device=device_id, bal_ind=bal_ind)
+                    # self.log.info('Indication-received',
+                    #               device=device_id, bal_ind=bal_ind)
                     self.ind_obj.handle_indication_from_bal(bal_ind, self.olt)
-                time.sleep(self.interval)
             except Exception as e:
                 self.log.info('Failed-to-get-indication-info', exc=str(e))
+            finally:
+                time.sleep(self.interval)
+
         self.log.debug('stop-indication-receive-thread')
diff --git a/voltha/adapters/broadcom_onu/broadcom_onu.py b/voltha/adapters/broadcom_onu/broadcom_onu.py
index d72f9de..60617ce 100644
--- a/voltha/adapters/broadcom_onu/broadcom_onu.py
+++ b/voltha/adapters/broadcom_onu/broadcom_onu.py
@@ -63,7 +63,7 @@
     supported_device_types = [
         DeviceType(
             id=name,
-            vendor_ids=['BRCM', 'ALPH'],
+            vendor_ids=['BRCM', 'TWSH', 'ALPH'],
             adapter=name,
             accepts_bulk_flow_update=True
         )
@@ -75,7 +75,7 @@
         self.descriptor = Adapter(
             id=self.name,
             vendor='Voltha project',
-            version='0.45',
+            version='0.46',
             config=AdapterConfig(log_level=LogLevel.INFO)
         )
         self.devices_handlers = dict()  # device_id -> BroadcomOnuHandler()
@@ -347,6 +347,8 @@
 
             else:
                 device = self.adapter_agent.get_device(self.device_id)
+                self.disable_ports(device)
+                device.connect_status = ConnectStatus.UNREACHABLE
                 device.oper_status = OperStatus.FAILED
                 self.adapter_agent.update_device(device)
 
@@ -379,6 +381,18 @@
                 device.oper_status = OperStatus.FAILED
                 self.adapter_agent.update_device(device)
 
+        elif event_msg['event'] == 'olt-disabled':
+            self.adapter_agent.disable_all_ports(self.device_id)
+            device = self.adapter_agent.get_device(self.device_id)
+            device.connect_status = ConnectStatus.UNREACHABLE
+            self.adapter_agent.update_device(device)
+
+        elif event_msg['event'] == 'olt-enabled':
+            self.adapter_agent.enable_all_ports(self.device_id)
+            device = self.adapter_agent.get_device(self.device_id)
+            device.connect_status = ConnectStatus.REACHABLE
+            self.adapter_agent.update_device(device)
+
         elif event_msg['event'] == 'create-tcont':
             tcont = TcontsConfigData()
             tcont.alloc_id = event_msg['event_data']['alloc_id']
@@ -393,15 +407,6 @@
             gem_port = GemportsConfigData()
             gem_port.gemport_id = event_msg['event_data']['gemport_id']
             self.create_gemport(gem_port)
-        elif event_msg['event'] == 'olt-disabled':
-            device = self.adapter_agent.get_device(self.device_id)
-            device.connect_status = ConnectStatus.UNREACHABLE
-            self.adapter_agent.update_device(device)
-
-        elif event_msg['event'] == 'olt-enabled':
-            device = self.adapter_agent.get_device(self.device_id)
-            device.connect_status = ConnectStatus.REACHABLE
-            self.adapter_agent.update_device(device)
 
         # Handle next event
         reactor.callLater(0, self.handle_onu_events)
@@ -470,13 +475,16 @@
         self.log.info('reconciling-broadcom-onu-device-ends')
 
     def update_logical_port(self, logical_device_id, port_id, state):
-        self.log.info('updating-logical-port', logical_port_id=port_id,
-                      logical_device_id=logical_device_id, state=state)
-        logical_port = self.adapter_agent.get_logical_port(logical_device_id,
-                                                           port_id)
-        logical_port.ofp_port.state = state
-        self.adapter_agent.update_logical_port(logical_device_id,
-                                               logical_port)
+        try:
+            self.log.info('updating-logical-port', logical_port_id=port_id,
+                          logical_device_id=logical_device_id, state=state)
+            logical_port = self.adapter_agent.get_logical_port(logical_device_id,
+                                                               port_id)
+            logical_port.ofp_port.state = state
+            self.adapter_agent.update_logical_port(logical_device_id,
+                                                   logical_port)
+        except Exception as e:
+            self.log.exception("exception-updating-port",e=e)
 
     def delete(self, device):
         self.log.info('delete-onu')
@@ -1410,13 +1418,10 @@
             yield self.wait_for_response()
 
 
-
-
-
-           # Set AR - ExtendedVlanTaggingOperationConfigData
+            # 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, cvid)
-            #yield self.wait_for_response()
+            # self.send_set_extended_vlan_tagging_operation_vlan_configuration_data_single_tag(0x200 + port_id, 8, 0, 0, 1, 8, cvid)
+            # yield self.wait_for_response()
 
             # Set AR - ExtendedVlanTaggingOperationConfigData
             #          514 - RxVlanTaggingOperationTable - add VLAN <cvid> to untagged pkts - c-vid
@@ -1528,19 +1533,10 @@
         self.adapter_agent.delete_logical_port_by_id(parent_logical_device_id,
                                                      'uni-{}'.format(port_no))
 
-    @inlineCallbacks
     def delete_v_ont_ani(self, data):
         self.log.info('deleting-v_ont_ani')
 
         device = self.adapter_agent.get_device(self.device_id)
-        # construct message
-        # MIB Reset - OntData - 0
-        if device.connect_status != ConnectStatus.REACHABLE:
-            self.log.error('device-unreachable')
-            returnValue(None)
-
-        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)
 
@@ -1551,6 +1547,22 @@
                     self.adapter_agent.delete_port(self.device_id, port)
                     break
 
+        # construct message
+        # MIB Reset - OntData - 0
+        if device.connect_status != ConnectStatus.REACHABLE:
+            self.log.error('device-unreachable')
+            return
+
+        self.send_mib_reset()
+
+        # It is observed that the device is already deleted before the response
+        # is received. So, there is no point waiting for response.
+        # Also, currently there is no response validation or timeout.
+        # Until we move to the OpenOMCI framework, it is ok to ignore this
+        # response for now.
+
+        # yield self.wait_for_response()
+
     def create_interface(self, data):
         if isinstance(data, VEnetConfig):
             parent_port_num = None
@@ -1586,6 +1598,8 @@
 
             pon_port.peers[0].device_id = onu_device.parent_id
             pon_port.peers[0].port_no = parent_port_num
+            pon_port.admin_state = AdminState.ENABLED
+            pon_port.oper_status = OperStatus.ACTIVE
             self.adapter_agent.add_port_reference_to_parent(self.device_id,
                                                             pon_port)
         else:
@@ -1655,7 +1669,7 @@
         device = self.adapter_agent.get_device(self.device_id)
         if device.connect_status != ConnectStatus.REACHABLE:
             self.log.error('device-unreachable')
-            returnValue(None)
+            return
 
         self.send_set_8021p_mapper_service_profile(0x8001,
                                                   0xFFFF)
@@ -1688,7 +1702,7 @@
         device = self.adapter_agent.get_device(self.device_id)
         if device.connect_status != ConnectStatus.REACHABLE:
             self.log.error('device-unreachable')
-            returnValue(None)
+            return
 
         self.send_set_tcont(0x8001, 0xFFFF)
         yield self.wait_for_response()
@@ -1700,21 +1714,10 @@
     def disable(self, device):
         try:
             self.log.info('sending-admin-state-lock-towards-device', device=device)
-
             self.send_set_admin_state(0x0000, ADMIN_STATE_LOCK)
             yield self.wait_for_response()
-            device = self.adapter_agent.get_device(device.id)
             # Disable all ports on that device
-            self.adapter_agent.disable_all_ports(self.device_id)
-            parent_device = self.adapter_agent.get_device(device.parent_id)
-            logical_device_id = parent_device.parent_id
-            assert logical_device_id
-            # Mark OF PORT STATE DOWN
-            ports = self.adapter_agent.get_ports(device.id, Port.ETHERNET_UNI)
-            for port in ports:
-                state = OFPPS_LINK_DOWN
-                port_id = 'uni-{}'.format(port.port_no)
-                self.update_logical_port(logical_device_id, port_id, state)
+            self.disable_ports(device)
             device.oper_status = OperStatus.UNKNOWN
             device.connect_status = ConnectStatus.UNREACHABLE
             self.adapter_agent.update_device(device)
@@ -1727,18 +1730,7 @@
             self.log.info('sending-admin-state-unlock-towards-device', device=device)
             self.send_set_admin_state(0x0000, ADMIN_STATE_UNLOCK)
             yield self.wait_for_response()
-            device = self.adapter_agent.get_device(device.id)
-            # Re-enable the ports on that device
-            self.adapter_agent.enable_all_ports(device.id)
-            parent_device = self.adapter_agent.get_device(device.parent_id)
-            logical_device_id = parent_device.parent_id
-            assert logical_device_id
-            # Mark OF PORT STATE UP
-            ports = self.adapter_agent.get_ports(device.id, Port.ETHERNET_UNI)
-            for port in ports:
-                state = OFPPS_LIVE
-                port_id = 'uni-{}'.format(port.port_no)
-                self.update_logical_port(logical_device_id, port_id, state)
+            self.enable_ports(device)
             device.oper_status = OperStatus.ACTIVE
             device.connect_status = ConnectStatus.REACHABLE
             self.adapter_agent.update_device(device)
@@ -1751,52 +1743,56 @@
         device = self.adapter_agent.get_device(self.device_id)
         if device.connect_status != ConnectStatus.REACHABLE:
             self.log.error("device-unreacable")
-            returnValue(None)
+            return
 
-        self.send_reboot()
-        response = yield self.wait_for_response()
-        if response is not None:
-            omci_response = response.getfieldval("omci_message")
-            success_code = omci_response.getfieldval("success_code")
-            if success_code == 0:
-                self.log.info("reboot-command-processed-successfully")
-                # Update the device connection and operation status
-                device = self.adapter_agent.get_device(self.device_id)
-                device.connect_status = ConnectStatus.UNREACHABLE
-                device.oper_status = OperStatus.DISCOVERED
-                self.adapter_agent.update_device(device)
-                self.disable_ports(device)
+        try:
+            self.send_reboot()
+            response = yield self.wait_for_response()
+            if response is not None:
+                omci_response = response.getfieldval("omci_message")
+                success_code = omci_response.getfieldval("success_code")
+                if success_code == 0:
+                    self.log.info("reboot-command-processed-successfully")
+                    # Update the device connection and operation status
+                    device = self.adapter_agent.get_device(self.device_id)
+                    device.connect_status = ConnectStatus.UNREACHABLE
+                    device.oper_status = OperStatus.DISCOVERED
+                    self.adapter_agent.update_device(device)
+                    self.disable_ports(device)
+                else:
+                    self.log.info("reboot-failed", success_code=success_code)
             else:
-                self.log.info("reboot-failed", success_code=success_code)
-        else:
-            self.log.info("error-in-processing-reboot-response")
+                self.log.info("error-in-processing-reboot-response")
+        except Exception as e:
+            self.log.info('wait-for-response-exception', exc=str(e))
 
-    def disable_ports(self, onu_device):
+    def disable_ports(self, device):
         self.log.info('disable-ports', device_id=self.device_id)
 
         # Disable all ports on that device
         self.adapter_agent.disable_all_ports(self.device_id)
 
-        parent_device = self.adapter_agent.get_device(onu_device.parent_id)
+        parent_device = self.adapter_agent.get_device(device.parent_id)
         assert parent_device
         logical_device_id = parent_device.parent_id
         assert logical_device_id
-        ports = self.adapter_agent.get_ports(onu_device.id, Port.ETHERNET_UNI)
+        ports = self.adapter_agent.get_ports(device.id, Port.ETHERNET_UNI)
         for port in ports:
             port_id = 'uni-{}'.format(port.port_no)
             self.update_logical_port(logical_device_id, port_id, OFPPS_LINK_DOWN)
 
-    def enable_ports(self, onu_device):
+    def enable_ports(self, device):
         self.log.info('enable-ports', device_id=self.device_id)
 
-        # Disable all ports on that device
+        # Enable all ports on that device
         self.adapter_agent.enable_all_ports(self.device_id)
 
-        parent_device = self.adapter_agent.get_device(onu_device.parent_id)
+        parent_device = self.adapter_agent.get_device(device.parent_id)
         assert parent_device
         logical_device_id = parent_device.parent_id
         assert logical_device_id
-        ports = self.adapter_agent.get_ports(onu_device.id, Port.ETHERNET_UNI)
+        ports = self.adapter_agent.get_ports(device.id, Port.ETHERNET_UNI)
         for port in ports:
             port_id = 'uni-{}'.format(port.port_no)
             self.update_logical_port(logical_device_id, port_id, OFPPS_LIVE)
+