From b3f3505adaca55ec9aeca1a1a6d6a0a2e584f3f9 Mon Sep 17 00:00:00 2001 From: Dmitrii Stepanov Date: Mon, 5 Feb 2024 16:33:09 +0300 Subject: [PATCH] [#947] cli: Allow to specify evacuation scope It may be required to evacuate only objects or only tree or all, so now it spossible to specify. Signed-off-by: Dmitrii Stepanov --- cmd/frostfs-cli/modules/control/evacuation.go | 23 ++++++++++++++ pkg/local_object_storage/engine/evacuate.go | 30 ++++++++++++++++++ pkg/services/control/server/evacuate.go | 1 + pkg/services/control/server/evacuate_async.go | 5 +++ pkg/services/control/service.pb.go | Bin 231700 -> 234031 bytes pkg/services/control/service.proto | 8 +++++ pkg/services/control/service_frostfs.pb.go | Bin 107641 -> 107736 bytes 7 files changed, 67 insertions(+) diff --git a/cmd/frostfs-cli/modules/control/evacuation.go b/cmd/frostfs-cli/modules/control/evacuation.go index 821dd1a6c..9d4cb64ac 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 d95098fbf..ad432e400 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 52ef083c4..6cba72d71 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 112d4449f..0b2851275 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 c9645941bfd63bc551c73279be3a9bfd67f8788b..9c1a8741561d3a4412aca26b8a4b7548b4c31c19 100644 GIT binary patch delta 8842 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 KZpX8tFZ~}#zU!a> delta 8670 zcmbVRd03R?+P~iWfq@xzlttD-aATcenE^M@)F>BF)Ra`fG&57NvV$#(Wyk!`peDIX zOYxM|r>R&Or!>L5PIXQinAt91mX?X2R;B`ezvp>pB)d-Eb*}Rd`fT_5yMNF9ytC^i z-)Elotu3H8p7EUG?pR~!?;eCsNFwYi0SZJ_4um{1ff@D%zUn%vN<_|{JHrF^593FrYc_10K=Zkgjc1Lq0GM?D%u!sF!rP^?umiF%1!Xtq&Q=p3aMRQYg1;uyodR__}i?CChNeRPTWY zh^8M88$&7FUQZTY=udH6UWf>{;DD5OppXira=UJ*QjRRe@J+Wv*g%3NKBTH!4onwW=mH*W+F*3Iq-I{FVOSwOxWJ`5DwfJ zcShX(c~c>RS2K%-d&%I;$Pyw>+&7a*)BY63{6vjEpV4GSu zl%?BT8q1|IsEFdZl+S*5VP8*yHO)Z?-^!y1&PxVD@~>((iG~voqwp<(6sT7^5|+=| zkr27LFECKrsb%r3O2mEsD6;Xl^8m%E2k0;SNz$m2g|UAUjPSH!?zg)D zryPl-`brdY$Gb>z*LyTdDpZ7E8Lc0Wm5}mt7NCnp0BZ@1;k{tR$_2nq{=GVKmn@}0 zoV^5SH%Fi@&&@%_v@JzFzgqxY)~-VFgQSM=q7NxZsycsOJP`1DgM{60C723isUpsh~d0OScTB}a^gQ=P2oc{N`f_j z+m_&e6(8st{%Csvn-j>}MY#6^Y~@!qqq{Xr!s4f8@VeoCB)DcYbZz%Tbf-KBxW+7{ zP+3>$c8M!n2CqC!Wn8=j>j;IJYXrP@h0vQ1KMXTUlaN8gu|NByBYB{KK(2a#lB(Ci z`0kGo3fK9_I&BJ-X_S5^2jzlrn#w>0#f!0R{5UW}+oP22OhXv2U52_{w;pRlo!o9Z-cta%VhNLhuMWe)JMiHfD0d_q)-?<+oH>wf$cHjC4qOg|Z7IdMtzFt5 zau+DgZ&;3exiUAV`YrvRaA%+g^R>gs^Ni0Co5gP-UdvZOg(~o)t5Bv* zkLggcK28O7RZr4>jYqC_0>fyPyds0fO{-Bb;H_wG%_MKl#`()pD8H>nuG*J_%5$Eg ze(Zk~mK8q^1g~*WFq}mFN}9lZ?Z>AwXdGv)LH*=kL!B2L0tTL3gDuHNbPH5rwj2UO z{PzU2y#iP26RQzFFNwlsYjJ%HS_^Eb+?1_Fmb#nB+g*B&bo618g$-NQ2r!J4hTDX0!eAFc6(DoT^|41$+`6`Qw~K{6aO z%9suKe+Zx;P@b`>lk$Q9Ht*yO&(bi-S5Z9f2o1hoDPcS$30eFi4_=@|gWcxHWNVUL zH$O}LomKLmkkyFw|20{mO_Xf*O9jCDb~R#>9|bQjRDcJ<8BOF4d2J4K0K77ivXAP` zV8{^mUxQ25mHG(Y^sGGl2Trzd!yptEIK435vKMnxG!#?eD69` z{!C4_)Zv`c3z8%g@WR!ouFEMf03DvmElnn8qJ;{a-N%7NVAdQfP#JX#Ac5A}o0qS| zAJrVkkHGbqSOe2$70ISTonBT0(pd2+_FOzFZ@?^=V=)2(F9}yv^S9g(RdX3^aM_;5 z-EdgKqO}IQKwL3S3VGv2)LoC|j)Oq?r5CZM3B@fBG%tZ0Hwc4TBk!ugT?2!XC298N z)?fs6MYk3u`>!CbB#Ll0GcUqY$5yca@0*#XZDhax@Gx_eKVku>> z(VCaxdFd^PYAIaMV_Eq!!WLYDD-V5Fl!_Do5)Is`9xnD3v@tSvAvzzH=rz?-S^U~7 zklCG&Xo6q*a?-2du0Om^nz}S!PA2%*_8G1RWIaq;6R;Ip%u|pY&()N{O%WJYB_U0E z(~Sz8UcE@ZH#WfSoJWC*{QCjHC#C{9D>7jhI&$^!h>3y;>X`*^Rg+L*hOFxClqMT8 zsq3j;h$NyL&xtWu>S>4WeofM;vmNs1v!8)e-#LarG>^iGl+Q6NFQ9>SNOM`wdnY_H zO}`%WK&Z~xNmQyoY}u*zk?;gWr1zFNyL8Z6XY-QXxFEf|HSms+EnQlSj7bzX>_H*j zT#HnwX9G4U2DGUL)u!5a%?L8+tVJw_xgq3H;Z9svfEPaNk=<^tCWD8Wlvq>UVI!r+6nL7)EZIj%4Anj8|t2dQO+G25pWE z_;|S<&bDZnVtxlZ^JicP8x1m^syqVv0fsx`X@C=f78|ac^0_!r9*??A?x&ai^YJhh z(iMmwdK)Iq|2y1xCZ3jXM=<0~d-FRVSUru_>pF4|pn!+Z$6W1Pn&k}mkpl?k zpS+DCCEDEijrV{eOD9^Lo#OxzkGs%p`Oktk&Gkq;zLI3{nHR}NE8$&PKv0pJV?X45 zoK;~sJWLNm$503Y(F_W(>8+z`U##loTK)mtD}Mq!nEe5~$nk>rO`pK2$3B1;YQfdM zD>88BSN{c|Hg{<$y!b=V{-0m$eL3!*5V=`?7D7KgNow%>H;r}?=kG;rKrUVblC<~E z#ATDW6t#%Qn?W{f=n>_Fv5WLR?*T8Z>jTGaRXsy@BY5}lpwbU%bj6*2PL!PtAA&6x|YRVd!VIq4RDY4GK$R$5gI&W zk~wD;qO$gg3q^1+o6pIw8+a;Se{d)?xHEr(q9{F##k#fhKL70~O_aTY@>Ba1PS#^qzMTYe!R7@KTEw_j_7(`b?Yq%#QS=Z;Bs8Kp})bxg+cGyHK8(yMZS8%$|! zdIe3_q5z5mgBsn_)%T>kKiBW3)VcC*vZ+VvN6tB$y3cHCU*Z?~KD&D8>DSLX%*k?! zsO$WKRuWkUan&U)>FFmu`OUA<#5=x$ZAIUpFz@-9!W?FA1Mg}B!09;M!u!tx9CsY2 zn|bVU3SHkQ4E*778p+#^V}AmP07r3@J0~<_%G3-P<}@Spm2$YS89RowfTaA+LE1Sv zcchs@9myF6p3s7kr3E`Sw!pfo7Iek#t(42>Fz(`Bu^By9KFUys$PQaUn*#@5REk;AXh2U-9(q!KJ zEtEAT3Qvwa3AX8$3-eE+m}i|t?^o^x7e6@(^ZtGkithdfEHdOf2nhO)VjQi1Gw@b9 zTK*kQ{g1B*b5vEibKv)w`r$k3r=nMm5iHsLD@8lXSGe;Ja`oW%WLC!~p28^M6pn`n z2(bETbawTpkb-AU$y@F;g5E7x$42X`H}U+_`htnCou=WGnuRajVj$1^0fL7AKyePs zF#~@pN85fto%@+Zh@-T@ok#wNsgNJ3zuK@HBUn`#B9MQnbkC0{uG&1|!N*U@#_UJg z+@HoRUv-AEHS~68Xf`*Vf&W!~5R-zRaNf>`JHaP!;{ROz6P^|RXDOG`vbg>%g8JJv zMC_5X2n1Ateqynk{EYndRsf>SF}3;;@^Q$M>{ZAVGzOmpd3!?a029H2jyy336N;c z_Jl_h&%@G$^G=k!ejdwdT=Oj)e!&RSBQIe2%6T0u27c%Q^v}8ga{v|}{#p(XUeFQa z!#7=|5wNNF8~M~&KvgBtc zcn}hN@cjjNIQlahi4UWMKmYX?STg1pe2>YF5+F`aNt>KZbOl zgA3Wef{dYm0@wZun}M}Rt}+WZzV#BUi@HR89mP8gT-*zuntw)t@@eYrXl*rBUjmn| zyM!}9UJ&umx@t2(_t-g#;VV5qWudcsL{d;zOeg!GhO$p)TE7<*92(&5lk6nR1 zFJD2luA3j;Nr8^mv+itZ)l`6kxwx5p$gVIPt!J~g9ag?|6<-r7uIiBX;D;~3)W}Z6 z`r=jSf3y=Xy&0YG{PRvKm1AeXxTd#Ie9(Tz9 zeQ7|0Q_>CdZtcc!S2sTLywr`G+Upt;d{xfHM+M5?T)sPzwt49&8Bx5)Pd$;*xD_zok&WgQv#1s@T{LkEcfIrKJWI*(@jZcBwG z5UUM(P9$QTspJZDtQ=mZQ!#c5_k~_53*Y1bR-WG!e(@; zuv+!G=Y&W!;&ZwbZ%yT=S`gV=!(g9(Zy>GN4IJz5CVZeS%1z|qQ`sUnG5xxb40#|} zma_-9xk2ATdBE&0CSTv;tn!WSBEg8C4wRluS^1&rLTGarc9-?2>~^#Q;|wC6(zCeC zAWES3D+AoLaEgbx!zDB|jmr%pTwNObJVYWVdq8c6hnRYOM~2Q4i#$bwmdYDEMG<6N z@e~D!*aV}v?fOPgvE++?8X;ypNQ2TWawb^bF9r@X$@Fo)N#ui1x0nzD3!gWMv0P(< z%28h8R!mfSiE%vC3lk^3#2ifIdLu|qZ__d*r{D|0uL#nWFeLnu1QB6O9=UkVG80nH z{yt(l*LdUf5+AV;r(1l)O#Cyw2oZ0RPg4b@bah2Z3NdiDpBRc$ z5BrH}M*N7T)@^dVzpgdXU&La4guj>$S2p@9RmK2OXhg>>cV}2QAV9>Zs)XO+yfZ-b zbHypc+OxPWR3wnij^9X>j7@=}kW&JMXLT=e7c|W8C1v7g8zq!CM~iqqBf#{<$s!1Z z;cdE(bGy6;2eDr;z~RDvCi@5B#NuH6L^xmTEj%ICGf2!dI%~*gVa+W1aYru^#~)v& zSVA1X2@mkA7FD_xWG`TO42M0lcGibJSd4tAtQ6N%L6e0$=;Ai8KGw1|!E+LqQ z0}*DsGmJKyJQ3Fu7@P`ok(V^>_E2cf;CDh5%EI~pWhj(B3cgK!0N+gR7bb4U&Xr*z z-(+{j2ESY4E)LZh>T;N{V}mtZOmcZ?w@C`=%R9qGn$bxCcDp|39U;)SboMgTdi8U*ajS^47)UTqH#aYqnpJ$^*F(xlZ1H2hL z%M8Qq++Y@qaljHIra{A+7`5dLMkf4nqH>#syLO=H!*lzJbOE3E`M!X?Geub$`r^8H zTz>;%MT#r_SvGy4ELIFQ;l~d@)OR6q&d=eUo!~ysNK!-T;-*9&`8B^^X&|lmm zTd@`W#S(aUzQ4H7XL0CJhZUk^ZL?L1_lC_4XL} z`9*>Vk$m3ABx}mKtSs~Hi+khoK_byh(t^I?wC~s;m3g0dRDCKhh!=Mtm5uQVODPE` zu~dE`0aV~bqt%+J`-OJ0HhUeAW1K7^BrsXvCCg;r bHC&U`a)ntyN+t*H5}h31)VlfK!RkiPw-9#$k^z%V#~PEI^9`2{K>@>)d{-NjAcDk`?!6n6zr+Z$ HfWlbfPnj4D