]>
git.proxmox.com Git - ceph.git/blob - ceph/src/tools/rbd/Schedule.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
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"
16 namespace at
= argument_types
;
17 namespace po
= boost::program_options
;
21 int parse_schedule_name(const std::string
&name
, bool allow_images
,
22 std::string
*pool_name
, std::string
*namespace_name
,
23 std::string
*image_name
) {
25 // '', 'rbd/', 'rbd/ns/', 'rbd/image', 'rbd/ns/image'
26 std::regex
pattern("^(?:([^/]+)/(?:(?:([^/]+)/|)(?:([^/@]+))?)?)?$");
28 if (!std::regex_match(name
, match
, pattern
)) {
32 if (match
[1].matched
) {
33 *pool_name
= match
[1];
38 if (match
[2].matched
) {
39 *namespace_name
= match
[2];
40 } else if (match
[3].matched
) {
43 *namespace_name
= "-";
46 if (match
[3].matched
) {
50 *image_name
= match
[3];
58 } // anonymous namespace
60 void add_level_spec_options(po::options_description
*options
,
62 at::add_pool_option(options
, at::ARGUMENT_MODIFIER_NONE
);
63 at::add_namespace_option(options
, at::ARGUMENT_MODIFIER_NONE
);
65 at::add_image_option(options
, at::ARGUMENT_MODIFIER_NONE
);
69 int 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
;
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
);
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"
89 if (vm
.count(at::NAMESPACE_NAME
)) {
90 std::cerr
<< "rbd: namespace is specified both via namespace and image"
91 << " options" << std::endl
;
96 if (vm
.count(at::POOL_NAME
)) {
97 pool_name
= vm
[at::POOL_NAME
].as
<std::string
>();
100 if (vm
.count(at::NAMESPACE_NAME
)) {
101 namespace_name
= vm
[at::NAMESPACE_NAME
].as
<std::string
>();
104 if (namespace_name
.empty()) {
105 (*args
)["level_spec"] = pool_name
+ "/" + image_name
;
107 (*args
)["level_spec"] = pool_name
+ "/" + namespace_name
+ "/" +
113 if (vm
.count(at::NAMESPACE_NAME
)) {
114 std::string pool_name
;
115 std::string namespace_name
;
117 if (vm
.count(at::POOL_NAME
)) {
118 pool_name
= vm
[at::POOL_NAME
].as
<std::string
>();
121 namespace_name
= vm
[at::NAMESPACE_NAME
].as
<std::string
>();
123 (*args
)["level_spec"] = pool_name
+ "/" + namespace_name
+ "/";
128 if (vm
.count(at::POOL_NAME
)) {
129 std::string pool_name
= vm
[at::POOL_NAME
].as
<std::string
>();
131 (*args
)["level_spec"] = pool_name
+ "/";
136 (*args
)["level_spec"] = "";
141 void 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
);
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
;
151 (*args
)[key
] = value
;
155 void 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");
162 int get_schedule_args(const po::variables_map
&vm
, bool mandatory
,
163 std::map
<std::string
, std::string
> *args
) {
164 size_t arg_index
= 0;
166 std::string interval
= utils::get_positional_argument(vm
, arg_index
++);
167 if (interval
.empty()) {
169 std::cerr
<< "rbd: missing 'interval' argument" << std::endl
;
174 (*args
)["interval"] = interval
;
176 std::string start_time
= utils::get_positional_argument(vm
, arg_index
++);
177 if (!start_time
.empty()) {
178 (*args
)["start_time"] = start_time
;
184 int 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
;
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
;
199 auto &item
= item_val
.get_obj();
201 if (item
["interval"].type() != json_spirit::str_type
) {
202 std::cerr
<< "rbd: unexpected schedule JSON received: "
203 << "interval is not string" << std::endl
;
206 auto interval
= item
["interval"].get_str();
208 std::string start_time
;
209 if (item
["start_time"].type() == json_spirit::str_type
) {
210 start_time
= item
["start_time"].get_str();
213 items
.push_back({interval
, start_time
});
216 } catch (std::runtime_error
&) {
217 std::cerr
<< "rbd: invalid schedule JSON received" << std::endl
;
224 void 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
232 f
->close_section(); // items
235 std::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
;
247 int 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
;
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
;
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
;
267 auto name
= schedule
["name"].get_str();
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
;
276 int r
= s
.parse(schedule
["schedule"]);
282 } catch (std::runtime_error
&) {
283 std::cerr
<< "rbd: invalid schedule list JSON received" << std::endl
;
290 Schedule
*ScheduleList::find(const std::string
&name
) {
291 auto it
= schedules
.find(name
);
292 if (it
== schedules
.end()) {
299 void 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
;
306 int r
= parse_schedule_name(name
, allow_images
, &pool_name
, &namespace_name
,
312 f
->open_object_section("schedule");
313 f
->dump_string("pool", pool_name
);
314 f
->dump_string("namespace", namespace_name
);
316 f
->dump_string("image", image_name
);
324 std::ostream
& operator<<(std::ostream
& os
, ScheduleList
&l
) {
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
);
331 tbl
.define_column("SCHEDULE", TextTable::LEFT
, TextTable::LEFT
);
333 for (auto &[name
, s
] : l
.schedules
) {
334 std::string pool_name
;
335 std::string namespace_name
;
336 std::string image_name
;
338 int r
= parse_schedule_name(name
, l
.allow_images
, &pool_name
,
339 &namespace_name
, &image_name
);
344 std::stringstream ss
;
347 tbl
<< pool_name
<< namespace_name
;
348 if (l
.allow_images
) {
351 tbl
<< ss
.str() << TextTable::endrow
;