diff --git a/cmd/frostfs-cli/modules/control/evacuation.go b/cmd/frostfs-cli/modules/control/evacuation.go index 821dd1a6..9d4cb64a 100644 --- a/cmd/frostfs-cli/modules/control/evacuation.go +++ b/cmd/frostfs-cli/modules/control/evacuation.go @@ -19,6 +19,11 @@ import ( const ( awaitFlag = "await" noProgressFlag = "no-progress" + scopeFlag = "scope" + + scopeAll = "all" + scopeObjects = "objects" + scopeTrees = "trees" ) var evacuationShardCmd = &cobra.Command{ @@ -57,6 +62,7 @@ func startEvacuateShard(cmd *cobra.Command, _ []string) { Body: &control.StartShardEvacuationRequest_Body{ Shard_ID: getShardIDList(cmd), IgnoreErrors: ignoreErrors, + Scope: getEvacuationScope(cmd), }, } @@ -82,6 +88,22 @@ func startEvacuateShard(cmd *cobra.Command, _ []string) { } } +func getEvacuationScope(cmd *cobra.Command) uint32 { + rawScope, err := cmd.Flags().GetString(scopeFlag) + commonCmd.ExitOnErr(cmd, "Invalid scope value: %w", err) + switch rawScope { + case scopeAll: + return uint32(control.StartShardEvacuationRequest_Body_OBJECTS) | uint32(control.StartShardEvacuationRequest_Body_TREES) + case scopeObjects: + return uint32(control.StartShardEvacuationRequest_Body_OBJECTS) + case scopeTrees: + return uint32(control.StartShardEvacuationRequest_Body_TREES) + default: + commonCmd.ExitOnErr(cmd, "Invalid scope value: %w", fmt.Errorf("unknown scope %s", rawScope)) + } + return uint32(control.StartShardEvacuationRequest_Body_NONE) +} + func getEvacuateShardStatus(cmd *cobra.Command, _ []string) { pk := key.Get(cmd) req := &control.GetShardEvacuationStatusRequest{ @@ -309,6 +331,7 @@ func initControlStartEvacuationShardCmd() { flags.StringSlice(shardIDFlag, nil, "List of shard IDs in base58 encoding") flags.Bool(shardAllFlag, false, "Process all shards") flags.Bool(ignoreErrorsFlag, true, "Skip invalid/unreadable objects") + flags.String(scopeFlag, scopeAll, fmt.Sprintf("Evacuation scope; possible values: %s, %s, %s", scopeTrees, scopeObjects, scopeAll)) flags.Bool(awaitFlag, false, "Block execution until evacuation is completed") flags.Bool(noProgressFlag, false, fmt.Sprintf("Print progress if %s provided", awaitFlag)) diff --git a/pkg/local_object_storage/engine/evacuate.go b/pkg/local_object_storage/engine/evacuate.go index d95098fb..ad432e40 100644 --- a/pkg/local_object_storage/engine/evacuate.go +++ b/pkg/local_object_storage/engine/evacuate.go @@ -4,6 +4,7 @@ import ( "context" "errors" "fmt" + "strings" "sync/atomic" "git.frostfs.info/TrueCloudLab/frostfs-node/internal/logs" @@ -28,12 +29,40 @@ var ( evacuationOperationLogField = zap.String("operation", "evacuation") ) +// EvacuateScope is an evacuation scope. Keep in sync with pkg/services/control/service.proto. +type EvacuateScope uint32 + +var ( + EvacuateScopeObjects EvacuateScope = 1 + EvacuateScopeTrees EvacuateScope = 2 +) + +func (s EvacuateScope) String() string { + var sb strings.Builder + first := true + if s&EvacuateScopeObjects == EvacuateScopeObjects { + if !first { + sb.WriteString(";") + } + sb.WriteString("objects") + first = false + } + if s&EvacuateScopeTrees == EvacuateScopeTrees { + if !first { + sb.WriteString(";") + } + sb.WriteString("trees") + } + return sb.String() +} + // EvacuateShardPrm represents parameters for the EvacuateShard operation. type EvacuateShardPrm struct { ShardID []*shard.ID Handler func(context.Context, oid.Address, *objectSDK.Object) error IgnoreErrors bool Async bool + Scope EvacuateScope } // EvacuateShardRes represents result of the EvacuateShard operation. @@ -135,6 +164,7 @@ func (e *StorageEngine) Evacuate(ctx context.Context, prm EvacuateShardPrm) (*Ev attribute.StringSlice("shardIDs", shardIDs), attribute.Bool("async", prm.Async), attribute.Bool("ignoreErrors", prm.IgnoreErrors), + attribute.Stringer("scope", prm.Scope), )) defer span.End() diff --git a/pkg/services/control/server/evacuate.go b/pkg/services/control/server/evacuate.go index 52ef083c..6cba72d7 100644 --- a/pkg/services/control/server/evacuate.go +++ b/pkg/services/control/server/evacuate.go @@ -29,6 +29,7 @@ func (s *Server) EvacuateShard(ctx context.Context, req *control.EvacuateShardRe ShardID: s.getShardIDList(req.GetBody().GetShard_ID()), IgnoreErrors: req.GetBody().GetIgnoreErrors(), Handler: s.replicate, + Scope: engine.EvacuateScopeObjects, } res, err := s.s.Evacuate(ctx, prm) diff --git a/pkg/services/control/server/evacuate_async.go b/pkg/services/control/server/evacuate_async.go index 112d4449..0b285127 100644 --- a/pkg/services/control/server/evacuate_async.go +++ b/pkg/services/control/server/evacuate_async.go @@ -17,11 +17,16 @@ func (s *Server) StartShardEvacuation(ctx context.Context, req *control.StartSha return nil, status.Error(codes.PermissionDenied, err.Error()) } + if req.GetBody().GetScope() == uint32(control.StartShardEvacuationRequest_Body_NONE) { + return nil, status.Error(codes.InvalidArgument, "no evacuation scope") + } + prm := engine.EvacuateShardPrm{ ShardID: s.getShardIDList(req.GetBody().GetShard_ID()), IgnoreErrors: req.GetBody().GetIgnoreErrors(), Handler: s.replicate, Async: true, + Scope: engine.EvacuateScope(req.GetBody().GetScope()), } _, err = s.s.Evacuate(ctx, prm) diff --git a/pkg/services/control/service.pb.go b/pkg/services/control/service.pb.go index c9645941..9c1a8741 100644 Binary files a/pkg/services/control/service.pb.go and b/pkg/services/control/service.pb.go differ diff --git a/pkg/services/control/service.proto b/pkg/services/control/service.proto index 4cb1dee3..73e8ca90 100644 --- a/pkg/services/control/service.proto +++ b/pkg/services/control/service.proto @@ -336,10 +336,18 @@ message DoctorResponse { message StartShardEvacuationRequest { // Request body structure. message Body { + enum Scope { + NONE = 0; + OBJECTS = 1; + TREES = 2; + } + // IDs of the shards. repeated bytes shard_ID = 1; // Flag indicating whether object read errors should be ignored. bool ignore_errors = 2; + // Evacuation scope. + uint32 scope = 3; } Body body = 1; diff --git a/pkg/services/control/service_frostfs.pb.go b/pkg/services/control/service_frostfs.pb.go index 125a12d0..b18d30db 100644 Binary files a/pkg/services/control/service_frostfs.pb.go and b/pkg/services/control/service_frostfs.pb.go differ