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/Context.h"
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"
14 #include <boost/bind.hpp>
15 #include <boost/program_options.hpp>
16 #include "global/global_context.h"
23 namespace at
= argument_types
;
24 namespace po
= boost::program_options
;
34 librbd::RBD::AioCompletion
* completion
;
45 int list_process_image(librados::Rados
* rados
, WorkerEntry
* w
, bool lflag
, Formatter
*f
, TextTable
&tbl
)
48 librbd::image_info_t info
;
51 // handle second-nth trips through loop
52 librbd::linked_image_spec_t parent_image_spec
;
53 librbd::snap_spec_t parent_snap_spec
;
54 r
= w
->img
.get_parent(&parent_image_spec
, &parent_snap_spec
);
55 if (r
< 0 && r
!= -ENOENT
) {
59 bool has_parent
= false;
61 parent
= parent_image_spec
.pool_name
+ "/";
62 if (!parent_image_spec
.pool_namespace
.empty()) {
63 parent
+= parent_image_spec
.pool_namespace
+ "/";
65 parent
+= parent_image_spec
.image_name
+ "@" + parent_snap_spec
.name
;
69 if (w
->img
.stat(info
, sizeof(info
)) < 0) {
74 w
->img
.old_format(&old_format
);
76 std::list
<librbd::locker_t
> lockers
;
78 r
= w
->img
.list_lockers(&lockers
, &exclusive
, NULL
);
82 if (!lockers
.empty()) {
83 lockstr
= (exclusive
) ? "excl" : "shr";
87 f
->open_object_section("image");
88 f
->dump_string("image", w
->name
);
89 f
->dump_unsigned("size", info
.size
);
91 f
->open_object_section("parent");
92 f
->dump_string("pool", parent_image_spec
.pool_name
);
93 f
->dump_string("pool_namespace", parent_image_spec
.pool_namespace
);
94 f
->dump_string("image", parent_image_spec
.image_name
);
95 f
->dump_string("snapshot", parent_snap_spec
.name
);
98 f
->dump_int("format", old_format
? 1 : 2);
100 f
->dump_string("lock_type", exclusive
? "exclusive" : "shared");
104 << stringify(byte_u_t(info
.size
))
106 << ((old_format
) ? '1' : '2')
107 << "" // protect doesn't apply to images
109 << TextTable::endrow
;
112 std::vector
<librbd::snap_info_t
> snaplist
;
113 if (w
->img
.snap_list(snaplist
) >= 0 && !snaplist
.empty()) {
114 snaplist
.erase(remove_if(snaplist
.begin(),
116 boost::bind(utils::is_not_user_snap_namespace
, &w
->img
, _1
)),
118 for (std::vector
<librbd::snap_info_t
>::iterator s
= snaplist
.begin();
119 s
!= snaplist
.end(); ++s
) {
121 bool has_parent
= false;
123 w
->img
.snap_set(s
->name
.c_str());
124 r
= w
->img
.snap_is_protected(s
->name
.c_str(), &is_protected
);
127 if (w
->img
.get_parent(&parent_image_spec
, &parent_snap_spec
) >= 0) {
128 parent
= parent_image_spec
.pool_name
+ "/";
129 if (!parent_image_spec
.pool_namespace
.empty()) {
130 parent
+= parent_image_spec
.pool_namespace
+ "/";
132 parent
+= parent_image_spec
.image_name
+ "@" + parent_snap_spec
.name
;
136 f
->open_object_section("snapshot");
137 f
->dump_string("image", w
->name
);
138 f
->dump_string("snapshot", s
->name
);
139 f
->dump_unsigned("size", s
->size
);
141 f
->open_object_section("parent");
142 f
->dump_string("pool", parent_image_spec
.pool_name
);
143 f
->dump_string("pool_namespace", parent_image_spec
.pool_namespace
);
144 f
->dump_string("image", parent_image_spec
.image_name
);
145 f
->dump_string("snapshot", parent_snap_spec
.name
);
148 f
->dump_int("format", old_format
? 1 : 2);
149 f
->dump_string("protected", is_protected
? "true" : "false");
152 tbl
<< w
->name
+ "@" + s
->name
153 << stringify(byte_u_t(s
->size
))
155 << ((old_format
) ? '1' : '2')
156 << (is_protected
? "yes" : "")
157 << "" // locks don't apply to snaps
158 << TextTable::endrow
;
166 int do_list(const std::string
&pool_name
, const std::string
& namespace_name
,
167 bool lflag
, int threads
, Formatter
*f
) {
168 std::vector
<WorkerEntry
*> workers
;
169 std::vector
<librbd::image_spec_t
> images
;
170 librados::Rados rados
;
172 librados::IoCtx ioctx
;
181 int r
= utils::init(pool_name
, namespace_name
, &rados
, &ioctx
);
186 utils::disable_cache();
188 r
= rbd
.list2(ioctx
, &images
);
194 f
->open_array_section("images");
195 for (auto& image
: images
) {
197 f
->dump_string("name", image
.name
);
199 std::cout
<< image
.name
<< std::endl
;
211 f
->open_array_section("images");
213 tbl
.define_column("NAME", TextTable::LEFT
, TextTable::LEFT
);
214 tbl
.define_column("SIZE", TextTable::LEFT
, TextTable::RIGHT
);
215 tbl
.define_column("PARENT", TextTable::LEFT
, TextTable::LEFT
);
216 tbl
.define_column("FMT", TextTable::LEFT
, TextTable::RIGHT
);
217 tbl
.define_column("PROT", TextTable::LEFT
, TextTable::LEFT
);
218 tbl
.define_column("LOCK", TextTable::LEFT
, TextTable::LEFT
);
221 for (size_t left
= 0; left
< std::min
<size_t>(threads
, images
.size());
223 workers
.push_back(new WorkerEntry());
226 auto i
= images
.begin();
228 size_t workers_idle
= 0;
229 for (auto comp
: workers
) {
230 switch (comp
->state
) {
232 comp
->completion
->wait_for_complete();
233 comp
->state
= STATE_IDLE
;
234 comp
->completion
->release();
235 comp
->completion
= nullptr;
236 // we want it to fall through in this case
238 if (i
== images
.end()) {
242 comp
->name
= i
->name
;
243 comp
->completion
= new librbd::RBD::AioCompletion(nullptr, nullptr);
244 r
= rbd
.aio_open_read_only(ioctx
, comp
->img
, i
->name
.c_str(), nullptr,
247 comp
->state
= STATE_OPENED
;
250 comp
->completion
->wait_for_complete();
251 // image might disappear between rbd.list() and rbd.open(); ignore
252 // that, warn about other possible errors (EPERM, say, for opening
253 // an old-format image, because you need execute permission for the
255 r
= comp
->completion
->get_return_value();
256 comp
->completion
->release();
258 std::cerr
<< "rbd: error opening " << comp
->name
<< ": "
259 << cpp_strerror(r
) << std::endl
;
261 // in any event, continue to next image
262 comp
->state
= STATE_IDLE
;
265 r
= list_process_image(&rados
, comp
, lflag
, f
, tbl
);
267 std::cerr
<< "rbd: error processing image " << comp
->name
<< ": "
268 << cpp_strerror(r
) << std::endl
;
270 comp
->completion
= new librbd::RBD::AioCompletion(nullptr, nullptr);
271 r
= comp
->img
.aio_close(comp
->completion
);
272 comp
->state
= STATE_DONE
;
276 if (workers_idle
== workers
.size()) {
284 } else if (!images
.empty()) {
290 for (auto comp
: workers
) {
294 return r
< 0 ? r
: 0;
297 void get_arguments(po::options_description
*positional
,
298 po::options_description
*options
) {
299 options
->add_options()
300 ("long,l", po::bool_switch(), "long listing format");
301 at::add_pool_options(positional
, options
, true);
302 at::add_format_options(options
);
305 int execute(const po::variables_map
&vm
,
306 const std::vector
<std::string
> &ceph_global_init_args
) {
307 std::string pool_name
;
308 std::string namespace_name
;
309 size_t arg_index
= 0;
310 int r
= utils::get_pool_and_namespace_names(vm
, true, false, &pool_name
,
311 &namespace_name
, &arg_index
);
316 at::Format::Formatter formatter
;
317 r
= utils::get_formatter(vm
, &formatter
);
322 r
= do_list(pool_name
, namespace_name
, vm
["long"].as
<bool>(),
323 g_conf().get_val
<uint64_t>("rbd_concurrent_management_ops"),
326 std::cerr
<< "rbd: listing images failed: " << cpp_strerror(r
)
334 Shell::SwitchArguments
switched_arguments({"long", "l"});
335 Shell::Action
action(
336 {"list"}, {"ls"}, "List rbd images.", "", &get_arguments
, &execute
);
339 } // namespace action