]> git.proxmox.com Git - ceph.git/blame - ceph/src/tools/rbd/action/Migration.cc
import ceph pacific 16.2.5
[ceph.git] / ceph / src / tools / rbd / action / Migration.cc
CommitLineData
11fdf7f2
TL
1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2// vim: ts=8 sw=2 smarttab
3
f67539c2 4#include "include/compat.h"
11fdf7f2 5#include "common/errno.h"
f67539c2 6#include "common/safe_io.h"
11fdf7f2
TL
7
8#include "tools/rbd/ArgumentTypes.h"
9#include "tools/rbd/Shell.h"
10#include "tools/rbd/Utils.h"
11
f67539c2
TL
12#include <sys/types.h>
13#include <fcntl.h>
11fdf7f2
TL
14#include <iostream>
15#include <boost/program_options.hpp>
16
17namespace rbd {
18namespace action {
19namespace migration {
20
21namespace at = argument_types;
22namespace po = boost::program_options;
23
11fdf7f2
TL
24static int do_execute(librados::IoCtx& io_ctx, const std::string &image_name,
25 bool no_progress) {
26 utils::ProgressContext pc("Image migration", no_progress);
27 int r = librbd::RBD().migration_execute_with_progress(io_ctx,
28 image_name.c_str(), pc);
29 if (r < 0) {
30 pc.fail();
31 std::cerr << "rbd: migration failed: " << cpp_strerror(r) << std::endl;
32 return r;
33 }
34 pc.finish();
35 return 0;
36}
37
38static int do_abort(librados::IoCtx& io_ctx, const std::string &image_name,
39 bool no_progress) {
40 utils::ProgressContext pc("Abort image migration", no_progress);
41 int r = librbd::RBD().migration_abort_with_progress(io_ctx,
42 image_name.c_str(), pc);
43 if (r < 0) {
44 pc.fail();
45 std::cerr << "rbd: aborting migration failed: " << cpp_strerror(r)
46 << std::endl;
47 return r;
48 }
49 pc.finish();
50 return 0;
51}
52
53static int do_commit(librados::IoCtx& io_ctx, const std::string &image_name,
54 bool force, bool no_progress) {
55 librbd::image_migration_status_t migration_status;
56 int r = librbd::RBD().migration_status(io_ctx, image_name.c_str(),
57 &migration_status,
58 sizeof(migration_status));
59 if (r < 0) {
60 std::cerr << "rbd: getting migration status failed: " << cpp_strerror(r)
61 << std::endl;
62 return r;
63 }
64
65 librados::IoCtx dst_io_ctx;
66 r = librados::Rados(io_ctx).ioctx_create2(migration_status.dest_pool_id, dst_io_ctx);
67 if (r < 0) {
68 std::cerr << "rbd: accessing source pool id="
69 << migration_status.dest_pool_id << " failed: "
70 << cpp_strerror(r) << std::endl;
71 return r;
72 }
73
74 r = utils::set_namespace(migration_status.dest_pool_namespace, &dst_io_ctx);
75 if (r < 0) {
76 return r;
77 }
78
79 librbd::Image image;
80 r = utils::open_image_by_id(dst_io_ctx, migration_status.dest_image_id,
81 true, &image);
82 if (r < 0) {
83 return r;
84 }
85
86 std::vector<librbd::linked_image_spec_t> children;
87 r = image.list_descendants(&children);
88 if (r < 0) {
89 std::cerr << "rbd: listing descendants failed: " << cpp_strerror(r)
90 << std::endl;
91 return r;
92 }
93
94 if (children.size() > 0) {
95 std::cerr << "rbd: the image has "
96 << (children.size() == 1 ? "a descendant" : "descendants") << ": "
97 << std::endl;
98 for (auto& child : children) {
99 std::cerr << " " << child.pool_name << "/";
100 if (!child.pool_namespace.empty()) {
101 std::cerr << child.pool_namespace << "/";
102 }
103 std::cerr << child.image_name;
104 if (child.trash) {
105 std::cerr << " (trash " << child.image_id << ")";
106 }
107 std::cerr << std::endl;
108 }
109 std::cerr << "Warning: in-use, read-only descendant images"
110 << " will not detect the parent update." << std::endl;
111 if (force) {
112 std::cerr << "Proceeding anyway due to force flag set." << std::endl;
113 } else {
114 std::cerr << "Ensure no descendant images are opened read-only"
115 << " and run again with force flag." << std::endl;
116 return -EBUSY;
117 }
118 }
119
120 utils::ProgressContext pc("Commit image migration", no_progress);
121 r = librbd::RBD().migration_commit_with_progress(io_ctx, image_name.c_str(),
122 pc);
123 if (r < 0) {
124 pc.fail();
125 std::cerr << "rbd: committing migration failed: " << cpp_strerror(r)
126 << std::endl;
127 return r;
128 }
129 pc.finish();
130 return 0;
131}
132
133void get_prepare_arguments(po::options_description *positional,
134 po::options_description *options) {
f67539c2
TL
135 options->add_options()
136 ("import-only", po::bool_switch(), "only import data from source")
137 ("source-spec-path", po::value<std::string>(),
138 "source-spec file (or '-' for stdin)")
139 ("source-spec", po::value<std::string>(),
140 "source-spec");
141 at::add_image_or_snap_spec_options(positional, options,
142 at::ARGUMENT_MODIFIER_SOURCE);
11fdf7f2
TL
143 at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_DEST);
144 at::add_create_image_options(options, true);
145 at::add_flatten_option(options);
146}
147
148int execute_prepare(const po::variables_map &vm,
149 const std::vector<std::string> &ceph_global_init_args) {
f67539c2
TL
150 bool import_only = vm["import-only"].as<bool>();
151
11fdf7f2
TL
152 size_t arg_index = 0;
153 std::string pool_name;
154 std::string namespace_name;
155 std::string image_name;
f67539c2 156 std::string snap_name;
11fdf7f2
TL
157 int r = utils::get_pool_image_snapshot_names(
158 vm, at::ARGUMENT_MODIFIER_SOURCE, &arg_index, &pool_name, &namespace_name,
f67539c2
TL
159 &image_name, import_only ? &snap_name : nullptr, true,
160 import_only ? utils::SNAPSHOT_PRESENCE_PERMITTED :
161 utils::SNAPSHOT_PRESENCE_NONE,
11fdf7f2
TL
162 utils::SPEC_VALIDATION_NONE);
163 if (r < 0) {
164 return r;
165 }
166
f67539c2
TL
167 std::string dst_pool_name;
168 std::string dst_namespace_name;
169 std::string dst_image_name;
170 r = utils::get_pool_image_snapshot_names(
171 vm, at::ARGUMENT_MODIFIER_DEST, &arg_index, &dst_pool_name,
172 &dst_namespace_name, &dst_image_name, nullptr, false,
173 utils::SNAPSHOT_PRESENCE_NONE, utils::SPEC_VALIDATION_FULL);
174 if (r < 0) {
175 return r;
176 }
177
178 std::string source_spec;
179 if (vm.count("source-spec") && vm.count("source-spec-path")) {
b3b6e05e
TL
180 std::cerr << "rbd: cannot specify both source-spec and source-spec-path"
181 << std::endl;
f67539c2
TL
182 return -EINVAL;
183 } else if (vm.count("source-spec-path")) {
184 std::string source_spec_path = vm["source-spec-path"].as<std::string>();
185
186 int fd = STDIN_FILENO;
187 if (source_spec_path != "-") {
188 fd = open(source_spec_path.c_str(), O_RDONLY);
189 if (fd < 0) {
190 r = -errno;
191 std::cerr << "rbd: error opening " << source_spec_path << std::endl;
192 return r;
193 }
194 }
195
196 source_spec.resize(4096);
197 r = safe_read(fd, source_spec.data(), source_spec.size() - 1);
198 if (fd != STDIN_FILENO) {
199 VOID_TEMP_FAILURE_RETRY(close(fd));
200 }
201
202 if (r >= 0) {
203 source_spec.resize(r);
204 } else {
205 std::cerr << "rbd: error reading source-spec file: " << cpp_strerror(r)
206 << std::endl;
207 return r;
208 }
209 } else if (vm.count("source-spec")) {
210 source_spec = vm["source-spec"].as<std::string>();
211 }
212
11fdf7f2
TL
213 librados::Rados rados;
214 librados::IoCtx io_ctx;
215 r = utils::init(pool_name, namespace_name, &rados, &io_ctx);
216 if (r < 0) {
217 return r;
218 }
11fdf7f2 219
f67539c2
TL
220 librados::IoCtx dst_io_ctx;
221 if (source_spec.empty()) {
222 utils::normalize_pool_name(&dst_pool_name);
223 r = utils::init_io_ctx(rados, dst_pool_name, dst_namespace_name,
224 &dst_io_ctx);
225 if (r < 0) {
226 return r;
227 }
228 }
229
230 if (import_only && source_spec.empty()) {
231 if (snap_name.empty()) {
232 std::cerr << "rbd: snapshot name was not specified" << std::endl;
233 return -EINVAL;
234 }
235
236 std::stringstream ss;
237 ss << R"({)"
238 << R"("type":"native",)"
239 << R"("pool_id":)" << io_ctx.get_id() << R"(,)"
240 << R"("pool_namespace":")" << io_ctx.get_namespace() << R"(",)"
241 << R"("image_name":")" << image_name << R"(",)"
242 << R"("snap_name":")" << snap_name << R"(")"
243 << R"(})";
244 source_spec = ss.str();
245
246 if (dst_image_name.empty()) {
247 std::cerr << "rbd: destination image name must be provided" << std::endl;
248 return -EINVAL;
249 }
250 io_ctx = dst_io_ctx;
251 image_name = dst_image_name;
252 snap_name = "";
253 } else if (!import_only && !source_spec.empty()) {
254 std::cerr << "rbd: --import-only must be used in combination with "
255 << "source-spec/source-spec-path" << std::endl;
256 return -EINVAL;
257 }
258
259 if (!snap_name.empty()) {
260 std::cerr << "rbd: snapshot name specified for a command that doesn't "
261 << "use it" << std::endl;
262 return -EINVAL;
11fdf7f2
TL
263 }
264
265 librbd::ImageOptions opts;
266 r = utils::get_image_options(vm, true, &opts);
267 if (r < 0) {
268 return r;
269 }
270
f67539c2
TL
271 if (source_spec.empty()) {
272 if (dst_image_name.empty()) {
273 dst_image_name = image_name;
274 }
275
276 int r = librbd::RBD().migration_prepare(io_ctx, image_name.c_str(),
277 dst_io_ctx, dst_image_name.c_str(),
278 opts);
11fdf7f2 279 if (r < 0) {
f67539c2
TL
280 std::cerr << "rbd: preparing migration failed: " << cpp_strerror(r)
281 << std::endl;
282 return r;
283 }
284 } else {
285 ceph_assert(import_only);
286 r = librbd::RBD().migration_prepare_import(source_spec.c_str(), io_ctx,
287 image_name.c_str(), opts);
288 if (r < 0) {
289 std::cerr << "rbd: preparing import migration failed: " << cpp_strerror(r)
290 << std::endl;
11fdf7f2
TL
291 return r;
292 }
11fdf7f2
TL
293 }
294
295 return 0;
296}
297
298void get_execute_arguments(po::options_description *positional,
299 po::options_description *options) {
300 at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
301 at::add_no_progress_option(options);
302}
303
304int execute_execute(const po::variables_map &vm,
305 const std::vector<std::string> &ceph_global_init_args) {
306 size_t arg_index = 0;
307 std::string pool_name;
308 std::string namespace_name;
309 std::string image_name;
310 int r = utils::get_pool_image_snapshot_names(
311 vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &namespace_name,
312 &image_name, nullptr, true, utils::SNAPSHOT_PRESENCE_NONE,
313 utils::SPEC_VALIDATION_NONE);
314 if (r < 0) {
315 return r;
316 }
317
318 librados::Rados rados;
319 librados::IoCtx io_ctx;
320 r = utils::init(pool_name, namespace_name, &rados, &io_ctx);
321 if (r < 0) {
322 return r;
323 }
9f95a23c 324 io_ctx.set_pool_full_try();
11fdf7f2
TL
325
326 r = do_execute(io_ctx, image_name, vm[at::NO_PROGRESS].as<bool>());
327 if (r < 0) {
328 return r;
329 }
330
331 return 0;
332}
333
334void get_abort_arguments(po::options_description *positional,
335 po::options_description *options) {
336 at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
337 at::add_no_progress_option(options);
338}
339
340int execute_abort(const po::variables_map &vm,
341 const std::vector<std::string> &ceph_global_init_args) {
342 size_t arg_index = 0;
343 std::string pool_name;
344 std::string namespace_name;
345 std::string image_name;
346 int r = utils::get_pool_image_snapshot_names(
347 vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &namespace_name,
348 &image_name, nullptr, true, utils::SNAPSHOT_PRESENCE_NONE,
349 utils::SPEC_VALIDATION_NONE);
350 if (r < 0) {
351 return r;
352 }
353
354 librados::Rados rados;
355 librados::IoCtx io_ctx;
356 r = utils::init(pool_name, namespace_name, &rados, &io_ctx);
357 if (r < 0) {
358 return r;
359 }
9f95a23c 360 io_ctx.set_pool_full_try();
11fdf7f2
TL
361
362 r = do_abort(io_ctx, image_name, vm[at::NO_PROGRESS].as<bool>());
363 if (r < 0) {
364 return r;
365 }
366
367 return 0;
368}
369
370void get_commit_arguments(po::options_description *positional,
371 po::options_description *options) {
372 at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
373 at::add_no_progress_option(options);
374 options->add_options()
375 ("force", po::bool_switch(), "proceed even if the image has children");
376}
377
378int execute_commit(const po::variables_map &vm,
379 const std::vector<std::string> &ceph_global_init_args) {
380 size_t arg_index = 0;
381 std::string pool_name;
382 std::string namespace_name;
383 std::string image_name;
384 int r = utils::get_pool_image_snapshot_names(
385 vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &namespace_name,
386 &image_name, nullptr, true, utils::SNAPSHOT_PRESENCE_NONE,
387 utils::SPEC_VALIDATION_NONE);
388 if (r < 0) {
389 return r;
390 }
391
392 librados::Rados rados;
393 librados::IoCtx io_ctx;
394 r = utils::init(pool_name, namespace_name, &rados, &io_ctx);
395 if (r < 0) {
396 return r;
397 }
9f95a23c 398 io_ctx.set_pool_full_try();
11fdf7f2
TL
399
400 r = do_commit(io_ctx, image_name, vm["force"].as<bool>(),
401 vm[at::NO_PROGRESS].as<bool>());
402 if (r < 0) {
403 return r;
404 }
405
406 return 0;
407}
408
409Shell::Action action_prepare(
410 {"migration", "prepare"}, {}, "Prepare image migration.",
411 at::get_long_features_help(), &get_prepare_arguments, &execute_prepare);
412
413Shell::Action action_execute(
414 {"migration", "execute"}, {}, "Execute image migration.", "",
415 &get_execute_arguments, &execute_execute);
416
417Shell::Action action_abort(
418 {"migration", "abort"}, {}, "Cancel interrupted image migration.", "",
419 &get_abort_arguments, &execute_abort);
420
421Shell::Action action_commit(
422 {"migration", "commit"}, {}, "Commit image migration.", "",
423 &get_commit_arguments, &execute_commit);
424
425} // namespace migration
426} // namespace action
427} // namespace rbd