]> git.proxmox.com Git - ceph.git/blob - ceph/src/common/options.cc
bump version to 18.2.2-pve1
[ceph.git] / ceph / src / common / options.cc
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3
4 #include "acconfig.h"
5 #include "options.h"
6 #include "common/Formatter.h"
7 #include "common/options/build_options.h"
8
9 // Helpers for validators
10 #include "include/stringify.h"
11 #include "include/common_fwd.h"
12 #include <boost/algorithm/string.hpp>
13 #include <boost/lexical_cast.hpp>
14 #include <regex>
15
16 // Definitions for enums
17 #include "common/perf_counters.h"
18
19 // rbd feature and io operation validation
20 #include "librbd/Features.h"
21 #include "librbd/io/IoOperations.h"
22
23 using std::ostream;
24 using std::ostringstream;
25
26 using ceph::Formatter;
27 using ceph::parse_timespan;
28
29 namespace {
30 class printer {
31 ostream& out;
32 public:
33 explicit printer(ostream& os)
34 : out(os) {}
35 template<typename T>
36 void operator()(const T& v) const {
37 out << v;
38 }
39 void operator()(std::monostate) const {
40 return;
41 }
42 void operator()(bool v) const {
43 out << (v ? "true" : "false");
44 }
45 void operator()(double v) const {
46 out << std::fixed << v << std::defaultfloat;
47 }
48 void operator()(const Option::size_t& v) const {
49 out << v.value;
50 }
51 void operator()(const std::chrono::seconds v) const {
52 out << v.count();
53 }
54 void operator()(const std::chrono::milliseconds v) const {
55 out << v.count();
56 }
57 };
58 }
59
60 ostream& operator<<(ostream& os, const Option::value_t& v) {
61 printer p{os};
62 std::visit(p, v);
63 return os;
64 }
65
66 void Option::dump_value(const char *field_name,
67 const Option::value_t &v, Formatter *f) const
68 {
69 if (v == value_t{}) {
70 // This should be nil but Formatter doesn't allow it.
71 f->dump_string(field_name, "");
72 return;
73 }
74 switch (type) {
75 case TYPE_INT:
76 f->dump_int(field_name, std::get<int64_t>(v)); break;
77 case TYPE_UINT:
78 f->dump_unsigned(field_name, std::get<uint64_t>(v)); break;
79 case TYPE_STR:
80 f->dump_string(field_name, std::get<std::string>(v)); break;
81 case TYPE_FLOAT:
82 f->dump_float(field_name, std::get<double>(v)); break;
83 case TYPE_BOOL:
84 f->dump_bool(field_name, std::get<bool>(v)); break;
85 default:
86 f->dump_stream(field_name) << v; break;
87 }
88 }
89
90 int Option::pre_validate(std::string *new_value, std::string *err) const
91 {
92 if (validator) {
93 return validator(new_value, err);
94 } else {
95 return 0;
96 }
97 }
98
99 int Option::validate(const Option::value_t &new_value, std::string *err) const
100 {
101 // Generic validation: min
102 if (min != value_t{}) {
103 if (new_value < min) {
104 std::ostringstream oss;
105 oss << "Value '" << new_value << "' is below minimum " << min;
106 *err = oss.str();
107 return -EINVAL;
108 }
109 }
110
111 // Generic validation: max
112 if (max != value_t{}) {
113 if (new_value > max) {
114 std::ostringstream oss;
115 oss << "Value '" << new_value << "' exceeds maximum " << max;
116 *err = oss.str();
117 return -EINVAL;
118 }
119 }
120
121 // Generic validation: enum
122 if (!enum_allowed.empty() && type == Option::TYPE_STR) {
123 auto found = std::find(enum_allowed.begin(), enum_allowed.end(),
124 std::get<std::string>(new_value));
125 if (found == enum_allowed.end()) {
126 std::ostringstream oss;
127 oss << "'" << new_value << "' is not one of the permitted "
128 "values: " << joinify(enum_allowed.begin(),
129 enum_allowed.end(),
130 std::string(", "));
131 *err = oss.str();
132 return -EINVAL;
133 }
134 }
135
136 return 0;
137 }
138
139 int Option::parse_value(
140 const std::string& raw_val,
141 value_t *out,
142 std::string *error_message,
143 std::string *normalized_value) const
144 {
145 std::string val = raw_val;
146
147 int r = pre_validate(&val, error_message);
148 if (r != 0) {
149 return r;
150 }
151
152 if (type == Option::TYPE_INT) {
153 int64_t f = strict_si_cast<int64_t>(val, error_message);
154 if (!error_message->empty()) {
155 return -EINVAL;
156 }
157 *out = f;
158 } else if (type == Option::TYPE_UINT) {
159 uint64_t f = strict_si_cast<uint64_t>(val, error_message);
160 if (!error_message->empty()) {
161 return -EINVAL;
162 }
163 *out = f;
164 } else if (type == Option::TYPE_STR) {
165 *out = val;
166 } else if (type == Option::TYPE_FLOAT) {
167 double f = strict_strtod(val.c_str(), error_message);
168 if (!error_message->empty()) {
169 return -EINVAL;
170 } else {
171 *out = f;
172 }
173 } else if (type == Option::TYPE_BOOL) {
174 bool b = strict_strtob(val.c_str(), error_message);
175 if (!error_message->empty()) {
176 return -EINVAL;
177 } else {
178 *out = b;
179 }
180 } else if (type == Option::TYPE_ADDR) {
181 entity_addr_t addr;
182 if (!addr.parse(val)){
183 return -EINVAL;
184 }
185 *out = addr;
186 } else if (type == Option::TYPE_ADDRVEC) {
187 entity_addrvec_t addr;
188 if (!addr.parse(val.c_str())){
189 return -EINVAL;
190 }
191 *out = addr;
192 } else if (type == Option::TYPE_UUID) {
193 uuid_d uuid;
194 if (!uuid.parse(val.c_str())) {
195 return -EINVAL;
196 }
197 *out = uuid;
198 } else if (type == Option::TYPE_SIZE) {
199 Option::size_t sz{strict_iecstrtoll(val, error_message)};
200 if (!error_message->empty()) {
201 return -EINVAL;
202 }
203 *out = sz;
204 } else if (type == Option::TYPE_SECS) {
205 try {
206 *out = parse_timespan(val);
207 } catch (const std::invalid_argument& e) {
208 *error_message = e.what();
209 return -EINVAL;
210 }
211 } else if (type == Option::TYPE_MILLISECS) {
212 try {
213 *out = std::chrono::milliseconds(std::stoull(val));
214 } catch (const std::logic_error& e) {
215 *error_message = e.what();
216 return -EINVAL;
217 }
218 } else {
219 ceph_abort();
220 }
221
222 r = validate(*out, error_message);
223 if (r != 0) {
224 return r;
225 }
226
227 if (normalized_value) {
228 *normalized_value = to_str(*out);
229 }
230 return 0;
231 }
232
233 void Option::dump(Formatter *f) const
234 {
235 f->dump_string("name", name);
236
237 f->dump_string("type", type_to_str(type));
238
239 f->dump_string("level", level_to_str(level));
240
241 f->dump_string("desc", desc);
242 f->dump_string("long_desc", long_desc);
243
244 dump_value("default", value, f);
245 dump_value("daemon_default", daemon_value, f);
246
247 f->open_array_section("tags");
248 for (const auto t : tags) {
249 f->dump_string("tag", t);
250 }
251 f->close_section();
252
253 f->open_array_section("services");
254 for (const auto s : services) {
255 f->dump_string("service", s);
256 }
257 f->close_section();
258
259 f->open_array_section("see_also");
260 for (const auto sa : see_also) {
261 f->dump_string("see_also", sa);
262 }
263 f->close_section();
264
265 if (type == TYPE_STR) {
266 f->open_array_section("enum_values");
267 for (const auto &ea : enum_allowed) {
268 f->dump_string("enum_value", ea);
269 }
270 f->close_section();
271 }
272
273 dump_value("min", min, f);
274 dump_value("max", max, f);
275
276 f->dump_bool("can_update_at_runtime", can_update_at_runtime());
277
278 f->open_array_section("flags");
279 if (has_flag(FLAG_RUNTIME)) {
280 f->dump_string("option", "runtime");
281 }
282 if (has_flag(FLAG_NO_MON_UPDATE)) {
283 f->dump_string("option", "no_mon_update");
284 }
285 if (has_flag(FLAG_STARTUP)) {
286 f->dump_string("option", "startup");
287 }
288 if (has_flag(FLAG_CLUSTER_CREATE)) {
289 f->dump_string("option", "cluster_create");
290 }
291 if (has_flag(FLAG_CREATE)) {
292 f->dump_string("option", "create");
293 }
294 f->close_section();
295 }
296
297 std::string Option::to_str(const Option::value_t& v)
298 {
299 return stringify(v);
300 }
301
302 void Option::print(ostream *out) const
303 {
304 *out << name << " - " << desc << "\n";
305 *out << " (" << type_to_str(type) << ", " << level_to_str(level) << ")\n";
306 if (daemon_value != value_t{}) {
307 *out << " Default (non-daemon): " << stringify(value) << "\n";
308 *out << " Default (daemon): " << stringify(daemon_value) << "\n";
309 } else {
310 *out << " Default: " << stringify(value) << "\n";
311 }
312 if (!enum_allowed.empty()) {
313 *out << " Possible values: ";
314 for (auto& i : enum_allowed) {
315 *out << " " << stringify(i);
316 }
317 *out << "\n";
318 }
319 if (min != value_t{}) {
320 *out << " Minimum: " << stringify(min) << "\n"
321 << " Maximum: " << stringify(max) << "\n";
322 }
323 *out << " Can update at runtime: "
324 << (can_update_at_runtime() ? "true" : "false") << "\n";
325 if (!services.empty()) {
326 *out << " Services: " << services << "\n";
327 }
328 if (!tags.empty()) {
329 *out << " Tags: " << tags << "\n";
330 }
331 if (!see_also.empty()) {
332 *out << " See also: " << see_also << "\n";
333 }
334
335 if (long_desc.size()) {
336 *out << "\n" << long_desc << "\n";
337 }
338 }
339
340 const std::vector<Option> ceph_options = build_options();