1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
4 * Ceph - scalable distributed file system
6 * Copyright (C) 2019 Red Hat <contact@redhat.com>
7 * Author: Adam C. Emerson <aemerson@redhat.com>
9 * This is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License version 2.1, as published by the Free Software
12 * Foundation. See file COPYING.
16 #define BOOST_COROUTINES_NO_DEPRECATION_WARNING
22 #include <string_view>
26 #include <boost/asio.hpp>
27 #include <boost/io/ios_state.hpp>
28 #include <boost/program_options.hpp>
29 #include <boost/system/system_error.hpp>
31 #include <fmt/format.h>
32 #include <fmt/ostream.h>
34 #include <spawn/spawn.hpp>
36 #include "include/buffer.h" // :(
38 #include "include/neorados/RADOS.hpp"
40 using namespace std::literals
;
42 namespace ba
= boost::asio
;
43 namespace bs
= boost::system
;
44 namespace R
= neorados
;
47 std::string
verstr(const std::tuple
<uint32_t, uint32_t, uint32_t>& v
)
49 const auto [maj
, min
, p
] = v
;
50 return fmt::format("v{}.{}.{}", maj
, min
, p
);
54 void printseq(const V
& v
, std::ostream
& m
)
56 std::for_each(v
.cbegin(), v
.cend(),
58 fmt::print(m
, "{}\n", e
);
62 template<typename V
, typename F
>
63 void printseq(const V
& v
, std::ostream
& m
, F
&& f
)
65 std::for_each(v
.cbegin(), v
.cend(),
66 [&m
, &f
](const auto& e
) {
67 fmt::print(m
, "{}\n", f(e
));
71 std::int64_t lookup_pool(R::RADOS
& r
, const std::string
& pname
,
75 auto p
= r
.lookup_pool(pname
, y
[ec
]);
77 throw bs::system_error(
78 ec
, fmt::format("when looking up '{}'", pname
));
83 void lspools(R::RADOS
& r
, const std::vector
<std::string
>&,
86 const auto l
= r
.list_pools(y
);
87 printseq(l
, std::cout
, [](const auto& p
) -> const std::string
& {
93 void ls(R::RADOS
& r
, const std::vector
<std::string
>& p
, s::yield_context y
)
95 const auto& pname
= p
[0];
96 const auto pool
= lookup_pool(r
, pname
, y
);
98 std::vector
<R::Entry
> ls
;
99 R::Cursor next
= R::Cursor::begin();
102 std::tie(ls
, next
) = r
.enumerate_objects(pool
, next
, R::Cursor::end(),
103 1000, {}, y
[ec
], R::all_nspaces
);
105 throw bs::system_error(ec
, fmt::format("when listing {}", pname
));
106 printseq(ls
, std::cout
);
108 } while (next
!= R::Cursor::end());
111 void mkpool(R::RADOS
& r
, const std::vector
<std::string
>& p
,
114 const auto& pname
= p
[0];
116 r
.create_pool(pname
, std::nullopt
, y
[ec
]);
118 throw bs::system_error(ec
, fmt::format("when creating pool '{}'", pname
));
121 void rmpool(R::RADOS
& r
, const std::vector
<std::string
>& p
,
124 const auto& pname
= p
[0];
126 r
.delete_pool(pname
, y
[ec
]);
128 throw bs::system_error(ec
, fmt::format("when removing pool '{}'", pname
));
131 void create(R::RADOS
& r
, const std::vector
<std::string
>& p
,
134 const auto& pname
= p
[0];
135 const R::Object obj
= p
[1];
136 const auto pool
= lookup_pool(r
, pname
, y
);
141 r
.execute(obj
, pool
, std::move(op
), y
[ec
]);
143 throw bs::system_error(ec
,
145 "when creating object '{}' in pool '{}'",
149 inline constexpr std::size_t io_size
= 4 << 20;
151 void write(R::RADOS
& r
, const std::vector
<std::string
>& p
, s::yield_context y
)
153 const auto& pname
= p
[0];
154 const R::Object
obj(p
[1]);
155 const auto pool
= lookup_pool(r
, pname
, y
);
158 std::unique_ptr
<char[]> buf
= std::make_unique
<char[]>(io_size
);
160 boost::io::ios_exception_saver
ies(std::cin
);
162 std::cin
.exceptions(std::istream::badbit
);
165 while (!std::cin
.eof()) {
167 std::cin
.read(buf
.get(), io_size
);
168 auto len
= std::cin
.gcount();
171 break; // Nothin' to do.
173 ceph::buffer::list bl
;
174 bl
.append(buffer::create_static(len
, buf
.get()));
176 op
.write(curoff
, std::move(bl
));
177 r
.execute(obj
, pool
, std::move(op
), y
[ec
]);
180 throw bs::system_error(ec
, fmt::format(
181 "when writing object '{}' in pool '{}'",
186 void read(R::RADOS
& r
, const std::vector
<std::string
>& p
, s::yield_context y
)
188 const auto& pname
= p
[0];
189 const R::Object
obj(p
[1]);
190 const auto pool
= lookup_pool(r
, pname
, y
);
196 op
.stat(&len
, nullptr);
197 r
.execute(obj
, pool
, std::move(op
),
200 throw bs::system_error(
202 fmt::format("when getting length of object '{}' in pool '{}'",
207 ceph::buffer::list bl
;
208 while (auto toread
= std::max(len
- off
, io_size
)) {
210 op
.read(off
, toread
, &bl
);
211 r
.execute(obj
, pool
, std::move(op
), nullptr, y
[ec
]);
213 throw bs::system_error(
215 fmt::format("when reading from object '{}' in pool '{}'",
219 bl
.write_stream(std::cout
);
224 void rm(R::RADOS
& r
, const std::vector
<std::string
>& p
, s::yield_context y
)
226 const auto& pname
= p
[0];
227 const R::Object obj
= p
[1];
228 const auto pool
= lookup_pool(r
, pname
, y
);
233 r
.execute(obj
, pool
, std::move(op
), y
[ec
]);
235 throw bs::system_error(ec
, fmt::format(
236 "when removing object '{}' in pool '{}'",
240 static constexpr auto version
= std::make_tuple(0ul, 0ul, 1ul);
242 using cmdfunc
= void (*)(R::RADOS
& r
, const std::vector
<std::string
>& p
,
246 std::string_view name
;
249 std::string_view usage
;
250 std::string_view desc
;
253 const std::array commands
= {
254 // Pools operations ;)
259 "List all pools"sv
},
266 "list all objects in POOL"sv
},
281 "exclusively create OBJECT in POOL"sv
},
285 "write to OBJECT in POOL from standard input"sv
},
289 "read contents of OBJECT in POOL to standard out"sv
},
293 "remove OBJECT in POOL"sv
}
296 int main(int argc
, char* argv
[])
298 const std::string_view
prog(argv
[0]);
300 namespace po
= boost::program_options
;
302 std::vector
<std::string
> parameters
;
304 po::options_description
desc(fmt::format("{} options", prog
));
306 ("help", "show help")
307 ("version", "show version")
308 ("command", po::value
<std::string
>(&command
), "the operation to perform")
309 ("parameters", po::value
<std::vector
<std::string
>>(¶meters
),
310 "parameters to the command");
312 po::positional_options_description p
;
314 p
.add("parameters", -1);
316 po::variables_map vm
;
318 po::store(po::command_line_parser(argc
, argv
).
319 options(desc
).positional(p
).run(), vm
);
323 if (vm
.count("help")) {
324 fmt::print("{}", desc
);
325 fmt::print("Commands:\n");
326 for (const auto& cmd
: commands
) {
327 fmt::print(" {} {}{}{}\n",
329 cmd
.name
.length() + cmd
.usage
.length() < 13 ?
336 if (vm
.count("version")) {
338 "{}: RADOS command exerciser, {},\n"
339 "RADOS library version {}\n"
340 "Copyright (C) 2019 Red Hat <contact@redhat.com>\n"
341 "This is free software; you can redistribute it and/or\n"
342 "modify it under the terms of the GNU Lesser General Public\n"
343 "License version 2.1, as published by the Free Software\n"
344 "Foundation. See file COPYING.\n", prog
,
345 verstr(version
), verstr(R::RADOS::version()));
349 if (vm
.find("command") == vm
.end()) {
350 fmt::print(std::cerr
, "{}: a command is required\n", prog
);
356 if (auto ci
= std::find_if(commands
.begin(), commands
.end(),
357 [&command
](const cmdesc
& c
) {
358 return c
.name
== command
;
359 }); ci
!= commands
.end()) {
360 if (parameters
.size() < ci
->arity
) {
361 fmt::print(std::cerr
, "{}: {}: too few arguments\n\t{} {}\n",
362 prog
, command
, ci
->name
, ci
->usage
);
365 if (parameters
.size() > ci
->arity
) {
366 fmt::print(std::cerr
, "{}: {}: too many arguments\n\t{} {}\n",
367 prog
, command
, ci
->name
, ci
->usage
);
370 s::spawn(c
, [&](s::yield_context y
) {
371 auto r
= R::RADOS::Builder
{}.build(c
, y
);
372 ci
->f(r
, parameters
, y
);
375 fmt::print(std::cerr
, "{}: {}: unknown command\n", prog
, command
);
379 } catch (const std::exception
& e
) {
380 fmt::print(std::cerr
, "{}: {}: {}\n", prog
, command
, e
.what());