diff --git a/grpc/oob/bmc/bmc.go b/grpc/oob/bmc/bmc.go index 385ef37..54c7381 100644 --- a/grpc/oob/bmc/bmc.go +++ b/grpc/oob/bmc/bmc.go @@ -22,10 +22,11 @@ import ( // Action for making bmc actions on BMCs, implements oob.User interface. type Action struct { common.Accessory - CreateUserRequest *v1.CreateUserRequest - DeleteUserRequest *v1.DeleteUserRequest - UpdateUserRequest *v1.UpdateUserRequest - ResetBMCRequest *v1.ResetRequest + CreateUserRequest *v1.CreateUserRequest + DeleteUserRequest *v1.DeleteUserRequest + UpdateUserRequest *v1.UpdateUserRequest + ResetBMCRequest *v1.ResetRequest + DeactivateSOLRequest *v1.DeactivateSOLRequest } // Option to add to an Actions. @@ -71,6 +72,14 @@ func WithUpdateUserRequest(in *v1.UpdateUserRequest) Option { } } +// WithDeactivateSOLRequest adds a DeactivateSOLRequest to the Action. +func WithDeactivateSOLRequest(in *v1.DeactivateSOLRequest) Option { + return func(a *Action) error { + a.DeactivateSOLRequest = in + return nil + } +} + // WithResetRequest adds ResetRequest to an Action struct. func WithResetRequest(in *v1.ResetRequest) Option { return func(a *Action) error { @@ -359,6 +368,60 @@ func (m Action) BMCReset(ctx context.Context, rType string) (err error) { return nil } +// DeactivateSOL deactivates a serial-over-LAN session on the device. +func (m Action) DeactivateSOL(ctx context.Context) error { + tracer := otel.Tracer("pbnj") + ctx, span := tracer.Start(ctx, "client.DeactivateSOL") + defer span.End() + + host, user, password, parseErr := m.ParseAuth(m.DeactivateSOLRequest.Authn) + if parseErr != nil { + return parseErr + } + span.SetAttributes(attribute.String("bmc.host", host), attribute.String("bmc.username", user)) + m.SendStatusMessage("working on SOL session deactivation") + + opts := []bmclib.Option{ + bmclib.WithLogger(m.Log), + bmclib.WithPerProviderTimeout(common.BMCTimeoutFromCtx(ctx)), + bmclib.WithIpmitoolPort("623"), + } + + client := bmclib.NewClient(host, user, password, opts...) + + if err := client.Open(ctx); err != nil { + span.SetStatus(codes.Error, "permission denied: "+err.Error()) + return &repository.Error{ + Code: v1.Code_value["PERMISSION_DENIED"], + Message: err.Error(), + } + } + + log := m.Log.WithValues("host", host, "user", user) + defer func() { + client.Close(ctx) + log.Info("closed connections", logMetadata(client.GetMetadata())...) + }() + log.Info("connected to BMC", logMetadata(client.GetMetadata())...) + m.SendStatusMessage("connected to BMC") + + err := client.DeactivateSOL(ctx) + log = m.Log.WithValues(logMetadata(client.GetMetadata())...) + if err != nil { + span.SetStatus(codes.Error, "failed to deactivate SOL session: "+err.Error()) + log.Error(err, "failed to deactivate SOL session") + m.SendStatusMessage("failed to deactivate SOL session") + return &repository.Error{ + Code: v1.Code_value["UNKNOWN"], + Message: err.Error(), + } + } + log.Info("SOL deactivation complete") + m.SendStatusMessage("SOL deactivation complete") + + return nil +} + func logMetadata(md bmc.Metadata) []interface{} { kvs := []interface{}{ "ProvidersAttempted", md.ProvidersAttempted, diff --git a/grpc/rpc/bmc.go b/grpc/rpc/bmc.go index 3d5fe62..ec91411 100644 --- a/grpc/rpc/bmc.go +++ b/grpc/rpc/bmc.go @@ -71,6 +71,38 @@ func (b *BmcService) Reset(ctx context.Context, in *v1.ResetRequest) (*v1.ResetR return &v1.ResetResponse{TaskId: taskID}, nil } +// DeactivateSOL deactivates any active SOL session on the BMC. +func (b *BmcService) DeactivateSOL(ctx context.Context, in *v1.DeactivateSOLRequest) (*v1.DeactivateSOLResponse, error) { + l := logging.ExtractLogr(ctx) + taskID := xid.New().String() + l = l.WithValues("taskID", taskID) + l.Info( + "start DeactivateSOL request", + "username", in.Authn.GetDirectAuthn().GetUsername(), + "vendor", in.Vendor.GetName(), + ) + + execFunc := func(s chan string) (string, error) { + t, err := bmc.NewBMCResetter( + bmc.WithDeactivateSOLRequest(in), + bmc.WithLogger(l), + bmc.WithStatusMessage(s), + ) + if err != nil { + return "", err + } + // Because this is a background task, we want to pass through the span context, but not be + // a child context. This allows us to correctly plumb otel into the background task. + c := trace.ContextWithSpanContext(context.Background(), trace.SpanContextFromContext(ctx)) + taskCtx, cancel := context.WithTimeout(c, b.Timeout) + defer cancel() + return "", t.DeactivateSOL(taskCtx) + } + b.TaskRunner.Execute(ctx, l, "deactivating SOL session", taskID, execFunc) + + return &v1.DeactivateSOLResponse{TaskId: taskID}, nil +} + // CreateUser sets the next boot device of a machine. func (b *BmcService) CreateUser(ctx context.Context, in *v1.CreateUserRequest) (*v1.CreateUserResponse, error) { l := logging.ExtractLogr(ctx)