1 /* Copyright (c) 2015 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.
23 #include "lib/vswitch-idl.h"
24 #include "openvswitch/vlog.h"
25 #include "ovn/lib/ovn-sb-idl.h"
26 #include "ovn-controller.h"
28 VLOG_DEFINE_THIS_MODULE(encaps);
31 encaps_register_ovs_idl(struct ovsdb_idl *ovs_idl)
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);
45 /* Enough context to create a new tunnel, using tunnel_add(). */
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;
54 /* Only valid within the process_full_encaps case in encaps_run(). */
55 struct hmap tunnel_hmap_by_uuid;
57 /* Names of all ports in the bridge, to allow checking uniqueness when
58 * adding a new tunnel. */
59 struct sset port_names;
61 struct ovsdb_idl_txn *ovs_txn;
62 const struct ovsrec_bridge *br_int;
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),
71 static bool process_full_encaps = false;
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;
82 port_hash(const char *chassis_id, const char *type, const char *ip)
84 size_t hash = hash_string(chassis_id, 0);
85 hash = hash_string(type, hash);
86 return hash_string(ip, hash);
90 port_hash_rec(const struct ovsrec_port *port)
92 const char *chassis_id, *ip;
93 const struct ovsrec_interface *iface;
95 chassis_id = smap_get(&port->external_ids, "ovn-chassis-id");
97 if (!chassis_id || !port->n_interfaces) {
98 /* This should not happen for an OVN-created port. */
102 iface = port->interfaces[0];
103 ip = smap_get(&iface->options, "remote_ip");
105 return port_hash(chassis_id, iface->type, ip);
109 tunnel_create_name(const char *chassis_id)
113 for (i = 0; i < UINT16_MAX; i++) {
115 port_name = xasprintf("ovn-%.6s-%x", chassis_id, i);
117 if (!sset_contains(&tc.port_names, port_name)) {
127 static struct port_hash_node *
128 port_lookup_by_uuid(struct hmap *hmap_p, const struct uuid *uuid)
130 struct port_hash_node *answer;
131 HMAP_FOR_EACH_WITH_HASH (answer, uuid_node, uuid_hash(uuid),
133 if (uuid_equals(uuid, answer->uuid)) {
140 static struct port_hash_node *
141 port_lookup_by_port(const struct ovsrec_port *port)
143 struct port_hash_node *answer;
144 HMAP_FOR_EACH_WITH_HASH (answer, node, port_hash_rec(port),
146 if (port == answer->port) {
154 tunnel_add(const struct sbrec_chassis *chassis_rec,
155 const struct sbrec_encap *encap)
157 struct port_hash_node *hash_node;
158 const char *new_chassis_id = chassis_rec->name;
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. */
164 HMAP_FOR_EACH_WITH_HASH (hash_node, node,
165 port_hash(new_chassis_id,
166 encap->type, encap->ip),
168 const struct ovsrec_port *port = hash_node->port;
169 const char *chassis_id = smap_get(&port->external_ids,
171 const struct ovsrec_interface *iface;
174 if (!chassis_id || !port->n_interfaces) {
178 iface = port->interfaces[0];
179 ip = smap_get(&iface->options, "remote_ip");
184 if (!strcmp(new_chassis_id, chassis_id)
185 && !strcmp(encap->type, iface->type)
186 && !strcmp(encap->ip, ip)) {
188 hash_node->uuid = &chassis_rec->header_.uuid;
189 if (!port_lookup_by_uuid(&tc.tunnel_hmap_by_uuid,
191 hmap_insert(&tc.tunnel_hmap_by_uuid, &hash_node->uuid_node,
192 uuid_hash(hash_node->uuid));
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;
205 port_name = tunnel_create_name(new_chassis_id);
207 VLOG_WARN("Unable to allocate unique name for '%s' tunnel",
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);
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);
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];
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);
234 sset_add(&tc.port_names, port_name);
237 process_full_encaps = true;
241 bridge_delete_port(const struct ovsrec_bridge *br,
242 const struct ovsrec_port *port)
244 struct ovsrec_port **ports;
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];
253 ovsrec_bridge_verify_ports(br);
254 ovsrec_bridge_set_ports(br, ports, n);
258 static struct sbrec_encap *
259 preferred_encap(const struct sbrec_chassis *chassis_rec)
261 struct sbrec_encap *best_encap = NULL;
262 uint32_t best_type = 0;
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];
276 check_and_add_tunnel(const struct sbrec_chassis *chassis_rec,
277 const char *chassis_id)
279 if (strcmp(chassis_rec->name, chassis_id)) {
280 const struct sbrec_encap *encap = preferred_encap(chassis_rec);
282 VLOG_INFO("No supported encaps for '%s'", chassis_rec->name);
285 tunnel_add(chassis_rec, encap);
292 check_and_update_tunnel(const struct sbrec_chassis *chassis_rec)
294 struct port_hash_node *port_node;
295 port_node = port_lookup_by_uuid(&tc.tunnel_hmap_by_uuid,
296 &chassis_rec->header_.uuid);
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);
303 VLOG_WARN("Unable to allocate unique name for '%s' tunnel",
307 if (strcmp(encap->type, iface->type)) {
308 ovsrec_interface_set_type(iface, encap->type);
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);
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",
322 ovsrec_port_set_external_ids(port, &id);
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'",
333 encaps_run(struct controller_ctx *ctx, const struct ovsrec_bridge *br_int,
334 const char *chassis_id)
336 if (!ctx->ovs_idl_txn || !br_int) {
340 const struct sbrec_chassis *chassis_rec;
341 const struct ovsrec_bridge *br;
344 tc.ovs_txn = ctx->ovs_idl_txn;
345 ovsdb_idl_txn_add_comment(tc.ovs_txn,
346 "ovn-controller: modifying OVS tunnels '%s'",
349 /* Collect all port names into tc.port_names.
351 * Collect all the OVN-created tunnels into tc.tunnel_hmap. */
352 OVSREC_BRIDGE_FOR_EACH(br, ctx->ovs_idl) {
355 for (i = 0; i < br->n_ports; i++) {
356 const struct ovsrec_port *port = br->ports[i];
358 sset_add(&tc.port_names, port->name);
360 const char *chassis_id = smap_get(&port->external_ids,
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;
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));
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);
401 hmap_destroy(&keep_tunnel_hmap_by_uuid);
402 process_full_encaps = false;
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;
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);
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);
427 check_and_update_tunnel(chassis_rec);
430 check_and_add_tunnel(chassis_rec, chassis_id);
437 /* Returns true if the database is all cleaned up, false if more work is
440 encaps_cleanup(struct controller_ctx *ctx, const struct ovsrec_bridge *br_int)
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);
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];
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);