This patch adds support for managing transit routers ports.
Ports create on a transit router are remote for all availability zones
except the zone containing the chassis.
This series also add support for commands to manage transit router
ports.
Commands to add and delete a transit router port.
$ ovn-ic-nbctl trp-add tr0 tr0-p0 00:00:00:11:22:00 192.168.10.10/24
192.168.10.20/24 chassis=ovn-chassis-1
$ ovn-ic-nbctl trp-del tr0-p0
Reported-at: https://issues.redhat.com/browse/FDP-1477
Reported-at: https://issues.redhat.com/browse/FDP-1478
Signed-off-by: Mairtin O'Loingsigh <[email protected]>
---
NEWS | 3 +
ic/ovn-ic.c | 390 +++++++++++++++++++++++++++++++--------
tests/ovn-ic-nbctl.at | 29 +++
tests/ovn-ic.at | 223 ++++++++++++++++++++++
utilities/ovn-ic-nbctl.c | 199 ++++++++++++++++++++
5 files changed, 762 insertions(+), 82 deletions(-)
diff --git a/NEWS b/NEWS
index 593af397d..4648d9d87 100644
--- a/NEWS
+++ b/NEWS
@@ -49,6 +49,9 @@ Post v25.09.0
* Support the creation of Transit Routers.
* Added new ovn-ic-nbctl 'tr-add','tr-del','tr-list' commands to manage
Transit Router.
+ * Support the creation of Transit Router Ports.
+ * Added new ovn-ic-nbctl 'trp-add' and 'tpr-del' commands to manage
+ Transit Router Ports.
OVN v25.09.0 - xxx xx xxxx
--------------------------
- STT tunnels are no longer supported in ovn-encap-type.
diff --git a/ic/ovn-ic.c b/ic/ovn-ic.c
index 284de97ea..43aca7ea7 100644
--- a/ic/ovn-ic.c
+++ b/ic/ovn-ic.c
@@ -66,6 +66,7 @@ struct ic_context {
struct ovsdb_idl_txn *ovnisb_txn;
const struct icsbrec_availability_zone *runned_az;
struct ovsdb_idl_index *nbrec_ls_by_name;
+ struct ovsdb_idl_index *nbrec_lr_by_name;
struct ovsdb_idl_index *nbrec_lrp_by_name;
struct ovsdb_idl_index *nbrec_port_by_name;
struct ovsdb_idl_index *sbrec_chassis_by_name;
@@ -75,8 +76,10 @@ struct ic_context {
struct ovsdb_idl_index *sbrec_service_monitor_by_remote_type_logical_port;
struct ovsdb_idl_index *icnbrec_transit_switch_by_name;
struct ovsdb_idl_index *icsbrec_port_binding_by_az;
- struct ovsdb_idl_index *icsbrec_port_binding_by_ts;
+ struct ovsdb_idl_index *icsbrec_port_binding_by_tr;
struct ovsdb_idl_index *icsbrec_port_binding_by_ts_az;
+ struct ovsdb_idl_index *icsbrec_port_binding_by_nb_ic_uuid;
+ struct ovsdb_idl_index *icsbrec_port_binding_by_nb_ic_uuid_type;
struct ovsdb_idl_index *icsbrec_route_by_az;
struct ovsdb_idl_index *icsbrec_route_by_ts;
struct ovsdb_idl_index *icsbrec_route_by_ts_az;
@@ -91,6 +94,7 @@ struct ic_state {
};
enum ic_datapath_type { IC_SWITCH, IC_ROUTER, IC_DATAPATH_MAX };
+enum ic_port_binding_type { IC_SWITCH_PORT, IC_ROUTER_PORT, IC_PORT_MAX };
static const char *ovnnb_db;
static const char *ovnsb_db;
@@ -215,6 +219,16 @@ ic_dp_get_type(const struct icsbrec_datapath_binding
*isb_dp)
return IC_SWITCH;
}
+static enum ic_port_binding_type
+ic_pb_get_type(const struct icsbrec_port_binding *isb_pb)
+{
+ if (isb_pb->type && !strcmp(isb_pb->type, "transit-router-port")) {
+ return IC_ROUTER_PORT;
+ }
+
+ return IC_SWITCH_PORT;
+}
+
static void
enumerate_datapaths(struct ic_context *ctx, struct hmap *dp_tnlids,
struct shash *isb_ts_dps, struct shash *isb_tr_dps)
@@ -439,8 +453,6 @@ tr_run(struct ic_context *ctx, struct hmap *dp_tnlids,
struct shash_node *node;
SHASH_FOR_EACH_SAFE (node, isb_tr_dps) {
- struct icsbrec_datapath_binding *isb_dp = node->data;
- ovn_free_tnlid(dp_tnlids, isb_dp->tunnel_key);
icsbrec_datapath_binding_delete(node->data);
}
}
@@ -616,6 +628,30 @@ find_ts_in_nb(struct ic_context *ctx, char *ts_name)
return NULL;
}
+static const struct nbrec_logical_router *
+find_tr_in_nb(struct ic_context *ctx, char *tr_name)
+{
+ const struct nbrec_logical_router *key =
+ nbrec_logical_router_index_init_row(ctx->nbrec_lr_by_name);
+ nbrec_logical_router_index_set_name(key, tr_name);
+
+ const struct nbrec_logical_router *lr;
+ bool found = false;
+ NBREC_LOGICAL_ROUTER_FOR_EACH_EQUAL (lr, key, ctx->nbrec_lr_by_name) {
+ if (smap_get(&lr->options, "interconn-tr")) {
+ found = true;
+ break;
+ }
+ }
+
+ nbrec_logical_router_index_destroy_row(key);
+ if (found) {
+ return lr;
+ }
+
+ return NULL;
+}
+
static const struct sbrec_port_binding *
find_sb_pb_by_name(struct ovsdb_idl_index *sbrec_port_binding_by_name,
const char *name)
@@ -733,6 +769,21 @@ sync_lsp_tnl_key(const struct nbrec_logical_switch_port
*lsp,
}
+static inline void
+sync_lrp_tnl_key(const struct nbrec_logical_router_port *lrp,
+ int64_t isb_tnl_key)
+{
+ int64_t tnl_key = smap_get_int(&lrp->options, "requested-tnl-key", 0);
+ if (tnl_key != isb_tnl_key) {
+ VLOG_DBG("Set options:requested-tnl-key %" PRId64 " for lrp %s in NB.",
+ isb_tnl_key, lrp->name);
+ char *tnl_key_str = xasprintf("%" PRId64, isb_tnl_key);
+ nbrec_logical_router_port_update_options_setkey(
+ lrp, "requested-tnl-key", tnl_key_str);
+ free(tnl_key_str);
+ }
+}
+
static bool
get_router_uuid_by_sb_pb(struct ic_context *ctx,
const struct sbrec_port_binding *sb_pb,
@@ -870,6 +921,52 @@ sync_remote_port(struct ic_context *ctx,
}
}
+/* For each remote port:
+ * - Sync from ISB to NB
+ */
+static void
+sync_router_port(const struct icsbrec_port_binding *isb_pb,
+ const struct icnbrec_transit_router_port *trp,
+ const struct nbrec_logical_router_port *lrp)
+{
+ /* Sync from ICNB to NB */
+ if (trp->chassis[0]) {
+ const char *chassis_name =
+ smap_get_def(&lrp->options, "requested-chassis", "");
+ if (strcmp(trp->chassis, chassis_name)) {
+ nbrec_logical_router_port_update_options_setkey(
+ lrp, "requested-chassis", trp->chassis);
+ }
+ } else {
+ nbrec_logical_router_port_update_options_delkey(
+ lrp, "requested-chassis");
+ }
+
+ if (strcmp(trp->mac, lrp->mac)) {
+ nbrec_logical_router_port_set_mac(lrp, trp->mac);
+ }
+
+ bool sync_networks = false;
+ if (trp->n_networks != lrp->n_networks) {
+ sync_networks = true;
+ } else {
+ for (size_t i = 0; i < trp->n_networks; i++) {
+ if (strcmp(trp->networks[i], lrp->networks[i])) {
+ sync_networks |= true;
+ break;
+ }
+ }
+ }
+
+ if (sync_networks) {
+ nbrec_logical_router_port_set_networks(
+ lrp, (const char **) trp->networks, trp->n_networks);
+ }
+
+ /* Sync tunnel key from ISB to NB */
+ sync_lrp_tnl_key(lrp, isb_pb->tunnel_key);
+}
+
static void
create_nb_lsp(struct ic_context *ctx,
const struct icsbrec_port_binding *isb_pb,
@@ -891,20 +988,90 @@ create_nb_lsp(struct ic_context *ctx,
nbrec_logical_switch_update_ports_addvalue(ls, lsp);
}
-static void
-create_isb_pb(struct ic_context *ctx,
- const struct sbrec_port_binding *sb_pb,
- const struct icsbrec_availability_zone *az,
- const char *ts_name,
- uint32_t pb_tnl_key)
+static const struct sbrec_port_binding *
+find_lp_in_sb(struct ic_context *ctx, const char *name)
{
+ return find_sb_pb_by_name(ctx->sbrec_port_binding_by_name, name);
+}
+
+static uint32_t
+allocate_port_key(struct hmap *pb_tnlids)
+{
+ static uint32_t hint;
+ return ovn_allocate_tnlid(pb_tnlids, "transit port",
+ 1, (1u << 15) - 1, &hint);
+}
+
+static const struct icsbrec_port_binding *
+create_isb_pb(struct ic_context *ctx, const char *logical_port,
+ const struct icsbrec_availability_zone *az, const char *ts_name,
+ const struct uuid *nb_ic_uuid, const char *type,
+ struct hmap *pb_tnlids)
+{
+ uint32_t pb_tnl_key = allocate_port_key(pb_tnlids);
+ if (pb_tnl_key == 0) {
+ return NULL;
+ }
+
const struct icsbrec_port_binding *isb_pb =
icsbrec_port_binding_insert(ctx->ovnisb_txn);
icsbrec_port_binding_set_availability_zone(isb_pb, az);
icsbrec_port_binding_set_transit_switch(isb_pb, ts_name);
- icsbrec_port_binding_set_logical_port(isb_pb, sb_pb->logical_port);
+ icsbrec_port_binding_set_logical_port(isb_pb, logical_port);
icsbrec_port_binding_set_tunnel_key(isb_pb, pb_tnl_key);
+ icsbrec_port_binding_set_nb_ic_uuid(isb_pb, nb_ic_uuid, 1);
+ icsbrec_port_binding_set_type(isb_pb, type);
+ return isb_pb;
+}
+static const struct nbrec_logical_router_port *
+get_lrp_by_lrp_name(struct ic_context *ctx, const char *lrp_name)
+{
+ const struct nbrec_logical_router_port *lrp;
+ const struct nbrec_logical_router_port *lrp_key =
+ nbrec_logical_router_port_index_init_row(ctx->nbrec_lrp_by_name);
+ nbrec_logical_router_port_index_set_name(lrp_key, lrp_name);
+ lrp =
+ nbrec_logical_router_port_index_find(ctx->nbrec_lrp_by_name, lrp_key);
+ nbrec_logical_router_port_index_destroy_row(lrp_key);
+
+ return lrp;
+}
+
+static bool
+trp_port_is_remote(struct ic_context *ctx, const char *chassis_name)
+{
+ if (chassis_name) {
+ const struct sbrec_chassis *chassis =
+ find_sb_chassis(ctx, chassis_name);
+ if (chassis) {
+ return smap_get_bool(&chassis->other_config, "is-remote", false);
+ } else {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static struct nbrec_logical_router_port *
+trp_port_create(struct ic_context *ctx, const struct nbrec_logical_router *lr,
+ const struct icnbrec_transit_router_port *trp)
+{
+ struct nbrec_logical_router_port *lrp =
+ nbrec_logical_router_port_insert(ctx->ovnnb_txn);
+ nbrec_logical_router_port_set_name(lrp, trp->name);
+
+ nbrec_logical_router_port_update_options_setkey(lrp, "interconn-tr",
+ trp->name);
+ nbrec_logical_router_update_ports_addvalue(lr, lrp);
+ return lrp;
+}
+
+static void
+sync_ts_isb_pb(struct ic_context *ctx, const struct sbrec_port_binding *sb_pb,
+ const struct icsbrec_port_binding *isb_pb)
+{
const char *address = get_lp_address_for_sb_pb(ctx, sb_pb);
if (address) {
icsbrec_port_binding_set_address(isb_pb, address);
@@ -912,32 +1079,11 @@ create_isb_pb(struct ic_context *ctx,
const struct sbrec_port_binding *crp = find_crp_for_sb_pb(ctx, sb_pb);
if (crp && crp->chassis) {
- icsbrec_port_binding_set_gateway(isb_pb, crp->chassis->name);
+ icsbrec_port_binding_set_gateway(isb_pb,
+ crp->chassis->name);
}
update_isb_pb_external_ids(ctx, sb_pb, isb_pb);
-
- /* XXX: Sync encap so that multiple encaps can be used for the same
- * gateway. However, it is not needed for now, since we don't yet
- * support specifying encap type/ip for gateway chassis or ha-chassis
- * for logical router port in NB DB, and now encap should always be
- * empty. The sync can be added if we add such support for gateway
- * chassis/ha-chassis in NB DB. */
-}
-
-static const struct sbrec_port_binding *
-find_lsp_in_sb(struct ic_context *ctx,
- const struct nbrec_logical_switch_port *lsp)
-{
- return find_sb_pb_by_name(ctx->sbrec_port_binding_by_name, lsp->name);
-}
-
-static uint32_t
-allocate_port_key(struct hmap *pb_tnlids)
-{
- static uint32_t hint;
- return ovn_allocate_tnlid(pb_tnlids, "transit port",
- 1, (1u << 15) - 1, &hint);
}
static void
@@ -947,18 +1093,24 @@ port_binding_run(struct ic_context *ctx)
return;
}
- struct shash isb_all_local_pbs = SHASH_INITIALIZER(&isb_all_local_pbs);
- struct shash_node *node;
+ struct shash switch_all_local_pbs =
+ SHASH_INITIALIZER(&switch_all_local_pbs);
+ struct shash router_all_local_pbs =
+ SHASH_INITIALIZER(&router_all_local_pbs);
+ struct hmap pb_tnlids = HMAP_INITIALIZER(&pb_tnlids);
const struct icsbrec_port_binding *isb_pb;
const struct icsbrec_port_binding *isb_pb_key =
icsbrec_port_binding_index_init_row(ctx->icsbrec_port_binding_by_az);
icsbrec_port_binding_index_set_availability_zone(isb_pb_key,
ctx->runned_az);
-
ICSBREC_PORT_BINDING_FOR_EACH_EQUAL (isb_pb, isb_pb_key,
ctx->icsbrec_port_binding_by_az) {
- shash_add(&isb_all_local_pbs, isb_pb->logical_port, isb_pb);
+ ic_pb_get_type(isb_pb) != IC_ROUTER_PORT
+ ? shash_add(&switch_all_local_pbs, isb_pb->logical_port, isb_pb)
+ : shash_add(&router_all_local_pbs, isb_pb->logical_port, isb_pb);
+
+ ovn_add_tnlid(&pb_tnlids, isb_pb->tunnel_key);
}
icsbrec_port_binding_index_destroy_row(isb_pb_key);
@@ -972,21 +1124,23 @@ port_binding_run(struct ic_context *ctx)
}
struct shash local_pbs = SHASH_INITIALIZER(&local_pbs);
struct shash remote_pbs = SHASH_INITIALIZER(&remote_pbs);
- struct hmap pb_tnlids = HMAP_INITIALIZER(&pb_tnlids);
- isb_pb_key = icsbrec_port_binding_index_init_row(
- ctx->icsbrec_port_binding_by_ts);
- icsbrec_port_binding_index_set_transit_switch(isb_pb_key, ts->name);
- ICSBREC_PORT_BINDING_FOR_EACH_EQUAL (isb_pb, isb_pb_key,
- ctx->icsbrec_port_binding_by_ts) {
+ isb_pb_key = icsbrec_port_binding_index_init_row(
+ ctx->icsbrec_port_binding_by_nb_ic_uuid_type);
+ icsbrec_port_binding_index_set_nb_ic_uuid(isb_pb_key,
+ &ts->header_.uuid, 1);
+ icsbrec_port_binding_index_set_type(isb_pb_key, "transit-switch-port");
+ ICSBREC_PORT_BINDING_FOR_EACH_EQUAL (
+ isb_pb, isb_pb_key, ctx->icsbrec_port_binding_by_nb_ic_uuid_type) {
if (isb_pb->availability_zone == ctx->runned_az) {
- shash_add(&local_pbs, isb_pb->logical_port, isb_pb);
- shash_find_and_delete(&isb_all_local_pbs,
+ shash_find_and_delete(&switch_all_local_pbs,
isb_pb->logical_port);
+ shash_add(&local_pbs, isb_pb->logical_port, isb_pb);
} else {
- shash_add(&remote_pbs, isb_pb->logical_port, isb_pb);
+ if (!shash_find_data(&remote_pbs, isb_pb->logical_port)) {
+ shash_add(&remote_pbs, isb_pb->logical_port, isb_pb);
+ }
}
- ovn_add_tnlid(&pb_tnlids, isb_pb->tunnel_key);
}
icsbrec_port_binding_index_destroy_row(isb_pb_key);
@@ -997,15 +1151,17 @@ port_binding_run(struct ic_context *ctx)
if (!strcmp(lsp->type, "router")
|| !strcmp(lsp->type, "switch")) {
/* The port is local. */
- sb_pb = find_lsp_in_sb(ctx, lsp);
+ sb_pb = find_lp_in_sb(ctx, lsp->name);
if (!sb_pb) {
continue;
}
+
isb_pb = shash_find_and_delete(&local_pbs, lsp->name);
if (!isb_pb) {
- uint32_t pb_tnl_key = allocate_port_key(&pb_tnlids);
- create_isb_pb(ctx, sb_pb, ctx->runned_az,
- ts->name, pb_tnl_key);
+ isb_pb = create_isb_pb(
+ ctx, sb_pb->logical_port, ctx->runned_az, ts->name,
+ &ts->header_.uuid, "transit-switch-port", &pb_tnlids);
+ sync_ts_isb_pb(ctx, sb_pb, isb_pb);
} else {
sync_local_port(ctx, isb_pb, sb_pb, lsp);
}
@@ -1015,7 +1171,7 @@ port_binding_run(struct ic_context *ctx)
if (!isb_pb) {
nbrec_logical_switch_update_ports_delvalue(ls, lsp);
} else {
- sb_pb = find_lsp_in_sb(ctx, lsp);
+ sb_pb = find_lp_in_sb(ctx, lsp->name);
if (!sb_pb) {
continue;
}
@@ -1027,26 +1183,97 @@ port_binding_run(struct ic_context *ctx)
}
}
- /* Delete extra port-binding from ISB */
- SHASH_FOR_EACH (node, &local_pbs) {
- icsbrec_port_binding_delete(node->data);
+ struct shash_node *node;
+ SHASH_FOR_EACH_SAFE (node, &local_pbs) {
+ isb_pb = node->data;
+ icsbrec_port_binding_delete(isb_pb);
}
/* Create lsp in NB for remote ports */
- SHASH_FOR_EACH (node, &remote_pbs) {
- create_nb_lsp(ctx, node->data, ls);
+ SHASH_FOR_EACH_SAFE (node, &remote_pbs) {
+ isb_pb = node->data;
+ create_nb_lsp(ctx, isb_pb, ls);
}
shash_destroy(&local_pbs);
shash_destroy(&remote_pbs);
- ovn_destroy_tnlids(&pb_tnlids);
}
- SHASH_FOR_EACH (node, &isb_all_local_pbs) {
- icsbrec_port_binding_delete(node->data);
+ struct shash_node *node;
+ SHASH_FOR_EACH_SAFE (node, &switch_all_local_pbs) {
+ isb_pb = node->data;
+ icsbrec_port_binding_delete(isb_pb);
+ }
+ shash_destroy(&switch_all_local_pbs);
+
+ const struct icnbrec_transit_router *tr;
+ ICNBREC_TRANSIT_ROUTER_FOR_EACH (tr, ctx->ovninb_idl) {
+ struct shash nb_ports = SHASH_INITIALIZER(&nb_ports);
+ const struct nbrec_logical_router *lr = find_tr_in_nb(ctx, tr->name);
+ if (!lr) {
+ VLOG_DBG("Transit router %s not found in NB.", tr->name);
+ continue;
+ }
+
+ for (size_t i = 0; i < tr->n_ports; i++) {
+ const struct icnbrec_transit_router_port *trp = tr->ports[i];
+ shash_add(&nb_ports, trp->name, trp);
+ isb_pb = shash_find_and_delete(&router_all_local_pbs, trp->name);
+ if (!isb_pb) {
+ if (!trp_port_is_remote(ctx, trp->chassis)) {
+ isb_pb = create_isb_pb(ctx, trp->name, ctx->runned_az,
+ tr->name, &tr->header_.uuid,
+ "transit-router-port", &pb_tnlids);
+ icsbrec_port_binding_set_address(isb_pb, trp->mac);
+ continue;
+ } else {
+ isb_pb_key = icsbrec_port_binding_index_init_row(
+ ctx->icsbrec_port_binding_by_tr);
+ icsbrec_port_binding_index_set_transit_switch(isb_pb_key,
+ tr->name);
+ ICSBREC_PORT_BINDING_FOR_EACH_EQUAL (
+ isb_pb, isb_pb_key, ctx->icsbrec_port_binding_by_tr) {
+ if (!strcmp(trp->name, isb_pb->logical_port)) {
+ break;
+ }
+ }
+ icsbrec_port_binding_index_destroy_row(isb_pb_key);
+ }
+ }
+ /* Don't allow remote ports to create NB LRP until ICSB entry is
+ * created in the appropriate AZ. */
+ if (isb_pb) {
+ const struct nbrec_logical_router_port *lrp =
+ get_lrp_by_lrp_name(ctx, trp->name);
+
+ if (!lrp) {
+ lrp = trp_port_create(ctx, lr, trp);
+ }
+
+ sync_router_port(isb_pb, trp, lrp);
+ }
+ }
+
+ for (size_t i = 0; i < lr->n_ports; i++) {
+ const struct nbrec_logical_router_port *lrp = lr->ports[i];
+ if (!shash_find_and_delete(&nb_ports, lrp->name) &&
+ smap_get(&lrp->options, "interconn-tr")) {
+ nbrec_logical_router_port_delete(lrp);
+ nbrec_logical_router_update_ports_delvalue(lr, lrp);
+ }
+ }
+
+ shash_destroy(&nb_ports);
}
+ ovn_destroy_tnlids(&pb_tnlids);
- shash_destroy(&isb_all_local_pbs);
+ SHASH_FOR_EACH_SAFE (node, &router_all_local_pbs) {
+ isb_pb = node->data;
+ icsbrec_port_binding_delete(isb_pb);
+ shash_delete(&router_all_local_pbs, node);
+ }
+
+ shash_destroy(&router_all_local_pbs);
}
struct ic_router_info {
@@ -1727,20 +1954,6 @@ get_lrp_name_by_ts_port_name(struct ic_context *ctx,
const char *ts_port_name)
return smap_get(&nb_lsp->options, "router-port");
}
-static const struct nbrec_logical_router_port *
-get_lrp_by_lrp_name(struct ic_context *ctx, const char *lrp_name)
-{
- const struct nbrec_logical_router_port *lrp;
- const struct nbrec_logical_router_port *lrp_key =
- nbrec_logical_router_port_index_init_row(ctx->nbrec_lrp_by_name);
- nbrec_logical_router_port_index_set_name(lrp_key, lrp_name);
- lrp = nbrec_logical_router_port_index_find(ctx->nbrec_lrp_by_name,
- lrp_key);
- nbrec_logical_router_port_index_destroy_row(lrp_key);
-
- return lrp;
-}
-
static const struct nbrec_logical_router_port *
find_lrp_of_nexthop(struct ic_context *ctx,
const struct icsbrec_route *isb_route)
@@ -2277,6 +2490,9 @@ route_run(struct ic_context *ctx)
ICSBREC_PORT_BINDING_FOR_EACH_EQUAL (isb_pb, isb_pb_key,
ctx->icsbrec_port_binding_by_az)
{
+ if (ic_pb_get_type(isb_pb) == IC_ROUTER_PORT) {
+ continue;
+ }
const struct nbrec_logical_switch_port *nb_lsp;
nb_lsp = get_lsp_by_ts_port_name(ctx, isb_pb->logical_port);
@@ -3255,6 +3471,8 @@ main(int argc, char *argv[])
struct ovsdb_idl_index *nbrec_ls_by_name
= ovsdb_idl_index_create1(ovnnb_idl_loop.idl,
&nbrec_logical_switch_col_name);
+ struct ovsdb_idl_index *nbrec_lr_by_name = ovsdb_idl_index_create1(
+ ovnnb_idl_loop.idl, &nbrec_logical_router_col_name);
struct ovsdb_idl_index *nbrec_port_by_name
= ovsdb_idl_index_create1(ovnnb_idl_loop.idl,
&nbrec_logical_switch_port_col_name);
@@ -3289,14 +3507,18 @@ main(int argc, char *argv[])
= ovsdb_idl_index_create1(ovnisb_idl_loop.idl,
&icsbrec_port_binding_col_availability_zone);
- struct ovsdb_idl_index *icsbrec_port_binding_by_ts
+ struct ovsdb_idl_index *icsbrec_port_binding_by_tr
= ovsdb_idl_index_create1(ovnisb_idl_loop.idl,
&icsbrec_port_binding_col_transit_switch);
- struct ovsdb_idl_index *icsbrec_port_binding_by_ts_az
- = ovsdb_idl_index_create2(ovnisb_idl_loop.idl,
- &icsbrec_port_binding_col_transit_switch,
- &icsbrec_port_binding_col_availability_zone);
+ struct ovsdb_idl_index *icsbrec_port_binding_by_nb_ic_uuid =
+ ovsdb_idl_index_create1(ovnisb_idl_loop.idl,
+ &icsbrec_port_binding_col_nb_ic_uuid);
+
+ struct ovsdb_idl_index *icsbrec_port_binding_by_nb_ic_uuid_type =
+ ovsdb_idl_index_create2(ovnisb_idl_loop.idl,
+ &icsbrec_port_binding_col_nb_ic_uuid,
+ &icsbrec_port_binding_col_type);
struct ovsdb_idl_index *icsbrec_route_by_az
= ovsdb_idl_index_create1(ovnisb_idl_loop.idl,
@@ -3371,6 +3593,7 @@ main(int argc, char *argv[])
.ovnisb_idl = ovnisb_idl_loop.idl,
.ovnisb_txn = ovsdb_idl_loop_run(&ovnisb_idl_loop),
.nbrec_ls_by_name = nbrec_ls_by_name,
+ .nbrec_lr_by_name = nbrec_lr_by_name,
.nbrec_lrp_by_name = nbrec_lrp_by_name,
.nbrec_port_by_name = nbrec_port_by_name,
.sbrec_port_binding_by_name = sbrec_port_binding_by_name,
@@ -3384,8 +3607,11 @@ main(int argc, char *argv[])
.icnbrec_transit_switch_by_name =
icnbrec_transit_switch_by_name,
.icsbrec_port_binding_by_az = icsbrec_port_binding_by_az,
- .icsbrec_port_binding_by_ts = icsbrec_port_binding_by_ts,
- .icsbrec_port_binding_by_ts_az = icsbrec_port_binding_by_ts_az,
+ .icsbrec_port_binding_by_tr = icsbrec_port_binding_by_tr,
+ .icsbrec_port_binding_by_nb_ic_uuid =
+ icsbrec_port_binding_by_nb_ic_uuid,
+ .icsbrec_port_binding_by_nb_ic_uuid_type =
+ icsbrec_port_binding_by_nb_ic_uuid_type,
.icsbrec_route_by_az = icsbrec_route_by_az,
.icsbrec_route_by_ts = icsbrec_route_by_ts,
.icsbrec_route_by_ts_az = icsbrec_route_by_ts_az,
diff --git a/tests/ovn-ic-nbctl.at b/tests/ovn-ic-nbctl.at
index 7aa3a46d8..4c5269784 100644
--- a/tests/ovn-ic-nbctl.at
+++ b/tests/ovn-ic-nbctl.at
@@ -97,5 +97,34 @@ AT_CHECK([ovn-ic-nbctl tr-del tr2], [1], [],
])
AT_CHECK([ovn-ic-nbctl --if-exists tr-del tr2])
+AT_CHECK([ovn-ic-nbctl tr-del tr0])
+
+AT_CHECK([ovn-ic-nbctl trp-add tr0 tr0-p0 00:11:22:11:22:33 192.168.10.10/24
chassis=chassis], [1], [],
+ [ovn-ic-nbctl: tr0: router name not found
+])
+
+AT_CHECK([ovn-ic-nbctl tr-add tr0])
+AT_CHECK([ovn-ic-nbctl trp-add tr0 tr0-p0], [1], [],
+ [ovn-ic-nbctl: 'trp-add' command requires at least 5 arguments
+])
+
+AT_CHECK([ovn-ic-nbctl trp-add tr0 tr0-p0 00:11:22:11:22:33 192.168.10.10/24
chassis=chassis])
+AT_CHECK([ovn-ic-nbctl trp-add tr0 tr0-p0 00:11:22:11:22:33 192.168.10.10/24
chassis=chassis], [1], [],
+ [ovn-ic-nbctl: tr0-p0: a port with this name already exists
+])
+
+AT_CHECK([ovn-ic-nbctl --may-exist trp-add tr0 tr0-p0 00:11:22:11:22:33
192.168.10.10/24 chassis=chassis])
+
+AT_CHECK([ovn-ic-nbctl trp-del], [1], [],
+ [ovn-ic-nbctl: 'trp-del' command requires at least 1 arguments
+])
+AT_CHECK([ovn-ic-nbctl trp-del tr0-p0])
+AT_CHECK([ovn-ic-nbctl trp-del tr0-p0], [1], [],
+ [ovn-ic-nbctl: tr0-p0: router port name not found
+])
+AT_CHECK([ovn-ic-nbctl --if-exists trp-del tr0-p0])
+
+AT_CHECK([ovn-ic-nbctl --if-exists tr-del tr0])
+
OVN_IC_NBCTL_TEST_STOP
AT_CLEANUP
diff --git a/tests/ovn-ic.at b/tests/ovn-ic.at
index a5e2e852d..7e3365048 100644
--- a/tests/ovn-ic.at
+++ b/tests/ovn-ic.at
@@ -4336,3 +4336,226 @@ wait_row_count Datapath_Binding 0
OVN_CLEANUP_IC([az1], [az2])
AT_CLEANUP
])
+
+OVN_FOR_EACH_NORTHD([
+AT_SETUP([ovn-ic -- Add transit router remote port])
+
+ovn_init_ic_db
+net_add n1
+net_add n2
+ovn_start az1
+ovn_start az2
+
+OVS_WAIT_UNTIL([test 2 = `ovn-ic-sbctl show | wc -l`])
+
+check ovn-ic-nbctl --wait=sb sync
+AT_CHECK([ovn-ic-sbctl show], [0], [dnl
+availability-zone az1
+availability-zone az2
+])
+
+sim_add hv1
+as hv1
+ovs-vsctl add-br br-phys
+ovn_az_attach az1 n1 br-phys 192.168.0.1
+ovs-vsctl set open . external-ids:ovn-is-interconn=true
+wait_row_count Chassis 1 name=hv1
+
+sim_add hv2
+as hv2
+ovs-vsctl add-br br-phys
+ovn_az_attach az2 n2 br-phys 192.168.0.2
+ovs-vsctl set open . external-ids:ovn-is-interconn=true
+wait_row_count Chassis 1 name=hv2
+
+ovn_as az1
+check ovn-ic-nbctl tr-add tr0
+check ovn-ic-nbctl trp-add tr0 tr0-p0 00:00:00:11:22:00 192.168.10.10/24
192.168.10.20/24 chassis=hv2
+wait_row_count Port_Binding 1
+
+# Check that port type is correctly set to remote
+check_row_count sb:Port_Binding 1 logical_port=tr0-p0
+check_row_count sb:Port_Binding 1 type=remote
+az1_tunnel_key=$(ovn-sbctl --bare --columns=tunnel_key find Port_Binding)
+az1_mac=$(ovn-sbctl --bare --columns=mac find Port_Binding)
+check_row_count nb:Logical_Router_Port 1
options:requested-tnl-key="${az1_tunnel_key}"
+
+ovn_as az2
+wait_row_count Port_Binding 1
+# Check that port type is correctly set to local
+check_row_count sb:Port_Binding 1 logical_port=tr0-p0
+check_row_count sb:Port_Binding 1 type=patch
+az2_tunnel_key=$(ovn-sbctl --bare --columns=tunnel_key find Port_Binding)
+az2_mac=$(ovn-sbctl --bare --columns=mac find Port_Binding)
+# Check that nbctl port tunnel_key matches sbctl
+check_row_count nb:Logical_Router_Port 1
options:requested-tnl-key="${az1_tunnel_key}"
+check test "${az1_tunnel_key}"=="${az2_tunnel_key}"
+check test "${az1_mac}"=="${az2_mac}"
+OVN_CLEANUP_IC([az1], [az2])
+AT_CLEANUP
+])
+
+OVN_FOR_EACH_NORTHD([
+AT_SETUP([ovn-ic -- Transit router delete port])
+
+ovn_init_ic_db
+net_add n1
+net_add n2
+ovn_start az1
+ovn_start az2
+
+check ovn-ic-nbctl --wait=sb sync
+AT_CHECK([ovn-ic-sbctl show], [0], [dnl
+availability-zone az1
+availability-zone az2
+])
+
+sim_add hv1
+as hv1
+ovs-vsctl add-br br-phys
+ovn_az_attach az1 n1 br-phys 192.168.0.1
+ovs-vsctl set open . external-ids:ovn-is-interconn=true
+check ovs-vsctl set open . external_ids:ovn-monitor-all=true
+wait_row_count Chassis 1 name=hv1
+
+sim_add hv2
+as hv2
+ovs-vsctl add-br br-phys
+ovn_az_attach az2 n2 br-phys 192.168.0.2
+ovs-vsctl set open . external-ids:ovn-is-interconn=true
+check ovs-vsctl set open . external_ids:ovn-monitor-all=true
+wait_row_count Chassis 1 name=hv2
+
+ovn_as az1
+check ovn-ic-nbctl --wait=sb tr-add tr0
+check ovn-ic-nbctl --wait=sb trp-add tr0 tr0-p0 00:00:00:11:22:00
192.168.10.10/24 192.168.10.20/24 chassis=hv2
+wait_row_count Port_Binding 1
+check_row_count sb:Port_Binding 1 logical_port=tr0-p0
+
+ovn_as az2
+wait_row_count Port_Binding 1
+check_row_count sb:Port_Binding 1 logical_port=tr0-p0
+
+ovn_as az1
+check ovn-ic-nbctl --wait=sb trp-del tr0-p0
+wait_row_count Port_Binding 0
+
+ovn_as az2
+wait_row_count Port_Binding 0
+
+OVN_CLEANUP_IC([az1], [az2])
+AT_CLEANUP
+])
+
+OVN_FOR_EACH_NORTHD([
+AT_SETUP([ovn-ic -- Port binding deletion upon transit router deletion])
+
+ovn_init_ic_db
+net_add n1
+net_add n2
+ovn_start az1
+ovn_start az2
+
+check ovn-ic-nbctl --wait=sb sync
+AT_CHECK([ovn-ic-sbctl show], [0], [dnl
+availability-zone az1
+availability-zone az2
+])
+
+sim_add hv1
+as hv1
+ovs-vsctl add-br br-phys
+ovn_az_attach az1 n1 br-phys 192.168.0.1
+ovs-vsctl set open . external-ids:ovn-is-interconn=true
+check ovs-vsctl set open . external_ids:ovn-monitor-all=true
+wait_row_count Chassis 1 name=hv1
+
+sim_add hv2
+as hv2
+ovs-vsctl add-br br-phys
+ovn_az_attach az2 n2 br-phys 192.168.0.2
+ovs-vsctl set open . external-ids:ovn-is-interconn=true
+check ovs-vsctl set open . external_ids:ovn-monitor-all=true
+wait_row_count Chassis 1 name=hv2
+
+ovn_as az1
+check ovn-ic-nbctl --wait=sb tr-add tr0
+check ovn-ic-nbctl --wait=sb trp-add tr0 tr0-p0 00:00:00:11:22:00
192.168.10.10/24 192.168.10.20/24 chassis=hv2
+wait_row_count Port_Binding 1
+check_row_count sb:Port_Binding 1 logical_port=tr0-p0
+
+ovn_as az2
+wait_row_count Port_Binding 1
+check_row_count sb:Port_Binding 1 logical_port=tr0-p0
+
+ovn_as az1
+check ovn-ic-nbctl tr-del tr0
+wait_row_count Port_Binding 0
+
+ovn_as az2
+wait_row_count Port_Binding 0
+
+AT_CHECK([ovn-ic-nbctl list Transit_Router], [0], [dnl
+])
+AT_CHECK([ovn-ic-nbctl list Transit_Router_Port], [0], [dnl
+])
+
+OVN_CLEANUP_IC([az1], [az2])
+AT_CLEANUP
+])
+
+OVN_FOR_EACH_NORTHD([
+AT_SETUP([ovn-ic -- Duplicate port addresses])
+
+ovn_init_ic_db
+net_add n1
+net_add n2
+ovn_start az1
+ovn_start az2
+
+OVS_WAIT_UNTIL([test 2 = `ovn-ic-sbctl show | wc -l`])
+
+check ovn-ic-nbctl --wait=sb sync
+AT_CHECK([ovn-ic-sbctl show], [0], [dnl
+availability-zone az1
+availability-zone az2
+])
+
+sim_add hv1
+as hv1
+ovs-vsctl add-br br-phys
+ovn_az_attach az1 n1 br-phys 192.168.0.1
+ovs-vsctl set open . external-ids:ovn-is-interconn=true
+check ovs-vsctl set open . external_ids:ovn-monitor-all=true
+wait_row_count Chassis 1 name=hv1
+
+sim_add hv2
+as hv2
+ovs-vsctl add-br br-phys
+ovn_az_attach az2 n2 br-phys 192.168.0.2
+ovs-vsctl set open . external-ids:ovn-is-interconn=true
+check ovs-vsctl set open . external_ids:ovn-monitor-all=true
+wait_row_count Chassis 1 name=hv2
+
+ovn_as az1
+check ovn-ic-nbctl --wait=sb tr-add tr0
+check ovn-ic-nbctl --wait=sb trp-add tr0 tr0-p0 00:00:00:11:22:00 \
+ 192.168.10.10/24 192.168.10.20/24 192.168.10.10/24 192.168.10.20/24 \
+ chassis=hv2
+wait_row_count Port_Binding 1 logical_port=tr0-p0
+check_row_count nb:Logical_Router_Port 1 networks="192.168.10.10/24
192.168.10.20/24"
+
+ovn_as az2
+wait_row_count Port_Binding 1 logical_port=tr0-p0
+check_row_count nb:Logical_Router_Port 1 networks="192.168.10.10/24
192.168.10.20/24"
+
+ovn_as az1
+check ovn-ic-nbctl --wait=sb tr-del tr0
+check_row_count sb:Datapath_Binding 0
+
+ovn_as az2
+check_row_count sb:Datapath_Binding 0
+
+OVN_CLEANUP_IC([az1], [az2])
+AT_CLEANUP
+])
diff --git a/utilities/ovn-ic-nbctl.c b/utilities/ovn-ic-nbctl.c
index 857f21dbf..24d376559 100644
--- a/utilities/ovn-ic-nbctl.c
+++ b/utilities/ovn-ic-nbctl.c
@@ -341,6 +341,9 @@ Transit router commands:\n\
tr-add ROUTER create a transit router named ROUTER\n\
tr-del ROUTER delete ROUTER\n\
tr-list print all transit routers\n\
+ trp-add ROUTER PORT MAC [NETWORK]...[chassis=CHASSIS]\n\
+ add a transit router PORT\n\
+ trp-del PORT delete a transit router PORT\n\
\n\
Connection commands:\n\
get-connection print the connections\n\
@@ -410,6 +413,11 @@ static struct cmd_show_table cmd_show_tables[] = {
{NULL, NULL, NULL},
{NULL, NULL, NULL}},
+ {&icnbrec_table_transit_router_port,
+ &icnbrec_transit_router_port_col_name,
+ {&icnbrec_transit_router_port_col_tr_uuid, NULL, NULL},
+ {NULL, NULL, NULL}},
+
{NULL, NULL, {NULL, NULL, NULL}, {NULL, NULL, NULL}},
};
@@ -576,6 +584,38 @@ ic_nbctl_tr_add(struct ctl_context *ctx)
icnbrec_transit_router_set_name(tr, tr_name);
}
+static char *
+trp_by_name_or_uuid(struct ctl_context *ctx, const char *id, bool must_exist,
+ const struct icnbrec_transit_router_port **trp_p)
+{
+ const struct icnbrec_transit_router_port *trp = NULL;
+ *trp_p = NULL;
+ struct uuid trp_uuid;
+ bool is_uuid = uuid_from_string(&trp_uuid, id);
+ if (is_uuid) {
+ trp = icnbrec_transit_router_port_get_for_uuid(ctx->idl, &trp_uuid);
+ }
+
+ if (!trp) {
+ const struct icnbrec_transit_router_port *iter;
+
+ ICNBREC_TRANSIT_ROUTER_PORT_FOR_EACH (iter, ctx->idl) {
+ if (!strcmp(iter->name, id)) {
+ trp = iter;
+ break;
+ }
+ }
+ }
+
+ if (!trp && must_exist) {
+ return xasprintf("%s: router port %s not found", id,
+ is_uuid ? "UUID" : "name");
+ }
+
+ *trp_p = trp;
+ return NULL;
+}
+
static void
ic_nbctl_tr_del(struct ctl_context *ctx)
{
@@ -595,6 +635,38 @@ ic_nbctl_tr_del(struct ctl_context *ctx)
icnbrec_transit_router_delete(tr);
}
+static void
+ic_nbctl_trp_del(struct ctl_context *ctx)
+{
+ bool must_exist = !shash_find(&ctx->options, "--if-exists");
+ const char *trp_name = ctx->argv[1];
+ const struct icnbrec_transit_router_port *trp = NULL;
+
+ ctx->error = trp_by_name_or_uuid(ctx, trp_name, must_exist, &trp);
+ if (ctx->error) {
+ return;
+ }
+
+ if (!trp) {
+ return;
+ }
+
+ const struct icnbrec_transit_router *tr = NULL;
+ char *tr_uuid = uuid_to_string(&trp->tr_uuid);
+ ctx->error = tr_by_name_or_uuid(ctx, tr_uuid, true, &tr);
+ free(tr_uuid);
+ if (ctx->error) {
+ return;
+ }
+
+ if (!trp) {
+ return;
+ }
+
+ icnbrec_transit_router_update_ports_delvalue(tr, trp);
+ icnbrec_transit_router_port_delete(trp);
+}
+
static void
ic_nbctl_tr_list(struct ctl_context *ctx)
{
@@ -616,6 +688,123 @@ ic_nbctl_tr_list(struct ctl_context *ctx)
smap_destroy(&routers);
free(nodes);
}
+
+static void
+ic_nbctl_trp_add(struct ctl_context *ctx)
+{
+ bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL;
+ const char *tr_name = ctx->argv[1];
+ const char *trp_name = ctx->argv[2];
+ const char *mac = ctx->argv[3];
+ const char **networks = (const char **) &ctx->argv[4];
+ const struct icnbrec_transit_router *tr;
+
+ ctx->error = tr_by_name_or_uuid(ctx, tr_name, true, &tr);
+ if (ctx->error) {
+ return;
+ }
+
+ const struct icnbrec_transit_router_port *trp;
+ ctx->error = trp_by_name_or_uuid(ctx, trp_name, false, &trp);
+ if (ctx->error) {
+ return;
+ }
+
+ /* Parse networks*/
+ int n_networks = ctx->argc - 4;
+ for (int i = 4; i < ctx->argc; i++) {
+ if (strchr(ctx->argv[i], '=')) {
+ n_networks = i - 4;
+ break;
+ }
+ }
+
+ char **settings = (char **) &ctx->argv[n_networks + 4];
+ int n_settings = ctx->argc - 4 - n_networks;
+ struct eth_addr ea;
+ if (!eth_addr_from_string(mac, &ea)) {
+ ctl_error(ctx, "%s: invalid mac address %s", trp_name, mac);
+ return;
+ }
+
+ if (trp) {
+ if (!may_exist) {
+ ctl_error(ctx, "%s: a port with this name already exists",
+ trp_name);
+ return;
+ }
+
+ struct eth_addr lrp_ea;
+ eth_addr_from_string(trp->mac, &lrp_ea);
+ if (!eth_addr_equals(ea, lrp_ea)) {
+ ctl_error(ctx, "%s: port already exists with mac %s", trp_name,
+ trp->mac);
+ return;
+ }
+
+ struct sset *new_networks = lrp_network_sset(networks, n_networks);
+ if (!new_networks) {
+ ctl_error(ctx, "%s: Invalid networks configured", trp_name);
+ return;
+ }
+
+ struct sset *orig_networks =
+ lrp_network_sset((const char **) trp->networks, trp->n_networks);
+ if (!orig_networks) {
+ ctl_error(ctx, "%s: Existing port has invalid networks configured",
+ trp_name);
+ sset_destroy(new_networks);
+ free(new_networks);
+ return;
+ }
+
+ bool same_networks = sset_equals(orig_networks, new_networks);
+ sset_destroy(orig_networks);
+ free(orig_networks);
+ sset_destroy(new_networks);
+ free(new_networks);
+ if (!same_networks) {
+ ctl_error(ctx, "%s: port already exists with different network",
+ trp_name);
+ return;
+ }
+
+ return;
+ }
+
+ for (int i = 0; i < n_networks; i++) {
+ ovs_be32 ipv4;
+ unsigned int plen;
+ char *error = ip_parse_cidr(networks[i], &ipv4, &plen);
+ if (error) {
+ free(error);
+ struct in6_addr ipv6;
+ error = ipv6_parse_cidr(networks[i], &ipv6, &plen);
+ if (error) {
+ free(error);
+ ctl_error(ctx, "%s: invalid network address: %s", trp_name,
+ networks[i]);
+ return;
+ }
+ }
+ }
+
+ trp = icnbrec_transit_router_port_insert(ctx->txn);
+ icnbrec_transit_router_port_set_name(trp, trp_name);
+ icnbrec_transit_router_port_set_mac(trp, mac);
+ icnbrec_transit_router_port_set_tr_uuid(trp, tr->header_.uuid);
+ icnbrec_transit_router_port_set_networks(trp, networks, n_networks);
+ for (int i = 0; i < n_settings; i++) {
+ ctx->error = ctl_set_column("Transit_Router_Port", &trp->header_,
+ settings[i], ctx->symtab);
+ if (ctx->error) {
+ return;
+ }
+ }
+
+ icnbrec_transit_router_update_ports_addvalue(tr, trp);
+}
+
static void
verify_connections(struct ctl_context *ctx)
{
@@ -826,6 +1015,11 @@ static const struct ctl_table_class
tables[ICNBREC_N_TABLES] = {
[ICNBREC_TABLE_TRANSIT_ROUTER].row_ids[0] =
{&icnbrec_transit_router_col_name, NULL, NULL},
+
+ [ICNBREC_TABLE_TRANSIT_ROUTER_PORT].row_ids =
+ {{&icnbrec_transit_router_port_col_name, NULL, NULL},
+ {&icnbrec_transit_router_port_col_tr_uuid, NULL, NULL},
+ {&icnbrec_transit_router_port_col_mac, NULL, NULL}},
};
@@ -1133,6 +1327,11 @@ static const struct ctl_command_syntax
ic_nbctl_commands[] = {
{ "tr-del", 1, 1, "ROUTER", NULL, ic_nbctl_tr_del, NULL, "--if-exists",
RW },
{ "tr-list", 0, 0, "", NULL, ic_nbctl_tr_list, NULL, "", RO },
+ { "trp-add", 5, INT_MAX,
+ "ROUTER PORT MAC [NETWORK]...[COLUMN[:KEY]=VALUE]...",
+ NULL, ic_nbctl_trp_add, NULL, "--may-exist", RW },
+ { "trp-del", 1, 1, "PORT", NULL, ic_nbctl_trp_del, NULL, "--if-exists",
+ RW },
/* Connection commands. */
{"get-connection", 0, 0, "", pre_connection, cmd_get_connection, NULL, "",
--
2.51.0
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev