]>
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/ArgumentTypes.h" | |
5 | #include "tools/rbd/Shell.h" | |
6 | #include "tools/rbd/Utils.h" | |
d2e6a577 | 7 | #include "include/Context.h" |
7c673cae FG |
8 | #include "include/stringify.h" |
9 | #include "include/types.h" | |
10 | #include "common/errno.h" | |
11 | #include "common/Formatter.h" | |
12 | #include "common/TextTable.h" | |
13 | #include <iostream> | |
11fdf7f2 | 14 | #include <boost/bind.hpp> |
7c673cae | 15 | #include <boost/program_options.hpp> |
d2e6a577 | 16 | #include "global/global_context.h" |
7c673cae FG |
17 | |
18 | namespace rbd { | |
d2e6a577 | 19 | |
7c673cae FG |
20 | namespace action { |
21 | namespace list { | |
22 | ||
23 | namespace at = argument_types; | |
24 | namespace po = boost::program_options; | |
25 | ||
d2e6a577 FG |
26 | enum WorkerState { |
27 | STATE_IDLE = 0, | |
28 | STATE_OPENED, | |
29 | STATE_DONE | |
30 | } ; | |
31 | ||
32 | struct WorkerEntry { | |
33 | librbd::Image img; | |
34 | librbd::RBD::AioCompletion* completion; | |
35 | WorkerState state; | |
36 | string name; | |
9f95a23c | 37 | string id; |
d2e6a577 FG |
38 | |
39 | WorkerEntry() { | |
40 | state = STATE_IDLE; | |
41 | completion = nullptr; | |
42 | } | |
43 | }; | |
44 | ||
45 | ||
46 | int list_process_image(librados::Rados* rados, WorkerEntry* w, bool lflag, Formatter *f, TextTable &tbl) | |
47 | { | |
48 | int r = 0; | |
49 | librbd::image_info_t info; | |
11fdf7f2 | 50 | std::string parent; |
d2e6a577 FG |
51 | |
52 | // handle second-nth trips through loop | |
11fdf7f2 TL |
53 | librbd::linked_image_spec_t parent_image_spec; |
54 | librbd::snap_spec_t parent_snap_spec; | |
55 | r = w->img.get_parent(&parent_image_spec, &parent_snap_spec); | |
56 | if (r < 0 && r != -ENOENT) { | |
d2e6a577 | 57 | return r; |
11fdf7f2 TL |
58 | } |
59 | ||
d2e6a577 FG |
60 | bool has_parent = false; |
61 | if (r != -ENOENT) { | |
11fdf7f2 TL |
62 | parent = parent_image_spec.pool_name + "/"; |
63 | if (!parent_image_spec.pool_namespace.empty()) { | |
64 | parent += parent_image_spec.pool_namespace + "/"; | |
65 | } | |
66 | parent += parent_image_spec.image_name + "@" + parent_snap_spec.name; | |
d2e6a577 FG |
67 | has_parent = true; |
68 | } | |
69 | ||
70 | if (w->img.stat(info, sizeof(info)) < 0) { | |
71 | return -EINVAL; | |
72 | } | |
73 | ||
74 | uint8_t old_format; | |
75 | w->img.old_format(&old_format); | |
76 | ||
77 | std::list<librbd::locker_t> lockers; | |
78 | bool exclusive; | |
79 | r = w->img.list_lockers(&lockers, &exclusive, NULL); | |
80 | if (r < 0) | |
81 | return r; | |
82 | std::string lockstr; | |
83 | if (!lockers.empty()) { | |
84 | lockstr = (exclusive) ? "excl" : "shr"; | |
85 | } | |
86 | ||
87 | if (f) { | |
88 | f->open_object_section("image"); | |
89 | f->dump_string("image", w->name); | |
9f95a23c | 90 | f->dump_string("id", w->id); |
d2e6a577 FG |
91 | f->dump_unsigned("size", info.size); |
92 | if (has_parent) { | |
93 | f->open_object_section("parent"); | |
11fdf7f2 TL |
94 | f->dump_string("pool", parent_image_spec.pool_name); |
95 | f->dump_string("pool_namespace", parent_image_spec.pool_namespace); | |
96 | f->dump_string("image", parent_image_spec.image_name); | |
97 | f->dump_string("snapshot", parent_snap_spec.name); | |
d2e6a577 FG |
98 | f->close_section(); |
99 | } | |
100 | f->dump_int("format", old_format ? 1 : 2); | |
101 | if (!lockers.empty()) | |
102 | f->dump_string("lock_type", exclusive ? "exclusive" : "shared"); | |
103 | f->close_section(); | |
104 | } else { | |
105 | tbl << w->name | |
1adf2230 | 106 | << stringify(byte_u_t(info.size)) |
d2e6a577 FG |
107 | << parent |
108 | << ((old_format) ? '1' : '2') | |
109 | << "" // protect doesn't apply to images | |
110 | << lockstr | |
111 | << TextTable::endrow; | |
112 | } | |
113 | ||
114 | std::vector<librbd::snap_info_t> snaplist; | |
115 | if (w->img.snap_list(snaplist) >= 0 && !snaplist.empty()) { | |
11fdf7f2 TL |
116 | snaplist.erase(remove_if(snaplist.begin(), |
117 | snaplist.end(), | |
118 | boost::bind(utils::is_not_user_snap_namespace, &w->img, _1)), | |
119 | snaplist.end()); | |
d2e6a577 FG |
120 | for (std::vector<librbd::snap_info_t>::iterator s = snaplist.begin(); |
121 | s != snaplist.end(); ++s) { | |
122 | bool is_protected; | |
123 | bool has_parent = false; | |
124 | parent.clear(); | |
125 | w->img.snap_set(s->name.c_str()); | |
126 | r = w->img.snap_is_protected(s->name.c_str(), &is_protected); | |
127 | if (r < 0) | |
128 | return r; | |
11fdf7f2 TL |
129 | if (w->img.get_parent(&parent_image_spec, &parent_snap_spec) >= 0) { |
130 | parent = parent_image_spec.pool_name + "/"; | |
131 | if (!parent_image_spec.pool_namespace.empty()) { | |
132 | parent += parent_image_spec.pool_namespace + "/"; | |
133 | } | |
134 | parent += parent_image_spec.image_name + "@" + parent_snap_spec.name; | |
d2e6a577 FG |
135 | has_parent = true; |
136 | } | |
137 | if (f) { | |
138 | f->open_object_section("snapshot"); | |
139 | f->dump_string("image", w->name); | |
9f95a23c | 140 | f->dump_string("id", w->id); |
d2e6a577 | 141 | f->dump_string("snapshot", s->name); |
9f95a23c | 142 | f->dump_unsigned("snapshot_id", s->id); |
d2e6a577 FG |
143 | f->dump_unsigned("size", s->size); |
144 | if (has_parent) { | |
145 | f->open_object_section("parent"); | |
11fdf7f2 TL |
146 | f->dump_string("pool", parent_image_spec.pool_name); |
147 | f->dump_string("pool_namespace", parent_image_spec.pool_namespace); | |
148 | f->dump_string("image", parent_image_spec.image_name); | |
149 | f->dump_string("snapshot", parent_snap_spec.name); | |
d2e6a577 FG |
150 | f->close_section(); |
151 | } | |
152 | f->dump_int("format", old_format ? 1 : 2); | |
153 | f->dump_string("protected", is_protected ? "true" : "false"); | |
154 | f->close_section(); | |
155 | } else { | |
156 | tbl << w->name + "@" + s->name | |
1adf2230 | 157 | << stringify(byte_u_t(s->size)) |
d2e6a577 FG |
158 | << parent |
159 | << ((old_format) ? '1' : '2') | |
160 | << (is_protected ? "yes" : "") | |
161 | << "" // locks don't apply to snaps | |
162 | << TextTable::endrow; | |
163 | } | |
164 | } | |
165 | } | |
166 | ||
11fdf7f2 | 167 | return 0; |
d2e6a577 FG |
168 | } |
169 | ||
11fdf7f2 TL |
170 | int do_list(const std::string &pool_name, const std::string& namespace_name, |
171 | bool lflag, int threads, Formatter *f) { | |
d2e6a577 | 172 | std::vector<WorkerEntry*> workers; |
11fdf7f2 | 173 | std::vector<librbd::image_spec_t> images; |
d2e6a577 FG |
174 | librados::Rados rados; |
175 | librbd::RBD rbd; | |
176 | librados::IoCtx ioctx; | |
177 | ||
178 | if (threads < 1) { | |
179 | threads = 1; | |
180 | } | |
181 | if (threads > 32) { | |
182 | threads = 32; | |
183 | } | |
184 | ||
11fdf7f2 | 185 | int r = utils::init(pool_name, namespace_name, &rados, &ioctx); |
d2e6a577 FG |
186 | if (r < 0) { |
187 | return r; | |
188 | } | |
189 | ||
11fdf7f2 TL |
190 | utils::disable_cache(); |
191 | ||
192 | r = rbd.list2(ioctx, &images); | |
7c673cae FG |
193 | if (r < 0) |
194 | return r; | |
195 | ||
196 | if (!lflag) { | |
197 | if (f) | |
198 | f->open_array_section("images"); | |
11fdf7f2 | 199 | for (auto& image : images) { |
7c673cae | 200 | if (f) |
11fdf7f2 | 201 | f->dump_string("name", image.name); |
7c673cae | 202 | else |
11fdf7f2 | 203 | std::cout << image.name << std::endl; |
7c673cae FG |
204 | } |
205 | if (f) { | |
206 | f->close_section(); | |
207 | f->flush(std::cout); | |
208 | } | |
209 | return 0; | |
210 | } | |
211 | ||
212 | TextTable tbl; | |
213 | ||
214 | if (f) { | |
215 | f->open_array_section("images"); | |
216 | } else { | |
217 | tbl.define_column("NAME", TextTable::LEFT, TextTable::LEFT); | |
11fdf7f2 | 218 | tbl.define_column("SIZE", TextTable::LEFT, TextTable::RIGHT); |
7c673cae | 219 | tbl.define_column("PARENT", TextTable::LEFT, TextTable::LEFT); |
11fdf7f2 | 220 | tbl.define_column("FMT", TextTable::LEFT, TextTable::RIGHT); |
7c673cae FG |
221 | tbl.define_column("PROT", TextTable::LEFT, TextTable::LEFT); |
222 | tbl.define_column("LOCK", TextTable::LEFT, TextTable::LEFT); | |
223 | } | |
224 | ||
11fdf7f2 TL |
225 | for (size_t left = 0; left < std::min<size_t>(threads, images.size()); |
226 | left++) { | |
d2e6a577 FG |
227 | workers.push_back(new WorkerEntry()); |
228 | } | |
7c673cae | 229 | |
11fdf7f2 | 230 | auto i = images.begin(); |
d2e6a577 FG |
231 | while (true) { |
232 | size_t workers_idle = 0; | |
233 | for (auto comp : workers) { | |
234 | switch (comp->state) { | |
235 | case STATE_DONE: | |
236 | comp->completion->wait_for_complete(); | |
237 | comp->state = STATE_IDLE; | |
238 | comp->completion->release(); | |
239 | comp->completion = nullptr; | |
240 | // we want it to fall through in this case | |
241 | case STATE_IDLE: | |
11fdf7f2 | 242 | if (i == images.end()) { |
d2e6a577 FG |
243 | workers_idle++; |
244 | continue; | |
245 | } | |
11fdf7f2 | 246 | comp->name = i->name; |
9f95a23c | 247 | comp->id = i->id; |
d2e6a577 | 248 | comp->completion = new librbd::RBD::AioCompletion(nullptr, nullptr); |
11fdf7f2 TL |
249 | r = rbd.aio_open_read_only(ioctx, comp->img, i->name.c_str(), nullptr, |
250 | comp->completion); | |
d2e6a577 FG |
251 | i++; |
252 | comp->state = STATE_OPENED; | |
253 | break; | |
254 | case STATE_OPENED: | |
255 | comp->completion->wait_for_complete(); | |
256 | // image might disappear between rbd.list() and rbd.open(); ignore | |
257 | // that, warn about other possible errors (EPERM, say, for opening | |
258 | // an old-format image, because you need execute permission for the | |
259 | // class method) | |
260 | r = comp->completion->get_return_value(); | |
261 | comp->completion->release(); | |
262 | if (r < 0) { | |
11fdf7f2 TL |
263 | std::cerr << "rbd: error opening " << comp->name << ": " |
264 | << cpp_strerror(r) << std::endl; | |
265 | ||
d2e6a577 FG |
266 | // in any event, continue to next image |
267 | comp->state = STATE_IDLE; | |
268 | continue; | |
269 | } | |
270 | r = list_process_image(&rados, comp, lflag, f, tbl); | |
271 | if (r < 0) { | |
11fdf7f2 TL |
272 | std::cerr << "rbd: error processing image " << comp->name << ": " |
273 | << cpp_strerror(r) << std::endl; | |
d2e6a577 FG |
274 | } |
275 | comp->completion = new librbd::RBD::AioCompletion(nullptr, nullptr); | |
276 | r = comp->img.aio_close(comp->completion); | |
277 | comp->state = STATE_DONE; | |
278 | break; | |
7c673cae | 279 | } |
7c673cae | 280 | } |
d2e6a577 FG |
281 | if (workers_idle == workers.size()) { |
282 | break; | |
7c673cae FG |
283 | } |
284 | } | |
285 | ||
7c673cae FG |
286 | if (f) { |
287 | f->close_section(); | |
288 | f->flush(std::cout); | |
11fdf7f2 | 289 | } else if (!images.empty()) { |
7c673cae FG |
290 | std::cout << tbl; |
291 | } | |
292 | ||
d2e6a577 FG |
293 | rados.shutdown(); |
294 | ||
295 | for (auto comp : workers) { | |
296 | delete comp; | |
297 | } | |
298 | ||
7c673cae FG |
299 | return r < 0 ? r : 0; |
300 | } | |
301 | ||
302 | void get_arguments(po::options_description *positional, | |
303 | po::options_description *options) { | |
304 | options->add_options() | |
305 | ("long,l", po::bool_switch(), "long listing format"); | |
11fdf7f2 | 306 | at::add_pool_options(positional, options, true); |
7c673cae FG |
307 | at::add_format_options(options); |
308 | } | |
309 | ||
11fdf7f2 TL |
310 | int execute(const po::variables_map &vm, |
311 | const std::vector<std::string> &ceph_global_init_args) { | |
312 | std::string pool_name; | |
313 | std::string namespace_name; | |
7c673cae | 314 | size_t arg_index = 0; |
11fdf7f2 TL |
315 | int r = utils::get_pool_and_namespace_names(vm, true, false, &pool_name, |
316 | &namespace_name, &arg_index); | |
317 | if (r < 0) { | |
318 | return r; | |
319 | } | |
7c673cae FG |
320 | |
321 | at::Format::Formatter formatter; | |
11fdf7f2 | 322 | r = utils::get_formatter(vm, &formatter); |
7c673cae FG |
323 | if (r < 0) { |
324 | return r; | |
325 | } | |
326 | ||
11fdf7f2 TL |
327 | r = do_list(pool_name, namespace_name, vm["long"].as<bool>(), |
328 | g_conf().get_val<uint64_t>("rbd_concurrent_management_ops"), | |
181888fb | 329 | formatter.get()); |
7c673cae | 330 | if (r < 0) { |
11fdf7f2 TL |
331 | std::cerr << "rbd: listing images failed: " << cpp_strerror(r) |
332 | << std::endl; | |
7c673cae FG |
333 | return r; |
334 | } | |
335 | ||
336 | return 0; | |
337 | } | |
338 | ||
339 | Shell::SwitchArguments switched_arguments({"long", "l"}); | |
340 | Shell::Action action( | |
341 | {"list"}, {"ls"}, "List rbd images.", "", &get_arguments, &execute); | |
342 | ||
343 | } // namespace list | |
344 | } // namespace action | |
345 | } // namespace rbd |