Skip to content

Commit

Permalink
ovn-controller: Change encaps_run to work incrementally.
Browse files Browse the repository at this point in the history
As a side effect, tunnel context is persisted.

Signed-off-by: Ryan Moats <[email protected]>
Signed-off-by: Ben Pfaff <[email protected]>
  • Loading branch information
jayhawk87 authored and blp committed Jun 23, 2016
1 parent 74658c8 commit 1d45d5a
Show file tree
Hide file tree
Showing 2 changed files with 192 additions and 42 deletions.
229 changes: 187 additions & 42 deletions ovn/controller/encaps.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@

#include <config.h>
#include "encaps.h"
#include "lflow.h"

#include "lib/hash.h"
#include "lib/sset.h"
Expand Down Expand Up @@ -50,6 +51,9 @@ struct tunnel_ctx {
* remaining in 'tunnel_hmap' must be deleted from the database. */
struct hmap tunnel_hmap;

/* Only valid within the process_full_encaps case in encaps_run(). */
struct hmap tunnel_hmap_by_uuid;

/* Names of all ports in the bridge, to allow checking uniqueness when
* adding a new tunnel. */
struct sset port_names;
Expand All @@ -58,8 +62,18 @@ struct tunnel_ctx {
const struct ovsrec_bridge *br_int;
};

static struct tunnel_ctx tc = {
.tunnel_hmap = HMAP_INITIALIZER(&tc.tunnel_hmap),
.tunnel_hmap_by_uuid = HMAP_INITIALIZER(&tc.tunnel_hmap_by_uuid),
.port_names = SSET_INITIALIZER(&tc.port_names),
};

static bool process_full_encaps = false;

struct port_hash_node {
struct hmap_node node;
struct hmap_node uuid_node;
const struct uuid *uuid;
const struct ovsrec_port *port;
const struct ovsrec_bridge *bridge;
};
Expand Down Expand Up @@ -92,15 +106,15 @@ port_hash_rec(const struct ovsrec_port *port)
}

static char *
tunnel_create_name(struct tunnel_ctx *tc, const char *chassis_id)
tunnel_create_name(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)) {
if (!sset_contains(&tc.port_names, port_name)) {
return port_name;
}

Expand All @@ -110,19 +124,47 @@ tunnel_create_name(struct tunnel_ctx *tc, const char *chassis_id)
return NULL;
}

static struct port_hash_node *
port_lookup_by_uuid(struct hmap *hmap_p, const struct uuid *uuid)
{
struct port_hash_node *answer;
HMAP_FOR_EACH_WITH_HASH (answer, uuid_node, uuid_hash(uuid),
hmap_p) {
if (uuid_equals(uuid, answer->uuid)) {
return answer;
}
}
return NULL;
}

static struct port_hash_node *
port_lookup_by_port(const struct ovsrec_port *port)
{
struct port_hash_node *answer;
HMAP_FOR_EACH_WITH_HASH (answer, node, port_hash_rec(port),
&tc.tunnel_hmap) {
if (port == answer->port) {
return answer;
}
}
return NULL;
}

static void
tunnel_add(struct tunnel_ctx *tc, const char *new_chassis_id,
tunnel_add(const struct sbrec_chassis *chassis_rec,
const struct sbrec_encap *encap)
{
struct port_hash_node *hash_node;
const char *new_chassis_id = chassis_rec->name;

/* Check whether such a row already exists in OVS. If so, update
* the uuid field and insert into the by uuid hashmap. If not,
* create the tunnel. */

/* 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) {
&tc.tunnel_hmap) {
const struct ovsrec_port *port = hash_node->port;
const char *chassis_id = smap_get(&port->external_ids,
"ovn-chassis-id");
Expand All @@ -142,8 +184,13 @@ tunnel_add(struct tunnel_ctx *tc, const char *new_chassis_id,
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);

hash_node->uuid = &chassis_rec->header_.uuid;
if (!port_lookup_by_uuid(&tc.tunnel_hmap_by_uuid,
hash_node->uuid)) {
hmap_insert(&tc.tunnel_hmap_by_uuid, &hash_node->uuid_node,
uuid_hash(hash_node->uuid));
}
return;
}
}
Expand All @@ -155,38 +202,39 @@ tunnel_add(struct tunnel_ctx *tc, const char *new_chassis_id,
char *port_name;
size_t i;

port_name = tunnel_create_name(tc, new_chassis_id);
port_name = tunnel_create_name(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);
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);
smap_add(&options, "key", "flow");
ovsrec_interface_set_options(iface, &options);
smap_destroy(&options);

port = ovsrec_port_insert(tc->ovs_txn);
port = ovsrec_port_insert(tc.ovs_txn);
ovsrec_port_set_name(port, port_name);
ovsrec_port_set_interfaces(port, &iface, 1);
const struct smap id = SMAP_CONST1(&id, "ovn-chassis-id", new_chassis_id);
ovsrec_port_set_external_ids(port, &id);

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 = 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_verify_ports(tc->br_int);
ovsrec_bridge_set_ports(tc->br_int, ports, tc->br_int->n_ports + 1);
ports[tc.br_int->n_ports] = port;
ovsrec_bridge_verify_ports(tc.br_int);
ovsrec_bridge_set_ports(tc.br_int, ports, tc.br_int->n_ports + 1);

sset_add(&tc->port_names, port_name);
sset_add(&tc.port_names, port_name);
free(port_name);
free(ports);
process_full_encaps = true;
}

static void
Expand Down Expand Up @@ -224,6 +272,63 @@ preferred_encap(const struct sbrec_chassis *chassis_rec)
return best_encap;
}

static bool
check_and_add_tunnel(const struct sbrec_chassis *chassis_rec,
const char *chassis_id)
{
if (strcmp(chassis_rec->name, chassis_id)) {
const struct sbrec_encap *encap = preferred_encap(chassis_rec);
if (!encap) {
VLOG_INFO("No supported encaps for '%s'", chassis_rec->name);
return false;
}
tunnel_add(chassis_rec, encap);
return true;
}
return false;
}

static void
check_and_update_tunnel(const struct sbrec_chassis *chassis_rec)
{
struct port_hash_node *port_node;
port_node = port_lookup_by_uuid(&tc.tunnel_hmap_by_uuid,
&chassis_rec->header_.uuid);
if (port_node) {
const struct sbrec_encap *encap = preferred_encap(chassis_rec);
const struct ovsrec_port *port = port_node->port;
const struct ovsrec_interface *iface = port->interfaces[0];
char *port_name = tunnel_create_name(chassis_rec->name);
if (!port_name) {
VLOG_WARN("Unable to allocate unique name for '%s' tunnel",
chassis_rec->name);
return;
}
if (strcmp(encap->type, iface->type)) {
ovsrec_interface_set_type(iface, encap->type);
}
if (strcmp(encap->ip, smap_get(&iface->options, "remote_ip"))) {
struct smap options = SMAP_INITIALIZER(&options);
smap_add(&options, "remote_ip", encap->ip);
smap_add(&options, "key", "flow");
ovsrec_interface_set_options(iface, &options);
smap_destroy(&options);
}

if (strcmp(chassis_rec->name, smap_get(&port->external_ids,
"ovn-chassis-id"))) {
const struct smap id = SMAP_CONST1(&id, "ovn-chassis-id",
chassis_rec->name);
ovsrec_port_set_external_ids(port, &id);
}
} else {
/* This tunnel has been lost and shouldn't have been, so
* warn the operator of that fact. */
VLOG_WARN("Unable to find tunnel for chassis '%s'",
chassis_rec->name);
}
}

void
encaps_run(struct controller_ctx *ctx, const struct ovsrec_bridge *br_int,
const char *chassis_id)
Expand All @@ -235,12 +340,7 @@ encaps_run(struct controller_ctx *ctx, const struct ovsrec_bridge *br_int,
const struct sbrec_chassis *chassis_rec;
const struct ovsrec_bridge *br;

struct tunnel_ctx tc = {
.tunnel_hmap = HMAP_INITIALIZER(&tc.tunnel_hmap),
.port_names = SSET_INITIALIZER(&tc.port_names),
.br_int = br_int
};

tc.br_int = br_int;
tc.ovs_txn = ctx->ovs_idl_txn;
ovsdb_idl_txn_add_comment(tc.ovs_txn,
"ovn-controller: modifying OVS tunnels '%s'",
Expand All @@ -257,36 +357,81 @@ encaps_run(struct controller_ctx *ctx, const struct ovsrec_bridge *br_int,

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);
const char *chassis_id = smap_get(&port->external_ids,
"ovn-chassis-id");
if (chassis_id && !port_lookup_by_port(port)) {
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));
process_full_encaps = true;
}
}
}

SBREC_CHASSIS_FOR_EACH(chassis_rec, ctx->ovnsb_idl) {
if (strcmp(chassis_rec->name, 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);
if (process_full_encaps) {
/* Create tunnels to the other chassis. */
struct hmap keep_tunnel_hmap_by_uuid =
HMAP_INITIALIZER(&keep_tunnel_hmap_by_uuid);
SBREC_CHASSIS_FOR_EACH (chassis_rec, ctx->ovnsb_idl) {
check_and_add_tunnel(chassis_rec, chassis_id);
struct port_hash_node *hash_node = xzalloc(sizeof *hash_node);
hash_node->uuid = &chassis_rec->header_.uuid;
hmap_insert(&keep_tunnel_hmap_by_uuid, &hash_node->uuid_node,
uuid_hash(hash_node->uuid));
}

/* Delete any tunnels that weren't recreated above. */
struct port_hash_node *old_hash_node, *next_hash_node;
HMAP_FOR_EACH_SAFE (old_hash_node, next_hash_node,
node, &tc.tunnel_hmap) {
if (!port_lookup_by_uuid(&keep_tunnel_hmap_by_uuid,
old_hash_node->uuid)) {
bridge_delete_port(old_hash_node->bridge, old_hash_node->port);
sset_find_and_delete(&tc.port_names,
old_hash_node->port->name);
hmap_remove(&tc.tunnel_hmap, &old_hash_node->node);
hmap_remove(&tc.tunnel_hmap_by_uuid,
&old_hash_node->uuid_node);
free(old_hash_node);
}
}
hmap_destroy(&keep_tunnel_hmap_by_uuid);
process_full_encaps = false;
} else {
SBREC_CHASSIS_FOR_EACH_TRACKED (chassis_rec, ctx->ovnsb_idl) {
bool is_deleted = sbrec_chassis_row_get_seqno(chassis_rec,
OVSDB_IDL_CHANGE_DELETE) > 0;
bool is_new = sbrec_chassis_row_get_seqno(chassis_rec,
OVSDB_IDL_CHANGE_MODIFY) == 0;

if (is_deleted) {
/* Lookup the tunnel by row uuid and remove it. */
struct port_hash_node *port_hash =
port_lookup_by_uuid(&tc.tunnel_hmap_by_uuid,
&chassis_rec->header_.uuid);
if (port_hash) {
bridge_delete_port(port_hash->bridge, port_hash->port);
sset_find_and_delete(&tc.port_names,
port_hash->port->name);
hmap_remove(&tc.tunnel_hmap, &port_hash->node);
hmap_remove(&tc.tunnel_hmap_by_uuid,
&port_hash->uuid_node);
free(port_hash);
}
continue;
}
if (!is_new) {
check_and_update_tunnel(chassis_rec);
continue;
} else {
check_and_add_tunnel(chassis_rec, chassis_id);
continue;
}
tunnel_add(&tc, chassis_rec->name, encap);
}
}

/* Delete any existing OVN tunnels that were not still around. */
struct port_hash_node *hash_node;
HMAP_FOR_EACH_POP (hash_node, node, &tc.tunnel_hmap) {
bridge_delete_port(hash_node->bridge, hash_node->port);
free(hash_node);
}
hmap_destroy(&tc.tunnel_hmap);
sset_destroy(&tc.port_names);
}

/* Returns true if the database is all cleaned up, false if more work is
Expand Down
5 changes: 5 additions & 0 deletions ovn/controller/ovn-controller.c
Original file line number Diff line number Diff line change
Expand Up @@ -383,6 +383,10 @@ main(int argc, char *argv[])
char *ovnsb_remote = get_ovnsb_remote(ovs_idl_loop.idl);
struct ovsdb_idl_loop ovnsb_idl_loop = OVSDB_IDL_LOOP_INITIALIZER(
ovsdb_idl_create(ovnsb_remote, &sbrec_idl_class, true, true));

/* Track the southbound idl. */
ovsdb_idl_track_add_all(ovnsb_idl_loop.idl);

ovsdb_idl_get_initial_snapshot(ovnsb_idl_loop.idl);

int probe_interval = 0;
Expand Down Expand Up @@ -494,6 +498,7 @@ main(int argc, char *argv[])
}
ovsdb_idl_loop_commit_and_wait(&ovnsb_idl_loop);
ovsdb_idl_loop_commit_and_wait(&ovs_idl_loop);
ovsdb_idl_track_clear(ovnsb_idl_loop.idl);
poll_block();
if (should_service_stop()) {
exiting = true;
Expand Down

0 comments on commit 1d45d5a

Please sign in to comment.