ovn-nbctl: Move ovn-nbctl to utilities directory.
authorAlex Wang <alexw@nicira.com>
Tue, 4 Aug 2015 18:31:22 +0000 (11:31 -0700)
committerAlex Wang <alexw@nicira.com>
Sat, 8 Aug 2015 16:49:34 +0000 (09:49 -0700)
Signed-off-by: Alex Wang <alexw@nicira.com>
Acked-by: Russell Bryant <rbryant@redhat.com>
ovn/.gitignore
ovn/automake.mk
ovn/ovn-nbctl.8.xml [deleted file]
ovn/ovn-nbctl.c [deleted file]
ovn/utilities/.gitignore
ovn/utilities/automake.mk
ovn/utilities/ovn-nbctl.8.xml [new file with mode: 0644]
ovn/utilities/ovn-nbctl.c [new file with mode: 0644]
tutorial/ovs-sandbox
utilities/ovs-sim.in

index 4c13616..5b3bc55 100644 (file)
@@ -5,5 +5,3 @@
 /ovn-sb.5
 /ovn-sb.gv
 /ovn-sb.pic
-/ovn-nbctl
-/ovn-nbctl.8
index 1170a0b..901e710 100644 (file)
@@ -66,20 +66,15 @@ ovn/ovn-nb.5: \
                $(srcdir)/ovn/ovn-nb.xml > $@.tmp && \
        mv $@.tmp $@
 
-man_MANS += ovn/ovn-architecture.7 ovn/ovn-nbctl.8
-EXTRA_DIST += ovn/ovn-architecture.7.xml ovn/ovn-nbctl.8.xml
-DISTCLEANFILES += ovn/ovn-nbctl.8 ovn/ovn-architecture.7
+man_MANS += ovn/ovn-architecture.7
+EXTRA_DIST += ovn/ovn-architecture.7.xml
+DISTCLEANFILES += ovn/ovn-architecture.7
 
 EXTRA_DIST += \
        ovn/TODO \
        ovn/CONTAINERS.OpenStack.md \
        ovn/OVN-GW-HA.md
 
-# ovn-nbctl
-bin_PROGRAMS += ovn/ovn-nbctl
-ovn_ovn_nbctl_SOURCES = ovn/ovn-nbctl.c
-ovn_ovn_nbctl_LDADD = ovn/lib/libovn.la ovsdb/libovsdb.la lib/libopenvswitch.la
-
 include ovn/controller/automake.mk
 include ovn/lib/automake.mk
 include ovn/northd/automake.mk
diff --git a/ovn/ovn-nbctl.8.xml b/ovn/ovn-nbctl.8.xml
deleted file mode 100644 (file)
index ba3cc82..0000000
+++ /dev/null
@@ -1,248 +0,0 @@
-<?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><code>ovn-nbctl</code> [<var>options</var>] <var>command</var> [<var>arg</var>...]</p>
-
-    <h1>Description</h1>
-    <p>This utility can be used to manage the OVN northbound database.</p>
-
-    <h1>General Commands</h1>
-
-    <dl>
-      <dt><code>show [<var>lswitch</var>]</code></dt>
-      <dd>
-        Prints a brief overview of the database contents.  If
-        <var>lswitch</var> is provided, only records related to that
-        logical switch are shown.
-      </dd>
-    </dl>
-
-    <h1>Logical Switch Commands</h1>
-
-    <dl>
-      <dt><code>lswitch-add</code> [<var>lswitch</var>]</dt> <dd> Creates a new logical switch named <var>lswitch</var>.  If
-        <var>lswitch</var> is not provided, the switch will not have a
-        name so other commands must refer to this switch by its UUID.
-        Initially the switch will have no ports.
-      </dd>
-
-      <dt><code>lswitch-del</code> <var>lswitch</var></dt>
-      <dd>
-        Deletes <var>lswitch</var>.
-      </dd>
-
-      <dt><code>lswitch-list</code></dt>
-      <dd>
-        Lists all existing switches on standard output, one per line.
-      </dd>
-
-      <dt><code>lswitch-set-external-id</code> <var>lswitch</var> <var>key</var> [<var>value</var>]</dt>
-      <dd>
-        <p>Sets or clears an ``external ID'' value on <var>lswitch</var>.
-        These values are intended to identify entities external to OVN
-        with which <var>lswitch</var> is associated.  The OVN Northbound
-        database schema may specify well-known <var>key</var> values,
-        but <var>key</var> and <var>value</var> are otherwise arbitrary
-        strings.</p>
-
-        <p>If <var>value</var> is specified, then <var>key</var> is set to
-        <var>value</var> for <var>lswitch</var>, overwriting any
-        previous value.  If <var>value</var> is omitted, then
-        <var>key</var> is removed from <var>lswitch</var>'s set of
-        external IDs (if it was present.</p>
-       </dd>
-
-      <dt><code>lswitch-get-external-id</code> <var>lswitch</var> [<var>key</var>]</dt>
-      <dd>
-        Queries the external IDs on <var>lswitch</var>.  If
-        <var>key</var> is specified, the output is the value for that
-        <var>key</var> or the empty string if <var>key</var> is unset.
-        If <var>key</var> is omitted, the output is
-        <var>key</var><code>=</code><var>value</var>, one per line, for
-        each key-value pair.
-      </dd>
-    </dl>
-
-    <h1>Logical Port Commands</h1>
-    <dl>
-      <dt><code>lport-add</code> <var>lswitch</var> <var>lport</var></dt>
-      <dd>
-        Creates on <var>lswitch</var> a new logical port named
-        <var>lport</var>.
-      </dd>
-
-      <dt><code>lport-add</code> <var>lswitch</var> <var>lport</var> <var>parent</var> <var>tag</var></dt>
-      <dd>
-        Creates on <var>lswitch</var> a logical port named <var>lport</var>
-        that is a child of <var>parent</var> that is identied with
-        <var>tag</var>.  This is useful in cases such as virtualized
-        container environments where Open vSwitch does not have a direct
-        connection to the container's port and it must be shared with
-        the virtual machine's port.
-      </dd>
-
-      <dt><code>lport-del</code> <var>lport</var></dt>
-      <dd>
-        Deletes <var>lport</var>.
-      </dd>
-
-      <dt><code>lport-list</code> <var>lswitch</var></dt>
-      <dd>
-        Lists all the logical ports within <var>lswitch</var> on
-        standard output, one per line.
-      </dd>
-
-      <dt><code>lport-get-parent</code> <var>lport</var></dt>
-      <dd>
-        If set, get the parent port of <var>lport</var>.  If not set, print
-        nothing.
-      </dd>
-
-      <dt><code>lport-get-tag</code> <var>lport</var></dt>
-      <dd>
-        If set, get the tag for <var>lport</var> traffic.  If not set, print
-        nothing.
-      </dd>
-
-      <dt><code>lport-set-external-id</code> <var>lport</var> <var>key</var> [<var>value</var>]</dt>
-      <dd>
-        <p>Sets or clears an ``external ID'' value on <var>lport</var>.
-        These values are intended to identify entities external to OVN
-        with which <var>lport</var> is associated.  The OVN Northbound
-        database schema may specify well-known <var>key</var> values,
-        but <var>key</var> and <var>value</var> are otherwise arbitrary
-        strings.</p>
-
-        <p>If <var>value</var> is specified, then <var>key</var> is set to
-        <var>value</var> for <var>lport</var>, overwriting any
-        previous value.  If <var>value</var> is omitted, then
-        <var>key</var> is removed from <var>lport</var>'s set of
-        external IDs (if it was present.</p>
-      </dd>
-
-      <dt><code>lport-get-external-id</code> <var>lport</var> [<var>key</var>]</dt>
-      <dd>
-        Queries the external IDs on <var>lport</var>.  If
-        <var>key</var> is specified, the output is the value for that
-        <var>key</var> or the empty string if <var>key</var> is unset.
-        If <var>key</var> is omitted, the output is
-        <var>key</var><code>=</code><var>value</var>, one per line, for
-        each key-value pair.
-      </dd>
-
-      <dt><code>lport-set-macs</code> <var>lport</var> [<var>mac</var>]...</dt>
-      <dd>
-        Sets the MACs associated with <var>lport</var> to
-        <var>mac</var>.  Multiple MACs may be sets by using multiple
-        <var>mac</var> arguments.  If no <var>mac</var> argument is
-        given, <var>lport</var> will have no MACs associated with it.
-      </dd>
-
-      <dt><code>lport-get-macs</code> <var>lport</var></dt>
-      <dd>
-        Lists all the MACs associated with <var>lport</var> on standard
-        output, one per line.
-      </dd>
-
-      <dt><code>lport-set-port-security</code> <var>lport</var> [<var>addrs</var>]...</dt>
-      <dd>
-       <p>
-          Sets the port security addresses associated with <var>lport</var> to
-          <var>addrs</var>.  Multiple sets of addresses may be set by using
-          multiple <var>addrs</var> arguments.  If no <var>addrs</var> argument
-          is given, <var>lport</var> will not have port security enabled.
-       </p>
-
-       <p>
-         Port security limits the addresses from which a logical port may send
-         packets and to which it may receive packets.  See the
-         <code>ovn-nb</code>(5) documentation for the <ref
-         column="port_security" table="Logical_Port"/> column in the <ref
-         table="Logical_Port"/> table for details.
-       </p>
-      </dd>
-
-      <dt><code>lport-get-port-security</code> <var>lport</var></dt>
-      <dd>
-        Lists all the port security addresses associated with <var>lport</var>
-        on standard output, one per line.
-      </dd>
-
-      <dt><code>lport-get-up</code> <var>lport</var></dt>
-      <dd>
-        Prints the state of <var>lport</var>, either <code>up</code> or
-        <code>down</code>.
-      </dd>
-
-      <dt><code>lport-set-enabled</code> <var>lport</var> <var>state</var></dt>
-      <dd>
-        Set the administrative state of <var>lport</var>, either <code>enabled</code>
-        or <code>disabled</code>.  When a port is disabled, no traffic is allowed into
-        or out of the port.
-      </dd>
-
-      <dt><code>lport-get-enabled</code> <var>lport</var></dt>
-      <dd>
-        Prints the administrative state of <var>lport</var>, either <code>enabled</code>
-        or <code>disabled</code>.
-      </dd>
-
-      <dt><code>lport-set-type</code> <var>lport</var> <var>type</var></dt>
-      <dd>
-        Set the type for the logical port.  No special types have been implemented yet.
-      </dd>
-
-      <dt><code>lport-get-type</code> <var>lport</var></dt>
-      <dd>
-        Get the type for the logical port.
-      </dd>
-
-      <dt><code>lport-set-options</code> <var>lport</var> [<var>key=value</var>]...</dt>
-      <dd>
-        Set type-specific key-value options for the logical port.
-      </dd>
-
-      <dt><code>lport-get-options</code> <var>lport</var></dt>
-      <dd>
-        Get the type-specific options for the logical port.
-      </dd>
-
-    </dl>
-
-    <h1>Options</h1>
-
-    <dl>
-    <dt><code>-d</code> <var>database</var></dt>
-    <dt><code>--db</code> <var>database</var></dt>
-    <dd>
-      The OVSDB database remote to contact.  If the <env>OVN_NB_DB</env>
-      environment variable is set, its value is used as the default.
-      Otherwise, the default is <code>unix:@RUNDIR@/db.sock</code>, but this
-      default is unlikely to be useful outside of single-machine OVN test
-      environments.
-    </dd>
-
-    <dt><code>-h</code> | <code>--help</code></dt>
-    <dt><code>-o</code> | <code>--options</code></dt>
-    <dt><code>-V</code> | <code>--version</code></dt>
-    </dl>
-
-    <h1>Logging options</h1>
-    <dl>
-    <dt><code>-v</code><var>spec</var>, <code>--verbose=</code><var>spec</var></dt>
-    <dt><code>-v</code>, <code>--verbose</code></dt>
-    <dt><code>--log-file</code>[<code>=</code><var>file</var>]</dt>
-    <dt><code>--syslog-target=</code><var>host</var><code>:</code><var>port</var></dt>
-    </dl>
-
-    <h1>PKI configuration (required to use SSL)</h1>
-    <dl>
-    <dt><code>-p</code>, <code>--private-key=</code><var>file</var>  file with private key</dt>
-    <dt><code>-c</code>, <code>--certificate=</code><var>file</var>  file with certificate for private key</dt>
-    <dt><code>-C</code>, <code>--ca-cert=</code><var>file</var>      file with peer CA certificate</dt>
-    </dl>
-
-</manpage>
diff --git a/ovn/ovn-nbctl.c b/ovn/ovn-nbctl.c
deleted file mode 100644 (file)
index 0bdb3a3..0000000
+++ /dev/null
@@ -1,1032 +0,0 @@
-/*
- * 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 <inttypes.h>
-#include <stdlib.h>
-#include <stdio.h>
-
-#include "command-line.h"
-#include "dirs.h"
-#include "fatal-signal.h"
-#include "ovn/lib/ovn-nb-idl.h"
-#include "poll-loop.h"
-#include "process.h"
-#include "stream.h"
-#include "stream-ssl.h"
-#include "util.h"
-#include "openvswitch/vlog.h"
-
-VLOG_DEFINE_THIS_MODULE(ovn_nbctl);
-
-struct nbctl_context {
-    struct ovsdb_idl *idl;
-    struct ovsdb_idl_txn *txn;
-};
-
-static const char *db;
-
-static const char *default_db(void);
-
-static void
-usage(void)
-{
-    printf("\
-%s: OVN northbound DB management utility\n\
-usage: %s [OPTIONS] COMMAND [ARG...]\n\
-\n\
-General commands:\n\
-  show                      print overview of database contents\n\
-  show LSWITCH              print overview of database contents for LSWITCH\n\
-\n\
-Logical switch commands:\n\
-  lswitch-add [LSWITCH]     create a logical switch named LSWITCH\n\
-  lswitch-del LSWITCH       delete LSWITCH and all its ports\n\
-  lswitch-list              print the names of all logical switches\n\
-  lswitch-set-external-id LSWITCH KEY [VALUE]\n\
-                            set or delete an external-id on LSWITCH\n\
-  lswitch-get-external-id LSWITCH [KEY]\n\
-                            list one or all external-ids on LSWITCH\n\
-\n\
-Logical port commands:\n\
-  lport-add LSWITCH LPORT   add logical port LPORT on LSWITCH\n\
-  lport-add LSWITCH LPORT PARENT TAG\n\
-                            add logical port LPORT on LSWITCH with PARENT\n\
-                            on TAG\n\
-  lport-del LPORT           delete LPORT from its attached switch\n\
-  lport-list LSWITCH        print the names of all logical ports on LSWITCH\n\
-  lport-get-parent LPORT    get the parent of LPORT if set\n\
-  lport-get-tag LPORT       get the LPORT's tag if set\n\
-  lport-set-external-id LPORT KEY [VALUE]\n\
-                            set or delete an external-id on LPORT\n\
-  lport-get-external-id LPORT [KEY]\n\
-                            list one or all external-ids on LPORT\n\
-  lport-set-macs LPORT [MAC]...\n\
-                            set MAC addresses for LPORT.\n\
-  lport-get-macs LPORT      get a list of MAC addresses on LPORT\n\
-  lport-set-port-security LPORT [ADDRS]...\n\
-                            set port security addresses for LPORT.\n\
-  lport-get-port-security LPORT    get LPORT's port security addresses\n\
-  lport-get-up LPORT        get state of LPORT ('up' or 'down')\n\
-  lport-set-enabled LPORT STATE\n\
-                            set administrative state LPORT\n\
-                            ('enabled' or 'disabled')\n\
-  lport-get-enabled LPORT   get administrative state LPORT\n\
-                            ('enabled' or 'disabled')\n\
-  lport-set-type LPORT TYPE Set the type for LPORT\n\
-  lport-get-type LPORT      Get the type for LPORT\n\
-  lport-set-options LPORT KEY=VALUE [KEY=VALUE]...\n\
-                            Set options related to the type of LPORT\n\
-  lport-get-options LPORT   Get the type specific options for LPORT\n\
-\n\
-Options:\n\
-  --db=DATABASE             connect to DATABASE\n\
-                            (default: %s)\n\
-  -h, --help                display this help message\n\
-  -o, --options             list available options\n\
-  -V, --version             display version information\n\
-", program_name, program_name, default_db());
-    vlog_usage();
-    stream_usage("database", true, true, false);
-}
-\f
-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;
-    bool is_uuid = false;
-    bool duplicate = false;
-    struct uuid lswitch_uuid;
-
-    if (uuid_from_string(&lswitch_uuid, id)) {
-        is_uuid = true;
-        lswitch = nbrec_logical_switch_get_for_uuid(nb_ctx->idl,
-                                                    &lswitch_uuid);
-    }
-
-    if (!lswitch) {
-        const struct nbrec_logical_switch *iter;
-
-        NBREC_LOGICAL_SWITCH_FOR_EACH(iter, nb_ctx->idl) {
-            if (strcmp(iter->name, id)) {
-                continue;
-            }
-            if (lswitch) {
-                VLOG_WARN("There is more than one logical switch named '%s'. "
-                        "Use a UUID.", id);
-                lswitch = NULL;
-                duplicate = true;
-                break;
-            }
-            lswitch = iter;
-        }
-    }
-
-    if (!lswitch && !duplicate) {
-        VLOG_WARN("lswitch not found for %s: '%s'",
-                is_uuid ? "UUID" : "name", id);
-    }
-
-    return lswitch;
-}
-
-static void
-print_lswitch(const struct nbrec_logical_switch *lswitch)
-{
-    printf("    lswitch "UUID_FMT" (%s)\n",
-           UUID_ARGS(&lswitch->header_.uuid), lswitch->name);
-
-    for (size_t i = 0; i < lswitch->n_ports; i++) {
-        const struct nbrec_logical_port *lport = lswitch->ports[i];
-
-        printf("        lport %s\n", lport->name);
-        if (lport->parent_name && lport->n_tag) {
-            printf("            parent: %s, tag:%"PRIu64"\n",
-                   lport->parent_name, lport->tag[0]);
-        }
-        if (lport->n_macs) {
-            printf("            macs:");
-            for (size_t j = 0; j < lport->n_macs; j++) {
-                printf(" %s", lport->macs[j]);
-            }
-            printf("\n");
-        }
-    }
-}
-
-static void
-do_show(struct ovs_cmdl_context *ctx)
-{
-    struct nbctl_context *nb_ctx = ctx->pvt;
-    const struct nbrec_logical_switch *lswitch;
-
-    if (ctx->argc == 2) {
-        lswitch = lswitch_by_name_or_uuid(nb_ctx, ctx->argv[1]);
-        if (lswitch) {
-            print_lswitch(lswitch);
-        }
-    } else {
-        NBREC_LOGICAL_SWITCH_FOR_EACH(lswitch, nb_ctx->idl) {
-            print_lswitch(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)
-{
-    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) {
-            printf("%s\n", value);
-        }
-    } else {
-        struct smap_node *node;
-
-        /* List all external IDs */
-
-        SMAP_FOR_EACH(node, &lswitch->external_ids) {
-            printf("%s=%s\n", node->key, node->value);
-        }
-    }
-}
-\f
-static const struct nbrec_logical_port *
-lport_by_name_or_uuid(struct nbctl_context *nb_ctx, const char *id)
-{
-    const struct nbrec_logical_port *lport = NULL;
-    bool is_uuid = false;
-    struct uuid lport_uuid;
-
-    if (uuid_from_string(&lport_uuid, id)) {
-        is_uuid = true;
-        lport = nbrec_logical_port_get_for_uuid(nb_ctx->idl, &lport_uuid);
-    }
-
-    if (!lport) {
-        NBREC_LOGICAL_PORT_FOR_EACH(lport, nb_ctx->idl) {
-            if (!strcmp(lport->name, id)) {
-                break;
-            }
-        }
-    }
-
-    if (!lport) {
-        VLOG_WARN("lport not found for %s: '%s'",
-                is_uuid ? "UUID" : "name", id);
-    }
-
-    return lport;
-}
-
-static void
-do_lport_add(struct ovs_cmdl_context *ctx)
-{
-    struct nbctl_context *nb_ctx = ctx->pvt;
-    struct nbrec_logical_port *lport;
-    const struct nbrec_logical_switch *lswitch;
-    int64_t tag;
-
-    lswitch = lswitch_by_name_or_uuid(nb_ctx, ctx->argv[1]);
-    if (!lswitch) {
-        return;
-    }
-
-    if (ctx->argc != 3 && ctx->argc != 5) {
-        /* If a parent_name is specified, a tag must be specified as well. */
-        VLOG_WARN("Invalid arguments to lport-add.");
-        return;
-    }
-
-    if (ctx->argc == 5) {
-        /* Validate tag. */
-        if (!ovs_scan(ctx->argv[4], "%"SCNd64, &tag) || tag < 0 || tag > 4095) {
-            VLOG_WARN("Invalid tag '%s'", ctx->argv[4]);
-            return;
-        }
-    }
-
-    /* Create the logical port. */
-    lport = nbrec_logical_port_insert(nb_ctx->txn);
-    nbrec_logical_port_set_name(lport, ctx->argv[2]);
-    if (ctx->argc == 5) {
-        nbrec_logical_port_set_parent_name(lport, ctx->argv[3]);
-        nbrec_logical_port_set_tag(lport, &tag, 1);
-    }
-
-    /* Insert the logical port into the logical switch. */
-    nbrec_logical_switch_verify_ports(lswitch);
-    struct nbrec_logical_port **new_ports = xmalloc(sizeof *new_ports *
-                                                    (lswitch->n_ports + 1));
-    memcpy(new_ports, lswitch->ports, sizeof *new_ports * lswitch->n_ports);
-    new_ports[lswitch->n_ports] = lport;
-    nbrec_logical_switch_set_ports(lswitch, new_ports, lswitch->n_ports + 1);
-    free(new_ports);
-}
-
-/* Removes lport 'lswitch->ports[idx]'. */
-static void
-remove_lport(const struct nbrec_logical_switch *lswitch, size_t idx)
-{
-    const struct nbrec_logical_port *lport = lswitch->ports[idx];
-
-    /* First remove 'lport' from the array of ports.  This is what will
-     * actually cause the logical port to be deleted when the transaction is
-     * sent to the database server (due to garbage collection). */
-    struct nbrec_logical_port **new_ports
-        = xmemdup(lswitch->ports, sizeof *new_ports * lswitch->n_ports);
-    new_ports[idx] = new_ports[lswitch->n_ports - 1];
-    nbrec_logical_switch_verify_ports(lswitch);
-    nbrec_logical_switch_set_ports(lswitch, new_ports, lswitch->n_ports - 1);
-    free(new_ports);
-
-    /* Delete 'lport' from the IDL.  This won't have a real effect on the
-     * database server (the IDL will suppress it in fact) but it means that it
-     * won't show up when we iterate with NBREC_LOGICAL_PORT_FOR_EACH later. */
-    nbrec_logical_port_delete(lport);
-}
-
-static void
-do_lport_del(struct ovs_cmdl_context *ctx)
-{
-    struct nbctl_context *nb_ctx = ctx->pvt;
-    const struct nbrec_logical_port *lport;
-
-    lport = lport_by_name_or_uuid(nb_ctx, ctx->argv[1]);
-    if (!lport) {
-        return;
-    }
-
-    /* Find the switch that contains 'lport', then delete it. */
-    const struct nbrec_logical_switch *lswitch;
-    NBREC_LOGICAL_SWITCH_FOR_EACH (lswitch, nb_ctx->idl) {
-        for (size_t i = 0; i < lswitch->n_ports; i++) {
-            if (lswitch->ports[i] == lport) {
-                remove_lport(lswitch, i);
-                return;
-            }
-        }
-    }
-
-    VLOG_WARN("logical port %s is not part of any logical switch",
-              ctx->argv[1]);
-}
-
-static void
-do_lport_list(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;
-    }
-
-    for (size_t i = 0; i < lswitch->n_ports; i++) {
-        const struct nbrec_logical_port *lport = lswitch->ports[i];
-        printf(UUID_FMT " (%s)\n",
-               UUID_ARGS(&lport->header_.uuid), lport->name);
-    }
-}
-
-static void
-do_lport_get_parent(struct ovs_cmdl_context *ctx)
-{
-    struct nbctl_context *nb_ctx = ctx->pvt;
-    const struct nbrec_logical_port *lport;
-
-    lport = lport_by_name_or_uuid(nb_ctx, ctx->argv[1]);
-    if (!lport) {
-        return;
-    }
-
-    if (lport->parent_name) {
-        printf("%s\n", lport->parent_name);
-    }
-}
-
-static void
-do_lport_get_tag(struct ovs_cmdl_context *ctx)
-{
-    struct nbctl_context *nb_ctx = ctx->pvt;
-    const struct nbrec_logical_port *lport;
-
-    lport = lport_by_name_or_uuid(nb_ctx, ctx->argv[1]);
-    if (!lport) {
-        return;
-    }
-
-    if (lport->n_tag > 0) {
-        printf("%"PRId64"\n", lport->tag[0]);
-    }
-}
-
-static void
-do_lport_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_port *lport;
-    struct smap new_external_ids;
-
-    lport = lport_by_name_or_uuid(nb_ctx, id);
-    if (!lport) {
-        return;
-    }
-
-    smap_init(&new_external_ids);
-    smap_clone(&new_external_ids, &lport->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_port_set_external_ids(lport, &new_external_ids);
-    smap_destroy(&new_external_ids);
-}
-
-static void
-do_lport_get_external_id(struct ovs_cmdl_context *ctx)
-{
-    struct nbctl_context *nb_ctx = ctx->pvt;
-    const char *id = ctx->argv[1];
-    const struct nbrec_logical_port *lport;
-
-    lport = lport_by_name_or_uuid(nb_ctx, id);
-    if (!lport) {
-        return;
-    }
-
-    if (ctx->argc == 3) {
-        const char *key = ctx->argv[2];
-        const char *value;
-
-        /* List one external ID */
-
-        value = smap_get(&lport->external_ids, key);
-        if (value) {
-            printf("%s\n", value);
-        }
-    } else {
-        struct smap_node *node;
-
-        /* List all external IDs */
-
-        SMAP_FOR_EACH(node, &lport->external_ids) {
-            printf("%s=%s\n", node->key, node->value);
-        }
-    }
-}
-
-static void
-do_lport_set_macs(struct ovs_cmdl_context *ctx)
-{
-    struct nbctl_context *nb_ctx = ctx->pvt;
-    const char *id = ctx->argv[1];
-    const struct nbrec_logical_port *lport;
-
-    lport = lport_by_name_or_uuid(nb_ctx, id);
-    if (!lport) {
-        return;
-    }
-
-    nbrec_logical_port_set_macs(lport,
-            (const char **) ctx->argv + 2, ctx->argc - 2);
-}
-
-static void
-do_lport_get_macs(struct ovs_cmdl_context *ctx)
-{
-    struct nbctl_context *nb_ctx = ctx->pvt;
-    const char *id = ctx->argv[1];
-    const struct nbrec_logical_port *lport;
-    size_t i;
-
-    lport = lport_by_name_or_uuid(nb_ctx, id);
-    if (!lport) {
-        return;
-    }
-
-    for (i = 0; i < lport->n_macs; i++) {
-        printf("%s\n", lport->macs[i]);
-    }
-}
-
-static void
-do_lport_set_port_security(struct ovs_cmdl_context *ctx)
-{
-    struct nbctl_context *nb_ctx = ctx->pvt;
-    const char *id = ctx->argv[1];
-    const struct nbrec_logical_port *lport;
-
-    lport = lport_by_name_or_uuid(nb_ctx, id);
-    if (!lport) {
-        return;
-    }
-
-    nbrec_logical_port_set_port_security(lport,
-            (const char **) ctx->argv + 2, ctx->argc - 2);
-}
-
-static void
-do_lport_get_port_security(struct ovs_cmdl_context *ctx)
-{
-    struct nbctl_context *nb_ctx = ctx->pvt;
-    const char *id = ctx->argv[1];
-    const struct nbrec_logical_port *lport;
-    size_t i;
-
-    lport = lport_by_name_or_uuid(nb_ctx, id);
-    if (!lport) {
-        return;
-    }
-
-    for (i = 0; i < lport->n_port_security; i++) {
-        printf("%s\n", lport->port_security[i]);
-    }
-}
-
-static void
-do_lport_get_up(struct ovs_cmdl_context *ctx)
-{
-    struct nbctl_context *nb_ctx = ctx->pvt;
-    const char *id = ctx->argv[1];
-    const struct nbrec_logical_port *lport;
-
-    lport = lport_by_name_or_uuid(nb_ctx, id);
-    if (!lport) {
-        return;
-    }
-
-    printf("%s\n", (lport->up && *lport->up) ? "up" : "down");
-}
-
-static void
-do_lport_set_enabled(struct ovs_cmdl_context *ctx)
-{
-    struct nbctl_context *nb_ctx = ctx->pvt;
-    const char *id = ctx->argv[1];
-    const char *state = ctx->argv[2];
-    const struct nbrec_logical_port *lport;
-
-    lport = lport_by_name_or_uuid(nb_ctx, id);
-    if (!lport) {
-        return;
-    }
-
-    if (!strcasecmp(state, "enabled")) {
-        bool enabled = true;
-        nbrec_logical_port_set_enabled(lport, &enabled, 1);
-    } else if (!strcasecmp(state, "disabled")) {
-        bool enabled = false;
-        nbrec_logical_port_set_enabled(lport, &enabled, 1);
-    } else {
-        VLOG_ERR("Invalid state '%s' provided to lport-set-enabled", state);
-    }
-}
-
-static void
-do_lport_get_enabled(struct ovs_cmdl_context *ctx)
-{
-    struct nbctl_context *nb_ctx = ctx->pvt;
-    const char *id = ctx->argv[1];
-    const struct nbrec_logical_port *lport;
-
-    lport = lport_by_name_or_uuid(nb_ctx, id);
-    if (!lport) {
-        return;
-    }
-
-    printf("%s\n",
-           (!lport->enabled || *lport->enabled) ? "enabled" : "disabled");
-}
-
-static void
-do_lport_set_type(struct ovs_cmdl_context *ctx)
-{
-    struct nbctl_context *nb_ctx = ctx->pvt;
-    const char *id = ctx->argv[1];
-    const char *type = ctx->argv[2];
-    const struct nbrec_logical_port *lport;
-
-    lport = lport_by_name_or_uuid(nb_ctx, id);
-    if (!lport) {
-        return;
-    }
-
-    nbrec_logical_port_set_type(lport, type);
-}
-
-static void
-do_lport_get_type(struct ovs_cmdl_context *ctx)
-{
-    struct nbctl_context *nb_ctx = ctx->pvt;
-    const char *id = ctx->argv[1];
-    const struct nbrec_logical_port *lport;
-
-    lport = lport_by_name_or_uuid(nb_ctx, id);
-    if (!lport) {
-        return;
-    }
-
-    printf("%s\n", lport->type);
-}
-
-static void
-do_lport_set_options(struct ovs_cmdl_context *ctx)
-{
-    struct nbctl_context *nb_ctx = ctx->pvt;
-    const char *id = ctx->argv[1];
-    const struct nbrec_logical_port *lport;
-    size_t i;
-    struct smap options = SMAP_INITIALIZER(&options);
-
-    lport = lport_by_name_or_uuid(nb_ctx, id);
-    if (!lport) {
-        return;
-    }
-
-    for (i = 2; i < ctx->argc; i++) {
-        char *key, *value;
-        value = xstrdup(ctx->argv[i]);
-        key = strsep(&value, "=");
-        if (value) {
-            smap_add(&options, key, value);
-        }
-        free(key);
-    }
-
-    nbrec_logical_port_set_options(lport, &options);
-
-    smap_destroy(&options);
-}
-
-static void
-do_lport_get_options(struct ovs_cmdl_context *ctx)
-{
-    struct nbctl_context *nb_ctx = ctx->pvt;
-    const char *id = ctx->argv[1];
-    const struct nbrec_logical_port *lport;
-    struct smap_node *node;
-
-    lport = lport_by_name_or_uuid(nb_ctx, id);
-    if (!lport) {
-        return;
-    }
-
-    SMAP_FOR_EACH(node, &lport->options) {
-        printf("%s=%s\n", node->key, node->value);
-    }
-}
-\f
-static void
-parse_options(int argc, char *argv[])
-{
-    enum {
-        VLOG_OPTION_ENUMS,
-    };
-    static const struct option long_options[] = {
-        {"db", required_argument, NULL, 'd'},
-        {"help", no_argument, NULL, 'h'},
-        {"options", no_argument, NULL, 'o'},
-        {"version", no_argument, NULL, 'V'},
-        VLOG_LONG_OPTIONS,
-        STREAM_SSL_LONG_OPTIONS,
-        {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) {
-        VLOG_OPTION_HANDLERS;
-        STREAM_SSL_OPTION_HANDLERS;
-
-        case 'd':
-            db = optarg;
-            break;
-
-        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;
-        }
-    }
-
-    if (!db) {
-        db = default_db();
-    }
-
-    free(short_options);
-}
-
-static const struct ovs_cmdl_command all_commands[] = {
-    {
-        .name = "show",
-        .usage = "[LSWITCH]",
-        .min_args = 0,
-        .max_args = 1,
-        .handler = do_show,
-    },
-    {
-        .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 KEY [VALUE]",
-        .min_args = 2,
-        .max_args = 3,
-        .handler = do_lswitch_set_external_id,
-    },
-    {
-        .name = "lswitch-get-external-id",
-        .usage = "LSWITCH [KEY]",
-        .min_args = 1,
-        .max_args = 2,
-        .handler = do_lswitch_get_external_id,
-    },
-    {
-        .name = "lport-add",
-        .usage = "LSWITCH LPORT [PARENT] [TAG]",
-        .min_args = 2,
-        .max_args = 4,
-        .handler = do_lport_add,
-    },
-    {
-        .name = "lport-del",
-        .usage = "LPORT",
-        .min_args = 1,
-        .max_args = 1,
-        .handler = do_lport_del,
-    },
-    {
-        .name = "lport-list",
-        .usage = "LSWITCH",
-        .min_args = 1,
-        .max_args = 1,
-        .handler = do_lport_list,
-    },
-    {
-        .name = "lport-get-parent",
-        .usage = "LPORT",
-        .min_args = 1,
-        .max_args = 1,
-        .handler = do_lport_get_parent,
-    },
-    {
-        .name = "lport-get-tag",
-        .usage = "LPORT",
-        .min_args = 1,
-        .max_args = 1,
-        .handler = do_lport_get_tag,
-    },
-    {
-        .name = "lport-set-external-id",
-        .usage = "LPORT KEY [VALUE]",
-        .min_args = 2,
-        .max_args = 3,
-        .handler = do_lport_set_external_id,
-    },
-    {
-        .name = "lport-get-external-id",
-        .usage = "LPORT [KEY]",
-        .min_args = 1,
-        .max_args = 2,
-        .handler = do_lport_get_external_id,
-    },
-    {
-        .name = "lport-set-macs",
-        .usage = "LPORT [MAC]...",
-        .min_args = 1,
-        /* Accept however many arguments the system will allow. */
-        .max_args = INT_MAX,
-        .handler = do_lport_set_macs,
-    },
-    {
-        .name = "lport-get-macs",
-        .usage = "LPORT",
-        .min_args = 1,
-        .max_args = 1,
-        .handler = do_lport_get_macs,
-    },
-    {
-        .name = "lport-set-port-security",
-        .usage = "LPORT [ADDRS]...",
-        .min_args = 0,
-        /* Accept however many arguments the system will allow. */
-        .max_args = INT_MAX,
-        .handler = do_lport_set_port_security,
-    },
-    {
-        .name = "lport-get-port-security",
-        .usage = "LPORT",
-        .min_args = 1,
-        .max_args = 1,
-        .handler = do_lport_get_port_security,
-    },
-    {
-        .name = "lport-get-up",
-        .usage = "LPORT",
-        .min_args = 1,
-        .max_args = 1,
-        .handler = do_lport_get_up,
-    },
-    {
-        .name = "lport-set-enabled",
-        .usage = "LPORT STATE",
-        .min_args = 2,
-        .max_args = 2,
-        .handler = do_lport_set_enabled,
-    },
-    {
-        .name = "lport-get-enabled",
-        .usage = "LPORT",
-        .min_args = 1,
-        .max_args = 1,
-        .handler = do_lport_get_enabled,
-    },
-    {
-        .name = "lport-set-type",
-        .usage = "LPORT TYPE",
-        .min_args = 2,
-        .max_args = 2,
-        .handler = do_lport_set_type,
-    },
-    {
-        .name = "lport-get-type",
-        .usage = "LPORT",
-        .min_args = 1,
-        .max_args = 1,
-        .handler = do_lport_get_type,
-    },
-    {
-        .name = "lport-set-options",
-        .usage = "LPORT KEY=VALUE [KEY=VALUE]...",
-        .min_args = 1,
-        .max_args = INT_MAX,
-        .handler = do_lport_set_options
-    },
-    {
-        .name = "lport-get-options",
-        .usage = "LPORT",
-        .min_args = 1,
-        .max_args = 1,
-        .handler = do_lport_get_options,
-    },
-
-    {
-        /* sentinel */
-        .name = NULL,
-    },
-};
-
-static const struct ovs_cmdl_command *
-get_all_commands(void)
-{
-    return all_commands;
-}
-
-static const char *
-default_db(void)
-{
-    static char *def;
-    if (!def) {
-        def = getenv("OVN_NB_DB");
-        if (!def) {
-            def = xasprintf("unix:%s/db.sock", ovs_rundir());
-        }
-    }
-    return def;
-}
-
-int
-main(int argc, char *argv[])
-{
-    extern struct vlog_module VLM_reconnect;
-    struct ovs_cmdl_context ctx;
-    struct nbctl_context nb_ctx = { .idl = NULL, };
-    enum ovsdb_idl_txn_status txn_status;
-    unsigned int seqno;
-    int res = 0;
-    char *args;
-
-    fatal_ignore_sigpipe();
-    set_program_name(argv[0]);
-    vlog_set_levels(NULL, VLF_CONSOLE, VLL_WARN);
-    vlog_set_levels(&VLM_reconnect, VLF_ANY_DESTINATION, VLL_WARN);
-    parse_options(argc, argv);
-    nbrec_init();
-
-    args = process_escape_args(argv);
-
-    nb_ctx.idl = ovsdb_idl_create(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);
-            VLOG_ERR("%s: database connection failed (%s)",
-                    db, ovs_retval_to_string(retval));
-            res = 1;
-            break;
-        }
-
-        if (seqno != ovsdb_idl_get_seqno(nb_ctx.idl)) {
-            nb_ctx.txn = ovsdb_idl_txn_create(nb_ctx.idl);
-            ovsdb_idl_txn_add_comment(nb_ctx.txn, "ovn-nbctl: %s", args);
-            ovs_cmdl_run_command(&ctx, get_all_commands());
-            txn_status = ovsdb_idl_txn_commit_block(nb_ctx.txn);
-            if (txn_status == TXN_TRY_AGAIN) {
-                ovsdb_idl_txn_destroy(nb_ctx.txn);
-                nb_ctx.txn = NULL;
-                continue;
-            } else {
-                break;
-            }
-        }
-
-        if (seqno == ovsdb_idl_get_seqno(nb_ctx.idl)) {
-            ovsdb_idl_wait(nb_ctx.idl);
-            poll_block();
-        }
-    }
-
-    if (nb_ctx.txn) {
-        ovsdb_idl_txn_destroy(nb_ctx.txn);
-    }
-    ovsdb_idl_destroy(nb_ctx.idl);
-    free(args);
-
-    exit(res);
-}
index 1ddc63e..8769651 100644 (file)
@@ -1 +1,3 @@
 /ovn-ctl.8
+/ovn-nbctl
+/ovn-nbctl.8
index 1373864..145ee44 100644 (file)
@@ -2,11 +2,19 @@ scripts_SCRIPTS += \
     ovn/utilities/ovn-ctl
 
 man_MANS += \
-    ovn/utilities/ovn-ctl.8
+    ovn/utilities/ovn-ctl.8 \
+    ovn/utilities/ovn-nbctl.8
 
 EXTRA_DIST += \
     ovn/utilities/ovn-ctl \
-    ovn/utilities/ovn-ctl.8.xml
+    ovn/utilities/ovn-ctl.8.xml \
+    ovn/utilities/ovn-nbctl.8.xml
 
 DISTCLEANFILES += \
-    ovn/utilities/ovn-ctl.8
+    ovn/utilities/ovn-ctl.8 \
+    ovn/utilities/ovn-nbctl.8
+
+# ovn-nbctl
+bin_PROGRAMS += ovn/utilities/ovn-nbctl
+ovn_utilities_ovn_nbctl_SOURCES = ovn/utilities/ovn-nbctl.c
+ovn_utilities_ovn_nbctl_LDADD = ovn/lib/libovn.la ovsdb/libovsdb.la lib/libopenvswitch.la
diff --git a/ovn/utilities/ovn-nbctl.8.xml b/ovn/utilities/ovn-nbctl.8.xml
new file mode 100644 (file)
index 0000000..ba3cc82
--- /dev/null
@@ -0,0 +1,248 @@
+<?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><code>ovn-nbctl</code> [<var>options</var>] <var>command</var> [<var>arg</var>...]</p>
+
+    <h1>Description</h1>
+    <p>This utility can be used to manage the OVN northbound database.</p>
+
+    <h1>General Commands</h1>
+
+    <dl>
+      <dt><code>show [<var>lswitch</var>]</code></dt>
+      <dd>
+        Prints a brief overview of the database contents.  If
+        <var>lswitch</var> is provided, only records related to that
+        logical switch are shown.
+      </dd>
+    </dl>
+
+    <h1>Logical Switch Commands</h1>
+
+    <dl>
+      <dt><code>lswitch-add</code> [<var>lswitch</var>]</dt> <dd> Creates a new logical switch named <var>lswitch</var>.  If
+        <var>lswitch</var> is not provided, the switch will not have a
+        name so other commands must refer to this switch by its UUID.
+        Initially the switch will have no ports.
+      </dd>
+
+      <dt><code>lswitch-del</code> <var>lswitch</var></dt>
+      <dd>
+        Deletes <var>lswitch</var>.
+      </dd>
+
+      <dt><code>lswitch-list</code></dt>
+      <dd>
+        Lists all existing switches on standard output, one per line.
+      </dd>
+
+      <dt><code>lswitch-set-external-id</code> <var>lswitch</var> <var>key</var> [<var>value</var>]</dt>
+      <dd>
+        <p>Sets or clears an ``external ID'' value on <var>lswitch</var>.
+        These values are intended to identify entities external to OVN
+        with which <var>lswitch</var> is associated.  The OVN Northbound
+        database schema may specify well-known <var>key</var> values,
+        but <var>key</var> and <var>value</var> are otherwise arbitrary
+        strings.</p>
+
+        <p>If <var>value</var> is specified, then <var>key</var> is set to
+        <var>value</var> for <var>lswitch</var>, overwriting any
+        previous value.  If <var>value</var> is omitted, then
+        <var>key</var> is removed from <var>lswitch</var>'s set of
+        external IDs (if it was present.</p>
+       </dd>
+
+      <dt><code>lswitch-get-external-id</code> <var>lswitch</var> [<var>key</var>]</dt>
+      <dd>
+        Queries the external IDs on <var>lswitch</var>.  If
+        <var>key</var> is specified, the output is the value for that
+        <var>key</var> or the empty string if <var>key</var> is unset.
+        If <var>key</var> is omitted, the output is
+        <var>key</var><code>=</code><var>value</var>, one per line, for
+        each key-value pair.
+      </dd>
+    </dl>
+
+    <h1>Logical Port Commands</h1>
+    <dl>
+      <dt><code>lport-add</code> <var>lswitch</var> <var>lport</var></dt>
+      <dd>
+        Creates on <var>lswitch</var> a new logical port named
+        <var>lport</var>.
+      </dd>
+
+      <dt><code>lport-add</code> <var>lswitch</var> <var>lport</var> <var>parent</var> <var>tag</var></dt>
+      <dd>
+        Creates on <var>lswitch</var> a logical port named <var>lport</var>
+        that is a child of <var>parent</var> that is identied with
+        <var>tag</var>.  This is useful in cases such as virtualized
+        container environments where Open vSwitch does not have a direct
+        connection to the container's port and it must be shared with
+        the virtual machine's port.
+      </dd>
+
+      <dt><code>lport-del</code> <var>lport</var></dt>
+      <dd>
+        Deletes <var>lport</var>.
+      </dd>
+
+      <dt><code>lport-list</code> <var>lswitch</var></dt>
+      <dd>
+        Lists all the logical ports within <var>lswitch</var> on
+        standard output, one per line.
+      </dd>
+
+      <dt><code>lport-get-parent</code> <var>lport</var></dt>
+      <dd>
+        If set, get the parent port of <var>lport</var>.  If not set, print
+        nothing.
+      </dd>
+
+      <dt><code>lport-get-tag</code> <var>lport</var></dt>
+      <dd>
+        If set, get the tag for <var>lport</var> traffic.  If not set, print
+        nothing.
+      </dd>
+
+      <dt><code>lport-set-external-id</code> <var>lport</var> <var>key</var> [<var>value</var>]</dt>
+      <dd>
+        <p>Sets or clears an ``external ID'' value on <var>lport</var>.
+        These values are intended to identify entities external to OVN
+        with which <var>lport</var> is associated.  The OVN Northbound
+        database schema may specify well-known <var>key</var> values,
+        but <var>key</var> and <var>value</var> are otherwise arbitrary
+        strings.</p>
+
+        <p>If <var>value</var> is specified, then <var>key</var> is set to
+        <var>value</var> for <var>lport</var>, overwriting any
+        previous value.  If <var>value</var> is omitted, then
+        <var>key</var> is removed from <var>lport</var>'s set of
+        external IDs (if it was present.</p>
+      </dd>
+
+      <dt><code>lport-get-external-id</code> <var>lport</var> [<var>key</var>]</dt>
+      <dd>
+        Queries the external IDs on <var>lport</var>.  If
+        <var>key</var> is specified, the output is the value for that
+        <var>key</var> or the empty string if <var>key</var> is unset.
+        If <var>key</var> is omitted, the output is
+        <var>key</var><code>=</code><var>value</var>, one per line, for
+        each key-value pair.
+      </dd>
+
+      <dt><code>lport-set-macs</code> <var>lport</var> [<var>mac</var>]...</dt>
+      <dd>
+        Sets the MACs associated with <var>lport</var> to
+        <var>mac</var>.  Multiple MACs may be sets by using multiple
+        <var>mac</var> arguments.  If no <var>mac</var> argument is
+        given, <var>lport</var> will have no MACs associated with it.
+      </dd>
+
+      <dt><code>lport-get-macs</code> <var>lport</var></dt>
+      <dd>
+        Lists all the MACs associated with <var>lport</var> on standard
+        output, one per line.
+      </dd>
+
+      <dt><code>lport-set-port-security</code> <var>lport</var> [<var>addrs</var>]...</dt>
+      <dd>
+       <p>
+          Sets the port security addresses associated with <var>lport</var> to
+          <var>addrs</var>.  Multiple sets of addresses may be set by using
+          multiple <var>addrs</var> arguments.  If no <var>addrs</var> argument
+          is given, <var>lport</var> will not have port security enabled.
+       </p>
+
+       <p>
+         Port security limits the addresses from which a logical port may send
+         packets and to which it may receive packets.  See the
+         <code>ovn-nb</code>(5) documentation for the <ref
+         column="port_security" table="Logical_Port"/> column in the <ref
+         table="Logical_Port"/> table for details.
+       </p>
+      </dd>
+
+      <dt><code>lport-get-port-security</code> <var>lport</var></dt>
+      <dd>
+        Lists all the port security addresses associated with <var>lport</var>
+        on standard output, one per line.
+      </dd>
+
+      <dt><code>lport-get-up</code> <var>lport</var></dt>
+      <dd>
+        Prints the state of <var>lport</var>, either <code>up</code> or
+        <code>down</code>.
+      </dd>
+
+      <dt><code>lport-set-enabled</code> <var>lport</var> <var>state</var></dt>
+      <dd>
+        Set the administrative state of <var>lport</var>, either <code>enabled</code>
+        or <code>disabled</code>.  When a port is disabled, no traffic is allowed into
+        or out of the port.
+      </dd>
+
+      <dt><code>lport-get-enabled</code> <var>lport</var></dt>
+      <dd>
+        Prints the administrative state of <var>lport</var>, either <code>enabled</code>
+        or <code>disabled</code>.
+      </dd>
+
+      <dt><code>lport-set-type</code> <var>lport</var> <var>type</var></dt>
+      <dd>
+        Set the type for the logical port.  No special types have been implemented yet.
+      </dd>
+
+      <dt><code>lport-get-type</code> <var>lport</var></dt>
+      <dd>
+        Get the type for the logical port.
+      </dd>
+
+      <dt><code>lport-set-options</code> <var>lport</var> [<var>key=value</var>]...</dt>
+      <dd>
+        Set type-specific key-value options for the logical port.
+      </dd>
+
+      <dt><code>lport-get-options</code> <var>lport</var></dt>
+      <dd>
+        Get the type-specific options for the logical port.
+      </dd>
+
+    </dl>
+
+    <h1>Options</h1>
+
+    <dl>
+    <dt><code>-d</code> <var>database</var></dt>
+    <dt><code>--db</code> <var>database</var></dt>
+    <dd>
+      The OVSDB database remote to contact.  If the <env>OVN_NB_DB</env>
+      environment variable is set, its value is used as the default.
+      Otherwise, the default is <code>unix:@RUNDIR@/db.sock</code>, but this
+      default is unlikely to be useful outside of single-machine OVN test
+      environments.
+    </dd>
+
+    <dt><code>-h</code> | <code>--help</code></dt>
+    <dt><code>-o</code> | <code>--options</code></dt>
+    <dt><code>-V</code> | <code>--version</code></dt>
+    </dl>
+
+    <h1>Logging options</h1>
+    <dl>
+    <dt><code>-v</code><var>spec</var>, <code>--verbose=</code><var>spec</var></dt>
+    <dt><code>-v</code>, <code>--verbose</code></dt>
+    <dt><code>--log-file</code>[<code>=</code><var>file</var>]</dt>
+    <dt><code>--syslog-target=</code><var>host</var><code>:</code><var>port</var></dt>
+    </dl>
+
+    <h1>PKI configuration (required to use SSL)</h1>
+    <dl>
+    <dt><code>-p</code>, <code>--private-key=</code><var>file</var>  file with private key</dt>
+    <dt><code>-c</code>, <code>--certificate=</code><var>file</var>  file with certificate for private key</dt>
+    <dt><code>-C</code>, <code>--ca-cert=</code><var>file</var>      file with peer CA certificate</dt>
+    </dl>
+
+</manpage>
diff --git a/ovn/utilities/ovn-nbctl.c b/ovn/utilities/ovn-nbctl.c
new file mode 100644 (file)
index 0000000..0bdb3a3
--- /dev/null
@@ -0,0 +1,1032 @@
+/*
+ * 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 <inttypes.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "command-line.h"
+#include "dirs.h"
+#include "fatal-signal.h"
+#include "ovn/lib/ovn-nb-idl.h"
+#include "poll-loop.h"
+#include "process.h"
+#include "stream.h"
+#include "stream-ssl.h"
+#include "util.h"
+#include "openvswitch/vlog.h"
+
+VLOG_DEFINE_THIS_MODULE(ovn_nbctl);
+
+struct nbctl_context {
+    struct ovsdb_idl *idl;
+    struct ovsdb_idl_txn *txn;
+};
+
+static const char *db;
+
+static const char *default_db(void);
+
+static void
+usage(void)
+{
+    printf("\
+%s: OVN northbound DB management utility\n\
+usage: %s [OPTIONS] COMMAND [ARG...]\n\
+\n\
+General commands:\n\
+  show                      print overview of database contents\n\
+  show LSWITCH              print overview of database contents for LSWITCH\n\
+\n\
+Logical switch commands:\n\
+  lswitch-add [LSWITCH]     create a logical switch named LSWITCH\n\
+  lswitch-del LSWITCH       delete LSWITCH and all its ports\n\
+  lswitch-list              print the names of all logical switches\n\
+  lswitch-set-external-id LSWITCH KEY [VALUE]\n\
+                            set or delete an external-id on LSWITCH\n\
+  lswitch-get-external-id LSWITCH [KEY]\n\
+                            list one or all external-ids on LSWITCH\n\
+\n\
+Logical port commands:\n\
+  lport-add LSWITCH LPORT   add logical port LPORT on LSWITCH\n\
+  lport-add LSWITCH LPORT PARENT TAG\n\
+                            add logical port LPORT on LSWITCH with PARENT\n\
+                            on TAG\n\
+  lport-del LPORT           delete LPORT from its attached switch\n\
+  lport-list LSWITCH        print the names of all logical ports on LSWITCH\n\
+  lport-get-parent LPORT    get the parent of LPORT if set\n\
+  lport-get-tag LPORT       get the LPORT's tag if set\n\
+  lport-set-external-id LPORT KEY [VALUE]\n\
+                            set or delete an external-id on LPORT\n\
+  lport-get-external-id LPORT [KEY]\n\
+                            list one or all external-ids on LPORT\n\
+  lport-set-macs LPORT [MAC]...\n\
+                            set MAC addresses for LPORT.\n\
+  lport-get-macs LPORT      get a list of MAC addresses on LPORT\n\
+  lport-set-port-security LPORT [ADDRS]...\n\
+                            set port security addresses for LPORT.\n\
+  lport-get-port-security LPORT    get LPORT's port security addresses\n\
+  lport-get-up LPORT        get state of LPORT ('up' or 'down')\n\
+  lport-set-enabled LPORT STATE\n\
+                            set administrative state LPORT\n\
+                            ('enabled' or 'disabled')\n\
+  lport-get-enabled LPORT   get administrative state LPORT\n\
+                            ('enabled' or 'disabled')\n\
+  lport-set-type LPORT TYPE Set the type for LPORT\n\
+  lport-get-type LPORT      Get the type for LPORT\n\
+  lport-set-options LPORT KEY=VALUE [KEY=VALUE]...\n\
+                            Set options related to the type of LPORT\n\
+  lport-get-options LPORT   Get the type specific options for LPORT\n\
+\n\
+Options:\n\
+  --db=DATABASE             connect to DATABASE\n\
+                            (default: %s)\n\
+  -h, --help                display this help message\n\
+  -o, --options             list available options\n\
+  -V, --version             display version information\n\
+", program_name, program_name, default_db());
+    vlog_usage();
+    stream_usage("database", true, true, false);
+}
+\f
+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;
+    bool is_uuid = false;
+    bool duplicate = false;
+    struct uuid lswitch_uuid;
+
+    if (uuid_from_string(&lswitch_uuid, id)) {
+        is_uuid = true;
+        lswitch = nbrec_logical_switch_get_for_uuid(nb_ctx->idl,
+                                                    &lswitch_uuid);
+    }
+
+    if (!lswitch) {
+        const struct nbrec_logical_switch *iter;
+
+        NBREC_LOGICAL_SWITCH_FOR_EACH(iter, nb_ctx->idl) {
+            if (strcmp(iter->name, id)) {
+                continue;
+            }
+            if (lswitch) {
+                VLOG_WARN("There is more than one logical switch named '%s'. "
+                        "Use a UUID.", id);
+                lswitch = NULL;
+                duplicate = true;
+                break;
+            }
+            lswitch = iter;
+        }
+    }
+
+    if (!lswitch && !duplicate) {
+        VLOG_WARN("lswitch not found for %s: '%s'",
+                is_uuid ? "UUID" : "name", id);
+    }
+
+    return lswitch;
+}
+
+static void
+print_lswitch(const struct nbrec_logical_switch *lswitch)
+{
+    printf("    lswitch "UUID_FMT" (%s)\n",
+           UUID_ARGS(&lswitch->header_.uuid), lswitch->name);
+
+    for (size_t i = 0; i < lswitch->n_ports; i++) {
+        const struct nbrec_logical_port *lport = lswitch->ports[i];
+
+        printf("        lport %s\n", lport->name);
+        if (lport->parent_name && lport->n_tag) {
+            printf("            parent: %s, tag:%"PRIu64"\n",
+                   lport->parent_name, lport->tag[0]);
+        }
+        if (lport->n_macs) {
+            printf("            macs:");
+            for (size_t j = 0; j < lport->n_macs; j++) {
+                printf(" %s", lport->macs[j]);
+            }
+            printf("\n");
+        }
+    }
+}
+
+static void
+do_show(struct ovs_cmdl_context *ctx)
+{
+    struct nbctl_context *nb_ctx = ctx->pvt;
+    const struct nbrec_logical_switch *lswitch;
+
+    if (ctx->argc == 2) {
+        lswitch = lswitch_by_name_or_uuid(nb_ctx, ctx->argv[1]);
+        if (lswitch) {
+            print_lswitch(lswitch);
+        }
+    } else {
+        NBREC_LOGICAL_SWITCH_FOR_EACH(lswitch, nb_ctx->idl) {
+            print_lswitch(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)
+{
+    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) {
+            printf("%s\n", value);
+        }
+    } else {
+        struct smap_node *node;
+
+        /* List all external IDs */
+
+        SMAP_FOR_EACH(node, &lswitch->external_ids) {
+            printf("%s=%s\n", node->key, node->value);
+        }
+    }
+}
+\f
+static const struct nbrec_logical_port *
+lport_by_name_or_uuid(struct nbctl_context *nb_ctx, const char *id)
+{
+    const struct nbrec_logical_port *lport = NULL;
+    bool is_uuid = false;
+    struct uuid lport_uuid;
+
+    if (uuid_from_string(&lport_uuid, id)) {
+        is_uuid = true;
+        lport = nbrec_logical_port_get_for_uuid(nb_ctx->idl, &lport_uuid);
+    }
+
+    if (!lport) {
+        NBREC_LOGICAL_PORT_FOR_EACH(lport, nb_ctx->idl) {
+            if (!strcmp(lport->name, id)) {
+                break;
+            }
+        }
+    }
+
+    if (!lport) {
+        VLOG_WARN("lport not found for %s: '%s'",
+                is_uuid ? "UUID" : "name", id);
+    }
+
+    return lport;
+}
+
+static void
+do_lport_add(struct ovs_cmdl_context *ctx)
+{
+    struct nbctl_context *nb_ctx = ctx->pvt;
+    struct nbrec_logical_port *lport;
+    const struct nbrec_logical_switch *lswitch;
+    int64_t tag;
+
+    lswitch = lswitch_by_name_or_uuid(nb_ctx, ctx->argv[1]);
+    if (!lswitch) {
+        return;
+    }
+
+    if (ctx->argc != 3 && ctx->argc != 5) {
+        /* If a parent_name is specified, a tag must be specified as well. */
+        VLOG_WARN("Invalid arguments to lport-add.");
+        return;
+    }
+
+    if (ctx->argc == 5) {
+        /* Validate tag. */
+        if (!ovs_scan(ctx->argv[4], "%"SCNd64, &tag) || tag < 0 || tag > 4095) {
+            VLOG_WARN("Invalid tag '%s'", ctx->argv[4]);
+            return;
+        }
+    }
+
+    /* Create the logical port. */
+    lport = nbrec_logical_port_insert(nb_ctx->txn);
+    nbrec_logical_port_set_name(lport, ctx->argv[2]);
+    if (ctx->argc == 5) {
+        nbrec_logical_port_set_parent_name(lport, ctx->argv[3]);
+        nbrec_logical_port_set_tag(lport, &tag, 1);
+    }
+
+    /* Insert the logical port into the logical switch. */
+    nbrec_logical_switch_verify_ports(lswitch);
+    struct nbrec_logical_port **new_ports = xmalloc(sizeof *new_ports *
+                                                    (lswitch->n_ports + 1));
+    memcpy(new_ports, lswitch->ports, sizeof *new_ports * lswitch->n_ports);
+    new_ports[lswitch->n_ports] = lport;
+    nbrec_logical_switch_set_ports(lswitch, new_ports, lswitch->n_ports + 1);
+    free(new_ports);
+}
+
+/* Removes lport 'lswitch->ports[idx]'. */
+static void
+remove_lport(const struct nbrec_logical_switch *lswitch, size_t idx)
+{
+    const struct nbrec_logical_port *lport = lswitch->ports[idx];
+
+    /* First remove 'lport' from the array of ports.  This is what will
+     * actually cause the logical port to be deleted when the transaction is
+     * sent to the database server (due to garbage collection). */
+    struct nbrec_logical_port **new_ports
+        = xmemdup(lswitch->ports, sizeof *new_ports * lswitch->n_ports);
+    new_ports[idx] = new_ports[lswitch->n_ports - 1];
+    nbrec_logical_switch_verify_ports(lswitch);
+    nbrec_logical_switch_set_ports(lswitch, new_ports, lswitch->n_ports - 1);
+    free(new_ports);
+
+    /* Delete 'lport' from the IDL.  This won't have a real effect on the
+     * database server (the IDL will suppress it in fact) but it means that it
+     * won't show up when we iterate with NBREC_LOGICAL_PORT_FOR_EACH later. */
+    nbrec_logical_port_delete(lport);
+}
+
+static void
+do_lport_del(struct ovs_cmdl_context *ctx)
+{
+    struct nbctl_context *nb_ctx = ctx->pvt;
+    const struct nbrec_logical_port *lport;
+
+    lport = lport_by_name_or_uuid(nb_ctx, ctx->argv[1]);
+    if (!lport) {
+        return;
+    }
+
+    /* Find the switch that contains 'lport', then delete it. */
+    const struct nbrec_logical_switch *lswitch;
+    NBREC_LOGICAL_SWITCH_FOR_EACH (lswitch, nb_ctx->idl) {
+        for (size_t i = 0; i < lswitch->n_ports; i++) {
+            if (lswitch->ports[i] == lport) {
+                remove_lport(lswitch, i);
+                return;
+            }
+        }
+    }
+
+    VLOG_WARN("logical port %s is not part of any logical switch",
+              ctx->argv[1]);
+}
+
+static void
+do_lport_list(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;
+    }
+
+    for (size_t i = 0; i < lswitch->n_ports; i++) {
+        const struct nbrec_logical_port *lport = lswitch->ports[i];
+        printf(UUID_FMT " (%s)\n",
+               UUID_ARGS(&lport->header_.uuid), lport->name);
+    }
+}
+
+static void
+do_lport_get_parent(struct ovs_cmdl_context *ctx)
+{
+    struct nbctl_context *nb_ctx = ctx->pvt;
+    const struct nbrec_logical_port *lport;
+
+    lport = lport_by_name_or_uuid(nb_ctx, ctx->argv[1]);
+    if (!lport) {
+        return;
+    }
+
+    if (lport->parent_name) {
+        printf("%s\n", lport->parent_name);
+    }
+}
+
+static void
+do_lport_get_tag(struct ovs_cmdl_context *ctx)
+{
+    struct nbctl_context *nb_ctx = ctx->pvt;
+    const struct nbrec_logical_port *lport;
+
+    lport = lport_by_name_or_uuid(nb_ctx, ctx->argv[1]);
+    if (!lport) {
+        return;
+    }
+
+    if (lport->n_tag > 0) {
+        printf("%"PRId64"\n", lport->tag[0]);
+    }
+}
+
+static void
+do_lport_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_port *lport;
+    struct smap new_external_ids;
+
+    lport = lport_by_name_or_uuid(nb_ctx, id);
+    if (!lport) {
+        return;
+    }
+
+    smap_init(&new_external_ids);
+    smap_clone(&new_external_ids, &lport->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_port_set_external_ids(lport, &new_external_ids);
+    smap_destroy(&new_external_ids);
+}
+
+static void
+do_lport_get_external_id(struct ovs_cmdl_context *ctx)
+{
+    struct nbctl_context *nb_ctx = ctx->pvt;
+    const char *id = ctx->argv[1];
+    const struct nbrec_logical_port *lport;
+
+    lport = lport_by_name_or_uuid(nb_ctx, id);
+    if (!lport) {
+        return;
+    }
+
+    if (ctx->argc == 3) {
+        const char *key = ctx->argv[2];
+        const char *value;
+
+        /* List one external ID */
+
+        value = smap_get(&lport->external_ids, key);
+        if (value) {
+            printf("%s\n", value);
+        }
+    } else {
+        struct smap_node *node;
+
+        /* List all external IDs */
+
+        SMAP_FOR_EACH(node, &lport->external_ids) {
+            printf("%s=%s\n", node->key, node->value);
+        }
+    }
+}
+
+static void
+do_lport_set_macs(struct ovs_cmdl_context *ctx)
+{
+    struct nbctl_context *nb_ctx = ctx->pvt;
+    const char *id = ctx->argv[1];
+    const struct nbrec_logical_port *lport;
+
+    lport = lport_by_name_or_uuid(nb_ctx, id);
+    if (!lport) {
+        return;
+    }
+
+    nbrec_logical_port_set_macs(lport,
+            (const char **) ctx->argv + 2, ctx->argc - 2);
+}
+
+static void
+do_lport_get_macs(struct ovs_cmdl_context *ctx)
+{
+    struct nbctl_context *nb_ctx = ctx->pvt;
+    const char *id = ctx->argv[1];
+    const struct nbrec_logical_port *lport;
+    size_t i;
+
+    lport = lport_by_name_or_uuid(nb_ctx, id);
+    if (!lport) {
+        return;
+    }
+
+    for (i = 0; i < lport->n_macs; i++) {
+        printf("%s\n", lport->macs[i]);
+    }
+}
+
+static void
+do_lport_set_port_security(struct ovs_cmdl_context *ctx)
+{
+    struct nbctl_context *nb_ctx = ctx->pvt;
+    const char *id = ctx->argv[1];
+    const struct nbrec_logical_port *lport;
+
+    lport = lport_by_name_or_uuid(nb_ctx, id);
+    if (!lport) {
+        return;
+    }
+
+    nbrec_logical_port_set_port_security(lport,
+            (const char **) ctx->argv + 2, ctx->argc - 2);
+}
+
+static void
+do_lport_get_port_security(struct ovs_cmdl_context *ctx)
+{
+    struct nbctl_context *nb_ctx = ctx->pvt;
+    const char *id = ctx->argv[1];
+    const struct nbrec_logical_port *lport;
+    size_t i;
+
+    lport = lport_by_name_or_uuid(nb_ctx, id);
+    if (!lport) {
+        return;
+    }
+
+    for (i = 0; i < lport->n_port_security; i++) {
+        printf("%s\n", lport->port_security[i]);
+    }
+}
+
+static void
+do_lport_get_up(struct ovs_cmdl_context *ctx)
+{
+    struct nbctl_context *nb_ctx = ctx->pvt;
+    const char *id = ctx->argv[1];
+    const struct nbrec_logical_port *lport;
+
+    lport = lport_by_name_or_uuid(nb_ctx, id);
+    if (!lport) {
+        return;
+    }
+
+    printf("%s\n", (lport->up && *lport->up) ? "up" : "down");
+}
+
+static void
+do_lport_set_enabled(struct ovs_cmdl_context *ctx)
+{
+    struct nbctl_context *nb_ctx = ctx->pvt;
+    const char *id = ctx->argv[1];
+    const char *state = ctx->argv[2];
+    const struct nbrec_logical_port *lport;
+
+    lport = lport_by_name_or_uuid(nb_ctx, id);
+    if (!lport) {
+        return;
+    }
+
+    if (!strcasecmp(state, "enabled")) {
+        bool enabled = true;
+        nbrec_logical_port_set_enabled(lport, &enabled, 1);
+    } else if (!strcasecmp(state, "disabled")) {
+        bool enabled = false;
+        nbrec_logical_port_set_enabled(lport, &enabled, 1);
+    } else {
+        VLOG_ERR("Invalid state '%s' provided to lport-set-enabled", state);
+    }
+}
+
+static void
+do_lport_get_enabled(struct ovs_cmdl_context *ctx)
+{
+    struct nbctl_context *nb_ctx = ctx->pvt;
+    const char *id = ctx->argv[1];
+    const struct nbrec_logical_port *lport;
+
+    lport = lport_by_name_or_uuid(nb_ctx, id);
+    if (!lport) {
+        return;
+    }
+
+    printf("%s\n",
+           (!lport->enabled || *lport->enabled) ? "enabled" : "disabled");
+}
+
+static void
+do_lport_set_type(struct ovs_cmdl_context *ctx)
+{
+    struct nbctl_context *nb_ctx = ctx->pvt;
+    const char *id = ctx->argv[1];
+    const char *type = ctx->argv[2];
+    const struct nbrec_logical_port *lport;
+
+    lport = lport_by_name_or_uuid(nb_ctx, id);
+    if (!lport) {
+        return;
+    }
+
+    nbrec_logical_port_set_type(lport, type);
+}
+
+static void
+do_lport_get_type(struct ovs_cmdl_context *ctx)
+{
+    struct nbctl_context *nb_ctx = ctx->pvt;
+    const char *id = ctx->argv[1];
+    const struct nbrec_logical_port *lport;
+
+    lport = lport_by_name_or_uuid(nb_ctx, id);
+    if (!lport) {
+        return;
+    }
+
+    printf("%s\n", lport->type);
+}
+
+static void
+do_lport_set_options(struct ovs_cmdl_context *ctx)
+{
+    struct nbctl_context *nb_ctx = ctx->pvt;
+    const char *id = ctx->argv[1];
+    const struct nbrec_logical_port *lport;
+    size_t i;
+    struct smap options = SMAP_INITIALIZER(&options);
+
+    lport = lport_by_name_or_uuid(nb_ctx, id);
+    if (!lport) {
+        return;
+    }
+
+    for (i = 2; i < ctx->argc; i++) {
+        char *key, *value;
+        value = xstrdup(ctx->argv[i]);
+        key = strsep(&value, "=");
+        if (value) {
+            smap_add(&options, key, value);
+        }
+        free(key);
+    }
+
+    nbrec_logical_port_set_options(lport, &options);
+
+    smap_destroy(&options);
+}
+
+static void
+do_lport_get_options(struct ovs_cmdl_context *ctx)
+{
+    struct nbctl_context *nb_ctx = ctx->pvt;
+    const char *id = ctx->argv[1];
+    const struct nbrec_logical_port *lport;
+    struct smap_node *node;
+
+    lport = lport_by_name_or_uuid(nb_ctx, id);
+    if (!lport) {
+        return;
+    }
+
+    SMAP_FOR_EACH(node, &lport->options) {
+        printf("%s=%s\n", node->key, node->value);
+    }
+}
+\f
+static void
+parse_options(int argc, char *argv[])
+{
+    enum {
+        VLOG_OPTION_ENUMS,
+    };
+    static const struct option long_options[] = {
+        {"db", required_argument, NULL, 'd'},
+        {"help", no_argument, NULL, 'h'},
+        {"options", no_argument, NULL, 'o'},
+        {"version", no_argument, NULL, 'V'},
+        VLOG_LONG_OPTIONS,
+        STREAM_SSL_LONG_OPTIONS,
+        {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) {
+        VLOG_OPTION_HANDLERS;
+        STREAM_SSL_OPTION_HANDLERS;
+
+        case 'd':
+            db = optarg;
+            break;
+
+        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;
+        }
+    }
+
+    if (!db) {
+        db = default_db();
+    }
+
+    free(short_options);
+}
+
+static const struct ovs_cmdl_command all_commands[] = {
+    {
+        .name = "show",
+        .usage = "[LSWITCH]",
+        .min_args = 0,
+        .max_args = 1,
+        .handler = do_show,
+    },
+    {
+        .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 KEY [VALUE]",
+        .min_args = 2,
+        .max_args = 3,
+        .handler = do_lswitch_set_external_id,
+    },
+    {
+        .name = "lswitch-get-external-id",
+        .usage = "LSWITCH [KEY]",
+        .min_args = 1,
+        .max_args = 2,
+        .handler = do_lswitch_get_external_id,
+    },
+    {
+        .name = "lport-add",
+        .usage = "LSWITCH LPORT [PARENT] [TAG]",
+        .min_args = 2,
+        .max_args = 4,
+        .handler = do_lport_add,
+    },
+    {
+        .name = "lport-del",
+        .usage = "LPORT",
+        .min_args = 1,
+        .max_args = 1,
+        .handler = do_lport_del,
+    },
+    {
+        .name = "lport-list",
+        .usage = "LSWITCH",
+        .min_args = 1,
+        .max_args = 1,
+        .handler = do_lport_list,
+    },
+    {
+        .name = "lport-get-parent",
+        .usage = "LPORT",
+        .min_args = 1,
+        .max_args = 1,
+        .handler = do_lport_get_parent,
+    },
+    {
+        .name = "lport-get-tag",
+        .usage = "LPORT",
+        .min_args = 1,
+        .max_args = 1,
+        .handler = do_lport_get_tag,
+    },
+    {
+        .name = "lport-set-external-id",
+        .usage = "LPORT KEY [VALUE]",
+        .min_args = 2,
+        .max_args = 3,
+        .handler = do_lport_set_external_id,
+    },
+    {
+        .name = "lport-get-external-id",
+        .usage = "LPORT [KEY]",
+        .min_args = 1,
+        .max_args = 2,
+        .handler = do_lport_get_external_id,
+    },
+    {
+        .name = "lport-set-macs",
+        .usage = "LPORT [MAC]...",
+        .min_args = 1,
+        /* Accept however many arguments the system will allow. */
+        .max_args = INT_MAX,
+        .handler = do_lport_set_macs,
+    },
+    {
+        .name = "lport-get-macs",
+        .usage = "LPORT",
+        .min_args = 1,
+        .max_args = 1,
+        .handler = do_lport_get_macs,
+    },
+    {
+        .name = "lport-set-port-security",
+        .usage = "LPORT [ADDRS]...",
+        .min_args = 0,
+        /* Accept however many arguments the system will allow. */
+        .max_args = INT_MAX,
+        .handler = do_lport_set_port_security,
+    },
+    {
+        .name = "lport-get-port-security",
+        .usage = "LPORT",
+        .min_args = 1,
+        .max_args = 1,
+        .handler = do_lport_get_port_security,
+    },
+    {
+        .name = "lport-get-up",
+        .usage = "LPORT",
+        .min_args = 1,
+        .max_args = 1,
+        .handler = do_lport_get_up,
+    },
+    {
+        .name = "lport-set-enabled",
+        .usage = "LPORT STATE",
+        .min_args = 2,
+        .max_args = 2,
+        .handler = do_lport_set_enabled,
+    },
+    {
+        .name = "lport-get-enabled",
+        .usage = "LPORT",
+        .min_args = 1,
+        .max_args = 1,
+        .handler = do_lport_get_enabled,
+    },
+    {
+        .name = "lport-set-type",
+        .usage = "LPORT TYPE",
+        .min_args = 2,
+        .max_args = 2,
+        .handler = do_lport_set_type,
+    },
+    {
+        .name = "lport-get-type",
+        .usage = "LPORT",
+        .min_args = 1,
+        .max_args = 1,
+        .handler = do_lport_get_type,
+    },
+    {
+        .name = "lport-set-options",
+        .usage = "LPORT KEY=VALUE [KEY=VALUE]...",
+        .min_args = 1,
+        .max_args = INT_MAX,
+        .handler = do_lport_set_options
+    },
+    {
+        .name = "lport-get-options",
+        .usage = "LPORT",
+        .min_args = 1,
+        .max_args = 1,
+        .handler = do_lport_get_options,
+    },
+
+    {
+        /* sentinel */
+        .name = NULL,
+    },
+};
+
+static const struct ovs_cmdl_command *
+get_all_commands(void)
+{
+    return all_commands;
+}
+
+static const char *
+default_db(void)
+{
+    static char *def;
+    if (!def) {
+        def = getenv("OVN_NB_DB");
+        if (!def) {
+            def = xasprintf("unix:%s/db.sock", ovs_rundir());
+        }
+    }
+    return def;
+}
+
+int
+main(int argc, char *argv[])
+{
+    extern struct vlog_module VLM_reconnect;
+    struct ovs_cmdl_context ctx;
+    struct nbctl_context nb_ctx = { .idl = NULL, };
+    enum ovsdb_idl_txn_status txn_status;
+    unsigned int seqno;
+    int res = 0;
+    char *args;
+
+    fatal_ignore_sigpipe();
+    set_program_name(argv[0]);
+    vlog_set_levels(NULL, VLF_CONSOLE, VLL_WARN);
+    vlog_set_levels(&VLM_reconnect, VLF_ANY_DESTINATION, VLL_WARN);
+    parse_options(argc, argv);
+    nbrec_init();
+
+    args = process_escape_args(argv);
+
+    nb_ctx.idl = ovsdb_idl_create(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);
+            VLOG_ERR("%s: database connection failed (%s)",
+                    db, ovs_retval_to_string(retval));
+            res = 1;
+            break;
+        }
+
+        if (seqno != ovsdb_idl_get_seqno(nb_ctx.idl)) {
+            nb_ctx.txn = ovsdb_idl_txn_create(nb_ctx.idl);
+            ovsdb_idl_txn_add_comment(nb_ctx.txn, "ovn-nbctl: %s", args);
+            ovs_cmdl_run_command(&ctx, get_all_commands());
+            txn_status = ovsdb_idl_txn_commit_block(nb_ctx.txn);
+            if (txn_status == TXN_TRY_AGAIN) {
+                ovsdb_idl_txn_destroy(nb_ctx.txn);
+                nb_ctx.txn = NULL;
+                continue;
+            } else {
+                break;
+            }
+        }
+
+        if (seqno == ovsdb_idl_get_seqno(nb_ctx.idl)) {
+            ovsdb_idl_wait(nb_ctx.idl);
+            poll_block();
+        }
+    }
+
+    if (nb_ctx.txn) {
+        ovsdb_idl_txn_destroy(nb_ctx.txn);
+    }
+    ovsdb_idl_destroy(nb_ctx.idl);
+    free(args);
+
+    exit(res);
+}
index e2bca9c..db469cc 100755 (executable)
@@ -248,7 +248,7 @@ if $built; then
     fi
     PATH=$builddir/ovsdb:$builddir/vswitchd:$builddir/utilities:$PATH
     if $ovn; then
-        PATH=$builddir/ovn:$builddir/ovn/controller:$builddir/ovn/northd:$PATH
+        PATH=$builddir/ovn:$builddir/ovn/controller:$builddir/ovn/northd:$builddir/ovn/utilities:$PATH
     fi
     export PATH
 else
index f77b5d1..2d9d66d 100755 (executable)
@@ -70,7 +70,7 @@ fi
 
 # Put built tools early in $PATH.
 PATH=$sim_builddir/ovsdb:$sim_builddir/vswitchd:$sim_builddir/utilities:$PATH
-PATH=$sim_builddir/ovn:$sim_srcdir/ovn:$sim_builddir/ovn/controller:$sim_builddir/ovn/northd:$PATH
+PATH=$sim_builddir/ovn:$sim_srcdir/ovn:$sim_builddir/ovn/controller:$sim_builddir/ovn/northd:$sim_builddir/ovn/utilities:$PATH
 export PATH
 
 rm -rf sandbox