1 /* Copyright (c) 2015, 2016 Nicira, Inc.
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:
7 * http://www.apache.org/licenses/LICENSE-2.0
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.
25 #include "lib/vswitch-idl.h"
26 #include "openvswitch/vlog.h"
27 #include "ovn/lib/ovn-sb-idl.h"
28 #include "ovn-controller.h"
30 VLOG_DEFINE_THIS_MODULE(encaps);
33 encaps_register_ovs_idl(struct ovsdb_idl *ovs_idl)
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);
47 /* Enough context to create a new tunnel, using tunnel_add(). */
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;
56 /* Only valid within the process_full_encaps case in encaps_run(). */
57 struct hmap tunnel_hmap_by_uuid;
59 /* Names of all ports in the bridge, to allow checking uniqueness when
60 * adding a new tunnel. */
61 struct sset port_names;
63 struct ovsdb_idl_txn *ovs_txn;
64 const struct ovsrec_bridge *br_int;
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),
73 static bool process_full_encaps = false;
75 struct port_hash_node {
76 struct hmap_node node;
77 struct hmap_node uuid_node;
79 const struct ovsrec_port *port;
80 const struct ovsrec_bridge *bridge;
84 port_hash(const char *chassis_id, const char *type, const char *ip)
86 size_t hash = hash_string(chassis_id, 0);
87 hash = hash_string(type, hash);
88 return hash_string(ip, hash);
92 port_hash_rec(const struct ovsrec_port *port)
94 const char *chassis_id, *ip;
95 const struct ovsrec_interface *iface;
97 chassis_id = smap_get(&port->external_ids, "ovn-chassis-id");
99 if (!chassis_id || !port->n_interfaces) {
100 /* This should not happen for an OVN-created port. */
104 iface = port->interfaces[0];
105 ip = smap_get(&iface->options, "remote_ip");
107 return port_hash(chassis_id, iface->type, ip);
111 tunnel_create_name(const char *chassis_id)
115 for (i = 0; i < UINT16_MAX; i++) {
117 port_name = xasprintf("ovn-%.6s-%x", chassis_id, i);
119 if (!sset_contains(&tc.port_names, port_name)) {
129 static struct port_hash_node *
130 port_lookup_by_uuid(struct hmap *hmap_p, const struct uuid *uuid)
132 struct port_hash_node *answer;
133 HMAP_FOR_EACH_WITH_HASH (answer, uuid_node, uuid_hash(uuid),
135 if (uuid_equals(uuid, &answer->uuid)) {
142 static struct port_hash_node *
143 port_lookup_by_port(const struct ovsrec_port *port)
145 struct port_hash_node *answer;
146 HMAP_FOR_EACH_WITH_HASH (answer, node, port_hash_rec(port),
148 if (port == answer->port) {
156 tunnel_add(const struct sbrec_chassis *chassis_rec,
157 const struct sbrec_encap *encap)
159 struct port_hash_node *hash_node;
160 const char *new_chassis_id = chassis_rec->name;
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. */
166 HMAP_FOR_EACH_WITH_HASH (hash_node, node,
167 port_hash(new_chassis_id,
168 encap->type, encap->ip),
170 const struct ovsrec_port *port = hash_node->port;
171 const char *chassis_id = smap_get(&port->external_ids,
173 const struct ovsrec_interface *iface;
176 if (!chassis_id || !port->n_interfaces) {
180 iface = port->interfaces[0];
181 ip = smap_get(&iface->options, "remote_ip");
186 if (!strcmp(new_chassis_id, chassis_id)
187 && !strcmp(encap->type, iface->type)
188 && !strcmp(encap->ip, ip)) {
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,
194 hmap_insert(&tc.tunnel_hmap_by_uuid, &hash_node->uuid_node,
195 uuid_hash(&hash_node->uuid));
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;
208 port_name = tunnel_create_name(new_chassis_id);
210 VLOG_WARN("Unable to allocate unique name for '%s' tunnel",
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);
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);
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];
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);
237 sset_add(&tc.port_names, port_name);
240 binding_reset_processing();
242 mcgroup_index_reset();
243 lflow_reset_processing();
244 process_full_encaps = true;
248 bridge_delete_port(const struct ovsrec_bridge *br,
249 const struct ovsrec_port *port)
251 struct ovsrec_port **ports;
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];
260 ovsrec_bridge_verify_ports(br);
261 ovsrec_bridge_set_ports(br, ports, n);
265 static struct sbrec_encap *
266 preferred_encap(const struct sbrec_chassis *chassis_rec)
268 struct sbrec_encap *best_encap = NULL;
269 uint32_t best_type = 0;
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];
283 check_and_add_tunnel(const struct sbrec_chassis *chassis_rec,
284 const char *chassis_id)
286 if (strcmp(chassis_rec->name, chassis_id)) {
287 const struct sbrec_encap *encap = preferred_encap(chassis_rec);
289 VLOG_INFO("No supported encaps for '%s'", chassis_rec->name);
292 tunnel_add(chassis_rec, encap);
299 check_and_update_tunnel(const struct sbrec_chassis *chassis_rec)
301 struct port_hash_node *port_node;
302 port_node = port_lookup_by_uuid(&tc.tunnel_hmap_by_uuid,
303 &chassis_rec->header_.uuid);
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);
310 VLOG_WARN("Unable to allocate unique name for '%s' tunnel",
314 if (strcmp(encap->type, iface->type)) {
315 ovsrec_interface_set_type(iface, encap->type);
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);
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",
329 ovsrec_port_set_external_ids(port, &id);
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'",
340 encaps_run(struct controller_ctx *ctx, const struct ovsrec_bridge *br_int,
341 const char *chassis_id)
343 if (!ctx->ovs_idl_txn || !br_int) {
347 const struct sbrec_chassis *chassis_rec;
348 const struct ovsrec_bridge *br;
351 tc.ovs_txn = ctx->ovs_idl_txn;
352 ovsdb_idl_txn_add_comment(tc.ovs_txn,
353 "ovn-controller: modifying OVS tunnels '%s'",
356 /* Collect all port names into tc.port_names.
358 * Collect all the OVN-created tunnels into tc.tunnel_hmap. */
359 OVSREC_BRIDGE_FOR_EACH(br, ctx->ovs_idl) {
362 for (i = 0; i < br->n_ports; i++) {
363 const struct ovsrec_port *port = br->ports[i];
365 sset_add(&tc.port_names, port->name);
367 const char *chassis_id = smap_get(&port->external_ids,
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;
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));
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);
410 hmap_destroy(&keep_tunnel_hmap_by_uuid);
411 process_full_encaps = false;
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);
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);
427 binding_reset_processing();
428 lflow_reset_processing();
430 } else if (sbrec_chassis_is_new(chassis_rec)) {
431 check_and_add_tunnel(chassis_rec, chassis_id);
433 check_and_update_tunnel(chassis_rec);
439 /* Returns true if the database is all cleaned up, false if more work is
442 encaps_cleanup(struct controller_ctx *ctx, const struct ovsrec_bridge *br_int)
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);
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];
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);