[VOL-5479] Implement IP Change RPC at Core and Trigger Towards OLT Adapter
Change-Id: I6fd056b127abe0c956c8e08a13829a23891af12a
Signed-off-by: mgouda <madhumati.gouda@radisys.com>
Signed-off-by: madhumatigouda <madhumati.gouda@radisys.com>
diff --git a/rw_core/core/device/agent.go b/rw_core/core/device/agent.go
index ac9b2fa..9631fee 100755
--- a/rw_core/core/device/agent.go
+++ b/rw_core/core/device/agent.go
@@ -55,6 +55,9 @@
var errReconcileAborted = errors.New("reconcile aborted")
var errContextExpired = errors.New("context expired")
var errNoConnection = errors.New("no connection")
+var errNoAddrChange = errors.New("no change in device address")
+var errAddrDuplicate = errors.New("ip address already in use")
+var errInvalidAddr = errors.New("invalid address type")
// Agent represents device agent attributes
type Agent struct {
@@ -2205,3 +2208,67 @@
}
return s
}
+
+// UpdateDevice updates the configuration of a device, such as changing the IP address of an OLT device.
+func (agent *Agent) updateDevice(ctx context.Context, config *voltha.UpdateDevice) error {
+ var desc string
+ var err error
+ requestStatus := &common.OperationResp{Code: common.OperationResp_OPERATION_FAILURE}
+ defer func() { agent.logDeviceUpdate(ctx, nil, nil, requestStatus, err, desc) }()
+
+ clonedDevice := agent.cloneDeviceWithoutLock()
+ if err = agent.requestQueue.WaitForGreenLight(ctx); err != nil {
+ return err
+ }
+
+ agent.UpdateAddress(ctx, clonedDevice, config)
+
+ logger.Infow(ctx, "update-device-ip-address", log.Fields{
+ "device-id": agent.deviceID,
+ "config": config,
+ })
+
+ if !agent.proceedWithRequest(clonedDevice) && clonedDevice.OperStatus != voltha.OperStatus_RECONCILING {
+ agent.requestQueue.RequestComplete()
+ err = status.Errorf(codes.FailedPrecondition, "cannot complete operation as device deletion is in progress/failed: %s", agent.deviceID)
+ return err
+ }
+
+ client, err := agent.adapterMgr.GetAdapterClient(ctx, agent.adapterEndpoint)
+ if err != nil {
+ agent.requestQueue.RequestComplete()
+ logger.Errorw(ctx, "grpc-client-nil",
+ log.Fields{
+ "error": err,
+ "device-id": agent.deviceID,
+ "device-type": agent.deviceType,
+ "adapter-endpoint": clonedDevice.AdapterEndpoint,
+ })
+ return err
+ }
+
+ subCtx, cancel := context.WithTimeout(coreutils.WithAllMetadataFromContext(ctx), agent.rpcTimeout)
+ defer cancel()
+ requestStatus.Code = common.OperationResp_OPERATION_IN_PROGRESS
+ logger.Infow(ctx, "sending-update-to-adapter", log.Fields{"device-id": agent.deviceID, "adapter-endpoint": clonedDevice.AdapterEndpoint, "config": config})
+ if _, err = client.UpdateDevice(subCtx, config); err == nil {
+ agent.onSuccess(subCtx, nil, nil, true)
+ } else {
+ logger.Errorw(ctx, "update-device-ip-failed", log.Fields{"device-id": agent.deviceID, "error": err})
+ agent.onFailure(subCtx, err, nil, nil, true)
+ agent.requestQueue.RequestComplete()
+ return err
+ }
+ return agent.updateDeviceAndReleaseLock(ctx, clonedDevice)
+}
+
+func (agent *Agent) UpdateAddress(ctx context.Context, device *voltha.Device, deviceConfig *voltha.UpdateDevice) {
+ switch addr := deviceConfig.Address.(type) {
+ case *voltha.UpdateDevice_Ipv4Address:
+ device.Address = &voltha.Device_Ipv4Address{Ipv4Address: addr.Ipv4Address}
+ case *voltha.UpdateDevice_Ipv6Address:
+ device.Address = &voltha.Device_Ipv6Address{Ipv6Address: addr.Ipv6Address}
+ case *voltha.UpdateDevice_HostAndPort:
+ device.Address = &voltha.Device_HostAndPort{HostAndPort: addr.HostAndPort}
+ }
+}
diff --git a/rw_core/core/device/manager.go b/rw_core/core/device/manager.go
index e39d822..6466744 100755
--- a/rw_core/core/device/manager.go
+++ b/rw_core/core/device/manager.go
@@ -898,3 +898,39 @@
}
return "", fmt.Errorf("ONU with serial %s not found under OLT %s", device.GetSerialNumber(), device.GetOltDeviceId().Id)
}
+
+func (dMgr *Manager) checkIPExists(ctx context.Context, config *voltha.UpdateDevice) error {
+
+ newType, newIP := utils.GetAddrTypeAndValue(config.Address)
+ if newType == "" {
+ return errInvalidAddr
+ }
+
+ for id := range dMgr.rootDevices {
+
+ agent := dMgr.getDeviceAgent(ctx, id)
+ if agent == nil || agent.device == nil {
+ continue
+ }
+
+ existingType, existingIP := utils.GetAddrTypeAndValue(agent.device.Address)
+ if existingType == "" || existingType != newType {
+ continue
+ }
+
+ if existingIP == newIP {
+
+ // Same device → no change
+ if agent.device.Id == config.Id {
+ logger.Debugw(ctx, "no-change-in-device-address", log.Fields{"device-id": agent.device.Id})
+ return errNoAddrChange
+ }
+
+ // Another root device → duplicate
+ logger.Debugw(ctx, "ip-address-already-used-by-another-device", log.Fields{"device-id": agent.device.Id, "type": newType, "address": newIP})
+ return errAddrDuplicate
+ }
+ }
+
+ return nil
+}
diff --git a/rw_core/core/device/manager_nbi.go b/rw_core/core/device/manager_nbi.go
index 2a8aeb8..866d6dc 100644
--- a/rw_core/core/device/manager_nbi.go
+++ b/rw_core/core/device/manager_nbi.go
@@ -1032,3 +1032,43 @@
return &empty.Empty{}, agent.enableOnuSerialNumber(ctx, device, oltAgent.adapterEndpoint)
}
+
+// UpdateDevice updates the configuration of a device, such as changing the IP address of an OLT device.
+func (dMgr *Manager) UpdateDevice(ctx context.Context, config *voltha.UpdateDevice) (*empty.Empty, error) {
+ ctx = utils.WithRPCMetadataContext(ctx, "UpdateDevice")
+ log.EnrichSpan(ctx, log.Fields{"device-id": config.Id})
+
+ logger.Infow(ctx, "update-ip-for-the-device", log.Fields{"device-id": config.Id, "config": config})
+
+ // Validate input
+ if config.Id == "" || config.Address == nil {
+ return nil, status.Error(codes.InvalidArgument, "missing device id or address")
+ }
+
+ // check if ip already exists
+ if err := dMgr.checkIPExists(ctx, config); err != nil {
+ switch {
+ case errors.Is(err, errNoAddrChange):
+ return nil, status.Error(codes.InvalidArgument, err.Error())
+ case errors.Is(err, errAddrDuplicate):
+ return nil, status.Error(codes.AlreadyExists, err.Error())
+ case errors.Is(err, errInvalidAddr):
+ return nil, status.Error(codes.InvalidArgument, err.Error())
+ default:
+ return nil, status.Error(codes.Internal, err.Error())
+ }
+ }
+
+ // Get the device agent
+ agent := dMgr.getDeviceAgent(ctx, config.Id)
+ if agent == nil {
+ return nil, status.Errorf(codes.NotFound, "device-%s", config.Id)
+ }
+
+ // Validate that this is a root device (typically OLT)
+ if !agent.isRootDevice {
+ return nil, status.Error(codes.InvalidArgument, "device-update-only-supported-for-olt-devices")
+ }
+
+ return &empty.Empty{}, agent.updateDevice(ctx, config)
+}
diff --git a/rw_core/mocks/adapter_olt.go b/rw_core/mocks/adapter_olt.go
index 9d4a4a6..eddaaf7 100644
--- a/rw_core/mocks/adapter_olt.go
+++ b/rw_core/mocks/adapter_olt.go
@@ -505,3 +505,9 @@
func (onuA *OLTAdapter) EnableOnuDevice(ctx context.Context, device *voltha.Device) (*empty.Empty, error) {
return &empty.Empty{}, nil
}
+
+func (onuA *OLTAdapter) UpdateDevice(ctx context.Context, req *voltha.UpdateDevice) (*empty.Empty, error) {
+ logger.Debugw(ctx, "UpdateDevice called", log.Fields{"device-config": req})
+ // You can add logic here to update the device in your mock if needed.
+ return &empty.Empty{}, nil
+}
diff --git a/rw_core/mocks/adapter_onu.go b/rw_core/mocks/adapter_onu.go
index c2e5a8f..610355e 100644
--- a/rw_core/mocks/adapter_onu.go
+++ b/rw_core/mocks/adapter_onu.go
@@ -354,3 +354,9 @@
func (onuA *ONUAdapter) EnableOnuDevice(ctx context.Context, device *voltha.Device) (*empty.Empty, error) {
return &empty.Empty{}, nil
}
+
+func (onuA *ONUAdapter) UpdateDevice(ctx context.Context, req *voltha.UpdateDevice) (*empty.Empty, error) {
+ logger.Debugw(ctx, "UpdateDevice called", log.Fields{"device-config": req})
+ // You can add logic here to update the device in your mock if needed.
+ return &empty.Empty{}, nil
+}
diff --git a/rw_core/utils/core_utils.go b/rw_core/utils/core_utils.go
index ce9fefa..24c380d 100644
--- a/rw_core/utils/core_utils.go
+++ b/rw_core/utils/core_utils.go
@@ -24,6 +24,7 @@
"time"
"github.com/opencord/voltha-lib-go/v7/pkg/log"
+ "github.com/opencord/voltha-protos/v5/go/voltha"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/peer"
"google.golang.org/grpc/status"
@@ -253,3 +254,24 @@
return 1
}
}
+
+func GetAddrTypeAndValue(addr interface{}) (string, string) {
+ switch a := addr.(type) {
+
+ case *voltha.UpdateDevice_Ipv4Address:
+ return "ipv4", a.Ipv4Address
+ case *voltha.UpdateDevice_Ipv6Address:
+ return "ipv6", a.Ipv6Address
+ case *voltha.UpdateDevice_HostAndPort:
+ return "hostport", a.HostAndPort
+
+ case *voltha.Device_Ipv4Address:
+ return "ipv4", a.Ipv4Address
+ case *voltha.Device_Ipv6Address:
+ return "ipv6", a.Ipv6Address
+ case *voltha.Device_HostAndPort:
+ return "hostport", a.HostAndPort
+ default:
+ return "", ""
+ }
+}