On 07/16/2015 03:56 AM, Alex Wang wrote: > 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>
As discussed before, I'd like to see this done using logical port type and options instead of a special name, but this could be reworked later if it merges before the addition of type and options. A few comments inline .. > > --- > 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. This is an important thing to note in docs somewhere. For now I suppose ovn-nb.xml is the best place. > + * > + */ > + > +/* 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; > + How about checking to make sure chassis_rec is non-NULL here? I think it shouldn't be possible today, but just in case ... > + /* '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 " It might help to clarify that "chassis" is a "gateway" or "vtep" chassis in this log message. It confused me at first. > + "(%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; > + }; I think the shash API would be a little easier here. > + > + /* 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 > -- Russell Bryant _______________________________________________ dev mailing list dev@openvswitch.org http://openvswitch.org/mailman/listinfo/dev