return retval;
}
+
+struct ovsdb_idl_index *
+ha_chassis_group_index_create(struct ovsdb_idl *idl)
+{
+ return ovsdb_idl_index_create1(idl, &sbrec_ha_chassis_group_col_name);
+}
+
+/* Finds and returns the HA chassis group with the given 'name', or NULL
+ * if no such HA chassis group exists. */
+const struct sbrec_ha_chassis_group *
+ha_chassis_group_lookup_by_name(
+ struct ovsdb_idl_index *sbrec_ha_chassis_grp_by_name,
+ const char *name)
+{
+ struct sbrec_ha_chassis_group *target =
+ sbrec_ha_chassis_group_index_init_row(sbrec_ha_chassis_grp_by_name);
+ sbrec_ha_chassis_group_set_name(target, name);
+
+ struct sbrec_ha_chassis_group *retval =
+ sbrec_ha_chassis_group_index_find(sbrec_ha_chassis_grp_by_name,
+ target);
+
+ sbrec_ha_chassis_group_index_destroy_row(target);
+
+ return retval;
+}
const struct sbrec_chassis *chassis_lookup_by_name(
struct ovsdb_idl_index *sbrec_chassis_by_name, const char *name);
+struct ovsdb_idl_index *ha_chassis_group_index_create(struct ovsdb_idl *idl);
+const struct sbrec_ha_chassis_group *ha_chassis_group_lookup_by_name(
+ struct ovsdb_idl_index *sbrec_ha_chassis_grp_by_name, const char *name);
+
#endif /* ovn/lib/chassis-index.h */
struct ovsdb_idl *ovnsb_idl;
struct ovsdb_idl_txn *ovnnb_txn;
struct ovsdb_idl_txn *ovnsb_txn;
+ struct ovsdb_idl_index *sbrec_ha_chassis_grp_by_name;
};
static const char *ovnnb_db;
struct ovn_port *l3redirect_port;
struct ovn_port *localnet_port;
+ struct ovs_list lr_list; /* In list of logical router datapaths. */
+ /* The logical router group to which this datapath belongs.
+ * Valid only if it is logical router datapath. NULL otherwise. */
+ struct lrouter_group *lr_group;
+
/* Port groups related to the datapath, used only when nbs is NOT NULL. */
struct hmap nb_pgs;
};
+/* A group of logical router datapaths which are connected - either
+ * directly or indirectly.
+ * Each logical router can belong to only one group. */
+struct lrouter_group {
+ struct ovn_datapath **router_dps;
+ int n_router_dps;
+ /* Set of ha_chassis_groups which are associated with the router dps. */
+ struct sset ha_chassis_groups;
+};
+
struct macam_node {
struct hmap_node hmap_node;
struct eth_addr mac_addr; /* Allocated MAC address. */
hmap_init(&od->nb_pgs);
od->port_key_hint = 0;
hmap_insert(datapaths, &od->key_node, uuid_hash(&od->key));
+ od->lr_group = NULL;
return od;
}
static void
join_datapaths(struct northd_context *ctx, struct hmap *datapaths,
struct ovs_list *sb_only, struct ovs_list *nb_only,
- struct ovs_list *both)
+ struct ovs_list *both, struct ovs_list *lr_list)
{
ovs_list_init(sb_only);
ovs_list_init(nb_only);
NULL, nbr, NULL);
ovs_list_push_back(nb_only, &od->list);
}
+ ovs_list_push_back(lr_list, &od->lr_list);
}
}
* Initializes 'datapaths' to contain a "struct ovn_datapath" for every logical
* switch and router. */
static void
-build_datapaths(struct northd_context *ctx, struct hmap *datapaths)
+build_datapaths(struct northd_context *ctx, struct hmap *datapaths,
+ struct ovs_list *lr_list)
{
struct ovs_list sb_only, nb_only, both;
- join_datapaths(ctx, datapaths, &sb_only, &nb_only, &both);
+ join_datapaths(ctx, datapaths, &sb_only, &nb_only, &both, lr_list);
if (!ovs_list_is_empty(&nb_only)) {
/* First index the in-use datapath tunnel IDs. */
return false;
}
+ if (lrp->n_gateway_chassis && !port_binding->ha_chassis_group) {
+ /* If there are gateway chassis in the NB DB, but there is
+ * no corresponding HA chassis group in SB DB we need to
+ * create the HA chassis group in SB DB for this lrp. */
+ return true;
+ }
+
/* These arrays are used to collect valid Gateway_Chassis and valid
* Chassis records from the Logical_Router_Port Gateway_Chassis list,
* we ignore the ones we can't match on the SBDB */
return false;
}
+static struct sbrec_ha_chassis *
+create_sb_ha_chassis(struct northd_context *ctx,
+ const struct sbrec_chassis *chassis, int priority)
+{
+ struct sbrec_ha_chassis *sb_ha_chassis =
+ sbrec_ha_chassis_insert(ctx->ovnsb_txn);
+ sbrec_ha_chassis_set_chassis(sb_ha_chassis, chassis);
+ sbrec_ha_chassis_set_priority(sb_ha_chassis, priority);
+ return sb_ha_chassis;
+}
+
/* This functions translates the gw chassis on the nb database
* to sb database entries, the only difference is that SB database
* Gateway_Chassis table references the chassis directly instead
- * of using the name */
+ * of using the name.
+ *
+ * This function also creates a HA Chassis group in SB DB for
+ * the gateway chassis associated to a distributed gateway
+ * router port in the NB DB.
+ *
+ * An upcoming patch will delete the code to create the Gateway chassis
+ * in SB DB.*/
static void
copy_gw_chassis_from_nbrp_to_sbpb(
struct northd_context *ctx,
struct ovsdb_idl_index *sbrec_chassis_by_name,
const struct nbrec_logical_router_port *lrp,
- const struct sbrec_port_binding *port_binding) {
-
- if (!lrp || !port_binding || !lrp->n_gateway_chassis) {
- return;
- }
-
+ const struct sbrec_port_binding *port_binding)
+{
struct sbrec_gateway_chassis **gw_chassis = NULL;
int n_gwc = 0;
int n;
+ /* Make use of the new HA chassis group table to support HA
+ * for the distributed gateway router port. We can delete
+ * the old gateway_chassis code once ovn-controller supports
+ * HA chassis group. */
+ const struct sbrec_ha_chassis_group *sb_ha_chassis_group =
+ ha_chassis_group_lookup_by_name(
+ ctx->sbrec_ha_chassis_grp_by_name, lrp->name);
+ if (!sb_ha_chassis_group) {
+ sb_ha_chassis_group = sbrec_ha_chassis_group_insert(ctx->ovnsb_txn);
+ sbrec_ha_chassis_group_set_name(sb_ha_chassis_group, lrp->name);
+ }
+
+ struct sbrec_ha_chassis **sb_ha_chassis = xcalloc(lrp->n_gateway_chassis,
+ sizeof *sb_ha_chassis);
+ size_t n_sb_ha_ch = 0;
/* XXX: This can be improved. This code will generate a set of new
* Gateway_Chassis and push them all in a single transaction, instead
* this would be more optimal if we just add/update/remove the rows in
* the southbound db that need to change. We don't expect lots of
* changes to the Gateway_Chassis table, but if that proves to be wrong
- * we should optimize this. */
+ * we should optimize this.
+ *
+ * Note: Remove the below code to add gateway_chassis row in OVN
+ * Southbound db once ovn-controller supports HA chassis group. */
for (n = 0; n < lrp->n_gateway_chassis; n++) {
struct nbrec_gateway_chassis *lrp_gwc = lrp->gateway_chassis[n];
if (!lrp_gwc->chassis_name) {
gw_chassis = xrealloc(gw_chassis, (n_gwc + 1) * sizeof *gw_chassis);
+ /* This code to create gateway_chassis in SB DB needs to be deleted
+ * once ovn-controller supports making use of HA chassis groups. */
struct sbrec_gateway_chassis *pb_gwc =
sbrec_gateway_chassis_insert(ctx->ovnsb_txn);
sbrec_gateway_chassis_set_external_ids(pb_gwc, &lrp_gwc->external_ids);
gw_chassis[n_gwc++] = pb_gwc;
+
+ sb_ha_chassis[n_sb_ha_ch] =
+ create_sb_ha_chassis(ctx, chassis, lrp_gwc->priority);
+ n_sb_ha_ch++;
}
sbrec_port_binding_set_gateway_chassis(port_binding, gw_chassis, n_gwc);
free(gw_chassis);
+
+ sbrec_ha_chassis_group_set_ha_chassis(sb_ha_chassis_group,
+ sb_ha_chassis, n_sb_ha_ch);
+ sbrec_port_binding_set_ha_chassis_group(port_binding, sb_ha_chassis_group);
+ free(sb_ha_chassis);
}
static void
ovn_port_update_sbrec(struct northd_context *ctx,
struct ovsdb_idl_index *sbrec_chassis_by_name,
const struct ovn_port *op,
- struct hmap *chassis_qdisc_queues)
+ struct hmap *chassis_qdisc_queues,
+ struct sset *active_ha_chassis_grps)
{
sbrec_port_binding_set_datapath(op->sb, op->od->sb);
if (op->nbrp) {
op->nbrp, op->sb);
}
+ sset_add(active_ha_chassis_grps, op->nbrp->name);
} else if (redirect_chassis) {
/* Handle ports that had redirect-chassis option attached
* to them, and for backwards compatibility convert them
if (chassis) {
/* If we found the chassis, and the gw chassis on record
* differs from what we expect go ahead and update */
+ char *gwc_name = xasprintf("%s_%s", op->nbrp->name,
+ chassis->name);
if (op->sb->n_gateway_chassis != 1
|| !op->sb->gateway_chassis[0]->chassis
|| strcmp(op->sb->gateway_chassis[0]->chassis->name,
chassis->name)
|| op->sb->gateway_chassis[0]->priority != 0) {
+ /* This code to create gateway_chassis in SB DB needs
+ * to be deleted once ovn-controller supports making
+ * use of HA chassis groups. */
+
/* Construct a single Gateway_Chassis entry on the
* Port_Binding attached to the redirect_chassis
* name */
struct sbrec_gateway_chassis *gw_chassis =
sbrec_gateway_chassis_insert(ctx->ovnsb_txn);
- char *gwc_name = xasprintf("%s_%s", op->nbrp->name,
- chassis->name);
-
/* XXX: Again, here, we could just update an existing
* Gateway_Chassis, instead of creating a new one
* and replacing it */
&op->nbrp->external_ids);
sbrec_port_binding_set_gateway_chassis(op->sb,
&gw_chassis, 1);
- free(gwc_name);
}
+
+ /* Create HA chassis group in SB DB for the
+ * redirect-chassis option. */
+ const struct sbrec_ha_chassis_group *sb_ha_ch_grp;
+ sb_ha_ch_grp = ha_chassis_group_lookup_by_name(
+ ctx->sbrec_ha_chassis_grp_by_name, gwc_name);
+ if (!sb_ha_ch_grp) {
+ sb_ha_ch_grp =
+ sbrec_ha_chassis_group_insert(ctx->ovnsb_txn);
+ sbrec_ha_chassis_group_set_name(sb_ha_ch_grp,
+ gwc_name);
+ }
+
+ if (sb_ha_ch_grp->n_ha_chassis != 1) {
+ struct sbrec_ha_chassis **sb_ha_ch =
+ xcalloc(1, sizeof *sb_ha_ch);
+ sb_ha_ch[0] = create_sb_ha_chassis(ctx, chassis, 0);
+ sbrec_ha_chassis_group_set_ha_chassis(sb_ha_ch_grp,
+ sb_ha_ch, 1);
+ }
+ sbrec_port_binding_set_ha_chassis_group(op->sb,
+ sb_ha_ch_grp);
+ sset_add(active_ha_chassis_grps, gwc_name);
+ free(gwc_name);
} else {
VLOG_WARN("chassis name '%s' from redirect from logical "
" router port '%s' redirect-chassis not found",
}
}
+static void
+cleanup_sb_ha_chassis_groups(struct northd_context *ctx,
+ struct sset *active_ha_chassis_groups)
+{
+ const struct sbrec_ha_chassis_group *b, *n;
+ SBREC_HA_CHASSIS_GROUP_FOR_EACH_SAFE (b, n, ctx->ovnsb_idl) {
+ if (!sset_contains(active_ha_chassis_groups, b->name)) {
+ sbrec_ha_chassis_group_delete(b);
+ }
+ }
+}
+
/* Updates the southbound Port_Binding table so that it contains the logical
* switch ports specified by the northbound database.
*
struct hmap tag_alloc_table = HMAP_INITIALIZER(&tag_alloc_table);
struct hmap chassis_qdisc_queues = HMAP_INITIALIZER(&chassis_qdisc_queues);
+ /* sset which stores the set of ha chassis group names used. */
+ struct sset active_ha_chassis_grps =
+ SSET_INITIALIZER(&active_ha_chassis_grps);
+
join_logical_ports(ctx, datapaths, ports, &chassis_qdisc_queues,
&tag_alloc_table, &sb_only, &nb_only, &both);
tag_alloc_create_new_tag(&tag_alloc_table, op->nbsp);
}
ovn_port_update_sbrec(ctx, sbrec_chassis_by_name,
- op, &chassis_qdisc_queues);
-
+ op, &chassis_qdisc_queues,
+ &active_ha_chassis_grps);
add_tnlid(&op->od->port_tnlids, op->sb->tunnel_key);
if (op->sb->tunnel_key > op->od->port_key_hint) {
op->od->port_key_hint = op->sb->tunnel_key;
op->sb = sbrec_port_binding_insert(ctx->ovnsb_txn);
ovn_port_update_sbrec(ctx, sbrec_chassis_by_name, op,
- &chassis_qdisc_queues);
-
+ &chassis_qdisc_queues,
+ &active_ha_chassis_grps);
sbrec_port_binding_set_logical_port(op->sb, op->key);
sbrec_port_binding_set_tunnel_key(op->sb, tunnel_key);
}
tag_alloc_destroy(&tag_alloc_table);
destroy_chassis_queues(&chassis_qdisc_queues);
+ cleanup_sb_ha_chassis_groups(ctx, &active_ha_chassis_grps);
+ sset_destroy(&active_ha_chassis_grps);
}
\f
#define OVN_MIN_MULTICAST 32768
}
}
+static void
+build_lrouter_groups__(struct hmap *ports, struct ovn_datapath *od)
+{
+ ovs_assert((od && od->nbr && od->lr_group));
+
+ if (od->l3dgw_port && od->l3redirect_port) {
+ /* It's a logical router with gateway port. If it
+ * has HA_Chassis_Group associated to it in SB DB, then store the
+ * ha chassis group name. */
+ if (od->l3redirect_port->sb->ha_chassis_group) {
+ sset_add(&od->lr_group->ha_chassis_groups,
+ od->l3redirect_port->sb->ha_chassis_group->name);
+ }
+ }
+
+ for (size_t i = 0; i < od->nbr->n_ports; i++) {
+ struct ovn_port *router_port =
+ ovn_port_find(ports, od->nbr->ports[i]->name);
+
+ if (!router_port || !router_port->peer) {
+ continue;
+ }
+
+ /* Get the peer logical switch/logical router datapath. */
+ struct ovn_datapath *peer_dp = router_port->peer->od;
+ if (peer_dp->nbr) {
+ if (!peer_dp->lr_group) {
+ peer_dp->lr_group = od->lr_group;
+ od->lr_group->router_dps[od->lr_group->n_router_dps++]
+ = peer_dp;
+ build_lrouter_groups__(ports, peer_dp);
+ }
+ } else {
+ for (size_t j = 0; j < peer_dp->n_router_ports; j++) {
+ if (!peer_dp->router_ports[j]->peer) {
+ /* If there is no peer port connecting to the
+ * router port, ignore it. */
+ continue;
+ }
+
+ struct ovn_datapath *router_dp;
+ router_dp = peer_dp->router_ports[j]->peer->od;
+ if (router_dp == od) {
+ continue;
+ }
+
+ if (router_dp->lr_group == od->lr_group) {
+ /* 'router_dp' and 'od' already belong to the same
+ * lrouter group. Nothing to be done. */
+ continue;
+ }
+
+ router_dp->lr_group = od->lr_group;
+ od->lr_group->router_dps[od->lr_group->n_router_dps++]
+ = router_dp;
+ build_lrouter_groups__(ports, router_dp);
+ }
+ }
+ }
+}
+
+/* Adds each logical router into a logical router group. All the
+ * logical routers which belong to a group are connected to
+ * each other either directly or indirectly (via transit logical switches
+ * in between).
+ *
+ * Suppose if 'lr_list' has lr0, lr1, lr2, lr3, lr4, lr5
+ * and the topology is like
+ * sw0 <-> lr0 <-> sw1 <-> lr1 <->sw2 <-> lr2
+ * sw3 <-> lr3 <-> lr4 <-> sw5
+ * sw6 <-> lr5 <-> sw7
+ * Then 3 groups are created.
+ * Group 1 -> lr0, lr1 and lr2
+ * lr0, lr1 and lr2's ovn_datapath->lr_group will point to this
+ * group. This means sw0's logical ports can send packets to sw2's
+ * logical ports if proper static route's are added.
+ * Group 2 -> lr3 and lr4
+ * lr3 and lr4's ovn_datapath->lr_group will point to this group.
+ * Group 3 -> lr5
+ *
+ * Each logical router can belong to only one group.
+ */
+static void
+build_lrouter_groups(struct hmap *ports, struct ovs_list *lr_list)
+{
+ struct ovn_datapath *od;
+ size_t n_router_dps = ovs_list_size(lr_list);
+
+ LIST_FOR_EACH (od, lr_list, lr_list) {
+ if (!od->lr_group) {
+ od->lr_group = xzalloc(sizeof *od->lr_group);
+ /* Each logical router group can have max
+ * 'n_router_dps'. So allocate enough memory. */
+ od->lr_group->router_dps = xcalloc(sizeof *od, n_router_dps);
+ od->lr_group->router_dps[0] = od;
+ od->lr_group->n_router_dps = 1;
+ sset_init(&od->lr_group->ha_chassis_groups);
+ build_lrouter_groups__(ports, od);
+ }
+ }
+}
+
static void
build_lswitch_flows(struct hmap *datapaths, struct hmap *ports,
struct hmap *port_groups, struct hmap *lflows,
}
\f
static void
-destroy_datapaths_and_ports(struct hmap *datapaths, struct hmap *ports)
+destroy_datapaths_and_ports(struct hmap *datapaths, struct hmap *ports,
+ struct ovs_list *lr_list)
{
+ struct ovn_datapath *router_dp;
+ LIST_FOR_EACH_POP (router_dp, lr_list, lr_list) {
+ if (router_dp->lr_group) {
+ struct lrouter_group *lr_group = router_dp->lr_group;
+
+ for (size_t i = 0; i < lr_group->n_router_dps; i++) {
+ lr_group->router_dps[i]->lr_group = NULL;
+ }
+
+ free(lr_group->router_dps);
+ sset_destroy(&lr_group->ha_chassis_groups);
+ free(lr_group);
+ }
+ }
+
struct ovn_datapath *dp, *next_dp;
HMAP_FOR_EACH_SAFE (dp, next_dp, key_node, datapaths) {
ovn_datapath_destroy(datapaths, dp);
ovnnb_db_run(struct northd_context *ctx,
struct ovsdb_idl_index *sbrec_chassis_by_name,
struct ovsdb_idl_loop *sb_loop,
- struct hmap *datapaths, struct hmap *ports)
+ struct hmap *datapaths, struct hmap *ports,
+ struct ovs_list *lr_list)
{
if (!ctx->ovnsb_txn || !ctx->ovnnb_txn) {
return;
}
struct hmap port_groups;
- build_datapaths(ctx, datapaths);
+
+ build_datapaths(ctx, datapaths, lr_list);
build_ports(ctx, sbrec_chassis_by_name, datapaths, ports);
build_ipam(datapaths, ports);
build_port_group_lswitches(ctx, &port_groups, ports);
+ build_lrouter_groups(ports, lr_list);
build_lflows(ctx, datapaths, ports, &port_groups);
sync_address_sets(ctx);
cleanup_macam(&macam);
}
+/* Stores the list of chassis which references an ha_chassis_group.
+ */
+struct ha_ref_chassis_info {
+ const struct sbrec_ha_chassis_group *ha_chassis_group;
+ struct sbrec_chassis **ref_chassis;
+ size_t n_ref_chassis;
+ size_t free_slots;
+};
+
+static void
+add_to_ha_ref_chassis_info(struct ha_ref_chassis_info *ref_ch_info,
+ const struct sbrec_chassis *chassis)
+{
+ for (size_t j = 0; j < ref_ch_info->n_ref_chassis; j++) {
+ if (ref_ch_info->ref_chassis[j] == chassis) {
+ return;
+ }
+ }
+
+ /* Allocate space for 3 chassis at a time. */
+ if (!ref_ch_info->free_slots) {
+ ref_ch_info->ref_chassis =
+ xrealloc(ref_ch_info->ref_chassis,
+ sizeof *ref_ch_info->ref_chassis *
+ (ref_ch_info->n_ref_chassis + 3));
+ ref_ch_info->free_slots = 3;
+ }
+
+ ref_ch_info->ref_chassis[ref_ch_info->n_ref_chassis] =
+ CONST_CAST(struct sbrec_chassis *, chassis);
+ ref_ch_info->n_ref_chassis++;
+ ref_ch_info->free_slots--;
+}
+
+static void
+update_sb_ha_group_ref_chassis(struct shash *ha_ref_chassis_map)
+{
+ struct shash_node *node, *next;
+ SHASH_FOR_EACH_SAFE (node, next, ha_ref_chassis_map) {
+ struct ha_ref_chassis_info *ha_ref_info = node->data;
+ sbrec_ha_chassis_group_set_ref_chassis(ha_ref_info->ha_chassis_group,
+ ha_ref_info->ref_chassis,
+ ha_ref_info->n_ref_chassis);
+ free(ha_ref_info->ref_chassis);
+ free(ha_ref_info);
+ shash_delete(ha_ref_chassis_map, node);
+ }
+}
+
+/* This function checks if the port binding 'sb' references
+ * a HA chassis group.
+ * Eg. Suppose a distributed logical router port - lr0-public
+ * uses an HA chassis group - hagrp1 and if hagrp1 has 3 ha
+ * chassis - gw1, gw2 and gw3.
+ * Or
+ * If the distributed logical router port - lr0-public has
+ * 3 gateway chassis - gw1, gw2 and gw3.
+ * ovn-northd creates ha chassis group - hagrp1 in SB DB
+ * and adds gw1, gw2 and gw3 to its ha_chassis list.
+ *
+ * If port binding 'sb' represents a logical switch port 'p1'
+ * and its logical switch is connected to the logical router
+ * 'lr0' directly or indirectly (i.e p1's logical switch is
+ * connected to a router 'lr1' and 'lr1' has a path to lr0 via
+ * transit logical switches) and 'sb' is claimed by chassis - 'c1' then
+ * this function adds c1 to the list of the reference chassis
+ * - 'ref_chassis' of hagrp1.
+ */
+static void
+build_ha_chassis_group_ref_chassis(struct northd_context *ctx,
+ const struct sbrec_port_binding *sb,
+ struct ovn_port *op,
+ struct shash *ha_ref_chassis_map)
+{
+ struct lrouter_group *lr_group = NULL;
+ for (size_t i = 0; i < op->od->n_router_ports; i++) {
+ if (!op->od->router_ports[i]->peer) {
+ continue;
+ }
+
+ lr_group = op->od->router_ports[i]->peer->od->lr_group;
+ /* If a logical switch has multiple router ports, then
+ * all the logical routers belong to the same logical
+ * router group. */
+ break;
+ }
+
+ if (!lr_group) {
+ return;
+ }
+
+ const char *ha_group_name;
+ SSET_FOR_EACH (ha_group_name, &lr_group->ha_chassis_groups) {
+ const struct sbrec_ha_chassis_group *sb_ha_chassis_grp;
+ sb_ha_chassis_grp = ha_chassis_group_lookup_by_name(
+ ctx->sbrec_ha_chassis_grp_by_name, ha_group_name);
+
+ if (sb_ha_chassis_grp) {
+ struct ha_ref_chassis_info *ref_ch_info =
+ shash_find_data(ha_ref_chassis_map, sb_ha_chassis_grp->name);
+ ovs_assert(ref_ch_info);
+ add_to_ha_ref_chassis_info(ref_ch_info, sb->chassis);
+ }
+ }
+}
+
/* Handle changes to the 'chassis' column of the 'Port_Binding' table. When
* this column is not empty, it means we need to set the corresponding logical
* port as 'up' in the northbound DB. */
static void
-update_logical_port_status(struct northd_context *ctx, struct hmap *ports)
+handle_port_binding_changes(struct northd_context *ctx, struct hmap *ports,
+ struct shash *ha_ref_chassis_map)
{
const struct sbrec_port_binding *sb;
+ bool build_ha_chassis_ref = false;
+ if (ctx->ovnsb_txn) {
+ const struct sbrec_ha_chassis_group *ha_ch_grp;
+ SBREC_HA_CHASSIS_GROUP_FOR_EACH (ha_ch_grp, ctx->ovnsb_idl) {
+ struct ha_ref_chassis_info *ref_ch_info =
+ xzalloc(sizeof *ref_ch_info);
+ ref_ch_info->ha_chassis_group = ha_ch_grp;
+ build_ha_chassis_ref = true;
+ shash_add(ha_ref_chassis_map, ha_ch_grp->name, ref_ch_info);
+ }
+ }
SBREC_PORT_BINDING_FOR_EACH(sb, ctx->ovnsb_idl) {
struct ovn_port *op = ovn_port_find(ports, sb->logical_port);
if (!op->nbsp->up || *op->nbsp->up != up) {
nbrec_logical_switch_port_set_up(op->nbsp, &up, 1);
}
+
+ if (build_ha_chassis_ref && ctx->ovnsb_txn && sb->chassis) {
+ /* Check and add the chassis which has claimed this 'sb'
+ * to the ha chassis group's ref_chassis if required. */
+ build_ha_chassis_group_ref_chassis(ctx, sb, op,
+ ha_ref_chassis_map);
+ }
}
}
return;
}
- update_logical_port_status(ctx, ports);
+ struct shash ha_ref_chassis_map = SHASH_INITIALIZER(&ha_ref_chassis_map);
+ handle_port_binding_changes(ctx, ports, &ha_ref_chassis_map);
update_northbound_cfg(ctx, sb_loop);
+ if (ctx->ovnsb_txn) {
+ update_sb_ha_group_ref_chassis(&ha_ref_chassis_map);
+ }
+ shash_destroy(&ha_ref_chassis_map);
}
static void
struct ovsdb_idl_loop *ovnsb_idl_loop)
{
struct hmap datapaths, ports;
+ struct ovs_list lr_list;
+ ovs_list_init(&lr_list);
hmap_init(&datapaths);
hmap_init(&ports);
ovnnb_db_run(ctx, sbrec_chassis_by_name, ovnsb_idl_loop,
- &datapaths, &ports);
+ &datapaths, &ports, &lr_list);
ovnsb_db_run(ctx, ovnsb_idl_loop, &ports);
- destroy_datapaths_and_ports(&datapaths, &ports);
+ destroy_datapaths_and_ports(&datapaths, &ports, &lr_list);
}
\f
static void
ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_port_binding_col_chassis);
ovsdb_idl_add_column(ovnsb_idl_loop.idl,
&sbrec_port_binding_col_gateway_chassis);
+ ovsdb_idl_add_column(ovnsb_idl_loop.idl,
+ &sbrec_port_binding_col_ha_chassis_group);
ovsdb_idl_add_column(ovnsb_idl_loop.idl,
&sbrec_gateway_chassis_col_chassis);
ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_gateway_chassis_col_name);
ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_chassis_col_nb_cfg);
ovsdb_idl_add_column(ovnsb_idl_loop.idl, &sbrec_chassis_col_name);
+ ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_ha_chassis);
+ add_column_noalert(ovnsb_idl_loop.idl,
+ &sbrec_ha_chassis_col_chassis);
+ add_column_noalert(ovnsb_idl_loop.idl,
+ &sbrec_ha_chassis_col_priority);
+ add_column_noalert(ovnsb_idl_loop.idl,
+ &sbrec_ha_chassis_col_external_ids);
+
+ ovsdb_idl_add_table(ovnsb_idl_loop.idl, &sbrec_table_ha_chassis_group);
+ add_column_noalert(ovnsb_idl_loop.idl,
+ &sbrec_ha_chassis_group_col_name);
+ add_column_noalert(ovnsb_idl_loop.idl,
+ &sbrec_ha_chassis_group_col_ha_chassis);
+ add_column_noalert(ovnsb_idl_loop.idl,
+ &sbrec_ha_chassis_group_col_external_ids);
+ add_column_noalert(ovnsb_idl_loop.idl,
+ &sbrec_ha_chassis_group_col_ref_chassis);
+
struct ovsdb_idl_index *sbrec_chassis_by_name
= chassis_index_create(ovnsb_idl_loop.idl);
+ struct ovsdb_idl_index *sbrec_ha_chassis_grp_by_name
+ = ha_chassis_group_index_create(ovnsb_idl_loop.idl);
+
/* Ensure that only a single ovn-northd is active in the deployment by
* acquiring a lock called "ovn_northd" on the southbound database
* and then only performing DB transactions if the lock is held. */
.ovnnb_txn = ovsdb_idl_loop_run(&ovnnb_idl_loop),
.ovnsb_idl = ovnsb_idl_loop.idl,
.ovnsb_txn = ovsdb_idl_loop_run(&ovnsb_idl_loop),
+ .sbrec_ha_chassis_grp_by_name = sbrec_ha_chassis_grp_by_name,
};
if (!had_lock && ovsdb_idl_has_lock(ovnsb_idl_loop.idl)) {
{
"name": "OVN_Northbound",
- "version": "5.14.1",
- "cksum": "3758097843 20509",
+ "version": "5.15.0",
+ "cksum": "1015978933 21919",
"tables": {
"NB_Global": {
"columns": {
"refType": "strong"},
"min": 0,
"max": "unlimited"}},
+ "ha_chassis_group": {
+ "type": {"key": {"type": "uuid",
+ "refTable": "HA_Chassis_Group",
+ "refType": "strong"},
+ "min": 0,
+ "max": 1}},
"options": {
"type": {"key": "string",
"value": "string",
"type": {"key": "string", "value": "string",
"min": 0, "max": "unlimited"}}},
"indexes": [["name"]],
- "isRoot": false}}
+ "isRoot": false},
+ "HA_Chassis": {
+ "columns": {
+ "chassis_name": {"type": "string"},
+ "priority": {"type": {"key": {"type": "integer",
+ "minInteger": 0,
+ "maxInteger": 32767}}},
+ "external_ids": {
+ "type": {"key": "string", "value": "string",
+ "min": 0, "max": "unlimited"}}},
+ "isRoot": false},
+ "HA_Chassis_Group": {
+ "columns": {
+ "name": {"type": "string"},
+ "ha_chassis": {
+ "type": {"key": {"type": "uuid",
+ "refTable": "HA_Chassis",
+ "refType": "strong"},
+ "min": 0,
+ "max": "unlimited"}},
+ "external_ids": {
+ "type": {"key": "string", "value": "string",
+ "min": 0, "max": "unlimited"}}},
+ "indexes": [["name"]],
+ "isRoot": true}}
}
</column>
<column name="gateway_chassis">
+ <p>
+ This column is ignored if the column
+ <ref column="ha_chassis_group" table="Logical_Router_Port"/>.
+ is set.
+ </p>
+
<p>
If set, this indicates that this logical router port represents
a distributed gateway port that connects this router to a logical
</p>
</column>
+ <column name="ha_chassis_group">
+ <p>
+ If set, this indicates that this logical router port represents
+ a distributed gateway port that connects this router to a logical
+ switch with a localnet port. There may be at most one such
+ logical router port on each logical router. The HA chassis which
+ are part of the HA chassis group will provide the gateway high
+ availability. Please see the <ref table="HA_Chassis_Group"/> for
+ more details.
+ </p>
+
+ <p>
+ When this column is set, the column
+ <ref column="gateway_chassis" table="Logical_Router_Port"/> will
+ be ignored.
+ </p>
+ </column>
+
<column name="networks">
<p>
The IP addresses and netmasks of the router. For example,
</group>
</table>
+ <table name="HA_Chassis_Group">
+ <p>
+ Table representing a group of chassis which can provide High availability
+ services. Each chassis in the group is represented by the table
+ <ref table="HA_Chassis"/>. The HA chassis with highest priority will
+ be the master of this group. If the master chassis failover is detected,
+ the HA chassis with the next higher priority takes over the
+ responsibility of providing the HA. If a distributed gateway router port
+ references a row in this table, then the master HA chassis in this group
+ provides the gateway functionality.
+ </p>
+
+ <column name="name">
+ Name of the <ref table="HA_Chassis_Group"/>. Name should be unique.
+ </column>
+
+ <column name="ha_chassis">
+ A list of HA chassis which belongs to this group.
+ </column>
+
+ <group title="Common Columns">
+ <column name="external_ids">
+ See <em>External IDs</em> at the beginning of this document.
+ </column>
+ </group>
+ </table>
+
+ <table name="HA_Chassis">
+ <column name="chassis_name">
+ <p>
+ Name of the chassis which is part of the HA chassis group.
+ The value must match the
+ <ref db="OVN_Southbound" table="Chassis" column="name"/> column
+ of the <ref db="OVN_Southbound" table="Chassis"/> table in the
+ <ref db="OVN_Southbound"/> database.
+ </p>
+ </column>
+
+ <column name="priority">
+ <p>
+ Priority of the chassis. Chassis with highest priority will be
+ the master.
+ </p>
+ </column>
+
+ <group title="Common Columns">
+ <column name="external_ids">
+ See <em>External IDs</em> at the beginning of this document.
+ </column>
+ </group>
+ </table>
</database>
{
"name": "OVN_Southbound",
- "version": "2.1.0",
- "cksum": "3806083220 15332",
+ "version": "2.2.0",
+ "cksum": "2100715070 17222",
"tables": {
"SB_Global": {
"columns": {
"refType": "strong"},
"min": 0,
"max": "unlimited"}},
+ "ha_chassis_group": {
+ "type": {"key": {"type": "uuid",
+ "refTable": "HA_Chassis_Group",
+ "refType": "strong"},
+ "min": 0,
+ "max": 1}},
"options": {
"type": {"key": "string",
"value": "string",
"type": {"key": "string", "value": "string",
"min": 0, "max": "unlimited"}}},
"indexes": [["name"]],
- "isRoot": false}}}
+ "isRoot": false},
+ "HA_Chassis": {
+ "columns": {
+ "chassis": {"type": {"key": {"type": "uuid",
+ "refTable": "Chassis",
+ "refType": "weak"},
+ "min": 0, "max": 1}},
+ "priority": {"type": {"key": {"type": "integer",
+ "minInteger": 0,
+ "maxInteger": 32767}}},
+ "external_ids": {
+ "type": {"key": "string", "value": "string",
+ "min": 0, "max": "unlimited"}}},
+ "isRoot": false},
+ "HA_Chassis_Group": {
+ "columns": {
+ "name": {"type": "string"},
+ "ha_chassis": {
+ "type": {"key": {"type": "uuid",
+ "refTable": "HA_Chassis",
+ "refType": "strong"},
+ "min": 0,
+ "max": "unlimited"}},
+ "ref_chassis": {"type": {"key": {"type": "uuid",
+ "refTable": "Chassis",
+ "refType": "weak"},
+ "min": 0, "max": "unlimited"}},
+ "external_ids": {
+ "type": {"key": "string", "value": "string",
+ "min": 0, "max": "unlimited"}}},
+ "indexes": [["name"]],
+ "isRoot": true}}}
</p>
</column>
+ <column name="ha_chassis_group">
+ <p>
+ This should only be populated for ports with
+ <ref column="type"/> set to <code>chassisredirect</code>.
+ This column defines the HA chassis group with a list of
+ HA chassis used as gateways where traffic will be redirected
+ through.
+ </p>
+ </column>
+
<column name="tunnel_key">
<p>
A number that represents the logical port in the key (e.g. STT key or
<column name="external_ids"/>
</group>
</table>
+
+ <table name="HA_Chassis">
+ <column name="chassis">
+ <p>
+ The <ref table="Chassis"/> which provides the HA functionality.
+ </p>
+ </column>
+
+ <column name="priority">
+ <p>
+ Priority of the HA chassis. Chassis with highest priority will be
+ the master in the HA chassis group.
+ </p>
+ </column>
+
+ <group title="Common Columns">
+ <column name="external_ids">
+ See <em>External IDs</em> at the beginning of this document.
+ </column>
+ </group>
+ </table>
+
+ <table name="HA_Chassis_Group">
+ <p>
+ Table representing a group of chassis which can provide High availability
+ services. Each chassis in the group is represented by the table
+ <ref table="HA_Chassis"/>. The HA chassis with highest priority will
+ be the master of this group. If the master chassis failover is detected,
+ the HA chassis with the next higher priority takes over the
+ responsibility of providing the HA. If <ref db="OVN_Southbound"
+ table="Port_Binding" column="ha_chassis_group"/> column of the table
+ <ref db="OVN_Southbound" table="Port_Binding"/> references this table,
+ then this HA chassis group provides the gateway functionality and
+ redirects the gateway traffic to the master of this group.
+ </p>
+ <column name="name">
+ Name of the <ref table="HA_Chassis_Group"/>. Name should be unique.
+ </column>
+
+ <column name="ha_chassis">
+ A list of <ref table="HA_Chassis"/> which belongs to this group.
+ </column>
+
+ <column name="ref_chassis">
+ A list of <ref table="chassis"/> which references this HA chassis group.
+ </column>
+
+ <group title="Common Columns">
+ <column name="external_ids">
+ See <em>External IDs</em> at the beginning of this document.
+ </column>
+ </group>
+ </table>
</database>
</dd>
</dl>
+ <h1> HA Chassis Group commands</h1>
+
+ <dl>
+ <dt><code>ha-chassis-group-add</code> <var>group</var></dt>
+ <dd>
+ Creates a new HA chassis group in the <code>HA_Chassis_Group</code>
+ table named <code>group</code>.
+ </dd>
+
+ <dt><code>ha-chassis-group-del</code> <var>group</var></dt>
+ <dd>
+ Deletes the HA chassis group <code>group</code>. It is an error if
+ <code>group</code> does not exist.
+ </dd>
+
+ <dt><code>ha-chassis-group-list</code></dt>
+ <dd>
+ Lists the HA chassis group <code>group</code> along with the
+ <code>HA chassis</code> if any associated with it.
+ </dd>
+
+ <dt><code>ha-chassis-group-add-chassis</code> <var>group</var>
+ <var>chassis</var> <var>priority</var></dt>
+ <dd>
+ Adds a new HA chassis <code>chassis</code> to the
+ HA Chassis group <code>group</code> with the specified priority.
+ If the <code>chassis</code> already exists, then the
+ <code>priority</code> is updated.
+ The <code>chassis</code> should be the name of the chassis in the
+ <code>OVN_Southbound</code>.
+ </dd>
+
+ <dt><code>ha-chassis-group-remove-chassis</code> <var>group</var>
+ <var>chassis</var></dt>
+ <dd>
+ Removes the HA chassis <code>chassis</code> from the HA chassis
+ group <code>group</code>. It is an error if <code>chassis</code> does
+ not exist.
+ </dd>
+ </dl>
+
<h1>Database Commands</h1>
<p>These commands query and modify the contents of <code>ovsdb</code> tables.
They are a slight abstraction of the <code>ovsdb</code> interface and
Port group commands:\n\
pg-add PG [PORTS] Create port group PG with optional PORTS\n\
pg-set-ports PG PORTS Set PORTS on port group PG\n\
- pg-del PG Delete port group PG\n\
+ pg-del PG Delete port group PG\n\n",
+ program_name, program_name);
+ printf("\
+HA chassis group commands:\n\
+ ha-chassis-group-add GRP Create an HA chassis group GRP\n\
+ ha-chassis-group-del GRP Delete the HA chassis group GRP\n\
+ ha-chassis-group-list List the HA chassis groups\n\
+ ha-chassis-group-add-chassis GRP CHASSIS [PRIORITY] Adds an HA\
+chassis with optional PRIORITY to the HA chassis group GRP\n\
+ ha-chassis-group-del-chassis GRP CHASSIS Deletes the HA chassis\
+CHASSIS from the HA chassis group GRP\n\
\n\
%s\
%s\
-t, --timeout=SECS wait at most SECS seconds\n\
--dry-run do not commit changes to database\n\
--oneline print exactly one line of output per command\n",
- program_name, program_name, ctl_get_db_cmd_usage(),
+ ctl_get_db_cmd_usage(),
ctl_list_db_tables_usage(), default_nb_db());
table_usage();
daemon_usage();
nbrec_port_group_delete(pg);
}
+static const struct nbrec_ha_chassis_group*
+ha_chassis_group_by_name_or_uuid(struct ctl_context *ctx, const char *id,
+ bool must_exist)
+{
+ struct uuid ch_grp_uuid;
+ const struct nbrec_ha_chassis_group *ha_ch_grp = NULL;
+ bool is_uuid = uuid_from_string(&ch_grp_uuid, id);
+ if (is_uuid) {
+ ha_ch_grp = nbrec_ha_chassis_group_get_for_uuid(ctx->idl,
+ &ch_grp_uuid);
+ }
+
+ if (!ha_ch_grp) {
+ const struct nbrec_ha_chassis_group *iter;
+ NBREC_HA_CHASSIS_GROUP_FOR_EACH (iter, ctx->idl) {
+ if (!strcmp(iter->name, id)) {
+ ha_ch_grp = iter;
+ break;
+ }
+ }
+ }
+
+ if (!ha_ch_grp && must_exist) {
+ ctx->error = xasprintf("%s: ha_chassi_group %s not found",
+ id, is_uuid ? "UUID" : "name");
+ }
+
+ return ha_ch_grp;
+}
+
+static void
+cmd_ha_ch_grp_add(struct ctl_context *ctx)
+{
+ const char *name = ctx->argv[1];
+ struct nbrec_ha_chassis_group *ha_ch_grp =
+ nbrec_ha_chassis_group_insert(ctx->txn);
+ nbrec_ha_chassis_group_set_name(ha_ch_grp, name);
+}
+
+static void
+cmd_ha_ch_grp_del(struct ctl_context *ctx)
+{
+ const char *name_or_id = ctx->argv[1];
+
+ const struct nbrec_ha_chassis_group *ha_ch_grp =
+ ha_chassis_group_by_name_or_uuid(ctx, name_or_id, true);
+
+ if (ha_ch_grp) {
+ nbrec_ha_chassis_group_delete(ha_ch_grp);
+ }
+}
+
+static void
+cmd_ha_ch_grp_list(struct ctl_context *ctx)
+{
+ const struct nbrec_ha_chassis_group *ha_ch_grp;
+
+ NBREC_HA_CHASSIS_GROUP_FOR_EACH (ha_ch_grp, ctx->idl) {
+ ds_put_format(&ctx->output, UUID_FMT " (%s)\n",
+ UUID_ARGS(&ha_ch_grp->header_.uuid), ha_ch_grp->name);
+ const struct nbrec_ha_chassis *ha_ch;
+ for (size_t i = 0; i < ha_ch_grp->n_ha_chassis; i++) {
+ ha_ch = ha_ch_grp->ha_chassis[i];
+ ds_put_format(&ctx->output,
+ " "UUID_FMT " (%s)\n"
+ " priority %lu\n\n",
+ UUID_ARGS(&ha_ch->header_.uuid), ha_ch->chassis_name,
+ ha_ch->priority);
+ }
+ ds_put_cstr(&ctx->output, "\n");
+ }
+}
+
+static void
+cmd_ha_ch_grp_add_chassis(struct ctl_context *ctx)
+{
+ const struct nbrec_ha_chassis_group *ha_ch_grp =
+ ha_chassis_group_by_name_or_uuid(ctx, ctx->argv[1], true);
+
+ if (!ha_ch_grp) {
+ return;
+ }
+
+ const char *chassis_name = ctx->argv[2];
+ int64_t priority;
+ char *error = parse_priority(ctx->argv[3], &priority);
+ if (error) {
+ ctx->error = error;
+ return;
+ }
+
+ struct nbrec_ha_chassis *ha_chassis = NULL;
+ for (size_t i = 0; i < ha_ch_grp->n_ha_chassis; i++) {
+ if (!strcmp(ha_ch_grp->ha_chassis[i]->chassis_name, chassis_name)) {
+ ha_chassis = ha_ch_grp->ha_chassis[i];
+ break;
+ }
+ }
+
+ if (ha_chassis) {
+ nbrec_ha_chassis_set_priority(ha_chassis, priority);
+ return;
+ }
+
+ ha_chassis = nbrec_ha_chassis_insert(ctx->txn);
+ nbrec_ha_chassis_set_chassis_name(ha_chassis, chassis_name);
+ nbrec_ha_chassis_set_priority(ha_chassis, priority);
+
+ nbrec_ha_chassis_group_verify_ha_chassis(ha_ch_grp);
+
+ struct nbrec_ha_chassis **new_ha_chs =
+ xmalloc(sizeof *new_ha_chs * (ha_ch_grp->n_ha_chassis + 1));
+ nullable_memcpy(new_ha_chs, ha_ch_grp->ha_chassis,
+ sizeof *new_ha_chs * ha_ch_grp->n_ha_chassis);
+ new_ha_chs[ha_ch_grp->n_ha_chassis] =
+ CONST_CAST(struct nbrec_ha_chassis *, ha_chassis);
+ nbrec_ha_chassis_group_set_ha_chassis(ha_ch_grp, new_ha_chs,
+ ha_ch_grp->n_ha_chassis + 1);
+ free(new_ha_chs);
+}
+
+static void
+cmd_ha_ch_grp_remove_chassis(struct ctl_context *ctx)
+{
+ const struct nbrec_ha_chassis_group *ha_ch_grp =
+ ha_chassis_group_by_name_or_uuid(ctx, ctx->argv[1], true);
+
+ if (!ha_ch_grp) {
+ return;
+ }
+
+ const char *chassis_name = ctx->argv[2];
+ struct nbrec_ha_chassis *ha_chassis = NULL;
+ size_t idx = 0;
+ for (size_t i = 0; i < ha_ch_grp->n_ha_chassis; i++) {
+ if (!strcmp(ha_ch_grp->ha_chassis[i]->chassis_name, chassis_name)) {
+ ha_chassis = ha_ch_grp->ha_chassis[i];
+ idx = i;
+ break;
+ }
+ }
+
+ if (!ha_chassis) {
+ ctx->error = xasprintf("%s: ha chassis not found in %s ha "
+ "chassis group", chassis_name, ctx->argv[1]);
+ return;
+ }
+
+ struct nbrec_ha_chassis **new_ha_ch
+ = xmemdup(ha_ch_grp->ha_chassis,
+ sizeof *new_ha_ch * ha_ch_grp->n_ha_chassis);
+ new_ha_ch[idx] = new_ha_ch[ha_ch_grp->n_ha_chassis - 1];
+ nbrec_ha_chassis_group_verify_ha_chassis(ha_ch_grp);
+ nbrec_ha_chassis_group_set_ha_chassis(ha_ch_grp, new_ha_ch,
+ ha_ch_grp->n_ha_chassis - 1);
+ free(new_ha_ch);
+ nbrec_ha_chassis_delete(ha_chassis);
+}
+
+static void
+cmd_ha_ch_grp_set_chassis_prio(struct ctl_context *ctx)
+{
+ const struct nbrec_ha_chassis_group *ha_ch_grp =
+ ha_chassis_group_by_name_or_uuid(ctx, ctx->argv[1], true);
+
+ if (!ha_ch_grp) {
+ return;
+ }
+
+ int64_t priority;
+ char *error = parse_priority(ctx->argv[3], &priority);
+ if (error) {
+ ctx->error = error;
+ return;
+ }
+
+ const char *chassis_name = ctx->argv[2];
+ struct nbrec_ha_chassis *ha_chassis = NULL;
+
+ for (size_t i = 0; i < ha_ch_grp->n_ha_chassis; i++) {
+ if (!strcmp(ha_ch_grp->ha_chassis[i]->chassis_name, chassis_name)) {
+ ha_chassis = ha_ch_grp->ha_chassis[i];
+ break;
+ }
+ }
+
+ if (!ha_chassis) {
+ ctx->error = xasprintf("%s: ha chassis not found in %s ha "
+ "chassis group", chassis_name, ctx->argv[1]);
+ return;
+ }
+
+ nbrec_ha_chassis_set_priority(ha_chassis, priority);
+}
+
static const struct ctl_table_class tables[NBREC_N_TABLES] = {
[NBREC_TABLE_DHCP_OPTIONS].row_ids
= {{&nbrec_logical_switch_port_col_name, NULL,
= {&nbrec_port_group_col_name, NULL, NULL},
[NBREC_TABLE_ACL].row_ids[0] = {&nbrec_acl_col_name, NULL, NULL},
+
+ [NBREC_TABLE_HA_CHASSIS_GROUP].row_ids[0]
+ = {&nbrec_ha_chassis_group_col_name, NULL, NULL},
};
\f
static char *
{"pg-set-ports", 2, INT_MAX, "", NULL, cmd_pg_set_ports, NULL, "", RW },
{"pg-del", 1, 1, "", NULL, cmd_pg_del, NULL, "", RW },
+ /* HA chassis group commands. */
+ {"ha-chassis-group-add", 1, 1, "[CHASSIS GROUP]", NULL,
+ cmd_ha_ch_grp_add, NULL, "", RW },
+ {"ha-chassis-group-del", 1, 1, "[CHASSIS GROUP]", NULL,
+ cmd_ha_ch_grp_del, NULL, "", RW },
+ {"ha-chassis-group-list", 0, 0, "[CHASSIS GROUP]", NULL,
+ cmd_ha_ch_grp_list, NULL, "", RO },
+ {"ha-chassis-group-add-chassis", 3, 3, "[CHASSIS GROUP]", NULL,
+ cmd_ha_ch_grp_add_chassis, NULL, "", RW },
+ {"ha-chassis-group-remove-chassis", 2, 2, "[CHASSIS GROUP]", NULL,
+ cmd_ha_ch_grp_remove_chassis, NULL, "", RW },
+ {"ha-chassis-group-set-chassis-prio", 3, 3, "[CHASSIS GROUP]", NULL,
+ cmd_ha_ch_grp_set_chassis_prio, NULL, "", RW },
+
{NULL, 0, 0, NULL, NULL, NULL, NULL, "", RO},
};
[SBREC_TABLE_ADDRESS_SET].row_ids[0]
= {&sbrec_address_set_col_name, NULL, NULL},
+
+ [SBREC_TABLE_HA_CHASSIS_GROUP].row_ids[0]
+ = {&sbrec_ha_chassis_group_col_name, NULL, NULL},
+
+ [SBREC_TABLE_HA_CHASSIS].row_ids[0]
+ = {&sbrec_ha_chassis_col_chassis, NULL, NULL},
};
\f
AT_CHECK([ovn-sbctl --bare --columns gateway_chassis find port_binding logical_port="cr-alice" | grep $gwc2_uuid | wc -l], [0], [1
])
+# There should be one ha_chassis_group with the name "alice"
+ha_chassi_grp_name=`ovn-sbctl --bare --columns name find \
+ha_chassis_group name="alice"`
+
+AT_CHECK([test $ha_chassi_grp_name = alice])
+
+ha_chgrp_uuid=`ovn-sbctl --bare --columns _uuid find ha_chassis_group name=alice`
+
+AT_CHECK([ovn-sbctl --bare --columns ha_chassis_group find port_binding \
+logical_port="cr-alice" | grep $ha_chgrp_uuid | wc -l], [0], [1
+])
+
+ha_ch=`ovn-sbctl --bare --columns ha_chassis find ha_chassis_group`
+# Trim the spaces.
+ha_ch=`echo $ha_ch | sed 's/ //g'`
+
+ha_ch_list=''
+for i in `ovn-sbctl --bare --columns _uuid list ha_chassis | sort`
+do
+ ha_ch_list="$ha_ch_list $i"
+done
+
+# Trim the spaces.
+ha_ch_list=`echo $ha_ch_list | sed 's/ //g'`
+
+AT_CHECK([test "$ha_ch_list" = "$ha_ch"])
+
# delete the 2nd Gateway_Chassis on NBDB for alice port
ovn-nbctl --wait=sb set Logical_Router_Port alice gateway_chassis=${nb_gwc1_uuid}
AT_CHECK([ovn-sbctl find Gateway_Chassis name=alice_gw2], [0], [])
+# There should be only 1 row in ha_chassis SB DB table.
+AT_CHECK([ovn-sbctl --bare --columns _uuid list ha_chassis | wc -l], [0], [1
+])
+
# delete all the gateway_chassis on NBDB for alice port
ovn-nbctl --wait=sb clear Logical_Router_Port alice gateway_chassis
AT_CHECK([ovn-sbctl find Gateway_Chassis name=alice_gw1], [0], [])
AT_CHECK([ovn-sbctl find Gateway_Chassis name=alice_gw2], [0], [])
+# expect that the ha_chassis doesn't exist anymore
+AT_CHECK([ovn-sbctl list ha_chassis | wc -l], [0], [0
+])
+AT_CHECK([ovn-sbctl list ha_chassis_group | wc -l], [0], [0
+])
+
AT_CLEANUP
AT_SETUP([ovn -- check Gateway_Chassis propagation from NBDB to SBDB backwards compatibility])
OVS_APP_EXIT_AND_WAIT([ovn-northd])
AT_CLEANUP
+
+AT_SETUP([ovn -- check HA_Chassis_Group propagation from NBDB to SBDB])
+AT_SKIP_IF([test $HAVE_PYTHON = no])
+ovn_start
+
+ovn-nbctl --wait=sb ha-chassis-group-add hagrp1
+
+# ovn-northd should not create HA chassis group and HA chassis rows
+# unless the HA chassis group in OVN NB DB is associated to
+# a logical router port. ovn-northd still doesn't support
+# associating a HA chassis group to a logical router port.
+AT_CHECK([ovn-sbctl --bare --columns name find ha_chassis_group name="hagrp1" \
+| wc -l], [0], [0
+])
+
+ovn-nbctl --wait=sb ha-chassis-group-add-chassis hagrp1 ch1 30
+ovn-nbctl --wait=sb ha-chassis-group-add-chassis hagrp1 ch2 20
+ovn-nbctl --wait=sb ha-chassis-group-add-chassis hagrp1 ch3 10
+
+# There should be no HA_Chassis rows in SB DB.
+AT_CHECK([ovn-sbctl list ha_chassis | grep chassis | awk '{print $3}' \
+| grep -v '-' | wc -l ], [0], [0
+])
+
+# Add chassis ch1.
+ovn-sbctl chassis-add ch1 geneve 127.0.0.2
+
+OVS_WAIT_UNTIL([test 1 = `ovn-sbctl list chassis | grep ch1 | wc -l`])
+
+# There should be no HA_Chassis rows
+AT_CHECK([ovn-sbctl list ha_chassis | grep chassis | awk '{print $3}' \
+| grep -v '-' | wc -l ], [0], [0
+])
+
+ovn-nbctl ha-chassis-group-del hagrp1
+OVS_WAIT_UNTIL([test 0 = `ovn-sbctl list ha_chassis_group | wc -l`])
+OVS_WAIT_UNTIL([test 0 = `ovn-sbctl list ha_chassis | wc -l`])
+
+# Create a logical router port and attach Gateway chassis.
+# ovn-northd should create HA chassis group rows in SB DB.
+ovn-nbctl lr-add lr0
+
+ovn-nbctl lrp-add lr0 lr0-public 00:00:20:20:12:13 172.168.0.100/24
+ovn-nbctl lrp-set-gateway-chassis lr0-public ch1 20
+
+OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns name find ha_chassis_group \
+name="lr0-public" | wc -l`])
+
+OVS_WAIT_UNTIL([test 1 = `ovn-sbctl --bare --columns _uuid \
+find ha_chassis | wc -l`])
+
+ovn-nbctl lrp-set-gateway-chassis lr0-public ch2 10
+
+ovn-sbctl --bare --columns _uuid find ha_chassis
+OVS_WAIT_UNTIL([test 2 = `ovn-sbctl list ha_chassis | grep chassis | wc -l`])
+
+# Test if 'ref_chassis' column is properly set or not in
+# SB DB ha_chassis_group.
+ovn-nbctl ls-add sw0
+ovn-nbctl lsp-add sw0 sw0-p1
+
+ovn-sbctl chassis-add ch2 geneve 127.0.0.3
+ovn-sbctl chassis-add ch3 geneve 127.0.0.4
+ovn-sbctl chassis-add comp1 geneve 127.0.0.5
+ovn-sbctl chassis-add comp2 geneve 127.0.0.6
+
+ovn-nbctl lrp-add lr0 lr0-sw0 00:00:20:20:12:14 10.0.0.1/24
+ovn-nbctl lsp-add sw0 sw0-lr0
+ovn-nbctl lsp-set-type sw0-lr0 router
+ovn-nbctl lsp-set-addresses sw0-lr0 router
+ovn-nbctl lsp-set-options sw0-lr0 router-port=lr0-sw0
+
+ovn-sbctl lsp-bind sw0-p1 comp1
+OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up sw0-p1` = xup])
+
+comp1_ch_uuid=`ovn-sbctl --bare --columns _uuid find chassis name="comp1"`
+comp2_ch_uuid=`ovn-sbctl --bare --columns _uuid find chassis name="comp2"`
+ch2_ch_uuid=`ovn-sbctl --bare --columns _uuid find chassis name="comp1"`
+
+echo "comp1_ch_uuid = $comp1_ch_uuid"
+OVS_WAIT_UNTIL(
+ [ref_ch_list=`ovn-sbctl --bare --columns ref_chassis find ha_chassis_group | sort`
+ # Trim the spaces.
+ ref_ch_list=`echo $ref_ch_list | sed 's/ //g'`
+ test "$comp1_ch_uuid" = "$ref_ch_list"])
+
+# unbind sw0-p1
+ovn-sbctl lsp-unbind sw0-p1
+OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up sw0-p1` = xdown])
+OVS_WAIT_UNTIL(
+ [ref_ch_list=`ovn-sbctl --bare --columns ref_chassis find ha_chassis_group | sort`
+ # Trim the spaces.
+ ref_ch_list=`echo $ref_ch_list | sed 's/ //g'`
+ test "" = "$ref_ch_list"])
+
+# Bind sw0-p1 in comp2
+ovn-sbctl lsp-bind sw0-p1 comp2
+OVS_WAIT_UNTIL(
+ [ref_ch_list=`ovn-sbctl --bare --columns ref_chassis find ha_chassis_group | sort`
+ # Trim the spaces.
+ ref_ch_list=`echo $ref_ch_list | sed 's/ //g'`
+ test "$comp2_ch_uuid" = "$ref_ch_list"])
+
+ovn-nbctl ls-add sw1
+ovn-nbctl lsp-add sw1 sw1-p1
+ovn-nbctl lr-add lr1
+ovn-nbctl lrp-add lr1 lr1-sw1 00:00:20:20:12:15 20.0.0.1/24
+ovn-nbctl lsp-add sw1 sw1-lr1
+ovn-nbctl lsp-set-type sw1-lr1 router
+ovn-nbctl lsp-set-addresses sw1-lr1 router
+ovn-nbctl lsp-set-options sw1-lr1 router-port=lr1-sw1
+
+# Bind sw1-p1 in comp1.
+ovn-sbctl lsp-bind sw1-p1 comp1
+# Wait until sw1-p1 is up
+OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up sw1-p1` = xup])
+
+# sw1-p1 is not connected to lr0. So comp1 should not be in 'ref_chassis'
+OVS_WAIT_UNTIL(
+ [ref_ch_list=`ovn-sbctl --bare --columns ref_chassis find ha_chassis_group | sort`
+ # Trim the spaces.
+ ref_ch_list=`echo $ref_ch_list | sed 's/ //g'`
+ test "$comp2_ch_uuid" = "$ref_ch_list"])
+
+# Now attach sw0 to lr1
+ovn-nbctl lrp-add lr1 lr1-sw0 00:00:20:20:12:16 10.0.0.10/24
+ovn-nbctl lsp-add sw0 sw0-lr1
+ovn-nbctl lsp-set-type sw0-lr1 router
+ovn-nbctl lsp-set-addresses sw0-lr1 router
+ovn-nbctl lsp-set-options sw0-lr1 router-port=lr1-sw0
+
+# Both comp1 and comp2 should be in 'ref_chassis' as sw1 is indirectly
+# connected to lr0
+exp_ref_ch_list=''
+for i in `ovn-sbctl --bare --columns _uuid list chassis | sort`
+do
+ if test $i = $comp1_ch_uuid; then
+ exp_ref_ch_list="${exp_ref_ch_list}$i"
+ elif test $i = $comp2_ch_uuid; then
+ exp_ref_ch_list="${exp_ref_ch_list}$i"
+ fi
+done
+
+OVS_WAIT_UNTIL(
+ [ref_ch_list=`ovn-sbctl --bare --columns ref_chassis find ha_chassis_group | sort`
+ # Trim the spaces.
+ ref_ch_list=`echo $ref_ch_list | sed 's/ //g'`
+ test "$exp_ref_ch_list" = "$ref_ch_list"])
+
+# Unind sw1-p1. comp2 should not be in the ref_chassis.
+ovn-sbctl lsp-unbind sw1-p1
+OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up sw1-p1` = xdown])
+OVS_WAIT_UNTIL(
+ [ref_ch_list=`ovn-sbctl --bare --columns ref_chassis find ha_chassis_group | sort`
+ # Trim the spaces.
+ ref_ch_list=`echo $ref_ch_list | sed 's/ //g'`
+ test "$comp2_ch_uuid" = "$ref_ch_list"])
+
+# Create sw2 and attach it to lr2
+ovn-nbctl ls-add sw2
+ovn-nbctl lsp-add sw2 sw2-p1
+ovn-nbctl lr-add lr2
+ovn-nbctl lrp-add lr2 lr2-sw2 00:00:20:20:12:17 30.0.0.1/24
+ovn-nbctl lsp-add sw2 sw2-lr2
+ovn-nbctl lsp-set-type sw2-lr2 router
+ovn-nbctl lsp-set-addresses sw2-lr2 router
+ovn-nbctl lsp-set-options sw2-lr2 router-port=lr2-sw2
+
+# Bind sw2-p1 to comp1
+ovn-sbctl lsp-bind sw2-p1 comp1
+# Wait until sw2-p1 is up
+OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up sw2-p1` = xup])
+
+# sw2-p1 is not connected to lr0. So comp1 should not be in 'ref_chassis'
+OVS_WAIT_UNTIL(
+ [ref_ch_list=`ovn-sbctl --bare --columns ref_chassis find ha_chassis_group | sort`
+ # Trim the spaces.
+ ref_ch_list=`echo $ref_ch_list | sed 's/ //g'`
+ test "$comp2_ch_uuid" = "$ref_ch_list"])
+
+# Now attach sw1 to lr2. With this sw2-p1 is indirectly connected to lr0.
+ovn-nbctl lrp-add lr2 lr2-sw1 00:00:20:20:12:18 20.0.0.10/24
+ovn-nbctl lsp-add sw1 sw1-lr2
+ovn-nbctl lsp-set-type sw1-lr2 router
+ovn-nbctl lsp-set-addresses sw1-lr2 router
+ovn-nbctl lsp-set-options sw1-lr2 router-port=lr2-sw1
+
+# sw2-p1 is indirectly connected to lr0. So comp1 (and comp2) should be in
+# 'ref_chassis'
+OVS_WAIT_UNTIL(
+ [ref_ch_list=`ovn-sbctl --bare --columns ref_chassis find ha_chassis_group | sort`
+ # Trim the spaces.
+ ref_ch_list=`echo $ref_ch_list | sed 's/ //g'`
+ test "$exp_ref_ch_list" = "$ref_ch_list"])
+
+# Create sw0-p2 and bind it to comp1
+ovn-nbctl lsp-add sw0 sw0-p2
+ovn-sbctl lsp-bind sw0-p2 comp1
+OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up sw0-p2` = xup])
+OVS_WAIT_UNTIL(
+ [ref_ch_list=`ovn-sbctl --bare --columns ref_chassis find ha_chassis_group | sort`
+ # Trim the spaces.
+ ref_ch_list=`echo $ref_ch_list | sed 's/ //g'`
+ test "$exp_ref_ch_list" = "$ref_ch_list"])
+
+# unbind sw0-p2
+ovn-sbctl lsp-unbind sw0-p2
+OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up sw0-p2` = xdown])
+OVS_WAIT_UNTIL(
+ [ref_ch_list=`ovn-sbctl --bare --columns ref_chassis find ha_chassis_group | sort`
+ # Trim the spaces.
+ ref_ch_list=`echo $ref_ch_list | sed 's/ //g'`
+ test "$exp_ref_ch_list" = "$ref_ch_list"])
+
+# Delete lr1-sw0. comp1 should be deleted from ref_chassis as there is no link
+# from sw1 and sw2 to lr0.
+ovn-nbctl lrp-del lr1-sw0
+
+OVS_WAIT_UNTIL(
+ [ref_ch_list=`ovn-sbctl --bare --columns ref_chassis find ha_chassis_group | sort`
+ # Trim the spaces.
+ ref_ch_list=`echo $ref_ch_list | sed 's/ //g'`
+ test "$comp2_ch_uuid" = "$ref_ch_list"])
+
+AT_CLEANUP
AT_CHECK([ovn-nbctl --timeout=3 --wait=sb sync], [0], [ignore])
+# hv1 should be in 'ref_chassis' of the ha_chasssi_group as logical
+# switch 'foo' can reach the router 'R1' (which has gw router port)
+# via foo1 -> foo -> R0 -> join -> R1
+hv1_ch_uuid=`ovn-sbctl --bare --columns _uuid find chassis name="hv1"`
+OVS_WAIT_UNTIL(
+ [ref_ch_list=`ovn-sbctl --bare --columns ref_chassis find ha_chassis_group | sort`
+ # Trim the spaces.
+ ref_ch_list=`echo $ref_ch_list | sed 's/ //g'`
+ test "$hv1_ch_uuid" = "$ref_ch_list"])
+
# Allow some time for ovn-northd and ovn-controller to catch up.
# XXX This should be more systematic.
sleep 2
ovn-sbctl find Port_Binding type=chassisredirect
echo "-------------------------------------------"
+# There should be one ha_chassis_group with the name "outside"
+ha_chassi_grp_name=`ovn-sbctl --bare --columns name find \
+ha_chassis_group name="outside"`
+
+AT_CHECK([test $ha_chassi_grp_name = outside])
+
+# There should be 2 ha_chassis rows in SB DB.
+AT_CHECK([ovn-sbctl list ha_chassis | grep chassis | awk '{print $3}' \
+| grep '-' | wc -l ], [0], [2
+])
+
+ha_ch=`ovn-sbctl --bare --columns ha_chassis find ha_chassis_group`
+# Trim the spaces.
+ha_ch=`echo $ha_ch | sed 's/ //g'`
+
+ha_ch_list=''
+for i in `ovn-sbctl --bare --columns _uuid list ha_chassis | sort`
+do
+ ha_ch_list="$ha_ch_list $i"
+done
+
+# Trim the spaces.
+ha_ch_list=`echo $ha_ch_list | sed 's/ //g'`
+
+AT_CHECK([test "$ha_ch_list" = "$ha_ch"])
+
for chassis in gw1 gw2 hv1 hv2; do
as $chassis
echo "------ $chassis dump ----------"
gw1_chassis=$(ovn-sbctl --bare --columns=_uuid find Chassis name=gw1)
gw2_chassis=$(ovn-sbctl --bare --columns=_uuid find Chassis name=gw2)
-AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=32 | grep active_backup | grep slaves:$hv1_gw1_ofport,$hv1_gw2_ofport | wc -l], [0], [1
+OVS_WAIT_UNTIL([as hv1 ovs-ofctl dump-flows br-int table=32 | \
+grep active_backup | grep slaves:$hv1_gw1_ofport,$hv1_gw2_ofport \
+| wc -l], [0], [1
])
-AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=32 | grep active_backup | grep slaves:$hv2_gw1_ofport,$hv2_gw2_ofport | wc -l], [0], [1
+OVS_WAIT_UNTIL([as hv2 ovs-ofctl dump-flows br-int table=32 | \
+grep active_backup | grep slaves:$hv2_gw1_ofport,$hv2_gw2_ofport \
+| wc -l], [0], [1
])
-sleep 3 # let BFD sessions settle so we get the right flows on the right chassis
-
# make sure that flows for handling the outside router port reside on gw1
-AT_CHECK([as gw1 ovs-ofctl dump-flows br-int table=24 | grep 00:00:02:01:02:04 | wc -l], [0], [[1
+OVS_WAIT_UNTIL([as gw1 ovs-ofctl dump-flows br-int table=24 | \
+grep 00:00:02:01:02:04 | wc -l], [0], [[1
]])
-AT_CHECK([as gw2 ovs-ofctl dump-flows br-int table=24 | grep 00:00:02:01:02:04 | wc -l], [0], [[0
+
+OVS_WAIT_UNTIL([as gw2 ovs-ofctl dump-flows br-int table=24 | \
+grep 00:00:02:01:02:04 | wc -l], [0], [[0
]])
# make sure ARP responder flows for outside router port reside on gw1 too
-AT_CHECK([as gw1 ovs-ofctl dump-flows br-int table=9 | grep arp_tpa=192.168.0.101 | wc -l], [0], [[1
+OVS_WAIT_UNTIL([as gw1 ovs-ofctl dump-flows br-int table=9 | \
+grep arp_tpa=192.168.0.101 | wc -l], [0], [[1
]])
-AT_CHECK([as gw2 ovs-ofctl dump-flows br-int table=9 | grep arp_tpa=192.168.0.101 | wc -l], [0], [[0
+OVS_WAIT_UNTIL([as gw2 ovs-ofctl dump-flows br-int table=9 | grep arp_tpa=192.168.0.101 | wc -l], [0], [[0
]])
-
-
# check that the chassis redirect port has been claimed by the gw1 chassis
-AT_CHECK([ovn-sbctl --columns chassis --bare find Port_Binding logical_port=cr-outside | grep $gw1_chassis | wc -l],
- [0],[[1
+OVS_WAIT_UNTIL([ovn-sbctl --columns chassis --bare find Port_Binding \
+logical_port=cr-outside | grep $gw1_chassis | wc -l], [0],[[1
]])
+hv1_ch_uuid=`ovn-sbctl --bare --columns _uuid find chassis name="hv1"`
+hv2_ch_uuid=`ovn-sbctl --bare --columns _uuid find chassis name="hv2"`
+
+exp_ref_ch_list=''
+for i in `ovn-sbctl --bare --columns _uuid list chassis | sort`
+do
+ if test $i = $hv1_ch_uuid; then
+ exp_ref_ch_list="${exp_ref_ch_list}$i"
+ elif test $i = $hv2_ch_uuid; then
+ exp_ref_ch_list="${exp_ref_ch_list}$i"
+ fi
+done
+
+OVS_WAIT_UNTIL(
+ [ref_ch_list=`ovn-sbctl --bare --columns ref_chassis find ha_chassis_group | sort`
+ # Trim the spaces.
+ ref_ch_list=`echo $ref_ch_list | sed 's/ //g'`
+ test "$exp_ref_ch_list" = "$ref_ch_list"])
+
# at this point, we invert the priority of the gw chassis between gw1 and gw2
ovn-nbctl --wait=hv --timeout=3 sync
# we make sure that the hypervisors noticed, and inverted the slave ports
-AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=32 | grep active_backup | grep slaves:$hv1_gw2_ofport,$hv1_gw1_ofport | wc -l], [0], [1
+OVS_WAIT_UNTIL([as hv1 ovs-ofctl dump-flows br-int table=32 | \
+grep active_backup | grep slaves:$hv1_gw2_ofport,$hv1_gw1_ofport \
+| wc -l], [0], [1
])
-AT_CHECK([as hv2 ovs-ofctl dump-flows br-int table=32 | grep active_backup | grep slaves:$hv2_gw2_ofport,$hv2_gw1_ofport | wc -l], [0], [1
+OVS_WAIT_UNTIL([as hv2 ovs-ofctl dump-flows br-int table=32 | \
+grep active_backup | grep slaves:$hv2_gw2_ofport,$hv2_gw1_ofport \
+| wc -l], [0], [1
])
# check that the chassis redirect port has been reclaimed by the gw2 chassis
-AT_CHECK([ovn-sbctl --columns chassis --bare find Port_Binding logical_port=cr-outside | grep $gw2_chassis | wc -l],
- [0],[[1
+OVS_WAIT_UNTIL([ovn-sbctl --columns chassis --bare find Port_Binding \
+logical_port=cr-outside | grep $gw2_chassis | wc -l], [0],[[1
]])
# check BFD enablement on tunnel ports from gw1 #########
[[
]])
-sleep 3 # let BFD sessions settle so we get the right flows on the right chassis
-
# make sure that flows for handling the outside router port reside on gw2 now
-AT_CHECK([as gw2 ovs-ofctl dump-flows br-int table=24 | grep 00:00:02:01:02:04 | wc -l], [0], [[1
+OVS_WAIT_UNTIL([as gw2 ovs-ofctl dump-flows br-int table=24 | \
+grep 00:00:02:01:02:04 | wc -l], [0], [[1
]])
-AT_CHECK([as gw1 ovs-ofctl dump-flows br-int table=24 | grep 00:00:02:01:02:04 | wc -l], [0], [[0
+OVS_WAIT_UNTIL([as gw1 ovs-ofctl dump-flows br-int table=24 | \
+grep 00:00:02:01:02:04 | wc -l], [0], [[0
]])
# disconnect GW2 from the network, GW1 should take over
as gw2
port=${sandbox}_br-phys
as main ovs-vsctl del-port n1 $port
-sleep 4
bfd_dump
# make sure that flows for handling the outside router port reside on gw2 now
-AT_CHECK([as gw1 ovs-ofctl dump-flows br-int table=24 | grep 00:00:02:01:02:04 | wc -l], [0], [[1
+OVS_WAIT_UNTIL([as gw1 ovs-ofctl dump-flows br-int table=24 | \
+grep 00:00:02:01:02:04 | wc -l], [0], [[1
]])
-AT_CHECK([as gw2 ovs-ofctl dump-flows br-int table=24 | grep 00:00:02:01:02:04 | wc -l], [0], [[0
+OVS_WAIT_UNTIL([as gw2 ovs-ofctl dump-flows br-int table=24 | \
+grep 00:00:02:01:02:04 | wc -l], [0], [[0
]])
# check that the chassis redirect port has been reclaimed by the gw1 chassis
-AT_CHECK([ovn-sbctl --columns chassis --bare find Port_Binding logical_port=cr-outside | grep $gw1_chassis | wc -l],
- [0],[[1
+OVS_WAIT_UNTIL([ovn-sbctl --columns chassis --bare find Port_Binding \
+logical_port=cr-outside | grep $gw1_chassis | wc -l], [0],[[1
]])
ovn-nbctl --wait=hv set NB_Global . options:"bfd-min-rx"=2000
])
done
+# Delete the inside1 vif. The ref_chassis in ha_chassis_group shouldn't have
+# reference to hv1.
+as hv1 ovs-vsctl del-port hv1-vif1
+
+OVS_WAIT_UNTIL(
+ [ref_ch_list=`ovn-sbctl --bare --columns ref_chassis find ha_chassis_group | sort`
+ # Trim the spaces.
+ ref_ch_list=`echo $ref_ch_list | sed 's/ //g'`
+ test "$hv2_ch_uuid" = "$ref_ch_list"])
+
+# Delete the inside2 vif.
+ovn-sbctl show
+
+echo "Deleting hv2-vif1"
+as hv2 ovs-vsctl del-port hv2-vif1
+
+# ref_chassis of ha_chassis_group should be empty
+OVS_WAIT_UNTIL(
+ [ref_ch_list=`ovn-sbctl --bare --columns ref_chassis find ha_chassis_group | sort`
+ # Trim the spaces.
+ ref_ch_list=`echo $ref_ch_list | sed 's/ //g'`
+ exp_ref_ch_list=""
+ test "$exp_ref_ch_list" = "$ref_ch_list"])
+
+# Delete the Gateway_Chassis for lrp - outside
+ovn-nbctl clear Logical_Router_Port outside gateway_chassis
+
+# There shoud be no ha_chassis_group rows in SB DB.
+OVS_WAIT_UNTIL([test 0 = `ovn-sbctl list ha_chassis_group | wc -l`])
+OVS_WAIT_UNTIL([test 0 = `ovn-sbctl list ha_chassis | wc -l`])
+
OVN_CLEANUP([gw1],[gw2],[hv1],[hv2])
AT_CLEANUP
gw2_chassis=$(ovn-sbctl --bare --columns=_uuid find Chassis name=gw2)
ovn-sbctl destroy Chassis $gw2_chassis
-# Ensure ovn-controller has processed latest sbdb update
-# ovn-nbctl --wait=hv sync
-
-AT_CHECK([grep "Releasing lport" gw1/ovn-controller.log], [1], [])
+OVS_WAIT_UNTIL([test 0 = `grep -c "Releasing lport" gw1/ovn-controller.log`])
OVN_CLEANUP([gw1],[gw2],[hv1])