[VOL-1036] Device management implementation.  This update includes
the the ability to reboot and delete a device.  It contains changes
to both the Go Core and the Twisted ponsim adapters.

Change-Id: I15539827c654d7186cdae3300a107ffc8e921756
diff --git a/rw_core/core/device_agent.go b/rw_core/core/device_agent.go
index d9dacbc..52ab584 100644
--- a/rw_core/core/device_agent.go
+++ b/rw_core/core/device_agent.go
@@ -40,6 +40,8 @@
 	lockDevice       sync.RWMutex
 }
 
+//newDeviceAgent creates a new device agent along as creating a unique ID for the device and set the device state to
+//preprovisioning
 func newDeviceAgent(ap *AdapterProxy, device *voltha.Device, deviceMgr *DeviceManager, cdProxy *model.Proxy) *DeviceAgent {
 	var agent DeviceAgent
 	agent.adapterProxy = ap
@@ -55,6 +57,7 @@
 	return &agent
 }
 
+// start save the device to the data model and registers for callbacks on that device
 func (agent *DeviceAgent) start(ctx context.Context) {
 	agent.lockDevice.Lock()
 	defer agent.lockDevice.Unlock()
@@ -64,12 +67,12 @@
 		log.Errorw("failed-to-add-device", log.Fields{"deviceId": agent.deviceId})
 	}
 	agent.deviceProxy = agent.clusterDataProxy.Root.GetProxy("/devices/"+agent.deviceId, false)
-	//agent.deviceProxy = agent.clusterDataProxy.Root.Node.GetProxy("/", false)
 	agent.deviceProxy.RegisterCallback(model.POST_UPDATE, agent.processUpdate, nil)
 	log.Debug("device-agent-started")
 }
 
-func (agent *DeviceAgent) Stop(ctx context.Context) {
+// stop stops the device agent.  Not much to do for now
+func (agent *DeviceAgent) stop(ctx context.Context) {
 	agent.lockDevice.Lock()
 	defer agent.lockDevice.Unlock()
 	log.Debug("stopping-device-agent")
@@ -77,6 +80,7 @@
 	log.Debug("device-agent-stopped")
 }
 
+// getDevice retrieves the latest device information from the data model
 func (agent *DeviceAgent) getDevice() (*voltha.Device, error) {
 	agent.lockDevice.Lock()
 	defer agent.lockDevice.Unlock()
@@ -89,7 +93,7 @@
 	return nil, status.Errorf(codes.NotFound, "device-%s", agent.deviceId)
 }
 
-//getDeviceWithoutLock is a helper function to be used ONLY by any device agent function AFTER it has acquired the device lock.
+// getDeviceWithoutLock is a helper function to be used ONLY by any device agent function AFTER it has acquired the device lock.
 // This function is meant so that we do not have duplicate code all over the device agent functions
 func (agent *DeviceAgent) getDeviceWithoutLock() (*voltha.Device, error) {
 	if device := agent.clusterDataProxy.Get("/devices/"+agent.deviceId, 1, false, ""); device != nil {
@@ -101,6 +105,7 @@
 	return nil, status.Errorf(codes.NotFound, "device-%s", agent.deviceId)
 }
 
+// enableDevice activates a preprovisioned or disable device
 func (agent *DeviceAgent) enableDevice(ctx context.Context) error {
 	agent.lockDevice.Lock()
 	defer agent.lockDevice.Unlock()
@@ -113,6 +118,7 @@
 			//TODO:  Needs customized error message
 			return nil
 		}
+		//TODO: if parent device is disabled then do not enable device
 		// Verify whether we need to adopt the device the first time
 		// TODO: A state machine for these state transitions would be better (we just have to handle
 		// a limited set of states now or it may be an overkill)
@@ -140,6 +146,7 @@
 	return nil
 }
 
+//disableDevice disable a device
 func (agent *DeviceAgent) disableDevice(ctx context.Context) error {
 	agent.lockDevice.Lock()
 	//defer agent.lockDevice.Unlock()
@@ -183,6 +190,70 @@
 	return nil
 }
 
+func (agent *DeviceAgent) rebootDevice(ctx context.Context) error {
+	agent.lockDevice.Lock()
+	defer agent.lockDevice.Unlock()
+	log.Debugw("rebootDevice", log.Fields{"id": agent.deviceId})
+	// Get the most up to date the device info
+	if device, err := agent.getDeviceWithoutLock(); err != nil {
+		return status.Errorf(codes.NotFound, "%s", agent.deviceId)
+	} else {
+		if device.AdminState != voltha.AdminState_DISABLED {
+			log.Debugw("device-not-disabled", log.Fields{"id": agent.deviceId})
+			//TODO:  Needs customized error message
+			return status.Errorf(codes.FailedPrecondition, "deviceId:%s, expected-admin-state:%s", agent.deviceId, voltha.AdminState_DISABLED)
+		}
+		// First send the request to an Adapter and wait for a response
+		if err := agent.adapterProxy.RebootDevice(ctx, device); err != nil {
+			log.Debugw("rebootDevice-error", log.Fields{"id": agent.lastData.Id, "error": err})
+			return err
+		}
+	}
+	return nil
+}
+
+func (agent *DeviceAgent) deleteDevice(ctx context.Context) error {
+	agent.lockDevice.Lock()
+	log.Debugw("deleteDevice", log.Fields{"id": agent.deviceId})
+	// Get the most up to date the device info
+	if device, err := agent.getDeviceWithoutLock(); err != nil {
+		agent.lockDevice.Unlock()
+		return status.Errorf(codes.NotFound, "%s", agent.deviceId)
+	} else {
+		if device.AdminState != voltha.AdminState_DISABLED {
+			log.Debugw("device-not-disabled", log.Fields{"id": agent.deviceId})
+			//TODO:  Needs customized error message
+			agent.lockDevice.Unlock()
+			return status.Errorf(codes.FailedPrecondition, "deviceId:%s, expected-admin-state:%s", agent.deviceId, voltha.AdminState_DISABLED)
+		}
+		// Send the request to an Adapter and wait for a response
+		if err := agent.adapterProxy.DeleteDevice(ctx, device); err != nil {
+			log.Debugw("deleteDevice-error", log.Fields{"id": agent.lastData.Id, "error": err})
+			agent.lockDevice.Unlock()
+			return err
+		}
+		//	Set the device Admin state to DELETED in order to trigger the callback to delete
+		// child devices, if any
+		// Received an Ack (no error found above).  Now update the device in the model to the expected state
+		cloned := proto.Clone(device).(*voltha.Device)
+		cloned.AdminState = voltha.AdminState_DELETED
+		if afterUpdate := agent.clusterDataProxy.Update("/devices/"+agent.deviceId, cloned, false, ""); afterUpdate == nil {
+			agent.lockDevice.Unlock()
+			return status.Errorf(codes.Internal, "failed-update-device:%s", agent.deviceId)
+		}
+		agent.lockDevice.Unlock()
+		//TODO: callback will be invoked to handle this state change
+		//For now force the state transition to happen
+		if err := agent.deviceMgr.processTransition(device, cloned); err != nil {
+			log.Warnw("process-transition-error", log.Fields{"deviceid": device.Id, "error": err})
+			return err
+		}
+
+	}
+	return nil
+}
+
+// getPorts retrieves the ports information of the device based on the port type.
 func (agent *DeviceAgent) getPorts(ctx context.Context, portType voltha.Port_PortType) *voltha.Ports {
 	log.Debugw("getPorts", log.Fields{"id": agent.deviceId, "portType": portType})
 	ports := &voltha.Ports{}
@@ -196,6 +267,8 @@
 	return ports
 }
 
+// getSwitchCapability is a helper method that a logical device agent uses to retrieve the switch capability of a
+// parent device
 func (agent *DeviceAgent) getSwitchCapability(ctx context.Context) (*core_adapter.SwitchCapability, error) {
 	log.Debugw("getSwitchCapability", log.Fields{"deviceId": agent.deviceId})
 	if device, err := agent.deviceMgr.getDevice(agent.deviceId); device == nil {
@@ -211,6 +284,8 @@
 	}
 }
 
+// getPortCapability is a helper method that a logical device agent uses to retrieve the port capability of a
+// device
 func (agent *DeviceAgent) getPortCapability(ctx context.Context, portNo uint32) (*core_adapter.PortCapability, error) {
 	log.Debugw("getPortCapability", log.Fields{"deviceId": agent.deviceId})
 	if device, err := agent.deviceMgr.getDevice(agent.deviceId); device == nil {
@@ -226,6 +301,8 @@
 	}
 }
 
+// TODO: implement when callback from the data model is ready
+// processUpdate is a callback invoked whenever there is a change on the device manages by this device agent
 func (agent *DeviceAgent) processUpdate(args ...interface{}) interface{} {
 	log.Debug("!!!!!!!!!!!!!!!!!!!!!!!!!")
 	log.Debugw("processUpdate", log.Fields{"deviceId": agent.deviceId, "args": args})
@@ -405,11 +482,20 @@
 				break
 			}
 		}
+		//To track an issue when adding peer-port.
+		log.Debugw("before-peer-added", log.Fields{"device": cloned})
 		// Store the device
 		afterUpdate := agent.clusterDataProxy.Update("/devices/"+agent.deviceId, cloned, false, "")
 		if afterUpdate == nil {
 			return status.Errorf(codes.Internal, "%s", agent.deviceId)
 		}
+		//To track an issue when adding peer-port.
+		if d, ok := afterUpdate.(*voltha.Device); ok {
+			log.Debugw("after-peer-added", log.Fields{"device": d})
+		} else {
+			log.Debug("after-peer-added-incorrect-type", log.Fields{"type": reflect.ValueOf(afterUpdate).Type()})
+		}
+
 		return nil
 	}
 }