Fix some bugs when testing opensds ansible
[stor4nfv.git] / src / ceph / src / tools / rbd / action / DiskUsage.cc
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
81         << stringify(si_t(size))
82         << stringify(si_t(*used_size))
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   }
211   if (!found) {
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>"
228           << stringify(si_t(total_prov))
229           << stringify(si_t(total_used))
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) {
286     std::cerr << "rbd: du failed: " << cpp_strerror(r) << std::endl;
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