[VOL-4687] Add transceivers to DMI, add commands to bbsimctl to manage them

Change-Id: Id1fe29eb48a7abb3c86958827edce70b75707de9
diff --git a/VERSION b/VERSION
index 89c881b..e0a6b34 100644
--- a/VERSION
+++ b/VERSION
@@ -1 +1 @@
-1.12.4
+1.12.5
diff --git a/api/bbsim/bbsim_dmi.pb.go b/api/bbsim/bbsim_dmi.pb.go
index d2cc5f8..a296e5e 100644
--- a/api/bbsim/bbsim_dmi.pb.go
+++ b/api/bbsim/bbsim_dmi.pb.go
@@ -63,7 +63,7 @@
 	return ""
 }
 
-type DmiCreateEventResponse struct {
+type DmiResponse 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:"-"`
@@ -71,68 +71,272 @@
 	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) {
+func (m *DmiResponse) Reset()         { *m = DmiResponse{} }
+func (m *DmiResponse) String() string { return proto.CompactTextString(m) }
+func (*DmiResponse) ProtoMessage()    {}
+func (*DmiResponse) 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 *DmiResponse) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_DmiResponse.Unmarshal(m, b)
 }
-func (m *DmiCreateEventResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
-	return xxx_messageInfo_DmiCreateEventResponse.Marshal(b, m, deterministic)
+func (m *DmiResponse) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_DmiResponse.Marshal(b, m, deterministic)
 }
-func (m *DmiCreateEventResponse) XXX_Merge(src proto.Message) {
-	xxx_messageInfo_DmiCreateEventResponse.Merge(m, src)
+func (m *DmiResponse) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_DmiResponse.Merge(m, src)
 }
-func (m *DmiCreateEventResponse) XXX_Size() int {
-	return xxx_messageInfo_DmiCreateEventResponse.Size(m)
+func (m *DmiResponse) XXX_Size() int {
+	return xxx_messageInfo_DmiResponse.Size(m)
 }
-func (m *DmiCreateEventResponse) XXX_DiscardUnknown() {
-	xxx_messageInfo_DmiCreateEventResponse.DiscardUnknown(m)
+func (m *DmiResponse) XXX_DiscardUnknown() {
+	xxx_messageInfo_DmiResponse.DiscardUnknown(m)
 }
 
-var xxx_messageInfo_DmiCreateEventResponse proto.InternalMessageInfo
+var xxx_messageInfo_DmiResponse proto.InternalMessageInfo
 
-func (m *DmiCreateEventResponse) GetStatusCode() int32 {
+func (m *DmiResponse) GetStatusCode() int32 {
 	if m != nil {
 		return m.StatusCode
 	}
 	return 0
 }
 
-func (m *DmiCreateEventResponse) GetMessage() string {
+func (m *DmiResponse) GetMessage() string {
 	if m != nil {
 		return m.Message
 	}
 	return ""
 }
 
+type DmiEmpty struct {
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *DmiEmpty) Reset()         { *m = DmiEmpty{} }
+func (m *DmiEmpty) String() string { return proto.CompactTextString(m) }
+func (*DmiEmpty) ProtoMessage()    {}
+func (*DmiEmpty) Descriptor() ([]byte, []int) {
+	return fileDescriptor_49e784b4938902cc, []int{2}
+}
+
+func (m *DmiEmpty) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_DmiEmpty.Unmarshal(m, b)
+}
+func (m *DmiEmpty) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_DmiEmpty.Marshal(b, m, deterministic)
+}
+func (m *DmiEmpty) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_DmiEmpty.Merge(m, src)
+}
+func (m *DmiEmpty) XXX_Size() int {
+	return xxx_messageInfo_DmiEmpty.Size(m)
+}
+func (m *DmiEmpty) XXX_DiscardUnknown() {
+	xxx_messageInfo_DmiEmpty.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_DmiEmpty proto.InternalMessageInfo
+
+type TransceiverRequest struct {
+	TransceiverId        uint32   `protobuf:"varint,1,opt,name=TransceiverId,proto3" json:"TransceiverId,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *TransceiverRequest) Reset()         { *m = TransceiverRequest{} }
+func (m *TransceiverRequest) String() string { return proto.CompactTextString(m) }
+func (*TransceiverRequest) ProtoMessage()    {}
+func (*TransceiverRequest) Descriptor() ([]byte, []int) {
+	return fileDescriptor_49e784b4938902cc, []int{3}
+}
+
+func (m *TransceiverRequest) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_TransceiverRequest.Unmarshal(m, b)
+}
+func (m *TransceiverRequest) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_TransceiverRequest.Marshal(b, m, deterministic)
+}
+func (m *TransceiverRequest) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_TransceiverRequest.Merge(m, src)
+}
+func (m *TransceiverRequest) XXX_Size() int {
+	return xxx_messageInfo_TransceiverRequest.Size(m)
+}
+func (m *TransceiverRequest) XXX_DiscardUnknown() {
+	xxx_messageInfo_TransceiverRequest.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_TransceiverRequest proto.InternalMessageInfo
+
+func (m *TransceiverRequest) GetTransceiverId() uint32 {
+	if m != nil {
+		return m.TransceiverId
+	}
+	return 0
+}
+
+type Transceiver struct {
+	ID                   uint32   `protobuf:"varint,1,opt,name=ID,proto3" json:"ID,omitempty"`
+	UUID                 string   `protobuf:"bytes,2,opt,name=UUID,proto3" json:"UUID,omitempty"`
+	Name                 string   `protobuf:"bytes,3,opt,name=Name,proto3" json:"Name,omitempty"`
+	Technology           string   `protobuf:"bytes,4,opt,name=Technology,proto3" json:"Technology,omitempty"`
+	PluggedIn            bool     `protobuf:"varint,5,opt,name=PluggedIn,proto3" json:"PluggedIn,omitempty"`
+	PonIds               []uint32 `protobuf:"varint,6,rep,packed,name=PonIds,proto3" json:"PonIds,omitempty"`
+	XXX_NoUnkeyedLiteral struct{} `json:"-"`
+	XXX_unrecognized     []byte   `json:"-"`
+	XXX_sizecache        int32    `json:"-"`
+}
+
+func (m *Transceiver) Reset()         { *m = Transceiver{} }
+func (m *Transceiver) String() string { return proto.CompactTextString(m) }
+func (*Transceiver) ProtoMessage()    {}
+func (*Transceiver) Descriptor() ([]byte, []int) {
+	return fileDescriptor_49e784b4938902cc, []int{4}
+}
+
+func (m *Transceiver) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_Transceiver.Unmarshal(m, b)
+}
+func (m *Transceiver) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_Transceiver.Marshal(b, m, deterministic)
+}
+func (m *Transceiver) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Transceiver.Merge(m, src)
+}
+func (m *Transceiver) XXX_Size() int {
+	return xxx_messageInfo_Transceiver.Size(m)
+}
+func (m *Transceiver) XXX_DiscardUnknown() {
+	xxx_messageInfo_Transceiver.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Transceiver proto.InternalMessageInfo
+
+func (m *Transceiver) GetID() uint32 {
+	if m != nil {
+		return m.ID
+	}
+	return 0
+}
+
+func (m *Transceiver) GetUUID() string {
+	if m != nil {
+		return m.UUID
+	}
+	return ""
+}
+
+func (m *Transceiver) GetName() string {
+	if m != nil {
+		return m.Name
+	}
+	return ""
+}
+
+func (m *Transceiver) GetTechnology() string {
+	if m != nil {
+		return m.Technology
+	}
+	return ""
+}
+
+func (m *Transceiver) GetPluggedIn() bool {
+	if m != nil {
+		return m.PluggedIn
+	}
+	return false
+}
+
+func (m *Transceiver) GetPonIds() []uint32 {
+	if m != nil {
+		return m.PonIds
+	}
+	return nil
+}
+
+type Transceivers struct {
+	Items                []*Transceiver `protobuf:"bytes,1,rep,name=Items,proto3" json:"Items,omitempty"`
+	XXX_NoUnkeyedLiteral struct{}       `json:"-"`
+	XXX_unrecognized     []byte         `json:"-"`
+	XXX_sizecache        int32          `json:"-"`
+}
+
+func (m *Transceivers) Reset()         { *m = Transceivers{} }
+func (m *Transceivers) String() string { return proto.CompactTextString(m) }
+func (*Transceivers) ProtoMessage()    {}
+func (*Transceivers) Descriptor() ([]byte, []int) {
+	return fileDescriptor_49e784b4938902cc, []int{5}
+}
+
+func (m *Transceivers) XXX_Unmarshal(b []byte) error {
+	return xxx_messageInfo_Transceivers.Unmarshal(m, b)
+}
+func (m *Transceivers) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) {
+	return xxx_messageInfo_Transceivers.Marshal(b, m, deterministic)
+}
+func (m *Transceivers) XXX_Merge(src proto.Message) {
+	xxx_messageInfo_Transceivers.Merge(m, src)
+}
+func (m *Transceivers) XXX_Size() int {
+	return xxx_messageInfo_Transceivers.Size(m)
+}
+func (m *Transceivers) XXX_DiscardUnknown() {
+	xxx_messageInfo_Transceivers.DiscardUnknown(m)
+}
+
+var xxx_messageInfo_Transceivers proto.InternalMessageInfo
+
+func (m *Transceivers) GetItems() []*Transceiver {
+	if m != nil {
+		return m.Items
+	}
+	return nil
+}
+
 func init() {
 	proto.RegisterType((*DmiEvent)(nil), "bbsim.DmiEvent")
-	proto.RegisterType((*DmiCreateEventResponse)(nil), "bbsim.DmiCreateEventResponse")
+	proto.RegisterType((*DmiResponse)(nil), "bbsim.DmiResponse")
+	proto.RegisterType((*DmiEmpty)(nil), "bbsim.DmiEmpty")
+	proto.RegisterType((*TransceiverRequest)(nil), "bbsim.TransceiverRequest")
+	proto.RegisterType((*Transceiver)(nil), "bbsim.Transceiver")
+	proto.RegisterType((*Transceivers)(nil), "bbsim.Transceivers")
 }
 
 func init() { proto.RegisterFile("api/bbsim/bbsim_dmi.proto", fileDescriptor_49e784b4938902cc) }
 
 var fileDescriptor_49e784b4938902cc = []byte{
-	// 216 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x6c, 0x90, 0x31, 0x4b, 0xc5, 0x30,
-	0x14, 0x85, 0x7d, 0xc2, 0x53, 0x7b, 0xdf, 0x20, 0x64, 0x90, 0x2a, 0x3c, 0x94, 0x8a, 0xa0, 0x4b,
-	0x0a, 0xfa, 0x03, 0x84, 0xb6, 0x6e, 0xe2, 0x50, 0x37, 0x97, 0x92, 0x34, 0x97, 0x9a, 0xe1, 0xe6,
-	0x86, 0x26, 0xf5, 0xf7, 0x8b, 0x69, 0xab, 0x0e, 0x6f, 0x09, 0xb9, 0x1f, 0x9c, 0xc3, 0xc7, 0x81,
-	0x4b, 0xe5, 0x6d, 0xa9, 0x75, 0xb0, 0x34, 0xbf, 0x9d, 0x21, 0x2b, 0xfd, 0xc8, 0x91, 0xc5, 0x36,
-	0x81, 0xe2, 0x01, 0xce, 0x1a, 0xb2, 0x2f, 0x5f, 0xe8, 0xa2, 0xd8, 0x03, 0xe0, 0xcf, 0xa7, 0x73,
-	0x8a, 0x30, 0xdf, 0xdc, 0x6c, 0xee, 0xb3, 0x36, 0x4b, 0xe4, 0x4d, 0x11, 0x16, 0xef, 0x70, 0xd1,
-	0x90, 0xad, 0x47, 0x54, 0x11, 0x53, 0xa0, 0xc5, 0xe0, 0xd9, 0x05, 0x14, 0xd7, 0xb0, 0x0b, 0x51,
-	0xc5, 0x29, 0x74, 0x3d, 0x9b, 0x39, 0xb9, 0x6d, 0x61, 0x46, 0x35, 0x1b, 0x14, 0x39, 0x9c, 0x12,
-	0x86, 0xa0, 0x06, 0xcc, 0x8f, 0x53, 0xed, 0x7a, 0x3e, 0xbe, 0x42, 0x56, 0x55, 0x8b, 0x99, 0x78,
-	0x86, 0xdd, 0xbf, 0x7a, 0x71, 0x2e, 0x93, 0xa3, 0x5c, 0x05, 0xaf, 0xf6, 0x7f, 0xe0, 0x80, 0x46,
-	0x71, 0x54, 0xdd, 0x7d, 0xdc, 0x0e, 0x36, 0x7e, 0x4e, 0x5a, 0xf6, 0x4c, 0x25, 0x7b, 0x74, 0x3d,
-	0x8f, 0x66, 0x59, 0xe0, 0x77, 0x0b, 0x7d, 0x92, 0x26, 0x78, 0xfa, 0x0e, 0x00, 0x00, 0xff, 0xff,
-	0xd2, 0x6b, 0xb3, 0x25, 0x1f, 0x01, 0x00, 0x00,
+	// 414 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xa4, 0x92, 0xcd, 0x6e, 0xd4, 0x30,
+	0x10, 0xc7, 0x9b, 0xdd, 0xee, 0xd2, 0x9d, 0xb0, 0x54, 0x0c, 0x12, 0x72, 0x2b, 0x3e, 0x56, 0x06,
+	0xa4, 0x70, 0x49, 0xa5, 0xc2, 0x01, 0x38, 0x6e, 0x53, 0x81, 0x2f, 0x50, 0x45, 0xed, 0x85, 0xcb,
+	0x2a, 0x1f, 0xa3, 0x34, 0x52, 0x6d, 0x87, 0xd8, 0xa9, 0xd4, 0x47, 0xe0, 0x19, 0x78, 0x59, 0x14,
+	0x3b, 0x4b, 0x53, 0x01, 0xa7, 0x5e, 0xa2, 0xf1, 0x2f, 0x33, 0xe3, 0xff, 0xf8, 0x3f, 0x70, 0x90,
+	0x35, 0xf5, 0x51, 0x9e, 0x9b, 0x5a, 0xfa, 0xef, 0xa6, 0x94, 0x75, 0xdc, 0xb4, 0xda, 0x6a, 0x9c,
+	0x39, 0xc0, 0xdf, 0xc2, 0x5e, 0x22, 0xeb, 0xd3, 0x6b, 0x52, 0x16, 0x9f, 0x03, 0x50, 0x1f, 0x6c,
+	0x54, 0x26, 0x89, 0x05, 0xab, 0x20, 0x5a, 0xa4, 0x0b, 0x47, 0xbe, 0x66, 0x92, 0xf8, 0x17, 0x08,
+	0x13, 0x59, 0xa7, 0x64, 0x1a, 0xad, 0x0c, 0xe1, 0x4b, 0x08, 0x8d, 0xcd, 0x6c, 0x67, 0x36, 0x85,
+	0x2e, 0x7d, 0xfa, 0x2c, 0x05, 0x8f, 0x4e, 0x74, 0x49, 0xc8, 0xe0, 0x81, 0x24, 0x63, 0xb2, 0x8a,
+	0xd8, 0xc4, 0xf5, 0xda, 0x1e, 0x39, 0xf8, 0x4b, 0x65, 0x63, 0x6f, 0xf8, 0x27, 0xc0, 0xf3, 0x36,
+	0x53, 0xa6, 0xa0, 0xfa, 0x9a, 0xda, 0x94, 0x7e, 0x74, 0x64, 0x2c, 0xbe, 0x86, 0xe5, 0x88, 0x8a,
+	0xd2, 0xb5, 0x5f, 0xa6, 0x77, 0x21, 0xff, 0x15, 0x40, 0x38, 0x22, 0xf8, 0x08, 0x26, 0x22, 0x19,
+	0x52, 0x27, 0x22, 0x41, 0x84, 0xdd, 0x8b, 0x0b, 0x91, 0x0c, 0xd7, 0xbb, 0xb8, 0x67, 0xfd, 0x34,
+	0x6c, 0xea, 0x59, 0x1f, 0xe3, 0x0b, 0x80, 0x73, 0x2a, 0x2e, 0x95, 0xbe, 0xd2, 0xd5, 0x0d, 0xdb,
+	0x75, 0x7f, 0x46, 0x04, 0x9f, 0xc1, 0xe2, 0xec, 0xaa, 0xab, 0x2a, 0x2a, 0x85, 0x62, 0xb3, 0x55,
+	0x10, 0xed, 0xa5, 0xb7, 0x00, 0x9f, 0xc2, 0xfc, 0x4c, 0x2b, 0x51, 0x1a, 0x36, 0x5f, 0x4d, 0xa3,
+	0x65, 0x3a, 0x9c, 0xf8, 0x07, 0x78, 0x38, 0x12, 0x67, 0x30, 0x82, 0x99, 0xb0, 0x24, 0x0d, 0x0b,
+	0x56, 0xd3, 0x28, 0x3c, 0xc6, 0xd8, 0x39, 0x10, 0x8f, 0xa7, 0xf7, 0x09, 0xc7, 0x3f, 0x27, 0xb0,
+	0x58, 0xaf, 0x07, 0xbf, 0xf0, 0x3d, 0x84, 0x27, 0x2d, 0x65, 0x96, 0xbc, 0x4b, 0xfb, 0x43, 0xdd,
+	0xd6, 0xb6, 0x43, 0xbc, 0x05, 0x5b, 0x73, 0xf8, 0x0e, 0x7e, 0x84, 0xfd, 0xcf, 0x64, 0xef, 0x08,
+	0x18, 0x57, 0xf6, 0x6f, 0x7f, 0xf8, 0xe4, 0x6f, 0x09, 0x86, 0xef, 0xe0, 0x29, 0x60, 0x3f, 0xdd,
+	0xb7, 0x6e, 0x5c, 0x8e, 0x07, 0xff, 0xd0, 0xeb, 0xdd, 0xfa, 0x8f, 0x82, 0x04, 0x1e, 0xf7, 0x6d,
+	0x84, 0xba, 0x4f, 0x97, 0xf5, 0x9b, 0xef, 0xaf, 0xaa, 0xda, 0x5e, 0x76, 0x79, 0x5c, 0x68, 0x79,
+	0xa4, 0x1b, 0x52, 0x85, 0x6e, 0xcb, 0x61, 0xa9, 0xff, 0xac, 0x77, 0x3e, 0x77, 0x5b, 0xfd, 0xee,
+	0x77, 0x00, 0x00, 0x00, 0xff, 0xff, 0x62, 0xd2, 0xd2, 0x82, 0xf2, 0x02, 0x00, 0x00,
 }
 
 // Reference imports to suppress errors if they are not otherwise used.
@@ -148,7 +352,12 @@
 // 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)
+	CreateEvent(ctx context.Context, in *DmiEvent, opts ...grpc.CallOption) (*DmiResponse, error)
+	GetTransceivers(ctx context.Context, in *DmiEmpty, opts ...grpc.CallOption) (*Transceivers, error)
+	// Plug out the transceiver by transceiverId
+	PlugOutTransceiver(ctx context.Context, in *TransceiverRequest, opts ...grpc.CallOption) (*DmiResponse, error)
+	// Plug in the transceiver of a PON by pon-port-ID
+	PlugInTransceiver(ctx context.Context, in *TransceiverRequest, opts ...grpc.CallOption) (*DmiResponse, error)
 }
 
 type bBsimDmiClient struct {
@@ -159,8 +368,8 @@
 	return &bBsimDmiClient{cc}
 }
 
-func (c *bBsimDmiClient) CreateEvent(ctx context.Context, in *DmiEvent, opts ...grpc.CallOption) (*DmiCreateEventResponse, error) {
-	out := new(DmiCreateEventResponse)
+func (c *bBsimDmiClient) CreateEvent(ctx context.Context, in *DmiEvent, opts ...grpc.CallOption) (*DmiResponse, error) {
+	out := new(DmiResponse)
 	err := c.cc.Invoke(ctx, "/bbsim.BBsim_dmi/CreateEvent", in, out, opts...)
 	if err != nil {
 		return nil, err
@@ -168,19 +377,60 @@
 	return out, nil
 }
 
+func (c *bBsimDmiClient) GetTransceivers(ctx context.Context, in *DmiEmpty, opts ...grpc.CallOption) (*Transceivers, error) {
+	out := new(Transceivers)
+	err := c.cc.Invoke(ctx, "/bbsim.BBsim_dmi/GetTransceivers", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *bBsimDmiClient) PlugOutTransceiver(ctx context.Context, in *TransceiverRequest, opts ...grpc.CallOption) (*DmiResponse, error) {
+	out := new(DmiResponse)
+	err := c.cc.Invoke(ctx, "/bbsim.BBsim_dmi/PlugOutTransceiver", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *bBsimDmiClient) PlugInTransceiver(ctx context.Context, in *TransceiverRequest, opts ...grpc.CallOption) (*DmiResponse, error) {
+	out := new(DmiResponse)
+	err := c.cc.Invoke(ctx, "/bbsim.BBsim_dmi/PlugInTransceiver", 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)
+	CreateEvent(context.Context, *DmiEvent) (*DmiResponse, error)
+	GetTransceivers(context.Context, *DmiEmpty) (*Transceivers, error)
+	// Plug out the transceiver by transceiverId
+	PlugOutTransceiver(context.Context, *TransceiverRequest) (*DmiResponse, error)
+	// Plug in the transceiver of a PON by pon-port-ID
+	PlugInTransceiver(context.Context, *TransceiverRequest) (*DmiResponse, error)
 }
 
 // UnimplementedBBsimDmiServer can be embedded to have forward compatible implementations.
 type UnimplementedBBsimDmiServer struct {
 }
 
-func (*UnimplementedBBsimDmiServer) CreateEvent(ctx context.Context, req *DmiEvent) (*DmiCreateEventResponse, error) {
+func (*UnimplementedBBsimDmiServer) CreateEvent(ctx context.Context, req *DmiEvent) (*DmiResponse, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method CreateEvent not implemented")
 }
+func (*UnimplementedBBsimDmiServer) GetTransceivers(ctx context.Context, req *DmiEmpty) (*Transceivers, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method GetTransceivers not implemented")
+}
+func (*UnimplementedBBsimDmiServer) PlugOutTransceiver(ctx context.Context, req *TransceiverRequest) (*DmiResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method PlugOutTransceiver not implemented")
+}
+func (*UnimplementedBBsimDmiServer) PlugInTransceiver(ctx context.Context, req *TransceiverRequest) (*DmiResponse, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method PlugInTransceiver not implemented")
+}
 
 func RegisterBBsimDmiServer(s *grpc.Server, srv BBsimDmiServer) {
 	s.RegisterService(&_BBsimDmi_serviceDesc, srv)
@@ -204,6 +454,60 @@
 	return interceptor(ctx, in, info, handler)
 }
 
+func _BBsimDmi_GetTransceivers_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(DmiEmpty)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(BBsimDmiServer).GetTransceivers(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/bbsim.BBsim_dmi/GetTransceivers",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(BBsimDmiServer).GetTransceivers(ctx, req.(*DmiEmpty))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _BBsimDmi_PlugOutTransceiver_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(TransceiverRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(BBsimDmiServer).PlugOutTransceiver(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/bbsim.BBsim_dmi/PlugOutTransceiver",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(BBsimDmiServer).PlugOutTransceiver(ctx, req.(*TransceiverRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _BBsimDmi_PlugInTransceiver_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(TransceiverRequest)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(BBsimDmiServer).PlugInTransceiver(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/bbsim.BBsim_dmi/PlugInTransceiver",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(BBsimDmiServer).PlugInTransceiver(ctx, req.(*TransceiverRequest))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
 var _BBsimDmi_serviceDesc = grpc.ServiceDesc{
 	ServiceName: "bbsim.BBsim_dmi",
 	HandlerType: (*BBsimDmiServer)(nil),
@@ -212,6 +516,18 @@
 			MethodName: "CreateEvent",
 			Handler:    _BBsimDmi_CreateEvent_Handler,
 		},
+		{
+			MethodName: "GetTransceivers",
+			Handler:    _BBsimDmi_GetTransceivers_Handler,
+		},
+		{
+			MethodName: "PlugOutTransceiver",
+			Handler:    _BBsimDmi_PlugOutTransceiver_Handler,
+		},
+		{
+			MethodName: "PlugInTransceiver",
+			Handler:    _BBsimDmi_PlugInTransceiver_Handler,
+		},
 	},
 	Streams:  []grpc.StreamDesc{},
 	Metadata: "api/bbsim/bbsim_dmi.proto",
diff --git a/api/bbsim/bbsim_dmi.proto b/api/bbsim/bbsim_dmi.proto
index bc89a16..48c7da9 100644
--- a/api/bbsim/bbsim_dmi.proto
+++ b/api/bbsim/bbsim_dmi.proto
@@ -23,13 +23,44 @@
    string event_name= 1;
 }
 
-message DmiCreateEventResponse {
+message DmiResponse {
     int32 status_code = 1;
     string message = 2;
 }
 
+message DmiEmpty {
+}
+
+message TransceiverRequest {
+    uint32 TransceiverId = 1;
+}
+
+message Transceiver{
+    uint32 ID = 1;
+    string UUID = 2;
+    string Name = 3;
+    string Technology = 4;
+    bool PluggedIn = 5;
+    repeated uint32 PonIds = 6;
+}
+
+message Transceivers{
+    repeated Transceiver Items = 1;
+}
+
+
 service BBsim_dmi {
     // Ask the DMI Server to create an event
-    rpc CreateEvent (DmiEvent) returns (DmiCreateEventResponse){
+    rpc CreateEvent (DmiEvent) returns (DmiResponse){
+    }
+
+    rpc GetTransceivers (DmiEmpty) returns (Transceivers) {
+    }
+
+    // Plug out the transceiver by transceiverId
+    rpc PlugOutTransceiver (TransceiverRequest) returns (DmiResponse) {
+    }
+    // Plug in the transceiver of a PON by pon-port-ID
+    rpc PlugInTransceiver (TransceiverRequest) returns (DmiResponse) {
     }
 }
diff --git a/docs/source/DMI_Server_README.md b/docs/source/DMI_Server_README.md
index ac42994..14aa301 100755
--- a/docs/source/DMI_Server_README.md
+++ b/docs/source/DMI_Server_README.md
@@ -29,27 +29,41 @@
 ```sh
 $ grpcurl -plaintext -d '{"device_uuid": {"uuid": "5295a1d5-a121-372e-b8dc-6f7eda83f0ba"}}' 172.17.0.2:50075 dmi.NativeHWManagementService.GetPhysicalInventory
 {
-  "status": "OK",
+  "status": "OK_STATUS",
   "inventory": {
     "lastChange": "1970-01-01T00:00:00Z",
     "root": {
-      "name": "BBSim-BBSIM_OLT_0",
+      "name": "BBSim-BBSIM_OLT_10",
       "children": [
         {
-          "name": "sfp-plus-transceiver-cage-pon-0"
+          "name": "sfp-plus-transceiver-cage-0",
           "class": "COMPONENT_TYPE_CONTAINER",
           "description": "cage",
-          "parent": "BBSim-BBSIM_OLT_0",
+          "parent": "BBSim-BBSIM_OLT_10",
           "children": [
             {
-              "name": "sfp-0",
+              "name": "sfp-plus-0",
               "class": "COMPONENT_TYPE_TRANSCEIVER",
-              "description": "XGS-PON",
-              "parent": "sfp-plus-transceiver-cage-pon-0",
+              "description": "bbsim-transceiver",
+              "parent": "sfp-plus-transceiver-cage-0",
+              "children": [
+                {
+                  "name": "pon-0",
+                  "class": "COMPONENT_TYPE_PORT",
+                  "description": "bbsim-pon-port",
+                  "parent": "sfp-plus-0",
+                  "uuid": {
+                    "uuid": "38c3cdcc-f428-370b-a08f-8c64a90552e3"
+                  },
+                  "portAttr": {
+                    "protocol": "XGSPON"
+                  }
+                }
+              ],
               "uuid": {
-                "uuid": "788ba741-507f-37b1-8e47-4a389460873b"
+                "uuid": "2d0ab069-f461-34e5-a16a-5ebb1f26b1a7"
               },
-             "transceiverAttr": {
+              "transceiverAttr": {
                 "formFactor": "SFP_PLUS",
                 "transType": "XGSPON",
                 "maxDistance": 10,
@@ -65,7 +79,7 @@
             }
           ],
           "uuid": {
-            "uuid": "6bfab16e-8003-3e18-9143-8933df64aa52"
+            "uuid": "2410ab0a-1f17-3326-9708-da5b62b48d85"
           }
         },
         {
@@ -78,7 +92,7 @@
             "uuid": "2f4b639c-80a9-340f-b8d8-4ad06580b3cf"
           },
           "state": {
-
+            
           }
         },
         {
@@ -91,7 +105,7 @@
             "uuid": "4fb1a981-5697-3813-955d-fbb2a2908b2f"
           },
           "state": {
-
+            
           }
         },
         {
@@ -104,7 +118,7 @@
             "uuid": "b1d63346-c0b9-3a29-a4e6-e047efff9ddf"
           },
           "state": {
-
+            
           }
         },
         {
@@ -117,7 +131,7 @@
             "uuid": "0bcbe296-9855-3218-9479-9c501073773f"
           },
           "state": {
-
+            
           }
         },
         {
@@ -130,7 +144,7 @@
             "uuid": "78cd16fd-73d1-3aad-8f13-644ed3101c63"
           },
           "state": {
-
+            
           }
         },
         {
@@ -143,20 +157,33 @@
             "uuid": "b9ada337-63a1-3991-96f5-95416dec1bf0"
           },
           "state": {
-
+            
+          }
+        },
+        {
+          "name": "Thermal/PSU/SystemPSU/0",
+          "class": "COMPONENT_TYPE_POWER_SUPPLY",
+          "description": "bbsim-psu",
+          "serialNum": "bbsim-psu-serial-0",
+          "mfgName": "bbsim-psu",
+          "uuid": {
+            "uuid": "ee5fa7a2-0707-3bfb-90dd-678b2d6711c6"
+          },
+          "state": {
+            
           }
         }
       ],
-      "serialNum": "BBSIM_OLT_0",
+      "serialNum": "BBSIM_OLT_10",
       "mfgName": "BBSim",
       "uri": {
         "uri": "XXX.YYY.ZZZ.AAA"
       },
       "uuid": {
-        "uuid": "5295a1d5-a121-372e-b8dc-6f7eda83f0ba"
+        "uuid": "895f65ab-64a3-3957-a2db-568a168faa3a"
       },
       "state": {
-
+        
       }
     }
   }
@@ -415,13 +442,13 @@
 
 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>
+$ bbsimctl dmi events raise <event_name>
 
 ###  FAN FAILURE EVENT
 
 .. code:: bash
 ```
-$ bsimctl dmi events create EVENT_FAN_FAILURE
+$ bbsimctl dmi events create EVENT_FAN_FAILURE
 [Status: 0] DMI Event Indication Sent.
 ```
 
@@ -429,7 +456,7 @@
 
 .. code:: bash
 ```
-$ bsimctl dmi events create EVENT_PSU_FAILURE
+$ bbsimctl dmi events create EVENT_PSU_FAILURE
 [Status: 0] DMI Event Indication Sent.
 ```
 
@@ -437,6 +464,32 @@
 
 .. code:: bash
 ```
-$ bsimctl dmi events create EVENT_HW_DEVICE_TEMPERATURE_ABOVE_CRITICAL
+$ bbsimctl dmi events create EVENT_HW_DEVICE_TEMPERATURE_ABOVE_CRITICAL
 [Status: 0] DMI Event Indication Sent.
+```
+
+## Plug transceivers in or out
+
+Access ``bbsimctl``, as described in the previous section
+
+###  List transceivers
+.. code:: bash
+```
+$ bbsimctl dmi transceiver list
+ID    UUID                                    NAME          TECHNOLOGY    PLUGGEDIN    PONIDS
+0     2d0ab069-f461-34e5-a16a-5ebb1f26b1a7    sfp-plus-0    XGSPON        false        [0]
+```
+
+### Plug transceiver out
+.. code:: bash
+```
+$ bbsimctl dmi transceiver plug_out 0
+[Status: 0] Plugged out transceiver 0
+```
+
+### Plug transceiver in
+.. code:: bash
+```
+$ bbsimctl dmi transceiver plug_in 0
+[Status: 0] Plugged in transceiver 0
 ```
\ 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 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
-}