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