package commands import ( "context" "time" pb "code.fbi.h-da.de/cocsn/gosdn/api/proto" "code.fbi.h-da.de/cocsn/gosdn/cmd/gosdn-tview/app" "github.com/rivo/tview" grpc "google.golang.org/grpc" //Package google.golang.org/grpc/health is needed to make gRPC Health Check work _ "google.golang.org/grpc/health" healthpb "google.golang.org/grpc/health/grpc_health_v1" "google.golang.org/protobuf/types/known/emptypb" ) const ( defaultName = "gosdn-cli" ) type command struct { Name string Description string Function func(conn *grpc.ClientConn) string } //CommandList contains the specific goSDN gRPC calls var CommandList = []command{ {"hello", "test connection to controller", goSDNSayHello}, {"testdb", "test all database connections", goSDNTestDB}, {"tapiGetDevices", "creates devices", TAPIGetEdge}, {"tapiGetInterfaces", "creates interfaces", TAPIGetEdgeNode}, {"tapiGetLinks", "creates links between devices", TAPIGetLink}, {"shutdown", "request controller to shutdown", goSDNShutdown}, } var serviceConfig = `{ "loadBalancingPolicy": "round_robin", "healthCheckConfig": { "serviceName": "" } }` //Connect creates a new connection to the gRPC server func Connect(address string) (*grpc.ClientConn, error) { options := []grpc.DialOption{ grpc.WithInsecure(), grpc.WithBlock(), grpc.WithDefaultServiceConfig(serviceConfig), grpc.WithTimeout(5 * time.Second), } return grpc.Dial(address, options...) } //GoSDNLogStream creates a continuous gRPC stream to recieve goSDN logs func GoSDNLogStream(app *app.App, conn *grpc.ClientConn, tv *tview.TextView) error { var streamError error c := pb.NewGrpcCliClient(conn) stream, err := c.CreateLogStream(context.Background(), &emptypb.Empty{}) if err != nil { return err } go func(stream pb.GrpcCli_CreateLogStreamClient) { for { msg, err := stream.Recv() if err != nil { streamError = err go func() { ticker := time.NewTicker(5 * time.Second) for range ticker.C { if err := GoSDNLogStream(app, conn, tv); err == nil { ticker.Stop() return } } }() break } response := []byte(msg.Log) app.QueueUpdateDraw(func() { tv.Write(response) }) } }(stream) return streamError } func goSDNSayHello(conn *grpc.ClientConn) string { c := pb.NewGrpcCliClient(conn) // Contact the server and print out its response. name := defaultName ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() r, err := c.SayHello(ctx, &pb.HelloRequest{Name: name}) if err != nil { return err.Error() } return r.GetMessage() } //WatchHealth continuous gRPC Health Check stream to recieve health changes func WatchHealth(service string, app *app.App, conn *grpc.ClientConn, tv *tview.TextView) error { var streamError error c := healthpb.NewHealthClient(conn) stream, err := c.Watch(context.Background(), &healthpb.HealthCheckRequest{Service: service}) if err != nil { app.QueueUpdateDraw(func() { tv.Clear() tv.SetText(err.Error()) }) return err } go func(stream healthpb.Health_WatchClient) { for { msg, err := stream.Recv() if err != nil { streamError = err go func() { ticker := time.NewTicker(5 * time.Second) for range ticker.C { if err := WatchHealth(service, app, conn, tv); err == nil { ticker.Stop() return } } }() break } app.QueueUpdateDraw(func() { tv.Clear() tv.SetText(msg.GetStatus().String()) }) } }(stream) return streamError } func goSDNShutdown(conn *grpc.ClientConn) string { c := pb.NewGrpcCliClient(conn) // Contact the server and print out its response. name := defaultName ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() r, err := c.Shutdown(ctx, &pb.ShutdownRequest{Name: name}) if err != nil { return err.Error() } return r.GetMessage() } func goSDNTestDB(conn *grpc.ClientConn) string { // TODO: fill with code and also see if grpc interface has this stub implemented. return "not implemented yet" } // TAPIGetEdge triggers the GetEdge function of the Ciena // flavoured TAPI client func TAPIGetEdge(conn *grpc.ClientConn) string { c := pb.NewGrpcCliClient(conn) // Contact the server and print out its response. name := defaultName ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() r, err := c.TAPIGetEdge(ctx, &pb.TAPIRequest{Name: name}) if err != nil { return err.Error() } return r.GetMessage() } // TAPIGetEdgeNode triggers the GetEdgeNode function of the Ciena // flavoured TAPI client func TAPIGetEdgeNode(conn *grpc.ClientConn) string { c := pb.NewGrpcCliClient(conn) // Contact the server and print out its response. name := defaultName ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() r, err := c.TAPIGetEdgeNode(ctx, &pb.TAPIRequest{Name: name}) if err != nil { return err.Error() } return r.GetMessage() } // TAPIGetLink triggers the GetLink function of the Ciena // flavoured TAPI client func TAPIGetLink(conn *grpc.ClientConn) string { c := pb.NewGrpcCliClient(conn) // Contact the server and print out its response. name := defaultName ctx, cancel := context.WithTimeout(context.Background(), time.Second) defer cancel() r, err := c.TAPIGetLink(ctx, &pb.TAPIRequest{Name: name}) if err != nil { return err.Error() } return r.GetMessage() }