]> git.proxmox.com Git - ceph.git/blame - ceph/src/tools/neorados.cc
update source to Ceph Pacific 16.2.2
[ceph.git] / ceph / src / tools / neorados.cc
CommitLineData
f67539c2
TL
1// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2// vim: ts=8 sw=2 smarttab
3/*
4 * Ceph - scalable distributed file system
5 *
6 * Copyright (C) 2019 Red Hat <contact@redhat.com>
7 * Author: Adam C. Emerson <aemerson@redhat.com>
8 *
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.
13 *
14 */
15
16#define BOOST_COROUTINES_NO_DEPRECATION_WARNING
17
18#include <algorithm>
19#include <cassert>
20#include <iostream>
21#include <string>
22#include <string_view>
23#include <tuple>
24#include <vector>
25
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>
30
31#include <fmt/format.h>
32#include <fmt/ostream.h>
33
34#include <spawn/spawn.hpp>
35
36#include "include/buffer.h" // :(
37
38#include "include/neorados/RADOS.hpp"
39
40using namespace std::literals;
41
42namespace ba = boost::asio;
43namespace bs = boost::system;
44namespace R = neorados;
45namespace s = spawn;
46
47std::string verstr(const std::tuple<uint32_t, uint32_t, uint32_t>& v)
48{
49 const auto [maj, min, p] = v;
50 return fmt::format("v{}.{}.{}", maj, min, p);
51}
52
53template<typename V>
54void printseq(const V& v, std::ostream& m)
55{
56 std::for_each(v.cbegin(), v.cend(),
57 [&m](const auto& e) {
58 fmt::print(m, "{}\n", e);
59 });
60}
61
62template<typename V, typename F>
63void printseq(const V& v, std::ostream& m, F&& f)
64{
65 std::for_each(v.cbegin(), v.cend(),
66 [&m, &f](const auto& e) {
67 fmt::print(m, "{}\n", f(e));
68 });
69}
70
71std::int64_t lookup_pool(R::RADOS& r, const std::string& pname,
72 s::yield_context y)
73{
74 bs::error_code ec;
75 auto p = r.lookup_pool(pname, y[ec]);
76 if (ec)
77 throw bs::system_error(
78 ec, fmt::format("when looking up '{}'", pname));
79 return p;
80}
81
82
83void lspools(R::RADOS& r, const std::vector<std::string>&,
84 s::yield_context y)
85{
86 const auto l = r.list_pools(y);
87 printseq(l, std::cout, [](const auto& p) -> const std::string& {
88 return p.second;
89 });
90}
91
92
93void ls(R::RADOS& r, const std::vector<std::string>& p, s::yield_context y)
94{
95 const auto& pname = p[0];
96 const auto pool = lookup_pool(r, pname, y);
97
98 std::vector<R::Entry> ls;
99 R::Cursor next = R::Cursor::begin();
100 bs::error_code ec;
101 do {
102 std::tie(ls, next) = r.enumerate_objects(pool, next, R::Cursor::end(),
103 1000, {}, y[ec], R::all_nspaces);
104 if (ec)
105 throw bs::system_error(ec, fmt::format("when listing {}", pname));
106 printseq(ls, std::cout);
107 ls.clear();
108 } while (next != R::Cursor::end());
109}
110
111void mkpool(R::RADOS& r, const std::vector<std::string>& p,
112 s::yield_context y)
113{
114 const auto& pname = p[0];
115 bs::error_code ec;
116 r.create_pool(pname, std::nullopt, y[ec]);
117 if (ec)
118 throw bs::system_error(ec, fmt::format("when creating pool '{}'", pname));
119}
120
121void rmpool(R::RADOS& r, const std::vector<std::string>& p,
122 s::yield_context y)
123{
124 const auto& pname = p[0];
125 bs::error_code ec;
126 r.delete_pool(pname, y[ec]);
127 if (ec)
128 throw bs::system_error(ec, fmt::format("when removing pool '{}'", pname));
129}
130
131void create(R::RADOS& r, const std::vector<std::string>& p,
132 s::yield_context y)
133{
134 const auto& pname = p[0];
135 const R::Object obj = p[1];
136 const auto pool = lookup_pool(r, pname, y);
137
138 bs::error_code ec;
139 R::WriteOp op;
140 op.create(true);
141 r.execute(obj, pool, std::move(op), y[ec]);
142 if (ec)
143 throw bs::system_error(ec,
144 fmt::format(
145 "when creating object '{}' in pool '{}'",
146 obj, pname));
147}
148
149inline constexpr std::size_t io_size = 4 << 20;
150
151void write(R::RADOS& r, const std::vector<std::string>& p, s::yield_context y)
152{
153 const auto& pname = p[0];
154 const R::Object obj(p[1]);
155 const auto pool = lookup_pool(r, pname, y);
156
157 bs::error_code ec;
158 std::unique_ptr<char[]> buf = std::make_unique<char[]>(io_size);
159 std::size_t off = 0;
160 boost::io::ios_exception_saver ies(std::cin);
161
162 std::cin.exceptions(std::istream::badbit);
163 std::cin.clear();
164
165 while (!std::cin.eof()) {
166 auto curoff = off;
167 std::cin.read(buf.get(), io_size);
168 auto len = std::cin.gcount();
169 off += len;
170 if (len == 0)
171 break; // Nothin' to do.
172
173 ceph::buffer::list bl;
174 bl.append(buffer::create_static(len, buf.get()));
175 R::WriteOp op;
176 op.write(curoff, std::move(bl));
177 r.execute(obj, pool, std::move(op), y[ec]);
178
179 if (ec)
180 throw bs::system_error(ec, fmt::format(
181 "when writing object '{}' in pool '{}'",
182 obj, pname));
183 }
184}
185
186void read(R::RADOS& r, const std::vector<std::string>& p, s::yield_context y)
187{
188 const auto& pname = p[0];
189 const R::Object obj(p[1]);
190 const auto pool = lookup_pool(r, pname, y);
191
192 bs::error_code ec;
193 std::uint64_t len;
194 {
195 R::ReadOp op;
196 op.stat(&len, nullptr);
197 r.execute(obj, pool, std::move(op),
198 nullptr, y[ec]);
199 if (ec)
200 throw bs::system_error(
201 ec,
202 fmt::format("when getting length of object '{}' in pool '{}'",
203 obj, pname));
204 }
205
206 std::size_t off = 0;
207 ceph::buffer::list bl;
208 while (auto toread = std::max(len - off, io_size)) {
209 R::ReadOp op;
210 op.read(off, toread, &bl);
211 r.execute(obj, pool, std::move(op), nullptr, y[ec]);
212 if (ec)
213 throw bs::system_error(
214 ec,
215 fmt::format("when reading from object '{}' in pool '{}'",
216 obj, pool));
217
218 off += bl.length();
219 bl.write_stream(std::cout);
220 bl.clear();
221 }
222}
223
224void rm(R::RADOS& r, const std::vector<std::string>& p, s::yield_context y)
225{
226 const auto& pname = p[0];
227 const R::Object obj = p[1];
228 const auto pool = lookup_pool(r, pname, y);
229
230 bs::error_code ec;
231 R::WriteOp op;
232 op.remove();
233 r.execute(obj, pool, std::move(op), y[ec]);
234 if (ec)
235 throw bs::system_error(ec, fmt::format(
236 "when removing object '{}' in pool '{}'",
237 obj, pname));
238}
239
240static constexpr auto version = std::make_tuple(0ul, 0ul, 1ul);
241
242using cmdfunc = void (*)(R::RADOS& r, const std::vector<std::string>& p,
243 s::yield_context);
244
245struct cmdesc {
246 std::string_view name;
247 std::size_t arity;
248 cmdfunc f;
249 std::string_view usage;
250 std::string_view desc;
251};
252
253const std::array commands = {
254 // Pools operations ;)
255
256 cmdesc{ "lspools"sv,
257 0, &lspools,
258 ""sv,
259 "List all pools"sv },
260
261 // Pool operations
262
263 cmdesc{ "ls"sv,
264 1, &ls,
265 "POOL"sv,
266 "list all objects in POOL"sv },
267 cmdesc{ "mkpool"sv,
268 1, &mkpool,
269 "POOL"sv,
270 "create POOL"sv },
271 cmdesc{ "rmpool"sv,
272 1, &rmpool,
273 "POOL"sv,
274 "remove POOL"sv },
275
276 // Object operations
277
278 cmdesc{ "create"sv,
279 2, &create,
280 "POOL OBJECT"sv,
281 "exclusively create OBJECT in POOL"sv },
282 cmdesc{ "write"sv,
283 2, &write,
284 "POOL OBJECT"sv,
285 "write to OBJECT in POOL from standard input"sv },
286 cmdesc{ "read"sv,
287 2, &read,
288 "POOL OBJECT"sv,
289 "read contents of OBJECT in POOL to standard out"sv },
290 cmdesc{ "rm"sv,
291 2, &rm,
292 "POOL OBJECT"sv,
293 "remove OBJECT in POOL"sv }
294};
295
296int main(int argc, char* argv[])
297{
298 const std::string_view prog(argv[0]);
299 std::string command;
300 namespace po = boost::program_options;
301 try {
302 std::vector<std::string> parameters;
303
304 po::options_description desc(fmt::format("{} options", prog));
305 desc.add_options()
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>>(&parameters),
310 "parameters to the command");
311
312 po::positional_options_description p;
313 p.add("command", 1);
314 p.add("parameters", -1);
315
316 po::variables_map vm;
317
318 po::store(po::command_line_parser(argc, argv).
319 options(desc).positional(p).run(), vm);
320
321 po::notify(vm);
322
323 if (vm.count("help")) {
324 fmt::print("{}", desc);
325 fmt::print("Commands:\n");
326 for (const auto& cmd : commands) {
327 fmt::print(" {} {}{}{}\n",
328 cmd.name, cmd.usage,
329 cmd.name.length() + cmd.usage.length() < 13 ?
330 "\t\t"sv : "\t"sv,
331 cmd.desc);
332 }
333 return 0;
334 }
335
336 if (vm.count("version")) {
337 fmt::print(
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()));
346 return 0;
347 }
348
349 if (vm.find("command") == vm.end()) {
350 fmt::print(std::cerr, "{}: a command is required\n", prog);
351 return 1;
352 }
353
354 ba::io_context c;
355
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);
363 return 1;
364 }
365 if (parameters.size() > ci->arity) {
366 fmt::print(std::cerr, "{}: {}: too many arguments\n\t{} {}\n",
367 prog, command, ci->name, ci->usage);
368 return 1;
369 }
370 s::spawn(c, [&](s::yield_context y) {
371 auto r = R::RADOS::Builder{}.build(c, y);
372 ci->f(r, parameters, y);
373 });
374 } else {
375 fmt::print(std::cerr, "{}: {}: unknown command\n", prog, command);
376 return 1;
377 }
378 c.run();
379 } catch (const std::exception& e) {
380 fmt::print(std::cerr, "{}: {}: {}\n", prog, command, e.what());
381 return 1;
382 }
383
384 return 0;
385}