On 1/9/24 23:49, Ilya Maximets wrote:
> Add a new structure 'db_config' that holds the user-provided
> configuration of the database.  And attach this configuration
> to each of the databases on the server.
> 
> Each database has a service model: standalone, clustered, relay
> or active-backup.  Relays and A-B databases have a source, each
> source has its own set of JSON-RPC session options.  A-B also
> have an indicator of it being active or backup and an optional
> list of tables to exclude from replication.
> 
> All of that should be stored per database in the temporary
> configuration file that is used in order to restore the config
> after the OVSDB crash.  For that, the save/load functions are
> also updates.
> 
> This change is written in generic way assuming all the databases
> can have different configuration including service model.
> The only user-visible change here is a slight modification of
> the ovsdb-server/sync-status appctl, since it now needs to
> skip databases that are not active-backup and also should report
> active-backup databases that are currently active, i.e. not
> added to the replication module.
> 
> If the service model is not defined in the configuration, it
> is assumed to be standalone or clustered, and determined from
> the storage type while opening the database.  If the service
> model is defined, but doesn't match the actual storage type
> in the database file, ovsdb-server will fail to open the
> database.  This should never happen with internally generated
> config file, but may happen in the future with user-provided
> configuration files.  In this case the service model is used
> for verification purposes only, if administrator wants to
> assert a particular model.
> 
> Since the database 'source' connections can't use 'role' or
> 'read-only' options, a new flag added to corresponding JSON
> parsing functions to skip these fields.
> 
> Signed-off-by: Ilya Maximets <[email protected]>
> ---
>  ovsdb/jsonrpc-server.c |  31 +-
>  ovsdb/jsonrpc-server.h |   5 +-
>  ovsdb/ovsdb-server.c   | 775 +++++++++++++++++++++++++++++++----------
>  ovsdb/replication.c    |   1 -
>  tests/ovsdb-server.at  |   8 +-
>  5 files changed, 623 insertions(+), 197 deletions(-)
> 
> diff --git a/ovsdb/jsonrpc-server.c b/ovsdb/jsonrpc-server.c
> index 299afbb1d..81e9e48a0 100644
> --- a/ovsdb/jsonrpc-server.c
> +++ b/ovsdb/jsonrpc-server.c
> @@ -231,7 +231,8 @@ ovsdb_jsonrpc_options_clone(const struct 
> ovsdb_jsonrpc_options *options)
>  }
>  
>  struct json *
> -ovsdb_jsonrpc_options_to_json(const struct ovsdb_jsonrpc_options *options)
> +ovsdb_jsonrpc_options_to_json(const struct ovsdb_jsonrpc_options *options,
> +                              bool jsonrpc_session_only)
>  {
>      struct json *json = json_object_create();
>  
> @@ -239,9 +240,15 @@ ovsdb_jsonrpc_options_to_json(const struct 
> ovsdb_jsonrpc_options *options)
>                      json_integer_create(options->max_backoff));
>      json_object_put(json, "inactivity-probe",
>                      json_integer_create(options->probe_interval));
> +    json_object_put(json, "dscp", json_integer_create(options->dscp));
> +
> +    if (jsonrpc_session_only) {
> +        /* Caller is not interested in OVSDB-specific options. */
> +        return json;
> +    }
> +
>      json_object_put(json, "read-only",
>                      json_boolean_create(options->read_only));
> -    json_object_put(json, "dscp", json_integer_create(options->dscp));
>      if (options->role) {
>          json_object_put(json, "role", json_string_create(options->role));
>      }
> @@ -251,7 +258,8 @@ ovsdb_jsonrpc_options_to_json(const struct 
> ovsdb_jsonrpc_options *options)
>  
>  void
>  ovsdb_jsonrpc_options_update_from_json(struct ovsdb_jsonrpc_options *options,
> -                                       const struct json *json)
> +                                       const struct json *json,
> +                                       bool jsonrpc_session_only)
>  {
>      const struct json *max_backoff, *probe_interval, *read_only, *dscp, 
> *role;
>      struct ovsdb_parser parser;
> @@ -271,22 +279,29 @@ ovsdb_jsonrpc_options_update_from_json(struct 
> ovsdb_jsonrpc_options *options,
>          options->probe_interval = json_integer(probe_interval);
>      }
>  
> +    dscp = ovsdb_parser_member(&parser, "dscp", OP_INTEGER | OP_OPTIONAL);
> +    if (dscp) {
> +        options->dscp = json_integer(dscp);
> +    }
> +
> +    if (jsonrpc_session_only) {
> +        /* Caller is not interested to OVSDB-specific options. */
> +        goto exit;
> +    }
> +
>      read_only = ovsdb_parser_member(&parser, "read-only",
>                                      OP_BOOLEAN | OP_OPTIONAL);
>      if (read_only) {
>          options->read_only = json_boolean(read_only);
>      }
>  
> -    dscp = ovsdb_parser_member(&parser, "dscp", OP_INTEGER | OP_OPTIONAL);
> -    if (dscp) {
> -        options->dscp = json_integer(dscp);
> -    }
> -
>      role = ovsdb_parser_member(&parser, "role", OP_STRING | OP_OPTIONAL);
>      if (role) {
> +        free(options->role);
>          options->role = nullable_xstrdup(json_string(role));
>      }
>  
> +exit:
>      error = ovsdb_parser_finish(&parser);
>      if (error) {
>          char *s = ovsdb_error_to_string_free(error);
> diff --git a/ovsdb/jsonrpc-server.h b/ovsdb/jsonrpc-server.h
> index 39366ad70..0d8f87da7 100644
> --- a/ovsdb/jsonrpc-server.h
> +++ b/ovsdb/jsonrpc-server.h
> @@ -45,10 +45,11 @@ struct ovsdb_jsonrpc_options *ovsdb_jsonrpc_options_clone(
>      const struct ovsdb_jsonrpc_options *);
>  
>  struct json *ovsdb_jsonrpc_options_to_json(
> -                                const struct ovsdb_jsonrpc_options *)
> +    const struct ovsdb_jsonrpc_options *, bool jsonrpc_session_only)
>      OVS_WARN_UNUSED_RESULT;
>  void ovsdb_jsonrpc_options_update_from_json(struct ovsdb_jsonrpc_options *,
> -                                            const struct json *);
> +                                            const struct json *,
> +                                            bool jsonrpc_session_only);
>  
>  void ovsdb_jsonrpc_server_set_remotes(struct ovsdb_jsonrpc_server *,
>                                        const struct shash *);
> diff --git a/ovsdb/ovsdb-server.c b/ovsdb/ovsdb-server.c
> index 9a3b0add1..d7a823220 100644
> --- a/ovsdb/ovsdb-server.c
> +++ b/ovsdb/ovsdb-server.c
> @@ -42,6 +42,7 @@
>  #include "ovsdb-data.h"
>  #include "ovsdb-types.h"
>  #include "ovsdb-error.h"
> +#include "ovsdb-parser.h"
>  #include "openvswitch/poll-loop.h"
>  #include "process.h"
>  #include "replication.h"
> @@ -65,12 +66,6 @@
>  
>  VLOG_DEFINE_THIS_MODULE(ovsdb_server);
>  
> -struct db {
> -    char *filename;
> -    struct ovsdb *db;
> -    struct uuid row_uuid;
> -};
> -
>  /* SSL configuration. */
>  static char *private_key_file;
>  static char *certificate_file;
> @@ -100,16 +95,79 @@ static unixctl_cb_func 
> ovsdb_server_get_sync_exclude_tables;
>  static unixctl_cb_func ovsdb_server_get_sync_status;
>  static unixctl_cb_func ovsdb_server_get_db_storage_status;
>  
> +#define SERVICE_MODELS \
> +    SERVICE_MODEL(UNDEFINED,      undefined)         \
> +    SERVICE_MODEL(STANDALONE,     standalone)        \
> +    SERVICE_MODEL(CLUSTERED,      clustered)         \
> +    SERVICE_MODEL(ACTIVE_BACKUP,  active-backup)     \
> +    SERVICE_MODEL(RELAY,          relay)
> +
> +enum service_model {
> +#define SERVICE_MODEL(ENUM, NAME) SM_##ENUM,
> +    SERVICE_MODELS
> +#undef SERVICE_MODEL
> +};
> +
> +static const char *
> +service_model_to_string(enum service_model model)
> +{
> +    switch (model) {
> +#define SERVICE_MODEL(ENUM, NAME) \
> +    case SM_##ENUM: return #NAME;
> +    SERVICE_MODELS
> +#undef SERVICE_MODEL
> +    default: OVS_NOT_REACHED();
> +    }
> +}
> +
> +static enum service_model
> +service_model_from_string(const char *model)
> +{
> +#define SERVICE_MODEL(ENUM, NAME) \
> +    if (!strcmp(model, #NAME)) {  \
> +        return SM_##ENUM;         \
> +    }
> +    SERVICE_MODELS
> +#undef SERVICE_MODEL
> +
> +    VLOG_WARN("Unrecognized database service model: '%s'", model);
> +
> +    return SM_UNDEFINED;
> +}
> +
> +struct db_config {
> +    enum service_model model;
> +    char *source;  /* sync-from for backup or relay source. */
> +    struct ovsdb_jsonrpc_options *options;  /* For 'source' connection. */
> +
> +    /* Configuration specific to SM_ACTIVE_BACKUP. */
> +    struct {
> +        char *sync_exclude;  /* Tables to exclude. */
> +        bool backup;  /* If true, the database is read-only and receives
> +                       * updates from the 'source'. */
> +    } ab;

Nit: I mean, I get what 'ab' stands for, but I still find it an extra
step I need to do when reading the code.  I would've just defined the
fields in the outer structure I think.  But it's up to you. :)

> +};
> +
> +struct db {
> +    struct ovsdb *db;
> +    char *filename;
> +    struct db_config *config;
> +    struct uuid row_uuid;
> +};
> +
>  struct server_config {
>      struct shash *remotes;
> -    struct shash *all_dbs;
> -    FILE *config_tmpfile;
> +    struct shash *all_dbs;     /* All the currently serviced databases.
> +                                * 'struct db' by a schema name. */
> +    struct ovsdb_jsonrpc_server *jsonrpc;
> +
> +    /* Command line + appctl configuration. */
>      char **sync_from;
>      char **sync_exclude;
>      bool *is_backup;
>      int *replication_probe_interval;
>      int *relay_source_probe_interval;
> -    struct ovsdb_jsonrpc_server *jsonrpc;
> +    FILE *config_tmpfile;
>  };
>  static unixctl_cb_func ovsdb_server_add_remote;
>  static unixctl_cb_func ovsdb_server_remove_remote;
> @@ -123,14 +181,15 @@ static unixctl_cb_func ovsdb_server_tlog_list;
>  
>  static void read_db(struct server_config *, struct db *);
>  static struct ovsdb_error *open_db(struct server_config *,
> -                                   const char *filename)
> +                                   const char *filename,
> +                                   const struct db_config *)
>      OVS_WARN_UNUSED_RESULT;
>  static void add_server_db(struct server_config *);
>  static void remove_db(struct server_config *, struct shash_node *db, char *);
>  static void close_db(struct server_config *, struct db *, char *);
>  
>  static void parse_options(int argc, char *argvp[],
> -                          struct sset *db_filenames, struct shash *remotes,
> +                          struct shash *db_conf, struct shash *remotes,
>                            char **unixctl_pathp, char **run_command,
>                            char **sync_from, char **sync_exclude,
>                            bool *is_backup);
> @@ -153,29 +212,14 @@ static void update_remote_status(const struct 
> ovsdb_jsonrpc_server *jsonrpc,
>  static void update_server_status(struct shash *all_dbs);
>  
>  static void save_config__(FILE *config_file, const struct shash *remotes,
> -                          const struct sset *db_filenames,
> +                          const struct shash *db_conf,
>                            const char *sync_from, const char *sync_exclude,
>                            bool is_backup);
>  static void save_config(struct server_config *);
>  static void load_config(FILE *config_file, struct shash *remotes,
> -                        struct sset *db_filenames, char **sync_from,
> +                        struct shash *db_conf, char **sync_from,
>                          char **sync_exclude, bool *is_backup);
>  
> -static void
> -ovsdb_replication_init(const char *sync_from, const char *exclude,
> -                       struct shash *all_dbs, const struct uuid *server_uuid,
> -                       int probe_interval)
> -{
> -    struct shash_node *node;
> -    SHASH_FOR_EACH (node, all_dbs) {
> -        struct db *db = node->data;
> -        if (node->name[0] != '_' && db->db) {
> -            replication_set_db(db->db, sync_from, exclude,
> -                               server_uuid, probe_interval);
> -        }
> -    }
> -}
> -
>  static void
>  log_and_free_error(struct ovsdb_error *error)
>  {
> @@ -186,11 +230,52 @@ log_and_free_error(struct ovsdb_error *error)
>      }
>  }
>  
> +static void
> +ovsdb_server_replication_remove_db(struct db *db)
> +{
> +    replication_remove_db(db->db);
> +    db->config->ab.backup = false;
> +}
> +
> +static void
> +ovsdb_server_replication_run(struct server_config *config)
> +{
> +    struct shash_node *node;
> +    bool all_alive = true;
> +
> +    replication_run();
> +
> +    SHASH_FOR_EACH (node, config->all_dbs) {
> +        struct db *db = node->data;
> +
> +        if (db->config->model == SM_ACTIVE_BACKUP && db->config->ab.backup
> +            && !replication_is_alive(db->db)) {
> +            ovsdb_server_replication_remove_db(db);
> +            all_alive = false;
> +        }
> +    }
> +
> +    /* If one connection is broken, switch all databases to active,
> +     * since they are configured via the same command line / appctl. */
> +    if (!all_alive && *config->is_backup) {
> +        *config->is_backup = false;
> +
> +        SHASH_FOR_EACH (node, config->all_dbs) {
> +            struct db *db = node->data;
> +
> +            if (db->config->model == SM_ACTIVE_BACKUP
> +                && db->config->ab.backup) {
> +                ovsdb_server_replication_remove_db(db);
> +            }
> +        }
> +    }
> +}
> +
>  static void
>  main_loop(struct server_config *config,
>            struct ovsdb_jsonrpc_server *jsonrpc, struct shash *all_dbs,
>            struct unixctl_server *unixctl, struct shash *remotes,
> -          struct process *run_process, bool *exiting, bool *is_backup)
> +          struct process *run_process, bool *exiting)
>  {
>      char *remotes_error, *ssl_error;
>      struct shash_node *node;
> @@ -220,7 +305,7 @@ main_loop(struct server_config *config,
>           * the set of remotes that reconfigure_remotes() uses. */
>          unixctl_server_run(unixctl);
>  
> -        ovsdb_jsonrpc_server_set_read_only(jsonrpc, *is_backup);
> +        ovsdb_jsonrpc_server_set_read_only(jsonrpc, false);
>  
>          report_error_if_changed(
>              reconfigure_remotes(jsonrpc, all_dbs, remotes),
> @@ -228,23 +313,7 @@ main_loop(struct server_config *config,
>          report_error_if_changed(reconfigure_ssl(all_dbs), &ssl_error);
>          ovsdb_jsonrpc_server_run(jsonrpc);
>  
> -        replication_run();
> -        if (*is_backup) {
> -            SHASH_FOR_EACH (node, all_dbs) {
> -                struct db *db = node->data;
> -                if (db->db->name[0] != '_' && !replication_is_alive(db->db)) 
> {
> -                    *is_backup = false;
> -                    break;
> -                }
> -            }
> -            if (!*is_backup) {
> -                SHASH_FOR_EACH (node, all_dbs) {
> -                    struct db *db = node->data;
> -                    replication_remove_db(db->db);
> -                }
> -            }
> -        }
> -
> +        ovsdb_server_replication_run(config);
>          ovsdb_relay_run();
>  
>          SHASH_FOR_EACH_SAFE (node, all_dbs) {
> @@ -351,6 +420,92 @@ parse_relay_args(const char *arg, char **name, char 
> **remote)
>      return true;
>  }
>  
> +static void
> +db_config_destroy(struct db_config *conf)
> +{
> +    if (!conf) {
> +        return;
> +    }
> +
> +    free(conf->source);
> +    if (conf->options) {
> +        free(conf->options->role);
> +        free(conf->options);
> +    }

I think I would've added a ovsdb_jsonrpc_options_free(struct
ovsdb_jsonrpc_options *); function.

> +    free(conf->ab.sync_exclude);
> +    free(conf);
> +}
> +
> +static struct db_config *
> +db_config_clone(const struct db_config *c)
> +{
> +    struct db_config *conf = xmemdup(c, sizeof *c);
> +
> +    conf->source = nullable_xstrdup(c->source);
> +    if (c->options) {
> +        conf->options = ovsdb_jsonrpc_options_clone(c->options);
> +    }
> +    conf->ab.sync_exclude = nullable_xstrdup(c->ab.sync_exclude);
> +
> +    return conf;
> +}
> +
> +static struct ovsdb_jsonrpc_options *
> +get_jsonrpc_options(const char *target, enum service_model model)
> +{
> +    struct ovsdb_jsonrpc_options *options;
> +
> +    options = ovsdb_jsonrpc_default_options(target);
> +    if (model == SM_ACTIVE_BACKUP) {
> +        options->probe_interval = REPLICATION_DEFAULT_PROBE_INTERVAL;
> +    } else if (model == SM_RELAY) {
> +        options->probe_interval = RELAY_SOURCE_DEFAULT_PROBE_INTERVAL;
> +    }
> +
> +    return options;
> +}
> +
> +static void
> +add_database_config(struct shash *db_conf, const char *opt,
> +                    const char *sync_from, const char *sync_exclude,
> +                    bool active)
> +{
> +    struct db_config *conf = xzalloc(sizeof *conf);
> +    char *filename = NULL;
> +
> +    if (parse_relay_args(opt, &filename, &conf->source)) {
> +        conf->model = SM_RELAY;
> +        conf->options = get_jsonrpc_options(conf->source, conf->model);
> +    } else if (sync_from) {
> +        conf->model = SM_ACTIVE_BACKUP;
> +        conf->source = xstrdup(sync_from);
> +        conf->options = get_jsonrpc_options(conf->source, conf->model);
> +        conf->ab.sync_exclude = nullable_xstrdup(sync_exclude);
> +        conf->ab.backup = !active;
> +        filename = xstrdup(opt);
> +    } else {
> +        conf->model = SM_UNDEFINED; /* We'll update once the file is open. */
> +        filename = xstrdup(opt);
> +    }
> +
> +    conf = shash_replace_nocopy(db_conf, filename, conf);
> +    if (conf) {
> +        VLOG_WARN("Duplicate database configuration: %s", filename);
> +        db_config_destroy(conf);
> +    }
> +}
> +
> +static void
> +free_database_configs(struct shash *db_conf)
> +{
> +    struct shash_node *node;
> +
> +    SHASH_FOR_EACH (node, db_conf) {
> +        db_config_destroy(node->data);
> +    }
> +    shash_clear(db_conf);
> +}
> +
>  int
>  main(int argc, char *argv[])
>  {
> @@ -358,11 +513,10 @@ main(int argc, char *argv[])
>      char *run_command = NULL;
>      struct unixctl_server *unixctl;
>      struct ovsdb_jsonrpc_server *jsonrpc;
> -    struct sset db_filenames;
> +    struct shash db_conf;
>      struct shash remotes;
>      char *sync_from, *sync_exclude;
>      bool is_backup;
> -    const char *db_filename;
>      struct process *run_process;
>      bool exiting;
>      int retval;
> @@ -381,7 +535,7 @@ main(int argc, char *argv[])
>      dns_resolve_init(true);
>  
>      bool active = false;
> -    parse_options(argc, argv, &db_filenames, &remotes, &unixctl_path,
> +    parse_options(argc, argv, &db_conf, &remotes, &unixctl_path,
>                    &run_command, &sync_from, &sync_exclude, &active);
>      is_backup = sync_from && !active;
>  
> @@ -400,13 +554,15 @@ main(int argc, char *argv[])
>      server_config.remotes = &remotes;
>      server_config.config_tmpfile = config_tmpfile;
>  
> -    save_config__(config_tmpfile, &remotes, &db_filenames, sync_from,
> +    save_config__(config_tmpfile, &remotes, &db_conf, sync_from,
>                    sync_exclude, is_backup);
> +    free_remotes(&remotes);
> +    free_database_configs(&db_conf);
>  
>      daemonize_start(false, false);
>  
>      /* Load the saved config. */
> -    load_config(config_tmpfile, &remotes, &db_filenames, &sync_from,
> +    load_config(config_tmpfile, &remotes, &db_conf, &sync_from,
>                  &sync_exclude, &is_backup);
>  
>      /* Start ovsdb jsonrpc server. When running as a backup server,
> @@ -425,13 +581,16 @@ main(int argc, char *argv[])
>  
>      perf_counters_init();
>  
> -    SSET_FOR_EACH (db_filename, &db_filenames) {
> -        struct ovsdb_error *error = open_db(&server_config, db_filename);
> +    SHASH_FOR_EACH (node, &db_conf) {
> +        struct ovsdb_error *error = open_db(&server_config,
> +                                            node->name, node->data);
>          if (error) {
>              char *s = ovsdb_error_to_string_free(error);
>              ovs_fatal(0, "%s", s);
>          }
> +        db_config_destroy(node->data);
>      }
> +    shash_clear(&db_conf);
>      add_server_db(&server_config);
>  
>      char *error = reconfigure_remotes(jsonrpc, &all_dbs, &remotes);
> @@ -538,15 +697,8 @@ main(int argc, char *argv[])
>      unixctl_command_register("ovsdb-server/disable-monitor-cond", "", 0, 0,
>                               ovsdb_server_disable_monitor_cond, jsonrpc);
>  
> -    if (is_backup) {
> -        const struct uuid *server_uuid;
> -        server_uuid = ovsdb_jsonrpc_server_get_uuid(jsonrpc);
> -        ovsdb_replication_init(sync_from, sync_exclude, &all_dbs, 
> server_uuid,
> -                               replication_probe_interval);
> -    }
> -
>      main_loop(&server_config, jsonrpc, &all_dbs, unixctl, &remotes,
> -              run_process, &exiting, &is_backup);
> +              run_process, &exiting);
>  
>      SHASH_FOR_EACH_SAFE (node, &all_dbs) {
>          struct db *db = node->data;
> @@ -556,7 +708,8 @@ main(int argc, char *argv[])
>      ovsdb_jsonrpc_server_destroy(jsonrpc);
>      shash_destroy(&all_dbs);
>      free_remotes(&remotes);
> -    sset_destroy(&db_filenames);
> +    free_database_configs(&db_conf);
> +    shash_destroy(&db_conf);
>      free(sync_from);
>      free(sync_exclude);
>      unixctl_server_destroy(unixctl);
> @@ -580,7 +733,7 @@ main(int argc, char *argv[])
>   *
>   * "False negatives" are possible. */
>  static bool
> -is_already_open(struct server_config *config OVS_UNUSED,
> +is_already_open(struct server_config *server_config OVS_UNUSED,
>                  const char *filename OVS_UNUSED)
>  {
>  #ifndef _WIN32
> @@ -589,11 +742,12 @@ is_already_open(struct server_config *config OVS_UNUSED,
>      if (!stat(filename, &s)) {
>          struct shash_node *node;
>  
> -        SHASH_FOR_EACH (node, config->all_dbs) {
> +        SHASH_FOR_EACH (node, server_config->all_dbs) {
>              struct db *db = node->data;
>              struct stat s2;
>  
> -            if (!stat(db->filename, &s2)
> +            if (db->config->model != SM_RELAY
> +                && !stat(db->filename, &s2)
>                  && s.st_dev == s2.st_dev
>                  && s.st_ino == s2.st_ino) {
>                  return true;
> @@ -606,16 +760,19 @@ is_already_open(struct server_config *config OVS_UNUSED,
>  }
>  
>  static void
> -close_db(struct server_config *config, struct db *db, char *comment)
> +close_db(struct server_config *server_config, struct db *db, char *comment)
>  {
>      if (db) {
> -        ovsdb_jsonrpc_server_remove_db(config->jsonrpc, db->db, comment);
> -        if (db->db->is_relay) {
> +        ovsdb_jsonrpc_server_remove_db(server_config->jsonrpc,
> +                                       db->db, comment);
> +        if (db->config->model == SM_RELAY) {
>              ovsdb_relay_del_db(db->db);
>          }
> -        if (*config->is_backup) {
> -            replication_remove_db(db->db);
> +        if (db->config->model == SM_ACTIVE_BACKUP
> +            && db->config->ab.backup) {
> +            ovsdb_server_replication_remove_db(db);
>          }
> +        db_config_destroy(db->config);
>          ovsdb_destroy(db->db);
>          free(db->filename);
>          free(db);
> @@ -768,20 +925,17 @@ add_db(struct server_config *config, struct db *db)
>  }
>  
>  static struct ovsdb_error * OVS_WARN_UNUSED_RESULT
> -open_db(struct server_config *config, const char *filename)
> +open_db(struct server_config *server_config,
> +        const char *filename, const struct db_config *conf)
>  {
>      struct ovsdb_storage *storage;
> -    char *relay_remotes = NULL;
>      struct ovsdb_error *error;
> -    bool is_relay;
> -    char *name;
>  
> -    is_relay = parse_relay_args(filename, &name, &relay_remotes);
> -    if (!is_relay) {
> +    if (conf->model != SM_RELAY) {
>          /* If we know that the file is already open, return a good error
>           * message.  Otherwise, if the file is open, we'll fail later on with
>           * a harder to interpret file locking error. */
> -        if (is_already_open(config, filename)) {
> +        if (is_already_open(server_config, filename)) {
>              return ovsdb_error(NULL, "%s: already open", filename);
>          }
>  
> @@ -789,59 +943,78 @@ open_db(struct server_config *config, const char 
> *filename)
>          if (error) {
>              return error;
>          }
> -        name = xstrdup(filename);
>      } else {
> -        storage = ovsdb_storage_create_unbacked(name);
> +        storage = ovsdb_storage_create_unbacked(filename);
> +    }
> +
> +    enum service_model model = conf->model;
> +    if (model == SM_UNDEFINED || model == SM_STANDALONE
> +        || model == SM_CLUSTERED) {
> +        /* Check the actual service model from the storage. */
> +        model = ovsdb_storage_is_clustered(storage)
> +                ? SM_CLUSTERED : SM_STANDALONE;
> +    }
> +    if (conf->model != SM_UNDEFINED && conf->model != model) {
> +        ovsdb_storage_close(storage);
> +        return ovsdb_error(NULL, "%s: database is %s and not %s",
> +                           filename, service_model_to_string(model),
> +                           service_model_to_string(conf->model));
>      }
>  
>      struct ovsdb_schema *schema;
> -    if (is_relay || ovsdb_storage_is_clustered(storage)) {
> +    if (model == SM_RELAY || model == SM_CLUSTERED) {
>          schema = NULL;
>      } else {
>          struct json *txn_json;
>          error = ovsdb_storage_read(storage, &schema, &txn_json, NULL);
>          if (error) {
>              ovsdb_storage_close(storage);
> -            free(name);
>              return error;
>          }
>          ovs_assert(schema && !txn_json);
>      }
>  
>      struct db *db = xzalloc(sizeof *db);
> -    db->filename = name;
> +    db->filename = xstrdup(filename);
> +    db->config = db_config_clone(conf);
> +    db->config->model = model;
>      db->db = ovsdb_create(schema, storage);
> -    ovsdb_jsonrpc_server_add_db(config->jsonrpc, db->db);
> +    ovsdb_jsonrpc_server_add_db(server_config->jsonrpc, db->db);
>  
>      /* Enable txn history for clustered and relay modes.  It is not enabled 
> for
>       * other modes for now, since txn id is available for clustered and relay
>       * modes only. */
> -    ovsdb_txn_history_init(db->db,
> -                           is_relay || ovsdb_storage_is_clustered(storage));
> +    ovsdb_txn_history_init(db->db, model == SM_RELAY || model == 
> SM_CLUSTERED);
>  
> -    read_db(config, db);
> +    read_db(server_config, db);
>  
>      error = (db->db->name[0] == '_'
>               ? ovsdb_error(NULL, "%s: names beginning with \"_\" are 
> reserved",
>                             db->db->name)
> -             : shash_find(config->all_dbs, db->db->name)
> +             : shash_find(server_config->all_dbs, db->db->name)
>               ? ovsdb_error(NULL, "%s: duplicate database name", db->db->name)
>               : NULL);
>      if (error) {
>          char *error_s = ovsdb_error_to_string(error);
> -        close_db(config, db,
> +        close_db(server_config, db,
>                   xasprintf("cannot complete opening %s database (%s)",
>                             db->db->name, error_s));
>          free(error_s);
>          return error;
>      }
>  
> -    add_db(config, db);
> +    add_db(server_config, db);
> +
> +    if (model == SM_RELAY) {
> +        ovsdb_relay_add_db(db->db, conf->source, update_schema, 
> server_config,
> +                           conf->options->probe_interval);
> +    }
> +    if (model == SM_ACTIVE_BACKUP && conf->ab.backup) {
> +        const struct uuid *server_uuid;
>  
> -    if (is_relay) {
> -        ovsdb_relay_add_db(db->db, relay_remotes, update_schema, config,
> -                           *config->relay_source_probe_interval);
> -        free(relay_remotes);
> +        server_uuid = ovsdb_jsonrpc_server_get_uuid(server_config->jsonrpc);
> +        replication_set_db(db->db, conf->source, conf->ab.sync_exclude,
> +                           server_uuid, conf->options->probe_interval);
>      }
>      return NULL;
>  }
> @@ -865,6 +1038,8 @@ add_server_db(struct server_config *config)
>      /* We don't need txn_history for server_db. */
>  
>      db->filename = xstrdup("<internal>");
> +    db->config = xzalloc(sizeof *db->config);
> +    db->config->model = SM_UNDEFINED;
>      db->db = ovsdb_create(schema, ovsdb_storage_create_unbacked(NULL));
>      db->db->read_only = true;
>  
> @@ -1457,11 +1632,20 @@ ovsdb_server_set_active_ovsdb_server(struct 
> unixctl_conn *conn,
>                                       void *config_)
>  {
>      struct server_config *config = config_;
> +    struct shash_node *node;
>  
> -    if (*config->sync_from) {
> -        free(*config->sync_from);
> -    }
> +    free(*config->sync_from);
>      *config->sync_from = xstrdup(argv[1]);
> +
> +    SHASH_FOR_EACH (node, config->all_dbs) {
> +        struct db *db = node->data;
> +
> +        if (db->config->model == SM_ACTIVE_BACKUP) {
> +            free(db->config->source);
> +            db->config->source = xstrdup(argv[1]);
> +        }
> +    }
> +
>      save_config(config);
>  
>      unixctl_command_reply(conn, NULL);
> @@ -1485,20 +1669,39 @@ ovsdb_server_connect_active_ovsdb_server(struct 
> unixctl_conn *conn,
>                                           void *config_)
>  {
>      struct server_config *config = config_;
> +    struct shash_node *node;
>      char *msg = NULL;
>  
> -    if ( !*config->sync_from) {
> +    if (!*config->sync_from) {
>          msg = "Unable to connect: active server is not specified.\n";
>      } else {
>          const struct uuid *server_uuid;
>          server_uuid = ovsdb_jsonrpc_server_get_uuid(config->jsonrpc);
> -        ovsdb_replication_init(*config->sync_from, *config->sync_exclude,
> -                               config->all_dbs, server_uuid,
> -                               *config->replication_probe_interval);
> -        if (!*config->is_backup) {
> -            *config->is_backup = true;
> -            save_config(config);
> +
> +        SHASH_FOR_EACH (node, config->all_dbs) {
> +            struct db *db = node->data;
> +            struct db_config *conf = db->config;
> +
> +            /* This command also converts standalone databases to AB. */
> +            if (conf->model == SM_STANDALONE) {
> +                conf->model = SM_ACTIVE_BACKUP;
> +                conf->source = xstrdup(*config->sync_from);
> +                conf->options = ovsdb_jsonrpc_default_options(conf->source);
> +                conf->options->probe_interval =
> +                    *config->replication_probe_interval;
> +                conf->ab.sync_exclude =
> +                    nullable_xstrdup(*config->sync_exclude);
> +                conf->ab.backup = false;
> +            }
> +
> +            if (conf->model == SM_ACTIVE_BACKUP && !conf->ab.backup) {
> +                replication_set_db(db->db, conf->source, 
> conf->ab.sync_exclude,
> +                                   server_uuid, 
> conf->options->probe_interval);
> +                conf->ab.backup = true;
> +            }
>          }
> +        *config->is_backup = true;
> +        save_config(config);
>      }
>      unixctl_command_reply(conn, msg);
>  }
> @@ -1514,7 +1717,11 @@ ovsdb_server_disconnect_active_ovsdb_server(struct 
> unixctl_conn *conn,
>  
>      SHASH_FOR_EACH (node, config->all_dbs) {
>          struct db *db = node->data;
> -        replication_remove_db(db->db);
> +        struct db_config *conf = db->config;
> +
> +        if (conf->model == SM_ACTIVE_BACKUP && conf->ab.backup) {
> +            ovsdb_server_replication_remove_db(db);
> +        }
>      }
>      *config->is_backup = false;
>      save_config(config);
> @@ -1528,23 +1735,35 @@ 
> ovsdb_server_set_active_ovsdb_server_probe_interval(struct unixctl_conn *conn,
>                                                     void *config_)
>  {
>      struct server_config *config = config_;
> -
> +    struct shash_node *node;
>      int probe_interval;
> -    if (str_to_int(argv[1], 10, &probe_interval)) {
> -        *config->replication_probe_interval = probe_interval;
> -        save_config(config);
> -        if (*config->is_backup) {
> -            const struct uuid *server_uuid;
> -            server_uuid = ovsdb_jsonrpc_server_get_uuid(config->jsonrpc);
> -            ovsdb_replication_init(*config->sync_from, *config->sync_exclude,
> -                                   config->all_dbs, server_uuid,
> -                                   *config->replication_probe_interval);
> -        }
> -        unixctl_command_reply(conn, NULL);
> -    } else {
> -        unixctl_command_reply(
> +
> +    if (!str_to_int(argv[1], 10, &probe_interval)) {
> +        unixctl_command_reply_error(
>              conn, "Invalid probe interval, integer value expected");
> +        return;
> +    }
> +
> +    const struct uuid *server_uuid;
> +    server_uuid = ovsdb_jsonrpc_server_get_uuid(config->jsonrpc);
> +
> +    *config->replication_probe_interval = probe_interval;
> +
> +    SHASH_FOR_EACH (node, config->all_dbs) {
> +        struct db *db = node->data;
> +        struct db_config *conf = db->config;
> +
> +        if (conf->model == SM_ACTIVE_BACKUP) {
> +            conf->options->probe_interval = probe_interval;
> +            if (conf->ab.backup) {
> +                replication_set_db(db->db, conf->source, 
> conf->ab.sync_exclude,
> +                                   server_uuid, 
> conf->options->probe_interval);
> +            }
> +        }
>      }
> +
> +    save_config(config);
> +    unixctl_command_reply(conn, NULL);
>  }
>  
>  static void
> @@ -1554,17 +1773,30 @@ ovsdb_server_set_relay_source_interval(struct 
> unixctl_conn *conn,
>                                         void *config_)
>  {
>      struct server_config *config = config_;
> +    struct shash_node *node;
>      int probe_interval;
>  
> -    if (str_to_int(argv[1], 10, &probe_interval)) {
> -        *config->relay_source_probe_interval = probe_interval;
> -        save_config(config);
> -        ovsdb_relay_set_probe_interval(probe_interval);
> -        unixctl_command_reply(conn, NULL);
> -    } else {
> +    if (!str_to_int(argv[1], 10, &probe_interval)) {
>          unixctl_command_reply_error(
>              conn, "Invalid probe interval, integer value expected");
> +        return;
> +    }
> +
> +    *config->relay_source_probe_interval = probe_interval;
> +
> +    SHASH_FOR_EACH (node, config->all_dbs) {
> +        struct db *db = node->data;
> +        struct db_config *conf = db->config;
> +
> +        if (conf->model == SM_RELAY) {
> +            conf->options->probe_interval = probe_interval;
> +        }
>      }
> +
> +    ovsdb_relay_set_probe_interval(probe_interval);
> +    save_config(config);
> +
> +    unixctl_command_reply(conn, NULL);
>  }
>  
>  static void
> @@ -1574,20 +1806,36 @@ ovsdb_server_set_sync_exclude_tables(struct 
> unixctl_conn *conn,
>                                       void *config_)
>  {
>      struct server_config *config = config_;
> +    struct shash_node *node;
>  
>      char *err = parse_excluded_tables(argv[1]);
> -    if (!err) {
> -        free(*config->sync_exclude);
> -        *config->sync_exclude = xstrdup(argv[1]);
> -        save_config(config);
> -        if (*config->is_backup) {
> -            const struct uuid *server_uuid;
> -            server_uuid = ovsdb_jsonrpc_server_get_uuid(config->jsonrpc);
> -            ovsdb_replication_init(*config->sync_from, *config->sync_exclude,
> -                                   config->all_dbs, server_uuid,
> -                                   *config->replication_probe_interval);
> +    if (err) {
> +        goto exit;
> +    }
> +
> +    const struct uuid *server_uuid;
> +    server_uuid = ovsdb_jsonrpc_server_get_uuid(config->jsonrpc);
> +
> +    free(*config->sync_exclude);
> +    *config->sync_exclude = xstrdup(argv[1]);
> +
> +    SHASH_FOR_EACH (node, config->all_dbs) {
> +        struct db *db = node->data;
> +        struct db_config *conf = db->config;
> +
> +        if (conf->model == SM_ACTIVE_BACKUP) {
> +            free(conf->ab.sync_exclude);
> +            conf->ab.sync_exclude = xstrdup(argv[1]);
> +            if (conf->ab.backup) {
> +                replication_set_db(db->db, conf->source, 
> conf->ab.sync_exclude,
> +                                   server_uuid, 
> conf->options->probe_interval);
> +            }
>          }
>      }
> +
> +    save_config(config);
> +
> +exit:
>      unixctl_command_reply(conn, err);
>      free(err);
>  }
> @@ -1832,22 +2080,26 @@ ovsdb_server_add_database(struct unixctl_conn *conn, 
> int argc OVS_UNUSED,
>  {
>      struct server_config *config = config_;
>      const char *filename = argv[1];
> +    const struct shash_node *node;
> +    struct shash db_conf;
> +
> +    shash_init(&db_conf);
> +    add_database_config(&db_conf, filename, *config->sync_from,
> +                        *config->sync_exclude, !config->is_backup);
> +    ovs_assert(shash_count(&db_conf) == 1);
> +    node = shash_first(&db_conf);
>  
> -    char *error = ovsdb_error_to_string_free(open_db(config, filename));
> +    char *error = ovsdb_error_to_string_free(open_db(config,
> +                                                     node->name, 
> node->data));
>      if (!error) {
>          save_config(config);
> -        if (*config->is_backup) {
> -            const struct uuid *server_uuid;
> -            server_uuid = ovsdb_jsonrpc_server_get_uuid(config->jsonrpc);
> -            ovsdb_replication_init(*config->sync_from, *config->sync_exclude,
> -                                   config->all_dbs, server_uuid,
> -                                   *config->replication_probe_interval);
> -        }
>          unixctl_command_reply(conn, NULL);
>      } else {
>          unixctl_command_reply_error(conn, error);
>          free(error);
>      }
> +    db_config_destroy(node->data);
> +    shash_destroy(&db_conf);
>  }
>  
>  static void
> @@ -1994,23 +2246,34 @@ ovsdb_server_get_sync_status(struct unixctl_conn 
> *conn, int argc OVS_UNUSED,
>                               const char *argv[] OVS_UNUSED, void *config_)
>  {
>      struct server_config *config = config_;
> -    bool is_backup = *config->is_backup;
>      struct ds ds = DS_EMPTY_INITIALIZER;
> +    bool any_backup = false;
>  
> -    ds_put_format(&ds, "state: %s\n", is_backup ? "backup" : "active");
> +    const struct shash_node **db_nodes = shash_sort(config->all_dbs);
>  
> -    if (is_backup) {
> -        const struct shash_node **db_nodes = shash_sort(config->all_dbs);
> +    for (size_t i = 0; i < shash_count(config->all_dbs); i++) {
> +        const struct db *db = db_nodes[i]->data;
>  
> -        for (size_t i = 0; i < shash_count(config->all_dbs); i++) {
> -            const struct db *db = db_nodes[i]->data;
> +        if (db->config->model != SM_ACTIVE_BACKUP) {
> +            continue;
> +        }
>  
> -            if (db->db && db->db->name[0] != '_') {
> -                ds_put_and_free_cstr(&ds, replication_status(db->db));
> -                ds_put_char(&ds, '\n');
> -            }
> +        any_backup = true;
> +
> +        ds_put_format(&ds, "database: %s\n", db->db->name);
> +        ds_put_format(&ds, "state: %s\n",
> +                      db->config->ab.backup ? "backup" : "active");
> +        if (db->config->ab.backup) {
> +            ds_put_and_free_cstr(&ds, replication_status(db->db));
>          }
> -        free(db_nodes);
> +        if (i + 1 < shash_count(config->all_dbs)) {
> +            ds_put_char(&ds, '\n');
> +        }
> +    }
> +    free(db_nodes);
> +
> +    if (!any_backup) {
> +        ds_put_cstr(&ds, "state: active\n");
>      }
>  
>      unixctl_command_reply(conn, ds_cstr(&ds));
> @@ -2054,7 +2317,7 @@ ovsdb_server_get_db_storage_status(struct unixctl_conn 
> *conn,
>  
>  static void
>  parse_options(int argc, char *argv[],
> -              struct sset *db_filenames, struct shash *remotes,
> +              struct shash *db_conf, struct shash *remotes,
>                char **unixctl_pathp, char **run_command,
>                char **sync_from, char **sync_exclude, bool *active)
>  {
> @@ -2104,7 +2367,7 @@ parse_options(int argc, char *argv[],
>  
>      *sync_from = NULL;
>      *sync_exclude = NULL;
> -    sset_init(db_filenames);
> +    shash_init(db_conf);
>      shash_init(remotes);
>      for (;;) {
>          int c;
> @@ -2210,10 +2473,15 @@ parse_options(int argc, char *argv[],
>      argv += optind;
>      if (argc > 0) {
>          for (int i = 0; i < argc; i++) {
> -            sset_add(db_filenames, argv[i]);
> +            add_database_config(db_conf, argv[i], *sync_from, *sync_exclude,
> +                                *active);
>          }
>      } else if (add_default_db) {
> -        sset_add_and_free(db_filenames, xasprintf("%s/conf.db", 
> ovs_dbdir()));
> +        char *filename = xasprintf("%s/conf.db", ovs_dbdir());
> +
> +        add_database_config(db_conf, filename, *sync_from, *sync_exclude,
> +                            *active);
> +        free(filename);
>      }
>  }
>  
> @@ -2264,16 +2532,63 @@ remotes_to_json(const struct shash *remotes)
>      json = json_object_create();
>      SHASH_FOR_EACH (node, remotes) {
>          json_object_put(json, node->name,
> -                        ovsdb_jsonrpc_options_to_json(node->data));
> +                        ovsdb_jsonrpc_options_to_json(node->data, false));
> +    }
> +    return json;
> +}
> +
> +static struct json *
> +db_config_to_json(const struct db_config *conf)
> +{
> +    struct json *json;
> +
> +    json = json_object_create();
> +
> +    if (conf->model != SM_UNDEFINED) {
> +        json_object_put(json, "service-model",
> +                        json_string_create(
> +                            service_model_to_string(conf->model)));
> +    }
> +
> +    if (conf->source) {
> +        struct json *source = json_object_create();
> +
> +        json_object_put(source, conf->source,
> +                        ovsdb_jsonrpc_options_to_json(conf->options, true));
> +        json_object_put(json, "source", source);
> +    }
> +
> +    if (conf->model == SM_ACTIVE_BACKUP) {
> +        if (conf->ab.sync_exclude) {
> +            struct sset set = SSET_INITIALIZER(&set);
> +
> +            sset_from_delimited_string(&set, conf->ab.sync_exclude, " ,");
> +            json_object_put(json, "exclude-tables", sset_to_json(&set));
> +            sset_destroy(&set);
> +        }
> +        json_object_put(json, "backup", 
> json_boolean_create(conf->ab.backup));
> +    }
> +    return json;
> +}
> +
> +static struct json *
> +databases_to_json(const struct shash *db_conf)
> +{
> +    const struct shash_node *node;
> +    struct json *json;
> +
> +    json = json_object_create();
> +    SHASH_FOR_EACH (node, db_conf) {
> +        json_object_put(json, node->name, db_config_to_json(node->data));
>      }
>      return json;
>  }
>  
>  /* Truncates and replaces the contents of 'config_file' by a representation 
> of
> - * 'remotes' and 'db_filenames'. */
> + * 'remotes', 'db_conf' and a few global replication paramaters. */
>  static void
>  save_config__(FILE *config_file, const struct shash *remotes,
> -              const struct sset *db_filenames, const char *sync_from,
> +              const struct shash *db_conf, const char *sync_from,
>                const char *sync_exclude, bool is_backup)
>  {
>      struct json *obj;
> @@ -2286,7 +2601,8 @@ save_config__(FILE *config_file, const struct shash 
> *remotes,
>  
>      obj = json_object_create();
>      json_object_put(obj, "remotes", remotes_to_json(remotes));
> -    json_object_put(obj, "db_filenames", sset_to_json(db_filenames));
> +    json_object_put(obj, "databases", databases_to_json(db_conf));
> +
>      if (sync_from) {
>          json_object_put(obj, "sync_from", json_string_create(sync_from));
>      }
> @@ -2312,56 +2628,147 @@ save_config__(FILE *config_file, const struct shash 
> *remotes,
>  static void
>  save_config(struct server_config *config)
>  {
> -    struct sset db_filenames;
>      struct shash_node *node;
> +    struct shash db_conf;
>  
> -    sset_init(&db_filenames);
> +    shash_init(&db_conf);
>      SHASH_FOR_EACH (node, config->all_dbs) {
>          struct db *db = node->data;
> +
>          if (node->name[0] != '_') {
> -            sset_add(&db_filenames, db->filename);
> +            shash_add(&db_conf, db->filename, db->config);
>          }
>      }
>  
> -    save_config__(config->config_tmpfile, config->remotes, &db_filenames,
> +    save_config__(config->config_tmpfile, config->remotes, &db_conf,
>                    *config->sync_from, *config->sync_exclude,
>                    *config->is_backup);
>  
> -    sset_destroy(&db_filenames);
> +    shash_destroy(&db_conf);
>  }
>  
>  static void
> -sset_from_json(struct sset *sset, const struct json *array)
> +remotes_from_json(struct shash *remotes, const struct json *json)
>  {
> -    size_t i;
> +    struct ovsdb_jsonrpc_options *options;
> +    const struct shash_node *node;
> +    const struct shash *object;
>  
> -    sset_clear(sset);
> +    free_remotes(remotes);
>  
> -    ovs_assert(array);
> -    ovs_assert(array->type == JSON_ARRAY);
> -    for (i = 0; i < array->array.n; i++) {
> -        const struct json *elem = array->array.elems[i];
> -        sset_add(sset, json_string(elem));
> +    ovs_assert(json);
> +    ovs_assert(json->type == JSON_OBJECT);
> +
> +    object = json_object(json);
> +    SHASH_FOR_EACH (node, object) {
> +        options = ovsdb_jsonrpc_default_options(node->name);
> +        ovsdb_jsonrpc_options_update_from_json(options, node->data, false);
> +        shash_add(remotes, node->name, options);
>      }
>  }
>  
> +static struct db_config *
> +db_config_from_json(const char *name, const struct json *json)
> +{
> +    const struct json *model, *source, *sync_exclude, *backup;
> +    struct db_config *conf = xzalloc(sizeof *conf);
> +    struct ovsdb_parser parser;
> +    struct ovsdb_error *error;
> +
> +    ovsdb_parser_init(&parser, json, "database %s", name);
> +
> +    model = ovsdb_parser_member(&parser, "service-model",
> +                                OP_STRING | OP_OPTIONAL);
> +    conf->model = model ? service_model_from_string(json_string(model))
> +                        : SM_UNDEFINED;
> +
> +    if (conf->model == SM_ACTIVE_BACKUP) {
> +        backup = ovsdb_parser_member(&parser, "backup", OP_BOOLEAN);
> +        conf->ab.backup = backup ? json_boolean(backup) : false;
> +
> +        sync_exclude = ovsdb_parser_member(&parser, "exclude-tables",
> +                                           OP_ARRAY | OP_OPTIONAL);
> +        if (sync_exclude) {
> +            const struct json_array *exclude = json_array(sync_exclude);
> +            struct sset set = SSET_INITIALIZER(&set);
> +
> +            for (size_t i = 0; i < exclude->n; i++) {
> +                if (exclude->elems[i]->type != JSON_STRING) {
> +                    ovsdb_parser_raise_error(&parser,
> +                        "'exclude-tables' must contain strings");
> +                    break;
> +                }
> +                sset_add(&set, json_string(exclude->elems[i]));
> +            }
> +            conf->ab.sync_exclude = sset_join(&set, ",", "");
> +            sset_destroy(&set);
> +        }
> +    }
> +
> +    if (conf->model == SM_ACTIVE_BACKUP || conf->model == SM_RELAY) {
> +        enum ovsdb_parser_types type = OP_OBJECT;
> +
> +        if (conf->model == SM_ACTIVE_BACKUP && !conf->ab.backup) {
> +            /* Active database doesn't have to have a source. */
> +            type |= OP_OPTIONAL;
> +        }
> +        source = ovsdb_parser_member(&parser, "source", type);
> +
> +        if (source && shash_count(json_object(source)) != 1) {
> +            ovsdb_parser_raise_error(&parser,
> +                "'source' should be an object with exactly one element");
> +        } else if (source) {
> +            const struct shash_node *node = shash_first(json_object(source));
> +            const struct json *options;
> +
> +            ovs_assert(node);
> +            conf->source = xstrdup(node->name);
> +            options = node->data;
> +
> +            conf->options = get_jsonrpc_options(conf->source, conf->model);
> +
> +            if (options->type == JSON_OBJECT) {
> +                ovsdb_jsonrpc_options_update_from_json(conf->options,
> +                                                       options, true);
> +            } else if (options->type != JSON_NULL) {
> +                ovsdb_parser_raise_error(&parser,
> +                    "JSON-RPC options is not a JSON object or null");
> +            }
> +        }
> +    }
> +
> +    error = ovsdb_parser_finish(&parser);
> +    if (error) {
> +        char *s = ovsdb_error_to_string_free(error);
> +
> +        VLOG_WARN("%s", s);
> +        free(s);
> +        db_config_destroy(conf);
> +        return NULL;
> +    }
> +
> +    return conf;
> +}
> +
> +
>  static void
> -remotes_from_json(struct shash *remotes, const struct json *json)
> +databases_from_json(struct shash *db_conf, const struct json *json)
>  {
> -    struct ovsdb_jsonrpc_options *options;
>      const struct shash_node *node;
>      const struct shash *object;
>  
> -    free_remotes(remotes);
> +    free_database_configs(db_conf);
>  
>      ovs_assert(json);
>      ovs_assert(json->type == JSON_OBJECT);
>  
>      object = json_object(json);
>      SHASH_FOR_EACH (node, object) {
> -        options = ovsdb_jsonrpc_default_options(node->name);
> -        ovsdb_jsonrpc_options_update_from_json(options, node->data);
> -        shash_add(remotes, node->name, options);
> +        struct db_config *conf = db_config_from_json(node->name, node->data);
> +
> +        if (conf) {
> +            shash_add(db_conf, node->name, conf);
> +        }
>      }
>  }
>  
> @@ -2369,7 +2776,7 @@ remotes_from_json(struct shash *remotes, const struct 
> json *json)
>   * 'config_file', which must have been previously written by save_config(). 
> */
>  static void
>  load_config(FILE *config_file, struct shash *remotes,
> -            struct sset *db_filenames, char **sync_from,
> +            struct shash *db_conf, char **sync_from,
>              char **sync_exclude, bool *is_backup)
>  {
>      struct json *json;
> @@ -2384,8 +2791,8 @@ load_config(FILE *config_file, struct shash *remotes,
>      ovs_assert(json->type == JSON_OBJECT);
>  
>      remotes_from_json(remotes, shash_find_data(json_object(json), 
> "remotes"));
> -    sset_from_json(db_filenames,
> -                   shash_find_data(json_object(json), "db_filenames"));
> +    databases_from_json(db_conf,
> +                        shash_find_data(json_object(json), "databases"));
>  
>      struct json *string;
>      string = shash_find_data(json_object(json), "sync_from");
> diff --git a/ovsdb/replication.c b/ovsdb/replication.c
> index 3c59d4039..3a062b078 100644
> --- a/ovsdb/replication.c
> +++ b/ovsdb/replication.c
> @@ -795,7 +795,6 @@ replication_status(const struct ovsdb *db)
>      bool alive = rdb->session && jsonrpc_session_is_alive(rdb->session);
>      struct ds ds = DS_EMPTY_INITIALIZER;
>  
> -    ds_put_format(&ds, "database: %s\n", db->name);
>      if (alive) {
>          switch (rdb->state) {
>          case RPL_S_INIT:
> diff --git a/tests/ovsdb-server.at b/tests/ovsdb-server.at
> index 6eb758e22..45aa80cd6 100644
> --- a/tests/ovsdb-server.at
> +++ b/tests/ovsdb-server.at
> @@ -1988,7 +1988,9 @@ OVS_WAIT_UNTIL([ovs-appctl -t "`pwd`"/unixctl2 
> ovsdb-server/sync-status |grep re
>  
>  dnl Switch the 'db1' to active
>  AT_CHECK([ovs-appctl -t "`pwd`"/unixctl 
> ovsdb-server/disconnect-active-ovsdb-server])
> -AT_CHECK([ovs-appctl -t "`pwd`"/unixctl ovsdb-server/sync-status], [0], 
> [state: active
> +AT_CHECK([ovs-appctl -t "`pwd`"/unixctl ovsdb-server/sync-status], [0], [dnl
> +database: mydb
> +state: active
>  ])
>  
>  dnl Issue a transaction to 'db1'
> @@ -2007,7 +2009,9 @@ AT_CHECK([ovs-appctl -t "`pwd`"/unixctl 
> ovsdb-server/connect-active-ovsdb-server
>  
>  dnl Verify the change happend
>  OVS_WAIT_UNTIL([ovs-appctl -t "`pwd`"/unixctl ovsdb-server/sync-status |grep 
> replicating])
> -AT_CHECK([ovs-appctl -t "`pwd`"/unixctl2 ovsdb-server/sync-status], [0], 
> [state: active
> +AT_CHECK([ovs-appctl -t "`pwd`"/unixctl2 ovsdb-server/sync-status], [0], [dnl
> +database: mydb
> +state: active
>  ])
>  
>  dnl Issue an transaction to 'db2' which is now active.

The rest looks neat, thanks!

Acked-by: Dumitru Ceara <[email protected]>

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to