This commit extends the vtep module to support creating the 'Ucast_Macs_Remote' table entries in the vtep database for MAC addresses on the ovn logical ports.
Signed-off-by: Alex Wang <al...@nicira.com> --- V5->V6: - rebase. V4->V5: - rebase on top of master. - rewrite the feature since a lot have changed. V3->V4: - add logic to remove Ucast_Macs_Remote for non-existent MACs. V2->V3: - rebase to master. PATCH->V2: - split into separate commit. - few optimizations. --- ovn/controller-vtep/vtep.c | 329 ++++++++++++++++++++++++++++++++++++------ tests/ovn-controller-vtep.at | 136 +++++++++++++++++ 2 files changed, 418 insertions(+), 47 deletions(-) diff --git a/ovn/controller-vtep/vtep.c b/ovn/controller-vtep/vtep.c index 55d2e0d..1545b39 100644 --- a/ovn/controller-vtep/vtep.c +++ b/ovn/controller-vtep/vtep.c @@ -19,7 +19,8 @@ #include "lib/hash.h" #include "lib/hmap.h" -#include "lib/smap.h" +#include "lib/shash.h" +#include "lib/sset.h" #include "lib/util.h" #include "ovn-controller-vtep.h" #include "openvswitch/vlog.h" @@ -29,69 +30,239 @@ VLOG_DEFINE_THIS_MODULE(vtep); /* - * Scans through the Binding table in ovnsb and updates the vtep logical - * switch tunnel keys. + * Scans through the Binding table in ovnsb, and updates the vtep logical + * switch tunnel keys and the 'Ucast_Macs_Remote' table in the VTEP + * database. * */ +/* Searches the 'chassis_rec->encaps' for the first vtep tunnel + * configuration, returns the 'ip'. */ +static const char * +get_chassis_vtep_ip(const struct sbrec_chassis *chassis_rec) +{ + if (chassis_rec) { + size_t i; + + for (i = 0; i < chassis_rec->n_encaps; i++) { + if (!strcmp(chassis_rec->encaps[i]->type, "vxlan")) { + return chassis_rec->encaps[i]->ip; + } + } + } + + return NULL; +} + +/* Creates a new 'Ucast_Macs_Remote'. */ +static struct vteprec_ucast_macs_remote * +create_umr(struct ovsdb_idl_txn *vtep_idl_txn, const char *mac, + const struct vteprec_logical_switch *vtep_ls) +{ + struct vteprec_ucast_macs_remote *new_umr; + + new_umr = vteprec_ucast_macs_remote_insert(vtep_idl_txn); + vteprec_ucast_macs_remote_set_MAC(new_umr, mac); + vteprec_ucast_macs_remote_set_logical_switch(new_umr, vtep_ls); + + return new_umr; +} + +/* Creates a new 'Physical_Locator'. */ +static struct vteprec_physical_locator * +create_pl(struct ovsdb_idl_txn *vtep_idl_txn, const char *chassis_ip) +{ + struct vteprec_physical_locator *new_pl; + + new_pl = vteprec_physical_locator_insert(vtep_idl_txn); + vteprec_physical_locator_set_dst_ip(new_pl, chassis_ip); + vteprec_physical_locator_set_encapsulation_type(new_pl, VTEP_ENCAP_TYPE); + + return new_pl; +} + + /* Updates the vtep Logical_Switch table entries' tunnel keys based * on the port bindings. */ static void -vtep_lswitch_run(struct controller_vtep_ctx *ctx) +vtep_lswitch_run(struct shash *vtep_pbs, struct shash *vtep_lswitches) { - struct shash vtep_lswitches = SHASH_INITIALIZER(&vtep_lswitches); - const struct sbrec_port_binding *port_binding_rec; - const struct vteprec_logical_switch *vtep_ls; - - /* Stores all logical switches to 'vtep_lswitches' with name as key. */ - VTEPREC_LOGICAL_SWITCH_FOR_EACH (vtep_ls, ctx->vtep_idl) { - shash_add(&vtep_lswitches, vtep_ls->name, vtep_ls); - } + struct sset used_ls = SSET_INITIALIZER(&used_ls); + struct shash_node *node; - ovsdb_idl_txn_add_comment(ctx->vtep_idl_txn, - "ovn-controller-vtep: update logical switch " - "tunnel keys"); /* Collects the logical switch bindings from port binding entries. * Since the binding module has already guaranteed that each vtep * logical switch is bound only to one ovn-sb logical datapath, * we can just iterate and assign tunnel key to vtep logical switch. */ - SBREC_PORT_BINDING_FOR_EACH(port_binding_rec, ctx->ovnsb_idl) { - if (!strcmp(port_binding_rec->type, "vtep") - && port_binding_rec->chassis) { - const char *lswitch_name = smap_get(&port_binding_rec->options, - "vtep-logical-switch"); - - /* If 'port_binding_rec->chassis' exists then 'lswitch_name' - * must also exist. */ - ovs_assert(lswitch_name); - vtep_ls = shash_find_and_delete(&vtep_lswitches, lswitch_name); - if (!vtep_ls) { - VLOG_ERR("logical port (%s) bound to non-exist vtep logical " - "switch (%s), something is seriously wrong", - port_binding_rec->logical_port, lswitch_name); - } else { - int64_t tnl_key; - - tnl_key = port_binding_rec->datapath->tunnel_key; - if (vtep_ls->n_tunnel_key - && vtep_ls->tunnel_key[0] != tnl_key) { - VLOG_DBG("set vtep logical switch (%s) tunnel key from " - "(%"PRId64") to (%"PRId64")", vtep_ls->name, - vtep_ls->tunnel_key[0], tnl_key); - } + SHASH_FOR_EACH (node, vtep_pbs) { + const struct sbrec_port_binding *port_binding_rec = node->data; + const char *lswitch_name = smap_get(&port_binding_rec->options, + "vtep-logical-switch"); + const struct vteprec_logical_switch *vtep_ls; + + /* 'lswitch_name' must exist. */ + ovs_assert(lswitch_name); + vtep_ls = shash_find_data(vtep_lswitches, lswitch_name); + if (!vtep_ls) { + /* This could only happen when someone is messing up + * using ovn-sbctl command. */ + VLOG_ERR("logical port (%s) bound to non-exist vtep logical " + "switch (%s), something is seriously wrong", + port_binding_rec->logical_port, lswitch_name); + } else { + int64_t tnl_key; + + tnl_key = port_binding_rec->datapath->tunnel_key; + if (vtep_ls->n_tunnel_key + && vtep_ls->tunnel_key[0] != tnl_key) { + VLOG_DBG("set vtep logical switch (%s) tunnel key from " + "(%"PRId64") to (%"PRId64")", vtep_ls->name, + vtep_ls->tunnel_key[0], tnl_key); vteprec_logical_switch_set_tunnel_key(vtep_ls, &tnl_key, 1); } + /* sset will ignore duplicates. */ + sset_add(&used_ls, lswitch_name); } } + /* Resets the tunnel keys for unused vtep logical switches. */ + SHASH_FOR_EACH (node, vtep_lswitches) { + const struct vteprec_logical_switch *vtep_ls = node->data; + if (!sset_find(&used_ls, vtep_ls->name)) { + int64_t tnl_key = 0; + vteprec_logical_switch_set_tunnel_key(node->data, &tnl_key, 1); + } + } + sset_destroy(&used_ls); +} + +/* Updates the vtep 'Ucast_Macs_Remote' table based on non-vtep port + * bindings. */ +static void +vtep_macs_run(struct ovsdb_idl_txn *vtep_idl_txn, struct shash *ucast_macs_rmts, + struct shash *physical_locators, struct shash *vtep_lswitches, + struct shash *other_pbs) +{ struct shash_node *node; - /* Resets the tunnel keys for the rest of vtep logical switches. */ - SHASH_FOR_EACH (node, &vtep_lswitches) { - int64_t tnl_key = 0; + struct hmap ls_map; + + /* Maps from ovn logical datapath tunnel key (which is also the vtep + * logical switch tunnel key) to the corresponding vtep logical switch + * instance. Also, the shash map 'added_macs' is used for checking + * duplicated MAC addresses in the same ovn logical datapath. */ + struct ls_hash_node { + struct hmap_node hmap_node; + + const struct vteprec_logical_switch *vtep_ls; + struct shash added_macs; + }; + + hmap_init(&ls_map); + SHASH_FOR_EACH (node, vtep_lswitches) { + struct ls_hash_node *ls_node = xmalloc(sizeof *ls_node); - vteprec_logical_switch_set_tunnel_key(node->data, &tnl_key, 1); + ls_node->vtep_ls = node->data; + shash_init(&ls_node->added_macs); + hmap_insert(&ls_map, &ls_node->hmap_node, + hash_uint64((uint64_t) ls_node->vtep_ls->tunnel_key[0])); } - shash_destroy(&vtep_lswitches); + SHASH_FOR_EACH (node, other_pbs) { + const struct sbrec_port_binding *port_binding_rec = node->data; + const struct sbrec_chassis *chassis_rec; + struct ls_hash_node *ls_node; + const char *chassis_ip; + int64_t tnl_key; + int i; + + chassis_rec = port_binding_rec->chassis; + chassis_ip = get_chassis_vtep_ip(chassis_rec); + + /* Unreachable chassis, continue. */ + if (!chassis_ip) { + continue; + } + + tnl_key = port_binding_rec->datapath->tunnel_key; + HMAP_FOR_EACH_WITH_HASH (ls_node, hmap_node, + hash_uint64((uint64_t) tnl_key), + &ls_map) { + if (ls_node->vtep_ls->tunnel_key[0] == tnl_key) { + break; + } + } + /* If 'ls_node' is NULL, that means no vtep logical switch is + * attached to the corresponding ovn logical datapath, so pass. */ + if (!ls_node) { + continue; + } + + for (i = 0; i < port_binding_rec->n_mac; i++) { + const struct vteprec_ucast_macs_remote *umr; + const struct vteprec_physical_locator *pl; + const struct sbrec_port_binding *conflict; + char *mac = port_binding_rec->mac[i]; + + /* xxx Need to address this. */ + if (!strcmp(mac, "unknown")) { + continue; + } + + /* Checks for duplicate MAC in the same vtep logical switch. */ + conflict = shash_find_data(&ls_node->added_macs, mac); + if (conflict) { + VLOG_WARN("MAC address (%s) has already been known to be " + "on logical port (%s) in the same logical " + "datapath, so just ignore this logical port (%s)", + mac, conflict->logical_port, + port_binding_rec->logical_port); + continue; + } + shash_add(&ls_node->added_macs, mac, port_binding_rec); + + char *mac_ip = xasprintf("%s%s", mac, chassis_ip); + umr = shash_find_data(ucast_macs_rmts, mac_ip); + /* If finds the 'umr' entry for the mac and ip, deletes the + * entry from shash so that it is not gargage collected. + * + * If not found, creates a new 'umr' entry. + * + * If vtep logical switch does not match, the logical port + * owning the MAC may be moved to a new ovn logical datapath. + * In that case, just creates a new umr entry for the MAC. + * The old one will be garbage collected. */ + if (umr && umr->logical_switch == ls_node->vtep_ls) { + shash_find_and_delete(ucast_macs_rmts, mac_ip); + } else { + const struct vteprec_ucast_macs_remote *new_umr; + + new_umr = create_umr(vtep_idl_txn, mac, ls_node->vtep_ls); + pl = shash_find_data(physical_locators, chassis_ip); + if (pl) { + vteprec_ucast_macs_remote_set_locator(new_umr, pl); + } else { + const struct vteprec_physical_locator *new_pl; + + new_pl = create_pl(vtep_idl_txn, chassis_ip); + vteprec_ucast_macs_remote_set_locator(new_umr, new_pl); + /* Updates the 'physical_locators'. */ + shash_add(physical_locators, chassis_ip, new_pl); + } + } + free(mac_ip); + } + } + + /* Removes all remaining 'umr's, since they do not exist anymore. */ + SHASH_FOR_EACH (node, ucast_macs_rmts) { + vteprec_ucast_macs_remote_delete(node->data); + } + struct ls_hash_node *iter, *next; + HMAP_FOR_EACH_SAFE (iter, next, hmap_node, &ls_map) { + hmap_remove(&ls_map, &iter->hmap_node); + shash_destroy(&iter->added_macs); + free(iter); + } + hmap_destroy(&ls_map); } /* Since we do not own any vtep logical switch, just sets their tunnel key @@ -107,6 +278,19 @@ vtep_lswitch_cleanup(struct ovsdb_idl *vtep_idl) } } +/* Removes all entries in the 'Ucast_Macs_Remote' table in vtep database. + * Returns true when all done. */ +static bool +vtep_macs_cleanup(struct ovsdb_idl *vtep_idl) +{ + const struct vteprec_ucast_macs_remote *umr; + + VTEPREC_UCAST_MACS_REMOTE_FOR_EACH (umr, vtep_idl) { + vteprec_ucast_macs_remote_delete(umr); + return false; + } + return true; +} /* Updates vtep logical switch tunnel keys. */ void @@ -115,7 +299,59 @@ vtep_run(struct controller_vtep_ctx *ctx) if (!ctx->vtep_idl_txn) { return; } - vtep_lswitch_run(ctx); + + struct shash vtep_lswitches = SHASH_INITIALIZER(&vtep_lswitches); + struct shash ucast_macs_rmts = SHASH_INITIALIZER(&ucast_macs_rmts); + struct shash physical_locators = SHASH_INITIALIZER(&physical_locators); + struct shash vtep_pbs = SHASH_INITIALIZER(&vtep_pbs); + struct shash other_pbs = SHASH_INITIALIZER(&other_pbs); + const struct vteprec_logical_switch *vtep_ls; + const struct vteprec_ucast_macs_remote *umr; + const struct vteprec_physical_locator *pl; + const struct sbrec_port_binding *port_binding_rec; + + /* Collects 'Logical_Switch's. */ + VTEPREC_LOGICAL_SWITCH_FOR_EACH (vtep_ls, ctx->vtep_idl) { + shash_add(&vtep_lswitches, vtep_ls->name, vtep_ls); + } + + /* Collects 'Ucast_Macs_Remote's. */ + VTEPREC_UCAST_MACS_REMOTE_FOR_EACH (umr, ctx->vtep_idl) { + char *mac_ip = + xasprintf("%s%s", umr->MAC, + umr->locator ? umr->locator->dst_ip : ""); + + shash_add(&ucast_macs_rmts, mac_ip, umr); + free(mac_ip); + } + /* Collects 'Physical_Locator's. */ + VTEPREC_PHYSICAL_LOCATOR_FOR_EACH (pl, ctx->vtep_idl) { + shash_add(&physical_locators, pl->dst_ip, pl); + } + /* Collects and classifies 'Port_Binding's. */ + SBREC_PORT_BINDING_FOR_EACH(port_binding_rec, ctx->ovnsb_idl) { + struct shash *target = + !strcmp(port_binding_rec->type, "vtep") ? &vtep_pbs : &other_pbs; + + if (!port_binding_rec->chassis) { + continue; + } + shash_add(target, port_binding_rec->logical_port, port_binding_rec); + } + + ovsdb_idl_txn_add_comment(ctx->vtep_idl_txn, + "ovn-controller-vtep: update logical switch " + "tunnel keys and 'ucast_macs_remote's"); + + vtep_lswitch_run(&vtep_pbs, &vtep_lswitches); + vtep_macs_run(ctx->vtep_idl_txn, &ucast_macs_rmts, &physical_locators, + &vtep_lswitches, &other_pbs); + + shash_destroy(&vtep_lswitches); + shash_destroy(&ucast_macs_rmts); + shash_destroy(&physical_locators); + shash_destroy(&vtep_pbs); + shash_destroy(&other_pbs); } /* Cleans up all created vtep related entries. Returns true when all done. */ @@ -129,6 +365,5 @@ vtep_cleanup(struct controller_vtep_ctx *ctx) ovsdb_idl_txn_add_comment(ctx->vtep_idl_txn, "cleans up vtep configuration"); vtep_lswitch_cleanup(ctx->vtep_idl); - - return true; + return vtep_macs_cleanup(ctx->vtep_idl); } diff --git a/tests/ovn-controller-vtep.at b/tests/ovn-controller-vtep.at index ea58636..8d8b7c4 100644 --- a/tests/ovn-controller-vtep.at +++ b/tests/ovn-controller-vtep.at @@ -294,3 +294,139 @@ AT_CHECK([vtep-ctl --columns=tunnel_key list Logical_Switch | cut -d ':' -f2 | t OVN_CONTROLLER_VTEP_STOP AT_CLEANUP + + +# Tests vtep module 'Ucast_Macs_Remote's. +AT_SETUP([ovn-controller-vtep - test vtep-macs 1]) +OVN_CONTROLLER_VTEP_START + +# creates a simple logical network with the vtep device and a fake hv chassis +# 'ch0'. +AT_CHECK([ovn-nbctl lport-add br-test vif0]) +AT_CHECK([ovn-nbctl lport-set-macs vif0 f0:ab:cd:ef:01:02]) +AT_CHECK([ovn-sbctl chassis-add ch0 vxlan 1.2.3.5]) +AT_CHECK([ovn-sbctl lport-bind vif0 ch0]) + +# creates the logical switch in vtep and adds the corresponding logical +# port to 'br-test'. +AT_CHECK([vtep-ctl add-ls lswitch0 -- bind-ls br-vtep p0 100 lswitch0]) +OVN_NB_ADD_VTEP_PORT([br-test], [br-vtep_lswitch0], [br-vtep], [lswitch0]) +OVS_WAIT_UNTIL([test -n "`ovn-sbctl list Port_Binding | grep br-vtep_lswitch0`"]) + +# adds another lswitch 'br-void' in ovn-nb database. +AT_CHECK([ovn-nbctl lswitch-add br-void]) +# adds fake hv chassis 'ch1'. +AT_CHECK([ovn-nbctl lport-add br-void vif1]) +AT_CHECK([ovn-nbctl lport-set-macs vif1 f0:ab:cd:ef:01:02]) +AT_CHECK([ovn-sbctl chassis-add ch1 vxlan 1.2.3.6]) +AT_CHECK([ovn-sbctl lport-bind vif1 ch1]) +OVS_WAIT_UNTIL([test -n "`ovn-sbctl list Port_Binding | grep vif1`"]) + +# checks Ucast_Macs_Remote creation. +OVS_WAIT_UNTIL([test -n "`vtep-ctl list Ucast_Macs_Remote | grep _uuid`"]) +AT_CHECK([vtep-ctl --columns=MAC list Ucast_Macs_Remote | cut -d ':' -f2- | tr -d ' '], [0], [dnl +"f0:ab:cd:ef:01:02" +]) + +# checks physical locator creation. +OVS_WAIT_UNTIL([test -n "`vtep-ctl list Physical_Locator | grep _uuid`"]) +AT_CHECK([vtep-ctl --columns=dst_ip list Physical_Locator | cut -d ':' -f2 | tr -d ' ' | grep -v 1.2.3.4 | sed '/^$/d'], [0], [dnl +"1.2.3.5" +]) + +# checks tunnel creation by ovs-vtep. +OVS_WAIT_UNTIL([test -n "`ovs-vsctl list Interface bfd1.2.3.5`"]) +AT_CHECK([ovs-vsctl --columns=options list Interface bfd1.2.3.5 | cut -d ':' -f2 | tr -d ' '], [0], [dnl +{remote_ip="1.2.3.5"} +]) + +# adds another mac to lport. +AT_CHECK([ovn-nbctl lport-set-macs vif0 f0:ab:cd:ef:01:02 f0:ab:cd:ef:01:03]) +OVS_WAIT_UNTIL([test -n "`vtep-ctl list Ucast_Macs_Remote | grep 03`"]) +AT_CHECK([vtep-ctl --columns=MAC list Ucast_Macs_Remote | cut -d ':' -f2- | tr -d ' ' | sort], [0], [dnl + +"f0:ab:cd:ef:01:02" +"f0:ab:cd:ef:01:03" +]) + +# removes one mac to lport. +AT_CHECK([ovn-nbctl lport-set-macs vif0 f0:ab:cd:ef:01:03]) +OVS_WAIT_UNTIL([test -z "`vtep-ctl --columns=MAC list Ucast_Macs_Remote | grep 02`"]) +AT_CHECK([vtep-ctl --columns=MAC list Ucast_Macs_Remote | cut -d ':' -f2- | tr -d ' ' | sort], [0], [dnl +"f0:ab:cd:ef:01:03" +]) + +# migrates mac to lport vif1 on 'br-void'. +AT_CHECK([ovn-nbctl lport-set-macs vif0]) +AT_CHECK([ovn-nbctl lport-set-macs vif1 f0:ab:cd:ef:01:03]) +OVS_WAIT_UNTIL([test -z "`vtep-ctl --columns=MAC list Ucast_Macs_Remote | grep 03`"]) +AT_CHECK([vtep-ctl --columns=MAC list Ucast_Macs_Remote | cut -d ':' -f2- | tr -d ' ' | sort], [0], [dnl +]) + +OVN_CONTROLLER_VTEP_STOP +AT_CLEANUP + + +# Tests vtep module 'Ucast_Macs_Remote's (corner cases). +AT_SETUP([ovn-controller-vtep - test vtep-macs 2]) +OVN_CONTROLLER_VTEP_START + +# creates a simple logical network with the vtep device and a fake hv chassis +# 'ch0'. +AT_CHECK([ovn-nbctl lport-add br-test vif0]) +AT_CHECK([ovn-nbctl lport-set-macs vif0 f0:ab:cd:ef:01:02]) +AT_CHECK([ovn-sbctl chassis-add ch0 vxlan 1.2.3.5]) +AT_CHECK([ovn-sbctl lport-bind vif0 ch0]) + +# creates another vif in the same logical switch with duplicate mac. +AT_CHECK([ovn-nbctl lport-add br-test vif1]) +AT_CHECK([ovn-nbctl lport-set-macs vif1 f0:ab:cd:ef:01:02]) +AT_CHECK([ovn-sbctl lport-bind vif1 ch0]) + +# creates the logical switch in vtep and adds the corresponding logical +# port to 'br-test'. +AT_CHECK([vtep-ctl add-ls lswitch0 -- bind-ls br-vtep p0 100 lswitch0]) +OVN_NB_ADD_VTEP_PORT([br-test], [br-vtep_lswitch0], [br-vtep], [lswitch0]) +OVS_WAIT_UNTIL([test -n "`ovn-sbctl list Port_Binding | grep br-vtep_lswitch0`"]) + +# checks Ucast_Macs_Remote creation. Should still only be one entry, since duplicate +# mac in the same logical switch is not allowed. +OVS_WAIT_UNTIL([test -n "`vtep-ctl list Ucast_Macs_Remote | grep _uuid`"]) +AT_CHECK([vtep-ctl --columns=MAC list Ucast_Macs_Remote | cut -d ':' -f2- | tr -d ' '], [0], [dnl +"f0:ab:cd:ef:01:02" +]) +# confirms the warning log. +OVS_WAIT_UNTIL([test -n "`grep WARN ovn-controller-vtep.log`"]) +AT_CHECK([sed -n 's/^.*\(|WARN|.*\)$/\1/p' ovn-controller-vtep.log | sed 's/([[-_:0-9a-z]][[-_:0-9a-z]]*)/()/g' | uniq], [0], [dnl +|WARN|MAC address () has already been known to be on logical port () in the same logical datapath, so just ignore this logical port () +]) + +# deletes vif1. +AT_CHECK([ovn-nbctl lport-del vif1]) + +# adds another lswitch 'br-void' in ovn-nb database. +AT_CHECK([ovn-nbctl lswitch-add br-void]) +# adds fake hv chassis 'ch1' and vif1 with same mac address as vif0. +AT_CHECK([ovn-nbctl lport-add br-void vif1]) +AT_CHECK([ovn-nbctl lport-set-macs vif1 f0:ab:cd:ef:01:02]) +AT_CHECK([ovn-sbctl chassis-add ch1 vxlan 1.2.3.6]) +AT_CHECK([ovn-sbctl lport-bind vif1 ch1]) +OVS_WAIT_UNTIL([test -n "`ovn-sbctl list Port_Binding | grep vif1`"]) + +# creates another logical switch in vtep and adds the corresponding logical +# port to 'br-void'. +AT_CHECK([vtep-ctl add-ls lswitch1 -- bind-ls br-vtep p0 200 lswitch1]) +OVN_NB_ADD_VTEP_PORT([br-void], [br-void_lswitch1], [br-vtep], [lswitch1]) +OVS_WAIT_UNTIL([test -n "`ovn-sbctl list Port_Binding | grep br-void_lswitch1`"]) + +# checks Ucast_Macs_Remote creation. Should see two entries since it is allowed +# to have duplicate macs in different logical switches. +OVS_WAIT_UNTIL([test `vtep-ctl --columns=MAC list Ucast_Macs_Remote | grep 02 | wc -l` -gt 1]) +AT_CHECK([vtep-ctl --columns=MAC list Ucast_Macs_Remote | cut -d ':' -f2- | tr -d ' ' | sort], [0], [dnl + +"f0:ab:cd:ef:01:02" +"f0:ab:cd:ef:01:02" +]) + +OVN_CONTROLLER_VTEP_STOP(["/has already been known to be on logical port/d"]) +AT_CLEANUP -- 1.7.9.5 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev