blob: 30c27f06c6e40e250b264588be58b6dff8c88526 [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
36func getUUID(seed string) string {
37 return guuid.NewMD5(guuid.Nil, []byte(seed)).String()
38}
39
40//StartManagingDevice establishes connection with the device and does checks to ascertain if the device with passed identity can be managed
41func (dms *DmiAPIServer) StartManagingDevice(req *dmi.ModifiableComponent, stream dmi.NativeHWManagementService_StartManagingDeviceServer) error {
42 //Get serial number and generate the UUID based on this serial number. Store this UUID in local cache
43 logger.Debugf("StartManagingDevice() invoked with request %+v", req)
44 if req == nil {
45 return status.Errorf(codes.FailedPrecondition, "request is empty")
46 }
47
48 if req.Name == "" {
49 return status.Errorf(codes.InvalidArgument, "'Name' can not be empty in the request")
50 }
51
52 olt := devices.GetOLT()
53
54 // Uri is the IP address
55 dms.ipAddress = req.GetUri().GetUri()
56 dms.deviceSerial = olt.SerialNumber
57 dms.deviceName = fmt.Sprintf("%s-%s", common.Config.Olt.Vendor, dms.deviceSerial)
58
59 dms.uuid = getUUID(dms.deviceSerial)
60
61 dms.ponTransceiverUuids = make([]string, olt.NumPon)
62 dms.ponTransceiverCageUuids = make([]string, olt.NumPon)
63
64 var components []*dmi.Component
65
66 // Create and store the component for transceivers and transceiver cages
67 for i := 0; i < olt.NumPon; i++ {
68 label := fmt.Sprintf("pon-%d", olt.Pons[i].ID)
69 dms.ponTransceiverUuids[i] = getUUID(dms.deviceSerial + label)
70 dms.ponTransceiverCageUuids[i] = getUUID(dms.deviceSerial + "cage" + label)
71
72 transName := fmt.Sprintf("sfp-%d", i)
73 cageName := fmt.Sprintf("cage-%d", i)
74
75 trans := dmi.Component{
76 Name: transName,
77 Class: dmi.ComponentType_COMPONENT_TYPE_TRANSCEIVER,
78 Description: "XGS-PON",
79 Uuid: &dmi.Uuid{
80 Uuid: dms.ponTransceiverUuids[i],
81 },
82 Parent: cageName,
83 }
84
85 cage := dmi.Component{
86 Name: cageName,
87 Class: dmi.ComponentType_COMPONENT_TYPE_CONTAINER,
88 Description: "cage",
89 Uuid: &dmi.Uuid{
90 Uuid: dms.ponTransceiverCageUuids[i],
91 },
92 Parent: dms.deviceName,
93 Children: []*dmi.Component{&trans},
94 }
95
96 components = append(components, &cage)
97 }
98
Humera Kousera4442952020-11-23 23:51:19 +053099 // create the fans
100 numFans := 2
101 fans := make([]*dmi.Component, numFans)
102
103 for i := 0; i < numFans; i++ {
104 fans[i] = createFanComponent(i + 1)
105 }
106 components = append(components, fans...)
107
108 // Create 1 disk, 1 Processor and 1 ram
109 components = append(components, createDiskComponent(0))
110 components = append(components, createProcessorComponent(0))
111 components = append(components, createMemoryComponent(0))
112 components = append(components, createInnerSurroundingTempComponentSensor(0))
113
114 // create the root component
115 dms.root = &dmi.Component{
116 Name: dms.deviceName,
117 Class: 0,
118 Description: "",
119 Parent: "",
120 ParentRelPos: 0,
121 Children: components,
122 SerialNum: dms.deviceSerial,
123 MfgName: common.Config.Olt.Vendor,
124 IsFru: false,
125 Uri: &dmi.Uri{
126 Uri: dms.ipAddress,
127 },
128 Uuid: &dmi.Uuid{
129 Uuid: dms.uuid,
130 },
131 State: &dmi.ComponentState{},
132 }
amit.ghosh258d14c2020-10-02 15:13:38 +0200133
134 logger.Debugf("Generated UUID for the uri %s is %s", dms.ipAddress, dms.uuid)
135 response := &dmi.StartManagingDeviceResponse{
Humera Kousera4442952020-11-23 23:51:19 +0530136 Status: dmi.Status_OK_STATUS,
amit.ghosh258d14c2020-10-02 15:13:38 +0200137 DeviceUuid: &dmi.Uuid{
138 Uuid: dms.uuid,
139 },
140 }
141
142 err := stream.Send(response)
143 if err != nil {
144 logger.Errorf("Error while sending response to client %v", err.Error())
145 return status.Errorf(codes.Unknown, err.Error())
146 }
147
148 return nil
149}
150
Humera Kousera4442952020-11-23 23:51:19 +0530151func createFanComponent(fanIdx int) *dmi.Component {
152 fanName := fmt.Sprintf("Thermal/Fans/System Fan/%d", fanIdx)
153 fanSerial := fmt.Sprintf("bbsim-fan-serial-%d", fanIdx)
154 return &dmi.Component{
155 Name: fanName,
156 Class: dmi.ComponentType_COMPONENT_TYPE_FAN,
157 Description: "bbsim-fan",
158 Parent: "",
159 ParentRelPos: 0,
160 SerialNum: fanSerial,
161 MfgName: "bbsim-fan",
162 IsFru: false,
163 Uuid: &dmi.Uuid{
164 Uuid: getUUID(fanName),
165 },
166 State: &dmi.ComponentState{},
167 }
168}
169
170func createProcessorComponent(cpuIdx int) *dmi.Component {
171 cpuName := fmt.Sprintf("Systems/1/Processors/%d", cpuIdx)
172 cpuSerial := fmt.Sprintf("bbsim-cpu-serial-%d", cpuIdx)
173 return &dmi.Component{
174 Name: cpuName,
175 Class: dmi.ComponentType_COMPONENT_TYPE_CPU,
176 Description: "bbsim-cpu",
177 Parent: "",
178 ParentRelPos: 0,
179 SerialNum: cpuSerial,
180 MfgName: "bbsim-cpu",
181 IsFru: false,
182 Uuid: &dmi.Uuid{
183 Uuid: getUUID(cpuName),
184 },
185 State: &dmi.ComponentState{},
186 }
187}
188
189func createMemoryComponent(memIdx int) *dmi.Component {
190 memName := fmt.Sprintf("Systems/1/Memory/%d", memIdx)
191 memSerial := fmt.Sprintf("bbsim-ram-serial-%d", memIdx)
192 return &dmi.Component{
193 Name: memName,
194 Class: dmi.ComponentType_COMPONENT_TYPE_MEMORY,
195 Description: "bbsim-ram",
196 Parent: "",
197 ParentRelPos: 0,
198 SerialNum: memSerial,
199 MfgName: "bbsim-ram",
200 IsFru: false,
201 Uuid: &dmi.Uuid{
202 Uuid: getUUID(memName),
203 },
204 State: &dmi.ComponentState{},
205 }
206}
207
208func createDiskComponent(diskIdx int) *dmi.Component {
209 diskName := fmt.Sprintf("Systems/1/Disk/%d", diskIdx)
210 diskSerial := fmt.Sprintf("bbsim-disk-serial-%d", diskIdx)
211 return &dmi.Component{
212 Name: diskName,
213 Class: dmi.ComponentType_COMPONENT_TYPE_STORAGE,
214 Description: "bbsim-disk",
215 Parent: "",
216 ParentRelPos: 0,
217 SerialNum: diskSerial,
218 MfgName: "bbsim-disk",
219 IsFru: false,
220 Uuid: &dmi.Uuid{
221 Uuid: getUUID(diskName),
222 },
223 State: &dmi.ComponentState{},
224 }
225}
226
227func createInnerSurroundingTempComponentSensor(sensorIdx int) *dmi.Component {
228 sensorName := fmt.Sprintf("Systems/1/Sensor/%d", sensorIdx)
229 sensorSerial := fmt.Sprintf("bbsim-sensor-istemp-serial-%d", sensorIdx)
230 return &dmi.Component{
231 Name: sensorName,
232 Class: dmi.ComponentType_COMPONENT_TYPE_SENSOR,
233 Description: "bbsim-istemp",
234 Parent: "",
235 ParentRelPos: 0,
236 SerialNum: sensorSerial,
237 MfgName: "bbsim-istemp",
238 IsFru: false,
239 Uuid: &dmi.Uuid{
240 Uuid: getUUID(sensorName),
241 },
242 State: &dmi.ComponentState{},
243 }
244}
245
amit.ghosh258d14c2020-10-02 15:13:38 +0200246//StopManagingDevice stops management of a device and cleans up any context and caches for that device
247func (dms *DmiAPIServer) StopManagingDevice(context.Context, *dmi.StopManagingDeviceRequest) (*dmi.StopManagingDeviceResponse, error) {
248 return nil, status.Errorf(codes.Unimplemented, "rpc StopManagingDevice not implemented")
249}
250
251//GetPhysicalInventory gets the HW inventory details of the Device
252func (dms *DmiAPIServer) GetPhysicalInventory(req *dmi.PhysicalInventoryRequest, stream dmi.NativeHWManagementService_GetPhysicalInventoryServer) error {
253 if req == nil || req.DeviceUuid == nil || req.DeviceUuid.Uuid == "" {
254 return status.Errorf(codes.InvalidArgument, "device-UUID missing in the request")
255 }
256
257 // Function to send the response back on the stream
258 sendResponseBackOnStream := func(stream dmi.NativeHWManagementService_GetPhysicalInventoryServer, msg *dmi.PhysicalInventoryResponse) error {
259 err := stream.Send(msg)
260 if err != nil {
261 logger.Errorf("Error sending response to client, error: %v", err)
262 return status.Errorf(codes.Internal, "Error sending response to client "+err.Error())
263 }
264 return nil
265 }
266
267 if req.DeviceUuid.Uuid != dms.uuid {
268 logger.Errorf("Requested uuid =%s, uuid of existing device = %s", req.DeviceUuid.Uuid, dms.uuid)
269 // Wrong uuid, return error
270 errResponse := &dmi.PhysicalInventoryResponse{
Humera Kousera4442952020-11-23 23:51:19 +0530271 Status: dmi.Status_ERROR_STATUS,
amit.ghosh258d14c2020-10-02 15:13:38 +0200272 Reason: dmi.Reason_UNKNOWN_DEVICE,
273 Inventory: &dmi.Hardware{},
274 }
275
276 return sendResponseBackOnStream(stream, errResponse)
277 }
278
279 response := &dmi.PhysicalInventoryResponse{
Humera Kousera4442952020-11-23 23:51:19 +0530280 Status: dmi.Status_OK_STATUS,
amit.ghosh258d14c2020-10-02 15:13:38 +0200281 Inventory: &dmi.Hardware{
282 LastChange: &timestamp.Timestamp{
283 Seconds: 0,
284 Nanos: 0,
285 },
Humera Kousera4442952020-11-23 23:51:19 +0530286 Root: dms.root,
amit.ghosh258d14c2020-10-02 15:13:38 +0200287 },
288 }
289 return sendResponseBackOnStream(stream, response)
290}
291
292//Contains tells whether arr contains element.
293func Contains(arr []string, element string) bool {
294 for _, item := range arr {
amit.ghosh258d14c2020-10-02 15:13:38 +0200295 if element == item {
296 return true
297 }
298 }
299 return false
300}
301
302func findComponent(l []*dmi.Component, compUUID string) *dmi.Component {
Humera Kousera4442952020-11-23 23:51:19 +0530303 var foundComp *dmi.Component
304
amit.ghosh258d14c2020-10-02 15:13:38 +0200305 for _, comp := range l {
306 logger.Debugf("findComponent slice comp = %v compUUID = %s", comp, compUUID)
307 if comp.Uuid.Uuid == compUUID {
308 return comp
309 }
310
Humera Kousera4442952020-11-23 23:51:19 +0530311 foundComp = findComponent(comp.GetChildren(), compUUID)
312 if foundComp != nil {
313 return foundComp
amit.ghosh258d14c2020-10-02 15:13:38 +0200314 }
315 }
316
317 return nil
318}
319
Humera Kousera4442952020-11-23 23:51:19 +0530320func findComponentsOfType(l []*dmi.Component, compType dmi.ComponentType) []*dmi.Component {
321 var comps []*dmi.Component
322 findComponents(l, compType, &comps)
323 return comps
324}
325
326func findComponents(l []*dmi.Component, compType dmi.ComponentType, collector *[]*dmi.Component) {
327
328 for _, comp := range l {
329 if comp.Class == compType {
330 *collector = append(*collector, comp)
331 //logger.Debugf("Added collector = %v", *collector)
332 }
333
334 findComponents(comp.GetChildren(), compType, collector)
335 }
336}
337
amit.ghosh258d14c2020-10-02 15:13:38 +0200338func sendGetHWComponentResponse(c *dmi.Component, stream dmi.NativeHWManagementService_GetHWComponentInfoServer) error {
Humera Kousera4442952020-11-23 23:51:19 +0530339 apiStatus := dmi.Status_OK_STATUS
amit.ghosh258d14c2020-10-02 15:13:38 +0200340 reason := dmi.Reason_UNDEFINED_REASON
341
342 if c == nil {
Humera Kousera4442952020-11-23 23:51:19 +0530343 apiStatus = dmi.Status_ERROR_STATUS
amit.ghosh258d14c2020-10-02 15:13:38 +0200344 reason = dmi.Reason_UNKNOWN_DEVICE
345 }
346
347 response := &dmi.HWComponentInfoGetResponse{
348 Status: apiStatus,
349 Reason: reason,
350 Component: c,
351 }
352
353 err := stream.Send(response)
354 if err != nil {
355 logger.Errorf("Error sending response to client, error: %v", err)
356 return status.Errorf(codes.Internal, "Error sending response to client "+err.Error())
357 }
358 return nil
359}
360
361//GetHWComponentInfo gets the details of a particular HW component
362func (dms *DmiAPIServer) GetHWComponentInfo(req *dmi.HWComponentInfoGetRequest, stream dmi.NativeHWManagementService_GetHWComponentInfoServer) error {
363 logger.Debugf("GetHWComponentInfo() invoked with request %+v", req)
364
365 if req == nil {
366 return status.Errorf(codes.FailedPrecondition, "can not entertain nil request")
367 }
368 if stream == nil {
369 logger.Errorf("stream to send is nil, not sending response from gRPC server ")
370 return status.Errorf(codes.Internal, "stream to send is nil, can not send response from gRPC server")
371 }
372
amit.ghosh258d14c2020-10-02 15:13:38 +0200373 // Search for the component and return it
Humera Kousera4442952020-11-23 23:51:19 +0530374 c := findComponent(dms.root.Children, req.ComponentUuid.Uuid)
375
amit.ghosh258d14c2020-10-02 15:13:38 +0200376 return sendGetHWComponentResponse(c, stream)
377}
378
379//SetHWComponentInfo sets the permissible attributes of a HW component
380func (dms *DmiAPIServer) SetHWComponentInfo(context.Context, *dmi.HWComponentInfoSetRequest) (*dmi.HWComponentInfoSetResponse, error) {
381 return nil, status.Errorf(codes.Unimplemented, "rpc SetHWComponentInfo not implemented")
382}
383
384//SetLoggingEndpoint sets the location to which logs need to be shipped
385func (dms *DmiAPIServer) SetLoggingEndpoint(context.Context, *dmi.SetLoggingEndpointRequest) (*dmi.SetRemoteEndpointResponse, error) {
386 return nil, status.Errorf(codes.Unimplemented, "rpc SetLoggingEndpoint not implemented")
387}
388
389//GetLoggingEndpoint gets the configured location to which the logs are being shipped
Humera Kousera4442952020-11-23 23:51:19 +0530390func (dms *DmiAPIServer) GetLoggingEndpoint(context.Context, *dmi.HardwareID) (*dmi.GetLoggingEndpointResponse, error) {
amit.ghosh258d14c2020-10-02 15:13:38 +0200391 return nil, status.Errorf(codes.Unimplemented, "rpc GetLoggingEndpoint not implemented")
392}
393
394//SetMsgBusEndpoint sets the location of the Message Bus to which events and metrics are shipped
Humera Kousera4442952020-11-23 23:51:19 +0530395func (dms *DmiAPIServer) SetMsgBusEndpoint(ctx context.Context, request *dmi.SetMsgBusEndpointRequest) (*dmi.SetRemoteEndpointResponse, error) {
396 logger.Debugf("SetMsgBusEndpoint() invoked with request: %+v and context: %v", request, ctx)
397 if request == nil || request.MsgbusEndpoint == "" {
398 return &dmi.SetRemoteEndpointResponse{Status: dmi.Status_ERROR_STATUS, Reason: dmi.Reason_KAFKA_ENDPOINT_ERROR},
399 status.Errorf(codes.FailedPrecondition, "request is nil")
400 }
401 olt := devices.GetOLT()
402 dms.kafkaEndpoint = request.MsgbusEndpoint
403
404 // close the old publisher
405 if dms.mPublisherCancelFunc != nil {
406 dms.mPublisherCancelFunc()
407 }
408
409 // initialize a new publisher
410 var nCtx context.Context
411 nCtx, dms.mPublisherCancelFunc = context.WithCancel(context.Background())
412 // initialize a publisher
413 if err := InitializeDMKafkaPublishers(sarama.NewAsyncProducer, olt.ID, dms.kafkaEndpoint); err == nil {
414 // start a go routine which will read from channel and publish on kafka
415 go DMKafkaPublisher(nCtx, dms.metricChannel, "dm.metrics")
416 } else {
417 logger.Errorf("Failed to start kafka publisher: %v", err)
418 return &dmi.SetRemoteEndpointResponse{Status: dmi.Status_ERROR_STATUS, Reason: dmi.Reason_KAFKA_ENDPOINT_ERROR}, err
419 }
420
421 return &dmi.SetRemoteEndpointResponse{Status: dmi.Status_OK_STATUS, Reason: dmi.Reason_UNDEFINED_REASON}, nil
amit.ghosh258d14c2020-10-02 15:13:38 +0200422}
423
424//GetMsgBusEndpoint gets the configured location to which the events and metrics are being shipped
425func (dms *DmiAPIServer) GetMsgBusEndpoint(context.Context, *empty.Empty) (*dmi.GetMsgBusEndpointResponse, error) {
Humera Kousera4442952020-11-23 23:51:19 +0530426 logger.Debugf("GetMsgBusEndpoint() invoked")
427 if dms.kafkaEndpoint != "" {
428 return &dmi.GetMsgBusEndpointResponse{
429 Status: dmi.Status_OK_STATUS,
430 Reason: dmi.Reason_UNDEFINED_REASON,
431 MsgbusEndpoint: dms.kafkaEndpoint,
432 }, nil
433 }
434 return &dmi.GetMsgBusEndpointResponse{
435 Status: dmi.Status_ERROR_STATUS,
436 Reason: dmi.Reason_KAFKA_ENDPOINT_ERROR,
437 MsgbusEndpoint: "",
438 }, nil
amit.ghosh258d14c2020-10-02 15:13:38 +0200439}
440
441//GetManagedDevices returns an object containing a list of devices managed by this entity
442func (dms *DmiAPIServer) GetManagedDevices(context.Context, *empty.Empty) (*dmi.ManagedDevicesResponse, error) {
443 retResponse := dmi.ManagedDevicesResponse{}
444 //If our uuid is empty, we return empty list; else we fill details and return
445 if dms.uuid != "" {
446 root := dmi.ModifiableComponent{
447 Name: dms.deviceName,
448 Uri: &dmi.Uri{
449 Uri: dms.ipAddress,
450 },
451 }
452
453 retResponse.Devices = append(retResponse.Devices, &root)
454 }
455
456 return &retResponse, nil
457}
Humera Kousera4442952020-11-23 23:51:19 +0530458
459//GetLogLevel Gets the configured log level for a certain entity on a certain device.
460func (dms *DmiAPIServer) GetLogLevel(context.Context, *dmi.GetLogLevelRequest) (*dmi.GetLogLevelResponse, error) {
461 return &dmi.GetLogLevelResponse{
462 Status: dmi.Status_OK_STATUS,
463 DeviceUuid: &dmi.Uuid{
464 Uuid: dms.uuid,
465 },
466 LogLevels: []*dmi.EntitiesLogLevel{},
467 }, nil
468}
469
470// SetLogLevel Sets the log level of the device, for each given entity to a certain level.
471func (dms *DmiAPIServer) SetLogLevel(context.Context, *dmi.SetLogLevelRequest) (*dmi.SetLogLevelResponse, error) {
472 return &dmi.SetLogLevelResponse{
473 Status: dmi.Status_OK_STATUS,
474 DeviceUuid: &dmi.Uuid{
475 Uuid: dms.uuid,
476 },
477 }, nil
478}
479
480// GetLoggableEntities Gets the entities of a device on which log can be configured.
481func (dms *DmiAPIServer) GetLoggableEntities(context.Context, *dmi.GetLoggableEntitiesRequest) (*dmi.GetLogLevelResponse, error) {
482 return &dmi.GetLogLevelResponse{
483 Status: dmi.Status_OK_STATUS,
484 DeviceUuid: &dmi.Uuid{
485 Uuid: dms.uuid,
486 },
487 LogLevels: []*dmi.EntitiesLogLevel{},
488 }, nil
489}