[VOL-4687] Add transceivers to DMI, add commands to bbsimctl to manage them
Change-Id: Id1fe29eb48a7abb3c86958827edce70b75707de9
diff --git a/internal/bbsim/dmiserver/dmi_api_server.go b/internal/bbsim/dmiserver/dmi_api_server.go
index fea52a8..9ad8f94 100755
--- a/internal/bbsim/dmiserver/dmi_api_server.go
+++ b/internal/bbsim/dmiserver/dmi_api_server.go
@@ -18,9 +18,11 @@
import (
"context"
+ "fmt"
"net"
"github.com/opencord/bbsim/api/bbsim"
+ "github.com/opencord/bbsim/internal/bbsim/devices"
"github.com/opencord/bbsim/internal/common"
dmi "github.com/opencord/device-management-interface/go/dmi"
log "github.com/sirupsen/logrus"
@@ -35,19 +37,16 @@
//DmiAPIServer has the attributes for the Server handling the Device Management Interface
type DmiAPIServer struct {
- deviceSerial string
- deviceName string
- ipAddress string
- uuid string
- ponTransceiverUuids []string
- ponTransceiverCageUuids []string
- root *dmi.Component
- metricChannel chan interface{}
- eventChannel chan interface{}
- kafkaEndpoint string
- loggingEndpoint string
- loggingProtocol string
- mPublisherCancelFunc context.CancelFunc
+ ipAddress string
+ uuid *dmi.Uuid
+ root *dmi.Component
+ Transceivers []*Transceiver
+ metricChannel chan interface{}
+ eventChannel chan interface{}
+ kafkaEndpoint string
+ loggingEndpoint string
+ loggingProtocol string
+ mPublisherCancelFunc context.CancelFunc
}
var dmiServ DmiAPIServer
@@ -56,9 +55,27 @@
func StartDmiAPIServer() (*grpc.Server, error) {
dmiServ = DmiAPIServer{}
+ // Create the mapping between transceivers and PONS
+ // TODO: at the moment we create one transceiver for each PON,
+ // but in the case of COMBO PON this will not always be the case.
+ // This should be expanded to cover that scenario
+ logger.Debug("Creating transceivers from DMI")
+ dmiServ.Transceivers = []*Transceiver{}
+ for _, pon := range devices.GetOLT().Pons {
+ trans := newTransceiver(pon.ID, []*devices.PonPort{pon})
+ dmiServ.Transceivers = append(dmiServ.Transceivers, trans)
+ }
+
return dmiServ.newDmiAPIServer()
}
+func getDmiAPIServer() (*DmiAPIServer, error) {
+ if dmiServ.root == nil {
+ return nil, fmt.Errorf("Device management not started")
+ }
+ return &dmiServ, nil
+}
+
// newDmiAPIServer launches a new grpc server for the Device Manager Interface
func (dms *DmiAPIServer) newDmiAPIServer() (*grpc.Server, error) {
address := common.Config.BBSim.DmiServerAddress
diff --git a/internal/bbsim/dmiserver/dmi_event_generator.go b/internal/bbsim/dmiserver/dmi_event_generator.go
index 2a9073e..93aa28f 100644
--- a/internal/bbsim/dmiserver/dmi_event_generator.go
+++ b/internal/bbsim/dmiserver/dmi_event_generator.go
@@ -57,6 +57,14 @@
componentType: dmi.ComponentType_COMPONENT_TYPE_FAN,
genFunc: noThresholdEventGenerationFunc,
}
+ eventGenMap[dmi.EventIds_EVENT_TRANSCEIVER_PLUG_IN] = eventGenerationUtil{
+ componentType: dmi.ComponentType_COMPONENT_TYPE_TRANSCEIVER,
+ genFunc: noThresholdEventGenerationFunc,
+ }
+ eventGenMap[dmi.EventIds_EVENT_TRANSCEIVER_PLUG_OUT] = eventGenerationUtil{
+ componentType: dmi.ComponentType_COMPONENT_TYPE_TRANSCEIVER,
+ genFunc: noThresholdEventGenerationFunc,
+ }
eventGenMap[dmi.EventIds_EVENT_PSU_FAILURE] = eventGenerationUtil{
componentType: dmi.ComponentType_COMPONENT_TYPE_POWER_SUPPLY,
@@ -113,6 +121,16 @@
EventId: dmi.EventIds_EVENT_PSU_FAILURE,
IsConfigured: true,
}
+
+ // Add Transceiver Plug in and out event configuration
+ dmiEG.configuredEvents[dmi.EventIds_EVENT_TRANSCEIVER_PLUG_IN] = dmi.EventCfg{
+ EventId: dmi.EventIds_EVENT_TRANSCEIVER_PLUG_IN,
+ IsConfigured: true,
+ }
+ dmiEG.configuredEvents[dmi.EventIds_EVENT_TRANSCEIVER_PLUG_OUT] = dmi.EventCfg{
+ EventId: dmi.EventIds_EVENT_TRANSCEIVER_PLUG_OUT,
+ IsConfigured: true,
+ }
}
// get the events list
@@ -146,9 +164,7 @@
// 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,
- },
+ DeviceUuid: apiSrv.uuid,
ComponentUuid: c.Uuid,
ComponentName: c.Name,
}
@@ -212,9 +228,9 @@
}
// CreateEvent creates and the passed event if it's valid and sends it to the msg bus
-func (dms *DmiAPIServer) CreateEvent(ctx context.Context, evt *bbsim.DmiEvent) (*bbsim.DmiCreateEventResponse, error) {
- retFunc := func(code codes.Code, msg string) (*bbsim.DmiCreateEventResponse, error) {
- res := &bbsim.DmiCreateEventResponse{}
+func (dms *DmiAPIServer) CreateEvent(ctx context.Context, evt *bbsim.DmiEvent) (*bbsim.DmiResponse, error) {
+ retFunc := func(code codes.Code, msg string) (*bbsim.DmiResponse, error) {
+ res := &bbsim.DmiResponse{}
res.StatusCode = int32(code)
res.Message = msg
return res, nil
@@ -222,7 +238,7 @@
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")
+ return retFunc(codes.Internal, "Inventory does not exist")
}
eventID, exists := dmi.EventIds_value[evt.EventName]
diff --git a/internal/bbsim/dmiserver/dmi_hw_mgmt.go b/internal/bbsim/dmiserver/dmi_hw_mgmt.go
index 06c078f..00e4a92 100755
--- a/internal/bbsim/dmiserver/dmi_hw_mgmt.go
+++ b/internal/bbsim/dmiserver/dmi_hw_mgmt.go
@@ -21,6 +21,7 @@
"fmt"
"github.com/Shopify/sarama"
+ log "github.com/sirupsen/logrus"
"github.com/golang/protobuf/ptypes/empty"
"github.com/golang/protobuf/ptypes/timestamp"
@@ -41,6 +42,46 @@
return guuid.NewMD5(guuid.Nil, []byte(seed)).String()
}
+func getOltName() string {
+ return fmt.Sprintf("%s-%s", common.Config.Olt.Vendor, devices.GetOLT().SerialNumber)
+}
+
+func getOltUUID() *dmi.Uuid {
+ return &dmi.Uuid{
+ Uuid: getUUID(devices.GetOLT().SerialNumber),
+ }
+}
+
+func getCageName(id uint32) string {
+ return fmt.Sprintf("sfp-plus-transceiver-cage-%d", id)
+}
+
+func getCageUUID(id uint32) *dmi.Uuid {
+ return &dmi.Uuid{
+ Uuid: getUUID(fmt.Sprintf("%s-%s", devices.GetOLT().SerialNumber, getCageName(id))),
+ }
+}
+
+func getTransceiverName(id uint32) string {
+ return fmt.Sprintf("sfp-plus-%d", id)
+}
+
+func getTransceiverUUID(id uint32) *dmi.Uuid {
+ return &dmi.Uuid{
+ Uuid: getUUID(fmt.Sprintf("%s-%s", devices.GetOLT().SerialNumber, getTransceiverName(id))),
+ }
+}
+
+func getPonName(id uint32) string {
+ return fmt.Sprintf("pon-%d", id)
+}
+
+func getPonUUID(id uint32) *dmi.Uuid {
+ return &dmi.Uuid{
+ Uuid: getUUID(fmt.Sprintf("%s-%s", devices.GetOLT().SerialNumber, getPonName(id))),
+ }
+}
+
//StartManagingDevice establishes connection with the device and does checks to ascertain if the device with passed identity can be managed
func (dms *DmiAPIServer) StartManagingDevice(req *dmi.ModifiableComponent, stream dmi.NativeHWManagementService_StartManagingDeviceServer) error {
//Get serial number and generate the UUID based on this serial number. Store this UUID in local cache
@@ -57,13 +98,9 @@
// Uri is the IP address
dms.ipAddress = req.GetUri().GetUri()
- dms.deviceSerial = olt.SerialNumber
- dms.deviceName = fmt.Sprintf("%s-%s", common.Config.Olt.Vendor, dms.deviceSerial)
- dms.uuid = getUUID(dms.deviceSerial)
-
- dms.ponTransceiverUuids = make([]string, olt.NumPon)
- dms.ponTransceiverCageUuids = make([]string, olt.NumPon)
+ deviceName := getOltName()
+ dms.uuid = getOltUUID()
// Start device metrics generator
dms.metricChannel = make(chan interface{}, kafkaChannelSize)
@@ -76,57 +113,27 @@
var components []*dmi.Component
// Create and store the component for transceivers and transceiver cages
- for i, pon := range olt.Pons {
- label := fmt.Sprintf("pon-%d", pon.ID)
- dms.ponTransceiverUuids[i] = getUUID(dms.deviceSerial + label)
- dms.ponTransceiverCageUuids[i] = getUUID(dms.deviceSerial + "cage" + label)
-
- transName := fmt.Sprintf("sfp-%d", i)
- cageName := fmt.Sprintf("sfp-plus-transceiver-cage-pon-%d", i)
-
- var transType dmi.TransceiverType
- var rxWavelength, txWavelength []uint32
- switch pon.Technology {
- case common.GPON:
- transType = dmi.TransceiverType_GPON
- rxWavelength = []uint32{1490} // nanometers
- txWavelength = []uint32{1550} // nanometers
- case common.XGSPON:
- transType = dmi.TransceiverType_XGSPON
- rxWavelength = []uint32{1270} // nanometers
- txWavelength = []uint32{1577} // nanometers
- }
-
- trans := dmi.Component{
- Name: transName,
- Class: dmi.ComponentType_COMPONENT_TYPE_TRANSCEIVER,
- Description: pon.Technology.String(),
- Uuid: &dmi.Uuid{
- Uuid: dms.ponTransceiverUuids[i],
- },
- Parent: cageName,
- Specific: &dmi.Component_TransceiverAttr{
- TransceiverAttr: &dmi.TransceiverComponentsAttributes{
- FormFactor: dmi.TransceiverComponentsAttributes_SFP_PLUS,
- TransType: transType,
- MaxDistance: 10, // kilometers (see scale below)
- MaxDistanceScale: dmi.ValueScale_VALUE_SCALE_KILO,
- RxWavelength: rxWavelength,
- TxWavelength: txWavelength,
- WavelengthScale: dmi.ValueScale_VALUE_SCALE_NANO,
- },
- },
- }
+ for _, trans := range dms.Transceivers {
+ //Make one cage for each of the transceivers
+ cageName := getCageName(trans.ID)
cage := dmi.Component{
Name: cageName,
Class: dmi.ComponentType_COMPONENT_TYPE_CONTAINER,
Description: "cage",
- Uuid: &dmi.Uuid{
- Uuid: dms.ponTransceiverCageUuids[i],
- },
- Parent: dms.deviceName,
- Children: []*dmi.Component{&trans},
+ Uuid: getCageUUID(trans.ID),
+ Parent: deviceName,
+ Children: []*dmi.Component{},
+ }
+
+ //If the transceiver is not plugged in, only the empty cage is created
+ if trans.PluggedIn {
+ transComponent, err := createTransceiverComponent(trans, cageName)
+ if err != nil {
+ logger.Error(err)
+ continue
+ }
+ cage.Children = append(cage.Children, transComponent)
}
components = append(components, &cage)
@@ -150,30 +157,26 @@
// create the root component
dms.root = &dmi.Component{
- Name: dms.deviceName,
+ Name: deviceName,
Class: 0,
Description: "",
Parent: "",
ParentRelPos: 0,
Children: components,
- SerialNum: dms.deviceSerial,
+ SerialNum: olt.SerialNumber,
MfgName: common.Config.Olt.Vendor,
IsFru: false,
Uri: &dmi.Uri{
Uri: dms.ipAddress,
},
- Uuid: &dmi.Uuid{
- Uuid: dms.uuid,
- },
+ Uuid: dms.uuid,
State: &dmi.ComponentState{},
}
- logger.Debugf("Generated UUID for the uri %s is %s", dms.ipAddress, dms.uuid)
+ logger.Debugf("Generated UUID for the uri %s is %s", dms.ipAddress, dms.uuid.Uuid)
response := &dmi.StartManagingDeviceResponse{
- Status: dmi.Status_OK_STATUS,
- DeviceUuid: &dmi.Uuid{
- Uuid: dms.uuid,
- },
+ Status: dmi.Status_OK_STATUS,
+ DeviceUuid: dms.uuid,
}
err := stream.Send(response)
@@ -185,6 +188,90 @@
return nil
}
+func createTransceiverComponent(trans *Transceiver, cageName string) (*dmi.Component, error) {
+ portName := getPonName(trans.ID)
+
+ var rxWavelength, txWavelength []uint32
+
+ if len(trans.Pons) == 0 {
+ return nil, fmt.Errorf("No pons in list for transceiver %d", trans.ID)
+ } else if len(trans.Pons) <= 1 {
+ //Assuming a transceiver with only one PON
+ //has the technology of the PON
+
+ switch trans.Pons[0].Technology {
+ case common.GPON:
+ trans.Technology = dmi.TransceiverType_GPON
+ rxWavelength = []uint32{1490} // nanometers
+ txWavelength = []uint32{1550} // nanometers
+ case common.XGSPON:
+ trans.Technology = dmi.TransceiverType_XGSPON
+ rxWavelength = []uint32{1270} // nanometers
+ txWavelength = []uint32{1577} // nanometers
+ }
+ } else {
+ //Assuming more than one PON for the transceiver
+ //is COMBO PON
+
+ trans.Technology = dmi.TransceiverType_COMBO_GPON_XGSPON
+
+ rxWavelength = []uint32{1490, 1270} // nanometers
+ txWavelength = []uint32{1550, 1577} // nanometers
+ }
+
+ //Create all ports mapped to this transceiver
+ ports := []*dmi.Component{}
+ for _, pon := range trans.Pons {
+ var portProto dmi.PortComponentAttributes_Protocol
+
+ switch pon.Technology {
+ case common.GPON:
+ portProto = dmi.PortComponentAttributes_GPON
+ case common.XGSPON:
+ portProto = dmi.PortComponentAttributes_XGSPON
+ }
+
+ p := dmi.Component{
+ Name: portName,
+ Class: dmi.ComponentType_COMPONENT_TYPE_PORT,
+ Description: "bbsim-pon-port",
+ Uuid: getPonUUID(pon.ID),
+ Parent: trans.Name,
+ Specific: &dmi.Component_PortAttr{
+ PortAttr: &dmi.PortComponentAttributes{
+ Protocol: portProto,
+ },
+ },
+ }
+
+ ports = append(ports, &p)
+ }
+
+ transComponent := dmi.Component{
+ Name: trans.Name,
+ Class: dmi.ComponentType_COMPONENT_TYPE_TRANSCEIVER,
+ Description: "bbsim-transceiver",
+ Uuid: &dmi.Uuid{
+ Uuid: trans.Uuid,
+ },
+ Parent: cageName,
+ Specific: &dmi.Component_TransceiverAttr{
+ TransceiverAttr: &dmi.TransceiverComponentsAttributes{
+ FormFactor: dmi.TransceiverComponentsAttributes_SFP_PLUS,
+ TransType: trans.Technology,
+ MaxDistance: 10, // kilometers (see scale below)
+ MaxDistanceScale: dmi.ValueScale_VALUE_SCALE_KILO,
+ RxWavelength: rxWavelength,
+ TxWavelength: txWavelength,
+ WavelengthScale: dmi.ValueScale_VALUE_SCALE_NANO,
+ },
+ },
+ Children: ports,
+ }
+
+ return &transComponent, nil
+}
+
func createFanComponent(fanIdx int) *dmi.Component {
fanName := fmt.Sprintf("Thermal/Fans/System Fan/%d", fanIdx)
fanSerial := fmt.Sprintf("bbsim-fan-serial-%d", fanIdx)
@@ -299,6 +386,115 @@
}
}
+func PlugoutTransceiverComponent(transId uint32, dms *DmiAPIServer) error {
+ if dms == nil {
+ return fmt.Errorf("Nil API server")
+ }
+
+ if dms.root == nil {
+ return fmt.Errorf("Device management not started")
+ }
+
+ trans, err := getTransceiverWithId(transId, dms)
+ if err != nil {
+ return err
+ }
+
+ if !trans.PluggedIn {
+ return fmt.Errorf("Cannot plug out transceiver with ID %d since it's not plugged in", transId)
+ }
+
+ //Find the transceiver node in the tree
+ targetUuid := getTransceiverUUID(transId)
+
+ var targetCage *dmi.Component
+ targetTransIndex := -1
+
+loop:
+ for _, rootChild := range dms.root.Children {
+ if rootChild.Uuid.Uuid == getCageUUID(transId).Uuid {
+ currentCage := rootChild
+
+ for j, cageChild := range currentCage.Children {
+ if cageChild.Uuid.Uuid == targetUuid.Uuid {
+ targetCage = currentCage
+ targetTransIndex = j
+ break loop
+ }
+ }
+ }
+ }
+
+ if targetCage == nil || targetTransIndex == -1 {
+ return fmt.Errorf("Cannot find transceiver with id %d", transId)
+ }
+
+ //Remove transceiver
+ targetCage.Children = append(targetCage.Children[:targetTransIndex], targetCage.Children[targetTransIndex+1:]...)
+ logger.WithFields(log.Fields{
+ "transId": transId,
+ "cageName": targetCage.Name,
+ "cageChildren": targetCage.Children,
+ }).Debug("Removed transceiver from DMI inventory")
+
+ //Change plugged-in state
+ trans.PluggedIn = false
+
+ return nil
+}
+
+func PluginTransceiverComponent(transId uint32, dms *DmiAPIServer) error {
+ if dms == nil {
+ return fmt.Errorf("Nil API server")
+ }
+
+ if dms.root == nil {
+ return fmt.Errorf("Device management not started")
+ }
+
+ trans, err := getTransceiverWithId(transId, dms)
+ if err != nil {
+ return err
+ }
+
+ if trans.PluggedIn {
+ return fmt.Errorf("Cannot plug in transceiver with ID %d since it's already plugged in", transId)
+ }
+
+ //Find transceiver node in the tree
+ var targetCage *dmi.Component
+
+ for _, rootChild := range dms.root.Children {
+ if rootChild.Uuid.Uuid == getCageUUID(transId).Uuid {
+ targetCage = rootChild
+ break
+ }
+ }
+
+ if targetCage == nil {
+ return fmt.Errorf("Cannot find cage for transceiver with id %d", transId)
+ }
+
+ //Add transceiver
+ transComponent, err := createTransceiverComponent(trans, targetCage.Name)
+ if err != nil {
+ return err
+ }
+
+ targetCage.Children = append(targetCage.Children, transComponent)
+
+ logger.WithFields(log.Fields{
+ "transId": transId,
+ "cageName": targetCage.Name,
+ "cageChildren": targetCage.Children,
+ }).Debug("Added transceiver to DMI inventory")
+
+ //Change plugged-in state
+ trans.PluggedIn = true
+
+ return nil
+}
+
//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")
@@ -318,16 +514,15 @@
dms.mPublisherCancelFunc()
}
- dms.deviceName = ""
dms.kafkaEndpoint = ""
dms.ipAddress = ""
- dms.deviceSerial = ""
- dms.ponTransceiverUuids = nil
- dms.ponTransceiverCageUuids = nil
- dms.uuid = ""
+ dms.uuid = nil
dms.root = nil
dms.metricChannel = nil
+ //Don't clear the Transceivers, so that they will survive
+ //new StartManagingDevice calls
+
logger.Infof("Stopped managing the device")
return &dmi.StopManagingDeviceResponse{Status: dmi.Status_OK_STATUS}, nil
}
@@ -348,8 +543,8 @@
return nil
}
- if req.DeviceUuid.Uuid != dms.uuid {
- logger.Errorf("Requested uuid =%s, uuid of existing device = %s", req.DeviceUuid.Uuid, dms.uuid)
+ if req.DeviceUuid.Uuid != dms.uuid.Uuid {
+ logger.Errorf("Requested uuid =%s, uuid of existing device = %s", req.DeviceUuid.Uuid, dms.uuid.Uuid)
// Wrong uuid, return error
errResponse := &dmi.PhysicalInventoryResponse{
Status: dmi.Status_ERROR_STATUS,
@@ -490,7 +685,7 @@
if request.LoggingProtocol == "" {
return errRetFunc(dmi.Status_ERROR_STATUS, dmi.SetRemoteEndpointResponse_LOGGING_ENDPOINT_PROTOCOL_ERROR)
}
- if request.DeviceUuid == nil || request.DeviceUuid.Uuid != dms.uuid {
+ if request.DeviceUuid == nil || request.DeviceUuid.Uuid != dms.uuid.Uuid {
return errRetFunc(dmi.Status_ERROR_STATUS, dmi.SetRemoteEndpointResponse_UNKNOWN_DEVICE)
}
@@ -511,7 +706,7 @@
Reason: dmi.GetLoggingEndpointResponse_UNKNOWN_DEVICE,
}, status.Errorf(codes.InvalidArgument, "invalid request")
}
- if request.Uuid.Uuid != dms.uuid {
+ if request.Uuid.Uuid != dms.uuid.Uuid {
return &dmi.GetLoggingEndpointResponse{
Status: dmi.Status_ERROR_STATUS,
Reason: dmi.GetLoggingEndpointResponse_UNKNOWN_DEVICE,
@@ -578,17 +773,15 @@
func (dms *DmiAPIServer) GetManagedDevices(context.Context, *empty.Empty) (*dmi.ManagedDevicesResponse, error) {
retResponse := dmi.ManagedDevicesResponse{}
//If our uuid is empty, we return empty list; else we fill details and return
- if dms.uuid != "" {
+ if dms.root != nil {
root := dmi.ManagedDeviceInfo{
Info: &dmi.ModifiableComponent{
- Name: dms.deviceName,
+ Name: getOltName(),
Uri: &dmi.Uri{
Uri: dms.ipAddress,
},
},
- DeviceUuid: &dmi.Uuid{
- Uuid: dms.uuid,
- },
+ DeviceUuid: dms.uuid,
}
retResponse.Devices = append(retResponse.Devices, &root)
@@ -601,32 +794,26 @@
//GetLogLevel Gets the configured log level for a certain entity on a certain device.
func (dms *DmiAPIServer) GetLogLevel(context.Context, *dmi.GetLogLevelRequest) (*dmi.GetLogLevelResponse, error) {
return &dmi.GetLogLevelResponse{
- Status: dmi.Status_OK_STATUS,
- DeviceUuid: &dmi.Uuid{
- Uuid: dms.uuid,
- },
- LogLevels: []*dmi.EntitiesLogLevel{},
+ Status: dmi.Status_OK_STATUS,
+ DeviceUuid: dms.uuid,
+ LogLevels: []*dmi.EntitiesLogLevel{},
}, nil
}
// SetLogLevel Sets the log level of the device, for each given entity to a certain level.
func (dms *DmiAPIServer) SetLogLevel(context.Context, *dmi.SetLogLevelRequest) (*dmi.SetLogLevelResponse, error) {
return &dmi.SetLogLevelResponse{
- Status: dmi.Status_OK_STATUS,
- DeviceUuid: &dmi.Uuid{
- Uuid: dms.uuid,
- },
+ Status: dmi.Status_OK_STATUS,
+ DeviceUuid: dms.uuid,
}, nil
}
// GetLoggableEntities Gets the entities of a device on which log can be configured.
func (dms *DmiAPIServer) GetLoggableEntities(context.Context, *dmi.GetLoggableEntitiesRequest) (*dmi.GetLogLevelResponse, error) {
return &dmi.GetLogLevelResponse{
- Status: dmi.Status_OK_STATUS,
- DeviceUuid: &dmi.Uuid{
- Uuid: dms.uuid,
- },
- LogLevels: []*dmi.EntitiesLogLevel{},
+ Status: dmi.Status_OK_STATUS,
+ DeviceUuid: dms.uuid,
+ LogLevels: []*dmi.EntitiesLogLevel{},
}, nil
}
diff --git a/internal/bbsim/dmiserver/dmi_metrics_generator.go b/internal/bbsim/dmiserver/dmi_metrics_generator.go
index fdb5bb4..c76e940 100755
--- a/internal/bbsim/dmiserver/dmi_metrics_generator.go
+++ b/internal/bbsim/dmiserver/dmi_metrics_generator.go
@@ -238,9 +238,7 @@
func updateMetricIDAndMetaData(id dmi.MetricNames, c *dmi.Component, apiSrv *DmiAPIServer, m *dmi.Metric) *dmi.Metric {
m.MetricId = id
m.MetricMetadata = &dmi.MetricMetaData{
- DeviceUuid: &dmi.Uuid{
- Uuid: apiSrv.uuid,
- },
+ DeviceUuid: apiSrv.uuid,
ComponentUuid: c.Uuid,
ComponentName: c.Name,
}
diff --git a/internal/bbsim/dmiserver/dmi_sw_mgmt.go b/internal/bbsim/dmiserver/dmi_sw_mgmt.go
index 0c89d42..934b41d 100755
--- a/internal/bbsim/dmiserver/dmi_sw_mgmt.go
+++ b/internal/bbsim/dmiserver/dmi_sw_mgmt.go
@@ -98,7 +98,7 @@
return status.Errorf(codes.InvalidArgument, "ConfigRequest is nil")
}
- if request.DeviceUuid == nil || request.DeviceUuid.Uuid != dms.uuid {
+ if request.DeviceUuid == nil || request.DeviceUuid.Uuid != dms.uuid.Uuid {
if err := stream.Send(&dmi.ConfigResponse{
Status: dmi.Status_ERROR_STATUS,
Reason: dmi.ConfigResponse_UNKNOWN_DEVICE,
@@ -129,7 +129,7 @@
return nil, status.Errorf(codes.InvalidArgument, "DeviceUuid is nil")
}
- if request.DeviceUuid.Uuid != dms.uuid {
+ if request.DeviceUuid.Uuid != dms.uuid.Uuid {
return &dmi.StartupConfigInfoResponse{
Status: dmi.Status_ERROR_STATUS,
Reason: dmi.StartupConfigInfoResponse_UNKNOWN_DEVICE,
diff --git a/internal/bbsim/dmiserver/dmi_transceiver.go b/internal/bbsim/dmiserver/dmi_transceiver.go
new file mode 100644
index 0000000..5dcc9b0
--- /dev/null
+++ b/internal/bbsim/dmiserver/dmi_transceiver.go
@@ -0,0 +1,283 @@
+/*
+ * 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"
+
+ "github.com/opencord/bbsim/api/bbsim"
+ "github.com/opencord/bbsim/internal/bbsim/devices"
+ dmi "github.com/opencord/device-management-interface/go/dmi"
+ log "github.com/sirupsen/logrus"
+ "google.golang.org/grpc/codes"
+ "google.golang.org/protobuf/types/known/timestamppb"
+)
+
+const (
+ ponInterfaceType = "pon"
+ alarmStatusRaise = "on"
+ alarmStatusClear = "off"
+)
+
+type Transceiver struct {
+ ID uint32
+ Uuid string
+ Name string
+ Pons []*devices.PonPort
+ Technology dmi.TransceiverType
+ //Setting this bool will prevent the transceiver
+ //from being plugged out while already out, and
+ //plugged in while already in, but won't prevent
+ //the associated PONs from being enabled in other
+ //ways while the transceiver is plugged out
+ PluggedIn bool
+}
+
+func newTransceiver(id uint32, pons []*devices.PonPort) *Transceiver {
+ return &Transceiver{
+ ID: id,
+ Uuid: getTransceiverUUID(id).Uuid,
+ Name: getTransceiverName(id),
+ Pons: pons,
+ Technology: dmi.TransceiverType_TYPE_UNDEFINED,
+ PluggedIn: true,
+ }
+}
+
+func getTransceiverWithId(transId uint32, dms *DmiAPIServer) (*Transceiver, error) {
+ for _, t := range dms.Transceivers {
+ if t.ID == transId {
+ return t, nil
+ }
+ }
+
+ return nil, fmt.Errorf("Cannot find transceiver with ID %d", transId)
+}
+
+/////// Handler methods for grpc API
+
+func (s DmiAPIServer) GetTransceivers(ctx context.Context, req *bbsim.DmiEmpty) (*bbsim.Transceivers, error) {
+ res := &bbsim.Transceivers{
+ Items: []*bbsim.Transceiver{},
+ }
+
+ for _, t := range s.Transceivers {
+ item := bbsim.Transceiver{
+ ID: t.ID,
+ UUID: t.Uuid,
+ Name: t.Name,
+ Technology: t.Technology.String(),
+ PluggedIn: t.PluggedIn,
+ PonIds: []uint32{},
+ }
+
+ for _, pon := range t.Pons {
+ item.PonIds = append(item.PonIds, pon.ID)
+ }
+
+ res.Items = append(res.Items, &item)
+ }
+
+ return res, nil
+}
+
+// PlugOutTransceiver plugs out the transceiver by its ID
+func (s DmiAPIServer) PlugOutTransceiver(ctx context.Context, req *bbsim.TransceiverRequest) (*bbsim.DmiResponse, error) {
+ logger.WithFields(log.Fields{
+ "IntfId": req.TransceiverId,
+ }).Infof("Received request to plug out PON transceiver")
+
+ res := &bbsim.DmiResponse{}
+ olt := devices.GetOLT()
+
+ //Generate DMI event
+ dmiServ, err := getDmiAPIServer()
+ if err != nil {
+ res.StatusCode = int32(codes.Unavailable)
+ res.Message = fmt.Sprintf("Cannot get DMI server instance: %v", err)
+ return res, nil
+ }
+
+ trans, err := getTransceiverWithId(req.TransceiverId, dmiServ)
+ if err != nil {
+ res.StatusCode = int32(codes.NotFound)
+ res.Message = fmt.Sprintf("Cannot find transceiver with ID %d: %v", req.TransceiverId, err)
+ return res, nil
+ }
+
+ if !trans.PluggedIn {
+ res.StatusCode = int32(codes.Aborted)
+ res.Message = fmt.Sprintf("Cannot plug out transceiver with ID %d since it's not plugged in", req.TransceiverId)
+ return res, nil
+ }
+
+ err = PlugoutTransceiverComponent(req.TransceiverId, dmiServ)
+ if err != nil {
+ res.StatusCode = int32(codes.NotFound)
+ res.Message = fmt.Sprintf("Cannot remove transceiver with ID %d: %v", req.TransceiverId, err)
+ return res, nil
+ }
+ logger.Debug("Removed transceiver from DMI inventory")
+
+ if olt.InternalState.Is(devices.OltInternalStateEnabled) {
+ logger.Debug("Sending alarms for transceiver plug out")
+ for _, pon := range trans.Pons {
+ if pon.InternalState.Is("enabled") {
+
+ if err = olt.SetAlarm(pon.ID, ponInterfaceType, alarmStatusRaise); err != nil {
+ logger.WithFields(log.Fields{
+ "ponId": pon.ID,
+ "err": err,
+ }).Error("Cannot raise LOS alarm for PON")
+ }
+
+ if err = pon.InternalState.Event("disable"); err != nil {
+ logger.WithFields(log.Fields{
+ "ponId": pon.ID,
+ "err": err,
+ }).Error("Cannot disable PON")
+ continue
+ }
+
+ for _, onu := range pon.Onus {
+ if err := onu.SetAlarm(bbsim.AlarmType_ONU_ALARM_LOS.String(), alarmStatusRaise); err != nil {
+ logger.WithFields(log.Fields{
+ "ponId": pon.ID,
+ "onuId": onu.ID,
+ "err": err,
+ }).Error("Cannot raise LOS alarm for ONU")
+ }
+ }
+ }
+ }
+ } else {
+ logger.Debug("No operation on devices since the OLT is not enabled")
+ }
+
+ event := dmi.Event{
+ EventId: dmi.EventIds_EVENT_TRANSCEIVER_PLUG_OUT,
+ EventMetadata: &dmi.EventMetaData{
+ DeviceUuid: dmiServ.uuid,
+ ComponentUuid: &dmi.Uuid{
+ Uuid: trans.Uuid,
+ },
+ ComponentName: trans.Name,
+ },
+ RaisedTs: timestamppb.Now(),
+ }
+
+ sendOutEventOnKafka(event, dmiServ)
+ logger.Debug("Transceiver plug out event sent")
+
+ res.StatusCode = int32(codes.OK)
+ res.Message = fmt.Sprintf("Plugged out transceiver %d", req.TransceiverId)
+
+ return res, nil
+}
+
+// PlugInTransceiver plugs in the transceiver by its ID
+func (s DmiAPIServer) PlugInTransceiver(ctx context.Context, req *bbsim.TransceiverRequest) (*bbsim.DmiResponse, error) {
+ logger.WithFields(log.Fields{
+ "IntfId": req.TransceiverId,
+ }).Infof("Received request to plug in PON transceiver")
+
+ res := &bbsim.DmiResponse{}
+ olt := devices.GetOLT()
+
+ //Generate DMI event
+ dmiServ, err := getDmiAPIServer()
+ if err != nil {
+ res.StatusCode = int32(codes.Unavailable)
+ res.Message = fmt.Sprintf("Cannot get DMI server instance: %v", err)
+ return res, nil
+ }
+
+ trans, err := getTransceiverWithId(req.TransceiverId, dmiServ)
+ if err != nil {
+ res.StatusCode = int32(codes.NotFound)
+ res.Message = fmt.Sprintf("Cannot find transceiver with ID %d: %v", req.TransceiverId, err)
+ return res, nil
+ }
+
+ if trans.PluggedIn {
+ res.StatusCode = int32(codes.Aborted)
+ res.Message = fmt.Sprintf("Cannot plug in transceiver with ID %d since it's already plugged in", req.TransceiverId)
+ return res, nil
+ }
+
+ err = PluginTransceiverComponent(req.TransceiverId, dmiServ)
+ if err != nil {
+ res.StatusCode = int32(codes.NotFound)
+ res.Message = fmt.Sprintf("Cannot add transceiver with ID %d: %v", req.TransceiverId, err)
+ return res, nil
+ }
+ logger.Debug("Added transceiver to DMI inventory")
+
+ if olt.InternalState.Is(devices.OltInternalStateEnabled) {
+ logger.Debug("Sending alarms for transceiver plug in")
+ for _, pon := range trans.Pons {
+
+ if err = olt.SetAlarm(pon.ID, ponInterfaceType, alarmStatusClear); err != nil {
+ logger.WithFields(log.Fields{
+ "ponId": pon.ID,
+ "err": err,
+ }).Error("Cannot clear LOS alarm for ONU")
+ }
+
+ if err = pon.InternalState.Event("enable"); err != nil {
+ logger.WithFields(log.Fields{
+ "ponId": pon.ID,
+ "err": err,
+ }).Error("Cannot enable PON")
+ continue
+ }
+
+ for _, onu := range pon.Onus {
+ if err := onu.SetAlarm(bbsim.AlarmType_ONU_ALARM_LOS.String(), alarmStatusClear); err != nil {
+ logger.WithFields(log.Fields{
+ "ponId": pon.ID,
+ "onuId": onu.ID,
+ "err": err,
+ }).Error("Cannot clear LOS alarm for ONU")
+ }
+ }
+ }
+ } else {
+ logger.Debug("No operation on devices since the OLT is not enabled")
+ }
+
+ event := dmi.Event{
+ EventId: dmi.EventIds_EVENT_TRANSCEIVER_PLUG_IN,
+ EventMetadata: &dmi.EventMetaData{
+ DeviceUuid: dmiServ.uuid,
+ ComponentUuid: &dmi.Uuid{
+ Uuid: trans.Uuid,
+ },
+ ComponentName: trans.Name,
+ },
+ RaisedTs: timestamppb.Now(),
+ }
+
+ sendOutEventOnKafka(event, dmiServ)
+ logger.Debug("Transceiver plug in event sent")
+
+ res.StatusCode = int32(codes.OK)
+ res.Message = fmt.Sprintf("Plugged in transceiver %d", req.TransceiverId)
+
+ return res, nil
+}
diff --git a/internal/bbsimctl/commands/dmi.go b/internal/bbsimctl/commands/dmi.go
new file mode 100644
index 0000000..526cbad
--- /dev/null
+++ b/internal/bbsimctl/commands/dmi.go
@@ -0,0 +1,171 @@
+/*
+ * 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"
+ "os"
+
+ "github.com/jessevdk/go-flags"
+ "github.com/opencord/bbsim/api/bbsim"
+ pb "github.com/opencord/bbsim/api/bbsim"
+ "github.com/opencord/bbsim/internal/bbsimctl/config"
+ "github.com/opencord/cordctl/pkg/format"
+ log "github.com/sirupsen/logrus"
+ "google.golang.org/grpc"
+)
+
+const (
+ DEFAULT_TRANSCEIVER_HEADER_FORMAT = "table{{ .ID }}\t{{ .UUID }}\t{{ .Name }}\t{{ .Technology }}\t{{ .PluggedIn }}\t{{ .PonIds }}"
+)
+
+type DMIOptions struct {
+ Events DmiEventOptions `command:"events"`
+ Transceiver DmiTransceiverOptions `command:"transceiver"`
+}
+
+type DmiEventOptions struct {
+ Create DmiEventCreate `command:"create"`
+}
+
+type DmiEventCreate struct {
+ Args struct {
+ Name string
+ } `positional-args:"yes" required:"yes"`
+}
+
+type DmiTransceiverOptions struct {
+ PlugIn DmiTransceiverPlugIn `command:"plug_in"`
+ PlugOut DmiTransceiverPlugOut `command:"plug_out"`
+ List DmiTransceiversList `command:"list"`
+}
+
+type DmiTransceiversList struct {
+}
+
+type DmiTransceiverPlugIn struct {
+ Args struct {
+ TransceiverId uint32
+ } `positional-args:"yes" required:"yes"`
+}
+
+type DmiTransceiverPlugOut struct {
+ Args struct {
+ TransceiverId uint32
+ } `positional-args:"yes" required:"yes"`
+}
+
+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
+}
+
+//Print a list of the transceivers and their state
+func (pon *DmiTransceiversList) Execute(args []string) error {
+ client, conn := dmiEventGrpcClient()
+ defer conn.Close()
+
+ ctx, cancel := context.WithTimeout(context.Background(), config.GlobalConfig.Grpc.Timeout)
+ defer cancel()
+
+ transceivers, err := client.GetTransceivers(ctx, &pb.DmiEmpty{})
+ if err != nil {
+ log.Errorf("Cannot get transceivers list: %v", err)
+ return err
+ }
+
+ // print out
+ tableFormat := format.Format(DEFAULT_TRANSCEIVER_HEADER_FORMAT)
+
+ if err := tableFormat.Execute(os.Stdout, true, transceivers.Items); err != nil {
+ log.Fatalf("Error while formatting transceivers table: %s", err)
+ }
+
+ return nil
+}
+
+//Plug in the specified transceiver
+func (pon *DmiTransceiverPlugIn) Execute(args []string) error {
+ client, conn := dmiEventGrpcClient()
+ defer conn.Close()
+
+ ctx, cancel := context.WithTimeout(context.Background(), config.GlobalConfig.Grpc.Timeout)
+ defer cancel()
+
+ req := pb.TransceiverRequest{
+ TransceiverId: uint32(pon.Args.TransceiverId),
+ }
+
+ res, err := client.PlugInTransceiver(ctx, &req)
+ if err != nil {
+ log.Errorf("Cannot plug in PON transceiver: %v", err)
+ return err
+ }
+
+ fmt.Println(fmt.Sprintf("[Status: %d] %s", res.StatusCode, res.Message))
+ return nil
+}
+
+//Plug out the specified transceiver
+func (pon *DmiTransceiverPlugOut) Execute(args []string) error {
+ client, conn := dmiEventGrpcClient()
+ defer conn.Close()
+
+ ctx, cancel := context.WithTimeout(context.Background(), config.GlobalConfig.Grpc.Timeout)
+ defer cancel()
+
+ req := pb.TransceiverRequest{
+ TransceiverId: uint32(pon.Args.TransceiverId),
+ }
+
+ res, err := client.PlugOutTransceiver(ctx, &req)
+ if err != nil {
+ log.Errorf("Cannot plug out PON transceiver: %v", err)
+ return err
+ }
+
+ fmt.Println(fmt.Sprintf("[Status: %d] %s", res.StatusCode, res.Message))
+ return nil
+}
diff --git a/internal/bbsimctl/commands/dmi_events.go b/internal/bbsimctl/commands/dmi_events.go
deleted file mode 100644
index 1608965..0000000
--- a/internal/bbsimctl/commands/dmi_events.go
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * 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
-}