7aa523f8a928df70bfb6b3d034876f0ee9b8b886
[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
19 #include "lib/hash.h"
20 #include "lib/sset.h"
21 #include "lib/util.h"
22 #include "lib/vswitch-idl.h"
23 #include "openvswitch/vlog.h"
24 #include "ovn/lib/ovn-sb-idl.h"
25 #include "ovn-controller.h"
26
27 VLOG_DEFINE_THIS_MODULE(encaps);
28
29 void
30 encaps_init(struct controller_ctx *ctx)
31 {
32     ovsdb_idl_add_table(ctx->ovs_idl, &ovsrec_table_bridge);
33     ovsdb_idl_add_column(ctx->ovs_idl, &ovsrec_bridge_col_ports);
34     ovsdb_idl_add_table(ctx->ovs_idl, &ovsrec_table_port);
35     ovsdb_idl_add_column(ctx->ovs_idl, &ovsrec_port_col_name);
36     ovsdb_idl_add_column(ctx->ovs_idl, &ovsrec_port_col_interfaces);
37     ovsdb_idl_add_column(ctx->ovs_idl, &ovsrec_port_col_external_ids);
38     ovsdb_idl_add_table(ctx->ovs_idl, &ovsrec_table_interface);
39     ovsdb_idl_add_column(ctx->ovs_idl, &ovsrec_interface_col_name);
40     ovsdb_idl_add_column(ctx->ovs_idl, &ovsrec_interface_col_type);
41     ovsdb_idl_add_column(ctx->ovs_idl, &ovsrec_interface_col_options);
42 }
43
44 /* Enough context to create a new tunnel, using tunnel_add(). */
45 struct tunnel_ctx {
46     /* Contains "struct port_hash_node"s.  Used to figure out what
47      * existing tunnels should be deleted: we index all of the OVN encap
48      * rows into this data structure, then as existing rows are
49      * generated we remove them.  After generating all the rows, any
50      * remaining in 'tunnel_hmap' must be deleted from the database. */
51     struct hmap tunnel_hmap;
52
53     /* Names of all ports in the bridge, to allow checking uniqueness when
54      * adding a new tunnel. */
55     struct sset port_names;
56
57     struct ovsdb_idl_txn *ovs_txn;
58     const struct ovsrec_bridge *br_int;
59 };
60
61 struct port_hash_node {
62     struct hmap_node node;
63     const struct ovsrec_port *port;
64     const struct ovsrec_bridge *bridge;
65 };
66
67 static size_t
68 port_hash(const char *chassis_id, const char *type, const char *ip)
69 {
70     size_t hash = hash_string(chassis_id, 0);
71     hash = hash_string(type, hash);
72     return hash_string(ip, hash);
73 }
74
75 static size_t
76 port_hash_rec(const struct ovsrec_port *port)
77 {
78     const char *chassis_id, *ip;
79     const struct ovsrec_interface *iface;
80
81     chassis_id = smap_get(&port->external_ids, "ovn-chassis-id");
82
83     if (!chassis_id || !port->n_interfaces) {
84         /* This should not happen for an OVN-created port. */
85         return 0;
86     }
87
88     iface = port->interfaces[0];
89     ip = smap_get(&iface->options, "remote_ip");
90
91     return port_hash(chassis_id, iface->type, ip);
92 }
93
94 static char *
95 tunnel_create_name(struct tunnel_ctx *tc, const char *chassis_id)
96 {
97     int i;
98
99     for (i = 0; i < UINT16_MAX; i++) {
100         char *port_name;
101         port_name = xasprintf("ovn-%.6s-%x", chassis_id, i);
102
103         if (!sset_contains(&tc->port_names, port_name)) {
104             return port_name;
105         }
106
107         free(port_name);
108     }
109
110     return NULL;
111 }
112
113
114 static void
115 tunnel_add(struct tunnel_ctx *tc, const char *new_chassis_id,
116            const struct sbrec_encap *encap)
117 {
118     struct port_hash_node *hash_node;
119
120     /* Check whether such a row already exists in OVS.  If so, remove it
121      * from 'tc->tunnel_hmap' and we're done. */
122     HMAP_FOR_EACH_WITH_HASH (hash_node, node,
123                              port_hash(new_chassis_id,
124                                        encap->type, encap->ip),
125                              &tc->tunnel_hmap) {
126         const struct ovsrec_port *port = hash_node->port;
127         const char *chassis_id = smap_get(&port->external_ids,
128                                           "ovn-chassis-id");
129         const struct ovsrec_interface *iface;
130         const char *ip;
131
132         if (!chassis_id || !port->n_interfaces) {
133             continue;
134         }
135
136         iface = port->interfaces[0];
137         ip = smap_get(&iface->options, "remote_ip");
138         if (!ip) {
139             continue;
140         }
141
142         if (!strcmp(new_chassis_id, chassis_id)
143             && !strcmp(encap->type, iface->type)
144             && !strcmp(encap->ip, ip)) {
145             hmap_remove(&tc->tunnel_hmap, &hash_node->node);
146             free(hash_node);
147             return;
148         }
149     }
150
151     /* No such port, so add one. */
152     struct smap external_ids = SMAP_INITIALIZER(&external_ids);
153     struct smap options = SMAP_INITIALIZER(&options);
154     struct ovsrec_port *port, **ports;
155     struct ovsrec_interface *iface;
156     char *port_name;
157     size_t i;
158
159     port_name = tunnel_create_name(tc, new_chassis_id);
160     if (!port_name) {
161         VLOG_WARN("Unable to allocate unique name for '%s' tunnel",
162                   new_chassis_id);
163         return;
164     }
165
166     iface = ovsrec_interface_insert(tc->ovs_txn);
167     ovsrec_interface_set_name(iface, port_name);
168     ovsrec_interface_set_type(iface, encap->type);
169     smap_add(&options, "remote_ip", encap->ip);
170     smap_add(&options, "key", "flow");
171     ovsrec_interface_set_options(iface, &options);
172     smap_destroy(&options);
173
174     port = ovsrec_port_insert(tc->ovs_txn);
175     ovsrec_port_set_name(port, port_name);
176     ovsrec_port_set_interfaces(port, &iface, 1);
177     smap_add(&external_ids, "ovn-chassis-id", new_chassis_id);
178     ovsrec_port_set_external_ids(port, &external_ids);
179     smap_destroy(&external_ids);
180
181     ports = xmalloc(sizeof *tc->br_int->ports * (tc->br_int->n_ports + 1));
182     for (i = 0; i < tc->br_int->n_ports; i++) {
183         ports[i] = tc->br_int->ports[i];
184     }
185     ports[tc->br_int->n_ports] = port;
186     ovsrec_bridge_verify_ports(tc->br_int);
187     ovsrec_bridge_set_ports(tc->br_int, ports, tc->br_int->n_ports + 1);
188
189     sset_add(&tc->port_names, port_name);
190     free(port_name);
191     free(ports);
192 }
193
194 static void
195 bridge_delete_port(const struct ovsrec_bridge *br,
196                    const struct ovsrec_port *port)
197 {
198     struct ovsrec_port **ports;
199     size_t i, n;
200
201     ports = xmalloc(sizeof *br->ports * br->n_ports);
202     for (i = n = 0; i < br->n_ports; i++) {
203         if (br->ports[i] != port) {
204             ports[n++] = br->ports[i];
205         }
206     }
207     ovsrec_bridge_verify_ports(br);
208     ovsrec_bridge_set_ports(br, ports, n);
209     free(ports);
210 }
211
212 static struct sbrec_encap *
213 preferred_encap(const struct sbrec_chassis *chassis_rec)
214 {
215     size_t i;
216
217     /* For hypervisors, we only support Geneve and STT encapsulations.
218      * Sets are returned alphabetically, so "geneve" will be preferred
219      * over "stt". */
220     for (i = 0; i < chassis_rec->n_encaps; i++) {
221         if (!strcmp(chassis_rec->encaps[i]->type, "geneve")
222                 || !strcmp(chassis_rec->encaps[i]->type, "stt")) {
223             return chassis_rec->encaps[i];
224         }
225     }
226
227     return NULL;
228 }
229
230 void
231 encaps_run(struct controller_ctx *ctx, const struct ovsrec_bridge *br_int,
232            const char *chassis_id)
233 {
234     if (!ctx->ovs_idl_txn) {
235         return;
236     }
237
238     const struct sbrec_chassis *chassis_rec;
239     const struct ovsrec_bridge *br;
240
241     struct tunnel_ctx tc = {
242         .tunnel_hmap = HMAP_INITIALIZER(&tc.tunnel_hmap),
243         .port_names = SSET_INITIALIZER(&tc.port_names),
244         .br_int = br_int
245     };
246
247     tc.ovs_txn = ctx->ovs_idl_txn;
248     ovsdb_idl_txn_add_comment(tc.ovs_txn,
249                               "ovn-controller: modifying OVS tunnels '%s'",
250                               chassis_id);
251
252     /* Collect all port names into tc.port_names.
253      *
254      * Collect all the OVN-created tunnels into tc.tunnel_hmap. */
255     OVSREC_BRIDGE_FOR_EACH(br, ctx->ovs_idl) {
256         size_t i;
257
258         for (i = 0; i < br->n_ports; i++) {
259             const struct ovsrec_port *port = br->ports[i];
260
261             sset_add(&tc.port_names, port->name);
262
263             if (smap_get(&port->external_ids, "ovn-chassis-id")) {
264                 struct port_hash_node *hash_node = xzalloc(sizeof *hash_node);
265                 hash_node->bridge = br;
266                 hash_node->port = port;
267                 hmap_insert(&tc.tunnel_hmap, &hash_node->node,
268                             port_hash_rec(port));
269             }
270         }
271     }
272
273     SBREC_CHASSIS_FOR_EACH(chassis_rec, ctx->ovnsb_idl) {
274         if (strcmp(chassis_rec->name, chassis_id)) {
275             /* Create tunnels to the other chassis. */
276             const struct sbrec_encap *encap = preferred_encap(chassis_rec);
277             if (!encap) {
278                 VLOG_INFO("No supported encaps for '%s'", chassis_rec->name);
279                 continue;
280             }
281             tunnel_add(&tc, chassis_rec->name, encap);
282         }
283     }
284
285     /* Delete any existing OVN tunnels that were not still around. */
286     struct port_hash_node *hash_node, *next_hash_node;
287     HMAP_FOR_EACH_SAFE (hash_node, next_hash_node, node, &tc.tunnel_hmap) {
288         hmap_remove(&tc.tunnel_hmap, &hash_node->node);
289         bridge_delete_port(hash_node->bridge, hash_node->port);
290         free(hash_node);
291     }
292     hmap_destroy(&tc.tunnel_hmap);
293     sset_destroy(&tc.port_names);
294 }
295
296 /* Returns true if the database is all cleaned up, false if more work is
297  * required. */
298 bool
299 encaps_cleanup(struct controller_ctx *ctx, const struct ovsrec_bridge *br_int)
300 {
301     /* Delete all the OVS-created tunnels from the integration bridge. */
302     struct ovsrec_port **ports
303         = xmalloc(sizeof *br_int->ports * br_int->n_ports);
304     size_t n = 0;
305     for (size_t i = 0; i < br_int->n_ports; i++) {
306         if (!smap_get(&br_int->ports[i]->external_ids, "ovn-chassis-id")) {
307             ports[n++] = br_int->ports[i];
308         }
309     }
310
311     bool any_changes = n != br_int->n_ports;
312     if (any_changes && ctx->ovs_idl_txn) {
313         ovsdb_idl_txn_add_comment(ctx->ovs_idl_txn,
314                                   "ovn-controller: destroying tunnels");
315         ovsrec_bridge_verify_ports(br_int);
316         ovsrec_bridge_set_ports(br_int, ports, n);
317     }
318     free(ports);
319
320     return !any_changes;
321 }