[VOL-1036] Initial implementation of device lifecycle management

Change-Id: I5aa58fdcbcd852f6f5eef35d48f25f76e20c0418
diff --git a/rw_core/core/device_manager.go b/rw_core/core/device_manager.go
index fd18c10..e3dbed2 100644
--- a/rw_core/core/device_manager.go
+++ b/rw_core/core/device_manager.go
@@ -18,6 +18,7 @@
 import (
 	"context"
 	"errors"
+	"github.com/gogo/protobuf/proto"
 	"github.com/opencord/voltha-go/common/log"
 	"github.com/opencord/voltha-go/db/model"
 	"github.com/opencord/voltha-go/kafka"
@@ -95,18 +96,18 @@
 }
 
 func (dMgr *DeviceManager) createDevice(ctx context.Context, device *voltha.Device, ch chan interface{}) {
-	log.Debugw("createDevice-start", log.Fields{"device": device, "aproxy": dMgr.adapterProxy})
+	log.Debugw("createDevice", log.Fields{"device": device, "aproxy": dMgr.adapterProxy})
 
 	// Create and start a device agent for that device
 	agent := newDeviceAgent(dMgr.adapterProxy, device, dMgr, dMgr.clusterDataProxy)
 	dMgr.addDeviceAgentToMap(agent)
 	agent.start(ctx)
 
-	sendResponse(ctx, ch, nil)
+	sendResponse(ctx, ch, agent.lastData)
 }
 
 func (dMgr *DeviceManager) enableDevice(ctx context.Context, id *voltha.ID, ch chan interface{}) {
-	log.Debugw("enableDevice-start", log.Fields{"deviceid": id})
+	log.Debugw("enableDevice", log.Fields{"deviceid": id})
 
 	var res interface{}
 	if agent := dMgr.getDeviceAgent(id.Id); agent != nil {
@@ -119,33 +120,44 @@
 	sendResponse(ctx, ch, res)
 }
 
-func (dMgr *DeviceManager) getDevice(id string) (*voltha.Device, error) {
-	log.Debugw("getDevice-start", log.Fields{"deviceid": id})
+func (dMgr *DeviceManager) disableDevice(ctx context.Context, id *voltha.ID, ch chan interface{}) {
+	log.Debugw("disableDevice", log.Fields{"deviceid": id})
 
-	if device := dMgr.clusterDataProxy.Get("/devices/"+id, 1, false, ""); device == nil {
-		return nil, status.Errorf(codes.NotFound, "%s", id)
+	var res interface{}
+	if agent := dMgr.getDeviceAgent(id.Id); agent != nil {
+		res = agent.disableDevice(ctx)
+		log.Debugw("disableDevice-result", log.Fields{"result": res})
 	} else {
-		cloned := reflect.ValueOf(device).Elem().Interface().(voltha.Device)
-		return &cloned, nil
+		res = status.Errorf(codes.NotFound, "%s", id.Id)
 	}
+
+	sendResponse(ctx, ch, res)
+}
+
+func (dMgr *DeviceManager) getDevice(id string) (*voltha.Device, error) {
+	log.Debugw("getDevice", log.Fields{"deviceid": id})
+	if agent := dMgr.getDeviceAgent(id); agent != nil {
+		return agent.getDevice()
+	}
+	return nil, status.Errorf(codes.NotFound, "%s", id)
 }
 
 func (dMgr *DeviceManager) ListDevices() (*voltha.Devices, error) {
-	log.Debug("ListDevices-start")
+	log.Debug("ListDevices")
 	result := &voltha.Devices{}
 	dMgr.lockDeviceAgentsMap.Lock()
 	defer dMgr.lockDeviceAgentsMap.Unlock()
 	for _, agent := range dMgr.deviceAgents {
-		if device := dMgr.clusterDataProxy.Get("/devices/"+agent.deviceId, 1, false, ""); device != nil {
-			cloned := reflect.ValueOf(device).Elem().Interface().(voltha.Device)
-			result.Items = append(result.Items, &cloned)
+		if device, err := agent.getDevice(); err == nil {
+			cloned := proto.Clone(device).(*voltha.Device)
+			result.Items = append(result.Items, cloned)
 		}
 	}
 	return result, nil
 }
 
 func (dMgr *DeviceManager) updateDevice(device *voltha.Device) error {
-	log.Debugw("updateDevice-start", log.Fields{"deviceid": device.Id, "device": device})
+	log.Debugw("updateDevice", log.Fields{"deviceid": device.Id, "device": device})
 
 	if agent := dMgr.getDeviceAgent(device.Id); agent != nil {
 		return agent.updateDevice(device)
@@ -155,9 +167,23 @@
 
 func (dMgr *DeviceManager) addPort(deviceId string, port *voltha.Port) error {
 	if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
-		return agent.addPort(port)
+		if err := agent.addPort(port); err != nil {
+			return err
+		}
+		//	Setup peer ports
+		meAsPeer := &voltha.Port_PeerPort{DeviceId: deviceId, PortNo: port.PortNo}
+		for _, peerPort := range port.Peers {
+			if agent := dMgr.getDeviceAgent(peerPort.DeviceId); agent != nil {
+				if err := agent.addPeerPort(meAsPeer); err != nil {
+					log.Errorw("failed-to-add-peer", log.Fields{"peer-device-id": peerPort.DeviceId})
+					return err
+				}
+			}
+		}
+		return nil
+	} else {
+		return status.Errorf(codes.NotFound, "%s", deviceId)
 	}
-	return status.Errorf(codes.NotFound, "%s", deviceId)
 }
 
 func (dMgr *DeviceManager) updatePmConfigs(deviceId string, pmConfigs *voltha.PmConfigs) error {
@@ -168,7 +194,7 @@
 }
 
 func (dMgr *DeviceManager) getSwitchCapability(ctx context.Context, deviceId string) (*core_adapter.SwitchCapability, error) {
-	log.Debugw("getSwitchCapability-start", log.Fields{"deviceid": deviceId})
+	log.Debugw("getSwitchCapability", log.Fields{"deviceid": deviceId})
 
 	if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
 		return agent.getSwitchCapability(ctx)
@@ -176,17 +202,18 @@
 	return nil, status.Errorf(codes.NotFound, "%s", deviceId)
 }
 
-func (dMgr *DeviceManager) getNNIPorts(ctx context.Context, deviceId string) (*voltha.Ports, error) {
-	log.Debugw("getNNIPorts-start", log.Fields{"deviceid": deviceId})
+func (dMgr *DeviceManager) getPorts(ctx context.Context, deviceId string, portType voltha.Port_PortType) (*voltha.Ports, error) {
+	log.Debugw("getPorts", log.Fields{"deviceid": deviceId, "portType": portType})
 
 	if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
-		return agent.getNNIPorts(ctx), nil
+		return agent.getPorts(ctx, portType), nil
 	}
 	return nil, status.Errorf(codes.NotFound, "%s", deviceId)
+
 }
 
 func (dMgr *DeviceManager) getPortCapability(ctx context.Context, deviceId string, portNo uint32) (*core_adapter.PortCapability, error) {
-	log.Debugw("getPortCapability-start", log.Fields{"deviceid": deviceId})
+	log.Debugw("getPortCapability", log.Fields{"deviceid": deviceId})
 
 	if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
 		return agent.getPortCapability(ctx, portNo)
@@ -194,20 +221,27 @@
 	return nil, status.Errorf(codes.NotFound, "%s", deviceId)
 }
 
-func (dMgr *DeviceManager) updateDeviceState(deviceId string, operState *core_adapter.IntType, connState *core_adapter.IntType) error {
-	log.Debugw("updateDeviceState-start", log.Fields{"deviceid": deviceId, "operState": operState, "connState": connState})
+func (dMgr *DeviceManager) updateDeviceStatus(deviceId string, operStatus voltha.OperStatus_OperStatus, connStatus voltha.ConnectStatus_ConnectStatus) error {
+	log.Debugw("updateDeviceStatus", log.Fields{"deviceid": deviceId, "operStatus": operStatus, "connStatus": connStatus})
 	if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
-		return agent.updateDeviceState(operState, connState)
+		return agent.updateDeviceStatus(operStatus, connStatus)
+	}
+	return status.Errorf(codes.NotFound, "%s", deviceId)
+}
+
+func (dMgr *DeviceManager) updatePortState(deviceId string, portType voltha.Port_PortType, portNo uint32, operStatus voltha.OperStatus_OperStatus) error {
+	log.Debugw("updatePortState", log.Fields{"deviceid": deviceId, "portType": portType, "portNo": portNo, "operStatus": operStatus})
+	if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
+		return agent.updatePortState(portType, portNo, operStatus)
 	}
 	return status.Errorf(codes.NotFound, "%s", deviceId)
 }
 
 func (dMgr *DeviceManager) childDeviceDetected(parentDeviceId string, parentPortNo int64, deviceType string, channelId int64) error {
-	log.Debugw("childDeviceDetected-start", log.Fields{"parentDeviceId": parentDeviceId})
+	log.Debugw("childDeviceDetected", log.Fields{"parentDeviceId": parentDeviceId})
 
 	// Create the ONU device
 	childDevice := &voltha.Device{}
-	childDevice.Id = CreateDeviceId()
 	childDevice.Type = deviceType
 	childDevice.ParentId = parentDeviceId
 	childDevice.ParentPortNo = uint32(parentPortNo)
@@ -220,7 +254,7 @@
 	agent.start(nil)
 
 	// Activate the child device
-	if agent := dMgr.getDeviceAgent(childDevice.Id); agent != nil {
+	if agent := dMgr.getDeviceAgent(agent.deviceId); agent != nil {
 		return agent.enableDevice(nil)
 	}
 
@@ -229,16 +263,25 @@
 
 func (dMgr *DeviceManager) processTransition(previous *voltha.Device, current *voltha.Device) error {
 	// This will be triggered on every update to the device.
-	handler := dMgr.stateTransitions.GetTransitionHandler(previous, current)
-	if handler != nil {
-		log.Debugw("found-handler", log.Fields{"handler": funcName(handler)})
-		return handler(previous, current)
+	handlers := dMgr.stateTransitions.GetTransitionHandler(previous, current)
+	if handlers == nil {
+		log.Debugw("handlers-not-found", log.Fields{"deviceId": current.Id})
+		return nil
 	}
-	log.Debugw("handler-not-found", log.Fields{"deviceId": current.Id})
+	for _, handler := range handlers {
+		log.Debugw("running-handler", log.Fields{"handler": funcName(handler)})
+		if err := handler(current); err != nil {
+			return err
+		}
+	}
+	//if handler != nil {
+	//	log.Debugw("found-handlers", log.Fields{"handlers": funcName(handler)})
+	//	return handler(current)
+	//}
 	return nil
 }
 
-func (dMgr *DeviceManager) createLogicalDevice(pDevice *voltha.Device, cDevice *voltha.Device) error {
+func (dMgr *DeviceManager) createLogicalDevice(cDevice *voltha.Device) error {
 	log.Info("createLogicalDevice")
 	var logicalId *string
 	var err error
@@ -251,7 +294,80 @@
 	return nil
 }
 
-func (dMgr *DeviceManager) addUNILogicalPort(pDevice *voltha.Device, cDevice *voltha.Device) error {
+func (dMgr *DeviceManager) deleteLogicalDevice(cDevice *voltha.Device) error {
+	log.Info("deleteLogicalDevice")
+	var err error
+	if err = dMgr.logicalDeviceMgr.DeleteLogicalDevice(nil, cDevice); err != nil {
+		log.Warnw("deleteLogical-device-error", log.Fields{"deviceId": cDevice.Id})
+		return err
+	}
+	// Remove the logical device Id from the parent device
+	logicalId := ""
+	dMgr.UpdateDeviceAttribute(cDevice.Id, "ParentId", logicalId)
+	return nil
+}
+
+func (dMgr *DeviceManager) deleteLogicalPort(cDevice *voltha.Device) error {
+	log.Info("deleteLogicalPort")
+	var err error
+	if err = dMgr.logicalDeviceMgr.DeleteLogicalPort(nil, cDevice); err != nil {
+		log.Warnw("deleteLogical-port-error", log.Fields{"deviceId": cDevice.Id})
+		return err
+	}
+	//// Remove the logical device Id from the parent device
+	//logicalId := ""
+	//dMgr.UpdateDeviceAttribute(cDevice.Id, "ParentId", logicalId)
+	return nil
+}
+
+func (dMgr *DeviceManager) getParentDevice(childDevice *voltha.Device) *voltha.Device {
+	//	Sanity check
+	if childDevice.Root {
+		// childDevice is the parent device
+		return childDevice
+	}
+	parentDevice, _ := dMgr.getDevice(childDevice.ParentId)
+	return parentDevice
+}
+
+func (dMgr *DeviceManager) disableAllChildDevices(cDevice *voltha.Device) error {
+	log.Debug("disableAllChildDevices")
+	var childDeviceIds []string
+	var err error
+	if childDeviceIds, err = dMgr.getAllChildDeviceIds(cDevice); err != nil {
+		return status.Errorf(codes.NotFound, "%s", cDevice.Id)
+	}
+	if len(childDeviceIds) == 0 {
+		log.Debugw("no-child-device", log.Fields{"deviceId": cDevice.Id})
+	}
+	for _, childDeviceId := range childDeviceIds {
+		if agent := dMgr.getDeviceAgent(childDeviceId); agent != nil {
+			if err = agent.disableDevice(nil); err != nil {
+				log.Errorw("failure-disable-device", log.Fields{"deviceId": childDeviceId, "error": err.Error()})
+			}
+		}
+	}
+	return nil
+}
+
+func (dMgr *DeviceManager) getAllChildDeviceIds(cDevice *voltha.Device) ([]string, error) {
+	log.Info("getAllChildDeviceIds")
+	// Get latest device info
+	var device *voltha.Device
+	var err error
+	if device, err = dMgr.getDevice(cDevice.Id); err != nil {
+		return nil, status.Errorf(codes.NotFound, "%s", cDevice.Id)
+	}
+	childDeviceIds := make([]string, 0)
+	for _, port := range device.Ports {
+		for _, peer := range port.Peers {
+			childDeviceIds = append(childDeviceIds, peer.DeviceId)
+		}
+	}
+	return childDeviceIds, nil
+}
+
+func (dMgr *DeviceManager) addUNILogicalPort(cDevice *voltha.Device) error {
 	log.Info("addUNILogicalPort")
 	if err := dMgr.logicalDeviceMgr.AddUNILogicalPort(nil, cDevice); err != nil {
 		log.Warnw("addUNILogicalPort-error", log.Fields{"device": cDevice, "err": err})
@@ -260,32 +376,32 @@
 	return nil
 }
 
-func (dMgr *DeviceManager) activateDevice(pDevice *voltha.Device, cDevice *voltha.Device) error {
+func (dMgr *DeviceManager) activateDevice(cDevice *voltha.Device) error {
 	log.Info("activateDevice")
 	return nil
 }
 
-func (dMgr *DeviceManager) disableDevice(pDevice *voltha.Device, cDevice *voltha.Device) error {
-	log.Info("disableDevice")
+func (dMgr *DeviceManager) disableDeviceHandler(cDevice *voltha.Device) error {
+	log.Info("disableDevice-donothing")
 	return nil
 }
 
-func (dMgr *DeviceManager) abandonDevice(pDevice *voltha.Device, cDevice *voltha.Device) error {
+func (dMgr *DeviceManager) abandonDevice(cDevice *voltha.Device) error {
 	log.Info("abandonDevice")
 	return nil
 }
 
-func (dMgr *DeviceManager) reEnableDevice(pDevice *voltha.Device, cDevice *voltha.Device) error {
+func (dMgr *DeviceManager) reEnableDevice(cDevice *voltha.Device) error {
 	log.Info("reEnableDevice")
 	return nil
 }
 
-func (dMgr *DeviceManager) noOp(pDevice *voltha.Device, cDevice *voltha.Device) error {
+func (dMgr *DeviceManager) noOp(cDevice *voltha.Device) error {
 	log.Info("noOp")
 	return nil
 }
 
-func (dMgr *DeviceManager) notAllowed(pDevice *voltha.Device, cDevice *voltha.Device) error {
+func (dMgr *DeviceManager) notAllowed(pcDevice *voltha.Device) error {
 	log.Info("notAllowed")
 	return errors.New("Transition-not-allowed")
 }
@@ -304,7 +420,7 @@
 
 func (dMgr *DeviceManager) GetParentDeviceId(deviceId string) *string {
 	if device, _ := dMgr.getDevice(deviceId); device != nil {
-		log.Infow("GetParentDeviceId", log.Fields{"device": device})
+		log.Infow("GetParentDeviceId", log.Fields{"deviceId": device.Id, "parentId": device.ParentId})
 		return &device.ParentId
 	}
 	return nil