]> git.proxmox.com Git - ceph.git/blame - ceph/src/rgw/rgw_admin.cc
bump version to 18.2.4-pve3
[ceph.git] / ceph / src / rgw / rgw_admin.cc
CommitLineData
7c673cae 1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
9f95a23c 2// vim: ts=8 sw=2 smarttab ft=cpp
7c673cae
FG
3
4#include <errno.h>
5#include <iostream>
6#include <sstream>
7#include <string>
8
aee94f69 9#include <boost/asio.hpp>
7c673cae
FG
10#include <boost/optional.hpp>
11
11fdf7f2
TL
12extern "C" {
13#include <liboath/oath.h>
14}
15
1e59de90
TL
16#include <fmt/format.h>
17
7c673cae 18#include "auth/Crypto.h"
3efd9988 19#include "compressor/Compressor.h"
7c673cae
FG
20
21#include "common/armor.h"
22#include "common/ceph_json.h"
23#include "common/config.h"
24#include "common/ceph_argparse.h"
25#include "common/Formatter.h"
26#include "common/errno.h"
27#include "common/safe_io.h"
1e59de90 28#include "common/fault_injector.h"
7c673cae 29
11fdf7f2
TL
30#include "include/util.h"
31
92f5a8d4 32#include "cls/rgw/cls_rgw_types.h"
7c673cae
FG
33#include "cls/rgw/cls_rgw_client.h"
34
7c673cae
FG
35#include "include/utime.h"
36#include "include/str_list.h"
37
38#include "rgw_user.h"
11fdf7f2 39#include "rgw_otp.h"
7c673cae
FG
40#include "rgw_rados.h"
41#include "rgw_acl.h"
42#include "rgw_acl_s3.h"
f67539c2 43#include "rgw_datalog.h"
7c673cae
FG
44#include "rgw_lc.h"
45#include "rgw_log.h"
46#include "rgw_formats.h"
47#include "rgw_usage.h"
7c673cae
FG
48#include "rgw_orphan.h"
49#include "rgw_sync.h"
9f95a23c
TL
50#include "rgw_trim_bilog.h"
51#include "rgw_trim_datalog.h"
52#include "rgw_trim_mdlog.h"
7c673cae
FG
53#include "rgw_data_sync.h"
54#include "rgw_rest_conn.h"
55#include "rgw_realm_watcher.h"
56#include "rgw_role.h"
31f18b77 57#include "rgw_reshard.h"
94b18763 58#include "rgw_http_client_curl.h"
11fdf7f2
TL
59#include "rgw_zone.h"
60#include "rgw_pubsub.h"
9f95a23c 61#include "rgw_bucket_sync.h"
f67539c2
TL
62#include "rgw_sync_checkpoint.h"
63#include "rgw_lua.h"
1e59de90
TL
64#include "rgw_sal.h"
65#include "rgw_sal_config.h"
7c673cae 66
11fdf7f2 67#include "services/svc_sync_modules.h"
9f95a23c
TL
68#include "services/svc_cls.h"
69#include "services/svc_bilog_rados.h"
9f95a23c
TL
70#include "services/svc_mdlog.h"
71#include "services/svc_meta_be_otp.h"
1e59de90 72#include "services/svc_user.h"
9f95a23c 73#include "services/svc_zone.h"
7c673cae 74
1e59de90
TL
75#include "driver/rados/rgw_bucket.h"
76#include "driver/rados/rgw_sal_rados.h"
77
7c673cae 78#define dout_context g_ceph_context
7c673cae
FG
79
80#define SECRET_KEY_LEN 40
81#define PUBLIC_ID_LEN 20
82
20effc67
TL
83using namespace std;
84
1e59de90
TL
85static rgw::sal::Driver* driver = NULL;
86static constexpr auto dout_subsys = ceph_subsys_rgw;
87
7c673cae 88
11fdf7f2
TL
89static const DoutPrefixProvider* dpp() {
90 struct GlobalPrefix : public DoutPrefixProvider {
b3b6e05e 91 CephContext *get_cct() const override { return dout_context; }
11fdf7f2
TL
92 unsigned get_subsys() const override { return dout_subsys; }
93 std::ostream& gen_prefix(std::ostream& out) const override { return out; }
94 };
95 static GlobalPrefix global_dpp;
96 return &global_dpp;
97}
98
9f95a23c
TL
99#define CHECK_TRUE(x, msg, err) \
100 do { \
101 if (!x) { \
102 cerr << msg << std::endl; \
103 return err; \
104 } \
105 } while (0)
106
107#define CHECK_SUCCESS(x, msg) \
108 do { \
109 int _x_val = (x); \
110 if (_x_val < 0) { \
111 cerr << msg << ": " << cpp_strerror(-_x_val) << std::endl; \
112 return _x_val; \
113 } \
114 } while (0)
115
1e59de90
TL
116static inline int posix_errortrans(int r)
117{
118 switch(r) {
119 case ERR_NO_SUCH_BUCKET:
120 r = ENOENT;
121 break;
122 default:
123 break;
124 }
125 return r;
126}
127
128
129static const std::string LUA_CONTEXT_LIST("prerequest, postrequest, background, getdata, putdata");
130
d2e6a577 131void usage()
7c673cae
FG
132{
133 cout << "usage: radosgw-admin <cmd> [options...]" << std::endl;
134 cout << "commands:\n";
135 cout << " user create create a new user\n" ;
136 cout << " user modify modify user\n";
137 cout << " user info get user info\n";
9f95a23c 138 cout << " user rename rename user\n";
7c673cae
FG
139 cout << " user rm remove user\n";
140 cout << " user suspend suspend a user\n";
141 cout << " user enable re-enable user after suspension\n";
142 cout << " user check check user info\n";
143 cout << " user stats show user stats as accounted by quota subsystem\n";
144 cout << " user list list users\n";
145 cout << " caps add add user capabilities\n";
146 cout << " caps rm remove user capabilities\n";
147 cout << " subuser create create a new subuser\n" ;
148 cout << " subuser modify modify subuser\n";
149 cout << " subuser rm remove subuser\n";
150 cout << " key create create access key\n";
151 cout << " key rm remove access key\n";
494da23a
TL
152 cout << " bucket list list buckets (specify --allow-unordered for\n";
153 cout << " faster, unsorted listing)\n";
7c673cae
FG
154 cout << " bucket limit check show bucket sharding stats\n";
155 cout << " bucket link link bucket to specified user\n";
156 cout << " bucket unlink unlink bucket from specified user\n";
157 cout << " bucket stats returns bucket statistics\n";
158 cout << " bucket rm remove bucket\n";
aee94f69
TL
159 cout << " bucket check check bucket index by verifying size and object count stats\n";
160 cout << " bucket check olh check for olh index entries and objects that are pending removal\n";
161 cout << " bucket check unlinked check for object versions that are not visible in a bucket listing \n";
9f95a23c 162 cout << " bucket chown link bucket to specified user and update its object ACLs\n";
7c673cae 163 cout << " bucket reshard reshard bucket\n";
11fdf7f2 164 cout << " bucket rewrite rewrite all objects in the specified bucket\n";
f67539c2 165 cout << " bucket sync checkpoint poll a bucket's sync status until it catches up to its remote\n";
c07f9fc5
FG
166 cout << " bucket sync disable disable bucket sync\n";
167 cout << " bucket sync enable enable bucket sync\n";
e306af50 168 cout << " bucket radoslist list rados objects backing bucket's objects\n";
7c673cae
FG
169 cout << " bi get retrieve bucket index object entries\n";
170 cout << " bi put store bucket index object entries\n";
171 cout << " bi list list raw bucket index entries\n";
11fdf7f2 172 cout << " bi purge purge bucket index entries\n";
7c673cae 173 cout << " object rm remove object\n";
11fdf7f2 174 cout << " object put put object\n";
7c673cae
FG
175 cout << " object stat stat an object for its metadata\n";
176 cout << " object unlink unlink object from bucket index\n";
11fdf7f2 177 cout << " object rewrite rewrite the specified object\n";
1e59de90 178 cout << " object reindex reindex the object(s) indicated by --bucket and either --object or --objects-file\n";
7c673cae 179 cout << " objects expire run expired objects cleanup\n";
81eedcae
TL
180 cout << " objects expire-stale list list stale expired objects (caused by reshard)\n";
181 cout << " objects expire-stale rm remove stale expired objects\n";
11fdf7f2 182 cout << " period rm remove a period\n";
7c673cae
FG
183 cout << " period get get period info\n";
184 cout << " period get-current get current period info\n";
185 cout << " period pull pull a period\n";
186 cout << " period push push a period\n";
187 cout << " period list list all periods\n";
188 cout << " period update update the staging period\n";
189 cout << " period commit commit the staging period\n";
190 cout << " quota set set quota params\n";
191 cout << " quota enable enable quota\n";
192 cout << " quota disable disable quota\n";
20effc67
TL
193 cout << " ratelimit get get ratelimit params\n";
194 cout << " ratelimit set set ratelimit params\n";
195 cout << " ratelimit enable enable ratelimit\n";
196 cout << " ratelimit disable disable ratelimit\n";
7c673cae
FG
197 cout << " global quota get view global quota params\n";
198 cout << " global quota set set global quota params\n";
199 cout << " global quota enable enable a global quota\n";
200 cout << " global quota disable disable a global quota\n";
20effc67
TL
201 cout << " global ratelimit get view global ratelimit params\n";
202 cout << " global ratelimit set set global ratelimit params\n";
203 cout << " global ratelimit enable enable a ratelimit quota\n";
204 cout << " global ratelimit disable disable a ratelimit quota\n";
7c673cae 205 cout << " realm create create a new realm\n";
11fdf7f2 206 cout << " realm rm remove a realm\n";
7c673cae
FG
207 cout << " realm get show realm info\n";
208 cout << " realm get-default get default realm name\n";
209 cout << " realm list list realms\n";
210 cout << " realm list-periods list all realm periods\n";
7c673cae
FG
211 cout << " realm rename rename a realm\n";
212 cout << " realm set set realm info (requires infile)\n";
213 cout << " realm default set realm as default\n";
214 cout << " realm pull pull a realm and its current period\n";
215 cout << " zonegroup add add a zone to a zonegroup\n";
216 cout << " zonegroup create create a new zone group info\n";
217 cout << " zonegroup default set default zone group\n";
9f95a23c 218 cout << " zonegroup delete delete a zone group info\n";
7c673cae
FG
219 cout << " zonegroup get show zone group info\n";
220 cout << " zonegroup modify modify an existing zonegroup\n";
221 cout << " zonegroup set set zone group info (requires infile)\n";
11fdf7f2 222 cout << " zonegroup rm remove a zone from a zonegroup\n";
7c673cae
FG
223 cout << " zonegroup rename rename a zone group\n";
224 cout << " zonegroup list list all zone groups set on this cluster\n";
225 cout << " zonegroup placement list list zonegroup's placement targets\n";
92f5a8d4 226 cout << " zonegroup placement get get a placement target of a specific zonegroup\n";
7c673cae
FG
227 cout << " zonegroup placement add add a placement target id to a zonegroup\n";
228 cout << " zonegroup placement modify modify a placement target of a specific zonegroup\n";
229 cout << " zonegroup placement rm remove a placement target from a zonegroup\n";
230 cout << " zonegroup placement default set a zonegroup's default placement target\n";
231 cout << " zone create create a new zone\n";
11fdf7f2 232 cout << " zone rm remove a zone\n";
7c673cae
FG
233 cout << " zone get show zone cluster params\n";
234 cout << " zone modify modify an existing zone\n";
235 cout << " zone set set zone cluster params (requires infile)\n";
236 cout << " zone list list all zones set on this cluster\n";
237 cout << " zone rename rename a zone\n";
238 cout << " zone placement list list zone's placement targets\n";
92f5a8d4 239 cout << " zone placement get get a zone placement target\n";
7c673cae
FG
240 cout << " zone placement add add a zone placement target\n";
241 cout << " zone placement modify modify a zone placement target\n";
242 cout << " zone placement rm remove a zone placement target\n";
11fdf7f2
TL
243 cout << " metadata sync status get metadata sync status\n";
244 cout << " metadata sync init init metadata sync\n";
245 cout << " metadata sync run run metadata sync\n";
246 cout << " data sync status get data sync status of the specified source zone\n";
247 cout << " data sync init init data sync for the specified source zone\n";
248 cout << " data sync run run data sync for the specified source zone\n";
7c673cae
FG
249 cout << " pool add add an existing pool for data placement\n";
250 cout << " pool rm remove an existing pool from data placement set\n";
251 cout << " pools list list placement active set\n";
252 cout << " policy read bucket/object policy\n";
253 cout << " log list list log objects\n";
254 cout << " log show dump a log from specific object or (bucket + date\n";
255 cout << " + bucket-id)\n";
256 cout << " (NOTE: required to specify formatting of date\n";
257 cout << " to \"YYYY-MM-DD-hh\")\n";
258 cout << " log rm remove log object\n";
11fdf7f2
TL
259 cout << " usage show show usage (by user, by bucket, date range)\n";
260 cout << " usage trim trim usage (by user, by bucket, date range)\n";
261 cout << " usage clear reset all the usage stats for the cluster\n";
7c673cae
FG
262 cout << " gc list dump expired garbage collection objects (specify\n";
263 cout << " --include-all to list all entries, including unexpired)\n";
11fdf7f2
TL
264 cout << " gc process manually process garbage (specify\n";
265 cout << " --include-all to process all entries, including unexpired)\n";
7c673cae 266 cout << " lc list list all bucket lifecycle progress\n";
11fdf7f2 267 cout << " lc get get a lifecycle bucket configuration\n";
7c673cae 268 cout << " lc process manually process lifecycle\n";
11fdf7f2 269 cout << " lc reshard fix fix LC for a resharded bucket\n";
7c673cae
FG
270 cout << " metadata get get metadata info\n";
271 cout << " metadata put put metadata info\n";
272 cout << " metadata rm remove metadata info\n";
273 cout << " metadata list list metadata info\n";
274 cout << " mdlog list list metadata log\n";
20effc67 275 cout << " mdlog autotrim auto trim metadata log\n";
f67539c2 276 cout << " mdlog trim trim metadata log (use marker)\n";
7c673cae
FG
277 cout << " mdlog status read metadata log status\n";
278 cout << " bilog list list bucket index log\n";
279 cout << " bilog trim trim bucket index log (use start-marker, end-marker)\n";
9f95a23c 280 cout << " bilog status read bucket index log status\n";
20effc67 281 cout << " bilog autotrim auto trim bucket index log\n";
7c673cae
FG
282 cout << " datalog list list data log\n";
283 cout << " datalog trim trim data log\n";
284 cout << " datalog status read data log status\n";
f67539c2 285 cout << " datalog type change datalog type to --log_type={fifo,omap}\n";
e306af50
TL
286 cout << " orphans find deprecated -- init and run search for leaked rados objects (use job-id, pool)\n";
287 cout << " orphans finish deprecated -- clean up search for leaked rados objects\n";
288 cout << " orphans list-jobs deprecated -- list the current job-ids for orphans search\n";
289 cout << " * the three 'orphans' sub-commands are now deprecated; consider using the `rgw-orphan-list` tool\n";
7c673cae 290 cout << " role create create a AWS role for use with STS\n";
20effc67 291 cout << " role delete remove a role\n";
7c673cae
FG
292 cout << " role get get a role\n";
293 cout << " role list list roles with specified path prefix\n";
1e59de90 294 cout << " role-trust-policy modify modify the assume role policy of an existing role\n";
7c673cae
FG
295 cout << " role-policy put add/update permission policy to role\n";
296 cout << " role-policy list list policies attached to a role\n";
297 cout << " role-policy get get the specified inline policy document embedded with the given role\n";
20effc67 298 cout << " role-policy delete remove policy attached to a role\n";
1e59de90 299 cout << " role update update max_session_duration of a role\n";
31f18b77 300 cout << " reshard add schedule a resharding of a bucket\n";
11fdf7f2
TL
301 cout << " reshard list list all bucket resharding or scheduled to be resharded\n";
302 cout << " reshard status read bucket resharding status\n";
31f18b77
FG
303 cout << " reshard process process of scheduled reshard jobs\n";
304 cout << " reshard cancel cancel resharding a bucket\n";
f64942e4 305 cout << " reshard stale-instances list list stale-instances from bucket resharding\n";
1e59de90 306 cout << " reshard stale-instances delete cleanup stale-instances from bucket resharding\n";
94b18763
FG
307 cout << " sync error list list sync error\n";
308 cout << " sync error trim trim sync error\n";
11fdf7f2
TL
309 cout << " mfa create create a new MFA TOTP token\n";
310 cout << " mfa list list MFA TOTP tokens\n";
311 cout << " mfa get show MFA TOTP token\n";
312 cout << " mfa remove delete MFA TOTP token\n";
313 cout << " mfa check check MFA TOTP token\n";
314 cout << " mfa resync re-sync MFA TOTP token\n";
1e59de90
TL
315 cout << " topic list list bucket notifications topics\n";
316 cout << " topic get get a bucket notifications topic\n";
317 cout << " topic rm remove a bucket notifications topic\n";
f67539c2
TL
318 cout << " script put upload a lua script to a context\n";
319 cout << " script get get the lua script of a context\n";
320 cout << " script rm remove the lua scripts of a context\n";
321 cout << " script-package add add a lua package to the scripts allowlist\n";
322 cout << " script-package rm remove a lua package from the scripts allowlist\n";
323 cout << " script-package list get the lua packages allowlist\n";
aee94f69
TL
324 cout << " notification list list bucket notifications configuration\n";
325 cout << " notification get get a bucket notifications configuration\n";
326 cout << " notification rm remove a bucket notifications configuration\n";
7c673cae
FG
327 cout << "options:\n";
328 cout << " --tenant=<tenant> tenant name\n";
f67539c2 329 cout << " --user_ns=<namespace> namespace of user (oidc in case of users authenticated with oidc provider)\n";
7c673cae 330 cout << " --uid=<id> user id\n";
9f95a23c 331 cout << " --new-uid=<id> new user id\n";
7c673cae
FG
332 cout << " --subuser=<name> subuser name\n";
333 cout << " --access-key=<key> S3 access key\n";
11fdf7f2 334 cout << " --email=<email> user's email address\n";
7c673cae
FG
335 cout << " --secret/--secret-key=<key>\n";
336 cout << " specify secret key\n";
337 cout << " --gen-access-key generate random access key (for S3)\n";
338 cout << " --gen-secret generate random secret key\n";
339 cout << " --key-type=<type> key type, options are: swift, s3\n";
340 cout << " --temp-url-key[-2]=<key> temp url key\n";
341 cout << " --access=<access> Set access permissions for sub-user, should be one\n";
342 cout << " of read, write, readwrite, full\n";
11fdf7f2 343 cout << " --display-name=<name> user's display name\n";
c07f9fc5 344 cout << " --max-buckets max number of buckets for a user\n";
7c673cae
FG
345 cout << " --admin set the admin flag on the user\n";
346 cout << " --system set the system flag on the user\n";
11fdf7f2 347 cout << " --op-mask set the op mask on the user\n";
28e407b8
AA
348 cout << " --bucket=<bucket> Specify the bucket name. Also used by the quota command.\n";
349 cout << " --pool=<pool> Specify the pool name. Also used to scan for leaked rados objects.\n";
350 cout << " --object=<object> object name\n";
1e59de90 351 cout << " --objects-file=<file> file containing a list of object names to process\n";
9f95a23c 352 cout << " --object-version=<version> object version\n";
28e407b8
AA
353 cout << " --date=<date> date in the format yyyy-mm-dd\n";
354 cout << " --start-date=<date> start date in the format yyyy-mm-dd\n";
355 cout << " --end-date=<date> end date in the format yyyy-mm-dd\n";
356 cout << " --bucket-id=<bucket-id> bucket id\n";
9f95a23c
TL
357 cout << " --bucket-new-name=<bucket>\n";
358 cout << " for bucket link: optional new name\n";
28e407b8
AA
359 cout << " --shard-id=<shard-id> optional for: \n";
360 cout << " mdlog list\n";
361 cout << " data sync status\n";
7c673cae
FG
362 cout << " required for: \n";
363 cout << " mdlog trim\n";
1e59de90
TL
364 cout << " --gen=<gen-id> optional for: \n";
365 cout << " bilog list\n";
366 cout << " bilog trim\n";
367 cout << " bilog status\n";
28e407b8 368 cout << " --max-entries=<entries> max entries for listing operations\n";
7c673cae
FG
369 cout << " --metadata-key=<key> key to retrieve metadata from with metadata get\n";
370 cout << " --remote=<remote> zone or zonegroup id of remote gateway\n";
371 cout << " --period=<id> period id\n";
11fdf7f2 372 cout << " --url=<url> url for pushing/pulling period/realm\n";
7c673cae
FG
373 cout << " --epoch=<number> period epoch\n";
374 cout << " --commit commit the period during 'period update'\n";
375 cout << " --staging get staging period info\n";
376 cout << " --master set as master\n";
7c673cae
FG
377 cout << " --master-zone=<id> master zone id\n";
378 cout << " --rgw-realm=<name> realm name\n";
379 cout << " --realm-id=<id> realm id\n";
380 cout << " --realm-new-name=<name> realm new name\n";
381 cout << " --rgw-zonegroup=<name> zonegroup name\n";
382 cout << " --zonegroup-id=<id> zonegroup id\n";
383 cout << " --zonegroup-new-name=<name>\n";
384 cout << " zonegroup new name\n";
385 cout << " --rgw-zone=<name> name of zone in which radosgw is running\n";
386 cout << " --zone-id=<id> zone id\n";
387 cout << " --zone-new-name=<name> zone new name\n";
388 cout << " --source-zone specify the source zone (for data sync)\n";
389 cout << " --default set entity (realm, zonegroup, zone) as default\n";
390 cout << " --read-only set zone as read-only (when adding to zonegroup)\n";
11fdf7f2 391 cout << " --redirect-zone specify zone id to redirect when response is 404 (not found)\n";
7c673cae 392 cout << " --placement-id placement id for zonegroup placement commands\n";
11fdf7f2 393 cout << " --storage-class storage class for zonegroup placement commands\n";
7c673cae
FG
394 cout << " --tags=<list> list of tags for zonegroup placement add and modify commands\n";
395 cout << " --tags-add=<list> list of tags to add for zonegroup placement modify command\n";
396 cout << " --tags-rm=<list> list of tags to remove for zonegroup placement modify command\n";
397 cout << " --endpoints=<list> zone endpoints\n";
c07f9fc5 398 cout << " --index-pool=<pool> placement target index pool\n";
224ce89b
WB
399 cout << " --data-pool=<pool> placement target data pool\n";
400 cout << " --data-extra-pool=<pool> placement target data extra (non-ec) pool\n";
7c673cae
FG
401 cout << " --placement-index-type=<type>\n";
402 cout << " placement target index type (normal, indexless, or #id)\n";
39ae355f
TL
403 cout << " --placement-inline-data=<true>\n";
404 cout << " set whether the placement target is configured to store a data\n";
405 cout << " chunk inline in head objects\n";
7c673cae
FG
406 cout << " --compression=<type> placement target compression type (plugin name or empty/none)\n";
407 cout << " --tier-type=<type> zone tier type\n";
408 cout << " --tier-config=<k>=<v>[,...]\n";
409 cout << " set zone tier config keys, values\n";
410 cout << " --tier-config-rm=<k>[,...]\n";
411 cout << " unset zone tier config keys\n";
412 cout << " --sync-from-all[=false] set/reset whether zone syncs from all zonegroup peers\n";
413 cout << " --sync-from=[zone-name][,...]\n";
414 cout << " set list of zones to sync from\n";
415 cout << " --sync-from-rm=[zone-name][,...]\n";
416 cout << " remove zones from list of zones to sync from\n";
9f95a23c 417 cout << " --bucket-index-max-shards override a zone/zonegroup's default bucket index shard count\n";
7c673cae
FG
418 cout << " --fix besides checking bucket index, will also fix it\n";
419 cout << " --check-objects bucket check: rebuilds bucket index according to\n";
420 cout << " actual objects state\n";
421 cout << " --format=<format> specify output format for certain operations: xml,\n";
422 cout << " json\n";
423 cout << " --purge-data when specified, user removal will also purge all the\n";
424 cout << " user data\n";
425 cout << " --purge-keys when specified, subuser removal will also purge all the\n";
426 cout << " subuser keys\n";
427 cout << " --purge-objects remove a bucket's objects before deleting it\n";
428 cout << " (NOTE: required to delete a non-empty bucket)\n";
429 cout << " --sync-stats option to 'user stats', update user stats with current\n";
430 cout << " stats reported by user's buckets indexes\n";
94b18763 431 cout << " --reset-stats option to 'user stats', reset stats in accordance with user buckets\n";
20effc67 432 cout << " --show-config show configuration\n";
7c673cae
FG
433 cout << " --show-log-entries=<flag> enable/disable dump of log entries on log show\n";
434 cout << " --show-log-sum=<flag> enable/disable dump of log summation on log show\n";
435 cout << " --skip-zero-entries log show only dumps entries that don't have zero value\n";
436 cout << " in one of the numeric field\n";
1e59de90 437 cout << " --infile=<file> file to read in when setting data\n";
7c673cae
FG
438 cout << " --categories=<list> comma separated list of categories, used in usage show\n";
439 cout << " --caps=<caps> list of caps (e.g., \"usage=read, write; user=read\")\n";
9f95a23c 440 cout << " --op-mask=<op-mask> permission of user's operations (e.g., \"read, write, delete, *\")\n";
7c673cae
FG
441 cout << " --yes-i-really-mean-it required for certain operations\n";
442 cout << " --warnings-only when specified with bucket limit check, list\n";
443 cout << " only buckets nearing or over the current max\n";
444 cout << " objects per shard value\n";
445 cout << " --bypass-gc when specified with bucket deletion, triggers\n";
446 cout << " object deletions by not involving GC\n";
447 cout << " --inconsistent-index when specified with bucket deletion and bypass-gc set to true,\n";
448 cout << " ignores bucket index consistency\n";
11fdf7f2
TL
449 cout << " --min-rewrite-size min object size for bucket rewrite (default 4M)\n";
450 cout << " --max-rewrite-size max object size for bucket rewrite (default ULLONG_MAX)\n";
451 cout << " --min-rewrite-stripe-size min stripe size for object rewrite (default 0)\n";
452 cout << " --trim-delay-ms time interval in msec to limit the frequency of sync error log entries trimming operations,\n";
453 cout << " the trimming process will sleep the specified msec for every 1000 entries trimmed\n";
9f95a23c 454 cout << " --max-concurrent-ios maximum concurrent ios for bucket operations (default: 32)\n";
1e59de90
TL
455 cout << " --enable-feature enable a zone/zonegroup feature\n";
456 cout << " --disable-feature disable a zone/zonegroup feature\n";
7c673cae
FG
457 cout << "\n";
458 cout << "<date> := \"YYYY-MM-DD[ hh:mm:ss]\"\n";
459 cout << "\nQuota options:\n";
7c673cae
FG
460 cout << " --max-objects specify max objects (negative value to disable)\n";
461 cout << " --max-size specify max size (in B/K/M/G/T, negative value to disable)\n";
462 cout << " --quota-scope scope of quota (bucket, user)\n";
20effc67
TL
463 cout << "\nRate limiting options:\n";
464 cout << " --max-read-ops specify max requests per minute for READ ops per RGW (GET and HEAD request methods), 0 means unlimited\n";
465 cout << " --max-read-bytes specify max bytes per minute for READ ops per RGW (GET and HEAD request methods), 0 means unlimited\n";
466 cout << " --max-write-ops specify max requests per minute for WRITE ops per RGW (Not GET or HEAD request methods), 0 means unlimited\n";
467 cout << " --max-write-bytes specify max bytes per minute for WRITE ops per RGW (Not GET or HEAD request methods), 0 means unlimited\n";
468 cout << " --ratelimit-scope scope of rate limiting: bucket, user, anonymous\n";
469 cout << " anonymous can be configured only with global rate limit\n";
7c673cae 470 cout << "\nOrphans search options:\n";
7c673cae
FG
471 cout << " --num-shards num of shards to use for keeping the temporary scan info\n";
472 cout << " --orphan-stale-secs num of seconds to wait before declaring an object to be an orphan (default: 86400)\n";
473 cout << " --job-id set the job id (for orphans find)\n";
11fdf7f2 474 cout << " --detail detailed mode, log and stat head objects as well\n";
7c673cae
FG
475 cout << "\nOrphans list-jobs options:\n";
476 cout << " --extra-info provide extra info in job list\n";
477 cout << "\nRole options:\n";
478 cout << " --role-name name of the role to create\n";
479 cout << " --path path to the role\n";
480 cout << " --assume-role-policy-doc the trust relationship policy document that grants an entity permission to assume the role\n";
481 cout << " --policy-name name of the policy document\n";
482 cout << " --policy-doc permission policy document\n";
483 cout << " --path-prefix path prefix for filtering roles\n";
11fdf7f2
TL
484 cout << "\nMFA options:\n";
485 cout << " --totp-serial a string that represents the ID of a TOTP token\n";
486 cout << " --totp-seed the secret seed that is used to calculate the TOTP\n";
487 cout << " --totp-seconds the time resolution that is being used for TOTP generation\n";
488 cout << " --totp-window the number of TOTP tokens that are checked before and after the current token when validating token\n";
489 cout << " --totp-pin the valid value of a TOTP token at a certain time\n";
1e59de90
TL
490 cout << "\nBucket notifications options:\n";
491 cout << " --topic bucket notifications topic name\n";
aee94f69 492 cout << " --notification-id bucket notifications id\n";
f67539c2 493 cout << "\nScript options:\n";
1e59de90 494 cout << " --context context in which the script runs. one of: "+LUA_CONTEXT_LIST+"\n";
f67539c2
TL
495 cout << " --package name of the lua package that should be added/removed to/from the allowlist\n";
496 cout << " --allow-compilation package is allowed to compile C code as part of its installation\n";
aee94f69
TL
497 cout << "\nBucket check olh/unlinked options:\n";
498 cout << " --min-age-hours minimum age of unlinked objects to consider for bucket check unlinked (default: 1)\n";
499 cout << " --dump-keys when specified, all keys identified as problematic are printed to stdout\n";
500 cout << " --hide-progress when specified, per-shard progress details are not printed to stderr\n";
f67539c2
TL
501 cout << "\nradoslist options:\n";
502 cout << " --rgw-obj-fs the field separator that will separate the rados\n";
503 cout << " object name from the rgw object name;\n";
504 cout << " additionally rados objects for incomplete\n";
505 cout << " multipart uploads will not be output\n";
7c673cae
FG
506 cout << "\n";
507 generic_client_usage();
508}
509
7c673cae 510
9f95a23c
TL
511class SimpleCmd {
512public:
513 struct Def {
514 string cmd;
515 std::any opt;
516 };
517
518 using Aliases = std::vector<std::set<string> >;
519 using Commands = std::vector<Def>;
520
521private:
522 struct Node {
523 map<string, Node> next;
524 set<string> expected; /* separate un-normalized list */
525 std::any opt;
526 };
527
528 Node cmd_root;
529 map<string, string> alias_map;
530
531 string normalize_alias(const string& s) const {
532 auto iter = alias_map.find(s);
533 if (iter == alias_map.end()) {
534 return s;
535 }
536
537 return iter->second;
31f18b77 538 }
9f95a23c
TL
539 void init_alias_map(Aliases& aliases) {
540 for (auto& alias_set : aliases) {
541 std::optional<string> first;
31f18b77 542
9f95a23c
TL
543 for (auto& alias : alias_set) {
544 if (!first) {
545 first = alias;
546 } else {
547 alias_map[alias] = *first;
548 }
549 }
550 }
31f18b77 551 }
9f95a23c
TL
552
553 bool gen_next_expected(Node *node, vector<string> *expected, bool ret) {
554 for (auto& next_cmd : node->expected) {
555 expected->push_back(next_cmd);
556 }
557 return ret;
7c673cae
FG
558 }
559
9f95a23c 560 Node root;
7c673cae 561
9f95a23c
TL
562public:
563 SimpleCmd() {}
7c673cae 564
9f95a23c
TL
565 SimpleCmd(std::optional<Commands> cmds,
566 std::optional<Aliases> aliases) {
567 if (aliases) {
568 add_aliases(*aliases);
7c673cae 569 }
9f95a23c
TL
570
571 if (cmds) {
572 add_commands(*cmds);
31f18b77 573 }
9f95a23c
TL
574 }
575
576 void add_aliases(Aliases& aliases) {
577 init_alias_map(aliases);
578 }
579
580 void add_commands(std::vector<Def>& cmds) {
581 for (auto& cmd : cmds) {
582 vector<string> words;
583 get_str_vec(cmd.cmd, " ", words);
584
585 auto node = &cmd_root;
586 for (auto& word : words) {
587 auto norm = normalize_alias(word);
588 auto parent = node;
589
590 node->expected.insert(word);
591
592 node = &node->next[norm];
593
594 if (norm == "[*]") { /* optional param at the end */
595 parent->next["*"] = *node; /* can be also looked up by '*' */
596 parent->opt = cmd.opt;
597 }
598 }
599
600 node->opt = cmd.opt;
7c673cae 601 }
9f95a23c
TL
602 }
603
604 template <class Container>
605 bool find_command(Container& args,
606 std::any *opt_cmd,
607 vector<string> *extra_args,
608 string *error,
609 vector<string> *expected) {
610 auto node = &cmd_root;
611
612 std::optional<std::any> found_opt;
613
614 for (auto& arg : args) {
615 string norm = normalize_alias(arg);
616 auto iter = node->next.find(norm);
617 if (iter == node->next.end()) {
618 iter = node->next.find("*");
619 if (iter == node->next.end()) {
620 *error = string("ERROR: Unrecognized argument: '") + arg + "'";
621 return gen_next_expected(node, expected, false);
622 }
623 extra_args->push_back(arg);
624 if (!found_opt) {
625 found_opt = node->opt;
626 }
627 }
628 node = &(iter->second);
629 }
630
631 *opt_cmd = found_opt.value_or(node->opt);
632
633 if (!opt_cmd->has_value()) {
634 *error ="ERROR: Unknown command";
635 return gen_next_expected(node, expected, false);
7c673cae 636 }
9f95a23c
TL
637
638 return true;
639 }
640};
641
642
643namespace rgw_admin {
644
645enum class OPT {
646 NO_CMD,
647 USER_CREATE,
648 USER_INFO,
649 USER_MODIFY,
650 USER_RENAME,
651 USER_RM,
652 USER_SUSPEND,
653 USER_ENABLE,
654 USER_CHECK,
655 USER_STATS,
656 USER_LIST,
657 SUBUSER_CREATE,
658 SUBUSER_MODIFY,
659 SUBUSER_RM,
660 KEY_CREATE,
661 KEY_RM,
662 BUCKETS_LIST,
663 BUCKET_LIMIT_CHECK,
664 BUCKET_LINK,
665 BUCKET_UNLINK,
1e59de90 666 BUCKET_LAYOUT,
9f95a23c
TL
667 BUCKET_STATS,
668 BUCKET_CHECK,
aee94f69
TL
669 BUCKET_CHECK_OLH,
670 BUCKET_CHECK_UNLINKED,
f67539c2 671 BUCKET_SYNC_CHECKPOINT,
9f95a23c
TL
672 BUCKET_SYNC_INFO,
673 BUCKET_SYNC_STATUS,
674 BUCKET_SYNC_MARKERS,
675 BUCKET_SYNC_INIT,
676 BUCKET_SYNC_RUN,
677 BUCKET_SYNC_DISABLE,
678 BUCKET_SYNC_ENABLE,
679 BUCKET_RM,
680 BUCKET_REWRITE,
681 BUCKET_RESHARD,
682 BUCKET_CHOWN,
e306af50 683 BUCKET_RADOS_LIST,
1e59de90
TL
684 BUCKET_SHARD_OBJECTS,
685 BUCKET_OBJECT_SHARD,
aee94f69 686 BUCKET_RESYNC_ENCRYPTED_MULTIPART,
9f95a23c
TL
687 POLICY,
688 POOL_ADD,
689 POOL_RM,
690 POOLS_LIST,
691 LOG_LIST,
692 LOG_SHOW,
693 LOG_RM,
694 USAGE_SHOW,
695 USAGE_TRIM,
696 USAGE_CLEAR,
697 OBJECT_PUT,
698 OBJECT_RM,
699 OBJECT_UNLINK,
700 OBJECT_STAT,
701 OBJECT_REWRITE,
1e59de90 702 OBJECT_REINDEX,
9f95a23c
TL
703 OBJECTS_EXPIRE,
704 OBJECTS_EXPIRE_STALE_LIST,
705 OBJECTS_EXPIRE_STALE_RM,
706 BI_GET,
707 BI_PUT,
708 BI_LIST,
709 BI_PURGE,
710 OLH_GET,
711 OLH_READLOG,
712 QUOTA_SET,
713 QUOTA_ENABLE,
714 QUOTA_DISABLE,
715 GC_LIST,
716 GC_PROCESS,
717 LC_LIST,
718 LC_GET,
719 LC_PROCESS,
720 LC_RESHARD_FIX,
721 ORPHANS_FIND,
722 ORPHANS_FINISH,
723 ORPHANS_LIST_JOBS,
20effc67
TL
724 RATELIMIT_GET,
725 RATELIMIT_SET,
726 RATELIMIT_ENABLE,
727 RATELIMIT_DISABLE,
9f95a23c
TL
728 ZONEGROUP_ADD,
729 ZONEGROUP_CREATE,
730 ZONEGROUP_DEFAULT,
731 ZONEGROUP_DELETE,
732 ZONEGROUP_GET,
733 ZONEGROUP_MODIFY,
734 ZONEGROUP_SET,
735 ZONEGROUP_LIST,
736 ZONEGROUP_REMOVE,
737 ZONEGROUP_RENAME,
738 ZONEGROUP_PLACEMENT_ADD,
739 ZONEGROUP_PLACEMENT_MODIFY,
740 ZONEGROUP_PLACEMENT_RM,
741 ZONEGROUP_PLACEMENT_LIST,
742 ZONEGROUP_PLACEMENT_GET,
743 ZONEGROUP_PLACEMENT_DEFAULT,
744 ZONE_CREATE,
745 ZONE_DELETE,
746 ZONE_GET,
747 ZONE_MODIFY,
748 ZONE_SET,
749 ZONE_LIST,
750 ZONE_RENAME,
751 ZONE_DEFAULT,
752 ZONE_PLACEMENT_ADD,
753 ZONE_PLACEMENT_MODIFY,
754 ZONE_PLACEMENT_RM,
755 ZONE_PLACEMENT_LIST,
756 ZONE_PLACEMENT_GET,
757 CAPS_ADD,
758 CAPS_RM,
759 METADATA_GET,
760 METADATA_PUT,
761 METADATA_RM,
762 METADATA_LIST,
763 METADATA_SYNC_STATUS,
764 METADATA_SYNC_INIT,
765 METADATA_SYNC_RUN,
766 MDLOG_LIST,
767 MDLOG_AUTOTRIM,
768 MDLOG_TRIM,
769 MDLOG_FETCH,
770 MDLOG_STATUS,
771 SYNC_ERROR_LIST,
772 SYNC_ERROR_TRIM,
773 SYNC_GROUP_CREATE,
774 SYNC_GROUP_MODIFY,
775 SYNC_GROUP_GET,
776 SYNC_GROUP_REMOVE,
777 SYNC_GROUP_FLOW_CREATE,
778 SYNC_GROUP_FLOW_REMOVE,
779 SYNC_GROUP_PIPE_CREATE,
780 SYNC_GROUP_PIPE_MODIFY,
781 SYNC_GROUP_PIPE_REMOVE,
782 SYNC_POLICY_GET,
783 BILOG_LIST,
784 BILOG_TRIM,
785 BILOG_STATUS,
786 BILOG_AUTOTRIM,
787 DATA_SYNC_STATUS,
788 DATA_SYNC_INIT,
789 DATA_SYNC_RUN,
790 DATALOG_LIST,
791 DATALOG_STATUS,
792 DATALOG_AUTOTRIM,
793 DATALOG_TRIM,
f67539c2
TL
794 DATALOG_TYPE,
795 DATALOG_PRUNE,
9f95a23c
TL
796 REALM_CREATE,
797 REALM_DELETE,
798 REALM_GET,
799 REALM_GET_DEFAULT,
800 REALM_LIST,
801 REALM_LIST_PERIODS,
802 REALM_RENAME,
803 REALM_SET,
804 REALM_DEFAULT,
805 REALM_PULL,
806 PERIOD_DELETE,
807 PERIOD_GET,
808 PERIOD_GET_CURRENT,
809 PERIOD_PULL,
810 PERIOD_PUSH,
811 PERIOD_LIST,
812 PERIOD_UPDATE,
813 PERIOD_COMMIT,
814 GLOBAL_QUOTA_GET,
815 GLOBAL_QUOTA_SET,
816 GLOBAL_QUOTA_ENABLE,
817 GLOBAL_QUOTA_DISABLE,
20effc67
TL
818 GLOBAL_RATELIMIT_GET,
819 GLOBAL_RATELIMIT_SET,
820 GLOBAL_RATELIMIT_ENABLE,
821 GLOBAL_RATELIMIT_DISABLE,
9f95a23c
TL
822 SYNC_INFO,
823 SYNC_STATUS,
824 ROLE_CREATE,
825 ROLE_DELETE,
826 ROLE_GET,
1e59de90 827 ROLE_TRUST_POLICY_MODIFY,
9f95a23c
TL
828 ROLE_LIST,
829 ROLE_POLICY_PUT,
830 ROLE_POLICY_LIST,
831 ROLE_POLICY_GET,
832 ROLE_POLICY_DELETE,
1e59de90 833 ROLE_UPDATE,
9f95a23c
TL
834 RESHARD_ADD,
835 RESHARD_LIST,
836 RESHARD_STATUS,
837 RESHARD_PROCESS,
838 RESHARD_CANCEL,
839 MFA_CREATE,
840 MFA_REMOVE,
841 MFA_GET,
842 MFA_LIST,
843 MFA_CHECK,
844 MFA_RESYNC,
845 RESHARD_STALE_INSTANCES_LIST,
846 RESHARD_STALE_INSTANCES_DELETE,
aee94f69 847 PUBSUB_TOPIC_LIST,
9f95a23c
TL
848 PUBSUB_TOPIC_GET,
849 PUBSUB_TOPIC_RM,
aee94f69
TL
850 PUBSUB_NOTIFICATION_LIST,
851 PUBSUB_NOTIFICATION_GET,
852 PUBSUB_NOTIFICATION_RM,
f67539c2
TL
853 SCRIPT_PUT,
854 SCRIPT_GET,
855 SCRIPT_RM,
856 SCRIPT_PACKAGE_ADD,
857 SCRIPT_PACKAGE_RM,
858 SCRIPT_PACKAGE_LIST
9f95a23c
TL
859};
860
7c673cae
FG
861}
862
9f95a23c
TL
863using namespace rgw_admin;
864
865static SimpleCmd::Commands all_cmds = {
866 { "user create", OPT::USER_CREATE },
867 { "user info", OPT::USER_INFO },
868 { "user modify", OPT::USER_MODIFY },
869 { "user rename", OPT::USER_RENAME },
870 { "user rm", OPT::USER_RM },
871 { "user suspend", OPT::USER_SUSPEND },
872 { "user enable", OPT::USER_ENABLE },
873 { "user check", OPT::USER_CHECK },
874 { "user stats", OPT::USER_STATS },
875 { "user list", OPT::USER_LIST },
876 { "subuser create", OPT::SUBUSER_CREATE },
877 { "subuser modify", OPT::SUBUSER_MODIFY },
878 { "subuser rm", OPT::SUBUSER_RM },
879 { "key create", OPT::KEY_CREATE },
880 { "key rm", OPT::KEY_RM },
881 { "buckets list", OPT::BUCKETS_LIST },
882 { "bucket list", OPT::BUCKETS_LIST },
883 { "bucket limit check", OPT::BUCKET_LIMIT_CHECK },
884 { "bucket link", OPT::BUCKET_LINK },
885 { "bucket unlink", OPT::BUCKET_UNLINK },
1e59de90 886 { "bucket layout", OPT::BUCKET_LAYOUT },
9f95a23c
TL
887 { "bucket stats", OPT::BUCKET_STATS },
888 { "bucket check", OPT::BUCKET_CHECK },
aee94f69
TL
889 { "bucket check olh", OPT::BUCKET_CHECK_OLH },
890 { "bucket check unlinked", OPT::BUCKET_CHECK_UNLINKED },
f67539c2 891 { "bucket sync checkpoint", OPT::BUCKET_SYNC_CHECKPOINT },
9f95a23c
TL
892 { "bucket sync info", OPT::BUCKET_SYNC_INFO },
893 { "bucket sync status", OPT::BUCKET_SYNC_STATUS },
894 { "bucket sync markers", OPT::BUCKET_SYNC_MARKERS },
895 { "bucket sync init", OPT::BUCKET_SYNC_INIT },
896 { "bucket sync run", OPT::BUCKET_SYNC_RUN },
897 { "bucket sync disable", OPT::BUCKET_SYNC_DISABLE },
898 { "bucket sync enable", OPT::BUCKET_SYNC_ENABLE },
899 { "bucket rm", OPT::BUCKET_RM },
900 { "bucket rewrite", OPT::BUCKET_REWRITE },
901 { "bucket reshard", OPT::BUCKET_RESHARD },
902 { "bucket chown", OPT::BUCKET_CHOWN },
e306af50
TL
903 { "bucket radoslist", OPT::BUCKET_RADOS_LIST },
904 { "bucket rados list", OPT::BUCKET_RADOS_LIST },
1e59de90
TL
905 { "bucket shard objects", OPT::BUCKET_SHARD_OBJECTS },
906 { "bucket shard object", OPT::BUCKET_SHARD_OBJECTS },
907 { "bucket object shard", OPT::BUCKET_OBJECT_SHARD },
aee94f69 908 { "bucket resync encrypted multipart", OPT::BUCKET_RESYNC_ENCRYPTED_MULTIPART },
9f95a23c
TL
909 { "policy", OPT::POLICY },
910 { "pool add", OPT::POOL_ADD },
911 { "pool rm", OPT::POOL_RM },
912 { "pool list", OPT::POOLS_LIST },
913 { "pools list", OPT::POOLS_LIST },
914 { "log list", OPT::LOG_LIST },
915 { "log show", OPT::LOG_SHOW },
916 { "log rm", OPT::LOG_RM },
917 { "usage show", OPT::USAGE_SHOW },
918 { "usage trim", OPT::USAGE_TRIM },
919 { "usage clear", OPT::USAGE_CLEAR },
920 { "object put", OPT::OBJECT_PUT },
921 { "object rm", OPT::OBJECT_RM },
922 { "object unlink", OPT::OBJECT_UNLINK },
923 { "object stat", OPT::OBJECT_STAT },
924 { "object rewrite", OPT::OBJECT_REWRITE },
1e59de90 925 { "object reindex", OPT::OBJECT_REINDEX },
9f95a23c
TL
926 { "objects expire", OPT::OBJECTS_EXPIRE },
927 { "objects expire-stale list", OPT::OBJECTS_EXPIRE_STALE_LIST },
928 { "objects expire-stale rm", OPT::OBJECTS_EXPIRE_STALE_RM },
929 { "bi get", OPT::BI_GET },
930 { "bi put", OPT::BI_PUT },
931 { "bi list", OPT::BI_LIST },
932 { "bi purge", OPT::BI_PURGE },
933 { "olh get", OPT::OLH_GET },
934 { "olh readlog", OPT::OLH_READLOG },
935 { "quota set", OPT::QUOTA_SET },
936 { "quota enable", OPT::QUOTA_ENABLE },
937 { "quota disable", OPT::QUOTA_DISABLE },
20effc67
TL
938 { "ratelimit get", OPT::RATELIMIT_GET },
939 { "ratelimit set", OPT::RATELIMIT_SET },
940 { "ratelimit enable", OPT::RATELIMIT_ENABLE },
941 { "ratelimit disable", OPT::RATELIMIT_DISABLE },
9f95a23c
TL
942 { "gc list", OPT::GC_LIST },
943 { "gc process", OPT::GC_PROCESS },
944 { "lc list", OPT::LC_LIST },
945 { "lc get", OPT::LC_GET },
946 { "lc process", OPT::LC_PROCESS },
947 { "lc reshard fix", OPT::LC_RESHARD_FIX },
948 { "orphans find", OPT::ORPHANS_FIND },
949 { "orphans finish", OPT::ORPHANS_FINISH },
950 { "orphans list jobs", OPT::ORPHANS_LIST_JOBS },
951 { "orphans list-jobs", OPT::ORPHANS_LIST_JOBS },
952 { "zonegroup add", OPT::ZONEGROUP_ADD },
953 { "zonegroup create", OPT::ZONEGROUP_CREATE },
954 { "zonegroup default", OPT::ZONEGROUP_DEFAULT },
955 { "zonegroup delete", OPT::ZONEGROUP_DELETE },
956 { "zonegroup get", OPT::ZONEGROUP_GET },
957 { "zonegroup modify", OPT::ZONEGROUP_MODIFY },
958 { "zonegroup set", OPT::ZONEGROUP_SET },
959 { "zonegroup list", OPT::ZONEGROUP_LIST },
960 { "zonegroups list", OPT::ZONEGROUP_LIST },
961 { "zonegroup remove", OPT::ZONEGROUP_REMOVE },
962 { "zonegroup remove zone", OPT::ZONEGROUP_REMOVE },
963 { "zonegroup rename", OPT::ZONEGROUP_RENAME },
964 { "zonegroup placement add", OPT::ZONEGROUP_PLACEMENT_ADD },
965 { "zonegroup placement modify", OPT::ZONEGROUP_PLACEMENT_MODIFY },
966 { "zonegroup placement rm", OPT::ZONEGROUP_PLACEMENT_RM },
967 { "zonegroup placement list", OPT::ZONEGROUP_PLACEMENT_LIST },
968 { "zonegroup placement get", OPT::ZONEGROUP_PLACEMENT_GET },
969 { "zonegroup placement default", OPT::ZONEGROUP_PLACEMENT_DEFAULT },
970 { "zone create", OPT::ZONE_CREATE },
971 { "zone delete", OPT::ZONE_DELETE },
972 { "zone get", OPT::ZONE_GET },
973 { "zone modify", OPT::ZONE_MODIFY },
974 { "zone set", OPT::ZONE_SET },
975 { "zone list", OPT::ZONE_LIST },
976 { "zones list", OPT::ZONE_LIST },
977 { "zone rename", OPT::ZONE_RENAME },
978 { "zone default", OPT::ZONE_DEFAULT },
979 { "zone placement add", OPT::ZONE_PLACEMENT_ADD },
980 { "zone placement modify", OPT::ZONE_PLACEMENT_MODIFY },
981 { "zone placement rm", OPT::ZONE_PLACEMENT_RM },
982 { "zone placement list", OPT::ZONE_PLACEMENT_LIST },
983 { "zone placement get", OPT::ZONE_PLACEMENT_GET },
984 { "caps add", OPT::CAPS_ADD },
985 { "caps rm", OPT::CAPS_RM },
986 { "metadata get [*]", OPT::METADATA_GET },
987 { "metadata put [*]", OPT::METADATA_PUT },
988 { "metadata rm [*]", OPT::METADATA_RM },
989 { "metadata list [*]", OPT::METADATA_LIST },
990 { "metadata sync status", OPT::METADATA_SYNC_STATUS },
991 { "metadata sync init", OPT::METADATA_SYNC_INIT },
992 { "metadata sync run", OPT::METADATA_SYNC_RUN },
993 { "mdlog list", OPT::MDLOG_LIST },
994 { "mdlog autotrim", OPT::MDLOG_AUTOTRIM },
995 { "mdlog trim", OPT::MDLOG_TRIM },
996 { "mdlog fetch", OPT::MDLOG_FETCH },
997 { "mdlog status", OPT::MDLOG_STATUS },
998 { "sync error list", OPT::SYNC_ERROR_LIST },
999 { "sync error trim", OPT::SYNC_ERROR_TRIM },
1000 { "sync policy get", OPT::SYNC_POLICY_GET },
1001 { "sync group create", OPT::SYNC_GROUP_CREATE },
1002 { "sync group modify", OPT::SYNC_GROUP_MODIFY },
1003 { "sync group get", OPT::SYNC_GROUP_GET },
1004 { "sync group remove", OPT::SYNC_GROUP_REMOVE },
1005 { "sync group flow create", OPT::SYNC_GROUP_FLOW_CREATE },
1006 { "sync group flow remove", OPT::SYNC_GROUP_FLOW_REMOVE },
1007 { "sync group pipe create", OPT::SYNC_GROUP_PIPE_CREATE },
1008 { "sync group pipe modify", OPT::SYNC_GROUP_PIPE_MODIFY },
1009 { "sync group pipe remove", OPT::SYNC_GROUP_PIPE_REMOVE },
1010 { "bilog list", OPT::BILOG_LIST },
1011 { "bilog trim", OPT::BILOG_TRIM },
1012 { "bilog status", OPT::BILOG_STATUS },
1013 { "bilog autotrim", OPT::BILOG_AUTOTRIM },
1014 { "data sync status", OPT::DATA_SYNC_STATUS },
1015 { "data sync init", OPT::DATA_SYNC_INIT },
1016 { "data sync run", OPT::DATA_SYNC_RUN },
1017 { "datalog list", OPT::DATALOG_LIST },
1018 { "datalog status", OPT::DATALOG_STATUS },
1019 { "datalog autotrim", OPT::DATALOG_AUTOTRIM },
1020 { "datalog trim", OPT::DATALOG_TRIM },
f67539c2
TL
1021 { "datalog type", OPT::DATALOG_TYPE },
1022 { "datalog prune", OPT::DATALOG_PRUNE },
9f95a23c 1023 { "realm create", OPT::REALM_CREATE },
20effc67 1024 { "realm rm", OPT::REALM_DELETE },
9f95a23c
TL
1025 { "realm get", OPT::REALM_GET },
1026 { "realm get default", OPT::REALM_GET_DEFAULT },
1027 { "realm get-default", OPT::REALM_GET_DEFAULT },
1028 { "realm list", OPT::REALM_LIST },
1029 { "realm list periods", OPT::REALM_LIST_PERIODS },
1030 { "realm list-periods", OPT::REALM_LIST_PERIODS },
1031 { "realm rename", OPT::REALM_RENAME },
1032 { "realm set", OPT::REALM_SET },
1033 { "realm default", OPT::REALM_DEFAULT },
1034 { "realm pull", OPT::REALM_PULL },
1035 { "period delete", OPT::PERIOD_DELETE },
1036 { "period get", OPT::PERIOD_GET },
1037 { "period get-current", OPT::PERIOD_GET_CURRENT },
1038 { "period get current", OPT::PERIOD_GET_CURRENT },
1039 { "period pull", OPT::PERIOD_PULL },
1040 { "period push", OPT::PERIOD_PUSH },
1041 { "period list", OPT::PERIOD_LIST },
1042 { "period update", OPT::PERIOD_UPDATE },
1043 { "period commit", OPT::PERIOD_COMMIT },
1044 { "global quota get", OPT::GLOBAL_QUOTA_GET },
1045 { "global quota set", OPT::GLOBAL_QUOTA_SET },
1046 { "global quota enable", OPT::GLOBAL_QUOTA_ENABLE },
1047 { "global quota disable", OPT::GLOBAL_QUOTA_DISABLE },
20effc67
TL
1048 { "global ratelimit get", OPT::GLOBAL_RATELIMIT_GET },
1049 { "global ratelimit set", OPT::GLOBAL_RATELIMIT_SET },
1050 { "global ratelimit enable", OPT::GLOBAL_RATELIMIT_ENABLE },
1051 { "global ratelimit disable", OPT::GLOBAL_RATELIMIT_DISABLE },
9f95a23c
TL
1052 { "sync info", OPT::SYNC_INFO },
1053 { "sync status", OPT::SYNC_STATUS },
1054 { "role create", OPT::ROLE_CREATE },
1055 { "role delete", OPT::ROLE_DELETE },
1056 { "role get", OPT::ROLE_GET },
1e59de90 1057 { "role-trust-policy modify", OPT::ROLE_TRUST_POLICY_MODIFY },
9f95a23c
TL
1058 { "role list", OPT::ROLE_LIST },
1059 { "role policy put", OPT::ROLE_POLICY_PUT },
1060 { "role-policy put", OPT::ROLE_POLICY_PUT },
1061 { "role policy list", OPT::ROLE_POLICY_LIST },
1062 { "role-policy list", OPT::ROLE_POLICY_LIST },
1063 { "role policy get", OPT::ROLE_POLICY_GET },
1064 { "role-policy get", OPT::ROLE_POLICY_GET },
1065 { "role policy delete", OPT::ROLE_POLICY_DELETE },
1066 { "role-policy delete", OPT::ROLE_POLICY_DELETE },
1e59de90 1067 { "role update", OPT::ROLE_UPDATE },
9f95a23c
TL
1068 { "reshard bucket", OPT::BUCKET_RESHARD },
1069 { "reshard add", OPT::RESHARD_ADD },
1070 { "reshard list", OPT::RESHARD_LIST },
1071 { "reshard status", OPT::RESHARD_STATUS },
1072 { "reshard process", OPT::RESHARD_PROCESS },
1073 { "reshard cancel", OPT::RESHARD_CANCEL },
1074 { "mfa create", OPT::MFA_CREATE },
1075 { "mfa remove", OPT::MFA_REMOVE },
1076 { "mfa get", OPT::MFA_GET },
1077 { "mfa list", OPT::MFA_LIST },
1078 { "mfa check", OPT::MFA_CHECK },
1079 { "mfa resync", OPT::MFA_RESYNC },
1080 { "reshard stale-instances list", OPT::RESHARD_STALE_INSTANCES_LIST },
1081 { "reshard stale list", OPT::RESHARD_STALE_INSTANCES_LIST },
1082 { "reshard stale-instances delete", OPT::RESHARD_STALE_INSTANCES_DELETE },
1083 { "reshard stale delete", OPT::RESHARD_STALE_INSTANCES_DELETE },
aee94f69 1084 { "topic list", OPT::PUBSUB_TOPIC_LIST },
f67539c2
TL
1085 { "topic get", OPT::PUBSUB_TOPIC_GET },
1086 { "topic rm", OPT::PUBSUB_TOPIC_RM },
aee94f69
TL
1087 { "notification list", OPT::PUBSUB_NOTIFICATION_LIST },
1088 { "notification get", OPT::PUBSUB_NOTIFICATION_GET },
1089 { "notification rm", OPT::PUBSUB_NOTIFICATION_RM },
f67539c2
TL
1090 { "script put", OPT::SCRIPT_PUT },
1091 { "script get", OPT::SCRIPT_GET },
1092 { "script rm", OPT::SCRIPT_RM },
1093 { "script-package add", OPT::SCRIPT_PACKAGE_ADD },
1094 { "script-package rm", OPT::SCRIPT_PACKAGE_RM },
1095 { "script-package list", OPT::SCRIPT_PACKAGE_LIST },
9f95a23c
TL
1096};
1097
1098static SimpleCmd::Aliases cmd_aliases = {
1099 { "delete", "del" },
1100 { "remove", "rm" },
1101 { "rename", "mv" },
1102};
1103
1104
1105
7c673cae
FG
1106BIIndexType get_bi_index_type(const string& type_str) {
1107 if (type_str == "plain")
11fdf7f2 1108 return BIIndexType::Plain;
7c673cae 1109 if (type_str == "instance")
11fdf7f2 1110 return BIIndexType::Instance;
7c673cae 1111 if (type_str == "olh")
11fdf7f2 1112 return BIIndexType::OLH;
7c673cae 1113
11fdf7f2 1114 return BIIndexType::Invalid;
7c673cae
FG
1115}
1116
f67539c2
TL
1117log_type get_log_type(const string& type_str) {
1118 if (strcasecmp(type_str.c_str(), "fifo") == 0)
1119 return log_type::fifo;
1120 if (strcasecmp(type_str.c_str(), "omap") == 0)
1121 return log_type::omap;
1122
1123 return static_cast<log_type>(0xff);
1124}
1125
7c673cae
FG
1126void dump_bi_entry(bufferlist& bl, BIIndexType index_type, Formatter *formatter)
1127{
11fdf7f2 1128 auto iter = bl.cbegin();
7c673cae 1129 switch (index_type) {
11fdf7f2
TL
1130 case BIIndexType::Plain:
1131 case BIIndexType::Instance:
7c673cae
FG
1132 {
1133 rgw_bucket_dir_entry entry;
11fdf7f2 1134 decode(entry, iter);
7c673cae
FG
1135 encode_json("entry", entry, formatter);
1136 }
1137 break;
11fdf7f2 1138 case BIIndexType::OLH:
7c673cae
FG
1139 {
1140 rgw_bucket_olh_entry entry;
11fdf7f2 1141 decode(entry, iter);
7c673cae
FG
1142 encode_json("entry", entry, formatter);
1143 }
1144 break;
1145 default:
1146 ceph_abort();
1147 break;
1148 }
1149}
1150
1151static void show_user_info(RGWUserInfo& info, Formatter *formatter)
1152{
1153 encode_json("user_info", info, formatter);
1154 formatter->flush(cout);
1155 cout << std::endl;
1156}
1157
1158static void show_perm_policy(string perm_policy, Formatter* formatter)
1159{
1160 formatter->open_object_section("role");
1161 formatter->dump_string("Permission policy", perm_policy);
1162 formatter->close_section();
1163 formatter->flush(cout);
1164}
1165
1166static void show_policy_names(std::vector<string> policy_names, Formatter* formatter)
1167{
1168 formatter->open_array_section("PolicyNames");
1169 for (const auto& it : policy_names) {
1170 formatter->dump_string("policyname", it);
1171 }
1172 formatter->close_section();
1173 formatter->flush(cout);
1174}
1175
20effc67 1176static void show_role_info(rgw::sal::RGWRole* role, Formatter* formatter)
7c673cae
FG
1177{
1178 formatter->open_object_section("role");
20effc67 1179 role->dump(formatter);
7c673cae
FG
1180 formatter->close_section();
1181 formatter->flush(cout);
1182}
1183
20effc67 1184static void show_roles_info(vector<std::unique_ptr<rgw::sal::RGWRole>>& roles, Formatter* formatter)
7c673cae
FG
1185{
1186 formatter->open_array_section("Roles");
1187 for (const auto& it : roles) {
1188 formatter->open_object_section("role");
20effc67 1189 it->dump(formatter);
7c673cae
FG
1190 formatter->close_section();
1191 }
1192 formatter->close_section();
1193 formatter->flush(cout);
1194}
1195
f64942e4
AA
1196static void show_reshard_status(
1197 const list<cls_rgw_bucket_instance_entry>& status, Formatter *formatter)
1198{
1199 formatter->open_array_section("status");
1200 for (const auto& entry : status) {
1201 formatter->open_object_section("entry");
1202 formatter->dump_string("reshard_status", to_string(entry.reshard_status));
f64942e4
AA
1203 formatter->close_section();
1204 }
1205 formatter->close_section();
1206 formatter->flush(cout);
1207}
1208
7c673cae 1209class StoreDestructor {
1e59de90 1210 rgw::sal::Driver* driver;
7c673cae 1211public:
1e59de90 1212 explicit StoreDestructor(rgw::sal::Driver* _s) : driver(_s) {}
7c673cae 1213 ~StoreDestructor() {
1e59de90 1214 DriverManager::close_storage(driver);
11fdf7f2 1215 rgw_http_client_cleanup();
7c673cae
FG
1216 }
1217};
1218
20effc67
TL
1219static int init_bucket(rgw::sal::User* user, const rgw_bucket& b,
1220 std::unique_ptr<rgw::sal::Bucket>* bucket)
7c673cae 1221{
1e59de90 1222 return driver->get_bucket(dpp(), user, b, bucket, null_yield);
7c673cae
FG
1223}
1224
20effc67
TL
1225static int init_bucket(rgw::sal::User* user,
1226 const string& tenant_name,
1227 const string& bucket_name,
1228 const string& bucket_id,
1229 std::unique_ptr<rgw::sal::Bucket>* bucket)
9f95a23c 1230{
20effc67
TL
1231 rgw_bucket b{tenant_name, bucket_name, bucket_id};
1232 return init_bucket(user, b, bucket);
9f95a23c
TL
1233}
1234
7c673cae
FG
1235static int read_input(const string& infile, bufferlist& bl)
1236{
1237 int fd = 0;
1238 if (infile.size()) {
1239 fd = open(infile.c_str(), O_RDONLY);
1240 if (fd < 0) {
1241 int err = -errno;
1242 cerr << "error reading input file " << infile << std::endl;
1243 return err;
1244 }
1245 }
1246
1247#define READ_CHUNK 8196
1248 int r;
1249 int err;
1250
1251 do {
1252 char buf[READ_CHUNK];
1253
1254 r = safe_read(fd, buf, READ_CHUNK);
1255 if (r < 0) {
1256 err = -errno;
1257 cerr << "error while reading input" << std::endl;
1258 goto out;
1259 }
1260 bl.append(buf, r);
1261 } while (r > 0);
1262 err = 0;
1263
1264 out:
1265 if (infile.size()) {
1266 close(fd);
1267 }
1268 return err;
1269}
1270
1271template <class T>
1272static int read_decode_json(const string& infile, T& t)
1273{
1274 bufferlist bl;
1275 int ret = read_input(infile, bl);
1276 if (ret < 0) {
1277 cerr << "ERROR: failed to read input: " << cpp_strerror(-ret) << std::endl;
1278 return ret;
1279 }
1280 JSONParser p;
1281 if (!p.parse(bl.c_str(), bl.length())) {
1282 cout << "failed to parse JSON" << std::endl;
1283 return -EINVAL;
1284 }
1285
1286 try {
1287 decode_json_obj(t, &p);
9f95a23c
TL
1288 } catch (const JSONDecoder::err& e) {
1289 cout << "failed to decode JSON input: " << e.what() << std::endl;
7c673cae
FG
1290 return -EINVAL;
1291 }
1292 return 0;
1293}
1294
1295template <class T, class K>
1296static int read_decode_json(const string& infile, T& t, K *k)
1297{
1298 bufferlist bl;
1299 int ret = read_input(infile, bl);
1300 if (ret < 0) {
1301 cerr << "ERROR: failed to read input: " << cpp_strerror(-ret) << std::endl;
1302 return ret;
1303 }
1304 JSONParser p;
1305 if (!p.parse(bl.c_str(), bl.length())) {
1306 cout << "failed to parse JSON" << std::endl;
1307 return -EINVAL;
1308 }
1309
1310 try {
1311 t.decode_json(&p, k);
9f95a23c
TL
1312 } catch (const JSONDecoder::err& e) {
1313 cout << "failed to decode JSON input: " << e.what() << std::endl;
7c673cae
FG
1314 return -EINVAL;
1315 }
1316 return 0;
1317}
1318
7c673cae
FG
1319template <class T>
1320static bool decode_dump(const char *field_name, bufferlist& bl, Formatter *f)
1321{
1322 T t;
1323
11fdf7f2 1324 auto iter = bl.cbegin();
7c673cae
FG
1325
1326 try {
11fdf7f2 1327 decode(t, iter);
7c673cae
FG
1328 } catch (buffer::error& err) {
1329 return false;
1330 }
1331
1332 encode_json(field_name, t, f);
1333
1334 return true;
1335}
1336
1337static bool dump_string(const char *field_name, bufferlist& bl, Formatter *f)
1338{
11fdf7f2
TL
1339 string val = bl.to_str();
1340 f->dump_string(field_name, val.c_str() /* hide encoded null termination chars */);
7c673cae
FG
1341
1342 return true;
1343}
1344
20effc67
TL
1345bool set_ratelimit_info(RGWRateLimitInfo& ratelimit, OPT opt_cmd, int64_t max_read_ops, int64_t max_write_ops,
1346 int64_t max_read_bytes, int64_t max_write_bytes,
1347 bool have_max_read_ops, bool have_max_write_ops,
1348 bool have_max_read_bytes, bool have_max_write_bytes)
1349{
1350 bool ratelimit_configured = true;
1351 switch (opt_cmd) {
1352 case OPT::RATELIMIT_ENABLE:
1353 case OPT::GLOBAL_RATELIMIT_ENABLE:
1354 ratelimit.enabled = true;
1355 break;
1356
1357 case OPT::RATELIMIT_SET:
1358 case OPT::GLOBAL_RATELIMIT_SET:
1359 ratelimit_configured = false;
1360 if (have_max_read_ops) {
1361 if (max_read_ops >= 0) {
1362 ratelimit.max_read_ops = max_read_ops;
1363 ratelimit_configured = true;
1364 }
1365 }
1366 if (have_max_write_ops) {
1367 if (max_write_ops >= 0) {
1368 ratelimit.max_write_ops = max_write_ops;
1369 ratelimit_configured = true;
1370 }
1371 }
1372 if (have_max_read_bytes) {
1373 if (max_read_bytes >= 0) {
1374 ratelimit.max_read_bytes = max_read_bytes;
1375 ratelimit_configured = true;
1376 }
1377 }
1378 if (have_max_write_bytes) {
1379 if (max_write_bytes >= 0) {
1380 ratelimit.max_write_bytes = max_write_bytes;
1381 ratelimit_configured = true;
1382 }
1383 }
1384 break;
1385 case OPT::RATELIMIT_DISABLE:
1386 case OPT::GLOBAL_RATELIMIT_DISABLE:
1387 ratelimit.enabled = false;
1388 break;
1389 default:
1390 break;
1391 }
1392 return ratelimit_configured;
1393}
1394
9f95a23c 1395void set_quota_info(RGWQuotaInfo& quota, OPT opt_cmd, int64_t max_size, int64_t max_objects,
7c673cae
FG
1396 bool have_max_size, bool have_max_objects)
1397{
1398 switch (opt_cmd) {
9f95a23c
TL
1399 case OPT::QUOTA_ENABLE:
1400 case OPT::GLOBAL_QUOTA_ENABLE:
7c673cae
FG
1401 quota.enabled = true;
1402
1403 // falling through on purpose
1404
9f95a23c
TL
1405 case OPT::QUOTA_SET:
1406 case OPT::GLOBAL_QUOTA_SET:
7c673cae
FG
1407 if (have_max_objects) {
1408 if (max_objects < 0) {
1409 quota.max_objects = -1;
1410 } else {
1411 quota.max_objects = max_objects;
1412 }
1413 }
1414 if (have_max_size) {
1415 if (max_size < 0) {
1416 quota.max_size = -1;
1417 } else {
1418 quota.max_size = rgw_rounded_kb(max_size) * 1024;
1419 }
1420 }
1421 break;
9f95a23c
TL
1422 case OPT::QUOTA_DISABLE:
1423 case OPT::GLOBAL_QUOTA_DISABLE:
7c673cae
FG
1424 quota.enabled = false;
1425 break;
9f95a23c
TL
1426 default:
1427 break;
7c673cae
FG
1428 }
1429}
1430
1e59de90 1431int set_bucket_quota(rgw::sal::Driver* driver, OPT opt_cmd,
7c673cae
FG
1432 const string& tenant_name, const string& bucket_name,
1433 int64_t max_size, int64_t max_objects,
1434 bool have_max_size, bool have_max_objects)
1435{
20effc67 1436 std::unique_ptr<rgw::sal::Bucket> bucket;
1e59de90 1437 int r = driver->get_bucket(dpp(), nullptr, tenant_name, bucket_name, &bucket, null_yield);
7c673cae
FG
1438 if (r < 0) {
1439 cerr << "could not get bucket info for bucket=" << bucket_name << ": " << cpp_strerror(-r) << std::endl;
1440 return -r;
1441 }
1442
20effc67 1443 set_quota_info(bucket->get_info().quota, opt_cmd, max_size, max_objects, have_max_size, have_max_objects);
7c673cae 1444
20effc67 1445 r = bucket->put_info(dpp(), false, real_time());
7c673cae
FG
1446 if (r < 0) {
1447 cerr << "ERROR: failed writing bucket instance info: " << cpp_strerror(-r) << std::endl;
1448 return -r;
1449 }
1450 return 0;
1451}
1452
1e59de90 1453int set_bucket_ratelimit(rgw::sal::Driver* driver, OPT opt_cmd,
20effc67
TL
1454 const string& tenant_name, const string& bucket_name,
1455 int64_t max_read_ops, int64_t max_write_ops,
1456 int64_t max_read_bytes, int64_t max_write_bytes,
1457 bool have_max_read_ops, bool have_max_write_ops,
1458 bool have_max_read_bytes, bool have_max_write_bytes)
1459{
1460 std::unique_ptr<rgw::sal::Bucket> bucket;
1e59de90 1461 int r = driver->get_bucket(dpp(), nullptr, tenant_name, bucket_name, &bucket, null_yield);
20effc67
TL
1462 if (r < 0) {
1463 cerr << "could not get bucket info for bucket=" << bucket_name << ": " << cpp_strerror(-r) << std::endl;
1464 return -r;
1465 }
1466 RGWRateLimitInfo ratelimit_info;
1467 auto iter = bucket->get_attrs().find(RGW_ATTR_RATELIMIT);
1468 if(iter != bucket->get_attrs().end()) {
1469 try {
1470 bufferlist& bl = iter->second;
1471 auto biter = bl.cbegin();
1472 decode(ratelimit_info, biter);
1473 } catch (buffer::error& err) {
1474 ldpp_dout(dpp(), 0) << "ERROR: failed to decode rate limit" << dendl;
1475 return -EIO;
1476 }
1477 }
1478 bool ratelimit_configured = set_ratelimit_info(ratelimit_info, opt_cmd, max_read_ops, max_write_ops,
1479 max_read_bytes, max_write_bytes,
1480 have_max_read_ops, have_max_write_ops,
1481 have_max_read_bytes, have_max_write_bytes);
1482 if (!ratelimit_configured) {
1483 ldpp_dout(dpp(), 0) << "ERROR: no rate limit values have been specified" << dendl;
1484 return -EINVAL;
1485 }
1486 bufferlist bl;
1487 ratelimit_info.encode(bl);
1488 rgw::sal::Attrs attr;
1489 attr[RGW_ATTR_RATELIMIT] = bl;
1490 r = bucket->merge_and_store_attrs(dpp(), attr, null_yield);
1491 if (r < 0) {
1492 cerr << "ERROR: failed writing bucket instance info: " << cpp_strerror(-r) << std::endl;
1493 return -r;
1494 }
1495 return 0;
1496}
1497
1498int set_user_ratelimit(OPT opt_cmd, std::unique_ptr<rgw::sal::User>& user,
1499 int64_t max_read_ops, int64_t max_write_ops,
1500 int64_t max_read_bytes, int64_t max_write_bytes,
1501 bool have_max_read_ops, bool have_max_write_ops,
1502 bool have_max_read_bytes, bool have_max_write_bytes)
1503{
1504 RGWRateLimitInfo ratelimit_info;
1505 user->load_user(dpp(), null_yield);
1506 auto iter = user->get_attrs().find(RGW_ATTR_RATELIMIT);
1507 if(iter != user->get_attrs().end()) {
1508 try {
1509 bufferlist& bl = iter->second;
1510 auto biter = bl.cbegin();
1511 decode(ratelimit_info, biter);
1512 } catch (buffer::error& err) {
1513 ldpp_dout(dpp(), 0) << "ERROR: failed to decode rate limit" << dendl;
1514 return -EIO;
1515 }
1516 }
1517 bool ratelimit_configured = set_ratelimit_info(ratelimit_info, opt_cmd, max_read_ops, max_write_ops,
1518 max_read_bytes, max_write_bytes,
1519 have_max_read_ops, have_max_write_ops,
1520 have_max_read_bytes, have_max_write_bytes);
1521 if (!ratelimit_configured) {
1522 ldpp_dout(dpp(), 0) << "ERROR: no rate limit values have been specified" << dendl;
1523 return -EINVAL;
1524 }
1525 bufferlist bl;
1526 ratelimit_info.encode(bl);
1527 rgw::sal::Attrs attr;
1528 attr[RGW_ATTR_RATELIMIT] = bl;
1529 int r = user->merge_and_store_attrs(dpp(), attr, null_yield);
1530 if (r < 0) {
1531 cerr << "ERROR: failed writing user instance info: " << cpp_strerror(-r) << std::endl;
1532 return -r;
1533 }
1534 return 0;
1535}
1536
1537int show_user_ratelimit(std::unique_ptr<rgw::sal::User>& user, Formatter *formatter)
1538{
1539 RGWRateLimitInfo ratelimit_info;
1540 user->load_user(dpp(), null_yield);
1541 auto iter = user->get_attrs().find(RGW_ATTR_RATELIMIT);
1542 if(iter != user->get_attrs().end()) {
1543 try {
1544 bufferlist& bl = iter->second;
1545 auto biter = bl.cbegin();
1546 decode(ratelimit_info, biter);
1547 } catch (buffer::error& err) {
1548 ldpp_dout(dpp(), 0) << "ERROR: failed to decode rate limit" << dendl;
1549 return -EIO;
1550 }
1551 }
1552 formatter->open_object_section("user_ratelimit");
1553 encode_json("user_ratelimit", ratelimit_info, formatter);
1554 formatter->close_section();
1555 formatter->flush(cout);
1556 cout << std::endl;
1557 return 0;
1558}
1559
1e59de90 1560int show_bucket_ratelimit(rgw::sal::Driver* driver, const string& tenant_name,
20effc67
TL
1561 const string& bucket_name, Formatter *formatter)
1562{
1563 std::unique_ptr<rgw::sal::Bucket> bucket;
1e59de90 1564 int r = driver->get_bucket(dpp(), nullptr, tenant_name, bucket_name, &bucket, null_yield);
20effc67
TL
1565 if (r < 0) {
1566 cerr << "could not get bucket info for bucket=" << bucket_name << ": " << cpp_strerror(-r) << std::endl;
1567 return -r;
1568 }
1569 RGWRateLimitInfo ratelimit_info;
1570 auto iter = bucket->get_attrs().find(RGW_ATTR_RATELIMIT);
1571 if (iter != bucket->get_attrs().end()) {
1572 try {
1573 bufferlist& bl = iter->second;
1574 auto biter = bl.cbegin();
1575 decode(ratelimit_info, biter);
1576 } catch (buffer::error& err) {
1577 ldpp_dout(dpp(), 0) << "ERROR: failed to decode rate limit" << dendl;
1578 return -EIO;
1579 }
1580 }
1581 formatter->open_object_section("bucket_ratelimit");
1582 encode_json("bucket_ratelimit", ratelimit_info, formatter);
1583 formatter->close_section();
1584 formatter->flush(cout);
1585 cout << std::endl;
1586 return 0;
1587}
9f95a23c 1588int set_user_bucket_quota(OPT opt_cmd, RGWUser& user, RGWUserAdminOpState& op_state, int64_t max_size, int64_t max_objects,
7c673cae
FG
1589 bool have_max_size, bool have_max_objects)
1590{
1591 RGWUserInfo& user_info = op_state.get_user_info();
1592
1e59de90 1593 set_quota_info(user_info.quota.bucket_quota, opt_cmd, max_size, max_objects, have_max_size, have_max_objects);
7c673cae 1594
1e59de90 1595 op_state.set_bucket_quota(user_info.quota.bucket_quota);
7c673cae
FG
1596
1597 string err;
b3b6e05e 1598 int r = user.modify(dpp(), op_state, null_yield, &err);
7c673cae
FG
1599 if (r < 0) {
1600 cerr << "ERROR: failed updating user info: " << cpp_strerror(-r) << ": " << err << std::endl;
1601 return -r;
1602 }
1603 return 0;
1604}
1605
9f95a23c 1606int set_user_quota(OPT opt_cmd, RGWUser& user, RGWUserAdminOpState& op_state, int64_t max_size, int64_t max_objects,
7c673cae
FG
1607 bool have_max_size, bool have_max_objects)
1608{
1609 RGWUserInfo& user_info = op_state.get_user_info();
1610
1e59de90 1611 set_quota_info(user_info.quota.user_quota, opt_cmd, max_size, max_objects, have_max_size, have_max_objects);
7c673cae 1612
1e59de90 1613 op_state.set_user_quota(user_info.quota.user_quota);
7c673cae
FG
1614
1615 string err;
b3b6e05e 1616 int r = user.modify(dpp(), op_state, null_yield, &err);
7c673cae
FG
1617 if (r < 0) {
1618 cerr << "ERROR: failed updating user info: " << cpp_strerror(-r) << ": " << err << std::endl;
1619 return -r;
1620 }
1621 return 0;
1622}
1623
1e59de90 1624int check_min_obj_stripe_size(rgw::sal::Driver* driver, rgw::sal::Object* obj, uint64_t min_stripe_size, bool *need_rewrite)
7c673cae 1625{
1e59de90 1626 int ret = obj->get_obj_attrs(null_yield, dpp());
7c673cae 1627 if (ret < 0) {
b3b6e05e 1628 ldpp_dout(dpp(), -1) << "ERROR: failed to stat object, returned error: " << cpp_strerror(-ret) << dendl;
7c673cae
FG
1629 return ret;
1630 }
1631
1632 map<string, bufferlist>::iterator iter;
f67539c2
TL
1633 iter = obj->get_attrs().find(RGW_ATTR_MANIFEST);
1634 if (iter == obj->get_attrs().end()) {
1635 *need_rewrite = (obj->get_obj_size() >= min_stripe_size);
7c673cae
FG
1636 return 0;
1637 }
1638
1639 RGWObjManifest manifest;
1640
1641 try {
1642 bufferlist& bl = iter->second;
11fdf7f2
TL
1643 auto biter = bl.cbegin();
1644 decode(manifest, biter);
7c673cae 1645 } catch (buffer::error& err) {
b3b6e05e 1646 ldpp_dout(dpp(), 0) << "ERROR: failed to decode manifest" << dendl;
7c673cae
FG
1647 return -EIO;
1648 }
1649
1650 map<uint64_t, RGWObjManifestPart>& objs = manifest.get_explicit_objs();
1651 map<uint64_t, RGWObjManifestPart>::iterator oiter;
1652 for (oiter = objs.begin(); oiter != objs.end(); ++oiter) {
1653 RGWObjManifestPart& part = oiter->second;
1654
1655 if (part.size >= min_stripe_size) {
1656 *need_rewrite = true;
1657 return 0;
1658 }
1659 }
1660 *need_rewrite = false;
1661
1662 return 0;
1663}
1664
1665
20effc67 1666int check_obj_locator_underscore(rgw::sal::Object* obj, bool fix, bool remove_bad, Formatter *f) {
7c673cae
FG
1667 f->open_object_section("object");
1668 f->open_object_section("key");
1669 f->dump_string("type", "head");
20effc67
TL
1670 f->dump_string("name", obj->get_name());
1671 f->dump_string("instance", obj->get_instance());
7c673cae
FG
1672 f->close_section();
1673
1674 string oid;
1675 string locator;
1676
20effc67 1677 get_obj_bucket_and_oid_loc(obj->get_obj(), oid, locator);
7c673cae
FG
1678
1679 f->dump_string("oid", oid);
1680 f->dump_string("locator", locator);
1681
1e59de90 1682 std::unique_ptr<rgw::sal::Object::ReadOp> read_op = obj->get_read_op();
7c673cae 1683
20effc67 1684 int ret = read_op->prepare(null_yield, dpp());
7c673cae
FG
1685 bool needs_fixing = (ret == -ENOENT);
1686
1687 f->dump_bool("needs_fixing", needs_fixing);
1688
1689 string status = (needs_fixing ? "needs_fixing" : "ok");
1690
1691 if ((needs_fixing || remove_bad) && fix) {
1e59de90 1692 ret = static_cast<rgw::sal::RadosStore*>(driver)->getRados()->fix_head_obj_locator(dpp(), obj->get_bucket()->get_info(), needs_fixing, remove_bad, obj->get_key());
7c673cae
FG
1693 if (ret < 0) {
1694 cerr << "ERROR: fix_head_object_locator() returned ret=" << ret << std::endl;
1695 goto done;
1696 }
1697 status = "fixed";
1698 }
1699
1700done:
1701 f->dump_string("status", status);
1702
1703 f->close_section();
1704
1705 return 0;
1706}
1707
20effc67 1708int check_obj_tail_locator_underscore(RGWBucketInfo& bucket_info, rgw_obj_key& key, bool fix, Formatter *f) {
7c673cae
FG
1709 f->open_object_section("object");
1710 f->open_object_section("key");
1711 f->dump_string("type", "tail");
1712 f->dump_string("name", key.name);
1713 f->dump_string("instance", key.instance);
1714 f->close_section();
1715
1716 bool needs_fixing;
1717 string status;
1718
1e59de90 1719 int ret = static_cast<rgw::sal::RadosStore*>(driver)->getRados()->fix_tail_obj_locator(dpp(), bucket_info, key, fix, &needs_fixing, null_yield);
7c673cae
FG
1720 if (ret < 0) {
1721 cerr << "ERROR: fix_tail_object_locator_underscore() returned ret=" << ret << std::endl;
1722 status = "failed";
1723 } else {
1724 status = (needs_fixing && !fix ? "needs_fixing" : "ok");
1725 }
1726
1727 f->dump_bool("needs_fixing", needs_fixing);
1728 f->dump_string("status", status);
1729
1730 f->close_section();
1731
1732 return 0;
1733}
1734
1735int do_check_object_locator(const string& tenant_name, const string& bucket_name,
1736 bool fix, bool remove_bad, Formatter *f)
1737{
1738 if (remove_bad && !fix) {
1739 cerr << "ERROR: can't have remove_bad specified without fix" << std::endl;
1740 return -EINVAL;
1741 }
1742
20effc67 1743 std::unique_ptr<rgw::sal::Bucket> bucket;
7c673cae
FG
1744 string bucket_id;
1745
1746 f->open_object_section("bucket");
1747 f->dump_string("bucket", bucket_name);
20effc67 1748 int ret = init_bucket(nullptr, tenant_name, bucket_name, bucket_id, &bucket);
7c673cae
FG
1749 if (ret < 0) {
1750 cerr << "ERROR: could not init bucket: " << cpp_strerror(-ret) << std::endl;
1751 return ret;
1752 }
7c673cae
FG
1753 int count = 0;
1754
1755 int max_entries = 1000;
1756
1757 string prefix;
1758 string delim;
20effc67 1759 string marker;
7c673cae 1760 vector<rgw_bucket_dir_entry> result;
7c673cae
FG
1761 string ns;
1762
20effc67
TL
1763 rgw::sal::Bucket::ListParams params;
1764 rgw::sal::Bucket::ListResults results;
7c673cae 1765
20effc67
TL
1766 params.prefix = prefix;
1767 params.delim = delim;
1768 params.marker = rgw_obj_key(marker);
1769 params.ns = ns;
1770 params.enforce_ns = true;
1771 params.list_versions = true;
7c673cae
FG
1772
1773 f->open_array_section("check_objects");
1774 do {
20effc67 1775 ret = bucket->list(dpp(), params, max_entries - count, results, null_yield);
7c673cae 1776 if (ret < 0) {
1e59de90 1777 cerr << "ERROR: driver->list_objects(): " << cpp_strerror(-ret) << std::endl;
7c673cae
FG
1778 return -ret;
1779 }
1780
20effc67 1781 count += results.objs.size();
7c673cae 1782
20effc67
TL
1783 for (vector<rgw_bucket_dir_entry>::iterator iter = results.objs.begin(); iter != results.objs.end(); ++iter) {
1784 std::unique_ptr<rgw::sal::Object> obj = bucket->get_object(iter->key);
7c673cae 1785
20effc67
TL
1786 if (obj->get_name()[0] == '_') {
1787 ret = check_obj_locator_underscore(obj.get(), fix, remove_bad, f);
7c673cae
FG
1788
1789 if (ret >= 0) {
20effc67 1790 ret = check_obj_tail_locator_underscore(bucket->get_info(), obj->get_key(), fix, f);
11fdf7f2
TL
1791 if (ret < 0) {
1792 cerr << "ERROR: check_obj_tail_locator_underscore(): " << cpp_strerror(-ret) << std::endl;
1793 return -ret;
1794 }
7c673cae
FG
1795 }
1796 }
1797 }
1798 f->flush(cout);
20effc67 1799 } while (results.is_truncated && count < max_entries);
7c673cae
FG
1800 f->close_section();
1801 f->close_section();
1802
1803 f->flush(cout);
1804
1805 return 0;
1806}
1807
1808/// search for a matching zone/zonegroup id and return a connection if found
1e59de90 1809static boost::optional<RGWRESTConn> get_remote_conn(rgw::sal::RadosStore* driver,
7c673cae
FG
1810 const RGWZoneGroup& zonegroup,
1811 const std::string& remote)
1812{
1813 boost::optional<RGWRESTConn> conn;
1814 if (remote == zonegroup.get_id()) {
1e59de90 1815 conn.emplace(driver->ctx(), driver, remote, zonegroup.endpoints, zonegroup.api_name);
7c673cae
FG
1816 } else {
1817 for (const auto& z : zonegroup.zones) {
1818 const auto& zone = z.second;
1819 if (remote == zone.id) {
1e59de90 1820 conn.emplace(driver->ctx(), driver, remote, zone.endpoints, zonegroup.api_name);
7c673cae
FG
1821 break;
1822 }
1823 }
1824 }
1825 return conn;
1826}
1827
1828/// search each zonegroup for a connection
1e59de90 1829static boost::optional<RGWRESTConn> get_remote_conn(rgw::sal::RadosStore* driver,
7c673cae
FG
1830 const RGWPeriodMap& period_map,
1831 const std::string& remote)
1832{
1833 boost::optional<RGWRESTConn> conn;
1834 for (const auto& zg : period_map.zonegroups) {
1e59de90 1835 conn = get_remote_conn(driver, zg.second, remote);
7c673cae
FG
1836 if (conn) {
1837 break;
1838 }
1839 }
1840 return conn;
1841}
1842
224ce89b
WB
1843// we expect a very small response
1844static constexpr size_t MAX_REST_RESPONSE = 128 * 1024;
1845
7c673cae
FG
1846static int send_to_remote_gateway(RGWRESTConn* conn, req_info& info,
1847 bufferlist& in_data, JSONParser& parser)
1848{
224ce89b
WB
1849 if (!conn) {
1850 return -EINVAL;
1851 }
1852
1853 ceph::bufferlist response;
7c673cae 1854 rgw_user user;
b3b6e05e 1855 int ret = conn->forward(dpp(), user, info, nullptr, MAX_REST_RESPONSE, &in_data, &response, null_yield);
7c673cae
FG
1856
1857 int parse_ret = parser.parse(response.c_str(), response.length());
1858 if (parse_ret < 0) {
1859 cerr << "failed to parse response" << std::endl;
1860 return parse_ret;
1861 }
1862 return ret;
1863}
1864
20effc67
TL
1865static int send_to_url(const string& url,
1866 std::optional<string> opt_region,
1867 const string& access,
7c673cae
FG
1868 const string& secret, req_info& info,
1869 bufferlist& in_data, JSONParser& parser)
1870{
1871 if (access.empty() || secret.empty()) {
1872 cerr << "An --access-key and --secret must be provided with --url." << std::endl;
1873 return -EINVAL;
1874 }
1875 RGWAccessKey key;
1876 key.id = access;
1877 key.key = secret;
1878
1879 param_vec_t params;
20effc67 1880 RGWRESTSimpleRequest req(g_ceph_context, info.method, url, NULL, &params, opt_region);
7c673cae
FG
1881
1882 bufferlist response;
b3b6e05e 1883 int ret = req.forward_request(dpp(), key, info, MAX_REST_RESPONSE, &in_data, &response, null_yield);
7c673cae
FG
1884
1885 int parse_ret = parser.parse(response.c_str(), response.length());
1886 if (parse_ret < 0) {
1887 cout << "failed to parse response" << std::endl;
1888 return parse_ret;
1889 }
1890 return ret;
1891}
1892
1893static int send_to_remote_or_url(RGWRESTConn *conn, const string& url,
20effc67 1894 std::optional<string> opt_region,
7c673cae
FG
1895 const string& access, const string& secret,
1896 req_info& info, bufferlist& in_data,
1897 JSONParser& parser)
1898{
1899 if (url.empty()) {
1900 return send_to_remote_gateway(conn, info, in_data, parser);
1901 }
20effc67 1902 return send_to_url(url, opt_region, access, secret, info, in_data, parser);
7c673cae
FG
1903}
1904
1e59de90
TL
1905static int commit_period(rgw::sal::ConfigStore* cfgstore,
1906 RGWRealm& realm, rgw::sal::RealmWriter& realm_writer,
1907 RGWPeriod& period, string remote, const string& url,
20effc67 1908 std::optional<string> opt_region,
7c673cae
FG
1909 const string& access, const string& secret,
1910 bool force)
1911{
1e59de90 1912 auto& master_zone = period.get_master_zone().id;
7c673cae
FG
1913 if (master_zone.empty()) {
1914 cerr << "cannot commit period: period does not have a master zone of a master zonegroup" << std::endl;
1915 return -EINVAL;
1916 }
1917 // are we the period's master zone?
1e59de90 1918 if (driver->get_zone()->get_id() == master_zone) {
7c673cae
FG
1919 // read the current period
1920 RGWPeriod current_period;
1e59de90
TL
1921 int ret = cfgstore->read_period(dpp(), null_yield, realm.current_period,
1922 std::nullopt, current_period);
7c673cae 1923 if (ret < 0) {
1e59de90 1924 cerr << "failed to load current period: " << cpp_strerror(ret) << std::endl;
7c673cae
FG
1925 return ret;
1926 }
1927 // the master zone can commit locally
1e59de90
TL
1928 ret = rgw::commit_period(dpp(), null_yield, cfgstore, driver,
1929 realm, realm_writer, current_period,
1930 period, cerr, force);
7c673cae
FG
1931 if (ret < 0) {
1932 cerr << "failed to commit period: " << cpp_strerror(-ret) << std::endl;
1933 }
1934 return ret;
1935 }
1936
1937 if (remote.empty() && url.empty()) {
1938 // use the new master zone's connection
1e59de90 1939 remote = master_zone;
e306af50 1940 cerr << "Sending period to new master zone " << remote << std::endl;
7c673cae
FG
1941 }
1942 boost::optional<RGWRESTConn> conn;
1943 RGWRESTConn *remote_conn = nullptr;
1944 if (!remote.empty()) {
1e59de90 1945 conn = get_remote_conn(static_cast<rgw::sal::RadosStore*>(driver), period.get_map(), remote);
7c673cae
FG
1946 if (!conn) {
1947 cerr << "failed to find a zone or zonegroup for remote "
1948 << remote << std::endl;
1949 return -ENOENT;
1950 }
1951 remote_conn = &*conn;
1952 }
1953
1954 // push period to the master with an empty period id
9f95a23c 1955 period.set_id(string());
7c673cae
FG
1956
1957 RGWEnv env;
1958 req_info info(g_ceph_context, &env);
1959 info.method = "POST";
1960 info.request_uri = "/admin/realm/period";
1961
1962 // json format into a bufferlist
1963 JSONFormatter jf(false);
1964 encode_json("period", period, &jf);
1965 bufferlist bl;
1966 jf.flush(bl);
1967
1968 JSONParser p;
20effc67 1969 int ret = send_to_remote_or_url(remote_conn, url, opt_region, access, secret, info, bl, p);
7c673cae
FG
1970 if (ret < 0) {
1971 cerr << "request failed: " << cpp_strerror(-ret) << std::endl;
1972
1973 // did we parse an error message?
1974 auto message = p.find_obj("Message");
1975 if (message) {
1976 cerr << "Reason: " << message->get_data() << std::endl;
1977 }
1978 return ret;
1979 }
1980
1e59de90 1981 // decode the response and driver it back
7c673cae
FG
1982 try {
1983 decode_json_obj(period, &p);
9f95a23c
TL
1984 } catch (const JSONDecoder::err& e) {
1985 cout << "failed to decode JSON input: " << e.what() << std::endl;
7c673cae
FG
1986 return -EINVAL;
1987 }
1988 if (period.get_id().empty()) {
1989 cerr << "Period commit got back an empty period id" << std::endl;
1990 return -EINVAL;
1991 }
1992 // the master zone gave us back the period that it committed, so it's
1993 // safe to save it as our latest epoch
1e59de90
TL
1994 constexpr bool exclusive = false;
1995 ret = cfgstore->create_period(dpp(), null_yield, exclusive, period);
7c673cae
FG
1996 if (ret < 0) {
1997 cerr << "Error storing committed period " << period.get_id() << ": "
1998 << cpp_strerror(ret) << std::endl;
1999 return ret;
2000 }
1e59de90 2001 ret = rgw::reflect_period(dpp(), null_yield, cfgstore, period);
7c673cae
FG
2002 if (ret < 0) {
2003 cerr << "Error updating local objects: " << cpp_strerror(ret) << std::endl;
2004 return ret;
2005 }
1e59de90 2006 (void) cfgstore->realm_notify_new_period(dpp(), null_yield, period);
7c673cae
FG
2007 return ret;
2008}
2009
1e59de90
TL
2010static int update_period(rgw::sal::ConfigStore* cfgstore,
2011 const string& realm_id, const string& realm_name,
2012 const string& period_epoch, bool commit,
2013 const string& remote, const string& url,
20effc67 2014 std::optional<string> opt_region,
7c673cae
FG
2015 const string& access, const string& secret,
2016 Formatter *formatter, bool force)
2017{
1e59de90
TL
2018 RGWRealm realm;
2019 std::unique_ptr<rgw::sal::RealmWriter> realm_writer;
2020 int ret = rgw::read_realm(dpp(), null_yield, cfgstore,
2021 realm_id, realm_name,
2022 realm, &realm_writer);
2023 if (ret < 0) {
2024 cerr << "failed to load realm " << cpp_strerror(-ret) << std::endl;
7c673cae
FG
2025 return ret;
2026 }
1e59de90 2027 std::optional<epoch_t> epoch;
7c673cae
FG
2028 if (!period_epoch.empty()) {
2029 epoch = atoi(period_epoch.c_str());
2030 }
1e59de90
TL
2031 RGWPeriod period;
2032 ret = cfgstore->read_period(dpp(), null_yield, realm.current_period,
2033 epoch, period);
7c673cae 2034 if (ret < 0) {
1e59de90 2035 cerr << "failed to load current period: " << cpp_strerror(-ret) << std::endl;
7c673cae
FG
2036 return ret;
2037 }
1e59de90
TL
2038 // convert to the realm's staging period
2039 rgw::fork_period(dpp(), period);
2040 // update the staging period with all of the realm's zonegroups
2041 ret = rgw::update_period(dpp(), null_yield, cfgstore, period);
2042 if (ret < 0) {
7c673cae
FG
2043 return ret;
2044 }
1e59de90
TL
2045
2046 constexpr bool exclusive = false;
2047 ret = cfgstore->create_period(dpp(), null_yield, exclusive, period);
7c673cae 2048 if (ret < 0) {
1e59de90 2049 cerr << "failed to driver period: " << cpp_strerror(-ret) << std::endl;
7c673cae
FG
2050 return ret;
2051 }
2052 if (commit) {
1e59de90
TL
2053 ret = commit_period(cfgstore, realm, *realm_writer, period, remote, url,
2054 opt_region, access, secret, force);
7c673cae
FG
2055 if (ret < 0) {
2056 cerr << "failed to commit period: " << cpp_strerror(-ret) << std::endl;
2057 return ret;
2058 }
2059 }
2060 encode_json("period", period, formatter);
2061 formatter->flush(cout);
7c673cae
FG
2062 return 0;
2063}
2064
20effc67
TL
2065static int init_bucket_for_sync(rgw::sal::User* user,
2066 const string& tenant, const string& bucket_name,
2067 const string& bucket_id,
2068 std::unique_ptr<rgw::sal::Bucket>* bucket)
7c673cae 2069{
20effc67 2070 int ret = init_bucket(user, tenant, bucket_name, bucket_id, bucket);
7c673cae
FG
2071 if (ret < 0) {
2072 cerr << "ERROR: could not init bucket: " << cpp_strerror(-ret) << std::endl;
2073 return ret;
2074 }
2075
2076 return 0;
2077}
2078
1e59de90
TL
2079static int do_period_pull(rgw::sal::ConfigStore* cfgstore,
2080 RGWRESTConn *remote_conn, const string& url,
20effc67 2081 std::optional<string> opt_region,
7c673cae
FG
2082 const string& access_key, const string& secret_key,
2083 const string& realm_id, const string& realm_name,
2084 const string& period_id, const string& period_epoch,
2085 RGWPeriod *period)
2086{
2087 RGWEnv env;
2088 req_info info(g_ceph_context, &env);
2089 info.method = "GET";
2090 info.request_uri = "/admin/realm/period";
2091
2092 map<string, string> &params = info.args.get_params();
2093 if (!realm_id.empty())
2094 params["realm_id"] = realm_id;
2095 if (!realm_name.empty())
2096 params["realm_name"] = realm_name;
2097 if (!period_id.empty())
2098 params["period_id"] = period_id;
2099 if (!period_epoch.empty())
2100 params["epoch"] = period_epoch;
2101
2102 bufferlist bl;
2103 JSONParser p;
20effc67 2104 int ret = send_to_remote_or_url(remote_conn, url, opt_region, access_key, secret_key,
7c673cae
FG
2105 info, bl, p);
2106 if (ret < 0) {
2107 cerr << "request failed: " << cpp_strerror(-ret) << std::endl;
2108 return ret;
2109 }
7c673cae
FG
2110 try {
2111 decode_json_obj(*period, &p);
9f95a23c
TL
2112 } catch (const JSONDecoder::err& e) {
2113 cout << "failed to decode JSON input: " << e.what() << std::endl;
7c673cae
FG
2114 return -EINVAL;
2115 }
1e59de90
TL
2116 constexpr bool exclusive = false;
2117 ret = cfgstore->create_period(dpp(), null_yield, exclusive, *period);
7c673cae
FG
2118 if (ret < 0) {
2119 cerr << "Error storing period " << period->get_id() << ": " << cpp_strerror(ret) << std::endl;
2120 }
7c673cae
FG
2121 return 0;
2122}
2123
2124void flush_ss(stringstream& ss, list<string>& l)
2125{
2126 if (!ss.str().empty()) {
2127 l.push_back(ss.str());
2128 }
2129 ss.str("");
2130}
2131
2132stringstream& push_ss(stringstream& ss, list<string>& l, int tab = 0)
2133{
2134 flush_ss(ss, l);
2135 if (tab > 0) {
2136 ss << setw(tab) << "" << setw(1);
2137 }
2138 return ss;
2139}
2140
2141static void get_md_sync_status(list<string>& status)
2142{
1e59de90 2143 RGWMetaSyncStatusManager sync(static_cast<rgw::sal::RadosStore*>(driver), static_cast<rgw::sal::RadosStore*>(driver)->svc()->rados->get_async_processor());
7c673cae 2144
b3b6e05e 2145 int ret = sync.init(dpp());
7c673cae
FG
2146 if (ret < 0) {
2147 status.push_back(string("failed to retrieve sync info: sync.init() failed: ") + cpp_strerror(-ret));
2148 return;
2149 }
2150
2151 rgw_meta_sync_status sync_status;
b3b6e05e 2152 ret = sync.read_sync_status(dpp(), &sync_status);
7c673cae
FG
2153 if (ret < 0) {
2154 status.push_back(string("failed to read sync status: ") + cpp_strerror(-ret));
2155 return;
2156 }
2157
2158 string status_str;
2159 switch (sync_status.sync_info.state) {
2160 case rgw_meta_sync_info::StateInit:
2161 status_str = "init";
2162 break;
2163 case rgw_meta_sync_info::StateBuildingFullSyncMaps:
2164 status_str = "preparing for full sync";
2165 break;
2166 case rgw_meta_sync_info::StateSync:
2167 status_str = "syncing";
2168 break;
2169 default:
2170 status_str = "unknown";
2171 }
2172
2173 status.push_back(status_str);
2174
2175 uint64_t full_total = 0;
2176 uint64_t full_complete = 0;
2177
2178 int num_full = 0;
2179 int num_inc = 0;
2180 int total_shards = 0;
28e407b8 2181 set<int> shards_behind_set;
7c673cae
FG
2182
2183 for (auto marker_iter : sync_status.sync_markers) {
2184 full_total += marker_iter.second.total_entries;
2185 total_shards++;
2186 if (marker_iter.second.state == rgw_meta_sync_marker::SyncState::FullSync) {
2187 num_full++;
2188 full_complete += marker_iter.second.pos;
11fdf7f2 2189 int shard_id = marker_iter.first;
28e407b8 2190 shards_behind_set.insert(shard_id);
7c673cae
FG
2191 } else {
2192 full_complete += marker_iter.second.total_entries;
2193 }
2194 if (marker_iter.second.state == rgw_meta_sync_marker::SyncState::IncrementalSync) {
2195 num_inc++;
2196 }
2197 }
2198
2199 stringstream ss;
2200 push_ss(ss, status) << "full sync: " << num_full << "/" << total_shards << " shards";
2201
2202 if (num_full > 0) {
2203 push_ss(ss, status) << "full sync: " << full_total - full_complete << " entries to sync";
2204 }
2205
2206 push_ss(ss, status) << "incremental sync: " << num_inc << "/" << total_shards << " shards";
2207
7c673cae 2208 map<int, RGWMetadataLogInfo> master_shards_info;
1e59de90 2209 string master_period = static_cast<rgw::sal::RadosStore*>(driver)->svc()->zone->get_current_period_id();
7c673cae 2210
b3b6e05e 2211 ret = sync.read_master_log_shards_info(dpp(), master_period, &master_shards_info);
7c673cae
FG
2212 if (ret < 0) {
2213 status.push_back(string("failed to fetch master sync status: ") + cpp_strerror(-ret));
2214 return;
2215 }
2216
2217 map<int, string> shards_behind;
2218 if (sync_status.sync_info.period != master_period) {
2219 status.push_back(string("master is on a different period: master_period=" +
2220 master_period + " local_period=" + sync_status.sync_info.period));
2221 } else {
2222 for (auto local_iter : sync_status.sync_markers) {
2223 int shard_id = local_iter.first;
2224 auto iter = master_shards_info.find(shard_id);
2225
2226 if (iter == master_shards_info.end()) {
2227 /* huh? */
2228 derr << "ERROR: could not find remote sync shard status for shard_id=" << shard_id << dendl;
2229 continue;
2230 }
2231 auto master_marker = iter->second.marker;
2232 if (local_iter.second.state == rgw_meta_sync_marker::SyncState::IncrementalSync &&
2233 master_marker > local_iter.second.marker) {
2234 shards_behind[shard_id] = local_iter.second.marker;
28e407b8 2235 shards_behind_set.insert(shard_id);
7c673cae
FG
2236 }
2237 }
2238 }
2239
20effc67
TL
2240 // fetch remote log entries to determine the oldest change
2241 std::optional<std::pair<int, ceph::real_time>> oldest;
2242 if (!shards_behind.empty()) {
7c673cae 2243 map<int, rgw_mdlog_shard_data> master_pos;
b3b6e05e 2244 ret = sync.read_master_log_shards_next(dpp(), sync_status.sync_info.period, shards_behind, &master_pos);
7c673cae
FG
2245 if (ret < 0) {
2246 derr << "ERROR: failed to fetch master next positions (" << cpp_strerror(-ret) << ")" << dendl;
2247 } else {
7c673cae
FG
2248 for (auto iter : master_pos) {
2249 rgw_mdlog_shard_data& shard_data = iter.second;
2250
20effc67
TL
2251 if (shard_data.entries.empty()) {
2252 // there aren't any entries in this shard, so we're not really behind
2253 shards_behind.erase(iter.first);
2254 shards_behind_set.erase(iter.first);
2255 } else {
7c673cae 2256 rgw_mdlog_entry& entry = shard_data.entries.front();
92f5a8d4
TL
2257 if (!oldest) {
2258 oldest.emplace(iter.first, entry.timestamp);
2259 } else if (!ceph::real_clock::is_zero(entry.timestamp) && entry.timestamp < oldest->second) {
2260 oldest.emplace(iter.first, entry.timestamp);
7c673cae
FG
2261 }
2262 }
2263 }
20effc67
TL
2264 }
2265 }
7c673cae 2266
20effc67
TL
2267 int total_behind = shards_behind.size() + (sync_status.sync_info.num_shards - num_inc);
2268 if (total_behind == 0) {
2269 push_ss(ss, status) << "metadata is caught up with master";
2270 } else {
2271 push_ss(ss, status) << "metadata is behind on " << total_behind << " shards";
2272 push_ss(ss, status) << "behind shards: " << "[" << shards_behind_set << "]";
2273 if (oldest) {
2274 push_ss(ss, status) << "oldest incremental change not applied: "
2275 << oldest->second << " [" << oldest->first << ']';
7c673cae
FG
2276 }
2277 }
2278
2279 flush_ss(ss, status);
2280}
2281
9f95a23c 2282static void get_data_sync_status(const rgw_zone_id& source_zone, list<string>& status, int tab)
7c673cae
FG
2283{
2284 stringstream ss;
2285
11fdf7f2
TL
2286 RGWZone *sz;
2287
1e59de90 2288 if (!(sz = static_cast<rgw::sal::RadosStore*>(driver)->svc()->zone->find_zone(source_zone))) {
7c673cae
FG
2289 push_ss(ss, status, tab) << string("zone not found");
2290 flush_ss(ss, status);
2291 return;
2292 }
7c673cae 2293
aee94f69 2294 if (!static_cast<rgw::sal::RadosStore*>(driver)->svc()->zone->zone_syncs_from(*sz)) {
7c673cae
FG
2295 push_ss(ss, status, tab) << string("not syncing from zone");
2296 flush_ss(ss, status);
2297 return;
2298 }
1e59de90 2299 RGWDataSyncStatusManager sync(static_cast<rgw::sal::RadosStore*>(driver), static_cast<rgw::sal::RadosStore*>(driver)->svc()->rados->get_async_processor(), source_zone, nullptr);
7c673cae 2300
b3b6e05e 2301 int ret = sync.init(dpp());
7c673cae
FG
2302 if (ret < 0) {
2303 push_ss(ss, status, tab) << string("failed to retrieve sync info: ") + cpp_strerror(-ret);
2304 flush_ss(ss, status);
2305 return;
2306 }
2307
2308 rgw_data_sync_status sync_status;
b3b6e05e 2309 ret = sync.read_sync_status(dpp(), &sync_status);
7c673cae
FG
2310 if (ret < 0 && ret != -ENOENT) {
2311 push_ss(ss, status, tab) << string("failed read sync status: ") + cpp_strerror(-ret);
2312 return;
2313 }
2314
28e407b8 2315 set<int> recovering_shards;
b3b6e05e 2316 ret = sync.read_recovering_shards(dpp(), sync_status.sync_info.num_shards, recovering_shards);
28e407b8
AA
2317 if (ret < 0 && ret != ENOENT) {
2318 push_ss(ss, status, tab) << string("failed read recovering shards: ") + cpp_strerror(-ret);
2319 return;
2320 }
2321
7c673cae
FG
2322 string status_str;
2323 switch (sync_status.sync_info.state) {
2324 case rgw_data_sync_info::StateInit:
2325 status_str = "init";
2326 break;
2327 case rgw_data_sync_info::StateBuildingFullSyncMaps:
2328 status_str = "preparing for full sync";
2329 break;
2330 case rgw_data_sync_info::StateSync:
2331 status_str = "syncing";
2332 break;
2333 default:
2334 status_str = "unknown";
2335 }
2336
2337 push_ss(ss, status, tab) << status_str;
2338
2339 uint64_t full_total = 0;
2340 uint64_t full_complete = 0;
2341
2342 int num_full = 0;
2343 int num_inc = 0;
2344 int total_shards = 0;
28e407b8 2345 set<int> shards_behind_set;
7c673cae
FG
2346
2347 for (auto marker_iter : sync_status.sync_markers) {
2348 full_total += marker_iter.second.total_entries;
2349 total_shards++;
2350 if (marker_iter.second.state == rgw_data_sync_marker::SyncState::FullSync) {
2351 num_full++;
2352 full_complete += marker_iter.second.pos;
11fdf7f2 2353 int shard_id = marker_iter.first;
28e407b8 2354 shards_behind_set.insert(shard_id);
7c673cae
FG
2355 } else {
2356 full_complete += marker_iter.second.total_entries;
2357 }
2358 if (marker_iter.second.state == rgw_data_sync_marker::SyncState::IncrementalSync) {
2359 num_inc++;
2360 }
2361 }
2362
2363 push_ss(ss, status, tab) << "full sync: " << num_full << "/" << total_shards << " shards";
2364
2365 if (num_full > 0) {
2366 push_ss(ss, status, tab) << "full sync: " << full_total - full_complete << " buckets to sync";
2367 }
2368
2369 push_ss(ss, status, tab) << "incremental sync: " << num_inc << "/" << total_shards << " shards";
2370
7c673cae
FG
2371 map<int, RGWDataChangesLogInfo> source_shards_info;
2372
b3b6e05e 2373 ret = sync.read_source_log_shards_info(dpp(), &source_shards_info);
7c673cae
FG
2374 if (ret < 0) {
2375 push_ss(ss, status, tab) << string("failed to fetch source sync status: ") + cpp_strerror(-ret);
2376 return;
2377 }
2378
2379 map<int, string> shards_behind;
2380
2381 for (auto local_iter : sync_status.sync_markers) {
2382 int shard_id = local_iter.first;
2383 auto iter = source_shards_info.find(shard_id);
2384
2385 if (iter == source_shards_info.end()) {
2386 /* huh? */
2387 derr << "ERROR: could not find remote sync shard status for shard_id=" << shard_id << dendl;
2388 continue;
2389 }
2390 auto master_marker = iter->second.marker;
2391 if (local_iter.second.state == rgw_data_sync_marker::SyncState::IncrementalSync &&
2392 master_marker > local_iter.second.marker) {
2393 shards_behind[shard_id] = local_iter.second.marker;
28e407b8 2394 shards_behind_set.insert(shard_id);
7c673cae
FG
2395 }
2396 }
2397
1e59de90
TL
2398 std::optional<std::pair<int, ceph::real_time>> oldest;
2399 if (!shards_behind.empty()) {
7c673cae 2400 map<int, rgw_datalog_shard_data> master_pos;
b3b6e05e 2401 ret = sync.read_source_log_shards_next(dpp(), shards_behind, &master_pos);
1e59de90 2402
7c673cae
FG
2403 if (ret < 0) {
2404 derr << "ERROR: failed to fetch next positions (" << cpp_strerror(-ret) << ")" << dendl;
2405 } else {
7c673cae
FG
2406 for (auto iter : master_pos) {
2407 rgw_datalog_shard_data& shard_data = iter.second;
1e59de90
TL
2408 if (shard_data.entries.empty()) {
2409 // there aren't any entries in this shard, so we're not really behind
2410 shards_behind.erase(iter.first);
2411 shards_behind_set.erase(iter.first);
2412 } else {
7c673cae 2413 rgw_datalog_entry& entry = shard_data.entries.front();
92f5a8d4
TL
2414 if (!oldest) {
2415 oldest.emplace(iter.first, entry.timestamp);
2416 } else if (!ceph::real_clock::is_zero(entry.timestamp) && entry.timestamp < oldest->second) {
2417 oldest.emplace(iter.first, entry.timestamp);
7c673cae
FG
2418 }
2419 }
2420 }
1e59de90
TL
2421 }
2422 }
7c673cae 2423
1e59de90
TL
2424 int total_behind = shards_behind.size() + (sync_status.sync_info.num_shards - num_inc);
2425 int total_recovering = recovering_shards.size();
2426
2427 if (total_behind == 0 && total_recovering == 0) {
2428 push_ss(ss, status, tab) << "data is caught up with source";
2429 } else if (total_behind > 0) {
2430 push_ss(ss, status, tab) << "data is behind on " << total_behind << " shards";
2431 push_ss(ss, status, tab) << "behind shards: " << "[" << shards_behind_set << "]" ;
2432 if (oldest) {
2433 push_ss(ss, status, tab) << "oldest incremental change not applied: "
2434 << oldest->second << " [" << oldest->first << ']';
7c673cae
FG
2435 }
2436 }
2437
28e407b8
AA
2438 if (total_recovering > 0) {
2439 push_ss(ss, status, tab) << total_recovering << " shards are recovering";
2440 push_ss(ss, status, tab) << "recovering shards: " << "[" << recovering_shards << "]";
2441 }
2442
7c673cae
FG
2443 flush_ss(ss, status);
2444}
2445
2446static void tab_dump(const string& header, int width, const list<string>& entries)
2447{
2448 string s = header;
2449
2450 for (auto e : entries) {
2451 cout << std::setw(width) << s << std::setw(1) << " " << e << std::endl;
2452 s.clear();
2453 }
2454}
2455
1e59de90
TL
2456// return features that are supported but not enabled
2457static auto get_disabled_features(const rgw::zone_features::set& enabled) {
2458 auto features = rgw::zone_features::set{rgw::zone_features::supported.begin(),
2459 rgw::zone_features::supported.end()};
2460 for (const auto& feature : enabled) {
2461 features.erase(feature);
2462 }
2463 return features;
2464}
2465
7c673cae
FG
2466
2467static void sync_status(Formatter *formatter)
2468{
1e59de90
TL
2469 const rgw::sal::ZoneGroup& zonegroup = driver->get_zone()->get_zonegroup();
2470 rgw::sal::Zone* zone = driver->get_zone();
7c673cae
FG
2471
2472 int width = 15;
2473
1e59de90 2474 cout << std::setw(width) << "realm" << std::setw(1) << " " << zone->get_realm_id() << " (" << zone->get_realm_name() << ")" << std::endl;
7c673cae 2475 cout << std::setw(width) << "zonegroup" << std::setw(1) << " " << zonegroup.get_id() << " (" << zonegroup.get_name() << ")" << std::endl;
1e59de90
TL
2476 cout << std::setw(width) << "zone" << std::setw(1) << " " << zone->get_id() << " (" << zone->get_name() << ")" << std::endl;
2477 cout << std::setw(width) << "current time" << std::setw(1) << " "
2478 << to_iso_8601(ceph::real_clock::now(), iso_8601_format::YMDhms) << std::endl;
2479
2480 const auto& rzg =
2481 static_cast<const rgw::sal::RadosZoneGroup&>(zonegroup).get_group();
2482
2483 cout << std::setw(width) << "zonegroup features enabled: " << rzg.enabled_features << std::endl;
2484 if (auto d = get_disabled_features(rzg.enabled_features); !d.empty()) {
2485 cout << std::setw(width) << " disabled: " << d << std::endl;
2486 }
7c673cae
FG
2487
2488 list<string> md_status;
2489
1e59de90 2490 if (driver->is_meta_master()) {
7c673cae
FG
2491 md_status.push_back("no sync (zone is master)");
2492 } else {
2493 get_md_sync_status(md_status);
2494 }
2495
2496 tab_dump("metadata sync", width, md_status);
2497
2498 list<string> data_status;
2499
1e59de90 2500 auto& zone_conn_map = static_cast<rgw::sal::RadosStore*>(driver)->svc()->zone->get_zone_conn_map();
11fdf7f2
TL
2501
2502 for (auto iter : zone_conn_map) {
9f95a23c 2503 const rgw_zone_id& source_id = iter.first;
7c673cae 2504 string source_str = "source: ";
9f95a23c 2505 string s = source_str + source_id.id;
1e59de90
TL
2506 std::unique_ptr<rgw::sal::Zone> sz;
2507 if (driver->get_zone()->get_zonegroup().get_zone_by_id(source_id.id, &sz) == 0) {
2508 s += string(" (") + sz->get_name() + ")";
7c673cae
FG
2509 }
2510 data_status.push_back(s);
2511 get_data_sync_status(source_id, data_status, source_str.size());
2512 }
2513
2514 tab_dump("data sync", width, data_status);
2515}
2516
28e407b8
AA
2517struct indented {
2518 int w; // indent width
11fdf7f2
TL
2519 std::string_view header;
2520 indented(int w, std::string_view header = "") : w(w), header(header) {}
28e407b8
AA
2521};
2522std::ostream& operator<<(std::ostream& out, const indented& h) {
2523 return out << std::setw(h.w) << h.header << std::setw(1) << ' ';
2524}
2525
1e59de90 2526static int bucket_source_sync_status(const DoutPrefixProvider *dpp, rgw::sal::RadosStore* driver, const RGWZone& zone,
28e407b8
AA
2527 const RGWZone& source, RGWRESTConn *conn,
2528 const RGWBucketInfo& bucket_info,
9f95a23c 2529 rgw_sync_bucket_pipe pipe,
28e407b8
AA
2530 int width, std::ostream& out)
2531{
9f95a23c 2532 out << indented{width, "source zone"} << source.id << " (" << source.name << ")" << std::endl;
28e407b8
AA
2533
2534 // syncing from this zone?
1e59de90 2535 if (!driver->svc()->zone->zone_syncs_from(zone, source)) {
9f95a23c 2536 out << indented{width} << "does not sync from zone\n";
28e407b8
AA
2537 return 0;
2538 }
9f95a23c
TL
2539
2540 if (!pipe.source.bucket) {
b3b6e05e 2541 ldpp_dout(dpp, -1) << __func__ << "(): missing source bucket" << dendl;
9f95a23c
TL
2542 return -EINVAL;
2543 }
2544
20effc67
TL
2545 std::unique_ptr<rgw::sal::Bucket> source_bucket;
2546 int r = init_bucket(nullptr, *pipe.source.bucket, &source_bucket);
9f95a23c 2547 if (r < 0) {
b3b6e05e 2548 ldpp_dout(dpp, -1) << "failed to read source bucket info: " << cpp_strerror(r) << dendl;
9f95a23c
TL
2549 return r;
2550 }
2551
1e59de90 2552 out << indented{width, "source bucket"} << source_bucket->get_key() << std::endl;
20effc67 2553 pipe.source.bucket = source_bucket->get_key();
28e407b8 2554
1e59de90 2555 pipe.dest.bucket = bucket_info.bucket;
9f95a23c 2556
1e59de90
TL
2557 uint64_t gen = 0;
2558 std::vector<rgw_bucket_shard_sync_info> shard_status;
28e407b8 2559
1e59de90
TL
2560 // check for full sync status
2561 rgw_bucket_sync_status full_status;
2562 r = rgw_read_bucket_full_sync_status(dpp, driver, pipe, &full_status, null_yield);
2563 if (r >= 0) {
2564 if (full_status.state == BucketSyncState::Init) {
2565 out << indented{width} << "init: bucket sync has not started\n";
2566 return 0;
2567 }
2568 if (full_status.state == BucketSyncState::Stopped) {
2569 out << indented{width} << "stopped: bucket sync is disabled\n";
2570 return 0;
2571 }
2572 if (full_status.state == BucketSyncState::Full) {
2573 out << indented{width} << "full sync: " << full_status.full.count << " objects completed\n";
2574 return 0;
2575 }
2576 gen = full_status.incremental_gen;
2577 shard_status.resize(full_status.shards_done_with_gen.size());
2578 } else if (r == -ENOENT) {
2579 // no full status, but there may be per-shard status from before upgrade
2580 const auto& logs = source_bucket->get_info().layout.logs;
2581 if (logs.empty()) {
2582 out << indented{width} << "init: bucket sync has not started\n";
2583 return 0;
2584 }
2585 const auto& log = logs.front();
2586 if (log.gen > 0) {
2587 // this isn't the backward-compatible case, so we just haven't started yet
2588 out << indented{width} << "init: bucket sync has not started\n";
2589 return 0;
2590 }
2591 if (log.layout.type != rgw::BucketLogType::InIndex) {
2592 ldpp_dout(dpp, -1) << "unrecognized log layout type " << log.layout.type << dendl;
2593 return -EINVAL;
28e407b8 2594 }
1e59de90
TL
2595 // use shard count from our log gen=0
2596 shard_status.resize(rgw::num_shards(log.layout.in_index));
2597 } else {
2598 lderr(driver->ctx()) << "failed to read bucket full sync status: " << cpp_strerror(r) << dendl;
2599 return r;
28e407b8
AA
2600 }
2601
1e59de90
TL
2602 r = rgw_read_bucket_inc_sync_status(dpp, driver, pipe, gen, &shard_status);
2603 if (r < 0) {
2604 lderr(driver->ctx()) << "failed to read bucket incremental sync status: " << cpp_strerror(r) << dendl;
2605 return r;
28e407b8 2606 }
28e407b8 2607
1e59de90
TL
2608 const int total_shards = shard_status.size();
2609
2610 out << indented{width} << "incremental sync on " << total_shards << " shards\n";
2611
2612 rgw_bucket_index_marker_info remote_info;
28e407b8 2613 BucketIndexShardsManager remote_markers;
1e59de90
TL
2614 r = rgw_read_remote_bilog_info(dpp, conn, source_bucket->get_key(),
2615 remote_info, remote_markers, null_yield);
28e407b8 2616 if (r < 0) {
b3b6e05e 2617 ldpp_dout(dpp, -1) << "failed to read remote log: " << cpp_strerror(r) << dendl;
28e407b8
AA
2618 return r;
2619 }
2620
2621 std::set<int> shards_behind;
1e59de90 2622 for (const auto& r : remote_markers.get()) {
28e407b8 2623 auto shard_id = r.first;
28e407b8
AA
2624 if (r.second.empty()) {
2625 continue; // empty bucket index shard
2626 }
1e59de90
TL
2627 if (shard_id >= total_shards) {
2628 // unexpected shard id. we don't have status for it, so we're behind
2629 shards_behind.insert(shard_id);
2630 continue;
2631 }
2632 auto& m = shard_status[shard_id];
2633 const auto pos = BucketIndexShardsManager::get_shard_marker(m.inc_marker.position);
2634 if (pos < r.second) {
28e407b8
AA
2635 shards_behind.insert(shard_id);
2636 }
2637 }
eafe8130 2638 if (!shards_behind.empty()) {
28e407b8
AA
2639 out << indented{width} << "bucket is behind on " << shards_behind.size() << " shards\n";
2640 out << indented{width} << "behind shards: [" << shards_behind << "]\n" ;
1e59de90 2641 } else {
eafe8130 2642 out << indented{width} << "bucket is caught up with source\n";
28e407b8
AA
2643 }
2644 return 0;
2645}
2646
9f95a23c
TL
2647void encode_json(const char *name, const RGWBucketSyncFlowManager::pipe_set& pset, Formatter *f)
2648{
2649 Formatter::ObjectSection top_section(*f, name);
2650 Formatter::ArraySection as(*f, "entries");
2651
2652 for (auto& pipe_handler : pset) {
2653 Formatter::ObjectSection hs(*f, "handler");
2654 encode_json("source", pipe_handler.source, f);
2655 encode_json("dest", pipe_handler.dest, f);
2656 }
2657}
2658
2659static std::vector<string> convert_bucket_set_to_str_vec(const std::set<rgw_bucket>& bs)
2660{
2661 std::vector<string> result;
2662 result.reserve(bs.size());
2663 for (auto& b : bs) {
2664 result.push_back(b.get_key());
2665 }
2666 return result;
2667}
2668
2669static void get_hint_entities(const std::set<rgw_zone_id>& zones, const std::set<rgw_bucket>& buckets,
2670 std::set<rgw_sync_bucket_entity> *hint_entities)
2671{
2672 for (auto& zone_id : zones) {
2673 for (auto& b : buckets) {
20effc67
TL
2674 std::unique_ptr<rgw::sal::Bucket> hint_bucket;
2675 int ret = init_bucket(nullptr, b, &hint_bucket);
9f95a23c 2676 if (ret < 0) {
b3b6e05e 2677 ldpp_dout(dpp(), 20) << "could not init bucket info for hint bucket=" << b << " ... skipping" << dendl;
9f95a23c
TL
2678 continue;
2679 }
2680
20effc67 2681 hint_entities->insert(rgw_sync_bucket_entity(zone_id, hint_bucket->get_key()));
9f95a23c
TL
2682 }
2683 }
2684}
2685
2686static rgw_zone_id resolve_zone_id(const string& s)
2687{
1e59de90
TL
2688 std::unique_ptr<rgw::sal::Zone> zone;
2689 int ret = driver->get_zone()->get_zonegroup().get_zone_by_id(s, &zone);
2690 if (ret < 0)
2691 ret = driver->get_zone()->get_zonegroup().get_zone_by_name(s, &zone);
2692 if (ret < 0)
9f95a23c 2693 return rgw_zone_id(s);
1e59de90
TL
2694
2695 return rgw_zone_id(zone->get_id());
9f95a23c
TL
2696}
2697
2698rgw_zone_id validate_zone_id(const rgw_zone_id& zone_id)
2699{
2700 return resolve_zone_id(zone_id.id);
2701}
2702
2703static int sync_info(std::optional<rgw_zone_id> opt_target_zone, std::optional<rgw_bucket> opt_bucket, Formatter *formatter)
2704{
1e59de90 2705 rgw_zone_id zone_id = opt_target_zone.value_or(driver->get_zone()->get_id());
9f95a23c 2706
1e59de90 2707 auto zone_policy_handler = driver->get_zone()->get_sync_policy_handler();
9f95a23c
TL
2708
2709 RGWBucketSyncPolicyHandlerRef bucket_handler;
2710
2711 std::optional<rgw_bucket> eff_bucket = opt_bucket;
2712
2713 auto handler = zone_policy_handler;
2714
2715 if (eff_bucket) {
20effc67 2716 std::unique_ptr<rgw::sal::Bucket> bucket;
9f95a23c 2717
20effc67 2718 int ret = init_bucket(nullptr, *eff_bucket, &bucket);
9f95a23c
TL
2719 if (ret < 0 && ret != -ENOENT) {
2720 cerr << "ERROR: init_bucket failed: " << cpp_strerror(-ret) << std::endl;
2721 return ret;
2722 }
2723
2724 if (ret >= 0) {
20effc67
TL
2725 rgw::sal::Attrs attrs = bucket->get_attrs();
2726 bucket_handler.reset(handler->alloc_child(bucket->get_info(), std::move(attrs)));
9f95a23c
TL
2727 } else {
2728 cerr << "WARNING: bucket not found, simulating result" << std::endl;
2729 bucket_handler.reset(handler->alloc_child(*eff_bucket, nullopt));
2730 }
2731
b3b6e05e 2732 ret = bucket_handler->init(dpp(), null_yield);
9f95a23c
TL
2733 if (ret < 0) {
2734 cerr << "ERROR: failed to init bucket sync policy handler: " << cpp_strerror(-ret) << " (ret=" << ret << ")" << std::endl;
2735 return ret;
2736 }
2737
2738 handler = bucket_handler;
2739 }
2740
2741 std::set<rgw_sync_bucket_pipe> sources;
2742 std::set<rgw_sync_bucket_pipe> dests;
2743
2744 handler->get_pipes(&sources, &dests, std::nullopt);
2745
2746 auto source_hints_vec = convert_bucket_set_to_str_vec(handler->get_source_hints());
2747 auto target_hints_vec = convert_bucket_set_to_str_vec(handler->get_target_hints());
2748
2749 std::set<rgw_sync_bucket_pipe> resolved_sources;
2750 std::set<rgw_sync_bucket_pipe> resolved_dests;
2751
2752 rgw_sync_bucket_entity self_entity(zone_id, opt_bucket);
2753
2754 set<rgw_zone_id> source_zones;
2755 set<rgw_zone_id> target_zones;
2756
20effc67 2757 zone_policy_handler->reflect(dpp(), nullptr, nullptr,
9f95a23c
TL
2758 nullptr, nullptr,
2759 &source_zones,
2760 &target_zones,
2761 false); /* relaxed: also get all zones that we allow to sync to/from */
2762
2763 std::set<rgw_sync_bucket_entity> hint_entities;
2764
2765 get_hint_entities(source_zones, handler->get_source_hints(), &hint_entities);
2766 get_hint_entities(target_zones, handler->get_target_hints(), &hint_entities);
2767
2768 for (auto& hint_entity : hint_entities) {
2769 if (!hint_entity.zone ||
2770 !hint_entity.bucket) {
2771 continue; /* shouldn't really happen */
2772 }
2773
2774 auto zid = validate_zone_id(*hint_entity.zone);
2775 auto& hint_bucket = *hint_entity.bucket;
2776
2777 RGWBucketSyncPolicyHandlerRef hint_bucket_handler;
1e59de90 2778 int r = driver->get_sync_policy_handler(dpp(), zid, hint_bucket, &hint_bucket_handler, null_yield);
9f95a23c 2779 if (r < 0) {
b3b6e05e 2780 ldpp_dout(dpp(), 20) << "could not get bucket sync policy handler for hint bucket=" << hint_bucket << " ... skipping" << dendl;
9f95a23c
TL
2781 continue;
2782 }
2783
2784 hint_bucket_handler->get_pipes(&resolved_dests,
2785 &resolved_sources,
2786 self_entity); /* flipping resolved dests and sources as these are
2787 relative to the remote entity */
2788 }
2789
2790 {
2791 Formatter::ObjectSection os(*formatter, "result");
2792 encode_json("sources", sources, formatter);
2793 encode_json("dests", dests, formatter);
2794 {
2795 Formatter::ObjectSection hints_section(*formatter, "hints");
2796 encode_json("sources", source_hints_vec, formatter);
2797 encode_json("dests", target_hints_vec, formatter);
2798 }
2799 {
2800 Formatter::ObjectSection resolved_hints_section(*formatter, "resolved-hints-1");
2801 encode_json("sources", resolved_sources, formatter);
2802 encode_json("dests", resolved_dests, formatter);
2803 }
2804 {
2805 Formatter::ObjectSection resolved_hints_section(*formatter, "resolved-hints");
2806 encode_json("sources", handler->get_resolved_source_hints(), formatter);
2807 encode_json("dests", handler->get_resolved_dest_hints(), formatter);
2808 }
2809 }
2810
2811 formatter->flush(cout);
2812
2813 return 0;
2814}
2815
1e59de90 2816static int bucket_sync_info(rgw::sal::Driver* driver, const RGWBucketInfo& info,
28e407b8
AA
2817 std::ostream& out)
2818{
1e59de90
TL
2819 const rgw::sal::ZoneGroup& zonegroup = driver->get_zone()->get_zonegroup();
2820 rgw::sal::Zone* zone = driver->get_zone();
28e407b8
AA
2821 constexpr int width = 15;
2822
1e59de90 2823 out << indented{width, "realm"} << zone->get_realm_id() << " (" << zone->get_realm_name() << ")\n";
28e407b8 2824 out << indented{width, "zonegroup"} << zonegroup.get_id() << " (" << zonegroup.get_name() << ")\n";
1e59de90 2825 out << indented{width, "zone"} << zone->get_id() << " (" << zone->get_name() << ")\n";
28e407b8
AA
2826 out << indented{width, "bucket"} << info.bucket << "\n\n";
2827
1e59de90 2828 if (!static_cast<rgw::sal::RadosStore*>(driver)->ctl()->bucket->bucket_imports_data(info.bucket, null_yield, dpp())) {
28e407b8
AA
2829 out << "Sync is disabled for bucket " << info.bucket.name << '\n';
2830 return 0;
2831 }
2832
9f95a23c
TL
2833 RGWBucketSyncPolicyHandlerRef handler;
2834
1e59de90 2835 int r = driver->get_sync_policy_handler(dpp(), std::nullopt, info.bucket, &handler, null_yield);
9f95a23c 2836 if (r < 0) {
b3b6e05e 2837 ldpp_dout(dpp(), -1) << "ERROR: failed to get policy handler for bucket (" << info.bucket << "): r=" << r << ": " << cpp_strerror(-r) << dendl;
9f95a23c
TL
2838 return r;
2839 }
2840
2841 auto& sources = handler->get_sources();
2842
2843 for (auto& m : sources) {
2844 auto& zone = m.first;
2845 out << indented{width, "source zone"} << zone << std::endl;
2846 for (auto& pipe_handler : m.second) {
2847 out << indented{width, "bucket"} << *pipe_handler.source.bucket << std::endl;
2848 }
2849 }
2850
2851 return 0;
2852}
2853
1e59de90 2854static int bucket_sync_status(rgw::sal::Driver* driver, const RGWBucketInfo& info,
9f95a23c
TL
2855 const rgw_zone_id& source_zone_id,
2856 std::optional<rgw_bucket>& opt_source_bucket,
2857 std::ostream& out)
2858{
1e59de90
TL
2859 const rgw::sal::ZoneGroup& zonegroup = driver->get_zone()->get_zonegroup();
2860 rgw::sal::Zone* zone = driver->get_zone();
9f95a23c
TL
2861 constexpr int width = 15;
2862
1e59de90 2863 out << indented{width, "realm"} << zone->get_realm_id() << " (" << zone->get_realm_name() << ")\n";
9f95a23c 2864 out << indented{width, "zonegroup"} << zonegroup.get_id() << " (" << zonegroup.get_name() << ")\n";
1e59de90
TL
2865 out << indented{width, "zone"} << zone->get_id() << " (" << zone->get_name() << ")\n";
2866 out << indented{width, "bucket"} << info.bucket << "\n";
2867 out << indented{width, "current time"}
2868 << to_iso_8601(ceph::real_clock::now(), iso_8601_format::YMDhms) << "\n\n";
2869
9f95a23c 2870
1e59de90 2871 if (!static_cast<rgw::sal::RadosStore*>(driver)->ctl()->bucket->bucket_imports_data(info.bucket, null_yield, dpp())) {
9f95a23c
TL
2872 out << "Sync is disabled for bucket " << info.bucket.name << " or bucket has no sync sources" << std::endl;
2873 return 0;
2874 }
2875
2876 RGWBucketSyncPolicyHandlerRef handler;
2877
1e59de90 2878 int r = driver->get_sync_policy_handler(dpp(), std::nullopt, info.bucket, &handler, null_yield);
9f95a23c 2879 if (r < 0) {
b3b6e05e 2880 ldpp_dout(dpp(), -1) << "ERROR: failed to get policy handler for bucket (" << info.bucket << "): r=" << r << ": " << cpp_strerror(-r) << dendl;
9f95a23c
TL
2881 return r;
2882 }
2883
2884 auto sources = handler->get_all_sources();
2885
1e59de90 2886 auto& zone_conn_map = static_cast<rgw::sal::RadosStore*>(driver)->svc()->zone->get_zone_conn_map();
9f95a23c
TL
2887 set<rgw_zone_id> zone_ids;
2888
28e407b8 2889 if (!source_zone_id.empty()) {
1e59de90
TL
2890 std::unique_ptr<rgw::sal::Zone> zone;
2891 int ret = driver->get_zone()->get_zonegroup().get_zone_by_id(source_zone_id.id, &zone);
2892 if (ret < 0) {
b3b6e05e 2893 ldpp_dout(dpp(), -1) << "Source zone not found in zonegroup "
28e407b8
AA
2894 << zonegroup.get_name() << dendl;
2895 return -EINVAL;
2896 }
11fdf7f2
TL
2897 auto c = zone_conn_map.find(source_zone_id);
2898 if (c == zone_conn_map.end()) {
1e59de90 2899 ldpp_dout(dpp(), -1) << "No connection to zone " << zone->get_name() << dendl;
28e407b8
AA
2900 return -EINVAL;
2901 }
9f95a23c
TL
2902 zone_ids.insert(source_zone_id);
2903 } else {
1e59de90
TL
2904 std::list<std::string> ids;
2905 int ret = driver->get_zone()->get_zonegroup().list_zones(ids);
2906 if (ret == 0) {
2907 for (const auto& entry : ids) {
2908 zone_ids.insert(entry);
2909 }
9f95a23c 2910 }
28e407b8
AA
2911 }
2912
9f95a23c 2913 for (auto& zone_id : zone_ids) {
1e59de90
TL
2914 auto z = static_cast<rgw::sal::RadosStore*>(driver)->svc()->zone->get_zonegroup().zones.find(zone_id.id);
2915 if (z == static_cast<rgw::sal::RadosStore*>(driver)->svc()->zone->get_zonegroup().zones.end()) { /* should't happen */
9f95a23c
TL
2916 continue;
2917 }
2918 auto c = zone_conn_map.find(zone_id.id);
2919 if (c == zone_conn_map.end()) { /* should't happen */
2920 continue;
2921 }
2922
2923 for (auto& entry : sources) {
2924 auto& pipe = entry.second;
2925 if (opt_source_bucket &&
2926 pipe.source.bucket != opt_source_bucket) {
2927 continue;
2928 }
2929 if (pipe.source.zone.value_or(rgw_zone_id()) == z->second.id) {
1e59de90 2930 bucket_source_sync_status(dpp(), static_cast<rgw::sal::RadosStore*>(driver), static_cast<rgw::sal::RadosStore*>(driver)->svc()->zone->get_zone(), z->second,
9f95a23c
TL
2931 c->second,
2932 info, pipe,
2933 width, out);
2934 }
28e407b8
AA
2935 }
2936 }
9f95a23c 2937
28e407b8
AA
2938 return 0;
2939}
2940
31f18b77 2941static void parse_tier_config_param(const string& s, map<string, string, ltstr_nocase>& out)
7c673cae 2942{
11fdf7f2
TL
2943 int level = 0;
2944 string cur_conf;
7c673cae 2945 list<string> confs;
11fdf7f2
TL
2946 for (auto c : s) {
2947 if (c == ',') {
2948 if (level == 0) {
2949 confs.push_back(cur_conf);
2950 cur_conf.clear();
2951 continue;
2952 }
2953 }
2954 if (c == '{') {
2955 ++level;
2956 } else if (c == '}') {
2957 --level;
2958 }
2959 cur_conf += c;
2960 }
2961 if (!cur_conf.empty()) {
2962 confs.push_back(cur_conf);
2963 }
2964
7c673cae
FG
2965 for (auto c : confs) {
2966 ssize_t pos = c.find("=");
2967 if (pos < 0) {
2968 out[c] = "";
2969 } else {
2970 out[c.substr(0, pos)] = c.substr(pos + 1);
2971 }
2972 }
2973}
2974
11fdf7f2
TL
2975static int check_pool_support_omap(const rgw_pool& pool)
2976{
2977 librados::IoCtx io_ctx;
1e59de90 2978 int ret = static_cast<rgw::sal::RadosStore*>(driver)->getRados()->get_rados_handle()->ioctx_create(pool.to_str().c_str(), io_ctx);
11fdf7f2
TL
2979 if (ret < 0) {
2980 // the pool may not exist at this moment, we have no way to check if it supports omap.
2981 return 0;
2982 }
2983
2984 ret = io_ctx.omap_clear("__omap_test_not_exist_oid__");
2985 if (ret == -EOPNOTSUPP) {
2986 io_ctx.close();
2987 return ret;
2988 }
2989 io_ctx.close();
2990 return 0;
2991}
2992
1e59de90 2993int check_reshard_bucket_params(rgw::sal::Driver* driver,
31f18b77
FG
2994 const string& bucket_name,
2995 const string& tenant,
2996 const string& bucket_id,
2997 bool num_shards_specified,
2998 int num_shards,
2999 int yes_i_really_mean_it,
20effc67 3000 std::unique_ptr<rgw::sal::Bucket>* bucket)
31f18b77
FG
3001{
3002 if (bucket_name.empty()) {
3003 cerr << "ERROR: bucket not specified" << std::endl;
3004 return -EINVAL;
7c673cae
FG
3005 }
3006
31f18b77
FG
3007 if (!num_shards_specified) {
3008 cerr << "ERROR: --num-shards not specified" << std::endl;
3009 return -EINVAL;
7c673cae
FG
3010 }
3011
1e59de90
TL
3012 if (num_shards > (int)static_cast<rgw::sal::RadosStore*>(driver)->getRados()->get_max_bucket_shards()) {
3013 cerr << "ERROR: num_shards too high, max value: " << static_cast<rgw::sal::RadosStore*>(driver)->getRados()->get_max_bucket_shards() << std::endl;
9f95a23c
TL
3014 return -EINVAL;
3015 }
3016
3017 if (num_shards < 0) {
3018 cerr << "ERROR: num_shards must be non-negative integer" << std::endl;
31f18b77 3019 return -EINVAL;
7c673cae
FG
3020 }
3021
20effc67 3022 int ret = init_bucket(nullptr, tenant, bucket_name, bucket_id, bucket);
31f18b77
FG
3023 if (ret < 0) {
3024 cerr << "ERROR: could not init bucket: " << cpp_strerror(-ret) << std::endl;
f64942e4 3025 return ret;
7c673cae 3026 }
7c673cae 3027
f38dd50b
TL
3028 if (! is_layout_reshardable((*bucket)->get_info().layout)) {
3029 std::cerr << "Bucket '" << (*bucket)->get_name() <<
3030 "' currently has layout '" <<
3031 current_layout_desc((*bucket)->get_info().layout) <<
3032 "', which does not support resharding." << std::endl;
3033 return -EINVAL;
3034 }
3035
1e59de90 3036 int num_source_shards = rgw::current_num_shards((*bucket)->get_info().layout);
7c673cae 3037
31f18b77
FG
3038 if (num_shards <= num_source_shards && !yes_i_really_mean_it) {
3039 cerr << "num shards is less or equal to current shards count" << std::endl
3040 << "do you really mean it? (requires --yes-i-really-mean-it)" << std::endl;
3041 return -EINVAL;
7c673cae 3042 }
31f18b77
FG
3043 return 0;
3044}
7c673cae 3045
11fdf7f2
TL
3046static int scan_totp(CephContext *cct, ceph::real_time& now, rados::cls::otp::otp_info_t& totp, vector<string>& pins,
3047 time_t *pofs)
3048{
3049#define MAX_TOTP_SKEW_HOURS (24 * 7)
11fdf7f2
TL
3050 time_t start_time = ceph::real_clock::to_time_t(now);
3051 time_t time_ofs = 0, time_ofs_abs = 0;
3052 time_t step_size = totp.step_size;
3053 if (step_size == 0) {
3054 step_size = OATH_TOTP_DEFAULT_TIME_STEP_SIZE;
3055 }
3056 uint32_t count = 0;
3057 int sign = 1;
3058
3059 uint32_t max_skew = MAX_TOTP_SKEW_HOURS * 3600;
3060
3061 while (time_ofs_abs < max_skew) {
3062 int rc = oath_totp_validate2(totp.seed_bin.c_str(), totp.seed_bin.length(),
3063 start_time,
3064 step_size,
3065 time_ofs,
3066 1,
3067 nullptr,
3068 pins[0].c_str());
3069 if (rc != OATH_INVALID_OTP) {
3070 rc = oath_totp_validate2(totp.seed_bin.c_str(), totp.seed_bin.length(),
3071 start_time,
3072 step_size,
3073 time_ofs - step_size, /* smaller time_ofs moves time forward */
3074 1,
3075 nullptr,
3076 pins[1].c_str());
3077 if (rc != OATH_INVALID_OTP) {
3078 *pofs = time_ofs - step_size + step_size * totp.window / 2;
b3b6e05e 3079 ldpp_dout(dpp(), 20) << "found at time=" << start_time - time_ofs << " time_ofs=" << time_ofs << dendl;
11fdf7f2
TL
3080 return 0;
3081 }
3082 }
3083 sign = -sign;
3084 time_ofs_abs = (++count) * step_size;
3085 time_ofs = sign * time_ofs_abs;
3086 }
3087
3088 return -ENOENT;
3089}
7c673cae 3090
f67539c2 3091static int trim_sync_error_log(int shard_id, const string& marker, int delay_ms)
91327a77
AA
3092{
3093 auto oid = RGWSyncErrorLogger::get_shard_oid(RGW_SYNC_ERROR_LOG_SHARD_PREFIX,
3094 shard_id);
3095 // call cls_log_trim() until it returns -ENODATA
3096 for (;;) {
1e59de90 3097 int ret = static_cast<rgw::sal::RadosStore*>(driver)->svc()->cls->timelog.trim(dpp(), oid, {}, {}, {}, marker, nullptr,
f67539c2 3098 null_yield);
91327a77
AA
3099 if (ret == -ENODATA) {
3100 return 0;
3101 }
3102 if (ret < 0) {
3103 return ret;
3104 }
3105 if (delay_ms) {
3106 std::this_thread::sleep_for(std::chrono::milliseconds(delay_ms));
3107 }
3108 }
3109 // unreachable
3110}
3111
9f95a23c
TL
3112static bool symmetrical_flow_opt(const string& opt)
3113{
3114 return (opt == "symmetrical" || opt == "symmetric");
3115}
3116
3117static bool directional_flow_opt(const string& opt)
3118{
3119 return (opt == "directional" || opt == "direction");
3120}
3121
3122template <class T>
3123static bool require_opt(std::optional<T> opt, bool extra_check = true)
3124{
3125 if (!opt || !extra_check) {
3126 return false;
3127 }
3128 return true;
3129}
3130
3131template <class T>
3132static bool require_non_empty_opt(std::optional<T> opt, bool extra_check = true)
3133{
3134 if (!opt || opt->empty() || !extra_check) {
3135 return false;
3136 }
3137 return true;
3138}
3139
3140template <class T>
3141static void show_result(T& obj,
3142 Formatter *formatter,
3143 ostream& os)
3144{
3145 encode_json("obj", obj, formatter);
3146
3147 formatter->flush(cout);
3148}
3149
3150void init_optional_bucket(std::optional<rgw_bucket>& opt_bucket,
3151 std::optional<string>& opt_tenant,
3152 std::optional<string>& opt_bucket_name,
3153 std::optional<string>& opt_bucket_id)
3154{
3155 if (opt_tenant || opt_bucket_name || opt_bucket_id) {
3156 opt_bucket.emplace();
3157 if (opt_tenant) {
3158 opt_bucket->tenant = *opt_tenant;
3159 }
3160 if (opt_bucket_name) {
3161 opt_bucket->name = *opt_bucket_name;
3162 }
3163 if (opt_bucket_id) {
3164 opt_bucket->bucket_id = *opt_bucket_id;
3165 }
3166 }
3167}
3168
3169class SyncPolicyContext
3170{
1e59de90 3171 rgw::sal::ConfigStore* cfgstore;
9f95a23c 3172 RGWZoneGroup zonegroup;
1e59de90 3173 std::unique_ptr<rgw::sal::ZoneGroupWriter> zonegroup_writer;
9f95a23c 3174
20effc67
TL
3175 std::optional<rgw_bucket> b;
3176 std::unique_ptr<rgw::sal::Bucket> bucket;
9f95a23c
TL
3177
3178 rgw_sync_policy_info *policy{nullptr};
3179
3180 std::optional<rgw_user> owner;
3181
3182public:
1e59de90
TL
3183 SyncPolicyContext(rgw::sal::ConfigStore* cfgstore,
3184 std::optional<rgw_bucket> _bucket)
3185 : cfgstore(cfgstore), b(std::move(_bucket)) {}
9f95a23c 3186
1e59de90
TL
3187 int init(const string& zonegroup_id, const string& zonegroup_name) {
3188 int ret = rgw::read_zonegroup(dpp(), null_yield, cfgstore,
3189 zonegroup_id, zonegroup_name,
3190 zonegroup, &zonegroup_writer);
9f95a23c
TL
3191 if (ret < 0) {
3192 cerr << "failed to init zonegroup: " << cpp_strerror(-ret) << std::endl;
3193 return ret;
3194 }
3195
20effc67 3196 if (!b) {
9f95a23c
TL
3197 policy = &zonegroup.sync_policy;
3198 return 0;
3199 }
3200
20effc67 3201 ret = init_bucket(nullptr, *b, &bucket);
9f95a23c
TL
3202 if (ret < 0) {
3203 cerr << "ERROR: could not init bucket: " << cpp_strerror(-ret) << std::endl;
3204 return ret;
3205 }
3206
20effc67 3207 owner = bucket->get_info().owner;
9f95a23c 3208
20effc67 3209 if (!bucket->get_info().sync_policy) {
9f95a23c 3210 rgw_sync_policy_info new_policy;
20effc67 3211 bucket->get_info().set_sync_policy(std::move(new_policy));
9f95a23c
TL
3212 }
3213
20effc67 3214 policy = &(*bucket->get_info().sync_policy);
9f95a23c
TL
3215
3216 return 0;
3217 }
3218
3219 int write_policy() {
20effc67 3220 if (!b) {
1e59de90 3221 int ret = zonegroup_writer->write(dpp(), null_yield, zonegroup);
9f95a23c
TL
3222 if (ret < 0) {
3223 cerr << "failed to update zonegroup: " << cpp_strerror(-ret) << std::endl;
3224 return -ret;
3225 }
3226 return 0;
3227 }
3228
20effc67 3229 int ret = bucket->put_info(dpp(), false, real_time());
9f95a23c 3230 if (ret < 0) {
1e59de90 3231 cerr << "failed to driver bucket info: " << cpp_strerror(-ret) << std::endl;
9f95a23c
TL
3232 return -ret;
3233 }
3234
3235 return 0;
3236 }
3237
3238 rgw_sync_policy_info& get_policy() {
3239 return *policy;
3240 }
3241
3242 std::optional<rgw_user>& get_owner() {
3243 return owner;
3244 }
3245};
3246
3247void resolve_zone_id_opt(std::optional<string>& zone_name, std::optional<rgw_zone_id>& zone_id)
3248{
3249 if (!zone_name || zone_id) {
3250 return;
3251 }
3252 zone_id.emplace();
1e59de90
TL
3253 std::unique_ptr<rgw::sal::Zone> zone;
3254 int ret = driver->get_zone()->get_zonegroup().get_zone_by_name(*zone_name, &zone);
3255 if (ret < 0) {
9f95a23c
TL
3256 cerr << "WARNING: cannot find source zone id for name=" << *zone_name << std::endl;
3257 zone_id = rgw_zone_id(*zone_name);
1e59de90
TL
3258 } else {
3259 zone_id->id = zone->get_id();
9f95a23c
TL
3260 }
3261}
3262void resolve_zone_ids_opt(std::optional<vector<string> >& names, std::optional<vector<rgw_zone_id> >& ids)
3263{
3264 if (!names || ids) {
3265 return;
3266 }
3267 ids.emplace();
3268 for (auto& name : *names) {
3269 rgw_zone_id zid;
1e59de90
TL
3270 std::unique_ptr<rgw::sal::Zone> zone;
3271 int ret = driver->get_zone()->get_zonegroup().get_zone_by_name(name, &zone);
3272 if (ret < 0) {
9f95a23c
TL
3273 cerr << "WARNING: cannot find source zone id for name=" << name << std::endl;
3274 zid = rgw_zone_id(name);
1e59de90
TL
3275 } else {
3276 zid.id = zone->get_id();
9f95a23c
TL
3277 }
3278 ids->push_back(zid);
3279 }
3280}
3281
3282static vector<rgw_zone_id> zone_ids_from_str(const string& val)
3283{
3284 vector<rgw_zone_id> result;
3285 vector<string> v;
3286 get_str_vec(val, v);
3287 for (auto& z : v) {
3288 result.push_back(rgw_zone_id(z));
3289 }
3290 return result;
11fdf7f2
TL
3291}
3292
9f95a23c
TL
3293class JSONFormatter_PrettyZone : public JSONFormatter {
3294 class Handler : public JSONEncodeFilter::Handler<rgw_zone_id> {
3295 void encode_json(const char *name, const void *pval, ceph::Formatter *f) const override {
3296 auto zone_id = *(static_cast<const rgw_zone_id *>(pval));
3297 string zone_name;
1e59de90
TL
3298 std::unique_ptr<rgw::sal::Zone> zone;
3299 if (driver->get_zone()->get_zonegroup().get_zone_by_id(zone_id.id, &zone) == 0) {
3300 zone_name = zone->get_name();
9f95a23c
TL
3301 } else {
3302 cerr << "WARNING: cannot find zone name for id=" << zone_id << std::endl;
3303 zone_name = zone_id.id;
3304 }
3305
3306 ::encode_json(name, zone_name, f);
3307 }
3308 } zone_id_type_handler;
3309
3310 JSONEncodeFilter encode_filter;
3311public:
3312 JSONFormatter_PrettyZone(bool pretty_format) : JSONFormatter(pretty_format) {
3313 encode_filter.register_type(&zone_id_type_handler);
3314 }
3315
3316 void *get_external_feature_handler(const std::string& feature) override {
3317 if (feature != "JSONEncodeFilter") {
3318 return nullptr;
3319 }
3320 return &encode_filter;
3321 }
3322};
3323
20effc67
TL
3324void init_realm_param(CephContext *cct, string& var, std::optional<string>& opt_var, const string& conf_name)
3325{
3326 var = cct->_conf.get_val<string>(conf_name);
3327 if (!var.empty()) {
3328 opt_var = var;
3329 }
3330}
3331
7c673cae 3332int main(int argc, const char **argv)
7c673cae 3333{
20effc67 3334 auto args = argv_to_vec(argc, argv);
11fdf7f2
TL
3335 if (args.empty()) {
3336 cerr << argv[0] << ": -h or --help for usage" << std::endl;
3337 exit(1);
3338 }
3339 if (ceph_argparse_need_usage(args)) {
3340 usage();
3341 exit(0);
3342 }
7c673cae 3343
1e59de90
TL
3344 auto cct = rgw_global_init(NULL, args, CEPH_ENTITY_TYPE_CLIENT,
3345 CODE_ENVIRONMENT_UTILITY, 0);
7c673cae
FG
3346
3347 // for region -> zonegroup conversion (must happen before common_init_finish())
11fdf7f2
TL
3348 if (!g_conf()->rgw_region.empty() && g_conf()->rgw_zonegroup.empty()) {
3349 g_conf().set_val_or_die("rgw_zonegroup", g_conf()->rgw_region.c_str());
7c673cae
FG
3350 }
3351
20effc67
TL
3352 rgw_user user_id_arg;
3353 std::unique_ptr<rgw::sal::User> user;
7c673cae 3354 string tenant;
f67539c2 3355 string user_ns;
9f95a23c 3356 rgw_user new_user_id;
7c673cae
FG
3357 std::string access_key, secret_key, user_email, display_name;
3358 std::string bucket_name, pool_name, object;
3359 rgw_pool pool;
3360 std::string date, subuser, access, format;
3361 std::string start_date, end_date;
3362 std::string key_type_str;
3363 std::string period_id, period_epoch, remote, url;
20effc67 3364 std::optional<string> opt_region;
11fdf7f2 3365 std::string master_zone;
7c673cae 3366 std::string realm_name, realm_id, realm_new_name;
20effc67 3367 std::optional<string> opt_realm_name, opt_realm_id;
7c673cae 3368 std::string zone_name, zone_id, zone_new_name;
20effc67 3369 std::optional<string> opt_zone_name, opt_zone_id;
7c673cae 3370 std::string zonegroup_name, zonegroup_id, zonegroup_new_name;
20effc67 3371 std::optional<string> opt_zonegroup_name, opt_zonegroup_id;
7c673cae 3372 std::string api_name;
1e59de90 3373 std::string role_name, path, assume_role_doc, policy_name, perm_policy_doc, path_prefix, max_session_duration;
11fdf7f2
TL
3374 std::string redirect_zone;
3375 bool redirect_zone_set = false;
7c673cae
FG
3376 list<string> endpoints;
3377 int tmp_int;
3378 int sync_from_all_specified = false;
3379 bool sync_from_all = false;
3380 list<string> sync_from;
3381 list<string> sync_from_rm;
7c673cae
FG
3382 int is_master_int;
3383 int set_default = 0;
3384 bool is_master = false;
3385 bool is_master_set = false;
3386 int read_only_int;
3387 bool read_only = false;
3388 int is_read_only_set = false;
3389 int commit = false;
3390 int staging = false;
3391 int key_type = KEY_TYPE_UNDEFINED;
20effc67 3392 std::unique_ptr<rgw::sal::Bucket> bucket;
7c673cae
FG
3393 uint32_t perm_mask = 0;
3394 RGWUserInfo info;
9f95a23c 3395 OPT opt_cmd = OPT::NO_CMD;
7c673cae
FG
3396 int gen_access_key = 0;
3397 int gen_secret_key = 0;
3398 bool set_perm = false;
3399 bool set_temp_url_key = false;
3400 map<int, string> temp_url_keys;
3401 string bucket_id;
9f95a23c 3402 string new_bucket_name;
f67539c2
TL
3403 std::unique_ptr<Formatter> formatter;
3404 std::unique_ptr<Formatter> zone_formatter;
7c673cae 3405 int purge_data = false;
7c673cae
FG
3406 int pretty_format = false;
3407 int show_log_entries = true;
3408 int show_log_sum = true;
3409 int skip_zero_entries = false; // log show
3410 int purge_keys = false;
3411 int yes_i_really_mean_it = false;
3412 int delete_child_objects = false;
3413 int fix = false;
3414 int remove_bad = false;
3415 int check_head_obj_locator = false;
3416 int max_buckets = -1;
3417 bool max_buckets_specified = false;
3418 map<string, bool> categories;
3419 string caps;
3420 int check_objects = false;
7c673cae
FG
3421 RGWBucketAdminOpState bucket_op;
3422 string infile;
3423 string metadata_key;
3424 RGWObjVersionTracker objv_tracker;
3425 string marker;
3426 string start_marker;
3427 string end_marker;
3428 int max_entries = -1;
181888fb 3429 bool max_entries_specified = false;
7c673cae
FG
3430 int admin = false;
3431 bool admin_specified = false;
3432 int system = false;
3433 bool system_specified = false;
3434 int shard_id = -1;
3435 bool specified_shard_id = false;
7c673cae
FG
3436 string client_id;
3437 string op_id;
7c673cae
FG
3438 string op_mask_str;
3439 string quota_scope;
20effc67 3440 string ratelimit_scope;
1e59de90 3441 std::string objects_file;
7c673cae
FG
3442 string object_version;
3443 string placement_id;
9f95a23c 3444 std::optional<string> opt_storage_class;
7c673cae
FG
3445 list<string> tags;
3446 list<string> tags_add;
3447 list<string> tags_rm;
39ae355f
TL
3448 int placement_inline_data = true;
3449 bool placement_inline_data_specified = false;
7c673cae
FG
3450
3451 int64_t max_objects = -1;
3452 int64_t max_size = -1;
20effc67
TL
3453 int64_t max_read_ops = 0;
3454 int64_t max_write_ops = 0;
3455 int64_t max_read_bytes = 0;
3456 int64_t max_write_bytes = 0;
7c673cae
FG
3457 bool have_max_objects = false;
3458 bool have_max_size = false;
20effc67
TL
3459 bool have_max_write_ops = false;
3460 bool have_max_read_ops = false;
3461 bool have_max_write_bytes = false;
3462 bool have_max_read_bytes = false;
7c673cae 3463 int include_all = false;
494da23a 3464 int allow_unordered = false;
7c673cae
FG
3465
3466 int sync_stats = false;
94b18763 3467 int reset_stats = false;
7c673cae
FG
3468 int bypass_gc = false;
3469 int warnings_only = false;
3470 int inconsistent_index = false;
3471
3472 int verbose = false;
3473
3474 int extra_info = false;
3475
3476 uint64_t min_rewrite_size = 4 * 1024 * 1024;
3477 uint64_t max_rewrite_size = ULLONG_MAX;
3478 uint64_t min_rewrite_stripe_size = 0;
3479
11fdf7f2 3480 BIIndexType bi_index_type = BIIndexType::Plain;
f67539c2 3481 std::optional<log_type> opt_log_type;
7c673cae
FG
3482
3483 string job_id;
3484 int num_shards = 0;
3485 bool num_shards_specified = false;
9f95a23c 3486 std::optional<int> bucket_index_max_shards;
39ae355f 3487
7c673cae 3488 int max_concurrent_ios = 32;
aee94f69
TL
3489 ceph::timespan min_age = std::chrono::hours(1);
3490 bool hide_progress = false;
3491 bool dump_keys = false;
7c673cae 3492 uint64_t orphan_stale_secs = (24 * 3600);
11fdf7f2 3493 int detail = false;
7c673cae
FG
3494
3495 std::string val;
3496 std::ostringstream errs;
3497 string err;
7c673cae
FG
3498
3499 string source_zone_name;
9f95a23c 3500 rgw_zone_id source_zone; /* zone id */
7c673cae
FG
3501
3502 string tier_type;
3503 bool tier_type_specified = false;
3504
31f18b77
FG
3505 map<string, string, ltstr_nocase> tier_config_add;
3506 map<string, string, ltstr_nocase> tier_config_rm;
7c673cae
FG
3507
3508 boost::optional<string> index_pool;
3509 boost::optional<string> data_pool;
3510 boost::optional<string> data_extra_pool;
f67539c2 3511 rgw::BucketIndexType placement_index_type = rgw::BucketIndexType::Normal;
7c673cae
FG
3512 bool index_type_specified = false;
3513
3514 boost::optional<std::string> compression_type;
3515
11fdf7f2
TL
3516 string totp_serial;
3517 string totp_seed;
3518 string totp_seed_type = "hex";
3519 vector<string> totp_pin;
3520 int totp_seconds = 0;
3521 int totp_window = 0;
91327a77
AA
3522 int trim_delay_ms = 0;
3523
11fdf7f2 3524 string topic_name;
aee94f69 3525 string notification_id;
11fdf7f2 3526 string sub_name;
11fdf7f2 3527 string event_id;
9f95a23c 3528
1e59de90 3529 std::optional<uint64_t> gen;
f67539c2
TL
3530 std::optional<std::string> str_script_ctx;
3531 std::optional<std::string> script_package;
3532 int allow_compilation = false;
3533
9f95a23c
TL
3534 std::optional<string> opt_group_id;
3535 std::optional<string> opt_status;
3536 std::optional<string> opt_flow_type;
3537 std::optional<vector<string> > opt_zone_names;
3538 std::optional<vector<rgw_zone_id> > opt_zone_ids;
3539 std::optional<string> opt_flow_id;
3540 std::optional<string> opt_source_zone_name;
3541 std::optional<rgw_zone_id> opt_source_zone_id;
3542 std::optional<string> opt_dest_zone_name;
3543 std::optional<rgw_zone_id> opt_dest_zone_id;
3544 std::optional<vector<string> > opt_source_zone_names;
3545 std::optional<vector<rgw_zone_id> > opt_source_zone_ids;
3546 std::optional<vector<string> > opt_dest_zone_names;
3547 std::optional<vector<rgw_zone_id> > opt_dest_zone_ids;
3548 std::optional<string> opt_pipe_id;
3549 std::optional<rgw_bucket> opt_bucket;
3550 std::optional<string> opt_tenant;
3551 std::optional<string> opt_bucket_name;
3552 std::optional<string> opt_bucket_id;
3553 std::optional<rgw_bucket> opt_source_bucket;
3554 std::optional<string> opt_source_tenant;
3555 std::optional<string> opt_source_bucket_name;
3556 std::optional<string> opt_source_bucket_id;
3557 std::optional<rgw_bucket> opt_dest_bucket;
3558 std::optional<string> opt_dest_tenant;
3559 std::optional<string> opt_dest_bucket_name;
3560 std::optional<string> opt_dest_bucket_id;
3561 std::optional<string> opt_effective_zone_name;
3562 std::optional<rgw_zone_id> opt_effective_zone_id;
3563
3564 std::optional<string> opt_prefix;
3565 std::optional<string> opt_prefix_rm;
3566
3567 std::optional<int> opt_priority;
3568 std::optional<string> opt_mode;
3569 std::optional<rgw_user> opt_dest_owner;
f67539c2
TL
3570 ceph::timespan opt_retry_delay_ms = std::chrono::milliseconds(2000);
3571 ceph::timespan opt_timeout_sec = std::chrono::seconds(60);
11fdf7f2 3572
1e59de90
TL
3573 std::optional<std::string> inject_error_at;
3574 std::optional<int> inject_error_code;
3575 std::optional<std::string> inject_abort_at;
05a536ef
TL
3576 std::optional<std::string> inject_delay_at;
3577 ceph::timespan inject_delay = std::chrono::milliseconds(2000);
1e59de90
TL
3578
3579 rgw::zone_features::set enable_features;
3580 rgw::zone_features::set disable_features;
3581
9f95a23c 3582 SimpleCmd cmd(all_cmds, cmd_aliases);
20effc67 3583 bool raw_storage_op = false;
9f95a23c 3584
f67539c2
TL
3585 std::optional<std::string> rgw_obj_fs; // radoslist field separator
3586
20effc67
TL
3587 init_realm_param(cct.get(), realm_id, opt_realm_id, "rgw_realm_id");
3588 init_realm_param(cct.get(), zonegroup_id, opt_zonegroup_id, "rgw_zonegroup_id");
3589 init_realm_param(cct.get(), zone_id, opt_zone_id, "rgw_zone_id");
3590
7c673cae
FG
3591 for (std::vector<const char*>::iterator i = args.begin(); i != args.end(); ) {
3592 if (ceph_argparse_double_dash(args, i)) {
3593 break;
7c673cae 3594 } else if (ceph_argparse_witharg(args, i, &val, "-i", "--uid", (char*)NULL)) {
20effc67
TL
3595 user_id_arg.from_str(val);
3596 if (user_id_arg.empty()) {
eafe8130
TL
3597 cerr << "no value for uid" << std::endl;
3598 exit(1);
3599 }
20effc67 3600 } else if (ceph_argparse_witharg(args, i, &val, "--new-uid", (char*)NULL)) {
9f95a23c 3601 new_user_id.from_str(val);
7c673cae
FG
3602 } else if (ceph_argparse_witharg(args, i, &val, "--tenant", (char*)NULL)) {
3603 tenant = val;
9f95a23c 3604 opt_tenant = val;
f67539c2
TL
3605 } else if (ceph_argparse_witharg(args, i, &val, "--user_ns", (char*)NULL)) {
3606 user_ns = val;
7c673cae
FG
3607 } else if (ceph_argparse_witharg(args, i, &val, "--access-key", (char*)NULL)) {
3608 access_key = val;
3609 } else if (ceph_argparse_witharg(args, i, &val, "--subuser", (char*)NULL)) {
3610 subuser = val;
3611 } else if (ceph_argparse_witharg(args, i, &val, "--secret", "--secret-key", (char*)NULL)) {
3612 secret_key = val;
3613 } else if (ceph_argparse_witharg(args, i, &val, "-e", "--email", (char*)NULL)) {
3614 user_email = val;
7c673cae
FG
3615 } else if (ceph_argparse_witharg(args, i, &val, "-n", "--display-name", (char*)NULL)) {
3616 display_name = val;
3617 } else if (ceph_argparse_witharg(args, i, &val, "-b", "--bucket", (char*)NULL)) {
3618 bucket_name = val;
9f95a23c 3619 opt_bucket_name = val;
7c673cae
FG
3620 } else if (ceph_argparse_witharg(args, i, &val, "-p", "--pool", (char*)NULL)) {
3621 pool_name = val;
3622 pool = rgw_pool(pool_name);
3623 } else if (ceph_argparse_witharg(args, i, &val, "-o", "--object", (char*)NULL)) {
3624 object = val;
1e59de90
TL
3625 } else if (ceph_argparse_witharg(args, i, &val, "--objects-file", (char*)NULL)) {
3626 objects_file = val;
7c673cae
FG
3627 } else if (ceph_argparse_witharg(args, i, &val, "--object-version", (char*)NULL)) {
3628 object_version = val;
3629 } else if (ceph_argparse_witharg(args, i, &val, "--client-id", (char*)NULL)) {
3630 client_id = val;
3631 } else if (ceph_argparse_witharg(args, i, &val, "--op-id", (char*)NULL)) {
3632 op_id = val;
7c673cae
FG
3633 } else if (ceph_argparse_witharg(args, i, &val, "--op-mask", (char*)NULL)) {
3634 op_mask_str = val;
3635 } else if (ceph_argparse_witharg(args, i, &val, "--key-type", (char*)NULL)) {
3636 key_type_str = val;
3637 if (key_type_str.compare("swift") == 0) {
3638 key_type = KEY_TYPE_SWIFT;
3639 } else if (key_type_str.compare("s3") == 0) {
3640 key_type = KEY_TYPE_S3;
3641 } else {
3642 cerr << "bad key type: " << key_type_str << std::endl;
11fdf7f2 3643 exit(1);
7c673cae
FG
3644 }
3645 } else if (ceph_argparse_witharg(args, i, &val, "--job-id", (char*)NULL)) {
3646 job_id = val;
3647 } else if (ceph_argparse_binary_flag(args, i, &gen_access_key, NULL, "--gen-access-key", (char*)NULL)) {
3648 // do nothing
3649 } else if (ceph_argparse_binary_flag(args, i, &gen_secret_key, NULL, "--gen-secret", (char*)NULL)) {
3650 // do nothing
11fdf7f2 3651 } else if (ceph_argparse_binary_flag(args, i, &show_log_entries, NULL, "--show-log-entries", (char*)NULL)) {
7c673cae 3652 // do nothing
11fdf7f2 3653 } else if (ceph_argparse_binary_flag(args, i, &show_log_sum, NULL, "--show-log-sum", (char*)NULL)) {
7c673cae 3654 // do nothing
11fdf7f2 3655 } else if (ceph_argparse_binary_flag(args, i, &skip_zero_entries, NULL, "--skip-zero-entries", (char*)NULL)) {
7c673cae
FG
3656 // do nothing
3657 } else if (ceph_argparse_binary_flag(args, i, &admin, NULL, "--admin", (char*)NULL)) {
3658 admin_specified = true;
3659 } else if (ceph_argparse_binary_flag(args, i, &system, NULL, "--system", (char*)NULL)) {
3660 system_specified = true;
3661 } else if (ceph_argparse_binary_flag(args, i, &verbose, NULL, "--verbose", (char*)NULL)) {
3662 // do nothing
3663 } else if (ceph_argparse_binary_flag(args, i, &staging, NULL, "--staging", (char*)NULL)) {
3664 // do nothing
3665 } else if (ceph_argparse_binary_flag(args, i, &commit, NULL, "--commit", (char*)NULL)) {
3666 // do nothing
7c673cae
FG
3667 } else if (ceph_argparse_witharg(args, i, &val, "--min-rewrite-size", (char*)NULL)) {
3668 min_rewrite_size = (uint64_t)atoll(val.c_str());
3669 } else if (ceph_argparse_witharg(args, i, &val, "--max-rewrite-size", (char*)NULL)) {
3670 max_rewrite_size = (uint64_t)atoll(val.c_str());
3671 } else if (ceph_argparse_witharg(args, i, &val, "--min-rewrite-stripe-size", (char*)NULL)) {
3672 min_rewrite_stripe_size = (uint64_t)atoll(val.c_str());
3673 } else if (ceph_argparse_witharg(args, i, &val, "--max-buckets", (char*)NULL)) {
3674 max_buckets = (int)strict_strtol(val.c_str(), 10, &err);
3675 if (!err.empty()) {
3676 cerr << "ERROR: failed to parse max buckets: " << err << std::endl;
3677 return EINVAL;
3678 }
3679 max_buckets_specified = true;
3680 } else if (ceph_argparse_witharg(args, i, &val, "--max-entries", (char*)NULL)) {
3681 max_entries = (int)strict_strtol(val.c_str(), 10, &err);
181888fb 3682 max_entries_specified = true;
7c673cae
FG
3683 if (!err.empty()) {
3684 cerr << "ERROR: failed to parse max entries: " << err << std::endl;
3685 return EINVAL;
3686 }
3687 } else if (ceph_argparse_witharg(args, i, &val, "--max-size", (char*)NULL)) {
20effc67 3688 max_size = strict_iec_cast<long long>(val, &err);
7c673cae
FG
3689 if (!err.empty()) {
3690 cerr << "ERROR: failed to parse max size: " << err << std::endl;
3691 return EINVAL;
3692 }
3693 have_max_size = true;
3694 } else if (ceph_argparse_witharg(args, i, &val, "--max-objects", (char*)NULL)) {
3695 max_objects = (int64_t)strict_strtoll(val.c_str(), 10, &err);
3696 if (!err.empty()) {
3697 cerr << "ERROR: failed to parse max objects: " << err << std::endl;
3698 return EINVAL;
3699 }
3700 have_max_objects = true;
20effc67
TL
3701 } else if (ceph_argparse_witharg(args, i, &val, "--max-read-ops", (char*)NULL)) {
3702 max_read_ops = (int64_t)strict_strtoll(val.c_str(), 10, &err);
3703 if (!err.empty()) {
3704 cerr << "ERROR: failed to parse max read requests: " << err << std::endl;
3705 return EINVAL;
3706 }
3707 have_max_read_ops = true;
3708 } else if (ceph_argparse_witharg(args, i, &val, "--max-write-ops", (char*)NULL)) {
3709 max_write_ops = (int64_t)strict_strtoll(val.c_str(), 10, &err);
3710 if (!err.empty()) {
3711 cerr << "ERROR: failed to parse max write requests: " << err << std::endl;
3712 return EINVAL;
3713 }
3714 have_max_write_ops = true;
3715 } else if (ceph_argparse_witharg(args, i, &val, "--max-read-bytes", (char*)NULL)) {
3716 max_read_bytes = (int64_t)strict_strtoll(val.c_str(), 10, &err);
3717 if (!err.empty()) {
3718 cerr << "ERROR: failed to parse max read bytes: " << err << std::endl;
3719 return EINVAL;
3720 }
3721 have_max_read_bytes = true;
3722 } else if (ceph_argparse_witharg(args, i, &val, "--max-write-bytes", (char*)NULL)) {
3723 max_write_bytes = (int64_t)strict_strtoll(val.c_str(), 10, &err);
3724 if (!err.empty()) {
3725 cerr << "ERROR: failed to parse max write bytes: " << err << std::endl;
3726 return EINVAL;
3727 }
3728 have_max_write_bytes = true;
7c673cae
FG
3729 } else if (ceph_argparse_witharg(args, i, &val, "--date", "--time", (char*)NULL)) {
3730 date = val;
3731 if (end_date.empty())
3732 end_date = date;
3733 } else if (ceph_argparse_witharg(args, i, &val, "--start-date", "--start-time", (char*)NULL)) {
3734 start_date = val;
3735 } else if (ceph_argparse_witharg(args, i, &val, "--end-date", "--end-time", (char*)NULL)) {
3736 end_date = val;
3737 } else if (ceph_argparse_witharg(args, i, &val, "--num-shards", (char*)NULL)) {
3738 num_shards = (int)strict_strtol(val.c_str(), 10, &err);
3739 if (!err.empty()) {
3740 cerr << "ERROR: failed to parse num shards: " << err << std::endl;
3741 return EINVAL;
3742 }
3743 num_shards_specified = true;
9f95a23c
TL
3744 } else if (ceph_argparse_witharg(args, i, &val, "--bucket-index-max-shards", (char*)NULL)) {
3745 bucket_index_max_shards = (int)strict_strtol(val.c_str(), 10, &err);
3746 if (!err.empty()) {
3747 cerr << "ERROR: failed to parse bucket-index-max-shards: " << err << std::endl;
3748 return EINVAL;
3749 }
7c673cae
FG
3750 } else if (ceph_argparse_witharg(args, i, &val, "--max-concurrent-ios", (char*)NULL)) {
3751 max_concurrent_ios = (int)strict_strtol(val.c_str(), 10, &err);
3752 if (!err.empty()) {
3753 cerr << "ERROR: failed to parse max concurrent ios: " << err << std::endl;
3754 return EINVAL;
3755 }
aee94f69
TL
3756 } else if (ceph_argparse_witharg(args, i, &val, "--min-age-hours", (char*)NULL)) {
3757 min_age = std::chrono::hours(atoi(val.c_str()));
7c673cae
FG
3758 } else if (ceph_argparse_witharg(args, i, &val, "--orphan-stale-secs", (char*)NULL)) {
3759 orphan_stale_secs = (uint64_t)strict_strtoll(val.c_str(), 10, &err);
3760 if (!err.empty()) {
3761 cerr << "ERROR: failed to parse orphan stale secs: " << err << std::endl;
3762 return EINVAL;
3763 }
3764 } else if (ceph_argparse_witharg(args, i, &val, "--shard-id", (char*)NULL)) {
3765 shard_id = (int)strict_strtol(val.c_str(), 10, &err);
3766 if (!err.empty()) {
3767 cerr << "ERROR: failed to parse shard id: " << err << std::endl;
3768 return EINVAL;
3769 }
3770 specified_shard_id = true;
1e59de90
TL
3771 } else if (ceph_argparse_witharg(args, i, &val, "--gen", (char*)NULL)) {
3772 gen = strict_strtoll(val.c_str(), 10, &err);
3773 if (!err.empty()) {
3774 cerr << "ERROR: failed to parse gen id: " << err << std::endl;
3775 return EINVAL;
3776 }
7c673cae
FG
3777 } else if (ceph_argparse_witharg(args, i, &val, "--access", (char*)NULL)) {
3778 access = val;
3779 perm_mask = rgw_str_to_perm(access.c_str());
3780 set_perm = true;
3781 } else if (ceph_argparse_witharg(args, i, &val, "--temp-url-key", (char*)NULL)) {
3782 temp_url_keys[0] = val;
3783 set_temp_url_key = true;
3784 } else if (ceph_argparse_witharg(args, i, &val, "--temp-url-key2", "--temp-url-key-2", (char*)NULL)) {
3785 temp_url_keys[1] = val;
3786 set_temp_url_key = true;
3787 } else if (ceph_argparse_witharg(args, i, &val, "--bucket-id", (char*)NULL)) {
3788 bucket_id = val;
9f95a23c 3789 opt_bucket_id = val;
7c673cae 3790 if (bucket_id.empty()) {
1911f103 3791 cerr << "no value for bucket-id" << std::endl;
11fdf7f2 3792 exit(1);
7c673cae 3793 }
9f95a23c
TL
3794 } else if (ceph_argparse_witharg(args, i, &val, "--bucket-new-name", (char*)NULL)) {
3795 new_bucket_name = val;
7c673cae
FG
3796 } else if (ceph_argparse_witharg(args, i, &val, "--format", (char*)NULL)) {
3797 format = val;
3798 } else if (ceph_argparse_witharg(args, i, &val, "--categories", (char*)NULL)) {
3799 string cat_str = val;
3800 list<string> cat_list;
3801 list<string>::iterator iter;
3802 get_str_list(cat_str, cat_list);
3803 for (iter = cat_list.begin(); iter != cat_list.end(); ++iter) {
3804 categories[*iter] = true;
3805 }
3806 } else if (ceph_argparse_binary_flag(args, i, &delete_child_objects, NULL, "--purge-objects", (char*)NULL)) {
3807 // do nothing
3808 } else if (ceph_argparse_binary_flag(args, i, &pretty_format, NULL, "--pretty-format", (char*)NULL)) {
3809 // do nothing
3810 } else if (ceph_argparse_binary_flag(args, i, &purge_data, NULL, "--purge-data", (char*)NULL)) {
3811 delete_child_objects = purge_data;
3812 } else if (ceph_argparse_binary_flag(args, i, &purge_keys, NULL, "--purge-keys", (char*)NULL)) {
3813 // do nothing
3814 } else if (ceph_argparse_binary_flag(args, i, &yes_i_really_mean_it, NULL, "--yes-i-really-mean-it", (char*)NULL)) {
3815 // do nothing
3816 } else if (ceph_argparse_binary_flag(args, i, &fix, NULL, "--fix", (char*)NULL)) {
3817 // do nothing
3818 } else if (ceph_argparse_binary_flag(args, i, &remove_bad, NULL, "--remove-bad", (char*)NULL)) {
3819 // do nothing
3820 } else if (ceph_argparse_binary_flag(args, i, &check_head_obj_locator, NULL, "--check-head-obj-locator", (char*)NULL)) {
3821 // do nothing
3822 } else if (ceph_argparse_binary_flag(args, i, &check_objects, NULL, "--check-objects", (char*)NULL)) {
3823 // do nothing
3824 } else if (ceph_argparse_binary_flag(args, i, &sync_stats, NULL, "--sync-stats", (char*)NULL)) {
3825 // do nothing
94b18763
FG
3826 } else if (ceph_argparse_binary_flag(args, i, &reset_stats, NULL, "--reset-stats", (char*)NULL)) {
3827 // do nothing
7c673cae
FG
3828 } else if (ceph_argparse_binary_flag(args, i, &include_all, NULL, "--include-all", (char*)NULL)) {
3829 // do nothing
494da23a
TL
3830 } else if (ceph_argparse_binary_flag(args, i, &allow_unordered, NULL, "--allow-unordered", (char*)NULL)) {
3831 // do nothing
7c673cae
FG
3832 } else if (ceph_argparse_binary_flag(args, i, &extra_info, NULL, "--extra-info", (char*)NULL)) {
3833 // do nothing
3834 } else if (ceph_argparse_binary_flag(args, i, &bypass_gc, NULL, "--bypass-gc", (char*)NULL)) {
3835 // do nothing
3836 } else if (ceph_argparse_binary_flag(args, i, &warnings_only, NULL, "--warnings-only", (char*)NULL)) {
3837 // do nothing
3838 } else if (ceph_argparse_binary_flag(args, i, &inconsistent_index, NULL, "--inconsistent-index", (char*)NULL)) {
3839 // do nothing
aee94f69
TL
3840 } else if (ceph_argparse_flag(args, i, "--hide-progress", (char*)NULL)) {
3841 hide_progress = true;
3842 } else if (ceph_argparse_flag(args, i, "--dump-keys", (char*)NULL)) {
3843 dump_keys = true;
39ae355f
TL
3844 } else if (ceph_argparse_binary_flag(args, i, &placement_inline_data, NULL, "--placement-inline-data", (char*)NULL)) {
3845 placement_inline_data_specified = true;
3846 // do nothing
7c673cae
FG
3847 } else if (ceph_argparse_witharg(args, i, &val, "--caps", (char*)NULL)) {
3848 caps = val;
20effc67 3849 } else if (ceph_argparse_witharg(args, i, &val, "--infile", (char*)NULL)) {
7c673cae
FG
3850 infile = val;
3851 } else if (ceph_argparse_witharg(args, i, &val, "--metadata-key", (char*)NULL)) {
3852 metadata_key = val;
3853 } else if (ceph_argparse_witharg(args, i, &val, "--marker", (char*)NULL)) {
3854 marker = val;
3855 } else if (ceph_argparse_witharg(args, i, &val, "--start-marker", (char*)NULL)) {
3856 start_marker = val;
3857 } else if (ceph_argparse_witharg(args, i, &val, "--end-marker", (char*)NULL)) {
3858 end_marker = val;
3859 } else if (ceph_argparse_witharg(args, i, &val, "--quota-scope", (char*)NULL)) {
3860 quota_scope = val;
20effc67
TL
3861 } else if (ceph_argparse_witharg(args, i, &val, "--ratelimit-scope", (char*)NULL)) {
3862 ratelimit_scope = val;
7c673cae
FG
3863 } else if (ceph_argparse_witharg(args, i, &val, "--index-type", (char*)NULL)) {
3864 string index_type_str = val;
3865 bi_index_type = get_bi_index_type(index_type_str);
11fdf7f2 3866 if (bi_index_type == BIIndexType::Invalid) {
7c673cae
FG
3867 cerr << "ERROR: invalid bucket index entry type" << std::endl;
3868 return EINVAL;
3869 }
f67539c2
TL
3870 } else if (ceph_argparse_witharg(args, i, &val, "--log-type", (char*)NULL)) {
3871 string log_type_str = val;
3872 auto l = get_log_type(log_type_str);
3873 if (l == static_cast<log_type>(0xff)) {
3874 cerr << "ERROR: invalid log type" << std::endl;
3875 return EINVAL;
3876 }
3877 opt_log_type = l;
7c673cae
FG
3878 } else if (ceph_argparse_binary_flag(args, i, &is_master_int, NULL, "--master", (char*)NULL)) {
3879 is_master = (bool)is_master_int;
3880 is_master_set = true;
3881 } else if (ceph_argparse_binary_flag(args, i, &set_default, NULL, "--default", (char*)NULL)) {
3882 /* do nothing */
11fdf7f2
TL
3883 } else if (ceph_argparse_witharg(args, i, &val, "--redirect-zone", (char*)NULL)) {
3884 redirect_zone = val;
3885 redirect_zone_set = true;
7c673cae
FG
3886 } else if (ceph_argparse_binary_flag(args, i, &read_only_int, NULL, "--read-only", (char*)NULL)) {
3887 read_only = (bool)read_only_int;
3888 is_read_only_set = true;
7c673cae
FG
3889 } else if (ceph_argparse_witharg(args, i, &val, "--master-zone", (char*)NULL)) {
3890 master_zone = val;
3891 } else if (ceph_argparse_witharg(args, i, &val, "--period", (char*)NULL)) {
3892 period_id = val;
3893 } else if (ceph_argparse_witharg(args, i, &val, "--epoch", (char*)NULL)) {
3894 period_epoch = val;
3895 } else if (ceph_argparse_witharg(args, i, &val, "--remote", (char*)NULL)) {
3896 remote = val;
3897 } else if (ceph_argparse_witharg(args, i, &val, "--url", (char*)NULL)) {
3898 url = val;
20effc67
TL
3899 } else if (ceph_argparse_witharg(args, i, &val, "--region", (char*)NULL)) {
3900 opt_region = val;
7c673cae
FG
3901 } else if (ceph_argparse_witharg(args, i, &val, "--realm-id", (char*)NULL)) {
3902 realm_id = val;
20effc67
TL
3903 opt_realm_id = val;
3904 g_conf().set_val("rgw_realm_id", val);
7c673cae
FG
3905 } else if (ceph_argparse_witharg(args, i, &val, "--realm-new-name", (char*)NULL)) {
3906 realm_new_name = val;
3907 } else if (ceph_argparse_witharg(args, i, &val, "--zonegroup-id", (char*)NULL)) {
3908 zonegroup_id = val;
20effc67
TL
3909 opt_zonegroup_id = val;
3910 g_conf().set_val("rgw_zonegroup_id", val);
7c673cae
FG
3911 } else if (ceph_argparse_witharg(args, i, &val, "--zonegroup-new-name", (char*)NULL)) {
3912 zonegroup_new_name = val;
3913 } else if (ceph_argparse_witharg(args, i, &val, "--placement-id", (char*)NULL)) {
3914 placement_id = val;
11fdf7f2 3915 } else if (ceph_argparse_witharg(args, i, &val, "--storage-class", (char*)NULL)) {
9f95a23c 3916 opt_storage_class = val;
7c673cae 3917 } else if (ceph_argparse_witharg(args, i, &val, "--tags", (char*)NULL)) {
9f95a23c 3918 get_str_list(val, ",", tags);
7c673cae 3919 } else if (ceph_argparse_witharg(args, i, &val, "--tags-add", (char*)NULL)) {
9f95a23c 3920 get_str_list(val, ",", tags_add);
7c673cae 3921 } else if (ceph_argparse_witharg(args, i, &val, "--tags-rm", (char*)NULL)) {
9f95a23c 3922 get_str_list(val, ",", tags_rm);
7c673cae
FG
3923 } else if (ceph_argparse_witharg(args, i, &val, "--api-name", (char*)NULL)) {
3924 api_name = val;
3925 } else if (ceph_argparse_witharg(args, i, &val, "--zone-id", (char*)NULL)) {
3926 zone_id = val;
20effc67
TL
3927 opt_zone_id = val;
3928 g_conf().set_val("rgw_zone_id", val);
7c673cae
FG
3929 } else if (ceph_argparse_witharg(args, i, &val, "--zone-new-name", (char*)NULL)) {
3930 zone_new_name = val;
3931 } else if (ceph_argparse_witharg(args, i, &val, "--endpoints", (char*)NULL)) {
3932 get_str_list(val, endpoints);
3933 } else if (ceph_argparse_witharg(args, i, &val, "--sync-from", (char*)NULL)) {
3934 get_str_list(val, sync_from);
3935 } else if (ceph_argparse_witharg(args, i, &val, "--sync-from-rm", (char*)NULL)) {
3936 get_str_list(val, sync_from_rm);
3937 } else if (ceph_argparse_binary_flag(args, i, &tmp_int, NULL, "--sync-from-all", (char*)NULL)) {
3938 sync_from_all = (bool)tmp_int;
3939 sync_from_all_specified = true;
3940 } else if (ceph_argparse_witharg(args, i, &val, "--source-zone", (char*)NULL)) {
3941 source_zone_name = val;
9f95a23c
TL
3942 opt_source_zone_name = val;
3943 } else if (ceph_argparse_witharg(args, i, &val, "--source-zone-id", (char*)NULL)) {
3944 opt_source_zone_id = val;
3945 } else if (ceph_argparse_witharg(args, i, &val, "--dest-zone", (char*)NULL)) {
3946 opt_dest_zone_name = val;
3947 } else if (ceph_argparse_witharg(args, i, &val, "--dest-zone-id", (char*)NULL)) {
3948 opt_dest_zone_id = val;
7c673cae
FG
3949 } else if (ceph_argparse_witharg(args, i, &val, "--tier-type", (char*)NULL)) {
3950 tier_type = val;
3951 tier_type_specified = true;
3952 } else if (ceph_argparse_witharg(args, i, &val, "--tier-config", (char*)NULL)) {
3953 parse_tier_config_param(val, tier_config_add);
3954 } else if (ceph_argparse_witharg(args, i, &val, "--tier-config-rm", (char*)NULL)) {
3955 parse_tier_config_param(val, tier_config_rm);
3956 } else if (ceph_argparse_witharg(args, i, &val, "--index-pool", (char*)NULL)) {
3957 index_pool = val;
3958 } else if (ceph_argparse_witharg(args, i, &val, "--data-pool", (char*)NULL)) {
3959 data_pool = val;
3960 } else if (ceph_argparse_witharg(args, i, &val, "--data-extra-pool", (char*)NULL)) {
3961 data_extra_pool = val;
3962 } else if (ceph_argparse_witharg(args, i, &val, "--placement-index-type", (char*)NULL)) {
3963 if (val == "normal") {
f67539c2 3964 placement_index_type = rgw::BucketIndexType::Normal;
7c673cae 3965 } else if (val == "indexless") {
f67539c2 3966 placement_index_type = rgw::BucketIndexType::Indexless;
7c673cae 3967 } else {
f67539c2 3968 placement_index_type = (rgw::BucketIndexType)strict_strtol(val.c_str(), 10, &err);
7c673cae
FG
3969 if (!err.empty()) {
3970 cerr << "ERROR: failed to parse index type index: " << err << std::endl;
3971 return EINVAL;
3972 }
3973 }
3974 index_type_specified = true;
3975 } else if (ceph_argparse_witharg(args, i, &val, "--compression", (char*)NULL)) {
3976 compression_type = val;
3977 } else if (ceph_argparse_witharg(args, i, &val, "--role-name", (char*)NULL)) {
3978 role_name = val;
3979 } else if (ceph_argparse_witharg(args, i, &val, "--path", (char*)NULL)) {
3980 path = val;
3981 } else if (ceph_argparse_witharg(args, i, &val, "--assume-role-policy-doc", (char*)NULL)) {
3982 assume_role_doc = val;
3983 } else if (ceph_argparse_witharg(args, i, &val, "--policy-name", (char*)NULL)) {
3984 policy_name = val;
3985 } else if (ceph_argparse_witharg(args, i, &val, "--policy-doc", (char*)NULL)) {
3986 perm_policy_doc = val;
3987 } else if (ceph_argparse_witharg(args, i, &val, "--path-prefix", (char*)NULL)) {
3988 path_prefix = val;
1e59de90
TL
3989 } else if (ceph_argparse_witharg(args, i, &val, "--max-session-duration", (char*)NULL)) {
3990 max_session_duration = val;
11fdf7f2
TL
3991 } else if (ceph_argparse_witharg(args, i, &val, "--totp-serial", (char*)NULL)) {
3992 totp_serial = val;
3993 } else if (ceph_argparse_witharg(args, i, &val, "--totp-pin", (char*)NULL)) {
3994 totp_pin.push_back(val);
3995 } else if (ceph_argparse_witharg(args, i, &val, "--totp-seed", (char*)NULL)) {
3996 totp_seed = val;
3997 } else if (ceph_argparse_witharg(args, i, &val, "--totp-seed-type", (char*)NULL)) {
3998 totp_seed_type = val;
3999 } else if (ceph_argparse_witharg(args, i, &val, "--totp-seconds", (char*)NULL)) {
4000 totp_seconds = atoi(val.c_str());
4001 } else if (ceph_argparse_witharg(args, i, &val, "--totp-window", (char*)NULL)) {
4002 totp_window = atoi(val.c_str());
91327a77
AA
4003 } else if (ceph_argparse_witharg(args, i, &val, "--trim-delay-ms", (char*)NULL)) {
4004 trim_delay_ms = atoi(val.c_str());
11fdf7f2
TL
4005 } else if (ceph_argparse_witharg(args, i, &val, "--topic", (char*)NULL)) {
4006 topic_name = val;
aee94f69
TL
4007 } else if (ceph_argparse_witharg(args, i, &val, "--notification-id", (char*)NULL)) {
4008 notification_id = val;
f67539c2 4009 } else if (ceph_argparse_witharg(args, i, &val, "--subscription", (char*)NULL)) {
11fdf7f2 4010 sub_name = val;
11fdf7f2
TL
4011 } else if (ceph_argparse_witharg(args, i, &val, "--event-id", (char*)NULL)) {
4012 event_id = val;
9f95a23c
TL
4013 } else if (ceph_argparse_witharg(args, i, &val, "--group-id", (char*)NULL)) {
4014 opt_group_id = val;
4015 } else if (ceph_argparse_witharg(args, i, &val, "--status", (char*)NULL)) {
4016 opt_status = val;
4017 } else if (ceph_argparse_witharg(args, i, &val, "--flow-type", (char*)NULL)) {
4018 opt_flow_type = val;
4019 } else if (ceph_argparse_witharg(args, i, &val, "--zones", "--zone-names", (char*)NULL)) {
4020 vector<string> v;
4021 get_str_vec(val, v);
4022 opt_zone_names = std::move(v);
4023 } else if (ceph_argparse_witharg(args, i, &val, "--zone-ids", (char*)NULL)) {
4024 opt_zone_ids = zone_ids_from_str(val);
4025 } else if (ceph_argparse_witharg(args, i, &val, "--source-zones", "--source-zone-names", (char*)NULL)) {
4026 vector<string> v;
4027 get_str_vec(val, v);
4028 opt_source_zone_names = std::move(v);
4029 } else if (ceph_argparse_witharg(args, i, &val, "--source-zone-ids", (char*)NULL)) {
4030 opt_source_zone_ids = zone_ids_from_str(val);
4031 } else if (ceph_argparse_witharg(args, i, &val, "--dest-zones", "--dest-zone-names", (char*)NULL)) {
4032 vector<string> v;
4033 get_str_vec(val, v);
4034 opt_dest_zone_names = std::move(v);
4035 } else if (ceph_argparse_witharg(args, i, &val, "--dest-zone-ids", (char*)NULL)) {
4036 opt_dest_zone_ids = zone_ids_from_str(val);
4037 } else if (ceph_argparse_witharg(args, i, &val, "--flow-id", (char*)NULL)) {
4038 opt_flow_id = val;
4039 } else if (ceph_argparse_witharg(args, i, &val, "--pipe-id", (char*)NULL)) {
4040 opt_pipe_id = val;
4041 } else if (ceph_argparse_witharg(args, i, &val, "--source-tenant", (char*)NULL)) {
4042 opt_source_tenant = val;
4043 } else if (ceph_argparse_witharg(args, i, &val, "--source-bucket", (char*)NULL)) {
4044 opt_source_bucket_name = val;
4045 } else if (ceph_argparse_witharg(args, i, &val, "--source-bucket-id", (char*)NULL)) {
4046 opt_source_bucket_id = val;
4047 } else if (ceph_argparse_witharg(args, i, &val, "--dest-tenant", (char*)NULL)) {
4048 opt_dest_tenant = val;
4049 } else if (ceph_argparse_witharg(args, i, &val, "--dest-bucket", (char*)NULL)) {
4050 opt_dest_bucket_name = val;
4051 } else if (ceph_argparse_witharg(args, i, &val, "--dest-bucket-id", (char*)NULL)) {
4052 opt_dest_bucket_id = val;
4053 } else if (ceph_argparse_witharg(args, i, &val, "--effective-zone-name", "--effective-zone", (char*)NULL)) {
4054 opt_effective_zone_name = val;
4055 } else if (ceph_argparse_witharg(args, i, &val, "--effective-zone-id", (char*)NULL)) {
4056 opt_effective_zone_id = rgw_zone_id(val);
4057 } else if (ceph_argparse_witharg(args, i, &val, "--prefix", (char*)NULL)) {
4058 opt_prefix = val;
4059 } else if (ceph_argparse_witharg(args, i, &val, "--prefix-rm", (char*)NULL)) {
4060 opt_prefix_rm = val;
4061 } else if (ceph_argparse_witharg(args, i, &val, "--priority", (char*)NULL)) {
4062 opt_priority = atoi(val.c_str());
4063 } else if (ceph_argparse_witharg(args, i, &val, "--mode", (char*)NULL)) {
4064 opt_mode = val;
4065 } else if (ceph_argparse_witharg(args, i, &val, "--dest-owner", (char*)NULL)) {
4066 opt_dest_owner.emplace(val);
4067 opt_dest_owner = val;
f67539c2
TL
4068 } else if (ceph_argparse_witharg(args, i, &val, "--retry-delay-ms", (char*)NULL)) {
4069 opt_retry_delay_ms = std::chrono::milliseconds(atoi(val.c_str()));
4070 } else if (ceph_argparse_witharg(args, i, &val, "--timeout-sec", (char*)NULL)) {
4071 opt_timeout_sec = std::chrono::seconds(atoi(val.c_str()));
1e59de90
TL
4072 } else if (ceph_argparse_witharg(args, i, &val, "--inject-error-at", (char*)NULL)) {
4073 inject_error_at = val;
4074 } else if (ceph_argparse_witharg(args, i, &val, "--inject-error-code", (char*)NULL)) {
4075 inject_error_code = atoi(val.c_str());
4076 } else if (ceph_argparse_witharg(args, i, &val, "--inject-abort-at", (char*)NULL)) {
4077 inject_abort_at = val;
05a536ef
TL
4078 } else if (ceph_argparse_witharg(args, i, &val, "--inject-delay-at", (char*)NULL)) {
4079 inject_delay_at = val;
4080 } else if (ceph_argparse_witharg(args, i, &val, "--inject-delay-ms", (char*)NULL)) {
4081 inject_delay = std::chrono::milliseconds(atoi(val.c_str()));
11fdf7f2
TL
4082 } else if (ceph_argparse_binary_flag(args, i, &detail, NULL, "--detail", (char*)NULL)) {
4083 // do nothing
f67539c2
TL
4084 } else if (ceph_argparse_witharg(args, i, &val, "--context", (char*)NULL)) {
4085 str_script_ctx = val;
4086 } else if (ceph_argparse_witharg(args, i, &val, "--package", (char*)NULL)) {
4087 script_package = val;
4088 } else if (ceph_argparse_binary_flag(args, i, &allow_compilation, NULL, "--allow-compilation", (char*)NULL)) {
4089 // do nothing
4090 } else if (ceph_argparse_witharg(args, i, &val, "--rgw-obj-fs", (char*)NULL)) {
4091 rgw_obj_fs = val;
1e59de90
TL
4092 } else if (ceph_argparse_witharg(args, i, &val, "--enable-feature", (char*)NULL)) {
4093 if (!rgw::zone_features::supports(val)) {
4094 std::cerr << "ERROR: Cannot enable unrecognized zone feature \"" << val << "\"" << std::endl;
4095 return EINVAL;
4096 }
4097 enable_features.insert(val);
4098 } else if (ceph_argparse_witharg(args, i, &val, "--disable-feature", (char*)NULL)) {
4099 disable_features.insert(val);
7c673cae
FG
4100 } else if (strncmp(*i, "-", 1) == 0) {
4101 cerr << "ERROR: invalid flag " << *i << std::endl;
4102 return EINVAL;
4103 } else {
4104 ++i;
4105 }
4106 }
7c673cae 4107
20effc67
TL
4108 /* common_init_finish needs to be called after g_conf().set_val() */
4109 common_init_finish(g_ceph_context);
4110
1e59de90
TL
4111 std::unique_ptr<rgw::sal::ConfigStore> cfgstore;
4112
7c673cae 4113 if (args.empty()) {
d2e6a577 4114 usage();
11fdf7f2 4115 exit(1);
7c673cae
FG
4116 }
4117 else {
9f95a23c
TL
4118 std::vector<string> extra_args;
4119 std::vector<string> expected;
7c673cae 4120
9f95a23c
TL
4121 std::any _opt_cmd;
4122
4123 if (!cmd.find_command(args, &_opt_cmd, &extra_args, &err, &expected)) {
4124 if (!expected.empty()) {
4125 cerr << err << std::endl;
4126 cerr << "Expected one of the following:" << std::endl;
4127 for (auto& exp : expected) {
4128 if (exp == "*" || exp == "[*]") {
4129 continue;
4130 }
4131 cerr << " " << exp << std::endl;
4132 }
4133 } else {
4134 cerr << "Command not found:";
4135 for (auto& arg : args) {
4136 cerr << " " << arg;
4137 }
4138 cerr << std::endl;
4139 }
11fdf7f2 4140 exit(1);
d2e6a577 4141 }
7c673cae 4142
9f95a23c
TL
4143 opt_cmd = std::any_cast<OPT>(_opt_cmd);
4144
7c673cae 4145 /* some commands may have an optional extra param */
9f95a23c 4146 if (!extra_args.empty()) {
7c673cae 4147 switch (opt_cmd) {
9f95a23c
TL
4148 case OPT::METADATA_GET:
4149 case OPT::METADATA_PUT:
4150 case OPT::METADATA_RM:
4151 case OPT::METADATA_LIST:
4152 metadata_key = extra_args[0];
7c673cae
FG
4153 break;
4154 default:
4155 break;
4156 }
4157 }
4158
20effc67
TL
4159 // not a raw op if 'period update' needs to commit to master
4160 bool raw_period_update = opt_cmd == OPT::PERIOD_UPDATE && !commit;
4161 // not a raw op if 'period pull' needs to read zone/period configuration
4162 bool raw_period_pull = opt_cmd == OPT::PERIOD_PULL && !url.empty();
9f95a23c 4163
20effc67
TL
4164 std::set<OPT> raw_storage_ops_list = {OPT::ZONEGROUP_ADD, OPT::ZONEGROUP_CREATE,
4165 OPT::ZONEGROUP_DELETE,
9f95a23c 4166 OPT::ZONEGROUP_GET, OPT::ZONEGROUP_LIST,
20effc67 4167 OPT::ZONEGROUP_SET, OPT::ZONEGROUP_DEFAULT,
9f95a23c
TL
4168 OPT::ZONEGROUP_RENAME, OPT::ZONEGROUP_MODIFY,
4169 OPT::ZONEGROUP_REMOVE,
4170 OPT::ZONEGROUP_PLACEMENT_ADD, OPT::ZONEGROUP_PLACEMENT_RM,
4171 OPT::ZONEGROUP_PLACEMENT_MODIFY, OPT::ZONEGROUP_PLACEMENT_LIST,
4172 OPT::ZONEGROUP_PLACEMENT_GET,
4173 OPT::ZONEGROUP_PLACEMENT_DEFAULT,
4174 OPT::ZONE_CREATE, OPT::ZONE_DELETE,
20effc67
TL
4175 OPT::ZONE_GET, OPT::ZONE_SET, OPT::ZONE_RENAME,
4176 OPT::ZONE_LIST, OPT::ZONE_MODIFY, OPT::ZONE_DEFAULT,
9f95a23c
TL
4177 OPT::ZONE_PLACEMENT_ADD, OPT::ZONE_PLACEMENT_RM,
4178 OPT::ZONE_PLACEMENT_MODIFY, OPT::ZONE_PLACEMENT_LIST,
4179 OPT::ZONE_PLACEMENT_GET,
4180 OPT::REALM_CREATE,
4181 OPT::PERIOD_DELETE, OPT::PERIOD_GET,
9f95a23c
TL
4182 OPT::PERIOD_GET_CURRENT, OPT::PERIOD_LIST,
4183 OPT::GLOBAL_QUOTA_GET, OPT::GLOBAL_QUOTA_SET,
4184 OPT::GLOBAL_QUOTA_ENABLE, OPT::GLOBAL_QUOTA_DISABLE,
20effc67
TL
4185 OPT::GLOBAL_RATELIMIT_GET, OPT::GLOBAL_RATELIMIT_SET,
4186 OPT::GLOBAL_RATELIMIT_ENABLE, OPT::GLOBAL_RATELIMIT_DISABLE,
9f95a23c
TL
4187 OPT::REALM_DELETE, OPT::REALM_GET, OPT::REALM_LIST,
4188 OPT::REALM_LIST_PERIODS,
4189 OPT::REALM_GET_DEFAULT,
4190 OPT::REALM_RENAME, OPT::REALM_SET,
4191 OPT::REALM_DEFAULT, OPT::REALM_PULL};
4192
20effc67 4193 std::set<OPT> readonly_ops_list = {
9f95a23c
TL
4194 OPT::USER_INFO,
4195 OPT::USER_STATS,
4196 OPT::BUCKETS_LIST,
4197 OPT::BUCKET_LIMIT_CHECK,
1e59de90 4198 OPT::BUCKET_LAYOUT,
9f95a23c 4199 OPT::BUCKET_STATS,
f67539c2 4200 OPT::BUCKET_SYNC_CHECKPOINT,
9f95a23c
TL
4201 OPT::BUCKET_SYNC_INFO,
4202 OPT::BUCKET_SYNC_STATUS,
4203 OPT::BUCKET_SYNC_MARKERS,
1e59de90
TL
4204 OPT::BUCKET_SHARD_OBJECTS,
4205 OPT::BUCKET_OBJECT_SHARD,
9f95a23c
TL
4206 OPT::LOG_LIST,
4207 OPT::LOG_SHOW,
4208 OPT::USAGE_SHOW,
4209 OPT::OBJECT_STAT,
4210 OPT::BI_GET,
4211 OPT::BI_LIST,
4212 OPT::OLH_GET,
4213 OPT::OLH_READLOG,
4214 OPT::GC_LIST,
4215 OPT::LC_LIST,
4216 OPT::ORPHANS_LIST_JOBS,
4217 OPT::ZONEGROUP_GET,
4218 OPT::ZONEGROUP_LIST,
4219 OPT::ZONEGROUP_PLACEMENT_LIST,
4220 OPT::ZONEGROUP_PLACEMENT_GET,
4221 OPT::ZONE_GET,
4222 OPT::ZONE_LIST,
4223 OPT::ZONE_PLACEMENT_LIST,
4224 OPT::ZONE_PLACEMENT_GET,
4225 OPT::METADATA_GET,
4226 OPT::METADATA_LIST,
4227 OPT::METADATA_SYNC_STATUS,
4228 OPT::MDLOG_LIST,
4229 OPT::MDLOG_STATUS,
4230 OPT::SYNC_ERROR_LIST,
4231 OPT::SYNC_GROUP_GET,
4232 OPT::SYNC_POLICY_GET,
4233 OPT::BILOG_LIST,
4234 OPT::BILOG_STATUS,
4235 OPT::DATA_SYNC_STATUS,
4236 OPT::DATALOG_LIST,
4237 OPT::DATALOG_STATUS,
4238 OPT::REALM_GET,
4239 OPT::REALM_GET_DEFAULT,
4240 OPT::REALM_LIST,
4241 OPT::REALM_LIST_PERIODS,
4242 OPT::PERIOD_GET,
4243 OPT::PERIOD_GET_CURRENT,
4244 OPT::PERIOD_LIST,
4245 OPT::GLOBAL_QUOTA_GET,
20effc67 4246 OPT::GLOBAL_RATELIMIT_GET,
9f95a23c
TL
4247 OPT::SYNC_INFO,
4248 OPT::SYNC_STATUS,
4249 OPT::ROLE_GET,
4250 OPT::ROLE_LIST,
4251 OPT::ROLE_POLICY_LIST,
4252 OPT::ROLE_POLICY_GET,
4253 OPT::RESHARD_LIST,
4254 OPT::RESHARD_STATUS,
aee94f69
TL
4255 OPT::PUBSUB_TOPIC_LIST,
4256 OPT::PUBSUB_NOTIFICATION_LIST,
522d829b 4257 OPT::PUBSUB_TOPIC_GET,
aee94f69 4258 OPT::PUBSUB_NOTIFICATION_GET,
522d829b
TL
4259 OPT::SCRIPT_GET,
4260 };
4261
20effc67
TL
4262 std::set<OPT> gc_ops_list = {
4263 OPT::GC_LIST,
4264 OPT::GC_PROCESS,
4265 OPT::OBJECT_RM,
4266 OPT::BUCKET_RM, // --purge-objects
4267 OPT::USER_RM, // --purge-data
4268 OPT::OBJECTS_EXPIRE,
4269 OPT::OBJECTS_EXPIRE_STALE_RM,
1d09f67e
TL
4270 OPT::LC_PROCESS,
4271 OPT::BUCKET_SYNC_RUN,
39ae355f
TL
4272 OPT::DATA_SYNC_RUN,
4273 OPT::BUCKET_REWRITE,
4274 OPT::OBJECT_REWRITE
20effc67
TL
4275 };
4276
4277 raw_storage_op = (raw_storage_ops_list.find(opt_cmd) != raw_storage_ops_list.end() ||
4278 raw_period_update || raw_period_pull);
4279 bool need_cache = readonly_ops_list.find(opt_cmd) == readonly_ops_list.end();
4280 bool need_gc = (gc_ops_list.find(opt_cmd) != gc_ops_list.end()) && !bypass_gc;
4281
1e59de90
TL
4282 DriverManager::Config cfg = DriverManager::get_config(true, g_ceph_context);
4283
4284 auto config_store_type = g_conf().get_val<std::string>("rgw_config_store");
4285 cfgstore = DriverManager::create_config_store(dpp(), config_store_type);
4286 if (!cfgstore) {
4287 cerr << "couldn't init config storage provider" << std::endl;
4288 return EIO;
4289 }
4290
20effc67 4291 if (raw_storage_op) {
1e59de90
TL
4292 driver = DriverManager::get_raw_storage(dpp(),
4293 g_ceph_context,
4294 cfg);
20effc67 4295 } else {
1e59de90
TL
4296 driver = DriverManager::get_storage(dpp(),
4297 g_ceph_context,
4298 cfg,
4299 false,
4300 false,
4301 false,
4302 false,
4303 false,
4304 need_cache && g_conf()->rgw_cache_enabled,
4305 need_gc);
4306 }
4307 if (!driver) {
20effc67 4308 cerr << "couldn't init storage provider" << std::endl;
1e59de90 4309 return EIO;
20effc67
TL
4310 }
4311
1e59de90
TL
4312 /* Needs to be after the driver is initialized. Note, user could be empty here. */
4313 user = driver->get_user(user_id_arg);
20effc67
TL
4314
4315 init_optional_bucket(opt_bucket, opt_tenant,
4316 opt_bucket_name, opt_bucket_id);
4317 init_optional_bucket(opt_source_bucket, opt_source_tenant,
4318 opt_source_bucket_name, opt_source_bucket_id);
4319 init_optional_bucket(opt_dest_bucket, opt_dest_tenant,
4320 opt_dest_bucket_name, opt_dest_bucket_id);
4321
4322 if (tenant.empty()) {
4323 tenant = user->get_tenant();
4324 } else {
4325 if (rgw::sal::User::empty(user) && opt_cmd != OPT::ROLE_CREATE
4326 && opt_cmd != OPT::ROLE_DELETE
4327 && opt_cmd != OPT::ROLE_GET
1e59de90 4328 && opt_cmd != OPT::ROLE_TRUST_POLICY_MODIFY
20effc67
TL
4329 && opt_cmd != OPT::ROLE_LIST
4330 && opt_cmd != OPT::ROLE_POLICY_PUT
4331 && opt_cmd != OPT::ROLE_POLICY_LIST
4332 && opt_cmd != OPT::ROLE_POLICY_GET
4333 && opt_cmd != OPT::ROLE_POLICY_DELETE
1e59de90 4334 && opt_cmd != OPT::ROLE_UPDATE
20effc67
TL
4335 && opt_cmd != OPT::RESHARD_ADD
4336 && opt_cmd != OPT::RESHARD_CANCEL
1e59de90 4337 && opt_cmd != OPT::RESHARD_STATUS
aee94f69
TL
4338 && opt_cmd != OPT::PUBSUB_TOPIC_LIST
4339 && opt_cmd != OPT::PUBSUB_NOTIFICATION_LIST
1e59de90 4340 && opt_cmd != OPT::PUBSUB_TOPIC_GET
aee94f69
TL
4341 && opt_cmd != OPT::PUBSUB_NOTIFICATION_GET
4342 && opt_cmd != OPT::PUBSUB_TOPIC_RM
4343 && opt_cmd != OPT::PUBSUB_NOTIFICATION_RM) {
20effc67
TL
4344 cerr << "ERROR: --tenant is set, but there's no user ID" << std::endl;
4345 return EINVAL;
4346 }
4347 user->set_tenant(tenant);
4348 }
4349 if (user_ns.empty()) {
4350 user_ns = user->get_id().ns;
4351 } else {
4352 user->set_ns(user_ns);
4353 }
4354
4355 if (!new_user_id.empty() && !tenant.empty()) {
4356 new_user_id.tenant = tenant;
4357 }
9f95a23c 4358
20effc67
TL
4359 /* check key parameter conflict */
4360 if ((!access_key.empty()) && gen_access_key) {
4361 cerr << "ERROR: key parameter conflict, --access-key & --gen-access-key" << std::endl;
4362 return EINVAL;
4363 }
4364 if ((!secret_key.empty()) && gen_secret_key) {
4365 cerr << "ERROR: key parameter conflict, --secret & --gen-secret" << std::endl;
4366 return EINVAL;
4367 }
4368 }
7c673cae 4369
20effc67
TL
4370 // default to pretty json
4371 if (format.empty()) {
4372 format = "json";
4373 pretty_format = true;
4374 }
4375
4376 if (format == "xml")
4377 formatter = make_unique<XMLFormatter>(new XMLFormatter(pretty_format));
4378 else if (format == "json")
4379 formatter = make_unique<JSONFormatter>(new JSONFormatter(pretty_format));
4380 else {
4381 cerr << "unrecognized format: " << format << std::endl;
4382 exit(1);
4383 }
4384
4385 zone_formatter = std::make_unique<JSONFormatter_PrettyZone>(pretty_format);
4386
4387 realm_name = g_conf()->rgw_realm;
4388 zone_name = g_conf()->rgw_zone;
4389 zonegroup_name = g_conf()->rgw_zonegroup;
4390
4391 if (!realm_name.empty()) {
4392 opt_realm_name = realm_name;
4393 }
4394
4395 if (!zone_name.empty()) {
4396 opt_zone_name = zone_name;
4397 }
4398
4399 if (!zonegroup_name.empty()) {
4400 opt_zonegroup_name = zonegroup_name;
7c673cae 4401 }
20effc67
TL
4402
4403 RGWStreamFlusher stream_flusher(formatter.get(), cout);
4404
1e59de90 4405 RGWUserAdminOpState user_op(driver);
20effc67
TL
4406 if (!user_email.empty()) {
4407 user_op.user_email_specified=true;
7c673cae
FG
4408 }
4409
4410 if (!source_zone_name.empty()) {
1e59de90
TL
4411 std::unique_ptr<rgw::sal::Zone> zone;
4412 if (driver->get_zone()->get_zonegroup().get_zone_by_name(source_zone_name, &zone) < 0) {
7c673cae
FG
4413 cerr << "WARNING: cannot find source zone id for name=" << source_zone_name << std::endl;
4414 source_zone = source_zone_name;
1e59de90
TL
4415 } else {
4416 source_zone.id = zone->get_id();
7c673cae
FG
4417 }
4418 }
4419
11fdf7f2 4420 rgw_http_client_init(g_ceph_context);
7c673cae 4421
94b18763
FG
4422 struct rgw_curl_setup {
4423 rgw_curl_setup() {
4424 rgw::curl::setup_curl(boost::none);
4425 }
4426 ~rgw_curl_setup() {
4427 rgw::curl::cleanup_curl();
4428 }
4429 } curl_cleanup;
4430
11fdf7f2
TL
4431 oath_init();
4432
1e59de90 4433 StoreDestructor store_destructor(driver);
7c673cae
FG
4434
4435 if (raw_storage_op) {
4436 switch (opt_cmd) {
9f95a23c 4437 case OPT::PERIOD_DELETE:
7c673cae
FG
4438 {
4439 if (period_id.empty()) {
4440 cerr << "missing period id" << std::endl;
4441 return EINVAL;
4442 }
1e59de90 4443 int ret = cfgstore->delete_period(dpp(), null_yield, period_id);
7c673cae
FG
4444 if (ret < 0) {
4445 cerr << "ERROR: couldn't delete period: " << cpp_strerror(-ret) << std::endl;
4446 return -ret;
4447 }
4448
4449 }
4450 break;
9f95a23c 4451 case OPT::PERIOD_GET:
7c673cae 4452 {
1e59de90 4453 std::optional<epoch_t> epoch;
7c673cae
FG
4454 if (!period_epoch.empty()) {
4455 epoch = atoi(period_epoch.c_str());
4456 }
4457 if (staging) {
1e59de90
TL
4458 RGWRealm realm;
4459 int ret = rgw::read_realm(dpp(), null_yield, cfgstore.get(),
4460 realm_id, realm_name, realm);
7c673cae 4461 if (ret < 0 ) {
1e59de90 4462 cerr << "failed to load realm: " << cpp_strerror(-ret) << std::endl;
7c673cae
FG
4463 return -ret;
4464 }
4465 realm_id = realm.get_id();
4466 realm_name = realm.get_name();
4467 period_id = RGWPeriod::get_staging_id(realm_id);
4468 epoch = 1;
4469 }
1e59de90
TL
4470 if (period_id.empty()) {
4471 // use realm's current period
4472 RGWRealm realm;
4473 int ret = rgw::read_realm(dpp(), null_yield, cfgstore.get(),
4474 realm_id, realm_name, realm);
4475 if (ret < 0 ) {
4476 cerr << "failed to load realm: " << cpp_strerror(-ret) << std::endl;
4477 return -ret;
4478 }
4479 period_id = realm.current_period;
4480 }
4481
4482 RGWPeriod period;
4483 int ret = cfgstore->read_period(dpp(), null_yield, period_id,
4484 epoch, period);
7c673cae 4485 if (ret < 0) {
1e59de90 4486 cerr << "failed to load period: " << cpp_strerror(-ret) << std::endl;
7c673cae
FG
4487 return -ret;
4488 }
f67539c2 4489 encode_json("period", period, formatter.get());
7c673cae 4490 formatter->flush(cout);
7c673cae
FG
4491 }
4492 break;
9f95a23c 4493 case OPT::PERIOD_GET_CURRENT:
7c673cae 4494 {
1e59de90
TL
4495 RGWRealm realm;
4496 int ret = rgw::read_realm(dpp(), null_yield, cfgstore.get(),
4497 realm_id, realm_name, realm);
7c673cae 4498 if (ret < 0) {
1e59de90 4499 std::cerr << "failed to load realm: " << cpp_strerror(ret) << std::endl;
7c673cae
FG
4500 return -ret;
4501 }
1e59de90 4502
7c673cae 4503 formatter->open_object_section("period_get_current");
1e59de90 4504 encode_json("current_period", realm.current_period, formatter.get());
7c673cae
FG
4505 formatter->close_section();
4506 formatter->flush(cout);
4507 }
4508 break;
9f95a23c 4509 case OPT::PERIOD_LIST:
7c673cae 4510 {
1e59de90
TL
4511 Formatter::ObjectSection periods_list{*formatter, "periods_list"};
4512 Formatter::ArraySection periods{*formatter, "periods"};
4513 rgw::sal::ListResult<std::string> listing;
4514 std::array<std::string, 1000> period_ids; // list in pages of 1000
4515 do {
4516 int ret = cfgstore->list_period_ids(dpp(), null_yield, listing.next,
4517 period_ids, listing);
4518 if (ret < 0) {
4519 std::cerr << "failed to list periods: " << cpp_strerror(-ret) << std::endl;
4520 return -ret;
4521 }
4522 for (const auto& id : listing.entries) {
4523 encode_json("id", id, formatter.get());
4524 }
4525 } while (!listing.next.empty());
4526 } // close sections periods and periods_list
4527 formatter->flush(cout);
7c673cae 4528 break;
9f95a23c 4529 case OPT::PERIOD_UPDATE:
7c673cae 4530 {
1e59de90
TL
4531 int ret = update_period(cfgstore.get(), realm_id, realm_name,
4532 period_epoch, commit, remote, url,
4533 opt_region, access_key, secret_key,
f67539c2 4534 formatter.get(), yes_i_really_mean_it);
7c673cae
FG
4535 if (ret < 0) {
4536 return -ret;
4537 }
4538 }
4539 break;
9f95a23c 4540 case OPT::PERIOD_PULL:
7c673cae
FG
4541 {
4542 boost::optional<RGWRESTConn> conn;
4543 RGWRESTConn *remote_conn = nullptr;
4544 if (url.empty()) {
4545 // load current period for endpoints
1e59de90
TL
4546 RGWRealm realm;
4547 int ret = rgw::read_realm(dpp(), null_yield, cfgstore.get(),
4548 realm_id, realm_name, realm);
4549 if (ret < 0 ) {
4550 cerr << "failed to load realm: " << cpp_strerror(-ret) << std::endl;
7c673cae
FG
4551 return -ret;
4552 }
1e59de90
TL
4553 period_id = realm.current_period;
4554
4555 RGWPeriod current_period;
4556 ret = cfgstore->read_period(dpp(), null_yield, period_id,
4557 std::nullopt, current_period);
7c673cae 4558 if (ret < 0) {
1e59de90 4559 cerr << "failed to load current period: " << cpp_strerror(-ret) << std::endl;
7c673cae
FG
4560 return -ret;
4561 }
4562 if (remote.empty()) {
4563 // use realm master zone as remote
9f95a23c 4564 remote = current_period.get_master_zone().id;
7c673cae 4565 }
1e59de90 4566 conn = get_remote_conn(static_cast<rgw::sal::RadosStore*>(driver), current_period.get_map(), remote);
7c673cae
FG
4567 if (!conn) {
4568 cerr << "failed to find a zone or zonegroup for remote "
4569 << remote << std::endl;
4570 return -ENOENT;
4571 }
4572 remote_conn = &*conn;
4573 }
4574
4575 RGWPeriod period;
1e59de90
TL
4576 int ret = do_period_pull(cfgstore.get(), remote_conn, url,
4577 opt_region, access_key, secret_key,
7c673cae
FG
4578 realm_id, realm_name, period_id, period_epoch,
4579 &period);
4580 if (ret < 0) {
4581 cerr << "period pull failed: " << cpp_strerror(-ret) << std::endl;
4582 return -ret;
4583 }
4584
f67539c2 4585 encode_json("period", period, formatter.get());
7c673cae 4586 formatter->flush(cout);
7c673cae
FG
4587 }
4588 break;
20effc67
TL
4589 case OPT::GLOBAL_RATELIMIT_GET:
4590 case OPT::GLOBAL_RATELIMIT_SET:
4591 case OPT::GLOBAL_RATELIMIT_ENABLE:
4592 case OPT::GLOBAL_RATELIMIT_DISABLE:
4593 {
4594 if (realm_id.empty()) {
20effc67
TL
4595 if (!realm_name.empty()) {
4596 // look up realm_id for the given realm_name
1e59de90
TL
4597 int ret = cfgstore->read_realm_id(dpp(), null_yield,
4598 realm_name, realm_id);
20effc67
TL
4599 if (ret < 0) {
4600 cerr << "ERROR: failed to read realm for " << realm_name
4601 << ": " << cpp_strerror(-ret) << std::endl;
4602 return -ret;
4603 }
4604 } else {
4605 // use default realm_id when none is given
1e59de90
TL
4606 int ret = cfgstore->read_default_realm_id(dpp(), null_yield,
4607 realm_id);
20effc67
TL
4608 if (ret < 0 && ret != -ENOENT) { // on ENOENT, use empty realm_id
4609 cerr << "ERROR: failed to read default realm: "
4610 << cpp_strerror(-ret) << std::endl;
4611 return -ret;
4612 }
4613 }
4614 }
4615
4616 RGWPeriodConfig period_config;
1e59de90
TL
4617 int ret = cfgstore->read_period_config(dpp(), null_yield, realm_id,
4618 period_config);
20effc67
TL
4619 if (ret < 0 && ret != -ENOENT) {
4620 cerr << "ERROR: failed to read period config: "
4621 << cpp_strerror(-ret) << std::endl;
4622 return -ret;
4623 }
4624 bool ratelimit_configured = true;
4625 formatter->open_object_section("period_config");
4626 if (ratelimit_scope == "bucket") {
4627 ratelimit_configured = set_ratelimit_info(period_config.bucket_ratelimit, opt_cmd,
4628 max_read_ops, max_write_ops,
4629 max_read_bytes, max_write_bytes,
4630 have_max_read_ops, have_max_write_ops,
4631 have_max_read_bytes, have_max_write_bytes);
4632 encode_json("bucket_ratelimit", period_config.bucket_ratelimit, formatter.get());
4633 } else if (ratelimit_scope == "user") {
4634 ratelimit_configured = set_ratelimit_info(period_config.user_ratelimit, opt_cmd,
4635 max_read_ops, max_write_ops,
4636 max_read_bytes, max_write_bytes,
4637 have_max_read_ops, have_max_write_ops,
4638 have_max_read_bytes, have_max_write_bytes);
4639 encode_json("user_ratelimit", period_config.user_ratelimit, formatter.get());
4640 } else if (ratelimit_scope == "anonymous") {
4641 ratelimit_configured = set_ratelimit_info(period_config.anon_ratelimit, opt_cmd,
4642 max_read_ops, max_write_ops,
4643 max_read_bytes, max_write_bytes,
4644 have_max_read_ops, have_max_write_ops,
4645 have_max_read_bytes, have_max_write_bytes);
4646 encode_json("anonymous_ratelimit", period_config.anon_ratelimit, formatter.get());
4647 } else if (ratelimit_scope.empty() && opt_cmd == OPT::GLOBAL_RATELIMIT_GET) {
4648 // if no scope is given for GET, print both
4649 encode_json("bucket_ratelimit", period_config.bucket_ratelimit, formatter.get());
4650 encode_json("user_ratelimit", period_config.user_ratelimit, formatter.get());
4651 encode_json("anonymous_ratelimit", period_config.anon_ratelimit, formatter.get());
4652 } else {
4653 cerr << "ERROR: invalid rate limit scope specification. Please specify "
4654 "either --ratelimit-scope=bucket, or --ratelimit-scope=user or --ratelimit-scope=anonymous" << std::endl;
4655 return EINVAL;
4656 }
4657 if (!ratelimit_configured) {
4658 cerr << "ERROR: no rate limit values have been specified" << std::endl;
4659 return EINVAL;
4660 }
4661
4662 formatter->close_section();
4663
4664 if (opt_cmd != OPT::GLOBAL_RATELIMIT_GET) {
4665 // write the modified period config
1e59de90
TL
4666 constexpr bool exclusive = false;
4667 ret = cfgstore->write_period_config(dpp(), null_yield, exclusive,
4668 realm_id, period_config);
20effc67
TL
4669 if (ret < 0) {
4670 cerr << "ERROR: failed to write period config: "
4671 << cpp_strerror(-ret) << std::endl;
4672 return -ret;
4673 }
4674 if (!realm_id.empty()) {
4675 cout << "Global ratelimit changes saved. Use 'period update' to apply "
4676 "them to the staging period, and 'period commit' to commit the "
4677 "new period." << std::endl;
4678 } else {
4679 cout << "Global ratelimit changes saved. They will take effect as "
4680 "the gateways are restarted." << std::endl;
4681 }
4682 }
4683
4684 formatter->flush(cout);
4685 }
4686 break;
9f95a23c
TL
4687 case OPT::GLOBAL_QUOTA_GET:
4688 case OPT::GLOBAL_QUOTA_SET:
4689 case OPT::GLOBAL_QUOTA_ENABLE:
4690 case OPT::GLOBAL_QUOTA_DISABLE:
7c673cae
FG
4691 {
4692 if (realm_id.empty()) {
7c673cae
FG
4693 if (!realm_name.empty()) {
4694 // look up realm_id for the given realm_name
1e59de90
TL
4695 int ret = cfgstore->read_realm_id(dpp(), null_yield,
4696 realm_name, realm_id);
7c673cae
FG
4697 if (ret < 0) {
4698 cerr << "ERROR: failed to read realm for " << realm_name
4699 << ": " << cpp_strerror(-ret) << std::endl;
4700 return -ret;
4701 }
4702 } else {
4703 // use default realm_id when none is given
1e59de90
TL
4704 int ret = cfgstore->read_default_realm_id(dpp(), null_yield,
4705 realm_id);
7c673cae
FG
4706 if (ret < 0 && ret != -ENOENT) { // on ENOENT, use empty realm_id
4707 cerr << "ERROR: failed to read default realm: "
4708 << cpp_strerror(-ret) << std::endl;
4709 return -ret;
4710 }
4711 }
4712 }
4713
4714 RGWPeriodConfig period_config;
1e59de90
TL
4715 int ret = cfgstore->read_period_config(dpp(), null_yield, realm_id,
4716 period_config);
7c673cae
FG
4717 if (ret < 0 && ret != -ENOENT) {
4718 cerr << "ERROR: failed to read period config: "
4719 << cpp_strerror(-ret) << std::endl;
4720 return -ret;
4721 }
4722
4723 formatter->open_object_section("period_config");
4724 if (quota_scope == "bucket") {
1e59de90 4725 set_quota_info(period_config.quota.bucket_quota, opt_cmd,
7c673cae
FG
4726 max_size, max_objects,
4727 have_max_size, have_max_objects);
1e59de90 4728 encode_json("bucket quota", period_config.quota.bucket_quota, formatter.get());
7c673cae 4729 } else if (quota_scope == "user") {
1e59de90 4730 set_quota_info(period_config.quota.user_quota, opt_cmd,
7c673cae
FG
4731 max_size, max_objects,
4732 have_max_size, have_max_objects);
1e59de90 4733 encode_json("user quota", period_config.quota.user_quota, formatter.get());
9f95a23c 4734 } else if (quota_scope.empty() && opt_cmd == OPT::GLOBAL_QUOTA_GET) {
7c673cae 4735 // if no scope is given for GET, print both
1e59de90
TL
4736 encode_json("bucket quota", period_config.quota.bucket_quota, formatter.get());
4737 encode_json("user quota", period_config.quota.user_quota, formatter.get());
7c673cae
FG
4738 } else {
4739 cerr << "ERROR: invalid quota scope specification. Please specify "
4740 "either --quota-scope=bucket, or --quota-scope=user" << std::endl;
4741 return EINVAL;
4742 }
4743 formatter->close_section();
4744
9f95a23c 4745 if (opt_cmd != OPT::GLOBAL_QUOTA_GET) {
7c673cae 4746 // write the modified period config
1e59de90
TL
4747 constexpr bool exclusive = false;
4748 ret = cfgstore->write_period_config(dpp(), null_yield, exclusive,
4749 realm_id, period_config);
7c673cae
FG
4750 if (ret < 0) {
4751 cerr << "ERROR: failed to write period config: "
4752 << cpp_strerror(-ret) << std::endl;
4753 return -ret;
4754 }
4755 if (!realm_id.empty()) {
4756 cout << "Global quota changes saved. Use 'period update' to apply "
4757 "them to the staging period, and 'period commit' to commit the "
4758 "new period." << std::endl;
4759 } else {
4760 cout << "Global quota changes saved. They will take effect as "
4761 "the gateways are restarted." << std::endl;
4762 }
4763 }
4764
4765 formatter->flush(cout);
7c673cae
FG
4766 }
4767 break;
9f95a23c 4768 case OPT::REALM_CREATE:
7c673cae
FG
4769 {
4770 if (realm_name.empty()) {
4771 cerr << "missing realm name" << std::endl;
4772 return EINVAL;
4773 }
4774
1e59de90
TL
4775 RGWRealm realm;
4776 realm.name = realm_name;
4777
4778 constexpr bool exclusive = true;
4779 int ret = rgw::create_realm(dpp(), null_yield, cfgstore.get(),
4780 exclusive, realm);
7c673cae
FG
4781 if (ret < 0) {
4782 cerr << "ERROR: couldn't create realm " << realm_name << ": " << cpp_strerror(-ret) << std::endl;
4783 return -ret;
4784 }
4785
4786 if (set_default) {
1e59de90 4787 ret = rgw::set_default_realm(dpp(), null_yield, cfgstore.get(), realm);
7c673cae
FG
4788 if (ret < 0) {
4789 cerr << "failed to set realm " << realm_name << " as default: " << cpp_strerror(-ret) << std::endl;
4790 }
4791 }
4792
f67539c2 4793 encode_json("realm", realm, formatter.get());
7c673cae 4794 formatter->flush(cout);
7c673cae
FG
4795 }
4796 break;
9f95a23c 4797 case OPT::REALM_DELETE:
7c673cae 4798 {
1e59de90 4799 if (realm_id.empty() && realm_name.empty()) {
7c673cae
FG
4800 cerr << "missing realm name or id" << std::endl;
4801 return EINVAL;
4802 }
1e59de90
TL
4803 RGWRealm realm;
4804 std::unique_ptr<rgw::sal::RealmWriter> writer;
4805 int ret = rgw::read_realm(dpp(), null_yield, cfgstore.get(),
4806 realm_id, realm_name, realm, &writer);
7c673cae 4807 if (ret < 0) {
1e59de90 4808 cerr << "failed to load realm: " << cpp_strerror(-ret) << std::endl;
7c673cae
FG
4809 return -ret;
4810 }
1e59de90 4811 ret = writer->remove(dpp(), null_yield);
7c673cae 4812 if (ret < 0) {
1e59de90 4813 cerr << "failed to remove realm: " << cpp_strerror(-ret) << std::endl;
7c673cae
FG
4814 return -ret;
4815 }
4816
4817 }
4818 break;
9f95a23c 4819 case OPT::REALM_GET:
7c673cae 4820 {
1e59de90
TL
4821 RGWRealm realm;
4822 int ret = rgw::read_realm(dpp(), null_yield, cfgstore.get(),
4823 realm_id, realm_name, realm);
7c673cae
FG
4824 if (ret < 0) {
4825 if (ret == -ENOENT && realm_name.empty() && realm_id.empty()) {
4826 cerr << "missing realm name or id, or default realm not found" << std::endl;
4827 } else {
1e59de90 4828 cerr << "failed to load realm: " << cpp_strerror(-ret) << std::endl;
7c673cae
FG
4829 }
4830 return -ret;
4831 }
f67539c2 4832 encode_json("realm", realm, formatter.get());
7c673cae 4833 formatter->flush(cout);
7c673cae
FG
4834 }
4835 break;
9f95a23c 4836 case OPT::REALM_GET_DEFAULT:
7c673cae 4837 {
7c673cae 4838 string default_id;
1e59de90 4839 int ret = cfgstore->read_default_realm_id(dpp(), null_yield, default_id);
7c673cae
FG
4840 if (ret == -ENOENT) {
4841 cout << "No default realm is set" << std::endl;
4842 return -ret;
4843 } else if (ret < 0) {
20effc67 4844 cerr << "Error reading default realm: " << cpp_strerror(-ret) << std::endl;
7c673cae
FG
4845 return -ret;
4846 }
4847 cout << "default realm: " << default_id << std::endl;
4848 }
4849 break;
9f95a23c 4850 case OPT::REALM_LIST:
7c673cae 4851 {
1e59de90
TL
4852 std::string default_id;
4853 int ret = cfgstore->read_default_realm_id(dpp(), null_yield,
4854 default_id);
7c673cae
FG
4855 if (ret < 0 && ret != -ENOENT) {
4856 cerr << "could not determine default realm: " << cpp_strerror(-ret) << std::endl;
4857 }
1e59de90
TL
4858
4859 Formatter::ObjectSection realms_list{*formatter, "realms_list"};
4860 encode_json("default_info", default_id, formatter.get());
4861
4862 Formatter::ArraySection realms{*formatter, "realms"};
4863 rgw::sal::ListResult<std::string> listing;
4864 std::array<std::string, 1000> names; // list in pages of 1000
4865 do {
4866 ret = cfgstore->list_realm_names(dpp(), null_yield, listing.next,
4867 names, listing);
4868 if (ret < 0) {
4869 std::cerr << "failed to list realms: " << cpp_strerror(-ret) << std::endl;
4870 return -ret;
4871 }
4872 for (const auto& name : listing.entries) {
4873 encode_json("name", name, formatter.get());
4874 }
4875 } while (!listing.next.empty());
4876 } // close sections realms and realms_list
4877 formatter->flush(cout);
7c673cae 4878 break;
9f95a23c 4879 case OPT::REALM_LIST_PERIODS:
7c673cae 4880 {
1e59de90
TL
4881 // use realm's current period
4882 RGWRealm realm;
4883 int ret = rgw::read_realm(dpp(), null_yield, cfgstore.get(),
4884 realm_id, realm_name, realm);
4885 if (ret < 0) {
4886 cerr << "failed to load realm: " << cpp_strerror(-ret) << std::endl;
4887 return -ret;
4888 }
4889 period_id = realm.current_period;
4890
4891 Formatter::ObjectSection periods_list{*formatter, "realm_periods_list"};
f67539c2 4892 encode_json("current_period", period_id, formatter.get());
1e59de90
TL
4893
4894 Formatter::ArraySection periods{*formatter, "periods"};
4895
4896 while (!period_id.empty()) {
4897 RGWPeriod period;
4898 ret = cfgstore->read_period(dpp(), null_yield, period_id,
4899 std::nullopt, period);
4900 if (ret < 0) {
4901 cerr << "failed to load period id " << period_id
4902 << ": " << cpp_strerror(-ret) << std::endl;
4903 return -ret;
4904 }
4905 encode_json("id", period_id, formatter.get());
4906 period_id = period.predecessor_uuid;
4907 }
4908 } // close sections periods and realm_periods_list
4909 formatter->flush(cout);
7c673cae
FG
4910 break;
4911
9f95a23c 4912 case OPT::REALM_RENAME:
7c673cae 4913 {
7c673cae
FG
4914 if (realm_new_name.empty()) {
4915 cerr << "missing realm new name" << std::endl;
4916 return EINVAL;
4917 }
4918 if (realm_name.empty() && realm_id.empty()) {
4919 cerr << "missing realm name or id" << std::endl;
4920 return EINVAL;
4921 }
1e59de90
TL
4922
4923 RGWRealm realm;
4924 std::unique_ptr<rgw::sal::RealmWriter> writer;
4925 int ret = rgw::read_realm(dpp(), null_yield, cfgstore.get(),
4926 realm_id, realm_name, realm, &writer);
7c673cae 4927 if (ret < 0) {
1e59de90 4928 cerr << "failed to load realm: " << cpp_strerror(-ret) << std::endl;
7c673cae
FG
4929 return -ret;
4930 }
1e59de90 4931 ret = writer->rename(dpp(), null_yield, realm, realm_new_name);
7c673cae 4932 if (ret < 0) {
1e59de90 4933 cerr << "rename failed: " << cpp_strerror(-ret) << std::endl;
7c673cae
FG
4934 return -ret;
4935 }
4936 cout << "Realm name updated. Note that this change only applies to "
4937 "the current cluster, so this command must be run separately "
4938 "on each of the realm's other clusters." << std::endl;
4939 }
4940 break;
9f95a23c 4941 case OPT::REALM_SET:
7c673cae
FG
4942 {
4943 if (realm_id.empty() && realm_name.empty()) {
4944 cerr << "no realm name or id provided" << std::endl;
4945 return EINVAL;
4946 }
7c673cae 4947 bool new_realm = false;
1e59de90
TL
4948 RGWRealm realm;
4949 std::unique_ptr<rgw::sal::RealmWriter> writer;
4950 int ret = rgw::read_realm(dpp(), null_yield, cfgstore.get(),
4951 realm_id, realm_name, realm, &writer);
7c673cae
FG
4952 if (ret < 0 && ret != -ENOENT) {
4953 cerr << "failed to init realm: " << cpp_strerror(-ret) << std::endl;
4954 return -ret;
4955 } else if (ret == -ENOENT) {
4956 new_realm = true;
4957 }
4958 ret = read_decode_json(infile, realm);
4959 if (ret < 0) {
4960 return 1;
4961 }
4962 if (!realm_name.empty() && realm.get_name() != realm_name) {
4963 cerr << "mismatch between --rgw-realm " << realm_name << " and json input file name " <<
4964 realm.get_name() << std::endl;
4965 return EINVAL;
4966 }
4967 /* new realm */
4968 if (new_realm) {
4969 cout << "clearing period and epoch for new realm" << std::endl;
4970 realm.clear_current_period_and_epoch();
1e59de90
TL
4971 constexpr bool exclusive = true;
4972 ret = rgw::create_realm(dpp(), null_yield, cfgstore.get(),
4973 exclusive, realm);
7c673cae
FG
4974 if (ret < 0) {
4975 cerr << "ERROR: couldn't create new realm: " << cpp_strerror(-ret) << std::endl;
4976 return 1;
4977 }
4978 } else {
1e59de90 4979 ret = writer->write(dpp(), null_yield, realm);
7c673cae 4980 if (ret < 0) {
1e59de90 4981 cerr << "ERROR: couldn't driver realm info: " << cpp_strerror(-ret) << std::endl;
7c673cae
FG
4982 return 1;
4983 }
4984 }
4985
4986 if (set_default) {
1e59de90 4987 ret = rgw::set_default_realm(dpp(), null_yield, cfgstore.get(), realm);
7c673cae
FG
4988 if (ret < 0) {
4989 cerr << "failed to set realm " << realm_name << " as default: " << cpp_strerror(-ret) << std::endl;
4990 }
4991 }
f67539c2 4992 encode_json("realm", realm, formatter.get());
7c673cae
FG
4993 formatter->flush(cout);
4994 }
4995 break;
4996
9f95a23c 4997 case OPT::REALM_DEFAULT:
7c673cae 4998 {
1e59de90
TL
4999 RGWRealm realm;
5000 int ret = rgw::read_realm(dpp(), null_yield, cfgstore.get(),
5001 realm_id, realm_name, realm);
7c673cae 5002 if (ret < 0) {
1e59de90 5003 cerr << "failed to load realm: " << cpp_strerror(-ret) << std::endl;
7c673cae
FG
5004 return -ret;
5005 }
1e59de90 5006 ret = rgw::set_default_realm(dpp(), null_yield, cfgstore.get(), realm);
7c673cae
FG
5007 if (ret < 0) {
5008 cerr << "failed to set realm as default: " << cpp_strerror(-ret) << std::endl;
5009 return -ret;
5010 }
5011 }
5012 break;
9f95a23c 5013 case OPT::REALM_PULL:
7c673cae
FG
5014 {
5015 if (url.empty()) {
5016 cerr << "A --url must be provided." << std::endl;
5017 return EINVAL;
5018 }
5019 RGWEnv env;
5020 req_info info(g_ceph_context, &env);
5021 info.method = "GET";
5022 info.request_uri = "/admin/realm";
5023
5024 map<string, string> &params = info.args.get_params();
5025 if (!realm_id.empty())
5026 params["id"] = realm_id;
5027 if (!realm_name.empty())
5028 params["name"] = realm_name;
5029
5030 bufferlist bl;
5031 JSONParser p;
20effc67 5032 int ret = send_to_url(url, opt_region, access_key, secret_key, info, bl, p);
7c673cae
FG
5033 if (ret < 0) {
5034 cerr << "request failed: " << cpp_strerror(-ret) << std::endl;
5035 if (ret == -EACCES) {
5036 cerr << "If the realm has been changed on the master zone, the "
5037 "master zone's gateway may need to be restarted to recognize "
5038 "this user." << std::endl;
5039 }
5040 return -ret;
5041 }
5042 RGWRealm realm;
7c673cae
FG
5043 try {
5044 decode_json_obj(realm, &p);
9f95a23c
TL
5045 } catch (const JSONDecoder::err& e) {
5046 cerr << "failed to decode JSON response: " << e.what() << std::endl;
7c673cae
FG
5047 return EINVAL;
5048 }
5049 RGWPeriod period;
5050 auto& current_period = realm.get_current_period();
5051 if (!current_period.empty()) {
5052 // pull the latest epoch of the realm's current period
1e59de90 5053 ret = do_period_pull(cfgstore.get(), nullptr, url, opt_region,
20effc67 5054 access_key, secret_key,
7c673cae
FG
5055 realm_id, realm_name, current_period, "",
5056 &period);
5057 if (ret < 0) {
5058 cerr << "could not fetch period " << current_period << std::endl;
5059 return -ret;
5060 }
5061 }
1e59de90
TL
5062 constexpr bool exclusive = false;
5063 ret = rgw::create_realm(dpp(), null_yield, cfgstore.get(),
5064 exclusive, realm);
5065 if (ret < 0) {
7c673cae
FG
5066 cerr << "Error storing realm " << realm.get_id() << ": "
5067 << cpp_strerror(ret) << std::endl;
5068 return -ret;
1e59de90 5069 }
7c673cae
FG
5070
5071 if (set_default) {
1e59de90 5072 ret = rgw::set_default_realm(dpp(), null_yield, cfgstore.get(), realm);
7c673cae
FG
5073 if (ret < 0) {
5074 cerr << "failed to set realm " << realm_name << " as default: " << cpp_strerror(-ret) << std::endl;
5075 }
5076 }
5077
f67539c2 5078 encode_json("realm", realm, formatter.get());
7c673cae 5079 formatter->flush(cout);
7c673cae 5080 }
11fdf7f2 5081 break;
7c673cae 5082
9f95a23c 5083 case OPT::ZONEGROUP_ADD:
7c673cae
FG
5084 {
5085 if (zonegroup_id.empty() && zonegroup_name.empty()) {
5086 cerr << "no zonegroup name or id provided" << std::endl;
5087 return EINVAL;
5088 }
5089
1e59de90
TL
5090 // load the zonegroup and zone params
5091 RGWZoneGroup zonegroup;
5092 std::unique_ptr<rgw::sal::ZoneGroupWriter> zonegroup_writer;
5093 int ret = rgw::read_zonegroup(dpp(), null_yield, cfgstore.get(),
5094 zonegroup_id, zonegroup_name,
5095 zonegroup, &zonegroup_writer);
7c673cae 5096 if (ret < 0) {
1e59de90
TL
5097 cerr << "failed to load zonegroup " << zonegroup_name << " id "
5098 << zonegroup_id << ": " << cpp_strerror(-ret) << std::endl;
7c673cae
FG
5099 return -ret;
5100 }
1e59de90
TL
5101
5102 RGWZoneParams zone_params;
5103 std::unique_ptr<rgw::sal::ZoneWriter> zone_writer;
5104 ret = rgw::read_zone(dpp(), null_yield, cfgstore.get(),
5105 zone_id, zone_name, zone_params, &zone_writer);
7c673cae 5106 if (ret < 0) {
1e59de90 5107 cerr << "unable to load zone: " << cpp_strerror(-ret) << std::endl;
7c673cae
FG
5108 return -ret;
5109 }
1e59de90
TL
5110
5111 // update zone_params if necessary
5112 bool need_zone_update = false;
5113
5114 if (zone_params.realm_id != zonegroup.realm_id) {
5115 if (!zone_params.realm_id.empty()) {
5116 cerr << "WARNING: overwriting zone realm_id=" << zone_params.realm_id
5117 << " to match zonegroup realm_id=" << zonegroup.realm_id << std::endl;
5118 }
5119 zone_params.realm_id = zonegroup.realm_id;
5120 need_zone_update = true;
5121 }
5122
5123 for (auto a : tier_config_add) {
5124 ret = zone_params.tier_config.set(a.first, a.second);
5125 if (ret < 0) {
5126 cerr << "ERROR: failed to set configurable: " << a << std::endl;
5127 return EINVAL;
5128 }
5129 need_zone_update = true;
5130 }
5131
5132 if (need_zone_update) {
5133 ret = zone_writer->write(dpp(), null_yield, zone_params);
224ce89b
WB
5134 if (ret < 0) {
5135 cerr << "failed to save zone info: " << cpp_strerror(-ret) << std::endl;
5136 return -ret;
5137 }
5138 }
5139
1e59de90
TL
5140 const bool *pis_master = (is_master_set ? &is_master : nullptr);
5141 const bool *pread_only = (is_read_only_set ? &read_only : nullptr);
5142 const bool *psync_from_all = (sync_from_all_specified ? &sync_from_all : nullptr);
5143 const string *predirect_zone = (redirect_zone_set ? &redirect_zone : nullptr);
5144
5145 // validate --tier-type if specified
5146 const string *ptier_type = (tier_type_specified ? &tier_type : nullptr);
5147 if (ptier_type) {
5148 auto sync_mgr = static_cast<rgw::sal::RadosStore*>(driver)->svc()->sync_modules->get_manager();
5149 if (!sync_mgr->get_module(*ptier_type, nullptr)) {
5150 ldpp_dout(dpp(), -1) << "ERROR: could not find sync module: "
5151 << *ptier_type << ", valid sync modules: "
5152 << sync_mgr->get_registered_module_names() << dendl;
11fdf7f2
TL
5153 return EINVAL;
5154 }
5155 }
7c673cae 5156
1e59de90
TL
5157 if (enable_features.empty()) { // enable all features by default
5158 enable_features.insert(rgw::zone_features::supported.begin(),
5159 rgw::zone_features::supported.end());
5160 }
5161
5162 // add/update the public zone information stored in the zonegroup
5163 ret = rgw::add_zone_to_group(dpp(), zonegroup, zone_params,
5164 pis_master, pread_only, endpoints,
5165 ptier_type, psync_from_all,
5166 sync_from, sync_from_rm,
5167 predirect_zone, bucket_index_max_shards,
5168 enable_features, disable_features);
5169 if (ret < 0) {
5170 return -ret;
5171 }
5172
5173 // write the updated zonegroup
5174 ret = zonegroup_writer->write(dpp(), null_yield, zonegroup);
7c673cae 5175 if (ret < 0) {
1e59de90
TL
5176 cerr << "failed to write updated zonegroup " << zonegroup.get_name()
5177 << ": " << cpp_strerror(-ret) << std::endl;
7c673cae
FG
5178 return -ret;
5179 }
5180
f67539c2 5181 encode_json("zonegroup", zonegroup, formatter.get());
7c673cae
FG
5182 formatter->flush(cout);
5183 }
5184 break;
9f95a23c 5185 case OPT::ZONEGROUP_CREATE:
7c673cae
FG
5186 {
5187 if (zonegroup_name.empty()) {
5188 cerr << "Missing zonegroup name" << std::endl;
5189 return EINVAL;
5190 }
1e59de90
TL
5191 RGWRealm realm;
5192 int ret = rgw::read_realm(dpp(), null_yield, cfgstore.get(),
5193 realm_id, realm_name, realm);
7c673cae
FG
5194 if (ret < 0) {
5195 cerr << "failed to init realm: " << cpp_strerror(-ret) << std::endl;
5196 return -ret;
5197 }
5198
1e59de90
TL
5199 RGWZoneGroup zonegroup;
5200 zonegroup.name = zonegroup_name;
5201 zonegroup.is_master = is_master;
5202 zonegroup.realm_id = realm.get_id();
5203 zonegroup.endpoints = endpoints;
7c673cae 5204 zonegroup.api_name = (api_name.empty() ? zonegroup_name : api_name);
1e59de90
TL
5205
5206 zonegroup.enabled_features = enable_features;
05a536ef
TL
5207 if (zonegroup.enabled_features.empty()) { // enable features by default
5208 zonegroup.enabled_features.insert(rgw::zone_features::enabled.begin(),
5209 rgw::zone_features::enabled.end());
1e59de90
TL
5210 }
5211 for (const auto& feature : disable_features) {
5212 auto i = zonegroup.enabled_features.find(feature);
5213 if (i == zonegroup.enabled_features.end()) {
5214 ldout(cct, 1) << "WARNING: zone feature \"" << feature
5215 << "\" was not enabled in zonegroup " << zonegroup_name << dendl;
5216 continue;
5217 }
5218 zonegroup.enabled_features.erase(i);
5219 }
5220
5221 constexpr bool exclusive = true;
5222 ret = rgw::create_zonegroup(dpp(), null_yield, cfgstore.get(),
5223 exclusive, zonegroup);
7c673cae
FG
5224 if (ret < 0) {
5225 cerr << "failed to create zonegroup " << zonegroup_name << ": " << cpp_strerror(-ret) << std::endl;
5226 return -ret;
5227 }
5228
5229 if (set_default) {
1e59de90
TL
5230 ret = rgw::set_default_zonegroup(dpp(), null_yield, cfgstore.get(),
5231 zonegroup);
7c673cae
FG
5232 if (ret < 0) {
5233 cerr << "failed to set zonegroup " << zonegroup_name << " as default: " << cpp_strerror(-ret) << std::endl;
5234 }
5235 }
5236
f67539c2 5237 encode_json("zonegroup", zonegroup, formatter.get());
7c673cae 5238 formatter->flush(cout);
7c673cae
FG
5239 }
5240 break;
9f95a23c 5241 case OPT::ZONEGROUP_DEFAULT:
7c673cae
FG
5242 {
5243 if (zonegroup_id.empty() && zonegroup_name.empty()) {
5244 cerr << "no zonegroup name or id provided" << std::endl;
5245 return EINVAL;
5246 }
5247
1e59de90
TL
5248 RGWZoneGroup zonegroup;
5249 int ret = rgw::read_zonegroup(dpp(), null_yield, cfgstore.get(),
5250 zonegroup_id, zonegroup_name,
5251 zonegroup);
7c673cae
FG
5252 if (ret < 0) {
5253 cerr << "failed to init zonegroup: " << cpp_strerror(-ret) << std::endl;
5254 return -ret;
5255 }
5256
1e59de90
TL
5257 ret = rgw::set_default_zonegroup(dpp(), null_yield, cfgstore.get(),
5258 zonegroup);
7c673cae
FG
5259 if (ret < 0) {
5260 cerr << "failed to set zonegroup as default: " << cpp_strerror(-ret) << std::endl;
5261 return -ret;
5262 }
5263 }
5264 break;
9f95a23c 5265 case OPT::ZONEGROUP_DELETE:
7c673cae 5266 {
1e59de90 5267 if (zonegroup_id.empty() && zonegroup_name.empty()) {
7c673cae
FG
5268 cerr << "no zonegroup name or id provided" << std::endl;
5269 return EINVAL;
5270 }
1e59de90
TL
5271 RGWZoneGroup zonegroup;
5272 std::unique_ptr<rgw::sal::ZoneGroupWriter> writer;
5273 int ret = rgw::read_zonegroup(dpp(), null_yield, cfgstore.get(),
5274 zonegroup_id, zonegroup_name,
5275 zonegroup, &writer);
7c673cae 5276 if (ret < 0) {
1e59de90 5277 cerr << "failed to load zonegroup: " << cpp_strerror(-ret) << std::endl;
7c673cae
FG
5278 return -ret;
5279 }
1e59de90 5280 ret = writer->remove(dpp(), null_yield);
7c673cae
FG
5281 if (ret < 0) {
5282 cerr << "ERROR: couldn't delete zonegroup: " << cpp_strerror(-ret) << std::endl;
5283 return -ret;
5284 }
5285 }
5286 break;
9f95a23c 5287 case OPT::ZONEGROUP_GET:
7c673cae 5288 {
1e59de90
TL
5289 RGWZoneGroup zonegroup;
5290 int ret = rgw::read_zonegroup(dpp(), null_yield, cfgstore.get(),
5291 zonegroup_id, zonegroup_name, zonegroup);
7c673cae 5292 if (ret < 0) {
1e59de90 5293 cerr << "failed to load zonegroup: " << cpp_strerror(-ret) << std::endl;
7c673cae
FG
5294 return -ret;
5295 }
5296
f67539c2 5297 encode_json("zonegroup", zonegroup, formatter.get());
7c673cae 5298 formatter->flush(cout);
7c673cae
FG
5299 }
5300 break;
9f95a23c 5301 case OPT::ZONEGROUP_LIST:
7c673cae 5302 {
1e59de90
TL
5303 RGWZoneGroup default_zonegroup;
5304 int ret = rgw::read_zonegroup(dpp(), null_yield, cfgstore.get(),
5305 {}, {}, default_zonegroup);
7c673cae
FG
5306 if (ret < 0 && ret != -ENOENT) {
5307 cerr << "could not determine default zonegroup: " << cpp_strerror(-ret) << std::endl;
5308 }
1e59de90
TL
5309
5310 Formatter::ObjectSection zonegroups_list{*formatter, "zonegroups_list"};
5311 encode_json("default_info", default_zonegroup.id, formatter.get());
5312
5313 Formatter::ArraySection zonegroups{*formatter, "zonegroups"};
5314 rgw::sal::ListResult<std::string> listing;
5315 std::array<std::string, 1000> names; // list in pages of 1000
5316 do {
5317 ret = cfgstore->list_zonegroup_names(dpp(), null_yield, listing.next,
5318 names, listing);
5319 if (ret < 0) {
5320 std::cerr << "failed to list zonegroups: " << cpp_strerror(-ret) << std::endl;
5321 return -ret;
5322 }
5323 for (const auto& name : listing.entries) {
5324 encode_json("name", name, formatter.get());
5325 }
5326 } while (!listing.next.empty());
5327 } // close sections zonegroups and zonegroups_list
5328 formatter->flush(cout);
7c673cae 5329 break;
9f95a23c 5330 case OPT::ZONEGROUP_MODIFY:
7c673cae 5331 {
1e59de90
TL
5332 RGWZoneGroup zonegroup;
5333 std::unique_ptr<rgw::sal::ZoneGroupWriter> writer;
5334 int ret = rgw::read_zonegroup(dpp(), null_yield, cfgstore.get(),
5335 zonegroup_id, zonegroup_name,
5336 zonegroup, &writer);
7c673cae
FG
5337 if (ret < 0) {
5338 cerr << "failed to init zonegroup: " << cpp_strerror(-ret) << std::endl;
5339 return -ret;
5340 }
5341
5342 bool need_update = false;
5343
5344 if (!master_zone.empty()) {
5345 zonegroup.master_zone = master_zone;
5346 need_update = true;
5347 }
5348
5349 if (is_master_set) {
1e59de90 5350 zonegroup.is_master = is_master;
7c673cae
FG
5351 need_update = true;
5352 }
5353
5354 if (!endpoints.empty()) {
5355 zonegroup.endpoints = endpoints;
5356 need_update = true;
5357 }
5358
5359 if (!api_name.empty()) {
5360 zonegroup.api_name = api_name;
5361 need_update = true;
5362 }
5363
5364 if (!realm_id.empty()) {
5365 zonegroup.realm_id = realm_id;
5366 need_update = true;
5367 } else if (!realm_name.empty()) {
5368 // get realm id from name
1e59de90
TL
5369 ret = cfgstore->read_realm_id(dpp(), null_yield, realm_name,
5370 zonegroup.realm_id);
7c673cae
FG
5371 if (ret < 0) {
5372 cerr << "failed to find realm by name " << realm_name << std::endl;
5373 return -ret;
5374 }
5375 need_update = true;
5376 }
5377
9f95a23c
TL
5378 if (bucket_index_max_shards) {
5379 for (auto& [name, zone] : zonegroup.zones) {
5380 zone.bucket_index_max_shards = *bucket_index_max_shards;
5381 }
5382 need_update = true;
5383 }
5384
1e59de90
TL
5385 for (const auto& feature : enable_features) {
5386 zonegroup.enabled_features.insert(feature);
5387 need_update = true;
5388 }
5389 for (const auto& feature : disable_features) {
5390 auto i = zonegroup.enabled_features.find(feature);
5391 if (i == zonegroup.enabled_features.end()) {
5392 ldout(cct, 1) << "WARNING: zone feature \"" << feature
5393 << "\" was not enabled in zonegroup "
5394 << zonegroup.get_name() << dendl;
5395 continue;
5396 }
5397 zonegroup.enabled_features.erase(i);
5398 need_update = true;
5399 }
5400
7c673cae 5401 if (need_update) {
1e59de90 5402 ret = writer->write(dpp(), null_yield, zonegroup);
7c673cae
FG
5403 if (ret < 0) {
5404 cerr << "failed to update zonegroup: " << cpp_strerror(-ret) << std::endl;
5405 return -ret;
5406 }
5407 }
5408
5409 if (set_default) {
1e59de90
TL
5410 ret = rgw::set_default_zonegroup(dpp(), null_yield, cfgstore.get(),
5411 zonegroup);
7c673cae
FG
5412 if (ret < 0) {
5413 cerr << "failed to set zonegroup " << zonegroup_name << " as default: " << cpp_strerror(-ret) << std::endl;
5414 }
5415 }
5416
f67539c2 5417 encode_json("zonegroup", zonegroup, formatter.get());
7c673cae
FG
5418 formatter->flush(cout);
5419 }
5420 break;
9f95a23c 5421 case OPT::ZONEGROUP_SET:
7c673cae 5422 {
1e59de90
TL
5423 RGWRealm realm;
5424 int ret = rgw::read_realm(dpp(), null_yield, cfgstore.get(),
5425 realm_id, realm_name, realm);
f67539c2 5426 bool default_realm_not_exist = (ret == -ENOENT && realm_id.empty() && realm_name.empty());
1adf2230 5427
1e59de90 5428 if (ret < 0 && !default_realm_not_exist) {
7c673cae
FG
5429 cerr << "failed to init realm: " << cpp_strerror(-ret) << std::endl;
5430 return -ret;
5431 }
5432
5433 RGWZoneGroup zonegroup;
7c673cae
FG
5434 ret = read_decode_json(infile, zonegroup);
5435 if (ret < 0) {
5436 return 1;
5437 }
1adf2230 5438 if (zonegroup.realm_id.empty() && !default_realm_not_exist) {
7c673cae
FG
5439 zonegroup.realm_id = realm.get_id();
5440 }
1e59de90
TL
5441 // validate zonegroup features
5442 for (const auto& feature : zonegroup.enabled_features) {
5443 if (!rgw::zone_features::supports(feature)) {
5444 std::cerr << "ERROR: Unrecognized zonegroup feature \""
5445 << feature << "\"" << std::endl;
5446 return EINVAL;
5447 }
5448 }
5449 for (const auto& [name, zone] : zonegroup.zones) {
5450 // validate zone features
5451 for (const auto& feature : zone.supported_features) {
5452 if (!rgw::zone_features::supports(feature)) {
5453 std::cerr << "ERROR: Unrecognized zone feature \""
5454 << feature << "\" in zone " << zone.name << std::endl;
5455 return EINVAL;
5456 }
5457 }
5458 // zone must support everything zonegroup does
5459 for (const auto& feature : zonegroup.enabled_features) {
5460 if (!zone.supports(feature)) {
5461 std::cerr << "ERROR: Zone " << name << " does not support feature \""
5462 << feature << "\" required by zonegroup" << std::endl;
5463 return EINVAL;
5464 }
5465 }
5466 }
5467
5468 // create/overwrite the zonegroup info
5469 constexpr bool exclusive = false;
5470 ret = rgw::create_zonegroup(dpp(), null_yield, cfgstore.get(),
5471 exclusive, zonegroup);
5472 if (ret < 0) {
7c673cae
FG
5473 cerr << "ERROR: couldn't create zonegroup info: " << cpp_strerror(-ret) << std::endl;
5474 return 1;
7c673cae
FG
5475 }
5476
5477 if (set_default) {
1e59de90
TL
5478 ret = rgw::set_default_zonegroup(dpp(), null_yield, cfgstore.get(),
5479 zonegroup);
7c673cae
FG
5480 if (ret < 0) {
5481 cerr << "failed to set zonegroup " << zonegroup_name << " as default: " << cpp_strerror(-ret) << std::endl;
5482 }
5483 }
5484
f67539c2 5485 encode_json("zonegroup", zonegroup, formatter.get());
7c673cae
FG
5486 formatter->flush(cout);
5487 }
5488 break;
9f95a23c 5489 case OPT::ZONEGROUP_REMOVE:
7c673cae 5490 {
1e59de90
TL
5491 RGWZoneGroup zonegroup;
5492 std::unique_ptr<rgw::sal::ZoneGroupWriter> writer;
5493 int ret = rgw::read_zonegroup(dpp(), null_yield, cfgstore.get(),
5494 zonegroup_id, zonegroup_name,
5495 zonegroup, &writer);
7c673cae
FG
5496 if (ret < 0) {
5497 cerr << "failed to init zonegroup: " << cpp_strerror(-ret) << std::endl;
5498 return -ret;
5499 }
5500
5501 if (zone_id.empty()) {
5502 if (zone_name.empty()) {
5503 cerr << "no --zone-id or --rgw-zone name provided" << std::endl;
5504 return EINVAL;
5505 }
5506 // look up zone id by name
5507 for (auto& z : zonegroup.zones) {
5508 if (zone_name == z.second.name) {
5509 zone_id = z.second.id;
5510 break;
5511 }
5512 }
5513 if (zone_id.empty()) {
5514 cerr << "zone name " << zone_name << " not found in zonegroup "
5515 << zonegroup.get_name() << std::endl;
5516 return ENOENT;
5517 }
5518 }
5519
1e59de90 5520 ret = rgw::remove_zone_from_group(dpp(), zonegroup, zone_id);
7c673cae
FG
5521 if (ret < 0) {
5522 cerr << "failed to remove zone: " << cpp_strerror(-ret) << std::endl;
5523 return -ret;
5524 }
5525
1e59de90
TL
5526 ret = writer->write(dpp(), null_yield, zonegroup);
5527 if (ret < 0) {
5528 cerr << "failed to write zonegroup: " << cpp_strerror(-ret) << std::endl;
5529 return -ret;
5530 }
5531
f67539c2 5532 encode_json("zonegroup", zonegroup, formatter.get());
7c673cae
FG
5533 formatter->flush(cout);
5534 }
5535 break;
9f95a23c 5536 case OPT::ZONEGROUP_RENAME:
7c673cae
FG
5537 {
5538 if (zonegroup_new_name.empty()) {
5539 cerr << " missing zonegroup new name" << std::endl;
5540 return EINVAL;
5541 }
5542 if (zonegroup_id.empty() && zonegroup_name.empty()) {
5543 cerr << "no zonegroup name or id provided" << std::endl;
5544 return EINVAL;
5545 }
1e59de90
TL
5546 RGWZoneGroup zonegroup;
5547 std::unique_ptr<rgw::sal::ZoneGroupWriter> writer;
5548 int ret = rgw::read_zonegroup(dpp(), null_yield, cfgstore.get(),
5549 zonegroup_id, zonegroup_name,
5550 zonegroup, &writer);
7c673cae 5551 if (ret < 0) {
1e59de90 5552 cerr << "failed to load zonegroup: " << cpp_strerror(-ret) << std::endl;
7c673cae
FG
5553 return -ret;
5554 }
1e59de90 5555 ret = writer->rename(dpp(), null_yield, zonegroup, zonegroup_new_name);
7c673cae
FG
5556 if (ret < 0) {
5557 cerr << "failed to rename zonegroup: " << cpp_strerror(-ret) << std::endl;
5558 return -ret;
5559 }
5560 }
5561 break;
9f95a23c 5562 case OPT::ZONEGROUP_PLACEMENT_LIST:
7c673cae 5563 {
1e59de90
TL
5564 RGWZoneGroup zonegroup;
5565 int ret = rgw::read_zonegroup(dpp(), null_yield, cfgstore.get(),
5566 zonegroup_id, zonegroup_name, zonegroup);
7c673cae 5567 if (ret < 0) {
1e59de90 5568 cerr << "failed to load zonegroup: " << cpp_strerror(-ret) << std::endl;
7c673cae
FG
5569 return -ret;
5570 }
5571
f67539c2 5572 encode_json("placement_targets", zonegroup.placement_targets, formatter.get());
7c673cae 5573 formatter->flush(cout);
7c673cae
FG
5574 }
5575 break;
9f95a23c 5576 case OPT::ZONEGROUP_PLACEMENT_GET:
92f5a8d4
TL
5577 {
5578 if (placement_id.empty()) {
5579 cerr << "ERROR: --placement-id not specified" << std::endl;
5580 return EINVAL;
5581 }
5582
1e59de90
TL
5583 RGWZoneGroup zonegroup;
5584 int ret = rgw::read_zonegroup(dpp(), null_yield, cfgstore.get(),
5585 zonegroup_id, zonegroup_name, zonegroup);
92f5a8d4 5586 if (ret < 0) {
1e59de90 5587 cerr << "failed to load zonegroup: " << cpp_strerror(-ret) << std::endl;
92f5a8d4
TL
5588 return -ret;
5589 }
5590
5591 auto p = zonegroup.placement_targets.find(placement_id);
5592 if (p == zonegroup.placement_targets.end()) {
5593 cerr << "failed to find a zonegroup placement target named '" << placement_id << "'" << std::endl;
5594 return -ENOENT;
5595 }
f67539c2 5596 encode_json("placement_targets", p->second, formatter.get());
92f5a8d4
TL
5597 formatter->flush(cout);
5598 }
5599 break;
9f95a23c
TL
5600 case OPT::ZONEGROUP_PLACEMENT_ADD:
5601 case OPT::ZONEGROUP_PLACEMENT_MODIFY:
5602 case OPT::ZONEGROUP_PLACEMENT_RM:
5603 case OPT::ZONEGROUP_PLACEMENT_DEFAULT:
7c673cae 5604 {
20effc67
TL
5605 if (placement_id.empty()) {
5606 cerr << "ERROR: --placement-id not specified" << std::endl;
5607 return EINVAL;
5608 }
7c673cae 5609
20effc67
TL
5610 rgw_placement_rule rule;
5611 rule.from_str(placement_id);
11fdf7f2 5612
20effc67
TL
5613 if (!rule.storage_class.empty() && opt_storage_class &&
5614 rule.storage_class != *opt_storage_class) {
5615 cerr << "ERROR: provided contradicting storage class configuration" << std::endl;
5616 return EINVAL;
5617 } else if (rule.storage_class.empty()) {
5618 rule.storage_class = opt_storage_class.value_or(string());
5619 }
11fdf7f2 5620
1e59de90
TL
5621 RGWZoneGroup zonegroup;
5622 std::unique_ptr<rgw::sal::ZoneGroupWriter> writer;
5623 int ret = rgw::read_zonegroup(dpp(), null_yield, cfgstore.get(),
5624 zonegroup_id, zonegroup_name,
5625 zonegroup, &writer);
7c673cae
FG
5626 if (ret < 0) {
5627 cerr << "failed to init zonegroup: " << cpp_strerror(-ret) << std::endl;
5628 return -ret;
5629 }
5630
20effc67
TL
5631 if (opt_cmd == OPT::ZONEGROUP_PLACEMENT_ADD ||
5632 opt_cmd == OPT::ZONEGROUP_PLACEMENT_MODIFY) {
5633 RGWZoneGroupPlacementTarget& target = zonegroup.placement_targets[placement_id];
5634 if (!tags.empty()) {
5635 target.tags.clear();
5636 for (auto& t : tags) {
5637 target.tags.insert(t);
5638 }
5639 }
5640
5641 target.name = placement_id;
5642 for (auto& t : tags_rm) {
5643 target.tags.erase(t);
5644 }
5645 for (auto& t : tags_add) {
5646 target.tags.insert(t);
5647 }
5648 target.storage_classes.insert(rule.get_storage_class());
5649
5650 /* Tier options */
5651 bool tier_class = false;
5652 std::string storage_class = rule.get_storage_class();
5653 RGWZoneGroupPlacementTier t{storage_class};
5654 RGWZoneGroupPlacementTier *pt = &t;
5655
5656 auto ptiter = target.tier_targets.find(storage_class);
5657 if (ptiter != target.tier_targets.end()) {
5658 pt = &ptiter->second;
5659 tier_class = true;
5660 } else if (tier_type_specified) {
5661 if (tier_type == "cloud-s3") {
5662 /* we support only cloud-s3 tier-type for now.
5663 * Once set cant be reset. */
5664 tier_class = true;
5665 pt->tier_type = tier_type;
5666 pt->storage_class = storage_class;
5667 } else {
5668 cerr << "ERROR: Invalid tier-type specified" << std::endl;
5669 return EINVAL;
5670 }
5671 }
5672
5673 if (tier_class) {
5674 if (tier_config_add.size() > 0) {
5675 JSONFormattable tconfig;
5676 for (auto add : tier_config_add) {
5677 int r = tconfig.set(add.first, add.second);
5678 if (r < 0) {
5679 cerr << "ERROR: failed to set configurable: " << add << std::endl;
5680 return EINVAL;
7c673cae
FG
5681 }
5682 }
20effc67
TL
5683 int r = pt->update_params(tconfig);
5684 if (r < 0) {
5685 cerr << "ERROR: failed to update tier_config options"<< std::endl;
7c673cae 5686 }
20effc67
TL
5687 }
5688 if (tier_config_rm.size() > 0) {
5689 JSONFormattable tconfig;
5690 for (auto add : tier_config_rm) {
5691 int r = tconfig.set(add.first, add.second);
5692 if (r < 0) {
5693 cerr << "ERROR: failed to set configurable: " << add << std::endl;
5694 return EINVAL;
9f95a23c
TL
5695 }
5696 }
20effc67
TL
5697 int r = pt->clear_params(tconfig);
5698 if (r < 0) {
5699 cerr << "ERROR: failed to update tier_config options"<< std::endl;
7c673cae 5700 }
7c673cae
FG
5701 }
5702
20effc67
TL
5703 target.tier_targets.emplace(std::make_pair(storage_class, *pt));
5704 }
5705
1e59de90
TL
5706 if (zonegroup.default_placement.empty()) {
5707 zonegroup.default_placement.init(rule.name, RGW_STORAGE_CLASS_STANDARD);
5708 }
20effc67
TL
5709 } else if (opt_cmd == OPT::ZONEGROUP_PLACEMENT_RM) {
5710 if (!opt_storage_class || opt_storage_class->empty()) {
5711 zonegroup.placement_targets.erase(placement_id);
1e59de90
TL
5712 if (zonegroup.default_placement.name == placement_id) {
5713 // clear default placement
5714 zonegroup.default_placement.clear();
5715 }
20effc67
TL
5716 } else {
5717 auto iter = zonegroup.placement_targets.find(placement_id);
5718 if (iter != zonegroup.placement_targets.end()) {
5719 RGWZoneGroupPlacementTarget& info = zonegroup.placement_targets[placement_id];
5720 info.storage_classes.erase(*opt_storage_class);
5721
1e59de90
TL
5722 if (zonegroup.default_placement == rule) {
5723 // clear default storage class
5724 zonegroup.default_placement.storage_class.clear();
5725 }
5726
20effc67
TL
5727 auto ptiter = info.tier_targets.find(*opt_storage_class);
5728 if (ptiter != info.tier_targets.end()) {
5729 info.tier_targets.erase(ptiter);
5730 }
7c673cae 5731 }
20effc67
TL
5732 }
5733 } else if (opt_cmd == OPT::ZONEGROUP_PLACEMENT_DEFAULT) {
5734 if (!zonegroup.placement_targets.count(placement_id)) {
5735 cerr << "failed to find a zonegroup placement target named '"
5736 << placement_id << "'" << std::endl;
5737 return -ENOENT;
5738 }
5739 zonegroup.default_placement = rule;
5740 }
7c673cae 5741
1e59de90 5742 ret = writer->write(dpp(), null_yield, zonegroup);
20effc67
TL
5743 if (ret < 0) {
5744 cerr << "failed to update zonegroup: " << cpp_strerror(-ret) << std::endl;
5745 return -ret;
5746 }
5747
5748 encode_json("placement_targets", zonegroup.placement_targets, formatter.get());
5749 formatter->flush(cout);
7c673cae
FG
5750 }
5751 break;
9f95a23c 5752 case OPT::ZONE_CREATE:
7c673cae
FG
5753 {
5754 if (zone_name.empty()) {
5755 cerr << "zone name not provided" << std::endl;
5756 return EINVAL;
5757 }
1e59de90
TL
5758
5759 RGWZoneGroup zonegroup;
5760 std::unique_ptr<rgw::sal::ZoneGroupWriter> zonegroup_writer;
7c673cae
FG
5761 /* if the user didn't provide zonegroup info , create stand alone zone */
5762 if (!zonegroup_id.empty() || !zonegroup_name.empty()) {
1e59de90
TL
5763 int ret = rgw::read_zonegroup(dpp(), null_yield, cfgstore.get(),
5764 zonegroup_id, zonegroup_name,
5765 zonegroup, &zonegroup_writer);
7c673cae 5766 if (ret < 0) {
1e59de90 5767 cerr << "failed to load zonegroup " << zonegroup_name << ": " << cpp_strerror(-ret) << std::endl;
7c673cae
FG
5768 return -ret;
5769 }
5770 if (realm_id.empty() && realm_name.empty()) {
5771 realm_id = zonegroup.realm_id;
5772 }
5773 }
5774
1e59de90
TL
5775 // create the local zone params
5776 RGWZoneParams zone_params;
5777 zone_params.id = zone_id;
5778 zone_params.name = zone_name;
7c673cae 5779
1e59de90
TL
5780 zone_params.system_key.id = access_key;
5781 zone_params.system_key.key = secret_key;
5782 zone_params.realm_id = realm_id;
5783 for (const auto& a : tier_config_add) {
5784 int r = zone_params.tier_config.set(a.first, a.second);
11fdf7f2
TL
5785 if (r < 0) {
5786 cerr << "ERROR: failed to set configurable: " << a << std::endl;
5787 return EINVAL;
5788 }
5789 }
7c673cae 5790
1e59de90
TL
5791 if (zone_params.realm_id.empty()) {
5792 RGWRealm realm;
5793 int ret = rgw::read_realm(dpp(), null_yield, cfgstore.get(),
5794 realm_id, realm_name, realm);
5795 if (ret < 0 && ret != -ENOENT) {
5796 cerr << "failed to load realm: " << cpp_strerror(-ret) << std::endl;
5797 return -ret;
5798 }
5799 zone_params.realm_id = realm.id;
5800 cerr << "NOTICE: set zone's realm_id=" << realm.id << std::endl;
5801 }
5802
5803 constexpr bool exclusive = true;
5804 int ret = rgw::create_zone(dpp(), null_yield, cfgstore.get(),
5805 exclusive, zone_params);
7c673cae
FG
5806 if (ret < 0) {
5807 cerr << "failed to create zone " << zone_name << ": " << cpp_strerror(-ret) << std::endl;
5808 return -ret;
5809 }
5810
1e59de90
TL
5811 if (zonegroup_writer) {
5812 const bool *pis_master = (is_master_set ? &is_master : nullptr);
5813 const bool *pread_only = (is_read_only_set ? &read_only : nullptr);
5814 const bool *psync_from_all = (sync_from_all_specified ? &sync_from_all : nullptr);
5815 const string *predirect_zone = (redirect_zone_set ? &redirect_zone : nullptr);
5816
5817 // validate --tier-type if specified
5818 const string *ptier_type = (tier_type_specified ? &tier_type : nullptr);
5819 if (ptier_type) {
5820 auto sync_mgr = static_cast<rgw::sal::RadosStore*>(driver)->svc()->sync_modules->get_manager();
5821 if (!sync_mgr->get_module(*ptier_type, nullptr)) {
5822 ldpp_dout(dpp(), -1) << "ERROR: could not find sync module: "
5823 << *ptier_type << ", valid sync modules: "
5824 << sync_mgr->get_registered_module_names() << dendl;
5825 return EINVAL;
5826 }
5827 }
5828
5829 if (enable_features.empty()) { // enable all features by default
5830 enable_features.insert(rgw::zone_features::supported.begin(),
5831 rgw::zone_features::supported.end());
5832 }
5833
5834 // add/update the public zone information stored in the zonegroup
5835 ret = rgw::add_zone_to_group(dpp(), zonegroup, zone_params,
5836 pis_master, pread_only, endpoints,
5837 ptier_type, psync_from_all,
5838 sync_from, sync_from_rm,
5839 predirect_zone, bucket_index_max_shards,
5840 enable_features, disable_features);
5841 if (ret < 0) {
5842 return -ret;
5843 }
5844
5845 // write the updated zonegroup
5846 ret = zonegroup_writer->write(dpp(), null_yield, zonegroup);
7c673cae
FG
5847 if (ret < 0) {
5848 cerr << "failed to add zone " << zone_name << " to zonegroup " << zonegroup.get_name()
5849 << ": " << cpp_strerror(-ret) << std::endl;
5850 return -ret;
5851 }
5852 }
5853
5854 if (set_default) {
1e59de90
TL
5855 ret = rgw::set_default_zone(dpp(), null_yield, cfgstore.get(),
5856 zone_params);
7c673cae
FG
5857 if (ret < 0) {
5858 cerr << "failed to set zone " << zone_name << " as default: " << cpp_strerror(-ret) << std::endl;
5859 }
5860 }
5861
1e59de90 5862 encode_json("zone", zone_params, formatter.get());
7c673cae 5863 formatter->flush(cout);
7c673cae
FG
5864 }
5865 break;
9f95a23c 5866 case OPT::ZONE_DEFAULT:
7c673cae 5867 {
7c673cae
FG
5868 if (zone_id.empty() && zone_name.empty()) {
5869 cerr << "no zone name or id provided" << std::endl;
5870 return EINVAL;
5871 }
1e59de90
TL
5872 RGWZoneParams zone_params;
5873 int ret = rgw::read_zone(dpp(), null_yield, cfgstore.get(),
5874 zone_id, zone_name, zone_params);
7c673cae 5875 if (ret < 0) {
1e59de90 5876 cerr << "unable to load zone: " << cpp_strerror(-ret) << std::endl;
7c673cae
FG
5877 return -ret;
5878 }
1e59de90
TL
5879
5880 ret = rgw::set_default_zone(dpp(), null_yield, cfgstore.get(),
5881 zone_params);
7c673cae
FG
5882 if (ret < 0) {
5883 cerr << "failed to set zone as default: " << cpp_strerror(-ret) << std::endl;
5884 return -ret;
5885 }
5886 }
5887 break;
9f95a23c 5888 case OPT::ZONE_DELETE:
7c673cae 5889 {
1e59de90 5890 if (zone_id.empty() && zone_name.empty()) {
7c673cae
FG
5891 cerr << "no zone name or id provided" << std::endl;
5892 return EINVAL;
5893 }
1e59de90
TL
5894 RGWZoneParams zone_params;
5895 std::unique_ptr<rgw::sal::ZoneWriter> writer;
5896 int ret = rgw::read_zone(dpp(), null_yield, cfgstore.get(),
5897 zone_id, zone_name, zone_params, &writer);
7c673cae 5898 if (ret < 0) {
1e59de90 5899 cerr << "failed to load zone: " << cpp_strerror(-ret) << std::endl;
7c673cae
FG
5900 return -ret;
5901 }
5902
1e59de90
TL
5903 ret = rgw::delete_zone(dpp(), null_yield, cfgstore.get(),
5904 zone_params, *writer);
7c673cae 5905 if (ret < 0) {
1e59de90
TL
5906 cerr << "failed to delete zone " << zone_params.get_name()
5907 << ": " << cpp_strerror(-ret) << std::endl;
7c673cae
FG
5908 return -ret;
5909 }
5910 }
5911 break;
9f95a23c 5912 case OPT::ZONE_GET:
7c673cae 5913 {
1e59de90
TL
5914 RGWZoneParams zone_params;
5915 int ret = rgw::read_zone(dpp(), null_yield, cfgstore.get(),
5916 zone_id, zone_name, zone_params);
7c673cae 5917 if (ret < 0) {
1e59de90 5918 cerr << "failed to load zone: " << cpp_strerror(-ret) << std::endl;
7c673cae
FG
5919 return -ret;
5920 }
1e59de90 5921 encode_json("zone", zone_params, formatter.get());
7c673cae
FG
5922 formatter->flush(cout);
5923 }
5924 break;
9f95a23c 5925 case OPT::ZONE_SET:
7c673cae 5926 {
1e59de90
TL
5927 RGWZoneParams zone;
5928 std::unique_ptr<rgw::sal::ZoneWriter> writer;
5929 int ret = rgw::read_zone(dpp(), null_yield, cfgstore.get(),
5930 zone_id, zone_name, zone, &writer);
7c673cae 5931 if (ret < 0 && ret != -ENOENT) {
1e59de90 5932 cerr << "failed to load zone: " << cpp_strerror(ret) << std::endl;
7c673cae
FG
5933 return -ret;
5934 }
5935
5936 string orig_id = zone.get_id();
5937
5938 ret = read_decode_json(infile, zone);
5939 if (ret < 0) {
5940 return 1;
5941 }
5942
1e59de90
TL
5943 if (zone.realm_id.empty()) {
5944 RGWRealm realm;
5945 ret = rgw::read_realm(dpp(), null_yield, cfgstore.get(),
5946 realm_id, realm_name, realm);
7c673cae 5947 if (ret < 0 && ret != -ENOENT) {
1e59de90 5948 cerr << "failed to load realm: " << cpp_strerror(-ret) << std::endl;
7c673cae
FG
5949 return -ret;
5950 }
5951 zone.realm_id = realm.get_id();
1e59de90 5952 cerr << "NOTICE: set zone's realm_id=" << zone.realm_id << std::endl;
7c673cae
FG
5953 }
5954
1e59de90 5955 if (!zone_name.empty() && !zone.get_name().empty() && zone.get_name() != zone_name) {
20effc67 5956 cerr << "Error: zone name " << zone_name << " is different than the zone name " << zone.get_name() << " in the provided json " << std::endl;
7c673cae
FG
5957 return EINVAL;
5958 }
5959
5960 if (zone.get_name().empty()) {
5961 zone.set_name(zone_name);
5962 if (zone.get_name().empty()) {
5963 cerr << "no zone name specified" << std::endl;
5964 return EINVAL;
5965 }
5966 }
5967
5968 zone_name = zone.get_name();
5969
5970 if (zone.get_id().empty()) {
5971 zone.set_id(orig_id);
5972 }
5973
1e59de90
TL
5974 constexpr bool exclusive = false;
5975 ret = rgw::create_zone(dpp(), null_yield, cfgstore.get(),
5976 exclusive, zone);
7c673cae
FG
5977 if (ret < 0) {
5978 cerr << "ERROR: couldn't create zone: " << cpp_strerror(-ret) << std::endl;
1e59de90 5979 return -ret;
7c673cae
FG
5980 }
5981
5982 if (set_default) {
1e59de90 5983 ret = rgw::set_default_zone(dpp(), null_yield, cfgstore.get(), zone);
7c673cae
FG
5984 if (ret < 0) {
5985 cerr << "failed to set zone " << zone_name << " as default: " << cpp_strerror(-ret) << std::endl;
5986 }
5987 }
5988
f67539c2 5989 encode_json("zone", zone, formatter.get());
7c673cae
FG
5990 formatter->flush(cout);
5991 }
5992 break;
9f95a23c 5993 case OPT::ZONE_LIST:
7c673cae 5994 {
1e59de90
TL
5995 RGWZoneParams default_zone_params;
5996 int ret = rgw::read_zone(dpp(), null_yield, cfgstore.get(),
5997 {}, {}, default_zone_params);
7c673cae
FG
5998 if (ret < 0 && ret != -ENOENT) {
5999 cerr << "could not determine default zone: " << cpp_strerror(-ret) << std::endl;
6000 }
1e59de90
TL
6001
6002 Formatter::ObjectSection zones_list{*formatter, "zones_list"};
6003 encode_json("default_info", default_zone_params.id, formatter.get());
6004
6005 Formatter::ArraySection zones{*formatter, "zones"};
6006 rgw::sal::ListResult<std::string> listing;
6007 std::array<std::string, 1000> names; // list in pages of 1000
6008 do {
6009 ret = cfgstore->list_zone_names(dpp(), null_yield, listing.next,
6010 names, listing);
6011 if (ret < 0) {
6012 std::cerr << "failed to list zones: " << cpp_strerror(-ret) << std::endl;
6013 return -ret;
6014 }
6015 for (const auto& name : listing.entries) {
6016 encode_json("name", name, formatter.get());
6017 }
6018 } while (!listing.next.empty());
6019 } // close sections zones and zones_list
6020 formatter->flush(cout);
7c673cae 6021 break;
9f95a23c 6022 case OPT::ZONE_MODIFY:
7c673cae 6023 {
1e59de90
TL
6024 RGWZoneParams zone_params;
6025 std::unique_ptr<rgw::sal::ZoneWriter> zone_writer;
6026 int ret = rgw::read_zone(dpp(), null_yield, cfgstore.get(),
6027 zone_id, zone_name, zone_params, &zone_writer);
7c673cae 6028 if (ret < 0) {
1e59de90 6029 cerr << "failed to load zone: " << cpp_strerror(-ret) << std::endl;
7c673cae
FG
6030 return -ret;
6031 }
6032
6033 bool need_zone_update = false;
6034 if (!access_key.empty()) {
1e59de90 6035 zone_params.system_key.id = access_key;
7c673cae
FG
6036 need_zone_update = true;
6037 }
6038
6039 if (!secret_key.empty()) {
1e59de90 6040 zone_params.system_key.key = secret_key;
7c673cae
FG
6041 need_zone_update = true;
6042 }
6043
6044 if (!realm_id.empty()) {
1e59de90 6045 zone_params.realm_id = realm_id;
7c673cae
FG
6046 need_zone_update = true;
6047 } else if (!realm_name.empty()) {
6048 // get realm id from name
1e59de90
TL
6049 ret = cfgstore->read_realm_id(dpp(), null_yield,
6050 realm_name, zone_params.realm_id);
7c673cae
FG
6051 if (ret < 0) {
6052 cerr << "failed to find realm by name " << realm_name << std::endl;
6053 return -ret;
6054 }
6055 need_zone_update = true;
6056 }
6057
1e59de90
TL
6058 for (const auto& add : tier_config_add) {
6059 ret = zone_params.tier_config.set(add.first, add.second);
6060 if (ret < 0) {
6061 cerr << "ERROR: failed to set configurable: " << add << std::endl;
6062 return EINVAL;
11fdf7f2 6063 }
7c673cae
FG
6064 need_zone_update = true;
6065 }
6066
1e59de90 6067 for (const auto& rm : tier_config_rm) {
11fdf7f2 6068 if (!rm.first.empty()) { /* otherwise will remove the entire config */
1e59de90 6069 zone_params.tier_config.erase(rm.first);
11fdf7f2
TL
6070 need_zone_update = true;
6071 }
7c673cae
FG
6072 }
6073
6074 if (need_zone_update) {
1e59de90 6075 ret = zone_writer->write(dpp(), null_yield, zone_params);
7c673cae
FG
6076 if (ret < 0) {
6077 cerr << "failed to save zone info: " << cpp_strerror(-ret) << std::endl;
6078 return -ret;
6079 }
6080 }
6081
1e59de90
TL
6082 RGWZoneGroup zonegroup;
6083 std::unique_ptr<rgw::sal::ZoneGroupWriter> zonegroup_writer;
6084 ret = rgw::read_zonegroup(dpp(), null_yield, cfgstore.get(),
6085 zonegroup_id, zonegroup_name,
6086 zonegroup, &zonegroup_writer);
7c673cae 6087 if (ret < 0) {
1e59de90 6088 cerr << "failed to load zonegroup: " << cpp_strerror(-ret) << std::endl;
7c673cae
FG
6089 return -ret;
6090 }
6091
1e59de90
TL
6092 const bool *pis_master = (is_master_set ? &is_master : nullptr);
6093 const bool *pread_only = (is_read_only_set ? &read_only : nullptr);
6094 const bool *psync_from_all = (sync_from_all_specified ? &sync_from_all : nullptr);
6095 const string *predirect_zone = (redirect_zone_set ? &redirect_zone : nullptr);
6096
6097 // validate --tier-type if specified
6098 const string *ptier_type = (tier_type_specified ? &tier_type : nullptr);
6099 if (ptier_type) {
6100 auto sync_mgr = static_cast<rgw::sal::RadosStore*>(driver)->svc()->sync_modules->get_manager();
6101 if (!sync_mgr->get_module(*ptier_type, nullptr)) {
6102 ldpp_dout(dpp(), -1) << "ERROR: could not find sync module: "
6103 << *ptier_type << ", valid sync modules: "
6104 << sync_mgr->get_registered_module_names() << dendl;
6105 return EINVAL;
6106 }
6107 }
6108
6109 if (enable_features.empty()) { // enable all features by default
6110 enable_features.insert(rgw::zone_features::supported.begin(),
6111 rgw::zone_features::supported.end());
6112 }
6113
6114 // add/update the public zone information stored in the zonegroup
6115 ret = rgw::add_zone_to_group(dpp(), zonegroup, zone_params,
6116 pis_master, pread_only, endpoints,
6117 ptier_type, psync_from_all,
6118 sync_from, sync_from_rm,
6119 predirect_zone, bucket_index_max_shards,
6120 enable_features, disable_features);
6121 if (ret < 0) {
6122 return -ret;
6123 }
6124
6125 // write the updated zonegroup
6126 ret = zonegroup_writer->write(dpp(), null_yield, zonegroup);
7c673cae
FG
6127 if (ret < 0) {
6128 cerr << "failed to update zonegroup: " << cpp_strerror(-ret) << std::endl;
6129 return -ret;
6130 }
6131
6132 if (set_default) {
1e59de90
TL
6133 ret = rgw::set_default_zone(dpp(), null_yield, cfgstore.get(),
6134 zone_params);
7c673cae
FG
6135 if (ret < 0) {
6136 cerr << "failed to set zone " << zone_name << " as default: " << cpp_strerror(-ret) << std::endl;
6137 }
6138 }
6139
1e59de90 6140 encode_json("zone", zone_params, formatter.get());
7c673cae
FG
6141 formatter->flush(cout);
6142 }
6143 break;
9f95a23c 6144 case OPT::ZONE_RENAME:
7c673cae
FG
6145 {
6146 if (zone_new_name.empty()) {
6147 cerr << " missing zone new name" << std::endl;
6148 return EINVAL;
6149 }
6150 if (zone_id.empty() && zone_name.empty()) {
9f95a23c 6151 cerr << "no zone name or id provided" << std::endl;
7c673cae
FG
6152 return EINVAL;
6153 }
1e59de90
TL
6154
6155 RGWZoneParams zone_params;
6156 std::unique_ptr<rgw::sal::ZoneWriter> zone_writer;
6157 int ret = rgw::read_zone(dpp(), null_yield, cfgstore.get(),
6158 zone_id, zone_name, zone_params, &zone_writer);
7c673cae 6159 if (ret < 0) {
1e59de90 6160 cerr << "failed to load zone: " << cpp_strerror(-ret) << std::endl;
7c673cae
FG
6161 return -ret;
6162 }
1e59de90
TL
6163
6164 ret = zone_writer->rename(dpp(), null_yield, zone_params, zone_new_name);
7c673cae
FG
6165 if (ret < 0) {
6166 cerr << "failed to rename zone " << zone_name << " to " << zone_new_name << ": " << cpp_strerror(-ret)
6167 << std::endl;
6168 return -ret;
6169 }
1e59de90
TL
6170
6171 RGWZoneGroup zonegroup;
6172 std::unique_ptr<rgw::sal::ZoneGroupWriter> zonegroup_writer;
6173 ret = rgw::read_zonegroup(dpp(), null_yield, cfgstore.get(),
6174 zonegroup_id, zonegroup_name,
6175 zonegroup, &zonegroup_writer);
7c673cae 6176 if (ret < 0) {
1e59de90
TL
6177 cerr << "WARNING: failed to load zonegroup " << zonegroup_name << std::endl;
6178 return EXIT_SUCCESS;
6179 }
6180
6181 auto z = zonegroup.zones.find(zone_params.id);
6182 if (z == zonegroup.zones.end()) {
6183 return EXIT_SUCCESS;
6184 }
6185 z->second.name = zone_params.name;
6186
6187 ret = zonegroup_writer->write(dpp(), null_yield, zonegroup);
6188 if (ret < 0) {
6189 cerr << "Error in zonegroup rename for " << zone_name << ": " << cpp_strerror(-ret) << std::endl;
6190 return -ret;
7c673cae
FG
6191 }
6192 }
6193 break;
9f95a23c
TL
6194 case OPT::ZONE_PLACEMENT_ADD:
6195 case OPT::ZONE_PLACEMENT_MODIFY:
6196 case OPT::ZONE_PLACEMENT_RM:
7c673cae
FG
6197 {
6198 if (placement_id.empty()) {
6199 cerr << "ERROR: --placement-id not specified" << std::endl;
6200 return EINVAL;
6201 }
3efd9988
FG
6202 // validate compression type
6203 if (compression_type && *compression_type != "random"
6204 && !Compressor::get_comp_alg_type(*compression_type)) {
6205 std::cerr << "Unrecognized compression type" << std::endl;
6206 return EINVAL;
6207 }
6208
1e59de90
TL
6209 RGWZoneParams zone;
6210 std::unique_ptr<rgw::sal::ZoneWriter> writer;
6211 int ret = rgw::read_zone(dpp(), null_yield, cfgstore.get(),
6212 zone_id, zone_name, zone, &writer);
7c673cae
FG
6213 if (ret < 0) {
6214 cerr << "failed to init zone: " << cpp_strerror(-ret) << std::endl;
6215 return -ret;
6216 }
6217
9f95a23c
TL
6218 if (opt_cmd == OPT::ZONE_PLACEMENT_ADD ||
6219 opt_cmd == OPT::ZONE_PLACEMENT_MODIFY) {
1e59de90
TL
6220 RGWZoneGroup zonegroup;
6221 ret = rgw::read_zonegroup(dpp(), null_yield, cfgstore.get(),
6222 zonegroup_id, zonegroup_name, zonegroup);
11fdf7f2
TL
6223 if (ret < 0) {
6224 cerr << "failed to init zonegroup: " << cpp_strerror(-ret) << std::endl;
6225 return -ret;
6226 }
6227
6228 auto ptiter = zonegroup.placement_targets.find(placement_id);
6229 if (ptiter == zonegroup.placement_targets.end()) {
6230 cerr << "ERROR: placement id '" << placement_id << "' is not configured in zonegroup placement targets" << std::endl;
6231 return EINVAL;
6232 }
6233
9f95a23c 6234 string storage_class = rgw_placement_rule::get_canonical_storage_class(opt_storage_class.value_or(string()));
11fdf7f2
TL
6235 if (ptiter->second.storage_classes.find(storage_class) == ptiter->second.storage_classes.end()) {
6236 cerr << "ERROR: storage class '" << storage_class << "' is not defined in zonegroup '" << placement_id << "' placement target" << std::endl;
6237 return EINVAL;
6238 }
20effc67
TL
6239 if (ptiter->second.tier_targets.find(storage_class) != ptiter->second.tier_targets.end()) {
6240 cerr << "ERROR: storage class '" << storage_class << "' is of tier type in zonegroup '" << placement_id << "' placement target" << std::endl;
6241 return EINVAL;
6242 }
7c673cae
FG
6243
6244 RGWZonePlacementInfo& info = zone.placement_pools[placement_id];
6245
11fdf7f2
TL
6246 string opt_index_pool = index_pool.value_or(string());
6247 string opt_data_pool = data_pool.value_or(string());
6248
6249 if (!opt_index_pool.empty()) {
6250 info.index_pool = opt_index_pool;
6251 }
6252
6253 if (info.index_pool.empty()) {
6254 cerr << "ERROR: index pool not configured, need to specify --index-pool" << std::endl;
6255 return EINVAL;
6256 }
6257
6258 if (opt_data_pool.empty()) {
6259 const RGWZoneStorageClass *porig_sc{nullptr};
6260 if (info.storage_classes.find(storage_class, &porig_sc)) {
6261 if (porig_sc->data_pool) {
6262 opt_data_pool = porig_sc->data_pool->to_str();
6263 }
6264 }
6265 if (opt_data_pool.empty()) {
6266 cerr << "ERROR: data pool not configured, need to specify --data-pool" << std::endl;
6267 return EINVAL;
6268 }
6269 }
6270
6271 rgw_pool dp = opt_data_pool;
6272 info.storage_classes.set_storage_class(storage_class, &dp, compression_type.get_ptr());
6273
7c673cae
FG
6274 if (data_extra_pool) {
6275 info.data_extra_pool = *data_extra_pool;
6276 }
6277 if (index_type_specified) {
11fdf7f2 6278 info.index_type = placement_index_type;
7c673cae 6279 }
39ae355f
TL
6280 if (placement_inline_data_specified) {
6281 info.inline_data = placement_inline_data;
6282 }
11fdf7f2
TL
6283
6284 ret = check_pool_support_omap(info.get_data_extra_pool());
6285 if (ret < 0) {
6286 cerr << "ERROR: the data extra (non-ec) pool '" << info.get_data_extra_pool()
6287 << "' does not support omap" << std::endl;
6288 return ret;
7c673cae 6289 }
9f95a23c
TL
6290 } else if (opt_cmd == OPT::ZONE_PLACEMENT_RM) {
6291 if (!opt_storage_class ||
6292 opt_storage_class->empty()) {
6293 zone.placement_pools.erase(placement_id);
6294 } else {
6295 auto iter = zone.placement_pools.find(placement_id);
6296 if (iter != zone.placement_pools.end()) {
6297 RGWZonePlacementInfo& info = zone.placement_pools[placement_id];
6298 info.storage_classes.remove_storage_class(*opt_storage_class);
6299 }
6300 }
7c673cae
FG
6301 }
6302
1e59de90 6303 ret = writer->write(dpp(), null_yield, zone);
7c673cae
FG
6304 if (ret < 0) {
6305 cerr << "failed to save zone info: " << cpp_strerror(-ret) << std::endl;
6306 return -ret;
6307 }
6308
f67539c2 6309 encode_json("zone", zone, formatter.get());
7c673cae
FG
6310 formatter->flush(cout);
6311 }
6312 break;
9f95a23c 6313 case OPT::ZONE_PLACEMENT_LIST:
7c673cae 6314 {
1e59de90
TL
6315 RGWZoneParams zone;
6316 int ret = rgw::read_zone(dpp(), null_yield, cfgstore.get(),
6317 zone_id, zone_name, zone);
7c673cae
FG
6318 if (ret < 0) {
6319 cerr << "unable to initialize zone: " << cpp_strerror(-ret) << std::endl;
6320 return -ret;
6321 }
f67539c2 6322 encode_json("placement_pools", zone.placement_pools, formatter.get());
7c673cae
FG
6323 formatter->flush(cout);
6324 }
6325 break;
9f95a23c 6326 case OPT::ZONE_PLACEMENT_GET:
92f5a8d4
TL
6327 {
6328 if (placement_id.empty()) {
6329 cerr << "ERROR: --placement-id not specified" << std::endl;
6330 return EINVAL;
6331 }
6332
1e59de90
TL
6333 RGWZoneParams zone;
6334 int ret = rgw::read_zone(dpp(), null_yield, cfgstore.get(),
6335 zone_id, zone_name, zone);
92f5a8d4
TL
6336 if (ret < 0) {
6337 cerr << "unable to initialize zone: " << cpp_strerror(-ret) << std::endl;
6338 return -ret;
6339 }
6340 auto p = zone.placement_pools.find(placement_id);
6341 if (p == zone.placement_pools.end()) {
6342 cerr << "ERROR: zone placement target '" << placement_id << "' not found" << std::endl;
1e59de90 6343 return ENOENT;
92f5a8d4 6344 }
f67539c2 6345 encode_json("placement_pools", p->second, formatter.get());
92f5a8d4
TL
6346 formatter->flush(cout);
6347 }
9f95a23c 6348 default:
92f5a8d4 6349 break;
7c673cae
FG
6350 }
6351 return 0;
6352 }
6353
9f95a23c
TL
6354 resolve_zone_id_opt(opt_effective_zone_name, opt_effective_zone_id);
6355 resolve_zone_id_opt(opt_source_zone_name, opt_source_zone_id);
6356 resolve_zone_id_opt(opt_dest_zone_name, opt_dest_zone_id);
6357 resolve_zone_ids_opt(opt_zone_names, opt_zone_ids);
6358 resolve_zone_ids_opt(opt_source_zone_names, opt_source_zone_ids);
6359 resolve_zone_ids_opt(opt_dest_zone_names, opt_dest_zone_ids);
6360
1e59de90 6361 bool non_master_cmd = (!driver->is_meta_master() && !yes_i_really_mean_it);
9f95a23c
TL
6362 std::set<OPT> non_master_ops_list = {OPT::USER_CREATE, OPT::USER_RM,
6363 OPT::USER_MODIFY, OPT::USER_ENABLE,
6364 OPT::USER_SUSPEND, OPT::SUBUSER_CREATE,
6365 OPT::SUBUSER_MODIFY, OPT::SUBUSER_RM,
6366 OPT::BUCKET_LINK, OPT::BUCKET_UNLINK,
1e59de90 6367 OPT::BUCKET_RM,
9f95a23c 6368 OPT::BUCKET_CHOWN, OPT::METADATA_PUT,
1e59de90 6369 OPT::METADATA_RM, OPT::MFA_CREATE,
9f95a23c 6370 OPT::MFA_REMOVE, OPT::MFA_RESYNC,
39ae355f
TL
6371 OPT::CAPS_ADD, OPT::CAPS_RM,
6372 OPT::ROLE_CREATE, OPT::ROLE_DELETE,
6373 OPT::ROLE_POLICY_PUT, OPT::ROLE_POLICY_DELETE};
494da23a
TL
6374
6375 bool print_warning_message = (non_master_ops_list.find(opt_cmd) != non_master_ops_list.end() &&
6376 non_master_cmd);
6377
6378 if (print_warning_message) {
6379 cerr << "Please run the command on master zone. Performing this operation on non-master zone leads to inconsistent metadata between zones" << std::endl;
6380 cerr << "Are you sure you want to go ahead? (requires --yes-i-really-mean-it)" << std::endl;
6381 return EINVAL;
6382 }
6383
20effc67
TL
6384 if (!rgw::sal::User::empty(user)) {
6385 user_op.set_user_id(user->get_id());
6386 bucket_op.set_user_id(user->get_id());
7c673cae
FG
6387 }
6388
6389 if (!display_name.empty())
6390 user_op.set_display_name(display_name);
6391
6392 if (!user_email.empty())
6393 user_op.set_user_email(user_email);
6394
20effc67 6395 if (!rgw::sal::User::empty(user)) {
9f95a23c
TL
6396 user_op.set_new_user_id(new_user_id);
6397 }
6398
7c673cae
FG
6399 if (!access_key.empty())
6400 user_op.set_access_key(access_key);
6401
6402 if (!secret_key.empty())
6403 user_op.set_secret_key(secret_key);
6404
6405 if (!subuser.empty())
6406 user_op.set_subuser(subuser);
6407
6408 if (!caps.empty())
6409 user_op.set_caps(caps);
6410
6411 user_op.set_purge_data(purge_data);
6412
6413 if (purge_keys)
6414 user_op.set_purge_keys();
6415
6416 if (gen_access_key)
6417 user_op.set_generate_key();
6418
6419 if (gen_secret_key)
6420 user_op.set_gen_secret(); // assume that a key pair should be created
6421
6422 if (max_buckets_specified)
6423 user_op.set_max_buckets(max_buckets);
6424
6425 if (admin_specified)
6426 user_op.set_admin(admin);
6427
6428 if (system_specified)
6429 user_op.set_system(system);
6430
6431 if (set_perm)
6432 user_op.set_perm(perm_mask);
6433
6434 if (set_temp_url_key) {
6435 map<int, string>::iterator iter = temp_url_keys.begin();
6436 for (; iter != temp_url_keys.end(); ++iter) {
6437 user_op.set_temp_url_key(iter->second, iter->first);
6438 }
6439 }
6440
6441 if (!op_mask_str.empty()) {
6442 uint32_t op_mask;
6443 int ret = rgw_parse_op_type_list(op_mask_str, &op_mask);
6444 if (ret < 0) {
6445 cerr << "failed to parse op_mask: " << cpp_strerror(-ret) << std::endl;
6446 return -ret;
6447 }
6448
6449 user_op.set_op_mask(op_mask);
6450 }
6451
6452 if (key_type != KEY_TYPE_UNDEFINED)
6453 user_op.set_key_type(key_type);
6454
6455 // set suspension operation parameters
9f95a23c 6456 if (opt_cmd == OPT::USER_ENABLE)
7c673cae 6457 user_op.set_suspension(false);
9f95a23c 6458 else if (opt_cmd == OPT::USER_SUSPEND)
7c673cae
FG
6459 user_op.set_suspension(true);
6460
1e59de90 6461 if (!placement_id.empty()) {
9f95a23c
TL
6462 rgw_placement_rule target_rule;
6463 target_rule.name = placement_id;
1e59de90
TL
6464 target_rule.storage_class = opt_storage_class.value_or("");
6465 if (!driver->valid_placement(target_rule)) {
9f95a23c
TL
6466 cerr << "NOTICE: invalid dest placement: " << target_rule.to_str() << std::endl;
6467 return EINVAL;
6468 }
6469 user_op.set_default_placement(target_rule);
6470 }
6471
6472 if (!tags.empty()) {
6473 user_op.set_placement_tags(tags);
6474 }
6475
7c673cae 6476 // RGWUser to use for user operations
20effc67 6477 RGWUser ruser;
7c673cae 6478 int ret = 0;
20effc67 6479 if (!(rgw::sal::User::empty(user) && access_key.empty()) || !subuser.empty()) {
1e59de90 6480 ret = ruser.init(dpp(), driver, user_op, null_yield);
7c673cae
FG
6481 if (ret < 0) {
6482 cerr << "user.init failed: " << cpp_strerror(-ret) << std::endl;
6483 return -ret;
6484 }
6485 }
6486
6487 /* populate bucket operation */
6488 bucket_op.set_bucket_name(bucket_name);
6489 bucket_op.set_object(object);
6490 bucket_op.set_check_objects(check_objects);
6491 bucket_op.set_delete_children(delete_child_objects);
6492 bucket_op.set_fix_index(fix);
6493 bucket_op.set_max_aio(max_concurrent_ios);
aee94f69
TL
6494 bucket_op.set_min_age(min_age);
6495 bucket_op.set_dump_keys(dump_keys);
6496 bucket_op.set_hide_progress(hide_progress);
7c673cae
FG
6497
6498 // required to gather errors from operations
6499 std::string err_msg;
6500
6501 bool output_user_info = true;
6502
6503 switch (opt_cmd) {
9f95a23c 6504 case OPT::USER_INFO:
20effc67 6505 if (rgw::sal::User::empty(user) && access_key.empty()) {
eafe8130 6506 cerr << "ERROR: --uid or --access-key required" << std::endl;
11fdf7f2
TL
6507 return EINVAL;
6508 }
7c673cae 6509 break;
9f95a23c 6510 case OPT::USER_CREATE:
7c673cae
FG
6511 if (!user_op.has_existing_user()) {
6512 user_op.set_generate_key(); // generate a new key by default
6513 }
20effc67 6514 ret = ruser.add(dpp(), user_op, null_yield, &err_msg);
7c673cae
FG
6515 if (ret < 0) {
6516 cerr << "could not create user: " << err_msg << std::endl;
d2e6a577
FG
6517 if (ret == -ERR_INVALID_TENANT_NAME)
6518 ret = -EINVAL;
6519
7c673cae
FG
6520 return -ret;
6521 }
6522 if (!subuser.empty()) {
20effc67 6523 ret = ruser.subusers.add(dpp(),user_op, null_yield, &err_msg);
7c673cae
FG
6524 if (ret < 0) {
6525 cerr << "could not create subuser: " << err_msg << std::endl;
6526 return -ret;
6527 }
6528 }
6529 break;
9f95a23c 6530 case OPT::USER_RM:
20effc67 6531 ret = ruser.remove(dpp(), user_op, null_yield, &err_msg);
31f18b77 6532 if (ret < 0) {
7c673cae
FG
6533 cerr << "could not remove user: " << err_msg << std::endl;
6534 return -ret;
6535 }
6536
6537 output_user_info = false;
6538 break;
9f95a23c
TL
6539 case OPT::USER_RENAME:
6540 if (yes_i_really_mean_it) {
6541 user_op.set_overwrite_new_user(true);
6542 }
20effc67 6543 ret = ruser.rename(user_op, null_yield, dpp(), &err_msg);
9f95a23c
TL
6544 if (ret < 0) {
6545 if (ret == -EEXIST) {
6546 err_msg += ". to overwrite this user, add --yes-i-really-mean-it";
6547 }
6548 cerr << "could not rename user: " << err_msg << std::endl;
6549 return -ret;
6550 }
6551
6552 break;
6553 case OPT::USER_ENABLE:
6554 case OPT::USER_SUSPEND:
6555 case OPT::USER_MODIFY:
20effc67 6556 ret = ruser.modify(dpp(), user_op, null_yield, &err_msg);
7c673cae
FG
6557 if (ret < 0) {
6558 cerr << "could not modify user: " << err_msg << std::endl;
6559 return -ret;
6560 }
6561
6562 break;
9f95a23c 6563 case OPT::SUBUSER_CREATE:
20effc67 6564 ret = ruser.subusers.add(dpp(), user_op, null_yield, &err_msg);
7c673cae
FG
6565 if (ret < 0) {
6566 cerr << "could not create subuser: " << err_msg << std::endl;
6567 return -ret;
6568 }
6569
6570 break;
9f95a23c 6571 case OPT::SUBUSER_MODIFY:
20effc67 6572 ret = ruser.subusers.modify(dpp(), user_op, null_yield, &err_msg);
7c673cae
FG
6573 if (ret < 0) {
6574 cerr << "could not modify subuser: " << err_msg << std::endl;
6575 return -ret;
6576 }
6577
6578 break;
9f95a23c 6579 case OPT::SUBUSER_RM:
20effc67 6580 ret = ruser.subusers.remove(dpp(), user_op, null_yield, &err_msg);
7c673cae
FG
6581 if (ret < 0) {
6582 cerr << "could not remove subuser: " << err_msg << std::endl;
6583 return -ret;
6584 }
6585
6586 break;
9f95a23c 6587 case OPT::CAPS_ADD:
20effc67 6588 ret = ruser.caps.add(dpp(), user_op, null_yield, &err_msg);
7c673cae
FG
6589 if (ret < 0) {
6590 cerr << "could not add caps: " << err_msg << std::endl;
6591 return -ret;
6592 }
6593
6594 break;
9f95a23c 6595 case OPT::CAPS_RM:
20effc67 6596 ret = ruser.caps.remove(dpp(), user_op, null_yield, &err_msg);
7c673cae
FG
6597 if (ret < 0) {
6598 cerr << "could not remove caps: " << err_msg << std::endl;
6599 return -ret;
6600 }
6601
6602 break;
9f95a23c 6603 case OPT::KEY_CREATE:
20effc67 6604 ret = ruser.keys.add(dpp(), user_op, null_yield, &err_msg);
7c673cae
FG
6605 if (ret < 0) {
6606 cerr << "could not create key: " << err_msg << std::endl;
6607 return -ret;
6608 }
6609
6610 break;
9f95a23c 6611 case OPT::KEY_RM:
20effc67 6612 ret = ruser.keys.remove(dpp(), user_op, null_yield, &err_msg);
7c673cae
FG
6613 if (ret < 0) {
6614 cerr << "could not remove key: " << err_msg << std::endl;
6615 return -ret;
6616 }
6617 break;
9f95a23c 6618 case OPT::PERIOD_PUSH:
7c673cae
FG
6619 {
6620 RGWEnv env;
6621 req_info info(g_ceph_context, &env);
6622 info.method = "POST";
6623 info.request_uri = "/admin/realm/period";
6624
6625 map<string, string> &params = info.args.get_params();
6626 if (!realm_id.empty())
6627 params["realm_id"] = realm_id;
6628 if (!realm_name.empty())
6629 params["realm_name"] = realm_name;
6630 if (!period_id.empty())
6631 params["period_id"] = period_id;
6632 if (!period_epoch.empty())
6633 params["epoch"] = period_epoch;
6634
6635 // load the period
1e59de90
TL
6636 RGWPeriod period;
6637 int ret = cfgstore->read_period(dpp(), null_yield, period_id,
6638 std::nullopt, period);
7c673cae 6639 if (ret < 0) {
1e59de90 6640 cerr << "failed to load period: " << cpp_strerror(-ret) << std::endl;
7c673cae
FG
6641 return -ret;
6642 }
6643 // json format into a bufferlist
6644 JSONFormatter jf(false);
6645 encode_json("period", period, &jf);
6646 bufferlist bl;
6647 jf.flush(bl);
6648
6649 JSONParser p;
20effc67
TL
6650 ret = send_to_remote_or_url(nullptr, url, opt_region,
6651 access_key, secret_key,
7c673cae
FG
6652 info, bl, p);
6653 if (ret < 0) {
6654 cerr << "request failed: " << cpp_strerror(-ret) << std::endl;
6655 return -ret;
6656 }
6657 }
6658 return 0;
9f95a23c 6659 case OPT::PERIOD_UPDATE:
7c673cae 6660 {
1e59de90
TL
6661 int ret = update_period(cfgstore.get(), realm_id, realm_name,
6662 period_epoch, commit, remote, url,
6663 opt_region, access_key, secret_key,
f67539c2 6664 formatter.get(), yes_i_really_mean_it);
7c673cae
FG
6665 if (ret < 0) {
6666 return -ret;
6667 }
6668 }
6669 return 0;
9f95a23c 6670 case OPT::PERIOD_COMMIT:
7c673cae
FG
6671 {
6672 // read realm and staging period
1e59de90
TL
6673 RGWRealm realm;
6674 std::unique_ptr<rgw::sal::RealmWriter> realm_writer;
6675 int ret = rgw::read_realm(dpp(), null_yield, cfgstore.get(),
6676 realm_id, realm_name,
6677 realm, &realm_writer);
7c673cae
FG
6678 if (ret < 0) {
6679 cerr << "Error initializing realm: " << cpp_strerror(-ret) << std::endl;
6680 return -ret;
6681 }
1e59de90
TL
6682 period_id = rgw::get_staging_period_id(realm.id);
6683 epoch_t epoch = 1;
6684
6685 RGWPeriod period;
6686 ret = cfgstore->read_period(dpp(), null_yield, period_id, epoch, period);
7c673cae 6687 if (ret < 0) {
1e59de90 6688 cerr << "failed to load period: " << cpp_strerror(-ret) << std::endl;
7c673cae
FG
6689 return -ret;
6690 }
1e59de90
TL
6691 ret = commit_period(cfgstore.get(), realm, *realm_writer, period,
6692 remote, url, opt_region, access_key, secret_key,
7c673cae
FG
6693 yes_i_really_mean_it);
6694 if (ret < 0) {
6695 cerr << "failed to commit period: " << cpp_strerror(-ret) << std::endl;
6696 return -ret;
6697 }
6698
f67539c2 6699 encode_json("period", period, formatter.get());
7c673cae 6700 formatter->flush(cout);
7c673cae
FG
6701 }
6702 return 0;
9f95a23c 6703 case OPT::ROLE_CREATE:
7c673cae 6704 {
31f18b77
FG
6705 if (role_name.empty()) {
6706 cerr << "ERROR: role name is empty" << std::endl;
6707 return -EINVAL;
6708 }
6709
6710 if (assume_role_doc.empty()) {
6711 cerr << "ERROR: assume role policy document is empty" << std::endl;
7c673cae
FG
6712 return -EINVAL;
6713 }
11fdf7f2
TL
6714 bufferlist bl = bufferlist::static_from_string(assume_role_doc);
6715 try {
1e59de90
TL
6716 const rgw::IAM::Policy p(
6717 g_ceph_context, tenant, bl,
6718 g_ceph_context->_conf.get_val<bool>(
6719 "rgw_policy_reject_invalid_principals"));
11fdf7f2
TL
6720 } catch (rgw::IAM::PolicyParseException& e) {
6721 cerr << "failed to parse policy: " << e.what() << std::endl;
7c673cae
FG
6722 return -EINVAL;
6723 }
1e59de90 6724 std::unique_ptr<rgw::sal::RGWRole> role = driver->get_role(role_name, tenant, path, assume_role_doc);
39ae355f 6725 ret = role->create(dpp(), true, "", null_yield);
7c673cae
FG
6726 if (ret < 0) {
6727 return -ret;
6728 }
20effc67 6729 show_role_info(role.get(), formatter.get());
7c673cae
FG
6730 return 0;
6731 }
9f95a23c 6732 case OPT::ROLE_DELETE:
7c673cae
FG
6733 {
6734 if (role_name.empty()) {
6735 cerr << "ERROR: empty role name" << std::endl;
6736 return -EINVAL;
6737 }
1e59de90 6738 std::unique_ptr<rgw::sal::RGWRole> role = driver->get_role(role_name, tenant);
20effc67 6739 ret = role->delete_obj(dpp(), null_yield);
7c673cae
FG
6740 if (ret < 0) {
6741 return -ret;
6742 }
6743 cout << "role: " << role_name << " successfully deleted" << std::endl;
6744 return 0;
6745 }
9f95a23c 6746 case OPT::ROLE_GET:
7c673cae
FG
6747 {
6748 if (role_name.empty()) {
6749 cerr << "ERROR: empty role name" << std::endl;
6750 return -EINVAL;
6751 }
1e59de90 6752 std::unique_ptr<rgw::sal::RGWRole> role = driver->get_role(role_name, tenant);
20effc67 6753 ret = role->get(dpp(), null_yield);
7c673cae
FG
6754 if (ret < 0) {
6755 return -ret;
6756 }
20effc67 6757 show_role_info(role.get(), formatter.get());
7c673cae
FG
6758 return 0;
6759 }
1e59de90 6760 case OPT::ROLE_TRUST_POLICY_MODIFY:
7c673cae 6761 {
31f18b77
FG
6762 if (role_name.empty()) {
6763 cerr << "ERROR: role name is empty" << std::endl;
7c673cae
FG
6764 return -EINVAL;
6765 }
31f18b77
FG
6766
6767 if (assume_role_doc.empty()) {
6768 cerr << "ERROR: assume role policy document is empty" << std::endl;
6769 return -EINVAL;
6770 }
6771
11fdf7f2
TL
6772 bufferlist bl = bufferlist::static_from_string(assume_role_doc);
6773 try {
1e59de90
TL
6774 const rgw::IAM::Policy p(g_ceph_context, tenant, bl,
6775 g_ceph_context->_conf.get_val<bool>(
6776 "rgw_policy_reject_invalid_principals"));
11fdf7f2
TL
6777 } catch (rgw::IAM::PolicyParseException& e) {
6778 cerr << "failed to parse policy: " << e.what() << std::endl;
7c673cae
FG
6779 return -EINVAL;
6780 }
11fdf7f2 6781
1e59de90 6782 std::unique_ptr<rgw::sal::RGWRole> role = driver->get_role(role_name, tenant);
20effc67 6783 ret = role->get(dpp(), null_yield);
7c673cae
FG
6784 if (ret < 0) {
6785 return -ret;
6786 }
20effc67
TL
6787 role->update_trust_policy(assume_role_doc);
6788 ret = role->update(dpp(), null_yield);
7c673cae
FG
6789 if (ret < 0) {
6790 return -ret;
6791 }
6792 cout << "Assume role policy document updated successfully for role: " << role_name << std::endl;
6793 return 0;
6794 }
9f95a23c 6795 case OPT::ROLE_LIST:
7c673cae 6796 {
20effc67 6797 vector<std::unique_ptr<rgw::sal::RGWRole>> result;
1e59de90 6798 ret = driver->get_roles(dpp(), null_yield, path_prefix, tenant, result);
7c673cae
FG
6799 if (ret < 0) {
6800 return -ret;
6801 }
f67539c2 6802 show_roles_info(result, formatter.get());
7c673cae
FG
6803 return 0;
6804 }
9f95a23c 6805 case OPT::ROLE_POLICY_PUT:
7c673cae 6806 {
31f18b77
FG
6807 if (role_name.empty()) {
6808 cerr << "role name is empty" << std::endl;
7c673cae
FG
6809 return -EINVAL;
6810 }
31f18b77
FG
6811
6812 if (policy_name.empty()) {
6813 cerr << "policy name is empty" << std::endl;
6814 return -EINVAL;
6815 }
6816
1e59de90 6817 if (perm_policy_doc.empty() && infile.empty()) {
31f18b77
FG
6818 cerr << "permission policy document is empty" << std::endl;
6819 return -EINVAL;
6820 }
6821
1e59de90
TL
6822 bufferlist bl;
6823 if (!infile.empty()) {
6824 int ret = read_input(infile, bl);
6825 if (ret < 0) {
6826 cerr << "ERROR: failed to read input policy document: " << cpp_strerror(-ret) << std::endl;
6827 return -ret;
6828 }
6829 perm_policy_doc = bl.to_str();
6830 } else {
6831 bl = bufferlist::static_from_string(perm_policy_doc);
6832 }
11fdf7f2 6833 try {
1e59de90
TL
6834 const rgw::IAM::Policy p(g_ceph_context, tenant, bl,
6835 g_ceph_context->_conf.get_val<bool>(
6836 "rgw_policy_reject_invalid_principals"));
11fdf7f2
TL
6837 } catch (rgw::IAM::PolicyParseException& e) {
6838 cerr << "failed to parse perm policy: " << e.what() << std::endl;
7c673cae
FG
6839 return -EINVAL;
6840 }
7c673cae 6841
1e59de90 6842 std::unique_ptr<rgw::sal::RGWRole> role = driver->get_role(role_name, tenant);
20effc67 6843 ret = role->get(dpp(), null_yield);
7c673cae
FG
6844 if (ret < 0) {
6845 return -ret;
6846 }
20effc67
TL
6847 role->set_perm_policy(policy_name, perm_policy_doc);
6848 ret = role->update(dpp(), null_yield);
7c673cae
FG
6849 if (ret < 0) {
6850 return -ret;
6851 }
6852 cout << "Permission policy attached successfully" << std::endl;
6853 return 0;
6854 }
9f95a23c 6855 case OPT::ROLE_POLICY_LIST:
7c673cae
FG
6856 {
6857 if (role_name.empty()) {
6858 cerr << "ERROR: Role name is empty" << std::endl;
6859 return -EINVAL;
6860 }
1e59de90 6861 std::unique_ptr<rgw::sal::RGWRole> role = driver->get_role(role_name, tenant);
20effc67 6862 ret = role->get(dpp(), null_yield);
7c673cae
FG
6863 if (ret < 0) {
6864 return -ret;
6865 }
20effc67 6866 std::vector<string> policy_names = role->get_role_policy_names();
f67539c2 6867 show_policy_names(policy_names, formatter.get());
7c673cae
FG
6868 return 0;
6869 }
9f95a23c 6870 case OPT::ROLE_POLICY_GET:
7c673cae 6871 {
31f18b77
FG
6872 if (role_name.empty()) {
6873 cerr << "ERROR: role name is empty" << std::endl;
7c673cae
FG
6874 return -EINVAL;
6875 }
31f18b77
FG
6876
6877 if (policy_name.empty()) {
6878 cerr << "ERROR: policy name is empty" << std::endl;
6879 return -EINVAL;
6880 }
1e59de90 6881 std::unique_ptr<rgw::sal::RGWRole> role = driver->get_role(role_name, tenant);
20effc67 6882 int ret = role->get(dpp(), null_yield);
7c673cae
FG
6883 if (ret < 0) {
6884 return -ret;
6885 }
6886 string perm_policy;
20effc67 6887 ret = role->get_role_policy(dpp(), policy_name, perm_policy);
7c673cae
FG
6888 if (ret < 0) {
6889 return -ret;
6890 }
f67539c2 6891 show_perm_policy(perm_policy, formatter.get());
7c673cae
FG
6892 return 0;
6893 }
9f95a23c 6894 case OPT::ROLE_POLICY_DELETE:
7c673cae 6895 {
31f18b77
FG
6896 if (role_name.empty()) {
6897 cerr << "ERROR: role name is empty" << std::endl;
6898 return -EINVAL;
6899 }
6900
6901 if (policy_name.empty()) {
6902 cerr << "ERROR: policy name is empty" << std::endl;
7c673cae
FG
6903 return -EINVAL;
6904 }
1e59de90 6905 std::unique_ptr<rgw::sal::RGWRole> role = driver->get_role(role_name, tenant);
20effc67 6906 ret = role->get(dpp(), null_yield);
7c673cae
FG
6907 if (ret < 0) {
6908 return -ret;
6909 }
20effc67 6910 ret = role->delete_policy(dpp(), policy_name);
7c673cae
FG
6911 if (ret < 0) {
6912 return -ret;
6913 }
20effc67 6914 ret = role->update(dpp(), null_yield);
7c673cae
FG
6915 if (ret < 0) {
6916 return -ret;
6917 }
6918 cout << "Policy: " << policy_name << " successfully deleted for role: "
6919 << role_name << std::endl;
6920 return 0;
6921 }
1e59de90
TL
6922 case OPT::ROLE_UPDATE:
6923 {
6924 if (role_name.empty()) {
6925 cerr << "ERROR: role name is empty" << std::endl;
6926 return -EINVAL;
6927 }
6928
6929 std::unique_ptr<rgw::sal::RGWRole> role = driver->get_role(role_name, tenant);
6930 ret = role->get(dpp(), null_yield);
6931 if (ret < 0) {
6932 return -ret;
6933 }
6934 if (!role->validate_max_session_duration(dpp())) {
6935 ret = -EINVAL;
6936 return ret;
6937 }
6938 role->update_max_session_duration(max_session_duration);
6939 ret = role->update(dpp(), null_yield);
6940 if (ret < 0) {
6941 return -ret;
6942 }
6943 cout << "Max session duration updated successfully for role: " << role_name << std::endl;
6944 return 0;
6945 }
7c673cae
FG
6946 default:
6947 output_user_info = false;
6948 }
6949
6950 // output the result of a user operation
6951 if (output_user_info) {
20effc67 6952 ret = ruser.info(info, &err_msg);
7c673cae
FG
6953 if (ret < 0) {
6954 cerr << "could not fetch user info: " << err_msg << std::endl;
6955 return -ret;
6956 }
f67539c2 6957 show_user_info(info, formatter.get());
7c673cae
FG
6958 }
6959
9f95a23c 6960 if (opt_cmd == OPT::POLICY) {
7c673cae 6961 if (format == "xml") {
1e59de90 6962 int ret = RGWBucketAdminOp::dump_s3_policy(driver, bucket_op, cout, dpp());
7c673cae
FG
6963 if (ret < 0) {
6964 cerr << "ERROR: failed to get policy: " << cpp_strerror(-ret) << std::endl;
6965 return -ret;
6966 }
6967 } else {
1e59de90 6968 int ret = RGWBucketAdminOp::get_policy(driver, bucket_op, stream_flusher, dpp());
7c673cae
FG
6969 if (ret < 0) {
6970 cerr << "ERROR: failed to get policy: " << cpp_strerror(-ret) << std::endl;
6971 return -ret;
6972 }
6973 }
6974 }
6975
9f95a23c 6976 if (opt_cmd == OPT::BUCKET_LIMIT_CHECK) {
7c673cae
FG
6977 void *handle;
6978 std::list<std::string> user_ids;
6979 metadata_key = "user";
6980 int max = 1000;
6981
6982 bool truncated;
6983
20effc67
TL
6984 if (!rgw::sal::User::empty(user)) {
6985 user_ids.push_back(user->get_id().id);
7c673cae 6986 ret =
1e59de90 6987 RGWBucketAdminOp::limit_check(driver, bucket_op, user_ids, stream_flusher,
b3b6e05e 6988 null_yield, dpp(), warnings_only);
7c673cae
FG
6989 } else {
6990 /* list users in groups of max-keys, then perform user-bucket
6991 * limit-check on each group */
1e59de90 6992 ret = driver->meta_list_keys_init(dpp(), metadata_key, string(), &handle);
7c673cae
FG
6993 if (ret < 0) {
6994 cerr << "ERROR: buckets limit check can't get user metadata_key: "
6995 << cpp_strerror(-ret) << std::endl;
6996 return -ret;
6997 }
6998
6999 do {
1e59de90 7000 ret = driver->meta_list_keys_next(dpp(), handle, max, user_ids,
7c673cae
FG
7001 &truncated);
7002 if (ret < 0 && ret != -ENOENT) {
7003 cerr << "ERROR: buckets limit check lists_keys_next(): "
7004 << cpp_strerror(-ret) << std::endl;
7005 break;
7006 } else {
7007 /* ok, do the limit checks for this group */
7008 ret =
1e59de90 7009 RGWBucketAdminOp::limit_check(driver, bucket_op, user_ids, stream_flusher,
b3b6e05e 7010 null_yield, dpp(), warnings_only);
7c673cae
FG
7011 if (ret < 0)
7012 break;
7013 }
7014 user_ids.clear();
7015 } while (truncated);
1e59de90 7016 driver->meta_list_keys_complete(handle);
7c673cae
FG
7017 }
7018 return -ret;
9f95a23c 7019 } /* OPT::BUCKET_LIMIT_CHECK */
7c673cae 7020
9f95a23c 7021 if (opt_cmd == OPT::BUCKETS_LIST) {
7c673cae 7022 if (bucket_name.empty()) {
20effc67 7023 if (!rgw::sal::User::empty(user)) {
eafe8130 7024 if (!user_op.has_existing_user()) {
20effc67 7025 cerr << "ERROR: could not find user: " << user << std::endl;
eafe8130
TL
7026 return -ENOENT;
7027 }
7028 }
1e59de90 7029 RGWBucketAdminOp::info(driver, bucket_op, stream_flusher, null_yield, dpp());
7c673cae 7030 } else {
20effc67 7031 int ret = init_bucket(user.get(), tenant, bucket_name, bucket_id, &bucket);
7c673cae
FG
7032 if (ret < 0) {
7033 cerr << "ERROR: could not init bucket: " << cpp_strerror(-ret) << std::endl;
7034 return -ret;
7035 }
7036 formatter->open_array_section("entries");
f91f0fd5 7037
7c673cae 7038 int count = 0;
f91f0fd5
TL
7039
7040 static constexpr int MAX_PAGINATE_SIZE = 10000;
7041 static constexpr int DEFAULT_MAX_ENTRIES = 1000;
7042
7043 if (max_entries < 0) {
7044 max_entries = DEFAULT_MAX_ENTRIES;
7045 }
7046 const int paginate_size = std::min(max_entries, MAX_PAGINATE_SIZE);
7c673cae
FG
7047
7048 string prefix;
7049 string delim;
7c673cae
FG
7050 string ns;
7051
20effc67
TL
7052 rgw::sal::Bucket::ListParams params;
7053 rgw::sal::Bucket::ListResults results;
7c673cae 7054
20effc67
TL
7055 params.prefix = prefix;
7056 params.delim = delim;
7057 params.marker = rgw_obj_key(marker);
7058 params.ns = ns;
7059 params.enforce_ns = false;
7060 params.list_versions = true;
7061 params.allow_unordered = bool(allow_unordered);
7c673cae
FG
7062
7063 do {
f91f0fd5 7064 const int remaining = max_entries - count;
20effc67
TL
7065 ret = bucket->list(dpp(), params, std::min(remaining, paginate_size), results,
7066 null_yield);
7c673cae 7067 if (ret < 0) {
1e59de90 7068 cerr << "ERROR: driver->list_objects(): " << cpp_strerror(-ret) << std::endl;
7c673cae
FG
7069 return -ret;
7070 }
1e59de90
TL
7071 ldpp_dout(dpp(), 20) << "INFO: " << __func__ <<
7072 ": list() returned without error; results.objs.sizie()=" <<
7073 results.objs.size() << "results.is_truncated=" << results.is_truncated << ", marker=" <<
7074 params.marker << dendl;
7c673cae 7075
20effc67 7076 count += results.objs.size();
7c673cae 7077
20effc67 7078 for (const auto& entry : results.objs) {
f67539c2 7079 encode_json("entry", entry, formatter.get());
7c673cae
FG
7080 }
7081 formatter->flush(cout);
20effc67 7082 } while (results.is_truncated && count < max_entries);
1e59de90 7083 ldpp_dout(dpp(), 20) << "INFO: " << __func__ << ": done" << dendl;
7c673cae
FG
7084
7085 formatter->close_section();
7086 formatter->flush(cout);
7087 } /* have bucket_name */
9f95a23c 7088 } /* OPT::BUCKETS_LIST */
7c673cae 7089
e306af50 7090 if (opt_cmd == OPT::BUCKET_RADOS_LIST) {
1e59de90 7091 RGWRadosList lister(static_cast<rgw::sal::RadosStore*>(driver),
e306af50 7092 max_concurrent_ios, orphan_stale_secs, tenant);
f67539c2
TL
7093 if (rgw_obj_fs) {
7094 lister.set_field_separator(*rgw_obj_fs);
7095 }
7096
e306af50 7097 if (bucket_name.empty()) {
1e59de90
TL
7098 // yes_i_really_mean_it means continue with listing even if
7099 // there are indexless buckets
7100 ret = lister.run(dpp(), yes_i_really_mean_it);
e306af50 7101 } else {
b3b6e05e 7102 ret = lister.run(dpp(), bucket_name);
e306af50
TL
7103 }
7104
7105 if (ret < 0) {
7106 std::cerr <<
7107 "ERROR: bucket radoslist failed to finish before " <<
7108 "encountering error: " << cpp_strerror(-ret) << std::endl;
7109 std::cerr << "************************************"
7110 "************************************" << std::endl;
7111 std::cerr << "WARNING: THE RESULTS ARE NOT RELIABLE AND SHOULD NOT " <<
7112 "BE USED IN DELETING ORPHANS" << std::endl;
7113 std::cerr << "************************************"
7114 "************************************" << std::endl;
7115 return -ret;
7116 }
7117 }
7118
1e59de90
TL
7119 if (opt_cmd == OPT::BUCKET_LAYOUT) {
7120 if (bucket_name.empty()) {
7121 cerr << "ERROR: bucket not specified" << std::endl;
7122 return EINVAL;
7123 }
7124 int ret = init_bucket(user.get(), tenant, bucket_name, bucket_id, &bucket);
7125 if (ret < 0) {
7126 return -ret;
7127 }
7128 const auto& bucket_info = bucket->get_info();
7129 formatter->open_object_section("layout");
7130 encode_json("layout", bucket_info.layout, formatter.get());
7131 formatter->close_section();
7132 formatter->flush(cout);
7133 }
7134
9f95a23c 7135 if (opt_cmd == OPT::BUCKET_STATS) {
1911f103
TL
7136 if (bucket_name.empty() && !bucket_id.empty()) {
7137 rgw_bucket bucket;
1e59de90 7138 if (!rgw_find_bucket_by_id(dpp(), driver->ctx(), driver, marker, bucket_id, &bucket)) {
1911f103
TL
7139 cerr << "failure: no such bucket id" << std::endl;
7140 return -ENOENT;
7141 }
7142 bucket_op.set_tenant(bucket.tenant);
7143 bucket_op.set_bucket_name(bucket.name);
7144 }
7c673cae
FG
7145 bucket_op.set_fetch_stats(true);
7146
1e59de90 7147 int r = RGWBucketAdminOp::info(driver, bucket_op, stream_flusher, null_yield, dpp());
31f18b77
FG
7148 if (r < 0) {
7149 cerr << "failure: " << cpp_strerror(-r) << ": " << err << std::endl;
1e59de90 7150 return posix_errortrans(-r);
31f18b77 7151 }
7c673cae
FG
7152 }
7153
9f95a23c 7154 if (opt_cmd == OPT::BUCKET_LINK) {
7c673cae 7155 bucket_op.set_bucket_id(bucket_id);
9f95a23c 7156 bucket_op.set_new_bucket_name(new_bucket_name);
7c673cae 7157 string err;
1e59de90 7158 int r = RGWBucketAdminOp::link(driver, bucket_op, dpp(), &err);
7c673cae
FG
7159 if (r < 0) {
7160 cerr << "failure: " << cpp_strerror(-r) << ": " << err << std::endl;
7161 return -r;
7162 }
7163 }
7164
9f95a23c 7165 if (opt_cmd == OPT::BUCKET_UNLINK) {
1e59de90 7166 int r = RGWBucketAdminOp::unlink(driver, bucket_op, dpp());
7c673cae
FG
7167 if (r < 0) {
7168 cerr << "failure: " << cpp_strerror(-r) << std::endl;
7169 return -r;
7170 }
7171 }
7172
1e59de90
TL
7173 if (opt_cmd == OPT::BUCKET_SHARD_OBJECTS) {
7174 const auto prefix = opt_prefix ? *opt_prefix : "obj"s;
7175 if (!num_shards_specified) {
7176 cerr << "ERROR: num-shards must be specified."
7177 << std::endl;
7178 return EINVAL;
7179 }
7180
7181 if (specified_shard_id) {
7182 if (shard_id >= num_shards) {
7183 cerr << "ERROR: shard-id must be less than num-shards."
7184 << std::endl;
7185 return EINVAL;
7186 }
7187 std::string obj;
7188 uint64_t ctr = 0;
7189 int shard;
7190 do {
7191 obj = fmt::format("{}{:0>20}", prefix, ctr);
7192 shard = RGWSI_BucketIndex_RADOS::bucket_shard_index(obj, num_shards);
7193 ++ctr;
7194 } while (shard != shard_id);
7195
7196 formatter->open_object_section("shard_obj");
7197 encode_json("obj", obj, formatter.get());
7198 formatter->close_section();
7199 formatter->flush(cout);
7200 } else {
7201 std::vector<std::string> objs(num_shards);
7202 for (uint64_t ctr = 0, shardsleft = num_shards; shardsleft > 0; ++ctr) {
7203 auto key = fmt::format("{}{:0>20}", prefix, ctr);
7204 auto shard = RGWSI_BucketIndex_RADOS::bucket_shard_index(key, num_shards);
7205 if (objs[shard].empty()) {
7206 objs[shard] = std::move(key);
7207 --shardsleft;
7208 }
7209 }
7210
7211 formatter->open_object_section("shard_objs");
7212 encode_json("objs", objs, formatter.get());
7213 formatter->close_section();
7214 formatter->flush(cout);
7215 }
7216 }
7217
7218 if (opt_cmd == OPT::BUCKET_OBJECT_SHARD) {
7219 if (!num_shards_specified || object.empty()) {
7220 cerr << "ERROR: num-shards and object must be specified."
7221 << std::endl;
7222 return EINVAL;
7223 }
7224 auto shard = RGWSI_BucketIndex_RADOS::bucket_shard_index(object, num_shards);
7225 formatter->open_object_section("obj_shard");
7226 encode_json("shard", shard, formatter.get());
7227 formatter->close_section();
7228 formatter->flush(cout);
7229 }
7230
aee94f69
TL
7231 if (opt_cmd == OPT::BUCKET_RESYNC_ENCRYPTED_MULTIPART) {
7232 // repair logic for replication of encrypted multipart uploads:
7233 // https://tracker.ceph.com/issues/46062
7234 if (bucket_name.empty()) {
7235 cerr << "ERROR: bucket not specified" << std::endl;
7236 return EINVAL;
7237 }
7238 int ret = init_bucket(user.get(), tenant, bucket_name, bucket_id, &bucket);
7239 if (ret < 0) {
7240 return -ret;
7241 }
7242
7243 auto rados_driver = dynamic_cast<rgw::sal::RadosStore*>(driver);
7244 if (!rados_driver) {
7245 cerr << "ERROR: this command can only work when the cluster "
7246 "has a RADOS backing store." << std::endl;
7247 return EPERM;
7248 }
7249
7250 // fail if recovery wouldn't generate replication log entries
7251 if (!rados_driver->svc()->zone->need_to_log_data() && !yes_i_really_mean_it) {
7252 cerr << "This command is only necessary for replicated buckets." << std::endl;
7253 cerr << "do you really mean it? (requires --yes-i-really-mean-it)" << std::endl;
7254 return EPERM;
7255 }
7256
7257 formatter->open_object_section("modified");
7258 encode_json("bucket", bucket->get_name(), formatter.get());
7259 encode_json("bucket_id", bucket->get_bucket_id(), formatter.get());
7260
7261 ret = rados_driver->getRados()->bucket_resync_encrypted_multipart(
7262 dpp(), null_yield, rados_driver, bucket->get_info(),
7263 marker, stream_flusher);
7264 if (ret < 0) {
7265 return -ret;
7266 }
7267 formatter->close_section();
7268 formatter->flush(cout);
7269 return 0;
7270 }
7271
9f95a23c 7272 if (opt_cmd == OPT::BUCKET_CHOWN) {
522d829b
TL
7273 if (bucket_name.empty()) {
7274 cerr << "ERROR: bucket name not specified" << std::endl;
7275 return EINVAL;
7276 }
9f95a23c
TL
7277
7278 bucket_op.set_bucket_name(bucket_name);
7279 bucket_op.set_new_bucket_name(new_bucket_name);
7280 string err;
9f95a23c 7281
1e59de90 7282 int r = RGWBucketAdminOp::chown(driver, bucket_op, marker, dpp(), &err);
9f95a23c
TL
7283 if (r < 0) {
7284 cerr << "failure: " << cpp_strerror(-r) << ": " << err << std::endl;
7285 return -r;
7286 }
7287 }
7288
7289 if (opt_cmd == OPT::LOG_LIST) {
7c673cae
FG
7290 // filter by date?
7291 if (date.size() && date.size() != 10) {
7292 cerr << "bad date format for '" << date << "', expect YYYY-MM-DD" << std::endl;
7293 return EINVAL;
7294 }
7295
7296 formatter->reset();
7297 formatter->open_array_section("logs");
7298 RGWAccessHandle h;
1e59de90 7299 int r = static_cast<rgw::sal::RadosStore*>(driver)->getRados()->log_list_init(dpp(), date, &h);
7c673cae
FG
7300 if (r == -ENOENT) {
7301 // no logs.
7302 } else {
7303 if (r < 0) {
7304 cerr << "log list: error " << r << std::endl;
7305 return -r;
7306 }
7307 while (true) {
7308 string name;
1e59de90 7309 int r = static_cast<rgw::sal::RadosStore*>(driver)->getRados()->log_list_next(h, &name);
7c673cae
FG
7310 if (r == -ENOENT)
7311 break;
7312 if (r < 0) {
7313 cerr << "log list: error " << r << std::endl;
7314 return -r;
7315 }
7316 formatter->dump_string("object", name);
7317 }
7318 }
7319 formatter->close_section();
7320 formatter->flush(cout);
7321 cout << std::endl;
7322 }
7323
9f95a23c 7324 if (opt_cmd == OPT::LOG_SHOW || opt_cmd == OPT::LOG_RM) {
7c673cae
FG
7325 if (object.empty() && (date.empty() || bucket_name.empty() || bucket_id.empty())) {
7326 cerr << "specify an object or a date, bucket and bucket-id" << std::endl;
11fdf7f2 7327 exit(1);
7c673cae
FG
7328 }
7329
7330 string oid;
7331 if (!object.empty()) {
7332 oid = object;
7333 } else {
7334 oid = date;
7335 oid += "-";
7336 oid += bucket_id;
7337 oid += "-";
7338 oid += bucket_name;
7339 }
7340
9f95a23c 7341 if (opt_cmd == OPT::LOG_SHOW) {
7c673cae
FG
7342 RGWAccessHandle h;
7343
1e59de90 7344 int r = static_cast<rgw::sal::RadosStore*>(driver)->getRados()->log_show_init(dpp(), oid, &h);
7c673cae
FG
7345 if (r < 0) {
7346 cerr << "error opening log " << oid << ": " << cpp_strerror(-r) << std::endl;
7347 return -r;
7348 }
7349
7350 formatter->reset();
7351 formatter->open_object_section("log");
7352
7353 struct rgw_log_entry entry;
7354
7355 // peek at first entry to get bucket metadata
1e59de90 7356 r = static_cast<rgw::sal::RadosStore*>(driver)->getRados()->log_show_next(dpp(), h, &entry);
7c673cae
FG
7357 if (r < 0) {
7358 cerr << "error reading log " << oid << ": " << cpp_strerror(-r) << std::endl;
7359 return -r;
7360 }
7361 formatter->dump_string("bucket_id", entry.bucket_id);
7362 formatter->dump_string("bucket_owner", entry.bucket_owner.to_str());
7363 formatter->dump_string("bucket", entry.bucket);
7364
7365 uint64_t agg_time = 0;
7366 uint64_t agg_bytes_sent = 0;
7367 uint64_t agg_bytes_received = 0;
7368 uint64_t total_entries = 0;
7369
7370 if (show_log_entries)
7371 formatter->open_array_section("log_entries");
7372
7373 do {
11fdf7f2
TL
7374 using namespace std::chrono;
7375 uint64_t total_time = duration_cast<milliseconds>(entry.total_time).count();
7c673cae
FG
7376
7377 agg_time += total_time;
7378 agg_bytes_sent += entry.bytes_sent;
7379 agg_bytes_received += entry.bytes_received;
7380 total_entries++;
7381
7382 if (skip_zero_entries && entry.bytes_sent == 0 &&
7383 entry.bytes_received == 0)
7384 goto next;
7385
7386 if (show_log_entries) {
7387
f67539c2 7388 rgw_format_ops_log_entry(entry, formatter.get());
7c673cae
FG
7389 formatter->flush(cout);
7390 }
7391next:
1e59de90 7392 r = static_cast<rgw::sal::RadosStore*>(driver)->getRados()->log_show_next(dpp(), h, &entry);
7c673cae
FG
7393 } while (r > 0);
7394
7395 if (r < 0) {
7396 cerr << "error reading log " << oid << ": " << cpp_strerror(-r) << std::endl;
7397 return -r;
7398 }
7399 if (show_log_entries)
7400 formatter->close_section();
7401
7402 if (show_log_sum) {
7403 formatter->open_object_section("log_sum");
7404 formatter->dump_int("bytes_sent", agg_bytes_sent);
7405 formatter->dump_int("bytes_received", agg_bytes_received);
7406 formatter->dump_int("total_time", agg_time);
7407 formatter->dump_int("total_entries", total_entries);
7408 formatter->close_section();
7409 }
7410 formatter->close_section();
7411 formatter->flush(cout);
7412 cout << std::endl;
7413 }
9f95a23c 7414 if (opt_cmd == OPT::LOG_RM) {
1e59de90 7415 int r = static_cast<rgw::sal::RadosStore*>(driver)->getRados()->log_remove(dpp(), oid);
7c673cae
FG
7416 if (r < 0) {
7417 cerr << "error removing log " << oid << ": " << cpp_strerror(-r) << std::endl;
7418 return -r;
7419 }
7420 }
7421 }
7422
9f95a23c 7423 if (opt_cmd == OPT::POOL_ADD) {
7c673cae
FG
7424 if (pool_name.empty()) {
7425 cerr << "need to specify pool to add!" << std::endl;
11fdf7f2 7426 exit(1);
7c673cae
FG
7427 }
7428
1e59de90 7429 int ret = static_cast<rgw::sal::RadosStore*>(driver)->svc()->zone->add_bucket_placement(dpp(), pool, null_yield);
7c673cae
FG
7430 if (ret < 0)
7431 cerr << "failed to add bucket placement: " << cpp_strerror(-ret) << std::endl;
7432 }
7433
9f95a23c 7434 if (opt_cmd == OPT::POOL_RM) {
7c673cae
FG
7435 if (pool_name.empty()) {
7436 cerr << "need to specify pool to remove!" << std::endl;
11fdf7f2 7437 exit(1);
7c673cae
FG
7438 }
7439
1e59de90 7440 int ret = static_cast<rgw::sal::RadosStore*>(driver)->svc()->zone->remove_bucket_placement(dpp(), pool, null_yield);
7c673cae
FG
7441 if (ret < 0)
7442 cerr << "failed to remove bucket placement: " << cpp_strerror(-ret) << std::endl;
7443 }
7444
9f95a23c 7445 if (opt_cmd == OPT::POOLS_LIST) {
7c673cae 7446 set<rgw_pool> pools;
1e59de90 7447 int ret = static_cast<rgw::sal::RadosStore*>(driver)->svc()->zone->list_placement_set(dpp(), pools, null_yield);
7c673cae
FG
7448 if (ret < 0) {
7449 cerr << "could not list placement set: " << cpp_strerror(-ret) << std::endl;
7450 return -ret;
7451 }
7452 formatter->reset();
7453 formatter->open_array_section("pools");
7454 for (auto siter = pools.begin(); siter != pools.end(); ++siter) {
7455 formatter->open_object_section("pool");
7456 formatter->dump_string("name", siter->to_str());
7457 formatter->close_section();
7458 }
7459 formatter->close_section();
7460 formatter->flush(cout);
7461 cout << std::endl;
7462 }
7463
9f95a23c 7464 if (opt_cmd == OPT::USAGE_SHOW) {
7c673cae
FG
7465 uint64_t start_epoch = 0;
7466 uint64_t end_epoch = (uint64_t)-1;
7467
7468 int ret;
7469
7470 if (!start_date.empty()) {
7471 ret = utime_t::parse_date(start_date, &start_epoch, NULL);
7472 if (ret < 0) {
7473 cerr << "ERROR: failed to parse start date" << std::endl;
7474 return 1;
7475 }
7476 }
7477 if (!end_date.empty()) {
7478 ret = utime_t::parse_date(end_date, &end_epoch, NULL);
7479 if (ret < 0) {
7480 cerr << "ERROR: failed to parse end date" << std::endl;
7481 return 1;
7482 }
7483 }
7484
7485
20effc67
TL
7486 if (!bucket_name.empty()) {
7487 int ret = init_bucket(user.get(), tenant, bucket_name, bucket_id, &bucket);
7488 if (ret < 0) {
7489 cerr << "ERROR: could not init bucket: " << cpp_strerror(-ret) << std::endl;
7490 return -ret;
7491 }
7492 }
1e59de90 7493 ret = RGWUsage::show(dpp(), driver, user.get(), bucket.get(), start_epoch,
20effc67
TL
7494 end_epoch, show_log_entries, show_log_sum, &categories,
7495 stream_flusher);
7c673cae
FG
7496 if (ret < 0) {
7497 cerr << "ERROR: failed to show usage" << std::endl;
7498 return 1;
7499 }
7500 }
7501
9f95a23c 7502 if (opt_cmd == OPT::USAGE_TRIM) {
20effc67 7503 if (rgw::sal::User::empty(user) && bucket_name.empty() &&
11fdf7f2
TL
7504 start_date.empty() && end_date.empty() && !yes_i_really_mean_it) {
7505 cerr << "usage trim without user/date/bucket specified will remove *all* users data" << std::endl;
7c673cae
FG
7506 cerr << "do you really mean it? (requires --yes-i-really-mean-it)" << std::endl;
7507 return 1;
7508 }
7509 int ret;
7510 uint64_t start_epoch = 0;
7511 uint64_t end_epoch = (uint64_t)-1;
7512
7513
7514 if (!start_date.empty()) {
7515 ret = utime_t::parse_date(start_date, &start_epoch, NULL);
7516 if (ret < 0) {
7517 cerr << "ERROR: failed to parse start date" << std::endl;
7518 return 1;
7519 }
7520 }
7521
7522 if (!end_date.empty()) {
7523 ret = utime_t::parse_date(end_date, &end_epoch, NULL);
7524 if (ret < 0) {
7525 cerr << "ERROR: failed to parse end date" << std::endl;
7526 return 1;
7527 }
7528 }
7529
20effc67
TL
7530 if (!bucket_name.empty()) {
7531 int ret = init_bucket(user.get(), tenant, bucket_name, bucket_id, &bucket);
7532 if (ret < 0) {
7533 cerr << "ERROR: could not init bucket: " << cpp_strerror(-ret) << std::endl;
7534 return -ret;
7535 }
7536 }
1e59de90 7537 ret = RGWUsage::trim(dpp(), driver, user.get(), bucket.get(), start_epoch, end_epoch);
7c673cae
FG
7538 if (ret < 0) {
7539 cerr << "ERROR: read_usage() returned ret=" << ret << std::endl;
7540 return 1;
7541 }
7542 }
7543
9f95a23c 7544 if (opt_cmd == OPT::USAGE_CLEAR) {
11fdf7f2
TL
7545 if (!yes_i_really_mean_it) {
7546 cerr << "usage clear would remove *all* users usage data for all time" << std::endl;
7547 cerr << "do you really mean it? (requires --yes-i-really-mean-it)" << std::endl;
7548 return 1;
7549 }
7550
1e59de90 7551 ret = RGWUsage::clear(dpp(), driver);
11fdf7f2
TL
7552 if (ret < 0) {
7553 return ret;
7554 }
7555 }
7556
7557
9f95a23c 7558 if (opt_cmd == OPT::OLH_GET || opt_cmd == OPT::OLH_READLOG) {
7c673cae
FG
7559 if (bucket_name.empty()) {
7560 cerr << "ERROR: bucket not specified" << std::endl;
7561 return EINVAL;
7562 }
7563 if (object.empty()) {
7564 cerr << "ERROR: object not specified" << std::endl;
7565 return EINVAL;
7566 }
7c673cae
FG
7567 }
7568
9f95a23c 7569 if (opt_cmd == OPT::OLH_GET) {
20effc67 7570 int ret = init_bucket(user.get(), tenant, bucket_name, bucket_id, &bucket);
7c673cae
FG
7571 if (ret < 0) {
7572 cerr << "ERROR: could not init bucket: " << cpp_strerror(-ret) << std::endl;
7573 return -ret;
7574 }
7575 RGWOLHInfo olh;
20effc67 7576 rgw_obj obj(bucket->get_key(), object);
1e59de90 7577 ret = static_cast<rgw::sal::RadosStore*>(driver)->getRados()->get_olh(dpp(), bucket->get_info(), obj, &olh);
7c673cae
FG
7578 if (ret < 0) {
7579 cerr << "ERROR: failed reading olh: " << cpp_strerror(-ret) << std::endl;
7580 return -ret;
7581 }
f67539c2 7582 encode_json("olh", olh, formatter.get());
7c673cae
FG
7583 formatter->flush(cout);
7584 }
7585
9f95a23c 7586 if (opt_cmd == OPT::OLH_READLOG) {
20effc67 7587 int ret = init_bucket(user.get(), tenant, bucket_name, bucket_id, &bucket);
7c673cae
FG
7588 if (ret < 0) {
7589 cerr << "ERROR: could not init bucket: " << cpp_strerror(-ret) << std::endl;
7590 return -ret;
7591 }
7592 map<uint64_t, vector<rgw_bucket_olh_log_entry> > log;
7593 bool is_truncated;
7594
20effc67 7595 std::unique_ptr<rgw::sal::Object> obj = bucket->get_object(object);
7c673cae
FG
7596
7597 RGWObjState *state;
7598
1e59de90 7599 ret = obj->get_obj_state(dpp(), &state, null_yield);
7c673cae
FG
7600 if (ret < 0) {
7601 return -ret;
7602 }
7603
1e59de90 7604 ret = static_cast<rgw::sal::RadosStore*>(driver)->getRados()->bucket_index_read_olh_log(dpp(), bucket->get_info(), *state, obj->get_obj(), 0, &log, &is_truncated);
7c673cae
FG
7605 if (ret < 0) {
7606 cerr << "ERROR: failed reading olh: " << cpp_strerror(-ret) << std::endl;
7607 return -ret;
7608 }
7609 formatter->open_object_section("result");
f67539c2
TL
7610 encode_json("is_truncated", is_truncated, formatter.get());
7611 encode_json("log", log, formatter.get());
7c673cae
FG
7612 formatter->close_section();
7613 formatter->flush(cout);
7614 }
7615
9f95a23c 7616 if (opt_cmd == OPT::BI_GET) {
11fdf7f2
TL
7617 if (bucket_name.empty()) {
7618 cerr << "ERROR: bucket name not specified" << std::endl;
7619 return EINVAL;
7620 }
7621 if (object.empty()) {
7622 cerr << "ERROR: object not specified" << std::endl;
7623 return EINVAL;
7624 }
20effc67 7625 int ret = init_bucket(user.get(), tenant, bucket_name, bucket_id, &bucket);
7c673cae
FG
7626 if (ret < 0) {
7627 cerr << "ERROR: could not init bucket: " << cpp_strerror(-ret) << std::endl;
7628 return -ret;
7629 }
20effc67 7630 rgw_obj obj(bucket->get_key(), object);
7c673cae
FG
7631 if (!object_version.empty()) {
7632 obj.key.set_instance(object_version);
7633 }
7634
7635 rgw_cls_bi_entry entry;
1e59de90 7636 ret = static_cast<rgw::sal::RadosStore*>(driver)->getRados()->bi_get(dpp(), bucket->get_info(), obj, bi_index_type, &entry);
7c673cae
FG
7637 if (ret < 0) {
7638 cerr << "ERROR: bi_get(): " << cpp_strerror(-ret) << std::endl;
7639 return -ret;
7640 }
7641
f67539c2 7642 encode_json("entry", entry, formatter.get());
7c673cae
FG
7643 formatter->flush(cout);
7644 }
7645
9f95a23c 7646 if (opt_cmd == OPT::BI_PUT) {
11fdf7f2
TL
7647 if (bucket_name.empty()) {
7648 cerr << "ERROR: bucket name not specified" << std::endl;
7649 return EINVAL;
7650 }
20effc67 7651 int ret = init_bucket(user.get(), tenant, bucket_name, bucket_id, &bucket);
7c673cae
FG
7652 if (ret < 0) {
7653 cerr << "ERROR: could not init bucket: " << cpp_strerror(-ret) << std::endl;
7654 return -ret;
7655 }
7656
7657 rgw_cls_bi_entry entry;
7658 cls_rgw_obj_key key;
7659 ret = read_decode_json(infile, entry, &key);
7660 if (ret < 0) {
7661 return 1;
7662 }
7663
20effc67 7664 rgw_obj obj(bucket->get_key(), key);
7c673cae 7665
1e59de90 7666 ret = static_cast<rgw::sal::RadosStore*>(driver)->getRados()->bi_put(dpp(), bucket->get_key(), obj, entry);
7c673cae
FG
7667 if (ret < 0) {
7668 cerr << "ERROR: bi_put(): " << cpp_strerror(-ret) << std::endl;
7669 return -ret;
7670 }
7671 }
7672
9f95a23c 7673 if (opt_cmd == OPT::BI_LIST) {
7c673cae
FG
7674 if (bucket_name.empty()) {
7675 cerr << "ERROR: bucket name not specified" << std::endl;
7676 return EINVAL;
7677 }
1e59de90 7678
20effc67 7679 int ret = init_bucket(user.get(), tenant, bucket_name, bucket_id, &bucket);
7c673cae
FG
7680 if (ret < 0) {
7681 cerr << "ERROR: could not init bucket: " << cpp_strerror(-ret) << std::endl;
7682 return -ret;
7683 }
7684
1e59de90 7685 std::list<rgw_cls_bi_entry> entries;
7c673cae 7686 bool is_truncated;
1e59de90
TL
7687 const auto& index = bucket->get_info().layout.current_index;
7688 const int max_shards = rgw::num_shards(index);
7c673cae
FG
7689 if (max_entries < 0) {
7690 max_entries = 1000;
7691 }
7692
1e59de90
TL
7693 ldpp_dout(dpp(), 20) << "INFO: " << __func__ << ": max_entries=" << max_entries <<
7694 ", index=" << index << ", max_shards=" << max_shards << dendl;
7c673cae
FG
7695
7696 formatter->open_array_section("entries");
7697
9f95a23c
TL
7698 int i = (specified_shard_id ? shard_id : 0);
7699 for (; i < max_shards; i++) {
1e59de90 7700 ldpp_dout(dpp(), 20) << "INFO: " << __func__ << ": starting shard=" << i << dendl;
f67539c2 7701
1e59de90
TL
7702 RGWRados::BucketShard bs(static_cast<rgw::sal::RadosStore*>(driver)->getRados());
7703 int ret = bs.init(dpp(), bucket->get_info(), index, i);
7c673cae
FG
7704 marker.clear();
7705
7706 if (ret < 0) {
1e59de90 7707 cerr << "ERROR: bs.init(bucket=" << bucket << ", shard=" << i << "): " << cpp_strerror(-ret) << std::endl;
7c673cae
FG
7708 return -ret;
7709 }
7710
7711 do {
7712 entries.clear();
20effc67 7713 // if object is specified, we use that as a filter to only retrieve some some entries
1e59de90 7714 ret = static_cast<rgw::sal::RadosStore*>(driver)->getRados()->bi_list(bs, object, marker, max_entries, &entries, &is_truncated);
7c673cae
FG
7715 if (ret < 0) {
7716 cerr << "ERROR: bi_list(): " << cpp_strerror(-ret) << std::endl;
7717 return -ret;
7718 }
1e59de90
TL
7719 ldpp_dout(dpp(), 20) << "INFO: " << __func__ <<
7720 ": bi_list() returned without error; entries.size()=" <<
7721 entries.size() << ", is_truncated=" << is_truncated <<
7722 ", marker=" << marker << dendl;
7c673cae 7723
1e59de90 7724 for (const auto& entry : entries) {
f67539c2 7725 encode_json("entry", entry, formatter.get());
7c673cae
FG
7726 marker = entry.idx;
7727 }
7728 formatter->flush(cout);
7729 } while (is_truncated);
1e59de90 7730
7c673cae 7731 formatter->flush(cout);
9f95a23c 7732
1e59de90 7733 if (specified_shard_id) {
9f95a23c 7734 break;
1e59de90 7735 }
7c673cae 7736 }
1e59de90
TL
7737 ldpp_dout(dpp(), 20) << "INFO: " << __func__ << ": done" << dendl;
7738
7c673cae
FG
7739 formatter->close_section();
7740 formatter->flush(cout);
7741 }
7742
9f95a23c 7743 if (opt_cmd == OPT::BI_PURGE) {
7c673cae
FG
7744 if (bucket_name.empty()) {
7745 cerr << "ERROR: bucket name not specified" << std::endl;
7746 return EINVAL;
7747 }
20effc67 7748 int ret = init_bucket(user.get(), tenant, bucket_name, bucket_id, &bucket);
7c673cae
FG
7749 if (ret < 0) {
7750 cerr << "ERROR: could not init bucket: " << cpp_strerror(-ret) << std::endl;
7751 return -ret;
7752 }
7753
20effc67
TL
7754 std::unique_ptr<rgw::sal::Bucket> cur_bucket;
7755 ret = init_bucket(user.get(), tenant, bucket_name, string(), &cur_bucket);
7756 if (ret == -ENOENT) {
7757 // no bucket entrypoint
7758 } else if (ret < 0) {
7c673cae
FG
7759 cerr << "ERROR: could not init current bucket info for bucket_name=" << bucket_name << ": " << cpp_strerror(-ret) << std::endl;
7760 return -ret;
20effc67
TL
7761 } else if (cur_bucket->get_bucket_id() == bucket->get_bucket_id() &&
7762 !yes_i_really_mean_it) {
7c673cae
FG
7763 cerr << "specified bucket instance points to a current bucket instance" << std::endl;
7764 cerr << "do you really mean it? (requires --yes-i-really-mean-it)" << std::endl;
7765 return EINVAL;
7766 }
7767
1e59de90
TL
7768 const auto& index = bucket->get_info().layout.current_index;
7769 if (index.layout.type == rgw::BucketIndexType::Indexless) {
7770 cerr << "ERROR: indexless bucket has no index to purge" << std::endl;
7771 return EINVAL;
7772 }
7773
7774 const int max_shards = rgw::num_shards(index);
7775 for (int i = 0; i < max_shards; i++) {
7776 RGWRados::BucketShard bs(static_cast<rgw::sal::RadosStore*>(driver)->getRados());
7777 int ret = bs.init(dpp(), bucket->get_info(), index, i);
7778 if (ret < 0) {
7779 cerr << "ERROR: bs.init(bucket=" << bucket << ", shard=" << i << "): " << cpp_strerror(-ret) << std::endl;
7780 return -ret;
7781 }
7782
7783 ret = static_cast<rgw::sal::RadosStore*>(driver)->getRados()->bi_remove(dpp(), bs);
7784 if (ret < 0) {
7785 cerr << "ERROR: failed to remove bucket index object: " << cpp_strerror(-ret) << std::endl;
7786 return -ret;
7787 }
7c673cae
FG
7788 }
7789 }
7790
9f95a23c 7791 if (opt_cmd == OPT::OBJECT_PUT) {
11fdf7f2
TL
7792 if (bucket_name.empty()) {
7793 cerr << "ERROR: bucket not specified" << std::endl;
7794 return EINVAL;
7795 }
7796 if (object.empty()) {
7797 cerr << "ERROR: object not specified" << std::endl;
7798 return EINVAL;
7799 }
7800
1e59de90 7801 RGWDataAccess data_access(driver);
11fdf7f2
TL
7802 rgw_obj_key key(object, object_version);
7803
7804 RGWDataAccess::BucketRef b;
7805 RGWDataAccess::ObjectRef obj;
7806
b3b6e05e 7807 int ret = data_access.get_bucket(dpp(), tenant, bucket_name, bucket_id, &b, null_yield);
11fdf7f2
TL
7808 if (ret < 0) {
7809 cerr << "ERROR: failed to init bucket: " << cpp_strerror(-ret) << std::endl;
7810 return -ret;
7811 }
7812
7813 ret = b->get_object(key, &obj);
7814 if (ret < 0) {
7815 cerr << "ERROR: failed to get object: " << cpp_strerror(-ret) << std::endl;
7816 return -ret;
7817 }
7818
7819 bufferlist bl;
7820 ret = read_input(infile, bl);
7821 if (ret < 0) {
7822 cerr << "ERROR: failed to read input: " << cpp_strerror(-ret) << std::endl;
7823 }
7824
7825 map<string, bufferlist> attrs;
9f95a23c 7826 ret = obj->put(bl, attrs, dpp(), null_yield);
11fdf7f2
TL
7827 if (ret < 0) {
7828 cerr << "ERROR: put object returned error: " << cpp_strerror(-ret) << std::endl;
7829 }
7830 }
7831
9f95a23c 7832 if (opt_cmd == OPT::OBJECT_RM) {
20effc67 7833 int ret = init_bucket(user.get(), tenant, bucket_name, bucket_id, &bucket);
7c673cae
FG
7834 if (ret < 0) {
7835 cerr << "ERROR: could not init bucket: " << cpp_strerror(-ret) << std::endl;
7836 return -ret;
7837 }
7838 rgw_obj_key key(object, object_version);
1e59de90 7839 ret = rgw_remove_object(dpp(), driver, bucket.get(), key);
7c673cae
FG
7840
7841 if (ret < 0) {
7842 cerr << "ERROR: object remove returned: " << cpp_strerror(-ret) << std::endl;
7843 return -ret;
7844 }
7845 }
7846
9f95a23c 7847 if (opt_cmd == OPT::OBJECT_REWRITE) {
7c673cae
FG
7848 if (bucket_name.empty()) {
7849 cerr << "ERROR: bucket not specified" << std::endl;
7850 return EINVAL;
7851 }
7852 if (object.empty()) {
7853 cerr << "ERROR: object not specified" << std::endl;
7854 return EINVAL;
7855 }
7856
20effc67 7857 int ret = init_bucket(user.get(), tenant, bucket_name, bucket_id, &bucket);
7c673cae
FG
7858 if (ret < 0) {
7859 cerr << "ERROR: could not init bucket: " << cpp_strerror(-ret) << std::endl;
7860 return -ret;
7861 }
7862
20effc67
TL
7863 std::unique_ptr<rgw::sal::Object> obj = bucket->get_object(object);
7864 obj->set_instance(object_version);
7c673cae
FG
7865 bool need_rewrite = true;
7866 if (min_rewrite_stripe_size > 0) {
1e59de90 7867 ret = check_min_obj_stripe_size(driver, obj.get(), min_rewrite_stripe_size, &need_rewrite);
7c673cae 7868 if (ret < 0) {
b3b6e05e 7869 ldpp_dout(dpp(), 0) << "WARNING: check_min_obj_stripe_size failed, r=" << ret << dendl;
7c673cae
FG
7870 }
7871 }
7872 if (need_rewrite) {
1e59de90
TL
7873 RGWRados* store = static_cast<rgw::sal::RadosStore*>(driver)->getRados();
7874 ret = store->rewrite_obj(bucket->get_info(), obj->get_obj(), dpp(), null_yield);
7c673cae
FG
7875 if (ret < 0) {
7876 cerr << "ERROR: object rewrite returned: " << cpp_strerror(-ret) << std::endl;
7877 return -ret;
7878 }
7879 } else {
b3b6e05e 7880 ldpp_dout(dpp(), 20) << "skipped object" << dendl;
7c673cae 7881 }
1e59de90
TL
7882 } // OPT::OBJECT_REWRITE
7883
7884 if (opt_cmd == OPT::OBJECT_REINDEX) {
7885 if (bucket_name.empty()) {
7886 cerr << "ERROR: --bucket not specified." << std::endl;
7887 return EINVAL;
7888 }
7889 if (object.empty() && objects_file.empty()) {
7890 cerr << "ERROR: neither --object nor --objects-file specified." << std::endl;
7891 return EINVAL;
7892 } else if (!object.empty() && !objects_file.empty()) {
7893 cerr << "ERROR: both --object and --objects-file specified and only one is allowed." << std::endl;
7894 return EINVAL;
7895 } else if (!objects_file.empty() && !object_version.empty()) {
7896 cerr << "ERROR: cannot specify --object_version when --objects-file specified." << std::endl;
7897 return EINVAL;
7898 }
7899
7900 int ret = init_bucket(user.get(), tenant, bucket_name, bucket_id, &bucket);
7901 if (ret < 0) {
7902 cerr << "ERROR: could not init bucket: " << cpp_strerror(-ret) <<
7903 "." << std::endl;
7904 return -ret;
7905 }
7906
7907 rgw::sal::RadosStore* rados_store = dynamic_cast<rgw::sal::RadosStore*>(driver);
7908 if (!rados_store) {
7909 cerr <<
7910 "ERROR: this command can only work when the cluster has a RADOS backing store." <<
7911 std::endl;
7912 return EPERM;
7913 }
7914 RGWRados* store = rados_store->getRados();
7915
7916 auto process = [&](const std::string& p_object, const std::string& p_object_version) -> int {
7917 std::unique_ptr<rgw::sal::Object> obj = bucket->get_object(p_object);
7918 obj->set_instance(p_object_version);
7919 ret = store->reindex_obj(bucket->get_info(), obj->get_obj(), dpp(), null_yield);
7920 if (ret < 0) {
7921 return ret;
7922 }
7923 return 0;
7924 };
7925
7926 if (!object.empty()) {
7927 ret = process(object, object_version);
7928 if (ret < 0) {
7929 return -ret;
7930 }
7931 } else {
7932 std::ifstream file;
7933 file.open(objects_file);
7934 if (!file.is_open()) {
7935 std::cerr << "ERROR: unable to open objects-file \"" <<
7936 objects_file << "\"." << std::endl;
7937 return ENOENT;
7938 }
7939
7940 std::string obj_name;
7941 const std::string empty_version;
7942 while (std::getline(file, obj_name)) {
7943 ret = process(obj_name, empty_version);
7944 if (ret < 0) {
7945 std::cerr << "ERROR: while processing \"" << obj_name <<
7946 "\", received " << cpp_strerror(-ret) << "." << std::endl;
7947 if (!yes_i_really_mean_it) {
7948 std::cerr <<
7949 "NOTE: with *caution* you can use --yes-i-really-mean-it to push through errors and continue processing." <<
7950 std::endl;
7951 return -ret;
7952 }
7953 }
7954 } // while
7955 }
7956 } // OPT::OBJECT_REINDEX
7c673cae 7957
9f95a23c 7958 if (opt_cmd == OPT::OBJECTS_EXPIRE) {
1e59de90 7959 if (!static_cast<rgw::sal::RadosStore*>(driver)->getRados()->process_expire_objects(dpp())) {
1adf2230 7960 cerr << "ERROR: process_expire_objects() processing returned error." << std::endl;
7c673cae
FG
7961 return 1;
7962 }
7963 }
7964
9f95a23c 7965 if (opt_cmd == OPT::OBJECTS_EXPIRE_STALE_LIST) {
1e59de90 7966 ret = RGWBucketAdminOp::fix_obj_expiry(driver, bucket_op, stream_flusher, dpp(), true);
81eedcae
TL
7967 if (ret < 0) {
7968 cerr << "ERROR: listing returned " << cpp_strerror(-ret) << std::endl;
7969 return -ret;
7970 }
7971 }
7972
9f95a23c 7973 if (opt_cmd == OPT::OBJECTS_EXPIRE_STALE_RM) {
1e59de90 7974 ret = RGWBucketAdminOp::fix_obj_expiry(driver, bucket_op, stream_flusher, dpp(), false);
81eedcae
TL
7975 if (ret < 0) {
7976 cerr << "ERROR: removing returned " << cpp_strerror(-ret) << std::endl;
7977 return -ret;
7978 }
7979 }
7980
9f95a23c 7981 if (opt_cmd == OPT::BUCKET_REWRITE) {
7c673cae
FG
7982 if (bucket_name.empty()) {
7983 cerr << "ERROR: bucket not specified" << std::endl;
7984 return EINVAL;
7985 }
7986
20effc67 7987 int ret = init_bucket(user.get(), tenant, bucket_name, bucket_id, &bucket);
7c673cae
FG
7988 if (ret < 0) {
7989 cerr << "ERROR: could not init bucket: " << cpp_strerror(-ret) << std::endl;
7990 return -ret;
7991 }
7992
7993 uint64_t start_epoch = 0;
7994 uint64_t end_epoch = 0;
7995
7996 if (!end_date.empty()) {
7997 int ret = utime_t::parse_date(end_date, &end_epoch, NULL);
7998 if (ret < 0) {
7999 cerr << "ERROR: failed to parse end date" << std::endl;
8000 return EINVAL;
8001 }
8002 }
8003 if (!start_date.empty()) {
8004 int ret = utime_t::parse_date(start_date, &start_epoch, NULL);
8005 if (ret < 0) {
8006 cerr << "ERROR: failed to parse start date" << std::endl;
8007 return EINVAL;
8008 }
8009 }
8010
8011 bool is_truncated = true;
9f95a23c 8012 bool cls_filtered = true;
7c673cae
FG
8013
8014 rgw_obj_index_key marker;
9f95a23c
TL
8015 string empty_prefix;
8016 string empty_delimiter;
7c673cae
FG
8017
8018 formatter->open_object_section("result");
8019 formatter->dump_string("bucket", bucket_name);
8020 formatter->open_array_section("objects");
7c673cae 8021
9f95a23c
TL
8022 constexpr uint32_t NUM_ENTRIES = 1000;
8023 uint16_t expansion_factor = 1;
8024 while (is_truncated) {
8025 RGWRados::ent_map_t result;
8026 result.reserve(NUM_ENTRIES);
8027
1e59de90
TL
8028 const auto& current_index = bucket->get_info().layout.current_index;
8029 int r = static_cast<rgw::sal::RadosStore*>(driver)->getRados()->cls_bucket_list_ordered(
8030 dpp(), bucket->get_info(), current_index, RGW_NO_SHARD,
9f95a23c
TL
8031 marker, empty_prefix, empty_delimiter,
8032 NUM_ENTRIES, true, expansion_factor,
8033 result, &is_truncated, &cls_filtered, &marker,
8034 null_yield,
8035 rgw_bucket_object_check_filter);
7c673cae
FG
8036 if (r < 0 && r != -ENOENT) {
8037 cerr << "ERROR: failed operation r=" << r << std::endl;
9f95a23c
TL
8038 } else if (r == -ENOENT) {
8039 break;
7c673cae
FG
8040 }
8041
9f95a23c
TL
8042 if (result.size() < NUM_ENTRIES / 8) {
8043 ++expansion_factor;
8044 } else if (result.size() > NUM_ENTRIES * 7 / 8 &&
8045 expansion_factor > 1) {
8046 --expansion_factor;
8047 }
7c673cae 8048
9f95a23c 8049 for (auto iter = result.begin(); iter != result.end(); ++iter) {
7c673cae
FG
8050 rgw_obj_key key = iter->second.key;
8051 rgw_bucket_dir_entry& entry = iter->second;
8052
8053 formatter->open_object_section("object");
8054 formatter->dump_string("name", key.name);
8055 formatter->dump_string("instance", key.instance);
8056 formatter->dump_int("size", entry.meta.size);
8057 utime_t ut(entry.meta.mtime);
8058 ut.gmtime(formatter->dump_stream("mtime"));
8059
8060 if ((entry.meta.size < min_rewrite_size) ||
8061 (entry.meta.size > max_rewrite_size) ||
8062 (start_epoch > 0 && start_epoch > (uint64_t)ut.sec()) ||
8063 (end_epoch > 0 && end_epoch < (uint64_t)ut.sec())) {
8064 formatter->dump_string("status", "Skipped");
8065 } else {
20effc67 8066 std::unique_ptr<rgw::sal::Object> obj = bucket->get_object(key);
7c673cae
FG
8067
8068 bool need_rewrite = true;
8069 if (min_rewrite_stripe_size > 0) {
1e59de90 8070 r = check_min_obj_stripe_size(driver, obj.get(), min_rewrite_stripe_size, &need_rewrite);
7c673cae 8071 if (r < 0) {
b3b6e05e 8072 ldpp_dout(dpp(), 0) << "WARNING: check_min_obj_stripe_size failed, r=" << r << dendl;
7c673cae
FG
8073 }
8074 }
8075 if (!need_rewrite) {
8076 formatter->dump_string("status", "Skipped");
8077 } else {
1e59de90
TL
8078 RGWRados* store = static_cast<rgw::sal::RadosStore*>(driver)->getRados();
8079 r = store->rewrite_obj(bucket->get_info(), obj->get_obj(), dpp(), null_yield);
7c673cae
FG
8080 if (r == 0) {
8081 formatter->dump_string("status", "Success");
8082 } else {
8083 formatter->dump_string("status", cpp_strerror(-r));
8084 }
8085 }
8086 }
8087 formatter->dump_int("flags", entry.flags);
8088
8089 formatter->close_section();
8090 formatter->flush(cout);
8091 }
8092 }
8093 formatter->close_section();
8094 formatter->close_section();
8095 formatter->flush(cout);
8096 }
8097
9f95a23c 8098 if (opt_cmd == OPT::BUCKET_RESHARD) {
1e59de90 8099 int ret = check_reshard_bucket_params(driver,
31f18b77
FG
8100 bucket_name,
8101 tenant,
8102 bucket_id,
8103 num_shards_specified,
8104 num_shards,
8105 yes_i_really_mean_it,
20effc67 8106 &bucket);
7c673cae 8107 if (ret < 0) {
31f18b77 8108 return ret;
7c673cae
FG
8109 }
8110
1e59de90
TL
8111 auto zone_svc = static_cast<rgw::sal::RadosStore*>(driver)->svc()->zone;
8112 if (!zone_svc->can_reshard()) {
8113 const auto& zonegroup = zone_svc->get_zonegroup();
8114 std::cerr << "The zonegroup '" << zonegroup.get_name() << "' does not "
8115 "have the resharding feature enabled." << std::endl;
8116 return ENOTSUP;
8117 }
f38dd50b
TL
8118
8119 if (!RGWBucketReshard::should_zone_reshard_now(bucket->get_info(), zone_svc) &&
1e59de90
TL
8120 !yes_i_really_mean_it) {
8121 std::cerr << "Bucket '" << bucket->get_name() << "' already has too many "
8122 "log generations (" << bucket->get_info().layout.logs.size() << ") "
8123 "from previous reshards that peer zones haven't finished syncing. "
8124 "Resharding is not recommended until the old generations sync, but "
8125 "you can force a reshard with --yes-i-really-mean-it." << std::endl;
8126 return EINVAL;
8127 }
8128
8129 RGWBucketReshard br(static_cast<rgw::sal::RadosStore*>(driver),
8130 bucket->get_info(), bucket->get_attrs(),
8131 nullptr /* no callback */);
7c673cae 8132
31f18b77
FG
8133#define DEFAULT_RESHARD_MAX_ENTRIES 1000
8134 if (max_entries < 1) {
8135 max_entries = DEFAULT_RESHARD_MAX_ENTRIES;
7c673cae
FG
8136 }
8137
1e59de90
TL
8138 ReshardFaultInjector fault;
8139 if (inject_error_at) {
8140 const int code = -inject_error_code.value_or(EIO);
8141 fault.inject(*inject_error_at, InjectError{code, dpp()});
8142 } else if (inject_abort_at) {
8143 fault.inject(*inject_abort_at, InjectAbort{});
05a536ef
TL
8144 } else if (inject_delay_at) {
8145 fault.inject(*inject_delay_at, InjectDelay{inject_delay, dpp()});
1e59de90
TL
8146 }
8147 ret = br.execute(num_shards, fault, max_entries, dpp(),
8148 verbose, &cout, formatter.get());
8149 return -ret;
31f18b77 8150 }
7c673cae 8151
9f95a23c 8152 if (opt_cmd == OPT::RESHARD_ADD) {
1e59de90 8153 int ret = check_reshard_bucket_params(driver,
31f18b77
FG
8154 bucket_name,
8155 tenant,
8156 bucket_id,
8157 num_shards_specified,
8158 num_shards,
8159 yes_i_really_mean_it,
20effc67 8160 &bucket);
7c673cae 8161 if (ret < 0) {
31f18b77 8162 return ret;
7c673cae
FG
8163 }
8164
1e59de90 8165 int num_source_shards = rgw::current_num_shards(bucket->get_info().layout);
7c673cae 8166
1e59de90 8167 RGWReshard reshard(static_cast<rgw::sal::RadosStore*>(driver), dpp());
31f18b77
FG
8168 cls_rgw_reshard_entry entry;
8169 entry.time = real_clock::now();
8170 entry.tenant = tenant;
8171 entry.bucket_name = bucket_name;
20effc67 8172 entry.bucket_id = bucket->get_info().bucket.bucket_id;
31f18b77
FG
8173 entry.old_num_shards = num_source_shards;
8174 entry.new_num_shards = num_shards;
7c673cae 8175
b3b6e05e 8176 return reshard.add(dpp(), entry);
31f18b77 8177 }
7c673cae 8178
9f95a23c 8179 if (opt_cmd == OPT::RESHARD_LIST) {
31f18b77
FG
8180 int ret;
8181 int count = 0;
8182 if (max_entries < 0) {
8183 max_entries = 1000;
7c673cae
FG
8184 }
8185
11fdf7f2 8186 int num_logshards =
1e59de90 8187 driver->ctx()->_conf.get_val<uint64_t>("rgw_reshard_num_logs");
7c673cae 8188
1e59de90 8189 RGWReshard reshard(static_cast<rgw::sal::RadosStore*>(driver), dpp());
7c673cae 8190
31f18b77
FG
8191 formatter->open_array_section("reshard");
8192 for (int i = 0; i < num_logshards; i++) {
7c673cae 8193 bool is_truncated = true;
1d09f67e 8194 std::string marker;
31f18b77 8195 do {
1d09f67e 8196 std::list<cls_rgw_reshard_entry> entries;
20effc67 8197 ret = reshard.list(dpp(), i, marker, max_entries - count, entries, &is_truncated);
7c673cae 8198 if (ret < 0) {
31f18b77
FG
8199 cerr << "Error listing resharding buckets: " << cpp_strerror(-ret) << std::endl;
8200 return ret;
7c673cae 8201 }
1d09f67e 8202 for (const auto& entry : entries) {
f67539c2 8203 encode_json("entry", entry, formatter.get());
31f18b77 8204 }
1d09f67e
TL
8205 if (is_truncated) {
8206 entries.crbegin()->get_key(&marker); // last entry's key becomes marker
8207 }
31f18b77
FG
8208 count += entries.size();
8209 formatter->flush(cout);
8210 } while (is_truncated && count < max_entries);
7c673cae 8211
31f18b77
FG
8212 if (count >= max_entries) {
8213 break;
8214 }
8215 }
7c673cae 8216
31f18b77
FG
8217 formatter->close_section();
8218 formatter->flush(cout);
1d09f67e 8219
31f18b77
FG
8220 return 0;
8221 }
7c673cae 8222
9f95a23c 8223 if (opt_cmd == OPT::RESHARD_STATUS) {
31f18b77
FG
8224 if (bucket_name.empty()) {
8225 cerr << "ERROR: bucket not specified" << std::endl;
8226 return EINVAL;
8227 }
7c673cae 8228
20effc67 8229 ret = init_bucket(user.get(), tenant, bucket_name, bucket_id, &bucket);
31f18b77
FG
8230 if (ret < 0) {
8231 cerr << "ERROR: could not init bucket: " << cpp_strerror(-ret) << std::endl;
8232 return -ret;
8233 }
7c673cae 8234
1e59de90
TL
8235 RGWBucketReshard br(static_cast<rgw::sal::RadosStore*>(driver),
8236 bucket->get_info(), bucket->get_attrs(),
8237 nullptr /* no callback */);
31f18b77 8238 list<cls_rgw_bucket_instance_entry> status;
b3b6e05e 8239 int r = br.get_status(dpp(), &status);
31f18b77 8240 if (r < 0) {
f64942e4
AA
8241 cerr << "ERROR: could not get resharding status for bucket " <<
8242 bucket_name << std::endl;
31f18b77 8243 return -r;
7c673cae 8244 }
31f18b77 8245
f67539c2 8246 show_reshard_status(status, formatter.get());
31f18b77
FG
8247 }
8248
9f95a23c 8249 if (opt_cmd == OPT::RESHARD_PROCESS) {
1e59de90 8250 RGWReshard reshard(static_cast<rgw::sal::RadosStore*>(driver), true, &cout);
31f18b77 8251
b3b6e05e 8252 int ret = reshard.process_all_logshards(dpp());
31f18b77
FG
8253 if (ret < 0) {
8254 cerr << "ERROR: failed to process reshard logs, error=" << cpp_strerror(-ret) << std::endl;
8255 return -ret;
7c673cae 8256 }
31f18b77 8257 }
7c673cae 8258
9f95a23c 8259 if (opt_cmd == OPT::RESHARD_CANCEL) {
31f18b77
FG
8260 if (bucket_name.empty()) {
8261 cerr << "ERROR: bucket not specified" << std::endl;
8262 return EINVAL;
8263 }
94b18763 8264
92f5a8d4 8265 bool bucket_initable = true;
20effc67 8266 ret = init_bucket(user.get(), tenant, bucket_name, bucket_id, &bucket);
7c673cae 8267 if (ret < 0) {
92f5a8d4
TL
8268 if (yes_i_really_mean_it) {
8269 bucket_initable = false;
8270 } else {
8271 cerr << "ERROR: could not init bucket: " << cpp_strerror(-ret) <<
8272 "; if you want to cancel the reshard request nonetheless, please "
8273 "use the --yes-i-really-mean-it option" << std::endl;
8274 return -ret;
8275 }
94b18763
FG
8276 }
8277
39ae355f
TL
8278 bool resharding_underway = true;
8279
92f5a8d4
TL
8280 if (bucket_initable) {
8281 // we did not encounter an error, so let's work with the bucket
1e59de90
TL
8282 RGWBucketReshard br(static_cast<rgw::sal::RadosStore*>(driver),
8283 bucket->get_info(), bucket->get_attrs(),
8284 nullptr /* no callback */);
b3b6e05e 8285 int ret = br.cancel(dpp());
92f5a8d4
TL
8286 if (ret < 0) {
8287 if (ret == -EBUSY) {
8288 cerr << "There is ongoing resharding, please retry after " <<
1e59de90 8289 driver->ctx()->_conf.get_val<uint64_t>("rgw_reshard_bucket_lock_duration") <<
39ae355f
TL
8290 " seconds." << std::endl;
8291 return -ret;
8292 } else if (ret == -EINVAL) {
8293 resharding_underway = false;
8294 // we can continue and try to unschedule
92f5a8d4 8295 } else {
39ae355f
TL
8296 cerr << "Error cancelling bucket \"" << bucket_name <<
8297 "\" resharding: " << cpp_strerror(-ret) << std::endl;
8298 return -ret;
92f5a8d4 8299 }
94b18763 8300 }
7c673cae 8301 }
92f5a8d4 8302
1e59de90 8303 RGWReshard reshard(static_cast<rgw::sal::RadosStore*>(driver), dpp());
7c673cae 8304
94b18763 8305 cls_rgw_reshard_entry entry;
81eedcae 8306 entry.tenant = tenant;
94b18763 8307 entry.bucket_name = bucket_name;
31f18b77 8308
b3b6e05e 8309 ret = reshard.remove(dpp(), entry);
39ae355f
TL
8310 if (ret == -ENOENT) {
8311 if (!resharding_underway) {
8312 cerr << "Error, bucket \"" << bucket_name <<
8313 "\" is neither undergoing resharding nor scheduled to undergo "
8314 "resharding." << std::endl;
8315 return EINVAL;
8316 } else {
8317 // we cancelled underway resharding above, so we're good
8318 return 0;
8319 }
8320 } else if (ret < 0) {
8321 cerr << "Error in updating reshard log with bucket \"" <<
8322 bucket_name << "\": " << cpp_strerror(-ret) << std::endl;
8323 return -ret;
7c673cae 8324 }
92f5a8d4 8325 } // OPT_RESHARD_CANCEL
7c673cae 8326
9f95a23c 8327 if (opt_cmd == OPT::OBJECT_UNLINK) {
20effc67 8328 int ret = init_bucket(user.get(), tenant, bucket_name, bucket_id, &bucket);
7c673cae
FG
8329 if (ret < 0) {
8330 cerr << "ERROR: could not init bucket: " << cpp_strerror(-ret) << std::endl;
8331 return -ret;
8332 }
8333 list<rgw_obj_index_key> oid_list;
8334 rgw_obj_key key(object, object_version);
8335 rgw_obj_index_key index_key;
8336 key.get_index_key(&index_key);
8337 oid_list.push_back(index_key);
1e59de90
TL
8338
8339 // note: under rados this removes directly from rados index objects
20effc67 8340 ret = bucket->remove_objs_from_index(dpp(), oid_list);
7c673cae
FG
8341 if (ret < 0) {
8342 cerr << "ERROR: remove_obj_from_index() returned error: " << cpp_strerror(-ret) << std::endl;
8343 return 1;
8344 }
8345 }
8346
9f95a23c 8347 if (opt_cmd == OPT::OBJECT_STAT) {
20effc67 8348 int ret = init_bucket(user.get(), tenant, bucket_name, bucket_id, &bucket);
7c673cae
FG
8349 if (ret < 0) {
8350 cerr << "ERROR: could not init bucket: " << cpp_strerror(-ret) << std::endl;
8351 return -ret;
8352 }
20effc67
TL
8353 std::unique_ptr<rgw::sal::Object> obj = bucket->get_object(object);
8354 obj->set_instance(object_version);
7c673cae 8355
1e59de90 8356 ret = obj->get_obj_attrs(null_yield, dpp());
7c673cae
FG
8357 if (ret < 0) {
8358 cerr << "ERROR: failed to stat object, returned error: " << cpp_strerror(-ret) << std::endl;
8359 return 1;
8360 }
8361 formatter->open_object_section("object_metadata");
8362 formatter->dump_string("name", object);
20effc67 8363 formatter->dump_unsigned("size", obj->get_obj_size());
7c673cae
FG
8364
8365 map<string, bufferlist>::iterator iter;
8366 map<string, bufferlist> other_attrs;
20effc67 8367 for (iter = obj->get_attrs().begin(); iter != obj->get_attrs().end(); ++iter) {
7c673cae
FG
8368 bufferlist& bl = iter->second;
8369 bool handled = false;
8370 if (iter->first == RGW_ATTR_MANIFEST) {
f67539c2 8371 handled = decode_dump<RGWObjManifest>("manifest", bl, formatter.get());
7c673cae 8372 } else if (iter->first == RGW_ATTR_ACL) {
f67539c2 8373 handled = decode_dump<RGWAccessControlPolicy>("policy", bl, formatter.get());
7c673cae 8374 } else if (iter->first == RGW_ATTR_ID_TAG) {
f67539c2 8375 handled = dump_string("tag", bl, formatter.get());
7c673cae 8376 } else if (iter->first == RGW_ATTR_ETAG) {
f67539c2 8377 handled = dump_string("etag", bl, formatter.get());
11fdf7f2 8378 } else if (iter->first == RGW_ATTR_COMPRESSION) {
f67539c2 8379 handled = decode_dump<RGWCompressionInfo>("compression", bl, formatter.get());
81eedcae 8380 } else if (iter->first == RGW_ATTR_DELETE_AT) {
f67539c2 8381 handled = decode_dump<utime_t>("delete_at", bl, formatter.get());
7c673cae
FG
8382 }
8383
8384 if (!handled)
8385 other_attrs[iter->first] = bl;
8386 }
8387
8388 formatter->open_object_section("attrs");
8389 for (iter = other_attrs.begin(); iter != other_attrs.end(); ++iter) {
f67539c2 8390 dump_string(iter->first.c_str(), iter->second, formatter.get());
7c673cae
FG
8391 }
8392 formatter->close_section();
8393 formatter->close_section();
8394 formatter->flush(cout);
8395 }
8396
9f95a23c 8397 if (opt_cmd == OPT::BUCKET_CHECK) {
7c673cae
FG
8398 if (check_head_obj_locator) {
8399 if (bucket_name.empty()) {
8400 cerr << "ERROR: need to specify bucket name" << std::endl;
8401 return EINVAL;
8402 }
f67539c2 8403 do_check_object_locator(tenant, bucket_name, fix, remove_bad, formatter.get());
7c673cae 8404 } else {
1e59de90 8405 RGWBucketAdminOp::check_index(driver, bucket_op, stream_flusher, null_yield, dpp());
7c673cae
FG
8406 }
8407 }
8408
aee94f69
TL
8409 if (opt_cmd == OPT::BUCKET_CHECK_OLH) {
8410 rgw::sal::RadosStore* store = dynamic_cast<rgw::sal::RadosStore*>(driver);
8411 if (!store) {
8412 cerr <<
8413 "WARNING: this command is only relevant when the cluster has a RADOS backing store." <<
8414 std::endl;
8415 return 0;
8416 }
8417 RGWBucketAdminOp::check_index_olh(store, bucket_op, stream_flusher, dpp());
8418 }
8419
8420 if (opt_cmd == OPT::BUCKET_CHECK_UNLINKED) {
8421 rgw::sal::RadosStore* store = dynamic_cast<rgw::sal::RadosStore*>(driver);
8422 if (!store) {
8423 cerr <<
8424 "WARNING: this command is only relevant when the cluster has a RADOS backing store." <<
8425 std::endl;
8426 return 0;
8427 }
8428 RGWBucketAdminOp::check_index_unlinked(store, bucket_op, stream_flusher, dpp());
8429 }
8430
9f95a23c 8431 if (opt_cmd == OPT::BUCKET_RM) {
11fdf7f2 8432 if (!inconsistent_index) {
1e59de90 8433 RGWBucketAdminOp::remove_bucket(driver, bucket_op, null_yield, dpp(), bypass_gc, true);
7c673cae 8434 } else {
181888fb
FG
8435 if (!yes_i_really_mean_it) {
8436 cerr << "using --inconsistent_index can corrupt the bucket index " << std::endl
8437 << "do you really mean it? (requires --yes-i-really-mean-it)" << std::endl;
8438 return 1;
8439 }
1e59de90 8440 RGWBucketAdminOp::remove_bucket(driver, bucket_op, null_yield, dpp(), bypass_gc, false);
7c673cae
FG
8441 }
8442 }
8443
9f95a23c 8444 if (opt_cmd == OPT::GC_LIST) {
7c673cae
FG
8445 int index = 0;
8446 bool truncated;
9f95a23c 8447 bool processing_queue = false;
7c673cae
FG
8448 formatter->open_array_section("entries");
8449
8450 do {
8451 list<cls_rgw_gc_obj_info> result;
1e59de90 8452 int ret = static_cast<rgw::sal::RadosStore*>(driver)->getRados()->list_gc_objs(&index, marker, 1000, !include_all, result, &truncated, processing_queue);
7c673cae
FG
8453 if (ret < 0) {
8454 cerr << "ERROR: failed to list objs: " << cpp_strerror(-ret) << std::endl;
8455 return 1;
8456 }
8457
8458
8459 list<cls_rgw_gc_obj_info>::iterator iter;
8460 for (iter = result.begin(); iter != result.end(); ++iter) {
8461 cls_rgw_gc_obj_info& info = *iter;
8462 formatter->open_object_section("chain_info");
8463 formatter->dump_string("tag", info.tag);
8464 formatter->dump_stream("time") << info.time;
8465 formatter->open_array_section("objs");
8466 list<cls_rgw_obj>::iterator liter;
8467 cls_rgw_obj_chain& chain = info.chain;
8468 for (liter = chain.objs.begin(); liter != chain.objs.end(); ++liter) {
8469 cls_rgw_obj& obj = *liter;
f67539c2 8470 encode_json("obj", obj, formatter.get());
7c673cae
FG
8471 }
8472 formatter->close_section(); // objs
8473 formatter->close_section(); // obj_chain
8474 formatter->flush(cout);
8475 }
8476 } while (truncated);
8477 formatter->close_section();
8478 formatter->flush(cout);
8479 }
8480
9f95a23c 8481 if (opt_cmd == OPT::GC_PROCESS) {
1e59de90 8482 int ret = static_cast<rgw::sal::RadosStore*>(driver)->getRados()->process_gc(!include_all);
7c673cae
FG
8483 if (ret < 0) {
8484 cerr << "ERROR: gc processing returned error: " << cpp_strerror(-ret) << std::endl;
8485 return 1;
8486 }
8487 }
8488
9f95a23c 8489 if (opt_cmd == OPT::LC_LIST) {
7c673cae 8490 formatter->open_array_section("lifecycle_list");
1e59de90 8491 vector<std::unique_ptr<rgw::sal::Lifecycle::LCEntry>> bucket_lc_map;
7c673cae 8492 string marker;
f6b5b4d7 8493 int index{0};
7c673cae
FG
8494#define MAX_LC_LIST_ENTRIES 100
8495 if (max_entries < 0) {
8496 max_entries = MAX_LC_LIST_ENTRIES;
8497 }
8498 do {
1e59de90 8499 int ret = static_cast<rgw::sal::RadosStore*>(driver)->getRados()->list_lc_progress(marker, max_entries,
f6b5b4d7 8500 bucket_lc_map, index);
7c673cae 8501 if (ret < 0) {
f6b5b4d7
TL
8502 cerr << "ERROR: failed to list objs: " << cpp_strerror(-ret)
8503 << std::endl;
7c673cae
FG
8504 return 1;
8505 }
f6b5b4d7 8506 for (const auto& entry : bucket_lc_map) {
7c673cae 8507 formatter->open_object_section("bucket_lc_info");
1e59de90
TL
8508 formatter->dump_string("bucket", entry->get_bucket());
8509 formatter->dump_string("shard", entry->get_oid());
f6b5b4d7 8510 char exp_buf[100];
1e59de90 8511 time_t t{time_t(entry->get_start_time())};
f6b5b4d7
TL
8512 if (std::strftime(
8513 exp_buf, sizeof(exp_buf),
8514 "%a, %d %b %Y %T %Z", std::gmtime(&t))) {
8515 formatter->dump_string("started", exp_buf);
8516 }
1e59de90 8517 string lc_status = LC_STATUS[entry->get_status()];
7c673cae
FG
8518 formatter->dump_string("status", lc_status);
8519 formatter->close_section(); // objs
8520 formatter->flush(cout);
7c673cae
FG
8521 }
8522 } while (!bucket_lc_map.empty());
8523
8524 formatter->close_section(); //lifecycle list
8525 formatter->flush(cout);
8526 }
8527
8528
9f95a23c 8529 if (opt_cmd == OPT::LC_GET) {
11fdf7f2
TL
8530 if (bucket_name.empty()) {
8531 cerr << "ERROR: bucket not specified" << std::endl;
8532 return EINVAL;
8533 }
8534
11fdf7f2 8535 RGWLifecycleConfiguration config;
20effc67 8536 ret = init_bucket(user.get(), tenant, bucket_name, bucket_id, &bucket);
11fdf7f2
TL
8537 if (ret < 0) {
8538 cerr << "ERROR: could not init bucket: " << cpp_strerror(-ret) << std::endl;
8539 return -ret;
8540 }
8541
20effc67
TL
8542 auto aiter = bucket->get_attrs().find(RGW_ATTR_LC);
8543 if (aiter == bucket->get_attrs().end()) {
11fdf7f2
TL
8544 return -ENOENT;
8545 }
8546
8547 bufferlist::const_iterator iter{&aiter->second};
8548 try {
8549 config.decode(iter);
8550 } catch (const buffer::error& e) {
8551 cerr << "ERROR: decode life cycle config failed" << std::endl;
8552 return -EIO;
8553 }
8554
f67539c2 8555 encode_json("result", config, formatter.get());
11fdf7f2
TL
8556 formatter->flush(cout);
8557 }
8558
9f95a23c 8559 if (opt_cmd == OPT::LC_PROCESS) {
20effc67
TL
8560 if ((! bucket_name.empty()) ||
8561 (! bucket_id.empty())) {
8562 int ret = init_bucket(nullptr, tenant, bucket_name, bucket_id, &bucket);
8563 if (ret < 0) {
8564 cerr << "ERROR: could not init bucket: " << cpp_strerror(-ret)
8565 << std::endl;
8566 return ret;
8567 }
8568 }
8569
8570 int ret =
1e59de90 8571 static_cast<rgw::sal::RadosStore*>(driver)->getRados()->process_lc(bucket);
7c673cae
FG
8572 if (ret < 0) {
8573 cerr << "ERROR: lc processing returned error: " << cpp_strerror(-ret) << std::endl;
8574 return 1;
8575 }
8576 }
8577
9f95a23c 8578 if (opt_cmd == OPT::LC_RESHARD_FIX) {
1e59de90 8579 ret = RGWBucketAdminOp::fix_lc_shards(driver, bucket_op, stream_flusher, dpp());
11fdf7f2 8580 if (ret < 0) {
20effc67 8581 cerr << "ERROR: fixing lc shards: " << cpp_strerror(-ret) << std::endl;
11fdf7f2
TL
8582 }
8583
8584 }
8585
9f95a23c 8586 if (opt_cmd == OPT::ORPHANS_FIND) {
1adf2230 8587 if (!yes_i_really_mean_it) {
e306af50
TL
8588 cerr << "this command is now deprecated; please consider using the rgw-orphan-list tool; "
8589 << "accidental removal of active objects cannot be reversed; "
1adf2230
AA
8590 << "do you really mean it? (requires --yes-i-really-mean-it)"
8591 << std::endl;
8592 return EINVAL;
e306af50
TL
8593 } else {
8594 cerr << "IMPORTANT: this command is now deprecated; please consider using the rgw-orphan-list tool"
8595 << std::endl;
1adf2230
AA
8596 }
8597
1e59de90 8598 RGWOrphanSearch search(static_cast<rgw::sal::RadosStore*>(driver), max_concurrent_ios, orphan_stale_secs);
7c673cae
FG
8599
8600 if (job_id.empty()) {
8601 cerr << "ERROR: --job-id not specified" << std::endl;
8602 return EINVAL;
8603 }
8604 if (pool_name.empty()) {
8605 cerr << "ERROR: --pool not specified" << std::endl;
8606 return EINVAL;
8607 }
8608
8609 RGWOrphanSearchInfo info;
8610
8611 info.pool = pool;
8612 info.job_name = job_id;
8613 info.num_shards = num_shards;
8614
b3b6e05e 8615 int ret = search.init(dpp(), job_id, &info, detail);
7c673cae
FG
8616 if (ret < 0) {
8617 cerr << "could not init search, ret=" << ret << std::endl;
8618 return -ret;
8619 }
b3b6e05e 8620 ret = search.run(dpp());
7c673cae
FG
8621 if (ret < 0) {
8622 return -ret;
8623 }
8624 }
8625
9f95a23c 8626 if (opt_cmd == OPT::ORPHANS_FINISH) {
e306af50
TL
8627 if (!yes_i_really_mean_it) {
8628 cerr << "this command is now deprecated; please consider using the rgw-orphan-list tool; "
8629 << "accidental removal of active objects cannot be reversed; "
8630 << "do you really mean it? (requires --yes-i-really-mean-it)"
8631 << std::endl;
8632 return EINVAL;
8633 } else {
8634 cerr << "IMPORTANT: this command is now deprecated; please consider using the rgw-orphan-list tool"
8635 << std::endl;
8636 }
8637
1e59de90 8638 RGWOrphanSearch search(static_cast<rgw::sal::RadosStore*>(driver), max_concurrent_ios, orphan_stale_secs);
7c673cae
FG
8639
8640 if (job_id.empty()) {
8641 cerr << "ERROR: --job-id not specified" << std::endl;
8642 return EINVAL;
8643 }
b3b6e05e 8644 int ret = search.init(dpp(), job_id, NULL);
7c673cae
FG
8645 if (ret < 0) {
8646 if (ret == -ENOENT) {
8647 cerr << "job not found" << std::endl;
8648 }
8649 return -ret;
8650 }
8651 ret = search.finish();
8652 if (ret < 0) {
8653 return -ret;
8654 }
8655 }
8656
9f95a23c 8657 if (opt_cmd == OPT::ORPHANS_LIST_JOBS){
e306af50
TL
8658 if (!yes_i_really_mean_it) {
8659 cerr << "this command is now deprecated; please consider using the rgw-orphan-list tool; "
8660 << "do you really mean it? (requires --yes-i-really-mean-it)"
8661 << std::endl;
8662 return EINVAL;
8663 } else {
8664 cerr << "IMPORTANT: this command is now deprecated; please consider using the rgw-orphan-list tool"
8665 << std::endl;
8666 }
8667
1e59de90 8668 RGWOrphanStore orphan_store(static_cast<rgw::sal::RadosStore*>(driver));
b3b6e05e 8669 int ret = orphan_store.init(dpp());
7c673cae
FG
8670 if (ret < 0){
8671 cerr << "connection to cluster failed!" << std::endl;
8672 return -ret;
8673 }
8674
8675 map <string,RGWOrphanSearchState> m;
8676 ret = orphan_store.list_jobs(m);
8677 if (ret < 0) {
8678 cerr << "job list failed" << std::endl;
8679 return -ret;
8680 }
8681 formatter->open_array_section("entries");
8682 for (const auto &it: m){
8683 if (!extra_info){
8684 formatter->dump_string("job-id",it.first);
8685 } else {
f67539c2 8686 encode_json("orphan_search_state", it.second, formatter.get());
7c673cae
FG
8687 }
8688 }
8689 formatter->close_section();
8690 formatter->flush(cout);
8691 }
8692
9f95a23c 8693 if (opt_cmd == OPT::USER_CHECK) {
1e59de90 8694 check_bad_user_bucket_mapping(driver, *user.get(), fix, null_yield, dpp());
7c673cae
FG
8695 }
8696
9f95a23c 8697 if (opt_cmd == OPT::USER_STATS) {
20effc67 8698 if (rgw::sal::User::empty(user)) {
94b18763
FG
8699 cerr << "ERROR: uid not specified" << std::endl;
8700 return EINVAL;
8701 }
94b18763 8702 if (reset_stats) {
eafe8130
TL
8703 if (!bucket_name.empty()) {
8704 cerr << "ERROR: --reset-stats does not work on buckets and "
8705 "bucket specified" << std::endl;
8706 return EINVAL;
8707 }
8708 if (sync_stats) {
8709 cerr << "ERROR: sync-stats includes the reset-stats functionality, "
8710 "so at most one of the two should be specified" << std::endl;
94b18763
FG
8711 return EINVAL;
8712 }
1e59de90 8713 ret = static_cast<rgw::sal::RadosStore*>(driver)->svc()->user->reset_bucket_stats(dpp(), user->get_id(), null_yield);
94b18763 8714 if (ret < 0) {
eafe8130
TL
8715 cerr << "ERROR: could not reset user stats: " << cpp_strerror(-ret) <<
8716 std::endl;
94b18763
FG
8717 return -ret;
8718 }
8719 }
8720
7c673cae
FG
8721 if (sync_stats) {
8722 if (!bucket_name.empty()) {
20effc67 8723 int ret = init_bucket(user.get(), tenant, bucket_name, bucket_id, &bucket);
9f95a23c
TL
8724 if (ret < 0) {
8725 cerr << "ERROR: could not init bucket: " << cpp_strerror(-ret) << std::endl;
8726 return -ret;
8727 }
20effc67 8728 ret = bucket->sync_user_stats(dpp(), null_yield);
7c673cae 8729 if (ret < 0) {
eafe8130
TL
8730 cerr << "ERROR: could not sync bucket stats: " <<
8731 cpp_strerror(-ret) << std::endl;
7c673cae
FG
8732 return -ret;
8733 }
8734 } else {
1e59de90 8735 int ret = rgw_user_sync_all_stats(dpp(), driver, user.get(), null_yield);
7c673cae 8736 if (ret < 0) {
eafe8130
TL
8737 cerr << "ERROR: could not sync user stats: " <<
8738 cpp_strerror(-ret) << std::endl;
7c673cae
FG
8739 return -ret;
8740 }
8741 }
8742 }
8743
20effc67
TL
8744 constexpr bool omit_utilized_stats = false;
8745 RGWStorageStats stats(omit_utilized_stats);
9f95a23c
TL
8746 ceph::real_time last_stats_sync;
8747 ceph::real_time last_stats_update;
1e59de90 8748 int ret = user->read_stats(dpp(), null_yield, &stats, &last_stats_sync, &last_stats_update);
7c673cae
FG
8749 if (ret < 0) {
8750 if (ret == -ENOENT) { /* in case of ENOENT */
8751 cerr << "User has not been initialized or user does not exist" << std::endl;
8752 } else {
8753 cerr << "ERROR: can't read user: " << cpp_strerror(ret) << std::endl;
8754 }
8755 return -ret;
8756 }
8757
9f95a23c
TL
8758
8759 {
8760 Formatter::ObjectSection os(*formatter, "result");
f67539c2 8761 encode_json("stats", stats, formatter.get());
9f95a23c 8762 utime_t last_sync_ut(last_stats_sync);
f67539c2 8763 encode_json("last_stats_sync", last_sync_ut, formatter.get());
9f95a23c 8764 utime_t last_update_ut(last_stats_update);
f67539c2 8765 encode_json("last_stats_update", last_update_ut, formatter.get());
9f95a23c 8766 }
7c673cae
FG
8767 formatter->flush(cout);
8768 }
8769
9f95a23c 8770 if (opt_cmd == OPT::METADATA_GET) {
1e59de90 8771 int ret = static_cast<rgw::sal::RadosStore*>(driver)->ctl()->meta.mgr->get(metadata_key, formatter.get(), null_yield, dpp());
7c673cae
FG
8772 if (ret < 0) {
8773 cerr << "ERROR: can't get key: " << cpp_strerror(-ret) << std::endl;
8774 return -ret;
8775 }
8776
8777 formatter->flush(cout);
8778 }
8779
9f95a23c 8780 if (opt_cmd == OPT::METADATA_PUT) {
7c673cae
FG
8781 bufferlist bl;
8782 int ret = read_input(infile, bl);
8783 if (ret < 0) {
8784 cerr << "ERROR: failed to read input: " << cpp_strerror(-ret) << std::endl;
8785 return -ret;
8786 }
1e59de90 8787 ret = static_cast<rgw::sal::RadosStore*>(driver)->ctl()->meta.mgr->put(metadata_key, bl, null_yield, dpp(), RGWMDLogSyncType::APPLY_ALWAYS, false);
7c673cae
FG
8788 if (ret < 0) {
8789 cerr << "ERROR: can't put key: " << cpp_strerror(-ret) << std::endl;
8790 return -ret;
8791 }
8792 }
8793
9f95a23c 8794 if (opt_cmd == OPT::METADATA_RM) {
1e59de90 8795 int ret = static_cast<rgw::sal::RadosStore*>(driver)->ctl()->meta.mgr->remove(metadata_key, null_yield, dpp());
7c673cae
FG
8796 if (ret < 0) {
8797 cerr << "ERROR: can't remove key: " << cpp_strerror(-ret) << std::endl;
8798 return -ret;
8799 }
8800 }
8801
9f95a23c
TL
8802 if (opt_cmd == OPT::METADATA_LIST || opt_cmd == OPT::USER_LIST) {
8803 if (opt_cmd == OPT::USER_LIST) {
7c673cae
FG
8804 metadata_key = "user";
8805 }
8806 void *handle;
8807 int max = 1000;
1e59de90 8808 int ret = driver->meta_list_keys_init(dpp(), metadata_key, marker, &handle);
7c673cae
FG
8809 if (ret < 0) {
8810 cerr << "ERROR: can't get key: " << cpp_strerror(-ret) << std::endl;
8811 return -ret;
8812 }
8813
8814 bool truncated;
181888fb 8815 uint64_t count = 0;
7c673cae 8816
181888fb
FG
8817 if (max_entries_specified) {
8818 formatter->open_object_section("result");
8819 }
7c673cae
FG
8820 formatter->open_array_section("keys");
8821
181888fb 8822 uint64_t left;
7c673cae
FG
8823 do {
8824 list<string> keys;
181888fb 8825 left = (max_entries_specified ? max_entries - count : max);
1e59de90 8826 ret = driver->meta_list_keys_next(dpp(), handle, left, keys, &truncated);
7c673cae
FG
8827 if (ret < 0 && ret != -ENOENT) {
8828 cerr << "ERROR: lists_keys_next(): " << cpp_strerror(-ret) << std::endl;
8829 return -ret;
8830 } if (ret != -ENOENT) {
8831 for (list<string>::iterator iter = keys.begin(); iter != keys.end(); ++iter) {
8832 formatter->dump_string("key", *iter);
181888fb 8833 ++count;
7c673cae
FG
8834 }
8835 formatter->flush(cout);
8836 }
181888fb 8837 } while (truncated && left > 0);
7c673cae
FG
8838
8839 formatter->close_section();
181888fb
FG
8840
8841 if (max_entries_specified) {
f67539c2
TL
8842 encode_json("truncated", truncated, formatter.get());
8843 encode_json("count", count, formatter.get());
181888fb 8844 if (truncated) {
1e59de90 8845 encode_json("marker", driver->meta_get_marker(handle), formatter.get());
181888fb
FG
8846 }
8847 formatter->close_section();
8848 }
7c673cae
FG
8849 formatter->flush(cout);
8850
1e59de90 8851 driver->meta_list_keys_complete(handle);
7c673cae
FG
8852 }
8853
9f95a23c 8854 if (opt_cmd == OPT::MDLOG_LIST) {
f67539c2
TL
8855 if (!start_date.empty()) {
8856 std::cerr << "start-date not allowed." << std::endl;
8857 return -EINVAL;
8858 }
8859 if (!end_date.empty()) {
8860 std::cerr << "end-date not allowed." << std::endl;
8861 return -EINVAL;
8862 }
8863 if (!end_marker.empty()) {
8864 std::cerr << "end-marker not allowed." << std::endl;
8865 return -EINVAL;
8866 }
8867 if (!start_marker.empty()) {
8868 if (marker.empty()) {
8869 marker = start_marker;
8870 } else {
8871 std::cerr << "start-marker and marker not both allowed." << std::endl;
8872 return -EINVAL;
8873 }
8874 }
7c673cae
FG
8875
8876 int i = (specified_shard_id ? shard_id : 0);
8877
8878 if (period_id.empty()) {
1e59de90
TL
8879 // use realm's current period
8880 RGWRealm realm;
8881 int ret = rgw::read_realm(dpp(), null_yield, cfgstore.get(),
8882 realm_id, realm_name, realm);
8883 if (ret < 0 ) {
8884 cerr << "failed to load realm: " << cpp_strerror(-ret) << std::endl;
7c673cae
FG
8885 return -ret;
8886 }
1e59de90 8887 period_id = realm.current_period;
7c673cae
FG
8888 std::cerr << "No --period given, using current period="
8889 << period_id << std::endl;
8890 }
1e59de90 8891 RGWMetadataLog *meta_log = static_cast<rgw::sal::RadosStore*>(driver)->svc()->mdlog->get_log(period_id);
7c673cae
FG
8892
8893 formatter->open_array_section("entries");
8894 for (; i < g_ceph_context->_conf->rgw_md_log_max_shards; i++) {
8895 void *handle;
8896 list<cls_log_entry> entries;
8897
f67539c2 8898 meta_log->init_list_entries(i, {}, {}, marker, &handle);
7c673cae
FG
8899 bool truncated;
8900 do {
b3b6e05e 8901 int ret = meta_log->list_entries(dpp(), handle, 1000, entries, NULL, &truncated);
7c673cae
FG
8902 if (ret < 0) {
8903 cerr << "ERROR: meta_log->list_entries(): " << cpp_strerror(-ret) << std::endl;
8904 return -ret;
8905 }
8906
8907 for (list<cls_log_entry>::iterator iter = entries.begin(); iter != entries.end(); ++iter) {
8908 cls_log_entry& entry = *iter;
1e59de90 8909 static_cast<rgw::sal::RadosStore*>(driver)->ctl()->meta.mgr->dump_log_entry(entry, formatter.get());
7c673cae
FG
8910 }
8911 formatter->flush(cout);
8912 } while (truncated);
8913
8914 meta_log->complete_list_entries(handle);
8915
8916 if (specified_shard_id)
8917 break;
8918 }
8919
8920
8921 formatter->close_section();
8922 formatter->flush(cout);
8923 }
8924
9f95a23c 8925 if (opt_cmd == OPT::MDLOG_STATUS) {
7c673cae
FG
8926 int i = (specified_shard_id ? shard_id : 0);
8927
8928 if (period_id.empty()) {
1e59de90
TL
8929 // use realm's current period
8930 RGWRealm realm;
8931 int ret = rgw::read_realm(dpp(), null_yield, cfgstore.get(),
8932 realm_id, realm_name, realm);
8933 if (ret < 0 ) {
8934 cerr << "failed to load realm: " << cpp_strerror(-ret) << std::endl;
7c673cae
FG
8935 return -ret;
8936 }
1e59de90 8937 period_id = realm.current_period;
7c673cae
FG
8938 std::cerr << "No --period given, using current period="
8939 << period_id << std::endl;
8940 }
1e59de90 8941 RGWMetadataLog *meta_log = static_cast<rgw::sal::RadosStore*>(driver)->svc()->mdlog->get_log(period_id);
7c673cae
FG
8942
8943 formatter->open_array_section("entries");
8944
8945 for (; i < g_ceph_context->_conf->rgw_md_log_max_shards; i++) {
8946 RGWMetadataLogInfo info;
b3b6e05e 8947 meta_log->get_info(dpp(), i, &info);
7c673cae 8948
f67539c2 8949 ::encode_json("info", info, formatter.get());
7c673cae
FG
8950
8951 if (specified_shard_id)
8952 break;
8953 }
8954
8955
8956 formatter->close_section();
8957 formatter->flush(cout);
8958 }
8959
9f95a23c 8960 if (opt_cmd == OPT::MDLOG_AUTOTRIM) {
7c673cae 8961 // need a full history for purging old mdlog periods
1e59de90 8962 static_cast<rgw::sal::RadosStore*>(driver)->svc()->mdlog->init_oldest_log_period(null_yield, dpp());
7c673cae 8963
1e59de90
TL
8964 RGWCoroutinesManager crs(driver->ctx(), driver->get_cr_registry());
8965 RGWHTTPManager http(driver->ctx(), crs.get_completion_mgr());
11fdf7f2 8966 int ret = http.start();
7c673cae
FG
8967 if (ret < 0) {
8968 cerr << "failed to initialize http client with " << cpp_strerror(ret) << std::endl;
8969 return -ret;
8970 }
8971
11fdf7f2 8972 auto num_shards = g_conf()->rgw_md_log_max_shards;
20effc67 8973 auto mltcr = create_admin_meta_log_trim_cr(
1e59de90 8974 dpp(), static_cast<rgw::sal::RadosStore*>(driver), &http, num_shards);
20effc67
TL
8975 if (!mltcr) {
8976 cerr << "Cluster misconfigured! Unable to trim." << std::endl;
8977 return -EIO;
8978 }
8979 ret = crs.run(dpp(), mltcr);
7c673cae
FG
8980 if (ret < 0) {
8981 cerr << "automated mdlog trim failed with " << cpp_strerror(ret) << std::endl;
8982 return -ret;
8983 }
8984 }
8985
9f95a23c 8986 if (opt_cmd == OPT::MDLOG_TRIM) {
f67539c2
TL
8987 if (!start_date.empty()) {
8988 std::cerr << "start-date not allowed." << std::endl;
8989 return -EINVAL;
8990 }
8991 if (!end_date.empty()) {
8992 std::cerr << "end-date not allowed." << std::endl;
8993 return -EINVAL;
8994 }
8995 if (!start_marker.empty()) {
8996 std::cerr << "start-marker not allowed." << std::endl;
8997 return -EINVAL;
8998 }
8999 if (!end_marker.empty()) {
9000 if (marker.empty()) {
9001 marker = end_marker;
9002 } else {
9003 std::cerr << "end-marker and marker not both allowed." << std::endl;
9004 return -EINVAL;
9005 }
9006 }
7c673cae
FG
9007
9008 if (!specified_shard_id) {
9009 cerr << "ERROR: shard-id must be specified for trim operation" << std::endl;
9010 return EINVAL;
9011 }
9012
522d829b
TL
9013 if (marker.empty()) {
9014 cerr << "ERROR: marker must be specified for trim operation" << std::endl;
9015 return EINVAL;
9016 }
9017
7c673cae
FG
9018 if (period_id.empty()) {
9019 std::cerr << "missing --period argument" << std::endl;
9020 return EINVAL;
9021 }
1e59de90 9022 RGWMetadataLog *meta_log = static_cast<rgw::sal::RadosStore*>(driver)->svc()->mdlog->get_log(period_id);
7c673cae 9023
eafe8130
TL
9024 // trim until -ENODATA
9025 do {
b3b6e05e 9026 ret = meta_log->trim(dpp(), shard_id, {}, {}, {}, marker);
eafe8130
TL
9027 } while (ret == 0);
9028 if (ret < 0 && ret != -ENODATA) {
7c673cae
FG
9029 cerr << "ERROR: meta_log->trim(): " << cpp_strerror(-ret) << std::endl;
9030 return -ret;
9031 }
9032 }
9033
9f95a23c 9034 if (opt_cmd == OPT::SYNC_INFO) {
f67539c2 9035 sync_info(opt_effective_zone_id, opt_bucket, zone_formatter.get());
9f95a23c
TL
9036 }
9037
9038 if (opt_cmd == OPT::SYNC_STATUS) {
f67539c2 9039 sync_status(formatter.get());
7c673cae
FG
9040 }
9041
9f95a23c 9042 if (opt_cmd == OPT::METADATA_SYNC_STATUS) {
1e59de90 9043 RGWMetaSyncStatusManager sync(static_cast<rgw::sal::RadosStore*>(driver), static_cast<rgw::sal::RadosStore*>(driver)->svc()->rados->get_async_processor());
7c673cae 9044
b3b6e05e 9045 int ret = sync.init(dpp());
7c673cae
FG
9046 if (ret < 0) {
9047 cerr << "ERROR: sync.init() returned ret=" << ret << std::endl;
9048 return -ret;
9049 }
9050
9051 rgw_meta_sync_status sync_status;
b3b6e05e 9052 ret = sync.read_sync_status(dpp(), &sync_status);
7c673cae
FG
9053 if (ret < 0) {
9054 cerr << "ERROR: sync.read_sync_status() returned ret=" << ret << std::endl;
9055 return -ret;
9056 }
9057
9058 formatter->open_object_section("summary");
f67539c2 9059 encode_json("sync_status", sync_status, formatter.get());
7c673cae
FG
9060
9061 uint64_t full_total = 0;
9062 uint64_t full_complete = 0;
9063
9064 for (auto marker_iter : sync_status.sync_markers) {
9065 full_total += marker_iter.second.total_entries;
9066 if (marker_iter.second.state == rgw_meta_sync_marker::SyncState::FullSync) {
9067 full_complete += marker_iter.second.pos;
9068 } else {
9069 full_complete += marker_iter.second.total_entries;
9070 }
9071 }
9072
9073 formatter->open_object_section("full_sync");
f67539c2
TL
9074 encode_json("total", full_total, formatter.get());
9075 encode_json("complete", full_complete, formatter.get());
7c673cae 9076 formatter->close_section();
1e59de90
TL
9077 formatter->dump_string("current_time",
9078 to_iso_8601(ceph::real_clock::now(),
9079 iso_8601_format::YMDhms));
7c673cae
FG
9080 formatter->close_section();
9081
9082 formatter->flush(cout);
9083
9084 }
9085
9f95a23c 9086 if (opt_cmd == OPT::METADATA_SYNC_INIT) {
1e59de90 9087 RGWMetaSyncStatusManager sync(static_cast<rgw::sal::RadosStore*>(driver), static_cast<rgw::sal::RadosStore*>(driver)->svc()->rados->get_async_processor());
7c673cae 9088
b3b6e05e 9089 int ret = sync.init(dpp());
7c673cae
FG
9090 if (ret < 0) {
9091 cerr << "ERROR: sync.init() returned ret=" << ret << std::endl;
9092 return -ret;
9093 }
b3b6e05e 9094 ret = sync.init_sync_status(dpp());
7c673cae
FG
9095 if (ret < 0) {
9096 cerr << "ERROR: sync.init_sync_status() returned ret=" << ret << std::endl;
9097 return -ret;
9098 }
9099 }
9100
9101
9f95a23c 9102 if (opt_cmd == OPT::METADATA_SYNC_RUN) {
1e59de90 9103 RGWMetaSyncStatusManager sync(static_cast<rgw::sal::RadosStore*>(driver), static_cast<rgw::sal::RadosStore*>(driver)->svc()->rados->get_async_processor());
7c673cae 9104
b3b6e05e 9105 int ret = sync.init(dpp());
7c673cae
FG
9106 if (ret < 0) {
9107 cerr << "ERROR: sync.init() returned ret=" << ret << std::endl;
9108 return -ret;
9109 }
9110
b3b6e05e 9111 ret = sync.run(dpp(), null_yield);
7c673cae
FG
9112 if (ret < 0) {
9113 cerr << "ERROR: sync.run() returned ret=" << ret << std::endl;
9114 return -ret;
9115 }
9116 }
9117
9f95a23c 9118 if (opt_cmd == OPT::DATA_SYNC_STATUS) {
7c673cae
FG
9119 if (source_zone.empty()) {
9120 cerr << "ERROR: source zone not specified" << std::endl;
9121 return EINVAL;
9122 }
1e59de90 9123 RGWDataSyncStatusManager sync(static_cast<rgw::sal::RadosStore*>(driver), static_cast<rgw::sal::RadosStore*>(driver)->svc()->rados->get_async_processor(), source_zone, nullptr);
7c673cae 9124
b3b6e05e 9125 int ret = sync.init(dpp());
7c673cae
FG
9126 if (ret < 0) {
9127 cerr << "ERROR: sync.init() returned ret=" << ret << std::endl;
9128 return -ret;
9129 }
9130
9131 rgw_data_sync_status sync_status;
28e407b8
AA
9132 if (specified_shard_id) {
9133 set<string> pending_buckets;
9134 set<string> recovering_buckets;
9135 rgw_data_sync_marker sync_marker;
b3b6e05e 9136 ret = sync.read_shard_status(dpp(), shard_id, pending_buckets, recovering_buckets, &sync_marker,
28e407b8
AA
9137 max_entries_specified ? max_entries : 20);
9138 if (ret < 0 && ret != -ENOENT) {
9139 cerr << "ERROR: sync.read_shard_status() returned ret=" << ret << std::endl;
9140 return -ret;
9141 }
9142 formatter->open_object_section("summary");
f67539c2
TL
9143 encode_json("shard_id", shard_id, formatter.get());
9144 encode_json("marker", sync_marker, formatter.get());
9145 encode_json("pending_buckets", pending_buckets, formatter.get());
9146 encode_json("recovering_buckets", recovering_buckets, formatter.get());
1e59de90
TL
9147 formatter->dump_string("current_time",
9148 to_iso_8601(ceph::real_clock::now(),
9149 iso_8601_format::YMDhms));
28e407b8
AA
9150 formatter->close_section();
9151 formatter->flush(cout);
9152 } else {
b3b6e05e 9153 ret = sync.read_sync_status(dpp(), &sync_status);
28e407b8
AA
9154 if (ret < 0 && ret != -ENOENT) {
9155 cerr << "ERROR: sync.read_sync_status() returned ret=" << ret << std::endl;
9156 return -ret;
9157 }
7c673cae 9158
28e407b8 9159 formatter->open_object_section("summary");
f67539c2 9160 encode_json("sync_status", sync_status, formatter.get());
7c673cae 9161
28e407b8
AA
9162 uint64_t full_total = 0;
9163 uint64_t full_complete = 0;
7c673cae 9164
28e407b8
AA
9165 for (auto marker_iter : sync_status.sync_markers) {
9166 full_total += marker_iter.second.total_entries;
9167 if (marker_iter.second.state == rgw_meta_sync_marker::SyncState::FullSync) {
9168 full_complete += marker_iter.second.pos;
9169 } else {
9170 full_complete += marker_iter.second.total_entries;
9171 }
7c673cae 9172 }
7c673cae 9173
28e407b8 9174 formatter->open_object_section("full_sync");
f67539c2
TL
9175 encode_json("total", full_total, formatter.get());
9176 encode_json("complete", full_complete, formatter.get());
28e407b8 9177 formatter->close_section();
1e59de90
TL
9178 formatter->dump_string("current_time",
9179 to_iso_8601(ceph::real_clock::now(),
9180 iso_8601_format::YMDhms));
28e407b8 9181 formatter->close_section();
7c673cae 9182
28e407b8
AA
9183 formatter->flush(cout);
9184 }
7c673cae
FG
9185 }
9186
9f95a23c 9187 if (opt_cmd == OPT::DATA_SYNC_INIT) {
7c673cae
FG
9188 if (source_zone.empty()) {
9189 cerr << "ERROR: source zone not specified" << std::endl;
9190 return EINVAL;
9191 }
94b18763 9192
1e59de90 9193 RGWDataSyncStatusManager sync(static_cast<rgw::sal::RadosStore*>(driver), static_cast<rgw::sal::RadosStore*>(driver)->svc()->rados->get_async_processor(), source_zone, nullptr);
7c673cae 9194
b3b6e05e 9195 int ret = sync.init(dpp());
7c673cae
FG
9196 if (ret < 0) {
9197 cerr << "ERROR: sync.init() returned ret=" << ret << std::endl;
9198 return -ret;
9199 }
9200
b3b6e05e 9201 ret = sync.init_sync_status(dpp());
7c673cae
FG
9202 if (ret < 0) {
9203 cerr << "ERROR: sync.init_sync_status() returned ret=" << ret << std::endl;
9204 return -ret;
9205 }
9206 }
9207
9f95a23c 9208 if (opt_cmd == OPT::DATA_SYNC_RUN) {
7c673cae
FG
9209 if (source_zone.empty()) {
9210 cerr << "ERROR: source zone not specified" << std::endl;
9211 return EINVAL;
9212 }
7c673cae 9213
94b18763 9214 RGWSyncModuleInstanceRef sync_module;
1e59de90
TL
9215 int ret = static_cast<rgw::sal::RadosStore*>(driver)->svc()->sync_modules->get_manager()->create_instance(dpp(), g_ceph_context, static_cast<rgw::sal::RadosStore*>(driver)->svc()->zone->get_zone().tier_type,
9216 static_cast<rgw::sal::RadosStore*>(driver)->svc()->zone->get_zone_params().tier_config, &sync_module);
94b18763 9217 if (ret < 0) {
b3b6e05e 9218 ldpp_dout(dpp(), -1) << "ERROR: failed to init sync module instance, ret=" << ret << dendl;
94b18763
FG
9219 return ret;
9220 }
9221
1e59de90 9222 RGWDataSyncStatusManager sync(static_cast<rgw::sal::RadosStore*>(driver), static_cast<rgw::sal::RadosStore*>(driver)->svc()->rados->get_async_processor(), source_zone, nullptr, sync_module);
94b18763 9223
b3b6e05e 9224 ret = sync.init(dpp());
7c673cae
FG
9225 if (ret < 0) {
9226 cerr << "ERROR: sync.init() returned ret=" << ret << std::endl;
9227 return -ret;
9228 }
9229
b3b6e05e 9230 ret = sync.run(dpp());
7c673cae
FG
9231 if (ret < 0) {
9232 cerr << "ERROR: sync.run() returned ret=" << ret << std::endl;
9233 return -ret;
9234 }
9235 }
9236
9f95a23c 9237 if (opt_cmd == OPT::BUCKET_SYNC_INIT) {
7c673cae
FG
9238 if (source_zone.empty()) {
9239 cerr << "ERROR: source zone not specified" << std::endl;
9240 return EINVAL;
9241 }
9242 if (bucket_name.empty()) {
9243 cerr << "ERROR: bucket not specified" << std::endl;
9244 return EINVAL;
9245 }
20effc67 9246 int ret = init_bucket_for_sync(user.get(), tenant, bucket_name, bucket_id, &bucket);
7c673cae
FG
9247 if (ret < 0) {
9248 return -ret;
9249 }
9f95a23c
TL
9250 auto opt_sb = opt_source_bucket;
9251 if (opt_sb && opt_sb->bucket_id.empty()) {
9252 string sbid;
20effc67
TL
9253 std::unique_ptr<rgw::sal::Bucket> sbuck;
9254 int ret = init_bucket_for_sync(user.get(), opt_sb->tenant, opt_sb->name, sbid, &sbuck);
9f95a23c
TL
9255 if (ret < 0) {
9256 return -ret;
9257 }
20effc67 9258 opt_sb = sbuck->get_key();
9f95a23c
TL
9259 }
9260
1e59de90
TL
9261 auto sync = RGWBucketPipeSyncStatusManager::construct(
9262 dpp(), static_cast<rgw::sal::RadosStore*>(driver), source_zone, opt_sb,
9263 bucket->get_key(), extra_info ? &std::cout : nullptr);
7c673cae 9264
1e59de90
TL
9265 if (!sync) {
9266 cerr << "ERROR: sync.init() returned error=" << sync.error() << std::endl;
9267 return -sync.error();
7c673cae 9268 }
1e59de90 9269 ret = (*sync)->init_sync_status(dpp());
7c673cae
FG
9270 if (ret < 0) {
9271 cerr << "ERROR: sync.init_sync_status() returned ret=" << ret << std::endl;
9272 return -ret;
9273 }
9274 }
9275
f67539c2
TL
9276 if (opt_cmd == OPT::BUCKET_SYNC_CHECKPOINT) {
9277 std::optional<rgw_zone_id> opt_source_zone;
9278 if (!source_zone.empty()) {
9279 opt_source_zone = source_zone;
9280 }
9281 if (bucket_name.empty()) {
9282 cerr << "ERROR: bucket not specified" << std::endl;
9283 return EINVAL;
9284 }
20effc67 9285 int ret = init_bucket(user.get(), tenant, bucket_name, bucket_id, &bucket);
f67539c2
TL
9286 if (ret < 0) {
9287 return -ret;
9288 }
9289
1e59de90 9290 if (!static_cast<rgw::sal::RadosStore*>(driver)->ctl()->bucket->bucket_imports_data(bucket->get_key(), null_yield, dpp())) {
f67539c2
TL
9291 std::cout << "Sync is disabled for bucket " << bucket_name << std::endl;
9292 return 0;
9293 }
9294
9295 RGWBucketSyncPolicyHandlerRef handler;
1e59de90 9296 ret = driver->get_sync_policy_handler(dpp(), std::nullopt, bucket->get_key(), &handler, null_yield);
f67539c2
TL
9297 if (ret < 0) {
9298 std::cerr << "ERROR: failed to get policy handler for bucket ("
20effc67 9299 << bucket << "): r=" << ret << ": " << cpp_strerror(-ret) << std::endl;
f67539c2
TL
9300 return -ret;
9301 }
9302
9303 auto timeout_at = ceph::coarse_mono_clock::now() + opt_timeout_sec;
1e59de90 9304 ret = rgw_bucket_sync_checkpoint(dpp(), static_cast<rgw::sal::RadosStore*>(driver), *handler, bucket->get_info(),
f67539c2
TL
9305 opt_source_zone, opt_source_bucket,
9306 opt_retry_delay_ms, timeout_at);
9307 if (ret < 0) {
b3b6e05e 9308 ldpp_dout(dpp(), -1) << "bucket sync checkpoint failed: " << cpp_strerror(ret) << dendl;
f67539c2
TL
9309 return -ret;
9310 }
9311 }
9312
9f95a23c 9313 if ((opt_cmd == OPT::BUCKET_SYNC_DISABLE) || (opt_cmd == OPT::BUCKET_SYNC_ENABLE)) {
c07f9fc5
FG
9314 if (bucket_name.empty()) {
9315 cerr << "ERROR: bucket not specified" << std::endl;
9316 return EINVAL;
9f95a23c
TL
9317 }
9318 if (opt_cmd == OPT::BUCKET_SYNC_DISABLE) {
9319 bucket_op.set_sync_bucket(false);
9320 } else {
9321 bucket_op.set_sync_bucket(true);
c07f9fc5 9322 }
9f95a23c
TL
9323 bucket_op.set_tenant(tenant);
9324 string err_msg;
1e59de90 9325 ret = RGWBucketAdminOp::sync_bucket(driver, bucket_op, dpp(), &err_msg);
c07f9fc5 9326 if (ret < 0) {
9f95a23c
TL
9327 cerr << err_msg << std::endl;
9328 return -ret;
c07f9fc5 9329 }
9f95a23c 9330 }
c07f9fc5 9331
9f95a23c
TL
9332 if (opt_cmd == OPT::BUCKET_SYNC_INFO) {
9333 if (bucket_name.empty()) {
9334 cerr << "ERROR: bucket not specified" << std::endl;
c07f9fc5
FG
9335 return EINVAL;
9336 }
20effc67 9337 int ret = init_bucket(user.get(), tenant, bucket_name, bucket_id, &bucket);
9f95a23c 9338 if (ret < 0) {
c07f9fc5 9339 return -ret;
9f95a23c 9340 }
1e59de90 9341 bucket_sync_info(driver, bucket->get_info(), std::cout);
28e407b8 9342 }
c07f9fc5 9343
9f95a23c 9344 if (opt_cmd == OPT::BUCKET_SYNC_STATUS) {
28e407b8
AA
9345 if (bucket_name.empty()) {
9346 cerr << "ERROR: bucket not specified" << std::endl;
9347 return EINVAL;
9348 }
20effc67 9349 int ret = init_bucket(user.get(), tenant, bucket_name, bucket_id, &bucket);
28e407b8
AA
9350 if (ret < 0) {
9351 return -ret;
9352 }
1e59de90 9353 bucket_sync_status(driver, bucket->get_info(), source_zone, opt_source_bucket, std::cout);
28e407b8
AA
9354 }
9355
9f95a23c 9356 if (opt_cmd == OPT::BUCKET_SYNC_MARKERS) {
7c673cae
FG
9357 if (source_zone.empty()) {
9358 cerr << "ERROR: source zone not specified" << std::endl;
9359 return EINVAL;
9360 }
9361 if (bucket_name.empty()) {
9362 cerr << "ERROR: bucket not specified" << std::endl;
9363 return EINVAL;
9364 }
20effc67 9365 int ret = init_bucket_for_sync(user.get(), tenant, bucket_name, bucket_id, &bucket);
7c673cae
FG
9366 if (ret < 0) {
9367 return -ret;
9368 }
1e59de90
TL
9369 auto sync = RGWBucketPipeSyncStatusManager::construct(
9370 dpp(), static_cast<rgw::sal::RadosStore*>(driver), source_zone,
9371 opt_source_bucket, bucket->get_key(), nullptr);
7c673cae 9372
1e59de90
TL
9373 if (!sync) {
9374 cerr << "ERROR: sync.init() returned error=" << sync.error() << std::endl;
9375 return -sync.error();
7c673cae
FG
9376 }
9377
1e59de90
TL
9378 auto sync_status = (*sync)->read_sync_status(dpp());
9379 if (!sync_status) {
9380 cerr << "ERROR: sync.read_sync_status() returned error="
9381 << sync_status.error() << std::endl;
9382 return -sync_status.error();
9383 }
7c673cae 9384
1e59de90 9385 encode_json("sync_status", *sync_status, formatter.get());
7c673cae
FG
9386 formatter->flush(cout);
9387 }
9388
1e59de90 9389 if (opt_cmd == OPT::BUCKET_SYNC_RUN) {
7c673cae
FG
9390 if (source_zone.empty()) {
9391 cerr << "ERROR: source zone not specified" << std::endl;
9392 return EINVAL;
9393 }
9394 if (bucket_name.empty()) {
9395 cerr << "ERROR: bucket not specified" << std::endl;
9396 return EINVAL;
9397 }
20effc67 9398 int ret = init_bucket_for_sync(user.get(), tenant, bucket_name, bucket_id, &bucket);
7c673cae
FG
9399 if (ret < 0) {
9400 return -ret;
9401 }
1e59de90
TL
9402 auto sync = RGWBucketPipeSyncStatusManager::construct(
9403 dpp(), static_cast<rgw::sal::RadosStore*>(driver), source_zone,
9404 opt_source_bucket, bucket->get_key(), extra_info ? &std::cout : nullptr);
7c673cae 9405
1e59de90
TL
9406 if (!sync) {
9407 cerr << "ERROR: sync.init() returned error=" << sync.error() << std::endl;
9408 return -sync.error();
7c673cae
FG
9409 }
9410
1e59de90 9411 ret = (*sync)->run(dpp());
7c673cae
FG
9412 if (ret < 0) {
9413 cerr << "ERROR: sync.run() returned ret=" << ret << std::endl;
9414 return -ret;
9415 }
9416 }
9417
9f95a23c 9418 if (opt_cmd == OPT::BILOG_LIST) {
7c673cae
FG
9419 if (bucket_name.empty()) {
9420 cerr << "ERROR: bucket not specified" << std::endl;
9421 return EINVAL;
9422 }
20effc67 9423 int ret = init_bucket(user.get(), tenant, bucket_name, bucket_id, &bucket);
7c673cae
FG
9424 if (ret < 0) {
9425 cerr << "ERROR: could not init bucket: " << cpp_strerror(-ret) << std::endl;
9426 return -ret;
9427 }
9428 formatter->open_array_section("entries");
9429 bool truncated;
9430 int count = 0;
9431 if (max_entries < 0)
9432 max_entries = 1000;
9433
1e59de90
TL
9434 const auto& logs = bucket->get_info().layout.logs;
9435 auto log_layout = std::reference_wrapper{logs.back()};
9436 if (gen) {
9437 auto i = std::find_if(logs.begin(), logs.end(), rgw::matches_gen(*gen));
9438 if (i == logs.end()) {
9439 cerr << "ERROR: no log layout with gen=" << *gen << std::endl;
9440 return ENOENT;
9441 }
9442 log_layout = *i;
9443 }
9444
7c673cae
FG
9445 do {
9446 list<rgw_bi_log_entry> entries;
1e59de90 9447 ret = static_cast<rgw::sal::RadosStore*>(driver)->svc()->bilog_rados->log_list(dpp(), bucket->get_info(), log_layout, shard_id, marker, max_entries - count, entries, &truncated);
7c673cae
FG
9448 if (ret < 0) {
9449 cerr << "ERROR: list_bi_log_entries(): " << cpp_strerror(-ret) << std::endl;
9450 return -ret;
9451 }
9452
9453 count += entries.size();
9454
9455 for (list<rgw_bi_log_entry>::iterator iter = entries.begin(); iter != entries.end(); ++iter) {
9456 rgw_bi_log_entry& entry = *iter;
f67539c2 9457 encode_json("entry", entry, formatter.get());
7c673cae
FG
9458
9459 marker = entry.id;
9460 }
9461 formatter->flush(cout);
9462 } while (truncated && count < max_entries);
9463
9464 formatter->close_section();
9465 formatter->flush(cout);
9466 }
9467
9f95a23c 9468 if (opt_cmd == OPT::SYNC_ERROR_LIST) {
7c673cae
FG
9469 if (max_entries < 0) {
9470 max_entries = 1000;
9471 }
f67539c2
TL
9472 if (!start_date.empty()) {
9473 std::cerr << "start-date not allowed." << std::endl;
9474 return -EINVAL;
9475 }
9476 if (!end_date.empty()) {
9477 std::cerr << "end-date not allowed." << std::endl;
9478 return -EINVAL;
9479 }
9480 if (!end_marker.empty()) {
9481 std::cerr << "end-marker not allowed." << std::endl;
9482 return -EINVAL;
9483 }
9484 if (!start_marker.empty()) {
9485 if (marker.empty()) {
9486 marker = start_marker;
9487 } else {
9488 std::cerr << "start-marker and marker not both allowed." << std::endl;
9489 return -EINVAL;
9490 }
9491 }
7c673cae
FG
9492
9493 bool truncated;
7c673cae
FG
9494
9495 if (shard_id < 0) {
9496 shard_id = 0;
9497 }
9498
9499 formatter->open_array_section("entries");
9500
9501 for (; shard_id < ERROR_LOGGER_SHARDS; ++shard_id) {
9502 formatter->open_object_section("shard");
f67539c2 9503 encode_json("shard_id", shard_id, formatter.get());
7c673cae
FG
9504 formatter->open_array_section("entries");
9505
9506 int count = 0;
9507 string oid = RGWSyncErrorLogger::get_shard_oid(RGW_SYNC_ERROR_LOG_SHARD_PREFIX, shard_id);
9508
9509 do {
9510 list<cls_log_entry> entries;
1e59de90 9511 ret = static_cast<rgw::sal::RadosStore*>(driver)->svc()->cls->timelog.list(dpp(), oid, {}, {}, max_entries - count, entries, marker, &marker, &truncated,
f67539c2
TL
9512 null_yield);
9513 if (ret == -ENOENT) {
9514 break;
7c673cae
FG
9515 }
9516 if (ret < 0) {
9f95a23c 9517 cerr << "ERROR: svc.cls->timelog.list(): " << cpp_strerror(-ret) << std::endl;
7c673cae
FG
9518 return -ret;
9519 }
9520
9521 count += entries.size();
9522
9523 for (auto& cls_entry : entries) {
9524 rgw_sync_error_info log_entry;
9525
11fdf7f2 9526 auto iter = cls_entry.data.cbegin();
7c673cae 9527 try {
11fdf7f2 9528 decode(log_entry, iter);
7c673cae
FG
9529 } catch (buffer::error& err) {
9530 cerr << "ERROR: failed to decode log entry" << std::endl;
9531 continue;
9532 }
9533 formatter->open_object_section("entry");
f67539c2
TL
9534 encode_json("id", cls_entry.id, formatter.get());
9535 encode_json("section", cls_entry.section, formatter.get());
9536 encode_json("name", cls_entry.name, formatter.get());
9537 encode_json("timestamp", cls_entry.timestamp, formatter.get());
9538 encode_json("info", log_entry, formatter.get());
7c673cae
FG
9539 formatter->close_section();
9540 formatter->flush(cout);
9541 }
9542 } while (truncated && count < max_entries);
9543
9544 formatter->close_section();
9545 formatter->close_section();
9546
9547 if (specified_shard_id) {
9548 break;
9549 }
9550 }
9551
9552 formatter->close_section();
9553 formatter->flush(cout);
9554 }
9555
9f95a23c 9556 if (opt_cmd == OPT::SYNC_ERROR_TRIM) {
f67539c2
TL
9557 if (!start_date.empty()) {
9558 std::cerr << "start-date not allowed." << std::endl;
9559 return -EINVAL;
9560 }
9561 if (!end_date.empty()) {
9562 std::cerr << "end-date not allowed." << std::endl;
9563 return -EINVAL;
9564 }
9565 if (!start_marker.empty()) {
9566 std::cerr << "end-date not allowed." << std::endl;
9567 return -EINVAL;
9568 }
9569 if (!end_marker.empty()) {
9570 std::cerr << "end-date not allowed." << std::endl;
9571 return -EINVAL;
9572 }
94b18763
FG
9573
9574 if (shard_id < 0) {
9575 shard_id = 0;
9576 }
9577
9578 for (; shard_id < ERROR_LOGGER_SHARDS; ++shard_id) {
f67539c2 9579 ret = trim_sync_error_log(shard_id, marker, trim_delay_ms);
91327a77 9580 if (ret < 0) {
94b18763
FG
9581 cerr << "ERROR: sync error trim: " << cpp_strerror(-ret) << std::endl;
9582 return -ret;
9583 }
9584 if (specified_shard_id) {
9585 break;
9586 }
9587 }
9588 }
9589
9f95a23c
TL
9590 if (opt_cmd == OPT::SYNC_GROUP_CREATE ||
9591 opt_cmd == OPT::SYNC_GROUP_MODIFY) {
aee94f69 9592 CHECK_TRUE(require_non_empty_opt(opt_group_id), "ERROR: --group-id not specified", EINVAL);
9f95a23c
TL
9593 CHECK_TRUE(require_opt(opt_status), "ERROR: --status is not specified (options: forbidden, allowed, enabled)", EINVAL);
9594
1e59de90
TL
9595 SyncPolicyContext sync_policy_ctx(cfgstore.get(), opt_bucket);
9596 ret = sync_policy_ctx.init(zonegroup_id, zonegroup_name);
9f95a23c
TL
9597 if (ret < 0) {
9598 return -ret;
9599 }
9600 auto& sync_policy = sync_policy_ctx.get_policy();
9601
9602 if (opt_cmd == OPT::SYNC_GROUP_MODIFY) {
9603 auto iter = sync_policy.groups.find(*opt_group_id);
9604 if (iter == sync_policy.groups.end()) {
9605 cerr << "ERROR: could not find group '" << *opt_group_id << "'" << std::endl;
9606 return ENOENT;
9607 }
9608 }
9609
9610 auto& group = sync_policy.groups[*opt_group_id];
9611 group.id = *opt_group_id;
9612
9613 if (opt_status) {
9614 if (!group.set_status(*opt_status)) {
9615 cerr << "ERROR: unrecognized status (options: forbidden, allowed, enabled)" << std::endl;
9616 return EINVAL;
9617 }
9618 }
9619
9620 ret = sync_policy_ctx.write_policy();
9621 if (ret < 0) {
9622 return -ret;
9623 }
9624
f67539c2 9625 show_result(sync_policy, zone_formatter.get(), cout);
9f95a23c
TL
9626 }
9627
9628 if (opt_cmd == OPT::SYNC_GROUP_GET) {
1e59de90
TL
9629 SyncPolicyContext sync_policy_ctx(cfgstore.get(), opt_bucket);
9630 ret = sync_policy_ctx.init(zonegroup_id, zonegroup_name);
9f95a23c
TL
9631 if (ret < 0) {
9632 return -ret;
9633 }
9634 auto& sync_policy = sync_policy_ctx.get_policy();
9635
9636 auto& groups = sync_policy.groups;
9637
9638 if (!opt_group_id) {
f67539c2 9639 show_result(groups, zone_formatter.get(), cout);
9f95a23c
TL
9640 } else {
9641 auto iter = sync_policy.groups.find(*opt_group_id);
9642 if (iter == sync_policy.groups.end()) {
9643 cerr << "ERROR: could not find group '" << *opt_group_id << "'" << std::endl;
9644 return ENOENT;
9645 }
9646
f67539c2 9647 show_result(iter->second, zone_formatter.get(), cout);
9f95a23c
TL
9648 }
9649 }
9650
9651 if (opt_cmd == OPT::SYNC_GROUP_REMOVE) {
aee94f69 9652 CHECK_TRUE(require_non_empty_opt(opt_group_id), "ERROR: --group-id not specified", EINVAL);
9f95a23c 9653
1e59de90
TL
9654 SyncPolicyContext sync_policy_ctx(cfgstore.get(), opt_bucket);
9655 ret = sync_policy_ctx.init(zonegroup_id, zonegroup_name);
9f95a23c
TL
9656 if (ret < 0) {
9657 return -ret;
9658 }
9659 auto& sync_policy = sync_policy_ctx.get_policy();
9660
9661 sync_policy.groups.erase(*opt_group_id);
9662
9663 ret = sync_policy_ctx.write_policy();
9664 if (ret < 0) {
9665 return -ret;
9666 }
9667
9668 {
f67539c2
TL
9669 Formatter::ObjectSection os(*zone_formatter.get(), "result");
9670 encode_json("sync_policy", sync_policy, zone_formatter.get());
9f95a23c
TL
9671 }
9672
9673 zone_formatter->flush(cout);
9674 }
9675
9676 if (opt_cmd == OPT::SYNC_GROUP_FLOW_CREATE) {
aee94f69
TL
9677 CHECK_TRUE(require_non_empty_opt(opt_group_id), "ERROR: --group-id not specified", EINVAL);
9678 CHECK_TRUE(require_non_empty_opt(opt_flow_id), "ERROR: --flow-id not specified", EINVAL);
2a845540
TL
9679 CHECK_TRUE(require_opt(opt_flow_type),
9680 "ERROR: --flow-type not specified (options: symmetrical, directional)", EINVAL);
9681 CHECK_TRUE((symmetrical_flow_opt(*opt_flow_type) ||
9682 directional_flow_opt(*opt_flow_type)),
9683 "ERROR: --flow-type invalid (options: symmetrical, directional)", EINVAL);
9f95a23c 9684
1e59de90
TL
9685 SyncPolicyContext sync_policy_ctx(cfgstore.get(), opt_bucket);
9686 ret = sync_policy_ctx.init(zonegroup_id, zonegroup_name);
9f95a23c
TL
9687 if (ret < 0) {
9688 return -ret;
9689 }
9690 auto& sync_policy = sync_policy_ctx.get_policy();
9691
9692 auto iter = sync_policy.groups.find(*opt_group_id);
9693 if (iter == sync_policy.groups.end()) {
9694 cerr << "ERROR: could not find group '" << *opt_group_id << "'" << std::endl;
9695 return ENOENT;
9696 }
9697
9698 auto& group = iter->second;
9699
9700 if (symmetrical_flow_opt(*opt_flow_type)) {
9701 CHECK_TRUE(require_non_empty_opt(opt_zone_ids), "ERROR: --zones not provided for symmetrical flow, or is empty", EINVAL);
9702
9703 rgw_sync_symmetric_group *flow_group;
9704
9705 group.data_flow.find_or_create_symmetrical(*opt_flow_id, &flow_group);
9706
9707 for (auto& z : *opt_zone_ids) {
9708 flow_group->zones.insert(z);
9709 }
9710 } else { /* directional */
9711 CHECK_TRUE(require_non_empty_opt(opt_source_zone_id), "ERROR: --source-zone not provided for directional flow rule, or is empty", EINVAL);
9712 CHECK_TRUE(require_non_empty_opt(opt_dest_zone_id), "ERROR: --dest-zone not provided for directional flow rule, or is empty", EINVAL);
9713
9714 rgw_sync_directional_rule *flow_rule;
9715
9716 group.data_flow.find_or_create_directional(*opt_source_zone_id, *opt_dest_zone_id, &flow_rule);
9717 }
9718
9719 ret = sync_policy_ctx.write_policy();
9720 if (ret < 0) {
9721 return -ret;
9722 }
9723
f67539c2 9724 show_result(sync_policy, zone_formatter.get(), cout);
9f95a23c
TL
9725 }
9726
9727 if (opt_cmd == OPT::SYNC_GROUP_FLOW_REMOVE) {
aee94f69
TL
9728 CHECK_TRUE(require_non_empty_opt(opt_group_id), "ERROR: --group-id not specified", EINVAL);
9729 CHECK_TRUE(require_non_empty_opt(opt_flow_id), "ERROR: --flow-id not specified", EINVAL);
2a845540
TL
9730 CHECK_TRUE(require_opt(opt_flow_type),
9731 "ERROR: --flow-type not specified (options: symmetrical, directional)", EINVAL);
9732 CHECK_TRUE((symmetrical_flow_opt(*opt_flow_type) ||
9733 directional_flow_opt(*opt_flow_type)),
9734 "ERROR: --flow-type invalid (options: symmetrical, directional)", EINVAL);
9f95a23c 9735
1e59de90
TL
9736 SyncPolicyContext sync_policy_ctx(cfgstore.get(), opt_bucket);
9737 ret = sync_policy_ctx.init(zonegroup_id, zonegroup_name);
9f95a23c
TL
9738 if (ret < 0) {
9739 return -ret;
9740 }
9741 auto& sync_policy = sync_policy_ctx.get_policy();
9742
9743 auto iter = sync_policy.groups.find(*opt_group_id);
9744 if (iter == sync_policy.groups.end()) {
9745 cerr << "ERROR: could not find group '" << *opt_group_id << "'" << std::endl;
9746 return ENOENT;
9747 }
9748
9749 auto& group = iter->second;
9750
9751 if (symmetrical_flow_opt(*opt_flow_type)) {
9752 group.data_flow.remove_symmetrical(*opt_flow_id, opt_zone_ids);
9753 } else { /* directional */
9754 CHECK_TRUE(require_non_empty_opt(opt_source_zone_id), "ERROR: --source-zone not provided for directional flow rule, or is empty", EINVAL);
9755 CHECK_TRUE(require_non_empty_opt(opt_dest_zone_id), "ERROR: --dest-zone not provided for directional flow rule, or is empty", EINVAL);
9756
9757 group.data_flow.remove_directional(*opt_source_zone_id, *opt_dest_zone_id);
9758 }
9759
9760 ret = sync_policy_ctx.write_policy();
9761 if (ret < 0) {
9762 return -ret;
9763 }
9764
f67539c2 9765 show_result(sync_policy, zone_formatter.get(), cout);
9f95a23c
TL
9766 }
9767
9768 if (opt_cmd == OPT::SYNC_GROUP_PIPE_CREATE ||
9769 opt_cmd == OPT::SYNC_GROUP_PIPE_MODIFY) {
aee94f69
TL
9770 CHECK_TRUE(require_non_empty_opt(opt_group_id), "ERROR: --group-id not specified", EINVAL);
9771 CHECK_TRUE(require_non_empty_opt(opt_pipe_id), "ERROR: --pipe-id not specified", EINVAL);
9f95a23c
TL
9772 if (opt_cmd == OPT::SYNC_GROUP_PIPE_CREATE) {
9773 CHECK_TRUE(require_non_empty_opt(opt_source_zone_ids), "ERROR: --source-zones not provided or is empty; should be list of zones or '*'", EINVAL);
9774 CHECK_TRUE(require_non_empty_opt(opt_dest_zone_ids), "ERROR: --dest-zones not provided or is empty; should be list of zones or '*'", EINVAL);
9775 }
9776
1e59de90
TL
9777 SyncPolicyContext sync_policy_ctx(cfgstore.get(), opt_bucket);
9778 ret = sync_policy_ctx.init(zonegroup_id, zonegroup_name);
9f95a23c
TL
9779 if (ret < 0) {
9780 return -ret;
9781 }
9782 auto& sync_policy = sync_policy_ctx.get_policy();
9783
9784 auto iter = sync_policy.groups.find(*opt_group_id);
9785 if (iter == sync_policy.groups.end()) {
9786 cerr << "ERROR: could not find group '" << *opt_group_id << "'" << std::endl;
9787 return ENOENT;
9788 }
9789
9790 auto& group = iter->second;
9791
9792 rgw_sync_bucket_pipes *pipe;
9793
9794 if (opt_cmd == OPT::SYNC_GROUP_PIPE_CREATE) {
9795 group.find_pipe(*opt_pipe_id, true, &pipe);
9796 } else {
9797 if (!group.find_pipe(*opt_pipe_id, false, &pipe)) {
9798 cerr << "ERROR: could not find pipe '" << *opt_pipe_id << "'" << std::endl;
9799 return ENOENT;
9800 }
9801 }
9802
1e59de90
TL
9803 if (opt_source_zone_ids) {
9804 pipe->source.add_zones(*opt_source_zone_ids);
9805 }
9f95a23c
TL
9806 pipe->source.set_bucket(opt_source_tenant,
9807 opt_source_bucket_name,
9808 opt_source_bucket_id);
1e59de90
TL
9809 if (opt_dest_zone_ids) {
9810 pipe->dest.add_zones(*opt_dest_zone_ids);
9811 }
9f95a23c
TL
9812 pipe->dest.set_bucket(opt_dest_tenant,
9813 opt_dest_bucket_name,
9814 opt_dest_bucket_id);
9815
9816 pipe->params.source.filter.set_prefix(opt_prefix, !!opt_prefix_rm);
9817 pipe->params.source.filter.set_tags(tags_add, tags_rm);
9818 if (opt_dest_owner) {
9819 pipe->params.dest.set_owner(*opt_dest_owner);
9820 }
9821 if (opt_storage_class) {
9822 pipe->params.dest.set_storage_class(*opt_storage_class);
9823 }
9824 if (opt_priority) {
9825 pipe->params.priority = *opt_priority;
9826 }
9827 if (opt_mode) {
9828 if (*opt_mode == "system") {
9829 pipe->params.mode = rgw_sync_pipe_params::MODE_SYSTEM;
9830 } else if (*opt_mode == "user") {
9831 pipe->params.mode = rgw_sync_pipe_params::MODE_USER;
9832 } else {
9833 cerr << "ERROR: bad mode value: should be one of the following: system, user" << std::endl;
9834 return EINVAL;
9835 }
9836 }
9837
20effc67
TL
9838 if (!rgw::sal::User::empty(user)) {
9839 pipe->params.user = user->get_id();
9f95a23c
TL
9840 } else if (pipe->params.user.empty()) {
9841 auto owner = sync_policy_ctx.get_owner();
9842 if (owner) {
9843 pipe->params.user = *owner;
9844 }
9845 }
9846
9847 ret = sync_policy_ctx.write_policy();
9848 if (ret < 0) {
9849 return -ret;
9850 }
9851
f67539c2 9852 show_result(sync_policy, zone_formatter.get(), cout);
9f95a23c
TL
9853 }
9854
9855 if (opt_cmd == OPT::SYNC_GROUP_PIPE_REMOVE) {
aee94f69
TL
9856 CHECK_TRUE(require_non_empty_opt(opt_group_id), "ERROR: --group-id not specified", EINVAL);
9857 CHECK_TRUE(require_non_empty_opt(opt_pipe_id), "ERROR: --pipe-id not specified", EINVAL);
9f95a23c 9858
1e59de90
TL
9859 SyncPolicyContext sync_policy_ctx(cfgstore.get(), opt_bucket);
9860 ret = sync_policy_ctx.init(zonegroup_id, zonegroup_name);
9f95a23c
TL
9861 if (ret < 0) {
9862 return -ret;
9863 }
9864 auto& sync_policy = sync_policy_ctx.get_policy();
9865
9866 auto iter = sync_policy.groups.find(*opt_group_id);
9867 if (iter == sync_policy.groups.end()) {
9868 cerr << "ERROR: could not find group '" << *opt_group_id << "'" << std::endl;
9869 return ENOENT;
9870 }
9871
9872 auto& group = iter->second;
9873
9874 rgw_sync_bucket_pipes *pipe;
9875
9876 if (!group.find_pipe(*opt_pipe_id, false, &pipe)) {
9877 cerr << "ERROR: could not find pipe '" << *opt_pipe_id << "'" << std::endl;
9878 return ENOENT;
9879 }
9880
9881 if (opt_source_zone_ids) {
9882 pipe->source.remove_zones(*opt_source_zone_ids);
9883 }
9884
9885 pipe->source.remove_bucket(opt_source_tenant,
9886 opt_source_bucket_name,
9887 opt_source_bucket_id);
9888 if (opt_dest_zone_ids) {
9889 pipe->dest.remove_zones(*opt_dest_zone_ids);
9890 }
9891 pipe->dest.remove_bucket(opt_dest_tenant,
9892 opt_dest_bucket_name,
9893 opt_dest_bucket_id);
9894
9895 if (!(opt_source_zone_ids ||
9896 opt_source_tenant ||
9897 opt_source_bucket ||
9898 opt_source_bucket_id ||
9899 opt_dest_zone_ids ||
9900 opt_dest_tenant ||
9901 opt_dest_bucket ||
9902 opt_dest_bucket_id)) {
9903 group.remove_pipe(*opt_pipe_id);
9904 }
9905
9906 ret = sync_policy_ctx.write_policy();
9907 if (ret < 0) {
9908 return -ret;
9909 }
9910
f67539c2 9911 show_result(sync_policy, zone_formatter.get(), cout);
9f95a23c
TL
9912 }
9913
9914 if (opt_cmd == OPT::SYNC_POLICY_GET) {
1e59de90
TL
9915 SyncPolicyContext sync_policy_ctx(cfgstore.get(), opt_bucket);
9916 ret = sync_policy_ctx.init(zonegroup_id, zonegroup_name);
9f95a23c
TL
9917 if (ret < 0) {
9918 return -ret;
9919 }
9920 auto& sync_policy = sync_policy_ctx.get_policy();
9921
f67539c2 9922 show_result(sync_policy, zone_formatter.get(), cout);
9f95a23c
TL
9923 }
9924
9925 if (opt_cmd == OPT::BILOG_TRIM) {
7c673cae
FG
9926 if (bucket_name.empty()) {
9927 cerr << "ERROR: bucket not specified" << std::endl;
9928 return EINVAL;
9929 }
20effc67 9930 int ret = init_bucket(user.get(), tenant, bucket_name, bucket_id, &bucket);
7c673cae
FG
9931 if (ret < 0) {
9932 cerr << "ERROR: could not init bucket: " << cpp_strerror(-ret) << std::endl;
9933 return -ret;
9934 }
1e59de90
TL
9935
9936 if (!gen) {
9937 gen = 0;
9938 }
9939 ret = bilog_trim(dpp(), static_cast<rgw::sal::RadosStore*>(driver),
9940 bucket->get_info(), *gen,
9941 shard_id, start_marker, end_marker);
7c673cae
FG
9942 if (ret < 0) {
9943 cerr << "ERROR: trim_bi_log_entries(): " << cpp_strerror(-ret) << std::endl;
9944 return -ret;
9945 }
9946 }
9947
9f95a23c 9948 if (opt_cmd == OPT::BILOG_STATUS) {
7c673cae
FG
9949 if (bucket_name.empty()) {
9950 cerr << "ERROR: bucket not specified" << std::endl;
9951 return EINVAL;
9952 }
20effc67 9953 int ret = init_bucket(user.get(), tenant, bucket_name, bucket_id, &bucket);
7c673cae
FG
9954 if (ret < 0) {
9955 cerr << "ERROR: could not init bucket: " << cpp_strerror(-ret) << std::endl;
9956 return -ret;
9957 }
9958 map<int, string> markers;
1e59de90
TL
9959 const auto& logs = bucket->get_info().layout.logs;
9960 auto log_layout = std::reference_wrapper{logs.back()};
9961 if (gen) {
9962 auto i = std::find_if(logs.begin(), logs.end(), rgw::matches_gen(*gen));
9963 if (i == logs.end()) {
9964 cerr << "ERROR: no log layout with gen=" << *gen << std::endl;
9965 return ENOENT;
9966 }
9967 log_layout = *i;
9968 }
9969
9970 ret = static_cast<rgw::sal::RadosStore*>(driver)->svc()->bilog_rados->get_log_status(dpp(), bucket->get_info(), log_layout, shard_id,
f67539c2 9971 &markers, null_yield);
7c673cae 9972 if (ret < 0) {
31f18b77 9973 cerr << "ERROR: get_bi_log_status(): " << cpp_strerror(-ret) << std::endl;
7c673cae
FG
9974 return -ret;
9975 }
9976 formatter->open_object_section("entries");
f67539c2 9977 encode_json("markers", markers, formatter.get());
1e59de90
TL
9978 formatter->dump_string("current_time",
9979 to_iso_8601(ceph::real_clock::now(),
9980 iso_8601_format::YMDhms));
7c673cae
FG
9981 formatter->close_section();
9982 formatter->flush(cout);
9983 }
9984
9f95a23c 9985 if (opt_cmd == OPT::BILOG_AUTOTRIM) {
1e59de90
TL
9986 RGWCoroutinesManager crs(driver->ctx(), driver->get_cr_registry());
9987 RGWHTTPManager http(driver->ctx(), crs.get_completion_mgr());
11fdf7f2 9988 int ret = http.start();
b32b8144
FG
9989 if (ret < 0) {
9990 cerr << "failed to initialize http client with " << cpp_strerror(ret) << std::endl;
9991 return -ret;
9992 }
9993
9994 rgw::BucketTrimConfig config;
1e59de90 9995 configure_bucket_trim(driver->ctx(), config);
b32b8144 9996
1e59de90 9997 rgw::BucketTrimManager trim(static_cast<rgw::sal::RadosStore*>(driver), config);
b32b8144
FG
9998 ret = trim.init();
9999 if (ret < 0) {
10000 cerr << "trim manager init failed with " << cpp_strerror(ret) << std::endl;
10001 return -ret;
10002 }
b3b6e05e 10003 ret = crs.run(dpp(), trim.create_admin_bucket_trim_cr(&http));
b32b8144
FG
10004 if (ret < 0) {
10005 cerr << "automated bilog trim failed with " << cpp_strerror(ret) << std::endl;
10006 return -ret;
10007 }
10008 }
10009
9f95a23c 10010 if (opt_cmd == OPT::DATALOG_LIST) {
7c673cae
FG
10011 formatter->open_array_section("entries");
10012 bool truncated;
10013 int count = 0;
10014 if (max_entries < 0)
10015 max_entries = 1000;
f67539c2
TL
10016 if (!start_date.empty()) {
10017 std::cerr << "start-date not allowed." << std::endl;
10018 return -EINVAL;
10019 }
10020 if (!end_date.empty()) {
10021 std::cerr << "end-date not allowed." << std::endl;
10022 return -EINVAL;
10023 }
10024 if (!end_marker.empty()) {
10025 std::cerr << "end-marker not allowed." << std::endl;
10026 return -EINVAL;
10027 }
10028 if (!start_marker.empty()) {
10029 if (marker.empty()) {
10030 marker = start_marker;
10031 } else {
10032 std::cerr << "start-marker and marker not both allowed." << std::endl;
10033 return -EINVAL;
10034 }
10035 }
7c673cae 10036
1e59de90 10037 auto datalog_svc = static_cast<rgw::sal::RadosStore*>(driver)->svc()->datalog_rados;
11fdf7f2 10038 RGWDataChangesLog::LogMarker log_marker;
7c673cae
FG
10039
10040 do {
f67539c2 10041 std::vector<rgw_data_change_log_entry> entries;
11fdf7f2 10042 if (specified_shard_id) {
b3b6e05e 10043 ret = datalog_svc->list_entries(dpp(), shard_id, max_entries - count,
522d829b 10044 entries, marker,
1e59de90
TL
10045 &marker, &truncated,
10046 null_yield);
11fdf7f2 10047 } else {
b3b6e05e 10048 ret = datalog_svc->list_entries(dpp(), max_entries - count, entries,
1e59de90 10049 log_marker, &truncated, null_yield);
11fdf7f2 10050 }
7c673cae 10051 if (ret < 0) {
f67539c2 10052 cerr << "ERROR: datalog_svc->list_entries(): " << cpp_strerror(-ret) << std::endl;
7c673cae
FG
10053 return -ret;
10054 }
10055
10056 count += entries.size();
10057
f67539c2 10058 for (const auto& entry : entries) {
7c673cae 10059 if (!extra_info) {
f67539c2 10060 encode_json("entry", entry.entry, formatter.get());
7c673cae 10061 } else {
f67539c2 10062 encode_json("entry", entry, formatter.get());
7c673cae
FG
10063 }
10064 }
f67539c2 10065 formatter.get()->flush(cout);
7c673cae
FG
10066 } while (truncated && count < max_entries);
10067
10068 formatter->close_section();
10069 formatter->flush(cout);
10070 }
10071
9f95a23c 10072 if (opt_cmd == OPT::DATALOG_STATUS) {
7c673cae
FG
10073 int i = (specified_shard_id ? shard_id : 0);
10074
10075 formatter->open_array_section("entries");
10076 for (; i < g_ceph_context->_conf->rgw_data_log_num_shards; i++) {
10077 list<cls_log_entry> entries;
10078
10079 RGWDataChangesLogInfo info;
1e59de90
TL
10080 static_cast<rgw::sal::RadosStore*>(driver)->svc()->
10081 datalog_rados->get_info(dpp(), i, &info, null_yield);
7c673cae 10082
f67539c2 10083 ::encode_json("info", info, formatter.get());
7c673cae
FG
10084
10085 if (specified_shard_id)
10086 break;
10087 }
10088
10089 formatter->close_section();
10090 formatter->flush(cout);
10091 }
10092
9f95a23c 10093 if (opt_cmd == OPT::DATALOG_AUTOTRIM) {
1e59de90
TL
10094 RGWCoroutinesManager crs(driver->ctx(), driver->get_cr_registry());
10095 RGWHTTPManager http(driver->ctx(), crs.get_completion_mgr());
11fdf7f2 10096 int ret = http.start();
a8e16298
TL
10097 if (ret < 0) {
10098 cerr << "failed to initialize http client with " << cpp_strerror(ret) << std::endl;
10099 return -ret;
10100 }
10101
11fdf7f2 10102 auto num_shards = g_conf()->rgw_data_log_num_shards;
a8e16298 10103 std::vector<std::string> markers(num_shards);
1e59de90 10104 ret = crs.run(dpp(), create_admin_data_log_trim_cr(dpp(), static_cast<rgw::sal::RadosStore*>(driver), &http, num_shards, markers));
a8e16298
TL
10105 if (ret < 0) {
10106 cerr << "automated datalog trim failed with " << cpp_strerror(ret) << std::endl;
10107 return -ret;
10108 }
10109 }
10110
9f95a23c 10111 if (opt_cmd == OPT::DATALOG_TRIM) {
f67539c2
TL
10112 if (!start_date.empty()) {
10113 std::cerr << "start-date not allowed." << std::endl;
10114 return -EINVAL;
10115 }
10116 if (!end_date.empty()) {
10117 std::cerr << "end-date not allowed." << std::endl;
10118 return -EINVAL;
10119 }
10120 if (!start_marker.empty()) {
10121 std::cerr << "start-marker not allowed." << std::endl;
10122 return -EINVAL;
10123 }
10124 if (!end_marker.empty()) {
10125 if (marker.empty()) {
10126 marker = end_marker;
10127 } else {
10128 std::cerr << "end-marker and marker not both allowed." << std::endl;
10129 return -EINVAL;
10130 }
10131 }
7c673cae 10132
eafe8130
TL
10133 if (!specified_shard_id) {
10134 cerr << "ERROR: requires a --shard-id" << std::endl;
10135 return EINVAL;
10136 }
10137
522d829b
TL
10138 if (marker.empty()) {
10139 cerr << "ERROR: requires a --marker" << std::endl;
10140 return EINVAL;
10141 }
10142
1e59de90
TL
10143 auto datalog = static_cast<rgw::sal::RadosStore*>(driver)->svc()->datalog_rados;
10144 ret = datalog->trim_entries(dpp(), shard_id, marker, null_yield);
eafe8130
TL
10145
10146 if (ret < 0 && ret != -ENODATA) {
7c673cae
FG
10147 cerr << "ERROR: trim_entries(): " << cpp_strerror(-ret) << std::endl;
10148 return -ret;
10149 }
10150 }
10151
f67539c2
TL
10152 if (opt_cmd == OPT::DATALOG_TYPE) {
10153 if (!opt_log_type) {
10154 std::cerr << "log-type not specified." << std::endl;
10155 return -EINVAL;
10156 }
1e59de90 10157 auto datalog = static_cast<rgw::sal::RadosStore*>(driver)->svc()->datalog_rados;
b3b6e05e 10158 ret = datalog->change_format(dpp(), *opt_log_type, null_yield);
f67539c2
TL
10159 if (ret < 0) {
10160 cerr << "ERROR: change_format(): " << cpp_strerror(-ret) << std::endl;
10161 return -ret;
10162 }
10163 }
10164
10165 if (opt_cmd == OPT::DATALOG_PRUNE) {
1e59de90 10166 auto datalog = static_cast<rgw::sal::RadosStore*>(driver)->svc()->datalog_rados;
f67539c2 10167 std::optional<uint64_t> through;
1e59de90 10168 ret = datalog->trim_generations(dpp(), through, null_yield);
f67539c2
TL
10169
10170 if (ret < 0) {
10171 cerr << "ERROR: trim_generations(): " << cpp_strerror(-ret) << std::endl;
10172 return -ret;
10173 }
10174
10175 if (through) {
10176 std::cout << "Pruned " << *through << " empty generations." << std::endl;
10177 } else {
10178 std::cout << "No empty generations." << std::endl;
10179 }
10180 }
10181
9f95a23c 10182 bool quota_op = (opt_cmd == OPT::QUOTA_SET || opt_cmd == OPT::QUOTA_ENABLE || opt_cmd == OPT::QUOTA_DISABLE);
7c673cae 10183
11fdf7f2 10184 if (quota_op) {
20effc67 10185 if (bucket_name.empty() && rgw::sal::User::empty(user)) {
11fdf7f2
TL
10186 cerr << "ERROR: bucket name or uid is required for quota operation" << std::endl;
10187 return EINVAL;
10188 }
7c673cae 10189
11fdf7f2
TL
10190 if (!bucket_name.empty()) {
10191 if (!quota_scope.empty() && quota_scope != "bucket") {
10192 cerr << "ERROR: invalid quota scope specification." << std::endl;
10193 return EINVAL;
7c673cae 10194 }
1e59de90 10195 set_bucket_quota(driver, opt_cmd, tenant, bucket_name,
11fdf7f2 10196 max_size, max_objects, have_max_size, have_max_objects);
20effc67 10197 } else if (!rgw::sal::User::empty(user)) {
11fdf7f2 10198 if (quota_scope == "bucket") {
20effc67 10199 return set_user_bucket_quota(opt_cmd, ruser, user_op, max_size, max_objects, have_max_size, have_max_objects);
11fdf7f2 10200 } else if (quota_scope == "user") {
20effc67 10201 return set_user_quota(opt_cmd, ruser, user_op, max_size, max_objects, have_max_size, have_max_objects);
11fdf7f2
TL
10202 } else {
10203 cerr << "ERROR: invalid quota scope specification. Please specify either --quota-scope=bucket, or --quota-scope=user" << std::endl;
10204 return EINVAL;
10205 }
10206 }
7c673cae
FG
10207 }
10208
20effc67
TL
10209 bool ratelimit_op_set = (opt_cmd == OPT::RATELIMIT_SET || opt_cmd == OPT::RATELIMIT_ENABLE || opt_cmd == OPT::RATELIMIT_DISABLE);
10210 bool ratelimit_op_get = opt_cmd == OPT::RATELIMIT_GET;
10211 if (ratelimit_op_set) {
10212 if (bucket_name.empty() && rgw::sal::User::empty(user)) {
10213 cerr << "ERROR: bucket name or uid is required for ratelimit operation" << std::endl;
10214 return EINVAL;
10215 }
10216
10217 if (!bucket_name.empty()) {
10218 if (!ratelimit_scope.empty() && ratelimit_scope != "bucket") {
10219 cerr << "ERROR: invalid ratelimit scope specification. (bucket scope is not bucket but bucket has been specified)" << std::endl;
10220 return EINVAL;
10221 }
1e59de90 10222 return set_bucket_ratelimit(driver, opt_cmd, tenant, bucket_name,
20effc67
TL
10223 max_read_ops, max_write_ops,
10224 max_read_bytes, max_write_bytes,
10225 have_max_read_ops, have_max_write_ops,
10226 have_max_read_bytes, have_max_write_bytes);
10227 } else if (!rgw::sal::User::empty(user)) {
10228 } if (ratelimit_scope == "user") {
10229 return set_user_ratelimit(opt_cmd, user, max_read_ops, max_write_ops,
10230 max_read_bytes, max_write_bytes,
10231 have_max_read_ops, have_max_write_ops,
10232 have_max_read_bytes, have_max_write_bytes);
10233 } else {
10234 cerr << "ERROR: invalid ratelimit scope specification. Please specify either --ratelimit-scope=bucket, or --ratelimit-scope=user" << std::endl;
10235 return EINVAL;
10236 }
10237 }
10238
10239 if (ratelimit_op_get) {
10240 if (bucket_name.empty() && rgw::sal::User::empty(user)) {
10241 cerr << "ERROR: bucket name or uid is required for ratelimit operation" << std::endl;
10242 return EINVAL;
10243 }
10244
10245 if (!bucket_name.empty()) {
10246 if (!ratelimit_scope.empty() && ratelimit_scope != "bucket") {
10247 cerr << "ERROR: invalid ratelimit scope specification. (bucket scope is not bucket but bucket has been specified)" << std::endl;
10248 return EINVAL;
10249 }
1e59de90 10250 return show_bucket_ratelimit(driver, tenant, bucket_name, formatter.get());
20effc67
TL
10251 } else if (!rgw::sal::User::empty(user)) {
10252 } if (ratelimit_scope == "user") {
10253 return show_user_ratelimit(user, formatter.get());
10254 } else {
10255 cerr << "ERROR: invalid ratelimit scope specification. Please specify either --ratelimit-scope=bucket, or --ratelimit-scope=user" << std::endl;
10256 return EINVAL;
10257 }
10258 }
10259
9f95a23c 10260 if (opt_cmd == OPT::MFA_CREATE) {
11fdf7f2
TL
10261 rados::cls::otp::otp_info_t config;
10262
20effc67 10263 if (rgw::sal::User::empty(user)) {
11fdf7f2
TL
10264 cerr << "ERROR: user id was not provided (via --uid)" << std::endl;
10265 return EINVAL;
10266 }
10267
10268 if (totp_serial.empty()) {
10269 cerr << "ERROR: TOTP device serial number was not provided (via --totp-serial)" << std::endl;
10270 return EINVAL;
10271 }
7c673cae 10272
11fdf7f2
TL
10273 if (totp_seed.empty()) {
10274 cerr << "ERROR: TOTP device seed was not provided (via --totp-seed)" << std::endl;
7c673cae
FG
10275 return EINVAL;
10276 }
11fdf7f2
TL
10277
10278
10279 rados::cls::otp::SeedType seed_type;
10280 if (totp_seed_type == "hex") {
10281 seed_type = rados::cls::otp::OTP_SEED_HEX;
10282 } else if (totp_seed_type == "base32") {
10283 seed_type = rados::cls::otp::OTP_SEED_BASE32;
10284 } else {
10285 cerr << "ERROR: invalid seed type: " << totp_seed_type << std::endl;
7c673cae
FG
10286 return EINVAL;
10287 }
11fdf7f2
TL
10288
10289 config.id = totp_serial;
10290 config.seed = totp_seed;
10291 config.seed_type = seed_type;
10292
10293 if (totp_seconds > 0) {
10294 config.step_size = totp_seconds;
10295 }
10296
10297 if (totp_window > 0) {
10298 config.window = totp_window;
10299 }
10300
10301 real_time mtime = real_clock::now();
1e59de90 10302 string oid = static_cast<rgw::sal::RadosStore*>(driver)->svc()->cls->mfa.get_mfa_oid(user->get_id());
9f95a23c 10303
1e59de90 10304 int ret = static_cast<rgw::sal::RadosStore*>(driver)->ctl()->meta.mgr->mutate(RGWSI_MetaBackend_OTP::get_meta_key(user->get_id()),
9f95a23c 10305 mtime, &objv_tracker,
b3b6e05e 10306 null_yield, dpp(),
9f95a23c
TL
10307 MDLOG_STATUS_WRITE,
10308 [&] {
1e59de90 10309 return static_cast<rgw::sal::RadosStore*>(driver)->svc()->cls->mfa.create_mfa(dpp(), user->get_id(), config, &objv_tracker, mtime, null_yield);
11fdf7f2 10310 });
7c673cae 10311 if (ret < 0) {
11fdf7f2 10312 cerr << "MFA creation failed, error: " << cpp_strerror(-ret) << std::endl;
7c673cae
FG
10313 return -ret;
10314 }
11fdf7f2
TL
10315
10316 RGWUserInfo& user_info = user_op.get_user_info();
10317 user_info.mfa_ids.insert(totp_serial);
10318 user_op.set_mfa_ids(user_info.mfa_ids);
10319 string err;
20effc67 10320 ret = ruser.modify(dpp(), user_op, null_yield, &err);
11fdf7f2
TL
10321 if (ret < 0) {
10322 cerr << "ERROR: failed storing user info, error: " << err << std::endl;
10323 return -ret;
10324 }
10325 }
7c673cae 10326
9f95a23c 10327 if (opt_cmd == OPT::MFA_REMOVE) {
20effc67 10328 if (rgw::sal::User::empty(user)) {
11fdf7f2
TL
10329 cerr << "ERROR: user id was not provided (via --uid)" << std::endl;
10330 return EINVAL;
10331 }
10332
10333 if (totp_serial.empty()) {
10334 cerr << "ERROR: TOTP device serial number was not provided (via --totp-serial)" << std::endl;
10335 return EINVAL;
10336 }
10337
10338 real_time mtime = real_clock::now();
11fdf7f2 10339
1e59de90 10340 int ret = static_cast<rgw::sal::RadosStore*>(driver)->ctl()->meta.mgr->mutate(RGWSI_MetaBackend_OTP::get_meta_key(user->get_id()),
9f95a23c 10341 mtime, &objv_tracker,
b3b6e05e 10342 null_yield, dpp(),
9f95a23c
TL
10343 MDLOG_STATUS_WRITE,
10344 [&] {
1e59de90 10345 return static_cast<rgw::sal::RadosStore*>(driver)->svc()->cls->mfa.remove_mfa(dpp(), user->get_id(), totp_serial, &objv_tracker, mtime, null_yield);
11fdf7f2
TL
10346 });
10347 if (ret < 0) {
10348 cerr << "MFA removal failed, error: " << cpp_strerror(-ret) << std::endl;
10349 return -ret;
10350 }
10351
10352 RGWUserInfo& user_info = user_op.get_user_info();
10353 user_info.mfa_ids.erase(totp_serial);
10354 user_op.set_mfa_ids(user_info.mfa_ids);
10355 string err;
20effc67 10356 ret = ruser.modify(dpp(), user_op, null_yield, &err);
11fdf7f2
TL
10357 if (ret < 0) {
10358 cerr << "ERROR: failed storing user info, error: " << err << std::endl;
10359 return -ret;
10360 }
10361 }
10362
9f95a23c 10363 if (opt_cmd == OPT::MFA_GET) {
20effc67 10364 if (rgw::sal::User::empty(user)) {
11fdf7f2
TL
10365 cerr << "ERROR: user id was not provided (via --uid)" << std::endl;
10366 return EINVAL;
10367 }
10368
10369 if (totp_serial.empty()) {
10370 cerr << "ERROR: TOTP device serial number was not provided (via --totp-serial)" << std::endl;
10371 return EINVAL;
10372 }
10373
10374 rados::cls::otp::otp_info_t result;
1e59de90 10375 int ret = static_cast<rgw::sal::RadosStore*>(driver)->svc()->cls->mfa.get_mfa(dpp(), user->get_id(), totp_serial, &result, null_yield);
11fdf7f2
TL
10376 if (ret < 0) {
10377 if (ret == -ENOENT || ret == -ENODATA) {
10378 cerr << "MFA serial id not found" << std::endl;
10379 } else {
10380 cerr << "MFA retrieval failed, error: " << cpp_strerror(-ret) << std::endl;
7c673cae 10381 }
11fdf7f2 10382 return -ret;
7c673cae 10383 }
11fdf7f2 10384 formatter->open_object_section("result");
f67539c2 10385 encode_json("entry", result, formatter.get());
11fdf7f2
TL
10386 formatter->close_section();
10387 formatter->flush(cout);
7c673cae 10388 }
7c673cae 10389
9f95a23c 10390 if (opt_cmd == OPT::MFA_LIST) {
20effc67 10391 if (rgw::sal::User::empty(user)) {
11fdf7f2 10392 cerr << "ERROR: user id was not provided (via --uid)" << std::endl;
7c673cae
FG
10393 return EINVAL;
10394 }
11fdf7f2
TL
10395
10396 list<rados::cls::otp::otp_info_t> result;
1e59de90
TL
10397 int ret = static_cast<rgw::sal::RadosStore*>(driver)->svc()->cls->mfa.list_mfa(dpp(), user->get_id(), &result, null_yield);
10398 if (ret < 0 && ret != -ENOENT) {
11fdf7f2 10399 cerr << "MFA listing failed, error: " << cpp_strerror(-ret) << std::endl;
7c673cae
FG
10400 return -ret;
10401 }
11fdf7f2 10402 formatter->open_object_section("result");
f67539c2 10403 encode_json("entries", result, formatter.get());
11fdf7f2
TL
10404 formatter->close_section();
10405 formatter->flush(cout);
7c673cae
FG
10406 }
10407
9f95a23c 10408 if (opt_cmd == OPT::MFA_CHECK) {
20effc67 10409 if (rgw::sal::User::empty(user)) {
11fdf7f2
TL
10410 cerr << "ERROR: user id was not provided (via --uid)" << std::endl;
10411 return EINVAL;
10412 }
10413
10414 if (totp_serial.empty()) {
10415 cerr << "ERROR: TOTP device serial number was not provided (via --totp-serial)" << std::endl;
10416 return EINVAL;
10417 }
10418
10419 if (totp_pin.empty()) {
10420 cerr << "ERROR: TOTP device serial number was not provided (via --totp-pin)" << std::endl;
7c673cae
FG
10421 return EINVAL;
10422 }
11fdf7f2
TL
10423
10424 list<rados::cls::otp::otp_info_t> result;
1e59de90 10425 int ret = static_cast<rgw::sal::RadosStore*>(driver)->svc()->cls->mfa.check_mfa(dpp(), user->get_id(), totp_serial, totp_pin.front(), null_yield);
11fdf7f2
TL
10426 if (ret < 0) {
10427 cerr << "MFA check failed, error: " << cpp_strerror(-ret) << std::endl;
10428 return -ret;
10429 }
10430
10431 cout << "ok" << std::endl;
7c673cae
FG
10432 }
10433
9f95a23c 10434 if (opt_cmd == OPT::MFA_RESYNC) {
20effc67 10435 if (rgw::sal::User::empty(user)) {
11fdf7f2
TL
10436 cerr << "ERROR: user id was not provided (via --uid)" << std::endl;
10437 return EINVAL;
10438 }
7c673cae 10439
11fdf7f2
TL
10440 if (totp_serial.empty()) {
10441 cerr << "ERROR: TOTP device serial number was not provided (via --totp-serial)" << std::endl;
10442 return EINVAL;
10443 }
10444
10445 if (totp_pin.size() != 2) {
10446 cerr << "ERROR: missing two --totp-pin params (--totp-pin=<first> --totp-pin=<second>)" << std::endl;
522d829b 10447 return EINVAL;
11fdf7f2
TL
10448 }
10449
10450 rados::cls::otp::otp_info_t config;
1e59de90 10451 int ret = static_cast<rgw::sal::RadosStore*>(driver)->svc()->cls->mfa.get_mfa(dpp(), user->get_id(), totp_serial, &config, null_yield);
11fdf7f2
TL
10452 if (ret < 0) {
10453 if (ret == -ENOENT || ret == -ENODATA) {
10454 cerr << "MFA serial id not found" << std::endl;
10455 } else {
10456 cerr << "MFA retrieval failed, error: " << cpp_strerror(-ret) << std::endl;
7c673cae 10457 }
11fdf7f2
TL
10458 return -ret;
10459 }
10460
10461 ceph::real_time now;
10462
1e59de90 10463 ret = static_cast<rgw::sal::RadosStore*>(driver)->svc()->cls->mfa.otp_get_current_time(dpp(), user->get_id(), &now, null_yield);
11fdf7f2
TL
10464 if (ret < 0) {
10465 cerr << "ERROR: failed to fetch current time from osd: " << cpp_strerror(-ret) << std::endl;
10466 return -ret;
10467 }
10468 time_t time_ofs;
10469
1e59de90 10470 ret = scan_totp(driver->ctx(), now, config, totp_pin, &time_ofs);
11fdf7f2
TL
10471 if (ret < 0) {
10472 if (ret == -ENOENT) {
10473 cerr << "failed to resync, TOTP values not found in range" << std::endl;
10474 } else {
10475 cerr << "ERROR: failed to scan for TOTP values: " << cpp_strerror(-ret) << std::endl;
7c673cae 10476 }
11fdf7f2
TL
10477 return -ret;
10478 }
10479
10480 config.time_ofs = time_ofs;
10481
10482 /* now update the backend */
10483 real_time mtime = real_clock::now();
11fdf7f2 10484
1e59de90 10485 ret = static_cast<rgw::sal::RadosStore*>(driver)->ctl()->meta.mgr->mutate(RGWSI_MetaBackend_OTP::get_meta_key(user->get_id()),
9f95a23c 10486 mtime, &objv_tracker,
b3b6e05e 10487 null_yield, dpp(),
9f95a23c
TL
10488 MDLOG_STATUS_WRITE,
10489 [&] {
1e59de90 10490 return static_cast<rgw::sal::RadosStore*>(driver)->svc()->cls->mfa.create_mfa(dpp(), user->get_id(), config, &objv_tracker, mtime, null_yield);
11fdf7f2
TL
10491 });
10492 if (ret < 0) {
10493 cerr << "MFA update failed, error: " << cpp_strerror(-ret) << std::endl;
10494 return -ret;
10495 }
10496
10497 }
10498
9f95a23c 10499 if (opt_cmd == OPT::RESHARD_STALE_INSTANCES_LIST) {
1e59de90 10500 if (!static_cast<rgw::sal::RadosStore*>(driver)->svc()->zone->can_reshard() && !yes_i_really_mean_it) {
11fdf7f2
TL
10501 cerr << "Resharding disabled in a multisite env, stale instances unlikely from resharding" << std::endl;
10502 cerr << "These instances may not be safe to delete." << std::endl;
10503 cerr << "Use --yes-i-really-mean-it to force displaying these instances." << std::endl;
10504 return EINVAL;
10505 }
10506
1e59de90 10507 ret = RGWBucketAdminOp::list_stale_instances(driver, bucket_op, stream_flusher, dpp());
11fdf7f2
TL
10508 if (ret < 0) {
10509 cerr << "ERROR: listing stale instances" << cpp_strerror(-ret) << std::endl;
10510 }
10511 }
10512
9f95a23c 10513 if (opt_cmd == OPT::RESHARD_STALE_INSTANCES_DELETE) {
1e59de90 10514 if (!static_cast<rgw::sal::RadosStore*>(driver)->svc()->zone->can_reshard()) {
11fdf7f2
TL
10515 cerr << "Resharding disabled in a multisite env. Stale instances are not safe to be deleted." << std::endl;
10516 return EINVAL;
10517 }
10518
1e59de90 10519 ret = RGWBucketAdminOp::clear_stale_instances(driver, bucket_op, stream_flusher, dpp());
11fdf7f2
TL
10520 if (ret < 0) {
10521 cerr << "ERROR: deleting stale instances" << cpp_strerror(-ret) << std::endl;
10522 }
10523 }
10524
aee94f69
TL
10525 if (opt_cmd == OPT::PUBSUB_NOTIFICATION_LIST) {
10526 if (bucket_name.empty()) {
10527 cerr << "ERROR: bucket name was not provided (via --bucket)" << std::endl;
10528 return EINVAL;
10529 }
11fdf7f2 10530
1e59de90 10531 RGWPubSub ps(driver, tenant);
11fdf7f2 10532
aee94f69
TL
10533 rgw_pubsub_bucket_topics result;
10534 int ret = init_bucket(user.get(), tenant, bucket_name, bucket_id, &bucket);
10535 if (ret < 0) {
10536 cerr << "ERROR: could not init bucket: " << cpp_strerror(-ret) << std::endl;
10537 return -ret;
10538 }
7c673cae 10539
aee94f69
TL
10540 const RGWPubSub::Bucket b(ps, bucket.get());
10541 ret = b.get_topics(dpp(), result, null_yield);
10542 if (ret < 0 && ret != -ENOENT) {
10543 cerr << "ERROR: could not get topics: " << cpp_strerror(-ret) << std::endl;
10544 return -ret;
11fdf7f2 10545 }
aee94f69
TL
10546 encode_json("result", result, formatter.get());
10547 formatter->flush(cout);
10548 }
10549
10550 if (opt_cmd == OPT::PUBSUB_TOPIC_LIST) {
10551 RGWPubSub ps(driver, tenant);
10552
10553 rgw_pubsub_topics result;
10554 int ret = ps.get_topics(dpp(), result, null_yield);
10555 if (ret < 0 && ret != -ENOENT) {
10556 cerr << "ERROR: could not get topics: " << cpp_strerror(-ret) << std::endl;
10557 return -ret;
10558 }
10559 encode_json("result", result, formatter.get());
11fdf7f2
TL
10560 formatter->flush(cout);
10561 }
7c673cae 10562
9f95a23c 10563 if (opt_cmd == OPT::PUBSUB_TOPIC_GET) {
11fdf7f2
TL
10564 if (topic_name.empty()) {
10565 cerr << "ERROR: topic name was not provided (via --topic)" << std::endl;
10566 return EINVAL;
10567 }
f67539c2 10568
1e59de90 10569 RGWPubSub ps(driver, tenant);
11fdf7f2 10570
1e59de90
TL
10571 rgw_pubsub_topic topic;
10572 ret = ps.get_topic(dpp(), topic_name, topic, null_yield);
7c673cae 10573 if (ret < 0) {
f67539c2 10574 cerr << "ERROR: could not get topic: " << cpp_strerror(-ret) << std::endl;
11fdf7f2
TL
10575 return -ret;
10576 }
f67539c2 10577 encode_json("topic", topic, formatter.get());
11fdf7f2
TL
10578 formatter->flush(cout);
10579 }
10580
aee94f69
TL
10581 if (opt_cmd == OPT::PUBSUB_NOTIFICATION_GET) {
10582 if (notification_id.empty()) {
10583 cerr << "ERROR: notification-id was not provided (via --notification-id)" << std::endl;
10584 return EINVAL;
10585 }
10586 if (bucket_name.empty()) {
10587 cerr << "ERROR: bucket name was not provided (via --bucket)" << std::endl;
10588 return EINVAL;
10589 }
10590
10591 int ret = init_bucket(user.get(), tenant, bucket_name, bucket_id, &bucket);
10592 if (ret < 0) {
10593 cerr << "ERROR: could not init bucket: " << cpp_strerror(-ret) << std::endl;
10594 return -ret;
10595 }
10596
10597 RGWPubSub ps(driver, tenant);
10598
10599 rgw_pubsub_bucket_topics bucket_topics;
10600 const RGWPubSub::Bucket b(ps, bucket.get());
10601 ret = b.get_topics(dpp(), bucket_topics, null_yield);
10602 if (ret < 0 && ret != -ENOENT) {
10603 cerr << "ERROR: could not get bucket notifications: " << cpp_strerror(-ret) << std::endl;
10604 return -ret;
10605 }
10606
10607 rgw_pubsub_topic_filter bucket_topic;
10608 ret = b.get_notification_by_id(dpp(), notification_id, bucket_topic, null_yield);
10609 if (ret < 0) {
10610 cerr << "ERROR: could not get notification: " << cpp_strerror(-ret) << std::endl;
10611 return -ret;
10612 }
10613 encode_json("notification", bucket_topic, formatter.get());
10614 formatter->flush(cout);
10615 }
10616
f67539c2 10617 if (opt_cmd == OPT::PUBSUB_TOPIC_RM) {
11fdf7f2
TL
10618 if (topic_name.empty()) {
10619 cerr << "ERROR: topic name was not provided (via --topic)" << std::endl;
10620 return EINVAL;
10621 }
7c673cae 10622
aee94f69
TL
10623 ret = rgw::notify::remove_persistent_topic(dpp(), static_cast<rgw::sal::RadosStore*>(driver)->getRados()->get_notif_pool_ctx(), topic_name, null_yield);
10624 if (ret < 0) {
10625 cerr << "ERROR: could not remove persistent topic: " << cpp_strerror(-ret) << std::endl;
10626 return -ret;
10627 }
10628
1e59de90 10629 RGWPubSub ps(driver, tenant);
11fdf7f2 10630
b3b6e05e 10631 ret = ps.remove_topic(dpp(), topic_name, null_yield);
11fdf7f2 10632 if (ret < 0) {
f67539c2 10633 cerr << "ERROR: could not remove topic: " << cpp_strerror(-ret) << std::endl;
11fdf7f2 10634 return -ret;
7c673cae
FG
10635 }
10636 }
10637
aee94f69
TL
10638 if (opt_cmd == OPT::PUBSUB_NOTIFICATION_RM) {
10639 if (bucket_name.empty()) {
10640 cerr << "ERROR: bucket name was not provided (via --bucket)" << std::endl;
10641 return EINVAL;
10642 }
10643
10644 int ret = init_bucket(user.get(), tenant, bucket_name, bucket_id, &bucket);
10645 if (ret < 0) {
10646 cerr << "ERROR: could not init bucket: " << cpp_strerror(-ret) << std::endl;
10647 return -ret;
10648 }
10649
10650 RGWPubSub ps(driver, tenant);
10651
10652 rgw_pubsub_bucket_topics bucket_topics;
10653 const RGWPubSub::Bucket b(ps, bucket.get());
10654 ret = b.get_topics(dpp(), bucket_topics, null_yield);
10655 if (ret < 0 && ret != -ENOENT) {
10656 cerr << "ERROR: could not get bucket notifications: " << cpp_strerror(-ret) << std::endl;
10657 return -ret;
10658 }
10659
10660 rgw_pubsub_topic_filter bucket_topic;
10661 if(notification_id.empty()) {
10662 ret = b.remove_notifications(dpp(), null_yield);
10663 } else {
10664 ret = b.remove_notification_by_id(dpp(), notification_id, null_yield);
10665 }
10666 }
10667
f67539c2
TL
10668 if (opt_cmd == OPT::SCRIPT_PUT) {
10669 if (!str_script_ctx) {
10670 cerr << "ERROR: context was not provided (via --context)" << std::endl;
11fdf7f2
TL
10671 return EINVAL;
10672 }
f67539c2
TL
10673 if (infile.empty()) {
10674 cerr << "ERROR: infile was not provided (via --infile)" << std::endl;
11fdf7f2
TL
10675 return EINVAL;
10676 }
f67539c2
TL
10677 bufferlist bl;
10678 auto rc = read_input(infile, bl);
10679 if (rc < 0) {
10680 cerr << "ERROR: failed to read script: '" << infile << "'. error: " << rc << std::endl;
10681 return -rc;
10682 }
10683 const std::string script = bl.to_str();
10684 std::string err_msg;
10685 if (!rgw::lua::verify(script, err_msg)) {
10686 cerr << "ERROR: script: '" << infile << "' has error: " << std::endl << err_msg << std::endl;
11fdf7f2
TL
10687 return EINVAL;
10688 }
f67539c2
TL
10689 const rgw::lua::context script_ctx = rgw::lua::to_context(*str_script_ctx);
10690 if (script_ctx == rgw::lua::context::none) {
1e59de90
TL
10691 cerr << "ERROR: invalid script context: " << *str_script_ctx << ". must be one of: " << LUA_CONTEXT_LIST << std::endl;
10692 return EINVAL;
10693 }
10694 if (script_ctx == rgw::lua::context::background && !tenant.empty()) {
10695 cerr << "ERROR: cannot specify tenant in background context" << std::endl;
f67539c2
TL
10696 return EINVAL;
10697 }
1e59de90
TL
10698 auto lua_manager = driver->get_lua_manager();
10699 rc = rgw::lua::write_script(dpp(), lua_manager.get(), tenant, null_yield, script_ctx, script);
f67539c2
TL
10700 if (rc < 0) {
10701 cerr << "ERROR: failed to put script. error: " << rc << std::endl;
10702 return -rc;
11fdf7f2
TL
10703 }
10704 }
10705
f67539c2
TL
10706 if (opt_cmd == OPT::SCRIPT_GET) {
10707 if (!str_script_ctx) {
10708 cerr << "ERROR: context was not provided (via --context)" << std::endl;
11fdf7f2
TL
10709 return EINVAL;
10710 }
f67539c2
TL
10711 const rgw::lua::context script_ctx = rgw::lua::to_context(*str_script_ctx);
10712 if (script_ctx == rgw::lua::context::none) {
1e59de90 10713 cerr << "ERROR: invalid script context: " << *str_script_ctx << ". must be one of: " << LUA_CONTEXT_LIST << std::endl;
11fdf7f2
TL
10714 return EINVAL;
10715 }
1e59de90 10716 auto lua_manager = driver->get_lua_manager();
f67539c2 10717 std::string script;
1e59de90 10718 const auto rc = rgw::lua::read_script(dpp(), lua_manager.get(), tenant, null_yield, script_ctx, script);
f67539c2
TL
10719 if (rc == -ENOENT) {
10720 std::cout << "no script exists for context: " << *str_script_ctx <<
10721 (tenant.empty() ? "" : (" in tenant: " + tenant)) << std::endl;
10722 } else if (rc < 0) {
10723 cerr << "ERROR: failed to read script. error: " << rc << std::endl;
10724 return -rc;
10725 } else {
10726 std::cout << script << std::endl;
10727 }
10728 }
10729
10730 if (opt_cmd == OPT::SCRIPT_RM) {
10731 if (!str_script_ctx) {
10732 cerr << "ERROR: context was not provided (via --context)" << std::endl;
11fdf7f2
TL
10733 return EINVAL;
10734 }
f67539c2
TL
10735 const rgw::lua::context script_ctx = rgw::lua::to_context(*str_script_ctx);
10736 if (script_ctx == rgw::lua::context::none) {
1e59de90 10737 cerr << "ERROR: invalid script context: " << *str_script_ctx << ". must be one of: " << LUA_CONTEXT_LIST << std::endl;
f67539c2 10738 return EINVAL;
11fdf7f2 10739 }
1e59de90
TL
10740 auto lua_manager = driver->get_lua_manager();
10741 const auto rc = rgw::lua::delete_script(dpp(), lua_manager.get(), tenant, null_yield, script_ctx);
f67539c2
TL
10742 if (rc < 0) {
10743 cerr << "ERROR: failed to remove script. error: " << rc << std::endl;
10744 return -rc;
11fdf7f2 10745 }
f67539c2 10746 }
f64942e4 10747
f67539c2
TL
10748 if (opt_cmd == OPT::SCRIPT_PACKAGE_ADD) {
10749#ifdef WITH_RADOSGW_LUA_PACKAGES
10750 if (!script_package) {
10751 cerr << "ERROR: lua package name was not provided (via --package)" << std::endl;
11fdf7f2
TL
10752 return EINVAL;
10753 }
1e59de90 10754 const auto rc = rgw::lua::add_package(dpp(), driver, null_yield, *script_package, bool(allow_compilation));
f67539c2
TL
10755 if (rc < 0) {
10756 cerr << "ERROR: failed to add lua package: " << script_package << " .error: " << rc << std::endl;
10757 return -rc;
11fdf7f2 10758 }
f67539c2
TL
10759#else
10760 cerr << "ERROR: adding lua packages is not permitted" << std::endl;
10761 return EPERM;
10762#endif
10763 }
10764
10765 if (opt_cmd == OPT::SCRIPT_PACKAGE_RM) {
10766#ifdef WITH_RADOSGW_LUA_PACKAGES
10767 if (!script_package) {
10768 cerr << "ERROR: lua package name was not provided (via --package)" << std::endl;
11fdf7f2
TL
10769 return EINVAL;
10770 }
1e59de90 10771 const auto rc = rgw::lua::remove_package(dpp(), driver, null_yield, *script_package);
f67539c2
TL
10772 if (rc == -ENOENT) {
10773 cerr << "WARNING: package " << script_package << " did not exists or already removed" << std::endl;
10774 return 0;
11fdf7f2 10775 }
f67539c2
TL
10776 if (rc < 0) {
10777 cerr << "ERROR: failed to remove lua package: " << script_package << " .error: " << rc << std::endl;
10778 return -rc;
10779 }
10780#else
10781 cerr << "ERROR: removing lua packages in not permitted" << std::endl;
10782 return EPERM;
10783#endif
10784 }
10785
10786 if (opt_cmd == OPT::SCRIPT_PACKAGE_LIST) {
10787#ifdef WITH_RADOSGW_LUA_PACKAGES
10788 rgw::lua::packages_t packages;
1e59de90 10789 const auto rc = rgw::lua::list_packages(dpp(), driver, null_yield, packages);
f67539c2
TL
10790 if (rc == -ENOENT) {
10791 std::cout << "no lua packages in allowlist" << std::endl;
10792 } else if (rc < 0) {
10793 cerr << "ERROR: failed to read lua packages allowlist. error: " << rc << std::endl;
10794 return rc;
10795 } else {
10796 for (const auto& package : packages) {
10797 std::cout << package << std::endl;
10798 }
11fdf7f2 10799 }
f67539c2
TL
10800#else
10801 cerr << "ERROR: listing lua packages in not permitted" << std::endl;
10802 return EPERM;
10803#endif
11fdf7f2
TL
10804 }
10805
10806 return 0;
7c673cae 10807}
f67539c2 10808