1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
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"
14 #include <boost/bind.hpp>
15 #include <boost/program_options.hpp>
19 namespace disk_usage
{
21 namespace at
= argument_types
;
22 namespace po
= boost::program_options
;
24 static int disk_usage_callback(uint64_t offset
, size_t len
, int exists
,
26 uint64_t *used_size
= reinterpret_cast<uint64_t *>(arg
);
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();
45 int r
= image
.get_flags(&flags
);
47 std::cerr
<< "rbd: failed to retrieve image flags: " << cpp_strerror(r
)
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
;
58 r
= image
.diff_iterate2(from
, 0, size
, false, true,
59 &disk_usage_callback
, used_size
);
61 std::cerr
<< "rbd: failed to iterate diffs: " << cpp_strerror(r
)
67 f
->open_object_section("image");
68 f
->dump_string("name", name
);
69 if (!snap_name
.empty()) {
70 f
->dump_string("snapshot", snap_name
);
72 f
->dump_unsigned("provisioned_size", size
);
73 f
->dump_unsigned("used_size" , *used_size
);
76 std::string full_name
= name
;
77 if (!snap_name
.empty()) {
78 full_name
+= "@" + snap_name
;
81 << stringify(si_t(size
))
82 << stringify(si_t(*used_size
))
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
);
101 f
->open_object_section("stats");
102 f
->open_array_section("images");
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
);
110 uint64_t used_size
= 0;
111 uint64_t total_prov
= 0;
112 uint64_t total_used
= 0;
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
) {
123 r
= rbd
.open_read_only(io_ctx
, image
, name
->c_str(), NULL
);
126 std::cerr
<< "rbd: error opening " << *name
<< ": " << cpp_strerror(r
)
133 int r
= image
.features(&features
);
135 std::cerr
<< "rbd: failed to retrieve image features: " << cpp_strerror(r
)
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
;
144 librbd::image_info_t info
;
145 if (image
.stat(info
, sizeof(info
)) < 0) {
150 std::vector
<librbd::snap_info_t
> snap_list
;
151 r
= image
.snap_list(snap_list
);
153 std::cerr
<< "rbd: error opening " << *name
<< " snapshots: "
154 << cpp_strerror(r
) << std::endl
;
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(),
169 std::cerr
<< "rbd: error opening snapshot " << *name
<< "@"
170 << snap
->name
<< ": " << cpp_strerror(r
) << std::endl
;
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
,
183 if (snapname
!= NULL
) {
184 total_prov
+= snap
->size
;
186 total_used
+= used_size
;
190 if (!found_from_snap
&& from_snapname
!= nullptr &&
191 snap
->name
== from_snapname
) {
192 found_from_snap
= true;
194 if (snapname
!= nullptr && snap
->name
== snapname
) {
197 last_snap_name
= snap
->name
;
200 if (snapname
== NULL
) {
201 r
= compute_image_disk_usage(*name
, "", last_snap_name
, image
, info
.size
,
206 total_prov
+= info
.size
;
207 total_used
+= used_size
;
212 std::cerr
<< "specified image " << imgname
<< " is not found." << std::endl
;
219 if (imgname
== NULL
) {
220 f
->dump_unsigned("total_provisioned_size", total_prov
);
221 f
->dump_unsigned("total_used_size", total_used
);
228 << stringify(si_t(total_prov
))
229 << stringify(si_t(total_used
))
230 << TextTable::endrow
;
235 return r
< 0 ? r
: 0;
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");
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
));
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
>();
266 at::Format::Formatter formatter
;
267 r
= utils::get_formatter(vm
, &formatter
);
272 librados::Rados rados
;
273 librados::IoCtx io_ctx
;
274 r
= utils::init(pool_name
, &rados
, &io_ctx
);
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(),
286 std::cerr
<< "rbd: du failed: " << cpp_strerror(r
) << std::endl
;
292 Shell::Action
action(
293 {"disk-usage"}, {"du"}, "Show disk usage stats for pool, image or snapshot",
294 "", &get_arguments
, &execute
);
296 } // namespace disk_usage
297 } // namespace action