VOL-3510:Implement Device Management Interface (dmi) on bbsim

Change-Id: If3ff7e8b085b173fd44a37bd005cc2087dff4c63
diff --git a/internal/bbsim/dmiserver/dmi_api_server.go b/internal/bbsim/dmiserver/dmi_api_server.go
new file mode 100644
index 0000000..e90f852
--- /dev/null
+++ b/internal/bbsim/dmiserver/dmi_api_server.go
@@ -0,0 +1,75 @@
+/*
+ * 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 (
+	"net"
+
+	"github.com/opencord/bbsim/internal/common"
+	dmi "github.com/opencord/device-management-interface/go/dmi"
+	log "github.com/sirupsen/logrus"
+
+	"google.golang.org/grpc"
+	"google.golang.org/grpc/reflection"
+)
+
+var logger = log.WithFields(log.Fields{
+	"module": "DmiServer",
+})
+
+//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
+	components              []*dmi.Component
+}
+
+var dmiServ DmiAPIServer
+
+//StartDmiAPIServer starts a new grpc server for the Device Manager Interface
+func StartDmiAPIServer() (*grpc.Server, error) {
+	dmiServ = DmiAPIServer{}
+
+	return dmiServ.newDmiAPIServer()
+}
+
+// newDmiAPIServer launches a new grpc server for the Device Manager Interface
+func (dms *DmiAPIServer) newDmiAPIServer() (*grpc.Server, error) {
+	address := common.Config.BBSim.DmiServerAddress
+	lis, err := net.Listen("tcp", address)
+	if err != nil {
+		return nil, err
+	}
+
+	grpcServer := grpc.NewServer()
+
+	dmi.RegisterNativeHWManagementServiceServer(grpcServer, dms)
+	dmi.RegisterNativeSoftwareManagementServiceServer(grpcServer, dms)
+	dmi.RegisterNativeEventsManagementServiceServer(grpcServer, dms)
+	dmi.RegisterNativeMetricsManagementServiceServer(grpcServer, dms)
+
+	reflection.Register(grpcServer)
+
+	go func() { _ = grpcServer.Serve(lis) }()
+	logger.Debugf("DMI grpc Server listening on %v", address)
+
+	return grpcServer, nil
+}
diff --git a/internal/bbsim/dmiserver/dmi_events_mgmt.go b/internal/bbsim/dmiserver/dmi_events_mgmt.go
new file mode 100644
index 0000000..6db9e84
--- /dev/null
+++ b/internal/bbsim/dmiserver/dmi_events_mgmt.go
@@ -0,0 +1,45 @@
+/*
+ * 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"
+
+	dmi "github.com/opencord/device-management-interface/go/dmi"
+)
+
+//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{{}}
+	return &dmi.ListEventsResponse{
+		Status: dmi.Status_OK,
+		Reason: dmi.Reason_UNDEFINED_REASON,
+		Events: &dmi.EventsCfg{
+			Items: events,
+		},
+	}, nil
+}
+
+//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)
+	return &dmi.EventsConfigurationResponse{
+		Status: dmi.Status_OK,
+	}, nil
+}
diff --git a/internal/bbsim/dmiserver/dmi_hw_mgmt.go b/internal/bbsim/dmiserver/dmi_hw_mgmt.go
new file mode 100644
index 0000000..ba5a0c4
--- /dev/null
+++ b/internal/bbsim/dmiserver/dmi_hw_mgmt.go
@@ -0,0 +1,299 @@
+/*
+ * 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/golang/protobuf/ptypes/empty"
+	"github.com/golang/protobuf/ptypes/timestamp"
+	"github.com/opencord/bbsim/internal/bbsim/devices"
+	"github.com/opencord/bbsim/internal/common"
+	dmi "github.com/opencord/device-management-interface/go/dmi"
+
+	guuid "github.com/google/uuid"
+	"google.golang.org/grpc/codes"
+	"google.golang.org/grpc/status"
+)
+
+func getUUID(seed string) string {
+	return guuid.NewMD5(guuid.Nil, []byte(seed)).String()
+}
+
+//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
+	logger.Debugf("StartManagingDevice() invoked with request  %+v", req)
+	if req == nil {
+		return status.Errorf(codes.FailedPrecondition, "request is empty")
+	}
+
+	if req.Name == "" {
+		return status.Errorf(codes.InvalidArgument, "'Name' can not be empty in the request")
+	}
+
+	olt := devices.GetOLT()
+
+	// 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)
+
+	var components []*dmi.Component
+
+	// Create and store the component for transceivers and transceiver cages
+	for i := 0; i < olt.NumPon; i++ {
+		label := fmt.Sprintf("pon-%d", olt.Pons[i].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("cage-%d", i)
+
+		trans := dmi.Component{
+			Name:        transName,
+			Class:       dmi.ComponentType_COMPONENT_TYPE_TRANSCEIVER,
+			Description: "XGS-PON",
+			Uuid: &dmi.Uuid{
+				Uuid: dms.ponTransceiverUuids[i],
+			},
+			Parent: cageName,
+		}
+
+		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},
+		}
+
+		components = append(components, &cage)
+	}
+
+	dms.components = components
+
+	logger.Debugf("Generated UUID for the uri %s is %s", dms.ipAddress, dms.uuid)
+	response := &dmi.StartManagingDeviceResponse{
+		Status: dmi.Status_OK,
+		DeviceUuid: &dmi.Uuid{
+			Uuid: dms.uuid,
+		},
+	}
+
+	err := stream.Send(response)
+	if err != nil {
+		logger.Errorf("Error while sending response to client %v", err.Error())
+		return status.Errorf(codes.Unknown, err.Error())
+	}
+
+	return nil
+}
+
+//StopManagingDevice stops management of a device and cleans up any context and caches for that device
+func (dms *DmiAPIServer) StopManagingDevice(context.Context, *dmi.StopManagingDeviceRequest) (*dmi.StopManagingDeviceResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "rpc StopManagingDevice not implemented")
+}
+
+//GetPhysicalInventory gets the HW inventory details of the Device
+func (dms *DmiAPIServer) GetPhysicalInventory(req *dmi.PhysicalInventoryRequest, stream dmi.NativeHWManagementService_GetPhysicalInventoryServer) error {
+	if req == nil || req.DeviceUuid == nil || req.DeviceUuid.Uuid == "" {
+		return status.Errorf(codes.InvalidArgument, "device-UUID missing in the request")
+	}
+
+	// Function to send the response back on the stream
+	sendResponseBackOnStream := func(stream dmi.NativeHWManagementService_GetPhysicalInventoryServer, msg *dmi.PhysicalInventoryResponse) error {
+		err := stream.Send(msg)
+		if err != nil {
+			logger.Errorf("Error sending response to client, error: %v", err)
+			return status.Errorf(codes.Internal, "Error sending response to client "+err.Error())
+		}
+		return nil
+	}
+
+	if req.DeviceUuid.Uuid != dms.uuid {
+		logger.Errorf("Requested uuid =%s, uuid of existing device = %s", req.DeviceUuid.Uuid, dms.uuid)
+		// Wrong uuid, return error
+		errResponse := &dmi.PhysicalInventoryResponse{
+			Status:    dmi.Status_ERROR,
+			Reason:    dmi.Reason_UNKNOWN_DEVICE,
+			Inventory: &dmi.Hardware{},
+		}
+
+		return sendResponseBackOnStream(stream, errResponse)
+	}
+
+	response := &dmi.PhysicalInventoryResponse{
+		Status: dmi.Status_OK,
+		Inventory: &dmi.Hardware{
+			LastChange: &timestamp.Timestamp{
+				Seconds: 0,
+				Nanos:   0,
+			},
+			Root: &dmi.Component{
+				Name:         dms.deviceName,
+				Class:        0,
+				Description:  "",
+				Parent:       "",
+				ParentRelPos: 0,
+				Children:     dms.components,
+				SerialNum:    dms.deviceSerial,
+				MfgName:      common.Config.Olt.Vendor,
+				IsFru:        false,
+				Uri: &dmi.Uri{
+					Uri: dms.ipAddress,
+				},
+				Uuid: &dmi.Uuid{
+					Uuid: dms.uuid,
+				},
+				State: &dmi.ComponentState{},
+			},
+		},
+	}
+	return sendResponseBackOnStream(stream, response)
+}
+
+//Contains tells whether arr contains element.
+func Contains(arr []string, element string) bool {
+	for _, item := range arr {
+		logger.Debugf("Checking in Contains slice elem = %v str = %s", item, element)
+		if element == item {
+			return true
+		}
+	}
+	return false
+}
+
+func findComponent(l []*dmi.Component, compUUID string) *dmi.Component {
+	for _, comp := range l {
+		logger.Debugf("findComponent slice comp = %v compUUID = %s", comp, compUUID)
+		if comp.Uuid.Uuid == compUUID {
+			return comp
+		}
+
+		for _, child := range comp.GetChildren() {
+			logger.Debugf("findComponent Child slice comp = %v compUUID = %s", comp, compUUID)
+			if child.Uuid.Uuid == compUUID {
+				return child
+			}
+		}
+	}
+
+	return nil
+}
+
+func sendGetHWComponentResponse(c *dmi.Component, stream dmi.NativeHWManagementService_GetHWComponentInfoServer) error {
+	apiStatus := dmi.Status_OK
+	reason := dmi.Reason_UNDEFINED_REASON
+
+	if c == nil {
+		apiStatus = dmi.Status_ERROR
+		reason = dmi.Reason_UNKNOWN_DEVICE
+	}
+
+	response := &dmi.HWComponentInfoGetResponse{
+		Status:    apiStatus,
+		Reason:    reason,
+		Component: c,
+	}
+
+	err := stream.Send(response)
+	if err != nil {
+		logger.Errorf("Error sending response to client, error: %v", err)
+		return status.Errorf(codes.Internal, "Error sending response to client "+err.Error())
+	}
+	return nil
+}
+
+//GetHWComponentInfo gets the details of a particular HW component
+func (dms *DmiAPIServer) GetHWComponentInfo(req *dmi.HWComponentInfoGetRequest, stream dmi.NativeHWManagementService_GetHWComponentInfoServer) error {
+	logger.Debugf("GetHWComponentInfo() invoked with request %+v", req)
+
+	if req == nil {
+		return status.Errorf(codes.FailedPrecondition, "can not entertain nil request")
+	}
+	if stream == nil {
+		logger.Errorf("stream to send is nil, not sending response from gRPC server ")
+		return status.Errorf(codes.Internal, "stream to send is nil, can not send response from gRPC server")
+	}
+
+	componentFound := Contains(dms.ponTransceiverUuids, req.ComponentUuid.Uuid)
+	if !componentFound {
+		componentFound = Contains(dms.ponTransceiverCageUuids, req.ComponentUuid.Uuid)
+	}
+
+	if req.DeviceUuid.Uuid != dms.uuid || !componentFound {
+		// Wrong uuid, return error
+		return sendGetHWComponentResponse(nil, stream)
+	}
+
+	// Search for the component and return it
+	c := findComponent(dms.components, req.ComponentUuid.Uuid)
+	return sendGetHWComponentResponse(c, stream)
+}
+
+//SetHWComponentInfo sets the permissible attributes of a HW component
+func (dms *DmiAPIServer) SetHWComponentInfo(context.Context, *dmi.HWComponentInfoSetRequest) (*dmi.HWComponentInfoSetResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "rpc SetHWComponentInfo not implemented")
+}
+
+//SetLoggingEndpoint sets the location to which logs need to be shipped
+func (dms *DmiAPIServer) SetLoggingEndpoint(context.Context, *dmi.SetLoggingEndpointRequest) (*dmi.SetRemoteEndpointResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "rpc SetLoggingEndpoint not implemented")
+}
+
+//GetLoggingEndpoint gets the configured location to which the logs are being shipped
+func (dms *DmiAPIServer) GetLoggingEndpoint(context.Context, *dmi.Uuid) (*dmi.GetLoggingEndpointResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "rpc GetLoggingEndpoint not implemented")
+}
+
+//SetMsgBusEndpoint sets the location of the Message Bus to which events and metrics are shipped
+func (dms *DmiAPIServer) SetMsgBusEndpoint(context.Context, *dmi.SetMsgBusEndpointRequest) (*dmi.SetRemoteEndpointResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "rpc SetMsgBusEndpoint not implemented")
+}
+
+//GetMsgBusEndpoint gets the configured location to which the events and metrics are being shipped
+func (dms *DmiAPIServer) GetMsgBusEndpoint(context.Context, *empty.Empty) (*dmi.GetMsgBusEndpointResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "rpc GetMsgBusEndpoint not implemented")
+}
+
+//GetManagedDevices returns an object containing a list of devices managed by this entity
+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 != "" {
+		root := dmi.ModifiableComponent{
+			Name: dms.deviceName,
+			Uri: &dmi.Uri{
+				Uri: dms.ipAddress,
+			},
+		}
+
+		retResponse.Devices = append(retResponse.Devices, &root)
+	}
+
+	return &retResponse, nil
+}
diff --git a/internal/bbsim/dmiserver/dmi_metrics_mgmt.go b/internal/bbsim/dmiserver/dmi_metrics_mgmt.go
new file mode 100644
index 0000000..51b5dcc
--- /dev/null
+++ b/internal/bbsim/dmiserver/dmi_metrics_mgmt.go
@@ -0,0 +1,56 @@
+/*
+ * 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"
+
+	dmi "github.com/opencord/device-management-interface/go/dmi"
+)
+
+//ListMetrics lists the supported metrics for the passed device.
+func (dms *DmiAPIServer) ListMetrics(ctx context.Context, req *dmi.HardwareID) (*dmi.ListMetricsResponse, error) {
+	logger.Debugf("ListMetrics invoked with request %+v", req)
+	//return empty list of metrics for now
+	metrics := []*dmi.MetricConfig{{}}
+	return &dmi.ListMetricsResponse{
+		Status: dmi.Status_OK,
+		Reason: 0,
+		Metrics: &dmi.MetricsConfig{
+			Metrics: metrics,
+		},
+	}, nil
+}
+
+//UpdateMetricsConfiguration updates the configuration of the list of metrics in the request
+func (dms *DmiAPIServer) UpdateMetricsConfiguration(ctx context.Context, req *dmi.MetricsConfigurationRequest) (*dmi.MetricsConfigurationResponse, error) {
+	logger.Debugf("UpdateMetricConfiguration invoked with request %+v", req)
+	return &dmi.MetricsConfigurationResponse{
+		Status: dmi.Status_OK,
+	}, nil
+}
+
+//GetMetric gets the instantenous value of a metric
+func (dms *DmiAPIServer) GetMetric(ctx context.Context, req *dmi.GetMetricRequest) (*dmi.GetMetricResponse, error) {
+	logger.Debugf("GetMetric invoked with request %+v", req)
+	return &dmi.GetMetricResponse{
+		Status: dmi.Status_OK,
+		Reason: dmi.Reason_UNDEFINED_REASON,
+		Metric: &dmi.Metric{},
+	}, nil
+
+}
diff --git a/internal/bbsim/dmiserver/dmi_sw_mgmt.go b/internal/bbsim/dmiserver/dmi_sw_mgmt.go
new file mode 100644
index 0000000..0d24d9d
--- /dev/null
+++ b/internal/bbsim/dmiserver/dmi_sw_mgmt.go
@@ -0,0 +1,89 @@
+/*
+ * 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"
+
+	dmi "github.com/opencord/device-management-interface/go/dmi"
+)
+
+//GetSoftwareVersion gets the software version information of the Active and Standby images
+func (dms *DmiAPIServer) GetSoftwareVersion(ctx context.Context, req *dmi.HardwareID) (*dmi.GetSoftwareVersionInformationResponse, error) {
+	// TODO: Make this more interesting by taking values from BBSim if available
+	logger.Debugf("GetSoftwareVersion invoked with for device %+v", req)
+	return &dmi.GetSoftwareVersionInformationResponse{
+		Status: dmi.Status_OK,
+		Reason: dmi.Reason_UNDEFINED_REASON,
+		Info: &dmi.SoftwareVersionInformation{
+			ActiveVersions: []*dmi.ImageVersion{{
+				ImageName: "BBSIM-DUMMY-IMAGE-1",
+				Version:   "BBSIM-DUMMY-VERSION-1",
+			}},
+			StandbyVersions: []*dmi.ImageVersion{{
+				ImageName: "BBSIM-DUMMY-IMAGE-2",
+				Version:   "BBSIM-DUMMY-VERSION-2",
+			}},
+		},
+	}, nil
+}
+
+//DownloadImage downloads and installs the image in the standby partition, returns the status/progress of the Install
+func (dms *DmiAPIServer) DownloadImage(req *dmi.DownloadImageRequest, stream dmi.NativeSoftwareManagementService_DownloadImageServer) error {
+	logger.Debugf("DownloadImage invoked with request %+v", req)
+	err := stream.Send(&dmi.ImageStatus{
+		Status: dmi.Status_OK,
+		State:  dmi.ImageStatus_COPYING_IMAGE,
+	})
+	if err != nil {
+		logger.Errorf("Error sending image-status for DownloadImage, %v", err)
+		return err
+	}
+	return nil
+
+}
+
+//ActivateImage Activates and runs the OLT with the image in the standby partition
+func (dms *DmiAPIServer) ActivateImage(req *dmi.HardwareID, stream dmi.NativeSoftwareManagementService_ActivateImageServer) error {
+	logger.Debugf("ActivateImage invoked with request %+v", req)
+	err := stream.Send(&dmi.ImageStatus{
+		Status: dmi.Status_OK,
+		State:  dmi.ImageStatus_ACTIVATION_COMPLETE,
+	})
+
+	if err != nil {
+		logger.Errorf("Error sending image-status for ActivateImage, %v", err)
+		return err
+	}
+	return nil
+
+}
+
+//RevertToStandbyImage marks the image in the Standby as Active and reboots the device, so that it boots from that image which was in the standby.
+func (dms *DmiAPIServer) RevertToStandbyImage(req *dmi.HardwareID, stream dmi.NativeSoftwareManagementService_RevertToStandbyImageServer) error {
+	logger.Debugf("RevertToStandbyImage invoked with request %+v", req)
+	err := stream.Send(&dmi.ImageStatus{
+		Status: dmi.Status_OK,
+		State:  dmi.ImageStatus_ACTIVATION_COMPLETE,
+	})
+
+	if err != nil {
+		logger.Errorf("Error sending image-status for RevertToStandbyImage, %v", err)
+		return err
+	}
+	return nil
+}
diff --git a/internal/common/options.go b/internal/common/options.go
index 4ba9301..9978d9e 100644
--- a/internal/common/options.go
+++ b/internal/common/options.go
@@ -109,6 +109,7 @@
 	ControlledActivation string  `yaml:"controlled_activation"`
 	EnablePerf           bool    `yaml:"enable_perf"`
 	KafkaEventTopic      string  `yaml:"kafka_event_topic"`
+	DmiServerAddress     string  `yaml:"dmi_server_address"`
 }
 
 type BBRConfig struct {
@@ -226,6 +227,7 @@
 	openolt_address := flag.String("openolt_address", conf.BBSim.OpenOltAddress, "IP address:port")
 	api_address := flag.String("api_address", conf.BBSim.ApiAddress, "IP address:port")
 	rest_api_address := flag.String("rest_api_address", conf.BBSim.RestApiAddress, "IP address:port")
+	dmi_server_address := flag.String("dmi_server_address", conf.BBSim.DmiServerAddress, "IP address:port")
 
 	profileCpu := flag.String("cpuprofile", "", "write cpu profile to file")
 
@@ -265,6 +267,7 @@
 	conf.BBSim.KafkaEventTopic = *kafkaEventTopic
 	conf.BBSim.AuthRetry = *authRetry
 	conf.BBSim.DhcpRetry = *dhcpRetry
+	conf.BBSim.DmiServerAddress = *dmi_server_address
 
 	// update device id if not set
 	if conf.Olt.DeviceId == "" {
@@ -297,6 +300,7 @@
 			KafkaEventTopic:      "",
 			DhcpRetry:            false,
 			AuthRetry:            false,
+			DmiServerAddress:     ":50075",
 		},
 		OltConfig{
 			Vendor:             "BBSim",