This commit adds the gateway module to ovn-controller-vtep. The module will register the physical switches to ovnsb as chassis and constantly update the "logical_switches" column in Chassis table.
Limitation: - Do not support reading multiple tunnel ips of physical switch. Signed-off-by: Alex Wang <al...@nicira.com> --- V2->V3: - since ovn-sb schema changes (removal of Gateway table), the gateway module code needs to be adapted. - rebase to master. PATCH->V2: - split into separate commit. - can register all physical switches controlled by vtep database. --- ovn/controller-vtep/automake.mk | 2 + ovn/controller-vtep/gateway.c | 281 ++++++++++++++++++++ .../{ovn-controller-vtep.h => gateway.h} | 15 +- ovn/controller-vtep/ovn-controller-vtep.c | 3 + ovn/controller-vtep/ovn-controller-vtep.h | 18 ++ tests/ovn-controller-vtep.at | 151 +++++++++++ 6 files changed, 461 insertions(+), 9 deletions(-) create mode 100644 ovn/controller-vtep/gateway.c copy ovn/controller-vtep/{ovn-controller-vtep.h => gateway.h} (72%) diff --git a/ovn/controller-vtep/automake.mk b/ovn/controller-vtep/automake.mk index 7adda15..514cafa 100644 --- a/ovn/controller-vtep/automake.mk +++ b/ovn/controller-vtep/automake.mk @@ -1,5 +1,7 @@ bin_PROGRAMS += ovn/controller-vtep/ovn-controller-vtep ovn_controller_vtep_ovn_controller_vtep_SOURCES = \ + ovn/controller-vtep/gateway.c \ + ovn/controller-vtep/gateway.h \ ovn/controller-vtep/ovn-controller-vtep.c \ ovn/controller-vtep/ovn-controller-vtep.h ovn_controller_vtep_ovn_controller_vtep_LDADD = ovn/lib/libovn.la lib/libopenvswitch.la vtep/libvtep.la diff --git a/ovn/controller-vtep/gateway.c b/ovn/controller-vtep/gateway.c new file mode 100644 index 0000000..54a5b63 --- /dev/null +++ b/ovn/controller-vtep/gateway.c @@ -0,0 +1,281 @@ +/* Copyright (c) 2015 Nicira, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at: + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include <config.h> +#include "gateway.h" + +#include "lib/hash.h" +#include "lib/hmap.h" +#include "lib/poll-loop.h" +#include "lib/sset.h" +#include "lib/util.h" +#include "openvswitch/vlog.h" +#include "ovn/lib/ovn-sb-idl.h" +#include "vtep/vtep-idl.h" +#include "ovn-controller-vtep.h" + +VLOG_DEFINE_THIS_MODULE(gateway); + +/* + * Registers the physical switches in vtep to ovnsb as chassis. For each + * physical switch in the vtep database, finds all logical switches that + * are associated with the physical switch, and updates the corresponding + * chassis's 'logical_switches' column. + * + */ + +/* Global revalidation sequence number, incremented at each call to + * 'revalidate_gateway()'. */ +static uint64_t gw_reval_seq; + +/* Represents a chassis added by the gateway module. The 'reval_seq' + * is increment each time a chassis is revalidated. Chassis whose 'reval_seq' + * not equal to 'gw_reval_seq' will be removed. */ +struct gw_chassis { + struct hmap_node hmap_node; /* In 'gw_chassis'. */ + char *name; /* Name of the Chassis. */ + uint64_t reval_seq; /* Chassis revalidation sequence number. */ +}; + +/* Contains all chassis created by the gateway module. */ +static struct hmap gw_chassis_map = HMAP_INITIALIZER(&gw_chassis_map); + +/* Searchs 'gw_chassis_map' for chassis 'name' and returns the pointer. + * Returns NULL, if the chassis is not found. */ +static struct gw_chassis * +get_gw_chassis(const char *name) +{ + struct gw_chassis *gw_chassis; + + HMAP_FOR_EACH_WITH_HASH (gw_chassis, hmap_node, hash_string(name, 0), + &gw_chassis_map) { + if (!strcmp(gw_chassis->name, name)) { + return gw_chassis; + } + } + + return NULL; +} + +/* Creates and returns a new instance of 'struct sbrec_chassis'. */ +static const struct sbrec_chassis * +create_chassis_rec(struct ovsdb_idl_txn *txn, const char *name, + const char *encap_ip) +{ + const struct sbrec_chassis *chassis_rec; + struct sbrec_encap *encap_rec; + + chassis_rec = sbrec_chassis_insert(txn); + sbrec_chassis_set_name(chassis_rec, name); + encap_rec = sbrec_encap_insert(txn); + sbrec_encap_set_type(encap_rec, OVN_SB_ENCAP_TYPE); + sbrec_encap_set_ip(encap_rec, encap_ip); + sbrec_chassis_set_encaps(chassis_rec, &encap_rec, 1); + + return chassis_rec; +} + +/* Revalidates chassis in ovnsb against vtep database. Creates chassis for + * new vtep physical switch. And removes chassis which no longer have + * physical switch in vtep. + * + * xxx: Support multiple tunnel encaps. + * + * */ +static void +revalidate_gateway(struct controller_vtep_ctx *ctx) +{ + const struct vteprec_physical_switch *pswitch; + struct ovsdb_idl_txn *txn; + struct gw_chassis *iter, *next; + int retval; + + /* Increments the global revalidation sequence number. */ + gw_reval_seq++; + + txn = ovsdb_idl_txn_create(ctx->ovnsb_idl); + ovsdb_idl_txn_add_comment(txn, "ovn-controller-vtep: updating vtep chassis"); + + VTEPREC_PHYSICAL_SWITCH_FOR_EACH (pswitch, ctx->vtep_idl) { + const struct sbrec_chassis *chassis_rec; + struct gw_chassis *gw_chassis; + const char *encap_ip; + + encap_ip = pswitch->n_tunnel_ips ? pswitch->tunnel_ips[0] : ""; + gw_chassis = get_gw_chassis(pswitch->name); + chassis_rec = get_chassis_by_name(ctx->ovnsb_idl, pswitch->name); + if (gw_chassis && !chassis_rec) { + VLOG_WARN("Chassis for VTEP physical switch (%s) disappears, " + "maybe deleted by ovn-sbctl, adding it back", + pswitch->name); + create_chassis_rec(txn, pswitch->name, encap_ip); + } else if (!gw_chassis && chassis_rec) { + VLOG_WARN("Chassis for new VTEP physical switch (%s) has already " + "been added, maybe by ovn-sbctl", pswitch->name); + if (strcmp(chassis_rec->encaps[0]->type, OVN_SB_ENCAP_TYPE) + && strcmp(chassis_rec->encaps[0]->ip, encap_ip)) { + VLOG_WARN("Chassis config changing on startup, make sure " + "multiple chassis are not configured : %s/%s->%s/%s", + chassis_rec->encaps[0]->type, + chassis_rec->encaps[0]->ip, + OVN_SB_ENCAP_TYPE, encap_ip); + VLOG_WARN("Skip adding chassis for physical switch (%s)", + pswitch->name); + continue; + } + gw_chassis = xmalloc(sizeof *gw_chassis); + gw_chassis->name = xstrdup(pswitch->name); + hmap_insert(&gw_chassis_map, &gw_chassis->hmap_node, + hash_string(gw_chassis->name, 0)); + } else if (gw_chassis && chassis_rec) { + /* Updates chassis's encap if anything changed. */ + if (strcmp(chassis_rec->encaps[0]->type, OVN_SB_ENCAP_TYPE)) { + VLOG_WARN("Chassis for VTEP physical switch (%s) can only have " + "encap type \"%s\"", pswitch->name, OVN_SB_ENCAP_TYPE); + sbrec_encap_set_type(chassis_rec->encaps[0], OVN_SB_ENCAP_TYPE); + } + if (strcmp(chassis_rec->encaps[0]->ip, encap_ip)) { + sbrec_encap_set_ip(chassis_rec->encaps[0], encap_ip); + } + } else { + /* Creates a new chassis for the VTEP physical switch and a new + * gw_chassis record. */ + create_chassis_rec(txn, pswitch->name, encap_ip); + gw_chassis = xmalloc(sizeof *gw_chassis); + gw_chassis->name = xstrdup(pswitch->name); + hmap_insert(&gw_chassis_map, &gw_chassis->hmap_node, + hash_string(gw_chassis->name, 0)); + } + /* Updates the 'gw_chassis's revalidation seq number to prevent + * it from being garbage collected. */ + gw_chassis->reval_seq = gw_reval_seq; + } + + /* For 'gw_chassis' in 'gw_chassis_map' whose reval_seq is not + * 'gw_reval_seq', it means the corresponding physical switch no + * longer exist. So, garbage collects them. */ + HMAP_FOR_EACH_SAFE (iter, next, hmap_node, &gw_chassis_map) { + if (iter->reval_seq != gw_reval_seq) { + const struct sbrec_chassis *chassis_rec; + + chassis_rec = get_chassis_by_name(ctx->ovnsb_idl, iter->name); + if (chassis_rec) { + sbrec_chassis_delete(chassis_rec); + } + hmap_remove(&gw_chassis_map, &iter->hmap_node); + free(iter->name); + free(iter); + } + } + + retval = ovsdb_idl_txn_commit_block(txn); + if (retval != TXN_SUCCESS && retval != TXN_UNCHANGED) { + VLOG_INFO("Problem registering chassis: %s", + ovsdb_idl_txn_status_to_string(retval)); + poll_immediate_wake(); + } + ovsdb_idl_txn_destroy(txn); +} + +/* Updates the logical switches in the Chassis table based on vtep database + * configuration. */ +static void +update_logical_switches(struct controller_vtep_ctx *ctx) +{ + const struct vteprec_physical_switch *pswitch; + struct ovsdb_idl_txn *txn; + int retval; + + txn = ovsdb_idl_txn_create(ctx->ovnsb_idl); + ovsdb_idl_txn_add_comment(txn, "ovn-controller-vtep: " + "updating logical switches"); + + VTEPREC_PHYSICAL_SWITCH_FOR_EACH (pswitch, ctx->vtep_idl) { + const struct sbrec_chassis *chassis_rec = + get_chassis_by_name(ctx->ovnsb_idl, pswitch->name); + struct smap lswitches = SMAP_INITIALIZER(&lswitches); + size_t i; + + for (i = 0; i < pswitch->n_ports; i++) { + const struct vteprec_physical_port *port = pswitch->ports[i]; + size_t j; + + for (j = 0; j < port->n_vlan_bindings; j++) { + const struct vteprec_logical_switch *ls; + + ls = port->value_vlan_bindings[j]; + /* If not already in 'lswitches', adds the logical switch + * to logical port map. The logical port is defined as + * {pswitch->name}_{ls->name}. */ + if (!smap_get(&lswitches, ls->name)) { + char *lport = xasprintf("%s_%s", pswitch->name, ls->name); + + smap_add(&lswitches, ls->name, lport); + free(lport); + } + } + } + sbrec_chassis_set_logical_switches(chassis_rec, &lswitches); + smap_destroy(&lswitches); + } + + retval = ovsdb_idl_txn_commit_block(txn); + if (retval != TXN_SUCCESS && retval != TXN_UNCHANGED) { + VLOG_INFO("Problem updating chassis's logical_switches: %s", + ovsdb_idl_txn_status_to_string(retval)); + poll_immediate_wake(); + } + ovsdb_idl_txn_destroy(txn); +} + + +void +gateway_run(struct controller_vtep_ctx *ctx) +{ + revalidate_gateway(ctx); + update_logical_switches(ctx); +} + +/* Destroys the chassis table entries of the vtep physical switches. */ +void +gateway_destroy(struct controller_vtep_ctx *ctx) +{ + int retval = TXN_TRY_AGAIN; + + ovs_assert(ctx->ovnsb_idl); + while (retval != TXN_SUCCESS && retval != TXN_UNCHANGED) { + const struct vteprec_physical_switch *pswitch; + struct ovsdb_idl_txn *txn = ovsdb_idl_txn_create(ctx->ovnsb_idl); + + ovsdb_idl_txn_add_comment(txn, + "ovn-controller-vtep: " + "unregistering vtep chassis"); + VTEPREC_PHYSICAL_SWITCH_FOR_EACH (pswitch, ctx->vtep_idl) { + const struct sbrec_chassis *chassis_rec; + + chassis_rec = get_chassis_by_name(ctx->ovnsb_idl, pswitch->name); + if (!chassis_rec) { + continue; + } + sbrec_chassis_delete(chassis_rec); + } + retval = ovsdb_idl_txn_commit_block(txn); + if (retval == TXN_ERROR) { + VLOG_INFO("Problem unregistering chassis: %s", + ovsdb_idl_txn_status_to_string(retval)); + } + ovsdb_idl_txn_destroy(txn); + } +} diff --git a/ovn/controller-vtep/ovn-controller-vtep.h b/ovn/controller-vtep/gateway.h similarity index 72% copy from ovn/controller-vtep/ovn-controller-vtep.h copy to ovn/controller-vtep/gateway.h index 121a9e3..edc895e 100644 --- a/ovn/controller-vtep/ovn-controller-vtep.h +++ b/ovn/controller-vtep/gateway.h @@ -13,15 +13,12 @@ * limitations under the License. */ +#ifndef OVN_GATEWAY_H +#define OVN_GATEWAY_H 1 -#ifndef OVN_CONTROLLER_VTEP_H -#define OVN_CONTROLLER_VTEP_H 1 +struct controller_vtep_ctx; -#include "ovn/lib/ovn-sb-idl.h" +void gateway_run(struct controller_vtep_ctx *); +void gateway_destroy(struct controller_vtep_ctx *); -struct controller_vtep_ctx { - struct ovsdb_idl *ovnsb_idl; - struct ovsdb_idl *vtep_idl; -}; - -#endif /* ovn/ovn-controller-vtep.h */ +#endif /* ovn/controller-gw/gateway.h */ diff --git a/ovn/controller-vtep/ovn-controller-vtep.c b/ovn/controller-vtep/ovn-controller-vtep.c index 50d727f..dbc754b 100644 --- a/ovn/controller-vtep/ovn-controller-vtep.c +++ b/ovn/controller-vtep/ovn-controller-vtep.c @@ -38,6 +38,7 @@ #include "unixctl.h" #include "util.h" +#include "gateway.h" #include "ovn-controller-vtep.h" VLOG_DEFINE_THIS_MODULE(ovn_vtep); @@ -121,6 +122,7 @@ main(int argc, char *argv[]) break; } + gateway_run(&ctx); unixctl_server_run(unixctl); unixctl_server_wait(unixctl); @@ -134,6 +136,7 @@ main(int argc, char *argv[]) } unixctl_server_destroy(unixctl); + gateway_destroy(&ctx); ovsdb_idl_destroy(ctx.vtep_idl); ovsdb_idl_destroy(ctx.ovnsb_idl); diff --git a/ovn/controller-vtep/ovn-controller-vtep.h b/ovn/controller-vtep/ovn-controller-vtep.h index 121a9e3..f00d3c4 100644 --- a/ovn/controller-vtep/ovn-controller-vtep.h +++ b/ovn/controller-vtep/ovn-controller-vtep.h @@ -24,4 +24,22 @@ struct controller_vtep_ctx { struct ovsdb_idl *vtep_idl; }; +/* VTEP needs what VTEP needs. */ +#define OVN_SB_ENCAP_TYPE "vxlan" +#define VTEP_ENCAP_TYPE "vxlan_over_ipv4" + +static inline const struct sbrec_chassis * +get_chassis_by_name(struct ovsdb_idl *ovnsb_idl, char *chassis_id) +{ + const struct sbrec_chassis *chassis_rec; + + SBREC_CHASSIS_FOR_EACH(chassis_rec, ovnsb_idl) { + if (!strcmp(chassis_rec->name, chassis_id)) { + break; + } + } + + return chassis_rec; +} + #endif /* ovn/ovn-controller-vtep.h */ diff --git a/tests/ovn-controller-vtep.at b/tests/ovn-controller-vtep.at index bcae89c..3065a6b 100644 --- a/tests/ovn-controller-vtep.at +++ b/tests/ovn-controller-vtep.at @@ -1 +1,152 @@ AT_BANNER([ovn_controller_vtep]) + +# OVN_CONTROLLER_VTEP_START +# +# Starts the test with a setup with vtep device. +# +# Uses vtep-ovs to simulate the vtep switch 'br-vtep' with two physical ports +# 'p0', 'p1'. +# +# Configures ovn-nb with a logical switch 'br-test'. +# +# +m4_define([OVN_CONTROLLER_VTEP_START], + [OVS_RUNDIR=`pwd`; export OVS_RUNDIR + OVS_LOGDIR=`pwd`; export OVS_LOGDIR + OVS_DBDIR=`pwd`; export OVS_DBDIR + OVS_SYSCONFDIR=`pwd`; export OVS_SYSCONFDIR + + dnl Create databases (ovn-nb, ovn-sb, vtep). + AT_CHECK([ovsdb-tool create vswitchd.db $abs_top_srcdir/vswitchd/vswitch.ovsschema]) + for daemon in ovn-nb ovn-sb vtep; do + AT_CHECK([ovsdb-tool create $daemon.db $abs_top_srcdir/${daemon%%-*}/${daemon}.ovsschema]) + done + + dnl Start ovsdb-server. + AT_CHECK([ovsdb-server --detach --no-chdir --pidfile --log-file --remote=punix:$OVS_RUNDIR/db.sock vswitchd.db vtep.db ovn-nb.db ovn-sb.db], [0], [], [stderr]) + ON_EXIT_UNQUOTED([kill `cat ovsdb-server.pid`]) + AT_CHECK([[sed < stderr ' +/vlog|INFO|opened log file/d +/ovsdb_server|INFO|ovsdb-server (Open vSwitch)/d']]) + AT_CAPTURE_FILE([ovsdb-server.log]) + + dnl Start ovs-vswitchd. + AT_CHECK([ovs-vswitchd --enable-dummy --disable-system --detach --no-chdir --pidfile --log-file -vvconn -vofproto_dpif], [0], [], [stderr]) + AT_CAPTURE_FILE([ovs-vswitchd.log]) + ON_EXIT_UNQUOTED([kill `cat ovs-vswitchd.pid`]) + AT_CHECK([[sed < stderr ' +/ovs_numa|INFO|Discovered /d +/vlog|INFO|opened log file/d +/vswitchd|INFO|ovs-vswitchd (Open vSwitch)/d +/reconnect|INFO|/d +/ofproto|INFO|using datapath ID/d +/ofproto|INFO|datapath ID changed to fedcba9876543210/d']]) + AT_CHECK([ovs-vsctl -- add-br br-vtep \ + -- set bridge br-vtep datapath-type=dummy other-config:datapath-id=fedcba9876543210 other-config:hwaddr=aa:55:aa:55:00:00 protocols=[[OpenFlow10,OpenFlow11,OpenFlow12,OpenFlow13,OpenFlow14,OpenFlow15]] fail-mode=secure \ + -- add-port br-vtep p0 -- set Interface p0 type=dummy ofport_request=1 \ + -- add-port br-vtep p1 -- set Interface p1 type=dummy ofport_request=2]) + + dnl Start ovs-vtep. + AT_CHECK([vtep-ctl add-ps br-vtep -- set Physical_Switch br-vtep tunnel_ips=1.2.3.4]) + AT_CHECK([ovs-vtep --log-file=ovs-vtep.log --pidfile=ovs-vtep.pid --detach br-vtep \], [0], [], [stderr]) + ON_EXIT_UNQUOTED([kill `cat ovs-vtep.pid`]) + AT_CHECK([[sed < stderr ' +/vlog|INFO|opened log file/d']]) + # waits until ovs-vtep starts up. + OVS_WAIT_UNTIL([test -n "`vtep-ctl show | grep Physical_Port`"]) + + dnl Start ovn-northd. + AT_CHECK([ovn-nbctl lswitch-add br-test]) + AT_CHECK([ovn-northd --detach --pidfile --log-file --ovnnb-db=unix:$OVS_RUNDIR/db.sock --ovnsb-db=unix:$OVS_RUNDIR/db.sock], [0], [], [stderr]) + ON_EXIT_UNQUOTED([kill `cat ovn-northd.pid`]) + AT_CHECK([[sed < stderr ' +/vlog|INFO|opened log file/d']]) + AT_CAPTURE_FILE([ovn-northd.log]) + + dnl Start ovn-controllger-vtep. + AT_CHECK([ovn-controller-vtep --detach --pidfile --log-file --vtep-db=unix:$OVS_RUNDIR/db.sock --ovnsb-db=unix:$OVS_RUNDIR/db.sock], [0], [], [stderr]) + AT_CAPTURE_FILE([ovn-controller-vtep.log]) + ON_EXIT_UNQUOTED([kill `cat ovn-controller-vtep.pid`]) + AT_CHECK([[sed < stderr ' +/vlog|INFO|opened log file/d +/reconnect|INFO|/d']]) +]) + +# OVN_CONTROLLER_VTEP_STOP +# +# So many exits... Yeah, we started a lot daemons~ +# +m4_define([OVN_CONTROLLER_VTEP_STOP], + [AT_CHECK([check_logs $1]) + AT_CHECK([ovs-appctl -t ovs-vtep exit]) + AT_CHECK([ovs-appctl -t ovn-northd exit]) + AT_CHECK([ovs-appctl -t ovn-controller-vtep exit]) + AT_CHECK([ovs-appctl -t ovsdb-server exit]) + AT_CHECK([ovs-appctl -t ovs-vswitchd exit])]) + + + +# tests chassis related updates. +AT_SETUP([ovn-controller-vtep - test chassis]) +OVN_CONTROLLER_VTEP_START + +# verifies the initial ovn-sb db configuration. +AT_CHECK([ovn-sbctl show], [0], [dnl +Chassis br-vtep + Encap vxlan + ip: "1.2.3.4" +]) + +# deletes the chassis via ovn-sbctl and check that it is readded back +# with the log. +AT_CHECK([ovn-sbctl del-chassis br-vtep]) +OVS_WAIT_UNTIL([test -n "`grep WARN ovn-controller-vtep.log`"]) +AT_CHECK([sed -n 's/^.*\(|WARN|.*\)$/\1/p' ovn-controller-vtep.log], [0], [dnl +|WARN|Chassis for VTEP physical switch (br-vtep) disappears, maybe deleted by ovn-sbctl, adding it back +]) + +# changes the tunnel_ip on physical switch, watches the update of chassis's +# encap. +AT_CHECK([vtep-ctl set Physical_Switch br-vtep tunnel_ips=1.2.3.5]) +OVS_WAIT_UNTIL([test -n "`ovn-sbctl show | grep 1\.2\.3\.5`"]) +AT_CHECK([ovn-sbctl --columns=ip list Encap | cut -d ':' -f2 | tr -d ' '], [0], [dnl +"1.2.3.5" +]) + +# adds vlan_bindings to physical ports. +AT_CHECK([vtep-ctl add-ls lswitch0 -- bind-ls br-vtep p0 100 lswitch0 -- bind-ls br-vtep p0 200 lswitch0 -- bind-ls br-vtep p1 300 lswitch0]) +OVS_WAIT_UNTIL([test -n "`ovn-sbctl list Chassis | grep -- br-vtep_lswitch0`"]) +AT_CHECK([ovn-sbctl --columns=logical_switches list Chassis | cut -d ':' -f2 | tr -d ' ' ], [0], [dnl +{"lswitch0"="br-vtep_lswitch0"} +]) + +# adds another logical switch and new vlan_bindings. +AT_CHECK([vtep-ctl add-ls lswitch1 -- bind-ls br-vtep p0 300 lswitch1]) +OVS_WAIT_UNTIL([test -n "`ovn-sbctl list Chassis | grep -- br-vtep_lswitch1`"]) +AT_CHECK([ovn-sbctl --columns=logical_switches list Chassis | cut -d ':' -f2 | tr -d ' '], [0], [dnl +{"lswitch0"="br-vtep_lswitch0","lswitch1"="br-vtep_lswitch1"} +]) + +# unbinds one port from lswitch0, nothing should change. +AT_CHECK([vtep-ctl unbind-ls br-vtep p0 200]) +OVS_WAIT_UNTIL([test -z "`vtep-ctl --columns=vlan_bindings list physical_port p0 | grep -- 200`"]) +AT_CHECK([ovn-sbctl --columns=logical_switches list Chassis | cut -d ':' -f2 | tr -d ' ' ], [0], [dnl +{"lswitch0"="br-vtep_lswitch0","lswitch1"="br-vtep_lswitch1"} +]) + +# unbinds all ports from lswitch0. +AT_CHECK([vtep-ctl unbind-ls br-vtep p0 100 -- unbind-ls br-vtep p1 300]) +OVS_WAIT_UNTIL([test -z "`ovn-sbctl list Chassis | grep -- br-vtep_lswitch0`"]) +AT_CHECK([ovn-sbctl --columns=logical_switches list Chassis | cut -d ':' -f2 | tr -d ' ' ], [0], [dnl +{"lswitch1"="br-vtep_lswitch1"} +]) + +# unbinds all ports from lswitch1. +AT_CHECK([vtep-ctl unbind-ls br-vtep p0 300]) +OVS_WAIT_UNTIL([test -z "`ovn-sbctl list Chassis | grep -- br-vtep_lswitch1`"]) +AT_CHECK([ovn-sbctl --columns=logical_switches list Chassis | cut -d ':' -f2 | tr -d ' '], [0], [dnl +{} +]) + +OVN_CONTROLLER_VTEP_STOP(["/Chassis for VTEP physical switch (br-vtep) disappears/d"]) +AT_CLEANUP -- 1.7.9.5 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev