]> git.proxmox.com Git - ceph.git/blame - ceph/src/tools/rbd/Schedule.cc
import quincy beta 17.1.0
[ceph.git] / ceph / src / tools / rbd / Schedule.cc
CommitLineData
9f95a23c
TL
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/Formatter.h"
5#include "common/TextTable.h"
6#include "common/ceph_json.h"
7#include "tools/rbd/ArgumentTypes.h"
8#include "tools/rbd/Schedule.h"
9#include "tools/rbd/Utils.h"
10
11#include <iostream>
12#include <regex>
13
14namespace rbd {
15
16namespace at = argument_types;
17namespace po = boost::program_options;
18
19namespace {
20
21int parse_schedule_name(const std::string &name, bool allow_images,
22 std::string *pool_name, std::string *namespace_name,
23 std::string *image_name) {
24 // parse names like:
25 // '', 'rbd/', 'rbd/ns/', 'rbd/image', 'rbd/ns/image'
26 std::regex pattern("^(?:([^/]+)/(?:(?:([^/]+)/|)(?:([^/@]+))?)?)?$");
27 std::smatch match;
28 if (!std::regex_match(name, match, pattern)) {
29 return -EINVAL;
30 }
31
32 if (match[1].matched) {
33 *pool_name = match[1];
34 } else {
35 *pool_name = "-";
36 }
37
38 if (match[2].matched) {
39 *namespace_name = match[2];
40 } else if (match[3].matched) {
41 *namespace_name = "";
42 } else {
43 *namespace_name = "-";
44 }
45
46 if (match[3].matched) {
47 if (!allow_images) {
48 return -EINVAL;
49 }
50 *image_name = match[3];
51 } else {
52 *image_name = "-";
53 }
54
55 return 0;
56}
57
58} // anonymous namespace
59
60void add_level_spec_options(po::options_description *options,
61 bool allow_image) {
62 at::add_pool_option(options, at::ARGUMENT_MODIFIER_NONE);
63 at::add_namespace_option(options, at::ARGUMENT_MODIFIER_NONE);
64 if (allow_image) {
65 at::add_image_option(options, at::ARGUMENT_MODIFIER_NONE);
66 }
67}
68
69int get_level_spec_args(const po::variables_map &vm,
70 std::map<std::string, std::string> *args) {
71 if (vm.count(at::IMAGE_NAME)) {
72 std::string pool_name;
73 std::string namespace_name;
74 std::string image_name;
75
76 int r = utils::extract_spec(vm[at::IMAGE_NAME].as<std::string>(),
77 &pool_name, &namespace_name, &image_name,
78 nullptr, utils::SPEC_VALIDATION_FULL);
79 if (r < 0) {
80 return r;
81 }
82
83 if (!pool_name.empty()) {
84 if (vm.count(at::POOL_NAME)) {
85 std::cerr << "rbd: pool is specified both via pool and image options"
86 << std::endl;
87 return -EINVAL;
88 }
89 if (vm.count(at::NAMESPACE_NAME)) {
90 std::cerr << "rbd: namespace is specified both via namespace and image"
91 << " options" << std::endl;
92 return -EINVAL;
93 }
94 }
95
96 if (vm.count(at::POOL_NAME)) {
97 pool_name = vm[at::POOL_NAME].as<std::string>();
9f95a23c
TL
98 }
99
100 if (vm.count(at::NAMESPACE_NAME)) {
101 namespace_name = vm[at::NAMESPACE_NAME].as<std::string>();
102 }
103
104 if (namespace_name.empty()) {
105 (*args)["level_spec"] = pool_name + "/" + image_name;
106 } else {
107 (*args)["level_spec"] = pool_name + "/" + namespace_name + "/" +
108 image_name;
109 }
110 return 0;
111 }
112
113 if (vm.count(at::NAMESPACE_NAME)) {
114 std::string pool_name;
115 std::string namespace_name;
116
117 if (vm.count(at::POOL_NAME)) {
118 pool_name = vm[at::POOL_NAME].as<std::string>();
9f95a23c
TL
119 }
120
121 namespace_name = vm[at::NAMESPACE_NAME].as<std::string>();
122
123 (*args)["level_spec"] = pool_name + "/" + namespace_name + "/";
124
125 return 0;
126 }
127
128 if (vm.count(at::POOL_NAME)) {
129 std::string pool_name = vm[at::POOL_NAME].as<std::string>();
130
131 (*args)["level_spec"] = pool_name + "/";
132
133 return 0;
134 }
135
136 (*args)["level_spec"] = "";
137
138 return 0;
139}
140
f67539c2
TL
141void normalize_level_spec_args(std::map<std::string, std::string> *args) {
142 std::map<std::string, std::string> raw_args;
143 std::swap(raw_args, *args);
144
145 auto default_pool_name = utils::get_default_pool_name();
146 for (auto [key, value] : raw_args) {
147 if (key == "level_spec" && !value.empty() && value[0] == '/') {
148 value = default_pool_name + value;
149 }
150
151 (*args)[key] = value;
152 }
153}
154
20effc67
TL
155void add_schedule_options(po::options_description *positional,
156 bool mandatory) {
157 if (mandatory) {
158 positional->add_options()
159 ("interval", "schedule interval");
160 } else {
161 positional->add_options()
162 ("interval", po::value<std::string>()->default_value(""),
163 "schedule interval");
164 }
9f95a23c 165 positional->add_options()
20effc67
TL
166 ("start-time", po::value<std::string>()->default_value(""),
167 "schedule start time");
9f95a23c
TL
168}
169
170int get_schedule_args(const po::variables_map &vm, bool mandatory,
171 std::map<std::string, std::string> *args) {
172 size_t arg_index = 0;
173
174 std::string interval = utils::get_positional_argument(vm, arg_index++);
175 if (interval.empty()) {
176 if (mandatory) {
177 std::cerr << "rbd: missing 'interval' argument" << std::endl;
178 return -EINVAL;
179 }
180 return 0;
181 }
182 (*args)["interval"] = interval;
183
184 std::string start_time = utils::get_positional_argument(vm, arg_index++);
185 if (!start_time.empty()) {
186 (*args)["start_time"] = start_time;
187 }
188
189 return 0;
190}
191
192int Schedule::parse(json_spirit::mValue &schedule_val) {
193 if (schedule_val.type() != json_spirit::array_type) {
194 std::cerr << "rbd: unexpected schedule JSON received: "
195 << "schedule is not array" << std::endl;
196 return -EBADMSG;
197 }
198
199 try {
200 for (auto &item_val : schedule_val.get_array()) {
201 if (item_val.type() != json_spirit::obj_type) {
202 std::cerr << "rbd: unexpected schedule JSON received: "
203 << "schedule item is not object" << std::endl;
204 return -EBADMSG;
205 }
206
207 auto &item = item_val.get_obj();
208
209 if (item["interval"].type() != json_spirit::str_type) {
210 std::cerr << "rbd: unexpected schedule JSON received: "
211 << "interval is not string" << std::endl;
212 return -EBADMSG;
213 }
214 auto interval = item["interval"].get_str();
215
216 std::string start_time;
217 if (item["start_time"].type() == json_spirit::str_type) {
218 start_time = item["start_time"].get_str();
219 }
220
221 items.push_back({interval, start_time});
222 }
223
224 } catch (std::runtime_error &) {
225 std::cerr << "rbd: invalid schedule JSON received" << std::endl;
226 return -EBADMSG;
227 }
228
229 return 0;
230}
231
232void Schedule::dump(ceph::Formatter *f) {
233 f->open_array_section("items");
234 for (auto &item : items) {
235 f->open_object_section("item");
236 f->dump_string("interval", item.first);
237 f->dump_string("start_time", item.second);
238 f->close_section(); // item
239 }
240 f->close_section(); // items
241}
242
243std::ostream& operator<<(std::ostream& os, Schedule &s) {
244 std::string delimiter;
245 for (auto &item : s.items) {
246 os << delimiter << "every " << item.first;
247 if (!item.second.empty()) {
248 os << " starting at " << item.second;
249 }
250 delimiter = ", ";
251 }
252 return os;
253}
254
255int ScheduleList::parse(const std::string &list) {
256 json_spirit::mValue json_root;
257 if (!json_spirit::read(list, json_root)) {
258 std::cerr << "rbd: invalid schedule list JSON received" << std::endl;
259 return -EBADMSG;
260 }
261
262 try {
263 for (auto &[id, schedule_val] : json_root.get_obj()) {
264 if (schedule_val.type() != json_spirit::obj_type) {
265 std::cerr << "rbd: unexpected schedule list JSON received: "
266 << "schedule_val is not object" << std::endl;
267 return -EBADMSG;
268 }
269 auto &schedule = schedule_val.get_obj();
270 if (schedule["name"].type() != json_spirit::str_type) {
271 std::cerr << "rbd: unexpected schedule list JSON received: "
272 << "schedule name is not string" << std::endl;
273 return -EBADMSG;
274 }
275 auto name = schedule["name"].get_str();
276
277 if (schedule["schedule"].type() != json_spirit::array_type) {
278 std::cerr << "rbd: unexpected schedule list JSON received: "
279 << "schedule is not array" << std::endl;
280 return -EBADMSG;
281 }
282
283 Schedule s;
284 int r = s.parse(schedule["schedule"]);
285 if (r < 0) {
286 return r;
287 }
288 schedules[name] = s;
289 }
290 } catch (std::runtime_error &) {
291 std::cerr << "rbd: invalid schedule list JSON received" << std::endl;
292 return -EBADMSG;
293 }
294
295 return 0;
296}
297
298Schedule *ScheduleList::find(const std::string &name) {
299 auto it = schedules.find(name);
300 if (it == schedules.end()) {
301 return nullptr;
302 }
303
304 return &it->second;
305}
306
307void ScheduleList::dump(ceph::Formatter *f) {
308 f->open_array_section("schedules");
309 for (auto &[name, s] : schedules) {
310 std::string pool_name;
311 std::string namespace_name;
312 std::string image_name;
313
314 int r = parse_schedule_name(name, allow_images, &pool_name, &namespace_name,
315 &image_name);
316 if (r < 0) {
317 continue;
318 }
319
320 f->open_object_section("schedule");
321 f->dump_string("pool", pool_name);
322 f->dump_string("namespace", namespace_name);
323 if (allow_images) {
324 f->dump_string("image", image_name);
325 }
326 s.dump(f);
327 f->close_section();
328 }
329 f->close_section();
330}
331
332std::ostream& operator<<(std::ostream& os, ScheduleList &l) {
333 TextTable tbl;
334 tbl.define_column("POOL", TextTable::LEFT, TextTable::LEFT);
335 tbl.define_column("NAMESPACE", TextTable::LEFT, TextTable::LEFT);
336 if (l.allow_images) {
337 tbl.define_column("IMAGE", TextTable::LEFT, TextTable::LEFT);
338 }
339 tbl.define_column("SCHEDULE", TextTable::LEFT, TextTable::LEFT);
340
341 for (auto &[name, s] : l.schedules) {
342 std::string pool_name;
343 std::string namespace_name;
344 std::string image_name;
345
346 int r = parse_schedule_name(name, l.allow_images, &pool_name,
347 &namespace_name, &image_name);
348 if (r < 0) {
349 continue;
350 }
351
352 std::stringstream ss;
353 ss << s;
354
355 tbl << pool_name << namespace_name;
356 if (l.allow_images) {
357 tbl << image_name;
358 }
359 tbl << ss.str() << TextTable::endrow;
360 }
361
362 os << tbl;
363 return os;
364}
365
366} // namespace rbd
367