diff options
author | David Lamparter <equinox@opensourcerouting.org> | 2018-10-26 18:41:00 +0200 |
---|---|---|
committer | David Lamparter <equinox@diac24.net> | 2019-12-06 15:13:32 +0100 |
commit | ac4adef44127e5e6659abd735827ce699c469718 (patch) | |
tree | 90b11e8366ef14802186f756636232c4e3774305 /lib | |
parent | lib: add frr_version_cmp() (diff) | |
download | frr-ac4adef44127e5e6659abd735827ce699c469718.tar.xz frr-ac4adef44127e5e6659abd735827ce699c469718.zip |
lib: new defaults logic
Since we've been writing out "frr version" and "frr defaults" for about
a year and a half now, we can now actually use them to manage defaults.
Signed-off-by: David Lamparter <equinox@diac24.net>
Diffstat (limited to 'lib')
-rw-r--r-- | lib/command.c | 17 | ||||
-rw-r--r-- | lib/defaults.c | 152 | ||||
-rw-r--r-- | lib/defaults.h | 136 | ||||
-rw-r--r-- | lib/lib_vty.c | 53 | ||||
-rw-r--r-- | lib/libfrr.c | 32 | ||||
-rw-r--r-- | lib/northbound_cli.c | 3 |
6 files changed, 376 insertions, 17 deletions
diff --git a/lib/command.c b/lib/command.c index 9238ae412..d2145d9f5 100644 --- a/lib/command.c +++ b/lib/command.c @@ -1575,18 +1575,6 @@ DEFUN (show_version, return CMD_SUCCESS; } -/* "Set" version ... ignore version tags */ -DEFUN (frr_version_defaults, - frr_version_defaults_cmd, - "frr <version|defaults> LINE...", - "FRRouting global parameters\n" - "version configuration was written by\n" - "set of configuration defaults used\n" - "version string\n") -{ - return CMD_SUCCESS; -} - /* Help display function for all node. */ DEFUN (config_help, config_help_cmd, @@ -1721,8 +1709,10 @@ static int vty_write_config(struct vty *vty) vty_out(vty, "!\n"); } + if (strcmp(frr_defaults_version(), FRR_VER_SHORT)) + vty_out(vty, "! loaded from %s\n", frr_defaults_version()); vty_out(vty, "frr version %s\n", FRR_VER_SHORT); - vty_out(vty, "frr defaults %s\n", DFLT_NAME); + vty_out(vty, "frr defaults %s\n", frr_defaults_profile()); vty_out(vty, "!\n"); for (i = 0; i < vector_active(cmdvec); i++) @@ -2941,7 +2931,6 @@ void cmd_init(int terminal) install_element(CONFIG_NODE, &no_hostname_cmd); install_element(CONFIG_NODE, &domainname_cmd); install_element(CONFIG_NODE, &no_domainname_cmd); - install_element(CONFIG_NODE, &frr_version_defaults_cmd); if (terminal > 0) { install_element(CONFIG_NODE, &debug_memstats_cmd); diff --git a/lib/defaults.c b/lib/defaults.c index cfc8fa27f..8a1cfebe1 100644 --- a/lib/defaults.c +++ b/lib/defaults.c @@ -18,9 +18,20 @@ #include <zebra.h> #include "defaults.h" -#include "libfrr.h" #include "version.h" +static char df_version[128] = FRR_VER_SHORT, df_profile[128] = DFLT_NAME; +static struct frr_default *dflt_first = NULL, **dflt_next = &dflt_first; + +/* these are global for all FRR daemons. they have to be, since we write an + * integrated config with the same value for all daemons. + */ +const char *frr_defaults_profiles[] = { + "traditional", + "datacenter", + NULL, +}; + static int version_value(int ch) { /* non-ASCII shouldn't happen */ @@ -73,3 +84,142 @@ int frr_version_cmp(const char *aa, const char *bb) } return 0; } + +static void frr_default_apply_one(struct frr_default *dflt, bool check); + +void frr_default_add(struct frr_default *dflt) +{ + dflt->next = NULL; + *dflt_next = dflt; + dflt_next = &dflt->next; + + frr_default_apply_one(dflt, true); +} + +static bool frr_match_version(const char *name, const char *vspec, + const char *version, bool check) +{ + int cmp; + static struct spec { + const char *str; + bool dir, eq; + } *s, specs[] = { + {"<=", -1, 1}, + {">=", 1, 1}, + {"==", 0, 1}, + {"<", -1, 0}, + {">", 1, 0}, + {"=", 0, 1}, + {NULL, 0, 0}, + }; + + if (!vspec) + /* NULL = all versions */ + return true; + + for (s = specs; s->str; s++) + if (!strncmp(s->str, vspec, strlen(s->str))) + break; + if (!s->str) { + if (check) + fprintf(stderr, "invalid version specifier for %s: %s", + name, vspec); + /* invalid version spec, never matches */ + return false; + } + + vspec += strlen(s->str); + while (isspace((unsigned char)*vspec)) + vspec++; + + cmp = frr_version_cmp(version, vspec); + if (cmp == s->dir || (s->eq && cmp == 0)) + return true; + + return false; +} + +static void frr_default_apply_one(struct frr_default *dflt, bool check) +{ + struct frr_default_entry *entry = dflt->entries; + struct frr_default_entry *dfltentry = NULL, *saveentry = NULL; + + for (; entry->match_version || entry->match_profile; entry++) { + if (entry->match_profile + && strcmp(entry->match_profile, df_profile)) + continue; + + if (!dfltentry && frr_match_version(dflt->name, + entry->match_version, df_version, check)) + dfltentry = entry; + if (!saveentry && frr_match_version(dflt->name, + entry->match_version, FRR_VER_SHORT, check)) + saveentry = entry; + + if (dfltentry && saveentry && !check) + break; + } + /* found default or arrived at last entry that has NULL,NULL spec */ + + if (!dfltentry) + dfltentry = entry; + if (!saveentry) + saveentry = entry; + + if (dflt->dflt_str) + *dflt->dflt_str = dfltentry->val_str; + if (dflt->dflt_long) + *dflt->dflt_long = dfltentry->val_long; + if (dflt->dflt_ulong) + *dflt->dflt_ulong = dfltentry->val_ulong; + if (dflt->dflt_float) + *dflt->dflt_float = dfltentry->val_float; + if (dflt->save_str) + *dflt->save_str = saveentry->val_str; + if (dflt->save_long) + *dflt->save_long = saveentry->val_long; + if (dflt->save_ulong) + *dflt->save_ulong = saveentry->val_ulong; + if (dflt->save_float) + *dflt->save_float = saveentry->val_float; +} + +void frr_defaults_apply(void) +{ + struct frr_default *dflt; + + for (dflt = dflt_first; dflt; dflt = dflt->next) + frr_default_apply_one(dflt, false); +} + +bool frr_defaults_profile_valid(const char *profile) +{ + const char **p; + + for (p = frr_defaults_profiles; *p; p++) + if (!strcmp(profile, *p)) + return true; + return false; +} + +const char *frr_defaults_version(void) +{ + return df_version; +} + +const char *frr_defaults_profile(void) +{ + return df_profile; +} + +void frr_defaults_version_set(const char *version) +{ + strlcpy(df_version, version, sizeof(df_version)); + frr_defaults_apply(); +} + +void frr_defaults_profile_set(const char *profile) +{ + strlcpy(df_profile, profile, sizeof(df_profile)); + frr_defaults_apply(); +} diff --git a/lib/defaults.h b/lib/defaults.h index c21534150..ad2f1ad2e 100644 --- a/lib/defaults.h +++ b/lib/defaults.h @@ -18,6 +18,142 @@ #ifndef _FRR_DEFAULTS_H #define _FRR_DEFAULTS_H +#include "config.h" +#include "compiler.h" + +#ifdef HAVE_DATACENTER + +#define DFLT_BGP_IMPORT_CHECK 1 +#define DFLT_BGP_TIMERS_CONNECT 10 +#define DFLT_BGP_HOLDTIME 9 +#define DFLT_BGP_KEEPALIVE 3 +#define DFLT_BGP_LOG_NEIGHBOR_CHANGES 1 +#define DFLT_BGP_SHOW_HOSTNAME 1 +#define DFLT_BGP_DETERMINISTIC_MED 1 + +#define DFLT_OSPF_LOG_ADJACENCY_CHANGES 1 +#define DFLT_OSPF6_LOG_ADJACENCY_CHANGES 1 + +#else /* !HAVE_DATACENTER */ + +#define DFLT_BGP_IMPORT_CHECK 0 +#define DFLT_BGP_TIMERS_CONNECT 120 +#define DFLT_BGP_HOLDTIME 180 +#define DFLT_BGP_KEEPALIVE 60 +#define DFLT_BGP_LOG_NEIGHBOR_CHANGES 0 +#define DFLT_BGP_SHOW_HOSTNAME 0 +#define DFLT_BGP_DETERMINISTIC_MED 0 + +#define DFLT_OSPF_LOG_ADJACENCY_CHANGES 0 +#define DFLT_OSPF6_LOG_ADJACENCY_CHANGES 0 + +#endif /* !HAVE_DATACENTER */ + +/* frr_default wraps information about a default that has different + * values depending on FRR version or default-set + * + * frr_default_entry describes one match rule and the resulting value; + * entries are evaluated in order and the first matching is used. + * + * If both match_version and match_profile are specified, they must both + * match. A NULL value matches everything. + */ +struct frr_default_entry { + /* syntax: "(<|<=|==|>=|>) [whitespace] version", e.g. + * ">= 6.1-dev" "<6.0" + */ + const char *match_version; + /* exact profile string to compare against */ + const char *match_profile; + + /* value to use */ + const char *val_str; + long val_long; + unsigned long val_ulong; + float val_float; +}; + +/* one struct frr_default exists for each malleable default value */ +struct frr_default { + struct frr_default *next; + + /* for UI/debug use */ + const char *name; + + /* the following two sets of variables differ because the written + * config always targets the *current* FRR version + * + * e.g. if you load a config that has "frr version 5.0" on 6.0 + * *dflt_long => set to the default value in 5.0 + * *save_long => set to the default value in 6.0 + * config save will write "frr version 6.0" with 6.0 defaults + */ + + /* variable holding the default value for reading/use */ + const char **dflt_str; + long *dflt_long; + unsigned long *dflt_ulong; + float *dflt_float; + + /* variable to use when comparing for config save */ + const char **save_str; + long *save_long; + unsigned long *save_ulong; + float *save_float; + + struct frr_default_entry entries[]; +}; + +#define _FRR_CFG_DEFAULT(type, typname, varname, ...) \ + static type DFLT_##varname; \ + static type SAVE_##varname; \ + static struct frr_default _dflt_##varname = { \ + .name = #varname, \ + .dflt_##typname = &DFLT_##varname, \ + .save_##typname = &SAVE_##varname, \ + .entries = { __VA_ARGS__ }, \ + }; \ + static void _dfltinit_##varname(void) \ + __attribute__((_CONSTRUCTOR(1000))); \ + static void _dfltinit_##varname(void) \ + { \ + frr_default_add(&_dflt_##varname); \ + } + +/* use: + * FRR_CFG_DEFAULT_LONG(SHARP_BLUNTNESS, + * { .val_long = 2, .match_version = ">= 10.0" }, + * { .val_long = 1, .match_profile = "datacenter" }, + * { .val_long = 0 }, + * ) + * + * This will create DFLT_SHARP_BLUNTNESS and SAVE_SHARP_BLUNTNESS variables. + * + * Note: preprocessor defines cannot be used as variable names because they + * will be expanded and blow up with a compile error. Use an enum or add an + * extra _ at the beginning (e.g. _SHARP_BLUNTNESS => DFLT__SHARP_BLUNTNESS) + */ +#define FRR_CFG_DEFAULT_LONG(varname, ...) \ + _FRR_CFG_DEFAULT(long, long, varname, ## __VA_ARGS__) +#define FRR_CFG_DEFAULT_ULONG(varname, ...) \ + _FRR_CFG_DEFAULT(unsigned long, ulong, varname, ## __VA_ARGS__) +#define FRR_CFG_DEFAULT_FLOAT(varname, ...) \ + _FRR_CFG_DEFAULT(float, float, varname, ## __VA_ARGS__) +#define FRR_CFG_DEFAULT_STR(varname, ...) \ + _FRR_CFG_DEFAULT(const char *, str, varname, ## __VA_ARGS__) + + +/* daemons don't need to call any of these, libfrr handles that */ +extern void frr_default_add(struct frr_default *dflt); +extern void frr_defaults_version_set(const char *version); +extern void frr_defaults_profile_set(const char *profile); +extern const char *frr_defaults_version(void); +extern const char *frr_defaults_profile(void); +extern void frr_defaults_apply(void); + +extern const char *frr_defaults_profiles[]; +extern bool frr_defaults_profile_valid(const char *profile); + /* like strcmp(), but with version ordering */ extern int frr_version_cmp(const char *aa, const char *bb); diff --git a/lib/lib_vty.c b/lib/lib_vty.c index 932b46501..787da08e2 100644 --- a/lib/lib_vty.c +++ b/lib/lib_vty.c @@ -35,6 +35,7 @@ #include "log.h" #include "memory.h" #include "module.h" +#include "defaults.h" #include "lib_vty.h" /* Looking up memory status from vty interface. */ @@ -177,8 +178,60 @@ DEFUN_NOSH (show_modules, return CMD_SUCCESS; } +DEFUN (frr_defaults, + frr_defaults_cmd, + "frr defaults PROFILE...", + "FRRouting global parameters\n" + "set of configuration defaults used\n" + "profile string\n") +{ + char *profile = argv_concat(argv, argc, 2); + int rv = CMD_SUCCESS; + + if (!frr_defaults_profile_valid(profile)) { + vty_out(vty, "%% WARNING: profile %s is not known in this version\n", + profile); + rv = CMD_WARNING; + } + frr_defaults_profile_set(profile); + XFREE(MTYPE_TMP, profile); + return rv; +} + +DEFUN (frr_version, + frr_version_cmd, + "frr version VERSION...", + "FRRouting global parameters\n" + "version configuration was written by\n" + "version string\n") +{ + char *version = argv_concat(argv, argc, 2); + + frr_defaults_version_set(version); + XFREE(MTYPE_TMP, version); + return CMD_SUCCESS; +} + +static void defaults_autocomplete(vector comps, struct cmd_token *token) +{ + const char **p; + + for (p = frr_defaults_profiles; *p; p++) + vector_set(comps, XSTRDUP(MTYPE_COMPLETION, *p)); +} + +static const struct cmd_variable_handler default_var_handlers[] = { + {.tokenname = "PROFILE", .completions = defaults_autocomplete}, + {.completions = NULL}, +}; + void lib_cmd_init(void) { + cmd_variable_handler_register(default_var_handlers); + + install_element(CONFIG_NODE, &frr_defaults_cmd); + install_element(CONFIG_NODE, &frr_version_cmd); + install_element(VIEW_NODE, &show_memory_cmd); install_element(VIEW_NODE, &show_modules_cmd); } diff --git a/lib/libfrr.c b/lib/libfrr.c index 1dd7e932c..4fb43edff 100644 --- a/lib/libfrr.c +++ b/lib/libfrr.c @@ -43,6 +43,7 @@ #include "debug.h" #include "frrcu.h" #include "frr_pthread.h" +#include "defaults.h" DEFINE_HOOK(frr_late_init, (struct thread_master * tm), (tm)) DEFINE_KOOH(frr_early_fini, (), ()) @@ -104,6 +105,7 @@ static const struct option lo_always[] = { {"version", no_argument, NULL, 'v'}, {"daemon", no_argument, NULL, 'd'}, {"module", no_argument, NULL, 'M'}, + {"profile", required_argument, NULL, 'F'}, {"vty_socket", required_argument, NULL, OPTION_VTYSOCK}, {"moduledir", required_argument, NULL, OPTION_MODULEDIR}, {"log", required_argument, NULL, OPTION_LOG}, @@ -112,11 +114,12 @@ static const struct option lo_always[] = { {"command-log-always", no_argument, NULL, OPTION_LOGGING}, {NULL}}; static const struct optspec os_always = { - "hvdM:", + "hvdM:F:", " -h, --help Display this help and exit\n" " -v, --version Print program version\n" " -d, --daemon Runs in daemon mode\n" " -M, --module Load specified module\n" + " -F, --profile Use specified configuration profile\n" " --vty_socket Override vty socket path\n" " --moduledir Override modules directory\n" " --log Set Logging to stdout, syslog, or file:<name>\n" @@ -389,6 +392,32 @@ static int frr_opt(int opt) *modnext = oc; modnext = &oc->next; break; + case 'F': + if (!frr_defaults_profile_valid(optarg)) { + const char **p; + FILE *ofd = stderr; + + if (!strcmp(optarg, "help")) + ofd = stdout; + else + fprintf(stderr, + "The \"%s\" configuration profile is not valid for this FRR version.\n", + optarg); + + fprintf(ofd, "Available profiles are:\n"); + for (p = frr_defaults_profiles; *p; p++) + fprintf(ofd, "%s%s\n", + strcmp(*p, DFLT_NAME) ? " " : " * ", + *p); + + if (ofd == stdout) + exit(0); + fprintf(ofd, "\n"); + errors++; + break; + } + frr_defaults_profile_set(optarg); + break; case 'i': if (di->flags & FRR_NO_CFG_PID_DRY) return 1; @@ -607,6 +636,7 @@ struct thread_master *frr_init(void) dir = di->module_path ? di->module_path : frr_moduledir; srandom(time(NULL)); + frr_defaults_apply(); if (di->instance) { snprintf(frr_protonameinst, sizeof(frr_protonameinst), "%s[%u]", diff --git a/lib/northbound_cli.c b/lib/northbound_cli.c index 009e5bd82..17dc25628 100644 --- a/lib/northbound_cli.c +++ b/lib/northbound_cli.c @@ -21,6 +21,7 @@ #include "libfrr.h" #include "version.h" +#include "defaults.h" #include "log.h" #include "lib_errors.h" #include "command.h" @@ -486,7 +487,7 @@ static void nb_cli_show_config_cmds(struct vty *vty, struct nb_config *config, vty_out(vty, "Configuration:\n"); vty_out(vty, "!\n"); vty_out(vty, "frr version %s\n", FRR_VER_SHORT); - vty_out(vty, "frr defaults %s\n", DFLT_NAME); + vty_out(vty, "frr defaults %s\n", frr_defaults_profile()); LY_TREE_FOR (config->dnode, root) nb_cli_show_dnode_cmds(vty, root, with_defaults); |