1 /* Copyright (c) 2015 Nicira, Inc.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at:
7 * http://www.apache.org/licenses/LICENSE-2.0
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
19 #include "lib/shash.h"
22 #include "openvswitch/vlog.h"
23 #include "ovn-controller-vtep.h"
24 #include "ovn/lib/ovn-sb-idl.h"
25 #include "vtep/vtep-idl.h"
27 VLOG_DEFINE_THIS_MODULE(binding);
30 * This module scans through the Port_Binding table in ovnsb. If there is a
31 * logical port binding entry for logical switch in vtep gateway chassis's
32 * 'vtep_logical_switches' column, sets the binding's chassis column to the
33 * corresponding vtep gateway chassis.
38 /* Returns true if the 'vtep_lswitch' specified in 'port_binding_rec'
39 * has already been bound to another port binding entry, and resets
40 * 'port_binding_rec''s chassis column. Otherwise, updates 'ls_to_pb'
41 * and returns false. */
43 check_pb_conflict(struct shash *ls_to_pb,
44 const struct sbrec_port_binding *port_binding_rec,
45 const char *chassis_name,
46 const char *vtep_lswitch)
48 const struct sbrec_port_binding *pb_conflict =
49 shash_find_data(ls_to_pb, vtep_lswitch);
52 VLOG_WARN("logical switch (%s), on vtep gateway chassis "
53 "(%s) has already been associated with logical "
54 "port (%s), ignore logical port (%s)",
55 vtep_lswitch, chassis_name,
56 pb_conflict->logical_port,
57 port_binding_rec->logical_port);
58 sbrec_port_binding_set_chassis(port_binding_rec, NULL);
63 shash_add(ls_to_pb, vtep_lswitch, port_binding_rec);
67 /* Returns true if the 'vtep_lswitch' specified in 'port_binding_rec'
68 * has already been bound to a different datapath, and resets
69 * 'port_binding_rec''s chassis column. Otherwise, updates 'ls_to_db' and
72 check_db_conflict(struct shash *ls_to_db,
73 const struct sbrec_port_binding *port_binding_rec,
74 const char *chassis_name,
75 const char *vtep_lswitch)
77 const struct sbrec_datapath_binding *db_conflict =
78 shash_find_data(ls_to_db, vtep_lswitch);
80 if (db_conflict && db_conflict != port_binding_rec->datapath) {
81 VLOG_WARN("logical switch (%s), on vtep gateway chassis "
82 "(%s) has already been associated with logical "
83 "datapath (with tunnel key %"PRId64"), ignore "
84 "logical port (%s) which belongs to logical "
85 "datapath (with tunnel key %"PRId64")",
86 vtep_lswitch, chassis_name,
87 db_conflict->tunnel_key,
88 port_binding_rec->logical_port,
89 port_binding_rec->datapath->tunnel_key);
90 sbrec_port_binding_set_chassis(port_binding_rec, NULL);
95 shash_replace(ls_to_db, vtep_lswitch, port_binding_rec->datapath);
99 /* Updates the 'port_binding_rec''s chassis column to 'chassis_rec'. */
101 update_pb_chassis(const struct sbrec_port_binding *port_binding_rec,
102 const struct sbrec_chassis *chassis_rec)
104 if (port_binding_rec->chassis != chassis_rec) {
105 if (chassis_rec && port_binding_rec->chassis) {
106 VLOG_DBG("Changing chassis association of logical "
107 "port (%s) from (%s) to (%s)",
108 port_binding_rec->logical_port,
109 port_binding_rec->chassis->name,
112 sbrec_port_binding_set_chassis(port_binding_rec, chassis_rec);
117 /* Checks and updates logical port to vtep logical switch bindings for each
118 * physical switch in VTEP. */
120 binding_run(struct controller_vtep_ctx *ctx)
122 if (!ctx->ovnsb_idl_txn) {
128 * Maps vtep logical switch name to the datapath binding entry. This is
129 * used to guarantee that each vtep logical switch is only included
130 * in only one ovn datapath (ovn logical switch). See check_db_conflict()
135 * Maps vtep logical switch name to the port binding entry. This is used
136 * to guarantee that each vtep logical switch on a vtep physical switch
137 * is only bound to one logical port. See check_pb_conflict() for
141 struct shash ls_to_db = SHASH_INITIALIZER(&ls_to_db);
143 /* Stores the 'chassis' and the 'ls_to_pb' map related to
144 * a vtep physcial switch. */
146 const struct sbrec_chassis *chassis_rec;
147 struct shash ls_to_pb;
149 struct shash ps_map = SHASH_INITIALIZER(&ps_map);
150 const struct vteprec_physical_switch *pswitch;
151 VTEPREC_PHYSICAL_SWITCH_FOR_EACH (pswitch, ctx->vtep_idl) {
152 const struct sbrec_chassis *chassis_rec
153 = get_chassis_by_name(ctx->ovnsb_idl, pswitch->name);
154 struct ps *ps = xmalloc(sizeof *ps);
157 /* 'chassis_rec' must exist. */
158 ovs_assert(chassis_rec);
159 ps->chassis_rec = chassis_rec;
160 shash_init(&ps->ls_to_pb);
161 for (i = 0; i < chassis_rec->n_vtep_logical_switches; i++) {
162 shash_add(&ps->ls_to_pb, chassis_rec->vtep_logical_switches[i],
165 shash_add(&ps_map, chassis_rec->name, ps);
168 ovsdb_idl_txn_add_comment(ctx->ovnsb_idl_txn,
169 "ovn-controller-vtep: updating bindings");
171 const struct sbrec_port_binding *port_binding_rec;
172 /* Port binding for vtep gateway chassis must have type "vtep",
173 * and matched physical switch name and logical switch name. */
174 SBREC_PORT_BINDING_FOR_EACH(port_binding_rec, ctx->ovnsb_idl) {
175 const char *type = port_binding_rec->type;
176 const char *vtep_pswitch = smap_get(&port_binding_rec->options,
177 "vtep-physical-switch");
178 const char *vtep_lswitch = smap_get(&port_binding_rec->options,
179 "vtep-logical-switch");
181 = vtep_pswitch ? shash_find_data(&ps_map, vtep_pswitch) : NULL;
183 = ps && vtep_lswitch && shash_find(&ps->ls_to_pb, vtep_lswitch);
185 if (!strcmp(type, "vtep") && found_ls) {
186 bool pb_conflict, db_conflict;
188 pb_conflict = check_pb_conflict(&ps->ls_to_pb, port_binding_rec,
189 ps->chassis_rec->name,
191 db_conflict = check_db_conflict(&ls_to_db, port_binding_rec,
192 ps->chassis_rec->name,
194 /* Updates port binding's chassis column when there
196 if (!pb_conflict && !db_conflict) {
197 update_pb_chassis(port_binding_rec, ps->chassis_rec);
199 } else if (port_binding_rec->chassis
200 && shash_find(&ps_map, port_binding_rec->chassis->name)) {
201 /* Resets 'port_binding_rec' since it is no longer bound to
202 * any vtep logical switch. */
203 update_pb_chassis(port_binding_rec, NULL);
207 struct shash_node *iter, *next;
208 SHASH_FOR_EACH_SAFE (iter, next, &ps_map) {
209 struct ps *ps = iter->data;
210 struct shash_node *node;
212 SHASH_FOR_EACH (node, &ps->ls_to_pb) {
214 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
215 VLOG_DBG_RL(&rl, "No port binding entry for logical switch (%s)"
216 "on vtep gateway chassis (%s)", node->name,
217 ps->chassis_rec->name);
220 shash_delete(&ps_map, iter);
221 shash_destroy(&ps->ls_to_pb);
224 shash_destroy(&ls_to_db);
225 shash_destroy(&ps_map);
228 /* Removes all port binding association with vtep gateway chassis.
229 * Returns true when done (i.e. there is no change made to 'ctx->ovnsb_idl'),
230 * otherwise returns false. */
232 binding_cleanup(struct controller_vtep_ctx *ctx)
234 if (!ctx->ovnsb_idl_txn) {
238 struct shash ch_to_pb = SHASH_INITIALIZER(&ch_to_pb);
239 const struct sbrec_port_binding *port_binding_rec;
240 bool all_done = true;
241 /* Hashs all port binding entries using the associated chassis name. */
242 SBREC_PORT_BINDING_FOR_EACH(port_binding_rec, ctx->ovnsb_idl) {
243 if (port_binding_rec->chassis) {
244 shash_add(&ch_to_pb, port_binding_rec->chassis->name,
249 ovsdb_idl_txn_add_comment(ctx->ovnsb_idl_txn,
250 "ovn-controller-vtep: removing bindings");
252 const struct vteprec_physical_switch *pswitch;
253 VTEPREC_PHYSICAL_SWITCH_FOR_EACH (pswitch, ctx->vtep_idl) {
254 const struct sbrec_chassis *chassis_rec
255 = get_chassis_by_name(ctx->ovnsb_idl, pswitch->name);
262 port_binding_rec = shash_find_and_delete(&ch_to_pb,
264 if (!port_binding_rec) {
268 update_pb_chassis(port_binding_rec, NULL);
271 shash_destroy(&ch_to_pb);