amit.ghosh | 258d14c | 2020-10-02 15:13:38 +0200 | [diff] [blame^] | 1 | /* |
| 2 | * Copyright 2018-present Open Networking Foundation |
| 3 | |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | package dmiserver |
| 18 | |
| 19 | import ( |
| 20 | "context" |
| 21 | "fmt" |
| 22 | |
| 23 | "github.com/golang/protobuf/ptypes/empty" |
| 24 | "github.com/golang/protobuf/ptypes/timestamp" |
| 25 | "github.com/opencord/bbsim/internal/bbsim/devices" |
| 26 | "github.com/opencord/bbsim/internal/common" |
| 27 | dmi "github.com/opencord/device-management-interface/go/dmi" |
| 28 | |
| 29 | guuid "github.com/google/uuid" |
| 30 | "google.golang.org/grpc/codes" |
| 31 | "google.golang.org/grpc/status" |
| 32 | ) |
| 33 | |
| 34 | func getUUID(seed string) string { |
| 35 | return guuid.NewMD5(guuid.Nil, []byte(seed)).String() |
| 36 | } |
| 37 | |
| 38 | //StartManagingDevice establishes connection with the device and does checks to ascertain if the device with passed identity can be managed |
| 39 | func (dms *DmiAPIServer) StartManagingDevice(req *dmi.ModifiableComponent, stream dmi.NativeHWManagementService_StartManagingDeviceServer) error { |
| 40 | //Get serial number and generate the UUID based on this serial number. Store this UUID in local cache |
| 41 | logger.Debugf("StartManagingDevice() invoked with request %+v", req) |
| 42 | if req == nil { |
| 43 | return status.Errorf(codes.FailedPrecondition, "request is empty") |
| 44 | } |
| 45 | |
| 46 | if req.Name == "" { |
| 47 | return status.Errorf(codes.InvalidArgument, "'Name' can not be empty in the request") |
| 48 | } |
| 49 | |
| 50 | olt := devices.GetOLT() |
| 51 | |
| 52 | // Uri is the IP address |
| 53 | dms.ipAddress = req.GetUri().GetUri() |
| 54 | dms.deviceSerial = olt.SerialNumber |
| 55 | dms.deviceName = fmt.Sprintf("%s-%s", common.Config.Olt.Vendor, dms.deviceSerial) |
| 56 | |
| 57 | dms.uuid = getUUID(dms.deviceSerial) |
| 58 | |
| 59 | dms.ponTransceiverUuids = make([]string, olt.NumPon) |
| 60 | dms.ponTransceiverCageUuids = make([]string, olt.NumPon) |
| 61 | |
| 62 | var components []*dmi.Component |
| 63 | |
| 64 | // Create and store the component for transceivers and transceiver cages |
| 65 | for i := 0; i < olt.NumPon; i++ { |
| 66 | label := fmt.Sprintf("pon-%d", olt.Pons[i].ID) |
| 67 | dms.ponTransceiverUuids[i] = getUUID(dms.deviceSerial + label) |
| 68 | dms.ponTransceiverCageUuids[i] = getUUID(dms.deviceSerial + "cage" + label) |
| 69 | |
| 70 | transName := fmt.Sprintf("sfp-%d", i) |
| 71 | cageName := fmt.Sprintf("cage-%d", i) |
| 72 | |
| 73 | trans := dmi.Component{ |
| 74 | Name: transName, |
| 75 | Class: dmi.ComponentType_COMPONENT_TYPE_TRANSCEIVER, |
| 76 | Description: "XGS-PON", |
| 77 | Uuid: &dmi.Uuid{ |
| 78 | Uuid: dms.ponTransceiverUuids[i], |
| 79 | }, |
| 80 | Parent: cageName, |
| 81 | } |
| 82 | |
| 83 | cage := dmi.Component{ |
| 84 | Name: cageName, |
| 85 | Class: dmi.ComponentType_COMPONENT_TYPE_CONTAINER, |
| 86 | Description: "cage", |
| 87 | Uuid: &dmi.Uuid{ |
| 88 | Uuid: dms.ponTransceiverCageUuids[i], |
| 89 | }, |
| 90 | Parent: dms.deviceName, |
| 91 | Children: []*dmi.Component{&trans}, |
| 92 | } |
| 93 | |
| 94 | components = append(components, &cage) |
| 95 | } |
| 96 | |
| 97 | dms.components = components |
| 98 | |
| 99 | logger.Debugf("Generated UUID for the uri %s is %s", dms.ipAddress, dms.uuid) |
| 100 | response := &dmi.StartManagingDeviceResponse{ |
| 101 | Status: dmi.Status_OK, |
| 102 | DeviceUuid: &dmi.Uuid{ |
| 103 | Uuid: dms.uuid, |
| 104 | }, |
| 105 | } |
| 106 | |
| 107 | err := stream.Send(response) |
| 108 | if err != nil { |
| 109 | logger.Errorf("Error while sending response to client %v", err.Error()) |
| 110 | return status.Errorf(codes.Unknown, err.Error()) |
| 111 | } |
| 112 | |
| 113 | return nil |
| 114 | } |
| 115 | |
| 116 | //StopManagingDevice stops management of a device and cleans up any context and caches for that device |
| 117 | func (dms *DmiAPIServer) StopManagingDevice(context.Context, *dmi.StopManagingDeviceRequest) (*dmi.StopManagingDeviceResponse, error) { |
| 118 | return nil, status.Errorf(codes.Unimplemented, "rpc StopManagingDevice not implemented") |
| 119 | } |
| 120 | |
| 121 | //GetPhysicalInventory gets the HW inventory details of the Device |
| 122 | func (dms *DmiAPIServer) GetPhysicalInventory(req *dmi.PhysicalInventoryRequest, stream dmi.NativeHWManagementService_GetPhysicalInventoryServer) error { |
| 123 | if req == nil || req.DeviceUuid == nil || req.DeviceUuid.Uuid == "" { |
| 124 | return status.Errorf(codes.InvalidArgument, "device-UUID missing in the request") |
| 125 | } |
| 126 | |
| 127 | // Function to send the response back on the stream |
| 128 | sendResponseBackOnStream := func(stream dmi.NativeHWManagementService_GetPhysicalInventoryServer, msg *dmi.PhysicalInventoryResponse) error { |
| 129 | err := stream.Send(msg) |
| 130 | if err != nil { |
| 131 | logger.Errorf("Error sending response to client, error: %v", err) |
| 132 | return status.Errorf(codes.Internal, "Error sending response to client "+err.Error()) |
| 133 | } |
| 134 | return nil |
| 135 | } |
| 136 | |
| 137 | if req.DeviceUuid.Uuid != dms.uuid { |
| 138 | logger.Errorf("Requested uuid =%s, uuid of existing device = %s", req.DeviceUuid.Uuid, dms.uuid) |
| 139 | // Wrong uuid, return error |
| 140 | errResponse := &dmi.PhysicalInventoryResponse{ |
| 141 | Status: dmi.Status_ERROR, |
| 142 | Reason: dmi.Reason_UNKNOWN_DEVICE, |
| 143 | Inventory: &dmi.Hardware{}, |
| 144 | } |
| 145 | |
| 146 | return sendResponseBackOnStream(stream, errResponse) |
| 147 | } |
| 148 | |
| 149 | response := &dmi.PhysicalInventoryResponse{ |
| 150 | Status: dmi.Status_OK, |
| 151 | Inventory: &dmi.Hardware{ |
| 152 | LastChange: ×tamp.Timestamp{ |
| 153 | Seconds: 0, |
| 154 | Nanos: 0, |
| 155 | }, |
| 156 | Root: &dmi.Component{ |
| 157 | Name: dms.deviceName, |
| 158 | Class: 0, |
| 159 | Description: "", |
| 160 | Parent: "", |
| 161 | ParentRelPos: 0, |
| 162 | Children: dms.components, |
| 163 | SerialNum: dms.deviceSerial, |
| 164 | MfgName: common.Config.Olt.Vendor, |
| 165 | IsFru: false, |
| 166 | Uri: &dmi.Uri{ |
| 167 | Uri: dms.ipAddress, |
| 168 | }, |
| 169 | Uuid: &dmi.Uuid{ |
| 170 | Uuid: dms.uuid, |
| 171 | }, |
| 172 | State: &dmi.ComponentState{}, |
| 173 | }, |
| 174 | }, |
| 175 | } |
| 176 | return sendResponseBackOnStream(stream, response) |
| 177 | } |
| 178 | |
| 179 | //Contains tells whether arr contains element. |
| 180 | func Contains(arr []string, element string) bool { |
| 181 | for _, item := range arr { |
| 182 | logger.Debugf("Checking in Contains slice elem = %v str = %s", item, element) |
| 183 | if element == item { |
| 184 | return true |
| 185 | } |
| 186 | } |
| 187 | return false |
| 188 | } |
| 189 | |
| 190 | func findComponent(l []*dmi.Component, compUUID string) *dmi.Component { |
| 191 | for _, comp := range l { |
| 192 | logger.Debugf("findComponent slice comp = %v compUUID = %s", comp, compUUID) |
| 193 | if comp.Uuid.Uuid == compUUID { |
| 194 | return comp |
| 195 | } |
| 196 | |
| 197 | for _, child := range comp.GetChildren() { |
| 198 | logger.Debugf("findComponent Child slice comp = %v compUUID = %s", comp, compUUID) |
| 199 | if child.Uuid.Uuid == compUUID { |
| 200 | return child |
| 201 | } |
| 202 | } |
| 203 | } |
| 204 | |
| 205 | return nil |
| 206 | } |
| 207 | |
| 208 | func sendGetHWComponentResponse(c *dmi.Component, stream dmi.NativeHWManagementService_GetHWComponentInfoServer) error { |
| 209 | apiStatus := dmi.Status_OK |
| 210 | reason := dmi.Reason_UNDEFINED_REASON |
| 211 | |
| 212 | if c == nil { |
| 213 | apiStatus = dmi.Status_ERROR |
| 214 | reason = dmi.Reason_UNKNOWN_DEVICE |
| 215 | } |
| 216 | |
| 217 | response := &dmi.HWComponentInfoGetResponse{ |
| 218 | Status: apiStatus, |
| 219 | Reason: reason, |
| 220 | Component: c, |
| 221 | } |
| 222 | |
| 223 | err := stream.Send(response) |
| 224 | if err != nil { |
| 225 | logger.Errorf("Error sending response to client, error: %v", err) |
| 226 | return status.Errorf(codes.Internal, "Error sending response to client "+err.Error()) |
| 227 | } |
| 228 | return nil |
| 229 | } |
| 230 | |
| 231 | //GetHWComponentInfo gets the details of a particular HW component |
| 232 | func (dms *DmiAPIServer) GetHWComponentInfo(req *dmi.HWComponentInfoGetRequest, stream dmi.NativeHWManagementService_GetHWComponentInfoServer) error { |
| 233 | logger.Debugf("GetHWComponentInfo() invoked with request %+v", req) |
| 234 | |
| 235 | if req == nil { |
| 236 | return status.Errorf(codes.FailedPrecondition, "can not entertain nil request") |
| 237 | } |
| 238 | if stream == nil { |
| 239 | logger.Errorf("stream to send is nil, not sending response from gRPC server ") |
| 240 | return status.Errorf(codes.Internal, "stream to send is nil, can not send response from gRPC server") |
| 241 | } |
| 242 | |
| 243 | componentFound := Contains(dms.ponTransceiverUuids, req.ComponentUuid.Uuid) |
| 244 | if !componentFound { |
| 245 | componentFound = Contains(dms.ponTransceiverCageUuids, req.ComponentUuid.Uuid) |
| 246 | } |
| 247 | |
| 248 | if req.DeviceUuid.Uuid != dms.uuid || !componentFound { |
| 249 | // Wrong uuid, return error |
| 250 | return sendGetHWComponentResponse(nil, stream) |
| 251 | } |
| 252 | |
| 253 | // Search for the component and return it |
| 254 | c := findComponent(dms.components, req.ComponentUuid.Uuid) |
| 255 | return sendGetHWComponentResponse(c, stream) |
| 256 | } |
| 257 | |
| 258 | //SetHWComponentInfo sets the permissible attributes of a HW component |
| 259 | func (dms *DmiAPIServer) SetHWComponentInfo(context.Context, *dmi.HWComponentInfoSetRequest) (*dmi.HWComponentInfoSetResponse, error) { |
| 260 | return nil, status.Errorf(codes.Unimplemented, "rpc SetHWComponentInfo not implemented") |
| 261 | } |
| 262 | |
| 263 | //SetLoggingEndpoint sets the location to which logs need to be shipped |
| 264 | func (dms *DmiAPIServer) SetLoggingEndpoint(context.Context, *dmi.SetLoggingEndpointRequest) (*dmi.SetRemoteEndpointResponse, error) { |
| 265 | return nil, status.Errorf(codes.Unimplemented, "rpc SetLoggingEndpoint not implemented") |
| 266 | } |
| 267 | |
| 268 | //GetLoggingEndpoint gets the configured location to which the logs are being shipped |
| 269 | func (dms *DmiAPIServer) GetLoggingEndpoint(context.Context, *dmi.Uuid) (*dmi.GetLoggingEndpointResponse, error) { |
| 270 | return nil, status.Errorf(codes.Unimplemented, "rpc GetLoggingEndpoint not implemented") |
| 271 | } |
| 272 | |
| 273 | //SetMsgBusEndpoint sets the location of the Message Bus to which events and metrics are shipped |
| 274 | func (dms *DmiAPIServer) SetMsgBusEndpoint(context.Context, *dmi.SetMsgBusEndpointRequest) (*dmi.SetRemoteEndpointResponse, error) { |
| 275 | return nil, status.Errorf(codes.Unimplemented, "rpc SetMsgBusEndpoint not implemented") |
| 276 | } |
| 277 | |
| 278 | //GetMsgBusEndpoint gets the configured location to which the events and metrics are being shipped |
| 279 | func (dms *DmiAPIServer) GetMsgBusEndpoint(context.Context, *empty.Empty) (*dmi.GetMsgBusEndpointResponse, error) { |
| 280 | return nil, status.Errorf(codes.Unimplemented, "rpc GetMsgBusEndpoint not implemented") |
| 281 | } |
| 282 | |
| 283 | //GetManagedDevices returns an object containing a list of devices managed by this entity |
| 284 | func (dms *DmiAPIServer) GetManagedDevices(context.Context, *empty.Empty) (*dmi.ManagedDevicesResponse, error) { |
| 285 | retResponse := dmi.ManagedDevicesResponse{} |
| 286 | //If our uuid is empty, we return empty list; else we fill details and return |
| 287 | if dms.uuid != "" { |
| 288 | root := dmi.ModifiableComponent{ |
| 289 | Name: dms.deviceName, |
| 290 | Uri: &dmi.Uri{ |
| 291 | Uri: dms.ipAddress, |
| 292 | }, |
| 293 | } |
| 294 | |
| 295 | retResponse.Devices = append(retResponse.Devices, &root) |
| 296 | } |
| 297 | |
| 298 | return &retResponse, nil |
| 299 | } |