]> git.proxmox.com Git - ceph.git/blob - ceph/src/tools/rbd/Utils.cc
update sources to ceph Nautilus 14.2.1
[ceph.git] / ceph / src / tools / rbd / Utils.cc
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"
5 #include "include/ceph_assert.h"
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"
13 #include "common/safe_io.h"
14 #include "global/global_context.h"
15 #include <iostream>
16 #include <regex>
17 #include <boost/algorithm/string.hpp>
18 #include <boost/lexical_cast.hpp>
19
20 namespace rbd {
21 namespace utils {
22
23 namespace at = argument_types;
24 namespace po = boost::program_options;
25
26 int ProgressContext::update_progress(uint64_t offset, uint64_t total) {
27 if (progress) {
28 int pc = total ? (offset * 100ull / total) : 0;
29 if (pc != last_pc) {
30 cerr << "\r" << operation << ": "
31 << pc << "% complete...";
32 cerr.flush();
33 last_pc = pc;
34 }
35 }
36 return 0;
37 }
38
39 void ProgressContext::finish() {
40 if (progress) {
41 cerr << "\r" << operation << ": 100% complete...done." << std::endl;
42 }
43 }
44
45 void ProgressContext::fail() {
46 if (progress) {
47 cerr << "\r" << operation << ": " << last_pc << "% complete...failed."
48 << std::endl;
49 }
50 }
51
52 void aio_context_callback(librbd::completion_t completion, void *arg)
53 {
54 librbd::RBD::AioCompletion *aio_completion =
55 reinterpret_cast<librbd::RBD::AioCompletion*>(completion);
56 Context *context = reinterpret_cast<Context *>(arg);
57 context->complete(aio_completion->get_return_value());
58 aio_completion->release();
59 }
60
61 int read_string(int fd, unsigned max, std::string *out) {
62 char buf[4];
63
64 int r = safe_read_exact(fd, buf, 4);
65 if (r < 0)
66 return r;
67
68 bufferlist bl;
69 bl.append(buf, 4);
70 auto p = bl.cbegin();
71 uint32_t len;
72 decode(len, p);
73 if (len > max)
74 return -EINVAL;
75
76 char sbuf[len];
77 r = safe_read_exact(fd, sbuf, len);
78 if (r < 0)
79 return r;
80 out->assign(sbuf, len);
81 return len;
82 }
83
84 int extract_spec(const std::string &spec, std::string *pool_name,
85 std::string *namespace_name, std::string *name,
86 std::string *snap_name, SpecValidation spec_validation) {
87 if (!g_ceph_context->_conf.get_val<bool>("rbd_validate_names")) {
88 spec_validation = SPEC_VALIDATION_NONE;
89 }
90
91 std::regex pattern;
92 switch (spec_validation) {
93 case SPEC_VALIDATION_FULL:
94 // disallow "/" and "@" in all names
95 pattern = "^(?:([^/@]+)/(?:([^/@]+)/)?)?([^/@]+)(?:@([^/@]+))?$";
96 break;
97 case SPEC_VALIDATION_SNAP:
98 // disallow "/" and "@" in snap name
99 pattern = "^(?:([^/]+)/(?:([^/@]+)/)?)?([^@]+)(?:@([^/@]+))?$";
100 break;
101 case SPEC_VALIDATION_NONE:
102 // relaxed pattern assumes pool is before first "/",
103 // namespace is before second "/", and snap name is after first "@"
104 pattern = "^(?:([^/]+)/(?:([^/@]+)/)?)?([^@]+)(?:@(.+))?$";
105 break;
106 default:
107 ceph_abort();
108 break;
109 }
110
111 std::smatch match;
112 if (!std::regex_match(spec, match, pattern)) {
113 std::cerr << "rbd: invalid spec '" << spec << "'" << std::endl;
114 return -EINVAL;
115 }
116
117 if (match[1].matched) {
118 if (pool_name != nullptr) {
119 *pool_name = match[1];
120 } else {
121 std::cerr << "rbd: pool name specified for a command that doesn't use it"
122 << std::endl;
123 return -EINVAL;
124 }
125 }
126
127 if (match[2].matched) {
128 if (namespace_name != nullptr) {
129 *namespace_name = match[2];
130 } else {
131 std::cerr << "rbd: namespace name specified for a command that doesn't "
132 << "use it" << std::endl;
133 return -EINVAL;
134 }
135 }
136
137 if (name != nullptr) {
138 *name = match[3];
139 }
140
141 if (match[4].matched) {
142 if (snap_name != nullptr) {
143 *snap_name = match[4];
144 } else {
145 std::cerr << "rbd: snapshot name specified for a command that doesn't "
146 << "use it" << std::endl;
147 return -EINVAL;
148 }
149 }
150 return 0;
151 }
152
153 std::string get_positional_argument(const po::variables_map &vm, size_t index) {
154 if (vm.count(at::POSITIONAL_ARGUMENTS) == 0) {
155 return "";
156 }
157
158 const std::vector<std::string> &args =
159 boost::any_cast<std::vector<std::string> >(
160 vm[at::POSITIONAL_ARGUMENTS].value());
161 if (index < args.size()) {
162 return args[index];
163 }
164 return "";
165 }
166
167 std::string get_default_pool_name() {
168 return g_ceph_context->_conf.get_val<std::string>("rbd_default_pool");
169 }
170
171 int get_pool_and_namespace_names(
172 const boost::program_options::variables_map &vm,
173 bool default_empty_pool_name, bool validate_pool_name,
174 std::string* pool_name, std::string* namespace_name, size_t *arg_index) {
175 if (namespace_name != nullptr && vm.count(at::NAMESPACE_NAME)) {
176 *namespace_name = vm[at::NAMESPACE_NAME].as<std::string>();
177 }
178
179 if (vm.count(at::POOL_NAME)) {
180 *pool_name = vm[at::POOL_NAME].as<std::string>();
181 } else {
182 *pool_name = get_positional_argument(vm, *arg_index);
183 if (!pool_name->empty()) {
184 if (namespace_name != nullptr) {
185 auto slash_pos = pool_name->find_last_of('/');
186 if (slash_pos != std::string::npos) {
187 *namespace_name = pool_name->substr(slash_pos + 1);
188 }
189 *pool_name = pool_name->substr(0, slash_pos);
190 }
191 ++(*arg_index);
192 }
193 }
194
195 if (default_empty_pool_name && pool_name->empty()) {
196 *pool_name = get_default_pool_name();
197 }
198
199 if (!g_ceph_context->_conf.get_val<bool>("rbd_validate_names")) {
200 validate_pool_name = false;
201 }
202
203 if (validate_pool_name &&
204 pool_name->find_first_of("/@") != std::string::npos) {
205 std::cerr << "rbd: invalid pool '" << *pool_name << "'" << std::endl;
206 return -EINVAL;
207 } else if (namespace_name != nullptr &&
208 namespace_name->find_first_of("/@") != std::string::npos) {
209 std::cerr << "rbd: invalid namespace '" << *namespace_name << "'"
210 << std::endl;
211 return -EINVAL;
212 }
213
214 return 0;
215 }
216
217 int get_pool_image_id(const po::variables_map &vm,
218 size_t *spec_arg_index,
219 std::string *pool_name,
220 std::string *namespace_name,
221 std::string *image_id) {
222
223 if (vm.count(at::POOL_NAME) && pool_name != nullptr) {
224 *pool_name = vm[at::POOL_NAME].as<std::string>();
225 }
226 if (vm.count(at::NAMESPACE_NAME) && namespace_name != nullptr) {
227 *namespace_name = vm[at::NAMESPACE_NAME].as<std::string>();
228 }
229 if (vm.count(at::IMAGE_ID) && image_id != nullptr) {
230 *image_id = vm[at::IMAGE_ID].as<std::string>();
231 }
232
233 int r;
234 if (image_id != nullptr && spec_arg_index != nullptr && image_id->empty()) {
235 std::string spec = get_positional_argument(vm, (*spec_arg_index)++);
236 if (!spec.empty()) {
237 r = extract_spec(spec, pool_name, namespace_name, image_id, nullptr,
238 SPEC_VALIDATION_FULL);
239 if (r < 0) {
240 return r;
241 }
242 }
243 }
244
245 if (pool_name != nullptr && pool_name->empty()) {
246 *pool_name = get_default_pool_name();
247 }
248
249 if (image_id != nullptr && image_id->empty()) {
250 std::cerr << "rbd: image id was not specified" << std::endl;
251 return -EINVAL;
252 }
253
254 return 0;
255 }
256
257 int get_pool_image_snapshot_names(const po::variables_map &vm,
258 at::ArgumentModifier mod,
259 size_t *spec_arg_index,
260 std::string *pool_name,
261 std::string *namespace_name,
262 std::string *image_name,
263 std::string *snap_name,
264 bool image_name_required,
265 SnapshotPresence snapshot_presence,
266 SpecValidation spec_validation) {
267 std::string pool_key = (mod == at::ARGUMENT_MODIFIER_DEST ?
268 at::DEST_POOL_NAME : at::POOL_NAME);
269 std::string image_key = (mod == at::ARGUMENT_MODIFIER_DEST ?
270 at::DEST_IMAGE_NAME : at::IMAGE_NAME);
271 return get_pool_generic_snapshot_names(vm, mod, spec_arg_index, pool_key,
272 pool_name, namespace_name, image_key,
273 "image", image_name, snap_name,
274 image_name_required, snapshot_presence,
275 spec_validation);
276 }
277
278 int get_pool_generic_snapshot_names(const po::variables_map &vm,
279 at::ArgumentModifier mod,
280 size_t *spec_arg_index,
281 const std::string& pool_key,
282 std::string *pool_name,
283 std::string *namespace_name,
284 const std::string& generic_key,
285 const std::string& generic_key_desc,
286 std::string *generic_name,
287 std::string *snap_name,
288 bool generic_name_required,
289 SnapshotPresence snapshot_presence,
290 SpecValidation spec_validation) {
291 std::string namespace_key = (mod == at::ARGUMENT_MODIFIER_DEST ?
292 at::DEST_NAMESPACE_NAME : at::NAMESPACE_NAME);
293 std::string snap_key = (mod == at::ARGUMENT_MODIFIER_DEST ?
294 at::DEST_SNAPSHOT_NAME : at::SNAPSHOT_NAME);
295
296 if (vm.count(pool_key) && pool_name != nullptr) {
297 *pool_name = vm[pool_key].as<std::string>();
298 }
299 if (vm.count(namespace_key) && namespace_name != nullptr) {
300 *namespace_name = vm[namespace_key].as<std::string>();
301 }
302 if (vm.count(generic_key) && generic_name != nullptr) {
303 *generic_name = vm[generic_key].as<std::string>();
304 }
305 if (vm.count(snap_key) && snap_name != nullptr) {
306 *snap_name = vm[snap_key].as<std::string>();
307 }
308
309 int r;
310 if ((generic_key == at::IMAGE_NAME || generic_key == at::DEST_IMAGE_NAME) &&
311 generic_name != nullptr && !generic_name->empty()) {
312 // despite the separate pool and snapshot name options,
313 // we can also specify them via the image option
314 std::string image_name_copy(*generic_name);
315 r = extract_spec(image_name_copy, pool_name, namespace_name, generic_name,
316 snap_name, spec_validation);
317 if (r < 0) {
318 return r;
319 }
320 }
321
322 if (generic_name != nullptr && spec_arg_index != nullptr &&
323 generic_name->empty()) {
324 std::string spec = get_positional_argument(vm, (*spec_arg_index)++);
325 if (!spec.empty()) {
326 r = extract_spec(spec, pool_name, namespace_name, generic_name, snap_name,
327 spec_validation);
328 if (r < 0) {
329 return r;
330 }
331 }
332 }
333
334 if (pool_name != nullptr && pool_name->empty()) {
335 *pool_name = get_default_pool_name();
336 }
337
338 if (generic_name != nullptr && generic_name_required &&
339 generic_name->empty()) {
340 std::string prefix = at::get_description_prefix(mod);
341 std::cerr << "rbd: "
342 << (mod == at::ARGUMENT_MODIFIER_DEST ? prefix : std::string())
343 << generic_key_desc << " name was not specified" << std::endl;
344 return -EINVAL;
345 }
346
347 std::regex pattern("^[^@/]+?$");
348 if (spec_validation == SPEC_VALIDATION_FULL) {
349 // validate pool name while creating/renaming/copying/cloning/importing/etc
350 if ((pool_name != nullptr) && !std::regex_match (*pool_name, pattern)) {
351 std::cerr << "rbd: invalid pool name '" << *pool_name << "'" << std::endl;
352 return -EINVAL;
353 }
354 }
355
356 if (namespace_name != nullptr && !namespace_name->empty() &&
357 !std::regex_match (*namespace_name, pattern)) {
358 std::cerr << "rbd: invalid namespace name '" << *namespace_name << "'"
359 << std::endl;
360 return -EINVAL;
361 }
362
363 if (snap_name != nullptr) {
364 r = validate_snapshot_name(mod, *snap_name, snapshot_presence,
365 spec_validation);
366 if (r < 0) {
367 return r;
368 }
369 }
370 return 0;
371 }
372
373 int validate_snapshot_name(at::ArgumentModifier mod,
374 const std::string &snap_name,
375 SnapshotPresence snapshot_presence,
376 SpecValidation spec_validation) {
377 std::string prefix = at::get_description_prefix(mod);
378 switch (snapshot_presence) {
379 case SNAPSHOT_PRESENCE_PERMITTED:
380 break;
381 case SNAPSHOT_PRESENCE_NONE:
382 if (!snap_name.empty()) {
383 std::cerr << "rbd: "
384 << (mod == at::ARGUMENT_MODIFIER_DEST ? prefix : std::string())
385 << "snapshot name specified for a command that doesn't use it"
386 << std::endl;
387 return -EINVAL;
388 }
389 break;
390 case SNAPSHOT_PRESENCE_REQUIRED:
391 if (snap_name.empty()) {
392 std::cerr << "rbd: "
393 << (mod == at::ARGUMENT_MODIFIER_DEST ? prefix : std::string())
394 << "snapshot name was not specified" << std::endl;
395 return -EINVAL;
396 }
397 break;
398 }
399
400 if (spec_validation == SPEC_VALIDATION_SNAP) {
401 // disallow "/" and "@" in snap name
402 std::regex pattern("^[^@/]*?$");
403 if (!std::regex_match (snap_name, pattern)) {
404 std::cerr << "rbd: invalid snap name '" << snap_name << "'" << std::endl;
405 return -EINVAL;
406 }
407 }
408 return 0;
409 }
410
411 int get_image_options(const boost::program_options::variables_map &vm,
412 bool get_format, librbd::ImageOptions *opts) {
413 uint64_t order = 0, stripe_unit = 0, stripe_count = 0, object_size = 0;
414 uint64_t features = 0, features_clear = 0;
415 std::string data_pool;
416 bool order_specified = true;
417 bool features_specified = false;
418 bool features_clear_specified = false;
419 bool stripe_specified = false;
420
421 if (vm.count(at::IMAGE_ORDER)) {
422 order = vm[at::IMAGE_ORDER].as<uint64_t>();
423 std::cerr << "rbd: --order is deprecated, use --object-size"
424 << std::endl;
425 } else if (vm.count(at::IMAGE_OBJECT_SIZE)) {
426 object_size = vm[at::IMAGE_OBJECT_SIZE].as<uint64_t>();
427 order = std::round(std::log2(object_size));
428 } else {
429 order_specified = false;
430 }
431
432 if (vm.count(at::IMAGE_FEATURES)) {
433 features = vm[at::IMAGE_FEATURES].as<uint64_t>();
434 features_specified = true;
435 } else {
436 features = get_rbd_default_features(g_ceph_context);
437 }
438
439 if (vm.count(at::IMAGE_STRIPE_UNIT)) {
440 stripe_unit = vm[at::IMAGE_STRIPE_UNIT].as<uint64_t>();
441 stripe_specified = true;
442 }
443
444 if (vm.count(at::IMAGE_STRIPE_COUNT)) {
445 stripe_count = vm[at::IMAGE_STRIPE_COUNT].as<uint64_t>();
446 stripe_specified = true;
447 }
448
449 if (vm.count(at::IMAGE_SHARED) && vm[at::IMAGE_SHARED].as<bool>()) {
450 if (features_specified) {
451 features &= ~RBD_FEATURES_SINGLE_CLIENT;
452 } else {
453 features_clear |= RBD_FEATURES_SINGLE_CLIENT;
454 features_clear_specified = true;
455 }
456 }
457
458 if (vm.count(at::IMAGE_DATA_POOL)) {
459 data_pool = vm[at::IMAGE_DATA_POOL].as<std::string>();
460 }
461
462 if (get_format) {
463 uint64_t format = 0;
464 bool format_specified = false;
465 if (vm.count(at::IMAGE_NEW_FORMAT)) {
466 format = 2;
467 format_specified = true;
468 } else if (vm.count(at::IMAGE_FORMAT)) {
469 format = vm[at::IMAGE_FORMAT].as<uint32_t>();
470 format_specified = true;
471 }
472 if (format == 1) {
473 std::cerr << "rbd: image format 1 is deprecated" << std::endl;
474 }
475
476 if (features_specified && features != 0) {
477 if (format_specified && format == 1) {
478 std::cerr << "rbd: features not allowed with format 1; "
479 << "use --image-format 2" << std::endl;
480 return -EINVAL;
481 } else {
482 format = 2;
483 format_specified = true;
484 }
485 }
486
487 if ((stripe_unit || stripe_count) &&
488 (stripe_unit != (1ull << order) && stripe_count != 1)) {
489 if (format_specified && format == 1) {
490 std::cerr << "rbd: non-default striping 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 (!data_pool.empty()) {
500 if (format_specified && format == 1) {
501 std::cerr << "rbd: data pool not allowed with format 1; "
502 << "use --image-format 2" << std::endl;
503 return -EINVAL;
504 } else {
505 format = 2;
506 format_specified = true;
507 }
508 }
509
510 if (format_specified) {
511 int r = g_conf().set_val("rbd_default_format", stringify(format));
512 ceph_assert(r == 0);
513 opts->set(RBD_IMAGE_OPTION_FORMAT, format);
514 }
515 }
516
517 if (order_specified)
518 opts->set(RBD_IMAGE_OPTION_ORDER, order);
519 if (features_specified)
520 opts->set(RBD_IMAGE_OPTION_FEATURES, features);
521 if (features_clear_specified) {
522 opts->set(RBD_IMAGE_OPTION_FEATURES_CLEAR, features_clear);
523 }
524 if (stripe_specified) {
525 opts->set(RBD_IMAGE_OPTION_STRIPE_UNIT, stripe_unit);
526 opts->set(RBD_IMAGE_OPTION_STRIPE_COUNT, stripe_count);
527 }
528 if (!data_pool.empty()) {
529 opts->set(RBD_IMAGE_OPTION_DATA_POOL, data_pool);
530 }
531 int r = get_journal_options(vm, opts);
532 if (r < 0) {
533 return r;
534 }
535
536 r = get_flatten_option(vm, opts);
537 if (r < 0) {
538 return r;
539 }
540
541 return 0;
542 }
543
544 int get_journal_options(const boost::program_options::variables_map &vm,
545 librbd::ImageOptions *opts) {
546
547 if (vm.count(at::JOURNAL_OBJECT_SIZE)) {
548 uint64_t size = vm[at::JOURNAL_OBJECT_SIZE].as<uint64_t>();
549 uint64_t order = 12;
550 while ((1ULL << order) < size) {
551 order++;
552 }
553 opts->set(RBD_IMAGE_OPTION_JOURNAL_ORDER, order);
554
555 int r = g_conf().set_val("rbd_journal_order", stringify(order));
556 ceph_assert(r == 0);
557 }
558 if (vm.count(at::JOURNAL_SPLAY_WIDTH)) {
559 opts->set(RBD_IMAGE_OPTION_JOURNAL_SPLAY_WIDTH,
560 vm[at::JOURNAL_SPLAY_WIDTH].as<uint64_t>());
561
562 int r = g_conf().set_val("rbd_journal_splay_width",
563 stringify(
564 vm[at::JOURNAL_SPLAY_WIDTH].as<uint64_t>()));
565 ceph_assert(r == 0);
566 }
567 if (vm.count(at::JOURNAL_POOL)) {
568 opts->set(RBD_IMAGE_OPTION_JOURNAL_POOL,
569 vm[at::JOURNAL_POOL].as<std::string>());
570
571 int r = g_conf().set_val("rbd_journal_pool",
572 vm[at::JOURNAL_POOL].as<std::string>());
573 ceph_assert(r == 0);
574 }
575
576 return 0;
577 }
578
579 int get_flatten_option(const boost::program_options::variables_map &vm,
580 librbd::ImageOptions *opts) {
581 if (vm.count(at::IMAGE_FLATTEN) && vm[at::IMAGE_FLATTEN].as<bool>()) {
582 uint64_t flatten = 1;
583 opts->set(RBD_IMAGE_OPTION_FLATTEN, flatten);
584 }
585 return 0;
586 }
587
588 int get_image_size(const boost::program_options::variables_map &vm,
589 uint64_t *size) {
590 if (vm.count(at::IMAGE_SIZE) == 0) {
591 std::cerr << "rbd: must specify --size <M/G/T>" << std::endl;
592 return -EINVAL;
593 }
594
595 *size = vm[at::IMAGE_SIZE].as<uint64_t>();
596 return 0;
597 }
598
599 int get_path(const boost::program_options::variables_map &vm,
600 size_t *arg_index, std::string *path) {
601 if (vm.count(at::PATH)) {
602 *path = vm[at::PATH].as<std::string>();
603 } else {
604 *path = get_positional_argument(vm, *arg_index);
605 if (!path->empty()) {
606 ++(*arg_index);
607 }
608 }
609
610 if (path->empty()) {
611 std::cerr << "rbd: path was not specified" << std::endl;
612 return -EINVAL;
613 }
614 return 0;
615 }
616
617 int get_formatter(const po::variables_map &vm,
618 at::Format::Formatter *formatter) {
619 if (vm.count(at::FORMAT)) {
620 bool pretty = vm[at::PRETTY_FORMAT].as<bool>();
621 *formatter = vm[at::FORMAT].as<at::Format>().create_formatter(pretty);
622 if (*formatter == nullptr && pretty) {
623 std::cerr << "rbd: --pretty-format only works when --format "
624 << "is json or xml" << std::endl;
625 return -EINVAL;
626 } else if (*formatter != nullptr && !pretty) {
627 formatter->get()->enable_line_break();
628 }
629 } else if (vm[at::PRETTY_FORMAT].as<bool>()) {
630 std::cerr << "rbd: --pretty-format only works when --format "
631 << "is json or xml" << std::endl;
632 return -EINVAL;
633 }
634 return 0;
635 }
636
637 void init_context() {
638 g_conf().set_val_or_die("rbd_cache_writethrough_until_flush", "false");
639 g_conf().apply_changes(nullptr);
640 common_init_finish(g_ceph_context);
641 }
642
643 int init_rados(librados::Rados *rados) {
644 init_context();
645
646 int r = rados->init_with_context(g_ceph_context);
647 if (r < 0) {
648 std::cerr << "rbd: couldn't initialize rados!" << std::endl;
649 return r;
650 }
651
652 r = rados->connect();
653 if (r < 0) {
654 std::cerr << "rbd: couldn't connect to the cluster!" << std::endl;
655 return r;
656 }
657
658 return 0;
659 }
660
661 int init(const std::string &pool_name, const std::string& namespace_name,
662 librados::Rados *rados, librados::IoCtx *io_ctx) {
663 init_context();
664
665 int r = init_rados(rados);
666 if (r < 0) {
667 return r;
668 }
669
670 r = init_io_ctx(*rados, pool_name, namespace_name, io_ctx);
671 if (r < 0) {
672 return r;
673 }
674 return 0;
675 }
676
677 int init_io_ctx(librados::Rados &rados, const std::string &pool_name,
678 const std::string& namespace_name, librados::IoCtx *io_ctx) {
679 int r = rados.ioctx_create(pool_name.c_str(), *io_ctx);
680 if (r < 0) {
681 if (r == -ENOENT && pool_name == get_default_pool_name()) {
682 std::cerr << "rbd: error opening default pool "
683 << "'" << pool_name << "'" << std::endl
684 << "Ensure that the default pool has been created or specify "
685 << "an alternate pool name." << std::endl;
686 } else {
687 std::cerr << "rbd: error opening pool '" << pool_name << "': "
688 << cpp_strerror(r) << std::endl;
689 }
690 return r;
691 }
692
693 return set_namespace(namespace_name, io_ctx);
694 }
695
696 int set_namespace(const std::string& namespace_name, librados::IoCtx *io_ctx) {
697 if (!namespace_name.empty()) {
698 librbd::RBD rbd;
699 bool exists = false;
700 int r = rbd.namespace_exists(*io_ctx, namespace_name.c_str(), &exists);
701 if (r < 0) {
702 std::cerr << "rbd: error asserting namespace: "
703 << cpp_strerror(r) << std::endl;
704 return r;
705 }
706 if (!exists) {
707 std::cerr << "rbd: namespace '" << namespace_name << "' does not exist."
708 << std::endl;
709 return -ENOENT;
710 }
711 }
712 io_ctx->set_namespace(namespace_name);
713 return 0;
714 }
715
716 void disable_cache() {
717 g_conf().set_val_or_die("rbd_cache", "false");
718 }
719
720 int open_image(librados::IoCtx &io_ctx, const std::string &image_name,
721 bool read_only, librbd::Image *image) {
722 int r;
723 librbd::RBD rbd;
724 if (read_only) {
725 r = rbd.open_read_only(io_ctx, *image, image_name.c_str(), NULL);
726 } else {
727 r = rbd.open(io_ctx, *image, image_name.c_str());
728 }
729
730 if (r < 0) {
731 std::cerr << "rbd: error opening image " << image_name << ": "
732 << cpp_strerror(r) << std::endl;
733 return r;
734 }
735 return 0;
736 }
737
738 int open_image_by_id(librados::IoCtx &io_ctx, const std::string &image_id,
739 bool read_only, librbd::Image *image) {
740 int r;
741 librbd::RBD rbd;
742 if (read_only) {
743 r = rbd.open_by_id_read_only(io_ctx, *image, image_id.c_str(), NULL);
744 } else {
745 r = rbd.open_by_id(io_ctx, *image, image_id.c_str());
746 }
747
748 if (r < 0) {
749 std::cerr << "rbd: error opening image with id " << image_id << ": "
750 << cpp_strerror(r) << std::endl;
751 return r;
752 }
753 return 0;
754 }
755
756 int init_and_open_image(const std::string &pool_name,
757 const std::string &namespace_name,
758 const std::string &image_name,
759 const std::string &image_id,
760 const std::string &snap_name, bool read_only,
761 librados::Rados *rados, librados::IoCtx *io_ctx,
762 librbd::Image *image) {
763 int r = init(pool_name, namespace_name, rados, io_ctx);
764 if (r < 0) {
765 return r;
766 }
767
768 if (image_id.empty()) {
769 r = open_image(*io_ctx, image_name, read_only, image);
770 } else {
771 r = open_image_by_id(*io_ctx, image_id, read_only, image);
772 }
773 if (r < 0) {
774 return r;
775 }
776
777 if (!snap_name.empty()) {
778 r = snap_set(*image, snap_name);
779 if (r < 0) {
780 return r;
781 }
782 }
783 return 0;
784 }
785
786 int snap_set(librbd::Image &image, const std::string &snap_name) {
787 int r = image.snap_set(snap_name.c_str());
788 if (r < 0) {
789 std::cerr << "error setting snapshot context: " << cpp_strerror(r)
790 << std::endl;
791 return r;
792 }
793 return 0;
794 }
795
796 void calc_sparse_extent(const bufferptr &bp,
797 size_t sparse_size,
798 size_t buffer_offset,
799 uint64_t buffer_length,
800 size_t *write_length,
801 bool *zeroed) {
802 if (sparse_size == 0) {
803 // sparse writes are disabled -- write the full extent
804 ceph_assert(buffer_offset == 0);
805 *write_length = buffer_length;
806 *zeroed = false;
807 return;
808 }
809
810 *write_length = 0;
811 size_t original_offset = buffer_offset;
812 while (buffer_offset < buffer_length) {
813 size_t extent_size = std::min<size_t>(
814 sparse_size, buffer_length - buffer_offset);
815
816 bufferptr extent(bp, buffer_offset, extent_size);
817
818 bool extent_is_zero = extent.is_zero();
819 if (original_offset == buffer_offset) {
820 *zeroed = extent_is_zero;
821 } else if (*zeroed != extent_is_zero) {
822 ceph_assert(*write_length > 0);
823 return;
824 }
825
826 buffer_offset += extent_size;
827 *write_length += extent_size;
828 }
829 }
830
831 std::string image_id(librbd::Image& image) {
832 std::string id;
833 int r = image.get_id(&id);
834 if (r < 0) {
835 return std::string();
836 }
837 return id;
838 }
839
840 std::string mirror_image_state(librbd::mirror_image_state_t state) {
841 switch (state) {
842 case RBD_MIRROR_IMAGE_DISABLING:
843 return "disabling";
844 case RBD_MIRROR_IMAGE_ENABLED:
845 return "enabled";
846 case RBD_MIRROR_IMAGE_DISABLED:
847 return "disabled";
848 default:
849 return "unknown";
850 }
851 }
852
853 std::string mirror_image_status_state(librbd::mirror_image_status_state_t state) {
854 switch (state) {
855 case MIRROR_IMAGE_STATUS_STATE_UNKNOWN:
856 return "unknown";
857 case MIRROR_IMAGE_STATUS_STATE_ERROR:
858 return "error";
859 case MIRROR_IMAGE_STATUS_STATE_SYNCING:
860 return "syncing";
861 case MIRROR_IMAGE_STATUS_STATE_STARTING_REPLAY:
862 return "starting_replay";
863 case MIRROR_IMAGE_STATUS_STATE_REPLAYING:
864 return "replaying";
865 case MIRROR_IMAGE_STATUS_STATE_STOPPING_REPLAY:
866 return "stopping_replay";
867 case MIRROR_IMAGE_STATUS_STATE_STOPPED:
868 return "stopped";
869 default:
870 return "unknown (" + stringify(static_cast<uint32_t>(state)) + ")";
871 }
872 }
873
874 std::string mirror_image_status_state(librbd::mirror_image_status_t status) {
875 return (status.up ? "up+" : "down+") +
876 mirror_image_status_state(status.state);
877 }
878
879 std::string timestr(time_t t) {
880 struct tm tm;
881
882 localtime_r(&t, &tm);
883
884 char buf[32];
885 strftime(buf, sizeof(buf), "%F %T", &tm);
886
887 return buf;
888 }
889
890 uint64_t get_rbd_default_features(CephContext* cct) {
891 auto features = cct->_conf.get_val<std::string>("rbd_default_features");
892 return boost::lexical_cast<uint64_t>(features);
893 }
894
895 bool is_not_user_snap_namespace(librbd::Image* image,
896 const librbd::snap_info_t &snap_info)
897 {
898 librbd::snap_namespace_type_t namespace_type;
899 int r = image->snap_get_namespace_type(snap_info.id, &namespace_type);
900 if (r < 0) {
901 return false;
902 }
903 return namespace_type != RBD_SNAP_NAMESPACE_TYPE_USER;
904 }
905
906 } // namespace utils
907 } // namespace rbd