[ovs-dev] [PATCH v2] ovn: add start of ovn-nbctl

Russell Bryant rbryant at redhat.com
Fri Mar 20 20:20:23 UTC 2015


ovn-nbctl is intended to be a utility for manually interacting with
the OVN northbound db.  A real consumer of OVN, such as OpenStack,
should be using ovsdb directly.  However, a utility like this is
useful during development and testing of both ovn itself as well as
the OpenStack Neutron plugin.  It could also be used when debugging
the state of an ovn deployment.

So far, the commands related to logical switches have been
implemented.  You can add, delete, list, and get/set external:ids.

Signed-off-by: Russell Bryant <rbryant at redhat.com>
---
 ovn/.gitignore      |   2 +
 ovn/automake.mk     |   8 +-
 ovn/ovn-nbctl.8.xml |  22 ++++
 ovn/ovn-nbctl.c     | 338 ++++++++++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 368 insertions(+), 2 deletions(-)

diff --git a/ovn/.gitignore b/ovn/.gitignore
index 011bb1c..f320a6b 100644
--- a/ovn/.gitignore
+++ b/ovn/.gitignore
@@ -12,3 +12,5 @@
 /ovn-nb-idl.c
 /ovn-nb-idl.h
 /ovn-nb-idl.ovsidl
+/ovn-nbctl
+/ovn-nbctl.8
diff --git a/ovn/automake.mk b/ovn/automake.mk
index cc1f8ae..37b0ca6 100644
--- a/ovn/automake.mk
+++ b/ovn/automake.mk
@@ -66,8 +66,8 @@ ovn/ovn-nb.5: \
 		$(srcdir)/ovn/ovn-nb.xml > $@.tmp && \
 	mv $@.tmp $@
 
-man_MANS += ovn/ovn-controller.8 ovn/ovn-architecture.7
-EXTRA_DIST += ovn/ovn-controller.8.in ovn/ovn-architecture.7.xml
+man_MANS += ovn/ovn-controller.8 ovn/ovn-architecture.7 ovn/ovn-nbctl.8
+EXTRA_DIST += ovn/ovn-controller.8.in ovn/ovn-architecture.7.xml ovn/ovn-nbctl.8.xml
 
 SUFFIXES += .xml
 %: %.xml
@@ -115,3 +115,7 @@ ovn_libovn_la_SOURCES = \
 	ovn/ovn-idl.h \
 	ovn/ovn-nb-idl.c \
 	ovn/ovn-nb-idl.h
+
+bin_PROGRAMS += ovn/ovn-nbctl
+ovn_ovn_nbctl_SOURCES = ovn/ovn-nbctl.c
+ovn_ovn_nbctl_LDADD = ovn/libovn.la ovsdb/libovsdb.la lib/libopenvswitch.la
diff --git a/ovn/ovn-nbctl.8.xml b/ovn/ovn-nbctl.8.xml
new file mode 100644
index 0000000..beccb79
--- /dev/null
+++ b/ovn/ovn-nbctl.8.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manpage program="ovn-nbctl" section="8" title="ovn-nbctl">
+    <h1>Name</h1>
+    <p>ovn-nbctl -- Open Virtual Network northbound db management utility</p>
+
+    <h1>Synopsys</h1>
+    <p>ovn-nbctl [OPTIONS] COMMAND [ARG...]</p>
+
+    <h1>Description</h1>
+    <p>This utility can be used to manage the OVN northbound database.</p>
+
+    <h1>Commands</h1>
+    <p>lswitch-add [name]</p>
+    <p>lswitch-del &lt;lswitch&gt;</p>
+    <p>lswitch-list</p>
+    <p>lswitch-set-external-id &lt;lswitch&gt; &lt;key&gt; [value]</p>
+    <p>lswitch-get-external-id &lt;lswitch&gt; [key]</p>
+
+    <h1>Options</h1>
+    <p>-h | --help</p>
+    <p>-V | --version</p>
+</manpage>
diff --git a/ovn/ovn-nbctl.c b/ovn/ovn-nbctl.c
new file mode 100644
index 0000000..64522bf
--- /dev/null
+++ b/ovn/ovn-nbctl.c
@@ -0,0 +1,338 @@
+/*
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <config.h>
+
+#include <getopt.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "command-line.h"
+#include "dirs.h"
+#include "fatal-signal.h"
+#include "ovn/ovn-nb-idl.h"
+#include "poll-loop.h"
+#include "util.h"
+#include "openvswitch/vlog.h"
+
+struct nbctl_context {
+    struct ovsdb_idl *idl;
+    struct ovsdb_idl_txn *txn;
+    const char *db;
+};
+
+static const struct ovs_cmdl_command *get_all_commands(void);
+
+static void
+usage(void)
+{
+    printf("\
+%s: OVN northbound DB management utility\n\
+usage: %s [OPTIONS] COMMAND [ARG...]\n\
+\n\
+Commands:\n\
+  lswitch-add [name]        Create a logical switch\n\
+  lswitch-del <lswitch>     Delete a logical switch\n\
+  lswitch-list              List configured logical switches\n\
+  lswitch-set-external-id <lswitch> <key> [value]\n\
+                            Set or delete an external:id on a logical switch\n\
+  lswitch-get-external-id <lswitch> [key]\n\
+                            List one or all external:ids set on a switch\n\
+Options:\n\
+  -h, --help                display this help message\n\
+  -o, --options             list available options\n\
+  -V, --version             display version information\n\
+", program_name, program_name);
+}
+
+static const char *
+default_db(void)
+{
+    static char *db;
+    if (!db) {
+        db = xasprintf("unix:%s/db.sock", ovs_rundir());
+    }
+    return db;
+}
+
+static const struct nbrec_logical_switch *
+lswitch_by_name_or_uuid(struct nbctl_context *nb_ctx, const char *id)
+{
+    const struct nbrec_logical_switch *lswitch = NULL;
+    int is_uuid = 0;
+    int duplicate = 0;
+    struct uuid lswitch_uuid;
+
+    if (uuid_from_string(&lswitch_uuid, id)) {
+        is_uuid = 1;
+        lswitch = nbrec_logical_switch_get_for_uuid(nb_ctx->idl, &lswitch_uuid);
+    } else {
+        const struct nbrec_logical_switch *iter;
+
+        NBREC_LOGICAL_SWITCH_FOR_EACH(iter, nb_ctx->idl) {
+            if (strcmp(iter->name, id)) {
+                continue;
+            }
+            if (lswitch) {
+                printf("There is more than one logical switch named '%s'. "
+                        "Use a UUID.\n", id);
+                lswitch = NULL;
+                duplicate = 1;
+                break;
+            }
+            lswitch = iter;
+        }
+    }
+
+    if (!lswitch && !duplicate) {
+        printf("lswitch not found for %s: '%s'\n",
+                is_uuid ? "UUID" : "name", id);
+    }
+
+    return lswitch;
+}
+
+static void
+do_lswitch_add(struct ovs_cmdl_context *ctx)
+{
+    struct nbctl_context *nb_ctx = ctx->pvt;
+    struct nbrec_logical_switch *lswitch;
+
+    lswitch = nbrec_logical_switch_insert(nb_ctx->txn);
+    if (ctx->argc == 2) {
+        nbrec_logical_switch_set_name(lswitch, ctx->argv[1]);
+    }
+}
+
+static void
+do_lswitch_del(struct ovs_cmdl_context *ctx)
+{
+    struct nbctl_context *nb_ctx = ctx->pvt;
+    const char *id = ctx->argv[1];
+    const struct nbrec_logical_switch *lswitch;
+
+    lswitch = lswitch_by_name_or_uuid(nb_ctx, id);
+    if (!lswitch) {
+        return;
+    }
+
+    nbrec_logical_switch_delete(lswitch);
+}
+
+static void
+do_lswitch_list(struct ovs_cmdl_context *ctx)
+{
+    struct nbctl_context *nb_ctx = ctx->pvt;
+    const struct nbrec_logical_switch *lswitch;
+
+    NBREC_LOGICAL_SWITCH_FOR_EACH(lswitch, nb_ctx->idl) {
+        printf(UUID_FMT " (%s)\n", UUID_ARGS(&lswitch->header_.uuid), lswitch->name);
+    }
+}
+static void
+do_lswitch_set_external_id(struct ovs_cmdl_context *ctx)
+{
+    struct nbctl_context *nb_ctx = ctx->pvt;
+    const char *id = ctx->argv[1];
+    const struct nbrec_logical_switch *lswitch;
+    struct smap new_external_ids;
+
+    lswitch = lswitch_by_name_or_uuid(nb_ctx, id);
+    if (!lswitch) {
+        return;
+    }
+
+    smap_init(&new_external_ids);
+    smap_clone(&new_external_ids, &lswitch->external_ids);
+    if (ctx->argc == 4) {
+        smap_replace(&new_external_ids, ctx->argv[2], ctx->argv[3]);
+    } else {
+        smap_remove(&new_external_ids, ctx->argv[2]);
+    }
+    nbrec_logical_switch_set_external_ids(lswitch, &new_external_ids);
+    smap_destroy(&new_external_ids);
+}
+
+static void
+do_lswitch_get_external_id(struct ovs_cmdl_context *ctx OVS_UNUSED)
+{
+    struct nbctl_context *nb_ctx = ctx->pvt;
+    const char *id = ctx->argv[1];
+    const struct nbrec_logical_switch *lswitch;
+
+    lswitch = lswitch_by_name_or_uuid(nb_ctx, id);
+    if (!lswitch) {
+        return;
+    }
+
+    if (ctx->argc == 3) {
+        const char *key = ctx->argv[2];
+        const char *value;
+
+        /* List one external ID */
+
+        value = smap_get(&lswitch->external_ids, key);
+        if (value && *value) {
+            printf("%s\n", value);
+        } else {
+            printf("external-id '%s' is not set.\n", key);
+        }
+    } else {
+        struct smap_node *node;
+
+        /* List all external IDs */
+
+        SMAP_FOR_EACH(node, &lswitch->external_ids) {
+            printf("%s=%s\n", node->key, node->value);
+        }
+    }
+}
+
+static void
+parse_options(int argc, char *argv[])
+{
+    static const struct option long_options[] = {
+        {"help", no_argument, NULL, 'h'},
+        {"options", no_argument, NULL, 'o'},
+        {"version", no_argument, NULL, 'V'},
+        {NULL, 0, NULL, 0},
+    };
+    char *short_options = ovs_cmdl_long_options_to_short_options(long_options);
+
+    for (;;) {
+        int c;
+
+        c = getopt_long(argc, argv, short_options, long_options, NULL);
+        if (c == -1) {
+            break;
+        }
+
+        switch (c) {
+        case 'h':
+            usage();
+            exit(EXIT_SUCCESS);
+
+        case 'o':
+            ovs_cmdl_print_options(long_options);
+            exit(EXIT_SUCCESS);
+
+        case 'V':
+            ovs_print_version(0, 0);
+            exit(EXIT_SUCCESS);
+
+        default:
+            break;
+        }
+    }
+
+    free(short_options);
+}
+
+static const struct ovs_cmdl_command all_commands[] = {
+    {
+        .name = "lswitch-add",
+        .usage = "[lswitch]",
+        .min_args = 0,
+        .max_args = 1,
+        .handler = do_lswitch_add,
+    },
+    {
+        .name = "lswitch-del",
+        .usage = "<lswitch>",
+        .min_args = 1,
+        .max_args = 1,
+        .handler = do_lswitch_del,
+    },
+    {
+        .name = "lswitch-list",
+        .usage = "",
+        .min_args = 0,
+        .max_args = 0,
+        .handler = do_lswitch_list,
+    },
+    {
+        .name = "lswitch-set-external-id",
+        .usage = "<lswitch-uuid> <key> [value]",
+        .min_args = 2,
+        .max_args = 3,
+        .handler = do_lswitch_set_external_id,
+    },
+    {
+        .name = "lswitch-get-external-id",
+        .usage = "<lswitch-uuid> [key]",
+        .min_args = 1,
+        .max_args = 2,
+        .handler = do_lswitch_get_external_id,
+    },
+
+    {
+        /* sentinel */
+        .name = NULL,
+    },
+};
+
+static const struct ovs_cmdl_command *
+get_all_commands(void)
+{
+    return all_commands;
+}
+
+int
+main(int argc, char *argv[])
+{
+    struct ovs_cmdl_context ctx;
+    struct nbctl_context nb_ctx = { .idl = NULL, };
+    enum ovsdb_idl_txn_status txn_status;
+    unsigned int seqno;
+
+    fatal_ignore_sigpipe();
+    set_program_name(argv[0]);
+    parse_options(argc, argv);
+    nbrec_init();
+
+    nb_ctx.db = default_db();
+    nb_ctx.idl = ovsdb_idl_create(nb_ctx.db, &nbrec_idl_class, true, false);
+    ctx.pvt = &nb_ctx;
+    ctx.argc = argc - optind;
+    ctx.argv = argv + optind;
+
+    seqno = ovsdb_idl_get_seqno(nb_ctx.idl);
+    for (;;) {
+        ovsdb_idl_run(nb_ctx.idl);
+
+        if (!ovsdb_idl_is_alive(nb_ctx.idl)) {
+            int retval = ovsdb_idl_get_last_error(nb_ctx.idl);
+            printf("%s: database connection failed (%s)",
+                   nb_ctx.db, ovs_retval_to_string(retval));
+        }
+
+        if (seqno != ovsdb_idl_get_seqno(nb_ctx.idl)) {
+            nb_ctx.txn = ovsdb_idl_txn_create(nb_ctx.idl);
+            ovs_cmdl_run_command(&ctx, get_all_commands());
+            txn_status = ovsdb_idl_txn_commit_block(nb_ctx.txn);
+            if (txn_status == TXN_TRY_AGAIN) {
+                continue;
+            } else {
+                break;
+            }
+        }
+
+        if (seqno == ovsdb_idl_get_seqno(nb_ctx.idl)) {
+            ovsdb_idl_wait(nb_ctx.idl);
+            poll_block();
+        }
+    }
+
+    exit(0);
+}
-- 
2.1.0




More information about the dev mailing list