| /* |
| * Copyright 2019-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 mocks |
| |
| import ( |
| "context" |
| "fmt" |
| "strconv" |
| "strings" |
| "sync" |
| "time" |
| |
| "github.com/golang/protobuf/ptypes/empty" |
| vgrpc "github.com/opencord/voltha-lib-go/v7/pkg/grpc" |
| "github.com/opencord/voltha-lib-go/v7/pkg/log" |
| "github.com/opencord/voltha-lib-go/v7/pkg/probe" |
| "github.com/opencord/voltha-protos/v5/go/adapter_service" |
| "github.com/opencord/voltha-protos/v5/go/common" |
| "github.com/opencord/voltha-protos/v5/go/core_service" |
| "github.com/opencord/voltha-protos/v5/go/health" |
| "google.golang.org/grpc" |
| |
| ca "github.com/opencord/voltha-protos/v5/go/core_adapter" |
| "github.com/opencord/voltha-protos/v5/go/extension" |
| "github.com/opencord/voltha-protos/v5/go/omci" |
| "github.com/opencord/voltha-protos/v5/go/openflow_13" |
| "github.com/opencord/voltha-protos/v5/go/voltha" |
| ) |
| |
| const ( |
| numONUPerOLT = 4 |
| startingUNIPortNo = 100 |
| ) |
| |
| func macAddressToUint32Array(mac string) []uint32 { |
| slist := strings.Split(mac, ":") |
| result := make([]uint32, len(slist)) |
| var err error |
| var tmp int64 |
| for index, val := range slist { |
| if tmp, err = strconv.ParseInt(val, 16, 32); err != nil { |
| return []uint32{1, 2, 3, 4, 5, 6} |
| } |
| result[index] = uint32(tmp) |
| } |
| return result |
| } |
| |
| // GetNumONUPerOLT returns number of ONUs per OLT |
| func GetNumONUPerOLT() int { |
| return numONUPerOLT |
| } |
| |
| // Returns the starting UNI port number |
| func GetStartingUNIPortNo() int { |
| return startingUNIPortNo |
| } |
| |
| // Adapter represents adapter attributes |
| type Adapter struct { |
| flows map[string]map[uint64]*openflow_13.OfpFlowStats |
| flowLock sync.RWMutex |
| devices map[string]*voltha.Device |
| deviceLock sync.RWMutex |
| failFlowAdd map[string]bool |
| failFlowAddLock sync.RWMutex |
| failFlowDelete map[string]bool |
| failFlowDeleteLock sync.RWMutex |
| failDeleteDevice map[string]bool |
| failDeleteDeviceLock sync.RWMutex |
| coreEnpoint string |
| coreClient *vgrpc.Client |
| serviceEndpoint string |
| DeviceType string |
| vendor string |
| Probe *probe.Probe |
| } |
| |
| // NewAdapter creates adapter instance |
| func NewAdapter(serviceEndpoint, coreEndpoint, deviceType, vendor string) *Adapter { |
| return &Adapter{ |
| flows: map[string]map[uint64]*openflow_13.OfpFlowStats{}, |
| devices: map[string]*voltha.Device{}, |
| failFlowAdd: map[string]bool{}, |
| failFlowDelete: map[string]bool{}, |
| failDeleteDevice: map[string]bool{}, |
| coreEnpoint: coreEndpoint, |
| serviceEndpoint: serviceEndpoint, |
| DeviceType: deviceType, |
| vendor: vendor, |
| } |
| } |
| |
| func (ta *Adapter) IsReady() bool { |
| return ta.Probe.IsReady() |
| } |
| |
| func (ta *Adapter) storeDevice(d *voltha.Device) { |
| ta.deviceLock.Lock() |
| defer ta.deviceLock.Unlock() |
| if d != nil { |
| ta.devices[d.Id] = d |
| } |
| } |
| |
| func (ta *Adapter) getDevice(id string) *voltha.Device { |
| ta.deviceLock.RLock() |
| defer ta.deviceLock.RUnlock() |
| return ta.devices[id] |
| } |
| |
| func (ta *Adapter) updateDevice(d *voltha.Device) { |
| ta.storeDevice(d) |
| } |
| |
| func (ta *Adapter) GetEndPoint() string { |
| return ta.serviceEndpoint |
| } |
| |
| func (ta *Adapter) GetCoreClient() (core_service.CoreServiceClient, error) { |
| // Wait until the Core is up and running |
| for { |
| if ta.coreClient != nil { |
| client, err := ta.coreClient.GetClient() |
| if err != nil { |
| logger.Infow(context.Background(), "got-error-core-client", log.Fields{"error": err}) |
| time.Sleep(1 * time.Second) |
| continue |
| } |
| c, ok := client.(core_service.CoreServiceClient) |
| if ok { |
| logger.Debug(context.Background(), "got-valid-client") |
| return c, nil |
| } |
| } |
| logger.Info(context.Background(), "waiting-for-grpc-core-client") |
| time.Sleep(1 * time.Second) |
| } |
| } |
| |
| // Helper methods |
| // startGRPCService creates the grpc service handlers, registers it to the grpc server and starts the server |
| func (ta *Adapter) startGRPCService(ctx context.Context, server *vgrpc.GrpcServer, handler adapter_service.AdapterServiceServer, serviceName string) { |
| logger.Infow(ctx, "service-created", log.Fields{"service": serviceName}) |
| |
| server.AddService(func(gs *grpc.Server) { adapter_service.RegisterAdapterServiceServer(gs, handler) }) |
| logger.Infow(ctx, "service-added", log.Fields{"service": serviceName}) |
| |
| ta.Probe.UpdateStatus(ctx, serviceName, probe.ServiceStatusRunning) |
| logger.Infow(ctx, "service-started", log.Fields{"service": serviceName}) |
| |
| // Note that there is a small window here in which the core could return its status as ready, |
| // when it really isn't. This is unlikely to cause issues, as the delay is incredibly short. |
| server.Start(ctx) |
| ta.Probe.UpdateStatus(ctx, serviceName, probe.ServiceStatusStopped) |
| } |
| |
| func setCoreServiceHandler(ctx context.Context, conn *grpc.ClientConn) interface{} { |
| if conn == nil { |
| return nil |
| } |
| return core_service.NewCoreServiceClient(conn) |
| } |
| |
| // gRPC service |
| func (ta *Adapter) GetHealthStatus(ctx context.Context, clientConn *common.Connection) (*health.HealthStatus, error) { |
| return &health.HealthStatus{State: health.HealthStatus_HEALTHY}, nil |
| } |
| |
| // Device |
| |
| func (ta *Adapter) AdoptDevice(ctx context.Context, device *voltha.Device) (*empty.Empty, error) { |
| return &empty.Empty{}, nil |
| } |
| |
| func (ta *Adapter) ReconcileDevice(ctx context.Context, device *voltha.Device) (*empty.Empty, error) { |
| return &empty.Empty{}, nil |
| } |
| |
| func (ta *Adapter) DeleteDevice(ctx context.Context, device *voltha.Device) (*empty.Empty, error) { |
| ta.failDeleteDeviceLock.RLock() |
| haveToFail, ok := ta.failDeleteDevice[device.Id] |
| if ok && haveToFail { |
| ta.failDeleteDeviceLock.RUnlock() |
| return nil, fmt.Errorf("delete-device-failure") |
| } |
| ta.failDeleteDeviceLock.RUnlock() |
| if ok { |
| ta.RemoveDevice(device.Id) |
| } |
| logger.Debugw(ctx, "device-deleted-in-adapter", log.Fields{"device-id": device.Id}) |
| return &empty.Empty{}, nil |
| } |
| |
| func (ta *Adapter) DisableDevice(ctx context.Context, device *voltha.Device) (*empty.Empty, error) { |
| return &empty.Empty{}, nil |
| } |
| |
| func (ta *Adapter) ReEnableDevice(ctx context.Context, device *voltha.Device) (*empty.Empty, error) { |
| return &empty.Empty{}, nil |
| } |
| |
| func (ta *Adapter) RebootDevice(ctx context.Context, device *voltha.Device) (*empty.Empty, error) { |
| return &empty.Empty{}, nil |
| } |
| |
| func (ta *Adapter) SelfTestDevice(ctx context.Context, device *voltha.Device) (*empty.Empty, error) { |
| return &empty.Empty{}, nil |
| } |
| |
| func (ta *Adapter) ChildDeviceLost(ctx context.Context, device *voltha.Device) (*empty.Empty, error) { |
| return &empty.Empty{}, nil |
| } |
| |
| func (ta *Adapter) GetOfpDeviceInfo(ctx context.Context, device *voltha.Device) (*ca.SwitchCapability, error) { |
| return nil, nil |
| } |
| |
| // Ports |
| |
| func (ta *Adapter) EnablePort(ctx context.Context, port *voltha.Port) (*empty.Empty, error) { |
| return &empty.Empty{}, nil |
| } |
| |
| func (ta *Adapter) DisablePort(ctx context.Context, port *voltha.Port) (*empty.Empty, error) { |
| return &empty.Empty{}, nil |
| } |
| |
| // Flows |
| func (ta *Adapter) UpdateFlowsBulk(ctx context.Context, flows *ca.BulkFlows) (*empty.Empty, error) { |
| return &empty.Empty{}, nil |
| } |
| |
| func (ta *Adapter) UpdateFlowsIncrementally(ctx context.Context, incrFlows *ca.IncrementalFlows) (*empty.Empty, error) { |
| ta.flowLock.Lock() |
| defer ta.flowLock.Unlock() |
| |
| if _, ok := ta.flows[incrFlows.Device.Id]; !ok { |
| ta.flows[incrFlows.Device.Id] = map[uint64]*openflow_13.OfpFlowStats{} |
| } |
| |
| if incrFlows.Flows.ToAdd != nil && len(incrFlows.Flows.ToAdd.Items) > 0 { |
| ta.failFlowAddLock.RLock() |
| if haveToFail, ok := ta.failFlowAdd[incrFlows.Device.Id]; ok && haveToFail { |
| ta.failFlowAddLock.RUnlock() |
| return nil, fmt.Errorf("flow-add-error") |
| } |
| ta.failFlowAddLock.RUnlock() |
| for _, f := range incrFlows.Flows.ToAdd.Items { |
| ta.flows[incrFlows.Device.Id][f.Id] = f |
| } |
| } |
| if incrFlows.Flows.ToRemove != nil && len(incrFlows.Flows.ToRemove.Items) > 0 { |
| ta.failFlowDeleteLock.RLock() |
| if haveToFail, ok := ta.failFlowDelete[incrFlows.Device.Id]; ok && haveToFail { |
| ta.failFlowDeleteLock.RUnlock() |
| return nil, fmt.Errorf("flow-delete-error") |
| } |
| ta.failFlowDeleteLock.RUnlock() |
| for _, f := range incrFlows.Flows.ToRemove.Items { |
| delete(ta.flows[incrFlows.Device.Id], f.Id) |
| } |
| } |
| return &empty.Empty{}, nil |
| } |
| |
| // Packets |
| func (ta *Adapter) SendPacketOut(ctx context.Context, packet *ca.PacketOut) (*empty.Empty, error) { |
| return &empty.Empty{}, nil |
| } |
| |
| // PM |
| func (ta *Adapter) UpdatePmConfig(ctx context.Context, configs *ca.PmConfigsInfo) (*empty.Empty, error) { |
| return &empty.Empty{}, nil |
| } |
| |
| // Image |
| func (ta *Adapter) DownloadOnuImage(ctx context.Context, request *voltha.DeviceImageDownloadRequest) (*voltha.DeviceImageResponse, error) { |
| return &voltha.DeviceImageResponse{}, nil |
| } |
| |
| func (ta *Adapter) GetOnuImageStatus(ctx context.Context, request *voltha.DeviceImageRequest) (*voltha.DeviceImageResponse, error) { |
| return &voltha.DeviceImageResponse{}, nil |
| } |
| |
| func (ta *Adapter) AbortOnuImageUpgrade(ctx context.Context, request *voltha.DeviceImageRequest) (*voltha.DeviceImageResponse, error) { |
| return &voltha.DeviceImageResponse{}, nil |
| } |
| |
| func (ta *Adapter) GetOnuImages(ctx context.Context, id *common.ID) (*voltha.OnuImages, error) { |
| return &voltha.OnuImages{}, nil |
| } |
| |
| func (ta *Adapter) ActivateOnuImage(ctx context.Context, request *voltha.DeviceImageRequest) (*voltha.DeviceImageResponse, error) { |
| return &voltha.DeviceImageResponse{}, nil |
| } |
| |
| func (ta *Adapter) CommitOnuImage(ctx context.Context, request *voltha.DeviceImageRequest) (*voltha.DeviceImageResponse, error) { |
| return &voltha.DeviceImageResponse{}, nil |
| } |
| |
| // Deprecated image APIs |
| func (ta *Adapter) DownloadImage(ctx context.Context, in *ca.ImageDownloadMessage) (*voltha.ImageDownload, error) { |
| return &voltha.ImageDownload{}, nil |
| } |
| |
| func (ta *Adapter) GetImageDownloadStatus(ctx context.Context, in *ca.ImageDownloadMessage) (*voltha.ImageDownload, error) { |
| return &voltha.ImageDownload{}, nil |
| } |
| |
| func (ta *Adapter) CancelImageDownload(ctx context.Context, in *ca.ImageDownloadMessage) (*voltha.ImageDownload, error) { |
| return &voltha.ImageDownload{}, nil |
| } |
| |
| func (ta *Adapter) ActivateImageUpdate(ctx context.Context, in *ca.ImageDownloadMessage) (*voltha.ImageDownload, error) { |
| return &voltha.ImageDownload{}, nil |
| } |
| |
| func (ta *Adapter) RevertImageUpdate(ctx context.Context, in *ca.ImageDownloadMessage) (*voltha.ImageDownload, error) { |
| return &voltha.ImageDownload{}, nil |
| } |
| |
| // OMCI test |
| func (ta *Adapter) StartOmciTest(ctx context.Context, test *ca.OMCITest) (*omci.TestResponse, error) { |
| return nil, nil |
| } |
| |
| // Events |
| func (ta *Adapter) SuppressEvent(ctx context.Context, filter *voltha.EventFilter) (*empty.Empty, error) { |
| return &empty.Empty{}, nil |
| } |
| |
| func (ta *Adapter) UnSuppressEvent(ctx context.Context, filter *voltha.EventFilter) (*empty.Empty, error) { |
| return &empty.Empty{}, nil |
| } |
| |
| func (ta *Adapter) SimulateAlarm(context.Context, *ca.SimulateAlarmMessage) (*common.OperationResp, error) { |
| return &common.OperationResp{}, nil |
| } |
| |
| func (ta *Adapter) GetExtValue(context.Context, *ca.GetExtValueMessage) (*extension.ReturnValues, error) { |
| return &extension.ReturnValues{}, nil |
| } |
| |
| func (ta *Adapter) SetExtValue(context.Context, *ca.SetExtValueMessage) (*empty.Empty, error) { |
| return &empty.Empty{}, nil |
| } |
| |
| func (ta *Adapter) GetSingleValue(context.Context, *extension.SingleGetValueRequest) (*extension.SingleGetValueResponse, error) { |
| return &extension.SingleGetValueResponse{}, nil |
| } |
| |
| func (ta *Adapter) SetSingleValue(context.Context, *extension.SingleSetValueRequest) (*extension.SingleSetValueResponse, error) { |
| return &extension.SingleSetValueResponse{}, nil |
| } |
| |
| // APIs for test ONLY |
| // GetFlowCount returns the total number of flows presently under this adapter |
| func (ta *Adapter) GetFlowCount(deviceID string) int { |
| ta.flowLock.RLock() |
| defer ta.flowLock.RUnlock() |
| |
| if _, ok := ta.flows[deviceID]; ok { |
| return len(ta.flows[deviceID]) |
| } |
| return 0 |
| } |
| |
| // RemoveDevice removes all flows in this adapter |
| func (ta *Adapter) RemoveDevice(deviceID string) { |
| ta.flowLock.Lock() |
| defer ta.flowLock.Unlock() |
| ta.failFlowAddLock.Lock() |
| defer ta.failFlowAddLock.Unlock() |
| ta.failFlowDeleteLock.Lock() |
| defer ta.failFlowDeleteLock.Unlock() |
| |
| delete(ta.flows, deviceID) |
| delete(ta.failFlowAdd, deviceID) |
| delete(ta.failFlowDelete, deviceID) |
| } |
| |
| // SetFlowAction sets the adapter action on addition and deletion of flows |
| func (ta *Adapter) SetFlowAction(deviceID string, failFlowAdd, failFlowDelete bool) { |
| ta.failFlowAddLock.Lock() |
| defer ta.failFlowAddLock.Unlock() |
| ta.failFlowDeleteLock.Lock() |
| defer ta.failFlowDeleteLock.Unlock() |
| ta.failFlowAdd[deviceID] = failFlowAdd |
| ta.failFlowDelete[deviceID] = failFlowDelete |
| } |
| |
| // SetDeleteAction sets the adapter action on delete device |
| func (ta *Adapter) SetDeleteAction(deviceID string, failDeleteDevice bool) { |
| ta.failDeleteDeviceLock.Lock() |
| defer ta.failDeleteDeviceLock.Unlock() |
| ta.failDeleteDevice[deviceID] = failDeleteDevice |
| } |