[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/adapters/common/core_proxy.go b/adapters/common/core_proxy.go
index 5bbd176..e1a7e34 100644
--- a/adapters/common/core_proxy.go
+++ b/adapters/common/core_proxy.go
@@ -178,6 +178,27 @@
return unPackResponse(rpc, deviceId, success, result)
}
+func (ap *CoreProxy) DeleteAllPorts(ctx context.Context, deviceId string) error {
+ log.Debugw("DeleteAllPorts", log.Fields{"deviceId": deviceId})
+ rpc := "DeleteAllPorts"
+ // Use a device specific topic to send the request. The adapter handling the device creates a device
+ // specific topic
+ toTopic := ap.getCoreTopic(deviceId)
+ args := make([]*kafka.KVArg, 2)
+ id := &voltha.ID{Id: deviceId}
+
+ args[0] = &kafka.KVArg{
+ Key: "device_id",
+ Value: id,
+ }
+
+ // Use a device specific topic as we are the only adaptercore handling requests for this device
+ replyToTopic := ap.getAdapterTopic()
+ success, result := ap.kafkaICProxy.InvokeRPC(nil, rpc, &toTopic, &replyToTopic, true, deviceId, args...)
+ log.Debugw("DeleteAllPorts-response", log.Fields{"deviceId": deviceId, "success": success})
+ return unPackResponse(rpc, deviceId, success, result)
+}
+
func (ap *CoreProxy) DeviceStateUpdate(ctx context.Context, deviceId string,
connStatus voltha.ConnectStatus_ConnectStatus, operStatus voltha.OperStatus_OperStatus) error {
log.Debugw("DeviceStateUpdate", log.Fields{"deviceId": deviceId})
@@ -211,7 +232,7 @@
func (ap *CoreProxy) ChildDeviceDetected(ctx context.Context, parentDeviceId string, parentPortNo int,
childDeviceType string, channelId int, vendorId string, serialNumber string, onuId int64) error {
- log.Debugw("ChildDeviceDetected", log.Fields{"pPeviceId": parentDeviceId, "channelId": channelId})
+ log.Debugw("ChildDeviceDetected", log.Fields{"pDeviceId": parentDeviceId, "channelId": channelId})
rpc := "ChildDeviceDetected"
// Use a device specific topic to send the request. The adapter handling the device creates a device
// specific topic
@@ -261,6 +282,46 @@
}
+func (ap *CoreProxy) ChildDevicesLost(ctx context.Context, parentDeviceId string) error {
+ log.Debugw("ChildDevicesLost", log.Fields{"pDeviceId": parentDeviceId})
+ rpc := "ChildDevicesLost"
+ // Use a device specific topic to send the request. The adapter handling the device creates a device
+ // specific topic
+ toTopic := ap.getCoreTopic(parentDeviceId)
+ replyToTopic := ap.getAdapterTopic()
+
+ args := make([]*kafka.KVArg, 1)
+ id := &voltha.ID{Id: parentDeviceId}
+ args[0] = &kafka.KVArg{
+ Key: "parent_device_id",
+ Value: id,
+ }
+
+ success, result := ap.kafkaICProxy.InvokeRPC(nil, rpc, &toTopic, &replyToTopic, true, parentDeviceId, args...)
+ log.Debugw("ChildDevicesLost-response", log.Fields{"pDeviceId": parentDeviceId, "success": success})
+ return unPackResponse(rpc, parentDeviceId, success, result)
+}
+
+func (ap *CoreProxy) ChildDevicesDetected(ctx context.Context, parentDeviceId string) error {
+ log.Debugw("ChildDevicesDetected", log.Fields{"pDeviceId": parentDeviceId})
+ rpc := "ChildDevicesDetected"
+ // Use a device specific topic to send the request. The adapter handling the device creates a device
+ // specific topic
+ toTopic := ap.getCoreTopic(parentDeviceId)
+ replyToTopic := ap.getAdapterTopic()
+
+ args := make([]*kafka.KVArg, 1)
+ id := &voltha.ID{Id: parentDeviceId}
+ args[0] = &kafka.KVArg{
+ Key: "parent_device_id",
+ Value: id,
+ }
+
+ success, result := ap.kafkaICProxy.InvokeRPC(nil, rpc, &toTopic, &replyToTopic, true, parentDeviceId, args...)
+ log.Debugw("ChildDevicesDetected-response", log.Fields{"pDeviceId": parentDeviceId, "success": success})
+ return unPackResponse(rpc, parentDeviceId, success, result)
+}
+
func (ap *CoreProxy) GetDevice(ctx context.Context, parentDeviceId string, deviceId string) (*voltha.Device, error) {
log.Debugw("GetDevice", log.Fields{"deviceId": deviceId})
rpc := "GetDevice"
diff --git a/adapters/common/request_handler.go b/adapters/common/request_handler.go
index 8b582b8..e0c7860 100644
--- a/adapters/common/request_handler.go
+++ b/adapters/common/request_handler.go
@@ -191,6 +191,40 @@
}
func (rhp *RequestHandlerProxy) Delete_device(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
+ }
+
+ device := &voltha.Device{}
+ transactionID := &ic.StrType{}
+ fromTopic := &ic.StrType{}
+ for _, arg := range args {
+ switch arg.Key {
+ case "device":
+ if err := ptypes.UnmarshalAny(arg.Value, device); err != nil {
+ log.Warnw("cannot-unmarshal-device", 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
+ }
+ case kafka.FromTopic:
+ if err := ptypes.UnmarshalAny(arg.Value, fromTopic); err != nil {
+ log.Warnw("cannot-unmarshal-from-topic", log.Fields{"error": err})
+ return nil, err
+ }
+ }
+ }
+ //Update the core reference for that device
+ rhp.coreProxy.UpdateCoreReference(device.Id, fromTopic.Val)
+ //Invoke the Disable_device API on the adapter
+ if err := rhp.adapter.Delete_device(device); err != nil {
+ return nil, status.Errorf(codes.NotFound, "%s", err.Error())
+ }
return new(empty.Empty), nil
}
diff --git a/adapters/iAdapter.go b/adapters/iAdapter.go
index a1dfa16..017082d 100644
--- a/adapters/iAdapter.go
+++ b/adapters/iAdapter.go
@@ -33,7 +33,7 @@
Reenable_device(device *voltha.Device) error
Reboot_device(device *voltha.Device) error
Self_test_device(device *voltha.Device) error
- Gelete_device(device *voltha.Device) error
+ Delete_device(device *voltha.Device) error
Get_device_details(device *voltha.Device) error
Update_flows_bulk(device *voltha.Device, flows *voltha.Flows, groups *voltha.FlowGroups) error
Update_flows_incrementally(device *voltha.Device, flows *openflow_13.FlowChanges, groups *openflow_13.FlowGroupChanges) error
diff --git a/adapters/simulated_olt/adaptercore/device_handler.go b/adapters/simulated_olt/adaptercore/device_handler.go
index 07f31ff..f64f99b 100644
--- a/adapters/simulated_olt/adaptercore/device_handler.go
+++ b/adapters/simulated_olt/adaptercore/device_handler.go
@@ -218,6 +218,13 @@
log.Errorw("device-state-update-failed", log.Fields{"deviceId": device.Id, "error": err})
return
}
+
+ // Tell the Core that all child devices have been disabled (by default it's an action already taken by the Core
+ if err := dh.coreProxy.ChildDevicesLost(nil, cloned.Id); err != nil {
+ log.Errorw("lost-notif-of-child-devices-failed", log.Fields{"deviceId": device.Id, "error": err})
+ return
+ }
+
log.Debugw("DisableDevice-end", log.Fields{"deviceId": device.Id})
}
@@ -239,5 +246,23 @@
log.Errorw("device-state-update-failed", log.Fields{"deviceId": device.Id, "error": err})
return
}
+
+ // Tell the Core that all child devices have been enabled
+ if err := dh.coreProxy.ChildDevicesDetected(nil, cloned.Id); err != nil {
+ log.Errorw("detection-notif-of-child-devices-failed", log.Fields{"deviceId": device.Id, "error": err})
+ return
+ }
+
log.Debugw("ReEnableDevice-end", log.Fields{"deviceId": device.Id})
}
+
+func (dh *DeviceHandler) DeleteDevice(device *voltha.Device) {
+ cloned := proto.Clone(device).(*voltha.Device)
+ // Update the all ports state on that device to disable
+ if err := dh.coreProxy.DeleteAllPorts(nil, cloned.Id); err != nil {
+ log.Errorw("delete-ports-failed", log.Fields{"deviceId": device.Id, "error": err})
+ return
+ }
+
+ log.Debugw("DeleteDevice-end", log.Fields{"deviceId": device.Id})
+}
diff --git a/adapters/simulated_olt/adaptercore/simulated_olt.go b/adapters/simulated_olt/adaptercore/simulated_olt.go
index fee4dc8..d29fc1e 100644
--- a/adapters/simulated_olt/adaptercore/simulated_olt.go
+++ b/adapters/simulated_olt/adaptercore/simulated_olt.go
@@ -198,8 +198,17 @@
return errors.New("UnImplemented")
}
-func (so *SimulatedOLT) Gelete_device(device *voltha.Device) error {
- return errors.New("UnImplemented")
+func (so *SimulatedOLT) Delete_device(device *voltha.Device) error {
+ if device == nil {
+ log.Warn("device-is-nil")
+ return errors.New("nil-device")
+ }
+ log.Infow("delete-device", log.Fields{"deviceId": device.Id})
+ var handler *DeviceHandler
+ if handler = so.getDeviceHandler(device.Id); handler != nil {
+ go handler.DeleteDevice(device)
+ }
+ return nil
}
func (so *SimulatedOLT) Get_device_details(device *voltha.Device) error {
diff --git a/adapters/simulated_onu/adaptercore/device_handler.go b/adapters/simulated_onu/adaptercore/device_handler.go
index a845427..96cb3d8 100644
--- a/adapters/simulated_onu/adaptercore/device_handler.go
+++ b/adapters/simulated_onu/adaptercore/device_handler.go
@@ -27,6 +27,7 @@
"strconv"
"strings"
"sync"
+ "time"
)
//DeviceHandler follows the same patterns as ponsim_olt. The only difference is that it does not
@@ -105,6 +106,27 @@
log.Errorw("error-updating-device", log.Fields{"deviceId": device.Id, "error": err})
}
+ // Update the device state to DISCOVERED
+ cloned.ConnectStatus = voltha.ConnectStatus_REACHABLE
+ cloned.OperStatus = voltha.OperStatus_DISCOVERED
+ if err := dh.coreProxy.DeviceStateUpdate(nil, cloned.Id, cloned.ConnectStatus, cloned.OperStatus); err != nil {
+ log.Errorw("error-creating-nni-port", log.Fields{"deviceId": device.Id, "error": err})
+ }
+
+ // Sleep to mimic the omci management channel creation with the OLT
+ time.Sleep(10 * time.Millisecond)
+
+ // Update the device state to ACTIVATING
+ cloned.ConnectStatus = voltha.ConnectStatus_REACHABLE
+ cloned.OperStatus = voltha.OperStatus_ACTIVATING
+ if err := dh.coreProxy.DeviceStateUpdate(nil, cloned.Id, cloned.ConnectStatus, cloned.OperStatus); err != nil {
+ log.Errorw("error-creating-nni-port", log.Fields{"deviceId": device.Id, "error": err})
+ }
+
+ // Sleep to mimic the omci discovery ( usually takes a few seconds but for ease of simulated environment we are
+ // setting it to 100ms only.
+ time.Sleep(100 * time.Millisecond)
+
// Use the channel Id, assigned by the parent device to me, as the port number
uni_port := uint32(2)
if device.ProxyAddress != nil {
@@ -223,3 +245,14 @@
}
log.Debugw("ReEnableDevice-end", log.Fields{"deviceId": device.Id})
}
+
+func (dh *DeviceHandler) DeleteDevice(device *voltha.Device) {
+ cloned := proto.Clone(device).(*voltha.Device)
+ // Update the all ports state on that device to disable
+ if err := dh.coreProxy.DeleteAllPorts(nil, cloned.Id); err != nil {
+ log.Errorw("delete-ports-failed", log.Fields{"deviceId": device.Id, "error": err})
+ return
+ }
+
+ log.Debugw("DeleteDevice-end", log.Fields{"deviceId": device.Id})
+}
diff --git a/adapters/simulated_onu/adaptercore/simulated_onu.go b/adapters/simulated_onu/adaptercore/simulated_onu.go
index a74b377..1bdd4bb 100644
--- a/adapters/simulated_onu/adaptercore/simulated_onu.go
+++ b/adapters/simulated_onu/adaptercore/simulated_onu.go
@@ -190,8 +190,17 @@
return errors.New("UnImplemented")
}
-func (so *SimulatedONU) Gelete_device(device *voltha.Device) error {
- return errors.New("UnImplemented")
+func (so *SimulatedONU) Delete_device(device *voltha.Device) error {
+ if device == nil {
+ log.Warn("device-is-nil")
+ return errors.New("nil-device")
+ }
+ log.Infow("delete-device", log.Fields{"deviceId": device.Id})
+ var handler *DeviceHandler
+ if handler = so.getDeviceHandler(device.Id); handler != nil {
+ go handler.DeleteDevice(device)
+ }
+ return nil
}
func (so *SimulatedONU) Get_device_details(device *voltha.Device) error {
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