[VOL-3622] Stop and restart gRPC server

Change-Id: I443e7ed75f79dd90782a1a4342c8e4d3a7294488
diff --git a/internal/bbsim/api/grpc_api_server.go b/internal/bbsim/api/grpc_api_server.go
index 7ed8f8d..84d3478 100644
--- a/internal/bbsim/api/grpc_api_server.go
+++ b/internal/bbsim/api/grpc_api_server.go
@@ -20,6 +20,7 @@
 	"context"
 	"fmt"
 	"strings"
+	"time"
 
 	"github.com/opencord/bbsim/api/bbsim"
 	"github.com/opencord/bbsim/internal/bbsim/alarmsim"
@@ -128,6 +129,58 @@
 	return res, nil
 }
 
+func (s BBSimServer) StopgRPCServer(ctx context.Context, req *bbsim.Empty) (*bbsim.Response, error) {
+	res := &bbsim.Response{}
+	res.StatusCode = int32(codes.OK)
+	res.Message = fmt.Sprintf("Openolt gRPC server stopped")
+	o := devices.GetOLT()
+
+	logger.Infof("Received request to stop Openolt gRPC Server")
+
+	o.StopOltServer()
+
+	return res, nil
+}
+
+func (s BBSimServer) StartgRPCServer(ctx context.Context, req *bbsim.Empty) (*bbsim.Response, error) {
+	res := &bbsim.Response{}
+	res.StatusCode = int32(codes.OK)
+	res.Message = fmt.Sprintf("Openolt gRPC server started")
+	o := devices.GetOLT()
+
+	logger.Infof("Received request to start Openolt gRPC Server")
+
+	_, err := o.StartOltServer()
+	if err != nil {
+		return nil, err
+	}
+
+	return res, nil
+}
+
+func (s BBSimServer) RestartgRPCServer(ctx context.Context, req *bbsim.Timeout) (*bbsim.Response, error) {
+	o := devices.GetOLT()
+	logger.Infof("Received request to restart Openolt gRPC Server in %v seconds", req.Delay)
+	o.StopOltServer()
+
+	res := &bbsim.Response{}
+	res.StatusCode = int32(codes.OK)
+	res.Message = fmt.Sprintf("Openolt gRPC server stopped, restarting in %v", req.Delay)
+
+	go func() {
+		time.Sleep(time.Duration(req.Delay) * time.Second)
+		_, err := o.StartOltServer()
+		if err != nil {
+			logger.WithFields(log.Fields{
+				"err": err,
+			}).Error("Cannot restart Openolt gRPC server")
+		}
+		logger.Infof("Openolt gRPC Server restarted after %v seconds", req.Delay)
+	}()
+
+	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/devices/olt.go b/internal/bbsim/devices/olt.go
index beeffce..008e3be 100644
--- a/internal/bbsim/devices/olt.go
+++ b/internal/bbsim/devices/olt.go
@@ -48,6 +48,7 @@
 
 type OltDevice struct {
 	sync.Mutex
+	oltServer *grpc.Server
 
 	// BBSIM Internals
 	ID                   int
@@ -79,7 +80,6 @@
 }
 
 var olt OltDevice
-var oltServer *grpc.Server
 
 func GetOLT() *OltDevice {
 	return &olt
@@ -224,10 +224,9 @@
 
 func (o *OltDevice) InitOlt() {
 
-	if oltServer == nil {
-		oltServer, _ = o.newOltServer()
+	if o.oltServer == nil {
+		o.oltServer, _ = o.StartOltServer()
 	} else {
-		// FIXME there should never be a server running if we are initializing the OLT
 		oltLogger.Fatal("OLT server already running.")
 	}
 
@@ -267,12 +266,8 @@
 		return err
 	}
 
-	// TODO handle hard poweroff (i.e. no indications sent to Voltha) vs soft poweroff
 	time.Sleep(1 * time.Second) // we need to give the OLT the time to respond to all the pending gRPC request before stopping the server
-	if err := o.StopOltServer(); err != nil {
-		oltLogger.Errorf("Error in stopping OLT server")
-		return err
-	}
+	o.StopOltServer()
 
 	if softReboot {
 		for _, pon := range o.Pons {
@@ -341,18 +336,28 @@
 	return grpcServer, nil
 }
 
+// StartOltServer will create the grpc server that VOLTHA uses
+// to communicate with the device
+func (o *OltDevice) StartOltServer() (*grpc.Server, error) {
+	oltServer, err := o.newOltServer()
+	if err != nil {
+		oltLogger.WithFields(log.Fields{
+			"err": err,
+		}).Error("Cannot OLT gRPC server")
+		return nil, err
+	}
+	return oltServer, nil
+}
+
 // StopOltServer stops the OpenOLT grpc server
-func (o *OltDevice) StopOltServer() error {
-	// TODO handle poweroff vs graceful shutdown
-	if oltServer != nil {
+func (o *OltDevice) StopOltServer() {
+	if o.oltServer != nil {
 		oltLogger.WithFields(log.Fields{
 			"oltId": o.SerialNumber,
 		}).Warnf("Stopping OLT gRPC server")
-		oltServer.Stop()
-		oltServer = nil
+		o.oltServer.Stop()
+		o.oltServer = nil
 	}
-
-	return nil
 }
 
 // Device Methods
diff --git a/internal/bbsimctl/commands/olt.go b/internal/bbsimctl/commands/olt.go
index 5f6747e..bed3733 100644
--- a/internal/bbsimctl/commands/olt.go
+++ b/internal/bbsimctl/commands/olt.go
@@ -49,6 +49,15 @@
 
 type OltReboot struct{}
 
+type StopGrpcServer struct{}
+
+type StartGrpcServer struct{}
+type RestartGrpcServer struct {
+	Args struct {
+		Delay uint32
+	} `positional-args:"yes" required:"yes"`
+}
+
 type OltFlows struct{}
 
 type OltPoweronAllOnus struct{}
@@ -66,6 +75,9 @@
 	Flows           OltFlows           `command:"flows"`
 	PoweronAllOnus  OltPoweronAllOnus  `command:"poweronAllONUs"`
 	ShutdownAllOnus OltShutdownAllOnus `command:"shutdownAllONUs"`
+	StopServer      StopGrpcServer     `command:"stopServer"`
+	StartServer     StartGrpcServer    `command:"startServer"`
+	RestartServer   RestartGrpcServer  `command:"restartServer"`
 }
 
 func RegisterOltCommands(parser *flags.Parser) {
@@ -184,6 +196,63 @@
 	return nil
 }
 
+func (o *StopGrpcServer) 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.StopgRPCServer(ctx, &pb.Empty{})
+
+	if err != nil {
+		log.Fatalf("Cannot stop Openolt server: %v", err)
+		return err
+	}
+
+	fmt.Println(fmt.Sprintf("[Status: %d] %s", res.StatusCode, res.Message))
+	return nil
+}
+
+func (o *StartGrpcServer) 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.StartgRPCServer(ctx, &pb.Empty{})
+
+	if err != nil {
+		log.Fatalf("Cannot start Openolt server: %v", err)
+		return err
+	}
+
+	fmt.Println(fmt.Sprintf("[Status: %d] %s", res.StatusCode, res.Message))
+	return nil
+}
+
+func (o *RestartGrpcServer) Execute(args []string) error {
+	req := &pb.Timeout{
+		Delay: o.Args.Delay,
+	}
+	client, conn := connect()
+	defer conn.Close()
+
+	ctx, cancel := context.WithTimeout(context.Background(), config.GlobalConfig.Grpc.Timeout)
+	defer cancel()
+
+	res, err := client.RestartgRPCServer(ctx, req)
+
+	if err != nil {
+		log.Fatalf("Cannot restart Openolt server: %v", err)
+		return err
+	}
+
+	fmt.Println(fmt.Sprintf("[Status: %d] %s", res.StatusCode, res.Message))
+	return nil
+}
+
 func (o *OltFlows) Execute(args []string) error {
 	client, conn := connect()
 	defer conn.Close()