1a2bb32ef3985a5fb767b610e319c72763c52aff
[cascardo/ovs.git] / ovn / controller / chassis.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 "chassis.h"
18
19 #include "lib/hash.h"
20 #include "lib/poll-loop.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(chassis);
29
30 void
31 chassis_init(struct controller_ctx *ctx)
32 {
33     ovsdb_idl_add_table(ctx->ovs_idl, &ovsrec_table_open_vswitch);
34     ovsdb_idl_add_column(ctx->ovs_idl, &ovsrec_open_vswitch_col_external_ids);
35     ovsdb_idl_add_table(ctx->ovs_idl, &ovsrec_table_bridge);
36     ovsdb_idl_add_column(ctx->ovs_idl, &ovsrec_bridge_col_ports);
37     ovsdb_idl_add_table(ctx->ovs_idl, &ovsrec_table_port);
38     ovsdb_idl_add_column(ctx->ovs_idl, &ovsrec_port_col_name);
39     ovsdb_idl_add_column(ctx->ovs_idl, &ovsrec_port_col_interfaces);
40     ovsdb_idl_add_column(ctx->ovs_idl, &ovsrec_port_col_external_ids);
41     ovsdb_idl_add_table(ctx->ovs_idl, &ovsrec_table_interface);
42     ovsdb_idl_add_column(ctx->ovs_idl, &ovsrec_interface_col_name);
43     ovsdb_idl_add_column(ctx->ovs_idl, &ovsrec_interface_col_type);
44     ovsdb_idl_add_column(ctx->ovs_idl, &ovsrec_interface_col_options);
45 }
46
47 static void
48 register_chassis(struct controller_ctx *ctx)
49 {
50     const struct sbrec_chassis *chassis_rec;
51     const struct ovsrec_open_vswitch *cfg;
52     const char *encap_type, *encap_ip;
53     struct sbrec_encap *encap_rec;
54     static bool inited = false;
55     int retval = TXN_TRY_AGAIN;
56     struct ovsdb_idl_txn *txn;
57
58     SBREC_CHASSIS_FOR_EACH(chassis_rec, ctx->ovnsb_idl) {
59         if (!strcmp(chassis_rec->name, ctx->chassis_id)) {
60             break;
61         }
62     }
63
64     /* xxx Need to support more than one encap.  Also need to support
65      * xxx encap options. */
66     cfg = ovsrec_open_vswitch_first(ctx->ovs_idl);
67     if (!cfg) {
68         VLOG_INFO("No Open_vSwitch row defined.");
69         return;
70     }
71
72     encap_type = smap_get(&cfg->external_ids, "ovn-encap-type");
73     encap_ip = smap_get(&cfg->external_ids, "ovn-encap-ip");
74     if (!encap_type || !encap_ip) {
75         VLOG_INFO("Need to specify an encap type and ip");
76         return;
77     }
78
79     if (chassis_rec) {
80         int i;
81
82         for (i = 0; i < chassis_rec->n_encaps; i++) {
83             if (!strcmp(chassis_rec->encaps[i]->type, encap_type)
84                 && !strcmp(chassis_rec->encaps[i]->ip, encap_ip)) {
85                 /* Nothing changed. */
86                 inited = true;
87                 return;
88             } else if (!inited) {
89                 VLOG_WARN("Chassis config changing on startup, make sure "
90                           "multiple chassis are not configured : %s/%s->%s/%s",
91                           chassis_rec->encaps[i]->type,
92                           chassis_rec->encaps[i]->ip,
93                           encap_type, encap_ip);
94             }
95
96         }
97     }
98
99     txn = ovsdb_idl_txn_create(ctx->ovnsb_idl);
100     ovsdb_idl_txn_add_comment(txn,
101                               "ovn-controller: registering chassis '%s'",
102                               ctx->chassis_id);
103
104     if (!chassis_rec) {
105         chassis_rec = sbrec_chassis_insert(txn);
106         sbrec_chassis_set_name(chassis_rec, ctx->chassis_id);
107     }
108
109     encap_rec = sbrec_encap_insert(txn);
110
111     sbrec_encap_set_type(encap_rec, encap_type);
112     sbrec_encap_set_ip(encap_rec, encap_ip);
113
114     sbrec_chassis_set_encaps(chassis_rec, &encap_rec, 1);
115
116     retval = ovsdb_idl_txn_commit_block(txn);
117     if (retval != TXN_SUCCESS && retval != TXN_UNCHANGED) {
118         VLOG_INFO("Problem registering chassis: %s",
119                   ovsdb_idl_txn_status_to_string(retval));
120         poll_immediate_wake();
121     }
122     ovsdb_idl_txn_destroy(txn);
123
124     inited = true;
125 }
126
127 /* Enough context to create a new tunnel, using tunnel_add(). */
128 struct tunnel_ctx {
129     /* Contains "struct port_hash_node"s.  Used to figure out what
130      * existing tunnels should be deleted: we index all of the OVN encap
131      * rows into this data structure, then as existing rows are
132      * generated we remove them.  After generating all the rows, any
133      * remaining in 'tunnel_hmap' must be deleted from the database. */
134     struct hmap tunnel_hmap;
135
136     /* Names of all ports in the bridge, to allow checking uniqueness when
137      * adding a new tunnel. */
138     struct sset port_names;
139
140     struct ovsdb_idl_txn *ovs_txn;
141     const struct ovsrec_bridge *br_int;
142 };
143
144 struct port_hash_node {
145     struct hmap_node node;
146     const struct ovsrec_port *port;
147     const struct ovsrec_bridge *bridge;
148 };
149
150 static size_t
151 port_hash(const char *chassis_id, const char *type, const char *ip)
152 {
153     size_t hash = hash_string(chassis_id, 0);
154     hash = hash_string(type, hash);
155     return hash_string(ip, hash);
156 }
157
158 static size_t
159 port_hash_rec(const struct ovsrec_port *port)
160 {
161     const char *chassis_id, *ip;
162     const struct ovsrec_interface *iface;
163
164     chassis_id = smap_get(&port->external_ids, "ovn-chassis-id");
165
166     if (!chassis_id || !port->n_interfaces) {
167         /* This should not happen for an OVN-created port. */
168         return 0;
169     }
170
171     iface = port->interfaces[0];
172     ip = smap_get(&iface->options, "remote_ip");
173
174     return port_hash(chassis_id, iface->type, ip);
175 }
176
177 static char *
178 tunnel_create_name(struct tunnel_ctx *tc, const char *chassis_id)
179 {
180     int i;
181
182     for (i = 0; i < UINT16_MAX; i++) {
183         char *port_name;
184         port_name = xasprintf("ovn-%.6s-%x", chassis_id, i);
185
186         if (!sset_contains(&tc->port_names, port_name)) {
187             return port_name;
188         }
189
190         free(port_name);
191     }
192
193     return NULL;
194 }
195
196
197 static void
198 tunnel_add(struct tunnel_ctx *tc, const char *new_chassis_id,
199            const struct sbrec_encap *encap)
200 {
201     struct port_hash_node *hash_node;
202
203     /* Check whether such a row already exists in OVS.  If so, remove it
204      * from 'tc->tunnel_hmap' and we're done. */
205     HMAP_FOR_EACH_WITH_HASH (hash_node, node,
206                              port_hash(new_chassis_id,
207                                        encap->type, encap->ip),
208                              &tc->tunnel_hmap) {
209         const struct ovsrec_port *port = hash_node->port;
210         const char *chassis_id = smap_get(&port->external_ids,
211                                           "ovn-chassis-id");
212         const struct ovsrec_interface *iface;
213         const char *ip;
214
215         if (!chassis_id || !port->n_interfaces) {
216             continue;
217         }
218
219         iface = port->interfaces[0];
220         ip = smap_get(&iface->options, "remote_ip");
221         if (!ip) {
222             continue;
223         }
224
225         if (!strcmp(new_chassis_id, chassis_id)
226             && !strcmp(encap->type, iface->type)
227             && !strcmp(encap->ip, ip)) {
228             hmap_remove(&tc->tunnel_hmap, &hash_node->node);
229             free(hash_node);
230             return;
231         }
232     }
233
234     /* No such port, so add one. */
235     struct smap external_ids = SMAP_INITIALIZER(&external_ids);
236     struct smap options = SMAP_INITIALIZER(&options);
237     struct ovsrec_port *port, **ports;
238     struct ovsrec_interface *iface;
239     char *port_name;
240     size_t i;
241
242     port_name = tunnel_create_name(tc, new_chassis_id);
243     if (!port_name) {
244         VLOG_WARN("Unable to allocate unique name for '%s' tunnel",
245                   new_chassis_id);
246         return;
247     }
248
249     iface = ovsrec_interface_insert(tc->ovs_txn);
250     ovsrec_interface_set_name(iface, port_name);
251     ovsrec_interface_set_type(iface, encap->type);
252     smap_add(&options, "remote_ip", encap->ip);
253     smap_add(&options, "key", "flow");
254     ovsrec_interface_set_options(iface, &options);
255     smap_destroy(&options);
256
257     port = ovsrec_port_insert(tc->ovs_txn);
258     ovsrec_port_set_name(port, port_name);
259     ovsrec_port_set_interfaces(port, &iface, 1);
260     smap_add(&external_ids, "ovn-chassis-id", new_chassis_id);
261     ovsrec_port_set_external_ids(port, &external_ids);
262     smap_destroy(&external_ids);
263
264     ports = xmalloc(sizeof *tc->br_int->ports * (tc->br_int->n_ports + 1));
265     for (i = 0; i < tc->br_int->n_ports; i++) {
266         ports[i] = tc->br_int->ports[i];
267     }
268     ports[tc->br_int->n_ports] = port;
269     ovsrec_bridge_set_ports(tc->br_int, ports, tc->br_int->n_ports + 1);
270
271     sset_add(&tc->port_names, port_name);
272     free(port_name);
273     free(ports);
274 }
275
276 static void
277 bridge_delete_port(const struct ovsrec_bridge *br,
278                    const struct ovsrec_port *port)
279 {
280     struct ovsrec_port **ports;
281     size_t i, n;
282
283     ports = xmalloc(sizeof *br->ports * br->n_ports);
284     for (i = n = 0; i < br->n_ports; i++) {
285         if (br->ports[i] != port) {
286             ports[n++] = br->ports[i];
287         }
288     }
289     ovsrec_bridge_set_ports(br, ports, n);
290     free(ports);
291 }
292
293 static struct sbrec_encap *
294 preferred_encap(const struct sbrec_chassis *chassis_rec)
295 {
296     size_t i;
297
298     /* For hypervisors, we only support Geneve and STT encapsulations.
299      * Sets are returned alphabetically, so "geneve" will be preferred
300      * over "stt". */
301     for (i = 0; i < chassis_rec->n_encaps; i++) {
302         if (!strcmp(chassis_rec->encaps[i]->type, "geneve")
303                 || !strcmp(chassis_rec->encaps[i]->type, "stt")) {
304             return chassis_rec->encaps[i];
305         }
306     }
307
308     return NULL;
309 }
310
311 static void
312 update_encaps(struct controller_ctx *ctx)
313 {
314     const struct sbrec_chassis *chassis_rec;
315     const struct ovsrec_bridge *br;
316     int retval;
317
318     struct tunnel_ctx tc = {
319         .tunnel_hmap = HMAP_INITIALIZER(&tc.tunnel_hmap),
320         .port_names = SSET_INITIALIZER(&tc.port_names),
321         .br_int = ctx->br_int
322     };
323
324     tc.ovs_txn = ovsdb_idl_txn_create(ctx->ovs_idl);
325     ovsdb_idl_txn_add_comment(tc.ovs_txn,
326                               "ovn-controller: modifying OVS tunnels '%s'",
327                               ctx->chassis_id);
328
329     /* Collect all port names into tc.port_names.
330      *
331      * Collect all the OVN-created tunnels into tc.tunnel_hmap. */
332     OVSREC_BRIDGE_FOR_EACH(br, ctx->ovs_idl) {
333         size_t i;
334
335         for (i = 0; i < br->n_ports; i++) {
336             const struct ovsrec_port *port = br->ports[i];
337
338             sset_add(&tc.port_names, port->name);
339
340             if (smap_get(&port->external_ids, "ovn-chassis-id")) {
341                 struct port_hash_node *hash_node = xzalloc(sizeof *hash_node);
342                 hash_node->bridge = br;
343                 hash_node->port = port;
344                 hmap_insert(&tc.tunnel_hmap, &hash_node->node,
345                             port_hash_rec(port));
346             }
347         }
348     }
349
350     SBREC_CHASSIS_FOR_EACH(chassis_rec, ctx->ovnsb_idl) {
351         if (strcmp(chassis_rec->name, ctx->chassis_id)) {
352             /* Create tunnels to the other chassis. */
353             const struct sbrec_encap *encap = preferred_encap(chassis_rec);
354             if (!encap) {
355                 VLOG_INFO("No supported encaps for '%s'", chassis_rec->name);
356                 continue;
357             }
358             tunnel_add(&tc, chassis_rec->name, encap);
359         }
360     }
361
362     /* Delete any existing OVN tunnels that were not still around. */
363     struct port_hash_node *hash_node, *next_hash_node;
364     HMAP_FOR_EACH_SAFE (hash_node, next_hash_node, node, &tc.tunnel_hmap) {
365         hmap_remove(&tc.tunnel_hmap, &hash_node->node);
366         bridge_delete_port(hash_node->bridge, hash_node->port);
367         free(hash_node);
368     }
369     hmap_destroy(&tc.tunnel_hmap);
370     sset_destroy(&tc.port_names);
371
372     retval = ovsdb_idl_txn_commit_block(tc.ovs_txn);
373     if (retval != TXN_SUCCESS && retval != TXN_UNCHANGED) {
374         VLOG_INFO("Problem modifying OVS tunnels: %s",
375                   ovsdb_idl_txn_status_to_string(retval));
376         poll_immediate_wake();
377     }
378     ovsdb_idl_txn_destroy(tc.ovs_txn);
379 }
380
381 void
382 chassis_run(struct controller_ctx *ctx)
383 {
384     register_chassis(ctx);
385     update_encaps(ctx);
386 }
387
388 void
389 chassis_destroy(struct controller_ctx *ctx)
390 {
391     int retval = TXN_TRY_AGAIN;
392
393     ovs_assert(ctx->ovnsb_idl);
394
395     while (retval != TXN_SUCCESS && retval != TXN_UNCHANGED) {
396         const struct sbrec_chassis *chassis_rec;
397         struct ovsdb_idl_txn *txn;
398
399         SBREC_CHASSIS_FOR_EACH(chassis_rec, ctx->ovnsb_idl) {
400             if (!strcmp(chassis_rec->name, ctx->chassis_id)) {
401                 break;
402             }
403         }
404
405         if (!chassis_rec) {
406             break;
407         }
408
409         txn = ovsdb_idl_txn_create(ctx->ovnsb_idl);
410         ovsdb_idl_txn_add_comment(txn,
411                                   "ovn-controller: unregistering chassis '%s'",
412                                   ctx->chassis_id);
413         sbrec_chassis_delete(chassis_rec);
414
415         retval = ovsdb_idl_txn_commit_block(txn);
416         if (retval == TXN_ERROR) {
417             VLOG_INFO("Problem unregistering chassis: %s",
418                       ovsdb_idl_txn_status_to_string(retval));
419         }
420         ovsdb_idl_txn_destroy(txn);
421     }
422
423     retval = TXN_TRY_AGAIN;
424     while (retval != TXN_SUCCESS && retval != TXN_UNCHANGED) {
425         struct ovsrec_port **ports;
426         struct ovsdb_idl_txn *txn;
427         size_t i, n;
428
429         txn = ovsdb_idl_txn_create(ctx->ovs_idl);
430         ovsdb_idl_txn_add_comment(txn,
431                                   "ovn-controller: destroying tunnels");
432
433         /* Delete all the OVS-created tunnels from the integration
434          * bridge. */
435         ports = xmalloc(sizeof *ctx->br_int->ports * ctx->br_int->n_ports);
436         for (i = n = 0; i < ctx->br_int->n_ports; i++) {
437             if (!smap_get(&ctx->br_int->ports[i]->external_ids,
438                           "ovn-chassis-id")) {
439                 ports[n++] = ctx->br_int->ports[i];
440             }
441         }
442         ovsrec_bridge_set_ports(ctx->br_int, ports, n);
443         free(ports);
444
445         retval = ovsdb_idl_txn_commit_block(txn);
446         if (retval == TXN_ERROR) {
447             VLOG_INFO("Problem destroying tunnels: %s",
448                       ovsdb_idl_txn_status_to_string(retval));
449         }
450         ovsdb_idl_txn_destroy(txn);
451     }
452 }