common/options: new options infrastructure

Define schema for config options.  Helper to generate a header fragment
to declare the types.

Unlike the old config_opts.h approach, we will not intialize values in
the header.  This avoids a recompile if there is a change and also allows
us to specify different defaults and do parsing and validation at runtime.
Instead, we'll intialize values in the constructure of the containing
class.

Signed-off-by: Sage Weil <sage@redhat.com>
This commit is contained in:
Sage Weil 2017-05-11 22:31:50 -05:00 committed by John Spray
parent 8463894575
commit fe1c592c8c
6 changed files with 687 additions and 0 deletions

View File

@ -508,6 +508,7 @@ set(libcommon_files
common/ceph_hash.cc
common/ceph_strings.cc
common/ceph_frag.cc
common/options.cc
common/config.cc
common/config_validators.cc
common/utf8.c
@ -890,6 +891,11 @@ add_subdirectory(compressor)
add_subdirectory(tools)
# to generate config header
add_executable(
generate_option_header
common/generate_option_header.cc)
# dmClock (after gmock)
add_subdirectory(dmclock/src)

View File

@ -0,0 +1,34 @@
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
#include <stdio.h>
#include <stdlib.h>
#include "options.c"
const char *type_to_str(ceph_option_type_t t)
{
switch (t) {
case OPT_INT: return "int";
case OPT_LONGLONG: return "long long";
case OPT_STR: return "std::string";
case OPT_DOUBLE: return "double";
case OPT_FLOAT: return "float";
case OPT_BOOL: return "bool";
case OPT_ADDR: return "entity_addr_t";
case OPT_U32: return "uint32_t";
case OPT_U64: return "uint64_t";
case OPT_UUID: return "uuid_d";
default: return "unknown_type";
}
};
int main(int argc, char **argv)
{
for (unsigned i=0; ceph_options[i].name; ++i) {
struct ceph_option *o = &ceph_options[i];
printf("\t%s %s;\n", type_to_str(o->type), o->name);
}
return 0;
}

View File

@ -0,0 +1,15 @@
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
#include "include/stringify.h"
#include "options.cc"
int main(int argc, char **argv)
{
for (unsigned i=0; ceph_options[i].name.size(); ++i) {
Option& o = ceph_options[i];
printf("\t%s %s;\n", o.type_to_str(o.type), o.name.c_str());
}
return 0;
}

307
src/common/options.c Normal file
View File

@ -0,0 +1,307 @@
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
#include "include/acconfig.h"
#include "options.h"
struct ceph_option _ceph_options[] = {
{
.name = "host",
.type = OPT_STR,
.description = "local hostname; if blank, we will use the short hostname (hostname -s)",
.tags = "common",
},
{
.name = "fsid",
.type = OPT_UUID,
.description = "cluster fsid (uuid)",
.tags = "common",
},
{
.name = "public_addr",
.type = OPT_STR,
.description = "public-facing address to bind to",
.tags = "mon mds osd mgr",
},
{
.name = "cluster_addr",
.type = OPT_STR,
.description = "cluster-facing address to bind to",
.tags = "osd",
},
{
.name = "monmap",
.type = OPT_STR,
.description = "path to MonMap file",
.long_description = "This option is normally used during mkfs.",
.tags = "mon mkfs",
},
{
.name = "mon_host",
.type = OPT_STR,
.description = "list of hosts or addresses to search for a monitor",
.tags = "common",
},
{
.name = "mon_dns_srv_name",
.type = OPT_STR,
.description = "name of DNS SRV record to check for monitor addresses",
.tags = "common",
.see_also = "mon_host",
},
{
.name = "lockdep",
.type = OPT_BOOL,
.value = "false",
.level = OPT_DEV,
.description = "enable lockdep lock dependency analyzer",
.tags = "common",
},
{
.name = "lockdep_force_backtrace",
.type = OPT_BOOL,
.level = OPT_DEV,
.description = "always gather current backtrace at every lock",
.tags = "common",
.see_also = "lockdep",
},
{
.name = "run_dir",
.type = OPT_STR,
.level = OPT_ADVANCED,
.daemon_value = "/var/run/ceph",
.description = "path for the 'run' directory for storing pid and socket files",
.tags = "common",
.see_also = "admin_socket",
},
{
.name = "admin_socket",
.type = OPT_STR,
.level = OPT_ADVANCED,
.daemon_value = "$run_dir/$cluster-$name.asok",
.description = "path for the runtime control socket file, used by the 'ceph daemon' command",
.tags = "common",
},
{
.name = "admin_socket_mode",
.type = OPT_STR,
.level = OPT_ADVANCED,
.description = "file mode to set for the admin socket file, e.g, '0755'",
.tags = "common",
.see_also = "admin_socket",
},
{
.name = "crushtool",
.type = OPT_STR,
.level = OPT_ADVANCED,
.description = "name of crushtool utility",
.tags = "mon",
},
{
.name = "daemonize",
.type = OPT_BOOL,
.level = OPT_ADVANCED,
.description = "whether to daemonize on startup",
.value = "false",
.daemon_value = "true",
.tags = "daemon",
.see_also = "pid_file chdir",
},
{
.name = "setuser",
.type = OPT_STR,
.level = OPT_ADVANCED,
.description = "uid or user name to switch to on startup",
.long_description = "This is normally specified by the systemd unit file.",
.tags = "daemon",
.see_also = "setgroup",
},
{
.name = "setgroup",
.type = OPT_STR,
.level = OPT_ADVANCED,
.description = "gid or group name to switch to on startup",
.long_description = "This is normally specified by the systemd unit file.",
.tags = "daemon",
.see_also = "setuser",
},
{
.name = "setuser_match_path",
.type = OPT_STR,
.level = OPT_ADVANCED,
.description = "if set, setuser/setgroup is condition on this path matching ownership",
.long_description = "If setuser or setgroup are specified, and this option is non-empty, then the uid/gid of the daemon will only be changed if the file or directory specified by this option has a matching uid and/or gid. This exists primarily to allow switching to user ceph for OSDs to be conditional on whether the osd data contents have also been chowned after an upgrade. This is normally specified by the systemd unit file.",
.tags = "daemon osd",
.see_also = "setuser setgroup",
},
{
.name = "pid_file",
.type = OPT_STR,
.level = OPT_ADVANCED,
.description = "path to write a pid file (if any)",
.tags = "daemon",
},
{
.name = "chdir",
.type = OPT_STR,
.level = OPT_ADVANCED,
.description = "path to chdir(2) to after daemonizing",
.tags = "daemon",
.see_also = "daemonize",
},
{
.name = "restapi_log_level",
.type = OPT_STR,
.level = OPT_ADVANCED,
.description = "default set by python code",
},
{
.name = "restapi_base_url",
.type = OPT_STR,
.level = OPT_ADVANCED,
.description = "default set by python code",
},
{
.name = "fatal_signal_handlers",
.type = OPT_BOOL,
.level = OPT_ADVANCED,
.value = "true",
.description = "whether to register signal handlers for SIGABRT etc that dump a stack trace",
.long_description = "This is normally true for daemons and values for libraries.",
.tags = "daemon",
},
{
.name = "erasure_code_dir",
.type = OPT_STR,
.level = OPT_ADVANCED,
.value = CEPH_PKGLIBDIR"/erasure-code",
.description = "directory where erasure-code plugins can be found",
.tags = "mon osd",
},
// -------------
// internal logging facility
{
.name = "log_file",
.type = OPT_STR,
.level = OPT_BASIC,
.daemon_value = "/var/log/ceph/$cluster-$name.log",
.description = "path to log file",
.see_also = "log_to_stderr err_to_stderr log_to_syslog err_to_syslog",
},
{
.name = "log_max_new",
.type = OPT_INT,
.level = OPT_ADVANCED,
.value = "1000",
.description = "max unwritten log entries to allow before waiting to flush to the log",
.see_also = "log_max_recent",
},
{
.name = "log_max_recent",
.type = OPT_INT,
.level = OPT_BASIC,
.daemon_value = "10000",
.nondaemon_value = "500",
.description = "recent log entries to keep in memory to dump in the event of a crash",
.long_description = "The purpose of this option is to log at a higher debug level only to the in-memory buffer, and write out the detailed log messages only if there is a crash. Only log entries below the lower log level will be written unconditionally to the log. For example, debug_osd=1/5 will write everything <= 1 to the log unconditionally but keep entries at levels 2-5 in memory. If there is a seg fault or assertion failure, all entries will be dumped to the log.",
},
{
.name = "log_to_stderr",
.type = OPT_BOOL,
.level = OPT_BASIC,
.description = "send log lines to stderr",
.daemon_value = "false",
.nondaemon_value = "true",
},
{
.name = "err_to_stderr",
.type = OPT_BOOL,
.level = OPT_BASIC,
.description = "send critical error log lines to stderr",
.daemon_value = "true",
.nondaemon_value = "false",
},
{
.name = "log_to_syslog",
.type = OPT_BOOL,
.level = OPT_BASIC,
.value = "false",
.description = "send log lines to syslog facility",
},
{
.name = "err_to_syslog",
.type = OPT_BOOL,
.level = OPT_BASIC,
.value = "false",
.description = "send critical error log lines to syslog facility",
},
{
.name = "log_flush_on_exit",
.type = OPT_BOOL,
.level = OPT_ADVANCED,
.description = "set a process exit handler to ensure the log is flushed on exit",
.nondaemon_value = "false",
.daemon_value = "true",
},
{
.name = "log_stop_at_utilization",
.type = OPT_FLOAT,
.level = OPT_BASIC,
.description = "stop writing to the log file when device utilization reaches this ratio",
.min_value = "0.0",
.max_value = "1.0",
.value = ".97",
.see_also = "log_file",
},
{
.name = "log_to_graylog",
.type = OPT_BOOL,
.level = OPT_BASIC,
.value = "false",
.description = "send log lines to remote graylog server",
.see_also = "err_to_graylog log_graylog_host log_graylog_port",
},
{
.name = "err_to_graylog",
.type = OPT_BOOL,
.level = OPT_BASIC,
.value = "false",
.description = "send critical error log lines to remote graylog server",
.see_also = "log_to_graylog log_graylog_host log_graylog_port",
},
{
.name = "log_graylog_host",
.type = OPT_STR,
.level = OPT_BASIC,
.value = "127.0.0.1",
.description = "address or hostname of graylog server to log to",
.see_also = "log_to_graylog err_to_graylog log_graylog_port",
},
{
.name = "log_graylog_port",
.type = OPT_INT,
.level = OPT_BASIC,
.value = "12201",
.description = "port number for the remote graylog server",
.see_also = "log_graylog_host",
},
// cluster log facility
{
// *** the last entry (name) is empty to mark the end of the list ***
}
};
struct ceph_option *ceph_options = _ceph_options;

216
src/common/options.cc Normal file
View File

@ -0,0 +1,216 @@
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
#include "acconfig.h"
#include "options.h"
Option _ceph_options[] = {
// ** global basics **
Option("host", Option::TYPE_STR, Option::LEVEL_BASIC)
.set_description("local hostname")
.set_long_description("if blank, ceph assumes the short hostname (hostname -s)")
.add_tag("common"),
Option("fsid", Option::TYPE_UUID, Option::LEVEL_BASIC)
.set_description("cluster fsid (uuid)")
.add_tag("common"),
Option("public_addr", Option::TYPE_ADDR, Option::LEVEL_BASIC)
.set_description("public-facing address to bind to")
.add_tag("mon mds osd mgr"),
Option("cluster_addr", Option::TYPE_STR, Option::LEVEL_BASIC)
.set_description("cluster-facing address to bind to")
.add_tag("osd"),
Option("monmap", Option::TYPE_STR, Option::LEVEL_ADVANCED)
.set_description("path to MonMap file")
.set_long_description("This option is normally used during mkfs, but can also "
"be used to identify which monitors to connect to.")
.add_tag("mon").add_tag("mkfs"),
Option("mon_host", Option::TYPE_STR, Option::LEVEL_BASIC)
.set_description("list of hosts or addresses to search for a monitor")
.set_long_description("This is a comma, whitespace, or semicolon separated "
"list of IP addresses or hostnames. Hostnames are "
"resolved via DNS and all A or AAAA records are "
"included in the search list.")
.add_tag("common"),
Option("mon_dns_srv_name", Option::TYPE_STR, Option::LEVEL_ADVANCED)
.set_description("name of DNS SRV record to check for monitor addresses")
.add_tag("common")
.add_see_also("mon_host"),
// lockdep
Option("lockdep", Option::TYPE_BOOL, Option::LEVEL_DEV)
.set_description("enable lockdep lock dependency analyzer")
.add_tag("common"),
Option("lockdep_force_backtrace", Option::TYPE_BOOL, Option::LEVEL_DEV)
.set_description("always gather current backtrace at every lock")
.add_tag("common")
.add_see_also("lockdep"),
Option("run_dir", Option::TYPE_STR, Option::LEVEL_ADVANCED)
.set_daemon_default("/var/run/ceph")
.set_description("path for the 'run' directory for storing pid and socket files")
.add_tag("common")
.add_see_also("admin_socket"),
Option("admin_socket", Option::TYPE_STR, Option::LEVEL_ADVANCED)
.set_daemon_default("$run_dir/$cluster-$name.asok")
.set_description("path for the runtime control socket file, used by the 'ceph daemon' command")
.add_tag("common"),
Option("admin_socket_mode", Option::TYPE_STR, Option::LEVEL_ADVANCED)
.set_description("file mode to set for the admin socket file, e.g, '0755'")
.add_tag("common")
.add_see_also("admin_socket"),
Option("crushtool", Option::TYPE_STR, Option::LEVEL_ADVANCED)
.set_description("name of the 'crushtool' utility")
.add_tag("mon"),
// daemon
Option("daemonize", Option::TYPE_BOOL, Option::LEVEL_ADVANCED)
.set_default(false)
.set_daemon_default(true)
.set_description("whether to daemonize (background) after startup")
.add_tag("daemon")
.add_see_also("pid_file").add_see_also("chdir"),
Option("setuser", Option::TYPE_STR, Option::LEVEL_ADVANCED)
.set_description("uid or user name to switch to on startup")
.set_long_description("This is normally specified by the systemd unit file.")
.add_tag("daemon")
.add_see_also("setgroup"),
Option("setgroup", Option::TYPE_STR, Option::LEVEL_ADVANCED)
.set_description("gid or group name to switch to on startup")
.set_long_description("This is normally specified by the systemd unit file.")
.add_tag("daemon")
.add_see_also("setuser"),
Option("setuser_match_path", Option::TYPE_STR, Option::LEVEL_ADVANCED)
.set_description("if set, setuser/setgroup is condition on this path matching ownership")
.set_long_description("If setuser or setgroup are specified, and this option is non-empty, then the uid/gid of the daemon will only be changed if the file or directory specified by this option has a matching uid and/or gid. This exists primarily to allow switching to user ceph for OSDs to be conditional on whether the osd data contents have also been chowned after an upgrade. This is normally specified by the systemd unit file.")
.add_tag("daemon").add_tag("osd")
.add_see_also("setuser").add_see_also("setgroup"),
Option("pid_file", Option::TYPE_STR, Option::LEVEL_ADVANCED)
.set_description("path to write a pid file (if any)")
.add_tag("daemon"),
Option("chdir", Option::TYPE_STR, Option::LEVEL_ADVANCED)
.set_description("path to chdir(2) to after daemonizing")
.add_tag("daemon")
.add_see_also("daemonize"),
Option("fatal_signal_handlers", Option::TYPE_BOOL, Option::LEVEL_ADVANCED)
.set_default(true)
.set_description("whether to register signal handlers for SIGABRT etc that dump a stack trace")
.set_long_description("This is normally true for daemons and values for libraries.")
.add_tag("daemon"),
// restapi
Option("restapi_log_level", Option::TYPE_STR, Option::LEVEL_ADVANCED)
.set_description("default set by python code"),
Option("restapi_base_url", Option::TYPE_STR, Option::LEVEL_ADVANCED)
.set_description("default set by python code"),
Option("erasure_code_dir", Option::TYPE_STR, Option::LEVEL_ADVANCED)
.set_default(CEPH_PKGLIBDIR"/erasure-code")
.set_description("directory where erasure-code plugins can be found")
.add_tag("mon").add_tag("osd"),
// logging
Option("log_file", Option::TYPE_STR, Option::LEVEL_BASIC)
.set_daemon_default("/var/log/ceph/$cluster-$name.log")
.set_description("path to log file")
.add_see_also("log_to_stderr")
.add_see_also("err_to_stderr")
.add_see_also("log_to_syslog")
.add_see_also("err_to_syslog"),
Option("log_max_new", Option::TYPE_INT, Option::LEVEL_ADVANCED)
.set_default(1000)
.set_description("max unwritten log entries to allow before waiting to flush to the log")
.add_see_also("log_max_recent"),
Option("log_max_recent", Option::TYPE_INT, Option::LEVEL_ADVANCED)
.set_daemon_default(10000)
.set_default(500)
.set_description("recent log entries to keep in memory to dump in the event of a crash")
.set_long_description("The purpose of this option is to log at a higher debug level only to the in-memory buffer, and write out the detailed log messages only if there is a crash. Only log entries below the lower log level will be written unconditionally to the log. For example, debug_osd=1/5 will write everything <= 1 to the log unconditionally but keep entries at levels 2-5 in memory. If there is a seg fault or assertion failure, all entries will be dumped to the log."),
Option("log_to_stderr", Option::TYPE_BOOL, Option::LEVEL_BASIC)
.set_daemon_default(false)
.set_default(true)
.set_description("send log lines to stderr"),
Option("err_to_stderr", Option::TYPE_BOOL, Option::LEVEL_BASIC)
.set_daemon_default(true)
.set_default(false)
.set_description("send critical error log lines to stderr"),
Option("log_to_syslog", Option::TYPE_BOOL, Option::LEVEL_BASIC)
.set_default(false)
.set_description("send log lines to syslog facility"),
Option("err_to_syslog", Option::TYPE_BOOL, Option::LEVEL_BASIC)
.set_default(false)
.set_description("send critical error log lines to syslog facility"),
Option("log_flush_on_exit", Option::TYPE_BOOL, Option::LEVEL_ADVANCED)
.set_daemon_default(true)
.set_default(false)
.set_description("set a process exit handler to ensure the log is flushed on exit"),
Option("log_stop_at_utilization", Option::TYPE_FLOAT, Option::LEVEL_BASIC)
.set_default(.97)
.set_min_max(0.0, 1.0)
.set_description("stop writing to the log file when device utilization reaches this ratio")
.add_see_also("log_file"),
Option("log_to_graylog", Option::TYPE_BOOL, Option::LEVEL_BASIC)
.set_default(false)
.set_description("send log lines to remote graylog server")
.add_see_also("err_to_graylog")
.add_see_also("log_graylog_host")
.add_see_also("log_graylog_port"),
Option("err_to_graylog", Option::TYPE_BOOL, Option::LEVEL_BASIC)
.set_default(false)
.set_description("send critical error log lines to remote graylog server")
.add_see_also("log_to_graylog")
.add_see_also("log_graylog_host")
.add_see_also("log_graylog_port"),
Option("log_graylog_host", Option::TYPE_STR, Option::LEVEL_BASIC)
.set_default("127.0.0.1")
.set_description("address or hostname of graylog server to log to")
.add_see_also("log_to_graylog")
.add_see_also("err_to_graylog")
.add_see_also("log_graylog_port"),
Option("log_graylog_port", Option::TYPE_INT, Option::LEVEL_BASIC)
.set_default(12201)
.set_description("port number for the remote graylog server")
.add_see_also("log_graylog_host"),
// ** end **
Option("", Option::TYPE_INT, Option::LEVEL_BASIC)
};
Option *ceph_options = _ceph_options;

109
src/common/options.h Normal file
View File

@ -0,0 +1,109 @@
// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
// vim: ts=8 sw=2 smarttab
#pragma once
#include <string>
#include <list>
#include <boost/variant.hpp>
//#include "include/uuid.h"
//#include "msg/msg_types.h"
struct Option {
typedef enum {
TYPE_INT,
TYPE_STR,
TYPE_FLOAT,
TYPE_BOOL,
TYPE_ADDR,
TYPE_UUID,
} type_t;
const char *type_to_str(type_t t) {
switch (t) {
case TYPE_INT: return "int64_t";
case TYPE_STR: return "std::string";
case TYPE_FLOAT: return "double";
case TYPE_BOOL: return "bool";
case TYPE_ADDR: return "entity_addr_t";
case TYPE_UUID: return "uuid_d";
default: return "unknown";
}
}
typedef enum {
LEVEL_BASIC,
LEVEL_ADVANCED,
LEVEL_DEV,
} level_t;
typedef boost::variant<
std::string,
int64_t,
double,
bool//,
//entity_addr_t,
/*uuid_d*/> value_t;
std::string name;
type_t type;
level_t level;
std::string desc;
std::string long_desc;
value_t value;
value_t daemon_value;
value_t nondaemon_value;
std::list<std::string> tags;
std::list<std::string> see_also;
value_t min, max;
std::list<std::string> enum_allowed;
Option(const char* name, type_t t, level_t l)
: name(name), type(t), level(l)
{}
template<typename T>
Option& set_default(const T& v) {
value = v;
return *this;
}
template<typename T>
Option& set_daemon_default(const T& v) {
daemon_value = v;
return *this;
}
template<typename T>
Option& set_nondaemon_default(const T& v) {
daemon_value = v;
return *this;
}
Option& add_tag(const char* t) {
tags.push_back(t);
return *this;
}
Option& add_see_also(const char* t) {
see_also.push_back(t);
return *this;
}
Option& set_description(const char* new_desc) {
desc = new_desc;
return *this;
}
Option& set_long_description(const char* new_desc) {
desc = new_desc;
return *this;
}
template<typename T>
Option& set_min_max(const T& mi, const T& ma) {
min = mi;
max = ma;
return *this;
}
};
// array of ceph options. the last one will have a blank name.
extern struct Option *ceph_options;