[VOL-2404] : RW-Core changes for handling OLT Reboot Scenario

- When the OLT's connection status goes from REACHABLE to UNREACHABLE
  in ENABLED/DISABLED admin state, delete all the logical ports,
  child devices, logical device and device flows.

- When OLT's connection status becomes reachable again, child devices,
  ports will be re-discovered again. The logical device will be recreated
  again.

- Will not handle the case where OLT goes UNREACHABLE when OLT is disabled
  as part of voltha2.3 release

Change-Id: I34c0c538b44afa19e889e9631f0a738060a58fef
diff --git a/rw_core/core/grpc_nbi_api_handler_test.go b/rw_core/core/grpc_nbi_api_handler_test.go
index 662302c..47a46e3 100755
--- a/rw_core/core/grpc_nbi_api_handler_test.go
+++ b/rw_core/core/grpc_nbi_api_handler_test.go
@@ -710,6 +710,104 @@
 
 }
 
+func (nb *NBTest) testDeviceRebootWhenOltIsEnabled(t *testing.T, nbi *APIHandler) {
+	//Get an OLT device
+	oltDevice, err := nb.getADevice(true, nbi)
+	assert.Nil(t, err)
+	assert.NotNil(t, oltDevice)
+	assert.Equal(t, oltDevice.ConnectStatus, voltha.ConnectStatus_REACHABLE)
+	assert.Equal(t, oltDevice.AdminState, voltha.AdminState_ENABLED)
+
+	// Verify that we have one or more ONUs to start with
+	onuDevices, err := nb.core.deviceMgr.getAllChildDevices(getContext(), oltDevice.Id)
+	assert.Nil(t, err)
+	assert.NotNil(t, onuDevices)
+	assert.Greater(t, len(onuDevices.Items), 0)
+
+	// Reboot the OLT and very that Connection Status goes to UNREACHABLE and operation status to UNKNOWN
+	_, err = nbi.RebootDevice(getContext(), &voltha.ID{Id: oltDevice.Id})
+	assert.Nil(t, err)
+
+	var vlFunction0 = func(d *voltha.Device) bool {
+		return d.ConnectStatus == voltha.ConnectStatus_UNREACHABLE && d.OperStatus == voltha.OperStatus_UNKNOWN
+	}
+
+	err = waitUntilDeviceReadiness(oltDevice.Id, nb.maxTimeout, vlFunction0, nbi)
+	assert.Nil(t, err)
+
+	// Wait for the logical device to satisfy the expected condition
+	var vlFunction1 = func(ld *voltha.LogicalDevice) bool {
+		return ld == nil
+	}
+
+	err = waitUntilLogicalDeviceReadiness(oltDevice.Id, nb.maxTimeout, nbi, vlFunction1)
+	assert.Nil(t, err)
+
+	// Wait for the device to satisfy the expected condition (device does not have flows)
+	var vlFunction2 = func(d *voltha.Device) bool {
+		var deviceFlows *ofp.Flows
+		var err error
+		if deviceFlows, err = nbi.ListDeviceFlows(getContext(), &voltha.ID{Id: d.Id}); err != nil {
+			return false
+		}
+		return len(deviceFlows.Items) == 0
+	}
+
+	err = waitUntilDeviceReadiness(oltDevice.Id, nb.maxTimeout, vlFunction2, nbi)
+	assert.Nil(t, err)
+
+	// Wait for the device to satisfy the expected condition (there are no child devices)
+	var vlFunction3 = func(d *voltha.Device) bool {
+		var devices *voltha.Devices
+		var err error
+		if devices, err = nbi.ListDevices(getContext(), nil); err != nil {
+			return false
+		}
+		for _, device := range devices.Items {
+			if device.ParentId == d.Id {
+				// We have a child device still left
+				return false
+			}
+		}
+		return true
+	}
+
+	err = waitUntilDeviceReadiness(oltDevice.Id, nb.maxTimeout, vlFunction3, nbi)
+	assert.Nil(t, err)
+
+	// Update the OLT Connection Status to REACHABLE and operation status to ACTIVE
+	// Normally, in a real adapter this happens after connection regain via a heartbeat mechanism with real hardware
+	deviceAgent := nbi.deviceMgr.getDeviceAgent(getContext(), oltDevice.Id)
+	err = deviceAgent.updateDeviceStatus(getContext(), voltha.OperStatus_ACTIVE, voltha.ConnectStatus_REACHABLE)
+	assert.Nil(t, err)
+
+	// Verify the device connection and operation states
+	oltDevice, err = nb.getADevice(true, nbi)
+	assert.Nil(t, err)
+	assert.NotNil(t, oltDevice)
+	assert.Equal(t, oltDevice.ConnectStatus, voltha.ConnectStatus_REACHABLE)
+	assert.Equal(t, oltDevice.AdminState, voltha.AdminState_ENABLED)
+
+	// Wait for the logical device to satisfy the expected condition
+	var vlFunction4 = func(ld *voltha.LogicalDevice) bool {
+		return ld != nil
+	}
+	err = waitUntilLogicalDeviceReadiness(oltDevice.Id, nb.maxTimeout, nbi, vlFunction4)
+	assert.Nil(t, err)
+
+	// Verify that logical device is created again
+	logicalDevices, err := nbi.ListLogicalDevices(getContext(), &empty.Empty{})
+	assert.Nil(t, err)
+	assert.NotNil(t, logicalDevices)
+	assert.Equal(t, 1, len(logicalDevices.Items))
+
+	// Verify that we have no ONUs left
+	onuDevices, err = nb.core.deviceMgr.getAllChildDevices(getContext(), oltDevice.Id)
+	assert.Nil(t, err)
+	assert.NotNil(t, onuDevices)
+	assert.Equal(t, 0, len(onuDevices.Items))
+}
+
 func makeSimpleFlowMod(fa *flows.FlowArgs) *ofp.OfpFlowMod {
 	matchFields := make([]*ofp.OfpOxmField, 0)
 	for _, val := range fa.MatchFields {
@@ -883,20 +981,25 @@
 	nb.sendTrapFlows(t, nbi, logicalDevice, uint64(meterID), startingVlan)
 
 	// Listen for port events
-	processedLogicalPorts := 0
 	start := time.Now()
+	processedNniLogicalPorts := 0
+	processedUniLogicalPorts := 0
+
 	for event := range nbi.changeEventQueue {
 		startingVlan++
 		if portStatus, ok := (event.Event).(*ofp.ChangeEvent_PortStatus); ok {
 			ps := portStatus.PortStatus
 			if ps.Reason == ofp.OfpPortReason_OFPPR_ADD {
-				processedLogicalPorts++
 				if ps.Desc.PortNo >= uint32(nb.startingUNIPortNo) {
+					processedUniLogicalPorts++
 					nb.sendEAPFlows(t, nbi, logicalDevice.Id, ps.Desc, startingVlan, uint64(meterID))
+				} else {
+					processedNniLogicalPorts++
 				}
 			}
 		}
-		if processedLogicalPorts >= numNNIPorts+numUNIPorts {
+
+		if processedNniLogicalPorts >= numNNIPorts && processedUniLogicalPorts >= numUNIPorts {
 			fmt.Println("Total time to send all flows:", time.Since(start))
 			break
 		}
@@ -968,10 +1071,13 @@
 		// 6. Test disable and Enable pon port of OLT device
 		nb.testDisableAndEnablePort(t, nbi)
 
-		// 6. Test disable and delete all devices
+		// 7.Test Device unreachable when OLT is enabled
+		nb.testDeviceRebootWhenOltIsEnabled(t, nbi)
+
+		// 8. Test disable and delete all devices
 		nb.testDisableAndDeleteAllDevice(t, nbi)
 
-		//7. Test enable and delete all devices
+		// 9. Test enable and delete all devices
 		nb.testEnableAndDeleteAllDevice(t, nbi)
 	}