]> git.proxmox.com Git - mirror_ovs.git/blob - ovn/controller-vtep/binding.c
ovn-controller: Handle physical changes correctly
[mirror_ovs.git] / ovn / controller-vtep / binding.c
1 /* Copyright (c) 2015 Nicira, Inc.
2 *
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:
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
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.
14 */
15
16 #include <config.h>
17 #include "binding.h"
18
19 #include "lib/shash.h"
20 #include "lib/smap.h"
21 #include "lib/util.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"
26
27 VLOG_DEFINE_THIS_MODULE(binding);
28
29 /*
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.
34 *
35 */
36
37 \f
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. */
42 static bool
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)
47 {
48 const struct sbrec_port_binding *pb_conflict =
49 shash_find_data(ls_to_pb, vtep_lswitch);
50
51 if (pb_conflict) {
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);
59
60 return true;
61 }
62
63 shash_add(ls_to_pb, vtep_lswitch, port_binding_rec);
64 return false;
65 }
66
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
70 * returns false. */
71 static bool
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)
76 {
77 const struct sbrec_datapath_binding *db_conflict =
78 shash_find_data(ls_to_db, vtep_lswitch);
79
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);
91
92 return true;
93 }
94
95 shash_replace(ls_to_db, vtep_lswitch, port_binding_rec->datapath);
96 return false;
97 }
98
99 /* Updates the 'port_binding_rec''s chassis column to 'chassis_rec'. */
100 static void
101 update_pb_chassis(const struct sbrec_port_binding *port_binding_rec,
102 const struct sbrec_chassis *chassis_rec)
103 {
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,
110 chassis_rec->name);
111 }
112 sbrec_port_binding_set_chassis(port_binding_rec, chassis_rec);
113 }
114 }
115
116 \f
117 /* Checks and updates logical port to vtep logical switch bindings for each
118 * physical switch in VTEP. */
119 void
120 binding_run(struct controller_vtep_ctx *ctx)
121 {
122 if (!ctx->ovnsb_idl_txn) {
123 return;
124 }
125
126 /* 'ls_to_db'
127 *
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()
131 * for details.
132 *
133 * 'ls_to_pb'
134 *
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
138 * details.
139 *
140 */
141 struct shash ls_to_db = SHASH_INITIALIZER(&ls_to_db);
142
143 /* Stores the 'chassis' and the 'ls_to_pb' map related to
144 * a vtep physcial switch. */
145 struct ps {
146 const struct sbrec_chassis *chassis_rec;
147 struct shash ls_to_pb;
148 };
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);
155 size_t i;
156
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],
163 NULL);
164 }
165 shash_add(&ps_map, chassis_rec->name, ps);
166 }
167
168 ovsdb_idl_txn_add_comment(ctx->ovnsb_idl_txn,
169 "ovn-controller-vtep: updating bindings");
170
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");
180 struct ps *ps
181 = vtep_pswitch ? shash_find_data(&ps_map, vtep_pswitch) : NULL;
182 bool found_ls
183 = ps && vtep_lswitch && shash_find(&ps->ls_to_pb, vtep_lswitch);
184
185 if (!strcmp(type, "vtep") && found_ls) {
186 bool pb_conflict, db_conflict;
187
188 pb_conflict = check_pb_conflict(&ps->ls_to_pb, port_binding_rec,
189 ps->chassis_rec->name,
190 vtep_lswitch);
191 db_conflict = check_db_conflict(&ls_to_db, port_binding_rec,
192 ps->chassis_rec->name,
193 vtep_lswitch);
194 /* Updates port binding's chassis column when there
195 * is no conflict. */
196 if (!pb_conflict && !db_conflict) {
197 update_pb_chassis(port_binding_rec, ps->chassis_rec);
198 }
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);
204 }
205 }
206
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;
211
212 SHASH_FOR_EACH (node, &ps->ls_to_pb) {
213 if (!node->data) {
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);
218 }
219 }
220 shash_delete(&ps_map, iter);
221 shash_destroy(&ps->ls_to_pb);
222 free(ps);
223 }
224 shash_destroy(&ls_to_db);
225 shash_destroy(&ps_map);
226 }
227
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. */
231 bool
232 binding_cleanup(struct controller_vtep_ctx *ctx)
233 {
234 if (!ctx->ovnsb_idl_txn) {
235 return false;
236 }
237
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,
245 port_binding_rec);
246 }
247 }
248
249 ovsdb_idl_txn_add_comment(ctx->ovnsb_idl_txn,
250 "ovn-controller-vtep: removing bindings");
251
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);
256
257 if (!chassis_rec) {
258 continue;
259 }
260
261 for (;;) {
262 port_binding_rec = shash_find_and_delete(&ch_to_pb,
263 chassis_rec->name);
264 if (!port_binding_rec) {
265 break;
266 }
267 all_done = false;
268 update_pb_chassis(port_binding_rec, NULL);
269 }
270 }
271 shash_destroy(&ch_to_pb);
272
273 return all_done;
274 }