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