]>
Commit | Line | Data |
---|---|---|
11fdf7f2 TL |
1 | // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- |
2 | // vim: ts=8 sw=2 smarttab | |
3 | ||
4 | #include "common/errno.h" | |
5 | ||
6 | #include "tools/rbd/ArgumentTypes.h" | |
7 | #include "tools/rbd/Shell.h" | |
8 | #include "tools/rbd/Utils.h" | |
9 | ||
10 | #include <iostream> | |
11 | #include <boost/program_options.hpp> | |
12 | ||
13 | namespace rbd { | |
14 | namespace action { | |
15 | namespace migration { | |
16 | ||
17 | namespace at = argument_types; | |
18 | namespace po = boost::program_options; | |
19 | ||
20 | static int do_prepare(librados::IoCtx& io_ctx, const std::string &image_name, | |
21 | librados::IoCtx& dest_io_ctx, | |
22 | const std::string &dest_image_name, | |
23 | librbd::ImageOptions& opts) { | |
24 | int r = librbd::RBD().migration_prepare(io_ctx, image_name.c_str(), | |
25 | dest_io_ctx, dest_image_name.c_str(), | |
26 | opts); | |
27 | if (r < 0) { | |
28 | std::cerr << "rbd: preparing migration failed: " << cpp_strerror(r) | |
29 | << std::endl; | |
30 | return r; | |
31 | } | |
32 | return 0; | |
33 | } | |
34 | ||
35 | static int do_execute(librados::IoCtx& io_ctx, const std::string &image_name, | |
36 | bool no_progress) { | |
37 | utils::ProgressContext pc("Image migration", no_progress); | |
38 | int r = librbd::RBD().migration_execute_with_progress(io_ctx, | |
39 | image_name.c_str(), pc); | |
40 | if (r < 0) { | |
41 | pc.fail(); | |
42 | std::cerr << "rbd: migration failed: " << cpp_strerror(r) << std::endl; | |
43 | return r; | |
44 | } | |
45 | pc.finish(); | |
46 | return 0; | |
47 | } | |
48 | ||
49 | static int do_abort(librados::IoCtx& io_ctx, const std::string &image_name, | |
50 | bool no_progress) { | |
51 | utils::ProgressContext pc("Abort image migration", no_progress); | |
52 | int r = librbd::RBD().migration_abort_with_progress(io_ctx, | |
53 | image_name.c_str(), pc); | |
54 | if (r < 0) { | |
55 | pc.fail(); | |
56 | std::cerr << "rbd: aborting migration failed: " << cpp_strerror(r) | |
57 | << std::endl; | |
58 | return r; | |
59 | } | |
60 | pc.finish(); | |
61 | return 0; | |
62 | } | |
63 | ||
64 | static int do_commit(librados::IoCtx& io_ctx, const std::string &image_name, | |
65 | bool force, bool no_progress) { | |
66 | librbd::image_migration_status_t migration_status; | |
67 | int r = librbd::RBD().migration_status(io_ctx, image_name.c_str(), | |
68 | &migration_status, | |
69 | sizeof(migration_status)); | |
70 | if (r < 0) { | |
71 | std::cerr << "rbd: getting migration status failed: " << cpp_strerror(r) | |
72 | << std::endl; | |
73 | return r; | |
74 | } | |
75 | ||
76 | librados::IoCtx dst_io_ctx; | |
77 | r = librados::Rados(io_ctx).ioctx_create2(migration_status.dest_pool_id, dst_io_ctx); | |
78 | if (r < 0) { | |
79 | std::cerr << "rbd: accessing source pool id=" | |
80 | << migration_status.dest_pool_id << " failed: " | |
81 | << cpp_strerror(r) << std::endl; | |
82 | return r; | |
83 | } | |
84 | ||
85 | r = utils::set_namespace(migration_status.dest_pool_namespace, &dst_io_ctx); | |
86 | if (r < 0) { | |
87 | return r; | |
88 | } | |
89 | ||
90 | librbd::Image image; | |
91 | r = utils::open_image_by_id(dst_io_ctx, migration_status.dest_image_id, | |
92 | true, &image); | |
93 | if (r < 0) { | |
94 | return r; | |
95 | } | |
96 | ||
97 | std::vector<librbd::linked_image_spec_t> children; | |
98 | r = image.list_descendants(&children); | |
99 | if (r < 0) { | |
100 | std::cerr << "rbd: listing descendants failed: " << cpp_strerror(r) | |
101 | << std::endl; | |
102 | return r; | |
103 | } | |
104 | ||
105 | if (children.size() > 0) { | |
106 | std::cerr << "rbd: the image has " | |
107 | << (children.size() == 1 ? "a descendant" : "descendants") << ": " | |
108 | << std::endl; | |
109 | for (auto& child : children) { | |
110 | std::cerr << " " << child.pool_name << "/"; | |
111 | if (!child.pool_namespace.empty()) { | |
112 | std::cerr << child.pool_namespace << "/"; | |
113 | } | |
114 | std::cerr << child.image_name; | |
115 | if (child.trash) { | |
116 | std::cerr << " (trash " << child.image_id << ")"; | |
117 | } | |
118 | std::cerr << std::endl; | |
119 | } | |
120 | std::cerr << "Warning: in-use, read-only descendant images" | |
121 | << " will not detect the parent update." << std::endl; | |
122 | if (force) { | |
123 | std::cerr << "Proceeding anyway due to force flag set." << std::endl; | |
124 | } else { | |
125 | std::cerr << "Ensure no descendant images are opened read-only" | |
126 | << " and run again with force flag." << std::endl; | |
127 | return -EBUSY; | |
128 | } | |
129 | } | |
130 | ||
131 | utils::ProgressContext pc("Commit image migration", no_progress); | |
132 | r = librbd::RBD().migration_commit_with_progress(io_ctx, image_name.c_str(), | |
133 | pc); | |
134 | if (r < 0) { | |
135 | pc.fail(); | |
136 | std::cerr << "rbd: committing migration failed: " << cpp_strerror(r) | |
137 | << std::endl; | |
138 | return r; | |
139 | } | |
140 | pc.finish(); | |
141 | return 0; | |
142 | } | |
143 | ||
144 | void get_prepare_arguments(po::options_description *positional, | |
145 | po::options_description *options) { | |
146 | at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_SOURCE); | |
147 | at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_DEST); | |
148 | at::add_create_image_options(options, true); | |
149 | at::add_flatten_option(options); | |
150 | } | |
151 | ||
152 | int execute_prepare(const po::variables_map &vm, | |
153 | const std::vector<std::string> &ceph_global_init_args) { | |
154 | size_t arg_index = 0; | |
155 | std::string pool_name; | |
156 | std::string namespace_name; | |
157 | std::string image_name; | |
158 | int r = utils::get_pool_image_snapshot_names( | |
159 | vm, at::ARGUMENT_MODIFIER_SOURCE, &arg_index, &pool_name, &namespace_name, | |
160 | &image_name, nullptr, true, utils::SNAPSHOT_PRESENCE_NONE, | |
161 | utils::SPEC_VALIDATION_NONE); | |
162 | if (r < 0) { | |
163 | return r; | |
164 | } | |
165 | ||
166 | librados::Rados rados; | |
167 | librados::IoCtx io_ctx; | |
168 | r = utils::init(pool_name, namespace_name, &rados, &io_ctx); | |
169 | if (r < 0) { | |
170 | return r; | |
171 | } | |
9f95a23c | 172 | io_ctx.set_pool_full_try(); |
11fdf7f2 TL |
173 | |
174 | std::string dest_pool_name; | |
175 | std::string dest_namespace_name; | |
176 | std::string dest_image_name; | |
177 | r = utils::get_pool_image_snapshot_names( | |
178 | vm, at::ARGUMENT_MODIFIER_DEST, &arg_index, &dest_pool_name, | |
179 | &dest_namespace_name, &dest_image_name, nullptr, false, | |
180 | utils::SNAPSHOT_PRESENCE_NONE, utils::SPEC_VALIDATION_FULL); | |
181 | if (r < 0) { | |
182 | return r; | |
183 | } | |
184 | ||
185 | librbd::ImageOptions opts; | |
186 | r = utils::get_image_options(vm, true, &opts); | |
187 | if (r < 0) { | |
188 | return r; | |
189 | } | |
190 | ||
191 | librados::IoCtx dest_io_ctx; | |
192 | if (!dest_pool_name.empty()) { | |
193 | r = utils::init_io_ctx(rados, dest_pool_name, dest_namespace_name, | |
194 | &dest_io_ctx); | |
195 | if (r < 0) { | |
196 | return r; | |
197 | } | |
198 | } | |
199 | ||
200 | r = do_prepare(io_ctx, image_name, dest_pool_name.empty() ? io_ctx : | |
201 | dest_io_ctx, dest_image_name, opts); | |
202 | if (r < 0) { | |
203 | return r; | |
204 | } | |
205 | ||
206 | return 0; | |
207 | } | |
208 | ||
209 | void get_execute_arguments(po::options_description *positional, | |
210 | po::options_description *options) { | |
211 | at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE); | |
212 | at::add_no_progress_option(options); | |
213 | } | |
214 | ||
215 | int execute_execute(const po::variables_map &vm, | |
216 | const std::vector<std::string> &ceph_global_init_args) { | |
217 | size_t arg_index = 0; | |
218 | std::string pool_name; | |
219 | std::string namespace_name; | |
220 | std::string image_name; | |
221 | int r = utils::get_pool_image_snapshot_names( | |
222 | vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &namespace_name, | |
223 | &image_name, nullptr, true, utils::SNAPSHOT_PRESENCE_NONE, | |
224 | utils::SPEC_VALIDATION_NONE); | |
225 | if (r < 0) { | |
226 | return r; | |
227 | } | |
228 | ||
229 | librados::Rados rados; | |
230 | librados::IoCtx io_ctx; | |
231 | r = utils::init(pool_name, namespace_name, &rados, &io_ctx); | |
232 | if (r < 0) { | |
233 | return r; | |
234 | } | |
9f95a23c | 235 | io_ctx.set_pool_full_try(); |
11fdf7f2 TL |
236 | |
237 | r = do_execute(io_ctx, image_name, vm[at::NO_PROGRESS].as<bool>()); | |
238 | if (r < 0) { | |
239 | return r; | |
240 | } | |
241 | ||
242 | return 0; | |
243 | } | |
244 | ||
245 | void get_abort_arguments(po::options_description *positional, | |
246 | po::options_description *options) { | |
247 | at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE); | |
248 | at::add_no_progress_option(options); | |
249 | } | |
250 | ||
251 | int execute_abort(const po::variables_map &vm, | |
252 | const std::vector<std::string> &ceph_global_init_args) { | |
253 | size_t arg_index = 0; | |
254 | std::string pool_name; | |
255 | std::string namespace_name; | |
256 | std::string image_name; | |
257 | int r = utils::get_pool_image_snapshot_names( | |
258 | vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &namespace_name, | |
259 | &image_name, nullptr, true, utils::SNAPSHOT_PRESENCE_NONE, | |
260 | utils::SPEC_VALIDATION_NONE); | |
261 | if (r < 0) { | |
262 | return r; | |
263 | } | |
264 | ||
265 | librados::Rados rados; | |
266 | librados::IoCtx io_ctx; | |
267 | r = utils::init(pool_name, namespace_name, &rados, &io_ctx); | |
268 | if (r < 0) { | |
269 | return r; | |
270 | } | |
9f95a23c | 271 | io_ctx.set_pool_full_try(); |
11fdf7f2 TL |
272 | |
273 | r = do_abort(io_ctx, image_name, vm[at::NO_PROGRESS].as<bool>()); | |
274 | if (r < 0) { | |
275 | return r; | |
276 | } | |
277 | ||
278 | return 0; | |
279 | } | |
280 | ||
281 | void get_commit_arguments(po::options_description *positional, | |
282 | po::options_description *options) { | |
283 | at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE); | |
284 | at::add_no_progress_option(options); | |
285 | options->add_options() | |
286 | ("force", po::bool_switch(), "proceed even if the image has children"); | |
287 | } | |
288 | ||
289 | int execute_commit(const po::variables_map &vm, | |
290 | const std::vector<std::string> &ceph_global_init_args) { | |
291 | size_t arg_index = 0; | |
292 | std::string pool_name; | |
293 | std::string namespace_name; | |
294 | std::string image_name; | |
295 | int r = utils::get_pool_image_snapshot_names( | |
296 | vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &namespace_name, | |
297 | &image_name, nullptr, true, utils::SNAPSHOT_PRESENCE_NONE, | |
298 | utils::SPEC_VALIDATION_NONE); | |
299 | if (r < 0) { | |
300 | return r; | |
301 | } | |
302 | ||
303 | librados::Rados rados; | |
304 | librados::IoCtx io_ctx; | |
305 | r = utils::init(pool_name, namespace_name, &rados, &io_ctx); | |
306 | if (r < 0) { | |
307 | return r; | |
308 | } | |
9f95a23c | 309 | io_ctx.set_pool_full_try(); |
11fdf7f2 TL |
310 | |
311 | r = do_commit(io_ctx, image_name, vm["force"].as<bool>(), | |
312 | vm[at::NO_PROGRESS].as<bool>()); | |
313 | if (r < 0) { | |
314 | return r; | |
315 | } | |
316 | ||
317 | return 0; | |
318 | } | |
319 | ||
320 | Shell::Action action_prepare( | |
321 | {"migration", "prepare"}, {}, "Prepare image migration.", | |
322 | at::get_long_features_help(), &get_prepare_arguments, &execute_prepare); | |
323 | ||
324 | Shell::Action action_execute( | |
325 | {"migration", "execute"}, {}, "Execute image migration.", "", | |
326 | &get_execute_arguments, &execute_execute); | |
327 | ||
328 | Shell::Action action_abort( | |
329 | {"migration", "abort"}, {}, "Cancel interrupted image migration.", "", | |
330 | &get_abort_arguments, &execute_abort); | |
331 | ||
332 | Shell::Action action_commit( | |
333 | {"migration", "commit"}, {}, "Commit image migration.", "", | |
334 | &get_commit_arguments, &execute_commit); | |
335 | ||
336 | } // namespace migration | |
337 | } // namespace action | |
338 | } // namespace rbd |