Skip to content

Commit

Permalink
northd: Add support for spine-leaf logical switch topology.
Browse files Browse the repository at this point in the history
Signed-off-by: Ilya Maximets <[email protected]>
  • Loading branch information
igsilya committed Jan 23, 2025
1 parent 7e63b02 commit 954fcb3
Show file tree
Hide file tree
Showing 10 changed files with 402 additions and 9 deletions.
1 change: 1 addition & 0 deletions lib/ovn-util.c
Original file line number Diff line number Diff line change
Expand Up @@ -610,6 +610,7 @@ static const char *OVN_NB_LSP_TYPES[] = {
"localnet",
"localport",
"router",
"switch",
"vtep",
"external",
"virtual",
Expand Down
42 changes: 38 additions & 4 deletions northd/northd.c
Original file line number Diff line number Diff line change
Expand Up @@ -1362,6 +1362,12 @@ lsp_is_router(const struct nbrec_logical_switch_port *nbsp)
return !strcmp(nbsp->type, "router");
}

static bool
lsp_is_switch(const struct nbrec_logical_switch_port *nbsp)
{
return !strcmp(nbsp->type, "switch");
}

static bool
lsp_is_remote(const struct nbrec_logical_switch_port *nbsp)
{
Expand Down Expand Up @@ -1449,7 +1455,8 @@ lsp_force_fdb_lookup(const struct ovn_port *op)
static struct ovn_port *
ovn_port_get_peer(const struct hmap *lr_ports, struct ovn_port *op)
{
if (!op->nbsp || !lsp_is_router(op->nbsp) || is_cr_port(op)) {
if (!op->nbsp || (!lsp_is_router(op->nbsp) && !lsp_is_switch(op->nbsp))
|| is_cr_port(op)) {
return NULL;
}

Expand Down Expand Up @@ -2114,6 +2121,12 @@ parse_lsp_addrs(struct ovn_port *op)
}
op->n_lsp_non_router_addrs = op->n_lsp_addrs;

/* Addresses are not leaked between directly connected switches, so
* we should expect unknown addresses behind the port. */
if (lsp_is_switch(nbsp)) {
op->has_unknown = true;
}

op->ps_addrs
= xmalloc(sizeof *op->ps_addrs * nbsp->n_port_security);
for (size_t j = 0; j < nbsp->n_port_security; j++) {
Expand Down Expand Up @@ -2401,7 +2414,8 @@ join_logical_ports(const struct sbrec_port_binding_table *sbrec_pb_table,
}

/* Connect logical router ports, and logical switch ports of type "router",
* to their peers. */
* to their peers. As well as logical switch ports of type "switch" to
* theirs. */
struct ovn_port *op;
HMAP_FOR_EACH (op, key_node, ports) {
if (op->nbsp && lsp_is_router(op->nbsp) && !op->primary_port) {
Expand Down Expand Up @@ -2452,6 +2466,20 @@ join_logical_ports(const struct sbrec_port_binding_table *sbrec_pb_table,
arp_proxy, op->nbsp->name);
}
}
} else if (op->nbsp && op->nbsp->peer && lsp_is_switch(op->nbsp)) {
struct ovn_port *peer = ovn_port_find(ports, op->nbsp->peer);

if (!peer) {
continue;
}
if (!peer->nbsp || !lsp_is_switch(peer->nbsp)) {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);

VLOG_WARN_RL(&rl, "Bad configuration: The peer of a switch "
"port %s in not a 'switch' port", op->key);
continue;
}
op->peer = peer;
} else if (op->nbrp && op->nbrp->peer && !is_cr_port(op)) {
struct ovn_port *peer = ovn_port_find(ports, op->nbrp->peer);
if (peer) {
Expand Down Expand Up @@ -3255,9 +3283,15 @@ ovn_port_update_sbrec(struct ovsdb_idl_txn *ovnsb_txn,
sbrec_port_binding_set_chassis(op->sb, NULL);
}

if (lsp_is_switch(op->nbsp) && op->peer) {
smap_add(&options, "peer", op->peer->key);
}

sbrec_port_binding_set_options(op->sb, &options);
smap_destroy(&options);
if (ovn_is_known_nb_lsp_type(op->nbsp->type)) {
if (lsp_is_switch(op->nbsp)) {
sbrec_port_binding_set_type(op->sb, "patch");
} else if (ovn_is_known_nb_lsp_type(op->nbsp->type)) {
sbrec_port_binding_set_type(op->sb, op->nbsp->type);
} else {
static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
Expand Down Expand Up @@ -19465,7 +19499,7 @@ handle_port_binding_changes(struct ovsdb_idl_txn *ovnsb_txn,

bool up = false;

if (lsp_is_router(op->nbsp)) {
if (lsp_is_router(op->nbsp) || lsp_is_switch(op->nbsp)) {
up = true;
} else if (sb->chassis) {
up = !smap_get_bool(&sb->chassis->other_config, "is-remote", false)
Expand Down
2 changes: 2 additions & 0 deletions northd/northd.h
Original file line number Diff line number Diff line change
Expand Up @@ -645,6 +645,8 @@ struct ovn_port {
*
* - Two connected logical router ports have each other as peer.
*
* - Two connected logical switch ports have each other as peer.
*
* - Other kinds of ports have no peer. */
struct ovn_port *peer;

Expand Down
5 changes: 3 additions & 2 deletions ovn-nb.ovsschema
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "OVN_Northbound",
"version": "7.9.0",
"cksum": "2414335430 38682",
"version": "7.10.0",
"cksum": "3177074739 38756",
"tables": {
"NB_Global": {
"columns": {
Expand Down Expand Up @@ -155,6 +155,7 @@
"port_security": {"type": {"key": "string",
"min": 0,
"max": "unlimited"}},
"peer": {"type": {"key": "string", "min": 0, "max": 1}},
"up": {"type": {"key": "boolean", "min": 0, "max": 1}},
"enabled": {"type": {"key": "boolean", "min": 0, "max": 1}},
"dhcpv4_options": {"type": {"key": {"type": "uuid",
Expand Down
19 changes: 19 additions & 0 deletions ovn-nb.xml
Original file line number Diff line number Diff line change
Expand Up @@ -987,6 +987,14 @@
to which this logical switch port is connected.
</dd>

<dt><code>switch</code></dt>
<dd>
A connection to another logical switch. The value of <ref
column="peer" /> specifies the <ref column="name"/> of the
<ref table="Logical_Switch_Port"/> to which this logical switch
port is connected.
</dd>

<dt><code>localnet</code></dt>
<dd>
A connection to a locally accessible network from
Expand Down Expand Up @@ -1847,6 +1855,17 @@
</column>
</group>

<column name="peer">
<p>
For a switch port used to connect two logical switches, this
identifies the other switch port in the pair by <ref column="name"/>.
</p>

<p>
For a switch port attached to a logical router, this column is empty.
</p>
</column>

<group title="DHCP">
<column name="dhcpv4_options">
This column defines the DHCPv4 Options to be included by the
Expand Down
21 changes: 20 additions & 1 deletion tests/ovn-nbctl.at
Original file line number Diff line number Diff line change
Expand Up @@ -2419,6 +2419,11 @@ AT_CHECK([ovn-nbctl lsp-get-type lp0], [0], [dnl
router
])

AT_CHECK([ovn-nbctl lsp-set-type lp0 switch])
AT_CHECK([ovn-nbctl lsp-get-type lp0], [0], [dnl
switch
])

AT_CHECK([ovn-nbctl lsp-set-type lp0 localnet])
AT_CHECK([ovn-nbctl lsp-get-type lp0], [0], [dnl
localnet
Expand Down Expand Up @@ -2460,8 +2465,22 @@ ovn-nbctl: Logical switch port type 'eggs' is unrecognized. Not setting type.

dnl Empty string should work too
AT_CHECK([ovn-nbctl lsp-set-type lp0 ""])
AT_CHECK([ovn-nbctl lsp-get-type lp0], [0], [dnl
AT_CHECK([ovn-nbctl lsp-get-type lp0], [0], [
])

dnl Only the 'switch' type should accept peer.
AT_CHECK([ovn-nbctl lsp-set-type lp0 router peer=qwe], [1], [], [dnl
ovn-nbctl: Peer can only be set for a logical switch port with type 'switch'.
])
AT_CHECK([ovn-nbctl lsp-get-type lp0], [0], [
])
dnl Check that peer can be set.
AT_CHECK([ovn-nbctl lsp-set-type lp0 switch peer=my-peer])
AT_CHECK([ovn-nbctl lsp-get-type lp0], [0], [dnl
switch
])
AT_CHECK([ovn-nbctl get logical-switch-port lp0 peer], [0], [dnl
my-peer
])])

dnl ---------------------------------------------------------------------
Expand Down
57 changes: 57 additions & 0 deletions tests/ovn-northd.at
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,23 @@ wait_row_count nb:Logical_Switch_Port 1 name=S1-vm1 'up=true'
AT_CLEANUP
])

OVN_FOR_EACH_NORTHD_NO_HV([
AT_SETUP([check up state of switch-switch LSP])
ovn_start

check ovn-nbctl ls-add S1
check ovn-nbctl lsp-add S1 S1-p1
check ovn-nbctl --wait=sb lsp-set-type S1-p1 switch peer=S2-p2

check ovn-nbctl ls-add S2
check ovn-nbctl lsp-add S2 S2-p2
check ovn-nbctl --wait=sb lsp-set-type S2-p2 switch peer=S1-p1
AT_CHECK([test x`ovn-nbctl lsp-get-up S1-p1` = xup])
AT_CHECK([test x`ovn-nbctl lsp-get-up S2-p2` = xup])

AT_CLEANUP
])

OVN_FOR_EACH_NORTHD_NO_HV([
AT_SETUP([check up state of router LSP linked to a distributed LR])
ovn_start
Expand Down Expand Up @@ -7298,7 +7315,47 @@ AT_CHECK([grep -e "ls_in_l2_lkup.*S1-vm1" S1flows | ovn_strip_lflows], [0], [dnl
AT_CLEANUP
])

OVN_FOR_EACH_NORTHD_NO_HV([
AT_SETUP([implicit unknown addresses on switch-switch LSPs])
ovn_start NORTHD_TYPE
check ovn-nbctl ls-add S1
check ovn-nbctl lsp-add S1 S1-vm
check ovn-nbctl lsp-set-addresses S1-vm "50:54:00:00:00:01 192.168.0.1"

check ovn-nbctl ls-add S2
check ovn-nbctl lsp-add S2 S2-vm
check ovn-nbctl lsp-set-addresses S2-vm "50:54:00:00:00:02 192.168.0.2"

check ovn-nbctl lsp-add S1 S1-S2
check ovn-nbctl lsp-add S2 S2-S1
check ovn-nbctl lsp-set-type S1-S2 switch peer=S2-S1
check ovn-nbctl lsp-set-type S2-S1 switch peer=S1-S2
check ovn-nbctl --wait=sb sync

ovn-sbctl dump-flows S1 > S1flows
AT_CAPTURE_FILE([S1flows])

dnl Check that S2-vm address is not known on S1 and the forwarding to
dnl _MC_unknown group is configured.
AT_CHECK([grep -E "ls_in_l2_lkup.*S1-|unknown" S1flows | ovn_strip_lflows], [0], [dnl
table=??(ls_in_l2_lkup ), priority=50 , match=(eth.dst == 50:54:00:00:00:01), action=(outport = "S1-vm"; output;)
table=??(ls_in_l2_unknown ), priority=0 , match=(1), action=(output;)
table=??(ls_in_l2_unknown ), priority=50 , match=(outport == "none"), action=(outport = "_MC_unknown"; output;)
])

ovn-sbctl dump-flows S2 > S2flows
AT_CAPTURE_FILE([S2flows])

dnl Check that S1-vm address is not known on S2 and the forwarding to
dnl _MC_unknown group is configured.
AT_CHECK([grep -E "ls_in_l2_lkup.*S2-|unknown" S2flows | ovn_strip_lflows], [0], [dnl
table=??(ls_in_l2_lkup ), priority=50 , match=(eth.dst == 50:54:00:00:00:02), action=(outport = "S2-vm"; output;)
table=??(ls_in_l2_unknown ), priority=0 , match=(1), action=(output;)
table=??(ls_in_l2_unknown ), priority=50 , match=(outport == "none"), action=(outport = "_MC_unknown"; output;)
])

AT_CLEANUP
])

# Duplicated datapaths shouldn't be created, but in case it is created because
# of bug or dirty data, it should be properly deleted instead of causing
Expand Down
Loading

0 comments on commit 954fcb3

Please sign in to comment.