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