[VOL-3695]: Support to create some of the OLT device events over the Device Management Interface
1. Following events and its corresponding recovered event creation is supported :
EVENT_FAN_FAILURE
EVENT_PSU_FAILURE
EVENT_HW_DEVICE_TEMPERATURE_ABOVE_CRITICAL
2. Following DMI Native Events Management Service APIs are implemented:
ListEvents
UpdateEventsConfiguration
3. Updated docs/source/DMI_Server_README.md
Change-Id: Ibc48302b61fd52a2f83bd888731f611eaf6c4c37
diff --git a/Makefile b/Makefile
index 3ffc3a9..481a9ff 100644
--- a/Makefile
+++ b/Makefile
@@ -39,7 +39,7 @@
# Public targets
all: help
-protos: api/bbsim/bbsim.pb.go api/bbsim/bbsim.pb.gw.go api/legacy/bbsim.pb.go api/legacy/bbsim.pb.gw.go # @HELP Build proto files
+protos: api/bbsim/bbsim.pb.go api/bbsim/bbsim.pb.gw.go api/legacy/bbsim.pb.go api/legacy/bbsim.pb.gw.go api/bbsim/bbsim_dmi.pb.go # @HELP Build proto files
.PHONY: build
build: protos build-bbsim build-bbsimctl build-bbr
@@ -244,6 +244,13 @@
--go_out=plugins=grpc:./ \
$<
+api/bbsim/bbsim_dmi.pb.go: api/bbsim/bbsim_dmi.proto setup_tools
+ @echo $@
+ @${PROTOC} -I. \
+ -I${GOOGLEAPI}/third_party/googleapis \
+ --go_out=plugins=grpc:./ \
+ $<
+
api/bbsim/bbsim.pb.go api/bbsim/bbsim.pb.gw.go: api/bbsim/bbsim.proto api/bbsim/bbsim.yaml setup_tools
@echo $@
@${PROTOC} -I. \
diff --git a/VERSION b/VERSION
index 347f583..9df886c 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.4.1
+1.4.2
diff --git a/api/bbsim/bbsim.pb.go b/api/bbsim/bbsim.pb.go
index ffa0154..a4b6f11 100644
--- a/api/bbsim/bbsim.pb.go
+++ b/api/bbsim/bbsim.pb.go
@@ -1419,7 +1419,7 @@
StopgRPCServer(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Response, error)
// Start the Openolt gRPC server
StartgRPCServer(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Response, error)
- // Start the Openolt gRPC server
+ // Restart the Openolt gRPC server after the given timeout
RestartgRPCServer(ctx context.Context, in *Timeout, opts ...grpc.CallOption) (*Response, error)
// Get status of an ONU by serial number
GetONU(ctx context.Context, in *ONURequest, opts ...grpc.CallOption) (*ONU, error)
@@ -1717,7 +1717,7 @@
StopgRPCServer(context.Context, *Empty) (*Response, error)
// Start the Openolt gRPC server
StartgRPCServer(context.Context, *Empty) (*Response, error)
- // Start the Openolt gRPC server
+ // Restart the Openolt gRPC server after the given timeout
RestartgRPCServer(context.Context, *Timeout) (*Response, error)
// Get status of an ONU by serial number
GetONU(context.Context, *ONURequest) (*ONU, error)
diff --git a/api/bbsim/bbsim_dmi.pb.go b/api/bbsim/bbsim_dmi.pb.go
new file mode 100644
index 0000000..7dd373d
--- /dev/null
+++ b/api/bbsim/bbsim_dmi.pb.go
@@ -0,0 +1,216 @@
+// Code generated by protoc-gen-go. DO NOT EDIT.
+// source: api/bbsim/bbsim_dmi.proto
+
+package bbsim
+
+import (
+ context "context"
+ fmt "fmt"
+ proto "github.com/golang/protobuf/proto"
+ grpc "google.golang.org/grpc"
+ codes "google.golang.org/grpc/codes"
+ status "google.golang.org/grpc/status"
+ math "math"
+)
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.ProtoPackageIsVersion3 // please upgrade the proto package
+
+type DmiEvent struct {
+ EventName string `protobuf:"bytes,1,opt,name=event_name,json=eventName,proto3" json:"event_name,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *DmiEvent) Reset() { *m = DmiEvent{} }
+func (m *DmiEvent) String() string { return proto.CompactTextString(m) }
+func (*DmiEvent) ProtoMessage() {}
+func (*DmiEvent) Descriptor() ([]byte, []int) {
+ return fileDescriptor_49e784b4938902cc, []int{0}
+}
+
+func (m *DmiEvent) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_DmiEvent.Unmarshal(m, b)
+}
+func (m *DmiEvent) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_DmiEvent.Marshal(b, m, deterministic)
+}
+func (m *DmiEvent) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_DmiEvent.Merge(m, src)
+}
+func (m *DmiEvent) XXX_Size() int {
+ return xxx_messageInfo_DmiEvent.Size(m)
+}
+func (m *DmiEvent) XXX_DiscardUnknown() {
+ xxx_messageInfo_DmiEvent.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_DmiEvent proto.InternalMessageInfo
+
+func (m *DmiEvent) GetEventName() string {
+ if m != nil {
+ return m.EventName
+ }
+ return ""
+}
+
+type DmiCreateEventResponse struct {
+ StatusCode int32 `protobuf:"varint,1,opt,name=status_code,json=statusCode,proto3" json:"status_code,omitempty"`
+ Message string `protobuf:"bytes,2,opt,name=message,proto3" json:"message,omitempty"`
+ XXX_NoUnkeyedLiteral struct{} `json:"-"`
+ XXX_unrecognized []byte `json:"-"`
+ XXX_sizecache int32 `json:"-"`
+}
+
+func (m *DmiCreateEventResponse) Reset() { *m = DmiCreateEventResponse{} }
+func (m *DmiCreateEventResponse) String() string { return proto.CompactTextString(m) }
+func (*DmiCreateEventResponse) ProtoMessage() {}
+func (*DmiCreateEventResponse) Descriptor() ([]byte, []int) {
+ return fileDescriptor_49e784b4938902cc, []int{1}
+}
+
+func (m *DmiCreateEventResponse) XXX_Unmarshal(b []byte) error {
+ return xxx_messageInfo_DmiCreateEventResponse.Unmarshal(m, b)
+}
+func (m *DmiCreateEventResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+ return xxx_messageInfo_DmiCreateEventResponse.Marshal(b, m, deterministic)
+}
+func (m *DmiCreateEventResponse) XXX_Merge(src proto.Message) {
+ xxx_messageInfo_DmiCreateEventResponse.Merge(m, src)
+}
+func (m *DmiCreateEventResponse) XXX_Size() int {
+ return xxx_messageInfo_DmiCreateEventResponse.Size(m)
+}
+func (m *DmiCreateEventResponse) XXX_DiscardUnknown() {
+ xxx_messageInfo_DmiCreateEventResponse.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_DmiCreateEventResponse proto.InternalMessageInfo
+
+func (m *DmiCreateEventResponse) GetStatusCode() int32 {
+ if m != nil {
+ return m.StatusCode
+ }
+ return 0
+}
+
+func (m *DmiCreateEventResponse) GetMessage() string {
+ if m != nil {
+ return m.Message
+ }
+ return ""
+}
+
+func init() {
+ proto.RegisterType((*DmiEvent)(nil), "bbsim.DmiEvent")
+ proto.RegisterType((*DmiCreateEventResponse)(nil), "bbsim.DmiCreateEventResponse")
+}
+
+func init() { proto.RegisterFile("api/bbsim/bbsim_dmi.proto", fileDescriptor_49e784b4938902cc) }
+
+var fileDescriptor_49e784b4938902cc = []byte{
+ // 190 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0x92, 0x4c, 0x2c, 0xc8, 0xd4,
+ 0x4f, 0x4a, 0x2a, 0xce, 0xcc, 0x85, 0x90, 0xf1, 0x29, 0xb9, 0x99, 0x7a, 0x05, 0x45, 0xf9, 0x25,
+ 0xf9, 0x42, 0xac, 0x60, 0x01, 0x25, 0x4d, 0x2e, 0x0e, 0x97, 0xdc, 0x4c, 0xd7, 0xb2, 0xd4, 0xbc,
+ 0x12, 0x21, 0x59, 0x2e, 0xae, 0x54, 0x10, 0x23, 0x3e, 0x2f, 0x31, 0x37, 0x55, 0x82, 0x51, 0x81,
+ 0x51, 0x83, 0x33, 0x88, 0x13, 0x2c, 0xe2, 0x97, 0x98, 0x9b, 0xaa, 0x14, 0xcc, 0x25, 0xe6, 0x92,
+ 0x9b, 0xe9, 0x5c, 0x94, 0x9a, 0x58, 0x92, 0x0a, 0xd6, 0x10, 0x94, 0x5a, 0x5c, 0x90, 0x9f, 0x57,
+ 0x9c, 0x2a, 0x24, 0xcf, 0xc5, 0x5d, 0x5c, 0x92, 0x58, 0x52, 0x5a, 0x1c, 0x9f, 0x9c, 0x9f, 0x02,
+ 0xd1, 0xc9, 0x1a, 0xc4, 0x05, 0x11, 0x72, 0xce, 0x4f, 0x49, 0x15, 0x92, 0xe0, 0x62, 0xcf, 0x4d,
+ 0x2d, 0x2e, 0x4e, 0x4c, 0x4f, 0x95, 0x60, 0x02, 0x1b, 0x0b, 0xe3, 0x1a, 0xf9, 0x70, 0x71, 0x3a,
+ 0x39, 0x41, 0x5d, 0x26, 0x64, 0xcf, 0xc5, 0x8d, 0x64, 0xbc, 0x10, 0xbf, 0x1e, 0xd8, 0x8d, 0x7a,
+ 0x30, 0x07, 0x4a, 0xc9, 0x22, 0x04, 0xb0, 0x38, 0x43, 0x89, 0x21, 0x89, 0x0d, 0xec, 0x37, 0x63,
+ 0x40, 0x00, 0x00, 0x00, 0xff, 0xff, 0x3f, 0x8b, 0xf2, 0xfd, 0xf8, 0x00, 0x00, 0x00,
+}
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ context.Context
+var _ grpc.ClientConn
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the grpc package it is being compiled against.
+const _ = grpc.SupportPackageIsVersion4
+
+// BBsimDmiClient is the client API for BBsimDmi service.
+//
+// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream.
+type BBsimDmiClient interface {
+ // Ask the DMI Server to create an event
+ CreateEvent(ctx context.Context, in *DmiEvent, opts ...grpc.CallOption) (*DmiCreateEventResponse, error)
+}
+
+type bBsimDmiClient struct {
+ cc *grpc.ClientConn
+}
+
+func NewBBsimDmiClient(cc *grpc.ClientConn) BBsimDmiClient {
+ return &bBsimDmiClient{cc}
+}
+
+func (c *bBsimDmiClient) CreateEvent(ctx context.Context, in *DmiEvent, opts ...grpc.CallOption) (*DmiCreateEventResponse, error) {
+ out := new(DmiCreateEventResponse)
+ err := c.cc.Invoke(ctx, "/bbsim.BBsim_dmi/CreateEvent", in, out, opts...)
+ if err != nil {
+ return nil, err
+ }
+ return out, nil
+}
+
+// BBsimDmiServer is the server API for BBsimDmi service.
+type BBsimDmiServer interface {
+ // Ask the DMI Server to create an event
+ CreateEvent(context.Context, *DmiEvent) (*DmiCreateEventResponse, error)
+}
+
+// UnimplementedBBsimDmiServer can be embedded to have forward compatible implementations.
+type UnimplementedBBsimDmiServer struct {
+}
+
+func (*UnimplementedBBsimDmiServer) CreateEvent(ctx context.Context, req *DmiEvent) (*DmiCreateEventResponse, error) {
+ return nil, status.Errorf(codes.Unimplemented, "method CreateEvent not implemented")
+}
+
+func RegisterBBsimDmiServer(s *grpc.Server, srv BBsimDmiServer) {
+ s.RegisterService(&_BBsimDmi_serviceDesc, srv)
+}
+
+func _BBsimDmi_CreateEvent_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+ in := new(DmiEvent)
+ if err := dec(in); err != nil {
+ return nil, err
+ }
+ if interceptor == nil {
+ return srv.(BBsimDmiServer).CreateEvent(ctx, in)
+ }
+ info := &grpc.UnaryServerInfo{
+ Server: srv,
+ FullMethod: "/bbsim.BBsim_dmi/CreateEvent",
+ }
+ handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+ return srv.(BBsimDmiServer).CreateEvent(ctx, req.(*DmiEvent))
+ }
+ return interceptor(ctx, in, info, handler)
+}
+
+var _BBsimDmi_serviceDesc = grpc.ServiceDesc{
+ ServiceName: "bbsim.BBsim_dmi",
+ HandlerType: (*BBsimDmiServer)(nil),
+ Methods: []grpc.MethodDesc{
+ {
+ MethodName: "CreateEvent",
+ Handler: _BBsimDmi_CreateEvent_Handler,
+ },
+ },
+ Streams: []grpc.StreamDesc{},
+ Metadata: "api/bbsim/bbsim_dmi.proto",
+}
diff --git a/api/bbsim/bbsim_dmi.proto b/api/bbsim/bbsim_dmi.proto
new file mode 100644
index 0000000..c560598
--- /dev/null
+++ b/api/bbsim/bbsim_dmi.proto
@@ -0,0 +1,32 @@
+// Copyright (c) 2018 Open Networking Foundation
+//
+// 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.
+
+syntax = "proto3";
+package bbsim;
+
+
+message DmiEvent {
+ string event_name= 1;
+}
+
+message DmiCreateEventResponse {
+ int32 status_code = 1;
+ string message = 2;
+}
+
+service BBsim_dmi {
+ // Ask the DMI Server to create an event
+ rpc CreateEvent (DmiEvent) returns (DmiCreateEventResponse){
+ }
+}
diff --git a/cmd/bbsimctl/bbsimctl.go b/cmd/bbsimctl/bbsimctl.go
index 08e7b5b..293c164 100644
--- a/cmd/bbsimctl/bbsimctl.go
+++ b/cmd/bbsimctl/bbsimctl.go
@@ -42,6 +42,7 @@
commands.RegisterPonCommands(parser)
commands.RegisterCompletionCommands(parser)
commands.RegisterLoggingCommands(parser)
+ commands.RegisterDMICommands(parser)
_, err = parser.ParseArgs(os.Args[1:])
if err != nil {
diff --git a/docs/source/DMI_Server_README.md b/docs/source/DMI_Server_README.md
index 1f3e660..229bb82 100755
--- a/docs/source/DMI_Server_README.md
+++ b/docs/source/DMI_Server_README.md
@@ -276,4 +276,105 @@
]
}
}
+```
+### List NativeEventsManagementService APIs
+``` sh
+$ grpcurl -plaintext 172.17.0.2:50075 list dmi.NativeEventsManagementService
+dmi.NativeEventsManagementService.ListEvents
+dmi.NativeEventsManagementService.UpdateEventsConfiguration
+```
+### ListEvents API
+``` sh
+$ grpcurl -plaintext -d '{"uuid": {"uuid":"5295a1d5-a121-372e-b8dc-6f7eda83f0ba"}}' 172.17.0.2:50075 dmi.NativeEventsManagementService.ListEvents
+{
+ "status": "OK_STATUS",
+ "events": {
+ "items": [
+ {
+ "eventId": "EVENT_FAN_FAILURE",
+ "isConfigured": true
+ },
+ {
+ "eventId": "EVENT_CPU_TEMPERATURE_ABOVE_CRITICAL",
+ "isConfigured": true,
+ "thresholds": {
+ "upper": {
+ "high": {
+ "intVal": "95"
+ },
+ "low": {
+ "intVal": "90"
+ }
+ }
+ }
+ },
+ {
+ "eventId": "EVENT_PSU_FAILURE",
+ "isConfigured": true
+ }
+ ]
+ }
+}
+
+```
+
+### UpdateEventsConfiguration API
+For FAN Failure, event_id must be set to 300 in grpc curl since enum value is 300
+Please refer enums values corresponding to events in
+https://github.com/opencord/device-management-interface/blob/master/protos/dmi/hw_events_mgmt_service.proto
+``` sh
+$ grpcurl -plaintext -d '{"device_uuid": {"uuid":"5295a1d5-a121-372e-b8dc-6f7eda83f0ba"}, "changes": {"items":{"event_id":"300", "is_configured": "false"}}}' 172.17.0.2:50075 dmi.NativeEventsManagementService.UpdateEventsConfiguration
+{
+ "status": "OK_STATUS"
+}
+```
+## Generate DMI Events
+Access bbsimctl
++++++++++++++++
+
+When running a test you can check the state of each ONU using ``BBSimCtl``.
+
+The easiest way to use ``bbsimctl`` is to ``exec`` inside the ``bbsim`` container:
+
+.. code:: bash
+
+ kubectl -n voltha exec -it $(kubectl -n voltha get pods -l app=bbsim -o name) -- /bin/bash
+
+In case you prefer to run ``bbsimctl`` on your machine,
+it can be configured via a config file such as:
+
+.. code:: bash
+
+ $ cat ~/.bbsim/config
+ apiVersion: v1
+ server: 127.0.0.1:50070
+ grpc:
+ timeout: 10s
+
+Note : For event names, refer EventIds enum in https://github.com/opencord/device-management-interface/blob/master/protos/dmi/hw_events_mgmt_service.proto .
+
+$ bsimctl dmi events raise <event_name>
+
+### FAN FAILURE EVENT
+
+.. code:: bash
+```
+$ bsimctl dmi events create EVENT_FAN_FAILURE
+[Status: 0] DMI Event Indication Sent.
+```
+
+### PSU FAILURE EVENT
+
+.. code:: bash
+```
+$ bsimctl dmi events create EVENT_PSU_FAILURE
+[Status: 0] DMI Event Indication Sent.
+```
+
+### HW DEVICE TEMPERATURE ABOVE CRITICAL EVENT
+
+.. code:: bash
+```
+$ bsimctl dmi events create EVENT_HW_DEVICE_TEMPERATURE_ABOVE_CRITICAL
+[Status: 0] DMI Event Indication Sent.
```
\ No newline at end of file
diff --git a/internal/bbsim/dmiserver/dmi_api_server.go b/internal/bbsim/dmiserver/dmi_api_server.go
index f64f801..272f3c9 100755
--- a/internal/bbsim/dmiserver/dmi_api_server.go
+++ b/internal/bbsim/dmiserver/dmi_api_server.go
@@ -20,6 +20,7 @@
"context"
"net"
+ "github.com/opencord/bbsim/api/bbsim"
"github.com/opencord/bbsim/internal/common"
dmi "github.com/opencord/device-management-interface/go/dmi"
log "github.com/sirupsen/logrus"
@@ -42,6 +43,7 @@
ponTransceiverCageUuids []string
root *dmi.Component
metricChannel chan interface{}
+ eventChannel chan interface{}
kafkaEndpoint string
mPublisherCancelFunc context.CancelFunc
}
@@ -69,6 +71,7 @@
dmi.RegisterNativeSoftwareManagementServiceServer(grpcServer, dms)
dmi.RegisterNativeEventsManagementServiceServer(grpcServer, dms)
dmi.RegisterNativeMetricsManagementServiceServer(grpcServer, dms)
+ bbsim.RegisterBBsimDmiServer(grpcServer, dms)
reflection.Register(grpcServer)
diff --git a/internal/bbsim/dmiserver/dmi_event_generator.go b/internal/bbsim/dmiserver/dmi_event_generator.go
new file mode 100644
index 0000000..b25cb59
--- /dev/null
+++ b/internal/bbsim/dmiserver/dmi_event_generator.go
@@ -0,0 +1,254 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+
+ * 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 dmiserver
+
+import (
+ "context"
+ "fmt"
+ "sync"
+
+ "github.com/golang/protobuf/ptypes"
+ "github.com/opencord/bbsim/api/bbsim"
+ "github.com/opencord/device-management-interface/go/dmi"
+ log "github.com/sirupsen/logrus"
+ "google.golang.org/grpc/codes"
+)
+
+//DmiEventsGenerator has the attributes for generating events
+type DmiEventsGenerator struct {
+ apiSrv *DmiAPIServer
+ configuredEvents map[dmi.EventIds]dmi.EventCfg
+ access sync.Mutex
+}
+
+// func to generate the different types of events, there are two types one with thresholds and one without
+type eventGenerationFunc func(dmi.EventIds, dmi.ComponentType)
+
+// eventGenerationUtil contains the component and the func for a specific eventId
+type eventGenerationUtil struct {
+ componentType dmi.ComponentType
+ genFunc eventGenerationFunc
+}
+
+var dmiEG DmiEventsGenerator
+var eventGenMap map[dmi.EventIds]eventGenerationUtil
+
+func init() {
+ eventGenMap = make(map[dmi.EventIds]eventGenerationUtil)
+ eventGenMap[dmi.EventIds_EVENT_FAN_FAILURE] = eventGenerationUtil{
+ componentType: dmi.ComponentType_COMPONENT_TYPE_FAN,
+ genFunc: noThresholdEventGenerationFunc,
+ }
+ eventGenMap[dmi.EventIds_EVENT_FAN_FAILURE_RECOVERED] = eventGenerationUtil{
+ componentType: dmi.ComponentType_COMPONENT_TYPE_FAN,
+ genFunc: noThresholdEventGenerationFunc,
+ }
+
+ eventGenMap[dmi.EventIds_EVENT_PSU_FAILURE] = eventGenerationUtil{
+ componentType: dmi.ComponentType_COMPONENT_TYPE_POWER_SUPPLY,
+ genFunc: noThresholdEventGenerationFunc,
+ }
+ eventGenMap[dmi.EventIds_EVENT_PSU_FAILURE_RECOVERED] = eventGenerationUtil{
+ componentType: dmi.ComponentType_COMPONENT_TYPE_POWER_SUPPLY,
+ genFunc: noThresholdEventGenerationFunc,
+ }
+
+ eventGenMap[dmi.EventIds_EVENT_HW_DEVICE_TEMPERATURE_ABOVE_CRITICAL] = eventGenerationUtil{
+ componentType: dmi.ComponentType_COMPONENT_TYPE_SENSOR,
+ genFunc: thresholdEventGenerationFunc,
+ }
+ eventGenMap[dmi.EventIds_EVENT_HW_DEVICE_TEMPERATURE_ABOVE_CRITICAL_RECOVERED] = eventGenerationUtil{
+ componentType: dmi.ComponentType_COMPONENT_TYPE_SENSOR,
+ genFunc: thresholdEventGenerationFunc,
+ }
+}
+
+//StartEventsGenerator initializes the event generator
+func StartEventsGenerator(apiSrv *DmiAPIServer) {
+ log.Debugf("StartEventsGenerator invoked")
+
+ dmiEG = DmiEventsGenerator{
+ apiSrv: apiSrv,
+ }
+ dmiEG.configuredEvents = make(map[dmi.EventIds]dmi.EventCfg)
+
+ // Add Fan Failure event configuration
+ dmiEG.configuredEvents[dmi.EventIds_EVENT_FAN_FAILURE] = dmi.EventCfg{
+ EventId: dmi.EventIds_EVENT_FAN_FAILURE,
+ IsConfigured: true,
+ }
+
+ // Add hardware device temp above critical event configuration
+ dmiEG.configuredEvents[dmi.EventIds_EVENT_HW_DEVICE_TEMPERATURE_ABOVE_CRITICAL] = dmi.EventCfg{
+ EventId: dmi.EventIds_EVENT_HW_DEVICE_TEMPERATURE_ABOVE_CRITICAL,
+ IsConfigured: true,
+ Thresholds: &dmi.Thresholds{
+ Threshold: &dmi.Thresholds_Upper{Upper: &dmi.WaterMarks{
+ High: &dmi.ValueType{
+ Val: &dmi.ValueType_IntVal{IntVal: 95},
+ },
+ Low: &dmi.ValueType{
+ Val: &dmi.ValueType_IntVal{IntVal: 90},
+ },
+ }},
+ },
+ }
+
+ // Add Power Supply Unit failure event configuration
+ dmiEG.configuredEvents[dmi.EventIds_EVENT_PSU_FAILURE] = dmi.EventCfg{
+ EventId: dmi.EventIds_EVENT_PSU_FAILURE,
+ IsConfigured: true,
+ }
+}
+
+// get the events list
+func getEventsList() []*dmi.EventCfg {
+ events := make(map[dmi.EventIds]dmi.EventCfg)
+ dmiEG.access.Lock()
+
+ for key, value := range dmiEG.configuredEvents {
+ events[key] = value
+ }
+
+ dmiEG.access.Unlock()
+
+ var toRet []*dmi.EventCfg
+ for _, v := range events {
+ eventConfig := v
+ toRet = append(toRet, &eventConfig)
+ }
+ logger.Debugf("Events list supported by device %+v", toRet)
+ return toRet
+}
+
+//UpdateEventConfig Adds/Updates the passed event configuration
+func UpdateEventConfig(newEventCfg *dmi.EventCfg) {
+ dmiEG.access.Lock()
+ dmiEG.configuredEvents[newEventCfg.GetEventId()] = *newEventCfg
+ dmiEG.access.Unlock()
+ logger.Infof("Events updated %v", newEventCfg)
+}
+
+// update Event MetaData
+func updateEventMetaData(c *dmi.Component, apiSrv *DmiAPIServer, evt *dmi.Event) *dmi.Event {
+ evt.EventMetadata = &dmi.EventMetaData{
+ DeviceUuid: &dmi.Uuid{
+ Uuid: apiSrv.uuid,
+ },
+ ComponentUuid: c.Uuid,
+ ComponentName: c.Name,
+ }
+ return evt
+}
+
+func sendOutEventOnKafka(event interface{}, apiSrv *DmiAPIServer) {
+ select {
+ case apiSrv.eventChannel <- event:
+ default:
+ logger.Debugf("Channel not ready dropping event")
+ }
+}
+
+func noThresholdEventGenerationFunc(eventID dmi.EventIds, cType dmi.ComponentType) {
+ for _, comp := range findComponentsOfType(dmiEG.apiSrv.root.Children, cType) {
+ var evnt dmi.Event
+ evnt.EventId = eventID
+ evnt = *updateEventMetaData(comp, dmiEG.apiSrv, &evnt)
+ evnt.RaisedTs = ptypes.TimestampNow()
+ logger.Debugf("Got a No Threshold event %+v", evnt)
+ sendOutEventOnKafka(evnt, dmiEG.apiSrv)
+ break
+ }
+}
+
+func thresholdEventGenerationFunc(eventID dmi.EventIds, cType dmi.ComponentType) {
+ eventGenerated := false
+ for _, comp := range findComponentsOfType(dmiEG.apiSrv.root.Children, cType) {
+ var evnt dmi.Event
+ evnt.EventId = eventID
+ evnt = *updateEventMetaData(comp, dmiEG.apiSrv, &evnt)
+ evnt.RaisedTs = ptypes.TimestampNow()
+ configuredEvents := make(map[dmi.EventIds]dmi.EventCfg)
+
+ dmiEG.access.Lock()
+ for key, value := range dmiEG.configuredEvents {
+ configuredEvents[key] = value
+ }
+ dmiEG.access.Unlock()
+
+ for k, v := range configuredEvents {
+ if k == eventID {
+ evnt.ThresholdInfo = &dmi.ThresholdInformation{
+ ObservedValue: &dmi.ValueType{
+ Val: &dmi.ValueType_IntVal{IntVal: int64(generateRand(int32(v.Thresholds.GetUpper().GetLow().GetIntVal()), int32(v.Thresholds.GetUpper().GetHigh().GetIntVal())))},
+ },
+ Thresholds: v.GetThresholds(),
+ }
+ }
+ }
+
+ logger.Debugf("Got Threshold event %v", evnt)
+ sendOutEventOnKafka(evnt, dmiEG.apiSrv)
+ eventGenerated = true
+ if eventGenerated {
+ break
+ }
+
+ }
+}
+
+// CreateEvent creates and the passed event if it's valid and sends it to the msg bus
+func (das *DmiAPIServer) CreateEvent(ctx context.Context, evt *bbsim.DmiEvent) (*bbsim.DmiCreateEventResponse, error) {
+ retFunc := func(code codes.Code, msg string) (*bbsim.DmiCreateEventResponse, error) {
+ res := &bbsim.DmiCreateEventResponse{}
+ res.StatusCode = int32(code)
+ res.Message = msg
+ return res, nil
+ }
+
+ if dmiEG.apiSrv == nil || dmiEG.apiSrv.root == nil || dmiEG.apiSrv.root.Children == nil {
+ // inventory might not yet be created
+ return retFunc(codes.Internal, "inventory do no exist")
+ }
+
+ eventID, exists := dmi.EventIds_value[evt.EventName]
+ if !exists {
+ return retFunc(codes.NotFound,
+ fmt.Sprintf("DMI Alarm not supported. Permissible values are %s", getValidEventNames()))
+ }
+
+ genUtil, exists := eventGenMap[dmi.EventIds(eventID)]
+ if !exists {
+ return retFunc(codes.Unimplemented, "Generation of this event not yet implemented")
+ }
+
+ genUtil.genFunc(dmi.EventIds(eventID), genUtil.componentType)
+
+ return retFunc(codes.OK, "DMI Event Indication Sent.")
+
+}
+
+func getValidEventNames() string {
+ s := ""
+ //keys := make([]string, len(dmi.EventIds_value)-1)
+ for k, v := range dmi.EventIds_value {
+ if v != 0 {
+ s = s + "\n" + k
+ }
+ }
+ return s
+}
diff --git a/internal/bbsim/dmiserver/dmi_events_mgmt.go b/internal/bbsim/dmiserver/dmi_events_mgmt.go
index 3fc3416..da1e8ae 100755
--- a/internal/bbsim/dmiserver/dmi_events_mgmt.go
+++ b/internal/bbsim/dmiserver/dmi_events_mgmt.go
@@ -18,6 +18,8 @@
import (
"context"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/grpc/status"
dmi "github.com/opencord/device-management-interface/go/dmi"
)
@@ -25,11 +27,10 @@
//ListEvents lists the supported events for the passed device
func (dms *DmiAPIServer) ListEvents(ctx context.Context, req *dmi.HardwareID) (*dmi.ListEventsResponse, error) {
logger.Debugf("ListEvents called with request %+v", req)
- //empty events
- events := []*dmi.EventCfg{{}}
+ events := getEventsList()
+
return &dmi.ListEventsResponse{
Status: dmi.Status_OK_STATUS,
- Reason: dmi.Reason_UNDEFINED_REASON,
Events: &dmi.EventsCfg{
Items: events,
},
@@ -39,6 +40,31 @@
//UpdateEventsConfiguration updates the configuration of the list of events in the request
func (dms *DmiAPIServer) UpdateEventsConfiguration(ctx context.Context, req *dmi.EventsConfigurationRequest) (*dmi.EventsConfigurationResponse, error) {
logger.Debugf("UpdateEventsConfiguration called with request %+v", req)
+
+ if req == nil || req.Operation == nil {
+ return &dmi.EventsConfigurationResponse{
+ Status: dmi.Status_ERROR_STATUS,
+ //TODO reason must be INVALID_PARAMS, currently this is not available in Device Management interface (DMI),
+ // change below reason with type INVALID_PARAMS once DMI is updated
+ Reason: dmi.Reason_UNDEFINED_REASON,
+ }, status.Errorf(codes.FailedPrecondition, "request is nil")
+ }
+
+ switch x := req.Operation.(type) {
+ case *dmi.EventsConfigurationRequest_Changes:
+ for _, eventConfig := range x.Changes.Items {
+ UpdateEventConfig(eventConfig)
+ }
+ case *dmi.EventsConfigurationRequest_ResetToDefault:
+ logger.Debugf("To be implemented later")
+ case nil:
+ // The field is not set.
+ logger.Debugf("Update request operation type is nil")
+ return &dmi.EventsConfigurationResponse{
+ Status: dmi.Status_UNDEFINED_STATUS,
+ }, nil
+ }
+
return &dmi.EventsConfigurationResponse{
Status: dmi.Status_OK_STATUS,
}, nil
diff --git a/internal/bbsim/dmiserver/dmi_hw_mgmt.go b/internal/bbsim/dmiserver/dmi_hw_mgmt.go
index cf6410d..cd0b0e9 100755
--- a/internal/bbsim/dmiserver/dmi_hw_mgmt.go
+++ b/internal/bbsim/dmiserver/dmi_hw_mgmt.go
@@ -34,7 +34,7 @@
)
const (
- metricChannelSize = 100
+ kafkaChannelSize = 100
)
func getUUID(seed string) string {
@@ -66,9 +66,13 @@
dms.ponTransceiverCageUuids = make([]string, olt.NumPon)
// Start device metrics generator
- dms.metricChannel = make(chan interface{}, metricChannelSize)
+ dms.metricChannel = make(chan interface{}, kafkaChannelSize)
StartMetricGenerator(dms)
+ // Start device event generator
+ dms.eventChannel = make(chan interface{}, kafkaChannelSize)
+ StartEventsGenerator(dms)
+
var components []*dmi.Component
// Create and store the component for transceivers and transceiver cages
@@ -113,11 +117,12 @@
}
components = append(components, fans...)
- // Create 1 disk, 1 Processor and 1 ram
+ // Create 1 disk, 1 processor, 1 ram, 1 temperature sensor and power supply unit
components = append(components, createDiskComponent(0))
components = append(components, createProcessorComponent(0))
components = append(components, createMemoryComponent(0))
components = append(components, createInnerSurroundingTempComponentSensor(0))
+ components = append(components, createPowerSupplyComponent(0))
// create the root component
dms.root = &dmi.Component{
@@ -251,6 +256,25 @@
}
}
+func createPowerSupplyComponent(psuIdx int) *dmi.Component {
+ psuName := fmt.Sprintf("Thermal/PSU/SystemPSU/%d", psuIdx)
+ psuSerial := fmt.Sprintf("bbsim-psu-serial-%d", psuIdx)
+ return &dmi.Component{
+ Name: psuName,
+ Class: dmi.ComponentType_COMPONENT_TYPE_POWER_SUPPLY,
+ Description: "bbsim-psu",
+ Parent: "",
+ ParentRelPos: 0,
+ SerialNum: psuSerial,
+ MfgName: "bbsim-psu",
+ IsFru: false,
+ Uuid: &dmi.Uuid{
+ Uuid: getUUID(psuName),
+ },
+ State: &dmi.ComponentState{},
+ }
+}
+
//StopManagingDevice stops management of a device and cleans up any context and caches for that device
func (dms *DmiAPIServer) StopManagingDevice(ctx context.Context, req *dmi.StopManagingDeviceRequest) (*dmi.StopManagingDeviceResponse, error) {
logger.Debugf("StopManagingDevice API invoked")
@@ -452,10 +476,12 @@
nCtx, dms.mPublisherCancelFunc = context.WithCancel(context.Background())
// initialize a publisher
if err := InitializeDMKafkaPublishers(sarama.NewAsyncProducer, olt.ID, dms.kafkaEndpoint); err == nil {
- // start a go routine which will read from channel and publish on kafka
+ // start a go routine which will read from channel and publish on kafka topic dm.metrics
go DMKafkaPublisher(nCtx, dms.metricChannel, "dm.metrics")
+ // start a go routine which will read from channel and publish on kafka topic dm.events
+ go DMKafkaPublisher(nCtx, dms.eventChannel, "dm.events")
} else {
- logger.Errorf("Failed to start kafka publisher: %v", err)
+ logger.Errorf("Failed to start metric kafka publisher: %v", err)
return &dmi.SetRemoteEndpointResponse{Status: dmi.Status_ERROR_STATUS, Reason: dmi.Reason_KAFKA_ENDPOINT_ERROR}, err
}
diff --git a/internal/bbsim/dmiserver/dmi_kafka_producer.go b/internal/bbsim/dmiserver/dmi_kafka_producer.go
index 9ee0172..8955dfa 100755
--- a/internal/bbsim/dmiserver/dmi_kafka_producer.go
+++ b/internal/bbsim/dmiserver/dmi_kafka_producer.go
@@ -26,7 +26,7 @@
log "github.com/sirupsen/logrus"
)
-var metricsProducer sarama.AsyncProducer
+var producer sarama.AsyncProducer
// InitializeDMKafkaPublishers initializes metrics kafka publisher
func InitializeDMKafkaPublishers(NewAsyncProducer func([]string, *sarama.Config) (sarama.AsyncProducer, error), oltID int, msgBusEndPoint string) error {
@@ -37,9 +37,9 @@
config.Producer.Retry.Max = 5
config.Metadata.Retry.Max = 10
config.Metadata.Retry.Backoff = 10 * time.Second
- config.ClientID = "BBSim-OLT-Metrics-" + strconv.Itoa(oltID)
+ config.ClientID = "BBSim-OLT-DMIServer-" + strconv.Itoa(oltID)
- metricsProducer, err = NewAsyncProducer([]string{msgBusEndPoint}, config)
+ producer, err = NewAsyncProducer([]string{msgBusEndPoint}, config)
return err
}
@@ -49,16 +49,16 @@
loop:
for {
select {
- case metric := <-ch:
- log.Tracef("Writing to kafka topic(%s): %v", topic, metric)
- jsonMet, err := json.Marshal(metric)
+ case data := <-ch:
+ log.Tracef("Writing to kafka topic(%s): %v", topic, data)
+ jsonData, err := json.Marshal(data)
if err != nil {
- log.Errorf("Failed to get json metric %v", err)
+ log.Errorf("Failed to get json %v", err)
continue
}
- metricsProducer.Input() <- &sarama.ProducerMessage{
+ producer.Input() <- &sarama.ProducerMessage{
Topic: topic,
- Value: sarama.ByteEncoder(jsonMet),
+ Value: sarama.ByteEncoder(jsonData),
}
case <-ctx.Done():
log.Infof("Stopping DM Kafka Publisher for topic %s", topic)
diff --git a/internal/bbsim/dmiserver/dmi_metrics_mgmt.go b/internal/bbsim/dmiserver/dmi_metrics_mgmt.go
index cb88cc7..e23786f 100755
--- a/internal/bbsim/dmiserver/dmi_metrics_mgmt.go
+++ b/internal/bbsim/dmiserver/dmi_metrics_mgmt.go
@@ -79,7 +79,7 @@
if req == nil || req.GetMetricId() < 0 {
return &dmi.GetMetricResponse{
Status: dmi.Status_ERROR_STATUS,
- //TODO reason must be INVALID_PARAMS, currently this is available in Device Management interface (DMI),
+ //TODO reason must be INVALID_PARAMS, currently this is not available in Device Management interface (DMI),
// change below reason with type INVALID_PARAMS once DMI is updated
Reason: dmi.Reason_UNDEFINED_REASON,
Metric: &dmi.Metric{},
diff --git a/internal/bbsimctl/commands/dmi_events.go b/internal/bbsimctl/commands/dmi_events.go
new file mode 100644
index 0000000..1608965
--- /dev/null
+++ b/internal/bbsimctl/commands/dmi_events.go
@@ -0,0 +1,74 @@
+/*
+ * Copyright 2018-present Open Networking Foundation
+
+ * 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 commands
+
+import (
+ "context"
+ "fmt"
+
+ "github.com/jessevdk/go-flags"
+ "github.com/opencord/bbsim/api/bbsim"
+ "github.com/opencord/bbsim/internal/bbsimctl/config"
+ log "github.com/sirupsen/logrus"
+ "google.golang.org/grpc"
+)
+
+type DMIOptions struct {
+ Events DmiEventOptions `command:"events"`
+}
+
+type DmiEventCreate struct {
+ Args struct {
+ Name string
+ } `positional-args:"yes" required:"yes"`
+}
+
+type DmiEventOptions struct {
+ Create DmiEventCreate `command:"create"`
+}
+
+func RegisterDMICommands(parser *flags.Parser) {
+ _, _ = parser.AddCommand("dmi", "DMI Commands", "Commands to create events", &DMIOptions{})
+}
+
+func dmiEventGrpcClient() (bbsim.BBsimDmiClient, *grpc.ClientConn) {
+ conn, err := grpc.Dial(config.DmiConfig.Server, grpc.WithInsecure())
+ if err != nil {
+ log.Errorf("BBsimDmiClient connection failed : %v", err)
+ return nil, conn
+ }
+ return bbsim.NewBBsimDmiClient(conn), conn
+}
+
+// Execute create event
+func (o *DmiEventCreate) Execute(args []string) error {
+ client, conn := dmiEventGrpcClient()
+ defer conn.Close()
+
+ ctx, cancel := context.WithTimeout(context.Background(), config.GlobalConfig.Grpc.Timeout)
+ defer cancel()
+
+ req := bbsim.DmiEvent{EventName: o.Args.Name}
+ res, err := client.CreateEvent(ctx, &req)
+ if err != nil {
+ log.Errorf("Cannot create DMI event: %v", err)
+ return err
+ }
+
+ fmt.Println(fmt.Sprintf("[Status: %d] %s", res.StatusCode, res.Message))
+ return nil
+}
diff --git a/internal/bbsimctl/commands/onualarms.go b/internal/bbsimctl/commands/onualarms.go
index fb1d00e..669c173 100755
--- a/internal/bbsimctl/commands/onualarms.go
+++ b/internal/bbsimctl/commands/onualarms.go
@@ -20,10 +20,11 @@
import (
"context"
"fmt"
- "github.com/opencord/bbsim/internal/common"
"os"
"strings"
+ "github.com/opencord/bbsim/internal/common"
+
"github.com/jessevdk/go-flags"
"github.com/olekukonko/tablewriter"
pb "github.com/opencord/bbsim/api/bbsim"
diff --git a/internal/bbsimctl/config/config.go b/internal/bbsimctl/config/config.go
index 7d11122..4bf762a 100644
--- a/internal/bbsimctl/config/config.go
+++ b/internal/bbsimctl/config/config.go
@@ -57,6 +57,13 @@
},
}
+var DmiConfig = GlobalConfigSpec{
+ Server: "localhost:50075",
+ Grpc: GrpcConfigSpec{
+ Timeout: time.Second * 10,
+ },
+}
+
func ProcessGlobalOptions() {
if len(GlobalOptions.Config) == 0 {
home, err := os.UserHomeDir()