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.
21 #include "lib/vswitch-idl.h"
22 #include "openvswitch/vlog.h"
23 #include "ovn-controller.h"
25 VLOG_DEFINE_THIS_MODULE(patch);
28 patch_port_name(const struct ovsrec_bridge *b1, const struct ovsrec_bridge *b2)
30 return xasprintf("patch-%s-to-%s", b1->name, b2->name);
34 * Return true if the port is a patch port from b1 to b2
37 match_patch_port(const struct ovsrec_port *port,
38 const struct ovsrec_bridge *b1,
39 const struct ovsrec_bridge *b2)
41 struct ovsrec_interface *iface;
46 peer_port_name = patch_port_name(b2, b1);
48 for (i = 0; i < port->n_interfaces; i++) {
49 iface = port->interfaces[i];
50 if (strcmp(iface->type, "patch")) {
54 peer = smap_get(&iface->options, "peer");
55 if (peer && !strcmp(peer, peer_port_name)) {
67 create_patch_port(struct controller_ctx *ctx,
69 const struct ovsrec_bridge *b1,
70 const struct ovsrec_bridge *b2)
72 char *port_name = patch_port_name(b1, b2);
73 char *peer_port_name = patch_port_name(b2, b1);
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);
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);
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);
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);
102 free(peer_port_name);
106 create_patch_ports(struct controller_ctx *ctx,
108 struct shash *existing_ports,
109 const struct ovsrec_bridge *b1,
110 const struct ovsrec_bridge *b2)
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);
121 if (i == b1->n_ports) {
122 create_patch_port(ctx, network, b1, b2);
127 init_existing_ports(struct controller_ctx *ctx,
128 struct shash *existing_ports)
130 const struct ovsrec_port *port;
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);
140 remove_port(struct controller_ctx *ctx,
141 const struct ovsrec_port *port)
143 const struct ovsrec_bridge *bridge;
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
148 OVSREC_BRIDGE_FOR_EACH (bridge, ctx->ovs_idl) {
150 for (i = 0; i < bridge->n_ports; i++) {
151 if (bridge->ports[i] != port) {
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];
161 ovsrec_bridge_verify_ports(bridge);
162 ovsrec_bridge_set_ports(bridge, new_ports, bridge->n_ports - 1);
164 ovsrec_port_delete(port);
171 parse_bridge_mappings(struct controller_ctx *ctx,
172 const struct ovsrec_bridge *br_int,
173 const char *mappings_cfg)
175 struct shash existing_ports = SHASH_INITIALIZER(&existing_ports);
176 init_existing_ports(ctx, &existing_ports);
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;
184 network = strsep(&bridge, ":");
185 if (!bridge || !*network || !*bridge) {
186 VLOG_ERR("Invalid ovn-bridge-mappings configuration: '%s'",
191 ovs_bridge = get_bridge(ctx->ovs_idl, bridge);
193 VLOG_WARN("Bridge '%s' not found for network '%s'",
198 create_patch_ports(ctx, network, &existing_ports, br_int, ovs_bridge);
199 create_patch_ports(ctx, network, &existing_ports, ovs_bridge, br_int);
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);
211 shash_destroy(&existing_ports);
215 patch_run(struct controller_ctx *ctx, const struct ovsrec_bridge *br_int)
217 if (!ctx->ovs_idl_txn) {
221 const char *mappings_cfg = "";
222 const struct ovsrec_open_vswitch *cfg;
224 cfg = ovsrec_open_vswitch_first(ctx->ovs_idl);
226 mappings_cfg = smap_get(&cfg->external_ids, "ovn-bridge-mappings");
231 parse_bridge_mappings(ctx, br_int, mappings_cfg);