]> git.proxmox.com Git - ceph.git/blob - ceph/src/tools/rbd/action/Status.cc
import ceph quincy 17.2.1
[ceph.git] / ceph / src / tools / rbd / action / Status.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 "common/errno.h"
5 #include "common/Formatter.h"
6 #include "json_spirit/json_spirit.h"
7 #include "tools/rbd/ArgumentTypes.h"
8 #include "tools/rbd/Shell.h"
9 #include "tools/rbd/Utils.h"
10 #include "include/rbd_types.h"
11 #include "include/stringify.h"
12 #include "librbd/cache/Types.h"
13 #include <iostream>
14 #include <boost/program_options.hpp>
15
16 namespace rbd {
17 namespace action {
18 namespace status {
19
20 namespace at = argument_types;
21 namespace po = boost::program_options;
22
23 static int do_show_status(librados::IoCtx& io_ctx, const std::string &image_name,
24 librbd::Image &image, Formatter *f)
25 {
26 int r;
27 std::list<librbd::image_watcher_t> watchers;
28
29 r = image.list_watchers(watchers);
30 if (r < 0)
31 return r;
32
33 uint64_t features;
34 r = image.features(&features);
35 if (r < 0) {
36 return r;
37 }
38
39 librbd::image_migration_status_t migration_status;
40 std::string source_spec;
41 std::string source_pool_name;
42 std::string dest_pool_name;
43 std::string migration_state;
44 if ((features & RBD_FEATURE_MIGRATING) != 0) {
45 r = librbd::RBD().migration_status(io_ctx, image_name.c_str(),
46 &migration_status,
47 sizeof(migration_status));
48 if (r < 0) {
49 std::cerr << "rbd: getting migration status failed: " << cpp_strerror(r)
50 << std::endl;
51 // not fatal
52 } else {
53 if (migration_status.source_pool_id >= 0) {
54 librados::IoCtx src_io_ctx;
55 r = librados::Rados(io_ctx).ioctx_create2(migration_status.source_pool_id, src_io_ctx);
56 if (r < 0) {
57 source_pool_name = stringify(migration_status.source_pool_id);
58 } else {
59 source_pool_name = src_io_ctx.get_pool_name();
60 }
61 } else {
62 r = image.get_migration_source_spec(&source_spec);
63 if (r < 0) {
64 std::cerr << "rbd: getting migration source spec failed: "
65 << cpp_strerror(r) << std::endl;
66 }
67 }
68
69 librados::IoCtx dst_io_ctx;
70 r = librados::Rados(io_ctx).ioctx_create2(migration_status.dest_pool_id, dst_io_ctx);
71 if (r < 0) {
72 dest_pool_name = stringify(migration_status.dest_pool_id);
73 } else {
74 dest_pool_name = dst_io_ctx.get_pool_name();
75 }
76
77 switch (migration_status.state) {
78 case RBD_IMAGE_MIGRATION_STATE_ERROR:
79 migration_state = "error";
80 break;
81 case RBD_IMAGE_MIGRATION_STATE_PREPARING:
82 migration_state = "preparing";
83 break;
84 case RBD_IMAGE_MIGRATION_STATE_PREPARED:
85 migration_state = "prepared";
86 break;
87 case RBD_IMAGE_MIGRATION_STATE_EXECUTING:
88 migration_state = "executing";
89 break;
90 case RBD_IMAGE_MIGRATION_STATE_EXECUTED:
91 migration_state = "executed";
92 break;
93 case RBD_IMAGE_MIGRATION_STATE_ABORTING:
94 migration_state = "aborting";
95 break;
96 default:
97 migration_state = "unknown";
98 }
99 }
100 }
101
102 struct {
103 // decoded
104 std::string host;
105 std::string path;
106 uint64_t size;
107 std::string mode;
108 std::string stats_timestamp;
109 bool present;
110 bool empty;
111 bool clean;
112 uint64_t allocated_bytes;
113 uint64_t cached_bytes;
114 uint64_t dirty_bytes;
115 uint64_t free_bytes;
116 uint64_t hits_full;
117 uint64_t hits_partial;
118 uint64_t misses;
119 uint64_t hit_bytes;
120 uint64_t miss_bytes;
121
122 // calculated
123 uint64_t total_read_ops;
124 uint64_t total_read_bytes;
125 int hits_full_percent;
126 int hits_partial_percent;
127 int hit_bytes_percent;
128 } cache_state;
129 std::string cache_str;
130 if (features & RBD_FEATURE_DIRTY_CACHE) {
131 r = image.metadata_get(librbd::cache::PERSISTENT_CACHE_STATE, &cache_str);
132 if (r < 0) {
133 std::cerr << "rbd: getting persistent cache state failed: " << cpp_strerror(r)
134 << std::endl;
135 // not fatal
136 }
137 json_spirit::mValue json_root;
138 if (!json_spirit::read(cache_str.c_str(), json_root)) {
139 std::cerr << "rbd: parsing persistent cache state failed" << std::endl;
140 cache_str.clear();
141 } else {
142 try {
143 auto& o = json_root.get_obj();
144 cache_state.host = o["host"].get_str();
145 cache_state.path = o["path"].get_str();
146 cache_state.size = o["size"].get_uint64();
147 cache_state.mode = o["mode"].get_str();
148 time_t stats_timestamp_sec = o["stats_timestamp"].get_uint64();
149 cache_state.stats_timestamp = ctime(&stats_timestamp_sec);
150 cache_state.stats_timestamp.pop_back();
151 cache_state.present = o["present"].get_bool();
152 cache_state.empty = o["empty"].get_bool();
153 cache_state.clean = o["clean"].get_bool();
154 cache_state.allocated_bytes = o["allocated_bytes"].get_uint64();
155 cache_state.cached_bytes = o["cached_bytes"].get_uint64();
156 cache_state.dirty_bytes = o["dirty_bytes"].get_uint64();
157 cache_state.free_bytes = o["free_bytes"].get_uint64();
158 cache_state.hits_full = o["hits_full"].get_uint64();
159 cache_state.hits_partial = o["hits_partial"].get_uint64();
160 cache_state.misses = o["misses"].get_uint64();
161 cache_state.hit_bytes = o["hit_bytes"].get_uint64();
162 cache_state.miss_bytes = o["miss_bytes"].get_uint64();
163 } catch (std::runtime_error &e) {
164 std::cerr << "rbd: parsing persistent cache state failed: " << e.what()
165 << std::endl;
166 cache_str.clear();
167 }
168 cache_state.total_read_ops = cache_state.hits_full +
169 cache_state.hits_partial + cache_state.misses;
170 cache_state.total_read_bytes = cache_state.hit_bytes +
171 cache_state.miss_bytes;
172 cache_state.hits_full_percent = utils::get_percentage(
173 cache_state.hits_full, cache_state.total_read_ops);
174 cache_state.hits_partial_percent = utils::get_percentage(
175 cache_state.hits_partial, cache_state.total_read_ops);
176 cache_state.hit_bytes_percent = utils::get_percentage(
177 cache_state.hit_bytes, cache_state.total_read_bytes);
178 }
179 }
180
181 if (f)
182 f->open_object_section("status");
183
184 if (f) {
185 f->open_array_section("watchers");
186 for (auto &watcher : watchers) {
187 f->open_object_section("watcher");
188 f->dump_string("address", watcher.addr);
189 f->dump_unsigned("client", watcher.id);
190 f->dump_unsigned("cookie", watcher.cookie);
191 f->close_section();
192 }
193 f->close_section(); // watchers
194 if (!migration_state.empty()) {
195 f->open_object_section("migration");
196 if (!source_spec.empty()) {
197 f->dump_string("source_spec", source_spec);
198 } else {
199 f->dump_string("source_pool_name", source_pool_name);
200 f->dump_string("source_pool_namespace",
201 migration_status.source_pool_namespace);
202 f->dump_string("source_image_name", migration_status.source_image_name);
203 f->dump_string("source_image_id", migration_status.source_image_id);
204 }
205 f->dump_string("dest_pool_name", dest_pool_name);
206 f->dump_string("dest_pool_namespace",
207 migration_status.dest_pool_namespace);
208 f->dump_string("dest_image_name", migration_status.dest_image_name);
209 f->dump_string("dest_image_id", migration_status.dest_image_id);
210 f->dump_string("state", migration_state);
211 f->dump_string("state_description", migration_status.state_description);
212 f->close_section(); // migration
213 }
214 if (!cache_str.empty()) {
215 f->open_object_section("persistent_cache");
216 f->dump_string("host", cache_state.host);
217 f->dump_string("path", cache_state.path);
218 f->dump_unsigned("size", cache_state.size);
219 f->dump_string("mode", cache_state.mode);
220 f->dump_string("stats_timestamp", cache_state.stats_timestamp);
221 f->dump_bool("present", cache_state.present);
222 f->dump_bool("empty", cache_state.empty);
223 f->dump_bool("clean", cache_state.clean);
224 f->dump_unsigned("allocated_bytes", cache_state.allocated_bytes);
225 f->dump_unsigned("cached_bytes", cache_state.cached_bytes);
226 f->dump_unsigned("dirty_bytes", cache_state.dirty_bytes);
227 f->dump_unsigned("free_bytes", cache_state.free_bytes);
228 f->dump_unsigned("hits_full", cache_state.hits_full);
229 f->dump_int("hits_full_percent", cache_state.hits_full_percent);
230 f->dump_unsigned("hits_partial", cache_state.hits_partial);
231 f->dump_int("hits_partial_percent", cache_state.hits_partial_percent);
232 f->dump_unsigned("misses", cache_state.misses);
233 f->dump_unsigned("hit_bytes", cache_state.hit_bytes);
234 f->dump_int("hit_bytes_percent", cache_state.hit_bytes_percent);
235 f->dump_unsigned("miss_bytes", cache_state.miss_bytes);
236 f->close_section(); // persistent_cache
237 }
238 } else {
239 if (watchers.size()) {
240 std::cout << "Watchers:" << std::endl;
241 for (auto &watcher : watchers) {
242 std::cout << "\twatcher=" << watcher.addr << " client." << watcher.id
243 << " cookie=" << watcher.cookie << std::endl;
244 }
245 } else {
246 std::cout << "Watchers: none" << std::endl;
247 }
248 if (!migration_state.empty()) {
249 if (!migration_status.source_pool_namespace.empty()) {
250 source_pool_name += ("/" + migration_status.source_pool_namespace);
251 }
252 if (!migration_status.dest_pool_namespace.empty()) {
253 dest_pool_name += ("/" + migration_status.dest_pool_namespace);
254 }
255
256 std::cout << "Migration:" << std::endl;
257 std::cout << "\tsource: ";
258 if (!source_spec.empty()) {
259 std::cout << source_spec;
260 } else {
261 std::cout << source_pool_name << "/"
262 << migration_status.source_image_name;
263 if (!migration_status.source_image_id.empty()) {
264 std::cout << " (" << migration_status.source_image_id << ")";
265 }
266 }
267 std::cout << std::endl;
268 std::cout << "\tdestination: " << dest_pool_name << "/"
269 << migration_status.dest_image_name << " ("
270 << migration_status.dest_image_id << ")" << std::endl;
271 std::cout << "\tstate: " << migration_state;
272 if (!migration_status.state_description.empty()) {
273 std::cout << " (" << migration_status.state_description << ")";
274 }
275 std::cout << std::endl;
276 }
277 if (!cache_str.empty()) {
278 std::cout << "Persistent cache state:" << std::endl;
279 std::cout << "\thost: " << cache_state.host << std::endl;
280 std::cout << "\tpath: " << cache_state.path << std::endl;
281 std::cout << "\tsize: " << byte_u_t(cache_state.size) << std::endl;
282 std::cout << "\tmode: " << cache_state.mode << std::endl;
283 std::cout << "\tstats_timestamp: " << cache_state.stats_timestamp
284 << std::endl;
285 std::cout << "\tpresent: " << (cache_state.present ? "true" : "false")
286 << "\tempty: " << (cache_state.empty ? "true" : "false")
287 << "\tclean: " << (cache_state.clean ? "true" : "false")
288 << std::endl;
289 std::cout << "\tallocated: " << byte_u_t(cache_state.allocated_bytes)
290 << std::endl;
291 std::cout << "\tcached: " << byte_u_t(cache_state.cached_bytes)
292 << std::endl;
293 std::cout << "\tdirty: " << byte_u_t(cache_state.dirty_bytes) << std::endl;
294 std::cout << "\tfree: " << byte_u_t(cache_state.free_bytes) << std::endl;
295 std::cout << "\thits_full: " << cache_state.hits_full << " / "
296 << cache_state.hits_full_percent << "%" << std::endl;
297 std::cout << "\thits_partial: " << cache_state.hits_partial << " / "
298 << cache_state.hits_partial_percent << "%" << std::endl;
299 std::cout << "\tmisses: " << cache_state.misses << std::endl;
300 std::cout << "\thit_bytes: " << byte_u_t(cache_state.hit_bytes) << " / "
301 << cache_state.hit_bytes_percent << "%" << std::endl;
302 std::cout << "\tmiss_bytes: " << byte_u_t(cache_state.miss_bytes)
303 << std::endl;
304 }
305 }
306
307 if (f) {
308 f->close_section(); // status
309 f->flush(std::cout);
310 }
311
312 return 0;
313 }
314
315 void get_arguments(po::options_description *positional,
316 po::options_description *options) {
317 at::add_image_spec_options(positional, options, at::ARGUMENT_MODIFIER_NONE);
318 at::add_format_options(options);
319 }
320
321 int execute(const po::variables_map &vm,
322 const std::vector<std::string> &ceph_global_init_args) {
323 size_t arg_index = 0;
324 std::string pool_name;
325 std::string namespace_name;
326 std::string image_name;
327 std::string snap_name;
328 int r = utils::get_pool_image_snapshot_names(
329 vm, at::ARGUMENT_MODIFIER_NONE, &arg_index, &pool_name, &namespace_name,
330 &image_name, &snap_name, true, utils::SNAPSHOT_PRESENCE_NONE,
331 utils::SPEC_VALIDATION_NONE);
332 if (r < 0) {
333 return r;
334 }
335
336 at::Format::Formatter formatter;
337 r = utils::get_formatter(vm, &formatter);
338 if (r < 0) {
339 return r;
340 }
341
342 librados::Rados rados;
343 librados::IoCtx io_ctx;
344 librbd::Image image;
345 r = utils::init_and_open_image(pool_name, namespace_name, image_name, "", "",
346 true, &rados, &io_ctx, &image);
347 if (r < 0) {
348 return r;
349 }
350
351 r = do_show_status(io_ctx, image_name, image, formatter.get());
352 if (r < 0) {
353 std::cerr << "rbd: show status failed: " << cpp_strerror(r) << std::endl;
354 return r;
355 }
356 return 0;
357 }
358
359 Shell::Action action(
360 {"status"}, {}, "Show the status of this image.", "", &get_arguments,
361 &execute);
362
363 } // namespace status
364 } // namespace action
365 } // namespace rbd