ovn-controller: Rename "ovn-patch-port" to "ovn-localnet-port".
[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 char *src, const char *dst)
29 {
30     return xasprintf("patch-%s-to-%s", src, dst);
31 }
32
33 /* Return true if 'port' is a patch port with the specified 'peer'. */
34 static bool
35 match_patch_port(const struct ovsrec_port *port, const char *peer)
36 {
37     for (size_t i = 0; i < port->n_interfaces; i++) {
38         struct ovsrec_interface *iface = port->interfaces[i];
39         if (strcmp(iface->type, "patch")) {
40             continue;
41         }
42         const char *iface_peer = smap_get(&iface->options, "peer");
43         if (peer && !strcmp(iface_peer, peer)) {
44             return true;
45         }
46     }
47     return false;
48 }
49
50 /* Creates a patch port in bridge 'src' named 'src_name', whose peer is
51  * 'dst_name' in bridge 'dst'.  Initializes the patch port's
52  * external-ids:ovn-localnet-port to 'network'.
53  *
54  * If such a patch port already exists, removes it from 'existing_ports'. */
55 static void
56 create_patch_port(struct controller_ctx *ctx,
57                   const char *network,
58                   const struct ovsrec_bridge *src, const char *src_name,
59                   const struct ovsrec_bridge *dst, const char *dst_name,
60                   struct shash *existing_ports)
61 {
62     for (size_t i = 0; i < src->n_ports; i++) {
63         if (match_patch_port(src->ports[i], dst_name)) {
64             /* Patch port already exists on 'src'. */
65             shash_find_and_delete(existing_ports, src->ports[i]->name);
66             return;
67         }
68     }
69
70     ovsdb_idl_txn_add_comment(ctx->ovs_idl_txn,
71             "ovn-controller: creating patch port '%s' from '%s' to '%s'",
72             src_name, src->name, dst->name);
73
74     struct ovsrec_interface *iface;
75     iface = ovsrec_interface_insert(ctx->ovs_idl_txn);
76     ovsrec_interface_set_name(iface, src_name);
77     ovsrec_interface_set_type(iface, "patch");
78     const struct smap options = SMAP_CONST1(&options, "peer", dst_name);
79     ovsrec_interface_set_options(iface, &options);
80
81     struct ovsrec_port *port;
82     port = ovsrec_port_insert(ctx->ovs_idl_txn);
83     ovsrec_port_set_name(port, src_name);
84     ovsrec_port_set_interfaces(port, &iface, 1);
85     const struct smap ids = SMAP_CONST1(&ids, "ovn-localnet-port", network);
86     ovsrec_port_set_external_ids(port, &ids);
87
88     struct ovsrec_port **ports;
89     ports = xmalloc(sizeof *ports * (src->n_ports + 1));
90     memcpy(ports, src->ports, sizeof *ports * src->n_ports);
91     ports[src->n_ports] = port;
92     ovsrec_bridge_verify_ports(src);
93     ovsrec_bridge_set_ports(src, ports, src->n_ports + 1);
94
95     free(ports);
96 }
97
98 /* Creates a pair of patch ports that connect bridges 'b1' and 'b2', using a
99  * port named 'name1' and 'name2' in each respective bridge.
100  * external-ids:ovn-localnet-port in each port is initialized to 'network'.
101  *
102  * If one or both of the ports already exists, leaves it there and removes it
103  * from 'existing_ports'. */
104 static void
105 create_patch_ports(struct controller_ctx *ctx,
106                    const char *network,
107                    const struct ovsrec_bridge *b1,
108                    const struct ovsrec_bridge *b2,
109                    struct shash *existing_ports)
110 {
111     char *name1 = patch_port_name(b1->name, b2->name);
112     char *name2 = patch_port_name(b2->name, b1->name);
113     create_patch_port(ctx, network, b1, name1, b2, name2, existing_ports);
114     create_patch_port(ctx, network, b2, name2, b1, name1, existing_ports);
115     free(name2);
116     free(name1);
117 }
118
119 static void
120 remove_port(struct controller_ctx *ctx,
121             const struct ovsrec_port *port)
122 {
123     const struct ovsrec_bridge *bridge;
124
125     /* We know the port we want to delete, but we have to find the bridge its
126      * on to do so.  Note this only runs on a config change that should be
127      * pretty rare. */
128     OVSREC_BRIDGE_FOR_EACH (bridge, ctx->ovs_idl) {
129         size_t i;
130         for (i = 0; i < bridge->n_ports; i++) {
131             if (bridge->ports[i] != port) {
132                 continue;
133             }
134             struct ovsrec_port **new_ports;
135             new_ports = xmemdup(bridge->ports,
136                     sizeof *new_ports * (bridge->n_ports - 1));
137             if (i != bridge->n_ports - 1) {
138                 /* Removed port was not last */
139                 new_ports[i] = bridge->ports[bridge->n_ports - 1];
140             }
141             ovsrec_bridge_verify_ports(bridge);
142             ovsrec_bridge_set_ports(bridge, new_ports, bridge->n_ports - 1);
143             free(new_ports);
144             ovsrec_port_delete(port);
145             return;
146         }
147     }
148 }
149
150 /* Obtains external-ids:ovn-bridge-mappings from OVSDB and adds patch ports for
151  * the local bridge mappings.  Removes any patch ports for bridge mappings that
152  * already existed from 'existing_ports'. */
153 static void
154 add_bridge_mappings(struct controller_ctx *ctx,
155                     const struct ovsrec_bridge *br_int,
156                     struct shash *existing_ports)
157 {
158     /* Get ovn-bridge-mappings. */
159     const char *mappings_cfg = "";
160     const struct ovsrec_open_vswitch *cfg;
161     cfg = ovsrec_open_vswitch_first(ctx->ovs_idl);
162     if (cfg) {
163         mappings_cfg = smap_get(&cfg->external_ids, "ovn-bridge-mappings");
164         if (!mappings_cfg) {
165             mappings_cfg = "";
166         }
167     }
168
169     /* Create patch ports. */
170     char *cur, *next, *start;
171     next = start = xstrdup(mappings_cfg);
172     while ((cur = strsep(&next, ",")) && *cur) {
173         char *network, *bridge = cur;
174         const struct ovsrec_bridge *ovs_bridge;
175
176         network = strsep(&bridge, ":");
177         if (!bridge || !*network || !*bridge) {
178             VLOG_ERR("Invalid ovn-bridge-mappings configuration: '%s'",
179                     mappings_cfg);
180             break;
181         }
182
183         ovs_bridge = get_bridge(ctx->ovs_idl, bridge);
184         if (!ovs_bridge) {
185             VLOG_WARN("Bridge '%s' not found for network '%s'",
186                     bridge, network);
187             continue;
188         }
189
190         create_patch_ports(ctx, network, br_int, ovs_bridge, existing_ports);
191     }
192     free(start);
193 }
194
195 void
196 patch_run(struct controller_ctx *ctx, const struct ovsrec_bridge *br_int)
197 {
198     if (!ctx->ovs_idl_txn) {
199         return;
200     }
201
202     /* Figure out what patch ports already exist. */
203     struct shash existing_ports = SHASH_INITIALIZER(&existing_ports);
204     const struct ovsrec_port *port;
205     OVSREC_PORT_FOR_EACH (port, ctx->ovs_idl) {
206         if (smap_get(&port->external_ids, "ovn-localnet-port")) {
207             shash_add(&existing_ports, port->name, port);
208         }
209     }
210
211     /* Create in the database any patch ports that should exist.  Remove from
212      * 'existing_ports' any patch ports that do exist in the database and
213      * should be there. */
214     add_bridge_mappings(ctx, br_int, &existing_ports);
215
216     /* Now 'existing_ports' only still contains patch ports that exist in the
217      * database but shouldn't.  Delete them from the database. */
218     struct shash_node *port_node, *port_next_node;
219     SHASH_FOR_EACH_SAFE (port_node, port_next_node, &existing_ports) {
220         struct ovsrec_port *port = port_node->data;
221         shash_delete(&existing_ports, port_node);
222         remove_port(ctx, port);
223     }
224     shash_destroy(&existing_ports);
225 }