]> git.proxmox.com Git - ceph.git/blame - ceph/src/tools/rbd/Schedule.cc
import ceph 15.2.14
[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
ec96510d
FG
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
9f95a23c
TL
155void add_schedule_options(po::options_description *positional) {
156 positional->add_options()
157 ("interval", "schedule interval");
158 positional->add_options()
159 ("start-time", "schedule start time");
160}
161
162int get_schedule_args(const po::variables_map &vm, bool mandatory,
163 std::map<std::string, std::string> *args) {
164 size_t arg_index = 0;
165
166 std::string interval = utils::get_positional_argument(vm, arg_index++);
167 if (interval.empty()) {
168 if (mandatory) {
169 std::cerr << "rbd: missing 'interval' argument" << std::endl;
170 return -EINVAL;
171 }
172 return 0;
173 }
174 (*args)["interval"] = interval;
175
176 std::string start_time = utils::get_positional_argument(vm, arg_index++);
177 if (!start_time.empty()) {
178 (*args)["start_time"] = start_time;
179 }
180
181 return 0;
182}
183
184int Schedule::parse(json_spirit::mValue &schedule_val) {
185 if (schedule_val.type() != json_spirit::array_type) {
186 std::cerr << "rbd: unexpected schedule JSON received: "
187 << "schedule is not array" << std::endl;
188 return -EBADMSG;
189 }
190
191 try {
192 for (auto &item_val : schedule_val.get_array()) {
193 if (item_val.type() != json_spirit::obj_type) {
194 std::cerr << "rbd: unexpected schedule JSON received: "
195 << "schedule item is not object" << std::endl;
196 return -EBADMSG;
197 }
198
199 auto &item = item_val.get_obj();
200
201 if (item["interval"].type() != json_spirit::str_type) {
202 std::cerr << "rbd: unexpected schedule JSON received: "
203 << "interval is not string" << std::endl;
204 return -EBADMSG;
205 }
206 auto interval = item["interval"].get_str();
207
208 std::string start_time;
209 if (item["start_time"].type() == json_spirit::str_type) {
210 start_time = item["start_time"].get_str();
211 }
212
213 items.push_back({interval, start_time});
214 }
215
216 } catch (std::runtime_error &) {
217 std::cerr << "rbd: invalid schedule JSON received" << std::endl;
218 return -EBADMSG;
219 }
220
221 return 0;
222}
223
224void Schedule::dump(ceph::Formatter *f) {
225 f->open_array_section("items");
226 for (auto &item : items) {
227 f->open_object_section("item");
228 f->dump_string("interval", item.first);
229 f->dump_string("start_time", item.second);
230 f->close_section(); // item
231 }
232 f->close_section(); // items
233}
234
235std::ostream& operator<<(std::ostream& os, Schedule &s) {
236 std::string delimiter;
237 for (auto &item : s.items) {
238 os << delimiter << "every " << item.first;
239 if (!item.second.empty()) {
240 os << " starting at " << item.second;
241 }
242 delimiter = ", ";
243 }
244 return os;
245}
246
247int ScheduleList::parse(const std::string &list) {
248 json_spirit::mValue json_root;
249 if (!json_spirit::read(list, json_root)) {
250 std::cerr << "rbd: invalid schedule list JSON received" << std::endl;
251 return -EBADMSG;
252 }
253
254 try {
255 for (auto &[id, schedule_val] : json_root.get_obj()) {
256 if (schedule_val.type() != json_spirit::obj_type) {
257 std::cerr << "rbd: unexpected schedule list JSON received: "
258 << "schedule_val is not object" << std::endl;
259 return -EBADMSG;
260 }
261 auto &schedule = schedule_val.get_obj();
262 if (schedule["name"].type() != json_spirit::str_type) {
263 std::cerr << "rbd: unexpected schedule list JSON received: "
264 << "schedule name is not string" << std::endl;
265 return -EBADMSG;
266 }
267 auto name = schedule["name"].get_str();
268
269 if (schedule["schedule"].type() != json_spirit::array_type) {
270 std::cerr << "rbd: unexpected schedule list JSON received: "
271 << "schedule is not array" << std::endl;
272 return -EBADMSG;
273 }
274
275 Schedule s;
276 int r = s.parse(schedule["schedule"]);
277 if (r < 0) {
278 return r;
279 }
280 schedules[name] = s;
281 }
282 } catch (std::runtime_error &) {
283 std::cerr << "rbd: invalid schedule list JSON received" << std::endl;
284 return -EBADMSG;
285 }
286
287 return 0;
288}
289
290Schedule *ScheduleList::find(const std::string &name) {
291 auto it = schedules.find(name);
292 if (it == schedules.end()) {
293 return nullptr;
294 }
295
296 return &it->second;
297}
298
299void ScheduleList::dump(ceph::Formatter *f) {
300 f->open_array_section("schedules");
301 for (auto &[name, s] : schedules) {
302 std::string pool_name;
303 std::string namespace_name;
304 std::string image_name;
305
306 int r = parse_schedule_name(name, allow_images, &pool_name, &namespace_name,
307 &image_name);
308 if (r < 0) {
309 continue;
310 }
311
312 f->open_object_section("schedule");
313 f->dump_string("pool", pool_name);
314 f->dump_string("namespace", namespace_name);
315 if (allow_images) {
316 f->dump_string("image", image_name);
317 }
318 s.dump(f);
319 f->close_section();
320 }
321 f->close_section();
322}
323
324std::ostream& operator<<(std::ostream& os, ScheduleList &l) {
325 TextTable tbl;
326 tbl.define_column("POOL", TextTable::LEFT, TextTable::LEFT);
327 tbl.define_column("NAMESPACE", TextTable::LEFT, TextTable::LEFT);
328 if (l.allow_images) {
329 tbl.define_column("IMAGE", TextTable::LEFT, TextTable::LEFT);
330 }
331 tbl.define_column("SCHEDULE", TextTable::LEFT, TextTable::LEFT);
332
333 for (auto &[name, s] : l.schedules) {
334 std::string pool_name;
335 std::string namespace_name;
336 std::string image_name;
337
338 int r = parse_schedule_name(name, l.allow_images, &pool_name,
339 &namespace_name, &image_name);
340 if (r < 0) {
341 continue;
342 }
343
344 std::stringstream ss;
345 ss << s;
346
347 tbl << pool_name << namespace_name;
348 if (l.allow_images) {
349 tbl << image_name;
350 }
351 tbl << ss.str() << TextTable::endrow;
352 }
353
354 os << tbl;
355 return os;
356}
357
358} // namespace rbd
359