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 <jpet...@nicira.com>
---
 ovn/.gitignore           |    2 +
 ovn/TODO                 |   19 ---
 ovn/automake.mk          |   16 ++-
 ovn/bindings.c           |  181 ++++++++++++++++++++++++++++
 ovn/bindings.h           |   26 ++++
 ovn/chassis.c            |  153 ++++++++++++++++++++++++
 ovn/chassis.h            |   25 ++++
 ovn/ovn-controller.8.in  |   41 -------
 ovn/ovn-controller.8.xml |  122 +++++++++++++++++++
 ovn/ovn-controller.c     |  298 ++++++++++++++++++++++++++++++++++++++++++++++
 10 files changed, 821 insertions(+), 62 deletions(-)
 create mode 100644 ovn/bindings.c
 create mode 100644 ovn/bindings.h
 create mode 100644 ovn/chassis.c
 create mode 100644 ovn/chassis.h
 delete mode 100644 ovn/ovn-controller.8.in
 create mode 100644 ovn/ovn-controller.8.xml
 create mode 100644 ovn/ovn-controller.c

diff --git a/ovn/.gitignore b/ovn/.gitignore
index e354a82..a130e5b 100644
--- a/ovn/.gitignore
+++ b/ovn/.gitignore
@@ -1,7 +1,9 @@
 /ovn.5
 /ovn.gv
+/ovn.ovsidl
 /ovn.pic
 /ovn-architecture.7
+/ovn-controller
 /ovn-controller.8
 /ovn-idl.c
 /ovn-idl.h
diff --git a/ovn/TODO b/ovn/TODO
index 43a867c..9142d73 100644
--- a/ovn/TODO
+++ b/ovn/TODO
@@ -129,17 +129,6 @@
 
 ** 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
@@ -155,14 +144,6 @@
 
     Default: VXLAN? Geneve?
 
-*** Location of Open_vSwitch database.
-
-    We can probably use the same default as ovs-vsctl.
-
-*** Location of OVN database.
-
-    Probably no useful default.
-
 *** SSL configuration.
 
     Can probably get this from Open_vSwitch database.
diff --git a/ovn/automake.mk b/ovn/automake.mk
index 426e547..b37fe2c 100644
--- a/ovn/automake.mk
+++ b/ovn/automake.mk
@@ -66,8 +66,8 @@ ovn/ovn-nb.5: \
                $(srcdir)/ovn/ovn-nb.xml > $@.tmp && \
        mv $@.tmp $@
 
-man_MANS += ovn/ovn-controller.8 ovn/ovn-architecture.7 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
@@ -127,3 +127,15 @@ ovn_ovn_nbctl_LDADD = ovn/libovn.la ovsdb/libovsdb.la 
lib/libopenvswitch.la
 bin_PROGRAMS += ovn/ovn-nbd
 ovn_ovn_nbd_SOURCES = ovn/ovn-nbd.c
 ovn_ovn_nbd_LDADD = ovn/libovn.la ovsdb/libovsdb.la lib/libopenvswitch.la
+
+# ovn-controller
+bin_PROGRAMS += ovn/ovn-controller
+ovn_ovn_controller_SOURCES = \
+       ovn/bindings.c \
+       ovn/bindings.h \
+       ovn/chassis.c \
+       ovn/chassis.h \
+       ovn/ovn-controller.c
+ovn_ovn_controller_LDADD = ovn/libovn.la lib/libopenvswitch.la
+man_MANS += ovn/ovn-controller.8
+EXTRA_DIST += ovn/ovn-controller.8.xml
diff --git a/ovn/bindings.c b/ovn/bindings.c
new file mode 100644
index 0000000..79a19cf
--- /dev/null
+++ b/ovn/bindings.c
@@ -0,0 +1,181 @@
+/* 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 "openvswitch/vlog.h"
+#include "lib/svec.h"
+#include "lib/vswitch-idl.h"
+#include "ovn/ovn-idl.h"
+#include "bindings.h"
+
+VLOG_DEFINE_THIS_MODULE(bindings);
+
+extern char *chassis_name;
+
+void
+bindings_init(struct ovsdb_idl *ovs_idl OVS_UNUSED)
+{
+    ovsdb_idl_add_table(ovs_idl, &ovsrec_table_open_vswitch);
+    ovsdb_idl_add_column(ovs_idl, &ovsrec_open_vswitch_col_bridges);
+
+    ovsdb_idl_add_table(ovs_idl, &ovsrec_table_bridge);
+    ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_name);
+    ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_ports);
+
+    ovsdb_idl_add_table(ovs_idl, &ovsrec_table_port);
+    ovsdb_idl_add_column(ovs_idl, &ovsrec_port_col_name);
+    ovsdb_idl_add_column(ovs_idl, &ovsrec_port_col_interfaces);
+
+    ovsdb_idl_add_table(ovs_idl, &ovsrec_table_interface);
+    ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_name);
+    ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_external_ids);
+}
+
+static void
+mod_binding(const struct ovnrec_bindings *bindings_rec, bool add,
+            struct ovsdb_idl *ovn_idl)
+{
+    struct ovsdb_idl_txn *txn;
+    int retval;
+
+    txn = ovsdb_idl_txn_create(ovn_idl);
+    ovsdb_idl_txn_add_comment(txn, "ovn-controller: %s lport '%s' to '%s'",
+                              add ? "registering" : "unregistering",
+                              bindings_rec->logical_port, chassis_name);
+
+    /* xxx Unclear if ovn-controller or ovn-nbd should be setting
+     * xxx "mac". */
+    ovnrec_bindings_set_chassis(bindings_rec, add ? chassis_name : "");
+
+    retval = ovsdb_idl_txn_commit_block(txn);
+    if (retval != TXN_SUCCESS && retval != TXN_UNCHANGED) {
+        VLOG_INFO("Problem committing bindings information: %s",
+                  ovsdb_idl_txn_status_to_string(retval));
+    }
+    ovsdb_idl_txn_destroy(txn);
+}
+
+static void
+add_binding(const struct ovnrec_bindings *bindings_rec,
+            struct ovsdb_idl *ovn_idl)
+{
+    mod_binding(bindings_rec, true, ovn_idl);
+}
+
+static void
+del_binding(const struct ovnrec_bindings *bindings_rec,
+            struct ovsdb_idl *ovn_idl)
+{
+    mod_binding(bindings_rec, false, ovn_idl);
+}
+
+static void
+get_local_iface_ids(struct svec *lports, struct ovsdb_idl *ovs_idl)
+{
+    const struct ovsrec_open_vswitch *cfg;
+    const struct ovsrec_bridge *bridge_rec;
+    const char *bridge_name;
+    int i;
+
+    cfg = ovsrec_open_vswitch_first(ovs_idl);
+
+    bridge_name = smap_get(&cfg->external_ids, "ovn-bridge");
+    if (!bridge_name) {
+        VLOG_INFO_ONCE("Need to specify the integration bridge");
+        return;
+    }
+
+    OVSREC_BRIDGE_FOR_EACH(bridge_rec, 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;
+            }
+            svec_add(lports, iface_id);
+        }
+    }
+
+    svec_sort(lports);
+}
+
+void
+bindings_run(struct ovsdb_idl *ovn_idl OVS_UNUSED, struct ovsdb_idl *ovs_idl)
+{
+    const struct ovnrec_bindings *bindings_rec;
+    struct svec lports;
+    const char *name;
+    int i;
+
+    svec_init(&lports);
+    get_local_iface_ids(&lports, ovs_idl);
+
+    OVNREC_BINDINGS_FOR_EACH(bindings_rec, ovn_idl) {
+        if (svec_contains(&lports, bindings_rec->logical_port)) {
+            svec_del(&lports, bindings_rec->logical_port);
+
+            if (!strcmp(bindings_rec->chassis, chassis_name)) {
+                continue;
+            }
+            if (strlen(bindings_rec->chassis)) {
+                VLOG_INFO("Changing chassis for lport %s from %s to %s",
+                          bindings_rec->logical_port, bindings_rec->chassis,
+                          chassis_name);
+            }
+            add_binding(bindings_rec, ovn_idl);
+        } else if (!strcmp(bindings_rec->chassis, chassis_name)) {
+            del_binding(bindings_rec, ovn_idl);
+        }
+    }
+    SVEC_FOR_EACH (i, name, &lports) {
+        VLOG_DBG("No binding record for lport %s", name);
+    }
+    svec_destroy(&lports);
+}
+
+void
+bindings_destroy(struct ovsdb_idl *ovn_idl)
+{
+    const struct ovnrec_bindings *bindings_rec;
+
+    OVNREC_BINDINGS_FOR_EACH(bindings_rec, ovn_idl) {
+        if (!strcmp(bindings_rec->chassis, chassis_name)) {
+            del_binding(bindings_rec, ovn_idl);
+        }
+    }
+}
diff --git a/ovn/bindings.h b/ovn/bindings.h
new file mode 100644
index 0000000..ae97ee5
--- /dev/null
+++ b/ovn/bindings.h
@@ -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 ovsdb_idl;
+
+void bindings_init(struct ovsdb_idl *ovs_idl);
+void bindings_run(struct ovsdb_idl *ovn_idl, struct ovsdb_idl *ovs_idl);
+void bindings_destroy(struct ovsdb_idl *ovn_idl);
+
+#endif /* ovn/bindings.h */
diff --git a/ovn/chassis.c b/ovn/chassis.c
new file mode 100644
index 0000000..370d2ac
--- /dev/null
+++ b/ovn/chassis.c
@@ -0,0 +1,153 @@
+/* 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 <string.h>
+#include <stdio.h>
+
+#include "openvswitch/vlog.h"
+#include "lib/vswitch-idl.h"
+#include "ovn/ovn-idl.h"
+#include "compiler.h"
+#include "chassis.h"
+
+VLOG_DEFINE_THIS_MODULE(chassis);
+
+extern char *chassis_name;
+
+void
+chassis_init(struct ovsdb_idl *ovs_idl)
+{
+    ovsdb_idl_add_table(ovs_idl, &ovsrec_table_open_vswitch);
+    ovsdb_idl_add_column(ovs_idl, &ovsrec_open_vswitch_col_external_ids);
+}
+
+static void
+register_chassis(const struct ovnrec_chassis *chassis_rec,
+                 const char *encap_type, const char *encap_ip,
+                 struct ovsdb_idl *ovn_idl)
+{
+    struct ovnrec_encap **encap_recs;
+    struct ovnrec_encap *encap_rec;
+    struct ovsdb_idl_txn *txn;
+    int n_encaps = 1;
+    int retval;
+
+    txn = ovsdb_idl_txn_create(ovn_idl);
+    ovsdb_idl_txn_add_comment(txn,
+                              "ovn-controller: registering chassis '%s'",
+                              chassis_name);
+
+    if (!chassis_rec) {
+        chassis_rec = ovnrec_chassis_insert(txn);
+        ovnrec_chassis_set_name(chassis_rec, chassis_name);
+    }
+
+    encap_rec = ovnrec_encap_insert(txn);
+
+    ovnrec_encap_set_type(encap_rec, encap_type);
+    ovnrec_encap_set_ip(encap_rec, encap_ip);
+
+    encap_recs = xmalloc(sizeof *encap_rec * n_encaps);
+    encap_recs[0] = encap_rec;
+
+    ovnrec_chassis_set_encaps(chassis_rec, encap_recs, 1);
+
+    retval = ovsdb_idl_txn_commit_block(txn);
+    if (retval != TXN_SUCCESS && retval != TXN_UNCHANGED) {
+        VLOG_INFO("Problem committing chassis information: %s",
+                  ovsdb_idl_txn_status_to_string(retval));
+    }
+    ovsdb_idl_txn_destroy(txn);
+}
+
+void
+chassis_run(struct ovsdb_idl *ovn_idl, struct ovsdb_idl *ovs_idl)
+{
+    const struct ovnrec_chassis *chassis_rec;
+    const struct ovsrec_open_vswitch *cfg;
+    const char *encap_type, *encap_ip;
+    static bool inited = false;
+
+    OVNREC_CHASSIS_FOR_EACH(chassis_rec, ovn_idl) {
+        if (!strcmp(chassis_rec->name, chassis_name)) {
+            break;
+        }
+    }
+
+    /* xxx Need to support more than one encap.  Also need to support
+     * xxx encap options. */
+    cfg = ovsrec_open_vswitch_first(ovs_idl);
+    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(chassis_rec, encap_type, encap_ip, ovn_idl);
+    inited = true;
+}
+
+void
+chassis_destroy(struct ovsdb_idl *ovn_idl)
+{
+    const struct ovnrec_chassis *chassis_rec;
+    struct ovsdb_idl_txn *txn;
+    int retval;
+
+    OVNREC_CHASSIS_FOR_EACH(chassis_rec, ovn_idl) {
+        if (!strcmp(chassis_rec->name, chassis_name)) {
+            break;
+        }
+    }
+
+    if (!chassis_rec) {
+        return;
+    }
+
+    txn = ovsdb_idl_txn_create(ovn_idl);
+    ovsdb_idl_txn_add_comment(txn,
+                              "ovn-controller: unregistering chassis '%s'",
+                              chassis_name);
+    ovnrec_chassis_delete(chassis_rec);
+
+    retval = ovsdb_idl_txn_commit_block(txn);
+    if (retval != TXN_SUCCESS && retval != TXN_UNCHANGED) {
+        VLOG_INFO("Problem committing chassis information: %s",
+                  ovsdb_idl_txn_status_to_string(retval));
+    }
+    ovsdb_idl_txn_destroy(txn);
+}
diff --git a/ovn/chassis.h b/ovn/chassis.h
new file mode 100644
index 0000000..acd9af0
--- /dev/null
+++ b/ovn/chassis.h
@@ -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 ovsdb_idl;
+
+void chassis_init(struct ovsdb_idl *ovs_idl);
+void chassis_run(struct ovsdb_idl *ovn_idl, struct ovsdb_idl *ovs_idl);
+void chassis_destroy(struct ovsdb_idl *ovn_idl);
+
+#endif /* ovn/chassis.h */
diff --git a/ovn/ovn-controller.8.in b/ovn/ovn-controller.8.in
deleted file mode 100644
index 59fcb59..0000000
--- a/ovn/ovn-controller.8.in
+++ /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 northbound to the OVN database (see
-\fBovn\fR(5)) over the OVSDB protocol, and southbound 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)
diff --git a/ovn/ovn-controller.8.xml b/ovn/ovn-controller.8.xml
new file mode 100644
index 0000000..54b4dc5
--- /dev/null
+++ b/ovn/ovn-controller.8.xml
@@ -0,0 +1,122 @@
+<?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>Synopsys</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 northbound to the OVN
+      database (see <code>ovn</code>(5)) over the OVSDB protocol, and
+      southbound 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 southbound 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 <code>unix:@RUNDIR@/db.sock</code>.
+      OVS-DATABASE  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.
+          </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>
+
+    <h1>Options</h1>
+    <p><code>-h</code> | <code>--help</code></p>
+    <p><code>-V</code> | <code>--version</code></p>
+
+    <h1>Logging options</h1>
+    <p><code>-v</code><var>spec</var>, 
<code>--verbose=</code><var>spec</var></p>
+    <p><code>-v</code>, <code>--verbose</code></p>
+    <p><code>--log-file</code>[<code>=</code><var>file</var>]</p>
+    
<p><code>--syslog-target=</code><var>host</var><code>:</code><var>port</var></p>
+</manpage>
diff --git a/ovn/ovn-controller.c b/ovn/ovn-controller.c
new file mode 100644
index 0000000..27ee435
--- /dev/null
+++ b/ovn/ovn-controller.c
@@ -0,0 +1,298 @@
+/* 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 "dummy.h"
+#include "poll-loop.h"
+#include "fatal-signal.h"
+#include "smap.h"
+#include "stream.h"
+#include "stream-ssl.h"
+#include "unixctl.h"
+#include "util.h"
+#include "openvswitch/vconn.h"
+#include "openvswitch/vlog.h"
+#include "lib/vswitch-idl.h"
+#include "ovn/ovn-idl.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 *ovn_remote;
+char *chassis_name;
+
+
+static void
+get_initial_snapshot(struct ovsdb_idl *idl)
+{
+    while (1) {
+        ovsdb_idl_run(idl);
+        if (ovsdb_idl_get_seqno(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 ovsdb_idl *ovs_idl)
+{
+    const struct ovsrec_open_vswitch *cfg;
+
+    cfg = ovsrec_open_vswitch_first(ovs_idl);
+
+    while (1) {
+        const char *remote, *system_id;
+
+        ovsdb_idl_run(ovs_idl);
+
+        /* xxx This does not support changing OVN 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;
+        }
+
+        ovn_remote = xstrdup(remote);
+        chassis_name = xstrdup(system_id);
+        return;
+
+try_again:
+        ovsdb_idl_wait(ovs_idl);
+        poll_block();
+    }
+
+}
+
+int
+main(int argc, char *argv[])
+{
+    struct unixctl_server *unixctl;
+    bool exiting;
+    int retval;
+    struct ovsdb_idl *ovn_idl;
+    struct ovsdb_idl *ovs_idl;
+
+    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();
+    ovnrec_init();
+
+    /* Connect to OVS OVSDB instance.  We do not monitor any tables or
+     * columns be default, so modules must register their interest
+     * explicitly.  */
+    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(ovs_idl, &ovsrec_table_open_vswitch);
+    ovsdb_idl_add_column(ovs_idl, &ovsrec_open_vswitch_col_external_ids);
+
+    chassis_init(ovs_idl);
+    bindings_init(ovs_idl);
+
+    get_initial_snapshot(ovs_idl);
+
+    get_core_config(ovs_idl);
+
+    ovn_idl = ovsdb_idl_create(ovn_remote, &ovnrec_idl_class, true, true);
+
+    get_initial_snapshot(ovn_idl);
+
+    exiting = false;
+    while (!exiting) {
+        ovsdb_idl_run(ovs_idl);
+        ovsdb_idl_run(ovn_idl);
+
+        if (!ovsdb_idl_is_alive(ovn_idl)) {
+            int retval = ovsdb_idl_get_last_error(ovn_idl);
+            VLOG_ERR("%s: database connection failed (%s)",
+                     ovn_remote, ovs_retval_to_string(retval));
+            retval = EXIT_FAILURE;
+            break;
+        }
+
+        if (!ovsdb_idl_is_alive(ovs_idl)) {
+            int retval = ovsdb_idl_get_last_error(ovs_idl);
+            VLOG_ERR("%s: database connection failed (%s)",
+                     ovs_remote, ovs_retval_to_string(retval));
+            retval = EXIT_FAILURE;
+            break;
+        }
+
+        chassis_run(ovn_idl, ovs_idl);
+        bindings_run(ovn_idl, ovs_idl);
+        unixctl_server_run(unixctl);
+
+        unixctl_server_wait(unixctl);
+        if (exiting) {
+            poll_immediate_wake();
+        }
+
+        ovsdb_idl_wait(ovs_idl);
+        ovsdb_idl_wait(ovn_idl);
+        poll_block();
+    }
+
+    unixctl_server_destroy(unixctl);
+    bindings_destroy(ovn_idl);
+    chassis_destroy(ovn_idl);
+
+    ovsdb_idl_destroy(ovs_idl);
+    ovsdb_idl_destroy(ovn_idl);
+
+    return retval;
+}
+
+static void
+parse_options(int argc, char *argv[])
+{
+    enum {
+        OPT_PEER_CA_CERT = UCHAR_MAX + 1,
+        VLOG_OPTION_ENUMS,
+        DAEMON_OPTION_ENUMS,
+        OPT_ENABLE_DUMMY
+    };
+
+    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},
+        {"enable-dummy", no_argument, NULL, OPT_ENABLE_DUMMY},
+        {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(OFP10_VERSION, OFP10_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 OPT_ENABLE_DUMMY:
+            timeval_dummy_register();
+            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 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);
+}
-- 
1.7.5.4

_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to