VOL-1139 Reboot works.  Refactored disable/enable and up/down indications handling to be consistent with existing broadcom handler.

Change-Id: I38ae1ccf6030d77a2f9bf4bef239ca899403522b
diff --git a/voltha/adapters/brcm_openomci_onu/brcm_openomci_onu.py b/voltha/adapters/brcm_openomci_onu/brcm_openomci_onu.py
index c0e32cf..1559cfa 100644
--- a/voltha/adapters/brcm_openomci_onu/brcm_openomci_onu.py
+++ b/voltha/adapters/brcm_openomci_onu/brcm_openomci_onu.py
@@ -33,6 +33,7 @@
 from common.frameio.frameio import hexify
 from voltha.extensions.omci.openomci_agent import OpenOMCIAgent, OpenOmciAgentDefaults
 from voltha.extensions.omci.omci_me import *
+from voltha.extensions.omci.database.mib_db_dict import MibDbVolatileDict
 from omci.brcm_capabilities_task import BrcmCapabilitiesTask
 from omci.brcm_get_mds_task import BrcmGetMdsTask
 from omci.brcm_mib_sync import BrcmMibSynchronizer
@@ -73,6 +74,7 @@
         self.broadcom_omci = deepcopy(OpenOmciAgentDefaults)
 
         self.broadcom_omci['mib-synchronizer']['state-machine'] = BrcmMibSynchronizer
+        self.broadcom_omci['mib-synchronizer']['database'] = MibDbVolatileDict
         #self.broadcom_omci['mib-synchronizer']['tasks']['get-mds'] = BrcmGetMdsTask
         #self.broadcom_omci['mib-synchronizer']['tasks']['mib-audit'] = BrcmGetMdsTask
         self.broadcom_omci['omci-capabilities']['tasks']['get-capabilities'] = BrcmCapabilitiesTask
diff --git a/voltha/adapters/brcm_openomci_onu/brcm_openomci_onu_handler.py b/voltha/adapters/brcm_openomci_onu/brcm_openomci_onu_handler.py
index 242c706..b561feb 100644
--- a/voltha/adapters/brcm_openomci_onu/brcm_openomci_onu_handler.py
+++ b/voltha/adapters/brcm_openomci_onu/brcm_openomci_onu_handler.py
@@ -40,8 +40,10 @@
 from voltha.extensions.omci.onu_configuration import OMCCVersion
 from voltha.extensions.omci.onu_device_entry import OnuDeviceEvents, \
             OnuDeviceEntry, IN_SYNC_KEY
+from voltha.extensions.omci.tasks.omci_modify_request import OmciModifyRequest
 from voltha.extensions.omci.omci_me import *
 from voltha.adapters.brcm_openomci_onu.omci.brcm_mib_download_task import BrcmMibDownloadTask
+from voltha.adapters.brcm_openomci_onu.omci.brcm_uni_lock_task import BrcmUniLockTask
 from voltha.adapters.brcm_openomci_onu.onu_gem_port import *
 from voltha.adapters.brcm_openomci_onu.onu_tcont import *
 from voltha.adapters.brcm_openomci_onu.pon_port import *
@@ -177,6 +179,7 @@
             device.root = True
             device.vendor = 'Broadcom'
             device.connect_status = ConnectStatus.REACHABLE
+            device.oper_status = OperStatus.DISCOVERED
             self.adapter_agent.update_device(device)
 
             self._pon = PonPort.create(self, self._pon_port_number)
@@ -187,8 +190,6 @@
             parent_device = self.adapter_agent.get_device(device.parent_id)
             self.logical_device_id = parent_device.parent_id
 
-            device = self.adapter_agent.get_device(device.id)
-            device.oper_status = OperStatus.DISCOVERED
             self.adapter_agent.update_device(device)
 
             self.log.debug('set-device-discovered')
@@ -235,11 +236,20 @@
         except Exception as e:
             self.log.exception("exception-updating-port",e=e)
 
+    @inlineCallbacks
     def delete(self, device):
-        self.log.debug('function-entry', device=device)
-        self.log.info('delete-onu - Not implemented yet')
-        # The device is already deleted in delete_v_ont_ani(). No more
-        # handling needed here
+        self.log.info('delete-onu', device=device)
+
+        parent_device = self.adapter_agent.get_device(device.parent_id)
+        if parent_device.type == 'openolt':
+            parent_adapter = registry('adapter_loader').get_agent(parent_device.adapter).adapter
+            self.log.debug('parent-adapter-delete-onu', onu_device=device,
+                          parent_device=parent_device,
+                          parent_adapter=parent_adapter)
+            try:
+                parent_adapter.delete_child_device(parent_device.id, device)
+            except AttributeError:
+                self.log.debug('parent-device-delete-child-not-implemented')
 
     @inlineCallbacks
     def update_flow_table(self, device, flows):
@@ -486,25 +496,22 @@
         self.log.debug('function-entry', data=data)
         self._onu_indication = data
 
-        onu_device = self.adapter_agent.get_device(self.device_id)
-
-        if onu_device.oper_status == OperStatus.DISCOVERED and data.oper_state == 'up':
-            if self._dev_info_loaded == True:
-                self.log.debug('reenabling-openomci-statemachine')
-                reactor.callLater(0, self.reenable(onu_device))
-            else:
-                self.log.debug('starting-openomci-statemachine')
-                self._subscribe_to_events()
-                reactor.callLater(1, self._onu_omci_device.start)
+        self.log.debug('starting-openomci-statemachine')
+        self._subscribe_to_events()
+        reactor.callLater(1, self._onu_omci_device.start)
 
     def update_interface(self, data):
         self.log.debug('function-entry', data=data)
 
         onu_device = self.adapter_agent.get_device(self.device_id)
 
-        if onu_device.oper_status == OperStatus.DISCOVERED and data.oper_state == 'down':
+        if data.oper_state == 'down':
             self.log.debug('stopping-openomci-statemachine')
-            reactor.callLater(0, self.disable(onu_device))
+            reactor.callLater(0, self._onu_omci_device.stop)
+            self.disable_ports(onu_device)
+            onu_device.connect_status = ConnectStatus.UNREACHABLE
+            onu_device.oper_status = OperStatus.DISCOVERED
+            self.adapter_agent.update_device(onu_device)
         else:
             self.log.debug('not-changing-openomci-statemachine')
 
@@ -515,6 +522,7 @@
 
         self.log.debug('stopping-openomci-statemachine')
         reactor.callLater(0, self._onu_omci_device.stop)
+        self.disable_ports(onu_device)
 
         # TODO: im sure there is more to do here
 
@@ -608,29 +616,35 @@
     def disable(self, device):
         self.log.debug('function-entry', device=device)
         try:
-            self.log.info('sending-admin-state-lock-towards-device', device=device)
+            self.log.info('sending-uni-lock-towards-device', device=device)
 
-            #TODO: Create uni lock/unlock omci task
+            def stop_anyway(reason):
+                # proceed with disable regardless if we could reach the onu. for example onu is unplugged
+                self.log.debug('stopping-openomci-statemachine')
+                reactor.callLater(0, self._onu_omci_device.stop)
+                self.disable_ports(device)
+                device.oper_status = OperStatus.UNKNOWN
+                device.connect_status = ConnectStatus.UNREACHABLE
+                self.adapter_agent.update_device(device)
 
-            # Stop up OpenOMCI state machines for this device
-            reactor.callLater(0, self._onu_omci_device.stop)
-
-            device = self.adapter_agent.get_device(device.id)
-            # Disable all ports on that device
-            self.adapter_agent.disable_all_ports(self.device_id)
+            # lock all the unis
+            task = BrcmUniLockTask(self.omci_agent, self.device_id, lock=True)
+            self._deferred = self._onu_omci_device.task_runner.queue_task(task)
+            self._deferred.addCallbacks(stop_anyway, stop_anyway)
+            '''
+            # Disable in parent device (OLT)
             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)
-                # TODO: move to UniPort
-                self.update_logical_port(logical_device_id, port_id, state)
-            device.oper_status = OperStatus.UNKNOWN
-            device.connect_status = ConnectStatus.UNREACHABLE
-            self.adapter_agent.update_device(device)
+
+            if parent_device.type == 'openolt':
+                parent_adapter = registry('adapter_loader').get_agent(parent_device.adapter).adapter
+                self.log.info('parent-adapter-disable-onu', onu_device=device,
+                              parent_device=parent_device,
+                              parent_adapter=parent_adapter)
+                try:
+                    parent_adapter.disable_child_device(parent_device.id, device)
+                except AttributeError:
+                    self.log.debug('parent-device-disable-child-not-implemented')
+            '''
         except Exception as e:
             log.exception('exception-in-onu-disable', exception=e)
 
@@ -638,23 +652,11 @@
     def reenable(self, device):
         self.log.debug('function-entry', device=device)
         try:
-            self.log.info('sending-admin-state-unlock-towards-device', device=device)
-
             # Start up OpenOMCI state machines for this device
+            # this will ultimately resync mib and unlock unis on successful redownloading the mib
+            self.log.debug('restarting-openomci-statemachine')
             self._subscribe_to_events()
             reactor.callLater(1, self._onu_omci_device.start)
-
-            #TODO: Create uni lock/unlock omci task
-
-            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
-            device.oper_status = OperStatus.ACTIVE
-            device.connect_status = ConnectStatus.REACHABLE
-            self.adapter_agent.update_device(device)
         except Exception as e:
             log.exception('exception-in-onu-reenable', exception=e)
 
@@ -666,24 +668,18 @@
             self.log.error("device-unreacable")
             returnValue(None)
 
-        #TODO: Create reboot omci task
+        def success(_results):
+            self.log.info('reboot-success', _results=_results)
+            self.disable_ports(device)
+            device.connect_status = ConnectStatus.UNREACHABLE
+            device.oper_status = OperStatus.DISCOVERED
+            self.adapter_agent.update_device(device)
 
-        response = None
-        if response is not None:
-            omci_response = response.getfieldval("omci_message")
-            success_code = omci_response.getfieldval("success_code")
-            if success_code == 0:
-                self.log.debug("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.error("reboot-failed", success_code=success_code)
-        else:
-            self.log.error("error-in-processing-reboot-response")
+        def failure(_reason):
+            self.log.info('reboot-failure', _reason=_reason)
+
+        self._deferred = self._onu_omci_device.reboot()
+        self._deferred.addCallbacks(success, failure)
 
     def disable_ports(self, onu_device):
         self.log.info('disable-ports', device_id=self.device_id,
@@ -771,14 +767,13 @@
         omci = self._onu_omci_device
         in_sync = omci.mib_db_in_sync
 
+        device = self.adapter_agent.get_device(self.device_id)
+        device.reason = 'discovery-mibsync-complete'
+        self.adapter_agent.update_device(device)
+
         if not self._dev_info_loaded:
             self.log.info('loading-device-data-from-mib', in_sync=in_sync, already_loaded=self._dev_info_loaded)
 
-            device = self.adapter_agent.get_device(self.device_id)
-            device.oper_status = OperStatus.ACTIVE
-            device.connect_status = ConnectStatus.REACHABLE
-            device.reason = 'discovery-mibsync-complete'
-
             omci_dev = self._onu_omci_device
             config = omci_dev.configuration
 
@@ -866,6 +861,12 @@
 
         def success(_results):
             self.log.info('mib-download-success', _results=_results)
+            device = self.adapter_agent.get_device(self.device_id)
+            device.reason = 'initial-mib-downloaded'
+            device.oper_status = OperStatus.ACTIVE
+            device.connect_status = ConnectStatus.REACHABLE
+            self.enable_ports(device)
+            self.adapter_agent.update_device(device)
             self._mib_download_task = None
 
         def failure(_reason):
diff --git a/voltha/adapters/brcm_openomci_onu/omci/brcm_mib_download_task.py b/voltha/adapters/brcm_openomci_onu/omci/brcm_mib_download_task.py
index 52f9142..2c9c961 100644
--- a/voltha/adapters/brcm_openomci_onu/omci/brcm_mib_download_task.py
+++ b/voltha/adapters/brcm_openomci_onu/omci/brcm_mib_download_task.py
@@ -15,7 +15,6 @@
 
 import structlog
 from common.frameio.frameio import hexify
-from voltha.protos.openflow_13_pb2 import OFPPS_LIVE, OFPPS_LINK_DOWN
 from twisted.internet import reactor
 from twisted.internet.defer import inlineCallbacks, returnValue, TimeoutError, failure
 from voltha.extensions.omci.omci_me import *
@@ -203,14 +202,6 @@
                 # And re-enable the UNIs if needed
                 yield self.enable_uni(self._uni_port, False)
 
-                # If here, we are done.  Set the openflow port live
-                # TODO: move to UniPort
-                self._handler.update_logical_port(self._handler.logical_device_id,
-                                                  self._uni_port.port_id_name(), OFPPS_LIVE)
-                device = self._handler.adapter_agent.get_device(self.device_id)
-
-                device.reason = 'initial-mib-downloaded'
-                self._handler.adapter_agent.update_device(device)
                 self.deferred.callback('initial-download-success')
 
             except TimeoutError as e:
diff --git a/voltha/adapters/brcm_openomci_onu/omci/brcm_uni_lock_task.py b/voltha/adapters/brcm_openomci_onu/omci/brcm_uni_lock_task.py
new file mode 100644
index 0000000..6866cba
--- /dev/null
+++ b/voltha/adapters/brcm_openomci_onu/omci/brcm_uni_lock_task.py
@@ -0,0 +1,112 @@
+#
+# Copyright 2018 the original author or authors.
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#      http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+from voltha.extensions.omci.tasks.task import Task
+from twisted.internet import reactor
+from twisted.internet.defer import inlineCallbacks, failure, returnValue
+from voltha.extensions.omci.omci_defs import ReasonCodes, EntityOperations
+from voltha.extensions.omci.omci_me import PptpEthernetUniFrame
+
+RC = ReasonCodes
+OP = EntityOperations
+
+
+class BrcmUniLockException(Exception):
+    pass
+
+
+class BrcmUniLockTask(Task):
+    """
+    Lock or unlock all discovered UNI/PPTP on the ONU
+    """
+    task_priority = 200
+    name = "Broadcom UNI Lock Task"
+
+    def __init__(self, omci_agent, device_id, lock=True, priority=task_priority):
+        """
+        Class initialization
+
+        :param omci_agent: (OmciAdapterAgent) OMCI Adapter agent
+        :param device_id: (str) ONU Device ID
+        :param lock: (bool) If true administratively lock all the UNI.  If false unlock
+        :param priority: (int) OpenOMCI Task priority (0..255) 255 is the highest
+        """
+        super(BrcmUniLockTask, self).__init__(BrcmUniLockTask.name,
+                                                omci_agent,
+                                                device_id,
+                                                priority=priority,
+                                                exclusive=False)
+        self._device = omci_agent.get_device(device_id)
+        self._lock = lock
+        self._results = None
+        self._local_deferred = None
+        self._config = self._device.configuration
+
+    def cancel_deferred(self):
+        super(BrcmUniLockTask, self).cancel_deferred()
+
+        d, self._local_deferred = self._local_deferred, None
+        try:
+            if d is not None and not d.called:
+                d.cancel()
+        except:
+            pass
+
+    def stop_if_not_running(self):
+        if not self.running:
+            raise BrcmUniLockException('UNI Lock Task was cancelled')
+
+    def start(self):
+        """
+        Start UNI/PPTP Lock/Unlock Task
+        """
+        super(BrcmUniLockTask, self).start()
+        self._local_deferred = reactor.callLater(0, self.perform_lock)
+
+    @inlineCallbacks
+    def perform_lock(self):
+        """
+        Perform the lock/unlock
+        """
+        self.log.info('setting-uni-lock-state', lock=self._lock)
+
+        try:
+            state = 1 if self._lock else 0
+
+            pptp = self._config.pptp_entities
+
+            for key, value in pptp.iteritems():
+                msg = PptpEthernetUniFrame(key,
+                                       attributes=dict(administrative_state=state))
+                frame = msg.set()
+                self.log.debug('openomci-msg', msg=msg)
+                results = yield self._device.omci_cc.send(frame)
+                self.stop_if_not_running()
+
+                status = results.fields['omci_message'].fields['success_code']
+                self.log.info('response-status', status=status)
+
+                # Success?
+                if status in (RC.Success.value, RC.InstanceExists):
+                    self.log.debug('set-lock-uni', uni=key, value=value, lock=self._lock)
+                else:
+                    self.log.warn('cannot-set-lock-uni', uni=key, value=value, lock=self._lock)
+
+            self.deferred.callback(self)
+
+        except Exception as e:
+            self.log.exception('setting-uni-lock-state', e=e)
+            self.deferred.errback(failure.Failure(e))
+
diff --git a/voltha/extensions/omci/onu_device_entry.py b/voltha/extensions/omci/onu_device_entry.py
index 40f2678..d3fd68b 100644
--- a/voltha/extensions/omci/onu_device_entry.py
+++ b/voltha/extensions/omci/onu_device_entry.py
@@ -470,7 +470,7 @@
 
     def reboot(self,
                flags=RebootFlags.Reboot_Unconditionally,
-               timeout=OmciRebootRequest.DEFAULT_PRIORITY):
+               timeout=OmciRebootRequest.DEFAULT_REBOOT_TIMEOUT):
         """
         Request a reboot of the ONU
 
diff --git a/voltha/extensions/omci/tasks/reboot_task.py b/voltha/extensions/omci/tasks/reboot_task.py
index ba4c290..8cbf808 100644
--- a/voltha/extensions/omci/tasks/reboot_task.py
+++ b/voltha/extensions/omci/tasks/reboot_task.py
@@ -45,10 +45,12 @@
     """
     task_priority = Task.MAX_PRIORITY
     name = "ONU OMCI Reboot Task"
+    # adopt the global default
+    DEFAULT_REBOOT_TIMEOUT = DEFAULT_OMCI_TIMEOUT
 
     def __init__(self, omci_agent, device_id,
                  flags=RebootFlags.Reboot_Unconditionally,
-                 timeout=DEFAULT_OMCI_TIMEOUT):
+                 timeout=DEFAULT_REBOOT_TIMEOUT):
         """
         Class initialization