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