Branch data Line data Source code
1 : : /* Copyright (c) 2015, 2016 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 : 94 : VLOG_DEFINE_THIS_MODULE(encaps);
28 : :
29 : : void
30 : 44 : encaps_register_ovs_idl(struct ovsdb_idl *ovs_idl)
31 : : {
32 : 44 : ovsdb_idl_add_table(ovs_idl, &ovsrec_table_bridge);
33 : 44 : ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_ports);
34 : 44 : ovsdb_idl_add_table(ovs_idl, &ovsrec_table_port);
35 : 44 : ovsdb_idl_add_column(ovs_idl, &ovsrec_port_col_name);
36 : 44 : ovsdb_idl_add_column(ovs_idl, &ovsrec_port_col_interfaces);
37 : 44 : ovsdb_idl_add_column(ovs_idl, &ovsrec_port_col_external_ids);
38 : 44 : ovsdb_idl_add_table(ovs_idl, &ovsrec_table_interface);
39 : 44 : ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_name);
40 : 44 : ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_type);
41 : 44 : ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_options);
42 : 44 : }
43 : :
44 : : /* Enough context to create a new tunnel, using tunnel_add(). */
45 : : struct tunnel_ctx {
46 : : /* Maps from a chassis name to "struct chassis_node *". */
47 : : struct shash chassis;
48 : :
49 : : /* Names of all ports in the bridge, to allow checking uniqueness when
50 : : * adding a new tunnel. */
51 : : struct sset port_names;
52 : :
53 : : struct ovsdb_idl_txn *ovs_txn;
54 : : const struct ovsrec_bridge *br_int;
55 : : };
56 : :
57 : : struct chassis_node {
58 : : const struct ovsrec_port *port;
59 : : const struct ovsrec_bridge *bridge;
60 : : };
61 : :
62 : : static char *
63 : 34 : tunnel_create_name(struct tunnel_ctx *tc, const char *chassis_id)
64 : : {
65 : : int i;
66 : :
67 [ + - ]: 34 : for (i = 0; i < UINT16_MAX; i++) {
68 : : char *port_name;
69 : 34 : port_name = xasprintf("ovn-%.6s-%x", chassis_id, i);
70 : :
71 [ + - ]: 34 : if (!sset_contains(&tc->port_names, port_name)) {
72 : 34 : return port_name;
73 : : }
74 : :
75 : 0 : free(port_name);
76 : : }
77 : :
78 : 0 : return NULL;
79 : : }
80 : :
81 : : static void
82 : 3330 : tunnel_add(struct tunnel_ctx *tc, const char *new_chassis_id,
83 : : const struct sbrec_encap *encap)
84 : : {
85 : 3330 : struct smap options = SMAP_INITIALIZER(&options);
86 : 3330 : smap_add(&options, "remote_ip", encap->ip);
87 : 3330 : smap_add(&options, "key", "flow");
88 : 3330 : const char *csum = smap_get(&encap->options, "csum");
89 [ + + ][ + + ]: 3330 : if (csum && (!strcmp(csum, "true") || !strcmp(csum, "false"))) {
[ + - ]
90 : 3304 : smap_add(&options, "csum", csum);
91 : : }
92 : :
93 : : /* If there's an existing chassis record that does not need any change,
94 : : * keep it. Otherwise, create a new record (if there was an existing
95 : : * record, the new record will supplant it and encaps_run() will delete
96 : : * it). */
97 : 3330 : struct chassis_node *chassis = shash_find_data(&tc->chassis,
98 : : new_chassis_id);
99 [ + + ]: 3330 : if (chassis
100 [ + - ]: 3296 : && chassis->port->n_interfaces == 1
101 [ + + ]: 3296 : && !strcmp(chassis->port->interfaces[0]->type, encap->type)
102 [ + + ]: 3294 : && smap_equal(&chassis->port->interfaces[0]->options, &options)) {
103 : 3293 : shash_find_and_delete(&tc->chassis, new_chassis_id);
104 : 3293 : free(chassis);
105 : 3293 : goto exit;
106 : : }
107 : :
108 : : /* Choose a name for the new port. If we're replacing an old port, reuse
109 : : * its name, otherwise generate a new, unique name. */
110 : 37 : char *port_name = (chassis
111 : 3 : ? xstrdup(chassis->port->name)
112 [ + + ]: 37 : : tunnel_create_name(tc, new_chassis_id));
113 [ - + ]: 37 : if (!port_name) {
114 [ # # ]: 0 : VLOG_WARN("Unable to allocate unique name for '%s' tunnel",
115 : : new_chassis_id);
116 : 0 : goto exit;
117 : : }
118 : :
119 : 37 : struct ovsrec_interface *iface = ovsrec_interface_insert(tc->ovs_txn);
120 : 37 : ovsrec_interface_set_name(iface, port_name);
121 : 37 : ovsrec_interface_set_type(iface, encap->type);
122 : 37 : ovsrec_interface_set_options(iface, &options);
123 : :
124 : 37 : struct ovsrec_port *port = ovsrec_port_insert(tc->ovs_txn);
125 : 37 : ovsrec_port_set_name(port, port_name);
126 : 37 : ovsrec_port_set_interfaces(port, &iface, 1);
127 : 37 : const struct smap id = SMAP_CONST1(&id, "ovn-chassis-id", new_chassis_id);
128 : 37 : ovsrec_port_set_external_ids(port, &id);
129 : :
130 : 37 : ovsrec_bridge_update_ports_addvalue(tc->br_int, port);
131 : :
132 : 37 : sset_add_and_free(&tc->port_names, port_name);
133 : :
134 : : exit:
135 : 3330 : smap_destroy(&options);
136 : 3330 : }
137 : :
138 : : static struct sbrec_encap *
139 : 3330 : preferred_encap(const struct sbrec_chassis *chassis_rec)
140 : : {
141 : 3330 : struct sbrec_encap *best_encap = NULL;
142 : 3330 : uint32_t best_type = 0;
143 : :
144 [ + + ]: 9962 : for (int i = 0; i < chassis_rec->n_encaps; i++) {
145 : 6632 : uint32_t tun_type = get_tunnel_type(chassis_rec->encaps[i]->type);
146 [ + + ]: 6632 : if (tun_type > best_type) {
147 : 4707 : best_type = tun_type;
148 : 4707 : best_encap = chassis_rec->encaps[i];
149 : : }
150 : : }
151 : :
152 : 3330 : return best_encap;
153 : : }
154 : :
155 : : void
156 : 3167 : encaps_run(struct controller_ctx *ctx, const struct ovsrec_bridge *br_int,
157 : : const char *chassis_id)
158 : : {
159 [ + + ][ - + ]: 3167 : if (!ctx->ovs_idl_txn || !br_int) {
160 : 174 : return;
161 : : }
162 : :
163 : : const struct sbrec_chassis *chassis_rec;
164 : : const struct ovsrec_bridge *br;
165 : :
166 : 2993 : struct tunnel_ctx tc = {
167 : : .chassis = SHASH_INITIALIZER(&tc.chassis),
168 : : .port_names = SSET_INITIALIZER(&tc.port_names),
169 : : .br_int = br_int
170 : : };
171 : :
172 : 2993 : tc.ovs_txn = ctx->ovs_idl_txn;
173 : 2993 : ovsdb_idl_txn_add_comment(tc.ovs_txn,
174 : : "ovn-controller: modifying OVS tunnels '%s'",
175 : : chassis_id);
176 : :
177 : : /* Collect all port names into tc.port_names.
178 : : *
179 : : * Collect all the OVN-created tunnels into tc.tunnel_hmap. */
180 [ + + ]: 9416 : OVSREC_BRIDGE_FOR_EACH(br, ctx->ovs_idl) {
181 [ + + ]: 56034 : for (size_t i = 0; i < br->n_ports; i++) {
182 : 49611 : const struct ovsrec_port *port = br->ports[i];
183 : 49611 : sset_add(&tc.port_names, port->name);
184 : :
185 : 49611 : const char *id = smap_get(&port->external_ids, "ovn-chassis-id");
186 [ + + ]: 49611 : if (id) {
187 [ + - ]: 3311 : if (!shash_find(&tc.chassis, id)) {
188 : 3311 : struct chassis_node *chassis = xzalloc(sizeof *chassis);
189 : 3311 : chassis->bridge = br;
190 : 3311 : chassis->port = port;
191 : 3311 : shash_add_assert(&tc.chassis, id, chassis);
192 : : } else {
193 : : /* Duplicate port for ovn-chassis-id. Arbitrarily choose
194 : : * to delete this one. */
195 : 0 : ovsrec_bridge_update_ports_delvalue(br, port);
196 : : }
197 : : }
198 : : }
199 : : }
200 : :
201 [ + + ]: 9314 : SBREC_CHASSIS_FOR_EACH(chassis_rec, ctx->ovnsb_idl) {
202 [ + + ]: 6321 : if (strcmp(chassis_rec->name, chassis_id)) {
203 : : /* Create tunnels to the other chassis. */
204 : 3330 : const struct sbrec_encap *encap = preferred_encap(chassis_rec);
205 [ - + ]: 3330 : if (!encap) {
206 [ # # ]: 0 : VLOG_INFO("No supported encaps for '%s'", chassis_rec->name);
207 : 0 : continue;
208 : : }
209 : 3330 : tunnel_add(&tc, chassis_rec->name, encap);
210 : : }
211 : : }
212 : :
213 : : /* Delete any existing OVN tunnels that were not still around. */
214 : : struct shash_node *node, *next_node;
215 [ + + ][ - + ]: 3011 : SHASH_FOR_EACH_SAFE (node, next_node, &tc.chassis) {
[ + + ]
216 : 18 : struct chassis_node *chassis = node->data;
217 : 18 : ovsrec_bridge_update_ports_delvalue(chassis->bridge, chassis->port);
218 : 18 : shash_delete(&tc.chassis, node);
219 : 18 : free(chassis);
220 : : }
221 : 2993 : shash_destroy(&tc.chassis);
222 : 2993 : sset_destroy(&tc.port_names);
223 : : }
224 : :
225 : : /* Returns true if the database is all cleaned up, false if more work is
226 : : * required. */
227 : : bool
228 : 138 : encaps_cleanup(struct controller_ctx *ctx, const struct ovsrec_bridge *br_int)
229 : : {
230 [ - + ]: 138 : if (!br_int) {
231 : 0 : return true;
232 : : }
233 : :
234 : : /* Delete all the OVS-created tunnels from the integration bridge. */
235 : 138 : struct ovsrec_port **ports
236 : 138 : = xmalloc(sizeof *br_int->ports * br_int->n_ports);
237 : 138 : size_t n = 0;
238 [ + + ]: 1223 : for (size_t i = 0; i < br_int->n_ports; i++) {
239 [ + + ]: 1085 : if (!smap_get(&br_int->ports[i]->external_ids, "ovn-chassis-id")) {
240 : 1049 : ports[n++] = br_int->ports[i];
241 : : }
242 : : }
243 : :
244 : 138 : bool any_changes = n != br_int->n_ports;
245 [ + + ][ + + ]: 138 : if (any_changes && ctx->ovs_idl_txn) {
246 : 14 : ovsdb_idl_txn_add_comment(ctx->ovs_idl_txn,
247 : : "ovn-controller: destroying tunnels");
248 : 14 : ovsrec_bridge_verify_ports(br_int);
249 : 14 : ovsrec_bridge_set_ports(br_int, ports, n);
250 : : }
251 : 138 : free(ports);
252 : :
253 : 138 : return !any_changes;
254 : : }
|