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