json: Move from lib to include/openvswitch.
[cascardo/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 "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 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 }