patch: Bail out earlier if OVS IDL transactions cannot be executed.
[cascardo/ovs.git] / ovn / controller / patch.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
18 #include "patch.h"
19
20 #include "hash.h"
21 #include "lib/vswitch-idl.h"
22 #include "openvswitch/vlog.h"
23 #include "ovn-controller.h"
24
25 VLOG_DEFINE_THIS_MODULE(patch);
26
27 static char *
28 patch_port_name(const struct ovsrec_bridge *b1, const struct ovsrec_bridge *b2)
29 {
30     return xasprintf("patch-%s-to-%s", b1->name, b2->name);
31 }
32
33 /*
34  * Return true if the port is a patch port from b1 to b2
35  */
36 static bool
37 match_patch_port(const struct ovsrec_port *port,
38                  const struct ovsrec_bridge *b1,
39                  const struct ovsrec_bridge *b2)
40 {
41     struct ovsrec_interface *iface;
42     size_t i;
43     char *peer_port_name;
44     bool res = false;
45
46     peer_port_name = patch_port_name(b2, b1);
47
48     for (i = 0; i < port->n_interfaces; i++) {
49         iface = port->interfaces[i];
50         if (strcmp(iface->type, "patch")) {
51             continue;
52         }
53         const char *peer;
54         peer = smap_get(&iface->options, "peer");
55         if (peer && !strcmp(peer, peer_port_name)) {
56             res = true;
57             break;
58         }
59     }
60
61     free(peer_port_name);
62
63     return res;
64 }
65
66 static void
67 create_patch_port(struct controller_ctx *ctx,
68                   const char *network,
69                   const struct ovsrec_bridge *b1,
70                   const struct ovsrec_bridge *b2)
71 {
72     char *port_name = patch_port_name(b1, b2);
73     char *peer_port_name = patch_port_name(b2, b1);
74
75     ovsdb_idl_txn_add_comment(ctx->ovs_idl_txn,
76             "ovn-controller: creating patch port '%s' from '%s' to '%s'",
77             port_name, b1->name, b2->name);
78
79     struct ovsrec_interface *iface;
80     iface = ovsrec_interface_insert(ctx->ovs_idl_txn);
81     ovsrec_interface_set_name(iface, port_name);
82     ovsrec_interface_set_type(iface, "patch");
83     const struct smap options = SMAP_CONST1(&options, "peer", peer_port_name);
84     ovsrec_interface_set_options(iface, &options);
85
86     struct ovsrec_port *port;
87     port = ovsrec_port_insert(ctx->ovs_idl_txn);
88     ovsrec_port_set_name(port, port_name);
89     ovsrec_port_set_interfaces(port, &iface, 1);
90     const struct smap ids = SMAP_CONST1(&ids, "ovn-patch-port", network);
91     ovsrec_port_set_external_ids(port, &ids);
92
93     struct ovsrec_port **ports;
94     ports = xmalloc(sizeof *ports * (b1->n_ports + 1));
95     memcpy(ports, b1->ports, sizeof *ports * b1->n_ports);
96     ports[b1->n_ports] = port;
97     ovsrec_bridge_verify_ports(b1);
98     ovsrec_bridge_set_ports(b1, ports, b1->n_ports + 1);
99
100     free(ports);
101     free(port_name);
102     free(peer_port_name);
103 }
104
105 static void
106 create_patch_ports(struct controller_ctx *ctx,
107                    const char *network,
108                    struct shash *existing_ports,
109                    const struct ovsrec_bridge *b1,
110                    const struct ovsrec_bridge *b2)
111 {
112     size_t i;
113
114     for (i = 0; i < b1->n_ports; i++) {
115         if (match_patch_port(b1->ports[i], b1, b2)) {
116             /* Patch port already exists on b1 */
117             shash_find_and_delete(existing_ports, b1->ports[i]->name);
118             break;
119         }
120     }
121     if (i == b1->n_ports) {
122         create_patch_port(ctx, network, b1, b2);
123     }
124 }
125
126 static void
127 init_existing_ports(struct controller_ctx *ctx,
128                     struct shash *existing_ports)
129 {
130     const struct ovsrec_port *port;
131
132     OVSREC_PORT_FOR_EACH (port, ctx->ovs_idl) {
133         if (smap_get(&port->external_ids, "ovn-patch-port")) {
134             shash_add(existing_ports, port->name, port);
135         }
136     }
137 }
138
139 static void
140 remove_port(struct controller_ctx *ctx,
141             const struct ovsrec_port *port)
142 {
143     const struct ovsrec_bridge *bridge;
144
145     /* We know the port we want to delete, but we have to find the bridge its
146      * on to do so.  Note this only runs on a config change that should be
147      * pretty rare. */
148     OVSREC_BRIDGE_FOR_EACH (bridge, ctx->ovs_idl) {
149         size_t i;
150         for (i = 0; i < bridge->n_ports; i++) {
151             if (bridge->ports[i] != port) {
152                 continue;
153             }
154             struct ovsrec_port **new_ports;
155             new_ports = xmemdup(bridge->ports,
156                     sizeof *new_ports * (bridge->n_ports - 1));
157             if (i != bridge->n_ports - 1) {
158                 /* Removed port was not last */
159                 new_ports[i] = bridge->ports[bridge->n_ports - 1];
160             }
161             ovsrec_bridge_verify_ports(bridge);
162             ovsrec_bridge_set_ports(bridge, new_ports, bridge->n_ports - 1);
163             free(new_ports);
164             ovsrec_port_delete(port);
165             return;
166         }
167     }
168 }
169
170 static void
171 parse_bridge_mappings(struct controller_ctx *ctx,
172                       const struct ovsrec_bridge *br_int,
173                       const char *mappings_cfg)
174 {
175     struct shash existing_ports = SHASH_INITIALIZER(&existing_ports);
176     init_existing_ports(ctx, &existing_ports);
177
178     char *cur, *next, *start;
179     next = start = xstrdup(mappings_cfg);
180     while ((cur = strsep(&next, ",")) && *cur) {
181         char *network, *bridge = cur;
182         const struct ovsrec_bridge *ovs_bridge;
183
184         network = strsep(&bridge, ":");
185         if (!bridge || !*network || !*bridge) {
186             VLOG_ERR("Invalid ovn-bridge-mappings configuration: '%s'",
187                     mappings_cfg);
188             break;
189         }
190
191         ovs_bridge = get_bridge(ctx->ovs_idl, bridge);
192         if (!ovs_bridge) {
193             VLOG_WARN("Bridge '%s' not found for network '%s'",
194                     bridge, network);
195             continue;
196         }
197
198         create_patch_ports(ctx, network, &existing_ports, br_int, ovs_bridge);
199         create_patch_ports(ctx, network, &existing_ports, ovs_bridge, br_int);
200     }
201     free(start);
202
203     /* Any ports left in existing_ports are related to configuration that has
204      * been removed, so we should delete the ports now. */
205     struct shash_node *port_node, *port_next_node;
206     SHASH_FOR_EACH_SAFE (port_node, port_next_node, &existing_ports) {
207         struct ovsrec_port *port = port_node->data;
208         shash_delete(&existing_ports, port_node);
209         remove_port(ctx, port);
210     }
211     shash_destroy(&existing_ports);
212 }
213
214 void
215 patch_run(struct controller_ctx *ctx, const struct ovsrec_bridge *br_int)
216 {
217     if (!ctx->ovs_idl_txn) {
218         return;
219     }
220
221     const char *mappings_cfg = "";
222     const struct ovsrec_open_vswitch *cfg;
223
224     cfg = ovsrec_open_vswitch_first(ctx->ovs_idl);
225     if (cfg) {
226         mappings_cfg = smap_get(&cfg->external_ids, "ovn-bridge-mappings");
227         if (!mappings_cfg) {
228             mappings_cfg = "";
229         }
230     }
231     parse_bridge_mappings(ctx, br_int, mappings_cfg);
232 }