[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/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
 	}