blob: cf6410dcb00d0a00c8b6b1eb47c9598ecb5d3811 [file] [log] [blame]
amit.ghosh258d14c2020-10-02 15:13:38 +02001/*
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
17package dmiserver
18
19import (
20 "context"
21 "fmt"
22
Humera Kousera4442952020-11-23 23:51:19 +053023 "github.com/Shopify/sarama"
24
amit.ghosh258d14c2020-10-02 15:13:38 +020025 "github.com/golang/protobuf/ptypes/empty"
26 "github.com/golang/protobuf/ptypes/timestamp"
27 "github.com/opencord/bbsim/internal/bbsim/devices"
28 "github.com/opencord/bbsim/internal/common"
29 dmi "github.com/opencord/device-management-interface/go/dmi"
30
31 guuid "github.com/google/uuid"
32 "google.golang.org/grpc/codes"
33 "google.golang.org/grpc/status"
34)
35
Humera Kouser18b275c2020-11-30 11:30:36 +053036const (
37 metricChannelSize = 100
38)
39
amit.ghosh258d14c2020-10-02 15:13:38 +020040func getUUID(seed string) string {
41 return guuid.NewMD5(guuid.Nil, []byte(seed)).String()
42}
43
44//StartManagingDevice establishes connection with the device and does checks to ascertain if the device with passed identity can be managed
45func (dms *DmiAPIServer) StartManagingDevice(req *dmi.ModifiableComponent, stream dmi.NativeHWManagementService_StartManagingDeviceServer) error {
46 //Get serial number and generate the UUID based on this serial number. Store this UUID in local cache
47 logger.Debugf("StartManagingDevice() invoked with request %+v", req)
48 if req == nil {
49 return status.Errorf(codes.FailedPrecondition, "request is empty")
50 }
51
52 if req.Name == "" {
53 return status.Errorf(codes.InvalidArgument, "'Name' can not be empty in the request")
54 }
55
56 olt := devices.GetOLT()
57
58 // Uri is the IP address
59 dms.ipAddress = req.GetUri().GetUri()
60 dms.deviceSerial = olt.SerialNumber
61 dms.deviceName = fmt.Sprintf("%s-%s", common.Config.Olt.Vendor, dms.deviceSerial)
62
63 dms.uuid = getUUID(dms.deviceSerial)
64
65 dms.ponTransceiverUuids = make([]string, olt.NumPon)
66 dms.ponTransceiverCageUuids = make([]string, olt.NumPon)
67
Humera Kouser18b275c2020-11-30 11:30:36 +053068 // Start device metrics generator
69 dms.metricChannel = make(chan interface{}, metricChannelSize)
70 StartMetricGenerator(dms)
71
amit.ghosh258d14c2020-10-02 15:13:38 +020072 var components []*dmi.Component
73
74 // Create and store the component for transceivers and transceiver cages
75 for i := 0; i < olt.NumPon; i++ {
76 label := fmt.Sprintf("pon-%d", olt.Pons[i].ID)
77 dms.ponTransceiverUuids[i] = getUUID(dms.deviceSerial + label)
78 dms.ponTransceiverCageUuids[i] = getUUID(dms.deviceSerial + "cage" + label)
79
80 transName := fmt.Sprintf("sfp-%d", i)
81 cageName := fmt.Sprintf("cage-%d", i)
82
83 trans := dmi.Component{
84 Name: transName,
85 Class: dmi.ComponentType_COMPONENT_TYPE_TRANSCEIVER,
86 Description: "XGS-PON",
87 Uuid: &dmi.Uuid{
88 Uuid: dms.ponTransceiverUuids[i],
89 },
90 Parent: cageName,
91 }
92
93 cage := dmi.Component{
94 Name: cageName,
95 Class: dmi.ComponentType_COMPONENT_TYPE_CONTAINER,
96 Description: "cage",
97 Uuid: &dmi.Uuid{
98 Uuid: dms.ponTransceiverCageUuids[i],
99 },
100 Parent: dms.deviceName,
101 Children: []*dmi.Component{&trans},
102 }
103
104 components = append(components, &cage)
105 }
106
Humera Kousera4442952020-11-23 23:51:19 +0530107 // create the fans
108 numFans := 2
109 fans := make([]*dmi.Component, numFans)
110
111 for i := 0; i < numFans; i++ {
112 fans[i] = createFanComponent(i + 1)
113 }
114 components = append(components, fans...)
115
116 // Create 1 disk, 1 Processor and 1 ram
117 components = append(components, createDiskComponent(0))
118 components = append(components, createProcessorComponent(0))
119 components = append(components, createMemoryComponent(0))
120 components = append(components, createInnerSurroundingTempComponentSensor(0))
121
122 // create the root component
123 dms.root = &dmi.Component{
124 Name: dms.deviceName,
125 Class: 0,
126 Description: "",
127 Parent: "",
128 ParentRelPos: 0,
129 Children: components,
130 SerialNum: dms.deviceSerial,
131 MfgName: common.Config.Olt.Vendor,
132 IsFru: false,
133 Uri: &dmi.Uri{
134 Uri: dms.ipAddress,
135 },
136 Uuid: &dmi.Uuid{
137 Uuid: dms.uuid,
138 },
139 State: &dmi.ComponentState{},
140 }
amit.ghosh258d14c2020-10-02 15:13:38 +0200141
142 logger.Debugf("Generated UUID for the uri %s is %s", dms.ipAddress, dms.uuid)
143 response := &dmi.StartManagingDeviceResponse{
Humera Kousera4442952020-11-23 23:51:19 +0530144 Status: dmi.Status_OK_STATUS,
amit.ghosh258d14c2020-10-02 15:13:38 +0200145 DeviceUuid: &dmi.Uuid{
146 Uuid: dms.uuid,
147 },
148 }
149
150 err := stream.Send(response)
151 if err != nil {
152 logger.Errorf("Error while sending response to client %v", err.Error())
153 return status.Errorf(codes.Unknown, err.Error())
154 }
155
156 return nil
157}
158
Humera Kousera4442952020-11-23 23:51:19 +0530159func createFanComponent(fanIdx int) *dmi.Component {
160 fanName := fmt.Sprintf("Thermal/Fans/System Fan/%d", fanIdx)
161 fanSerial := fmt.Sprintf("bbsim-fan-serial-%d", fanIdx)
162 return &dmi.Component{
163 Name: fanName,
164 Class: dmi.ComponentType_COMPONENT_TYPE_FAN,
165 Description: "bbsim-fan",
166 Parent: "",
167 ParentRelPos: 0,
168 SerialNum: fanSerial,
169 MfgName: "bbsim-fan",
170 IsFru: false,
171 Uuid: &dmi.Uuid{
172 Uuid: getUUID(fanName),
173 },
174 State: &dmi.ComponentState{},
175 }
176}
177
178func createProcessorComponent(cpuIdx int) *dmi.Component {
179 cpuName := fmt.Sprintf("Systems/1/Processors/%d", cpuIdx)
180 cpuSerial := fmt.Sprintf("bbsim-cpu-serial-%d", cpuIdx)
181 return &dmi.Component{
182 Name: cpuName,
183 Class: dmi.ComponentType_COMPONENT_TYPE_CPU,
184 Description: "bbsim-cpu",
185 Parent: "",
186 ParentRelPos: 0,
187 SerialNum: cpuSerial,
188 MfgName: "bbsim-cpu",
189 IsFru: false,
190 Uuid: &dmi.Uuid{
191 Uuid: getUUID(cpuName),
192 },
193 State: &dmi.ComponentState{},
194 }
195}
196
197func createMemoryComponent(memIdx int) *dmi.Component {
198 memName := fmt.Sprintf("Systems/1/Memory/%d", memIdx)
199 memSerial := fmt.Sprintf("bbsim-ram-serial-%d", memIdx)
200 return &dmi.Component{
201 Name: memName,
202 Class: dmi.ComponentType_COMPONENT_TYPE_MEMORY,
203 Description: "bbsim-ram",
204 Parent: "",
205 ParentRelPos: 0,
206 SerialNum: memSerial,
207 MfgName: "bbsim-ram",
208 IsFru: false,
209 Uuid: &dmi.Uuid{
210 Uuid: getUUID(memName),
211 },
212 State: &dmi.ComponentState{},
213 }
214}
215
216func createDiskComponent(diskIdx int) *dmi.Component {
217 diskName := fmt.Sprintf("Systems/1/Disk/%d", diskIdx)
218 diskSerial := fmt.Sprintf("bbsim-disk-serial-%d", diskIdx)
219 return &dmi.Component{
220 Name: diskName,
221 Class: dmi.ComponentType_COMPONENT_TYPE_STORAGE,
222 Description: "bbsim-disk",
223 Parent: "",
224 ParentRelPos: 0,
225 SerialNum: diskSerial,
226 MfgName: "bbsim-disk",
227 IsFru: false,
228 Uuid: &dmi.Uuid{
229 Uuid: getUUID(diskName),
230 },
231 State: &dmi.ComponentState{},
232 }
233}
234
235func createInnerSurroundingTempComponentSensor(sensorIdx int) *dmi.Component {
236 sensorName := fmt.Sprintf("Systems/1/Sensor/%d", sensorIdx)
237 sensorSerial := fmt.Sprintf("bbsim-sensor-istemp-serial-%d", sensorIdx)
238 return &dmi.Component{
239 Name: sensorName,
240 Class: dmi.ComponentType_COMPONENT_TYPE_SENSOR,
241 Description: "bbsim-istemp",
242 Parent: "",
243 ParentRelPos: 0,
244 SerialNum: sensorSerial,
245 MfgName: "bbsim-istemp",
246 IsFru: false,
247 Uuid: &dmi.Uuid{
248 Uuid: getUUID(sensorName),
249 },
250 State: &dmi.ComponentState{},
251 }
252}
253
amit.ghosh258d14c2020-10-02 15:13:38 +0200254//StopManagingDevice stops management of a device and cleans up any context and caches for that device
Humera Kouser18b275c2020-11-30 11:30:36 +0530255func (dms *DmiAPIServer) StopManagingDevice(ctx context.Context, req *dmi.StopManagingDeviceRequest) (*dmi.StopManagingDeviceResponse, error) {
256 logger.Debugf("StopManagingDevice API invoked")
257 if req == nil {
258 return &dmi.StopManagingDeviceResponse{Status: dmi.Status_ERROR_STATUS, Reason: dmi.Reason_UNKNOWN_DEVICE}, status.Errorf(codes.FailedPrecondition, "request is empty")
259 }
260
261 if req.Name == "" {
262 return &dmi.StopManagingDeviceResponse{Status: dmi.Status_ERROR_STATUS, Reason: dmi.Reason_UNKNOWN_DEVICE},
263 status.Errorf(codes.InvalidArgument, "'Name' can not be empty in the request")
264 }
265
266 // Stop the components/go routines created
267 StopMetricGenerator()
268
269 if dms.mPublisherCancelFunc != nil {
270 dms.mPublisherCancelFunc()
271 }
272
273 dms.deviceName = ""
274 dms.kafkaEndpoint = ""
275 dms.ipAddress = ""
276 dms.deviceSerial = ""
277 dms.ponTransceiverUuids = nil
278 dms.ponTransceiverCageUuids = nil
279 dms.uuid = ""
280 dms.root = nil
281 dms.metricChannel = nil
282
283 logger.Infof("Stopped managing the device")
284 return &dmi.StopManagingDeviceResponse{Status: dmi.Status_OK_STATUS}, nil
amit.ghosh258d14c2020-10-02 15:13:38 +0200285}
286
287//GetPhysicalInventory gets the HW inventory details of the Device
288func (dms *DmiAPIServer) GetPhysicalInventory(req *dmi.PhysicalInventoryRequest, stream dmi.NativeHWManagementService_GetPhysicalInventoryServer) error {
289 if req == nil || req.DeviceUuid == nil || req.DeviceUuid.Uuid == "" {
290 return status.Errorf(codes.InvalidArgument, "device-UUID missing in the request")
291 }
292
293 // Function to send the response back on the stream
294 sendResponseBackOnStream := func(stream dmi.NativeHWManagementService_GetPhysicalInventoryServer, msg *dmi.PhysicalInventoryResponse) error {
295 err := stream.Send(msg)
296 if err != nil {
297 logger.Errorf("Error sending response to client, error: %v", err)
298 return status.Errorf(codes.Internal, "Error sending response to client "+err.Error())
299 }
300 return nil
301 }
302
303 if req.DeviceUuid.Uuid != dms.uuid {
304 logger.Errorf("Requested uuid =%s, uuid of existing device = %s", req.DeviceUuid.Uuid, dms.uuid)
305 // Wrong uuid, return error
306 errResponse := &dmi.PhysicalInventoryResponse{
Humera Kousera4442952020-11-23 23:51:19 +0530307 Status: dmi.Status_ERROR_STATUS,
amit.ghosh258d14c2020-10-02 15:13:38 +0200308 Reason: dmi.Reason_UNKNOWN_DEVICE,
309 Inventory: &dmi.Hardware{},
310 }
311
312 return sendResponseBackOnStream(stream, errResponse)
313 }
314
315 response := &dmi.PhysicalInventoryResponse{
Humera Kousera4442952020-11-23 23:51:19 +0530316 Status: dmi.Status_OK_STATUS,
amit.ghosh258d14c2020-10-02 15:13:38 +0200317 Inventory: &dmi.Hardware{
318 LastChange: &timestamp.Timestamp{
319 Seconds: 0,
320 Nanos: 0,
321 },
Humera Kousera4442952020-11-23 23:51:19 +0530322 Root: dms.root,
amit.ghosh258d14c2020-10-02 15:13:38 +0200323 },
324 }
325 return sendResponseBackOnStream(stream, response)
326}
327
328//Contains tells whether arr contains element.
329func Contains(arr []string, element string) bool {
330 for _, item := range arr {
amit.ghosh258d14c2020-10-02 15:13:38 +0200331 if element == item {
332 return true
333 }
334 }
335 return false
336}
337
338func findComponent(l []*dmi.Component, compUUID string) *dmi.Component {
Humera Kousera4442952020-11-23 23:51:19 +0530339 var foundComp *dmi.Component
340
amit.ghosh258d14c2020-10-02 15:13:38 +0200341 for _, comp := range l {
342 logger.Debugf("findComponent slice comp = %v compUUID = %s", comp, compUUID)
343 if comp.Uuid.Uuid == compUUID {
344 return comp
345 }
346
Humera Kousera4442952020-11-23 23:51:19 +0530347 foundComp = findComponent(comp.GetChildren(), compUUID)
348 if foundComp != nil {
349 return foundComp
amit.ghosh258d14c2020-10-02 15:13:38 +0200350 }
351 }
352
353 return nil
354}
355
Humera Kousera4442952020-11-23 23:51:19 +0530356func findComponentsOfType(l []*dmi.Component, compType dmi.ComponentType) []*dmi.Component {
357 var comps []*dmi.Component
358 findComponents(l, compType, &comps)
359 return comps
360}
361
362func findComponents(l []*dmi.Component, compType dmi.ComponentType, collector *[]*dmi.Component) {
363
364 for _, comp := range l {
365 if comp.Class == compType {
366 *collector = append(*collector, comp)
367 //logger.Debugf("Added collector = %v", *collector)
368 }
369
370 findComponents(comp.GetChildren(), compType, collector)
371 }
372}
373
amit.ghosh258d14c2020-10-02 15:13:38 +0200374func sendGetHWComponentResponse(c *dmi.Component, stream dmi.NativeHWManagementService_GetHWComponentInfoServer) error {
Humera Kousera4442952020-11-23 23:51:19 +0530375 apiStatus := dmi.Status_OK_STATUS
amit.ghosh258d14c2020-10-02 15:13:38 +0200376 reason := dmi.Reason_UNDEFINED_REASON
377
378 if c == nil {
Humera Kousera4442952020-11-23 23:51:19 +0530379 apiStatus = dmi.Status_ERROR_STATUS
amit.ghosh258d14c2020-10-02 15:13:38 +0200380 reason = dmi.Reason_UNKNOWN_DEVICE
381 }
382
383 response := &dmi.HWComponentInfoGetResponse{
384 Status: apiStatus,
385 Reason: reason,
386 Component: c,
387 }
388
389 err := stream.Send(response)
390 if err != nil {
391 logger.Errorf("Error sending response to client, error: %v", err)
392 return status.Errorf(codes.Internal, "Error sending response to client "+err.Error())
393 }
394 return nil
395}
396
397//GetHWComponentInfo gets the details of a particular HW component
398func (dms *DmiAPIServer) GetHWComponentInfo(req *dmi.HWComponentInfoGetRequest, stream dmi.NativeHWManagementService_GetHWComponentInfoServer) error {
399 logger.Debugf("GetHWComponentInfo() invoked with request %+v", req)
400
401 if req == nil {
402 return status.Errorf(codes.FailedPrecondition, "can not entertain nil request")
403 }
404 if stream == nil {
405 logger.Errorf("stream to send is nil, not sending response from gRPC server ")
406 return status.Errorf(codes.Internal, "stream to send is nil, can not send response from gRPC server")
407 }
408
Humera Kouser18b275c2020-11-30 11:30:36 +0530409 //if component list is empty, return error
410 if dms.root == nil {
411 logger.Errorf("Error occurred, device is not managed")
412 return status.Errorf(codes.Internal, "Error occurred, device is not managed, please start managing device")
413 }
amit.ghosh258d14c2020-10-02 15:13:38 +0200414 // Search for the component and return it
Humera Kousera4442952020-11-23 23:51:19 +0530415 c := findComponent(dms.root.Children, req.ComponentUuid.Uuid)
416
amit.ghosh258d14c2020-10-02 15:13:38 +0200417 return sendGetHWComponentResponse(c, stream)
418}
419
420//SetHWComponentInfo sets the permissible attributes of a HW component
421func (dms *DmiAPIServer) SetHWComponentInfo(context.Context, *dmi.HWComponentInfoSetRequest) (*dmi.HWComponentInfoSetResponse, error) {
422 return nil, status.Errorf(codes.Unimplemented, "rpc SetHWComponentInfo not implemented")
423}
424
425//SetLoggingEndpoint sets the location to which logs need to be shipped
426func (dms *DmiAPIServer) SetLoggingEndpoint(context.Context, *dmi.SetLoggingEndpointRequest) (*dmi.SetRemoteEndpointResponse, error) {
427 return nil, status.Errorf(codes.Unimplemented, "rpc SetLoggingEndpoint not implemented")
428}
429
430//GetLoggingEndpoint gets the configured location to which the logs are being shipped
Humera Kousera4442952020-11-23 23:51:19 +0530431func (dms *DmiAPIServer) GetLoggingEndpoint(context.Context, *dmi.HardwareID) (*dmi.GetLoggingEndpointResponse, error) {
amit.ghosh258d14c2020-10-02 15:13:38 +0200432 return nil, status.Errorf(codes.Unimplemented, "rpc GetLoggingEndpoint not implemented")
433}
434
435//SetMsgBusEndpoint sets the location of the Message Bus to which events and metrics are shipped
Humera Kousera4442952020-11-23 23:51:19 +0530436func (dms *DmiAPIServer) SetMsgBusEndpoint(ctx context.Context, request *dmi.SetMsgBusEndpointRequest) (*dmi.SetRemoteEndpointResponse, error) {
437 logger.Debugf("SetMsgBusEndpoint() invoked with request: %+v and context: %v", request, ctx)
438 if request == nil || request.MsgbusEndpoint == "" {
439 return &dmi.SetRemoteEndpointResponse{Status: dmi.Status_ERROR_STATUS, Reason: dmi.Reason_KAFKA_ENDPOINT_ERROR},
440 status.Errorf(codes.FailedPrecondition, "request is nil")
441 }
442 olt := devices.GetOLT()
443 dms.kafkaEndpoint = request.MsgbusEndpoint
444
445 // close the old publisher
446 if dms.mPublisherCancelFunc != nil {
447 dms.mPublisherCancelFunc()
448 }
449
450 // initialize a new publisher
451 var nCtx context.Context
452 nCtx, dms.mPublisherCancelFunc = context.WithCancel(context.Background())
453 // initialize a publisher
454 if err := InitializeDMKafkaPublishers(sarama.NewAsyncProducer, olt.ID, dms.kafkaEndpoint); err == nil {
455 // start a go routine which will read from channel and publish on kafka
456 go DMKafkaPublisher(nCtx, dms.metricChannel, "dm.metrics")
457 } else {
458 logger.Errorf("Failed to start kafka publisher: %v", err)
459 return &dmi.SetRemoteEndpointResponse{Status: dmi.Status_ERROR_STATUS, Reason: dmi.Reason_KAFKA_ENDPOINT_ERROR}, err
460 }
461
462 return &dmi.SetRemoteEndpointResponse{Status: dmi.Status_OK_STATUS, Reason: dmi.Reason_UNDEFINED_REASON}, nil
amit.ghosh258d14c2020-10-02 15:13:38 +0200463}
464
465//GetMsgBusEndpoint gets the configured location to which the events and metrics are being shipped
466func (dms *DmiAPIServer) GetMsgBusEndpoint(context.Context, *empty.Empty) (*dmi.GetMsgBusEndpointResponse, error) {
Humera Kousera4442952020-11-23 23:51:19 +0530467 logger.Debugf("GetMsgBusEndpoint() invoked")
468 if dms.kafkaEndpoint != "" {
469 return &dmi.GetMsgBusEndpointResponse{
470 Status: dmi.Status_OK_STATUS,
471 Reason: dmi.Reason_UNDEFINED_REASON,
472 MsgbusEndpoint: dms.kafkaEndpoint,
473 }, nil
474 }
475 return &dmi.GetMsgBusEndpointResponse{
476 Status: dmi.Status_ERROR_STATUS,
477 Reason: dmi.Reason_KAFKA_ENDPOINT_ERROR,
478 MsgbusEndpoint: "",
479 }, nil
amit.ghosh258d14c2020-10-02 15:13:38 +0200480}
481
482//GetManagedDevices returns an object containing a list of devices managed by this entity
483func (dms *DmiAPIServer) GetManagedDevices(context.Context, *empty.Empty) (*dmi.ManagedDevicesResponse, error) {
484 retResponse := dmi.ManagedDevicesResponse{}
485 //If our uuid is empty, we return empty list; else we fill details and return
486 if dms.uuid != "" {
487 root := dmi.ModifiableComponent{
488 Name: dms.deviceName,
489 Uri: &dmi.Uri{
490 Uri: dms.ipAddress,
491 },
492 }
493
494 retResponse.Devices = append(retResponse.Devices, &root)
495 }
496
497 return &retResponse, nil
498}
Humera Kousera4442952020-11-23 23:51:19 +0530499
500//GetLogLevel Gets the configured log level for a certain entity on a certain device.
501func (dms *DmiAPIServer) GetLogLevel(context.Context, *dmi.GetLogLevelRequest) (*dmi.GetLogLevelResponse, error) {
502 return &dmi.GetLogLevelResponse{
503 Status: dmi.Status_OK_STATUS,
504 DeviceUuid: &dmi.Uuid{
505 Uuid: dms.uuid,
506 },
507 LogLevels: []*dmi.EntitiesLogLevel{},
508 }, nil
509}
510
511// SetLogLevel Sets the log level of the device, for each given entity to a certain level.
512func (dms *DmiAPIServer) SetLogLevel(context.Context, *dmi.SetLogLevelRequest) (*dmi.SetLogLevelResponse, error) {
513 return &dmi.SetLogLevelResponse{
514 Status: dmi.Status_OK_STATUS,
515 DeviceUuid: &dmi.Uuid{
516 Uuid: dms.uuid,
517 },
518 }, nil
519}
520
521// GetLoggableEntities Gets the entities of a device on which log can be configured.
522func (dms *DmiAPIServer) GetLoggableEntities(context.Context, *dmi.GetLoggableEntitiesRequest) (*dmi.GetLogLevelResponse, error) {
523 return &dmi.GetLogLevelResponse{
524 Status: dmi.Status_OK_STATUS,
525 DeviceUuid: &dmi.Uuid{
526 Uuid: dms.uuid,
527 },
528 LogLevels: []*dmi.EntitiesLogLevel{},
529 }, nil
530}