ovn: Introduce ovn-controller.
authorJustin Pettit <jpettit@nicira.com>
Thu, 9 Apr 2015 07:52:43 +0000 (00:52 -0700)
committerJustin Pettit <jpettit@nicira.com>
Sun, 12 Apr 2015 17:11:06 +0000 (10:11 -0700)
Add new ovn-controller daemon that runs locally on transport nodes.
This initial version registers itself in the Chassis table and registers
logical ports to the appropriate rows in the Bindings table.

Signed-off-by: Justin Pettit <jpettit@nicira.com>
Acked-by: Russell Bryant <rbryant@redhat.com>
15 files changed:
Makefile.am
ovn/.gitignore
ovn/TODO
ovn/automake.mk
ovn/controller/.gitignore [new file with mode: 0644]
ovn/controller/automake.mk [new file with mode: 0644]
ovn/controller/bindings.c [new file with mode: 0644]
ovn/controller/bindings.h [new file with mode: 0644]
ovn/controller/chassis.c [new file with mode: 0644]
ovn/controller/chassis.h [new file with mode: 0644]
ovn/controller/ovn-controller.8.xml [new file with mode: 0644]
ovn/controller/ovn-controller.c [new file with mode: 0644]
ovn/controller/ovn-controller.h [new file with mode: 0644]
ovn/ovn-controller.8.in [deleted file]
tutorial/ovs-sandbox

index 699a580..16d8e8d 100644 (file)
@@ -371,3 +371,4 @@ include vtep/automake.mk
 include datapath-windows/automake.mk
 include datapath-windows/include/automake.mk
 include ovn/automake.mk
+include ovn/controller/automake.mk
index 9ddcbb8..cbd65a1 100644 (file)
@@ -1,5 +1,4 @@
 /ovn-architecture.7
-/ovn-controller.8
 /ovn-nb.5
 /ovn-nb.gv
 /ovn-nb.pic
index 7bd89df..7a22606 100644 (file)
--- a/ovn/TODO
+++ b/ovn/TODO
 
 ** Interaction with Open_vSwitch and OVN databases:
 
-*** Monitor VIFs attached to the integration bridge in Open_vSwitch.
-
-    In response to changes, add or remove corresponding rows in
-    Bindings table in OVN.
-
-*** Populate Chassis row in OVN at startup.  Maintain Chassis row over time.
-
-    (Warn if any other Chassis claims the same IP address.)
-
-*** Remove Chassis and Bindings rows from OVN on exit.
-
 *** Monitor Chassis table in OVN.
 
     Populate Port records for tunnels to other chassis into
 
     Default: VXLAN? Geneve?
 
-*** Location of Open_vSwitch database.
-
-    We can probably use the same default as ovs-vsctl.
-
-*** Location of OVN Southbound database.
-
-    Probably no useful default.
-
 *** SSL configuration.
 
     Can probably get this from Open_vSwitch database.
index 180352e..6388fdc 100644 (file)
@@ -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 ovn/ovn-nbctl.8
-EXTRA_DIST += ovn/ovn-controller.8.in ovn/ovn-architecture.7.xml ovn/ovn-nbctl.8.xml
+man_MANS += ovn/ovn-architecture.7 ovn/ovn-nbctl.8
+EXTRA_DIST += ovn/ovn-architecture.7.xml ovn/ovn-nbctl.8.xml
 
 SUFFIXES += .xml
 %: %.xml
diff --git a/ovn/controller/.gitignore b/ovn/controller/.gitignore
new file mode 100644 (file)
index 0000000..4199a37
--- /dev/null
@@ -0,0 +1,2 @@
+/ovn-controller
+/ovn-controller.8
diff --git a/ovn/controller/automake.mk b/ovn/controller/automake.mk
new file mode 100644 (file)
index 0000000..7274cbd
--- /dev/null
@@ -0,0 +1,11 @@
+bin_PROGRAMS += ovn/controller/ovn-controller
+ovn_controller_ovn_controller_SOURCES = \
+       ovn/controller/bindings.c \
+       ovn/controller/bindings.h \
+       ovn/controller/chassis.c \
+       ovn/controller/chassis.h \
+       ovn/controller/ovn-controller.c \
+       ovn/controller/ovn-controller.h
+ovn_controller_ovn_controller_LDADD = ovn/libovn.la lib/libopenvswitch.la
+man_MANS += ovn/controller/ovn-controller.8
+EXTRA_DIST += ovn/controller/ovn-controller.8.xml
diff --git a/ovn/controller/bindings.c b/ovn/controller/bindings.c
new file mode 100644 (file)
index 0000000..bea4c38
--- /dev/null
@@ -0,0 +1,179 @@
+/* Copyright (c) 2015 Nicira, Inc.
+ *
+ * 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 "bindings.h"
+
+#include "lib/sset.h"
+#include "lib/util.h"
+#include "lib/vswitch-idl.h"
+#include "openvswitch/vlog.h"
+#include "ovn/ovn-sb-idl.h"
+#include "ovn-controller.h"
+
+VLOG_DEFINE_THIS_MODULE(bindings);
+
+#define DEFAULT_BRIDGE_NAME "br-int"
+
+void
+bindings_init(struct controller_ctx *ctx)
+{
+    ovsdb_idl_add_table(ctx->ovs_idl, &ovsrec_table_open_vswitch);
+    ovsdb_idl_add_column(ctx->ovs_idl, &ovsrec_open_vswitch_col_bridges);
+
+    ovsdb_idl_add_table(ctx->ovs_idl, &ovsrec_table_bridge);
+    ovsdb_idl_add_column(ctx->ovs_idl, &ovsrec_bridge_col_name);
+    ovsdb_idl_add_column(ctx->ovs_idl, &ovsrec_bridge_col_ports);
+
+    ovsdb_idl_add_table(ctx->ovs_idl, &ovsrec_table_port);
+    ovsdb_idl_add_column(ctx->ovs_idl, &ovsrec_port_col_name);
+    ovsdb_idl_add_column(ctx->ovs_idl, &ovsrec_port_col_interfaces);
+
+    ovsdb_idl_add_table(ctx->ovs_idl, &ovsrec_table_interface);
+    ovsdb_idl_add_column(ctx->ovs_idl, &ovsrec_interface_col_name);
+    ovsdb_idl_add_column(ctx->ovs_idl, &ovsrec_interface_col_external_ids);
+}
+
+static void
+get_local_iface_ids(struct controller_ctx *ctx, struct sset *lports)
+{
+    const struct ovsrec_open_vswitch *cfg;
+    const struct ovsrec_bridge *bridge_rec;
+    const char *bridge_name;
+    int i;
+
+    cfg = ovsrec_open_vswitch_first(ctx->ovs_idl);
+    if (!cfg) {
+        VLOG_INFO("No Open_vSwitch row defined.");
+        return;
+    }
+
+    bridge_name = smap_get(&cfg->external_ids, "ovn-bridge");
+    if (!bridge_name) {
+        bridge_name = DEFAULT_BRIDGE_NAME;
+    }
+
+    OVSREC_BRIDGE_FOR_EACH(bridge_rec, ctx->ovs_idl) {
+        if (!strcmp(bridge_rec->name, bridge_name)) {
+            break;
+        }
+    }
+
+    if (!bridge_rec) {
+        VLOG_INFO("Could not find bridge '%s'", bridge_name);
+        return;
+    }
+
+    for (i = 0; i < bridge_rec->n_ports; i++) {
+        const struct ovsrec_port *port_rec = bridge_rec->ports[i];
+        const char *iface_id;
+        int j;
+
+        if (!strcmp(port_rec->name, bridge_rec->name)) {
+            continue;
+        }
+
+        for (j = 0; j < port_rec->n_interfaces; j++) {
+            const struct ovsrec_interface *iface_rec;
+
+            iface_rec = port_rec->interfaces[j];
+            iface_id = smap_get(&iface_rec->external_ids, "iface-id");
+            if (!iface_id) {
+                VLOG_DBG("Could not find iface-id for '%s'", iface_rec->name);
+                continue;
+            }
+            sset_add(lports, iface_id);
+        }
+    }
+}
+
+void
+bindings_run(struct controller_ctx *ctx)
+{
+    const struct sbrec_bindings *bindings_rec;
+    struct ovsdb_idl_txn *txn;
+    struct sset lports;
+    const char *name;
+    int retval;
+
+    sset_init(&lports);
+    get_local_iface_ids(ctx, &lports);
+
+    txn = ovsdb_idl_txn_create(ctx->ovnsb_idl);
+    ovsdb_idl_txn_add_comment(txn,
+                              "ovn-controller: updating bindings for '%s'",
+                              ctx->chassis_name);
+
+    SBREC_BINDINGS_FOR_EACH(bindings_rec, ctx->ovnsb_idl) {
+        if (sset_find_and_delete(&lports, bindings_rec->logical_port)) {
+            if (!strcmp(bindings_rec->chassis, ctx->chassis_name)) {
+                continue;
+            }
+            if (bindings_rec->chassis[0]) {
+                VLOG_INFO("Changing chassis for lport %s from %s to %s",
+                          bindings_rec->logical_port, bindings_rec->chassis,
+                          ctx->chassis_name);
+            }
+            sbrec_bindings_set_chassis(bindings_rec, ctx->chassis_name);
+        } else if (!strcmp(bindings_rec->chassis, ctx->chassis_name)) {
+            sbrec_bindings_set_chassis(bindings_rec, "");
+        }
+    }
+
+    retval = ovsdb_idl_txn_commit_block(txn);
+    if (retval == TXN_ERROR) {
+        VLOG_INFO("Problem committing bindings information: %s",
+                  ovsdb_idl_txn_status_to_string(retval));
+    }
+
+    ovsdb_idl_txn_destroy(txn);
+
+    SSET_FOR_EACH (name, &lports) {
+        VLOG_DBG("No binding record for lport %s", name);
+    }
+    sset_destroy(&lports);
+}
+
+void
+bindings_destroy(struct controller_ctx *ctx)
+{
+    int retval = TXN_TRY_AGAIN;
+
+    ovs_assert(ctx->ovnsb_idl);
+
+    while (retval != TXN_SUCCESS && retval != TXN_UNCHANGED) {
+        const struct sbrec_bindings *bindings_rec;
+        struct ovsdb_idl_txn *txn;
+
+        txn = ovsdb_idl_txn_create(ctx->ovnsb_idl);
+        ovsdb_idl_txn_add_comment(txn,
+                              "ovn-controller: removing all bindings for '%s'",
+                              ctx->chassis_name);
+
+        SBREC_BINDINGS_FOR_EACH(bindings_rec, ctx->ovnsb_idl) {
+            if (!strcmp(bindings_rec->chassis, ctx->chassis_name)) {
+                sbrec_bindings_set_chassis(bindings_rec, "");
+            }
+        }
+
+        retval = ovsdb_idl_txn_commit_block(txn);
+        if (retval == TXN_ERROR) {
+            VLOG_INFO("Problem removing bindings: %s",
+                      ovsdb_idl_txn_status_to_string(retval));
+        }
+
+        ovsdb_idl_txn_destroy(txn);
+    }
+}
diff --git a/ovn/controller/bindings.h b/ovn/controller/bindings.h
new file mode 100644 (file)
index 0000000..07681cd
--- /dev/null
@@ -0,0 +1,26 @@
+/* Copyright (c) 2015 Nicira, Inc.
+ *
+ * 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.
+ */
+
+
+#ifndef OVN_BINDINGS_H
+#define OVN_BINDINGS_H 1
+
+struct controller_ctx;
+
+void bindings_init(struct controller_ctx *);
+void bindings_run(struct controller_ctx *);
+void bindings_destroy(struct controller_ctx *);
+
+#endif /* ovn/bindings.h */
diff --git a/ovn/controller/chassis.c b/ovn/controller/chassis.c
new file mode 100644 (file)
index 0000000..2d6e5e6
--- /dev/null
@@ -0,0 +1,157 @@
+/* Copyright (c) 2015 Nicira, Inc.
+ *
+ * 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 "chassis.h"
+
+#include "lib/poll-loop.h"
+#include "lib/util.h"
+#include "lib/vswitch-idl.h"
+#include "openvswitch/vlog.h"
+#include "ovn/ovn-sb-idl.h"
+#include "ovn-controller.h"
+
+VLOG_DEFINE_THIS_MODULE(chassis);
+
+void
+chassis_init(struct controller_ctx *ctx)
+{
+    ovsdb_idl_add_table(ctx->ovs_idl, &ovsrec_table_open_vswitch);
+    ovsdb_idl_add_column(ctx->ovs_idl, &ovsrec_open_vswitch_col_external_ids);
+}
+
+static void
+register_chassis(struct controller_ctx *ctx,
+                 const struct sbrec_chassis *chassis_rec,
+                 const char *encap_type, const char *encap_ip)
+{
+    struct sbrec_encap *encap_rec;
+    int retval = TXN_TRY_AGAIN;
+    struct ovsdb_idl_txn *txn;
+
+    txn = ovsdb_idl_txn_create(ctx->ovnsb_idl);
+    ovsdb_idl_txn_add_comment(txn,
+                              "ovn-controller: registering chassis '%s'",
+                              ctx->chassis_name);
+
+    if (!chassis_rec) {
+        chassis_rec = sbrec_chassis_insert(txn);
+        sbrec_chassis_set_name(chassis_rec, ctx->chassis_name);
+    }
+
+    encap_rec = sbrec_encap_insert(txn);
+
+    sbrec_encap_set_type(encap_rec, encap_type);
+    sbrec_encap_set_ip(encap_rec, encap_ip);
+
+    sbrec_chassis_set_encaps(chassis_rec, &encap_rec, 1);
+
+    retval = ovsdb_idl_txn_commit_block(txn);
+    if (retval != TXN_SUCCESS && retval != TXN_UNCHANGED) {
+        VLOG_INFO("Problem registering chassis: %s",
+                  ovsdb_idl_txn_status_to_string(retval));
+        poll_immediate_wake();
+    }
+    ovsdb_idl_txn_destroy(txn);
+}
+
+void
+chassis_run(struct controller_ctx *ctx)
+{
+    const struct sbrec_chassis *chassis_rec;
+    const struct ovsrec_open_vswitch *cfg;
+    const char *encap_type, *encap_ip;
+    static bool inited = false;
+
+    SBREC_CHASSIS_FOR_EACH(chassis_rec, ctx->ovnsb_idl) {
+        if (!strcmp(chassis_rec->name, ctx->chassis_name)) {
+            break;
+        }
+    }
+
+    /* xxx Need to support more than one encap.  Also need to support
+     * xxx encap options. */
+    cfg = ovsrec_open_vswitch_first(ctx->ovs_idl);
+    if (!cfg) {
+        VLOG_INFO("No Open_vSwitch row defined.");
+        return;
+    }
+
+    encap_type = smap_get(&cfg->external_ids, "ovn-encap-type");
+    encap_ip = smap_get(&cfg->external_ids, "ovn-encap-ip");
+    if (!encap_type || !encap_ip) {
+        VLOG_INFO("Need to specify an encap type and ip");
+        return;
+    }
+
+    if (chassis_rec) {
+        int i;
+
+        for (i = 0; i < chassis_rec->n_encaps; i++) {
+            if (!strcmp(chassis_rec->encaps[i]->type, encap_type)
+                && !strcmp(chassis_rec->encaps[i]->ip, encap_ip)) {
+                /* Nothing changed. */
+                inited = true;
+                return;
+            } else if (!inited) {
+                VLOG_WARN("Chassis config changing on startup, make sure "
+                          "multiple chassis are not configured : %s/%s->%s/%s",
+                          chassis_rec->encaps[i]->type,
+                          chassis_rec->encaps[i]->ip,
+                          encap_type, encap_ip);
+            }
+
+        }
+    }
+
+    register_chassis(ctx, chassis_rec, encap_type, encap_ip);
+    inited = true;
+}
+
+void
+chassis_destroy(struct controller_ctx *ctx)
+{
+    int retval = TXN_TRY_AGAIN;
+
+    ovs_assert(ctx->ovnsb_idl);
+
+    while (retval != TXN_SUCCESS && retval != TXN_UNCHANGED) {
+        const struct sbrec_chassis *chassis_rec;
+        struct ovsdb_idl_txn *txn;
+
+        SBREC_CHASSIS_FOR_EACH(chassis_rec, ctx->ovnsb_idl) {
+            if (!strcmp(chassis_rec->name, ctx->chassis_name)) {
+                break;
+            }
+        }
+
+        if (!chassis_rec) {
+            return;
+        }
+
+        txn = ovsdb_idl_txn_create(ctx->ovnsb_idl);
+        ovsdb_idl_txn_add_comment(txn,
+                                  "ovn-controller: unregistering chassis '%s'",
+                                  ctx->chassis_name);
+        sbrec_chassis_delete(chassis_rec);
+
+        retval = ovsdb_idl_txn_commit_block(txn);
+        if (retval == TXN_ERROR) {
+            VLOG_INFO("Problem unregistering chassis: %s",
+                      ovsdb_idl_txn_status_to_string(retval));
+        }
+        ovsdb_idl_txn_destroy(txn);
+    }
+}
diff --git a/ovn/controller/chassis.h b/ovn/controller/chassis.h
new file mode 100644 (file)
index 0000000..aee701b
--- /dev/null
@@ -0,0 +1,25 @@
+/* Copyright (c) 2015 Nicira, Inc.
+ *
+ * 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.
+ */
+
+#ifndef OVN_CHASSIS_H
+#define OVN_CHASSIS_H 1
+
+struct controller_ctx;
+
+void chassis_init(struct controller_ctx *);
+void chassis_run(struct controller_ctx *);
+void chassis_destroy(struct controller_ctx *);
+
+#endif /* ovn/chassis.h */
diff --git a/ovn/controller/ovn-controller.8.xml b/ovn/controller/ovn-controller.8.xml
new file mode 100644 (file)
index 0000000..ca7fa43
--- /dev/null
@@ -0,0 +1,114 @@
+<?xml version="1.0" encoding="utf-8"?>
+<manpage program="ovn-controller" section="8" title="ovn-controller">
+    <h1>Name</h1>
+    <p>ovn-controller -- Open Virtual Network local controller</p>
+
+    <h1>Synopsis</h1>
+    <p><code>ovn-controller</code> [<var>options</var>] [<var>ovs-database</var>]</p>
+
+    <h1>Description</h1>
+    <p>
+      <code>ovn-controller</code> is the local controller daemon for
+      OVN, the Open Virtual Network.  It connects up to the OVN
+      Southbound database (see <code>ovn-sb</code>(5)) over the OVSDB
+      protocol, and down to the Open vSwitch database (see
+      <code>ovs-vswitchd.conf.db</code>(5)) over the OVSDB protocol and
+      to <code>ovs-vswitchd</code>(8) via OpenFlow.  Each hypervisor and
+      software gateway in an OVN deployment runs its own independent
+      copy of <code>ovn-controller</code>; thus,
+      <code>ovn-controller</code>'s downward connections are
+      machine-local and do not run over a physical network.
+    </p>
+
+    <h1>Configuration</h1>
+    <p>
+      <code>ovn-controller</code> retrieves most of its configuration
+      information from the local Open vSwitch's ovsdb-server instance.
+      The default is the <code>db.sock</code> in local Open vSwitch's
+      "run" directory.  <var>ovs-database</var> must take one of the
+      following forms:
+    </p>
+    <ul>
+      <li>
+        <p>
+          <code>ssl:<var>ip</var>:<var>port</var></code>
+        </p>
+        <p>
+          The specified SSL <var>port</var> on the host at the given
+          <var>ip</var>, which must be expressed as an IP address (not a DNS
+          name) in IPv4 or IPv6 address format.  If <var>ip</var> is an IPv6
+          address, then wrap <var>ip</var> with square brackets, e.g.:
+          <code>ssl:[::1]:6640</code>.  The <code>--private-key</code>,
+          <code>--certificate</code>, and <code>--ca-cert</code> options are
+          mandatory when this form is used.
+        </p>
+      </li>
+      <li>
+        <p>
+          <code>tcp:<var>ip</var>:<var>port</var></code>
+        </p>
+        <p>
+          Connect to the given TCP <var>port</var> on <var>ip</var>, where
+          <var>ip</var> can be IPv4 or IPv6 address. If <var>ip</var> is an
+          IPv6 address, then wrap <var>ip</var> with square brackets, e.g.:
+          <code>tcp:[::1]:6640</code>.
+        </p>
+      </li>
+      <li>
+        <p>
+          <code>unix:<var>file</var></code>
+        </p>
+        <p>
+          On POSIX, connect to the Unix domain server socket named
+          <var>file</var>.
+        </p>
+        <p>
+          On Windows, connect to a localhost TCP port whose value is written
+          in <var>file</var>.
+        </p>
+      </li>
+    </ul>
+    <p>
+      <code>ovn-controller</code> assumes it gets configuration
+      information from the following keys in the <code>Open_vSwitch</code>
+      table of the local OVS instance:
+      <ul>
+        <li>
+          <p>
+            <code>external_ids:system-id</code> specifies the chassis
+            name to use in the Chassis table.
+          </p>
+        </li>
+        <li>
+          <p>
+            <code>external_ids:ovn-bridge</code> specifies the
+            integration bridge to which logical ports are attached.
+            The default is <code>br-int</code>.
+          </p>
+        </li>
+        <li>
+          <p>
+            <code>external_ids:ovn-remote</code> specifies the OVN
+            database that this system should connect to for its
+            configuration.
+          </p>
+        </li>
+        <li>
+          <p>
+            <code>external_ids:ovn-encap-type</code> specifies the
+            encapsulation type that a chassis should use to connect to
+            this node.  Examples include <code>geneve</code>,
+            <code>vxlan</code>, and <code>stt</code>.
+          </p>
+        </li>
+        <li>
+          <p>
+            <code>external_ids:ovn-encap-ip</code> specifies the IP
+            address that a chassis should use to connect to this node
+            using encapsulation type specified by
+            <code>external_ids:ovn-encap-ip</code>.
+          </p>
+        </li>
+      </ul>
+    </p>
+</manpage>
diff --git a/ovn/controller/ovn-controller.c b/ovn/controller/ovn-controller.c
new file mode 100644 (file)
index 0000000..35ab6ed
--- /dev/null
@@ -0,0 +1,294 @@
+/* Copyright (c) 2015 Nicira, Inc.
+ *
+ * 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 <errno.h>
+#include <getopt.h>
+#include <signal.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "command-line.h"
+#include "compiler.h"
+#include "daemon.h"
+#include "dirs.h"
+#include "openvswitch/vconn.h"
+#include "openvswitch/vlog.h"
+#include "ovn/ovn-sb-idl.h"
+#include "poll-loop.h"
+#include "fatal-signal.h"
+#include "lib/vswitch-idl.h"
+#include "smap.h"
+#include "stream.h"
+#include "stream-ssl.h"
+#include "unixctl.h"
+#include "util.h"
+
+#include "ovn-controller.h"
+#include "bindings.h"
+#include "chassis.h"
+
+VLOG_DEFINE_THIS_MODULE(main);
+
+static unixctl_cb_func ovn_controller_exit;
+
+static void parse_options(int argc, char *argv[]);
+OVS_NO_RETURN static void usage(void);
+
+static char *ovs_remote;
+static char *ovnsb_remote;
+
+
+static void
+get_initial_snapshot(struct ovsdb_idl *idl)
+{
+    while (1) {
+        ovsdb_idl_run(idl);
+        if (ovsdb_idl_has_ever_connected(idl)) {
+            return;
+        }
+        ovsdb_idl_wait(idl);
+        poll_block();
+    }
+}
+
+/* Retrieve the OVN remote location from the "external-ids:ovn-remote"
+ * key and the chassis name from the "external-ids:system-id" key in the
+ * Open_vSwitch table of the OVS database instance. */
+static void
+get_core_config(struct controller_ctx *ctx)
+{
+    const struct ovsrec_open_vswitch *cfg;
+
+    cfg = ovsrec_open_vswitch_first(ctx->ovs_idl);
+    if (!cfg) {
+        VLOG_ERR("No Open_vSwitch row defined.");
+        ovsdb_idl_destroy(ctx->ovs_idl);
+        exit(EXIT_FAILURE);
+    }
+
+    while (1) {
+        const char *remote, *system_id;
+
+        ovsdb_idl_run(ctx->ovs_idl);
+
+        /* xxx This does not support changing OVN Southbound OVSDB mid-run. */
+        remote = smap_get(&cfg->external_ids, "ovn-remote");
+        if (!remote) {
+            VLOG_INFO("OVN OVSDB remote not specified.  Waiting...");
+            goto try_again;
+        }
+
+        system_id = smap_get(&cfg->external_ids, "system-id");
+        if (!system_id) {
+            VLOG_INFO("system-id not specified.  Waiting...");
+            goto try_again;
+        }
+
+        ovnsb_remote = xstrdup(remote);
+        ctx->chassis_name = xstrdup(system_id);
+        return;
+
+try_again:
+        ovsdb_idl_wait(ctx->ovs_idl);
+        poll_block();
+    }
+
+}
+
+int
+main(int argc, char *argv[])
+{
+    struct unixctl_server *unixctl;
+    struct controller_ctx ctx = { .chassis_name = NULL };
+    bool exiting;
+    int retval;
+
+    ovs_cmdl_proctitle_init(argc, argv);
+    set_program_name(argv[0]);
+    parse_options(argc, argv);
+    fatal_ignore_sigpipe();
+
+    daemonize_start();
+
+    retval = unixctl_server_create(NULL, &unixctl);
+    if (retval) {
+        exit(EXIT_FAILURE);
+    }
+    unixctl_command_register("exit", "", 0, 0, ovn_controller_exit, &exiting);
+
+    daemonize_complete();
+
+    ovsrec_init();
+    sbrec_init();
+
+    /* Connect to OVS OVSDB instance.  We do not monitor all tables by
+     * default, so modules must register their interest explicitly.  */
+    ctx.ovs_idl = ovsdb_idl_create(ovs_remote, &ovsrec_idl_class, false, true);
+
+    /* Register interest in "external_ids" column in "Open_vSwitch" table,
+     * since we'll need to get the OVN OVSDB remote. */
+    ovsdb_idl_add_table(ctx.ovs_idl, &ovsrec_table_open_vswitch);
+    ovsdb_idl_add_column(ctx.ovs_idl, &ovsrec_open_vswitch_col_external_ids);
+
+    chassis_init(&ctx);
+    bindings_init(&ctx);
+
+    get_initial_snapshot(ctx.ovs_idl);
+
+    get_core_config(&ctx);
+
+    ctx.ovnsb_idl = ovsdb_idl_create(ovnsb_remote, &sbrec_idl_class,
+                                     true, true);
+
+    get_initial_snapshot(ctx.ovnsb_idl);
+
+    exiting = false;
+    while (!exiting) {
+        ovsdb_idl_run(ctx.ovs_idl);
+        ovsdb_idl_run(ctx.ovnsb_idl);
+
+        if (!ovsdb_idl_is_alive(ctx.ovnsb_idl)) {
+            int retval = ovsdb_idl_get_last_error(ctx.ovnsb_idl);
+            VLOG_ERR("%s: database connection failed (%s)",
+                     ovnsb_remote, ovs_retval_to_string(retval));
+            retval = EXIT_FAILURE;
+            break;
+        }
+
+        if (!ovsdb_idl_is_alive(ctx.ovs_idl)) {
+            int retval = ovsdb_idl_get_last_error(ctx.ovs_idl);
+            VLOG_ERR("%s: database connection failed (%s)",
+                     ovs_remote, ovs_retval_to_string(retval));
+            retval = EXIT_FAILURE;
+            break;
+        }
+
+        chassis_run(&ctx);
+        bindings_run(&ctx);
+        unixctl_server_run(unixctl);
+
+        unixctl_server_wait(unixctl);
+        if (exiting) {
+            poll_immediate_wake();
+        }
+
+        ovsdb_idl_wait(ctx.ovs_idl);
+        ovsdb_idl_wait(ctx.ovnsb_idl);
+        poll_block();
+    }
+
+    unixctl_server_destroy(unixctl);
+    bindings_destroy(&ctx);
+    chassis_destroy(&ctx);
+
+    ovsdb_idl_destroy(ctx.ovs_idl);
+    ovsdb_idl_destroy(ctx.ovnsb_idl);
+
+    exit(retval);
+}
+
+static void
+parse_options(int argc, char *argv[])
+{
+    enum {
+        OPT_PEER_CA_CERT = UCHAR_MAX + 1,
+        VLOG_OPTION_ENUMS,
+        DAEMON_OPTION_ENUMS
+    };
+
+    static struct option long_options[] = {
+        {"help", no_argument, NULL, 'h'},
+        {"version", no_argument, NULL, 'V'},
+        VLOG_LONG_OPTIONS,
+        DAEMON_LONG_OPTIONS,
+        STREAM_SSL_LONG_OPTIONS,
+        {"peer-ca-cert", required_argument, NULL, OPT_PEER_CA_CERT},
+        {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();
+
+        case 'V':
+            ovs_print_version(OFP13_VERSION, OFP13_VERSION);
+            exit(EXIT_SUCCESS);
+
+        VLOG_OPTION_HANDLERS
+        DAEMON_OPTION_HANDLERS
+        STREAM_SSL_OPTION_HANDLERS
+
+        case OPT_PEER_CA_CERT:
+            stream_ssl_set_peer_ca_cert_file(optarg);
+            break;
+
+        case '?':
+            exit(EXIT_FAILURE);
+
+        default:
+            abort();
+        }
+    }
+    free(short_options);
+
+    argc -= optind;
+    argv += optind;
+
+    if (argc == 0) {
+        ovs_remote = xasprintf("unix:%s/db.sock", ovs_rundir());
+    } else if (argc == 1) {
+        ovs_remote = argv[0];
+    } else {
+        VLOG_FATAL("exactly zero or one non-option argument required; "
+                   "use --help for usage");
+    }
+}
+
+static void
+usage(void)
+{
+    printf("%s: OVN controller\n"
+           "usage %s [OPTIONS] [OVS-DATABASE]\n"
+           "where OVS-DATABASE is a socket on which the OVS OVSDB server is listening.\n",
+               program_name, program_name);
+    stream_usage("OVS-DATABASE", true, false, false);
+    daemon_usage();
+    vlog_usage();
+    printf("\nOther options:\n"
+           "  -h, --help              display this help message\n"
+           "  -V, --version           display version information\n");
+    exit(EXIT_SUCCESS);
+}
+
+static void
+ovn_controller_exit(struct unixctl_conn *conn, int argc OVS_UNUSED,
+             const char *argv[] OVS_UNUSED, void *exiting_)
+{
+    bool *exiting = exiting_;
+    *exiting = true;
+
+    unixctl_command_reply(conn, NULL);
+}
diff --git a/ovn/controller/ovn-controller.h b/ovn/controller/ovn-controller.h
new file mode 100644 (file)
index 0000000..c701edc
--- /dev/null
@@ -0,0 +1,26 @@
+/* Copyright (c) 2015 Nicira, Inc.
+ *
+ * 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.
+ */
+
+
+#ifndef OVN_CONTROLLER_H
+#define OVN_CONTROLLER_H 1
+
+struct controller_ctx {
+    char *chassis_name;
+    struct ovsdb_idl *ovnsb_idl;
+    struct ovsdb_idl *ovs_idl;
+};
+
+#endif /* ovn/ovn-controller.h */
diff --git a/ovn/ovn-controller.8.in b/ovn/ovn-controller.8.in
deleted file mode 100644 (file)
index 22c18e8..0000000
+++ /dev/null
@@ -1,41 +0,0 @@
-.\" -*- nroff -*-
-.de IQ
-.  br
-.  ns
-.  IP "\\$1"
-..
-.TH ovn\-controller 8 "@VERSION@" "Open vSwitch" "Open vSwitch Manual"
-.ds PN ovn\-controller
-.
-.SH NAME
-ovn\-controller \- OVN local controller
-.
-.SH SYNOPSIS
-\fBovn\-controller\fR [\fIoptions\fR]
-.
-.SH DESCRIPTION
-\fBovn\-controller\fR is the local controller daemon for OVN, the Open
-Virtual Network.  It connects up to the OVN Southbound database (see
-\fBovn\-sb\fR(5)) over the OVSDB protocol, and down to the Open
-vSwitch database (see \fBovs-vswitchd.conf.db\fR(5)) over the OVSDB
-protocol and to \fBovs\-vswitchd\fR(8) via OpenFlow.  Each hypervisor
-and software gateway in an OVN deployment runs its own independent
-copy of \fBovn\-controller\fR; thus, \fBovn\-controller\fR's
-southbound connections are machine-local and do not run over a
-physical network.
-.PP
-XXX this is completely skeletal.
-.
-.SH OPTIONS
-.SS "Public Key Infrastructure Options"
-.so lib/ssl.man
-.so lib/ssl-peer-ca-cert.man
-.ds DD
-.so lib/daemon.man
-.so lib/vlog.man
-.so lib/unixctl.man
-.so lib/common.man
-.
-.SH "SEE ALSO"
-.
-\fBovn\-architecture\fR(7)
index c299de0..9e8ead0 100755 (executable)
@@ -45,6 +45,7 @@ rungdb() {
 gdb_vswitchd=false
 gdb_ovsdb=false
 gdb_ovn_nbd=false
+gdb_ovn_controller=false
 builddir=
 srcdir=
 schema=
@@ -94,6 +95,7 @@ These options force ovs-sandbox to use an installed Open vSwitch:
   -g, --gdb-vswitchd   run ovs-vswitchd under gdb
   -d, --gdb-ovsdb      run ovsdb-server under gdb
   --gdb-ovn-nbd        run ovn-nbd under gdb
+  --gdb-ovn-controller run ovn-controller under gdb
   -S, --schema=FILE    use FILE as vswitch.ovsschema
   -o, --ovn            enable OVN
 
@@ -139,6 +141,9 @@ EOF
         --gdb-ovn-nbd)
             gdb_ovn_nbd=true
             ;;
+        --gdb-ovn-controller)
+            gdb_ovn_controller=true
+            ;;
         -o|--ovn)
             ovn=true
             ;;
@@ -212,7 +217,7 @@ if $built; then
     fi
     PATH=$builddir/ovsdb:$builddir/vswitchd:$builddir/utilities:$PATH
     if $ovn; then
-        PATH=$builddir/ovn:$PATH
+        PATH=$builddir/ovn:$builddir/ovn/controller:$PATH
     fi
     export PATH
 else
@@ -279,7 +284,14 @@ rungdb $gdb_vswitchd ovs-vswitchd --detach --no-chdir --pidfile -vconsole:off --
     --enable-dummy=override -vvconn -vnetdev_dummy
 
 if $ovn; then
+    ovs-vsctl set open . external-ids:system-id=56b18105-5706-46ef-80c4-ff20979ab068
+    ovs-vsctl set open . external-ids:ovn-remote=unix:"$sandbox"/db.sock
+    ovs-vsctl set open . external-ids:ovn-encap-type=vxlan
+    ovs-vsctl set open . external-ids:ovn-encap-ip=127.0.0.1
+    ovs-vsctl add-br br-int
+
     rungdb $gdb_ovn_nbd ovn-nbd --detach --no-chdir --pidfile -vconsole:off --log-file
+    rungdb $gdb_ovn_controller ovn-controller --detach --no-chdir --pidfile -vconsole:off --log-file
 fi
 
 cat <<EOF