On Thu, Dec 14, 2023 at 2:05 AM Ilya Maximets <[email protected]> wrote:
>
> OVSDB server maintains a temporary file with the current database
> configuration for the case it is restarted by a monitor process
> after a crash. On startup the configuration from command line
> arguments is stored there in a JSON format, also whenever user
> changes the configuration with different UnixCtl commands, those
> changes are getting added to the file. When restarted from the
> crash it reads the configuration from the file and continues
> with all the necessary remotes and databases.
>
> This change allows it to be an external user-provided file that
> OVSDB server will read the configuration from. The file can be
> specified with a --config-file command line argument and it is
> mutually exclusive with most other command line arguments that
> set up remotes or databases, it is also mutually exclusive with
> use of appctl commands that modify same configurations, e.g.
> add/remove-db or add/remove-remote.
>
> If the user wants to change the configuration of a running server,
> they may change the file and call ovsdb-server/reload appctl.
> OVSDB server will open a file, read and parse it, compare the
> new configuration with the current one and adjust the running
> configuration as needed. OVSDB server will try to keep existing
> databases and connections intact, if the change can be applied
> without disrupting the normal operation.
>
> User-provided files are not trustworthy, so extra checks were
> added to ensure a correct file format. If the file cannot be
> correctly parsed, e.g. contains invalid JSON, no changes will
> be applied and the server will keep using the previous
> configuration until the next reload.
>
> If config-file is provided for active-backup databases, permanent
> disconnection of one of the backup databases no longer leads to
> switching all other databases to 'active'. Only the disconnected
> one will transition, since all of them have their own records in
> the configuration file.
>
> With this change, users can run all types of databases within
> the same ovsdb-server process at the same time.
>
> Simple configuration may look like this:
>
> {
> "remotes": {
> "punix:db.sock": {},
> "pssl:6641": {
> "inactivity-probe": 16000,
> "read-only": false,
> "role": "ovn-controller"
> }
> },
> "databases": {
> "conf.db": {},
> "sb.db": {
> "service-model": "clustered"
As noted earlier in the series, I think we should omit 'standalone'
and 'clustered' in the vocabulary so it is clear to the user that this
is determined from the on-disk database file and avoid duplication of
effort in keeping the two in sync.
--
Frode Nordahl
> },
> "OVN_Northbound": {
> "service-model": "relay",
> "source": {
> "ssl:[fe:::1]:6642,ssl:[fe:::2]:6642": {
> "max-backoff": 8000,
> "inactivity-probe": 10000
> }
> }
> }
> }
> }
>
> Signed-off-by: Ilya Maximets <[email protected]>
> ---
> Documentation/ref/ovsdb.7.rst | 86 +++++-
> Documentation/topics/ovsdb-relay.rst | 19 ++
> NEWS | 4 +
> ovsdb/ovsdb-server.1.in | 96 ++++++-
> ovsdb/ovsdb-server.c | 384 ++++++++++++++++++++++-----
> 5 files changed, 513 insertions(+), 76 deletions(-)
>
> diff --git a/Documentation/ref/ovsdb.7.rst b/Documentation/ref/ovsdb.7.rst
> index 84b153d24..42e5f4089 100644
> --- a/Documentation/ref/ovsdb.7.rst
> +++ b/Documentation/ref/ovsdb.7.rst
> @@ -155,6 +155,22 @@ standalone database, configure the server to listen on a
> "connection method"
> that the client can reach, then point the client to that connection method.
> See `Connection Methods`_ below for information about connection methods.
>
> +Open vSwitch 3.3 introduced support for configuration files via
> +``--config-file`` command line option. The configuration file for a server
> +with a **standalone** database may look like this::
> +
> + {
> + "remotes": { "<connection method>": {} },
> + "databases": {
> + "<database file>": {
> + "service-model": "standalone"
> + }
> + }
> + }
> +
> +The ``"service-model"`` key can be omitted. In this case ``ovsdb-server``
> +will infer the service model from the database file itself.
> +
> Active-Backup Database Service Model
> ------------------------------------
>
> @@ -177,10 +193,36 @@ database file from the active server. Then use
> connects to the active server. At that point, the backup server will fetch a
> copy of the active database and keep it up-to-date until it is killed.
>
> +Open vSwitch 3.3 introduced support for configuration files via
> +``--config-file`` command line option. The configuration file for a backup
> +server in this case may look like this::
> +
> + {
> + "remotes": { "<connection method>": {} },
> + "databases": {
> + "<database file>": {
> + "service-model": "active-backup",
> + "backup": true,
> + "source": {
> + "<active>": {
> + "inactivity-probe": <integer>,
> + "max-backoff": <integer>
> + }
> + }
> + }
> + }
> + }
> +
> +All the fields in the ``"<database file>"`` description above are required.
> +Options for the ``"<active>"`` connection method (``"inactivity-probe"``,
> etc.)
> +can be omitted.
> +
> When the active server in an active-backup server pair fails, an
> administrator
> can switch the backup server to an active role with the ``ovs-appctl``
> command
> ``ovsdb-server/disconnect-active-ovsdb-server``. Clients then have
> read/write
> -access to the now-active server. Of course, administrators are slow to
> respond
> +access to the now-active server. When the ``--config-file`` is in use, the
> +same can be achieved by changing the ``"backup"`` value in the file and
> running
> +``ovsdb-server/reload`` command. Of course, administrators are slow to
> respond
> compared to software, so in practice external management software detects the
> active server's failure and changes the backup server's role. For example,
> the
> "Integration Guide for Centralized Control" in the OVN documentation
> describes
> @@ -236,6 +278,22 @@ To set up a clustered database, first initialize it on a
> single node by running
> arguments, the ``create-cluster`` command can create an empty database or
> copy
> a standalone database's contents into the new database.
>
> +Open vSwitch 3.3 introduced support for configuration files via
> +``--config-file`` command line option. The configuration file for a server
> +with a **clustered** database may look like this::
> +
> + {
> + "remotes": { "<connection method>": {} },
> + "databases": {
> + "<database file>": {
> + "service-model": "clustered"
> + }
> + }
> + }
> +
> +The ``"service-model"`` key can be omitted. In this case ``ovsdb-server``
> +will infer the service model from the database file itself.
> +
> To configure a client to use a clustered database, first configure all of the
> servers to listen on a connection method that the client can reach, then
> point
> the client to all of the servers' connection methods, comma-separated. See
> @@ -505,6 +563,29 @@ server. ``<relay source>`` could contain a
> comma-separated list of connection
> methods, e.g. to connect to any server of the clustered database.
> Multiple relay servers could be started for the same relay source.
>
> +Open vSwitch 3.3 introduced support for configuration files via
> +``--config-file`` command line option. The configuration file for a relay
> +database server in this case may look like this::
> +
> + {
> + "remotes": { "<connection method>": {} },
> + "databases": {
> + "<DB_NAME>": {
> + "service-model": "relay",
> + "source": {
> + "<relay source>": {
> + "inactivity-probe": <integer>,
> + "max-backoff": <integer>
> + }
> + }
> + }
> + }
> + }
> +
> +Both the ``"service-model"`` and the ``"source"`` are required. Options for
> +the ``"<relay source>"`` connection method (``"inactivity-probe"``, etc.)
> +can be omitted.
> +
> Since the way relays handle read and write transactions is very similar
> to the clustered model where "cluster" means "set of relay servers connected
> to the same relay source", "follower" means "relay server" and the "leader"
> @@ -629,7 +710,8 @@ Creating a Database
>
> Creating and starting up the service for a new database was covered
> separately for each database service model in the `Service
> -Models`_ section, above.
> +Models`_ section, above. Single ``ovsdb-server`` process may serve
> +any number of databases with different service models at the same time.
>
> Backing Up and Restoring a Database
> -----------------------------------
> diff --git a/Documentation/topics/ovsdb-relay.rst
> b/Documentation/topics/ovsdb-relay.rst
> index 50a3c6d07..63ea5329b 100644
> --- a/Documentation/topics/ovsdb-relay.rst
> +++ b/Documentation/topics/ovsdb-relay.rst
> @@ -105,6 +105,25 @@ started like this::
> $ ...
> $ ovsdb-server --remote=ptcp:6642:172.16.0.K relay:OVN_Southbound:$REMOTES
>
> +Open vSwitch 3.3 introduced support for configuration files via
> +``--config-file`` command line option. The configuration file for a relay
> +database servers in this case may look like this::
> +
> + {
> + "remotes": { "ptcp:6642:172.16.0.X": {} },
> + "databases": {
> + "OVN_Southbound": {
> + "service-model": "relay",
> + "source": {
> + "$REMOTES": {}
> + }
> + }
> + }
> + }
> +
> +See ``ovsdb-server(1)`` and ``Relay Service Model`` in ``ovsdb(7)`` for more
> +configuration options.
> +
> Every relay server could connect to any of the cluster members of their
> choice,
> fairness of load distribution is achieved by shuffling remotes.
>
> diff --git a/NEWS b/NEWS
> index 63f2842ae..977e88174 100644
> --- a/NEWS
> +++ b/NEWS
> @@ -6,6 +6,10 @@ Post-v3.2.0
> from older version is supported but it may trigger more leader
> elections
> during the process, and error logs complaining unrecognized fields may
> be observed on old nodes.
> + * New command line option --config-file that allows a fine control over
> + remotes and database configuration, including setting options for
> + connection methods for relays and active-backup replication.
> + For more details see ovsdb-server(1) and ovsdb(7).
> - ovs-appctl:
> * 'ofproto/trace' now reports OpenFlow rules that make up a conjunctive
> flow match.
> diff --git a/ovsdb/ovsdb-server.1.in b/ovsdb/ovsdb-server.1.in
> index da7a6fd5d..91d6a6e0f 100644
> --- a/ovsdb/ovsdb-server.1.in
> +++ b/ovsdb/ovsdb-server.1.in
> @@ -12,6 +12,7 @@ ovsdb\-server \- Open vSwitch database server
> [\fIdatabase\fR]\&...
> [\fIrelay:schema_name:remote\fR]\&...
> [\fB\-\-remote=\fIremote\fR]\&...
> +[\fB\-\-config\-file=\fIfile\fR]
> [\fB\-\-run=\fIcommand\fR]
> .so lib/daemon-syn.man
> .so lib/service-syn.man
> @@ -44,6 +45,11 @@ If none of database files or relay databases is specified,
> the default is
> initialized using, for example, \fBovsdb\-tool\fR's \fBcreate\fR,
> \fBcreate\-cluster\fR, or \fBjoin\-cluster\fR command.
> .PP
> +All types of databases can alternatively be added using a configuration
> +file provided via \fB\-\-config\-file\fR option. This option is mutually
> +exclusive with specifying \fIdatabase\fR on the command line. For a detailed
> +description of a configuration file format see \fBovsdb\fR(7).
> +.PP
> This OVSDB implementation supports standalone, active-backup, relay and
> clustered database service models, as well as database replication.
> See the Service Models section of \fBovsdb\fR(7) for more information.
> @@ -105,6 +111,74 @@ It is an error for \fIcolumn\fR to have another type.
> .IP
> To connect or listen on multiple connection methods, use multiple
> \fB\-\-remote\fR options.
> +.IP
> +Alternatively, remotes can be specified in a "remotes" section of a
> +configuration file, if provided using \fB\-\-config\-file\fR option.
> +\fB\-\-config\-file\fR and \fB\-\-remote\fR options are mutually
> +exclusive.
> +.
> +.IP "\fB\-\-config-file=\fIfile\fR"
> +Specifies a configuration file for \fBovsdb\-server\fR. This \fIfile\fR
> +can contain connection methods and databases used by the server.
> +The \fIfile\fR contains a JSON object with two main elements:
> +.RS
> +.IP "\fBremotes\fR"
> +JSON object that contains a set of connection methods in a following format:
> +"\fItarget\fR": { "\fIoption\fR": \fIvalue\fR, ... }. Where \fItarget\fR
> +is in the same format as \fIremote\fR in \fB\-\-remote\fR option.
> +\fIoption\fR can be \fBmax-backoff\fR (integer), \fBinactivity-probe\fR
> +(integer), \fBread-only\fR (boolean), \fBrole\fR (string) or \fBdscp\fR
> +(integer) with their allowed \fIvalue\fRs respectively. The meaning of these
> +\fIoption\fRs is the same as in configuration of \fIremote\fR via a database
> +row with \fB\-\-remote\fR option.
> +.IP "\fBdatabases\fR"
> +JSON object that describes databases that should be added to the
> +\fBovsdb\-server\fR in the following format: "\fIname\fR":{ "\fIoption\fR":
> +\fIvalue\fR, ... }. Where \fIname\fR is either a file name of a previously
> +created and initialized database or a schema name in case of relay
> +databases. Available \fIoption\fRs are:
> +.RS
> +.IP "\fBservice-model\fR (string)"
> +Describes the service model of this database. One of: \fBstandalone\fR,
> +\fBclustered\fR, \fBactive-backup\fR or \fBrelay\fR. This option is
> +required for all types, except for standalone and clustered. For these
> +databases the service model will be inferred from the file, if not
> +specified explicitly. \fBovsdb-server\fR will refuse to add a database
> +if the specified \fBservice-model\fR doesn't match with the provided file.
> +.IP "\fBsource\fR (JSON object; active-backup or relay)"
> +Describes the connection method to the active database or to the relay
> +source. It is a JSON object with exactly one element in the same format
> +as elements of "\fBremotes\fR", except that \fBread-only\fR and \fBrole\fR
> +options are not applicable. E.g. \fB"source": { "unix:db.sock": {
> +"inactivity-probe": 10000, "max-backoff": 8000 } }\fR
> +.IP "\fBbackup\fR (boolean; active-backup only)"
> +If set to \fBtrue\fR, \fBovsdb-server\fR will use this database as a
> +backup for the specified \fBsource\fR. Will be served as an active
> +database otherwise.
> +.IP "\fBexclude-tables\fR (JSON array of strings; active-backup only)"
> +List of table names that should be excluded from replication in backup mode,
> +e.g. \fB"exclude-tables": [ "Table_One", "Table_Two" ]\fR.
> +.RE
> +.RE
> +.IP
> +Content of the most basic configuration file may look like this:
> +\fB{ "remotes": { "pssl:6640": {} }, "databases": { "conf.db": {} } }\fR
> +.IP
> +Examples of configuration files for different service models can be
> +found in in \fBovsdb\fR(7).
> +.IP
> +\fB\-\-config-file\fR option is mutually exclusive with \fB\-\-remote\fR
> +as well as with specifying \fIdatabase\fR on a command line. It is also
> +mutually exclusive with all the \fBActive-Backup Options\fR and all the
> +\fBRUNTIME MANAGEMENT COMMANDS\fR that can change the configuration of
> +the server in conflict with the content of the file, i.e. all the commands
> +that manipulate with remotes and databases. Read-only commands can still
> +be used.
> +.IP
> +In case of changes in the \fIfile\fR, users should run
> +\fBovsdb-server/reload\fR command with \fBovs-appctl\fR(8) in order for
> +changes to take effect.
> +.RE
> .
> .IP "\fB\-\-run=\fIcommand\fR]"
> Ordinarily \fBovsdb\-server\fR runs forever, or until it is told to
> @@ -178,6 +252,8 @@ allow the syncing options to be specified using command
> line options,
> yet start the server, as the default, active server. To switch the
> running server to backup mode, use \fBovs-appctl(1)\fR to execute the
> \fBovsdb\-server/connect\-active\-ovsdb\-server\fR command.
> +.PP
> +These options are mutually exclusive with \fB\-\-config\-file\fR.
> .SS "Public Key Infrastructure Options"
> The options described below for configuring the SSL public key
> infrastructure accept a special syntax for obtaining their
> @@ -230,6 +306,8 @@ clients.
> Adds a remote, as if \fB\-\-remote=\fIremote\fR had been specified on
> the \fBovsdb\-server\fR command line. (If \fIremote\fR is already a
> remote, this command succeeds without changing the configuration.)
> +.IP
> +Mutually exclusive with \fB\-\-config\-file\fR option.
> .
> .IP "\fBovsdb\-server/remove\-remote \fIremote\fR"
> Removes the specified \fIremote\fR from the configuration, failing
> @@ -241,6 +319,8 @@ configuring a \fBdb:\fIdb\fB,\fItable\fB,\fIcolumn\fR
> remote.
> (You can remove a database source with \fBovsdb\-server/remove\-remote
> \fBdb:\fIdb\fB,\fItable\fB,\fIcolumn\fR, but not individual
> remotes found indirectly through the database.)
> +.IP
> +Mutually exclusive with \fB\-\-config\-file\fR option.
> .
> .IP "\fBovsdb\-server/list\-remotes"
> Outputs a list of the currently configured remotes named on
> @@ -254,6 +334,8 @@ Adds the \fIdatabase\fR to the running
> \fBovsdb\-server\fR. \fIdatabase\fR
> could be a database file or a relay description in the following format:
> \fIrelay:schema_name:remote\fR. The database file must already have been
> created and initialized using, for example, \fBovsdb\-tool create\fR.
> +.IP
> +Mutually exclusive with \fB\-\-config\-file\fR option.
> .
> .IP "\fBovsdb\-server/remove\-db \fIdatabase\fR"
> Removes \fIdatabase\fR from the running \fBovsdb\-server\fR. \fIdatabase\fR
> @@ -268,6 +350,8 @@ Any public key infrastructure options specified through
> this database
> (e.g. \fB\-\-private\-key=db:\fIdatabase,\fR... on the command line)
> will be disabled until another database with the same name is added
> again (with \fBovsdb\-server/add\-db\fR).
> +.IP
> +Mutually exclusive with \fB\-\-config\-file\fR option.
> .
> .IP "\fBovsdb\-server/list\-dbs"
> Outputs a list of the currently configured databases added either through
> @@ -286,6 +370,9 @@ These commands query and update the role of
> \fBovsdb\-server\fR within
> an active-backup pair of servers. See \fBActive-Backup Options\fR,
> above, and \fBActive-Backup Database Service Model\fR in
> \fBovsdb\fR(7) for more information.
> +.PP
> +All \fBActive-Backup Commands\fR that change the state of \fBovsdb\-server\fR
> +are mutually exclusive with \fB\-\-config\-file\fR option.
> .
> .IP "\fBovsdb\-server/set\-active\-ovsdb\-server \fIserver"
> Sets the active \fIserver\fR from which \fBovsdb\-server\fR connects through
> @@ -324,11 +411,10 @@ Gets the tables that are currently excluded from
> synchronization.
> Prints a summary of replication run time information. The \fBstate\fR
> information is always provided, indicating whether the server is running
> in the \fIactive\fR or the \fIbackup\fR mode.
> -When running in backup mode, replication connection status, which
> -can be either \fIconnecting\fR, \fIreplicating\fR or \fIerror\fR, are shown.
> -When the connection is in \fIreplicating\fR state, further output shows
> -the list of databases currently replicating, and the tables that are
> -excluded.
> +For all databases with active-backup service model, replication connection
> +status, which can be either \fIconnecting\fR, \fIreplicating\fR or
> +\fIerror\fR, are shown. When the connection is in \fIreplicating\fR state,
> +further output shows the tables that are currently excluded from replication.
> .
> .SS "Cluster Commands"
> These commands support the \fBovsdb\-server\fR clustered service model.
> diff --git a/ovsdb/ovsdb-server.c b/ovsdb/ovsdb-server.c
> index 9726a8d72..05b91a197 100644
> --- a/ovsdb/ovsdb-server.c
> +++ b/ovsdb/ovsdb-server.c
> @@ -195,6 +195,13 @@ 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 struct ovsdb_error *update_schema(struct ovsdb *,
> + const struct ovsdb_schema *,
> + const struct uuid *txnid,
> + bool conversion_with_no_data,
> + void *aux)
> + OVS_WARN_UNUSED_RESULT;
> +
> static void parse_options(int argc, char *argvp[],
> struct shash *db_conf, struct shash *remotes,
> char **unixctl_pathp, char **run_command,
> @@ -223,7 +230,7 @@ static void save_config__(FILE *config_file, const struct
> shash *remotes,
> 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,
> +static bool load_config(FILE *config_file, struct shash *remotes,
> struct shash *db_conf, char **sync_from,
> char **sync_exclude, bool *is_backup);
>
> @@ -263,8 +270,9 @@ ovsdb_server_replication_run(struct server_config *config)
> }
>
> /* 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) {
> + * if they are configured via the command line / appctl and so have
> + * shared configuration. */
> + if (!config_file_path && !all_alive && *config->is_backup) {
> *config->is_backup = false;
>
> SHASH_FOR_EACH (node, config->all_dbs) {
> @@ -513,6 +521,196 @@ free_database_configs(struct shash *db_conf)
> shash_clear(db_conf);
> }
>
> +static bool
> +service_model_can_convert(enum service_model a, enum service_model b)
> +{
> + ovs_assert(a != SM_UNDEFINED);
> +
> + if (a == b) {
> + return true;
> + }
> +
> + if (b == SM_UNDEFINED) {
> + return a == SM_STANDALONE || a == SM_CLUSTERED;
> + }
> +
> + /* Conversion can happen only between standalone and active-backup. */
> + return (a == SM_STANDALONE && b == SM_ACTIVE_BACKUP)
> + || (a == SM_ACTIVE_BACKUP && b == SM_STANDALONE);
> +}
> +
> +static void
> +database_update_config(struct server_config *server_config,
> + struct db *db, const struct db_config *new_conf)
> +{
> + struct db_config *conf = db->config;
> + enum service_model model = conf->model;
> +
> + /* Stop replicating when transitioning to active or standalone. */
> + if (conf->model == SM_ACTIVE_BACKUP && conf->ab.backup
> + && (new_conf->model == SM_STANDALONE || !new_conf->ab.backup)) {
> + ovsdb_server_replication_remove_db(db);
> + }
> +
> + db_config_destroy(conf);
> + conf = db->config = db_config_clone(new_conf);
> +
> + if (conf->model == SM_UNDEFINED) {
> + /* We're operating on the same file, the model is the same. */
> + conf->model = model;
> + }
> +
> + if (conf->model == SM_RELAY) {
> + ovsdb_relay_add_db(db->db, conf->source, update_schema,
> server_config,
> + &conf->options->rpc);
> + }
> + if (conf->model == SM_ACTIVE_BACKUP && conf->ab.backup) {
> + const struct uuid *server_uuid;
> +
> + 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->rpc);
> + }
> +}
> +
> +static bool
> +reconfigure_databases(struct server_config *server_config,
> + struct shash *db_conf)
> +{
> + struct db_config *cur_conf, *new_conf;
> + struct shash_node *node, *conf_node;
> + bool res = true;
> + struct db *db;
> +
> + /* Remove databases that are no longer in the configuration or have
> + * incompatible configuration. Update compatible ones. */
> + SHASH_FOR_EACH_SAFE (node, server_config->all_dbs) {
> + db = node->data;
> +
> + if (node->name[0] == '_') {
> + /* Skip internal databases. */
> + continue;
> + }
> +
> + cur_conf = db->config;
> + conf_node = shash_find(db_conf, db->filename);
> + new_conf = conf_node ? conf_node->data : NULL;
> +
> + if (!new_conf) {
> + remove_db(server_config, node,
> + xasprintf("database %s removed from configuration",
> + node->name));
> + continue;
> + }
> + if (!service_model_can_convert(cur_conf->model, new_conf->model)) {
> + remove_db(server_config, node,
> + xasprintf("service model changed for database %s",
> + node->name));
> + continue;
> + }
> + database_update_config(server_config, db, new_conf);
> +
> + db_config_destroy(new_conf);
> + shash_delete(db_conf, conf_node);
> + }
> +
> + /* Create new databases. */
> + 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);
> +
> + VLOG_WARN("failed to open database '%s': %s", node->name, s);
> + free(s);
> + res = false;
> + }
> + db_config_destroy(node->data);
> + }
> + shash_clear(db_conf);
> +
> + return res;
> +}
> +
> +static bool
> +reconfigure_ovsdb_server(struct server_config *server_config)
> +{
> + char *sync_from = NULL, *sync_exclude = NULL;
> + bool is_backup = false;
> + struct shash remotes;
> + struct shash db_conf;
> + bool res = true;
> +
> + FILE *file = NULL;
> +
> + if (config_file_path) {
> + file = fopen(config_file_path, "r+b");
> + if (!file) {
> + VLOG_ERR("failed to open configuration file '%s': %s",
> + config_file_path, ovs_strerror(errno));
> + return false;
> + } else {
> + VLOG_INFO("loading configuration from '%s'", config_file_path);
> + }
> + } else {
> + file = server_config->config_tmpfile;
> + }
> + ovs_assert(file);
> +
> + shash_init(&remotes);
> + shash_init(&db_conf);
> +
> + if (!load_config(file, &remotes, &db_conf,
> + &sync_from, &sync_exclude, &is_backup)) {
> + if (config_file_path) {
> + VLOG_WARN("failed to load configuration from %s",
> + config_file_path);
> + } else {
> + VLOG_FATAL("failed to load configuration from a temporary file");
> + }
> + res = false;
> + goto exit_close;
> + }
> +
> + /* Parsing was successful. Update the server configuration. */
> + shash_swap(server_config->remotes, &remotes);
> + free(*server_config->sync_from);
> + *server_config->sync_from = sync_from;
> + free(*server_config->sync_exclude);
> + *server_config->sync_exclude = sync_exclude;
> + *server_config->is_backup = is_backup;
> +
> + if (!reconfigure_databases(server_config, &db_conf)) {
> + VLOG_WARN("failed to configure databases");
> + res = false;
> + }
> +
> + char *error = reconfigure_remotes(server_config->jsonrpc,
> + server_config->all_dbs,
> + server_config->remotes);
> + if (error) {
> + VLOG_WARN("failed to configure remotes: %s", error);
> + res = false;
> + } else {
> + error = reconfigure_ssl(server_config->all_dbs);
> + if (error) {
> + VLOG_WARN("failed to configure SSL: %s", error);
> + res = false;
> + }
> + }
> + free(error);
> +
> +exit_close:
> + if (config_file_path) {
> + fclose(file);
> + }
> + free_remotes(&remotes);
> + free_database_configs(&db_conf);
> + shash_destroy(&remotes);
> + shash_destroy(&db_conf);
> + return res;
> +}
> +
> int
> main(int argc, char *argv[])
> {
> @@ -527,13 +725,22 @@ main(int argc, char *argv[])
> struct process *run_process;
> bool exiting;
> int retval;
> - FILE *config_tmpfile;
> - struct server_config server_config;
> + FILE *config_tmpfile = NULL;
> struct shash all_dbs;
> struct shash_node *node;
> int replication_probe_interval = REPLICATION_DEFAULT_PROBE_INTERVAL;
> int relay_source_probe_interval = RELAY_SOURCE_DEFAULT_PROBE_INTERVAL;
>
> + struct server_config server_config = {
> + .remotes = &remotes,
> + .all_dbs = &all_dbs,
> + .sync_from = &sync_from,
> + .sync_exclude = &sync_exclude,
> + .is_backup = &is_backup,
> + .replication_probe_interval = &replication_probe_interval,
> + .relay_source_probe_interval = &relay_source_probe_interval,
> + };
> +
> ovs_cmdl_proctitle_init(argc, argv);
> set_program_name(argv[0]);
> service_start(&argc, &argv);
> @@ -548,64 +755,39 @@ main(int argc, char *argv[])
>
> daemon_become_new_user(false, false);
>
> - /* Create and initialize 'config_tmpfile' as a temporary file to hold
> - * ovsdb-server's most basic configuration, and then save our initial
> - * configuration to it. When --monitor is used, this preserves the
> effects
> - * of ovs-appctl commands such as ovsdb-server/add-remote (which saves
> the
> - * new configuration) across crashes. */
> - config_tmpfile = tmpfile();
> - if (!config_tmpfile) {
> - ovs_fatal(errno, "failed to create temporary file");
> + if (!config_file_path) {
> + /* Create and initialize 'config_tmpfile' as a temporary file to
> hold
> + * ovsdb-server's most basic configuration, and then save our initial
> + * configuration to it. When --monitor is used, this preserves the
> + * effects of ovs-appctl commands such as ovsdb-server/add-remote
> + * (which saves the new configuration) across crashes. */
> + config_tmpfile = tmpfile();
> + if (!config_tmpfile) {
> + ovs_fatal(errno, "failed to create temporary file");
> + }
> + server_config.config_tmpfile = config_tmpfile;
> + save_config__(config_tmpfile, &remotes, &db_conf, sync_from,
> + sync_exclude, is_backup);
> }
>
> - server_config.remotes = &remotes;
> - server_config.config_tmpfile = config_tmpfile;
> -
> - 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_conf, &sync_from,
> - &sync_exclude, &is_backup);
> -
> - /* Start ovsdb jsonrpc server. When running as a backup server,
> - * jsonrpc connections are read only. Otherwise, both read
> - * and write transactions are allowed. */
> - jsonrpc = ovsdb_jsonrpc_server_create(is_backup);
> + perf_counters_init();
>
> - shash_init(&all_dbs);
> - server_config.all_dbs = &all_dbs;
> + /* Start ovsdb jsonrpc server. Both read and write transactions are
> + * allowed by default, individual remotes and databases will be
> configured
> + * as read-only, if necessary. */
> + jsonrpc = ovsdb_jsonrpc_server_create(false);
> server_config.jsonrpc = jsonrpc;
> - server_config.sync_from = &sync_from;
> - server_config.sync_exclude = &sync_exclude;
> - server_config.is_backup = &is_backup;
> - server_config.replication_probe_interval = &replication_probe_interval;
> - server_config.relay_source_probe_interval = &relay_source_probe_interval;
> -
> - perf_counters_init();
>
> - 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);
> + shash_init(&all_dbs);
> add_server_db(&server_config);
>
> - char *error = reconfigure_remotes(jsonrpc, &all_dbs, &remotes);
> - if (!error) {
> - error = reconfigure_ssl(&all_dbs);
> - }
> - if (error) {
> - ovs_fatal(0, "%s", error);
> + if (!reconfigure_ovsdb_server(&server_config)) {
> + ovs_fatal(0, "server configuration failed");
> }
>
> retval = unixctl_server_create(unixctl_path, &unixctl);
> @@ -2057,14 +2239,21 @@ ovsdb_server_reconnect(struct unixctl_conn *conn, int
> argc OVS_UNUSED,
> * 'config_file_path', read it and sync the runtime configuration with it. */
> static void
> ovsdb_server_reload(struct unixctl_conn *conn, int argc OVS_UNUSED,
> - const char *argv[] OVS_UNUSED, void *config_ OVS_UNUSED)
> + const char *argv[] OVS_UNUSED, void *config_)
> {
> + struct server_config *config = config_;
> +
> if (!config_file_path) {
> unixctl_command_reply_error(conn,
> "Configuration file was not specified on command line");
> - } else {
> + return;
> + }
> +
> + if (!reconfigure_ovsdb_server(config)) {
> unixctl_command_reply_error(conn,
> - "Configuration file support is not implemented yet");
> + "Configuration failed. See the log file for details.");
> + } else {
> + unixctl_command_reply(conn, NULL);
> }
> }
>
> @@ -2739,6 +2928,10 @@ save_config(struct server_config *config)
> struct shash_node *node;
> struct shash db_conf;
>
> + if (config_file_path) {
> + return;
> + }
> +
> shash_init(&db_conf);
> SHASH_FOR_EACH (node, config->all_dbs) {
> struct db *db = node->data;
> @@ -2755,7 +2948,7 @@ save_config(struct server_config *config)
> shash_destroy(&db_conf);
> }
>
> -static void
> +static bool
> remotes_from_json(struct shash *remotes, const struct json *json)
> {
> struct ovsdb_jsonrpc_options *options;
> @@ -2765,14 +2958,31 @@ remotes_from_json(struct shash *remotes, const struct
> json *json)
> free_remotes(remotes);
>
> ovs_assert(json);
> - ovs_assert(json->type == JSON_OBJECT);
> + if (json->type == JSON_NULL) {
> + return true;
> + }
> + if (json->type != JSON_OBJECT) {
> + VLOG_WARN("config: 'remotes' is not a JSON object");
> + return false;
> + }
>
> 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);
> +
> + json = node->data;
> + if (json->type == JSON_OBJECT) {
> + ovsdb_jsonrpc_options_update_from_json(options, node->data,
> false);
> + } else if (json->type != JSON_NULL) {
> + VLOG_WARN("%s: JSON-RPC options are not a JSON object or null",
> + node->name);
> + free_remotes(remotes);
> + return false;
> + }
> }
> +
> + return true;
> }
>
> static struct db_config *
> @@ -2783,6 +2993,12 @@ db_config_from_json(const char *name, const struct
> json *json)
> struct ovsdb_parser parser;
> struct ovsdb_error *error;
>
> + ovs_assert(json);
> + if (json->type == JSON_NULL) {
> + conf->model = SM_UNDEFINED;
> + return conf;
> + }
> +
> ovsdb_parser_init(&parser, json, "database %s", name);
>
> model = ovsdb_parser_member(&parser, "service-model",
> @@ -2859,7 +3075,7 @@ db_config_from_json(const char *name, const struct json
> *json)
> }
>
>
> -static void
> +static bool
> databases_from_json(struct shash *db_conf, const struct json *json)
> {
> const struct shash_node *node;
> @@ -2868,7 +3084,12 @@ databases_from_json(struct shash *db_conf, const
> struct json *json)
> free_database_configs(db_conf);
>
> ovs_assert(json);
> - ovs_assert(json->type == JSON_OBJECT);
> + if (json->type == JSON_NULL) {
> + return true;
> + }
> + if (json->type != JSON_OBJECT) {
> + VLOG_WARN("config: 'databases' is not a JSON object or null");
> + }
>
> object = json_object(json);
> SHASH_FOR_EACH (node, object) {
> @@ -2876,13 +3097,19 @@ databases_from_json(struct shash *db_conf, const
> struct json *json)
>
> if (conf) {
> shash_add(db_conf, node->name, conf);
> + } else {
> + free_database_configs(db_conf);
> + return false;
> }
> }
> + return true;
> }
>
> -/* Clears and replaces 'remotes' and 'dbnames' by a configuration read from
> - * 'config_file', which must have been previously written by save_config().
> */
> -static void
> +/* Clears and replaces 'remotes' and 'db_conf' by a configuration read from
> + * 'config_file', which must have been previously written by save_config()
> + * or provided by the user with --config-file.
> + * Returns 'true', if parsing was successful, 'false' otherwise. */
> +static bool
> load_config(FILE *config_file, struct shash *remotes,
> struct shash *db_conf, char **sync_from,
> char **sync_exclude, bool *is_backup)
> @@ -2890,17 +3117,34 @@ load_config(FILE *config_file, struct shash *remotes,
> struct json *json;
>
> if (fseek(config_file, 0, SEEK_SET) != 0) {
> - VLOG_FATAL("seek failed in temporary file (%s)",
> ovs_strerror(errno));
> + VLOG_WARN("config: file seek failed (%s)", ovs_strerror(errno));
> + return false;
> }
> json = json_from_stream(config_file);
> if (json->type == JSON_STRING) {
> - VLOG_FATAL("reading json failed (%s)", json_string(json));
> + VLOG_WARN("config: reading JSON failed (%s)", json_string(json));
> + json_destroy(json);
> + return false;
> + }
> + if (json->type != JSON_OBJECT) {
> + VLOG_WARN("configuration in a file must be a JSON object");
> + json_destroy(json);
> + return false;
> }
> - ovs_assert(json->type == JSON_OBJECT);
>
> - remotes_from_json(remotes, shash_find_data(json_object(json),
> "remotes"));
> - databases_from_json(db_conf,
> - shash_find_data(json_object(json), "databases"));
> + if (!remotes_from_json(remotes,
> + shash_find_data(json_object(json), "remotes"))) {
> + VLOG_WARN("config: failed to parse 'remotes'");
> + json_destroy(json);
> + return false;
> + }
> + if (!databases_from_json(db_conf, shash_find_data(json_object(json),
> + "databases"))) {
> + VLOG_WARN("config: failed to parse 'databases'");
> + free_remotes(remotes);
> + json_destroy(json);
> + return false;
> + }
>
> struct json *string;
> string = shash_find_data(json_object(json), "sync_from");
> @@ -2911,7 +3155,9 @@ load_config(FILE *config_file, struct shash *remotes,
> free(*sync_exclude);
> *sync_exclude = string ? xstrdup(json_string(string)) : NULL;
>
> - *is_backup = json_boolean(shash_find_data(json_object(json),
> "is_backup"));
> + struct json *boolean = shash_find_data(json_object(json), "is_backup");
> + *is_backup = boolean ? json_boolean(boolean) : false;
>
> json_destroy(json);
> + return true;
> }
> --
> 2.43.0
>
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev