[ovs-dev] [ovn v2 6/6] ovn-controller: Create tunnels based on Chassis configuration.

Justin Pettit jpettit at nicira.com
Thu Apr 30 06:54:40 UTC 2015


This creates a tunnel to each Chassis's specified Encaps.  In the
future, we may want to limit this to only Chassis that share a logical
datapath on the local system.

Signed-off-by: Justin Pettit <jpettit at nicira.com>
---
 ovn/TODO                 |    9 -
 ovn/controller/chassis.c |  370 +++++++++++++++++++++++++++++++++++++++++-----
 2 files changed, 332 insertions(+), 47 deletions(-)

diff --git a/ovn/TODO b/ovn/TODO
index 209a315..ec05a83 100644
--- a/ovn/TODO
+++ b/ovn/TODO
@@ -78,15 +78,6 @@
     The split pipeline processing split will influence how tunnel keys
     are encoded.
 
-** Interaction with Open_vSwitch and OVN databases:
-
-*** Monitor Chassis table in OVN.
-
-    Populate Port records for tunnels to other chassis into
-    Open_vSwitch database.  As a scale optimization later on, one can
-    populate only records for tunnels to other chassis that have
-    logical networks in common with this one.
-
 *** Monitor Pipeline table in OVN, trigger flow table recomputation on change.
 
 ** ovn-controller parameters and configuration.
diff --git a/ovn/controller/chassis.c b/ovn/controller/chassis.c
index 37287c1..25d588f 100644
--- a/ovn/controller/chassis.c
+++ b/ovn/controller/chassis.c
@@ -16,7 +16,9 @@
 #include <config.h>
 #include "chassis.h"
 
+#include "lib/hash.h"
 #include "lib/poll-loop.h"
+#include "lib/sset.h"
 #include "lib/util.h"
 #include "lib/vswitch-idl.h"
 #include "openvswitch/vlog.h"
@@ -30,50 +32,28 @@ 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);
+    ovsdb_idl_add_table(ctx->ovs_idl, &ovsrec_table_bridge);
+    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_column(ctx->ovs_idl, &ovsrec_port_col_external_ids);
+    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_type);
+    ovsdb_idl_add_column(ctx->ovs_idl, &ovsrec_interface_col_options);
 }
 
 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_id);
-
-    if (!chassis_rec) {
-        chassis_rec = sbrec_chassis_insert(txn);
-        sbrec_chassis_set_name(chassis_rec, ctx->chassis_id);
-    }
-
-    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)
+register_chassis(struct controller_ctx *ctx)
 {
     const struct sbrec_chassis *chassis_rec;
     const struct ovsrec_open_vswitch *cfg;
     const char *encap_type, *encap_ip;
+    struct sbrec_encap *encap_rec;
     static bool inited = false;
+    int retval = TXN_TRY_AGAIN;
+    struct ovsdb_idl_txn *txn;
 
     SBREC_CHASSIS_FOR_EACH(chassis_rec, ctx->ovnsb_idl) {
         if (!strcmp(chassis_rec->name, ctx->chassis_id)) {
@@ -116,10 +96,294 @@ chassis_run(struct controller_ctx *ctx)
         }
     }
 
-    register_chassis(ctx, chassis_rec, encap_type, encap_ip);
+    txn = ovsdb_idl_txn_create(ctx->ovnsb_idl);
+    ovsdb_idl_txn_add_comment(txn,
+                              "ovn-controller: registering chassis '%s'",
+                              ctx->chassis_id);
+
+    if (!chassis_rec) {
+        chassis_rec = sbrec_chassis_insert(txn);
+        sbrec_chassis_set_name(chassis_rec, ctx->chassis_id);
+    }
+
+    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);
+
     inited = true;
 }
 
+/* Enough context to create a new tunnel, using tunnel_add(). */
+struct tunnel_ctx {
+    /* Contains "struct port_hash_node"s.  Used to figure out what
+     * existing tunnels should be deleted: we index all of the OVN encap
+     * rows into this data structure, then as existing rows are
+     * generated we remove them.  After generating all the rows, any
+     * remaining in 'tunnel_hmap' must be deleted from the database. */
+    struct hmap tunnel_hmap;
+
+    /* Names of all ports in the bridge, to allow checking uniqueness when
+     * adding a new tunnel. */
+    struct sset port_names;
+
+    struct ovsdb_idl_txn *ovs_txn;
+    const struct ovsrec_bridge *br_int;
+};
+
+struct port_hash_node {
+    struct hmap_node node;
+    const struct ovsrec_port *port;
+    const struct ovsrec_bridge *bridge;
+};
+
+static size_t
+port_hash(const char *chassis_id, const char *type, const char *ip)
+{
+    size_t hash = hash_string(chassis_id, 0);
+    hash = hash_string(type, hash);
+    return hash_string(ip, hash);
+}
+
+static size_t
+port_hash_rec(const struct ovsrec_port *port)
+{
+    const char *chassis_id, *ip;
+    const struct ovsrec_interface *iface;
+
+    chassis_id = smap_get(&port->external_ids, "ovn-chassis-id");
+
+    if (!chassis_id || !port->n_interfaces) {
+        /* This should not happen for an OVN-created port. */
+        return 0;
+    }
+
+    iface = port->interfaces[0];
+    ip = smap_get(&iface->options, "remote-ip");
+
+    return port_hash(chassis_id, iface->type, ip);
+}
+
+static char *
+tunnel_create_name(struct tunnel_ctx *tc, const char *chassis_id)
+{
+    int i;
+
+    for (i = 0; i < UINT16_MAX; i++) {
+        char *port_name;
+        port_name = xasprintf("ovn-%.6s-%x", chassis_id, i);
+
+        if (!sset_contains(&tc->port_names, port_name)) {
+            return port_name;
+        }
+
+        free(port_name);
+    }
+
+    return NULL;
+}
+
+
+static void
+tunnel_add(struct tunnel_ctx *tc, const char *new_chassis_id,
+           const struct sbrec_encap *encap)
+{
+    struct port_hash_node *hash_node;
+
+    /* Check whether such a row already exists in OVS.  If so, remove it
+     * from 'tc->tunnel_hmap' and we're done. */
+    HMAP_FOR_EACH_WITH_HASH (hash_node, node,
+                             port_hash(new_chassis_id,
+                                       encap->type, encap->ip),
+                             &tc->tunnel_hmap) {
+        const struct ovsrec_port *port = hash_node->port;
+        const char *chassis_id = smap_get(&port->external_ids,
+                                          "ovn-chassis-id");
+        const struct ovsrec_interface *iface;
+        const char *ip;
+
+        if (!chassis_id || !port->n_interfaces) {
+            continue;
+        }
+
+        iface = port->interfaces[0];
+        ip = smap_get(&iface->options, "remote-ip");
+        if (!ip) {
+            continue;
+        }
+
+        if (!strcmp(new_chassis_id, chassis_id)
+            && !strcmp(encap->type, iface->type)
+            && !strcmp(encap->ip, ip)) {
+            hmap_remove(&tc->tunnel_hmap, &hash_node->node);
+            free(hash_node);
+            return;
+        }
+    }
+
+    /* No such port, so add one. */
+    struct smap external_ids = SMAP_INITIALIZER(&external_ids);
+    struct smap options = SMAP_INITIALIZER(&options);
+    struct ovsrec_port *port, **ports;
+    struct ovsrec_interface *iface;
+    char *port_name;
+    size_t i;
+
+    port_name = tunnel_create_name(tc, new_chassis_id);
+    if (!port_name) {
+        VLOG_WARN("Unable to allocate unique name for '%s' tunnel",
+                  new_chassis_id);
+        return;
+    }
+
+    iface = ovsrec_interface_insert(tc->ovs_txn);
+    ovsrec_interface_set_name(iface, port_name);
+    ovsrec_interface_set_type(iface, encap->type);
+    smap_add(&options, "remote-ip", encap->ip);
+    ovsrec_interface_set_options(iface, &options);
+    smap_destroy(&options);
+
+    port = ovsrec_port_insert(tc->ovs_txn);
+    ovsrec_port_set_name(port, port_name);
+    ovsrec_port_set_interfaces(port, &iface, 1);
+    smap_add(&external_ids, "ovn-chassis-id", new_chassis_id);
+    ovsrec_port_set_external_ids(port, &external_ids);
+    smap_destroy(&external_ids);
+
+    ports = xmalloc(sizeof *tc->br_int->ports * (tc->br_int->n_ports + 1));
+    for (i = 0; i < tc->br_int->n_ports; i++) {
+        ports[i] = tc->br_int->ports[i];
+    }
+    ports[tc->br_int->n_ports] = port;
+    ovsrec_bridge_set_ports(tc->br_int, ports, tc->br_int->n_ports + 1);
+
+    sset_add(&tc->port_names, port_name);
+    free(port_name);
+    free(ports);
+}
+
+static void
+bridge_delete_port(const struct ovsrec_bridge *br,
+                   const struct ovsrec_port *port)
+{
+    struct ovsrec_port **ports;
+    size_t i, n;
+
+    ports = xmalloc(sizeof *br->ports * br->n_ports);
+    for (i = n = 0; i < br->n_ports; i++) {
+        if (br->ports[i] != port) {
+            ports[n++] = br->ports[i];
+        }
+    }
+    ovsrec_bridge_set_ports(br, ports, n);
+    free(ports);
+}
+
+static struct sbrec_encap *
+preferred_encap(const struct sbrec_chassis *chassis_rec)
+{
+    size_t i;
+
+    /* For hypervisors, we only support Geneve and STT encapsulations.
+     * Sets are returned alphabetically, so "geneve" will be preferred
+     * over "stt". */
+    for (i = 0; i < chassis_rec->n_encaps; i++) {
+        if (!strcmp(chassis_rec->encaps[i]->type, "geneve")
+                || !strcmp(chassis_rec->encaps[i]->type, "stt")) {
+            return chassis_rec->encaps[i];
+        }
+    }
+
+    return NULL;
+}
+
+static void
+update_encaps(struct controller_ctx *ctx)
+{
+    const struct sbrec_chassis *chassis_rec;
+    const struct ovsrec_bridge *br;
+    int retval;
+
+    struct tunnel_ctx tc = {
+        .tunnel_hmap = HMAP_INITIALIZER(&tc.tunnel_hmap),
+        .port_names = SSET_INITIALIZER(&tc.port_names),
+        .br_int = ctx->br_int
+    };
+
+    tc.ovs_txn = ovsdb_idl_txn_create(ctx->ovs_idl);
+    ovsdb_idl_txn_add_comment(tc.ovs_txn,
+                              "ovn-controller: modifying OVS tunnels '%s'",
+                              ctx->chassis_id);
+
+    /* Collect all port names into tc.port_names.
+     *
+     * Collect all the OVN-created tunnels into tc.tunnel_hmap. */
+    OVSREC_BRIDGE_FOR_EACH(br, ctx->ovs_idl) {
+        size_t i;
+
+        for (i = 0; i < br->n_ports; i++) {
+            const struct ovsrec_port *port = br->ports[i];
+
+            sset_add(&tc.port_names, port->name);
+
+            if (smap_get(&port->external_ids, "ovn-chassis-id")) {
+                struct port_hash_node *hash_node = xzalloc(sizeof *hash_node);
+                hash_node->bridge = br;
+                hash_node->port = port;
+                hmap_insert(&tc.tunnel_hmap, &hash_node->node,
+                            port_hash_rec(port));
+            }
+        }
+    }
+
+    SBREC_CHASSIS_FOR_EACH(chassis_rec, ctx->ovnsb_idl) {
+        if (strcmp(chassis_rec->name, ctx->chassis_id)) {
+            /* Create tunnels to the other chassis. */
+            const struct sbrec_encap *encap = preferred_encap(chassis_rec);
+            if (!encap) {
+                VLOG_INFO("No supported encaps for '%s'", chassis_rec->name);
+                continue;
+            }
+            tunnel_add(&tc, chassis_rec->name, encap);
+        }
+    }
+
+    /* Delete any existing OVN tunnels that were not still around. */
+    struct port_hash_node *hash_node, *next_hash_node;
+    HMAP_FOR_EACH_SAFE (hash_node, next_hash_node, node, &tc.tunnel_hmap) {
+        hmap_remove(&tc.tunnel_hmap, &hash_node->node);
+        bridge_delete_port(hash_node->bridge, hash_node->port);
+        free(hash_node);
+    }
+    hmap_destroy(&tc.tunnel_hmap);
+    sset_destroy(&tc.port_names);
+
+    retval = ovsdb_idl_txn_commit_block(tc.ovs_txn);
+    if (retval != TXN_SUCCESS && retval != TXN_UNCHANGED) {
+        VLOG_INFO("Problem modifying OVS tunnels: %s",
+                  ovsdb_idl_txn_status_to_string(retval));
+        poll_immediate_wake();
+    }
+    ovsdb_idl_txn_destroy(tc.ovs_txn);
+}
+
+void
+chassis_run(struct controller_ctx *ctx)
+{
+    register_chassis(ctx);
+    update_encaps(ctx);
+}
+
 void
 chassis_destroy(struct controller_ctx *ctx)
 {
@@ -138,7 +402,7 @@ chassis_destroy(struct controller_ctx *ctx)
         }
 
         if (!chassis_rec) {
-            return;
+            break;
         }
 
         txn = ovsdb_idl_txn_create(ctx->ovnsb_idl);
@@ -154,4 +418,34 @@ chassis_destroy(struct controller_ctx *ctx)
         }
         ovsdb_idl_txn_destroy(txn);
     }
+
+    retval = TXN_TRY_AGAIN;
+    while (retval != TXN_SUCCESS && retval != TXN_UNCHANGED) {
+        struct ovsrec_port **ports;
+        struct ovsdb_idl_txn *txn;
+        size_t i, n;
+
+        txn = ovsdb_idl_txn_create(ctx->ovs_idl);
+        ovsdb_idl_txn_add_comment(txn,
+                                  "ovn-controller: destroying tunnels");
+
+        /* Delete all the OVS-created tunnels from the integration
+         * bridge. */
+        ports = xmalloc(sizeof *ctx->br_int->ports * ctx->br_int->n_ports);
+        for (i = n = 0; i < ctx->br_int->n_ports; i++) {
+            if (!smap_get(&ctx->br_int->ports[i]->external_ids,
+                          "ovn-chassis-id")) {
+                ports[n++] = ctx->br_int->ports[i];
+            }
+        }
+        ovsrec_bridge_set_ports(ctx->br_int, ports, n);
+        free(ports);
+
+        retval = ovsdb_idl_txn_commit_block(txn);
+        if (retval == TXN_ERROR) {
+            VLOG_INFO("Problem destroying tunnels: %s",
+                      ovsdb_idl_txn_status_to_string(retval));
+        }
+        ovsdb_idl_txn_destroy(txn);
+    }
 }
-- 
1.7.5.4




More information about the dev mailing list