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 <jpet...@nicira.com> --- ovn/TODO | 9 - ovn/controller/chassis.c | 364 +++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 326 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..0148b67 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/shash.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,288 @@ 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 encap, using encap_add(). */ +struct encap_ctx { + /* Contains "struct port_hash_node"s. Used to figure out what + * existing tunnels should be deleted: we index all of the OVN + * tunnel rows into this data structure, then as existing rows are + * generated we remove them. After generarting all the rows, any + * remaing in 'port_hmap' must be deleted from the database. */ + struct hmap port_hmap; + + /* Contains a mapping of names of all ports on the switch, + * regardless of whether they are OVN, to their attached bridge. + * The names are used to make sure that any tunnel port we attempt + * to create is unique on the switch. The bridge record values are + * used when tunnels are removed to know their attachment points. */ + struct shash 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; +}; + +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 * +encap_create_name(struct encap_ctx *ec, 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 (!shash_find(&ec->port_names, port_name)) { + return port_name; + } + + free(port_name); + } + + return NULL; +} + + +static void +encap_add(struct encap_ctx *ec, 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 'ec->port_hmap' and we're done. */ + HMAP_FOR_EACH_WITH_HASH (hash_node, node, + port_hash(new_chassis_id, + encap->type, encap->ip), + &ec->port_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(&ec->port_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 = encap_create_name(ec, new_chassis_id); + if (!port_name) { + VLOG_WARN("Unable to allocate unique name for '%s' encap", + new_chassis_id); + } + + iface = ovsrec_interface_insert(ec->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); + + port = ovsrec_port_insert(ec->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); + + ports = xmalloc(sizeof *ec->br_int->ports * (ec->br_int->n_ports + 1)); + for (i = 0; i < ec->br_int->n_ports; i++) { + ports[i] = ec->br_int->ports[i]; + } + ports[ec->br_int->n_ports] = port; + ovsrec_bridge_set_ports(ec->br_int, ports, ec->br_int->n_ports + 1); + + shash_add(&ec->port_names, port_name, ec->br_int); + 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_tunnel(const struct sbrec_chassis *chassis_rec) +{ + size_t i; + + /* For hypervisors, we only support Geneve and STT encaps. */ + 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 encap_ctx ec = { + .port_hmap = HMAP_INITIALIZER(&ec.port_hmap), + .port_names = SHASH_INITIALIZER(&ec.port_names), + .br_int = ctx->br_int + }; + + ec.ovs_txn = ovsdb_idl_txn_create(ctx->ovs_idl); + ovsdb_idl_txn_add_comment(ec.ovs_txn, + "ovn-controller: modifying OVS encaps '%s'", + ctx->chassis_id); + + 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]; + + shash_add(&ec.port_names, port->name, br); + + if (smap_get(&port->external_ids, "ovn-chassis-id")) { + struct port_hash_node *hash_node = xzalloc(sizeof *hash_node); + hash_node->port = port; + hmap_insert(&ec.port_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_tunnel(chassis_rec); + if (!encap) { + VLOG_INFO("No supported encaps for '%s'", chassis_rec->name); + } + encap_add(&ec, chassis_rec->name, encap); + } + } + + /* Delete any existing OVN encaps that were not still around. */ + struct port_hash_node *hash_node, *next_hash_node; + HMAP_FOR_EACH_SAFE (hash_node, next_hash_node, node, &ec.port_hmap) { + const struct ovsrec_bridge *br = shash_find_data(&ec.port_names, + hash_node->port->name); + hmap_remove(&ec.port_hmap, &hash_node->node); + bridge_delete_port(br, hash_node->port); + free(hash_node); + } + hmap_destroy(&ec.port_hmap); + shash_destroy(&ec.port_names); + + retval = ovsdb_idl_txn_commit_block(ec.ovs_txn); + if (retval != TXN_SUCCESS && retval != TXN_UNCHANGED) { + VLOG_INFO("Problem modifying OVS encaps: %s", + ovsdb_idl_txn_status_to_string(retval)); + poll_immediate_wake(); + } + ovsdb_idl_txn_destroy(ec.ovs_txn); +} + +void +chassis_run(struct controller_ctx *ctx) +{ + register_chassis(ctx); + update_encaps(ctx); +} + void chassis_destroy(struct controller_ctx *ctx) { @@ -138,7 +396,7 @@ chassis_destroy(struct controller_ctx *ctx) } if (!chassis_rec) { - return; + break; } txn = ovsdb_idl_txn_create(ctx->ovnsb_idl); @@ -154,4 +412,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 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev