diff --git a/NEWS b/NEWS index ee5c2c393..c22466818 100644 --- a/NEWS +++ b/NEWS @@ -18,6 +18,11 @@ OVN v20.09.0 - xx xxx xxxx - Added support for external ip based NAT. Now, besides the logical ip, external ips will also decide if a packet will be NATed or not. - Added support for VXLAN encapsulation (not just for ramp/VTEP switches). + - Added support for multiple ovn-controller instances on the same host + (virtual chassis). Now all external-ids:* configuration options can be + customized for each controller instance running on the same host. The only + option that is not available per chassis is external-ids:system-id, which + stands for the chassis name and can be passed via config file or CLI (-n). OVN v20.06.0 -------------------------- diff --git a/controller/chassis.c b/controller/chassis.c index a365188e8..989ec5e1a 100644 --- a/controller/chassis.c +++ b/controller/chassis.c @@ -125,9 +125,10 @@ chassis_register_ovs_idl(struct ovsdb_idl *ovs_idl) } static const char * -get_hostname(const struct smap *ext_ids) +get_hostname(const struct smap *ext_ids, const char *chassis_id) { - const char *hostname = smap_get_def(ext_ids, "hostname", ""); + const char *hostname = get_chassis_external_id_value( + ext_ids, chassis_id, "hostname", ""); if (strlen(hostname) == 0) { static char hostname_[HOST_NAME_MAX + 1]; @@ -143,39 +144,45 @@ get_hostname(const struct smap *ext_ids) } static const char * -get_bridge_mappings(const struct smap *ext_ids) +get_bridge_mappings(const struct smap *ext_ids, const char *chassis_id) { - return smap_get_def(ext_ids, "ovn-bridge-mappings", ""); + return get_chassis_external_id_value( + ext_ids, chassis_id, "ovn-bridge-mappings", ""); } const char * -get_chassis_mac_mappings(const struct smap *ext_ids) +get_chassis_mac_mappings(const struct smap *ext_ids, const char *chassis_id) { - return smap_get_def(ext_ids, "ovn-chassis-mac-mappings", ""); + return get_chassis_external_id_value( + ext_ids, chassis_id, "ovn-chassis-mac-mappings", ""); } static const char * -get_cms_options(const struct smap *ext_ids) +get_cms_options(const struct smap *ext_ids, const char *chassis_id) { - return smap_get_def(ext_ids, "ovn-cms-options", ""); + return get_chassis_external_id_value( + ext_ids, chassis_id, "ovn-cms-options", ""); } static const char * -get_monitor_all(const struct smap *ext_ids) +get_monitor_all(const struct smap *ext_ids, const char *chassis_id) { - return smap_get_def(ext_ids, "ovn-monitor-all", "false"); + return get_chassis_external_id_value( + ext_ids, chassis_id, "ovn-monitor-all", "false"); } static const char * -get_enable_lflow_cache(const struct smap *ext_ids) +get_enable_lflow_cache(const struct smap *ext_ids, const char *chassis_id) { - return smap_get_def(ext_ids, "ovn-enable-lflow-cache", "true"); + return get_chassis_external_id_value( + ext_ids, chassis_id, "ovn-enable-lflow-cache", "true"); } static const char * -get_encap_csum(const struct smap *ext_ids) +get_encap_csum(const struct smap *ext_ids, const char *chassis_id) { - return smap_get_def(ext_ids, "ovn-encap-csum", "true"); + return get_chassis_external_id_value( + ext_ids, chassis_id, "ovn-encap-csum", "true"); } static const char * @@ -189,9 +196,10 @@ get_datapath_type(const struct ovsrec_bridge *br_int) } static bool -get_is_interconn(const struct smap *ext_ids) +get_is_interconn(const struct smap *ext_ids, const char *chassis_id) { - return smap_get_bool(ext_ids, "ovn-is-interconn", false); + return get_chassis_external_id_value_bool( + ext_ids, chassis_id, "ovn-is-interconn", false); } static void @@ -278,22 +286,27 @@ chassis_parse_ovs_config(const struct ovsrec_open_vswitch_table *ovs_table, return false; } - const char *encap_type = smap_get(&cfg->external_ids, "ovn-encap-type"); - const char *encap_ips = smap_get(&cfg->external_ids, "ovn-encap-ip"); + const char *chassis_id = get_ovs_chassis_id(cfg); + const struct smap *ext_ids = &cfg->external_ids; + + const char *encap_type = get_chassis_external_id_value( + ext_ids, chassis_id, "ovn-encap-type", NULL); + const char *encap_ips = get_chassis_external_id_value( + ext_ids, chassis_id, "ovn-encap-ip", NULL); if (!encap_type || !encap_ips) { static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); VLOG_INFO_RL(&rl, "Need to specify an encap type and ip"); return false; } - ovs_cfg->hostname = get_hostname(&cfg->external_ids); - ovs_cfg->bridge_mappings = get_bridge_mappings(&cfg->external_ids); + ovs_cfg->hostname = get_hostname(ext_ids, chassis_id); + ovs_cfg->bridge_mappings = get_bridge_mappings(ext_ids, chassis_id); ovs_cfg->datapath_type = get_datapath_type(br_int); - ovs_cfg->encap_csum = get_encap_csum(&cfg->external_ids); - ovs_cfg->cms_options = get_cms_options(&cfg->external_ids); - ovs_cfg->monitor_all = get_monitor_all(&cfg->external_ids); - ovs_cfg->chassis_macs = get_chassis_mac_mappings(&cfg->external_ids); - ovs_cfg->enable_lflow_cache = get_enable_lflow_cache(&cfg->external_ids); + ovs_cfg->encap_csum = get_encap_csum(ext_ids, chassis_id); + ovs_cfg->cms_options = get_cms_options(ext_ids, chassis_id); + ovs_cfg->monitor_all = get_monitor_all(ext_ids, chassis_id); + ovs_cfg->chassis_macs = get_chassis_mac_mappings(ext_ids, chassis_id); + ovs_cfg->enable_lflow_cache = get_enable_lflow_cache(ext_ids, chassis_id); if (!chassis_parse_ovs_encap_type(encap_type, &ovs_cfg->encap_type_set)) { return false; @@ -311,7 +324,7 @@ chassis_parse_ovs_config(const struct ovsrec_open_vswitch_table *ovs_table, sset_destroy(&ovs_cfg->encap_ip_set); } - ovs_cfg->is_interconn = get_is_interconn(&cfg->external_ids); + ovs_cfg->is_interconn = get_is_interconn(ext_ids, chassis_id); return true; } @@ -348,7 +361,7 @@ chassis_other_config_changed(const char *bridge_mappings, const struct sbrec_chassis *chassis_rec) { const char *chassis_bridge_mappings = - get_bridge_mappings(&chassis_rec->other_config); + get_bridge_mappings(&chassis_rec->other_config, NULL); if (strcmp(bridge_mappings, chassis_bridge_mappings)) { return true; @@ -362,28 +375,28 @@ chassis_other_config_changed(const char *bridge_mappings, } const char *chassis_cms_options = - get_cms_options(&chassis_rec->other_config); + get_cms_options(&chassis_rec->other_config, NULL); if (strcmp(cms_options, chassis_cms_options)) { return true; } const char *chassis_monitor_all = - get_monitor_all(&chassis_rec->other_config); + get_monitor_all(&chassis_rec->other_config, NULL); if (strcmp(monitor_all, chassis_monitor_all)) { return true; } const char *chassis_enable_lflow_cache = - get_enable_lflow_cache(&chassis_rec->other_config); + get_enable_lflow_cache(&chassis_rec->other_config, NULL); if (strcmp(enable_lflow_cache, chassis_enable_lflow_cache)) { return true; } const char *chassis_mac_mappings = - get_chassis_mac_mappings(&chassis_rec->other_config); + get_chassis_mac_mappings(&chassis_rec->other_config, NULL); if (strcmp(chassis_macs, chassis_mac_mappings)) { return true; } @@ -791,7 +804,7 @@ chassis_get_mac(const struct sbrec_chassis *chassis_rec, struct eth_addr *chassis_mac) { const char *tokens - = get_chassis_mac_mappings(&chassis_rec->other_config); + = get_chassis_mac_mappings(&chassis_rec->other_config, NULL); if (!tokens[0]) { return false; } diff --git a/controller/chassis.h b/controller/chassis.h index 220f726b9..c7345f0fa 100644 --- a/controller/chassis.h +++ b/controller/chassis.h @@ -49,7 +49,8 @@ bool chassis_get_mac(const struct sbrec_chassis *chassis, const char *bridge_mapping, struct eth_addr *chassis_mac); const char *chassis_get_id(void); -const char * get_chassis_mac_mappings(const struct smap *ext_ids); +const char * get_chassis_mac_mappings(const struct smap *ext_ids, + const char *chassis_id); #endif /* controller/chassis.h */ diff --git a/controller/encaps.c b/controller/encaps.c index 7eac4bb06..d8e343175 100644 --- a/controller/encaps.c +++ b/controller/encaps.c @@ -322,6 +322,9 @@ encaps_run(struct ovsdb_idl_txn *ovs_idl_txn, * * Collect all the OVN-created tunnels into tc.tunnel_hmap. */ OVSREC_BRIDGE_TABLE_FOR_EACH (br, bridge_table) { + if (strcmp(br_int->name, br->name)) { + continue; + } for (size_t i = 0; i < br->n_ports; i++) { const struct ovsrec_port *port = br->ports[i]; sset_add(&tc.port_names, port->name); @@ -381,6 +384,7 @@ encaps_run(struct ovsdb_idl_txn *ovs_idl_txn, shash_delete(&tc.chassis, node); free(chassis); } + shash_destroy(&tc.chassis); sset_destroy(&tc.port_names); } diff --git a/controller/ovn-controller.8.xml b/controller/ovn-controller.8.xml index 16bc47b20..4d2c4a2a7 100644 --- a/controller/ovn-controller.8.xml +++ b/controller/ovn-controller.8.xml @@ -235,6 +235,19 @@ +
+ Note that every external_ids:*
key listed above has its
+ external_ids:*-chassis_name
counterpart keys that allow to
+ configure values specific to chassis running on the same OVSDB. For
+ example, if two chassis named blue
and red
are
+ available on the same host, then an admin may configure different
+ ovn-cms-options
for each of them by setting
+ external_ids:ovn-cms-options-blue
and
+ external_ids:ovn-cms-options-red
keys in the database. The
+ only key that is not available for per-chassis configuration is
+ external_ids:system-id
.
+
ovn-controller
reads the following values from the
Open_vSwitch
database of the local OVS instance:
diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
index 8d8c678e5..973053dfe 100644
--- a/controller/ovn-controller.c
+++ b/controller/ovn-controller.c
@@ -18,10 +18,14 @@
#include "ovn-controller.h"
#include system-id-override
configuration file, or -n
+ CLI argument, or
+ in the Open_vSwitch database's
+ table.
+ ovn-controller-vtep populates this
column with in the hardware_vtep database's
table.
diff --git a/tests/ovn-controller.at b/tests/ovn-controller.at
index d8061345f..efb48c057 100644
--- a/tests/ovn-controller.at
+++ b/tests/ovn-controller.at
@@ -50,8 +50,7 @@ patch
# is mirrored into the Chassis record in the OVN_Southbound db.
check_bridge_mappings () {
local_mappings=$1
- sysid=$(ovs-vsctl get Open_vSwitch . external_ids:system-id)
- OVS_WAIT_UNTIL([test x"${local_mappings}" = x$(ovn-sbctl get Chassis ${sysid} other_config:ovn-bridge-mappings | sed -e 's/\"//g')])
+ OVS_WAIT_UNTIL([test x"${local_mappings}" = x$(ovn-sbctl get Chassis ${sandbox} other_config:ovn-bridge-mappings | sed -e 's/\"//g')])
}
# Initially there should be no patch ports.
@@ -133,13 +132,13 @@ ovs-vsctl \
-- add-br br-eth2
ovn_attach n1 br-phys 192.168.0.1
-sysid=$(ovs-vsctl get Open_vSwitch . external_ids:system-id)
+sysid=${sandbox}
# Make sure that the datapath_type set in the Bridge table
# is mirrored into the Chassis record in the OVN_Southbound db.
check_datapath_type () {
datapath_type=$1
- chassis_datapath_type=$(ovn-sbctl get Chassis ${sysid} other_config:datapath-type | sed -e 's/"//g') #"
+ chassis_datapath_type=$(ovn-sbctl get Chassis ${sandbox} other_config:datapath-type | sed -e 's/"//g') #"
test "${datapath_type}" = "${chassis_datapath_type}"
}
@@ -187,7 +186,7 @@ OVS_WAIT_UNTIL([
test "${expected_iface_types}" = "${chassis_iface_types}"
])
-# Change the value of external_ids:system-id and make sure it's mirrored
+# Set the value of external_ids:system-id and make sure it's mirrored
# in the Chassis record in the OVN_Southbound database.
sysid=${sysid}-foo
ovs-vsctl set Open_vSwitch . external-ids:system-id="${sysid}"
diff --git a/tests/ovn-macros.at b/tests/ovn-macros.at
index a6719be83..f846c6336 100644
--- a/tests/ovn-macros.at
+++ b/tests/ovn-macros.at
@@ -215,7 +215,7 @@ net_attach () {
# ovn_az_attach AZ NETWORK BRIDGE IP [MASKLEN]
ovn_az_attach() {
- local az=$1 net=$2 bridge=$3 ip=$4 masklen=${5-24} encap=${6-geneve,vxlan}
+ local az=$1 net=$2 bridge=$3 ip=$4 masklen=${5-24} encap=${6-geneve,vxlan} intbr=${7-br-int} chassis=$8
net_attach $net $bridge || return 1
mac=`ovs-vsctl get Interface $bridge mac_in_use | sed s/\"//g`
@@ -229,15 +229,48 @@ ovn_az_attach() {
else
ovn_remote=unix:$ovs_base/$az/ovn-sb/ovn-sb.sock
fi
+
+ if [[ -n "${chassis}" ]]; then
+ bridge_key=ovn-bridge-${chassis}
+ remote_key=ovn-remote-${chassis}
+ encap_type_key=ovn-encap-type-${chassis}
+ encap_ip_key=ovn-encap-ip-${chassis}
+ chassis_args="-n $chassis"
+ chassis_vsctl_args=
+ else
+ bridge_key=ovn-bridge
+ remote_key=ovn-remote
+ encap_type_key=ovn-encap-type
+ encap_ip_key=ovn-encap-ip
+ chassis=$sandbox
+ chassis_args=
+ chassis_vsctl_args="-- set Open_vSwitch . external-ids:system-id=$chassis"
+ fi
+
ovs-vsctl \
- -- set Open_vSwitch . external-ids:system-id=$sandbox \
- -- set Open_vSwitch . external-ids:ovn-remote=$ovn_remote \
- -- set Open_vSwitch . external-ids:ovn-encap-type=$encap \
- -- set Open_vSwitch . external-ids:ovn-encap-ip=$ip \
- -- --may-exist add-br br-int \
- -- set bridge br-int fail-mode=secure other-config:disable-in-band=true \
+ $chassis_vsctl_args \
+ -- set Open_vSwitch . external-ids:$bridge_key=$intbr \
+ -- set Open_vSwitch . external-ids:$remote_key=$ovn_remote \
+ -- set Open_vSwitch . external-ids:$encap_type_key=$encap \
+ -- set Open_vSwitch . external-ids:$encap_ip_key=$ip \
+ -- --may-exist add-br ${intbr} \
+ -- set bridge ${intbr} fail-mode=secure other-config:disable-in-band=true \
|| return 1
- start_daemon ovn-controller || return 1
+
+ if [[ "${intbr}" = br-int ]]; then
+ pidfile="${OVS_RUNDIR}/ovn-controller.pid"
+ logfile="${OVS_LOGDIR}/ovn-controller.log"
+ else
+ pidfile="${OVS_RUNDIR}/ovn-controller-${intbr}.pid"
+ logfile="${OVS_LOGDIR}/ovn-controller-${chassis}.log"
+ fi
+
+ ovn-controller \
+ ${chassis_args} \
+ -vconsole:off --detach --no-chdir \
+ --pidfile=${pidfile} \
+ --log-file=${logfile} || return 1
+ on_exit "test -e \"$pidfile\" && kill \`cat \"$pidfile\"\`"
}
# ovn_attach NETWORK BRIDGE IP [MASKLEN]
diff --git a/tests/ovn.at b/tests/ovn.at
index de1df3b6a..dfe3e02b5 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -1727,7 +1727,108 @@ AT_CLEANUP
AT_BANNER([OVN end-to-end tests])
-# 3 hypervisors, one logical switch, 3 logical ports per hypervisor
+AT_SETUP([ovn -- 3 virtual hosts, same node])
+AT_KEYWORDS([ovn])
+ovn_start
+ovn-nbctl ls-add lsw0
+net_add n1
+sim_add hv
+
+as hv
+for i in 1 2 3; do
+ chassis=host-$i
+ ovs-vsctl add-br br-phys-$i
+ ovn_attach n1 br-phys-$i 192.168.0.$i 24 geneve br-int-$i $chassis
+
+ for j in 1 2 3; do
+ lpname=lp$i$j
+ ovn-nbctl lsp-add lsw0 $lpname
+ ovn-nbctl --wait=hv --timeout=3 lsp-set-options $lpname requested-chassis=$chassis
+ ovs-vsctl add-port br-int-$i vif$i$j -- set Interface vif$i$j external-ids:iface-id=$lpname
+ OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up $lpname` = xup])
+
+ pb_chassis_id=$(ovn-sbctl --bare --columns chassis list port_binding $lpname)
+ pb_chassis_name=$(ovn-sbctl get chassis $pb_chassis_id name)
+ AT_FAIL_IF([test x$pb_chassis_name != x$chassis])
+ done
+done
+
+for i in 1 2 3; do
+ > expout
+ for vif in 1 2 3; do
+ echo vif$i$vif >> expout
+ done
+ AT_CHECK([ovs-vsctl list-ports br-int-$i | grep vif], [0], [expout])
+done
+
+AT_CLEANUP
+
+AT_SETUP([ovn -- system-id in file])
+AT_KEYWORDS([ovn])
+
+ovn_start
+net_add n1
+sim_add hv
+
+as hv
+
+mkdir -p ${OVS_SYSCONFDIR}/ovn
+echo otherid > ${OVS_SYSCONFDIR}/ovn/system-id-override
+ovs-vsctl add-br br-phys
+ovn_attach n1 br-phys 192.168.0.1
+
+# system-id-override file overrides chassis name selected via cli
+echo otherid > expout
+AT_CHECK([ovn-sbctl --bare --columns name list chassis], [0], [expout])
+
+AT_CLEANUP
+
+AT_SETUP([ovn -- concurrent controllers avoid fighting for each others' resources])
+AT_KEYWORDS([ovn])
+
+ovn_start
+sim_add hv
+
+for i in 1 2; do
+ net_add n-$i
+done
+
+as hv
+for i in 1 2; do
+ AT_CHECK([ovn-nbctl ls-add ls-$i])
+ AT_CHECK([ovn-nbctl lsp-add ls-$i ln_port-$i])
+ AT_CHECK([ovn-nbctl lsp-set-addresses ln_port-$i unknown])
+ AT_CHECK([ovn-nbctl lsp-set-type ln_port-$i localnet])
+ AT_CHECK([ovn-nbctl --wait=hv lsp-set-options ln_port-$i network_name=phys-$i])
+done
+
+for i in 1 2; do
+ as hv
+ ovs-vsctl add-br br-phys-$i
+ ovs-vsctl set open . external-ids:ovn-bridge-mappings-hv-$i=phys-$i:br-phys-$i
+ ovn_attach n-$i br-phys-$i 192.168.0.$i 24 geneve br-int-$i hv-$i
+
+ ovs-vsctl add-port br-int-$i vif-$i -- set Interface vif-$i external-ids:iface-id=lp-$i
+ ovn-nbctl lsp-add ls-$i lp-$i
+ OVS_WAIT_UNTIL([test x`ovn-nbctl lsp-get-up lp-$i` = xup])
+done
+
+# check that both patch ports are present
+AT_CHECK([ovs-vsctl --bare --columns=name find interface type="patch" | awk NF | sort], [0],
+[[patch-br-int-1-to-ln_port-1
+patch-br-int-2-to-ln_port-2
+patch-ln_port-1-to-br-int-1
+patch-ln_port-2-to-br-int-2
+]])
+
+# check that both tunnel endpoints are present
+AT_CHECK([ovs-vsctl --bare --columns=name find interface type="geneve" | awk NF | sort], [0],
+[[ovn-hv-1-0
+ovn-hv-2-0
+]])
+
+AT_CLEANUP
+
AT_SETUP([ovn -- 3 HVs, 1 LS, 3 lports/HV])
AT_KEYWORDS([ovnarp])
ovn_start