Convert binding_run to incremental processing.
[cascardo/ovs.git] / ovn / controller / encaps.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 "encaps.h"
18 #include "binding.h"
19 #include "lflow.h"
20
21 #include "lib/hash.h"
22 #include "lib/sset.h"
23 #include "lib/util.h"
24 #include "lib/vswitch-idl.h"
25 #include "openvswitch/vlog.h"
26 #include "ovn/lib/ovn-sb-idl.h"
27 #include "ovn-controller.h"
28
29 VLOG_DEFINE_THIS_MODULE(encaps);
30
31 void
32 encaps_register_ovs_idl(struct ovsdb_idl *ovs_idl)
33 {
34     ovsdb_idl_add_table(ovs_idl, &ovsrec_table_bridge);
35     ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_ports);
36     ovsdb_idl_add_table(ovs_idl, &ovsrec_table_port);
37     ovsdb_idl_add_column(ovs_idl, &ovsrec_port_col_name);
38     ovsdb_idl_add_column(ovs_idl, &ovsrec_port_col_interfaces);
39     ovsdb_idl_add_column(ovs_idl, &ovsrec_port_col_external_ids);
40     ovsdb_idl_add_table(ovs_idl, &ovsrec_table_interface);
41     ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_name);
42     ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_type);
43     ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_options);
44 }
45
46 /* Enough context to create a new tunnel, using tunnel_add(). */
47 struct tunnel_ctx {
48     /* Contains "struct port_hash_node"s.  Used to figure out what
49      * existing tunnels should be deleted: we index all of the OVN encap
50      * rows into this data structure, then as existing rows are
51      * generated we remove them.  After generating all the rows, any
52      * remaining in 'tunnel_hmap' must be deleted from the database. */
53     struct hmap tunnel_hmap;
54
55     /* Only valid within the process_full_encaps case in encaps_run(). */
56     struct hmap tunnel_hmap_by_uuid;
57
58     /* Names of all ports in the bridge, to allow checking uniqueness when
59      * adding a new tunnel. */
60     struct sset port_names;
61
62     struct ovsdb_idl_txn *ovs_txn;
63     const struct ovsrec_bridge *br_int;
64 };
65
66 static struct tunnel_ctx tc = {
67     .tunnel_hmap = HMAP_INITIALIZER(&tc.tunnel_hmap),
68     .tunnel_hmap_by_uuid = HMAP_INITIALIZER(&tc.tunnel_hmap_by_uuid),
69     .port_names = SSET_INITIALIZER(&tc.port_names),
70 };
71
72 static bool process_full_encaps = false;
73
74 struct port_hash_node {
75     struct hmap_node node;
76     struct hmap_node uuid_node;
77     const struct uuid *uuid;
78     const struct ovsrec_port *port;
79     const struct ovsrec_bridge *bridge;
80 };
81
82 static size_t
83 port_hash(const char *chassis_id, const char *type, const char *ip)
84 {
85     size_t hash = hash_string(chassis_id, 0);
86     hash = hash_string(type, hash);
87     return hash_string(ip, hash);
88 }
89
90 static size_t
91 port_hash_rec(const struct ovsrec_port *port)
92 {
93     const char *chassis_id, *ip;
94     const struct ovsrec_interface *iface;
95
96     chassis_id = smap_get(&port->external_ids, "ovn-chassis-id");
97
98     if (!chassis_id || !port->n_interfaces) {
99         /* This should not happen for an OVN-created port. */
100         return 0;
101     }
102
103     iface = port->interfaces[0];
104     ip = smap_get(&iface->options, "remote_ip");
105
106     return port_hash(chassis_id, iface->type, ip);
107 }
108
109 static char *
110 tunnel_create_name(const char *chassis_id)
111 {
112     int i;
113
114     for (i = 0; i < UINT16_MAX; i++) {
115         char *port_name;
116         port_name = xasprintf("ovn-%.6s-%x", chassis_id, i);
117
118         if (!sset_contains(&tc.port_names, port_name)) {
119             return port_name;
120         }
121
122         free(port_name);
123     }
124
125     return NULL;
126 }
127
128 static struct port_hash_node *
129 port_lookup_by_uuid(struct hmap *hmap_p, const struct uuid *uuid)
130 {
131     struct port_hash_node *answer;
132     HMAP_FOR_EACH_WITH_HASH (answer, uuid_node, uuid_hash(uuid),
133                              hmap_p) {
134         if (uuid_equals(uuid, answer->uuid)) {
135             return answer;
136         }
137     }
138     return NULL;
139 }
140
141 static struct port_hash_node *
142 port_lookup_by_port(const struct ovsrec_port *port)
143 {
144     struct port_hash_node *answer;
145     HMAP_FOR_EACH_WITH_HASH (answer, node, port_hash_rec(port),
146                              &tc.tunnel_hmap) {
147         if (port == answer->port) {
148             return answer;
149         }
150     }
151     return NULL;
152 }
153
154 static void
155 tunnel_add(const struct sbrec_chassis *chassis_rec,
156            const struct sbrec_encap *encap)
157 {
158     struct port_hash_node *hash_node;
159     const char *new_chassis_id = chassis_rec->name;
160
161     /* Check whether such a row already exists in OVS. If so, update
162      * the uuid field and insert into the by uuid hashmap. If not,
163      * create the tunnel. */
164
165     HMAP_FOR_EACH_WITH_HASH (hash_node, node,
166                              port_hash(new_chassis_id,
167                                        encap->type, encap->ip),
168                              &tc.tunnel_hmap) {
169         const struct ovsrec_port *port = hash_node->port;
170         const char *chassis_id = smap_get(&port->external_ids,
171                                           "ovn-chassis-id");
172         const struct ovsrec_interface *iface;
173         const char *ip;
174
175         if (!chassis_id || !port->n_interfaces) {
176             continue;
177         }
178
179         iface = port->interfaces[0];
180         ip = smap_get(&iface->options, "remote_ip");
181         if (!ip) {
182             continue;
183         }
184
185         if (!strcmp(new_chassis_id, chassis_id)
186             && !strcmp(encap->type, iface->type)
187             && !strcmp(encap->ip, ip)) {
188
189             hash_node->uuid = &chassis_rec->header_.uuid;
190             if (!port_lookup_by_uuid(&tc.tunnel_hmap_by_uuid,
191                                      hash_node->uuid)) {
192                 hmap_insert(&tc.tunnel_hmap_by_uuid, &hash_node->uuid_node,
193                             uuid_hash(hash_node->uuid));
194             }
195             return;
196         }
197     }
198
199     /* No such port, so add one. */
200     struct smap options = SMAP_INITIALIZER(&options);
201     struct ovsrec_port *port, **ports;
202     struct ovsrec_interface *iface;
203     char *port_name;
204     size_t i;
205
206     port_name = tunnel_create_name(new_chassis_id);
207     if (!port_name) {
208         VLOG_WARN("Unable to allocate unique name for '%s' tunnel",
209                   new_chassis_id);
210         return;
211     }
212
213     iface = ovsrec_interface_insert(tc.ovs_txn);
214     ovsrec_interface_set_name(iface, port_name);
215     ovsrec_interface_set_type(iface, encap->type);
216     smap_add(&options, "remote_ip", encap->ip);
217     smap_add(&options, "key", "flow");
218     ovsrec_interface_set_options(iface, &options);
219     smap_destroy(&options);
220
221     port = ovsrec_port_insert(tc.ovs_txn);
222     ovsrec_port_set_name(port, port_name);
223     ovsrec_port_set_interfaces(port, &iface, 1);
224     const struct smap id = SMAP_CONST1(&id, "ovn-chassis-id", new_chassis_id);
225     ovsrec_port_set_external_ids(port, &id);
226
227     ports = xmalloc(sizeof *tc.br_int->ports * (tc.br_int->n_ports + 1));
228     for (i = 0; i < tc.br_int->n_ports; i++) {
229         ports[i] = tc.br_int->ports[i];
230     }
231     ports[tc.br_int->n_ports] = port;
232     ovsrec_bridge_verify_ports(tc.br_int);
233     ovsrec_bridge_set_ports(tc.br_int, ports, tc.br_int->n_ports + 1);
234
235     sset_add(&tc.port_names, port_name);
236     free(port_name);
237     free(ports);
238     binding_reset_processing();
239     process_full_encaps = true;
240 }
241
242 static void
243 bridge_delete_port(const struct ovsrec_bridge *br,
244                    const struct ovsrec_port *port)
245 {
246     struct ovsrec_port **ports;
247     size_t i, n;
248
249     ports = xmalloc(sizeof *br->ports * br->n_ports);
250     for (i = n = 0; i < br->n_ports; i++) {
251         if (br->ports[i] != port) {
252             ports[n++] = br->ports[i];
253         }
254     }
255     ovsrec_bridge_verify_ports(br);
256     ovsrec_bridge_set_ports(br, ports, n);
257     free(ports);
258 }
259
260 static struct sbrec_encap *
261 preferred_encap(const struct sbrec_chassis *chassis_rec)
262 {
263     struct sbrec_encap *best_encap = NULL;
264     uint32_t best_type = 0;
265
266     for (int i = 0; i < chassis_rec->n_encaps; i++) {
267         uint32_t tun_type = get_tunnel_type(chassis_rec->encaps[i]->type);
268         if (tun_type > best_type) {
269             best_type = tun_type;
270             best_encap = chassis_rec->encaps[i];
271         }
272     }
273
274     return best_encap;
275 }
276
277 static bool
278 check_and_add_tunnel(const struct sbrec_chassis *chassis_rec,
279                      const char *chassis_id)
280 {
281     if (strcmp(chassis_rec->name, chassis_id)) {
282         const struct sbrec_encap *encap = preferred_encap(chassis_rec);
283         if (!encap) {
284             VLOG_INFO("No supported encaps for '%s'", chassis_rec->name);
285             return false;
286         }
287         tunnel_add(chassis_rec, encap);
288         return true;
289     }
290     return false;
291 }
292
293 static void
294 check_and_update_tunnel(const struct sbrec_chassis *chassis_rec)
295 {
296     struct port_hash_node *port_node;
297     port_node = port_lookup_by_uuid(&tc.tunnel_hmap_by_uuid,
298                                     &chassis_rec->header_.uuid);
299     if (port_node) {
300         const struct sbrec_encap *encap = preferred_encap(chassis_rec);
301         const struct ovsrec_port *port = port_node->port;
302         const struct ovsrec_interface *iface = port->interfaces[0];
303         char *port_name = tunnel_create_name(chassis_rec->name);
304         if (!port_name) {
305             VLOG_WARN("Unable to allocate unique name for '%s' tunnel",
306                       chassis_rec->name);
307             return;
308         }
309         if (strcmp(encap->type, iface->type)) {
310             ovsrec_interface_set_type(iface, encap->type);
311         }
312         if (strcmp(encap->ip, smap_get(&iface->options, "remote_ip"))) {
313             struct smap options = SMAP_INITIALIZER(&options);
314             smap_add(&options, "remote_ip", encap->ip);
315             smap_add(&options, "key", "flow");
316             ovsrec_interface_set_options(iface, &options);
317             smap_destroy(&options);
318         }
319
320         if (strcmp(chassis_rec->name, smap_get(&port->external_ids,
321                                                "ovn-chassis-id"))) {
322             const struct smap id = SMAP_CONST1(&id, "ovn-chassis-id",
323                                                chassis_rec->name);
324             ovsrec_port_set_external_ids(port, &id);
325         }
326     } else {
327         /* This tunnel has been lost and shouldn't have been, so
328          * warn the operator of that fact. */
329         VLOG_WARN("Unable to find tunnel for chassis '%s'",
330                   chassis_rec->name);
331     }
332 }
333
334 void
335 encaps_run(struct controller_ctx *ctx, const struct ovsrec_bridge *br_int,
336            const char *chassis_id)
337 {
338     if (!ctx->ovs_idl_txn || !br_int) {
339         return;
340     }
341
342     const struct sbrec_chassis *chassis_rec;
343     const struct ovsrec_bridge *br;
344
345     tc.br_int = br_int;
346     tc.ovs_txn = ctx->ovs_idl_txn;
347     ovsdb_idl_txn_add_comment(tc.ovs_txn,
348                               "ovn-controller: modifying OVS tunnels '%s'",
349                               chassis_id);
350
351     /* Collect all port names into tc.port_names.
352      *
353      * Collect all the OVN-created tunnels into tc.tunnel_hmap. */
354     OVSREC_BRIDGE_FOR_EACH(br, ctx->ovs_idl) {
355         size_t i;
356
357         for (i = 0; i < br->n_ports; i++) {
358             const struct ovsrec_port *port = br->ports[i];
359
360             sset_add(&tc.port_names, port->name);
361
362             const char *chassis_id = smap_get(&port->external_ids,
363                                               "ovn-chassis-id");
364             if (chassis_id && !port_lookup_by_port(port)) {
365                 struct port_hash_node *hash_node =
366                     xzalloc(sizeof *hash_node);
367                 hash_node->bridge = br;
368                 hash_node->port = port;
369                 hmap_insert(&tc.tunnel_hmap, &hash_node->node,
370                             port_hash_rec(port));
371                 process_full_encaps = true;
372             }
373         }
374     }
375
376     if (process_full_encaps) {
377         /* Create tunnels to the other chassis. */
378         struct hmap keep_tunnel_hmap_by_uuid =
379             HMAP_INITIALIZER(&keep_tunnel_hmap_by_uuid);
380         SBREC_CHASSIS_FOR_EACH (chassis_rec, ctx->ovnsb_idl) {
381             check_and_add_tunnel(chassis_rec, chassis_id);
382             struct port_hash_node *hash_node = xzalloc(sizeof *hash_node);
383             hash_node->uuid = &chassis_rec->header_.uuid;
384             hmap_insert(&keep_tunnel_hmap_by_uuid, &hash_node->uuid_node,
385                         uuid_hash(hash_node->uuid));
386         }
387
388         /* Delete any tunnels that weren't recreated above. */
389         struct port_hash_node *old_hash_node, *next_hash_node;
390         HMAP_FOR_EACH_SAFE (old_hash_node, next_hash_node,
391                             node, &tc.tunnel_hmap) {
392             if (!port_lookup_by_uuid(&keep_tunnel_hmap_by_uuid,
393                                      old_hash_node->uuid)) {
394                 bridge_delete_port(old_hash_node->bridge, old_hash_node->port);
395                 sset_find_and_delete(&tc.port_names,
396                                      old_hash_node->port->name);
397                 hmap_remove(&tc.tunnel_hmap, &old_hash_node->node);
398                 hmap_remove(&tc.tunnel_hmap_by_uuid,
399                             &old_hash_node->uuid_node);
400                 free(old_hash_node);
401             }
402         }
403         hmap_destroy(&keep_tunnel_hmap_by_uuid);
404         process_full_encaps = false;
405     } else {
406         SBREC_CHASSIS_FOR_EACH_TRACKED (chassis_rec, ctx->ovnsb_idl) {
407             bool is_deleted = sbrec_chassis_row_get_seqno(chassis_rec,
408                                                           OVSDB_IDL_CHANGE_DELETE) > 0;
409             bool is_new = sbrec_chassis_row_get_seqno(chassis_rec,
410                                                       OVSDB_IDL_CHANGE_MODIFY) == 0;
411
412             if (is_deleted) {
413                 /* Lookup the tunnel by row uuid and remove it. */
414                 struct port_hash_node *port_hash =
415                     port_lookup_by_uuid(&tc.tunnel_hmap_by_uuid,
416                                         &chassis_rec->header_.uuid);
417                 if (port_hash) {
418                     bridge_delete_port(port_hash->bridge, port_hash->port);
419                     sset_find_and_delete(&tc.port_names,
420                                          port_hash->port->name);
421                     hmap_remove(&tc.tunnel_hmap, &port_hash->node);
422                     hmap_remove(&tc.tunnel_hmap_by_uuid,
423                                 &port_hash->uuid_node);
424                     free(port_hash);
425                     binding_reset_processing();
426                 }
427                 continue;
428             }
429             if (!is_new) {
430                 check_and_update_tunnel(chassis_rec);
431                 continue;
432             } else {
433                 check_and_add_tunnel(chassis_rec, chassis_id);
434                 continue;
435             }
436         }
437     }
438 }
439
440 /* Returns true if the database is all cleaned up, false if more work is
441  * required. */
442 bool
443 encaps_cleanup(struct controller_ctx *ctx, const struct ovsrec_bridge *br_int)
444 {
445     if (!br_int) {
446         return true;
447     }
448
449     /* Delete all the OVS-created tunnels from the integration bridge. */
450     struct ovsrec_port **ports
451         = xmalloc(sizeof *br_int->ports * br_int->n_ports);
452     size_t n = 0;
453     for (size_t i = 0; i < br_int->n_ports; i++) {
454         if (!smap_get(&br_int->ports[i]->external_ids, "ovn-chassis-id")) {
455             ports[n++] = br_int->ports[i];
456         }
457     }
458
459     bool any_changes = n != br_int->n_ports;
460     if (any_changes && ctx->ovs_idl_txn) {
461         ovsdb_idl_txn_add_comment(ctx->ovs_idl_txn,
462                                   "ovn-controller: destroying tunnels");
463         ovsrec_bridge_verify_ports(br_int);
464         ovsrec_bridge_set_ports(br_int, ports, n);
465     }
466     free(ports);
467
468     return !any_changes;
469 }