This commit adds the binding module to ovn-controller-vtep. The module will scan through the Binding table in ovnsb. If there is a binding for a logical port in the vtep gateway chassis's "vtep_logical_switches" map, sets the binding's chassis column to the vtep gateway chassis.
Signed-off-by: Alex Wang <al...@nicira.com> --- V3->V4: - rebase to master. V2->V3: - since ovn-sb schema changes (removal of Gateway table), the binding module code needs to be adapted. PATCH->V2: - split into separate commit. - disallow and warn if more than one logical port from one 'vlan_map' are attached to the same logical datapath. --- ovn/controller-vtep/automake.mk | 2 + ovn/controller-vtep/binding.c | 194 +++++++++++++++++++++++++++++ ovn/controller-vtep/binding.h | 25 ++++ ovn/controller-vtep/ovn-controller-vtep.c | 3 + tests/ovn-controller-vtep.at | 48 +++++++ 5 files changed, 272 insertions(+) create mode 100644 ovn/controller-vtep/binding.c create mode 100644 ovn/controller-vtep/binding.h diff --git a/ovn/controller-vtep/automake.mk b/ovn/controller-vtep/automake.mk index 514cafa..33f063f 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/binding.c \ + ovn/controller-vtep/binding.h \ ovn/controller-vtep/gateway.c \ ovn/controller-vtep/gateway.h \ ovn/controller-vtep/ovn-controller-vtep.c \ diff --git a/ovn/controller-vtep/binding.c b/ovn/controller-vtep/binding.c new file mode 100644 index 0000000..caf2a86 --- /dev/null +++ b/ovn/controller-vtep/binding.c @@ -0,0 +1,194 @@ +/* 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 "binding.h" + +#include "lib/hash.h" +#include "lib/sset.h" +#include "lib/util.h" +#include "lib/uuid.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(binding); + +/* + * This module scans through the Binding table in ovnsb. If there is a + * row for the logical port on vtep gateway chassis's 'vtep_logical_switches' + * map sets the binding's chassis column to the vtep gateway chassis. + * + * Caution: each logical datapath can only have up to one logical port + * attached to it from each vtep gateway. + * + */ + +/* Checks and updates bindings for each physical switch in VTEP. */ +void +binding_run(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 bindings"); + + VTEPREC_PHYSICAL_SWITCH_FOR_EACH (pswitch, ctx->vtep_idl) { + const struct sbrec_chassis *chassis_rec + = get_chassis_by_name(ctx->ovnsb_idl, pswitch->name); + const struct sbrec_binding *binding_rec; + struct sset attached_ldps; + struct sset gw_lports; + struct smap_node *iter; + const char *name; + + /* 'attached_ldps' is used to guarantee that each logical datapath + * can only have up to one logical port attached from the same + * gateway chassis. + * + * If a lport is first added to a logical datapath, we add the + * logical datapath's uuid to 'attached_ldps' as string. Then for + * each following lport, we always first check if the logical + * datapath has already been attached, and warn if it has. + * (since it is not allowed)! + * + */ + sset_init(&attached_ldps); + sset_init(&gw_lports); + /* Collects all logical port names on the vtep gateway. */ + SMAP_FOR_EACH (iter, &chassis_rec->vtep_logical_switches) { + sset_add(&gw_lports, iter->value); + } + + SBREC_BINDING_FOR_EACH(binding_rec, ctx->ovnsb_idl) { + /* Finds a binding entry for the lport. */ + if (sset_find_and_delete(&gw_lports, binding_rec->logical_port)) { + char *ldp_uuid; + + /* Converts uuid to string. */ + ldp_uuid = xasprintf(UUID_FMT, + UUID_ARGS(&binding_rec->logical_datapath)); + /* Warns if the logical datapath has already + * been attached. */ + if (sset_find(&attached_ldps, ldp_uuid)) { + VLOG_WARN("Logical datapath ("UUID_FMT") already has " + "logical port from the same chassis " + "(%s) attached to it, so clear the " + "chassis column from binding (%s)", + UUID_ARGS(&binding_rec->logical_datapath), + chassis_rec->name, + binding_rec->logical_port); + sbrec_binding_set_chassis(binding_rec, NULL); + } else { + if (binding_rec->chassis != chassis_rec) { + if (binding_rec->chassis) { + VLOG_DBG("Changing chassis for lport (%s) from " + "(%s) to (%s)", + binding_rec->logical_port, + binding_rec->chassis->name, + chassis_rec->name); + } + sbrec_binding_set_chassis(binding_rec, chassis_rec); + } + /* Records the attachment in 'attached_ldps'. */ + sset_add(&attached_ldps, ldp_uuid); + } + free(ldp_uuid); + } else if (binding_rec->chassis == chassis_rec) { + /* The logical port no longer exist, so clear + * the binding->chassis. */ + sbrec_binding_set_chassis(binding_rec, NULL); + } + } + SSET_FOR_EACH (name, &gw_lports) { + VLOG_DBG("No binding record for lport %s", name); + } + sset_destroy(&attached_ldps); + sset_destroy(&gw_lports); + } + + retval = ovsdb_idl_txn_commit_block(txn); + if (retval == TXN_ERROR) { + VLOG_INFO("Problem committing binding information: %s", + ovsdb_idl_txn_status_to_string(retval)); + } + ovsdb_idl_txn_destroy(txn); +} + +/* Removes the chassis reference for each binding to the vtep gateway. */ +void +binding_destroy(struct controller_vtep_ctx *ctx) +{ + struct hmap bd_map = HMAP_INITIALIZER(&bd_map); + const struct sbrec_binding *binding_rec; + int retval = TXN_TRY_AGAIN; + + struct binding_hash_node { + struct hmap_node hmap_node; /* Inside 'bd_map'. */ + const struct sbrec_binding *binding; + }; + + /* Collects all bindings with chassis. */ + SBREC_BINDING_FOR_EACH(binding_rec, ctx->ovnsb_idl) { + if (binding_rec->chassis) { + struct binding_hash_node *bd = xmalloc(sizeof *bd); + + bd->binding = binding_rec; + hmap_insert(&bd_map, &bd->hmap_node, + hash_string(binding_rec->chassis->name, 0)); + } + } + + while (retval != TXN_SUCCESS && retval != TXN_UNCHANGED) { + const struct vteprec_physical_switch *pswitch; + struct ovsdb_idl_txn *txn; + + txn = ovsdb_idl_txn_create(ctx->ovnsb_idl); + ovsdb_idl_txn_add_comment(txn, "ovn-controller-vtep: removing bindings"); + + 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 binding_hash_node *bd; + + HMAP_FOR_EACH_WITH_HASH (bd, hmap_node, + hash_string(chassis_rec->name, 0), + &bd_map) { + if (!strcmp(bd->binding->chassis->name, chassis_rec->name)) { + sbrec_binding_set_chassis(bd->binding, NULL); + } + } + } + + retval = ovsdb_idl_txn_commit_block(txn); + if (retval == TXN_ERROR) { + VLOG_DBG("Problem removing binding: %s", + ovsdb_idl_txn_status_to_string(retval)); + } + ovsdb_idl_txn_destroy(txn); + } + + struct binding_hash_node *iter, *next; + + HMAP_FOR_EACH_SAFE (iter, next, hmap_node, &bd_map) { + hmap_remove(&bd_map, &iter->hmap_node); + free(iter); + } + hmap_destroy(&bd_map); +} diff --git a/ovn/controller-vtep/binding.h b/ovn/controller-vtep/binding.h new file mode 100644 index 0000000..156465d --- /dev/null +++ b/ovn/controller-vtep/binding.h @@ -0,0 +1,25 @@ +/* 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. + */ + + +#ifndef OVN_BINDING_H +#define OVN_BINDING_H 1 + +struct controller_vtep_ctx; + +void binding_run(struct controller_vtep_ctx *); +void binding_destroy(struct controller_vtep_ctx *); + +#endif /* ovn/controller-gw/binding.h */ diff --git a/ovn/controller-vtep/ovn-controller-vtep.c b/ovn/controller-vtep/ovn-controller-vtep.c index dbc754b..712116a 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 "binding.h" #include "gateway.h" #include "ovn-controller-vtep.h" @@ -123,6 +124,7 @@ main(int argc, char *argv[]) } gateway_run(&ctx); + binding_run(&ctx); unixctl_server_run(unixctl); unixctl_server_wait(unixctl); @@ -137,6 +139,7 @@ main(int argc, char *argv[]) unixctl_server_destroy(unixctl); gateway_destroy(&ctx); + binding_destroy(&ctx); ovsdb_idl_destroy(ctx.vtep_idl); ovsdb_idl_destroy(ctx.ovnsb_idl); diff --git a/tests/ovn-controller-vtep.at b/tests/ovn-controller-vtep.at index 747e3ed..a0cae26 100644 --- a/tests/ovn-controller-vtep.at +++ b/tests/ovn-controller-vtep.at @@ -150,3 +150,51 @@ AT_CHECK([ovn-sbctl --columns=vtep_logical_switches list Chassis | cut -d ':' -f OVN_CONTROLLER_VTEP_STOP(["/Chassis for VTEP physical switch (br-vtep) disappears/d"]) AT_CLEANUP + + +# Tests binding updates. +AT_SETUP([ovn-controller-vtep - test binding]) +OVN_CONTROLLER_VTEP_START + +# adds logical switch 'lswitch0' and vlan_bindings. +AT_CHECK([vtep-ctl add-ls lswitch0 -- bind-ls br-vtep p0 100 lswitch0 -- bind-ls br-vtep p1 300 lswitch0]) +# adds lport in ovn-nb db. +AT_CHECK_UNQUOTED([ovn-nbctl lport-add br-test br-vtep_lswitch0]) +OVS_WAIT_UNTIL([test -n "`ovn-sbctl list Binding | grep br-vtep_lswitch0`"]) +# should see one binding. +chassis_uuid=$(ovn-sbctl --columns=_uuid list Chassis br-vtep | cut -d ':' -f2 | tr -d ' ') +AT_CHECK_UNQUOTED([ovn-sbctl --columns=chassis list Binding br-vtep_lswitch0 | cut -d ':' -f2 | tr -d ' '], [0], [dnl +${chassis_uuid} +]) + +# adds another logical switch 'lswitch1' and vlan_bindings. +AT_CHECK([vtep-ctl add-ls lswitch1 -- bind-ls br-vtep p0 200 lswitch1]) +# adds lport in ovn-nb db to the same logical switch. +AT_CHECK_UNQUOTED([ovn-nbctl lport-add br-test br-vtep_lswitch1]) +OVS_WAIT_UNTIL([test -n "`ovn-sbctl list Binding | grep -- br-vtep_lswitch1`"]) +# should still see one binding, since it is not allowed to have more than +# one logical port from same chassis attached to the same logical datapath +# (logical switch in ovn-nb database). +AT_CHECK_UNQUOTED([ovn-sbctl --columns=chassis list Binding | cut -d ':' -f2 | tr -d ' ' | sort -d], [0], [dnl + +[[]] +${chassis_uuid} +]) +# confirms the warning 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|Logical datapath () already has logical port from the same chassis () attached to it, so clear the chassis column from binding () +]) + +# deletes physical ports from vtep. +AT_CHECK([ovs-vsctl del-port p0 -- del-port p1]) +AT_CHECK([vtep-ctl del-port br-vtep p0 -- del-port br-vtep p1]) +OVS_WAIT_UNTIL([test -z "`ovn-sbctl list Chassis | grep -- br-vtep_lswitch`"]) +# should see empty chassis column in both binding entries. +AT_CHECK_UNQUOTED([ovn-sbctl --columns=chassis list Binding | cut -d ':' -f2 | tr -d ' ' | sort], [0], [dnl + +[[]] +[[]] +]) + +OVN_CONTROLLER_VTEP_STOP(["/already has logical port from the same chassis/d"]) +AT_CLEANUP -- 1.7.9.5 _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev