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