]> git.proxmox.com Git - ceph.git/blame - ceph/src/tools/rbd/Utils.cc
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / tools / rbd / Utils.cc
CommitLineData
7c673cae
FG
1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2// vim: ts=8 sw=2 smarttab
3
4#include "tools/rbd/Utils.h"
11fdf7f2 5#include "include/ceph_assert.h"
7c673cae
FG
6#include "include/Context.h"
7#include "include/encoding.h"
8#include "common/common_init.h"
9#include "include/stringify.h"
10#include "include/rbd/features.h"
11#include "common/config.h"
12#include "common/errno.h"
9f95a23c 13#include "common/escape.h"
7c673cae
FG
14#include "common/safe_io.h"
15#include "global/global_context.h"
1e59de90 16#include <fstream>
7c673cae 17#include <iostream>
11fdf7f2 18#include <regex>
7c673cae
FG
19#include <boost/algorithm/string.hpp>
20#include <boost/lexical_cast.hpp>
21
22namespace rbd {
23namespace utils {
24
25namespace at = argument_types;
26namespace po = boost::program_options;
27
9f95a23c
TL
28namespace {
29
30static std::string mgr_command_args_to_str(
31 const std::map<std::string, std::string> &args) {
32 std::string out = "";
33
34 std::string delimiter;
35 for (auto &it : args) {
36 out += delimiter + "\"" + it.first + "\": \"" +
37 stringify(json_stream_escaper(it.second)) + "\"";
38 delimiter = ",\n";
39 }
40
41 return out;
42}
43
44} // anonymous namespace
45
7c673cae
FG
46int ProgressContext::update_progress(uint64_t offset, uint64_t total) {
47 if (progress) {
33c7a0ef 48 int pc = get_percentage(offset, total);
f67539c2 49 if (pc > last_pc) {
20effc67
TL
50 std::cerr << "\r" << operation << ": "
51 << pc << "% complete..." << std::flush;
7c673cae
FG
52 last_pc = pc;
53 }
54 }
55 return 0;
56}
57
58void ProgressContext::finish() {
59 if (progress) {
20effc67 60 std::cerr << "\r" << operation << ": 100% complete...done." << std::endl;
7c673cae
FG
61 }
62}
63
64void ProgressContext::fail() {
65 if (progress) {
20effc67
TL
66 std::cerr << "\r" << operation << ": " << last_pc << "% complete...failed."
67 << std::endl;
7c673cae
FG
68 }
69}
70
33c7a0ef
TL
71int get_percentage(uint64_t part, uint64_t whole) {
72 return whole ? (100 * part / whole) : 0;
73}
74
7c673cae
FG
75void aio_context_callback(librbd::completion_t completion, void *arg)
76{
77 librbd::RBD::AioCompletion *aio_completion =
78 reinterpret_cast<librbd::RBD::AioCompletion*>(completion);
79 Context *context = reinterpret_cast<Context *>(arg);
80 context->complete(aio_completion->get_return_value());
81 aio_completion->release();
82}
83
84int read_string(int fd, unsigned max, std::string *out) {
85 char buf[4];
86
87 int r = safe_read_exact(fd, buf, 4);
88 if (r < 0)
89 return r;
90
91 bufferlist bl;
92 bl.append(buf, 4);
11fdf7f2 93 auto p = bl.cbegin();
7c673cae 94 uint32_t len;
11fdf7f2 95 decode(len, p);
7c673cae
FG
96 if (len > max)
97 return -EINVAL;
98
99 char sbuf[len];
100 r = safe_read_exact(fd, sbuf, len);
101 if (r < 0)
102 return r;
103 out->assign(sbuf, len);
104 return len;
105}
106
107int extract_spec(const std::string &spec, std::string *pool_name,
11fdf7f2
TL
108 std::string *namespace_name, std::string *name,
109 std::string *snap_name, SpecValidation spec_validation) {
110 if (!g_ceph_context->_conf.get_val<bool>("rbd_validate_names")) {
7c673cae
FG
111 spec_validation = SPEC_VALIDATION_NONE;
112 }
113
11fdf7f2 114 std::regex pattern;
7c673cae
FG
115 switch (spec_validation) {
116 case SPEC_VALIDATION_FULL:
11fdf7f2
TL
117 // disallow "/" and "@" in all names
118 pattern = "^(?:([^/@]+)/(?:([^/@]+)/)?)?([^/@]+)(?:@([^/@]+))?$";
7c673cae
FG
119 break;
120 case SPEC_VALIDATION_SNAP:
121 // disallow "/" and "@" in snap name
11fdf7f2 122 pattern = "^(?:([^/]+)/(?:([^/@]+)/)?)?([^@]+)(?:@([^/@]+))?$";
7c673cae
FG
123 break;
124 case SPEC_VALIDATION_NONE:
11fdf7f2
TL
125 // relaxed pattern assumes pool is before first "/",
126 // namespace is before second "/", and snap name is after first "@"
127 pattern = "^(?:([^/]+)/(?:([^/@]+)/)?)?([^@]+)(?:@(.+))?$";
7c673cae
FG
128 break;
129 default:
11fdf7f2 130 ceph_abort();
7c673cae
FG
131 break;
132 }
133
11fdf7f2
TL
134 std::smatch match;
135 if (!std::regex_match(spec, match, pattern)) {
7c673cae
FG
136 std::cerr << "rbd: invalid spec '" << spec << "'" << std::endl;
137 return -EINVAL;
138 }
139
11fdf7f2
TL
140 if (match[1].matched) {
141 if (pool_name != nullptr) {
142 *pool_name = match[1];
143 } else {
144 std::cerr << "rbd: pool name specified for a command that doesn't use it"
145 << std::endl;
146 return -EINVAL;
147 }
7c673cae
FG
148 }
149
11fdf7f2
TL
150 if (match[2].matched) {
151 if (namespace_name != nullptr) {
152 *namespace_name = match[2];
153 } else {
154 std::cerr << "rbd: namespace name specified for a command that doesn't "
155 << "use it" << std::endl;
156 return -EINVAL;
157 }
7c673cae
FG
158 }
159
11fdf7f2
TL
160 if (name != nullptr) {
161 *name = match[3];
7c673cae
FG
162 }
163
11fdf7f2
TL
164 if (match[4].matched) {
165 if (snap_name != nullptr) {
166 *snap_name = match[4];
167 } else {
168 std::cerr << "rbd: snapshot name specified for a command that doesn't "
169 << "use it" << std::endl;
170 return -EINVAL;
171 }
7c673cae 172 }
7c673cae 173 return 0;
7c673cae
FG
174}
175
176std::string get_positional_argument(const po::variables_map &vm, size_t index) {
177 if (vm.count(at::POSITIONAL_ARGUMENTS) == 0) {
178 return "";
179 }
180
181 const std::vector<std::string> &args =
182 boost::any_cast<std::vector<std::string> >(
183 vm[at::POSITIONAL_ARGUMENTS].value());
184 if (index < args.size()) {
185 return args[index];
186 }
187 return "";
188}
189
f67539c2
TL
190void normalize_pool_name(std::string* pool_name) {
191 if (pool_name->empty()) {
192 *pool_name = get_default_pool_name();
193 }
194}
195
31f18b77 196std::string get_default_pool_name() {
11fdf7f2 197 return g_ceph_context->_conf.get_val<std::string>("rbd_default_pool");
31f18b77
FG
198}
199
11fdf7f2 200int get_pool_and_namespace_names(
2a845540 201 const boost::program_options::variables_map &vm, bool validate_pool_name,
11fdf7f2
TL
202 std::string* pool_name, std::string* namespace_name, size_t *arg_index) {
203 if (namespace_name != nullptr && vm.count(at::NAMESPACE_NAME)) {
204 *namespace_name = vm[at::NAMESPACE_NAME].as<std::string>();
7c673cae
FG
205 }
206
11fdf7f2
TL
207 if (vm.count(at::POOL_NAME)) {
208 *pool_name = vm[at::POOL_NAME].as<std::string>();
209 } else {
210 *pool_name = get_positional_argument(vm, *arg_index);
211 if (!pool_name->empty()) {
212 if (namespace_name != nullptr) {
213 auto slash_pos = pool_name->find_last_of('/');
214 if (slash_pos != std::string::npos) {
215 *namespace_name = pool_name->substr(slash_pos + 1);
216 }
217 *pool_name = pool_name->substr(0, slash_pos);
7c673cae 218 }
11fdf7f2 219 ++(*arg_index);
7c673cae
FG
220 }
221 }
222
11fdf7f2
TL
223 if (!g_ceph_context->_conf.get_val<bool>("rbd_validate_names")) {
224 validate_pool_name = false;
7c673cae
FG
225 }
226
11fdf7f2
TL
227 if (validate_pool_name &&
228 pool_name->find_first_of("/@") != std::string::npos) {
229 std::cerr << "rbd: invalid pool '" << *pool_name << "'" << std::endl;
7c673cae 230 return -EINVAL;
11fdf7f2
TL
231 } else if (namespace_name != nullptr &&
232 namespace_name->find_first_of("/@") != std::string::npos) {
233 std::cerr << "rbd: invalid namespace '" << *namespace_name << "'"
234 << std::endl;
7c673cae
FG
235 return -EINVAL;
236 }
237
238 return 0;
239}
240
241int get_pool_image_id(const po::variables_map &vm,
11fdf7f2
TL
242 size_t *spec_arg_index,
243 std::string *pool_name,
244 std::string *namespace_name,
245 std::string *image_id) {
7c673cae
FG
246
247 if (vm.count(at::POOL_NAME) && pool_name != nullptr) {
248 *pool_name = vm[at::POOL_NAME].as<std::string>();
249 }
11fdf7f2
TL
250 if (vm.count(at::NAMESPACE_NAME) && namespace_name != nullptr) {
251 *namespace_name = vm[at::NAMESPACE_NAME].as<std::string>();
252 }
7c673cae
FG
253 if (vm.count(at::IMAGE_ID) && image_id != nullptr) {
254 *image_id = vm[at::IMAGE_ID].as<std::string>();
255 }
256
257 int r;
258 if (image_id != nullptr && spec_arg_index != nullptr && image_id->empty()) {
259 std::string spec = get_positional_argument(vm, (*spec_arg_index)++);
260 if (!spec.empty()) {
11fdf7f2
TL
261 r = extract_spec(spec, pool_name, namespace_name, image_id, nullptr,
262 SPEC_VALIDATION_FULL);
7c673cae
FG
263 if (r < 0) {
264 return r;
265 }
266 }
267 }
268
7c673cae
FG
269 if (image_id != nullptr && image_id->empty()) {
270 std::cerr << "rbd: image id was not specified" << std::endl;
271 return -EINVAL;
272 }
273
274 return 0;
275}
276
39ae355f
TL
277int get_image_or_snap_spec(const po::variables_map &vm, std::string *spec) {
278 size_t arg_index = 0;
279 std::string pool_name;
280 std::string nspace_name;
281 std::string image_name;
282 std::string snap_name;
283 int r = get_pool_image_snapshot_names(
284 vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &nspace_name,
285 &image_name, &snap_name, true, SNAPSHOT_PRESENCE_PERMITTED,
286 SPEC_VALIDATION_NONE);
287 if (r < 0) {
288 return r;
289 }
290
291 if (pool_name.empty()) {
292 // connect to the cluster to get the default pool
293 librados::Rados rados;
294 r = init_rados(&rados);
295 if (r < 0) {
296 return r;
297 }
298
299 normalize_pool_name(&pool_name);
300 }
301
302 spec->append(pool_name);
303 spec->append("/");
304 if (!nspace_name.empty()) {
305 spec->append(nspace_name);
306 spec->append("/");
307 }
308 spec->append(image_name);
309 if (!snap_name.empty()) {
310 spec->append("@");
311 spec->append(snap_name);
312 }
313
314 return 0;
315}
316
317void append_options_as_args(const std::vector<std::string> &options,
318 std::vector<std::string> *args) {
319 for (auto &opts : options) {
320 std::vector<std::string> args_;
321 boost::split(args_, opts, boost::is_any_of(","));
322 for (auto &o : args_) {
323 args->push_back("--" + o);
324 }
325 }
326}
327
7c673cae
FG
328int get_pool_image_snapshot_names(const po::variables_map &vm,
329 at::ArgumentModifier mod,
330 size_t *spec_arg_index,
331 std::string *pool_name,
11fdf7f2 332 std::string *namespace_name,
7c673cae
FG
333 std::string *image_name,
334 std::string *snap_name,
11fdf7f2 335 bool image_name_required,
7c673cae 336 SnapshotPresence snapshot_presence,
11fdf7f2 337 SpecValidation spec_validation) {
7c673cae
FG
338 std::string pool_key = (mod == at::ARGUMENT_MODIFIER_DEST ?
339 at::DEST_POOL_NAME : at::POOL_NAME);
340 std::string image_key = (mod == at::ARGUMENT_MODIFIER_DEST ?
341 at::DEST_IMAGE_NAME : at::IMAGE_NAME);
11fdf7f2
TL
342 return get_pool_generic_snapshot_names(vm, mod, spec_arg_index, pool_key,
343 pool_name, namespace_name, image_key,
344 "image", image_name, snap_name,
345 image_name_required, snapshot_presence,
346 spec_validation);
347}
348
349int get_pool_generic_snapshot_names(const po::variables_map &vm,
350 at::ArgumentModifier mod,
351 size_t *spec_arg_index,
352 const std::string& pool_key,
353 std::string *pool_name,
354 std::string *namespace_name,
355 const std::string& generic_key,
356 const std::string& generic_key_desc,
357 std::string *generic_name,
358 std::string *snap_name,
359 bool generic_name_required,
360 SnapshotPresence snapshot_presence,
361 SpecValidation spec_validation) {
362 std::string namespace_key = (mod == at::ARGUMENT_MODIFIER_DEST ?
363 at::DEST_NAMESPACE_NAME : at::NAMESPACE_NAME);
7c673cae 364 std::string snap_key = (mod == at::ARGUMENT_MODIFIER_DEST ?
11fdf7f2 365 at::DEST_SNAPSHOT_NAME : at::SNAPSHOT_NAME);
7c673cae
FG
366
367 if (vm.count(pool_key) && pool_name != nullptr) {
368 *pool_name = vm[pool_key].as<std::string>();
369 }
11fdf7f2
TL
370 if (vm.count(namespace_key) && namespace_name != nullptr) {
371 *namespace_name = vm[namespace_key].as<std::string>();
372 }
373 if (vm.count(generic_key) && generic_name != nullptr) {
374 *generic_name = vm[generic_key].as<std::string>();
7c673cae
FG
375 }
376 if (vm.count(snap_key) && snap_name != nullptr) {
11fdf7f2 377 *snap_name = vm[snap_key].as<std::string>();
7c673cae
FG
378 }
379
380 int r;
11fdf7f2
TL
381 if ((generic_key == at::IMAGE_NAME || generic_key == at::DEST_IMAGE_NAME) &&
382 generic_name != nullptr && !generic_name->empty()) {
7c673cae
FG
383 // despite the separate pool and snapshot name options,
384 // we can also specify them via the image option
11fdf7f2
TL
385 std::string image_name_copy(*generic_name);
386 r = extract_spec(image_name_copy, pool_name, namespace_name, generic_name,
387 snap_name, spec_validation);
7c673cae
FG
388 if (r < 0) {
389 return r;
390 }
391 }
392
11fdf7f2
TL
393 if (generic_name != nullptr && spec_arg_index != nullptr &&
394 generic_name->empty()) {
7c673cae
FG
395 std::string spec = get_positional_argument(vm, (*spec_arg_index)++);
396 if (!spec.empty()) {
11fdf7f2
TL
397 r = extract_spec(spec, pool_name, namespace_name, generic_name, snap_name,
398 spec_validation);
7c673cae
FG
399 if (r < 0) {
400 return r;
401 }
402 }
403 }
404
11fdf7f2
TL
405 if (generic_name != nullptr && generic_name_required &&
406 generic_name->empty()) {
7c673cae
FG
407 std::string prefix = at::get_description_prefix(mod);
408 std::cerr << "rbd: "
409 << (mod == at::ARGUMENT_MODIFIER_DEST ? prefix : std::string())
11fdf7f2 410 << generic_key_desc << " name was not specified" << std::endl;
7c673cae
FG
411 return -EINVAL;
412 }
413
f67539c2 414 std::regex pattern("^[^@/]*?$");
7c673cae 415 if (spec_validation == SPEC_VALIDATION_FULL) {
11fdf7f2
TL
416 // validate pool name while creating/renaming/copying/cloning/importing/etc
417 if ((pool_name != nullptr) && !std::regex_match (*pool_name, pattern)) {
31f18b77 418 std::cerr << "rbd: invalid pool name '" << *pool_name << "'" << std::endl;
7c673cae
FG
419 return -EINVAL;
420 }
421 }
422
11fdf7f2
TL
423 if (namespace_name != nullptr && !namespace_name->empty() &&
424 !std::regex_match (*namespace_name, pattern)) {
425 std::cerr << "rbd: invalid namespace name '" << *namespace_name << "'"
426 << std::endl;
427 return -EINVAL;
7c673cae
FG
428 }
429
430 if (snap_name != nullptr) {
11fdf7f2
TL
431 r = validate_snapshot_name(mod, *snap_name, snapshot_presence,
432 spec_validation);
7c673cae
FG
433 if (r < 0) {
434 return r;
435 }
7c673cae 436 }
7c673cae
FG
437 return 0;
438}
439
440int validate_snapshot_name(at::ArgumentModifier mod,
441 const std::string &snap_name,
442 SnapshotPresence snapshot_presence,
443 SpecValidation spec_validation) {
444 std::string prefix = at::get_description_prefix(mod);
445 switch (snapshot_presence) {
446 case SNAPSHOT_PRESENCE_PERMITTED:
447 break;
448 case SNAPSHOT_PRESENCE_NONE:
449 if (!snap_name.empty()) {
450 std::cerr << "rbd: "
451 << (mod == at::ARGUMENT_MODIFIER_DEST ? prefix : std::string())
11fdf7f2 452 << "snapshot name specified for a command that doesn't use it"
7c673cae
FG
453 << std::endl;
454 return -EINVAL;
455 }
456 break;
457 case SNAPSHOT_PRESENCE_REQUIRED:
458 if (snap_name.empty()) {
459 std::cerr << "rbd: "
460 << (mod == at::ARGUMENT_MODIFIER_DEST ? prefix : std::string())
11fdf7f2 461 << "snapshot name was not specified" << std::endl;
7c673cae
FG
462 return -EINVAL;
463 }
464 break;
465 }
466
467 if (spec_validation == SPEC_VALIDATION_SNAP) {
468 // disallow "/" and "@" in snap name
11fdf7f2
TL
469 std::regex pattern("^[^@/]*?$");
470 if (!std::regex_match (snap_name, pattern)) {
31f18b77 471 std::cerr << "rbd: invalid snap name '" << snap_name << "'" << std::endl;
7c673cae
FG
472 return -EINVAL;
473 }
474 }
475 return 0;
476}
477
478int get_image_options(const boost::program_options::variables_map &vm,
479 bool get_format, librbd::ImageOptions *opts) {
480 uint64_t order = 0, stripe_unit = 0, stripe_count = 0, object_size = 0;
11fdf7f2 481 uint64_t features = 0, features_clear = 0;
7c673cae
FG
482 std::string data_pool;
483 bool order_specified = true;
484 bool features_specified = false;
485 bool features_clear_specified = false;
7c673cae
FG
486 bool stripe_specified = false;
487
488 if (vm.count(at::IMAGE_ORDER)) {
489 order = vm[at::IMAGE_ORDER].as<uint64_t>();
7c673cae
FG
490 } else if (vm.count(at::IMAGE_OBJECT_SIZE)) {
491 object_size = vm[at::IMAGE_OBJECT_SIZE].as<uint64_t>();
11fdf7f2 492 order = std::round(std::log2(object_size));
7c673cae
FG
493 } else {
494 order_specified = false;
495 }
496
497 if (vm.count(at::IMAGE_FEATURES)) {
498 features = vm[at::IMAGE_FEATURES].as<uint64_t>();
499 features_specified = true;
7c673cae
FG
500 }
501
502 if (vm.count(at::IMAGE_STRIPE_UNIT)) {
503 stripe_unit = vm[at::IMAGE_STRIPE_UNIT].as<uint64_t>();
504 stripe_specified = true;
505 }
506
507 if (vm.count(at::IMAGE_STRIPE_COUNT)) {
508 stripe_count = vm[at::IMAGE_STRIPE_COUNT].as<uint64_t>();
509 stripe_specified = true;
510 }
511
512 if (vm.count(at::IMAGE_SHARED) && vm[at::IMAGE_SHARED].as<bool>()) {
513 if (features_specified) {
514 features &= ~RBD_FEATURES_SINGLE_CLIENT;
515 } else {
516 features_clear |= RBD_FEATURES_SINGLE_CLIENT;
517 features_clear_specified = true;
518 }
519 }
520
521 if (vm.count(at::IMAGE_DATA_POOL)) {
522 data_pool = vm[at::IMAGE_DATA_POOL].as<std::string>();
523 }
524
525 if (get_format) {
526 uint64_t format = 0;
527 bool format_specified = false;
528 if (vm.count(at::IMAGE_NEW_FORMAT)) {
529 format = 2;
530 format_specified = true;
531 } else if (vm.count(at::IMAGE_FORMAT)) {
532 format = vm[at::IMAGE_FORMAT].as<uint32_t>();
533 format_specified = true;
534 }
535 if (format == 1) {
536 std::cerr << "rbd: image format 1 is deprecated" << std::endl;
537 }
538
539 if (features_specified && features != 0) {
540 if (format_specified && format == 1) {
541 std::cerr << "rbd: features not allowed with format 1; "
542 << "use --image-format 2" << std::endl;
543 return -EINVAL;
544 } else {
545 format = 2;
546 format_specified = true;
547 }
548 }
549
550 if ((stripe_unit || stripe_count) &&
551 (stripe_unit != (1ull << order) && stripe_count != 1)) {
552 if (format_specified && format == 1) {
553 std::cerr << "rbd: non-default striping not allowed with format 1; "
554 << "use --image-format 2" << std::endl;
555 return -EINVAL;
556 } else {
557 format = 2;
558 format_specified = true;
559 }
560 }
561
562 if (!data_pool.empty()) {
563 if (format_specified && format == 1) {
564 std::cerr << "rbd: data pool not allowed with format 1; "
565 << "use --image-format 2" << std::endl;
566 return -EINVAL;
567 } else {
568 format = 2;
569 format_specified = true;
570 }
571 }
572
573 if (format_specified) {
11fdf7f2
TL
574 int r = g_conf().set_val("rbd_default_format", stringify(format));
575 ceph_assert(r == 0);
7c673cae
FG
576 opts->set(RBD_IMAGE_OPTION_FORMAT, format);
577 }
578 }
579
580 if (order_specified)
581 opts->set(RBD_IMAGE_OPTION_ORDER, order);
582 if (features_specified)
583 opts->set(RBD_IMAGE_OPTION_FEATURES, features);
584 if (features_clear_specified) {
585 opts->set(RBD_IMAGE_OPTION_FEATURES_CLEAR, features_clear);
586 }
7c673cae
FG
587 if (stripe_specified) {
588 opts->set(RBD_IMAGE_OPTION_STRIPE_UNIT, stripe_unit);
589 opts->set(RBD_IMAGE_OPTION_STRIPE_COUNT, stripe_count);
590 }
591 if (!data_pool.empty()) {
592 opts->set(RBD_IMAGE_OPTION_DATA_POOL, data_pool);
593 }
594 int r = get_journal_options(vm, opts);
595 if (r < 0) {
596 return r;
597 }
598
11fdf7f2
TL
599 r = get_flatten_option(vm, opts);
600 if (r < 0) {
601 return r;
602 }
603
1911f103
TL
604 if (vm.count(at::IMAGE_MIRROR_IMAGE_MODE)) {
605 opts->set(RBD_IMAGE_OPTION_MIRROR_IMAGE_MODE,
606 vm[at::IMAGE_MIRROR_IMAGE_MODE].as<librbd::mirror_image_mode_t>());
607 }
608
7c673cae
FG
609 return 0;
610}
611
612int get_journal_options(const boost::program_options::variables_map &vm,
613 librbd::ImageOptions *opts) {
614
615 if (vm.count(at::JOURNAL_OBJECT_SIZE)) {
616 uint64_t size = vm[at::JOURNAL_OBJECT_SIZE].as<uint64_t>();
617 uint64_t order = 12;
618 while ((1ULL << order) < size) {
619 order++;
620 }
621 opts->set(RBD_IMAGE_OPTION_JOURNAL_ORDER, order);
622
11fdf7f2
TL
623 int r = g_conf().set_val("rbd_journal_order", stringify(order));
624 ceph_assert(r == 0);
7c673cae
FG
625 }
626 if (vm.count(at::JOURNAL_SPLAY_WIDTH)) {
627 opts->set(RBD_IMAGE_OPTION_JOURNAL_SPLAY_WIDTH,
628 vm[at::JOURNAL_SPLAY_WIDTH].as<uint64_t>());
629
11fdf7f2 630 int r = g_conf().set_val("rbd_journal_splay_width",
7c673cae
FG
631 stringify(
632 vm[at::JOURNAL_SPLAY_WIDTH].as<uint64_t>()));
11fdf7f2 633 ceph_assert(r == 0);
7c673cae
FG
634 }
635 if (vm.count(at::JOURNAL_POOL)) {
636 opts->set(RBD_IMAGE_OPTION_JOURNAL_POOL,
637 vm[at::JOURNAL_POOL].as<std::string>());
638
11fdf7f2 639 int r = g_conf().set_val("rbd_journal_pool",
7c673cae 640 vm[at::JOURNAL_POOL].as<std::string>());
11fdf7f2 641 ceph_assert(r == 0);
7c673cae
FG
642 }
643
644 return 0;
645}
646
11fdf7f2
TL
647int get_flatten_option(const boost::program_options::variables_map &vm,
648 librbd::ImageOptions *opts) {
649 if (vm.count(at::IMAGE_FLATTEN) && vm[at::IMAGE_FLATTEN].as<bool>()) {
650 uint64_t flatten = 1;
651 opts->set(RBD_IMAGE_OPTION_FLATTEN, flatten);
652 }
653 return 0;
654}
655
7c673cae
FG
656int get_image_size(const boost::program_options::variables_map &vm,
657 uint64_t *size) {
658 if (vm.count(at::IMAGE_SIZE) == 0) {
659 std::cerr << "rbd: must specify --size <M/G/T>" << std::endl;
660 return -EINVAL;
661 }
662
663 *size = vm[at::IMAGE_SIZE].as<uint64_t>();
664 return 0;
665}
666
667int get_path(const boost::program_options::variables_map &vm,
11fdf7f2
TL
668 size_t *arg_index, std::string *path) {
669 if (vm.count(at::PATH)) {
7c673cae 670 *path = vm[at::PATH].as<std::string>();
11fdf7f2
TL
671 } else {
672 *path = get_positional_argument(vm, *arg_index);
673 if (!path->empty()) {
674 ++(*arg_index);
675 }
7c673cae
FG
676 }
677
678 if (path->empty()) {
679 std::cerr << "rbd: path was not specified" << std::endl;
680 return -EINVAL;
681 }
682 return 0;
683}
684
685int get_formatter(const po::variables_map &vm,
686 at::Format::Formatter *formatter) {
687 if (vm.count(at::FORMAT)) {
688 bool pretty = vm[at::PRETTY_FORMAT].as<bool>();
689 *formatter = vm[at::FORMAT].as<at::Format>().create_formatter(pretty);
690 if (*formatter == nullptr && pretty) {
691 std::cerr << "rbd: --pretty-format only works when --format "
692 << "is json or xml" << std::endl;
693 return -EINVAL;
181888fb
FG
694 } else if (*formatter != nullptr && !pretty) {
695 formatter->get()->enable_line_break();
7c673cae 696 }
181888fb
FG
697 } else if (vm[at::PRETTY_FORMAT].as<bool>()) {
698 std::cerr << "rbd: --pretty-format only works when --format "
699 << "is json or xml" << std::endl;
700 return -EINVAL;
7c673cae
FG
701 }
702 return 0;
703}
704
f67539c2
TL
705int get_snap_create_flags(const po::variables_map &vm, uint32_t *flags) {
706 if (vm[at::SKIP_QUIESCE].as<bool>() &&
707 vm[at::IGNORE_QUIESCE_ERROR].as<bool>()) {
708 std::cerr << "rbd: " << at::IGNORE_QUIESCE_ERROR
709 << " cannot be used together with " << at::SKIP_QUIESCE
710 << std::endl;
711 return -EINVAL;
712 }
713
714 *flags = 0;
715 if (vm[at::SKIP_QUIESCE].as<bool>()) {
716 *flags |= RBD_SNAP_CREATE_SKIP_QUIESCE;
717 } else if (vm[at::IGNORE_QUIESCE_ERROR].as<bool>()) {
718 *flags |= RBD_SNAP_CREATE_IGNORE_QUIESCE_ERROR;
719 }
720 return 0;
721}
722
1e59de90
TL
723int get_encryption_options(const boost::program_options::variables_map &vm,
724 EncryptionOptions* result) {
725 std::vector<std::string> passphrase_files;
726 if (vm.count(at::ENCRYPTION_PASSPHRASE_FILE)) {
727 passphrase_files =
728 vm[at::ENCRYPTION_PASSPHRASE_FILE].as<std::vector<std::string>>();
729 }
730
731 std::vector<at::EncryptionFormat> formats;
732 if (vm.count(at::ENCRYPTION_FORMAT)) {
733 formats = vm[at::ENCRYPTION_FORMAT].as<decltype(formats)>();
734 } else if (vm.count(at::ENCRYPTION_PASSPHRASE_FILE)) {
735 formats.resize(passphrase_files.size(),
736 at::EncryptionFormat{RBD_ENCRYPTION_FORMAT_LUKS});
737 }
738
739 if (formats.size() != passphrase_files.size()) {
740 std::cerr << "rbd: encryption formats count does not match "
741 << "passphrase files count" << std::endl;
742 return -EINVAL;
743 }
744
745 result->specs.clear();
746 result->specs.reserve(formats.size());
747 for (size_t i = 0; i < formats.size(); ++i) {
748 std::ifstream file(passphrase_files[i], std::ios::in | std::ios::binary);
749 if (file.fail()) {
750 std::cerr << "rbd: unable to open passphrase file '"
751 << passphrase_files[i] << "': " << cpp_strerror(errno)
752 << std::endl;
753 return -errno;
754 }
755 std::string passphrase((std::istreambuf_iterator<char>(file)),
756 std::istreambuf_iterator<char>());
757 file.close();
758
759 switch (formats[i].format) {
760 case RBD_ENCRYPTION_FORMAT_LUKS: {
761 auto opts = new librbd::encryption_luks_format_options_t{
762 std::move(passphrase)};
763 result->specs.push_back(
764 {RBD_ENCRYPTION_FORMAT_LUKS, opts, sizeof(*opts)});
765 break;
766 }
767 case RBD_ENCRYPTION_FORMAT_LUKS1: {
768 auto opts = new librbd::encryption_luks1_format_options_t{
769 .passphrase = std::move(passphrase)};
770 result->specs.push_back(
771 {RBD_ENCRYPTION_FORMAT_LUKS1, opts, sizeof(*opts)});
772 break;
773 }
774 case RBD_ENCRYPTION_FORMAT_LUKS2: {
775 auto opts = new librbd::encryption_luks2_format_options_t{
776 .passphrase = std::move(passphrase)};
777 result->specs.push_back(
778 {RBD_ENCRYPTION_FORMAT_LUKS2, opts, sizeof(*opts)});
779 break;
780 }
781 default:
782 ceph_abort();
783 }
784 }
785
786 return 0;
787}
788
7c673cae 789void init_context() {
11fdf7f2
TL
790 g_conf().set_val_or_die("rbd_cache_writethrough_until_flush", "false");
791 g_conf().apply_changes(nullptr);
7c673cae
FG
792}
793
11fdf7f2 794int init_rados(librados::Rados *rados) {
7c673cae
FG
795 init_context();
796
797 int r = rados->init_with_context(g_ceph_context);
798 if (r < 0) {
799 std::cerr << "rbd: couldn't initialize rados!" << std::endl;
800 return r;
801 }
802
803 r = rados->connect();
804 if (r < 0) {
805 std::cerr << "rbd: couldn't connect to the cluster!" << std::endl;
806 return r;
807 }
808
11fdf7f2
TL
809 return 0;
810}
811
812int init(const std::string &pool_name, const std::string& namespace_name,
813 librados::Rados *rados, librados::IoCtx *io_ctx) {
814 init_context();
815
816 int r = init_rados(rados);
817 if (r < 0) {
818 return r;
819 }
820
821 r = init_io_ctx(*rados, pool_name, namespace_name, io_ctx);
7c673cae
FG
822 if (r < 0) {
823 return r;
824 }
825 return 0;
826}
827
f67539c2 828int init_io_ctx(librados::Rados &rados, std::string pool_name,
11fdf7f2 829 const std::string& namespace_name, librados::IoCtx *io_ctx) {
f67539c2
TL
830 normalize_pool_name(&pool_name);
831
7c673cae
FG
832 int r = rados.ioctx_create(pool_name.c_str(), *io_ctx);
833 if (r < 0) {
31f18b77
FG
834 if (r == -ENOENT && pool_name == get_default_pool_name()) {
835 std::cerr << "rbd: error opening default pool "
836 << "'" << pool_name << "'" << std::endl
837 << "Ensure that the default pool has been created or specify "
838 << "an alternate pool name." << std::endl;
839 } else {
840 std::cerr << "rbd: error opening pool '" << pool_name << "': "
841 << cpp_strerror(r) << std::endl;
842 }
7c673cae
FG
843 return r;
844 }
11fdf7f2
TL
845
846 return set_namespace(namespace_name, io_ctx);
847}
848
849int set_namespace(const std::string& namespace_name, librados::IoCtx *io_ctx) {
850 if (!namespace_name.empty()) {
851 librbd::RBD rbd;
852 bool exists = false;
853 int r = rbd.namespace_exists(*io_ctx, namespace_name.c_str(), &exists);
854 if (r < 0) {
855 std::cerr << "rbd: error asserting namespace: "
856 << cpp_strerror(r) << std::endl;
857 return r;
858 }
859 if (!exists) {
860 std::cerr << "rbd: namespace '" << namespace_name << "' does not exist."
861 << std::endl;
862 return -ENOENT;
863 }
864 }
865 io_ctx->set_namespace(namespace_name);
7c673cae
FG
866 return 0;
867}
868
11fdf7f2
TL
869void disable_cache() {
870 g_conf().set_val_or_die("rbd_cache", "false");
871}
872
7c673cae
FG
873int open_image(librados::IoCtx &io_ctx, const std::string &image_name,
874 bool read_only, librbd::Image *image) {
875 int r;
876 librbd::RBD rbd;
877 if (read_only) {
878 r = rbd.open_read_only(io_ctx, *image, image_name.c_str(), NULL);
879 } else {
880 r = rbd.open(io_ctx, *image, image_name.c_str());
881 }
882
883 if (r < 0) {
884 std::cerr << "rbd: error opening image " << image_name << ": "
885 << cpp_strerror(r) << std::endl;
886 return r;
887 }
888 return 0;
889}
890
891int open_image_by_id(librados::IoCtx &io_ctx, const std::string &image_id,
892 bool read_only, librbd::Image *image) {
893 int r;
894 librbd::RBD rbd;
895 if (read_only) {
896 r = rbd.open_by_id_read_only(io_ctx, *image, image_id.c_str(), NULL);
897 } else {
898 r = rbd.open_by_id(io_ctx, *image, image_id.c_str());
899 }
900
901 if (r < 0) {
902 std::cerr << "rbd: error opening image with id " << image_id << ": "
903 << cpp_strerror(r) << std::endl;
904 return r;
905 }
906 return 0;
907}
908
909int init_and_open_image(const std::string &pool_name,
11fdf7f2 910 const std::string &namespace_name,
7c673cae
FG
911 const std::string &image_name,
912 const std::string &image_id,
913 const std::string &snap_name, bool read_only,
914 librados::Rados *rados, librados::IoCtx *io_ctx,
915 librbd::Image *image) {
11fdf7f2 916 int r = init(pool_name, namespace_name, rados, io_ctx);
7c673cae
FG
917 if (r < 0) {
918 return r;
919 }
920
921 if (image_id.empty()) {
922 r = open_image(*io_ctx, image_name, read_only, image);
923 } else {
924 r = open_image_by_id(*io_ctx, image_id, read_only, image);
925 }
926 if (r < 0) {
927 return r;
928 }
929
930 if (!snap_name.empty()) {
931 r = snap_set(*image, snap_name);
932 if (r < 0) {
933 return r;
934 }
935 }
1e59de90 936
7c673cae
FG
937 return 0;
938}
939
940int snap_set(librbd::Image &image, const std::string &snap_name) {
941 int r = image.snap_set(snap_name.c_str());
942 if (r < 0) {
943 std::cerr << "error setting snapshot context: " << cpp_strerror(r)
944 << std::endl;
945 return r;
946 }
947 return 0;
948}
949
950void calc_sparse_extent(const bufferptr &bp,
951 size_t sparse_size,
952 size_t buffer_offset,
953 uint64_t buffer_length,
954 size_t *write_length,
955 bool *zeroed) {
956 if (sparse_size == 0) {
957 // sparse writes are disabled -- write the full extent
11fdf7f2 958 ceph_assert(buffer_offset == 0);
7c673cae
FG
959 *write_length = buffer_length;
960 *zeroed = false;
961 return;
962 }
963
964 *write_length = 0;
965 size_t original_offset = buffer_offset;
966 while (buffer_offset < buffer_length) {
967 size_t extent_size = std::min<size_t>(
968 sparse_size, buffer_length - buffer_offset);
969
970 bufferptr extent(bp, buffer_offset, extent_size);
971
972 bool extent_is_zero = extent.is_zero();
973 if (original_offset == buffer_offset) {
974 *zeroed = extent_is_zero;
975 } else if (*zeroed != extent_is_zero) {
11fdf7f2 976 ceph_assert(*write_length > 0);
7c673cae
FG
977 return;
978 }
979
980 buffer_offset += extent_size;
981 *write_length += extent_size;
982 }
983}
984
985std::string image_id(librbd::Image& image) {
986 std::string id;
987 int r = image.get_id(&id);
988 if (r < 0) {
989 return std::string();
990 }
991 return id;
992}
993
9f95a23c
TL
994std::string mirror_image_mode(librbd::mirror_image_mode_t mode) {
995 switch (mode) {
996 case RBD_MIRROR_IMAGE_MODE_JOURNAL:
997 return "journal";
998 case RBD_MIRROR_IMAGE_MODE_SNAPSHOT:
999 return "snapshot";
1000 default:
1001 return "unknown";
1002 }
1003}
1004
7c673cae
FG
1005std::string mirror_image_state(librbd::mirror_image_state_t state) {
1006 switch (state) {
1007 case RBD_MIRROR_IMAGE_DISABLING:
1008 return "disabling";
1009 case RBD_MIRROR_IMAGE_ENABLED:
1010 return "enabled";
1011 case RBD_MIRROR_IMAGE_DISABLED:
1012 return "disabled";
1013 default:
1014 return "unknown";
1015 }
1016}
1017
9f95a23c
TL
1018std::string mirror_image_status_state(
1019 librbd::mirror_image_status_state_t state) {
7c673cae
FG
1020 switch (state) {
1021 case MIRROR_IMAGE_STATUS_STATE_UNKNOWN:
1022 return "unknown";
1023 case MIRROR_IMAGE_STATUS_STATE_ERROR:
1024 return "error";
1025 case MIRROR_IMAGE_STATUS_STATE_SYNCING:
1026 return "syncing";
1027 case MIRROR_IMAGE_STATUS_STATE_STARTING_REPLAY:
1028 return "starting_replay";
1029 case MIRROR_IMAGE_STATUS_STATE_REPLAYING:
1030 return "replaying";
1031 case MIRROR_IMAGE_STATUS_STATE_STOPPING_REPLAY:
1032 return "stopping_replay";
1033 case MIRROR_IMAGE_STATUS_STATE_STOPPED:
1034 return "stopped";
1035 default:
1036 return "unknown (" + stringify(static_cast<uint32_t>(state)) + ")";
1037 }
1038}
1039
9f95a23c
TL
1040std::string mirror_image_site_status_state(
1041 const librbd::mirror_image_site_status_t& status) {
7c673cae
FG
1042 return (status.up ? "up+" : "down+") +
1043 mirror_image_status_state(status.state);
1044}
1045
9f95a23c
TL
1046std::string mirror_image_global_status_state(
1047 const librbd::mirror_image_global_status_t& status) {
1048 librbd::mirror_image_site_status_t local_status;
1049 int r = get_local_mirror_image_status(status, &local_status);
1050 if (r < 0) {
1051 return "down+unknown";
1052 }
1053
1054 return mirror_image_site_status_state(local_status);
1055}
1056
1057int get_local_mirror_image_status(
1058 const librbd::mirror_image_global_status_t& status,
1059 librbd::mirror_image_site_status_t* local_status) {
1060 auto it = std::find_if(status.site_statuses.begin(),
1061 status.site_statuses.end(),
1062 [](auto& site_status) {
1063 return (site_status.mirror_uuid ==
1064 RBD_MIRROR_IMAGE_STATUS_LOCAL_MIRROR_UUID);
1065 });
1066 if (it == status.site_statuses.end()) {
1067 return -ENOENT;
1068 }
1069
1070 *local_status = *it;
1071 return 0;
1072}
1073
7c673cae 1074std::string timestr(time_t t) {
9f95a23c
TL
1075 if (t == 0) {
1076 return "";
1077 }
1078
7c673cae
FG
1079 struct tm tm;
1080
1081 localtime_r(&t, &tm);
1082
1083 char buf[32];
f67539c2 1084 strftime(buf, sizeof(buf), "%Y-%m-%d %H:%M:%S", &tm);
7c673cae
FG
1085
1086 return buf;
1087}
1088
1089uint64_t get_rbd_default_features(CephContext* cct) {
11fdf7f2 1090 auto features = cct->_conf.get_val<std::string>("rbd_default_features");
7c673cae
FG
1091 return boost::lexical_cast<uint64_t>(features);
1092}
1093
11fdf7f2
TL
1094bool is_not_user_snap_namespace(librbd::Image* image,
1095 const librbd::snap_info_t &snap_info)
1096{
1097 librbd::snap_namespace_type_t namespace_type;
1098 int r = image->snap_get_namespace_type(snap_info.id, &namespace_type);
1099 if (r < 0) {
1100 return false;
7c673cae 1101 }
11fdf7f2 1102 return namespace_type != RBD_SNAP_NAMESPACE_TYPE_USER;
7c673cae
FG
1103}
1104
9f95a23c
TL
1105void get_mirror_peer_sites(
1106 librados::IoCtx& io_ctx,
1107 std::vector<librbd::mirror_peer_site_t>* mirror_peers) {
1108 librados::IoCtx default_io_ctx;
1109 default_io_ctx.dup(io_ctx);
1110 default_io_ctx.set_namespace("");
1111
1112 mirror_peers->clear();
1113
1114 librbd::RBD rbd;
1115 int r = rbd.mirror_peer_site_list(default_io_ctx, mirror_peers);
1116 if (r < 0 && r != -ENOENT) {
1117 std::cerr << "rbd: failed to list mirror peers" << std::endl;
1118 }
1119}
1120
1121void get_mirror_peer_mirror_uuids_to_names(
1122 const std::vector<librbd::mirror_peer_site_t>& mirror_peers,
1123 std::map<std::string, std::string>* mirror_uuids_to_name) {
1124 mirror_uuids_to_name->clear();
1125 for (auto& peer : mirror_peers) {
1126 if (!peer.mirror_uuid.empty() && !peer.site_name.empty()) {
1127 (*mirror_uuids_to_name)[peer.mirror_uuid] = peer.site_name;
1128 }
1129 }
1130}
1131
1132void populate_unknown_mirror_image_site_statuses(
1133 const std::vector<librbd::mirror_peer_site_t>& mirror_peers,
1134 librbd::mirror_image_global_status_t* global_status) {
1135 std::set<std::string> missing_mirror_uuids;
1136 librbd::mirror_peer_direction_t mirror_peer_direction =
1137 RBD_MIRROR_PEER_DIRECTION_RX_TX;
1138 for (auto& peer : mirror_peers) {
1139 if (peer.uuid == mirror_peers.begin()->uuid) {
1140 mirror_peer_direction = peer.direction;
1141 } else if (mirror_peer_direction != RBD_MIRROR_PEER_DIRECTION_RX_TX &&
1142 mirror_peer_direction != peer.direction) {
1143 mirror_peer_direction = RBD_MIRROR_PEER_DIRECTION_RX_TX;
1144 }
1145
1146 if (!peer.mirror_uuid.empty() &&
1147 peer.direction != RBD_MIRROR_PEER_DIRECTION_TX) {
1148 missing_mirror_uuids.insert(peer.mirror_uuid);
1149 }
1150 }
1151
1152 if (mirror_peer_direction != RBD_MIRROR_PEER_DIRECTION_TX) {
1153 missing_mirror_uuids.insert(RBD_MIRROR_IMAGE_STATUS_LOCAL_MIRROR_UUID);
1154 }
1155
1156 std::vector<librbd::mirror_image_site_status_t> site_statuses;
1157 site_statuses.reserve(missing_mirror_uuids.size());
1158
1159 for (auto& site_status : global_status->site_statuses) {
1160 if (missing_mirror_uuids.count(site_status.mirror_uuid) > 0) {
1161 missing_mirror_uuids.erase(site_status.mirror_uuid);
1162 site_statuses.push_back(site_status);
1163 }
1164 }
1165
1166 for (auto& mirror_uuid : missing_mirror_uuids) {
1167 site_statuses.push_back({mirror_uuid, MIRROR_IMAGE_STATUS_STATE_UNKNOWN,
1168 "status not found", 0, false});
1169 }
1170
1171 std::swap(global_status->site_statuses, site_statuses);
1172}
1173
1174int mgr_command(librados::Rados& rados, const std::string& cmd,
1175 const std::map<std::string, std::string> &args,
1176 std::ostream *out_os, std::ostream *err_os) {
1177 std::string command = R"(
1178 {
1179 "prefix": ")" + cmd + R"(", )" + mgr_command_args_to_str(args) + R"(
1180 })";
1181
1182 bufferlist in_bl;
1183 bufferlist out_bl;
1184 std::string outs;
1185 int r = rados.mgr_command(command, in_bl, &out_bl, &outs);
1186 if (r < 0) {
1187 (*err_os) << "rbd: " << cmd << " failed: " << cpp_strerror(r);
1188 if (!outs.empty()) {
1189 (*err_os) << ": " << outs;
1190 }
1191 (*err_os) << std::endl;
1192 return r;
1193 }
1194
1195 if (out_bl.length() != 0) {
1196 (*out_os) << out_bl.c_str();
1197 }
1198
1199 return 0;
1200}
1201
7c673cae
FG
1202} // namespace utils
1203} // namespace rbd