[SEBA-873] add reboot olt support

Change-Id: I1570d05313661a6d66e1596b9f9a1a1cc17d1a73
diff --git a/Makefile b/Makefile
index 9a642e9..a7e62f6 100644
--- a/Makefile
+++ b/Makefile
@@ -66,7 +66,7 @@
 	docker run --rm -v $(shell pwd):/bbsim ${DOCKER_REGISTRY}${DOCKER_REPOSITORY}bbsim-builder:${DOCKER_TAG} /bin/sh -c "cd /bbsim; make _build"
 
 test: clean dep fmt # @HELP Execute unit tests
-	GO111MODULE=on go test -v -mod vendor $(TEST_PACKAGES) -covermode count -coverprofile ./tests/results/go-test-coverage.out 2>&1 | tee ./tests/results/go-test-results.out
+	GO111MODULE=on go test -v -mod vendor $(TEST_PACKAGES) -timeout 10s -covermode count -coverprofile ./tests/results/go-test-coverage.out 2>&1 | tee ./tests/results/go-test-results.out
 	go-junit-report < ./tests/results/go-test-results.out > ./tests/results/go-test-results.xml
 	gocover-cobertura < ./tests/results/go-test-coverage.out > ./tests/results/go-test-coverage.xml
 
diff --git a/api/bbsim/bbsim.pb.go b/api/bbsim/bbsim.pb.go
index 7226f79..2f2f647 100644
--- a/api/bbsim/bbsim.pb.go
+++ b/api/bbsim/bbsim.pb.go
@@ -582,45 +582,46 @@
 func init() { proto.RegisterFile("api/bbsim/bbsim.proto", fileDescriptor_ef7750073d18011b) }
 
 var fileDescriptor_ef7750073d18011b = []byte{
-	// 593 bytes of a gzipped FileDescriptorProto
-	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x94, 0xd1, 0x6e, 0xda, 0x30,
-	0x14, 0x86, 0x13, 0x20, 0x04, 0x4e, 0x68, 0xa7, 0x59, 0xdd, 0x14, 0xa1, 0x6a, 0x43, 0x56, 0x35,
-	0xb1, 0x5d, 0xb4, 0x1b, 0x4c, 0xda, 0x6e, 0xd7, 0x82, 0x56, 0xa4, 0x2a, 0xa0, 0xa4, 0xec, 0x76,
-	0x0a, 0xc4, 0x82, 0x48, 0x49, 0x9c, 0xc5, 0xa6, 0x68, 0x0f, 0xb0, 0x77, 0xda, 0x8b, 0xec, 0x61,
-	0x76, 0x37, 0x39, 0x76, 0x02, 0x29, 0x9a, 0xd4, 0xed, 0xa6, 0x37, 0xc8, 0xe7, 0x3b, 0xe7, 0x3f,
-	0xb6, 0xff, 0x63, 0x02, 0xcf, 0xfc, 0x34, 0xbc, 0x58, 0x2c, 0x58, 0x18, 0xcb, 0xdf, 0xf3, 0x34,
-	0xa3, 0x9c, 0x22, 0x23, 0x0f, 0xf0, 0x07, 0x30, 0x67, 0x53, 0x67, 0x46, 0x33, 0x8e, 0x8e, 0xa1,
-	0x36, 0x19, 0xd9, 0x7a, 0x4f, 0xef, 0x1b, 0x6e, 0x6d, 0x32, 0x42, 0xa7, 0xd0, 0x9e, 0xa6, 0x24,
-	0xf3, 0xb8, 0xcf, 0x89, 0x5d, 0xeb, 0xe9, 0xfd, 0xb6, 0xbb, 0x03, 0x42, 0xe8, 0x38, 0x93, 0xff,
-	0x10, 0xfe, 0xd2, 0xa1, 0x3e, 0x8d, 0x0e, 0x55, 0x18, 0x3a, 0x1e, 0xc9, 0x42, 0x3f, 0x72, 0x36,
-	0xf1, 0x82, 0x64, 0x4a, 0x58, 0x61, 0xd5, 0xce, 0xf5, 0x7b, 0x9d, 0xd1, 0x19, 0x1c, 0x4d, 0x12,
-	0x4e, 0xb2, 0xc4, 0x8f, 0x64, 0x45, 0x23, 0xaf, 0xa8, 0x42, 0xf4, 0x06, 0x5a, 0xea, 0xe0, 0xcc,
-	0x36, 0x7a, 0xf5, 0xbe, 0x35, 0x38, 0x3e, 0x97, 0xc6, 0x28, 0xec, 0x96, 0x79, 0x51, 0xab, 0xdc,
-	0x61, 0x76, 0xb3, 0x52, 0xab, 0xb0, 0x5b, 0xe6, 0xf1, 0x6f, 0x71, 0x2f, 0x67, 0xfe, 0x68, 0xf7,
-	0x3a, 0x85, 0xf6, 0x8c, 0x26, 0xe2, 0x2c, 0x93, 0x91, 0x6d, 0xe4, 0xdb, 0xef, 0x00, 0x42, 0xd0,
-	0xf0, 0x6e, 0xfd, 0x95, 0xdd, 0xcc, 0x13, 0xf9, 0x5a, 0xb0, 0x2b, 0xc1, 0x4c, 0xc9, 0xc4, 0x5a,
-	0x74, 0xb9, 0xde, 0x7e, 0x0a, 0x82, 0x8c, 0x30, 0x66, 0xb7, 0xe4, 0x49, 0x4a, 0x80, 0x9e, 0x43,
-	0x53, 0xf4, 0x73, 0xa8, 0xdd, 0xce, 0x35, 0x2a, 0xc2, 0x7d, 0x68, 0x4c, 0x9d, 0x39, 0x43, 0x3d,
-	0x30, 0x42, 0x4e, 0x62, 0x66, 0xeb, 0xb9, 0x59, 0xa0, 0xcc, 0x9a, 0x3a, 0x73, 0x57, 0x26, 0xf0,
-	0x5b, 0x00, 0x11, 0x91, 0x6f, 0x1b, 0xc2, 0xf8, 0x81, 0x37, 0xfa, 0xa1, 0x37, 0xf8, 0x87, 0x0e,
-	0x47, 0x5f, 0x48, 0xc6, 0x42, 0x9a, 0x28, 0xb7, 0x6c, 0x30, 0xef, 0x24, 0x50, 0x82, 0x22, 0x14,
-	0xa7, 0x5f, 0x6c, 0xc2, 0x28, 0xb8, 0x0d, 0xe3, 0xf2, 0xe5, 0x95, 0x00, 0xbd, 0x00, 0x58, 0xd2,
-	0x38, 0x0e, 0xf9, 0xb5, 0xcf, 0xd6, 0xca, 0xe6, 0x3d, 0x22, 0xd4, 0xab, 0x90, 0x0b, 0x37, 0x37,
-	0x4c, 0x79, 0xbc, 0x03, 0xf8, 0x23, 0xb4, 0x6e, 0xe8, 0xea, 0x86, 0xdc, 0x91, 0x08, 0x9d, 0x80,
-	0x11, 0x89, 0x85, 0xda, 0x5f, 0x06, 0xc2, 0x9d, 0xa5, 0x1f, 0x45, 0x6a, 0xc6, 0x2d, 0x57, 0x45,
-	0x78, 0x0c, 0x2d, 0x97, 0xb0, 0x94, 0x26, 0x8c, 0xa0, 0x97, 0x60, 0xb1, 0xbc, 0xdf, 0xd7, 0x25,
-	0x0d, 0x88, 0x7a, 0x26, 0x20, 0xd1, 0x15, 0x0d, 0x88, 0xb8, 0x5c, 0x4c, 0x18, 0xf3, 0x57, 0xc5,
-	0x05, 0x8a, 0x10, 0x9b, 0x60, 0x8c, 0xe3, 0x94, 0x7f, 0x1f, 0xfc, 0xac, 0x83, 0x71, 0x79, 0xe9,
-	0x85, 0x31, 0xba, 0x00, 0x53, 0x59, 0x83, 0x3a, 0xca, 0xeb, 0xbc, 0xa4, 0x7b, 0xa2, 0xa2, 0x8a,
-	0x71, 0x58, 0x43, 0x67, 0xd0, 0xfc, 0x4c, 0xb8, 0xf8, 0xfb, 0x55, 0xeb, 0xcb, 0x49, 0x45, 0x1c,
-	0x6b, 0xe8, 0x15, 0x98, 0xa2, 0x4a, 0x4c, 0xb4, 0x5a, 0x66, 0xed, 0x06, 0xca, 0xb0, 0x86, 0x5e,
-	0xcb, 0x6e, 0xce, 0x1c, 0x3d, 0xdd, 0x9b, 0xb4, 0x9c, 0x6d, 0x77, 0x6f, 0xf8, 0x58, 0x43, 0xef,
-	0xc0, 0xf2, 0x08, 0x2f, 0x0d, 0x7c, 0xa2, 0x92, 0x05, 0xe8, 0xde, 0x07, 0x58, 0x43, 0x43, 0xb0,
-	0xbc, 0xf5, 0x86, 0x07, 0x74, 0x9b, 0xfc, 0x65, 0x8b, 0x42, 0x54, 0xb8, 0x8b, 0x35, 0x34, 0x00,
-	0x98, 0xd1, 0x2d, 0xc9, 0xe8, 0x3f, 0x68, 0xde, 0x43, 0xc7, 0x25, 0x8c, 0xfb, 0x19, 0x1f, 0xfb,
-	0x29, 0x8d, 0x1e, 0xa8, 0x1a, 0x82, 0xa5, 0x54, 0xa3, 0xf5, 0x32, 0x7d, 0x98, 0x68, 0xd1, 0xcc,
-	0x3f, 0xbe, 0xc3, 0x3f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x15, 0x8d, 0x8c, 0x02, 0x95, 0x05, 0x00,
-	0x00,
+	// 617 bytes of a gzipped FileDescriptorProto
+	0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xc4, 0x55, 0xd1, 0x8e, 0xd2, 0x4c,
+	0x18, 0x6d, 0x81, 0x52, 0xf8, 0xca, 0xee, 0x9f, 0x7f, 0xb2, 0x9a, 0x86, 0x6c, 0x94, 0x4c, 0x36,
+	0x06, 0x8d, 0xb2, 0x0a, 0x26, 0x7a, 0xeb, 0x2e, 0xc4, 0x25, 0xd9, 0x14, 0xd2, 0x2e, 0xde, 0x9a,
+	0x96, 0x4e, 0xa0, 0x49, 0xdb, 0xa9, 0x9d, 0x61, 0x89, 0x0f, 0xe0, 0xa3, 0xf9, 0x08, 0x3e, 0x8c,
+	0x77, 0x66, 0xda, 0x69, 0xa1, 0xcb, 0x5e, 0x10, 0x6f, 0xbc, 0x21, 0x73, 0xce, 0x9c, 0x33, 0xf3,
+	0x7d, 0x67, 0x3e, 0x52, 0x78, 0xe2, 0x26, 0xc1, 0xa5, 0xe7, 0xb1, 0x20, 0xca, 0x7f, 0x07, 0x49,
+	0x4a, 0x39, 0x45, 0x5a, 0x06, 0xf0, 0x07, 0xd0, 0xe7, 0x33, 0x6b, 0x4e, 0x53, 0x8e, 0x4e, 0xa1,
+	0x36, 0x1d, 0x9b, 0x6a, 0x4f, 0xed, 0x6b, 0x76, 0x6d, 0x3a, 0x46, 0xe7, 0xd0, 0x9e, 0x25, 0x24,
+	0x75, 0xb8, 0xcb, 0x89, 0x59, 0xeb, 0xa9, 0xfd, 0xb6, 0xbd, 0x23, 0x84, 0xd1, 0xb2, 0xa6, 0x7f,
+	0x61, 0xfc, 0xa5, 0x42, 0x7d, 0x16, 0x1e, 0xba, 0x30, 0x74, 0x1c, 0x92, 0x06, 0x6e, 0x68, 0x6d,
+	0x22, 0x8f, 0xa4, 0xd2, 0x58, 0xe1, 0xaa, 0x27, 0xd7, 0x1f, 0x9c, 0x8c, 0x2e, 0xe0, 0x64, 0x1a,
+	0x73, 0x92, 0xc6, 0x6e, 0x98, 0x2b, 0x1a, 0x99, 0xa2, 0x4a, 0xa2, 0x57, 0xd0, 0x92, 0x85, 0x33,
+	0x53, 0xeb, 0xd5, 0xfb, 0xc6, 0xf0, 0x74, 0x90, 0x07, 0x23, 0x69, 0xbb, 0xdc, 0x17, 0x5a, 0x99,
+	0x0e, 0x33, 0x9b, 0x15, 0xad, 0xa4, 0xed, 0x72, 0x1f, 0xff, 0x16, 0x7d, 0x59, 0x8b, 0x7f, 0xd6,
+	0xd7, 0x39, 0xb4, 0xe7, 0x34, 0x16, 0xb5, 0x4c, 0xc7, 0xa6, 0x96, 0x5d, 0xbf, 0x23, 0x10, 0x82,
+	0x86, 0x73, 0xe7, 0xae, 0xcc, 0x66, 0xb6, 0x91, 0xad, 0x05, 0x77, 0x2d, 0x38, 0x3d, 0xe7, 0xc4,
+	0x5a, 0x9c, 0x72, 0xb3, 0xfd, 0xe4, 0xfb, 0x29, 0x61, 0xcc, 0x6c, 0xe5, 0x95, 0x94, 0x04, 0x7a,
+	0x0a, 0x4d, 0x71, 0x9e, 0x45, 0xcd, 0x76, 0xe6, 0x91, 0x08, 0xf7, 0xa1, 0x31, 0xb3, 0x16, 0x0c,
+	0xf5, 0x40, 0x0b, 0x38, 0x89, 0x98, 0xa9, 0x66, 0x61, 0x81, 0x0c, 0x6b, 0x66, 0x2d, 0xec, 0x7c,
+	0x03, 0xbf, 0x05, 0x10, 0x88, 0x7c, 0xdb, 0x10, 0xc6, 0x0f, 0xb2, 0x51, 0x0f, 0xb3, 0xc1, 0x3f,
+	0x54, 0x38, 0xf9, 0x42, 0x52, 0x16, 0xd0, 0x58, 0xa6, 0x65, 0x82, 0x7e, 0x9f, 0x13, 0xd2, 0x50,
+	0x40, 0x51, 0xbd, 0xb7, 0x09, 0x42, 0xff, 0x2e, 0x88, 0xca, 0xc9, 0x2b, 0x09, 0xf4, 0x0c, 0x60,
+	0x49, 0xa3, 0x28, 0xe0, 0x37, 0x2e, 0x5b, 0xcb, 0x98, 0xf7, 0x18, 0xe1, 0x5e, 0x05, 0x5c, 0xa4,
+	0xb9, 0x61, 0x32, 0xe3, 0x1d, 0x81, 0x3f, 0x42, 0xeb, 0x96, 0xae, 0x6e, 0xc9, 0x3d, 0x09, 0xd1,
+	0x19, 0x68, 0xa1, 0x58, 0xc8, 0xfb, 0x73, 0x20, 0xd2, 0x59, 0xba, 0x61, 0x28, 0xdf, 0xb8, 0x65,
+	0x4b, 0x84, 0x27, 0xd0, 0xb2, 0x09, 0x4b, 0x68, 0xcc, 0x08, 0x7a, 0x0e, 0x06, 0xcb, 0xce, 0xfb,
+	0xba, 0xa4, 0x3e, 0x91, 0x63, 0x02, 0x39, 0x75, 0x4d, 0x7d, 0x22, 0x9a, 0x8b, 0x08, 0x63, 0xee,
+	0xaa, 0x68, 0xa0, 0x80, 0x58, 0x07, 0x6d, 0x12, 0x25, 0xfc, 0xfb, 0xf0, 0x67, 0x03, 0xb4, 0xab,
+	0x2b, 0x27, 0x88, 0xd0, 0x25, 0xe8, 0x32, 0x1a, 0xd4, 0x91, 0x59, 0x67, 0x92, 0xee, 0x99, 0x44,
+	0x95, 0xe0, 0xb0, 0x82, 0x2e, 0xa0, 0xf9, 0x99, 0x70, 0xf1, 0xf7, 0xab, 0xea, 0xcb, 0x97, 0x0a,
+	0x39, 0x56, 0xd0, 0x1b, 0x80, 0x39, 0xdd, 0x92, 0x94, 0xc6, 0x87, 0xca, 0xff, 0x24, 0x2a, 0x3a,
+	0xc2, 0x0a, 0x1a, 0x80, 0xe1, 0xac, 0x37, 0xdc, 0xa7, 0xdb, 0xe3, 0xf4, 0xaf, 0xa1, 0x6d, 0x13,
+	0x8f, 0x52, 0x7e, 0x94, 0xfa, 0x05, 0xe8, 0xa2, 0x64, 0x31, 0x5e, 0x55, 0xad, 0xb1, 0x9b, 0x2e,
+	0x86, 0x15, 0xf4, 0x32, 0x6f, 0xcd, 0x5a, 0xa0, 0xff, 0xf7, 0xc6, 0x2e, 0x1f, 0xb4, 0xee, 0xde,
+	0x24, 0x62, 0x05, 0xbd, 0x03, 0xc3, 0x21, 0xbc, 0x7c, 0xcd, 0xe2, 0xd2, 0x82, 0xe8, 0x3e, 0x24,
+	0xb0, 0x82, 0x46, 0x7b, 0x3d, 0x3e, 0x7e, 0xc5, 0x23, 0xa5, 0x0f, 0x77, 0x39, 0x1e, 0xed, 0x79,
+	0x0f, 0x1d, 0x9b, 0x30, 0xee, 0xa6, 0x7c, 0xe2, 0x26, 0x34, 0x3c, 0xd2, 0x35, 0x02, 0x43, 0xba,
+	0xc6, 0xeb, 0x65, 0x72, 0x9c, 0xc9, 0x6b, 0x66, 0x5f, 0x82, 0xd1, 0x9f, 0x00, 0x00, 0x00, 0xff,
+	0xff, 0x57, 0x53, 0xc7, 0x05, 0x22, 0x06, 0x00, 0x00,
 }
 
 // Reference imports to suppress errors if they are not otherwise used.
@@ -637,6 +638,9 @@
 type BBSimClient interface {
 	Version(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*VersionNumber, error)
 	GetOlt(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Olt, error)
+	PoweronOlt(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Response, error)
+	ShutdownOlt(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Response, error)
+	RebootOlt(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Response, error)
 	GetONUs(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*ONUs, error)
 	GetONU(ctx context.Context, in *ONURequest, opts ...grpc.CallOption) (*ONU, error)
 	SetLogLevel(ctx context.Context, in *LogLevel, opts ...grpc.CallOption) (*LogLevel, error)
@@ -672,6 +676,33 @@
 	return out, nil
 }
 
+func (c *bBSimClient) PoweronOlt(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Response, error) {
+	out := new(Response)
+	err := c.cc.Invoke(ctx, "/bbsim.BBSim/PoweronOlt", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *bBSimClient) ShutdownOlt(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Response, error) {
+	out := new(Response)
+	err := c.cc.Invoke(ctx, "/bbsim.BBSim/ShutdownOlt", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
+func (c *bBSimClient) RebootOlt(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*Response, error) {
+	out := new(Response)
+	err := c.cc.Invoke(ctx, "/bbsim.BBSim/RebootOlt", in, out, opts...)
+	if err != nil {
+		return nil, err
+	}
+	return out, nil
+}
+
 func (c *bBSimClient) GetONUs(ctx context.Context, in *Empty, opts ...grpc.CallOption) (*ONUs, error) {
 	out := new(ONUs)
 	err := c.cc.Invoke(ctx, "/bbsim.BBSim/GetONUs", in, out, opts...)
@@ -739,6 +770,9 @@
 type BBSimServer interface {
 	Version(context.Context, *Empty) (*VersionNumber, error)
 	GetOlt(context.Context, *Empty) (*Olt, error)
+	PoweronOlt(context.Context, *Empty) (*Response, error)
+	ShutdownOlt(context.Context, *Empty) (*Response, error)
+	RebootOlt(context.Context, *Empty) (*Response, error)
 	GetONUs(context.Context, *Empty) (*ONUs, error)
 	GetONU(context.Context, *ONURequest) (*ONU, error)
 	SetLogLevel(context.Context, *LogLevel) (*LogLevel, error)
@@ -758,6 +792,15 @@
 func (*UnimplementedBBSimServer) GetOlt(ctx context.Context, req *Empty) (*Olt, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method GetOlt not implemented")
 }
+func (*UnimplementedBBSimServer) PoweronOlt(ctx context.Context, req *Empty) (*Response, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method PoweronOlt not implemented")
+}
+func (*UnimplementedBBSimServer) ShutdownOlt(ctx context.Context, req *Empty) (*Response, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method ShutdownOlt not implemented")
+}
+func (*UnimplementedBBSimServer) RebootOlt(ctx context.Context, req *Empty) (*Response, error) {
+	return nil, status.Errorf(codes.Unimplemented, "method RebootOlt not implemented")
+}
 func (*UnimplementedBBSimServer) GetONUs(ctx context.Context, req *Empty) (*ONUs, error) {
 	return nil, status.Errorf(codes.Unimplemented, "method GetONUs not implemented")
 }
@@ -820,6 +863,60 @@
 	return interceptor(ctx, in, info, handler)
 }
 
+func _BBSim_PoweronOlt_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(Empty)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(BBSimServer).PoweronOlt(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/bbsim.BBSim/PoweronOlt",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(BBSimServer).PoweronOlt(ctx, req.(*Empty))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _BBSim_ShutdownOlt_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(Empty)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(BBSimServer).ShutdownOlt(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/bbsim.BBSim/ShutdownOlt",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(BBSimServer).ShutdownOlt(ctx, req.(*Empty))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
+func _BBSim_RebootOlt_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
+	in := new(Empty)
+	if err := dec(in); err != nil {
+		return nil, err
+	}
+	if interceptor == nil {
+		return srv.(BBSimServer).RebootOlt(ctx, in)
+	}
+	info := &grpc.UnaryServerInfo{
+		Server:     srv,
+		FullMethod: "/bbsim.BBSim/RebootOlt",
+	}
+	handler := func(ctx context.Context, req interface{}) (interface{}, error) {
+		return srv.(BBSimServer).RebootOlt(ctx, req.(*Empty))
+	}
+	return interceptor(ctx, in, info, handler)
+}
+
 func _BBSim_GetONUs_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) {
 	in := new(Empty)
 	if err := dec(in); err != nil {
@@ -959,6 +1056,18 @@
 			Handler:    _BBSim_GetOlt_Handler,
 		},
 		{
+			MethodName: "PoweronOlt",
+			Handler:    _BBSim_PoweronOlt_Handler,
+		},
+		{
+			MethodName: "ShutdownOlt",
+			Handler:    _BBSim_ShutdownOlt_Handler,
+		},
+		{
+			MethodName: "RebootOlt",
+			Handler:    _BBSim_RebootOlt_Handler,
+		},
+		{
 			MethodName: "GetONUs",
 			Handler:    _BBSim_GetONUs_Handler,
 		},
diff --git a/api/bbsim/bbsim.proto b/api/bbsim/bbsim.proto
index 29583c5..e147bb5 100644
--- a/api/bbsim/bbsim.proto
+++ b/api/bbsim/bbsim.proto
@@ -82,6 +82,9 @@
 service BBSim {
     rpc Version(Empty) returns (VersionNumber) {}
     rpc GetOlt(Empty) returns (Olt) {}
+    rpc PoweronOlt(Empty) returns (Response) {}
+    rpc ShutdownOlt(Empty) returns (Response) {}
+    rpc RebootOlt(Empty) returns (Response) {}
     rpc GetONUs(Empty) returns (ONUs) {}
     rpc GetONU(ONURequest) returns (ONU) {}
     rpc SetLogLevel(LogLevel) returns (LogLevel) {}
diff --git a/cmd/bbr/bbr.go b/cmd/bbr/bbr.go
index 4f9eb03..7d01417 100644
--- a/cmd/bbr/bbr.go
+++ b/cmd/bbr/bbr.go
@@ -22,13 +22,14 @@
 package main
 
 import (
+	"os"
+	"runtime/pprof"
+	"time"
+
 	bbrdevices "github.com/opencord/bbsim/internal/bbr/devices"
 	"github.com/opencord/bbsim/internal/bbsim/devices"
 	"github.com/opencord/bbsim/internal/common"
 	log "github.com/sirupsen/logrus"
-	"os"
-	"runtime/pprof"
-	"time"
 )
 
 // usage
@@ -65,10 +66,6 @@
 		"BBSimPort":    options.BBSimPort,
 	}).Info("BroadBand Reflector is on")
 
-	// NOTE this are probably useless in the MockOLT case, check if we can avoid using them in the CreateOlt method
-	oltDoneChannel := make(chan bool)
-	apiDoneChannel := make(chan bool)
-
 	// create the OLT device
 	olt := devices.CreateOLT(
 		options.OltID,
@@ -77,8 +74,6 @@
 		options.NumOnuPerPon,
 		options.STag,
 		options.CTagInit,
-		&oltDoneChannel,
-		&apiDoneChannel,
 		true, // this parameter is not important in the BBR Case
 		true, // this parameter is not important in the BBR Case
 		0,    // this parameter does not matter in the BBR case
diff --git a/cmd/bbsim/bbsim.go b/cmd/bbsim/bbsim.go
index 74bda32..23221c6 100644
--- a/cmd/bbsim/bbsim.go
+++ b/cmd/bbsim/bbsim.go
@@ -37,7 +37,7 @@
 	"google.golang.org/grpc/reflection"
 )
 
-func startApiServer(channel chan bool, group *sync.WaitGroup) {
+func startApiServer(apiDoneChannel chan bool, group *sync.WaitGroup) {
 	// TODO make configurable
 	address := "0.0.0.0:50070"
 	log.Debugf("APIServer listening on: %v", address)
@@ -51,11 +51,11 @@
 	reflection.Register(grpcServer)
 
 	go grpcServer.Serve(lis)
-	go startApiRestServer(channel, group, address)
+	go startApiRestServer(apiDoneChannel, group, address)
 
 	select {
-	case <-channel:
-		// if the api channel is closed, stop the gRPC server
+	case <-apiDoneChannel:
+		// if the API channel is closed, stop the gRPC server
 		grpcServer.Stop()
 		log.Warnf("Stopping API gRPC server")
 	}
@@ -64,7 +64,7 @@
 }
 
 // startApiRestServer method starts the REST server (grpc gateway) for BBSim.
-func startApiRestServer(channel chan bool, group *sync.WaitGroup, grpcAddress string) {
+func startApiRestServer(apiDoneChannel chan bool, group *sync.WaitGroup, grpcAddress string) {
 	ctx := context.Background()
 	ctx, cancel := context.WithCancel(ctx)
 	defer cancel()
@@ -91,7 +91,7 @@
 	}()
 
 	select {
-	case <-channel:
+	case <-apiDoneChannel:
 		log.Warnf("Stopping API REST server")
 		s.Shutdown(ctx)
 	}
@@ -100,7 +100,7 @@
 }
 
 // This server aims to provide compatibility with the previous BBSim version. It is deprecated and will be removed in the future.
-func startLegacyApiServer(channel chan bool, group *sync.WaitGroup) {
+func startLegacyApiServer(apiDoneChannel chan bool, group *sync.WaitGroup) {
 	// TODO make configurable
 	grpcAddress := "0.0.0.0:50072"
 	restAddress := "0.0.0.0:50073"
@@ -116,11 +116,11 @@
 
 	go apiserver.Serve(listener)
 	// Start rest gateway for BBSim server
-	go api.StartRestGatewayService(channel, group, grpcAddress, restAddress)
+	go api.StartRestGatewayService(apiDoneChannel, group, grpcAddress, restAddress)
 
 	select {
-	case <-channel:
-		// if the olt channel is closed, stop the gRPC server
+	case <-apiDoneChannel:
+		// if the API channel is closed, stop the gRPC server
 		log.Warnf("Stopping legacy API gRPC server")
 		apiserver.Stop()
 		break
@@ -157,43 +157,37 @@
 	}).Info("BroadBand Simulator is on")
 
 	// control channels, they are only closed when the goroutine needs to be terminated
-	oltDoneChannel := make(chan bool)
 	apiDoneChannel := make(chan bool)
 
-	sigs := make(chan os.Signal, 1)
-	// stop API and OLT servers on SIGTERM
-	signal.Notify(sigs, syscall.SIGTERM)
-
-	go func() {
-		<-sigs
-		// TODO check when these servers should be shutdown
-		close(apiDoneChannel)
-		close(oltDoneChannel)
-	}()
-
-	wg := sync.WaitGroup{}
-	wg.Add(5)
-
-	olt := devices.CreateOLT(
+	devices.CreateOLT(
 		options.OltID,
 		options.NumNniPerOlt,
 		options.NumPonPerOlt,
 		options.NumOnuPerPon,
 		options.STag,
 		options.CTagInit,
-		&oltDoneChannel,
-		&apiDoneChannel,
 		options.Auth,
 		options.Dhcp,
 		options.Delay,
 		false,
 	)
 
-	go devices.StartOlt(olt, &wg)
 	log.Debugf("Created OLT with id: %d", options.OltID)
+
+	sigs := make(chan os.Signal, 1)
+	// stop API servers on SIGTERM
+	signal.Notify(sigs, syscall.SIGTERM)
+
+	go func() {
+		<-sigs
+		close(apiDoneChannel)
+	}()
+
+	wg := sync.WaitGroup{}
+	wg.Add(4)
+
 	go startApiServer(apiDoneChannel, &wg)
 	go startLegacyApiServer(apiDoneChannel, &wg)
-
 	log.Debugf("Started APIService")
 
 	wg.Wait()
diff --git a/go.mod b/go.mod
index b1da32b..d2c4eef 100644
--- a/go.mod
+++ b/go.mod
@@ -14,7 +14,7 @@
 	github.com/looplab/fsm v0.1.0
 	github.com/opencord/cordctl v0.0.0-20190909161711-01e9c1f04bf4
 	github.com/opencord/omci-sim v0.0.0-20191011202236-3687c57a7252
-	github.com/opencord/voltha-protos/v2 v2.0.1
+	github.com/opencord/voltha-protos/v2 v2.1.0
 	github.com/pkg/errors v0.8.1 // indirect
 	github.com/sirupsen/logrus v1.4.2
 	github.com/stretchr/testify v1.4.0 // indirect
diff --git a/go.sum b/go.sum
index ff69d3f..dad1ad7 100644
--- a/go.sum
+++ b/go.sum
@@ -43,8 +43,8 @@
 github.com/opencord/cordctl v0.0.0-20190909161711-01e9c1f04bf4/go.mod h1:/+3S0pwQUy7HeKnH0KfKp5W6hmh/LdZzuZTNT/m7vA4=
 github.com/opencord/omci-sim v0.0.0-20191011202236-3687c57a7252 h1:CMRqdJmtqku04ImHZW5NtdRlc6RRcdxLOn5Ep/b9HBg=
 github.com/opencord/omci-sim v0.0.0-20191011202236-3687c57a7252/go.mod h1:ToOkj7hkHgoet9XQDadKMhYqgA7qItZsi2j1Pk/mX6Y=
-github.com/opencord/voltha-protos/v2 v2.0.1 h1:vcE0XxNVeu0SED0bW2lf2w24k/QMFrFqMexuedIyTEg=
-github.com/opencord/voltha-protos/v2 v2.0.1/go.mod h1:6kOcfYi1CadWowFxI2SH5wLfHrsRECZLZlD2MFK6WDI=
+github.com/opencord/voltha-protos/v2 v2.1.0 h1:Ppl4/3OBwgGuLk0ob9vIEwMGGRC2sqe7WWoxh0Uq/n0=
+github.com/opencord/voltha-protos/v2 v2.1.0/go.mod h1:6kOcfYi1CadWowFxI2SH5wLfHrsRECZLZlD2MFK6WDI=
 github.com/pkg/errors v0.8.1 h1:iURUrRGxPUNPdy5/HRSm+Yj6okJ6UtLINN0Q9M4+h3I=
 github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
diff --git a/internal/bbsim/api/grpc_api_server.go b/internal/bbsim/api/grpc_api_server.go
index 9df725b..09e4080 100644
--- a/internal/bbsim/api/grpc_api_server.go
+++ b/internal/bbsim/api/grpc_api_server.go
@@ -18,11 +18,13 @@
 
 import (
 	"context"
+	"fmt"
 
 	"github.com/opencord/bbsim/api/bbsim"
 	"github.com/opencord/bbsim/internal/bbsim/devices"
 	"github.com/opencord/bbsim/internal/common"
 	log "github.com/sirupsen/logrus"
+	"google.golang.org/grpc/codes"
 )
 
 var logger = log.WithFields(log.Fields{
@@ -81,6 +83,43 @@
 	return &res, nil
 }
 
+func (s BBSimServer) PoweronOlt(ctx context.Context, req *bbsim.Empty) (*bbsim.Response, error) {
+	res := &bbsim.Response{}
+	o := devices.GetOLT()
+
+	if err := o.InternalState.Event("initialize"); err != nil {
+		log.Errorf("Error initializing OLT: %v", err)
+		res.StatusCode = int32(codes.FailedPrecondition)
+		return res, err
+	}
+
+	res.StatusCode = int32(codes.OK)
+	return res, nil
+}
+
+func (s BBSimServer) ShutdownOlt(ctx context.Context, req *bbsim.Empty) (*bbsim.Response, error) {
+	res := &bbsim.Response{}
+	o := devices.GetOLT()
+
+	if err := o.InternalState.Event("disable"); err != nil {
+		log.Errorf("Error disabling OLT: %v", err)
+		res.StatusCode = int32(codes.FailedPrecondition)
+		return res, err
+	}
+
+	res.StatusCode = int32(codes.OK)
+	return res, nil
+}
+
+func (s BBSimServer) RebootOlt(ctx context.Context, req *bbsim.Empty) (*bbsim.Response, error) {
+	res := &bbsim.Response{}
+	o := devices.GetOLT()
+	go o.RestartOLT()
+	res.StatusCode = int32(codes.OK)
+	res.Message = fmt.Sprintf("OLT restart triggered.")
+	return res, nil
+}
+
 func (s BBSimServer) SetLogLevel(ctx context.Context, req *bbsim.LogLevel) (*bbsim.LogLevel, error) {
 
 	common.SetLogLevel(log.StandardLogger(), req.Level, req.Caller)
diff --git a/internal/bbsim/api/grpc_api_server_legacy.go b/internal/bbsim/api/grpc_api_server_legacy.go
index 0e9b67b..d4e9999 100644
--- a/internal/bbsim/api/grpc_api_server_legacy.go
+++ b/internal/bbsim/api/grpc_api_server_legacy.go
@@ -185,7 +185,7 @@
 	// Register REST endpoints
 	err := legacy.RegisterBBSimServiceHandlerFromEndpoint(ctx, mux, grpcAddress, opts)
 	if err != nil {
-		logger.Error("%v", err)
+		logger.Errorf("%v", err)
 		return
 	}
 
diff --git a/internal/bbsim/api/legacy_api_handler.go b/internal/bbsim/api/legacy_api_handler.go
index 8ae4854..018162f 100644
--- a/internal/bbsim/api/legacy_api_handler.go
+++ b/internal/bbsim/api/legacy_api_handler.go
@@ -123,7 +123,7 @@
 	var SerialNumberLength = 12
 
 	if len(SerialNumber) != SerialNumberLength {
-		logger.Error("Invalid serial number %s", SerialNumber)
+		logger.Errorf("Invalid serial number %s", SerialNumber)
 		return nil, errors.New("invalid serial number")
 	}
 	// First four characters are vendorId
diff --git a/internal/bbsim/devices/device_params.go b/internal/bbsim/devices/device_params.go
new file mode 100644
index 0000000..77421ca
--- /dev/null
+++ b/internal/bbsim/devices/device_params.go
@@ -0,0 +1,21 @@
+/*
+ * 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 devices
+
+const (
+	OltRebootDelay = 10
+)
diff --git a/internal/bbsim/devices/nni.go b/internal/bbsim/devices/nni.go
index 39430ef..6e5e34d 100644
--- a/internal/bbsim/devices/nni.go
+++ b/internal/bbsim/devices/nni.go
@@ -18,19 +18,18 @@
 
 import (
 	"bytes"
+	"os/exec"
+
 	"github.com/google/gopacket"
 	"github.com/google/gopacket/pcap"
 	"github.com/looplab/fsm"
 	"github.com/opencord/bbsim/internal/bbsim/packetHandlers"
 	"github.com/opencord/bbsim/internal/bbsim/types"
 	log "github.com/sirupsen/logrus"
-	"os/exec"
 )
 
 var (
 	nniLogger    = log.WithFields(log.Fields{"module": "NNI"})
-	nniVeth      = "nni"
-	upstreamVeth = "upstream"
 	dhcpServerIp = "192.168.254.1"
 )
 
@@ -52,7 +51,9 @@
 
 type NniPort struct {
 	// BBSIM Internals
-	ID uint32
+	ID           uint32
+	nniVeth      string
+	upstreamVeth string
 
 	// PON Attributes
 	OperState *fsm.FSM
@@ -61,19 +62,21 @@
 
 func CreateNNI(olt *OltDevice) (NniPort, error) {
 	nniPort := NniPort{
-		ID: uint32(0),
+		ID:           uint32(0),
+		nniVeth:      "nni",
+		upstreamVeth: "upstream",
 		OperState: getOperStateFSM(func(e *fsm.Event) {
 			oltLogger.Debugf("Changing NNI OperState from %s to %s", e.Src, e.Dst)
 		}),
 		Type: "nni",
 	}
-	createNNIPair(executor, olt)
+	createNNIPair(executor, olt, &nniPort)
 	return nniPort, nil
 }
 
 // sendNniPacket will send a packet out of the NNI interface.
 // We will send upstream only DHCP packets and drop anything else
-func sendNniPacket(packet gopacket.Packet) error {
+func (n *NniPort) sendNniPacket(packet gopacket.Packet) error {
 	isDhcp := packetHandlers.IsDhcpPacket(packet)
 	isLldp := packetHandlers.IsLldpPacket(packet)
 
@@ -93,7 +96,7 @@
 			return err
 		}
 
-		handle, err := getVethHandler(nniVeth)
+		handle, err := getVethHandler(n.nniVeth)
 		if err != nil {
 			return err
 		}
@@ -117,33 +120,47 @@
 //createNNIBridge will create a veth bridge to fake the connection between the NNI port
 //and something upstream, in this case a DHCP server.
 //It is also responsible to start the DHCP server itself
-func createNNIPair(executor Executor, olt *OltDevice) error {
+func createNNIPair(executor Executor, olt *OltDevice, nniPort *NniPort) error {
 
-	if err := executor.Command("ip", "link", "add", nniVeth, "type", "veth", "peer", "name", upstreamVeth).Run(); err != nil {
-		nniLogger.Errorf("Couldn't create veth pair between %s and %s", nniVeth, upstreamVeth)
+	if err := executor.Command("ip", "link", "add", nniPort.nniVeth, "type", "veth", "peer", "name", nniPort.upstreamVeth).Run(); err != nil {
+		nniLogger.Errorf("Couldn't create veth pair between %s and %s", nniPort.nniVeth, nniPort.upstreamVeth)
 		return err
 	}
 
-	if err := setVethUp(executor, nniVeth); err != nil {
+	if err := setVethUp(executor, nniPort.nniVeth); err != nil {
 		return err
 	}
 
-	if err := setVethUp(executor, upstreamVeth); err != nil {
+	if err := setVethUp(executor, nniPort.upstreamVeth); err != nil {
 		return err
 	}
 
-	if err := startDHCPServer(); err != nil {
+	// TODO should be moved out of this function in case there are multiple NNI interfaces.
+	// Only one DHCP server should be running and listening on all NNI interfaces
+	if err := startDHCPServer(nniPort.upstreamVeth, dhcpServerIp); err != nil {
 		return err
 	}
 
-	ch, err := listenOnVeth(nniVeth)
-	if err != nil {
-		return err
-	}
-	olt.nniPktInChannel = ch
 	return nil
 }
 
+func deleteNNIPair(executor Executor, nniPort *NniPort) error {
+	if err := executor.Command("ip", "link", "del", nniPort.nniVeth).Run(); err != nil {
+		nniLogger.Errorf("Couldn't delete veth pair between %s and %s", nniPort.nniVeth, nniPort.upstreamVeth)
+		return err
+	}
+	return nil
+}
+
+// NewVethChan returns a new channel for receiving packets over the NNI interface
+func (n *NniPort) NewVethChan() (chan *types.PacketMsg, error) {
+	ch, err := listenOnVeth(n.nniVeth)
+	if err != nil {
+		return nil, err
+	}
+	return ch, err
+}
+
 // setVethUp is responsible to activate a virtual interface
 func setVethUp(executor Executor, vethName string) error {
 	if err := executor.Command("ip", "link", "set", vethName, "up").Run(); err != nil {
@@ -153,7 +170,8 @@
 	return nil
 }
 
-var startDHCPServer = func() error {
+var startDHCPServer = func(upstreamVeth string, dhcpServerIp string) error {
+	// TODO the DHCP server should support multiple interfaces
 	if err := exec.Command("ip", "addr", "add", dhcpServerIp, "dev", upstreamVeth).Run(); err != nil {
 		nniLogger.Errorf("Couldn't assing ip %s to interface %s: %v", dhcpServerIp, upstreamVeth, err)
 		return err
diff --git a/internal/bbsim/devices/nni_test.go b/internal/bbsim/devices/nni_test.go
index 60856c6..348d40f 100644
--- a/internal/bbsim/devices/nni_test.go
+++ b/internal/bbsim/devices/nni_test.go
@@ -19,9 +19,10 @@
 
 import (
 	"errors"
+	"testing"
+
 	"github.com/opencord/bbsim/internal/bbsim/types"
 	"gotest.tools/assert"
-	"testing"
 )
 
 func TestSetVethUpSuccess(t *testing.T) {
@@ -49,7 +50,7 @@
 	startDHCPServerCalled := false
 	_startDHCPServer := startDHCPServer
 	defer func() { startDHCPServer = _startDHCPServer }()
-	startDHCPServer = func() error {
+	startDHCPServer = func(upstreamVeth string, dhcpServerIp string) error {
 		startDHCPServerCalled = true
 		return nil
 	}
@@ -67,8 +68,10 @@
 	}
 
 	olt := OltDevice{}
+	nni := NniPort{}
 
-	err := createNNIPair(spy, &olt)
+	err := createNNIPair(spy, &olt, &nni)
+	olt.nniPktInChannel, _ = nni.NewVethChan()
 
 	assert.Equal(t, spy.CommandCallCount, 3)
 	assert.Equal(t, startDHCPServerCalled, true)
diff --git a/internal/bbsim/devices/olt.go b/internal/bbsim/devices/olt.go
index e810ce1..3264bc3 100644
--- a/internal/bbsim/devices/olt.go
+++ b/internal/bbsim/devices/olt.go
@@ -22,6 +22,7 @@
 	"fmt"
 	"net"
 	"sync"
+	"time"
 
 	"github.com/google/gopacket"
 	"github.com/google/gopacket/layers"
@@ -33,6 +34,7 @@
 	"github.com/opencord/voltha-protos/v2/go/tech_profile"
 	log "github.com/sirupsen/logrus"
 	"google.golang.org/grpc"
+	"google.golang.org/grpc/reflection"
 )
 
 var oltLogger = log.WithFields(log.Fields{
@@ -48,9 +50,7 @@
 	NumOnuPerPon    int
 	InternalState   *fsm.FSM
 	channel         chan Message
-	oltDoneChannel  *chan bool
-	apiDoneChannel  *chan bool
-	nniPktInChannel chan *bbsim.PacketMsg
+	nniPktInChannel chan *bbsim.PacketMsg // packets coming in from the NNI and going to VOLTHA
 
 	Delay int
 
@@ -62,12 +62,13 @@
 }
 
 var olt OltDevice
+var oltServer *grpc.Server
 
 func GetOLT() *OltDevice {
 	return &olt
 }
 
-func CreateOLT(oltId int, nni int, pon int, onuPerPon int, sTag int, cTagInit int, oltDoneChannel *chan bool, apiDoneChannel *chan bool, auth bool, dhcp bool, delay int, isMock bool) *OltDevice {
+func CreateOLT(oltId int, nni int, pon int, onuPerPon int, sTag int, cTagInit int, auth bool, dhcp bool, delay int, isMock bool) *OltDevice {
 	oltLogger.WithFields(log.Fields{
 		"ID":           oltId,
 		"NumNni":       nni,
@@ -81,16 +82,12 @@
 		OperState: getOperStateFSM(func(e *fsm.Event) {
 			oltLogger.Debugf("Changing OLT OperState from %s to %s", e.Src, e.Dst)
 		}),
-		NumNni:          nni,
-		NumPon:          pon,
-		NumOnuPerPon:    onuPerPon,
-		Pons:            []*PonPort{},
-		Nnis:            []*NniPort{},
-		channel:         make(chan Message),
-		oltDoneChannel:  oltDoneChannel,
-		apiDoneChannel:  apiDoneChannel,
-		nniPktInChannel: make(chan *bbsim.PacketMsg, 1024), // packets coming in from the NNI and going to VOLTHA
-		Delay:           delay,
+		NumNni:       nni,
+		NumPon:       pon,
+		NumOnuPerPon: onuPerPon,
+		Pons:         []*PonPort{},
+		Nnis:         []*NniPort{},
+		Delay:        delay,
 	}
 
 	// OLT State machine
@@ -98,13 +95,16 @@
 	olt.InternalState = fsm.NewFSM(
 		"created",
 		fsm.Events{
-			{Name: "enable", Src: []string{"created"}, Dst: "enabled"},
+			{Name: "initialize", Src: []string{"disabled", "created"}, Dst: "initialized"},
+			{Name: "enable", Src: []string{"initialized", "disabled"}, Dst: "enabled"},
 			{Name: "disable", Src: []string{"enabled"}, Dst: "disabled"},
 		},
 		fsm.Callbacks{
 			"enter_state": func(e *fsm.Event) {
 				oltLogger.Debugf("Changing OLT InternalState from %s to %s", e.Src, e.Dst)
 			},
+			"enter_disabled":    func(e *fsm.Event) { olt.disableOlt() },
+			"enter_initialized": func(e *fsm.Event) { olt.InitOlt() },
 		},
 	)
 
@@ -143,16 +143,96 @@
 
 		olt.Pons = append(olt.Pons, &p)
 	}
+
+	if err := olt.InternalState.Event("initialize"); err != nil {
+		log.Errorf("Error initializing OLT: %v", err)
+		return nil
+	}
+
 	return &olt
 }
 
-// this function start the OLT gRPC server and blocks until it's done
-func StartOlt(olt *OltDevice, group *sync.WaitGroup) {
-	newOltServer(*olt)
-	group.Done()
+func (o *OltDevice) InitOlt() error {
+
+	if oltServer == nil {
+		oltServer, _ = newOltServer()
+	} else {
+		oltLogger.Warn("OLT server already running.")
+	}
+
+	// create new channel for processOltMessages Go routine
+	o.channel = make(chan Message)
+
+	o.nniPktInChannel = make(chan *bbsim.PacketMsg, 1024)
+	// FIXME we are assuming we have only one NNI
+	if o.Nnis[0] != nil {
+		ch, err := o.Nnis[0].NewVethChan()
+		if err == nil {
+			o.nniPktInChannel = ch
+		} else {
+			log.Errorf("Error getting NNI channel: %v", err)
+		}
+	}
+
+	for i := range olt.Pons {
+		for _, onu := range olt.Pons[i].Onus {
+			if err := onu.InternalState.Event("initialize"); err != nil {
+				log.Errorf("Error initializing ONU: %v", err)
+				return err
+			}
+		}
+	}
+
+	return nil
 }
 
-func newOltServer(o OltDevice) error {
+// callback for disable state entry
+func (o *OltDevice) disableOlt() error {
+
+	// disable all onus
+	for i := range o.Pons {
+		for _, onu := range o.Pons[i].Onus {
+			// NOTE order of these is important.
+			onu.OperState.Event("disable")
+			onu.InternalState.Event("disable")
+		}
+	}
+
+	// TODO handle hard poweroff (i.e. no indications sent to Voltha) vs soft poweroff
+	if err := StopOltServer(); err != nil {
+		return err
+	}
+
+	// terminate the OLT's processOltMessages go routine
+	close(o.channel)
+	// terminate the OLT's processNniPacketIns go routine
+	close(o.nniPktInChannel)
+	return nil
+}
+
+func (o *OltDevice) RestartOLT() error {
+	oltLogger.Infof("Simulating OLT restart... (%ds)", OltRebootDelay)
+
+	// transition internal state to disable
+	if !o.InternalState.Is("disabled") {
+		if err := o.InternalState.Event("disable"); err != nil {
+			log.Errorf("Error disabling OLT: %v", err)
+			return err
+		}
+	}
+
+	time.Sleep(OltRebootDelay * time.Second)
+
+	if err := o.InternalState.Event("initialize"); err != nil {
+		log.Errorf("Error initializing OLT: %v", err)
+		return err
+	}
+	oltLogger.Info("OLT restart completed")
+	return nil
+}
+
+// newOltServer launches a new grpc server for OpenOLT
+func newOltServer() (*grpc.Server, error) {
 	// TODO make configurable
 	address := "0.0.0.0:50060"
 	lis, err := net.Listen("tcp", address)
@@ -160,51 +240,50 @@
 		oltLogger.Fatalf("OLT failed to listen: %v", err)
 	}
 	grpcServer := grpc.NewServer()
+
+	o := GetOLT()
 	openolt.RegisterOpenoltServer(grpcServer, o)
 
-	wg := sync.WaitGroup{}
-	wg.Add(1)
+	reflection.Register(grpcServer)
 
 	go grpcServer.Serve(lis)
 	oltLogger.Debugf("OLT Listening on: %v", address)
 
-	for {
-		_, ok := <-*o.oltDoneChannel
-		if !ok {
-			// if the olt Channel is closed, stop the gRPC server
-			log.Warnf("Stopping OLT gRPC server")
-			grpcServer.Stop()
-			wg.Done()
-			break
-		}
+	return grpcServer, nil
+}
+
+// StopOltServer stops the OpenOLT grpc server
+func StopOltServer() error {
+	// TODO handle poweroff vs graceful shutdown
+	if oltServer != nil {
+		log.Warnf("Stopping OLT gRPC server")
+		oltServer.Stop()
+		oltServer = nil
 	}
-
-	wg.Wait()
-
 	return nil
 }
 
 // Device Methods
 
-func (o OltDevice) Enable(stream openolt.Openolt_EnableIndicationServer) error {
-
+// Enable implements the OpenOLT EnableIndicationServer functionality
+func (o *OltDevice) Enable(stream openolt.Openolt_EnableIndicationServer) error {
 	oltLogger.Debug("Enable OLT called")
 
 	wg := sync.WaitGroup{}
 	wg.Add(2)
 
-	// create a Channel for all the OLT events
-	go o.processOltMessages(stream)
-	go o.processNniPacketIns(stream)
+	// create Go routine to process all OLT events
+	go o.processOltMessages(stream, &wg)
+	go o.processNniPacketIns(stream, &wg)
 
 	// enable the OLT
-	olt_msg := Message{
+	oltMsg := Message{
 		Type: OltIndication,
 		Data: OltIndicationMessage{
 			OperState: UP,
 		},
 	}
-	o.channel <- olt_msg
+	o.channel <- oltMsg
 
 	// send NNI Port Indications
 	for _, nni := range o.Nnis {
@@ -217,9 +296,11 @@
 		}
 		o.channel <- msg
 	}
+
 	go o.processOmciMessages()
+
 	// send PON Port indications
-	for _, pon := range o.Pons {
+	for i, pon := range o.Pons {
 		msg := Message{
 			Type: PonIndication,
 			Data: PonIndicationMessage{
@@ -229,29 +310,24 @@
 		}
 		o.channel <- msg
 
-		for _, onu := range pon.Onus {
+		for _, onu := range o.Pons[i].Onus {
 			go onu.ProcessOnuMessages(stream, nil)
-			// FIXME move the message generation in the state transition
-			// from here only invoke the state transition
-			msg := Message{
-				Type: OnuDiscIndication,
-				Data: OnuDiscIndicationMessage{
-					Onu:       onu,
-					OperState: UP,
-				},
+			if err := onu.InternalState.Event("discover"); err != nil {
+				log.Errorf("Error discover ONU: %v", err)
+				return err
 			}
-			onu.Channel <- msg
 		}
 	}
 
+	oltLogger.Warn("Enable OLT Done")
 	wg.Wait()
 	return nil
 }
 
-func (o OltDevice) processOmciMessages() {
+func (o *OltDevice) processOmciMessages() {
 	ch := omcisim.GetChannel()
 
-	oltLogger.Debug("Started OMCI Indication Channel")
+	oltLogger.Debug("Starting OMCI Indication Channel")
 
 	for message := range ch {
 		onuId := message.Data.OnuId
@@ -284,7 +360,7 @@
 	return nil, errors.New(fmt.Sprintf("Cannot find NniPort with id %d in OLT %d", id, o.ID))
 }
 
-func (o OltDevice) sendOltIndication(msg OltIndicationMessage, stream openolt.Openolt_EnableIndicationServer) {
+func (o *OltDevice) sendOltIndication(msg OltIndicationMessage, stream openolt.Openolt_EnableIndicationServer) {
 	data := &openolt.Indication_OltInd{OltInd: &openolt.OltIndication{OperState: msg.OperState.String()}}
 	if err := stream.Send(&openolt.Indication{Data: data}); err != nil {
 		oltLogger.Errorf("Failed to send Indication_OltInd: %v", err)
@@ -295,7 +371,7 @@
 	}).Debug("Sent Indication_OltInd")
 }
 
-func (o OltDevice) sendNniIndication(msg NniIndicationMessage, stream openolt.Openolt_EnableIndicationServer) {
+func (o *OltDevice) sendNniIndication(msg NniIndicationMessage, stream openolt.Openolt_EnableIndicationServer) {
 	nni, _ := o.getNniById(msg.NniPortID)
 	nni.OperState.Event("enable")
 	// NOTE Operstate may need to be an integer
@@ -316,7 +392,7 @@
 	}).Debug("Sent Indication_IntfOperInd for NNI")
 }
 
-func (o OltDevice) sendPonIndication(msg PonIndicationMessage, stream openolt.Openolt_EnableIndicationServer) {
+func (o *OltDevice) sendPonIndication(msg PonIndicationMessage, stream openolt.Openolt_EnableIndicationServer) {
 	pon, _ := o.GetPonById(msg.PonPortID)
 	pon.OperState.Event("enable")
 	discoverData := &openolt.Indication_IntfInd{IntfInd: &openolt.IntfIndication{
@@ -350,8 +426,9 @@
 	}).Debug("Sent Indication_IntfOperInd for PON")
 }
 
-func (o OltDevice) processOltMessages(stream openolt.Openolt_EnableIndicationServer) {
-	oltLogger.Debug("Started OLT Indication Channel")
+// processOltMessages handles messages received over the OpenOLT interface
+func (o *OltDevice) processOltMessages(stream openolt.Openolt_EnableIndicationServer, wg *sync.WaitGroup) {
+	oltLogger.Debug("Starting OLT Indication Channel")
 	for message := range o.channel {
 
 		oltLogger.WithFields(log.Fields{
@@ -381,9 +458,12 @@
 		}
 
 	}
+	wg.Done()
+	oltLogger.Warn("Stopped handling OLT Indication Channel")
 }
 
-func (o OltDevice) processNniPacketIns(stream openolt.Openolt_EnableIndicationServer) {
+// processNniPacketIns handles messages received over the NNI interface
+func (o *OltDevice) processNniPacketIns(stream openolt.Openolt_EnableIndicationServer, wg *sync.WaitGroup) {
 	oltLogger.WithFields(log.Fields{
 		"nniChannel": o.nniPktInChannel,
 	}).Debug("Started NNI Channel")
@@ -436,6 +516,10 @@
 			"OnuSn":    onu.Sn(),
 		}).Tracef("Sent PktInd indication")
 	}
+	wg.Done()
+	oltLogger.WithFields(log.Fields{
+		"nniChannel": o.nniPktInChannel,
+	}).Warn("Stopped handling NNI Channel")
 }
 
 // returns an ONU with a given Serial Number
@@ -527,13 +611,13 @@
 
 func (o OltDevice) DisableOlt(context.Context, *openolt.Empty) (*openolt.Empty, error) {
 	// NOTE when we disable the OLT should we disable NNI, PONs and ONUs altogether?
-	olt_msg := Message{
+	oltMsg := Message{
 		Type: OltIndication,
 		Data: OltIndicationMessage{
 			OperState: DOWN,
 		},
 	}
-	o.channel <- olt_msg
+	o.channel <- oltMsg
 	return new(openolt.Empty), nil
 }
 
@@ -542,7 +626,7 @@
 	return new(openolt.Empty), nil
 }
 
-func (o OltDevice) EnableIndication(_ *openolt.Empty, stream openolt.Openolt_EnableIndicationServer) error {
+func (o *OltDevice) EnableIndication(_ *openolt.Empty, stream openolt.Openolt_EnableIndicationServer) error {
 	oltLogger.WithField("oltId", o.ID).Info("OLT receives EnableIndication call from VOLTHA")
 	o.Enable(stream)
 	return nil
@@ -703,11 +787,8 @@
 }
 
 func (o OltDevice) Reboot(context.Context, *openolt.Empty) (*openolt.Empty, error) {
-	defer func() {
-		oltLogger.Info("Shutting Down")
-		close(*o.oltDoneChannel)
-		close(*o.apiDoneChannel)
-	}()
+	oltLogger.Info("Shutting down")
+	o.RestartOLT()
 	return new(openolt.Empty), nil
 }
 
@@ -719,7 +800,7 @@
 func (o OltDevice) UplinkPacketOut(context context.Context, packet *openolt.UplinkPacket) (*openolt.Empty, error) {
 	pkt := gopacket.NewPacket(packet.Pkt, layers.LayerTypeEthernet, gopacket.Default)
 
-	sendNniPacket(pkt)
+	o.Nnis[0].sendNniPacket(pkt) // FIXME we are assuming we have only one NNI
 	// NOTE should we return an error if sendNniPakcet fails?
 	return new(openolt.Empty), nil
 }
diff --git a/internal/bbsim/devices/onu.go b/internal/bbsim/devices/onu.go
index bf5140b..a915962 100644
--- a/internal/bbsim/devices/onu.go
+++ b/internal/bbsim/devices/onu.go
@@ -20,6 +20,10 @@
 	"context"
 	"errors"
 	"fmt"
+	"net"
+
+	"time"
+
 	"github.com/cboling/omci"
 	"github.com/google/gopacket/layers"
 	"github.com/looplab/fsm"
@@ -31,8 +35,6 @@
 	omcisim "github.com/opencord/omci-sim"
 	"github.com/opencord/voltha-protos/v2/go/openolt"
 	log "github.com/sirupsen/logrus"
-	"net"
-	"time"
 )
 
 var onuLogger = log.WithFields(log.Fields{
@@ -87,7 +89,6 @@
 		Dhcp:             dhcp,
 		HwAddress:        net.HardwareAddr{0x2e, 0x60, 0x70, 0x13, byte(pon.ID), byte(id)},
 		PortNo:           0,
-		Channel:          make(chan Message, 2048),
 		tid:              0x1,
 		hpTid:            0x8000,
 		seqNumber:        0,
@@ -109,12 +110,13 @@
 		"created",
 		fsm.Events{
 			// DEVICE Lifecycle
-			{Name: "discover", Src: []string{"created"}, Dst: "discovered"},
+			{Name: "initialize", Src: []string{"created", "disabled"}, Dst: "initialized"},
+			{Name: "discover", Src: []string{"initialized"}, Dst: "discovered"},
 			{Name: "enable", Src: []string{"discovered", "disabled"}, Dst: "enabled"},
 			{Name: "receive_eapol_flow", Src: []string{"enabled", "gem_port_added"}, Dst: "eapol_flow_received"},
 			{Name: "add_gem_port", Src: []string{"enabled", "eapol_flow_received"}, Dst: "gem_port_added"},
-			// NOTE should disabled state be diffente for oper_disabled (emulating an error) and admin_disabled (received a disabled call via VOLTHA)?
-			{Name: "disable", Src: []string{"eap_response_success_received", "auth_failed", "dhcp_ack_received", "dhcp_failed"}, Dst: "disabled"},
+			// NOTE should disabled state be different for oper_disabled (emulating an error) and admin_disabled (received a disabled call via VOLTHA)?
+			{Name: "disable", Src: []string{"enabled", "eap_response_success_received", "auth_failed", "dhcp_ack_received", "dhcp_failed"}, Dst: "disabled"},
 			// EAPOL
 			{Name: "start_auth", Src: []string{"eapol_flow_received", "gem_port_added", "eap_start_sent", "eap_response_identity_sent", "eap_response_challenge_sent", "eap_response_success_received", "auth_failed", "dhcp_ack_received", "dhcp_failed"}, Dst: "auth_started"},
 			{Name: "eap_start_sent", Src: []string{"auth_started"}, Dst: "eap_start_sent"},
@@ -130,13 +132,27 @@
 			{Name: "dhcp_failed", Src: []string{"dhcp_started", "dhcp_discovery_sent", "dhcp_request_sent"}, Dst: "dhcp_failed"},
 			// BBR States
 			// TODO add start OMCI state
-			{Name: "send_eapol_flow", Src: []string{"created"}, Dst: "eapol_flow_sent"},
+			{Name: "send_eapol_flow", Src: []string{"initialized"}, Dst: "eapol_flow_sent"},
 			{Name: "send_dhcp_flow", Src: []string{"eapol_flow_sent"}, Dst: "dhcp_flow_sent"},
 		},
 		fsm.Callbacks{
 			"enter_state": func(e *fsm.Event) {
 				o.logStateChange(e.Src, e.Dst)
 			},
+			"enter_initialized": func(e *fsm.Event) {
+				// create new channel for ProcessOnuMessages Go routine
+				o.Channel = make(chan Message, 2048)
+			},
+			"enter_discovered": func(e *fsm.Event) {
+				msg := Message{
+					Type: OnuDiscIndication,
+					Data: OnuDiscIndicationMessage{
+						Onu:       &o,
+						OperState: UP,
+					},
+				}
+				o.Channel <- msg
+			},
 			"enter_enabled": func(event *fsm.Event) {
 				msg := Message{
 					Type: OnuIndication,
@@ -158,6 +174,8 @@
 					},
 				}
 				o.Channel <- msg
+				// terminate the ONU's ProcessOnuMessages Go routine
+				close(o.Channel)
 			},
 			"enter_auth_started": func(e *fsm.Event) {
 				o.logStateChange(e.Src, e.Dst)
@@ -213,6 +231,7 @@
 			},
 		},
 	)
+
 	return &o
 }
 
@@ -224,11 +243,13 @@
 	}).Debugf("Changing ONU InternalState from %s to %s", src, dst)
 }
 
+// ProcessOnuMessages starts indication channel for each ONU
 func (o *Onu) ProcessOnuMessages(stream openolt.Openolt_EnableIndicationServer, client openolt.OpenoltClient) {
 	onuLogger.WithFields(log.Fields{
-		"onuID": o.ID,
-		"onuSN": o.Sn(),
-	}).Debug("Started ONU Indication Channel")
+		"onuID":   o.ID,
+		"onuSN":   o.Sn(),
+		"ponPort": o.PonPortID,
+	}).Debug("Starting ONU Indication Channel")
 
 	for message := range o.Channel {
 		onuLogger.WithFields(log.Fields{
@@ -307,6 +328,10 @@
 			onuLogger.Warnf("Received unknown message data %v for type %v in OLT Channel", message.Data, message.Type)
 		}
 	}
+	onuLogger.WithFields(log.Fields{
+		"onuID": o.ID,
+		"onuSN": o.Sn(),
+	}).Debug("Stopped handling ONU Indication Channel")
 }
 
 func (o *Onu) processOmciMessage(message omcisim.OmciChMessage) {
@@ -331,7 +356,7 @@
 	}
 }
 
-func (o *Onu) NewSN(oltid int, intfid uint32, onuid uint32) *openolt.SerialNumber {
+func (o Onu) NewSN(oltid int, intfid uint32, onuid uint32) *openolt.SerialNumber {
 
 	sn := new(openolt.SerialNumber)
 
@@ -540,11 +565,11 @@
 		msg.Flow.Classifier.SrcPort == uint32(68) &&
 		msg.Flow.Classifier.DstPort == uint32(67) {
 
-		// keep track that we reveived the DHCP Flows so that we can transition the state to dhcp_started
+		// keep track that we received the DHCP Flows so that we can transition the state to dhcp_started
 		o.DhcpFlowReceived = true
 
 		if o.Dhcp == true {
-			// NOTE we are receiving mulitple DHCP flows but we shouldn't call the transition multiple times
+			// NOTE we are receiving multiple DHCP flows but we shouldn't call the transition multiple times
 			if err := o.InternalState.Event("start_dhcp"); err != nil {
 				log.Errorf("Can't go to dhcp_started: %v", err)
 			}
diff --git a/internal/bbsim/devices/onu_state_machine_test.go b/internal/bbsim/devices/onu_state_machine_test.go
index c169c12..4ceded9 100644
--- a/internal/bbsim/devices/onu_state_machine_test.go
+++ b/internal/bbsim/devices/onu_state_machine_test.go
@@ -17,14 +17,14 @@
 package devices
 
 import (
-	"gotest.tools/assert"
 	"testing"
+
+	"gotest.tools/assert"
 )
 
 func Test_Onu_StateMachine_enable(t *testing.T) {
 	onu := createTestOnu()
-
-	assert.Equal(t, onu.InternalState.Current(), "created")
+	assert.Equal(t, onu.InternalState.Current(), "initialized")
 	onu.InternalState.Event("discover")
 	assert.Equal(t, onu.InternalState.Current(), "discovered")
 	onu.InternalState.Event("enable")
diff --git a/internal/bbsim/devices/onu_test_helpers.go b/internal/bbsim/devices/onu_test_helpers.go
index 2f9926f..8e071c1 100644
--- a/internal/bbsim/devices/onu_test_helpers.go
+++ b/internal/bbsim/devices/onu_test_helpers.go
@@ -129,5 +129,7 @@
 		ID: 1,
 	}
 	onu := CreateONU(olt, pon, 1, 900, 900, false, false)
+	// NOTE we need this in order to create the OnuChannel
+	onu.InternalState.Event("initialize")
 	return onu
 }
diff --git a/internal/bbsimctl/commands/olt.go b/internal/bbsimctl/commands/olt.go
index fa847ff..9ff6a30 100644
--- a/internal/bbsimctl/commands/olt.go
+++ b/internal/bbsimctl/commands/olt.go
@@ -40,10 +40,19 @@
 
 type OltPONs struct{}
 
+type OltShutdown struct{}
+
+type OltPoweron struct{}
+
+type OltReboot struct{}
+
 type oltOptions struct {
-	Get OltGet  `command:"get"`
-	NNI OltNNIs `command:"nnis"`
-	PON OltPONs `command:"pons"`
+	Get      OltGet      `command:"get"`
+	NNI      OltNNIs     `command:"nnis"`
+	PON      OltPONs     `command:"pons"`
+	Shutdown OltShutdown `command:"shutdown"`
+	Poweron  OltPoweron  `command:"poweron"`
+	Reboot   OltReboot   `command:"reboot"`
 }
 
 func RegisterOltCommands(parser *flags.Parser) {
@@ -107,3 +116,57 @@
 
 	return nil
 }
+
+func (o *OltShutdown) Execute(args []string) error {
+	client, conn := connect()
+	defer conn.Close()
+
+	ctx, cancel := context.WithTimeout(context.Background(), config.GlobalConfig.Grpc.Timeout)
+	defer cancel()
+
+	res, err := client.ShutdownOlt(ctx, &pb.Empty{})
+
+	if err != nil {
+		log.Fatalf("Cannot shut down OLT: %v", err)
+		return err
+	}
+
+	fmt.Println(fmt.Sprintf("[Status: %d] %s", res.StatusCode, res.Message))
+	return nil
+}
+
+func (o *OltPoweron) Execute(args []string) error {
+	client, conn := connect()
+	defer conn.Close()
+
+	ctx, cancel := context.WithTimeout(context.Background(), config.GlobalConfig.Grpc.Timeout)
+	defer cancel()
+
+	res, err := client.PoweronOlt(ctx, &pb.Empty{})
+
+	if err != nil {
+		log.Fatalf("Cannot power on OLT: %v", err)
+		return err
+	}
+
+	fmt.Println(fmt.Sprintf("[Status: %d] %s", res.StatusCode, res.Message))
+	return nil
+}
+
+func (o *OltReboot) Execute(args []string) error {
+	client, conn := connect()
+	defer conn.Close()
+
+	ctx, cancel := context.WithTimeout(context.Background(), config.GlobalConfig.Grpc.Timeout)
+	defer cancel()
+
+	res, err := client.RebootOlt(ctx, &pb.Empty{})
+
+	if err != nil {
+		log.Fatalf("Cannot reboot OLT: %v", err)
+		return err
+	}
+
+	fmt.Println(fmt.Sprintf("[Status: %d] %s", res.StatusCode, res.Message))
+	return nil
+}
diff --git a/tests/results/.gitignore b/tests/results/.gitignore
index c96a04f..d6b7ef3 100644
--- a/tests/results/.gitignore
+++ b/tests/results/.gitignore
@@ -1,2 +1,2 @@
 *
-!.gitignore
\ No newline at end of file
+!.gitignore
diff --git a/vendor/modules.txt b/vendor/modules.txt
index 07df976..f014b80 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -65,7 +65,7 @@
 github.com/opencord/cordctl/pkg/format
 # github.com/opencord/omci-sim v0.0.0-20191011202236-3687c57a7252
 github.com/opencord/omci-sim
-# github.com/opencord/voltha-protos/v2 v2.0.1
+# github.com/opencord/voltha-protos/v2 v2.1.0
 github.com/opencord/voltha-protos/v2/go/openolt
 github.com/opencord/voltha-protos/v2/go/tech_profile
 # github.com/pkg/errors v0.8.1