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