Branch data Line data Source code
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 "openvswitch/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 : 14 : 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 : :
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 : 172 : 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 : 172 : const struct sbrec_port_binding *pb_conflict =
49 : : shash_find_data(ls_to_pb, vtep_lswitch);
50 : :
51 [ + + ]: 172 : if (pb_conflict) {
52 [ + - ]: 5 : 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 : 5 : sbrec_port_binding_set_chassis(port_binding_rec, NULL);
59 : :
60 : 5 : return true;
61 : : }
62 : :
63 : 167 : shash_add(ls_to_pb, vtep_lswitch, port_binding_rec);
64 : 167 : 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 : 172 : 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 : 172 : const struct sbrec_datapath_binding *db_conflict =
78 : : shash_find_data(ls_to_db, vtep_lswitch);
79 : :
80 [ + + ][ + + ]: 172 : if (db_conflict && db_conflict != port_binding_rec->datapath) {
81 [ + - ]: 2 : 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 : 2 : sbrec_port_binding_set_chassis(port_binding_rec, NULL);
91 : :
92 : 2 : return true;
93 : : }
94 : :
95 : 170 : shash_replace(ls_to_db, vtep_lswitch, port_binding_rec->datapath);
96 : 170 : return false;
97 : : }
98 : :
99 : : /* Updates the 'port_binding_rec''s chassis column to 'chassis_rec'. */
100 : : static void
101 : 174 : update_pb_chassis(const struct sbrec_port_binding *port_binding_rec,
102 : : const struct sbrec_chassis *chassis_rec)
103 : : {
104 [ + + ]: 174 : if (port_binding_rec->chassis != chassis_rec) {
105 [ + + ][ - + ]: 19 : if (chassis_rec && port_binding_rec->chassis) {
106 [ # # ]: 0 : 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 : 19 : sbrec_port_binding_set_chassis(port_binding_rec, chassis_rec);
113 : : }
114 : 174 : }
115 : :
116 : :
117 : : /* Checks and updates logical port to vtep logical switch bindings for each
118 : : * physical switch in VTEP. */
119 : : void
120 : 269 : binding_run(struct controller_vtep_ctx *ctx)
121 : : {
122 [ + + ]: 269 : if (!ctx->ovnsb_idl_txn) {
123 : 45 : 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 : 224 : 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 : 224 : struct shash ps_map = SHASH_INITIALIZER(&ps_map);
150 : : const struct vteprec_physical_switch *pswitch;
151 [ + + ]: 462 : VTEPREC_PHYSICAL_SWITCH_FOR_EACH (pswitch, ctx->vtep_idl) {
152 : 238 : const struct sbrec_chassis *chassis_rec
153 : 238 : = get_chassis_by_name(ctx->ovnsb_idl, pswitch->name);
154 : 238 : struct ps *ps = xmalloc(sizeof *ps);
155 : : size_t i;
156 : :
157 : : /* 'chassis_rec' must exist. */
158 [ - + ]: 238 : ovs_assert(chassis_rec);
159 : 238 : ps->chassis_rec = chassis_rec;
160 : 238 : shash_init(&ps->ls_to_pb);
161 [ + + ]: 468 : for (i = 0; i < chassis_rec->n_vtep_logical_switches; i++) {
162 : 230 : shash_add(&ps->ls_to_pb, chassis_rec->vtep_logical_switches[i],
163 : : NULL);
164 : : }
165 : 238 : shash_add(&ps_map, chassis_rec->name, ps);
166 : : }
167 : :
168 : 224 : 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 [ + + ]: 647 : SBREC_PORT_BINDING_FOR_EACH(port_binding_rec, ctx->ovnsb_idl) {
175 : 423 : const char *type = port_binding_rec->type;
176 : 423 : const char *vtep_pswitch = smap_get(&port_binding_rec->options,
177 : : "vtep-physical-switch");
178 : 423 : const char *vtep_lswitch = smap_get(&port_binding_rec->options,
179 : : "vtep-logical-switch");
180 : 423 : struct ps *ps
181 [ + + ]: 423 : = vtep_pswitch ? shash_find_data(&ps_map, vtep_pswitch) : NULL;
182 : 423 : bool found_ls
183 [ + + ][ + - ]: 423 : = ps && vtep_lswitch && shash_find(&ps->ls_to_pb, vtep_lswitch);
[ + + ]
184 : :
185 [ + + ][ + + ]: 595 : if (!strcmp(type, "vtep") && found_ls) {
186 : : bool pb_conflict, db_conflict;
187 : :
188 : 172 : pb_conflict = check_pb_conflict(&ps->ls_to_pb, port_binding_rec,
189 : 172 : ps->chassis_rec->name,
190 : : vtep_lswitch);
191 : 172 : db_conflict = check_db_conflict(&ls_to_db, port_binding_rec,
192 : 172 : ps->chassis_rec->name,
193 : : vtep_lswitch);
194 : : /* Updates port binding's chassis column when there
195 : : * is no conflict. */
196 [ + + ][ + + ]: 172 : if (!pb_conflict && !db_conflict) {
197 : 165 : update_pb_chassis(port_binding_rec, ps->chassis_rec);
198 : : }
199 [ + + ]: 251 : } else if (port_binding_rec->chassis
200 [ + + ]: 163 : && 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 : 4 : update_pb_chassis(port_binding_rec, NULL);
204 : : }
205 : : }
206 : :
207 : : struct shash_node *iter, *next;
208 [ + + ][ - + ]: 462 : SHASH_FOR_EACH_SAFE (iter, next, &ps_map) {
[ + + ]
209 : 238 : struct ps *ps = iter->data;
210 : : struct shash_node *node;
211 : :
212 [ + + ][ - + ]: 635 : SHASH_FOR_EACH (node, &ps->ls_to_pb) {
213 [ + + ]: 397 : if (!node->data) {
214 : : static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
215 [ - + ]: 230 : 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 : 238 : shash_delete(&ps_map, iter);
221 : 238 : shash_destroy(&ps->ls_to_pb);
222 : 238 : free(ps);
223 : : }
224 : 224 : shash_destroy(&ls_to_db);
225 : 224 : 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 : 27 : binding_cleanup(struct controller_vtep_ctx *ctx)
233 : : {
234 [ + + ]: 27 : if (!ctx->ovnsb_idl_txn) {
235 : 13 : return false;
236 : : }
237 : :
238 : 14 : struct shash ch_to_pb = SHASH_INITIALIZER(&ch_to_pb);
239 : : const struct sbrec_port_binding *port_binding_rec;
240 : 14 : bool all_done = true;
241 : : /* Hashs all port binding entries using the associated chassis name. */
242 [ + + ]: 46 : SBREC_PORT_BINDING_FOR_EACH(port_binding_rec, ctx->ovnsb_idl) {
243 [ + + ]: 32 : if (port_binding_rec->chassis) {
244 : 13 : shash_add(&ch_to_pb, port_binding_rec->chassis->name,
245 : : port_binding_rec);
246 : : }
247 : : }
248 : :
249 : 14 : ovsdb_idl_txn_add_comment(ctx->ovnsb_idl_txn,
250 : : "ovn-controller-vtep: removing bindings");
251 : :
252 : : const struct vteprec_physical_switch *pswitch;
253 [ + + ]: 28 : VTEPREC_PHYSICAL_SWITCH_FOR_EACH (pswitch, ctx->vtep_idl) {
254 : 14 : const struct sbrec_chassis *chassis_rec
255 : 14 : = get_chassis_by_name(ctx->ovnsb_idl, pswitch->name);
256 : :
257 [ + + ]: 14 : if (!chassis_rec) {
258 : 7 : continue;
259 : : }
260 : :
261 : : for (;;) {
262 : 12 : port_binding_rec = shash_find_and_delete(&ch_to_pb,
263 : 12 : chassis_rec->name);
264 [ + + ]: 12 : if (!port_binding_rec) {
265 : 7 : break;
266 : : }
267 : 5 : all_done = false;
268 : 5 : update_pb_chassis(port_binding_rec, NULL);
269 : 5 : }
270 : : }
271 : 14 : shash_destroy(&ch_to_pb);
272 : :
273 : 27 : return all_done;
274 : : }
|