]>
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" | |
7 | #include "include/types.h" | |
8 | #include "include/stringify.h" | |
9 | #include "common/errno.h" | |
10 | #include "common/Formatter.h" | |
11 | #include "common/TextTable.h" | |
12 | #include <algorithm> | |
13 | #include <iostream> | |
14 | #include <boost/bind.hpp> | |
15 | #include <boost/program_options.hpp> | |
16 | ||
17 | namespace rbd { | |
18 | namespace action { | |
19 | namespace disk_usage { | |
20 | ||
21 | namespace at = argument_types; | |
22 | namespace po = boost::program_options; | |
23 | ||
24 | static int disk_usage_callback(uint64_t offset, size_t len, int exists, | |
25 | void *arg) { | |
26 | uint64_t *used_size = reinterpret_cast<uint64_t *>(arg); | |
27 | if (exists) { | |
28 | (*used_size) += len; | |
29 | } | |
30 | return 0; | |
31 | } | |
32 | ||
33 | static int compute_image_disk_usage(const std::string& name, | |
34 | const std::string& snap_name, | |
35 | const std::string& from_snap_name, | |
36 | librbd::Image &image, uint64_t size, | |
37 | TextTable& tbl, Formatter *f, | |
38 | uint64_t *used_size) { | |
39 | const char* from = NULL; | |
40 | if (!from_snap_name.empty()) { | |
41 | from = from_snap_name.c_str(); | |
42 | } | |
43 | ||
44 | uint64_t flags; | |
45 | int r = image.get_flags(&flags); | |
46 | if (r < 0) { | |
47 | std::cerr << "rbd: failed to retrieve image flags: " << cpp_strerror(r) | |
48 | << std::endl; | |
49 | return r; | |
50 | } | |
51 | if ((flags & RBD_FLAG_FAST_DIFF_INVALID) != 0) { | |
52 | std::cerr << "warning: fast-diff map is invalid for " << name | |
53 | << (snap_name.empty() ? "" : "@" + snap_name) << ". " | |
54 | << "operation may be slow." << std::endl; | |
55 | } | |
56 | ||
57 | *used_size = 0; | |
58 | r = image.diff_iterate2(from, 0, size, false, true, | |
59 | &disk_usage_callback, used_size); | |
60 | if (r < 0) { | |
61 | std::cerr << "rbd: failed to iterate diffs: " << cpp_strerror(r) | |
62 | << std::endl; | |
63 | return r; | |
64 | } | |
65 | ||
66 | if (f) { | |
67 | f->open_object_section("image"); | |
68 | f->dump_string("name", name); | |
69 | if (!snap_name.empty()) { | |
70 | f->dump_string("snapshot", snap_name); | |
71 | } | |
72 | f->dump_unsigned("provisioned_size", size); | |
73 | f->dump_unsigned("used_size" , *used_size); | |
74 | f->close_section(); | |
75 | } else { | |
76 | std::string full_name = name; | |
77 | if (!snap_name.empty()) { | |
78 | full_name += "@" + snap_name; | |
79 | } | |
80 | tbl << full_name | |
1adf2230 AA |
81 | << stringify(byte_u_t(size)) |
82 | << stringify(byte_u_t(*used_size)) | |
7c673cae FG |
83 | << TextTable::endrow; |
84 | } | |
85 | return 0; | |
86 | } | |
87 | ||
88 | static int do_disk_usage(librbd::RBD &rbd, librados::IoCtx &io_ctx, | |
89 | const char *imgname, const char *snapname, | |
90 | const char *from_snapname, Formatter *f) { | |
91 | std::vector<std::string> names; | |
92 | int r = rbd.list(io_ctx, names); | |
93 | if (r == -ENOENT) { | |
94 | r = 0; | |
95 | } else if (r < 0) { | |
96 | return r; | |
97 | } | |
98 | ||
99 | TextTable tbl; | |
100 | if (f) { | |
101 | f->open_object_section("stats"); | |
102 | f->open_array_section("images"); | |
103 | } else { | |
104 | tbl.define_column("NAME", TextTable::LEFT, TextTable::LEFT); | |
105 | tbl.define_column("PROVISIONED", TextTable::RIGHT, TextTable::RIGHT); | |
106 | tbl.define_column("USED", TextTable::RIGHT, TextTable::RIGHT); | |
107 | } | |
108 | ||
109 | uint32_t count = 0; | |
110 | uint64_t used_size = 0; | |
111 | uint64_t total_prov = 0; | |
112 | uint64_t total_used = 0; | |
113 | bool found = false; | |
114 | std::sort(names.begin(), names.end()); | |
115 | for (std::vector<string>::const_iterator name = names.begin(); | |
116 | name != names.end(); ++name) { | |
117 | if (imgname != NULL && *name != imgname) { | |
118 | continue; | |
119 | } | |
120 | found = true; | |
121 | ||
122 | librbd::Image image; | |
123 | r = rbd.open_read_only(io_ctx, image, name->c_str(), NULL); | |
124 | if (r < 0) { | |
125 | if (r != -ENOENT) { | |
126 | std::cerr << "rbd: error opening " << *name << ": " << cpp_strerror(r) | |
127 | << std::endl; | |
128 | } | |
129 | continue; | |
130 | } | |
131 | ||
132 | uint64_t features; | |
133 | int r = image.features(&features); | |
134 | if (r < 0) { | |
135 | std::cerr << "rbd: failed to retrieve image features: " << cpp_strerror(r) | |
136 | << std::endl; | |
137 | goto out; | |
138 | } | |
139 | if ((features & RBD_FEATURE_FAST_DIFF) == 0) { | |
140 | std::cerr << "warning: fast-diff map is not enabled for " << *name << ". " | |
141 | << "operation may be slow." << std::endl; | |
142 | } | |
143 | ||
144 | librbd::image_info_t info; | |
145 | if (image.stat(info, sizeof(info)) < 0) { | |
146 | r = -EINVAL; | |
147 | goto out; | |
148 | } | |
149 | ||
150 | std::vector<librbd::snap_info_t> snap_list; | |
151 | r = image.snap_list(snap_list); | |
152 | if (r < 0) { | |
153 | std::cerr << "rbd: error opening " << *name << " snapshots: " | |
154 | << cpp_strerror(r) << std::endl; | |
155 | continue; | |
156 | } | |
157 | ||
158 | bool found_from_snap = (from_snapname == nullptr); | |
159 | std::string last_snap_name; | |
160 | std::sort(snap_list.begin(), snap_list.end(), | |
161 | boost::bind(&librbd::snap_info_t::id, _1) < | |
162 | boost::bind(&librbd::snap_info_t::id, _2)); | |
163 | for (std::vector<librbd::snap_info_t>::const_iterator snap = | |
164 | snap_list.begin(); snap != snap_list.end(); ++snap) { | |
165 | librbd::Image snap_image; | |
166 | r = rbd.open_read_only(io_ctx, snap_image, name->c_str(), | |
167 | snap->name.c_str()); | |
168 | if (r < 0) { | |
169 | std::cerr << "rbd: error opening snapshot " << *name << "@" | |
170 | << snap->name << ": " << cpp_strerror(r) << std::endl; | |
171 | goto out; | |
172 | } | |
173 | ||
174 | if (imgname == nullptr || found_from_snap || | |
175 | (found_from_snap && snapname != nullptr && snap->name == snapname)) { | |
176 | r = compute_image_disk_usage(*name, snap->name, last_snap_name, | |
177 | snap_image, snap->size, tbl, f, | |
178 | &used_size); | |
179 | if (r < 0) { | |
180 | goto out; | |
181 | } | |
182 | ||
183 | if (snapname != NULL) { | |
184 | total_prov += snap->size; | |
185 | } | |
186 | total_used += used_size; | |
187 | ++count; | |
188 | } | |
189 | ||
190 | if (!found_from_snap && from_snapname != nullptr && | |
191 | snap->name == from_snapname) { | |
192 | found_from_snap = true; | |
193 | } | |
194 | if (snapname != nullptr && snap->name == snapname) { | |
195 | break; | |
196 | } | |
197 | last_snap_name = snap->name; | |
198 | } | |
199 | ||
200 | if (snapname == NULL) { | |
201 | r = compute_image_disk_usage(*name, "", last_snap_name, image, info.size, | |
202 | tbl, f, &used_size); | |
203 | if (r < 0) { | |
204 | goto out; | |
205 | } | |
206 | total_prov += info.size; | |
207 | total_used += used_size; | |
208 | ++count; | |
209 | } | |
210 | } | |
b32b8144 | 211 | if (imgname != nullptr && !found) { |
7c673cae FG |
212 | std::cerr << "specified image " << imgname << " is not found." << std::endl; |
213 | return -ENOENT; | |
214 | } | |
215 | ||
216 | out: | |
217 | if (f) { | |
218 | f->close_section(); | |
219 | if (imgname == NULL) { | |
220 | f->dump_unsigned("total_provisioned_size", total_prov); | |
221 | f->dump_unsigned("total_used_size", total_used); | |
222 | } | |
223 | f->close_section(); | |
224 | f->flush(std::cout); | |
225 | } else { | |
226 | if (count > 1) { | |
227 | tbl << "<TOTAL>" | |
1adf2230 AA |
228 | << stringify(byte_u_t(total_prov)) |
229 | << stringify(byte_u_t(total_used)) | |
7c673cae FG |
230 | << TextTable::endrow; |
231 | } | |
232 | std::cout << tbl; | |
233 | } | |
234 | ||
235 | return r < 0 ? r : 0; | |
236 | } | |
237 | ||
238 | void get_arguments(po::options_description *positional, | |
239 | po::options_description *options) { | |
240 | at::add_image_or_snap_spec_options(positional, options, | |
241 | at::ARGUMENT_MODIFIER_NONE); | |
242 | at::add_format_options(options); | |
243 | options->add_options() | |
244 | (at::FROM_SNAPSHOT_NAME.c_str(), po::value<std::string>(), | |
245 | "snapshot starting point"); | |
246 | } | |
247 | ||
248 | int execute(const po::variables_map &vm) { | |
249 | size_t arg_index = 0; | |
250 | std::string pool_name; | |
251 | std::string image_name; | |
252 | std::string snap_name; | |
253 | int r = utils::get_pool_image_snapshot_names( | |
254 | vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &image_name, | |
255 | &snap_name, utils::SNAPSHOT_PRESENCE_PERMITTED, | |
256 | utils::SPEC_VALIDATION_NONE, vm.count(at::FROM_SNAPSHOT_NAME)); | |
257 | if (r < 0) { | |
258 | return r; | |
259 | } | |
260 | ||
261 | std::string from_snap_name; | |
262 | if (vm.count(at::FROM_SNAPSHOT_NAME)) { | |
263 | from_snap_name = vm[at::FROM_SNAPSHOT_NAME].as<std::string>(); | |
264 | } | |
265 | ||
266 | at::Format::Formatter formatter; | |
267 | r = utils::get_formatter(vm, &formatter); | |
268 | if (r < 0) { | |
269 | return r; | |
270 | } | |
271 | ||
272 | librados::Rados rados; | |
273 | librados::IoCtx io_ctx; | |
274 | r = utils::init(pool_name, &rados, &io_ctx); | |
275 | if (r < 0) { | |
276 | return r; | |
277 | } | |
278 | ||
279 | librbd::RBD rbd; | |
280 | r = do_disk_usage(rbd, io_ctx, | |
281 | image_name.empty() ? nullptr: image_name.c_str() , | |
282 | snap_name.empty() ? nullptr : snap_name.c_str(), | |
283 | from_snap_name.empty() ? nullptr : from_snap_name.c_str(), | |
284 | formatter.get()); | |
285 | if (r < 0) { | |
181888fb | 286 | std::cerr << "rbd: du failed: " << cpp_strerror(r) << std::endl; |
7c673cae FG |
287 | return r; |
288 | } | |
289 | return 0; | |
290 | } | |
291 | ||
292 | Shell::Action action( | |
293 | {"disk-usage"}, {"du"}, "Show disk usage stats for pool, image or snapshot", | |
294 | "", &get_arguments, &execute); | |
295 | ||
296 | } // namespace disk_usage | |
297 | } // namespace action | |
298 | } // namespace rbd |