blob: da73300eeb730a87edfac6d63a44fb7c9508f271 [file] [log] [blame]
amit.ghosh258d14c2020-10-02 15:13:38 +02001/*
Joey Armstrong14628cd2023-01-10 08:38:31 -05002 * Copyright 2018-2023 Open Networking Foundation (ONF) and the ONF Contributors
amit.ghosh258d14c2020-10-02 15:13:38 +02003
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"
Elia Battistone8d1fa42022-04-01 10:47:37 +020024 log "github.com/sirupsen/logrus"
Humera Kousera4442952020-11-23 23:51:19 +053025
amit.ghosh258d14c2020-10-02 15:13:38 +020026 "github.com/golang/protobuf/ptypes/empty"
27 "github.com/golang/protobuf/ptypes/timestamp"
28 "github.com/opencord/bbsim/internal/bbsim/devices"
29 "github.com/opencord/bbsim/internal/common"
30 dmi "github.com/opencord/device-management-interface/go/dmi"
31
32 guuid "github.com/google/uuid"
33 "google.golang.org/grpc/codes"
34 "google.golang.org/grpc/status"
35)
36
Humera Kouser18b275c2020-11-30 11:30:36 +053037const (
hkouser24361d42020-12-14 19:21:47 +053038 kafkaChannelSize = 100
Humera Kouser18b275c2020-11-30 11:30:36 +053039)
40
amit.ghosh258d14c2020-10-02 15:13:38 +020041func getUUID(seed string) string {
42 return guuid.NewMD5(guuid.Nil, []byte(seed)).String()
43}
44
Elia Battistone8d1fa42022-04-01 10:47:37 +020045func getOltName() string {
46 return fmt.Sprintf("%s-%s", common.Config.Olt.Vendor, devices.GetOLT().SerialNumber)
47}
48
49func getOltUUID() *dmi.Uuid {
50 return &dmi.Uuid{
51 Uuid: getUUID(devices.GetOLT().SerialNumber),
52 }
53}
54
55func getCageName(id uint32) string {
56 return fmt.Sprintf("sfp-plus-transceiver-cage-%d", id)
57}
58
59func getCageUUID(id uint32) *dmi.Uuid {
60 return &dmi.Uuid{
61 Uuid: getUUID(fmt.Sprintf("%s-%s", devices.GetOLT().SerialNumber, getCageName(id))),
62 }
63}
64
65func getTransceiverName(id uint32) string {
66 return fmt.Sprintf("sfp-plus-%d", id)
67}
68
69func getTransceiverUUID(id uint32) *dmi.Uuid {
70 return &dmi.Uuid{
71 Uuid: getUUID(fmt.Sprintf("%s-%s", devices.GetOLT().SerialNumber, getTransceiverName(id))),
72 }
73}
74
75func getPonName(id uint32) string {
76 return fmt.Sprintf("pon-%d", id)
77}
78
79func getPonUUID(id uint32) *dmi.Uuid {
80 return &dmi.Uuid{
81 Uuid: getUUID(fmt.Sprintf("%s-%s", devices.GetOLT().SerialNumber, getPonName(id))),
82 }
83}
84
amit.ghosh258d14c2020-10-02 15:13:38 +020085//StartManagingDevice establishes connection with the device and does checks to ascertain if the device with passed identity can be managed
86func (dms *DmiAPIServer) StartManagingDevice(req *dmi.ModifiableComponent, stream dmi.NativeHWManagementService_StartManagingDeviceServer) error {
87 //Get serial number and generate the UUID based on this serial number. Store this UUID in local cache
88 logger.Debugf("StartManagingDevice() invoked with request %+v", req)
89 if req == nil {
90 return status.Errorf(codes.FailedPrecondition, "request is empty")
91 }
92
93 if req.Name == "" {
94 return status.Errorf(codes.InvalidArgument, "'Name' can not be empty in the request")
95 }
96
97 olt := devices.GetOLT()
98
99 // Uri is the IP address
100 dms.ipAddress = req.GetUri().GetUri()
amit.ghosh258d14c2020-10-02 15:13:38 +0200101
Elia Battistone8d1fa42022-04-01 10:47:37 +0200102 deviceName := getOltName()
103 dms.uuid = getOltUUID()
amit.ghosh258d14c2020-10-02 15:13:38 +0200104
Humera Kouser18b275c2020-11-30 11:30:36 +0530105 // Start device metrics generator
hkouser24361d42020-12-14 19:21:47 +0530106 dms.metricChannel = make(chan interface{}, kafkaChannelSize)
Humera Kouser18b275c2020-11-30 11:30:36 +0530107 StartMetricGenerator(dms)
108
hkouser24361d42020-12-14 19:21:47 +0530109 // Start device event generator
110 dms.eventChannel = make(chan interface{}, kafkaChannelSize)
111 StartEventsGenerator(dms)
112
amit.ghosh258d14c2020-10-02 15:13:38 +0200113 var components []*dmi.Component
114
115 // Create and store the component for transceivers and transceiver cages
Elia Battistone8d1fa42022-04-01 10:47:37 +0200116 for _, trans := range dms.Transceivers {
117 //Make one cage for each of the transceivers
118 cageName := getCageName(trans.ID)
amit.ghosh258d14c2020-10-02 15:13:38 +0200119
120 cage := dmi.Component{
121 Name: cageName,
122 Class: dmi.ComponentType_COMPONENT_TYPE_CONTAINER,
123 Description: "cage",
Elia Battistone8d1fa42022-04-01 10:47:37 +0200124 Uuid: getCageUUID(trans.ID),
125 Parent: deviceName,
126 Children: []*dmi.Component{},
127 }
128
129 //If the transceiver is not plugged in, only the empty cage is created
130 if trans.PluggedIn {
131 transComponent, err := createTransceiverComponent(trans, cageName)
132 if err != nil {
133 logger.Error(err)
134 continue
135 }
136 cage.Children = append(cage.Children, transComponent)
amit.ghosh258d14c2020-10-02 15:13:38 +0200137 }
138
139 components = append(components, &cage)
140 }
141
Humera Kousera4442952020-11-23 23:51:19 +0530142 // create the fans
143 numFans := 2
144 fans := make([]*dmi.Component, numFans)
145
146 for i := 0; i < numFans; i++ {
147 fans[i] = createFanComponent(i + 1)
148 }
149 components = append(components, fans...)
150
hkouser24361d42020-12-14 19:21:47 +0530151 // Create 1 disk, 1 processor, 1 ram, 1 temperature sensor and power supply unit
Humera Kousera4442952020-11-23 23:51:19 +0530152 components = append(components, createDiskComponent(0))
153 components = append(components, createProcessorComponent(0))
154 components = append(components, createMemoryComponent(0))
155 components = append(components, createInnerSurroundingTempComponentSensor(0))
hkouser24361d42020-12-14 19:21:47 +0530156 components = append(components, createPowerSupplyComponent(0))
Humera Kousera4442952020-11-23 23:51:19 +0530157
158 // create the root component
159 dms.root = &dmi.Component{
Elia Battistone8d1fa42022-04-01 10:47:37 +0200160 Name: deviceName,
Humera Kousera4442952020-11-23 23:51:19 +0530161 Class: 0,
162 Description: "",
163 Parent: "",
164 ParentRelPos: 0,
165 Children: components,
Elia Battistone8d1fa42022-04-01 10:47:37 +0200166 SerialNum: olt.SerialNumber,
Humera Kousera4442952020-11-23 23:51:19 +0530167 MfgName: common.Config.Olt.Vendor,
168 IsFru: false,
169 Uri: &dmi.Uri{
170 Uri: dms.ipAddress,
171 },
Elia Battistone8d1fa42022-04-01 10:47:37 +0200172 Uuid: dms.uuid,
Humera Kousera4442952020-11-23 23:51:19 +0530173 State: &dmi.ComponentState{},
174 }
amit.ghosh258d14c2020-10-02 15:13:38 +0200175
Elia Battistone8d1fa42022-04-01 10:47:37 +0200176 logger.Debugf("Generated UUID for the uri %s is %s", dms.ipAddress, dms.uuid.Uuid)
amit.ghosh258d14c2020-10-02 15:13:38 +0200177 response := &dmi.StartManagingDeviceResponse{
Elia Battistone8d1fa42022-04-01 10:47:37 +0200178 Status: dmi.Status_OK_STATUS,
179 DeviceUuid: dms.uuid,
amit.ghosh258d14c2020-10-02 15:13:38 +0200180 }
181
182 err := stream.Send(response)
183 if err != nil {
184 logger.Errorf("Error while sending response to client %v", err.Error())
185 return status.Errorf(codes.Unknown, err.Error())
186 }
187
188 return nil
189}
190
Elia Battistone8d1fa42022-04-01 10:47:37 +0200191func createTransceiverComponent(trans *Transceiver, cageName string) (*dmi.Component, error) {
192 portName := getPonName(trans.ID)
193
194 var rxWavelength, txWavelength []uint32
195
196 if len(trans.Pons) == 0 {
197 return nil, fmt.Errorf("No pons in list for transceiver %d", trans.ID)
198 } else if len(trans.Pons) <= 1 {
199 //Assuming a transceiver with only one PON
200 //has the technology of the PON
201
202 switch trans.Pons[0].Technology {
203 case common.GPON:
204 trans.Technology = dmi.TransceiverType_GPON
205 rxWavelength = []uint32{1490} // nanometers
206 txWavelength = []uint32{1550} // nanometers
207 case common.XGSPON:
208 trans.Technology = dmi.TransceiverType_XGSPON
209 rxWavelength = []uint32{1270} // nanometers
210 txWavelength = []uint32{1577} // nanometers
211 }
212 } else {
213 //Assuming more than one PON for the transceiver
214 //is COMBO PON
215
216 trans.Technology = dmi.TransceiverType_COMBO_GPON_XGSPON
217
218 rxWavelength = []uint32{1490, 1270} // nanometers
219 txWavelength = []uint32{1550, 1577} // nanometers
220 }
221
222 //Create all ports mapped to this transceiver
223 ports := []*dmi.Component{}
224 for _, pon := range trans.Pons {
225 var portProto dmi.PortComponentAttributes_Protocol
226
227 switch pon.Technology {
228 case common.GPON:
229 portProto = dmi.PortComponentAttributes_GPON
230 case common.XGSPON:
231 portProto = dmi.PortComponentAttributes_XGSPON
232 }
233
234 p := dmi.Component{
235 Name: portName,
236 Class: dmi.ComponentType_COMPONENT_TYPE_PORT,
237 Description: "bbsim-pon-port",
238 Uuid: getPonUUID(pon.ID),
239 Parent: trans.Name,
240 Specific: &dmi.Component_PortAttr{
241 PortAttr: &dmi.PortComponentAttributes{
242 Protocol: portProto,
243 },
244 },
245 }
246
247 ports = append(ports, &p)
248 }
249
250 transComponent := dmi.Component{
251 Name: trans.Name,
252 Class: dmi.ComponentType_COMPONENT_TYPE_TRANSCEIVER,
253 Description: "bbsim-transceiver",
254 Uuid: &dmi.Uuid{
255 Uuid: trans.Uuid,
256 },
257 Parent: cageName,
258 Specific: &dmi.Component_TransceiverAttr{
259 TransceiverAttr: &dmi.TransceiverComponentsAttributes{
260 FormFactor: dmi.TransceiverComponentsAttributes_SFP_PLUS,
261 TransType: trans.Technology,
262 MaxDistance: 10, // kilometers (see scale below)
263 MaxDistanceScale: dmi.ValueScale_VALUE_SCALE_KILO,
264 RxWavelength: rxWavelength,
265 TxWavelength: txWavelength,
266 WavelengthScale: dmi.ValueScale_VALUE_SCALE_NANO,
267 },
268 },
269 Children: ports,
270 }
271
272 return &transComponent, nil
273}
274
Humera Kousera4442952020-11-23 23:51:19 +0530275func createFanComponent(fanIdx int) *dmi.Component {
276 fanName := fmt.Sprintf("Thermal/Fans/System Fan/%d", fanIdx)
277 fanSerial := fmt.Sprintf("bbsim-fan-serial-%d", fanIdx)
278 return &dmi.Component{
279 Name: fanName,
280 Class: dmi.ComponentType_COMPONENT_TYPE_FAN,
281 Description: "bbsim-fan",
282 Parent: "",
283 ParentRelPos: 0,
284 SerialNum: fanSerial,
285 MfgName: "bbsim-fan",
286 IsFru: false,
287 Uuid: &dmi.Uuid{
288 Uuid: getUUID(fanName),
289 },
290 State: &dmi.ComponentState{},
291 }
292}
293
294func createProcessorComponent(cpuIdx int) *dmi.Component {
295 cpuName := fmt.Sprintf("Systems/1/Processors/%d", cpuIdx)
296 cpuSerial := fmt.Sprintf("bbsim-cpu-serial-%d", cpuIdx)
297 return &dmi.Component{
298 Name: cpuName,
299 Class: dmi.ComponentType_COMPONENT_TYPE_CPU,
300 Description: "bbsim-cpu",
301 Parent: "",
302 ParentRelPos: 0,
303 SerialNum: cpuSerial,
304 MfgName: "bbsim-cpu",
305 IsFru: false,
306 Uuid: &dmi.Uuid{
307 Uuid: getUUID(cpuName),
308 },
309 State: &dmi.ComponentState{},
310 }
311}
312
313func createMemoryComponent(memIdx int) *dmi.Component {
314 memName := fmt.Sprintf("Systems/1/Memory/%d", memIdx)
315 memSerial := fmt.Sprintf("bbsim-ram-serial-%d", memIdx)
316 return &dmi.Component{
317 Name: memName,
318 Class: dmi.ComponentType_COMPONENT_TYPE_MEMORY,
319 Description: "bbsim-ram",
320 Parent: "",
321 ParentRelPos: 0,
322 SerialNum: memSerial,
323 MfgName: "bbsim-ram",
324 IsFru: false,
325 Uuid: &dmi.Uuid{
326 Uuid: getUUID(memName),
327 },
328 State: &dmi.ComponentState{},
329 }
330}
331
332func createDiskComponent(diskIdx int) *dmi.Component {
333 diskName := fmt.Sprintf("Systems/1/Disk/%d", diskIdx)
334 diskSerial := fmt.Sprintf("bbsim-disk-serial-%d", diskIdx)
335 return &dmi.Component{
336 Name: diskName,
337 Class: dmi.ComponentType_COMPONENT_TYPE_STORAGE,
338 Description: "bbsim-disk",
339 Parent: "",
340 ParentRelPos: 0,
341 SerialNum: diskSerial,
342 MfgName: "bbsim-disk",
343 IsFru: false,
344 Uuid: &dmi.Uuid{
345 Uuid: getUUID(diskName),
346 },
347 State: &dmi.ComponentState{},
348 }
349}
350
351func createInnerSurroundingTempComponentSensor(sensorIdx int) *dmi.Component {
352 sensorName := fmt.Sprintf("Systems/1/Sensor/%d", sensorIdx)
353 sensorSerial := fmt.Sprintf("bbsim-sensor-istemp-serial-%d", sensorIdx)
354 return &dmi.Component{
355 Name: sensorName,
356 Class: dmi.ComponentType_COMPONENT_TYPE_SENSOR,
357 Description: "bbsim-istemp",
358 Parent: "",
359 ParentRelPos: 0,
360 SerialNum: sensorSerial,
361 MfgName: "bbsim-istemp",
362 IsFru: false,
363 Uuid: &dmi.Uuid{
364 Uuid: getUUID(sensorName),
365 },
366 State: &dmi.ComponentState{},
367 }
368}
369
hkouser24361d42020-12-14 19:21:47 +0530370func createPowerSupplyComponent(psuIdx int) *dmi.Component {
371 psuName := fmt.Sprintf("Thermal/PSU/SystemPSU/%d", psuIdx)
372 psuSerial := fmt.Sprintf("bbsim-psu-serial-%d", psuIdx)
373 return &dmi.Component{
374 Name: psuName,
375 Class: dmi.ComponentType_COMPONENT_TYPE_POWER_SUPPLY,
376 Description: "bbsim-psu",
377 Parent: "",
378 ParentRelPos: 0,
379 SerialNum: psuSerial,
380 MfgName: "bbsim-psu",
381 IsFru: false,
382 Uuid: &dmi.Uuid{
383 Uuid: getUUID(psuName),
384 },
385 State: &dmi.ComponentState{},
386 }
387}
388
Elia Battistone8d1fa42022-04-01 10:47:37 +0200389func PlugoutTransceiverComponent(transId uint32, dms *DmiAPIServer) error {
390 if dms == nil {
391 return fmt.Errorf("Nil API server")
392 }
393
394 if dms.root == nil {
395 return fmt.Errorf("Device management not started")
396 }
397
398 trans, err := getTransceiverWithId(transId, dms)
399 if err != nil {
400 return err
401 }
402
403 if !trans.PluggedIn {
404 return fmt.Errorf("Cannot plug out transceiver with ID %d since it's not plugged in", transId)
405 }
406
407 //Find the transceiver node in the tree
408 targetUuid := getTransceiverUUID(transId)
409
410 var targetCage *dmi.Component
411 targetTransIndex := -1
412
413loop:
414 for _, rootChild := range dms.root.Children {
415 if rootChild.Uuid.Uuid == getCageUUID(transId).Uuid {
416 currentCage := rootChild
417
418 for j, cageChild := range currentCage.Children {
419 if cageChild.Uuid.Uuid == targetUuid.Uuid {
420 targetCage = currentCage
421 targetTransIndex = j
422 break loop
423 }
424 }
425 }
426 }
427
428 if targetCage == nil || targetTransIndex == -1 {
429 return fmt.Errorf("Cannot find transceiver with id %d", transId)
430 }
431
432 //Remove transceiver
433 targetCage.Children = append(targetCage.Children[:targetTransIndex], targetCage.Children[targetTransIndex+1:]...)
434 logger.WithFields(log.Fields{
435 "transId": transId,
436 "cageName": targetCage.Name,
437 "cageChildren": targetCage.Children,
438 }).Debug("Removed transceiver from DMI inventory")
439
440 //Change plugged-in state
441 trans.PluggedIn = false
442
443 return nil
444}
445
446func PluginTransceiverComponent(transId uint32, dms *DmiAPIServer) error {
447 if dms == nil {
448 return fmt.Errorf("Nil API server")
449 }
450
451 if dms.root == nil {
452 return fmt.Errorf("Device management not started")
453 }
454
455 trans, err := getTransceiverWithId(transId, dms)
456 if err != nil {
457 return err
458 }
459
460 if trans.PluggedIn {
461 return fmt.Errorf("Cannot plug in transceiver with ID %d since it's already plugged in", transId)
462 }
463
464 //Find transceiver node in the tree
465 var targetCage *dmi.Component
466
467 for _, rootChild := range dms.root.Children {
468 if rootChild.Uuid.Uuid == getCageUUID(transId).Uuid {
469 targetCage = rootChild
470 break
471 }
472 }
473
474 if targetCage == nil {
475 return fmt.Errorf("Cannot find cage for transceiver with id %d", transId)
476 }
477
478 //Add transceiver
479 transComponent, err := createTransceiverComponent(trans, targetCage.Name)
480 if err != nil {
481 return err
482 }
483
484 targetCage.Children = append(targetCage.Children, transComponent)
485
486 logger.WithFields(log.Fields{
487 "transId": transId,
488 "cageName": targetCage.Name,
489 "cageChildren": targetCage.Children,
490 }).Debug("Added transceiver to DMI inventory")
491
492 //Change plugged-in state
493 trans.PluggedIn = true
494
495 return nil
496}
497
amit.ghosh258d14c2020-10-02 15:13:38 +0200498//StopManagingDevice stops management of a device and cleans up any context and caches for that device
Humera Kouser18b275c2020-11-30 11:30:36 +0530499func (dms *DmiAPIServer) StopManagingDevice(ctx context.Context, req *dmi.StopManagingDeviceRequest) (*dmi.StopManagingDeviceResponse, error) {
500 logger.Debugf("StopManagingDevice API invoked")
501 if req == nil {
ssiddiqui6ca40702021-03-08 18:20:21 +0530502 return &dmi.StopManagingDeviceResponse{Status: dmi.Status_ERROR_STATUS, Reason: dmi.StopManagingDeviceResponse_UNDEFINED_REASON}, status.Errorf(codes.FailedPrecondition, "request is empty")
Humera Kouser18b275c2020-11-30 11:30:36 +0530503 }
504
505 if req.Name == "" {
ssiddiqui6ca40702021-03-08 18:20:21 +0530506 return &dmi.StopManagingDeviceResponse{Status: dmi.Status_ERROR_STATUS, Reason: dmi.StopManagingDeviceResponse_UNKNOWN_DEVICE},
Humera Kouser18b275c2020-11-30 11:30:36 +0530507 status.Errorf(codes.InvalidArgument, "'Name' can not be empty in the request")
508 }
509
510 // Stop the components/go routines created
511 StopMetricGenerator()
512
513 if dms.mPublisherCancelFunc != nil {
514 dms.mPublisherCancelFunc()
515 }
516
Humera Kouser18b275c2020-11-30 11:30:36 +0530517 dms.kafkaEndpoint = ""
518 dms.ipAddress = ""
Elia Battistone8d1fa42022-04-01 10:47:37 +0200519 dms.uuid = nil
Humera Kouser18b275c2020-11-30 11:30:36 +0530520 dms.root = nil
521 dms.metricChannel = nil
522
Elia Battistone8d1fa42022-04-01 10:47:37 +0200523 //Don't clear the Transceivers, so that they will survive
524 //new StartManagingDevice calls
525
Humera Kouser18b275c2020-11-30 11:30:36 +0530526 logger.Infof("Stopped managing the device")
527 return &dmi.StopManagingDeviceResponse{Status: dmi.Status_OK_STATUS}, nil
amit.ghosh258d14c2020-10-02 15:13:38 +0200528}
529
530//GetPhysicalInventory gets the HW inventory details of the Device
531func (dms *DmiAPIServer) GetPhysicalInventory(req *dmi.PhysicalInventoryRequest, stream dmi.NativeHWManagementService_GetPhysicalInventoryServer) error {
532 if req == nil || req.DeviceUuid == nil || req.DeviceUuid.Uuid == "" {
533 return status.Errorf(codes.InvalidArgument, "device-UUID missing in the request")
534 }
535
536 // Function to send the response back on the stream
537 sendResponseBackOnStream := func(stream dmi.NativeHWManagementService_GetPhysicalInventoryServer, msg *dmi.PhysicalInventoryResponse) error {
538 err := stream.Send(msg)
539 if err != nil {
540 logger.Errorf("Error sending response to client, error: %v", err)
541 return status.Errorf(codes.Internal, "Error sending response to client "+err.Error())
542 }
543 return nil
544 }
545
Elia Battistone8d1fa42022-04-01 10:47:37 +0200546 if req.DeviceUuid.Uuid != dms.uuid.Uuid {
547 logger.Errorf("Requested uuid =%s, uuid of existing device = %s", req.DeviceUuid.Uuid, dms.uuid.Uuid)
amit.ghosh258d14c2020-10-02 15:13:38 +0200548 // Wrong uuid, return error
549 errResponse := &dmi.PhysicalInventoryResponse{
Humera Kousera4442952020-11-23 23:51:19 +0530550 Status: dmi.Status_ERROR_STATUS,
ssiddiqui6ca40702021-03-08 18:20:21 +0530551 Reason: dmi.PhysicalInventoryResponse_UNKNOWN_DEVICE,
amit.ghosh258d14c2020-10-02 15:13:38 +0200552 Inventory: &dmi.Hardware{},
553 }
554
555 return sendResponseBackOnStream(stream, errResponse)
556 }
557
558 response := &dmi.PhysicalInventoryResponse{
Humera Kousera4442952020-11-23 23:51:19 +0530559 Status: dmi.Status_OK_STATUS,
amit.ghosh258d14c2020-10-02 15:13:38 +0200560 Inventory: &dmi.Hardware{
561 LastChange: &timestamp.Timestamp{
562 Seconds: 0,
563 Nanos: 0,
564 },
Humera Kousera4442952020-11-23 23:51:19 +0530565 Root: dms.root,
amit.ghosh258d14c2020-10-02 15:13:38 +0200566 },
567 }
568 return sendResponseBackOnStream(stream, response)
569}
570
571//Contains tells whether arr contains element.
572func Contains(arr []string, element string) bool {
573 for _, item := range arr {
amit.ghosh258d14c2020-10-02 15:13:38 +0200574 if element == item {
575 return true
576 }
577 }
578 return false
579}
580
581func findComponent(l []*dmi.Component, compUUID string) *dmi.Component {
Humera Kousera4442952020-11-23 23:51:19 +0530582 var foundComp *dmi.Component
583
amit.ghosh258d14c2020-10-02 15:13:38 +0200584 for _, comp := range l {
585 logger.Debugf("findComponent slice comp = %v compUUID = %s", comp, compUUID)
586 if comp.Uuid.Uuid == compUUID {
587 return comp
588 }
589
Humera Kousera4442952020-11-23 23:51:19 +0530590 foundComp = findComponent(comp.GetChildren(), compUUID)
591 if foundComp != nil {
592 return foundComp
amit.ghosh258d14c2020-10-02 15:13:38 +0200593 }
594 }
595
596 return nil
597}
598
Humera Kousera4442952020-11-23 23:51:19 +0530599func findComponentsOfType(l []*dmi.Component, compType dmi.ComponentType) []*dmi.Component {
600 var comps []*dmi.Component
601 findComponents(l, compType, &comps)
602 return comps
603}
604
605func findComponents(l []*dmi.Component, compType dmi.ComponentType, collector *[]*dmi.Component) {
606
607 for _, comp := range l {
608 if comp.Class == compType {
609 *collector = append(*collector, comp)
610 //logger.Debugf("Added collector = %v", *collector)
611 }
612
613 findComponents(comp.GetChildren(), compType, collector)
614 }
615}
616
amit.ghosh258d14c2020-10-02 15:13:38 +0200617func sendGetHWComponentResponse(c *dmi.Component, stream dmi.NativeHWManagementService_GetHWComponentInfoServer) error {
Humera Kousera4442952020-11-23 23:51:19 +0530618 apiStatus := dmi.Status_OK_STATUS
ssiddiqui6ca40702021-03-08 18:20:21 +0530619 reason := dmi.HWComponentInfoGetResponse_UNDEFINED_REASON
amit.ghosh258d14c2020-10-02 15:13:38 +0200620
621 if c == nil {
Humera Kousera4442952020-11-23 23:51:19 +0530622 apiStatus = dmi.Status_ERROR_STATUS
ssiddiqui6ca40702021-03-08 18:20:21 +0530623 reason = dmi.HWComponentInfoGetResponse_UNKNOWN_DEVICE
amit.ghosh258d14c2020-10-02 15:13:38 +0200624 }
625
626 response := &dmi.HWComponentInfoGetResponse{
627 Status: apiStatus,
628 Reason: reason,
629 Component: c,
630 }
631
632 err := stream.Send(response)
633 if err != nil {
634 logger.Errorf("Error sending response to client, error: %v", err)
635 return status.Errorf(codes.Internal, "Error sending response to client "+err.Error())
636 }
637 return nil
638}
639
640//GetHWComponentInfo gets the details of a particular HW component
641func (dms *DmiAPIServer) GetHWComponentInfo(req *dmi.HWComponentInfoGetRequest, stream dmi.NativeHWManagementService_GetHWComponentInfoServer) error {
642 logger.Debugf("GetHWComponentInfo() invoked with request %+v", req)
643
644 if req == nil {
645 return status.Errorf(codes.FailedPrecondition, "can not entertain nil request")
646 }
647 if stream == nil {
648 logger.Errorf("stream to send is nil, not sending response from gRPC server ")
649 return status.Errorf(codes.Internal, "stream to send is nil, can not send response from gRPC server")
650 }
651
Humera Kouser18b275c2020-11-30 11:30:36 +0530652 //if component list is empty, return error
653 if dms.root == nil {
654 logger.Errorf("Error occurred, device is not managed")
655 return status.Errorf(codes.Internal, "Error occurred, device is not managed, please start managing device")
656 }
amit.ghosh258d14c2020-10-02 15:13:38 +0200657 // Search for the component and return it
Humera Kousera4442952020-11-23 23:51:19 +0530658 c := findComponent(dms.root.Children, req.ComponentUuid.Uuid)
659
amit.ghosh258d14c2020-10-02 15:13:38 +0200660 return sendGetHWComponentResponse(c, stream)
661}
662
663//SetHWComponentInfo sets the permissible attributes of a HW component
664func (dms *DmiAPIServer) SetHWComponentInfo(context.Context, *dmi.HWComponentInfoSetRequest) (*dmi.HWComponentInfoSetResponse, error) {
665 return nil, status.Errorf(codes.Unimplemented, "rpc SetHWComponentInfo not implemented")
666}
667
668//SetLoggingEndpoint sets the location to which logs need to be shipped
ssiddiqui6ca40702021-03-08 18:20:21 +0530669func (dms *DmiAPIServer) SetLoggingEndpoint(_ context.Context, request *dmi.SetLoggingEndpointRequest) (*dmi.SetRemoteEndpointResponse, error) {
670 logger.Debugf("SetLoggingEndpoint called with request %+v", request)
671 errRetFunc := func(stat dmi.Status, reason dmi.SetRemoteEndpointResponse_Reason) (*dmi.SetRemoteEndpointResponse, error) {
672 return &dmi.SetRemoteEndpointResponse{
673 Status: stat,
674 Reason: reason,
675 }, status.Errorf(codes.InvalidArgument, "invalid request")
676 }
677
678 //check the validity of the request
679 if request == nil {
680 return errRetFunc(dmi.Status_ERROR_STATUS, dmi.SetRemoteEndpointResponse_UNKNOWN_DEVICE)
681 }
682 if request.LoggingEndpoint == "" {
683 return errRetFunc(dmi.Status_ERROR_STATUS, dmi.SetRemoteEndpointResponse_LOGGING_ENDPOINT_ERROR)
684 }
685 if request.LoggingProtocol == "" {
686 return errRetFunc(dmi.Status_ERROR_STATUS, dmi.SetRemoteEndpointResponse_LOGGING_ENDPOINT_PROTOCOL_ERROR)
687 }
Elia Battistone8d1fa42022-04-01 10:47:37 +0200688 if request.DeviceUuid == nil || request.DeviceUuid.Uuid != dms.uuid.Uuid {
ssiddiqui6ca40702021-03-08 18:20:21 +0530689 return errRetFunc(dmi.Status_ERROR_STATUS, dmi.SetRemoteEndpointResponse_UNKNOWN_DEVICE)
690 }
691
692 dms.loggingEndpoint = request.LoggingEndpoint
693 dms.loggingProtocol = request.LoggingProtocol
694
695 return &dmi.SetRemoteEndpointResponse{
696 Status: dmi.Status_OK_STATUS,
697 }, nil
amit.ghosh258d14c2020-10-02 15:13:38 +0200698}
699
700//GetLoggingEndpoint gets the configured location to which the logs are being shipped
ssiddiqui6ca40702021-03-08 18:20:21 +0530701func (dms *DmiAPIServer) GetLoggingEndpoint(_ context.Context, request *dmi.HardwareID) (*dmi.GetLoggingEndpointResponse, error) {
702 logger.Debugf("GetLoggingEndpoint called with request %+v", request)
703 if request == nil || request.Uuid == nil || request.Uuid.Uuid == "" {
704 return &dmi.GetLoggingEndpointResponse{
705 Status: dmi.Status_ERROR_STATUS,
706 Reason: dmi.GetLoggingEndpointResponse_UNKNOWN_DEVICE,
707 }, status.Errorf(codes.InvalidArgument, "invalid request")
708 }
Elia Battistone8d1fa42022-04-01 10:47:37 +0200709 if request.Uuid.Uuid != dms.uuid.Uuid {
ssiddiqui6ca40702021-03-08 18:20:21 +0530710 return &dmi.GetLoggingEndpointResponse{
711 Status: dmi.Status_ERROR_STATUS,
712 Reason: dmi.GetLoggingEndpointResponse_UNKNOWN_DEVICE,
713 }, nil
714 }
715
716 return &dmi.GetLoggingEndpointResponse{
717 Status: dmi.Status_OK_STATUS,
718 LoggingEndpoint: dms.loggingEndpoint,
719 LoggingProtocol: dms.loggingProtocol,
720 }, nil
amit.ghosh258d14c2020-10-02 15:13:38 +0200721}
722
723//SetMsgBusEndpoint sets the location of the Message Bus to which events and metrics are shipped
Humera Kousera4442952020-11-23 23:51:19 +0530724func (dms *DmiAPIServer) SetMsgBusEndpoint(ctx context.Context, request *dmi.SetMsgBusEndpointRequest) (*dmi.SetRemoteEndpointResponse, error) {
725 logger.Debugf("SetMsgBusEndpoint() invoked with request: %+v and context: %v", request, ctx)
726 if request == nil || request.MsgbusEndpoint == "" {
ssiddiqui6ca40702021-03-08 18:20:21 +0530727 return &dmi.SetRemoteEndpointResponse{Status: dmi.Status_ERROR_STATUS, Reason: dmi.SetRemoteEndpointResponse_MSGBUS_ENDPOINT_ERROR},
Humera Kousera4442952020-11-23 23:51:19 +0530728 status.Errorf(codes.FailedPrecondition, "request is nil")
729 }
730 olt := devices.GetOLT()
731 dms.kafkaEndpoint = request.MsgbusEndpoint
732
733 // close the old publisher
734 if dms.mPublisherCancelFunc != nil {
735 dms.mPublisherCancelFunc()
736 }
737
738 // initialize a new publisher
739 var nCtx context.Context
740 nCtx, dms.mPublisherCancelFunc = context.WithCancel(context.Background())
741 // initialize a publisher
742 if err := InitializeDMKafkaPublishers(sarama.NewAsyncProducer, olt.ID, dms.kafkaEndpoint); err == nil {
hkouser24361d42020-12-14 19:21:47 +0530743 // start a go routine which will read from channel and publish on kafka topic dm.metrics
Humera Kousera4442952020-11-23 23:51:19 +0530744 go DMKafkaPublisher(nCtx, dms.metricChannel, "dm.metrics")
hkouser24361d42020-12-14 19:21:47 +0530745 // start a go routine which will read from channel and publish on kafka topic dm.events
746 go DMKafkaPublisher(nCtx, dms.eventChannel, "dm.events")
Humera Kousera4442952020-11-23 23:51:19 +0530747 } else {
hkouser24361d42020-12-14 19:21:47 +0530748 logger.Errorf("Failed to start metric kafka publisher: %v", err)
ssiddiqui6ca40702021-03-08 18:20:21 +0530749 return &dmi.SetRemoteEndpointResponse{Status: dmi.Status_ERROR_STATUS, Reason: dmi.SetRemoteEndpointResponse_MSGBUS_ENDPOINT_ERROR}, err
Humera Kousera4442952020-11-23 23:51:19 +0530750 }
751
ssiddiqui6ca40702021-03-08 18:20:21 +0530752 return &dmi.SetRemoteEndpointResponse{Status: dmi.Status_OK_STATUS, Reason: dmi.SetRemoteEndpointResponse_UNDEFINED_REASON}, nil
amit.ghosh258d14c2020-10-02 15:13:38 +0200753}
754
755//GetMsgBusEndpoint gets the configured location to which the events and metrics are being shipped
756func (dms *DmiAPIServer) GetMsgBusEndpoint(context.Context, *empty.Empty) (*dmi.GetMsgBusEndpointResponse, error) {
Humera Kousera4442952020-11-23 23:51:19 +0530757 logger.Debugf("GetMsgBusEndpoint() invoked")
758 if dms.kafkaEndpoint != "" {
759 return &dmi.GetMsgBusEndpointResponse{
760 Status: dmi.Status_OK_STATUS,
ssiddiqui6ca40702021-03-08 18:20:21 +0530761 Reason: dmi.GetMsgBusEndpointResponse_UNDEFINED_REASON,
Humera Kousera4442952020-11-23 23:51:19 +0530762 MsgbusEndpoint: dms.kafkaEndpoint,
763 }, nil
764 }
765 return &dmi.GetMsgBusEndpointResponse{
766 Status: dmi.Status_ERROR_STATUS,
ssiddiqui6ca40702021-03-08 18:20:21 +0530767 Reason: dmi.GetMsgBusEndpointResponse_INTERNAL_ERROR,
Humera Kousera4442952020-11-23 23:51:19 +0530768 MsgbusEndpoint: "",
769 }, nil
amit.ghosh258d14c2020-10-02 15:13:38 +0200770}
771
772//GetManagedDevices returns an object containing a list of devices managed by this entity
773func (dms *DmiAPIServer) GetManagedDevices(context.Context, *empty.Empty) (*dmi.ManagedDevicesResponse, error) {
774 retResponse := dmi.ManagedDevicesResponse{}
775 //If our uuid is empty, we return empty list; else we fill details and return
Elia Battistone8d1fa42022-04-01 10:47:37 +0200776 if dms.root != nil {
Hardik Windlass63e35cf2022-02-03 05:58:09 +0000777 root := dmi.ManagedDeviceInfo{
778 Info: &dmi.ModifiableComponent{
Elia Battistone8d1fa42022-04-01 10:47:37 +0200779 Name: getOltName(),
Hardik Windlass63e35cf2022-02-03 05:58:09 +0000780 Uri: &dmi.Uri{
781 Uri: dms.ipAddress,
782 },
783 },
Elia Battistone8d1fa42022-04-01 10:47:37 +0200784 DeviceUuid: dms.uuid,
amit.ghosh258d14c2020-10-02 15:13:38 +0200785 }
786
787 retResponse.Devices = append(retResponse.Devices, &root)
788 }
Hardik Windlassa738f902022-02-11 07:50:38 +0000789 retResponse.Status = dmi.Status_OK_STATUS
amit.ghosh258d14c2020-10-02 15:13:38 +0200790
791 return &retResponse, nil
792}
Humera Kousera4442952020-11-23 23:51:19 +0530793
794//GetLogLevel Gets the configured log level for a certain entity on a certain device.
795func (dms *DmiAPIServer) GetLogLevel(context.Context, *dmi.GetLogLevelRequest) (*dmi.GetLogLevelResponse, error) {
796 return &dmi.GetLogLevelResponse{
Elia Battistone8d1fa42022-04-01 10:47:37 +0200797 Status: dmi.Status_OK_STATUS,
798 DeviceUuid: dms.uuid,
799 LogLevels: []*dmi.EntitiesLogLevel{},
Humera Kousera4442952020-11-23 23:51:19 +0530800 }, nil
801}
802
803// SetLogLevel Sets the log level of the device, for each given entity to a certain level.
804func (dms *DmiAPIServer) SetLogLevel(context.Context, *dmi.SetLogLevelRequest) (*dmi.SetLogLevelResponse, error) {
805 return &dmi.SetLogLevelResponse{
Elia Battistone8d1fa42022-04-01 10:47:37 +0200806 Status: dmi.Status_OK_STATUS,
807 DeviceUuid: dms.uuid,
Humera Kousera4442952020-11-23 23:51:19 +0530808 }, nil
809}
810
811// GetLoggableEntities Gets the entities of a device on which log can be configured.
812func (dms *DmiAPIServer) GetLoggableEntities(context.Context, *dmi.GetLoggableEntitiesRequest) (*dmi.GetLogLevelResponse, error) {
813 return &dmi.GetLogLevelResponse{
Elia Battistone8d1fa42022-04-01 10:47:37 +0200814 Status: dmi.Status_OK_STATUS,
815 DeviceUuid: dms.uuid,
816 LogLevels: []*dmi.EntitiesLogLevel{},
Humera Kousera4442952020-11-23 23:51:19 +0530817 }, nil
818}
Hardik Windlass63e35cf2022-02-03 05:58:09 +0000819
820// Performs the heartbeat check
821func (dms *DmiAPIServer) HeartbeatCheck(context.Context, *empty.Empty) (*dmi.Heartbeat, error) {
822 return nil, status.Errorf(codes.Unimplemented, "rpc HeartbeatCheck not implemented")
823}
824
825// Performs the reboot of the device
826func (dms *DmiAPIServer) RebootDevice(context.Context, *dmi.RebootDeviceRequest) (*dmi.RebootDeviceResponse, error) {
827 return nil, status.Errorf(codes.Unimplemented, "rpc RebootDevice not implemented")
828}