]> git.proxmox.com Git - ceph.git/blob - ceph/src/common/options.h
update ceph source to reef 18.2.0
[ceph.git] / ceph / src / common / options.h
1 // -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
2 // vim: ts=8 sw=2 smarttab
3
4 #pragma once
5
6 #include <chrono>
7 #include <string>
8 #include <variant>
9 #include <vector>
10 #include "include/str_list.h"
11 #include "msg/msg_types.h"
12 #include "include/uuid.h"
13
14 struct Option {
15 enum type_t {
16 TYPE_UINT = 0,
17 TYPE_INT = 1,
18 TYPE_STR = 2,
19 TYPE_FLOAT = 3,
20 TYPE_BOOL = 4,
21 TYPE_ADDR = 5,
22 TYPE_ADDRVEC = 6,
23 TYPE_UUID = 7,
24 TYPE_SIZE = 8,
25 TYPE_SECS = 9,
26 TYPE_MILLISECS = 10,
27 };
28
29 static const char *type_to_c_type_str(type_t t) {
30 switch (t) {
31 case TYPE_UINT: return "uint64_t";
32 case TYPE_INT: return "int64_t";
33 case TYPE_STR: return "std::string";
34 case TYPE_FLOAT: return "double";
35 case TYPE_BOOL: return "bool";
36 case TYPE_ADDR: return "entity_addr_t";
37 case TYPE_ADDRVEC: return "entity_addrvec_t";
38 case TYPE_UUID: return "uuid_d";
39 case TYPE_SIZE: return "uint64_t";
40 case TYPE_SECS: return "secs";
41 case TYPE_MILLISECS: return "millisecs";
42 default: return "unknown";
43 }
44 }
45 static const char *type_to_str(type_t t) {
46 switch (t) {
47 case TYPE_UINT: return "uint";
48 case TYPE_INT: return "int";
49 case TYPE_STR: return "str";
50 case TYPE_FLOAT: return "float";
51 case TYPE_BOOL: return "bool";
52 case TYPE_ADDR: return "addr";
53 case TYPE_ADDRVEC: return "addrvec";
54 case TYPE_UUID: return "uuid";
55 case TYPE_SIZE: return "size";
56 case TYPE_SECS: return "secs";
57 case TYPE_MILLISECS: return "millisecs";
58 default: return "unknown";
59 }
60 }
61 static int str_to_type(const std::string& s) {
62 if (s == "uint") {
63 return TYPE_UINT;
64 }
65 if (s == "int") {
66 return TYPE_INT;
67 }
68 if (s == "str") {
69 return TYPE_STR;
70 }
71 if (s == "float") {
72 return TYPE_FLOAT;
73 }
74 if (s == "bool") {
75 return TYPE_BOOL;
76 }
77 if (s == "addr") {
78 return TYPE_ADDR;
79 }
80 if (s == "addrvec") {
81 return TYPE_ADDRVEC;
82 }
83 if (s == "uuid") {
84 return TYPE_UUID;
85 }
86 if (s == "size") {
87 return TYPE_SIZE;
88 }
89 if (s == "secs") {
90 return TYPE_SECS;
91 }
92 if (s == "millisecs") {
93 return TYPE_MILLISECS;
94 }
95 return -1;
96 }
97
98 /**
99 * Basic: for users, configures some externally visible functional aspect
100 * Advanced: for users, configures some internal behaviour
101 * Development: not for users. May be dangerous, may not be documented.
102 */
103 enum level_t {
104 LEVEL_BASIC = 0,
105 LEVEL_ADVANCED = 1,
106 LEVEL_DEV = 2,
107 LEVEL_UNKNOWN = 3,
108 };
109
110 static const char *level_to_str(level_t l) {
111 switch (l) {
112 case LEVEL_BASIC: return "basic";
113 case LEVEL_ADVANCED: return "advanced";
114 case LEVEL_DEV: return "dev";
115 default: return "unknown";
116 }
117 }
118
119 enum flag_t {
120 FLAG_RUNTIME = 0x1, ///< option can be changed at runtime
121 FLAG_NO_MON_UPDATE = 0x2, ///< option cannot be changed via mon config
122 FLAG_STARTUP = 0x4, ///< option can only take effect at startup
123 FLAG_CLUSTER_CREATE = 0x8, ///< option only has effect at cluster creation
124 FLAG_CREATE = 0x10, ///< option only has effect at daemon creation
125 FLAG_MGR = 0x20, ///< option is a mgr module option
126 FLAG_MINIMAL_CONF = 0x40, ///< option should go in a minimal ceph.conf
127 };
128
129 struct size_t {
130 std::uint64_t value;
131 operator uint64_t() const {
132 return static_cast<uint64_t>(value);
133 }
134 bool operator==(const size_t& rhs) const {
135 return value == rhs.value;
136 }
137 };
138
139 using value_t = std::variant<
140 std::monostate,
141 std::string,
142 uint64_t,
143 int64_t,
144 double,
145 bool,
146 entity_addr_t,
147 entity_addrvec_t,
148 std::chrono::seconds,
149 std::chrono::milliseconds,
150 size_t,
151 uuid_d>;
152 const std::string name;
153 const type_t type;
154 const level_t level;
155
156 std::string desc;
157 std::string long_desc;
158
159 unsigned flags = 0;
160
161 int subsys = -1; // if >= 0, we are a subsys debug level
162
163 value_t value;
164 value_t daemon_value;
165
166 static std::string to_str(const value_t& v);
167
168 // Items like mon, osd, rgw, rbd, ceph-fuse. This is advisory metadata
169 // for presentation layers (like web dashboards, or generated docs), so that
170 // they know which options to display where.
171 // Additionally: "common" for settings that exist in any Ceph code. Do
172 // not use common for settings that are just shared some places: for those
173 // places, list them.
174 std::vector<const char*> services;
175
176 // Topics like:
177 // "service": a catchall for the boring stuff like log/asok paths.
178 // "network"
179 // "performance": a setting that may need adjustment depending on
180 // environment/workload to get best performance.
181 std::vector<const char*> tags;
182
183 std::vector<const char*> see_also;
184
185 value_t min, max;
186 std::vector<const char*> enum_allowed;
187
188 /**
189 * Return nonzero and set second argument to error string if the
190 * value is invalid.
191 *
192 * These callbacks are more than just validators, as they can also
193 * modify the value as it passes through.
194 */
195 typedef std::function<int(std::string *, std::string *)> validator_fn_t;
196 validator_fn_t validator;
197
198 Option(std::string const &name, type_t t, level_t l)
199 : name(name), type(t), level(l)
200 {
201 // While value_t is nullable (via std::monostate), we don't ever
202 // want it set that way in an Option instance: within an instance,
203 // the type of ::value should always match the declared type.
204 switch (type) {
205 case TYPE_INT:
206 value = int64_t(0); break;
207 case TYPE_UINT:
208 value = uint64_t(0); break;
209 case TYPE_STR:
210 value = std::string(""); break;
211 case TYPE_FLOAT:
212 value = 0.0; break;
213 case TYPE_BOOL:
214 value = false; break;
215 case TYPE_ADDR:
216 value = entity_addr_t(); break;
217 case TYPE_ADDRVEC:
218 value = entity_addrvec_t(); break;
219 case TYPE_UUID:
220 value = uuid_d(); break;
221 case TYPE_SIZE:
222 value = size_t{0}; break;
223 case TYPE_SECS:
224 value = std::chrono::seconds{0}; break;
225 case TYPE_MILLISECS:
226 value = std::chrono::milliseconds{0}; break;
227 default:
228 ceph_abort();
229 }
230 }
231
232 void dump_value(const char *field_name, const value_t &v, ceph::Formatter *f) const;
233
234 // Validate and potentially modify incoming string value
235 int pre_validate(std::string *new_value, std::string *err) const;
236
237 // Validate properly typed value against bounds
238 int validate(const Option::value_t &new_value, std::string *err) const;
239
240 // const char * must be explicit to avoid it being treated as an int
241 Option& set_value(value_t& v, const char *new_value) {
242 v = std::string(new_value);
243 return *this;
244 }
245
246 // bool is an integer, but we don't think so. teach it the hard way.
247 template<typename T>
248 using is_not_integer_t =
249 std::enable_if_t<!std::is_integral_v<T> || std::is_same_v<T, bool>, int>;
250 template<typename T>
251 using is_integer_t =
252 std::enable_if_t<std::is_integral_v<T> && !std::is_same_v<T, bool>, int>;
253 template<typename T, typename = is_not_integer_t<T>>
254 Option& set_value(value_t& v, const T& new_value) {
255 v = new_value;
256 return *this;
257 }
258
259 // For potentially ambiguous types, inspect Option::type and
260 // do some casting. This is necessary to make sure that setting
261 // a float option to "0" actually sets the double part of variant.
262 template<typename T, typename = is_integer_t<T>>
263 Option& set_value(value_t& v, T new_value) {
264 switch (type) {
265 case TYPE_INT:
266 v = int64_t(new_value); break;
267 case TYPE_UINT:
268 v = uint64_t(new_value); break;
269 case TYPE_FLOAT:
270 v = double(new_value); break;
271 case TYPE_BOOL:
272 v = bool(new_value); break;
273 case TYPE_SIZE:
274 v = size_t{static_cast<std::uint64_t>(new_value)}; break;
275 case TYPE_SECS:
276 v = std::chrono::seconds{new_value}; break;
277 case TYPE_MILLISECS:
278 v = std::chrono::milliseconds{new_value}; break;
279 default:
280 std::cerr << "Bad type in set_value: " << name << ": "
281 << typeid(T).name() << std::endl;
282 ceph_abort();
283 }
284 return *this;
285 }
286
287 /// parse and validate a string input
288 int parse_value(
289 const std::string& raw_val,
290 value_t *out,
291 std::string *error_message,
292 std::string *normalized_value=nullptr) const;
293
294 template<typename T>
295 Option& set_default(const T& v) {
296 return set_value(value, v);
297 }
298
299 template<typename T>
300 Option& set_daemon_default(const T& v) {
301 return set_value(daemon_value, v);
302 }
303 Option& add_tag(const char* tag) {
304 tags.push_back(tag);
305 return *this;
306 }
307 Option& add_tag(const std::initializer_list<const char*>& ts) {
308 tags.insert(tags.end(), ts);
309 return *this;
310 }
311 Option& add_service(const char* service) {
312 services.push_back(service);
313 return *this;
314 }
315 Option& add_service(const std::initializer_list<const char*>& ss) {
316 services.insert(services.end(), ss);
317 return *this;
318 }
319 Option& add_see_also(const char* t) {
320 see_also.push_back(t);
321 return *this;
322 }
323 Option& add_see_also(const std::initializer_list<const char*>& ts) {
324 see_also.insert(see_also.end(), ts);
325 return *this;
326 }
327 Option& set_description(const char* new_desc) {
328 desc = new_desc;
329 return *this;
330 }
331 Option& set_long_description(const char* new_desc) {
332 long_desc = new_desc;
333 return *this;
334 }
335
336 template<typename T>
337 Option& set_min(const T& mi) {
338 set_value(min, mi);
339 return *this;
340 }
341
342 template<typename T>
343 Option& set_min_max(const T& mi, const T& ma) {
344 set_value(min, mi);
345 set_value(max, ma);
346 return *this;
347 }
348
349 Option& set_enum_allowed(const std::vector<const char*>& allowed)
350 {
351 enum_allowed = allowed;
352 return *this;
353 }
354
355 Option &set_flag(flag_t f) {
356 flags |= f;
357 return *this;
358 }
359 Option &set_flags(flag_t f) {
360 flags |= f;
361 return *this;
362 }
363
364 Option &set_validator(const validator_fn_t &validator_)
365 {
366 validator = validator_;
367 return *this;
368 }
369
370 Option &set_subsys(int s) {
371 subsys = s;
372 return *this;
373 }
374
375 void dump(ceph::Formatter *f) const;
376 void print(std::ostream *out) const;
377
378 bool has_flag(flag_t f) const {
379 return flags & f;
380 }
381
382 /**
383 * A crude indicator of whether the value may be
384 * modified safely at runtime -- should be replaced
385 * with proper locking!
386 */
387 bool can_update_at_runtime() const
388 {
389 return
390 (has_flag(FLAG_RUNTIME)
391 || (!has_flag(FLAG_MGR)
392 && (type == TYPE_BOOL || type == TYPE_INT
393 || type == TYPE_UINT || type == TYPE_FLOAT
394 || type == TYPE_SIZE || type == TYPE_SECS
395 || type == TYPE_MILLISECS)))
396 && !has_flag(FLAG_STARTUP)
397 && !has_flag(FLAG_CLUSTER_CREATE)
398 && !has_flag(FLAG_CREATE);
399 }
400 };
401
402 constexpr unsigned long long operator"" _min (unsigned long long min) {
403 return min * 60;
404 }
405 constexpr unsigned long long operator"" _hr (unsigned long long hr) {
406 return hr * 60 * 60;
407 }
408 constexpr unsigned long long operator"" _day (unsigned long long day) {
409 return day * 24 * 60 * 60;
410 }
411 constexpr unsigned long long operator"" _K (unsigned long long n) {
412 return n << 10;
413 }
414 constexpr unsigned long long operator"" _M (unsigned long long n) {
415 return n << 20;
416 }
417 constexpr unsigned long long operator"" _G (unsigned long long n) {
418 return n << 30;
419 }
420 constexpr unsigned long long operator"" _T (unsigned long long n) {
421 return n << 40;
422 }
423
424 extern const std::vector<Option> ceph_options;