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.
22 #include "lib/vswitch-idl.h"
23 #include "openvswitch/vlog.h"
24 #include "ovn/lib/ovn-sb-idl.h"
25 #include "ovn-controller.h"
27 VLOG_DEFINE_THIS_MODULE(encaps);
30 encaps_register_ovs_idl(struct ovsdb_idl *ovs_idl)
32 ovsdb_idl_add_table(ovs_idl, &ovsrec_table_bridge);
33 ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_ports);
34 ovsdb_idl_add_table(ovs_idl, &ovsrec_table_port);
35 ovsdb_idl_add_column(ovs_idl, &ovsrec_port_col_name);
36 ovsdb_idl_add_column(ovs_idl, &ovsrec_port_col_interfaces);
37 ovsdb_idl_add_column(ovs_idl, &ovsrec_port_col_external_ids);
38 ovsdb_idl_add_table(ovs_idl, &ovsrec_table_interface);
39 ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_name);
40 ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_type);
41 ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_options);
44 /* Enough context to create a new tunnel, using tunnel_add(). */
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;
53 /* Names of all ports in the bridge, to allow checking uniqueness when
54 * adding a new tunnel. */
55 struct sset port_names;
57 struct ovsdb_idl_txn *ovs_txn;
58 const struct ovsrec_bridge *br_int;
61 struct port_hash_node {
62 struct hmap_node node;
63 const struct ovsrec_port *port;
64 const struct ovsrec_bridge *bridge;
68 port_hash(const char *chassis_id, const char *type, const char *ip)
70 size_t hash = hash_string(chassis_id, 0);
71 hash = hash_string(type, hash);
72 return hash_string(ip, hash);
76 port_hash_rec(const struct ovsrec_port *port)
78 const char *chassis_id, *ip;
79 const struct ovsrec_interface *iface;
81 chassis_id = smap_get(&port->external_ids, "ovn-chassis-id");
83 if (!chassis_id || !port->n_interfaces) {
84 /* This should not happen for an OVN-created port. */
88 iface = port->interfaces[0];
89 ip = smap_get(&iface->options, "remote_ip");
91 return port_hash(chassis_id, iface->type, ip);
95 tunnel_create_name(struct tunnel_ctx *tc, const char *chassis_id)
99 for (i = 0; i < UINT16_MAX; i++) {
101 port_name = xasprintf("ovn-%.6s-%x", chassis_id, i);
103 if (!sset_contains(&tc->port_names, port_name)) {
115 tunnel_add(struct tunnel_ctx *tc, const char *new_chassis_id,
116 const struct sbrec_encap *encap)
118 struct port_hash_node *hash_node;
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),
126 const struct ovsrec_port *port = hash_node->port;
127 const char *chassis_id = smap_get(&port->external_ids,
129 const struct ovsrec_interface *iface;
132 if (!chassis_id || !port->n_interfaces) {
136 iface = port->interfaces[0];
137 ip = smap_get(&iface->options, "remote_ip");
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);
151 /* No such port, so add one. */
152 struct smap options = SMAP_INITIALIZER(&options);
153 struct ovsrec_port *port, **ports;
154 struct ovsrec_interface *iface;
158 port_name = tunnel_create_name(tc, new_chassis_id);
160 VLOG_WARN("Unable to allocate unique name for '%s' tunnel",
165 iface = ovsrec_interface_insert(tc->ovs_txn);
166 ovsrec_interface_set_name(iface, port_name);
167 ovsrec_interface_set_type(iface, encap->type);
168 smap_add(&options, "remote_ip", encap->ip);
169 smap_add(&options, "key", "flow");
170 ovsrec_interface_set_options(iface, &options);
171 smap_destroy(&options);
173 port = ovsrec_port_insert(tc->ovs_txn);
174 ovsrec_port_set_name(port, port_name);
175 ovsrec_port_set_interfaces(port, &iface, 1);
176 const struct smap id = SMAP_CONST1(&id, "ovn-chassis-id", new_chassis_id);
177 ovsrec_port_set_external_ids(port, &id);
179 ports = xmalloc(sizeof *tc->br_int->ports * (tc->br_int->n_ports + 1));
180 for (i = 0; i < tc->br_int->n_ports; i++) {
181 ports[i] = tc->br_int->ports[i];
183 ports[tc->br_int->n_ports] = port;
184 ovsrec_bridge_verify_ports(tc->br_int);
185 ovsrec_bridge_set_ports(tc->br_int, ports, tc->br_int->n_ports + 1);
187 sset_add(&tc->port_names, port_name);
193 bridge_delete_port(const struct ovsrec_bridge *br,
194 const struct ovsrec_port *port)
196 struct ovsrec_port **ports;
199 ports = xmalloc(sizeof *br->ports * br->n_ports);
200 for (i = n = 0; i < br->n_ports; i++) {
201 if (br->ports[i] != port) {
202 ports[n++] = br->ports[i];
205 ovsrec_bridge_verify_ports(br);
206 ovsrec_bridge_set_ports(br, ports, n);
210 static struct sbrec_encap *
211 preferred_encap(const struct sbrec_chassis *chassis_rec)
215 /* For hypervisors, we only support Geneve and STT encapsulations.
216 * Sets are returned alphabetically, so "geneve" will be preferred
217 * over "stt". For gateways, we only support VXLAN encapsulation. */
218 for (i = 0; i < chassis_rec->n_encaps; i++) {
219 if (!strcmp(chassis_rec->encaps[i]->type, "geneve")
220 || !strcmp(chassis_rec->encaps[i]->type, "stt")
221 || !strcmp(chassis_rec->encaps[i]->type, "vxlan")) {
222 return chassis_rec->encaps[i];
230 encaps_run(struct controller_ctx *ctx, const struct ovsrec_bridge *br_int,
231 const char *chassis_id)
233 if (!ctx->ovs_idl_txn || !br_int) {
237 const struct sbrec_chassis *chassis_rec;
238 const struct ovsrec_bridge *br;
240 struct tunnel_ctx tc = {
241 .tunnel_hmap = HMAP_INITIALIZER(&tc.tunnel_hmap),
242 .port_names = SSET_INITIALIZER(&tc.port_names),
246 tc.ovs_txn = ctx->ovs_idl_txn;
247 ovsdb_idl_txn_add_comment(tc.ovs_txn,
248 "ovn-controller: modifying OVS tunnels '%s'",
251 /* Collect all port names into tc.port_names.
253 * Collect all the OVN-created tunnels into tc.tunnel_hmap. */
254 OVSREC_BRIDGE_FOR_EACH(br, ctx->ovs_idl) {
257 for (i = 0; i < br->n_ports; i++) {
258 const struct ovsrec_port *port = br->ports[i];
260 sset_add(&tc.port_names, port->name);
262 if (smap_get(&port->external_ids, "ovn-chassis-id")) {
263 struct port_hash_node *hash_node = xzalloc(sizeof *hash_node);
264 hash_node->bridge = br;
265 hash_node->port = port;
266 hmap_insert(&tc.tunnel_hmap, &hash_node->node,
267 port_hash_rec(port));
272 SBREC_CHASSIS_FOR_EACH(chassis_rec, ctx->ovnsb_idl) {
273 if (strcmp(chassis_rec->name, chassis_id)) {
274 /* Create tunnels to the other chassis. */
275 const struct sbrec_encap *encap = preferred_encap(chassis_rec);
277 VLOG_INFO("No supported encaps for '%s'", chassis_rec->name);
280 tunnel_add(&tc, chassis_rec->name, encap);
284 /* Delete any existing OVN tunnels that were not still around. */
285 struct port_hash_node *hash_node, *next_hash_node;
286 HMAP_FOR_EACH_SAFE (hash_node, next_hash_node, node, &tc.tunnel_hmap) {
287 hmap_remove(&tc.tunnel_hmap, &hash_node->node);
288 bridge_delete_port(hash_node->bridge, hash_node->port);
291 hmap_destroy(&tc.tunnel_hmap);
292 sset_destroy(&tc.port_names);
295 /* Returns true if the database is all cleaned up, false if more work is
298 encaps_cleanup(struct controller_ctx *ctx, const struct ovsrec_bridge *br_int)
304 /* Delete all the OVS-created tunnels from the integration bridge. */
305 struct ovsrec_port **ports
306 = xmalloc(sizeof *br_int->ports * br_int->n_ports);
308 for (size_t i = 0; i < br_int->n_ports; i++) {
309 if (!smap_get(&br_int->ports[i]->external_ids, "ovn-chassis-id")) {
310 ports[n++] = br_int->ports[i];
314 bool any_changes = n != br_int->n_ports;
315 if (any_changes && ctx->ovs_idl_txn) {
316 ovsdb_idl_txn_add_comment(ctx->ovs_idl_txn,
317 "ovn-controller: destroying tunnels");
318 ovsrec_bridge_verify_ports(br_int);
319 ovsrec_bridge_set_ports(br_int, ports, n);