[VOL-1614] Device Management update in the Core

This commit went over the device management of devices in the Core
and made the following changes:
1) Update the device state machine to not remove logical
device or ports when a device is disabled.
2) Fix some issues around device deletion
3) Add additional APIs between the Core and Adapters to handle
the scenarios of enable/disable/delete a device
4) Update the simulated Adapters to handle disable/reenable/delete
5) Add a new set of tests for teh device state machine.

Change-Id: Ib2be87ec011762d5315a6d54581a87c1891e92be
diff --git a/rw_core/core/adapter_request_handler.go b/rw_core/core/adapter_request_handler.go
index 9eae89e..0de7953 100644
--- a/rw_core/core/adapter_request_handler.go
+++ b/rw_core/core/adapter_request_handler.go
@@ -795,6 +795,138 @@
 	return new(empty.Empty), nil
 }
 
+func (rhp *AdapterRequestHandlerProxy) DeleteAllPorts(args []*ic.Argument) (*empty.Empty, error) {
+	if len(args) < 3 {
+		log.Warn("invalid-number-of-args", log.Fields{"args": args})
+		err := errors.New("invalid-number-of-args")
+		return nil, err
+	}
+	deviceId := &voltha.ID{}
+	transactionID := &ic.StrType{}
+	for _, arg := range args {
+		switch arg.Key {
+		case "device_id":
+			if err := ptypes.UnmarshalAny(arg.Value, deviceId); err != nil {
+				log.Warnw("cannot-unmarshal-device-id", log.Fields{"error": err})
+				return nil, err
+			}
+		case kafka.TransactionKey:
+			if err := ptypes.UnmarshalAny(arg.Value, transactionID); err != nil {
+				log.Warnw("cannot-unmarshal-transaction-ID", log.Fields{"error": err})
+				return nil, err
+			}
+		}
+	}
+	log.Debugw("DeleteAllPorts", log.Fields{"deviceId": deviceId.Id, "transactionID": transactionID.Val})
+
+	// Try to grab the transaction as this core may be competing with another Core
+	if rhp.competeForTransaction() {
+		if txn, err := rhp.takeRequestOwnership(transactionID.Val, deviceId.Id); err != nil {
+			log.Debugw("Another core handled the request", log.Fields{"transactionID": transactionID})
+			// returning nil, nil instructs the callee to ignore this request
+			return nil, nil
+		} else {
+			defer txn.Close()
+		}
+	}
+
+	if rhp.TestMode { // Execute only for test cases
+		return nil, nil
+	}
+
+	go rhp.deviceMgr.deleteAllPorts(deviceId.Id)
+
+	return new(empty.Empty), nil
+}
+
+func (rhp *AdapterRequestHandlerProxy) ChildDevicesLost(args []*ic.Argument) (*empty.Empty, error) {
+	if len(args) < 2 {
+		log.Warn("invalid-number-of-args", log.Fields{"args": args})
+		err := errors.New("invalid-number-of-args")
+		return nil, err
+	}
+	parentDeviceId := &voltha.ID{}
+	transactionID := &ic.StrType{}
+	for _, arg := range args {
+		switch arg.Key {
+		case "parent_device_id":
+			if err := ptypes.UnmarshalAny(arg.Value, parentDeviceId); err != nil {
+				log.Warnw("cannot-unmarshal-device-id", log.Fields{"error": err})
+				return nil, err
+			}
+		case kafka.TransactionKey:
+			if err := ptypes.UnmarshalAny(arg.Value, transactionID); err != nil {
+				log.Warnw("cannot-unmarshal-transaction-ID", log.Fields{"error": err})
+				return nil, err
+			}
+		}
+	}
+	log.Debugw("ChildDevicesLost", log.Fields{"deviceId": parentDeviceId.Id, "transactionID": transactionID.Val})
+
+	// Try to grab the transaction as this core may be competing with another Core
+	if rhp.competeForTransaction() {
+		if txn, err := rhp.takeRequestOwnership(transactionID.Val, parentDeviceId.Id); err != nil {
+			log.Debugw("Another core handled the request", log.Fields{"transactionID": transactionID})
+			// returning nil, nil instructs the callee to ignore this request
+			return nil, nil
+		} else {
+			defer txn.Close()
+		}
+	}
+
+	if rhp.TestMode { // Execute only for test cases
+		return nil, nil
+	}
+
+	go rhp.deviceMgr.childDevicesLost(parentDeviceId.Id)
+
+	return new(empty.Empty), nil
+}
+
+func (rhp *AdapterRequestHandlerProxy) ChildDevicesDetected(args []*ic.Argument) (*empty.Empty, error) {
+	if len(args) < 2 {
+		log.Warn("invalid-number-of-args", log.Fields{"args": args})
+		err := errors.New("invalid-number-of-args")
+		return nil, err
+	}
+	parentDeviceId := &voltha.ID{}
+	transactionID := &ic.StrType{}
+	for _, arg := range args {
+		switch arg.Key {
+		case "parent_device_id":
+			if err := ptypes.UnmarshalAny(arg.Value, parentDeviceId); err != nil {
+				log.Warnw("cannot-unmarshal-device-id", log.Fields{"error": err})
+				return nil, err
+			}
+		case kafka.TransactionKey:
+			if err := ptypes.UnmarshalAny(arg.Value, transactionID); err != nil {
+				log.Warnw("cannot-unmarshal-transaction-ID", log.Fields{"error": err})
+				return nil, err
+			}
+		}
+	}
+	log.Debugw("ChildDevicesDetected", log.Fields{"deviceId": parentDeviceId.Id, "transactionID": transactionID.Val})
+
+	// Try to grab the transaction as this core may be competing with another Core
+	if rhp.competeForTransaction() {
+		if txn, err := rhp.takeRequestOwnership(transactionID.Val, parentDeviceId.Id); err != nil {
+			log.Debugw("Another core handled the request", log.Fields{"transactionID": transactionID})
+			// returning nil, nil instructs the callee to ignore this request
+			return nil, nil
+		} else {
+			defer txn.Close()
+		}
+	}
+
+	if rhp.TestMode { // Execute only for test cases
+		return nil, nil
+	}
+
+	go rhp.deviceMgr.childDevicesDetected(parentDeviceId.Id)
+
+	return new(empty.Empty), nil
+}
+
 func (rhp *AdapterRequestHandlerProxy) PortCreated(args []*ic.Argument) (*empty.Empty, error) {
 	if len(args) < 3 {
 		log.Warn("invalid-number-of-args", log.Fields{"args": args})
diff --git a/rw_core/core/device_agent.go b/rw_core/core/device_agent.go
index 9782fee..d45fca6 100755
--- a/rw_core/core/device_agent.go
+++ b/rw_core/core/device_agent.go
@@ -112,8 +112,13 @@
 	agent.lockDevice.Lock()
 	defer agent.lockDevice.Unlock()
 	log.Debug("stopping-device-agent")
+	//	Remove the device from the KV store
+	if removed := agent.clusterDataProxy.Remove("/devices/"+agent.deviceId, ""); removed == nil {
+		log.Errorw("failed-removing-device", log.Fields{"id": agent.deviceId})
+	}
 	agent.exitChannel <- 1
 	log.Debug("device-agent-stopped")
+
 }
 
 // GetDevice retrieves the latest device information from the data model
@@ -186,16 +191,19 @@
 				log.Debugw("adoptDevice-error", log.Fields{"id": agent.lastData.Id, "error": err})
 				return err
 			}
-		} else {
+		} else if device.AdminState == voltha.AdminState_DISABLED {
 			// First send the request to an Adapter and wait for a response
 			if err := agent.adapterProxy.ReEnableDevice(ctx, device); err != nil {
 				log.Debugw("renableDevice-error", log.Fields{"id": agent.lastData.Id, "error": err})
 				return err
 			}
+		} else {
+			err = status.Error(codes.FailedPrecondition, fmt.Sprintf("cannot-delete-a-deleted-device: %s ", device.Id))
+			log.Warnw("invalid-state", log.Fields{"id": agent.deviceId, "state": device.AdminState, "error": err})
+			return err
 		}
 		// 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_ENABLED
 		cloned.OperStatus = voltha.OperStatus_ACTIVATING
 		if afterUpdate := agent.clusterDataProxy.Update("/devices/"+agent.deviceId, cloned, false, ""); afterUpdate == nil {
 			return status.Errorf(codes.Internal, "failed-update-device:%s", agent.deviceId)
@@ -323,32 +331,48 @@
 
 //disableDevice disable a device
 func (agent *DeviceAgent) disableDevice(ctx context.Context) error {
-	agent.lockDevice.Lock()
+	agent.lockDevice.RLock()
 	log.Debugw("disableDevice", log.Fields{"id": agent.deviceId})
 	// Get the most up to date the device info
 	if device, err := agent.getDeviceWithoutLock(); err != nil {
-		agent.lockDevice.Unlock()
+		agent.lockDevice.RUnlock()
 		return status.Errorf(codes.NotFound, "%s", agent.deviceId)
 	} else {
+		agent.lockDevice.RUnlock()
 		if device.AdminState == voltha.AdminState_DISABLED {
 			log.Debugw("device-already-disabled", log.Fields{"id": agent.deviceId})
-			agent.lockDevice.Unlock()
 			return nil
 		}
 		// First send the request to an Adapter and wait for a response
 		if err := agent.adapterProxy.DisableDevice(ctx, device); err != nil {
 			log.Debugw("disableDevice-error", log.Fields{"id": agent.lastData.Id, "error": err})
-			agent.lockDevice.Unlock()
 			return err
 		}
+		if err = agent.updateAdminState(voltha.AdminState_DISABLED); err != nil {
+			log.Errorw("failed-update-device", log.Fields{"deviceId": device.Id, "currentState": device.AdminState, "expectedState": voltha.AdminState_DISABLED})
+		}
+	}
+	return nil
+}
+
+func (agent *DeviceAgent) updateAdminState(adminState voltha.AdminState_AdminState) error {
+	agent.lockDevice.Lock()
+	defer agent.lockDevice.Unlock()
+	log.Debugw("updateAdminState", 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 == adminState {
+			log.Debugw("no-change-needed", log.Fields{"id": agent.deviceId, "state": adminState})
+			return nil
+		}
 		// 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_DISABLED
+		cloned.AdminState = adminState
 		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()
 	}
 	return nil
 }
@@ -377,32 +401,41 @@
 
 func (agent *DeviceAgent) deleteDevice(ctx context.Context) error {
 	agent.lockDevice.Lock()
+	defer agent.lockDevice.Unlock()
 	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_DELETED {
+			log.Debugw("device-already-in-deleted-state", log.Fields{"id": agent.deviceId})
+			return nil
+		}
 		if (device.AdminState != voltha.AdminState_DISABLED) &&
 			(device.AdminState != voltha.AdminState_PREPROVISIONED) {
 			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)
 		}
-		if device.AdminState != voltha.AdminState_PREPROVISIONED {
-			// 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
-			}
+		// 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})
+			return err
 		}
-		if removed := agent.clusterDataProxy.Remove("/devices/"+agent.deviceId, ""); removed == nil {
-			agent.lockDevice.Unlock()
+
+		//	Set the state to deleted - this will trigger some background process to clean up the device as well
+		// as its association with the logical device
+		cloned := proto.Clone(device).(*voltha.Device)
+		cloned.AdminState = voltha.AdminState_DELETED
+		if afterUpdate := agent.clusterDataProxy.Update("/devices/"+agent.deviceId, cloned, false, ""); afterUpdate == nil {
 			return status.Errorf(codes.Internal, "failed-update-device:%s", agent.deviceId)
 		}
-		agent.lockDevice.Unlock()
+
+		//	If this is a child device then remove the associated peer ports on the parent device
+		if !device.Root {
+			go agent.deviceMgr.deletePeerPorts(device.ParentId, device.Id)
+		}
+
 	}
 	return nil
 }
@@ -753,9 +786,9 @@
 
 func (agent *DeviceAgent) updateDeviceStatus(operStatus voltha.OperStatus_OperStatus, connStatus voltha.ConnectStatus_ConnectStatus) error {
 	agent.lockDevice.Lock()
+	defer agent.lockDevice.Unlock()
 	// Work only on latest data
 	if storeDevice, err := agent.getDeviceWithoutLock(); err != nil {
-		agent.lockDevice.Unlock()
 		return status.Errorf(codes.NotFound, "%s", agent.deviceId)
 	} else {
 		// clone the device
@@ -772,10 +805,8 @@
 		log.Debugw("updateDeviceStatus", log.Fields{"deviceId": cloned.Id, "operStatus": cloned.OperStatus, "connectStatus": cloned.ConnectStatus})
 		// Store the device
 		if afterUpdate := agent.clusterDataProxy.Update("/devices/"+agent.deviceId, cloned, false, ""); afterUpdate == nil {
-			agent.lockDevice.Unlock()
 			return status.Errorf(codes.Internal, "%s", agent.deviceId)
 		}
-		agent.lockDevice.Unlock()
 		return nil
 	}
 }
@@ -801,6 +832,7 @@
 }
 
 func (agent *DeviceAgent) disablePorts() error {
+	log.Debugw("disablePorts", log.Fields{"deviceid": agent.deviceId})
 	agent.lockDevice.Lock()
 	defer agent.lockDevice.Unlock()
 	if storeDevice, err := agent.getDeviceWithoutLock(); err != nil {
@@ -857,6 +889,35 @@
 	}
 }
 
+func (agent *DeviceAgent) deleteAllPorts() error {
+	log.Debugw("deleteAllPorts", log.Fields{"deviceId": agent.deviceId})
+	agent.lockDevice.Lock()
+	defer agent.lockDevice.Unlock()
+	// Work only on latest data
+	if storeDevice, err := agent.getDeviceWithoutLock(); err != nil {
+		return status.Errorf(codes.NotFound, "%s", agent.deviceId)
+	} else {
+		if storeDevice.AdminState != voltha.AdminState_DISABLED && storeDevice.AdminState != voltha.AdminState_DELETED {
+			err = status.Error(codes.FailedPrecondition, fmt.Sprintf("invalid-state-%v", storeDevice.AdminState))
+			log.Warnw("invalid-state-removing-ports", log.Fields{"state": storeDevice.AdminState, "error": err})
+			return err
+		}
+		if len(storeDevice.Ports) == 0 {
+			log.Debugw("no-ports-present", log.Fields{"deviceId": agent.deviceId})
+			return nil
+		}
+		// clone the device & set the fields to empty
+		cloned := proto.Clone(storeDevice).(*voltha.Device)
+		cloned.Ports = []*voltha.Port{}
+		log.Debugw("portStatusUpdate", log.Fields{"deviceId": cloned.Id})
+		// Store the device
+		if afterUpdate := agent.clusterDataProxy.Update("/devices/"+agent.deviceId, cloned, false, ""); afterUpdate == nil {
+			return status.Errorf(codes.Internal, "%s", agent.deviceId)
+		}
+		return nil
+	}
+}
+
 func (agent *DeviceAgent) updatePmConfigs(pmConfigs *voltha.PmConfigs) error {
 	agent.lockDevice.Lock()
 	defer agent.lockDevice.Unlock()
@@ -880,7 +941,7 @@
 func (agent *DeviceAgent) addPort(port *voltha.Port) error {
 	agent.lockDevice.Lock()
 	defer agent.lockDevice.Unlock()
-	log.Debugw("addLogicalPortToMap", log.Fields{"deviceId": agent.deviceId})
+	log.Debugw("addPort", log.Fields{"deviceId": agent.deviceId})
 	// Work only on latest data
 	if storeDevice, err := agent.getDeviceWithoutLock(); err != nil {
 		return status.Errorf(codes.NotFound, "%s", agent.deviceId)
@@ -889,7 +950,7 @@
 		cloned := proto.Clone(storeDevice).(*voltha.Device)
 		if cloned.Ports == nil {
 			//	First port
-			log.Debugw("addLogicalPortToMap-first-port-to-add", log.Fields{"deviceId": agent.deviceId})
+			log.Debugw("addPort-first-port-to-add", log.Fields{"deviceId": agent.deviceId})
 			cloned.Ports = make([]*voltha.Port, 0)
 		} else {
 			for _, p := range cloned.Ports {
@@ -898,7 +959,6 @@
 					return nil
 				}
 			}
-
 		}
 		cp := proto.Clone(port).(*voltha.Port)
 		// Set the admin state of the port to ENABLE if the operational state is ACTIVE
@@ -944,6 +1004,36 @@
 	}
 }
 
+func (agent *DeviceAgent) deletePeerPorts(deviceId string) error {
+	agent.lockDevice.Lock()
+	defer agent.lockDevice.Unlock()
+	log.Debug("deletePeerPorts")
+	// Work only on latest data
+	if storeDevice, err := agent.getDeviceWithoutLock(); err != nil {
+		return status.Errorf(codes.NotFound, "%s", agent.deviceId)
+	} else {
+		// clone the device
+		cloned := proto.Clone(storeDevice).(*voltha.Device)
+		var updatedPeers []*voltha.Port_PeerPort
+		for _, port := range cloned.Ports {
+			updatedPeers = make([]*voltha.Port_PeerPort, 0)
+			for _, peerPort := range port.Peers {
+				if peerPort.DeviceId != deviceId {
+					updatedPeers = append(updatedPeers, peerPort)
+				}
+			}
+			port.Peers = updatedPeers
+		}
+
+		// Store the device with updated peer ports
+		afterUpdate := agent.clusterDataProxy.Update("/devices/"+agent.deviceId, cloned, false, "")
+		if afterUpdate == nil {
+			return status.Errorf(codes.Internal, "%s", agent.deviceId)
+		}
+		return nil
+	}
+}
+
 // TODO: A generic device update by attribute
 func (agent *DeviceAgent) updateDeviceAttribute(name string, value interface{}) {
 	agent.lockDevice.Lock()
diff --git a/rw_core/core/device_manager.go b/rw_core/core/device_manager.go
index d173308..eff6fe8 100755
--- a/rw_core/core/device_manager.go
+++ b/rw_core/core/device_manager.go
@@ -199,11 +199,6 @@
 	var res interface{}
 	if agent := dMgr.getDeviceAgent(id.Id); agent != nil {
 		res = agent.deleteDevice(ctx)
-		if res == nil { //Success
-			agent.stop(ctx)
-			dMgr.deleteDeviceAgentToMap(agent)
-			dMgr.core.deviceOwnership.AbandonDevice(id.Id)
-		}
 		log.Debugw("deleteDevice-result", log.Fields{"result": res})
 	} else {
 		res = status.Errorf(codes.NotFound, "%s", id.Id)
@@ -211,6 +206,20 @@
 	sendResponse(ctx, ch, res)
 }
 
+func (dMgr *DeviceManager) RunPostDeviceDelete(cDevice *voltha.Device) error {
+	log.Infow("RunPostDeviceDelete", log.Fields{"deviceId": cDevice.Id})
+	if agent := dMgr.getDeviceAgent(cDevice.Id); agent != nil {
+		agent.stop(nil)
+		dMgr.deleteDeviceAgentToMap(agent)
+		if err := dMgr.core.deviceOwnership.AbandonDevice(cDevice.Id); err != nil {
+			log.Errorw("failed-abandoning-device-ownership", log.Fields{"deviceId": cDevice.Id, "error": err})
+			return err
+		}
+		return nil
+	}
+	return status.Errorf(codes.NotFound, "%s", cDevice.Id)
+}
+
 // GetDevice will returns a device, either from memory or from the dB, if present
 func (dMgr *DeviceManager) GetDevice(id string) (*voltha.Device, error) {
 	log.Debugw("GetDevice", log.Fields{"deviceid": id})
@@ -526,6 +535,14 @@
 	}
 }
 
+func (dMgr *DeviceManager) deletePeerPorts(fromDeviceId string, deviceId string) error {
+	log.Debugw("deletePeerPorts", log.Fields{"fromDeviceId": fromDeviceId, "deviceid": deviceId})
+	if agent := dMgr.getDeviceAgent(fromDeviceId); agent != nil {
+		return agent.deletePeerPorts(deviceId)
+	}
+	return status.Errorf(codes.NotFound, "%s", deviceId)
+}
+
 func (dMgr *DeviceManager) addFlowsAndGroups(deviceId string, flows []*ofp.OfpFlowStats, groups []*ofp.OfpGroupEntry) error {
 	log.Debugw("addFlowsAndGroups", log.Fields{"deviceid": deviceId})
 	if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
@@ -608,6 +625,26 @@
 	return status.Errorf(codes.NotFound, "%s", deviceId)
 }
 
+func (dMgr *DeviceManager) deleteAllPorts(deviceId string) error {
+	log.Debugw("DeleteAllPorts", log.Fields{"deviceid": deviceId})
+	if agent := dMgr.getDeviceAgent(deviceId); agent != nil {
+		if err := agent.deleteAllPorts(); err != nil {
+			return err
+		}
+		// Notify the logical device manager to remove all logical ports, if needed.
+		// At this stage the device itself may gave been deleted already at a deleteAllPorts
+		// typically is part of a device deletion phase.
+		if device, err := dMgr.GetDevice(deviceId); err == nil {
+			go dMgr.logicalDeviceMgr.deleteAllLogicalPorts(device)
+		} else {
+			log.Warnw("failed-to-retrieve-device", log.Fields{"deviceId": deviceId})
+			return err
+		}
+		return nil
+	}
+	return status.Errorf(codes.NotFound, "%s", deviceId)
+}
+
 //updatePortsState updates all ports on the device
 func (dMgr *DeviceManager) updatePortsState(deviceId string, state voltha.OperStatus_OperStatus) error {
 	log.Debugw("updatePortsState", log.Fields{"deviceid": deviceId})
@@ -695,9 +732,11 @@
 		log.Debugw("no-op-transition", log.Fields{"deviceId": current.Id})
 		return nil
 	}
+	log.Debugw("handler-found", log.Fields{"num-handlers": len(handlers), "isParent": current.Root})
 	for _, handler := range handlers {
 		log.Debugw("running-handler", log.Fields{"handler": funcName(handler)})
 		if err := handler(current); err != nil {
+			log.Warnw("handler-falied", log.Fields{"handler": funcName(handler), "error": err})
 			return err
 		}
 	}
@@ -732,8 +771,8 @@
 	return nil
 }
 
-func (dMgr *DeviceManager) createLogicalDevice(cDevice *voltha.Device) error {
-	log.Info("createLogicalDevice")
+func (dMgr *DeviceManager) CreateLogicalDevice(cDevice *voltha.Device) error {
+	log.Info("CreateLogicalDevice")
 	var logicalId *string
 	var err error
 	if logicalId, err = dMgr.logicalDeviceMgr.createLogicalDevice(nil, cDevice); err != nil {
@@ -745,8 +784,8 @@
 	return nil
 }
 
-func (dMgr *DeviceManager) deleteLogicalDevice(cDevice *voltha.Device) error {
-	log.Info("deleteLogicalDevice")
+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})
@@ -758,7 +797,7 @@
 	return nil
 }
 
-func (dMgr *DeviceManager) deleteLogicalPort(device *voltha.Device) error {
+func (dMgr *DeviceManager) DeleteLogicalPort(device *voltha.Device) error {
 	log.Info("deleteLogicalPort")
 	var err error
 	// Get the logical port associated with this device
@@ -774,6 +813,15 @@
 	return nil
 }
 
+func (dMgr *DeviceManager) DeleteLogicalPorts(device *voltha.Device) error {
+	log.Info("deleteLogicalPorts")
+	if err := dMgr.logicalDeviceMgr.deleteLogicalPorts(device.Id); err != nil {
+		log.Warnw("deleteLogical-ports-error", log.Fields{"deviceId": device.Id})
+		return err
+	}
+	return nil
+}
+
 func (dMgr *DeviceManager) getParentDevice(childDevice *voltha.Device) *voltha.Device {
 	//	Sanity check
 	if childDevice.Root {
@@ -784,14 +832,61 @@
 	return parentDevice
 }
 
+//childDevicesLost is invoked by an adapter to indicate that a parent device is in a state (Disabled) where it
+//cannot manage the child devices.  This will trigger the Core to disable all the child devices.
+func (dMgr *DeviceManager) childDevicesLost(parentDeviceId string) error {
+	log.Debug("childDevicesLost")
+	var err error
+	var parentDevice *voltha.Device
+	if parentDevice, err = dMgr.GetDevice(parentDeviceId); err != nil {
+		log.Warnw("failed-getting-device", log.Fields{"deviceId": parentDeviceId, "error": err})
+		return err
+	}
+	return dMgr.DisableAllChildDevices(parentDevice)
+}
+
+//childDevicesDetected is invoked by an adapter when child devices are found, typically after after a
+// disable/enable sequence.  This will trigger the Core to Enable all the child devices of that parent.
+func (dMgr *DeviceManager) childDevicesDetected(parentDeviceId string) error {
+	log.Debug("childDevicesDetected")
+	var err error
+	var parentDevice *voltha.Device
+	var childDeviceIds []string
+
+	if parentDevice, err = dMgr.GetDevice(parentDeviceId); err != nil {
+		log.Warnw("failed-getting-device", log.Fields{"deviceId": parentDeviceId, "error": err})
+		return err
+	}
+
+	if childDeviceIds, err = dMgr.getAllChildDeviceIds(parentDevice); err != nil {
+		return status.Errorf(codes.NotFound, "%s", parentDevice.Id)
+	}
+	if len(childDeviceIds) == 0 {
+		log.Debugw("no-child-device", log.Fields{"parentDeviceId": parentDevice.Id})
+	}
+	allChildDisable := true
+	for _, childDeviceId := range childDeviceIds {
+		if agent := dMgr.getDeviceAgent(childDeviceId); agent != nil {
+			if err = agent.enableDevice(nil); err != nil {
+				log.Errorw("failure-enable-device", log.Fields{"deviceId": childDeviceId, "error": err.Error()})
+				allChildDisable = false
+			}
+		}
+	}
+	if !allChildDisable {
+		return err
+	}
+	return nil
+}
+
 /*
 All the functions below are callback functions where they are invoked with the latest and previous data.  We can
 therefore use the data as is without trying to get the latest from the model.
 */
 
-//disableAllChildDevices is invoked as a callback when the parent device is disabled
-func (dMgr *DeviceManager) disableAllChildDevices(parentDevice *voltha.Device) error {
-	log.Debug("disableAllChildDevices")
+//DisableAllChildDevices is invoked as a callback when the parent device is disabled
+func (dMgr *DeviceManager) DisableAllChildDevices(parentDevice *voltha.Device) error {
+	log.Debug("DisableAllChildDevices")
 	var childDeviceIds []string
 	var err error
 	if childDeviceIds, err = dMgr.getAllChildDeviceIds(parentDevice); err != nil {
@@ -815,9 +910,9 @@
 	return nil
 }
 
-//deleteAllChildDevices is invoked as a callback when the parent device is deleted
-func (dMgr *DeviceManager) deleteAllChildDevices(parentDevice *voltha.Device) error {
-	log.Debug("deleteAllChildDevices")
+//DeleteAllChildDevices is invoked as a callback when the parent device is deleted
+func (dMgr *DeviceManager) DeleteAllChildDevices(parentDevice *voltha.Device) error {
+	log.Debug("DeleteAllChildDevices")
 	var childDeviceIds []string
 	var err error
 	if childDeviceIds, err = dMgr.getAllChildDeviceIds(parentDevice); err != nil {
@@ -876,7 +971,7 @@
 	return nil, status.Errorf(codes.NotFound, "%s", parentDeviceId)
 }
 
-func (dMgr *DeviceManager) setupUNILogicalPorts(cDevice *voltha.Device) error {
+func (dMgr *DeviceManager) SetupUNILogicalPorts(cDevice *voltha.Device) error {
 	log.Info("addUNILogicalPort")
 	if err := dMgr.logicalDeviceMgr.setupUNILogicalPorts(nil, cDevice); err != nil {
 		log.Warnw("addUNILogicalPort-error", log.Fields{"device": cDevice, "err": err})
@@ -989,6 +1084,14 @@
 	return nil, status.Errorf(codes.NotFound, "%s", deviceId)
 }
 
+func (dMgr *DeviceManager) SetAdminStateToEnable(cDevice *voltha.Device) error {
+	log.Info("SetAdminStateToEnable")
+	if agent := dMgr.getDeviceAgent(cDevice.Id); agent != nil {
+		return agent.updateAdminState(voltha.AdminState_ENABLED)
+	}
+	return status.Errorf(codes.NotFound, "%s", cDevice.Id)
+}
+
 func (dMgr *DeviceManager) activateDevice(cDevice *voltha.Device) error {
 	log.Info("activateDevice")
 	return nil
@@ -1019,6 +1122,12 @@
 	return errors.New("transition-not-allowed")
 }
 
+func (dMgr *DeviceManager) NotifyInvalidTransition(pcDevice *voltha.Device) error {
+	log.Errorw("NotifyInvalidTransition", log.Fields{"device": pcDevice.Id, "adminState": pcDevice.AdminState})
+	//TODO: notify over kafka?
+	return nil
+}
+
 func funcName(f interface{}) string {
 	p := reflect.ValueOf(f).Pointer()
 	rf := runtime.FuncForPC(p)
diff --git a/rw_core/core/device_ownership.go b/rw_core/core/device_ownership.go
index 97de41c..5deb1cb 100644
--- a/rw_core/core/device_ownership.go
+++ b/rw_core/core/device_ownership.go
@@ -200,20 +200,40 @@
 func (da *DeviceOwnership) AbandonDevice(id string) error {
 	da.deviceMapLock.Lock()
 	defer da.deviceMapLock.Unlock()
-	if o, exist := da.deviceMap[id]; exist {
+	if o, exist := da.deviceMap[id]; exist { // id is ownership key
+		// Need to clean up all deviceToKeyMap entries using this device as key
+		da.deviceToKeyMapLock.Lock()
+		defer da.deviceToKeyMapLock.Unlock()
+		for k, v := range da.deviceToKeyMap {
+			if id == v {
+				delete(da.deviceToKeyMap, k)
+			}
+		}
+		// Remove the device reference from the devicMap
+		delete(da.deviceMap, id)
+
 		// Stop the Go routine monitoring the device
 		close(o.chnl)
 		delete(da.deviceMap, id)
 		log.Debugw("abandoning-device", log.Fields{"Id": id})
 		return nil
+	} else { // id is not ownership key
+		if err := da.deleteDeviceKey(id); err != nil {
+			log.Errorw("failed-deleting-key", log.Fields{"id": id})
+		}
 	}
-	return status.Error(codes.NotFound, fmt.Sprintf("id-inexistent-%s", id))
+	return nil
 }
 
 //abandonAllDevices must be invoked whenever a device is deleted from the Core
 func (da *DeviceOwnership) abandonAllDevices() {
 	da.deviceMapLock.Lock()
 	defer da.deviceMapLock.Unlock()
+	da.deviceToKeyMapLock.Lock()
+	defer da.deviceToKeyMapLock.Unlock()
+	for k, _ := range da.deviceToKeyMap {
+		delete(da.deviceToKeyMap, k)
+	}
 	for _, val := range da.deviceMap {
 		close(val.chnl)
 	}
@@ -238,6 +258,17 @@
 	return nil
 }
 
+func (da *DeviceOwnership) deleteDeviceKey(id string) error {
+	da.deviceToKeyMapLock.Lock()
+	defer da.deviceToKeyMapLock.Unlock()
+	if _, exist := da.deviceToKeyMap[id]; exist {
+		delete(da.deviceToKeyMap, id)
+		return nil
+	}
+	log.Warnw("device-not-owned", log.Fields{"deviceId": id})
+	return nil
+}
+
 func (da *DeviceOwnership) getOwnershipKey(id interface{}) (string, error) {
 	if id == nil {
 		return "", status.Error(codes.InvalidArgument, "nil-id")
diff --git a/rw_core/core/device_state_transitions.go b/rw_core/core/device_state_transitions.go
index 21f7531..5c52a8f 100644
--- a/rw_core/core/device_state_transitions.go
+++ b/rw_core/core/device_state_transitions.go
@@ -17,6 +17,7 @@
 
 import (
 	"github.com/opencord/voltha-go/common/log"
+	"github.com/opencord/voltha-go/rw_core/coreIf"
 	"github.com/opencord/voltha-protos/go/voltha"
 )
 
@@ -45,89 +46,139 @@
 
 type TransitionMap struct {
 	transitions []Transition
+	dMgr        coreIf.DeviceManager
 }
 
-func NewTransitionMap(dMgr *DeviceManager) *TransitionMap {
+func NewTransitionMap(dMgr coreIf.DeviceManager) *TransitionMap {
 	var transitionMap TransitionMap
+	transitionMap.dMgr = dMgr
 	transitionMap.transitions = make([]Transition, 0)
 	transitionMap.transitions = append(transitionMap.transitions,
 		Transition{
 			deviceType:    any,
 			previousState: DeviceState{Admin: voltha.AdminState_PREPROVISIONED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN},
 			currentState:  DeviceState{Admin: voltha.AdminState_UNKNOWN, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN},
-			handlers:      []TransitionHandler{dMgr.notAllowed}})
+			handlers:      []TransitionHandler{dMgr.NotifyInvalidTransition}})
 	transitionMap.transitions = append(transitionMap.transitions,
 		Transition{
 			deviceType:    any,
 			previousState: DeviceState{Admin: voltha.AdminState_PREPROVISIONED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN},
 			currentState:  DeviceState{Admin: voltha.AdminState_DOWNLOADING_IMAGE, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN},
-			handlers:      []TransitionHandler{dMgr.notAllowed}})
+			handlers:      []TransitionHandler{dMgr.NotifyInvalidTransition}})
 	transitionMap.transitions = append(transitionMap.transitions,
 		Transition{
 			deviceType:    any,
 			previousState: DeviceState{Admin: voltha.AdminState_ENABLED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN},
 			currentState:  DeviceState{Admin: voltha.AdminState_UNKNOWN, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN},
-			handlers:      []TransitionHandler{dMgr.notAllowed}})
+			handlers:      []TransitionHandler{dMgr.NotifyInvalidTransition}})
 	transitionMap.transitions = append(transitionMap.transitions,
 		Transition{
 			deviceType:    parent,
-			previousState: DeviceState{Admin: voltha.AdminState_ENABLED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_ACTIVATING},
-			currentState:  DeviceState{Admin: voltha.AdminState_ENABLED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_ACTIVE},
-			handlers:      []TransitionHandler{dMgr.createLogicalDevice}})
+			previousState: DeviceState{Admin: voltha.AdminState_PREPROVISIONED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_ACTIVATING},
+			currentState:  DeviceState{Admin: voltha.AdminState_PREPROVISIONED, Connection: voltha.ConnectStatus_REACHABLE, Operational: voltha.OperStatus_ACTIVE},
+			handlers:      []TransitionHandler{dMgr.SetAdminStateToEnable}})
+	transitionMap.transitions = append(transitionMap.transitions,
+		Transition{
+			deviceType:    parent,
+			previousState: DeviceState{Admin: voltha.AdminState_PREPROVISIONED, Connection: voltha.ConnectStatus_REACHABLE, Operational: voltha.OperStatus_ACTIVE},
+			currentState:  DeviceState{Admin: voltha.AdminState_ENABLED, Connection: voltha.ConnectStatus_REACHABLE, Operational: voltha.OperStatus_ACTIVE},
+			handlers:      []TransitionHandler{dMgr.CreateLogicalDevice}})
+	transitionMap.transitions = append(transitionMap.transitions,
+		Transition{
+			deviceType:    parent,
+			previousState: DeviceState{Admin: voltha.AdminState_PREPROVISIONED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_ACTIVE},
+			currentState:  DeviceState{Admin: voltha.AdminState_PREPROVISIONED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_ACTIVATING},
+			handlers:      []TransitionHandler{dMgr.NotifyInvalidTransition}})
+	transitionMap.transitions = append(transitionMap.transitions,
+		Transition{
+			deviceType:    any,
+			previousState: DeviceState{Admin: voltha.AdminState_PREPROVISIONED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_ACTIVATING},
+			currentState:  DeviceState{Admin: voltha.AdminState_PREPROVISIONED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN},
+			handlers:      []TransitionHandler{dMgr.NotifyInvalidTransition}})
 	transitionMap.transitions = append(transitionMap.transitions,
 		Transition{
 			deviceType:    child,
-			previousState: DeviceState{Admin: voltha.AdminState_ENABLED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_ACTIVATING},
-			currentState:  DeviceState{Admin: voltha.AdminState_ENABLED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_ACTIVE},
-			handlers:      []TransitionHandler{dMgr.setupUNILogicalPorts}})
+			previousState: DeviceState{Admin: voltha.AdminState_PREPROVISIONED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_ACTIVATING},
+			currentState:  DeviceState{Admin: voltha.AdminState_PREPROVISIONED, Connection: voltha.ConnectStatus_REACHABLE, Operational: voltha.OperStatus_ACTIVE},
+			handlers:      []TransitionHandler{dMgr.SetAdminStateToEnable}})
+	transitionMap.transitions = append(transitionMap.transitions,
+		Transition{
+			deviceType:    child,
+			previousState: DeviceState{Admin: voltha.AdminState_PREPROVISIONED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_ACTIVATING},
+			currentState:  DeviceState{Admin: voltha.AdminState_PREPROVISIONED, Connection: voltha.ConnectStatus_REACHABLE, Operational: voltha.OperStatus_DISCOVERED},
+			handlers:      []TransitionHandler{}})
+	transitionMap.transitions = append(transitionMap.transitions,
+		Transition{
+			deviceType:    child,
+			previousState: DeviceState{Admin: voltha.AdminState_PREPROVISIONED, Connection: voltha.ConnectStatus_REACHABLE, Operational: voltha.OperStatus_DISCOVERED},
+			currentState:  DeviceState{Admin: voltha.AdminState_PREPROVISIONED, Connection: voltha.ConnectStatus_REACHABLE, Operational: voltha.OperStatus_ACTIVATING},
+			handlers:      []TransitionHandler{}})
+	transitionMap.transitions = append(transitionMap.transitions,
+		Transition{
+			deviceType:    child,
+			previousState: DeviceState{Admin: voltha.AdminState_PREPROVISIONED, Connection: voltha.ConnectStatus_REACHABLE, Operational: voltha.OperStatus_ACTIVATING},
+			currentState:  DeviceState{Admin: voltha.AdminState_PREPROVISIONED, Connection: voltha.ConnectStatus_REACHABLE, Operational: voltha.OperStatus_ACTIVE},
+			handlers:      []TransitionHandler{dMgr.SetAdminStateToEnable}})
+	transitionMap.transitions = append(transitionMap.transitions,
+		Transition{
+			deviceType:    child,
+			previousState: DeviceState{Admin: voltha.AdminState_PREPROVISIONED, Connection: voltha.ConnectStatus_REACHABLE, Operational: voltha.OperStatus_ACTIVE},
+			currentState:  DeviceState{Admin: voltha.AdminState_ENABLED, Connection: voltha.ConnectStatus_REACHABLE, Operational: voltha.OperStatus_ACTIVE},
+			handlers:      []TransitionHandler{dMgr.SetupUNILogicalPorts}})
 	transitionMap.transitions = append(transitionMap.transitions,
 		Transition{
 			deviceType:    parent,
 			previousState: DeviceState{Admin: voltha.AdminState_ENABLED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN},
 			currentState:  DeviceState{Admin: voltha.AdminState_DISABLED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN},
-			handlers:      []TransitionHandler{dMgr.deleteLogicalDevice, dMgr.disableAllChildDevices}})
+			handlers:      []TransitionHandler{dMgr.DisableAllChildDevices}})
 	transitionMap.transitions = append(transitionMap.transitions,
 		Transition{
 			deviceType:    child,
 			previousState: DeviceState{Admin: voltha.AdminState_ENABLED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN},
 			currentState:  DeviceState{Admin: voltha.AdminState_DISABLED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN},
-			handlers:      []TransitionHandler{dMgr.deleteLogicalPort}})
+			handlers:      []TransitionHandler{}})
 	transitionMap.transitions = append(transitionMap.transitions,
 		Transition{
 			deviceType:    any,
 			previousState: DeviceState{Admin: voltha.AdminState_ENABLED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN},
 			currentState:  DeviceState{Admin: voltha.AdminState_PREPROVISIONED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN},
-			handlers:      []TransitionHandler{dMgr.abandonDevice}})
+			handlers:      []TransitionHandler{dMgr.NotifyInvalidTransition}})
+	transitionMap.transitions = append(transitionMap.transitions,
+		Transition{
+			deviceType:    any,
+			previousState: DeviceState{Admin: voltha.AdminState_DISABLED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_ACTIVATING},
+			currentState:  DeviceState{Admin: voltha.AdminState_DISABLED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_ACTIVE},
+			handlers:      []TransitionHandler{dMgr.SetAdminStateToEnable}})
 	transitionMap.transitions = append(transitionMap.transitions,
 		Transition{
 			deviceType:    child,
 			previousState: DeviceState{Admin: voltha.AdminState_DISABLED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN},
 			currentState:  DeviceState{Admin: voltha.AdminState_UNKNOWN, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN},
-			handlers:      []TransitionHandler{dMgr.notAllowed}})
+			handlers:      []TransitionHandler{dMgr.NotifyInvalidTransition}})
 	transitionMap.transitions = append(transitionMap.transitions,
 		Transition{
 			deviceType:    any,
 			previousState: DeviceState{Admin: voltha.AdminState_DISABLED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN},
 			currentState:  DeviceState{Admin: voltha.AdminState_PREPROVISIONED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN},
-			handlers:      []TransitionHandler{dMgr.abandonDevice}})
-	transitionMap.transitions = append(transitionMap.transitions,
-		Transition{
-			deviceType:    any,
-			previousState: DeviceState{Admin: voltha.AdminState_DISABLED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN},
-			currentState:  DeviceState{Admin: voltha.AdminState_ENABLED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN},
-			handlers:      []TransitionHandler{dMgr.reEnableDevice}})
+			handlers:      []TransitionHandler{dMgr.NotifyInvalidTransition}})
 	transitionMap.transitions = append(transitionMap.transitions,
 		Transition{
 			deviceType:    parent,
 			previousState: DeviceState{Admin: voltha.AdminState_DISABLED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN},
 			currentState:  DeviceState{Admin: voltha.AdminState_DELETED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN},
-			handlers:      []TransitionHandler{dMgr.deleteAllChildDevices}})
+			handlers:      []TransitionHandler{dMgr.DeleteAllChildDevices, dMgr.DeleteLogicalDevice, dMgr.RunPostDeviceDelete}})
+	transitionMap.transitions = append(transitionMap.transitions,
+		Transition{
+			deviceType:    child,
+			previousState: DeviceState{Admin: voltha.AdminState_DISABLED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN},
+			currentState:  DeviceState{Admin: voltha.AdminState_DELETED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN},
+			handlers:      []TransitionHandler{dMgr.DeleteLogicalPorts, dMgr.RunPostDeviceDelete}})
 	transitionMap.transitions = append(transitionMap.transitions,
 		Transition{
 			deviceType:    any,
 			previousState: DeviceState{Admin: voltha.AdminState_DOWNLOADING_IMAGE, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN},
 			currentState:  DeviceState{Admin: voltha.AdminState_DISABLED, Connection: voltha.ConnectStatus_UNKNOWN, Operational: voltha.OperStatus_UNKNOWN},
-			handlers:      []TransitionHandler{dMgr.notAllowed}})
+			handlers:      []TransitionHandler{dMgr.NotifyInvalidTransition}})
 
 	return &transitionMap
 }
@@ -143,6 +194,12 @@
 	if *previous == transition.previousState && *current == transition.currentState {
 		return transition.handlers, true
 	}
+
+	// Admin states must match
+	if previous.Admin != transition.previousState.Admin || current.Admin != transition.currentState.Admin {
+		return nil, false
+	}
+
 	// If the admin state changed then prioritize it first
 	if previous.Admin != current.Admin {
 		if previous.Admin == transition.previousState.Admin && current.Admin == transition.currentState.Admin {
@@ -179,26 +236,22 @@
 	//2. Go over transition array to get the right transition
 	var currentMatch []TransitionHandler
 	var tempHandler []TransitionHandler
-	var exactMatch bool
-	var deviceTypeMatch bool
+	var exactStateMatch bool
+	var stateMatchFound bool
 	for _, aTransition := range tMap.transitions {
 		// consider transition only if it matches deviceType or is a wild card - any
 		if aTransition.deviceType != deviceType && aTransition.deviceType != any {
 			continue
 		}
-		tempHandler, exactMatch = getHandler(pState, cState, &aTransition)
+		tempHandler, exactStateMatch = getHandler(pState, cState, &aTransition)
 		if tempHandler != nil {
-			if exactMatch {
+			if exactStateMatch && aTransition.deviceType == deviceType {
 				return tempHandler
-			} else {
-				if currentMatch == nil {
-					currentMatch = tempHandler
-				} else if aTransition.deviceType == deviceType {
-					currentMatch = tempHandler
-					deviceTypeMatch = true
-				} else if !deviceTypeMatch {
-					currentMatch = tempHandler
-				}
+			} else if exactStateMatch {
+				currentMatch = tempHandler
+				stateMatchFound = true
+			} else if currentMatch == nil && !stateMatchFound {
+				currentMatch = tempHandler
 			}
 		}
 	}
diff --git a/rw_core/core/device_state_transitions_test.go b/rw_core/core/device_state_transitions_test.go
new file mode 100644
index 0000000..a9ec326
--- /dev/null
+++ b/rw_core/core/device_state_transitions_test.go
@@ -0,0 +1,223 @@
+/*
+ * Copyright 2019-present Open Networking Foundation
+ *
+ * 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.
+ */
+package core
+
+import (
+	"github.com/opencord/voltha-go/common/log"
+	"github.com/opencord/voltha-go/rw_core/coreIf"
+	"github.com/opencord/voltha-protos/go/voltha"
+	"github.com/stretchr/testify/assert"
+	"reflect"
+	"testing"
+)
+
+var transitionMap *TransitionMap
+var tdm coreIf.DeviceManager
+
+type testDeviceManager struct {
+}
+
+func newTestDeviceManager() *testDeviceManager {
+	return &testDeviceManager{}
+}
+
+func (tdm *testDeviceManager) GetDevice(string) (*voltha.Device, error) {
+	return nil, nil
+}
+
+func (tdm *testDeviceManager) IsRootDevice(string) (bool, error) {
+	return false, nil
+}
+
+func (tdm *testDeviceManager) NotifyInvalidTransition(pto *voltha.Device) error {
+	return nil
+}
+
+func (tdm *testDeviceManager) SetAdminStateToEnable(to *voltha.Device) error {
+	return nil
+}
+
+func (tdm *testDeviceManager) CreateLogicalDevice(to *voltha.Device) error {
+	return nil
+}
+
+func (tdm *testDeviceManager) SetupUNILogicalPorts(to *voltha.Device) error {
+	return nil
+}
+
+func (tdm *testDeviceManager) DisableAllChildDevices(to *voltha.Device) error {
+	return nil
+}
+
+func (tdm *testDeviceManager) DeleteLogicalDevice(to *voltha.Device) error {
+	return nil
+}
+
+func (tdm *testDeviceManager) DeleteLogicalPorts(to *voltha.Device) error {
+	return nil
+}
+
+func (tdm *testDeviceManager) DeleteAllChildDevices(to *voltha.Device) error {
+	return nil
+}
+
+func (tdm *testDeviceManager) RunPostDeviceDelete(to *voltha.Device) error {
+	return nil
+}
+
+func init() {
+	log.AddPackage(log.JSON, log.WarnLevel, nil)
+	//log.UpdateAllLoggers(log.Fields{"instanceId": "device-state-transition"})
+	//log.SetAllLogLevel(log.DebugLevel)
+	tdm = newTestDeviceManager()
+	transitionMap = NewTransitionMap(tdm)
+}
+
+func getDevice(root bool, admin voltha.AdminState_AdminState, conn voltha.ConnectStatus_ConnectStatus, oper voltha.OperStatus_OperStatus) *voltha.Device {
+	return &voltha.Device{
+		Id:            "test",
+		Root:          root,
+		AdminState:    admin,
+		ConnectStatus: conn,
+		OperStatus:    oper,
+	}
+}
+
+func assertInvalidTransition(t *testing.T, from *voltha.Device, to *voltha.Device) {
+	handlers := transitionMap.GetTransitionHandler(from, to)
+	assert.Equal(t, 1, len(handlers))
+	assert.True(t, reflect.ValueOf(tdm.NotifyInvalidTransition).Pointer() == reflect.ValueOf(handlers[0]).Pointer())
+}
+
+func assertNoOpTransition(t *testing.T, from *voltha.Device, to *voltha.Device) {
+	handlers := transitionMap.GetTransitionHandler(from, to)
+	assert.Equal(t, 0, len(handlers))
+}
+
+func TestValidTransitions(t *testing.T) {
+	from := getDevice(true, voltha.AdminState_PREPROVISIONED, voltha.ConnectStatus_UNKNOWN, voltha.OperStatus_ACTIVATING)
+	to := getDevice(true, voltha.AdminState_PREPROVISIONED, voltha.ConnectStatus_UNKNOWN, voltha.OperStatus_ACTIVE)
+	handlers := transitionMap.GetTransitionHandler(from, to)
+	assert.Equal(t, 1, len(handlers))
+	assert.True(t, reflect.ValueOf(tdm.SetAdminStateToEnable).Pointer() == reflect.ValueOf(handlers[0]).Pointer())
+
+	from = getDevice(true, voltha.AdminState_PREPROVISIONED, voltha.ConnectStatus_UNKNOWN, voltha.OperStatus_ACTIVE)
+	to = getDevice(true, voltha.AdminState_ENABLED, voltha.ConnectStatus_UNKNOWN, voltha.OperStatus_ACTIVE)
+	handlers = transitionMap.GetTransitionHandler(from, to)
+	assert.Equal(t, 1, len(handlers))
+	assert.True(t, reflect.ValueOf(tdm.CreateLogicalDevice).Pointer() == reflect.ValueOf(handlers[0]).Pointer())
+
+	from = getDevice(false, voltha.AdminState_PREPROVISIONED, voltha.ConnectStatus_UNKNOWN, voltha.OperStatus_ACTIVATING)
+	to = getDevice(false, voltha.AdminState_PREPROVISIONED, voltha.ConnectStatus_UNKNOWN, voltha.OperStatus_ACTIVE)
+	handlers = transitionMap.GetTransitionHandler(from, to)
+	assert.Equal(t, 1, len(handlers))
+	assert.True(t, reflect.ValueOf(tdm.SetAdminStateToEnable).Pointer() == reflect.ValueOf(handlers[0]).Pointer())
+
+	from = getDevice(false, voltha.AdminState_PREPROVISIONED, voltha.ConnectStatus_UNKNOWN, voltha.OperStatus_ACTIVE)
+	to = getDevice(false, voltha.AdminState_ENABLED, voltha.ConnectStatus_UNKNOWN, voltha.OperStatus_ACTIVE)
+	handlers = transitionMap.GetTransitionHandler(from, to)
+	assert.Equal(t, 1, len(handlers))
+	assert.True(t, reflect.ValueOf(tdm.SetupUNILogicalPorts).Pointer() == reflect.ValueOf(handlers[0]).Pointer())
+
+	from = getDevice(true, voltha.AdminState_ENABLED, voltha.ConnectStatus_UNKNOWN, voltha.OperStatus_UNKNOWN)
+	to = getDevice(true, voltha.AdminState_DISABLED, voltha.ConnectStatus_UNKNOWN, voltha.OperStatus_UNKNOWN)
+	handlers = transitionMap.GetTransitionHandler(from, to)
+	assert.Equal(t, 1, len(handlers))
+	assert.True(t, reflect.ValueOf(tdm.DisableAllChildDevices).Pointer() == reflect.ValueOf(handlers[0]).Pointer())
+
+	from = getDevice(false, voltha.AdminState_DISABLED, voltha.ConnectStatus_UNKNOWN, voltha.OperStatus_ACTIVATING)
+	to = getDevice(false, voltha.AdminState_DISABLED, voltha.ConnectStatus_UNKNOWN, voltha.OperStatus_ACTIVE)
+	handlers = transitionMap.GetTransitionHandler(from, to)
+	assert.Equal(t, 1, len(handlers))
+	assert.True(t, reflect.ValueOf(tdm.SetAdminStateToEnable).Pointer() == reflect.ValueOf(handlers[0]).Pointer())
+
+	from = getDevice(true, voltha.AdminState_DISABLED, voltha.ConnectStatus_UNKNOWN, voltha.OperStatus_ACTIVATING)
+	to = getDevice(true, voltha.AdminState_DISABLED, voltha.ConnectStatus_UNKNOWN, voltha.OperStatus_ACTIVE)
+	handlers = transitionMap.GetTransitionHandler(from, to)
+	assert.Equal(t, 1, len(handlers))
+	assert.True(t, reflect.ValueOf(tdm.SetAdminStateToEnable).Pointer() == reflect.ValueOf(handlers[0]).Pointer())
+
+	from = getDevice(true, voltha.AdminState_DISABLED, voltha.ConnectStatus_UNKNOWN, voltha.OperStatus_UNKNOWN)
+	to = getDevice(true, voltha.AdminState_DELETED, voltha.ConnectStatus_UNKNOWN, voltha.OperStatus_UNKNOWN)
+	handlers = transitionMap.GetTransitionHandler(from, to)
+	assert.Equal(t, 3, len(handlers))
+	assert.True(t, reflect.ValueOf(tdm.DeleteAllChildDevices).Pointer() == reflect.ValueOf(handlers[0]).Pointer())
+	assert.True(t, reflect.ValueOf(tdm.DeleteLogicalDevice).Pointer() == reflect.ValueOf(handlers[1]).Pointer())
+	assert.True(t, reflect.ValueOf(tdm.RunPostDeviceDelete).Pointer() == reflect.ValueOf(handlers[2]).Pointer())
+
+	from = getDevice(false, voltha.AdminState_DISABLED, voltha.ConnectStatus_UNKNOWN, voltha.OperStatus_UNKNOWN)
+	to = getDevice(false, voltha.AdminState_DELETED, voltha.ConnectStatus_UNKNOWN, voltha.OperStatus_UNKNOWN)
+	handlers = transitionMap.GetTransitionHandler(from, to)
+	assert.Equal(t, 2, len(handlers))
+	assert.True(t, reflect.ValueOf(tdm.DeleteLogicalPorts).Pointer() == reflect.ValueOf(handlers[0]).Pointer())
+	assert.True(t, reflect.ValueOf(tdm.RunPostDeviceDelete).Pointer() == reflect.ValueOf(handlers[1]).Pointer())
+}
+
+func TestInvalidTransitions(t *testing.T) {
+	from := getDevice(true, voltha.AdminState_PREPROVISIONED, voltha.ConnectStatus_UNKNOWN, voltha.OperStatus_ACTIVE)
+	to := getDevice(true, voltha.AdminState_PREPROVISIONED, voltha.ConnectStatus_UNKNOWN, voltha.OperStatus_ACTIVATING)
+	assertInvalidTransition(t, from, to)
+
+	from = getDevice(true, voltha.AdminState_PREPROVISIONED, voltha.ConnectStatus_UNKNOWN, voltha.OperStatus_ACTIVATING)
+	to = getDevice(true, voltha.AdminState_PREPROVISIONED, voltha.ConnectStatus_UNKNOWN, voltha.OperStatus_UNKNOWN)
+	assertInvalidTransition(t, from, to)
+
+	from = getDevice(true, voltha.AdminState_PREPROVISIONED, voltha.ConnectStatus_UNKNOWN, voltha.OperStatus_UNKNOWN)
+	to = getDevice(true, voltha.AdminState_UNKNOWN, voltha.ConnectStatus_UNKNOWN, voltha.OperStatus_UNKNOWN)
+	assertInvalidTransition(t, from, to)
+
+	from = getDevice(true, voltha.AdminState_PREPROVISIONED, voltha.ConnectStatus_UNKNOWN, voltha.OperStatus_UNKNOWN)
+	to = getDevice(true, voltha.AdminState_DOWNLOADING_IMAGE, voltha.ConnectStatus_UNKNOWN, voltha.OperStatus_UNKNOWN)
+	assertInvalidTransition(t, from, to)
+
+	from = getDevice(true, voltha.AdminState_ENABLED, voltha.ConnectStatus_UNKNOWN, voltha.OperStatus_UNKNOWN)
+	to = getDevice(true, voltha.AdminState_UNKNOWN, voltha.ConnectStatus_UNKNOWN, voltha.OperStatus_UNKNOWN)
+	assertInvalidTransition(t, from, to)
+
+	from = getDevice(true, voltha.AdminState_DISABLED, voltha.ConnectStatus_UNKNOWN, voltha.OperStatus_UNKNOWN)
+	to = getDevice(true, voltha.AdminState_PREPROVISIONED, voltha.ConnectStatus_UNKNOWN, voltha.OperStatus_UNKNOWN)
+	assertInvalidTransition(t, from, to)
+
+	from = getDevice(true, voltha.AdminState_DOWNLOADING_IMAGE, voltha.ConnectStatus_UNKNOWN, voltha.OperStatus_UNKNOWN)
+	to = getDevice(true, voltha.AdminState_DISABLED, voltha.ConnectStatus_UNKNOWN, voltha.OperStatus_UNKNOWN)
+	assertInvalidTransition(t, from, to)
+}
+
+func TestNoOpTransitions(t *testing.T) {
+	from := getDevice(true, voltha.AdminState_ENABLED, voltha.ConnectStatus_UNKNOWN, voltha.OperStatus_UNKNOWN)
+	to := getDevice(true, voltha.AdminState_ENABLED, voltha.ConnectStatus_UNKNOWN, voltha.OperStatus_UNKNOWN)
+	assertNoOpTransition(t, from, to)
+
+	from = getDevice(true, voltha.AdminState_PREPROVISIONED, voltha.ConnectStatus_UNKNOWN, voltha.OperStatus_UNKNOWN)
+	to = getDevice(true, voltha.AdminState_PREPROVISIONED, voltha.ConnectStatus_UNKNOWN, voltha.OperStatus_UNKNOWN)
+	assertNoOpTransition(t, from, to)
+
+	from = getDevice(true, voltha.AdminState_DISABLED, voltha.ConnectStatus_UNKNOWN, voltha.OperStatus_UNKNOWN)
+	to = getDevice(true, voltha.AdminState_DISABLED, voltha.ConnectStatus_UNKNOWN, voltha.OperStatus_UNKNOWN)
+	assertNoOpTransition(t, from, to)
+
+	from = getDevice(false, voltha.AdminState_ENABLED, voltha.ConnectStatus_UNKNOWN, voltha.OperStatus_UNKNOWN)
+	to = getDevice(false, voltha.AdminState_DISABLED, voltha.ConnectStatus_UNKNOWN, voltha.OperStatus_UNKNOWN)
+	assertNoOpTransition(t, from, to)
+
+	from = getDevice(false, voltha.AdminState_PREPROVISIONED, voltha.ConnectStatus_UNKNOWN, voltha.OperStatus_ACTIVATING)
+	to = getDevice(false, voltha.AdminState_PREPROVISIONED, voltha.ConnectStatus_REACHABLE, voltha.OperStatus_DISCOVERED)
+	assertNoOpTransition(t, from, to)
+
+	from = getDevice(false, voltha.AdminState_PREPROVISIONED, voltha.ConnectStatus_REACHABLE, voltha.OperStatus_DISCOVERED)
+	to = getDevice(false, voltha.AdminState_PREPROVISIONED, voltha.ConnectStatus_REACHABLE, voltha.OperStatus_ACTIVATING)
+	assertNoOpTransition(t, from, to)
+}
diff --git a/rw_core/core/logical_device_agent.go b/rw_core/core/logical_device_agent.go
index 2f85c45..7b2783e 100644
--- a/rw_core/core/logical_device_agent.go
+++ b/rw_core/core/logical_device_agent.go
@@ -329,10 +329,10 @@
 				switch state {
 				case voltha.AdminState_ENABLED:
 					lport.OfpPort.Config = lport.OfpPort.Config & ^uint32(ofp.OfpPortConfig_OFPPC_PORT_DOWN)
-					lport.OfpPort.State = lport.OfpPort.State & ^uint32(ofp.OfpPortState_OFPPS_LINK_DOWN)
+					lport.OfpPort.State = uint32(ofp.OfpPortState_OFPPS_LIVE)
 				case voltha.AdminState_DISABLED:
 					lport.OfpPort.Config = lport.OfpPort.Config | uint32(ofp.OfpPortConfig_OFPPC_PORT_DOWN)
-					lport.OfpPort.State = lport.OfpPort.State | uint32(ofp.OfpPortState_OFPPS_LINK_DOWN)
+					lport.OfpPort.State = uint32(ofp.OfpPortState_OFPPS_LINK_DOWN)
 				default:
 					log.Warnw("unsupported-state-change", log.Fields{"deviceId": device.Id, "state": state})
 				}
@@ -365,6 +365,37 @@
 	return err
 }
 
+// deleteAllLogicalPorts deletes all logical ports associated with this device
+func (agent *LogicalDeviceAgent) deleteAllLogicalPorts(device *voltha.Device) error {
+	log.Infow("updatePortsState-start", log.Fields{"logicalDeviceId": agent.logicalDeviceId})
+	agent.lockLogicalDevice.Lock()
+	defer agent.lockLogicalDevice.Unlock()
+	// Get the latest logical device info
+	if ld, err := agent.getLogicalDeviceWithoutLock(); err != nil {
+		log.Warnw("logical-device-unknown", log.Fields{"ldeviceId": agent.logicalDeviceId, "error": err})
+		return err
+	} else {
+		cloned := (proto.Clone(ld)).(*voltha.LogicalDevice)
+		updateLogicalPorts := []*voltha.LogicalPort{}
+		for _, lport := range cloned.Ports {
+			if lport.DeviceId != device.Id {
+				updateLogicalPorts = append(updateLogicalPorts, lport)
+			}
+		}
+		if len(updateLogicalPorts) < len(cloned.Ports) {
+			cloned.Ports = updateLogicalPorts
+			// Updating the logical device will trigger the poprt change events to be populated to the controller
+			if err := agent.updateLogicalDeviceWithoutLock(cloned); err != nil {
+				log.Warnw("logical-device-update-failed", log.Fields{"ldeviceId": agent.logicalDeviceId, "error": err})
+				return err
+			}
+		} else {
+			log.Debugw("no-change-required", log.Fields{"logicalDeviceId": agent.logicalDeviceId})
+		}
+	}
+	return nil
+}
+
 //updateLogicalDeviceWithoutLock updates the model with the logical device.  It clones the logicaldevice before saving it
 func (agent *LogicalDeviceAgent) updateLogicalDeviceWithoutLock(logicalDevice *voltha.LogicalDevice) error {
 	afterUpdate := agent.clusterDataProxy.Update("/logical_devices/"+agent.logicalDeviceId, logicalDevice, false, "")
@@ -770,11 +801,45 @@
 		logicaldevice.Ports[len(logicaldevice.Ports)-1] = nil
 		logicaldevice.Ports = logicaldevice.Ports[:len(logicaldevice.Ports)-1]
 		log.Debugw("logical-port-deleted", log.Fields{"logicalDeviceId": agent.logicalDeviceId})
-		return agent.updateLogicalDeviceWithoutLock(logicaldevice)
+		if err := agent.updateLogicalDeviceWithoutLock(logicaldevice); err != nil {
+			log.Errorw("logical-device-update-failed", log.Fields{"logicalDeviceId": agent.logicalDeviceId})
+			return err
+		}
+		// Reset the logical device graph
+		go agent.redoDeviceGraph()
 	}
 	return nil
 }
 
+// deleteLogicalPorts removes the logical ports associated with that deviceId
+func (agent *LogicalDeviceAgent) deleteLogicalPorts(deviceId string) error {
+	agent.lockLogicalDevice.Lock()
+	defer agent.lockLogicalDevice.Unlock()
+
+	// Get the most up to date logical device
+	var logicaldevice *voltha.LogicalDevice
+	if logicaldevice, _ = agent.getLogicalDeviceWithoutLock(); logicaldevice == nil {
+		log.Debugw("no-logical-device", log.Fields{"logicalDeviceId": agent.logicalDeviceId})
+		return nil
+	}
+	updatedLPorts := []*voltha.LogicalPort{}
+	for _, logicalPort := range logicaldevice.Ports {
+		if logicalPort.DeviceId != deviceId {
+			updatedLPorts = append(updatedLPorts, logicalPort)
+		}
+	}
+	logicaldevice.Ports = updatedLPorts
+	log.Debugw("updated-logical-ports", log.Fields{"ports": updatedLPorts})
+	if err := agent.updateLogicalDeviceWithoutLock(logicaldevice); err != nil {
+		log.Errorw("logical-device-update-failed", log.Fields{"logicalDeviceId": agent.logicalDeviceId})
+		return err
+	}
+	// Reset the logical device graph
+	go agent.redoDeviceGraph()
+
+	return nil
+}
+
 // enableLogicalPort enables the logical port
 func (agent *LogicalDeviceAgent) enableLogicalPort(lPort *voltha.LogicalPort) error {
 	agent.lockLogicalDevice.Lock()
@@ -1084,6 +1149,21 @@
 	agent.deviceGraph.Print()
 }
 
+//redoDeviceGraph regenerates the device graph upon port changes on a device graph
+//TODO: it may yield better performance to have separate deleteLogicalPort functions that would remove
+// all the routes/nodes related to the deleted logical port.
+func (agent *LogicalDeviceAgent) redoDeviceGraph() {
+	log.Debugf("redoDeviceGraph", log.Fields{"logicalDeviceId": agent.logicalDeviceId})
+	agent.lockLogicalDevice.Lock()
+	defer agent.lockLogicalDevice.Unlock()
+	// Get the latest logical device
+	if ld, err := agent.getLogicalDeviceWithoutLock(); err != nil {
+		log.Errorw("logical-device-not-present", log.Fields{"logicalDeviceId": agent.logicalDeviceId, "error": err})
+	} else {
+		agent.deviceGraph.ComputeRoutes(ld.Ports)
+	}
+}
+
 // portAdded is a callback invoked when a port is added to the logical device.
 // TODO: To use when POST_ADD is fixed.
 func (agent *LogicalDeviceAgent) portAdded(args ...interface{}) interface{} {
diff --git a/rw_core/core/logical_device_manager.go b/rw_core/core/logical_device_manager.go
index e9b5a4f..7a625b4 100644
--- a/rw_core/core/logical_device_manager.go
+++ b/rw_core/core/logical_device_manager.go
@@ -319,7 +319,7 @@
 	return nil
 }
 
-// deleteLogicalPort removes the logical port associated with a child device
+// deleteLogicalPort removes the logical port associated with a device
 func (ldMgr *LogicalDeviceManager) deleteLogicalPort(ctx context.Context, lPortId *voltha.LogicalPortId) error {
 	log.Debugw("deleting-logical-port", log.Fields{"LDeviceId": lPortId.Id})
 	// Get logical port
@@ -334,13 +334,34 @@
 		return errors.New("device-root")
 	}
 	if agent := ldMgr.getLogicalDeviceAgent(lPortId.Id); agent != nil {
-		agent.deleteLogicalPort(logicalPort)
+		if err := agent.deleteLogicalPort(logicalPort); err != nil {
+			log.Warnw("deleting-logicalport-failed", log.Fields{"LDeviceId": lPortId.Id, "error": err})
+		}
 	}
 
 	log.Debug("deleting-logical-port-ends")
 	return nil
 }
 
+// deleteLogicalPort removes the logical port associated with a child device
+func (ldMgr *LogicalDeviceManager) deleteLogicalPorts(deviceId string) error {
+	log.Debugw("deleting-logical-ports", log.Fields{"deviceId": deviceId})
+	// Get logical port
+	if ldId, err := ldMgr.getLogicalDeviceIdFromDeviceId(deviceId); err != nil {
+		log.Warnw("logical-device-not-found", log.Fields{"deviceId": deviceId})
+		return err
+	} else {
+		if agent := ldMgr.getLogicalDeviceAgent(*ldId); agent != nil {
+			if err = agent.deleteLogicalPorts(deviceId); err != nil {
+				log.Warnw("deleteLogicalPorts-failed", log.Fields{"ldeviceId": *ldId})
+				return err
+			}
+		}
+	}
+	log.Debug("deleting-logical-port-ends")
+	return nil
+}
+
 func (ldMgr *LogicalDeviceManager) setupUNILogicalPorts(ctx context.Context, childDevice *voltha.Device) error {
 	log.Debugw("setupUNILogicalPorts", log.Fields{"childDeviceId": childDevice.Id, "parentDeviceId": childDevice.ParentId})
 	// Sanity check
@@ -366,6 +387,24 @@
 	return nil
 }
 
+func (ldMgr *LogicalDeviceManager) deleteAllLogicalPorts(device *voltha.Device) error {
+	log.Debugw("deleteAllLogicalPorts", log.Fields{"deviceId": device.Id})
+
+	var ldId *string
+	var err error
+	//Get the logical device Id for this device
+	if ldId, err = ldMgr.getLogicalDeviceId(device); err != nil {
+		log.Warnw("no-logical-device-found", log.Fields{"deviceId": device.Id, "error": err})
+		return err
+	}
+	if agent := ldMgr.getLogicalDeviceAgent(*ldId); agent != nil {
+		if err := agent.deleteAllLogicalPorts(device); err != nil {
+			return err
+		}
+	}
+	return nil
+}
+
 func (ldMgr *LogicalDeviceManager) updatePortsState(device *voltha.Device, state voltha.AdminState_AdminState) error {
 	log.Debugw("updatePortsState", log.Fields{"deviceId": device.Id, "state": state})
 
diff --git a/rw_core/coreIf/device_manager_if.go b/rw_core/coreIf/device_manager_if.go
index ec191dc..367f442 100644
--- a/rw_core/coreIf/device_manager_if.go
+++ b/rw_core/coreIf/device_manager_if.go
@@ -25,4 +25,13 @@
 type DeviceManager interface {
 	GetDevice(string) (*voltha.Device, error)
 	IsRootDevice(string) (bool, error)
+	NotifyInvalidTransition(*voltha.Device) error
+	SetAdminStateToEnable(*voltha.Device) error
+	CreateLogicalDevice(*voltha.Device) error
+	SetupUNILogicalPorts(*voltha.Device) error
+	DisableAllChildDevices(cDevice *voltha.Device) error
+	DeleteLogicalDevice(cDevice *voltha.Device) error
+	DeleteLogicalPorts(cDevice *voltha.Device) error
+	DeleteAllChildDevices(cDevice *voltha.Device) error
+	RunPostDeviceDelete(cDevice *voltha.Device) error
 }
diff --git a/rw_core/flow_decomposition/flow_decomposer_test.go b/rw_core/flow_decomposition/flow_decomposer_test.go
index fdc2b86..5d914ac 100644
--- a/rw_core/flow_decomposition/flow_decomposer_test.go
+++ b/rw_core/flow_decomposition/flow_decomposer_test.go
@@ -101,6 +101,42 @@
 	return false, errors.New("ABSENT.")
 }
 
+func (tdm *testDeviceManager) NotifyInvalidTransition(pcDevice *voltha.Device) error {
+	return nil
+}
+
+func (tdm *testDeviceManager) SetAdminStateToEnable(cDevice *voltha.Device) error {
+	return nil
+}
+
+func (tdm *testDeviceManager) CreateLogicalDevice(cDevice *voltha.Device) error {
+	return nil
+}
+
+func (tdm *testDeviceManager) SetupUNILogicalPorts(cDevice *voltha.Device) error {
+	return nil
+}
+
+func (tdm *testDeviceManager) DisableAllChildDevices(cDevice *voltha.Device) error {
+	return nil
+}
+
+func (tdm *testDeviceManager) DeleteLogicalDevice(cDevice *voltha.Device) error {
+	return nil
+}
+
+func (tdm *testDeviceManager) DeleteLogicalPorts(cDevice *voltha.Device) error {
+	return nil
+}
+
+func (tdm *testDeviceManager) DeleteAllChildDevices(cDevice *voltha.Device) error {
+	return nil
+}
+
+func (tdm *testDeviceManager) RunPostDeviceDelete(cDevice *voltha.Device) error {
+	return nil
+}
+
 type testFlowDecomposer struct {
 	dMgr         *testDeviceManager
 	logicalPorts map[uint32]*voltha.LogicalPort