blob: 041f3945057645fd233b35de40adfe31ce80fdc3 [file] [log] [blame]
/*
* Copyright 2021-2023 Open Networking Foundation (ONF) and the ONF Contributors
* 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 device
import (
"context"
"errors"
"github.com/golang/protobuf/ptypes/empty"
"github.com/opencord/voltha-go/rw_core/utils"
"github.com/opencord/voltha-lib-go/v7/pkg/log"
"github.com/opencord/voltha-protos/v5/go/common"
"github.com/opencord/voltha-protos/v5/go/extension"
"github.com/opencord/voltha-protos/v5/go/omci"
ofp "github.com/opencord/voltha-protos/v5/go/openflow_13"
"github.com/opencord/voltha-protos/v5/go/voip_system_profile"
"github.com/opencord/voltha-protos/v5/go/voip_user_profile"
"github.com/opencord/voltha-protos/v5/go/voltha"
"google.golang.org/grpc/codes"
"google.golang.org/grpc/status"
)
// CreateDevice creates a new parent device in the data model
func (dMgr *Manager) CreateDevice(ctx context.Context, device *voltha.Device) (*voltha.Device, error) {
if device.MacAddress == "" && device.GetHostAndPort() == "" {
logger.Errorf(ctx, "no-device-info-present")
return &voltha.Device{}, errors.New("no-device-info-present; MAC or HOSTIP&PORT")
}
ctx = utils.WithRPCMetadataContext(ctx, "CreateDevice")
logger.Info(ctx, "create-device", log.Fields{"device": *device})
deviceExist, err := dMgr.isParentDeviceExist(ctx, device)
if err != nil {
logger.Errorf(ctx, "failed-to-fetch-parent-device-info")
return nil, err
}
if deviceExist {
logger.Errorf(ctx, "device-is-pre-provisioned-already-with-same-ip-port-or-mac-address")
return nil, errors.New("device is already pre-provisioned")
}
// Ensure this device is set as root
device.Root = true
// Create and start a device agent for that device
agent := newAgent(device, dMgr, dMgr.dbPath, dMgr.dProxy, dMgr.internalTimeout, dMgr.rpcTimeout, dMgr.flowTimeout)
device, err = agent.start(ctx, false, device)
if err != nil {
logger.Errorw(ctx, "fail-to-start-device", log.Fields{"device-id": agent.deviceID, "error": err})
return nil, err
}
dMgr.addDeviceAgentToMap(agent)
return device, nil
}
// EnableDevice activates a device by invoking the adopt_device API on the appropriate adapter
func (dMgr *Manager) EnableDevice(ctx context.Context, id *voltha.ID) (*empty.Empty, error) {
ctx = utils.WithRPCMetadataContext(ctx, "EnableDevice")
log.EnrichSpan(ctx, log.Fields{"device-id": id.Id})
logger.Info(ctx, "enable-device", log.Fields{"device-id": id.Id})
agent := dMgr.getDeviceAgent(ctx, id.Id)
if agent == nil {
return nil, status.Errorf(codes.NotFound, "%s", id.Id)
}
return &empty.Empty{}, agent.enableDevice(ctx)
}
// DisableDevice disables a device along with any child device it may have
func (dMgr *Manager) DisableDevice(ctx context.Context, id *voltha.ID) (*empty.Empty, error) {
ctx = utils.WithRPCMetadataContext(ctx, "DisableDevice")
log.EnrichSpan(ctx, log.Fields{"device-id": id.Id})
logger.Info(ctx, "disable-device", log.Fields{"device-id": id.Id})
agent := dMgr.getDeviceAgent(ctx, id.Id)
if agent == nil {
return nil, status.Errorf(codes.NotFound, "%s", id.Id)
}
return &empty.Empty{}, agent.disableDevice(ctx)
}
// RebootDevice invoked the reboot API to the corresponding adapter
func (dMgr *Manager) RebootDevice(ctx context.Context, id *voltha.ID) (*empty.Empty, error) {
ctx = utils.WithRPCMetadataContext(ctx, "RebootDevice")
log.EnrichSpan(ctx, log.Fields{"device-id": id.Id})
logger.Info(ctx, "reboot-device", log.Fields{"device-id": id.Id})
agent := dMgr.getDeviceAgent(ctx, id.Id)
if agent == nil {
return nil, status.Errorf(codes.NotFound, "%s", id.Id)
}
return &empty.Empty{}, agent.rebootDevice(ctx)
}
// DeleteDevice removes a device from the data model
func (dMgr *Manager) DeleteDevice(ctx context.Context, id *voltha.ID) (*empty.Empty, error) {
ctx = utils.WithRPCMetadataContext(ctx, "DeleteDevice")
log.EnrichSpan(ctx, log.Fields{"device-id": id.Id})
logger.Info(ctx, "delete-device", log.Fields{"device-id": id.Id})
agent := dMgr.getDeviceAgent(ctx, id.Id)
if agent == nil {
return nil, status.Errorf(codes.NotFound, "%s", id.Id)
}
return &empty.Empty{}, agent.deleteDevice(ctx)
}
// ForceDeleteDevice removes a device from the data model forcefully without successfully waiting for the adapters.
func (dMgr *Manager) ForceDeleteDevice(ctx context.Context, id *voltha.ID) (*empty.Empty, error) {
ctx = utils.WithRPCMetadataContext(ctx, "ForceDeleteDevice")
log.EnrichSpan(ctx, log.Fields{"device-id": id.Id})
logger.Info(ctx, "force-delete-device", log.Fields{"device-id": id.Id})
agent := dMgr.getDeviceAgent(ctx, id.Id)
if agent == nil {
return nil, status.Errorf(codes.NotFound, "%s", id.Id)
}
return &empty.Empty{}, agent.deleteDeviceForce(ctx)
}
// ListDevices retrieves the latest devices from the data model
func (dMgr *Manager) ListDevices(ctx context.Context, _ *empty.Empty) (*voltha.Devices, error) {
ctx = utils.WithRPCMetadataContext(ctx, "ListDevices")
logger.Debug(ctx, "list-devices")
result := &voltha.Devices{}
dMgr.deviceAgents.Range(func(key, value interface{}) bool {
result.Items = append(result.Items, value.(*Agent).device)
return true
})
logger.Debugw(ctx, "list-devices-end", log.Fields{"len": len(result.Items)})
return result, nil
}
// ListDeviceIds retrieves the latest device IDs information from the data model (memory data only)
func (dMgr *Manager) ListDeviceIds(ctx context.Context, _ *empty.Empty) (*voltha.IDs, error) {
ctx = utils.WithRPCMetadataContext(ctx, "ListDeviceIds")
logger.Debug(ctx, "list-device-ids")
// Report only device IDs that are in the device agent map
return dMgr.listDeviceIdsFromMap(), nil
}
// ReconcileDevices is a request to a voltha core to update its list of managed devices. This will
// trigger loading the devices along with their children and parent in memory
func (dMgr *Manager) ReconcileDevices(ctx context.Context, ids *voltha.IDs) (*empty.Empty, error) {
ctx = utils.WithRPCMetadataContext(ctx, "ReconcileDevices")
numDevices := 0
if ids != nil {
numDevices = len(ids.Items)
}
logger.Info(ctx, "reconcile-devices", log.Fields{"num-devices": numDevices})
if ids != nil && len(ids.Items) != 0 {
toReconcile := len(ids.Items)
reconciled := 0
var err error
for _, id := range ids.Items {
if err = dMgr.load(ctx, id.Id); err != nil {
logger.Warnw(ctx, "failure-reconciling-device", log.Fields{"device-id": id.Id, "error": err})
} else {
reconciled++
}
}
if toReconcile != reconciled {
return nil, status.Errorf(codes.DataLoss, "less-device-reconciled-than-requested:%d/%d", reconciled, toReconcile)
}
} else {
return nil, status.Errorf(codes.InvalidArgument, "empty-list-of-ids")
}
return &empty.Empty{}, nil
}
// GetDevice exists primarily to implement the gRPC interface.
// Internal functions should call getDeviceReadOnly instead.
func (dMgr *Manager) GetDevice(ctx context.Context, id *voltha.ID) (*voltha.Device, error) {
ctx = utils.WithRPCMetadataContext(ctx, "GetDevice")
log.EnrichSpan(ctx, log.Fields{"device-id": id.Id})
return dMgr.getDeviceReadOnly(ctx, id.Id)
}
// convenience to avoid redefining
var operationFailureResp = &common.OperationResp{Code: voltha.OperationResp_OPERATION_FAILURE}
// DownloadImage execute an image download request
func (dMgr *Manager) DownloadImage(ctx context.Context, img *voltha.ImageDownload) (*common.OperationResp, error) {
ctx = utils.WithRPCMetadataContext(ctx, "DownloadImage")
log.EnrichSpan(ctx, log.Fields{"device-id": img.Id})
logger.Info(ctx, "download-image", log.Fields{"device-id": img.Id, "image-name": img.Name})
agent := dMgr.getDeviceAgent(ctx, img.Id)
if agent == nil {
return operationFailureResp, status.Errorf(codes.NotFound, "%s", img.Id)
}
resp, err := agent.downloadImage(ctx, img)
if err != nil {
return operationFailureResp, err
}
return resp, nil
}
// CancelImageDownload cancels image download request
func (dMgr *Manager) CancelImageDownload(ctx context.Context, img *voltha.ImageDownload) (*common.OperationResp, error) {
ctx = utils.WithRPCMetadataContext(ctx, "CancelImageDownload")
log.EnrichSpan(ctx, log.Fields{"device-id": img.Id})
logger.Info(ctx, "cancel-image-download", log.Fields{"device-id": img.Id, "image-name": img.Name})
agent := dMgr.getDeviceAgent(ctx, img.Id)
if agent == nil {
return operationFailureResp, status.Errorf(codes.NotFound, "%s", img.Id)
}
resp, err := agent.cancelImageDownload(ctx, img)
if err != nil {
return operationFailureResp, err
}
return resp, nil
}
// ActivateImageUpdate activates image update request
func (dMgr *Manager) ActivateImageUpdate(ctx context.Context, img *voltha.ImageDownload) (*common.OperationResp, error) {
ctx = utils.WithRPCMetadataContext(ctx, "ActivateImageUpdate")
log.EnrichSpan(ctx, log.Fields{"device-id": img.Id})
logger.Info(ctx, "activate-image-update", log.Fields{"device-id": img.Id, "image-name": img.Name})
agent := dMgr.getDeviceAgent(ctx, img.Id)
if agent == nil {
return operationFailureResp, status.Errorf(codes.NotFound, "%s", img.Id)
}
resp, err := agent.activateImage(ctx, img)
if err != nil {
return operationFailureResp, err
}
return resp, nil
}
// RevertImageUpdate reverts image update
func (dMgr *Manager) RevertImageUpdate(ctx context.Context, img *voltha.ImageDownload) (*common.OperationResp, error) {
ctx = utils.WithRPCMetadataContext(ctx, "RevertImageUpdate")
log.EnrichSpan(ctx, log.Fields{"device-id": img.Id})
logger.Info(ctx, "rever-image-update", log.Fields{"device-id": img.Id, "image-name": img.Name})
agent := dMgr.getDeviceAgent(ctx, img.Id)
if agent == nil {
return operationFailureResp, status.Errorf(codes.NotFound, "%s", img.Id)
}
resp, err := agent.revertImage(ctx, img)
if err != nil {
return operationFailureResp, err
}
return resp, nil
}
func (dMgr *Manager) DownloadImageToDevice(ctx context.Context, request *voltha.DeviceImageDownloadRequest) (*voltha.DeviceImageResponse, error) {
if err := dMgr.validateImageDownloadRequest(request); err != nil {
return nil, err
}
ctx = utils.WithRPCMetadataContext(ctx, "DownloadImageToDevice")
respCh := make(chan []*voltha.DeviceImageState, len(request.GetDeviceId()))
for index, deviceID := range request.DeviceId {
// Create download request per device
downloadReq := &voltha.DeviceImageDownloadRequest{
Image: request.Image,
ActivateOnSuccess: request.ActivateOnSuccess,
CommitOnSuccess: request.CommitOnSuccess,
}
//slice-out only single deviceID from the request
downloadReq.DeviceId = request.DeviceId[index : index+1]
go func(deviceID string, req *voltha.DeviceImageDownloadRequest, ch chan []*voltha.DeviceImageState) {
agent := dMgr.getDeviceAgent(ctx, deviceID)
if agent == nil {
logger.Errorw(ctx, "Device-agent-not-found", log.Fields{"device-id": deviceID})
ch <- []*voltha.DeviceImageState{{
DeviceId: deviceID,
ImageState: &voltha.ImageState{
Version: req.GetImage().GetVersion(),
DownloadState: voltha.ImageState_DOWNLOAD_FAILED,
Reason: voltha.ImageState_UNKNOWN_ERROR,
ImageState: voltha.ImageState_IMAGE_UNKNOWN,
},
}}
return
}
resp, err := agent.downloadImageToDevice(ctx, req)
if err != nil {
logger.Errorw(ctx, "download-image-to-device-failed", log.Fields{"device-id": deviceID, "error": err})
ch <- []*voltha.DeviceImageState{{
DeviceId: deviceID,
ImageState: &voltha.ImageState{
Version: req.GetImage().GetVersion(),
DownloadState: voltha.ImageState_DOWNLOAD_FAILED,
Reason: voltha.ImageState_UNKNOWN_ERROR,
ImageState: voltha.ImageState_IMAGE_UNKNOWN,
},
}}
return
}
err = dMgr.validateDeviceImageResponse(resp)
if err != nil {
logger.Errorw(ctx, "download-image-to-device-failed", log.Fields{"device-id": deviceID, "error": err})
ch <- []*voltha.DeviceImageState{{
DeviceId: deviceID,
ImageState: &voltha.ImageState{
Version: req.GetImage().GetVersion(),
DownloadState: voltha.ImageState_DOWNLOAD_FAILED,
Reason: voltha.ImageState_UNKNOWN_ERROR,
ImageState: voltha.ImageState_IMAGE_UNKNOWN,
},
}}
return
}
ch <- resp.GetDeviceImageStates()
}(deviceID.GetId(), downloadReq, respCh)
}
return dMgr.waitForAllResponses(ctx, "download-image-to-device", respCh, len(request.GetDeviceId()))
}
func (dMgr *Manager) GetImageStatus(ctx context.Context, request *voltha.DeviceImageRequest) (*voltha.DeviceImageResponse, error) {
if err := dMgr.validateImageRequest(request); err != nil {
return nil, err
}
ctx = utils.WithRPCMetadataContext(ctx, "GetImageStatus")
respCh := make(chan []*voltha.DeviceImageState, len(request.GetDeviceId()))
if request.DeviceId == nil {
//Reply for every ONU
dMgr.deviceAgents.Range(func(key, value interface{}) bool {
device := value.(*Agent).device
if !device.Root {
request.DeviceId = append(request.DeviceId, &common.ID{Id: value.(*Agent).device.Id})
}
return true
})
}
for index, deviceID := range request.DeviceId {
// Create status request per device
imageStatusReq := &voltha.DeviceImageRequest{
Version: request.Version,
CommitOnSuccess: request.CommitOnSuccess,
}
//slice-out only single deviceID from the request
imageStatusReq.DeviceId = request.DeviceId[index : index+1]
go func(deviceID string, req *voltha.DeviceImageRequest, ch chan []*voltha.DeviceImageState) {
agent := dMgr.getDeviceAgent(ctx, deviceID)
if agent == nil {
logger.Errorw(ctx, "Device-agent-not-found", log.Fields{"device-id": deviceID})
ch <- []*voltha.DeviceImageState{{
DeviceId: deviceID,
ImageState: &voltha.ImageState{
Version: request.GetVersion(),
DownloadState: voltha.ImageState_DOWNLOAD_UNKNOWN,
Reason: voltha.ImageState_UNKNOWN_ERROR,
ImageState: voltha.ImageState_IMAGE_UNKNOWN,
},
}}
return
}
resp, err := agent.getImageStatus(ctx, req)
if err != nil {
logger.Errorw(ctx, "get-image-status-failed", log.Fields{"device-id": deviceID, "error": err})
ch <- []*voltha.DeviceImageState{{
DeviceId: deviceID,
ImageState: &voltha.ImageState{
Version: request.GetVersion(),
DownloadState: voltha.ImageState_DOWNLOAD_UNKNOWN,
Reason: voltha.ImageState_UNKNOWN_ERROR,
ImageState: voltha.ImageState_IMAGE_UNKNOWN,
},
}}
return
}
err = dMgr.validateDeviceImageResponse(resp)
if err != nil {
logger.Errorw(ctx, "get-image-status-failed", log.Fields{"device-id": deviceID, "error": err})
ch <- []*voltha.DeviceImageState{{
DeviceId: deviceID,
ImageState: &voltha.ImageState{
Version: request.GetVersion(),
DownloadState: voltha.ImageState_DOWNLOAD_UNKNOWN,
Reason: voltha.ImageState_UNKNOWN_ERROR,
ImageState: voltha.ImageState_IMAGE_UNKNOWN,
},
}}
return
}
ch <- resp.GetDeviceImageStates()
}(deviceID.GetId(), imageStatusReq, respCh)
}
return dMgr.waitForAllResponses(ctx, "get-image-status", respCh, len(request.GetDeviceId()))
}
func (dMgr *Manager) AbortImageUpgradeToDevice(ctx context.Context, request *voltha.DeviceImageRequest) (*voltha.DeviceImageResponse, error) {
if err := dMgr.validateImageRequest(request); err != nil {
return nil, err
}
ctx = utils.WithRPCMetadataContext(ctx, "AbortImageUpgradeToDevice")
respCh := make(chan []*voltha.DeviceImageState, len(request.GetDeviceId()))
for index, deviceID := range request.DeviceId {
// Create abort request per device
abortImageReq := &voltha.DeviceImageRequest{
Version: request.Version,
CommitOnSuccess: request.CommitOnSuccess,
}
//slice-out only single deviceID from the request
abortImageReq.DeviceId = request.DeviceId[index : index+1]
go func(deviceID string, req *voltha.DeviceImageRequest, ch chan []*voltha.DeviceImageState) {
agent := dMgr.getDeviceAgent(ctx, deviceID)
if agent == nil {
logger.Errorw(ctx, "Device-agent-not-found", log.Fields{"device-id": deviceID})
ch <- []*voltha.DeviceImageState{{
DeviceId: deviceID,
ImageState: &voltha.ImageState{
Version: request.GetVersion(),
DownloadState: voltha.ImageState_DOWNLOAD_UNKNOWN,
Reason: voltha.ImageState_UNKNOWN_ERROR,
ImageState: voltha.ImageState_IMAGE_UNKNOWN,
},
}}
return
}
resp, err := agent.abortImageUpgradeToDevice(ctx, req)
if err != nil {
logger.Errorw(ctx, "abort-image-upgrade-to-device-failed", log.Fields{"device-id": deviceID, "error": err})
ch <- []*voltha.DeviceImageState{{
DeviceId: deviceID,
ImageState: &voltha.ImageState{
Version: request.GetVersion(),
DownloadState: voltha.ImageState_DOWNLOAD_UNKNOWN,
Reason: voltha.ImageState_UNKNOWN_ERROR,
ImageState: voltha.ImageState_IMAGE_UNKNOWN,
},
}}
return
}
err = dMgr.validateDeviceImageResponse(resp)
if err != nil {
logger.Errorw(ctx, "abort-image-upgrade-to-device-failed", log.Fields{"device-id": deviceID, "error": err})
ch <- []*voltha.DeviceImageState{{
DeviceId: deviceID,
ImageState: &voltha.ImageState{
Version: request.GetVersion(),
DownloadState: voltha.ImageState_DOWNLOAD_UNKNOWN,
Reason: voltha.ImageState_UNKNOWN_ERROR,
ImageState: voltha.ImageState_IMAGE_UNKNOWN,
},
}}
return
}
ch <- resp.GetDeviceImageStates()
}(deviceID.GetId(), abortImageReq, respCh)
}
return dMgr.waitForAllResponses(ctx, "abort-image-upgrade-to-device", respCh, len(request.GetDeviceId()))
}
func (dMgr *Manager) GetOnuImages(ctx context.Context, id *common.ID) (*voltha.OnuImages, error) {
if id == nil || id.Id == "" {
return nil, status.Errorf(codes.InvalidArgument, "empty device id")
}
ctx = utils.WithRPCMetadataContext(ctx, "GetOnuImages")
log.EnrichSpan(ctx, log.Fields{"device-id": id.Id})
agent := dMgr.getDeviceAgent(ctx, id.Id)
if agent == nil {
return nil, status.Errorf(codes.NotFound, "%s", id.Id)
}
resp, err := agent.getOnuImages(ctx, id)
if err != nil {
return nil, err
}
logger.Debugw(ctx, "get-onu-images-result", log.Fields{"onu-image": resp.Items})
return resp, nil
}
func (dMgr *Manager) ActivateImage(ctx context.Context, request *voltha.DeviceImageRequest) (*voltha.DeviceImageResponse, error) {
if err := dMgr.validateImageRequest(request); err != nil {
return nil, err
}
ctx = utils.WithRPCMetadataContext(ctx, "ActivateImage")
respCh := make(chan []*voltha.DeviceImageState, len(request.GetDeviceId()))
for index, deviceID := range request.DeviceId {
// Create activate request per device
activateImageReq := &voltha.DeviceImageRequest{
Version: request.Version,
CommitOnSuccess: request.CommitOnSuccess,
}
//slice-out only single deviceID from the request
activateImageReq.DeviceId = request.DeviceId[index : index+1]
go func(deviceID string, req *voltha.DeviceImageRequest, ch chan []*voltha.DeviceImageState) {
agent := dMgr.getDeviceAgent(ctx, deviceID)
if agent == nil {
logger.Errorw(ctx, "Device-agent-not-found", log.Fields{"device-id": deviceID})
ch <- []*voltha.DeviceImageState{{
DeviceId: deviceID,
ImageState: &voltha.ImageState{
Version: request.GetVersion(),
DownloadState: voltha.ImageState_DOWNLOAD_UNKNOWN,
Reason: voltha.ImageState_UNKNOWN_ERROR,
ImageState: voltha.ImageState_IMAGE_ACTIVATION_ABORTED,
},
}}
return
}
resp, err := agent.activateImageOnDevice(ctx, req)
if err != nil {
logger.Errorw(ctx, "activate-image-failed", log.Fields{"device-id": deviceID, "error": err})
ch <- []*voltha.DeviceImageState{{
DeviceId: deviceID,
ImageState: &voltha.ImageState{
Version: request.GetVersion(),
DownloadState: voltha.ImageState_DOWNLOAD_UNKNOWN,
Reason: voltha.ImageState_UNKNOWN_ERROR,
ImageState: voltha.ImageState_IMAGE_ACTIVATION_ABORTED,
},
}}
return
}
err = dMgr.validateDeviceImageResponse(resp)
if err != nil {
logger.Errorw(ctx, "activate-image-failed", log.Fields{"device-id": deviceID, "error": err})
ch <- []*voltha.DeviceImageState{{
DeviceId: deviceID,
ImageState: &voltha.ImageState{
Version: request.GetVersion(),
DownloadState: voltha.ImageState_DOWNLOAD_UNKNOWN,
Reason: voltha.ImageState_UNKNOWN_ERROR,
ImageState: voltha.ImageState_IMAGE_UNKNOWN,
},
}}
return
}
ch <- resp.GetDeviceImageStates()
}(deviceID.GetId(), activateImageReq, respCh)
}
return dMgr.waitForAllResponses(ctx, "activate-image", respCh, len(request.GetDeviceId()))
}
func (dMgr *Manager) CommitImage(ctx context.Context, request *voltha.DeviceImageRequest) (*voltha.DeviceImageResponse, error) {
if err := dMgr.validateImageRequest(request); err != nil {
return nil, err
}
ctx = utils.WithRPCMetadataContext(ctx, "CommitImage")
respCh := make(chan []*voltha.DeviceImageState, len(request.GetDeviceId()))
for index, deviceID := range request.DeviceId {
// Create commit request per device
commitImageReq := &voltha.DeviceImageRequest{
Version: request.Version,
CommitOnSuccess: request.CommitOnSuccess,
}
//slice-out only single deviceID from the request
commitImageReq.DeviceId = request.DeviceId[index : index+1]
go func(deviceID string, req *voltha.DeviceImageRequest, ch chan []*voltha.DeviceImageState) {
agent := dMgr.getDeviceAgent(ctx, deviceID)
if agent == nil {
logger.Errorw(ctx, "Device-agent-not-found", log.Fields{"device-id": deviceID})
ch <- []*voltha.DeviceImageState{{
DeviceId: deviceID,
ImageState: &voltha.ImageState{
Version: request.GetVersion(),
DownloadState: voltha.ImageState_DOWNLOAD_UNKNOWN,
Reason: voltha.ImageState_UNKNOWN_ERROR,
ImageState: voltha.ImageState_IMAGE_COMMIT_ABORTED,
},
}}
return
}
resp, err := agent.commitImage(ctx, req)
if err != nil {
logger.Errorw(ctx, "commit-image-failed", log.Fields{"device-id": deviceID, "error": err})
ch <- []*voltha.DeviceImageState{{
DeviceId: deviceID,
ImageState: &voltha.ImageState{
Version: request.GetVersion(),
DownloadState: voltha.ImageState_DOWNLOAD_UNKNOWN,
Reason: voltha.ImageState_UNKNOWN_ERROR,
ImageState: voltha.ImageState_IMAGE_COMMIT_ABORTED,
},
}}
return
}
err = dMgr.validateDeviceImageResponse(resp)
if err != nil {
logger.Errorf(ctx, "commit-image-failed", log.Fields{"device-id": deviceID, "error": err})
ch <- []*voltha.DeviceImageState{{
DeviceId: deviceID,
ImageState: &voltha.ImageState{
Version: request.GetVersion(),
DownloadState: voltha.ImageState_DOWNLOAD_UNKNOWN,
Reason: voltha.ImageState_UNKNOWN_ERROR,
ImageState: voltha.ImageState_IMAGE_UNKNOWN,
},
}}
return
}
ch <- resp.GetDeviceImageStates()
}(deviceID.GetId(), commitImageReq, respCh)
}
return dMgr.waitForAllResponses(ctx, "commit-image", respCh, len(request.GetDeviceId()))
}
// convenience to avoid redefining
var imageDownloadFailureResp = &voltha.ImageDownload{DownloadState: voltha.ImageDownload_DOWNLOAD_UNKNOWN}
// GetImageDownloadStatus returns status of image download
func (dMgr *Manager) GetImageDownloadStatus(ctx context.Context, img *voltha.ImageDownload) (*voltha.ImageDownload, error) {
ctx = utils.WithRPCMetadataContext(ctx, "GetImageDownloadStatus")
log.EnrichSpan(ctx, log.Fields{"device-id": img.Id})
logger.Info(ctx, "get-image-download-status", log.Fields{"device-id": img.Id, "image-name": img.Name})
agent := dMgr.getDeviceAgent(ctx, img.Id)
if agent == nil {
return imageDownloadFailureResp, status.Errorf(codes.NotFound, "%s", img.Id)
}
resp, err := agent.getImageDownloadStatus(ctx, img)
if err != nil {
return imageDownloadFailureResp, err
}
return resp, nil
}
// GetImageDownload returns image download
func (dMgr *Manager) GetImageDownload(ctx context.Context, img *voltha.ImageDownload) (*voltha.ImageDownload, error) {
ctx = utils.WithRPCMetadataContext(ctx, "GetImageDownload")
log.EnrichSpan(ctx, log.Fields{"device-id": img.Id})
logger.Info(ctx, "get-image-download", log.Fields{"device-id": img.Id, "image-name": img.Name})
agent := dMgr.getDeviceAgent(ctx, img.Id)
if agent == nil {
return imageDownloadFailureResp, status.Errorf(codes.NotFound, "%s", img.Id)
}
resp, err := agent.getImageDownload(ctx, img)
if err != nil {
return imageDownloadFailureResp, err
}
return resp, nil
}
// ListImageDownloads returns image downloads
func (dMgr *Manager) ListImageDownloads(ctx context.Context, id *voltha.ID) (*voltha.ImageDownloads, error) {
ctx = utils.WithRPCMetadataContext(ctx, "ListImageDownloads")
log.EnrichSpan(ctx, log.Fields{"device-id": id.Id})
logger.Debugw(ctx, "list-image-downloads", log.Fields{"device-id": id.Id})
agent := dMgr.getDeviceAgent(ctx, id.Id)
if agent == nil {
return &voltha.ImageDownloads{Items: []*voltha.ImageDownload{imageDownloadFailureResp}}, status.Errorf(codes.NotFound, "%s", id.Id)
}
resp, err := agent.listImageDownloads(ctx, id.Id)
if err != nil {
return &voltha.ImageDownloads{Items: []*voltha.ImageDownload{imageDownloadFailureResp}}, err
}
return resp, nil
}
// GetImages returns all images for a specific device entry
func (dMgr *Manager) GetImages(ctx context.Context, id *voltha.ID) (*voltha.Images, error) {
ctx = utils.WithRPCMetadataContext(ctx, "GetImages")
log.EnrichSpan(ctx, log.Fields{"device-id": id.Id})
logger.Info(ctx, "get-images", log.Fields{"device-id": id.Id})
device, err := dMgr.getDeviceReadOnly(ctx, id.Id)
if err != nil {
return nil, err
}
return device.Images, nil
}
// ListDevicePorts returns the ports details for a specific device entry
func (dMgr *Manager) ListDevicePorts(ctx context.Context, id *voltha.ID) (*voltha.Ports, error) {
ctx = utils.WithRPCMetadataContext(ctx, "ListDevicePorts")
log.EnrichSpan(ctx, log.Fields{"device-id": id.Id})
logger.Debugw(ctx, "list-device-ports", log.Fields{"device-id": id.Id})
agent := dMgr.getDeviceAgent(ctx, id.Id)
if agent == nil {
return nil, status.Errorf(codes.NotFound, "device-%s", id.Id)
}
ports := agent.listDevicePorts()
ctr, ret := 0, make([]*voltha.Port, len(ports))
for _, port := range ports {
ret[ctr] = port
ctr++
}
return &voltha.Ports{Items: ret}, nil
}
// ListDevicePmConfigs returns pm configs of device
func (dMgr *Manager) ListDevicePmConfigs(ctx context.Context, id *voltha.ID) (*voltha.PmConfigs, error) {
ctx = utils.WithRPCMetadataContext(ctx, "ListDevicePmConfigs")
log.EnrichSpan(ctx, log.Fields{"device-id": id.Id})
agent := dMgr.getDeviceAgent(ctx, id.Id)
if agent == nil {
return nil, status.Errorf(codes.NotFound, "%s", id.Id)
}
return agent.listPmConfigs(ctx)
}
// UpdateDevicePmConfigs updates the PM configs. This is executed when the northbound gRPC API is invoked, typically
// following a user action
func (dMgr *Manager) UpdateDevicePmConfigs(ctx context.Context, configs *voltha.PmConfigs) (*empty.Empty, error) {
ctx = utils.WithRPCMetadataContext(ctx, "UpdateDevicePmConfigs")
log.EnrichSpan(ctx, log.Fields{"device-id": configs.Id})
if configs.Id == "" {
return nil, status.Error(codes.FailedPrecondition, "invalid-device-Id")
}
agent := dMgr.getDeviceAgent(ctx, configs.Id)
if agent == nil {
return nil, status.Errorf(codes.NotFound, "%s", configs.Id)
}
return &empty.Empty{}, agent.updatePmConfigs(ctx, configs)
}
// ListDeviceFlows returns the flow details for a specific device entry
func (dMgr *Manager) ListDeviceFlows(ctx context.Context, id *voltha.ID) (*ofp.Flows, error) {
ctx = utils.WithRPCMetadataContext(ctx, "ListDeviceFlows")
log.EnrichSpan(ctx, log.Fields{"device-id": id.Id})
logger.Debugw(ctx, "list-device-flows", log.Fields{"device-id": id.Id})
agent := dMgr.getDeviceAgent(ctx, id.Id)
if agent == nil {
return nil, status.Errorf(codes.NotFound, "device-%s", id.Id)
}
flows := agent.listDeviceFlows()
ctr, ret := 0, make([]*ofp.OfpFlowStats, len(flows))
for _, flow := range flows {
ret[ctr] = flow
ctr++
}
return &ofp.Flows{Items: ret}, nil
}
// ListDeviceFlowGroups returns the flow group details for a specific device entry
func (dMgr *Manager) ListDeviceFlowGroups(ctx context.Context, id *voltha.ID) (*ofp.FlowGroups, error) {
ctx = utils.WithRPCMetadataContext(ctx, "ListDeviceFlowGroups")
log.EnrichSpan(ctx, log.Fields{"device-id": id.Id})
logger.Debugw(ctx, "list-device-flow-groups", log.Fields{"device-id": id.Id})
agent := dMgr.getDeviceAgent(ctx, id.Id)
if agent == nil {
return nil, status.Errorf(codes.NotFound, "device-%s", id.Id)
}
groups := agent.listDeviceGroups()
ctr, ret := 0, make([]*ofp.OfpGroupEntry, len(groups))
for _, group := range groups {
ret[ctr] = group
ctr++
}
return &ofp.FlowGroups{Items: ret}, nil
}
func (dMgr *Manager) EnablePort(ctx context.Context, port *voltha.Port) (*empty.Empty, error) {
ctx = utils.WithRPCMetadataContext(ctx, "EnablePort")
log.EnrichSpan(ctx, log.Fields{"device-id": port.DeviceId})
logger.Info(ctx, "enable-port", log.Fields{"device-id": port.DeviceId, "port-no": port.PortNo})
agent := dMgr.getDeviceAgent(ctx, port.DeviceId)
if agent == nil {
return nil, status.Errorf(codes.NotFound, "%s", port.DeviceId)
}
return &empty.Empty{}, agent.enablePort(ctx, port.PortNo)
}
func (dMgr *Manager) DisablePort(ctx context.Context, port *voltha.Port) (*empty.Empty, error) {
ctx = utils.WithRPCMetadataContext(ctx, "DisablePort")
log.EnrichSpan(ctx, log.Fields{"device-id": port.DeviceId})
logger.Info(ctx, "disable-port", log.Fields{"device-id": port.DeviceId, "port-no": port.PortNo})
agent := dMgr.getDeviceAgent(ctx, port.DeviceId)
if agent == nil {
return nil, status.Errorf(codes.NotFound, "%s", port.DeviceId)
}
return &empty.Empty{}, agent.disablePort(ctx, port.PortNo)
}
func (dMgr *Manager) GetExtValue(ctx context.Context, value *extension.ValueSpecifier) (*extension.ReturnValues, error) {
ctx = utils.WithRPCMetadataContext(ctx, "GetExtValue")
log.EnrichSpan(ctx, log.Fields{"device-id": value.Id})
logger.Info(ctx, "get-ext-value", log.Fields{"onu-id": value.Id})
cDevice, err := dMgr.getDeviceReadOnly(ctx, value.Id)
if err != nil {
return nil, status.Errorf(codes.Aborted, "%s", err.Error())
}
pDevice, err := dMgr.getDeviceReadOnly(ctx, cDevice.ParentId)
if err != nil {
return nil, status.Errorf(codes.Aborted, "%s", err.Error())
}
if agent := dMgr.getDeviceAgent(ctx, cDevice.ParentId); agent != nil {
resp, err := agent.getExtValue(ctx, pDevice, cDevice, value)
if err != nil {
return nil, err
}
logger.Info(ctx, "get-ext-value-result", log.Fields{"result": resp})
return resp, nil
}
return nil, status.Errorf(codes.NotFound, "%s", value.Id)
}
// SetExtValue set some given configs or value
func (dMgr *Manager) SetExtValue(ctx context.Context, value *extension.ValueSet) (*empty.Empty, error) {
ctx = utils.WithRPCMetadataContext(ctx, "SetExtValue")
logger.Info(ctx, "set-ext-value", log.Fields{"onu-id": value.Id})
device, err := dMgr.getDeviceReadOnly(ctx, value.Id)
if err != nil {
return nil, status.Errorf(codes.Aborted, "%s", err.Error())
}
if agent := dMgr.getDeviceAgent(ctx, device.Id); agent != nil {
resp, err := agent.setExtValue(ctx, device, value)
if err != nil {
return nil, err
}
logger.Info(ctx, "set-ext-value-result", log.Fields{"result": resp})
return resp, nil
}
return nil, status.Errorf(codes.NotFound, "%s", value.Id)
}
func (dMgr *Manager) StartOmciTestAction(ctx context.Context, request *omci.OmciTestRequest) (*omci.TestResponse, error) {
ctx = utils.WithRPCMetadataContext(ctx, "StartOmciTestAction")
log.EnrichSpan(ctx, log.Fields{"device-id": request.Id})
logger.Debugw(ctx, "start-omci-test-action", log.Fields{"device-id": request.Id, "uuid": request.Uuid})
agent := dMgr.getDeviceAgent(ctx, request.Id)
if agent == nil {
return nil, status.Errorf(codes.NotFound, "%s", request.Id)
}
return agent.startOmciTest(ctx, request)
}
func (dMgr *Manager) SimulateAlarm(ctx context.Context, simulateReq *voltha.SimulateAlarmRequest) (*common.OperationResp, error) {
ctx = utils.WithRPCMetadataContext(ctx, "SimulateAlarm")
logger.Debugw(ctx, "simulate-alarm", log.Fields{"id": simulateReq.Id, "indicator": simulateReq.Indicator, "intf-id": simulateReq.IntfId,
"port-type-name": simulateReq.PortTypeName, "onu-device-id": simulateReq.OnuDeviceId, "inverse-bit-error-rate": simulateReq.InverseBitErrorRate,
"drift": simulateReq.Drift, "new-eqd": simulateReq.NewEqd, "onu-serial-number": simulateReq.OnuSerialNumber, "operation": simulateReq.Operation})
agent := dMgr.getDeviceAgent(ctx, simulateReq.Id)
if agent == nil {
return nil, status.Errorf(codes.NotFound, "%s", simulateReq.Id)
}
if err := agent.simulateAlarm(ctx, simulateReq); err != nil {
return nil, err
}
return &common.OperationResp{Code: common.OperationResp_OPERATION_SUCCESS}, nil
}
func (dMgr *Manager) PutVoipUserProfile(ctx context.Context, voipUserProfileRequest *voip_user_profile.VoipUserProfileRequest) (*empty.Empty, error) {
return nil, status.Error(codes.Unimplemented, "put-voip-user-profile-not-implemented")
}
func (dMgr *Manager) DeleteVoipUserProfile(ctx context.Context, key *common.Key) (*empty.Empty, error) {
return nil, status.Error(codes.Unimplemented, "delete-voip-user-profile-not-implemented")
}
func (dMgr *Manager) PutVoipSystemProfile(ctx context.Context, voipSystemProfileRequest *voip_system_profile.VoipSystemProfileRequest) (*empty.Empty, error) {
return nil, status.Error(codes.Unimplemented, "put-voip-system-profile-not-implemented")
}
func (dMgr *Manager) DeleteVoipSystemProfile(ctx context.Context, key *common.Key) (*empty.Empty, error) {
return nil, status.Error(codes.Unimplemented, "delete-voip-system-profile-not-implemented")
}