]>
Commit | Line | Data |
---|---|---|
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" | |
5 | #include "include/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 <boost/regex.hpp> | |
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 | bufferlist::iterator p = bl.begin(); | |
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 *image_name, std::string *snap_name, | |
86 | SpecValidation spec_validation) { | |
181888fb | 87 | if (!g_ceph_context->_conf->get_val<bool>("rbd_validate_names")) { |
7c673cae FG |
88 | spec_validation = SPEC_VALIDATION_NONE; |
89 | } | |
90 | ||
91 | boost::regex pattern; | |
92 | switch (spec_validation) { | |
93 | case SPEC_VALIDATION_FULL: | |
94 | // disallow "/" and "@" in image and snap name | |
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 "/" and snap | |
103 | // name is after first "@" | |
104 | pattern = "^(?:([^/]+)/)?([^@]+)(?:@(.+))?$"; | |
105 | break; | |
106 | default: | |
107 | assert(false); | |
108 | break; | |
109 | } | |
110 | ||
111 | boost::smatch match; | |
112 | if (!boost::regex_match(spec, match, pattern)) { | |
113 | std::cerr << "rbd: invalid spec '" << spec << "'" << std::endl; | |
114 | return -EINVAL; | |
115 | } | |
116 | ||
117 | if (pool_name != nullptr && match[1].matched) { | |
118 | *pool_name = match[1]; | |
119 | } | |
120 | if (image_name != nullptr) { | |
121 | *image_name = match[2]; | |
122 | } | |
123 | if (snap_name != nullptr && match[3].matched) { | |
124 | *snap_name = match[3]; | |
125 | } | |
126 | return 0; | |
127 | } | |
128 | ||
129 | int extract_group_spec(const std::string &spec, | |
130 | std::string *pool_name, | |
131 | std::string *group_name) { | |
132 | boost::regex pattern; | |
133 | pattern = "^(?:([^/]+)/)?(.+)?$"; | |
134 | ||
135 | boost::smatch match; | |
136 | if (!boost::regex_match(spec, match, pattern)) { | |
137 | std::cerr << "rbd: invalid spec '" << spec << "'" << std::endl; | |
138 | return -EINVAL; | |
139 | } | |
140 | ||
141 | if (pool_name != nullptr && match[1].matched) { | |
142 | *pool_name = match[1]; | |
143 | } | |
144 | if (group_name != nullptr) { | |
145 | *group_name = match[2]; | |
146 | } | |
147 | ||
148 | return 0; | |
149 | } | |
150 | ||
151 | int extract_image_id_spec(const std::string &spec, std::string *pool_name, | |
152 | std::string *image_id) { | |
153 | boost::regex pattern; | |
154 | pattern = "^(?:([^/]+)/)?(.+)?$"; | |
155 | ||
156 | boost::smatch match; | |
157 | if (!boost::regex_match(spec, match, pattern)) { | |
158 | std::cerr << "rbd: invalid spec '" << spec << "'" << std::endl; | |
159 | return -EINVAL; | |
160 | } | |
161 | ||
162 | if (pool_name != nullptr && match[1].matched) { | |
163 | *pool_name = match[1]; | |
164 | } | |
165 | if (image_id != nullptr) { | |
166 | *image_id = match[2]; | |
167 | } | |
168 | ||
169 | return 0; | |
7c673cae FG |
170 | } |
171 | ||
172 | std::string get_positional_argument(const po::variables_map &vm, size_t index) { | |
173 | if (vm.count(at::POSITIONAL_ARGUMENTS) == 0) { | |
174 | return ""; | |
175 | } | |
176 | ||
177 | const std::vector<std::string> &args = | |
178 | boost::any_cast<std::vector<std::string> >( | |
179 | vm[at::POSITIONAL_ARGUMENTS].value()); | |
180 | if (index < args.size()) { | |
181 | return args[index]; | |
182 | } | |
183 | return ""; | |
184 | } | |
185 | ||
31f18b77 | 186 | std::string get_default_pool_name() { |
181888fb | 187 | return g_ceph_context->_conf->get_val<std::string>("rbd_default_pool"); |
31f18b77 FG |
188 | } |
189 | ||
190 | std::string get_pool_name(const po::variables_map &vm, size_t *arg_index) { | |
7c673cae FG |
191 | std::string pool_name; |
192 | if (vm.count(at::POOL_NAME)) { | |
193 | pool_name = vm[at::POOL_NAME].as<std::string>(); | |
194 | } else { | |
195 | pool_name = get_positional_argument(vm, *arg_index); | |
196 | if (!pool_name.empty()) { | |
197 | ++(*arg_index); | |
198 | } | |
199 | } | |
200 | ||
201 | if (pool_name.empty()) { | |
31f18b77 | 202 | pool_name = get_default_pool_name(); |
7c673cae FG |
203 | } |
204 | return pool_name; | |
205 | } | |
206 | ||
207 | int get_special_pool_group_names(const po::variables_map &vm, | |
208 | size_t *arg_index, | |
209 | std::string *group_pool_name, | |
210 | std::string *group_name) { | |
211 | if (nullptr == group_pool_name) return -EINVAL; | |
212 | if (nullptr == group_name) return -EINVAL; | |
213 | std::string pool_key = at::POOL_NAME; | |
214 | ||
215 | std::string group_pool_key = "group-" + at::POOL_NAME; | |
216 | std::string group_key = at::GROUP_NAME; | |
217 | ||
218 | if (vm.count(group_pool_key)) { | |
219 | *group_pool_name = vm[group_pool_key].as<std::string>(); | |
220 | } | |
221 | ||
222 | if (vm.count(group_key)) { | |
223 | *group_name = vm[group_key].as<std::string>(); | |
224 | } | |
225 | ||
226 | int r; | |
227 | if (group_name->empty()) { | |
228 | std::string spec = utils::get_positional_argument(vm, (*arg_index)++); | |
229 | if (!spec.empty()) { | |
230 | r = utils::extract_group_spec(spec, group_pool_name, group_name); | |
231 | if (r < 0) { | |
232 | return r; | |
233 | } | |
234 | } | |
235 | } | |
236 | ||
237 | if (group_pool_name->empty() && vm.count(pool_key)) { | |
238 | *group_pool_name = vm[pool_key].as<std::string>(); | |
239 | } | |
240 | ||
241 | if (group_pool_name->empty()) { | |
31f18b77 | 242 | *group_pool_name = get_default_pool_name(); |
7c673cae FG |
243 | } |
244 | ||
245 | if (group_name->empty()) { | |
246 | std::cerr << "rbd: consistency group name was not specified" << std::endl; | |
247 | return -EINVAL; | |
248 | } | |
249 | ||
250 | return 0; | |
251 | } | |
252 | ||
253 | int get_special_pool_image_names(const po::variables_map &vm, | |
254 | size_t *arg_index, | |
255 | std::string *image_pool_name, | |
256 | std::string *image_name) { | |
257 | if (nullptr == image_pool_name) return -EINVAL; | |
258 | if (nullptr == image_name) return -EINVAL; | |
259 | ||
260 | std::string pool_key = at::POOL_NAME; | |
261 | ||
262 | std::string image_pool_key = "image-" + at::POOL_NAME; | |
263 | std::string image_key = at::IMAGE_NAME; | |
264 | ||
265 | if (vm.count(image_pool_key)) { | |
266 | *image_pool_name = vm[image_pool_key].as<std::string>(); | |
267 | } | |
268 | ||
269 | if (vm.count(image_key)) { | |
270 | *image_name = vm[image_key].as<std::string>(); | |
271 | } | |
272 | ||
273 | int r; | |
274 | if (image_name->empty()) { | |
275 | std::string spec = utils::get_positional_argument(vm, (*arg_index)++); | |
276 | if (!spec.empty()) { | |
277 | r = utils::extract_spec(spec, image_pool_name, | |
278 | image_name, nullptr, | |
279 | utils::SPEC_VALIDATION_NONE); | |
280 | if (r < 0) { | |
281 | return r; | |
282 | } | |
283 | } | |
284 | } | |
285 | ||
286 | if (image_pool_name->empty() && vm.count(pool_key)) { | |
287 | *image_pool_name = vm[pool_key].as<std::string>(); | |
288 | } | |
289 | ||
290 | if (image_pool_name->empty()) { | |
31f18b77 | 291 | *image_pool_name = get_default_pool_name(); |
7c673cae FG |
292 | } |
293 | ||
294 | if (image_name->empty()) { | |
295 | std::cerr << "rbd: image name was not specified" << std::endl; | |
296 | return -EINVAL; | |
297 | } | |
298 | ||
299 | return 0; | |
300 | } | |
301 | ||
302 | int get_pool_image_id(const po::variables_map &vm, | |
303 | size_t *spec_arg_index, | |
304 | std::string *pool_name, | |
305 | std::string *image_id) { | |
306 | ||
307 | if (vm.count(at::POOL_NAME) && pool_name != nullptr) { | |
308 | *pool_name = vm[at::POOL_NAME].as<std::string>(); | |
309 | } | |
310 | if (vm.count(at::IMAGE_ID) && image_id != nullptr) { | |
311 | *image_id = vm[at::IMAGE_ID].as<std::string>(); | |
312 | } | |
313 | ||
314 | int r; | |
315 | if (image_id != nullptr && spec_arg_index != nullptr && image_id->empty()) { | |
316 | std::string spec = get_positional_argument(vm, (*spec_arg_index)++); | |
317 | if (!spec.empty()) { | |
318 | r = extract_image_id_spec(spec, pool_name, image_id); | |
319 | if (r < 0) { | |
320 | return r; | |
321 | } | |
322 | } | |
323 | } | |
324 | ||
31f18b77 FG |
325 | if (pool_name != nullptr && pool_name->empty()) { |
326 | *pool_name = get_default_pool_name(); | |
7c673cae FG |
327 | } |
328 | ||
329 | if (image_id != nullptr && image_id->empty()) { | |
330 | std::cerr << "rbd: image id was not specified" << std::endl; | |
331 | return -EINVAL; | |
332 | } | |
333 | ||
334 | return 0; | |
335 | } | |
336 | ||
337 | int get_pool_group_names(const po::variables_map &vm, | |
338 | at::ArgumentModifier mod, | |
339 | size_t *spec_arg_index, | |
340 | std::string *pool_name, | |
341 | std::string *group_name) { | |
342 | std::string pool_key = (mod == at::ARGUMENT_MODIFIER_DEST ? | |
343 | at::DEST_POOL_NAME : at::POOL_NAME); | |
344 | std::string group_key = (mod == at::ARGUMENT_MODIFIER_DEST ? | |
345 | at::DEST_GROUP_NAME : at::GROUP_NAME); | |
346 | ||
347 | if (vm.count(pool_key) && pool_name != nullptr) { | |
348 | *pool_name = vm[pool_key].as<std::string>(); | |
349 | } | |
350 | if (vm.count(group_key) && group_name != nullptr) { | |
351 | *group_name = vm[group_key].as<std::string>(); | |
352 | } | |
353 | ||
354 | int r; | |
355 | if (group_name != nullptr && spec_arg_index != nullptr && | |
356 | group_name->empty()) { | |
357 | std::string spec = get_positional_argument(vm, (*spec_arg_index)++); | |
358 | if (!spec.empty()) { | |
359 | r = extract_group_spec(spec, pool_name, group_name); | |
360 | if (r < 0) { | |
361 | return r; | |
362 | } | |
363 | } | |
364 | } | |
365 | ||
31f18b77 FG |
366 | if (pool_name != nullptr && pool_name->empty()) { |
367 | *pool_name = get_default_pool_name(); | |
7c673cae FG |
368 | } |
369 | ||
370 | if (group_name != nullptr && group_name->empty()) { | |
371 | std::string prefix = at::get_description_prefix(mod); | |
372 | std::cerr << "rbd: " | |
373 | << (mod == at::ARGUMENT_MODIFIER_DEST ? prefix : std::string()) | |
374 | << "group name was not specified" << std::endl; | |
375 | return -EINVAL; | |
376 | } | |
377 | ||
378 | return 0; | |
379 | } | |
380 | ||
381 | int get_pool_image_snapshot_names(const po::variables_map &vm, | |
382 | at::ArgumentModifier mod, | |
383 | size_t *spec_arg_index, | |
384 | std::string *pool_name, | |
385 | std::string *image_name, | |
386 | std::string *snap_name, | |
387 | SnapshotPresence snapshot_presence, | |
388 | SpecValidation spec_validation, | |
389 | bool image_required) { | |
390 | std::string pool_key = (mod == at::ARGUMENT_MODIFIER_DEST ? | |
391 | at::DEST_POOL_NAME : at::POOL_NAME); | |
392 | std::string image_key = (mod == at::ARGUMENT_MODIFIER_DEST ? | |
393 | at::DEST_IMAGE_NAME : at::IMAGE_NAME); | |
394 | std::string snap_key = (mod == at::ARGUMENT_MODIFIER_DEST ? | |
395 | at::DEST_SNAPSHOT_NAME : at::SNAPSHOT_NAME); | |
396 | ||
397 | if (vm.count(pool_key) && pool_name != nullptr) { | |
398 | *pool_name = vm[pool_key].as<std::string>(); | |
399 | } | |
400 | if (vm.count(image_key) && image_name != nullptr) { | |
401 | *image_name = vm[image_key].as<std::string>(); | |
402 | } | |
403 | if (vm.count(snap_key) && snap_name != nullptr) { | |
404 | *snap_name = vm[snap_key].as<std::string>(); | |
405 | } | |
406 | ||
407 | int r; | |
408 | if (image_name != nullptr && !image_name->empty()) { | |
409 | // despite the separate pool and snapshot name options, | |
410 | // we can also specify them via the image option | |
411 | std::string image_name_copy(*image_name); | |
412 | r = extract_spec(image_name_copy, pool_name, image_name, snap_name, | |
413 | spec_validation); | |
414 | if (r < 0) { | |
415 | return r; | |
416 | } | |
417 | } | |
418 | ||
419 | if (image_name != nullptr && spec_arg_index != nullptr && | |
420 | image_name->empty()) { | |
421 | std::string spec = get_positional_argument(vm, (*spec_arg_index)++); | |
422 | if (!spec.empty()) { | |
423 | r = extract_spec(spec, pool_name, image_name, snap_name, spec_validation); | |
424 | if (r < 0) { | |
425 | return r; | |
426 | } | |
427 | } | |
428 | } | |
429 | ||
430 | if (pool_name != nullptr && pool_name->empty()) { | |
31f18b77 | 431 | *pool_name = get_default_pool_name(); |
7c673cae FG |
432 | } |
433 | ||
434 | if (image_name != nullptr && image_required && image_name->empty()) { | |
435 | std::string prefix = at::get_description_prefix(mod); | |
436 | std::cerr << "rbd: " | |
437 | << (mod == at::ARGUMENT_MODIFIER_DEST ? prefix : std::string()) | |
438 | << "image name was not specified" << std::endl; | |
439 | return -EINVAL; | |
440 | } | |
441 | ||
31f18b77 | 442 | //Validate pool name while creating/renaming/copying/cloning/importing/etc |
7c673cae | 443 | if (spec_validation == SPEC_VALIDATION_FULL) { |
31f18b77 | 444 | boost::regex pattern("^[^@/]+?$"); |
7c673cae | 445 | if (!boost::regex_match (*pool_name, pattern)) { |
31f18b77 | 446 | std::cerr << "rbd: invalid pool name '" << *pool_name << "'" << std::endl; |
7c673cae FG |
447 | return -EINVAL; |
448 | } | |
449 | } | |
450 | ||
451 | if (snap_name != nullptr) { | |
452 | r = validate_snapshot_name(mod, *snap_name, snapshot_presence, | |
453 | spec_validation); | |
454 | if (r < 0) { | |
455 | return r; | |
456 | } | |
457 | } | |
458 | return 0; | |
459 | } | |
460 | ||
461 | int get_pool_snapshot_names(const po::variables_map &vm, | |
462 | at::ArgumentModifier mod, | |
463 | size_t *spec_arg_index, | |
464 | std::string *pool_name, | |
465 | std::string *snap_name, | |
466 | SnapshotPresence snapshot_presence, | |
467 | SpecValidation spec_validation) { | |
468 | std::string pool_key = (mod == at::ARGUMENT_MODIFIER_DEST ? | |
469 | at::DEST_POOL_NAME : at::POOL_NAME); | |
470 | std::string snap_key = (mod == at::ARGUMENT_MODIFIER_DEST ? | |
471 | at::DEST_SNAPSHOT_NAME : at::SNAPSHOT_NAME); | |
472 | ||
473 | if (vm.count(pool_key) && pool_name != nullptr) { | |
474 | *pool_name = vm[pool_key].as<std::string>(); | |
475 | } | |
476 | if (vm.count(snap_key) && snap_name != nullptr) { | |
477 | *snap_name = vm[snap_key].as<std::string>(); | |
478 | } | |
479 | ||
480 | if (pool_name != nullptr && pool_name->empty()) { | |
31f18b77 | 481 | *pool_name = get_default_pool_name(); |
7c673cae FG |
482 | } |
483 | ||
484 | if (snap_name != nullptr) { | |
485 | int r = validate_snapshot_name(mod, *snap_name, snapshot_presence, | |
486 | spec_validation); | |
487 | if (r < 0) { | |
488 | return r; | |
489 | } | |
490 | } | |
491 | return 0; | |
492 | } | |
493 | ||
494 | int get_pool_journal_names(const po::variables_map &vm, | |
495 | at::ArgumentModifier mod, | |
496 | size_t *spec_arg_index, | |
497 | std::string *pool_name, | |
498 | std::string *journal_name) { | |
499 | std::string pool_key = (mod == at::ARGUMENT_MODIFIER_DEST ? | |
500 | at::DEST_POOL_NAME : at::POOL_NAME); | |
501 | std::string image_key = (mod == at::ARGUMENT_MODIFIER_DEST ? | |
502 | at::DEST_IMAGE_NAME : at::IMAGE_NAME); | |
503 | std::string journal_key = (mod == at::ARGUMENT_MODIFIER_DEST ? | |
504 | at::DEST_JOURNAL_NAME : at::JOURNAL_NAME); | |
505 | ||
506 | if (vm.count(pool_key) && pool_name != nullptr) { | |
507 | *pool_name = vm[pool_key].as<std::string>(); | |
508 | } | |
509 | if (vm.count(journal_key) && journal_name != nullptr) { | |
510 | *journal_name = vm[journal_key].as<std::string>(); | |
511 | } | |
512 | ||
513 | std::string image_name; | |
514 | if (vm.count(image_key)) { | |
515 | image_name = vm[image_key].as<std::string>(); | |
516 | } | |
517 | ||
518 | int r; | |
519 | if (journal_name != nullptr && !journal_name->empty()) { | |
520 | // despite the separate pool option, | |
521 | // we can also specify them via the journal option | |
522 | std::string journal_name_copy(*journal_name); | |
523 | r = extract_spec(journal_name_copy, pool_name, journal_name, nullptr, | |
524 | SPEC_VALIDATION_FULL); | |
525 | if (r < 0) { | |
526 | return r; | |
527 | } | |
528 | } | |
529 | ||
530 | if (!image_name.empty()) { | |
531 | // despite the separate pool option, | |
532 | // we can also specify them via the image option | |
533 | std::string image_name_copy(image_name); | |
534 | r = extract_spec(image_name_copy, pool_name, &image_name, nullptr, | |
535 | SPEC_VALIDATION_NONE); | |
536 | if (r < 0) { | |
537 | return r; | |
538 | } | |
539 | } | |
540 | ||
541 | if (journal_name != nullptr && spec_arg_index != nullptr && | |
542 | journal_name->empty()) { | |
543 | std::string spec = get_positional_argument(vm, (*spec_arg_index)++); | |
544 | if (!spec.empty()) { | |
545 | r = extract_spec(spec, pool_name, journal_name, nullptr, | |
546 | SPEC_VALIDATION_FULL); | |
547 | if (r < 0) { | |
548 | return r; | |
549 | } | |
550 | } | |
551 | } | |
552 | ||
553 | if (pool_name != nullptr && pool_name->empty()) { | |
31f18b77 | 554 | *pool_name = get_default_pool_name(); |
7c673cae FG |
555 | } |
556 | ||
557 | if (pool_name != nullptr && journal_name != nullptr && | |
558 | journal_name->empty() && !image_name.empty()) { | |
559 | // Try to get journal name from image info. | |
560 | librados::Rados rados; | |
561 | librados::IoCtx io_ctx; | |
562 | librbd::Image image; | |
563 | int r = init_and_open_image(*pool_name, image_name, "", "", true, &rados, | |
564 | &io_ctx, &image); | |
565 | if (r < 0) { | |
566 | std::cerr << "rbd: failed to open image " << image_name | |
567 | << " to get journal name: " << cpp_strerror(r) << std::endl; | |
568 | return r; | |
569 | } | |
570 | ||
571 | uint64_t features; | |
572 | r = image.features(&features); | |
573 | if (r < 0) { | |
574 | return r; | |
575 | } | |
576 | if ((features & RBD_FEATURE_JOURNALING) == 0) { | |
577 | std::cerr << "rbd: journaling is not enabled for image " << image_name | |
578 | << std::endl; | |
579 | return -EINVAL; | |
580 | } | |
581 | *journal_name = image_id(image); | |
582 | } | |
583 | ||
584 | if (journal_name != nullptr && journal_name->empty()) { | |
585 | std::string prefix = at::get_description_prefix(mod); | |
586 | std::cerr << "rbd: " | |
587 | << (mod == at::ARGUMENT_MODIFIER_DEST ? prefix : std::string()) | |
588 | << "journal was not specified" << std::endl; | |
589 | return -EINVAL; | |
590 | } | |
591 | ||
592 | return 0; | |
593 | } | |
594 | ||
595 | int validate_snapshot_name(at::ArgumentModifier mod, | |
596 | const std::string &snap_name, | |
597 | SnapshotPresence snapshot_presence, | |
598 | SpecValidation spec_validation) { | |
599 | std::string prefix = at::get_description_prefix(mod); | |
600 | switch (snapshot_presence) { | |
601 | case SNAPSHOT_PRESENCE_PERMITTED: | |
602 | break; | |
603 | case SNAPSHOT_PRESENCE_NONE: | |
604 | if (!snap_name.empty()) { | |
605 | std::cerr << "rbd: " | |
606 | << (mod == at::ARGUMENT_MODIFIER_DEST ? prefix : std::string()) | |
607 | << "snapname specified for a command that doesn't use it" | |
608 | << std::endl; | |
609 | return -EINVAL; | |
610 | } | |
611 | break; | |
612 | case SNAPSHOT_PRESENCE_REQUIRED: | |
613 | if (snap_name.empty()) { | |
614 | std::cerr << "rbd: " | |
615 | << (mod == at::ARGUMENT_MODIFIER_DEST ? prefix : std::string()) | |
616 | << "snap name was not specified" << std::endl; | |
617 | return -EINVAL; | |
618 | } | |
619 | break; | |
620 | } | |
621 | ||
622 | if (spec_validation == SPEC_VALIDATION_SNAP) { | |
623 | // disallow "/" and "@" in snap name | |
624 | boost::regex pattern("^[^@/]*?$"); | |
625 | if (!boost::regex_match (snap_name, pattern)) { | |
31f18b77 | 626 | std::cerr << "rbd: invalid snap name '" << snap_name << "'" << std::endl; |
7c673cae FG |
627 | return -EINVAL; |
628 | } | |
629 | } | |
630 | return 0; | |
631 | } | |
632 | ||
633 | int get_image_options(const boost::program_options::variables_map &vm, | |
634 | bool get_format, librbd::ImageOptions *opts) { | |
635 | uint64_t order = 0, stripe_unit = 0, stripe_count = 0, object_size = 0; | |
636 | uint64_t features = 0, features_clear = 0, features_set = 0; | |
637 | std::string data_pool; | |
638 | bool order_specified = true; | |
639 | bool features_specified = false; | |
640 | bool features_clear_specified = false; | |
641 | bool features_set_specified = false; | |
642 | bool stripe_specified = false; | |
643 | ||
644 | if (vm.count(at::IMAGE_ORDER)) { | |
645 | order = vm[at::IMAGE_ORDER].as<uint64_t>(); | |
646 | std::cerr << "rbd: --order is deprecated, use --object-size" | |
647 | << std::endl; | |
648 | } else if (vm.count(at::IMAGE_OBJECT_SIZE)) { | |
649 | object_size = vm[at::IMAGE_OBJECT_SIZE].as<uint64_t>(); | |
650 | order = std::round(std::log2(object_size)); | |
651 | } else { | |
652 | order_specified = false; | |
653 | } | |
654 | ||
655 | if (vm.count(at::IMAGE_FEATURES)) { | |
656 | features = vm[at::IMAGE_FEATURES].as<uint64_t>(); | |
657 | features_specified = true; | |
658 | } else { | |
659 | features = get_rbd_default_features(g_ceph_context); | |
660 | } | |
661 | ||
662 | if (vm.count(at::IMAGE_STRIPE_UNIT)) { | |
663 | stripe_unit = vm[at::IMAGE_STRIPE_UNIT].as<uint64_t>(); | |
664 | stripe_specified = true; | |
665 | } | |
666 | ||
667 | if (vm.count(at::IMAGE_STRIPE_COUNT)) { | |
668 | stripe_count = vm[at::IMAGE_STRIPE_COUNT].as<uint64_t>(); | |
669 | stripe_specified = true; | |
670 | } | |
671 | ||
672 | if (vm.count(at::IMAGE_SHARED) && vm[at::IMAGE_SHARED].as<bool>()) { | |
673 | if (features_specified) { | |
674 | features &= ~RBD_FEATURES_SINGLE_CLIENT; | |
675 | } else { | |
676 | features_clear |= RBD_FEATURES_SINGLE_CLIENT; | |
677 | features_clear_specified = true; | |
678 | } | |
679 | } | |
680 | ||
681 | if (vm.count(at::IMAGE_DATA_POOL)) { | |
682 | data_pool = vm[at::IMAGE_DATA_POOL].as<std::string>(); | |
683 | } | |
684 | ||
685 | if (get_format) { | |
686 | uint64_t format = 0; | |
687 | bool format_specified = false; | |
688 | if (vm.count(at::IMAGE_NEW_FORMAT)) { | |
689 | format = 2; | |
690 | format_specified = true; | |
691 | } else if (vm.count(at::IMAGE_FORMAT)) { | |
692 | format = vm[at::IMAGE_FORMAT].as<uint32_t>(); | |
693 | format_specified = true; | |
694 | } | |
695 | if (format == 1) { | |
696 | std::cerr << "rbd: image format 1 is deprecated" << std::endl; | |
697 | } | |
698 | ||
699 | if (features_specified && features != 0) { | |
700 | if (format_specified && format == 1) { | |
701 | std::cerr << "rbd: features not allowed with format 1; " | |
702 | << "use --image-format 2" << std::endl; | |
703 | return -EINVAL; | |
704 | } else { | |
705 | format = 2; | |
706 | format_specified = true; | |
707 | } | |
708 | } | |
709 | ||
710 | if ((stripe_unit || stripe_count) && | |
711 | (stripe_unit != (1ull << order) && stripe_count != 1)) { | |
712 | if (format_specified && format == 1) { | |
713 | std::cerr << "rbd: non-default striping not allowed with format 1; " | |
714 | << "use --image-format 2" << std::endl; | |
715 | return -EINVAL; | |
716 | } else { | |
717 | format = 2; | |
718 | format_specified = true; | |
719 | } | |
720 | } | |
721 | ||
722 | if (!data_pool.empty()) { | |
723 | if (format_specified && format == 1) { | |
724 | std::cerr << "rbd: data pool not allowed with format 1; " | |
725 | << "use --image-format 2" << std::endl; | |
726 | return -EINVAL; | |
727 | } else { | |
728 | format = 2; | |
729 | format_specified = true; | |
730 | } | |
731 | } | |
732 | ||
733 | if (format_specified) { | |
734 | int r = g_conf->set_val("rbd_default_format", stringify(format)); | |
735 | assert(r == 0); | |
736 | opts->set(RBD_IMAGE_OPTION_FORMAT, format); | |
737 | } | |
738 | } | |
739 | ||
740 | if (order_specified) | |
741 | opts->set(RBD_IMAGE_OPTION_ORDER, order); | |
742 | if (features_specified) | |
743 | opts->set(RBD_IMAGE_OPTION_FEATURES, features); | |
744 | if (features_clear_specified) { | |
745 | opts->set(RBD_IMAGE_OPTION_FEATURES_CLEAR, features_clear); | |
746 | } | |
747 | if (features_set_specified) | |
748 | opts->set(RBD_IMAGE_OPTION_FEATURES_SET, features_set); | |
749 | if (stripe_specified) { | |
750 | opts->set(RBD_IMAGE_OPTION_STRIPE_UNIT, stripe_unit); | |
751 | opts->set(RBD_IMAGE_OPTION_STRIPE_COUNT, stripe_count); | |
752 | } | |
753 | if (!data_pool.empty()) { | |
754 | opts->set(RBD_IMAGE_OPTION_DATA_POOL, data_pool); | |
755 | } | |
756 | int r = get_journal_options(vm, opts); | |
757 | if (r < 0) { | |
758 | return r; | |
759 | } | |
760 | ||
761 | return 0; | |
762 | } | |
763 | ||
764 | int get_journal_options(const boost::program_options::variables_map &vm, | |
765 | librbd::ImageOptions *opts) { | |
766 | ||
767 | if (vm.count(at::JOURNAL_OBJECT_SIZE)) { | |
768 | uint64_t size = vm[at::JOURNAL_OBJECT_SIZE].as<uint64_t>(); | |
769 | uint64_t order = 12; | |
770 | while ((1ULL << order) < size) { | |
771 | order++; | |
772 | } | |
773 | opts->set(RBD_IMAGE_OPTION_JOURNAL_ORDER, order); | |
774 | ||
775 | int r = g_conf->set_val("rbd_journal_order", stringify(order)); | |
776 | assert(r == 0); | |
777 | } | |
778 | if (vm.count(at::JOURNAL_SPLAY_WIDTH)) { | |
779 | opts->set(RBD_IMAGE_OPTION_JOURNAL_SPLAY_WIDTH, | |
780 | vm[at::JOURNAL_SPLAY_WIDTH].as<uint64_t>()); | |
781 | ||
782 | int r = g_conf->set_val("rbd_journal_splay_width", | |
783 | stringify( | |
784 | vm[at::JOURNAL_SPLAY_WIDTH].as<uint64_t>())); | |
785 | assert(r == 0); | |
786 | } | |
787 | if (vm.count(at::JOURNAL_POOL)) { | |
788 | opts->set(RBD_IMAGE_OPTION_JOURNAL_POOL, | |
789 | vm[at::JOURNAL_POOL].as<std::string>()); | |
790 | ||
791 | int r = g_conf->set_val("rbd_journal_pool", | |
792 | vm[at::JOURNAL_POOL].as<std::string>()); | |
793 | assert(r == 0); | |
794 | } | |
795 | ||
796 | return 0; | |
797 | } | |
798 | ||
799 | int get_image_size(const boost::program_options::variables_map &vm, | |
800 | uint64_t *size) { | |
801 | if (vm.count(at::IMAGE_SIZE) == 0) { | |
802 | std::cerr << "rbd: must specify --size <M/G/T>" << std::endl; | |
803 | return -EINVAL; | |
804 | } | |
805 | ||
806 | *size = vm[at::IMAGE_SIZE].as<uint64_t>(); | |
807 | return 0; | |
808 | } | |
809 | ||
810 | int get_path(const boost::program_options::variables_map &vm, | |
811 | const std::string &positional_path, std::string *path) { | |
812 | if (!positional_path.empty()) { | |
813 | *path = positional_path; | |
814 | } else if (vm.count(at::PATH)) { | |
815 | *path = vm[at::PATH].as<std::string>(); | |
816 | } | |
817 | ||
818 | if (path->empty()) { | |
819 | std::cerr << "rbd: path was not specified" << std::endl; | |
820 | return -EINVAL; | |
821 | } | |
822 | return 0; | |
823 | } | |
824 | ||
825 | int get_formatter(const po::variables_map &vm, | |
826 | at::Format::Formatter *formatter) { | |
827 | if (vm.count(at::FORMAT)) { | |
828 | bool pretty = vm[at::PRETTY_FORMAT].as<bool>(); | |
829 | *formatter = vm[at::FORMAT].as<at::Format>().create_formatter(pretty); | |
830 | if (*formatter == nullptr && pretty) { | |
831 | std::cerr << "rbd: --pretty-format only works when --format " | |
832 | << "is json or xml" << std::endl; | |
833 | return -EINVAL; | |
181888fb FG |
834 | } else if (*formatter != nullptr && !pretty) { |
835 | formatter->get()->enable_line_break(); | |
7c673cae | 836 | } |
181888fb FG |
837 | } else if (vm[at::PRETTY_FORMAT].as<bool>()) { |
838 | std::cerr << "rbd: --pretty-format only works when --format " | |
839 | << "is json or xml" << std::endl; | |
840 | return -EINVAL; | |
7c673cae FG |
841 | } |
842 | return 0; | |
843 | } | |
844 | ||
845 | void init_context() { | |
846 | g_conf->set_val_or_die("rbd_cache_writethrough_until_flush", "false"); | |
847 | g_conf->apply_changes(NULL); | |
848 | common_init_finish(g_ceph_context); | |
849 | } | |
850 | ||
851 | int init(const std::string &pool_name, librados::Rados *rados, | |
852 | librados::IoCtx *io_ctx) { | |
853 | init_context(); | |
854 | ||
855 | int r = rados->init_with_context(g_ceph_context); | |
856 | if (r < 0) { | |
857 | std::cerr << "rbd: couldn't initialize rados!" << std::endl; | |
858 | return r; | |
859 | } | |
860 | ||
861 | r = rados->connect(); | |
862 | if (r < 0) { | |
863 | std::cerr << "rbd: couldn't connect to the cluster!" << std::endl; | |
864 | return r; | |
865 | } | |
866 | ||
867 | r = init_io_ctx(*rados, pool_name, io_ctx); | |
868 | if (r < 0) { | |
869 | return r; | |
870 | } | |
871 | return 0; | |
872 | } | |
873 | ||
874 | int init_io_ctx(librados::Rados &rados, const std::string &pool_name, | |
875 | librados::IoCtx *io_ctx) { | |
876 | int r = rados.ioctx_create(pool_name.c_str(), *io_ctx); | |
877 | if (r < 0) { | |
31f18b77 FG |
878 | if (r == -ENOENT && pool_name == get_default_pool_name()) { |
879 | std::cerr << "rbd: error opening default pool " | |
880 | << "'" << pool_name << "'" << std::endl | |
881 | << "Ensure that the default pool has been created or specify " | |
882 | << "an alternate pool name." << std::endl; | |
883 | } else { | |
884 | std::cerr << "rbd: error opening pool '" << pool_name << "': " | |
885 | << cpp_strerror(r) << std::endl; | |
886 | } | |
7c673cae FG |
887 | return r; |
888 | } | |
889 | return 0; | |
890 | } | |
891 | ||
892 | int open_image(librados::IoCtx &io_ctx, const std::string &image_name, | |
893 | bool read_only, librbd::Image *image) { | |
894 | int r; | |
895 | librbd::RBD rbd; | |
896 | if (read_only) { | |
897 | r = rbd.open_read_only(io_ctx, *image, image_name.c_str(), NULL); | |
898 | } else { | |
899 | r = rbd.open(io_ctx, *image, image_name.c_str()); | |
900 | } | |
901 | ||
902 | if (r < 0) { | |
903 | std::cerr << "rbd: error opening image " << image_name << ": " | |
904 | << cpp_strerror(r) << std::endl; | |
905 | return r; | |
906 | } | |
907 | return 0; | |
908 | } | |
909 | ||
910 | int open_image_by_id(librados::IoCtx &io_ctx, const std::string &image_id, | |
911 | bool read_only, librbd::Image *image) { | |
912 | int r; | |
913 | librbd::RBD rbd; | |
914 | if (read_only) { | |
915 | r = rbd.open_by_id_read_only(io_ctx, *image, image_id.c_str(), NULL); | |
916 | } else { | |
917 | r = rbd.open_by_id(io_ctx, *image, image_id.c_str()); | |
918 | } | |
919 | ||
920 | if (r < 0) { | |
921 | std::cerr << "rbd: error opening image with id " << image_id << ": " | |
922 | << cpp_strerror(r) << std::endl; | |
923 | return r; | |
924 | } | |
925 | return 0; | |
926 | } | |
927 | ||
928 | int init_and_open_image(const std::string &pool_name, | |
929 | const std::string &image_name, | |
930 | const std::string &image_id, | |
931 | const std::string &snap_name, bool read_only, | |
932 | librados::Rados *rados, librados::IoCtx *io_ctx, | |
933 | librbd::Image *image) { | |
934 | int r = init(pool_name, rados, io_ctx); | |
935 | if (r < 0) { | |
936 | return r; | |
937 | } | |
938 | ||
939 | if (image_id.empty()) { | |
940 | r = open_image(*io_ctx, image_name, read_only, image); | |
941 | } else { | |
942 | r = open_image_by_id(*io_ctx, image_id, read_only, image); | |
943 | } | |
944 | if (r < 0) { | |
945 | return r; | |
946 | } | |
947 | ||
948 | if (!snap_name.empty()) { | |
949 | r = snap_set(*image, snap_name); | |
950 | if (r < 0) { | |
951 | return r; | |
952 | } | |
953 | } | |
954 | return 0; | |
955 | } | |
956 | ||
957 | int snap_set(librbd::Image &image, const std::string &snap_name) { | |
958 | int r = image.snap_set(snap_name.c_str()); | |
959 | if (r < 0) { | |
960 | std::cerr << "error setting snapshot context: " << cpp_strerror(r) | |
961 | << std::endl; | |
962 | return r; | |
963 | } | |
964 | return 0; | |
965 | } | |
966 | ||
967 | void calc_sparse_extent(const bufferptr &bp, | |
968 | size_t sparse_size, | |
969 | size_t buffer_offset, | |
970 | uint64_t buffer_length, | |
971 | size_t *write_length, | |
972 | bool *zeroed) { | |
973 | if (sparse_size == 0) { | |
974 | // sparse writes are disabled -- write the full extent | |
975 | assert(buffer_offset == 0); | |
976 | *write_length = buffer_length; | |
977 | *zeroed = false; | |
978 | return; | |
979 | } | |
980 | ||
981 | *write_length = 0; | |
982 | size_t original_offset = buffer_offset; | |
983 | while (buffer_offset < buffer_length) { | |
984 | size_t extent_size = std::min<size_t>( | |
985 | sparse_size, buffer_length - buffer_offset); | |
986 | ||
987 | bufferptr extent(bp, buffer_offset, extent_size); | |
988 | ||
989 | bool extent_is_zero = extent.is_zero(); | |
990 | if (original_offset == buffer_offset) { | |
991 | *zeroed = extent_is_zero; | |
992 | } else if (*zeroed != extent_is_zero) { | |
993 | assert(*write_length > 0); | |
994 | return; | |
995 | } | |
996 | ||
997 | buffer_offset += extent_size; | |
998 | *write_length += extent_size; | |
999 | } | |
1000 | } | |
1001 | ||
1002 | std::string image_id(librbd::Image& image) { | |
1003 | std::string id; | |
1004 | int r = image.get_id(&id); | |
1005 | if (r < 0) { | |
1006 | return std::string(); | |
1007 | } | |
1008 | return id; | |
1009 | } | |
1010 | ||
1011 | std::string mirror_image_state(librbd::mirror_image_state_t state) { | |
1012 | switch (state) { | |
1013 | case RBD_MIRROR_IMAGE_DISABLING: | |
1014 | return "disabling"; | |
1015 | case RBD_MIRROR_IMAGE_ENABLED: | |
1016 | return "enabled"; | |
1017 | case RBD_MIRROR_IMAGE_DISABLED: | |
1018 | return "disabled"; | |
1019 | default: | |
1020 | return "unknown"; | |
1021 | } | |
1022 | } | |
1023 | ||
1024 | std::string mirror_image_status_state(librbd::mirror_image_status_state_t state) { | |
1025 | switch (state) { | |
1026 | case MIRROR_IMAGE_STATUS_STATE_UNKNOWN: | |
1027 | return "unknown"; | |
1028 | case MIRROR_IMAGE_STATUS_STATE_ERROR: | |
1029 | return "error"; | |
1030 | case MIRROR_IMAGE_STATUS_STATE_SYNCING: | |
1031 | return "syncing"; | |
1032 | case MIRROR_IMAGE_STATUS_STATE_STARTING_REPLAY: | |
1033 | return "starting_replay"; | |
1034 | case MIRROR_IMAGE_STATUS_STATE_REPLAYING: | |
1035 | return "replaying"; | |
1036 | case MIRROR_IMAGE_STATUS_STATE_STOPPING_REPLAY: | |
1037 | return "stopping_replay"; | |
1038 | case MIRROR_IMAGE_STATUS_STATE_STOPPED: | |
1039 | return "stopped"; | |
1040 | default: | |
1041 | return "unknown (" + stringify(static_cast<uint32_t>(state)) + ")"; | |
1042 | } | |
1043 | } | |
1044 | ||
1045 | std::string mirror_image_status_state(librbd::mirror_image_status_t status) { | |
1046 | return (status.up ? "up+" : "down+") + | |
1047 | mirror_image_status_state(status.state); | |
1048 | } | |
1049 | ||
1050 | std::string timestr(time_t t) { | |
1051 | struct tm tm; | |
1052 | ||
1053 | localtime_r(&t, &tm); | |
1054 | ||
1055 | char buf[32]; | |
1056 | strftime(buf, sizeof(buf), "%F %T", &tm); | |
1057 | ||
1058 | return buf; | |
1059 | } | |
1060 | ||
1061 | uint64_t get_rbd_default_features(CephContext* cct) { | |
1062 | auto features = cct->_conf->get_val<std::string>("rbd_default_features"); | |
1063 | return boost::lexical_cast<uint64_t>(features); | |
1064 | } | |
1065 | ||
1066 | bool check_if_image_spec_present(const po::variables_map &vm, | |
1067 | at::ArgumentModifier mod, | |
1068 | size_t spec_arg_index) { | |
1069 | std::string image_key = (mod == at::ARGUMENT_MODIFIER_DEST ? | |
1070 | at::DEST_IMAGE_NAME : at::IMAGE_NAME); | |
1071 | ||
1072 | if (vm.count(image_key)) { | |
1073 | return true; | |
1074 | } | |
1075 | ||
1076 | std::string spec = get_positional_argument(vm, spec_arg_index); | |
1077 | if (!spec.empty()) { | |
1078 | return true; | |
1079 | } | |
1080 | ||
1081 | return false; | |
1082 | } | |
1083 | ||
1084 | } // namespace utils | |
1085 | } // namespace rbd |