]> git.proxmox.com Git - ovs.git/blame - ovn/controller/binding.c
ovn: l3ha, make is_chassis_active aware of gateway_chassis
[ovs.git] / ovn / controller / binding.c
CommitLineData
6094fc7f 1/* Copyright (c) 2015, 2016, 2017 Nicira, Inc.
717c7fc5
JP
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>
e387e3e8 17#include "binding.h"
1da17a0b 18#include "gchassis.h"
70c7cfef
RM
19#include "lflow.h"
20#include "lport.h"
717c7fc5 21
78aab811 22#include "lib/bitmap.h"
fdb3c70c 23#include "lib/poll-loop.h"
717c7fc5
JP
24#include "lib/sset.h"
25#include "lib/util.h"
a6095f81 26#include "lib/netdev.h"
717c7fc5 27#include "lib/vswitch-idl.h"
ee89ea7b 28#include "openvswitch/hmap.h"
717c7fc5 29#include "openvswitch/vlog.h"
1da17a0b 30#include "ovn/lib/chassis-index.h"
e3df8838 31#include "ovn/lib/ovn-sb-idl.h"
717c7fc5
JP
32#include "ovn-controller.h"
33
e387e3e8 34VLOG_DEFINE_THIS_MODULE(binding);
717c7fc5 35
a6095f81
BS
36#define OVN_QOS_TYPE "linux-htb"
37
38struct qos_queue {
39 struct hmap_node node;
40 uint32_t queue_id;
41 uint32_t max_rate;
42 uint32_t burst;
43};
44
717c7fc5 45void
4a5a9e06 46binding_register_ovs_idl(struct ovsdb_idl *ovs_idl)
717c7fc5 47{
4a5a9e06
BP
48 ovsdb_idl_add_table(ovs_idl, &ovsrec_table_open_vswitch);
49 ovsdb_idl_add_column(ovs_idl, &ovsrec_open_vswitch_col_bridges);
717c7fc5 50
4a5a9e06
BP
51 ovsdb_idl_add_table(ovs_idl, &ovsrec_table_bridge);
52 ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_name);
53 ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_ports);
717c7fc5 54
4a5a9e06
BP
55 ovsdb_idl_add_table(ovs_idl, &ovsrec_table_port);
56 ovsdb_idl_add_column(ovs_idl, &ovsrec_port_col_name);
57 ovsdb_idl_add_column(ovs_idl, &ovsrec_port_col_interfaces);
a6095f81 58 ovsdb_idl_add_column(ovs_idl, &ovsrec_port_col_qos);
717c7fc5 59
4a5a9e06
BP
60 ovsdb_idl_add_table(ovs_idl, &ovsrec_table_interface);
61 ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_name);
62 ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_external_ids);
3475695e
VA
63 ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_bfd);
64 ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_bfd_status);
a6095f81
BS
65 ovsdb_idl_add_column(ovs_idl, &ovsrec_interface_col_status);
66
67 ovsdb_idl_add_table(ovs_idl, &ovsrec_table_qos);
68 ovsdb_idl_add_column(ovs_idl, &ovsrec_qos_col_type);
717c7fc5
JP
69}
70
926c34fd 71static void
6f783c06 72get_local_iface_ids(const struct ovsrec_bridge *br_int,
c5f346a5 73 struct shash *lport_to_iface,
3a60b7cd 74 struct sset *local_lports,
a6095f81 75 struct sset *egress_ifaces)
717c7fc5 76{
717c7fc5
JP
77 int i;
78
422a9f73
BP
79 for (i = 0; i < br_int->n_ports; i++) {
80 const struct ovsrec_port *port_rec = br_int->ports[i];
717c7fc5
JP
81 const char *iface_id;
82 int j;
83
422a9f73 84 if (!strcmp(port_rec->name, br_int->name)) {
717c7fc5
JP
85 continue;
86 }
87
88 for (j = 0; j < port_rec->n_interfaces; j++) {
89 const struct ovsrec_interface *iface_rec;
90
91 iface_rec = port_rec->interfaces[j];
92 iface_id = smap_get(&iface_rec->external_ids, "iface-id");
c74578c1 93 int64_t ofport = iface_rec->n_ofport ? *iface_rec->ofport : 0;
a6095f81 94
c74578c1 95 if (iface_id && ofport > 0) {
a6095f81 96 shash_add(lport_to_iface, iface_id, iface_rec);
3a60b7cd 97 sset_add(local_lports, iface_id);
a6095f81
BS
98 }
99
100 /* Check if this is a tunnel interface. */
101 if (smap_get(&iface_rec->options, "remote_ip")) {
102 const char *tunnel_iface
103 = smap_get(&iface_rec->status, "tunnel_egress_iface");
104 if (tunnel_iface) {
105 sset_add(egress_ifaces, tunnel_iface);
106 }
717c7fc5 107 }
263064ae
RM
108 }
109 }
263064ae
RM
110}
111
bda5a056 112static void
1ea9b847
BP
113add_local_datapath__(const struct ldatapath_index *ldatapaths,
114 const struct lport_index *lports,
115 const struct sbrec_datapath_binding *datapath,
116 bool has_local_l3gateway, int depth,
117 struct hmap *local_datapaths)
bda5a056 118{
1ea9b847
BP
119 uint32_t dp_key = datapath->tunnel_key;
120
121 struct local_datapath *ld = get_local_datapath(local_datapaths, dp_key);
122 if (ld) {
123 if (has_local_l3gateway) {
124 ld->has_local_l3gateway = true;
125 }
6e6c3f91 126 return;
bda5a056 127 }
6e6c3f91 128
1ea9b847
BP
129 ld = xzalloc(sizeof *ld);
130 hmap_insert(local_datapaths, &ld->hmap_node, dp_key);
131 ld->datapath = datapath;
132 ld->ldatapath = ldatapath_lookup_by_key(ldatapaths, dp_key);
133 ovs_assert(ld->ldatapath);
134 ld->localnet_port = NULL;
135 ld->has_local_l3gateway = has_local_l3gateway;
136
137 if (depth >= 100) {
138 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
139 VLOG_WARN_RL(&rl, "datapaths nested too deep");
140 return;
141 }
142
143 /* Recursively add logical datapaths to which this one patches. */
144 for (size_t i = 0; i < ld->ldatapath->n_lports; i++) {
145 const struct sbrec_port_binding *pb = ld->ldatapath->lports[i];
146 if (!strcmp(pb->type, "patch")) {
147 const char *peer_name = smap_get(&pb->options, "peer");
148 if (peer_name) {
149 const struct sbrec_port_binding *peer = lport_lookup_by_name(
150 lports, peer_name);
151 if (peer && peer->datapath) {
152 add_local_datapath__(ldatapaths, lports, peer->datapath,
153 false, depth + 1, local_datapaths);
3475695e
VA
154 ld->n_peer_dps++;
155 ld->peer_dps = xrealloc(
156 ld->peer_dps,
157 ld->n_peer_dps * sizeof *ld->peer_dps);
158 ld->peer_dps[ld->n_peer_dps - 1] = ldatapath_lookup_by_key(
159 ldatapaths, peer->datapath->tunnel_key);
1ea9b847
BP
160 }
161 }
162 }
163 }
164}
165
166static void
167add_local_datapath(const struct ldatapath_index *ldatapaths,
168 const struct lport_index *lports,
169 const struct sbrec_datapath_binding *datapath,
170 bool has_local_l3gateway, struct hmap *local_datapaths)
171{
172 add_local_datapath__(ldatapaths, lports, datapath, has_local_l3gateway, 0,
173 local_datapaths);
bda5a056
RB
174}
175
aef5f431 176static void
a6095f81 177get_qos_params(const struct sbrec_port_binding *pb, struct hmap *queue_map)
aef5f431 178{
a6095f81
BS
179 uint32_t max_rate = smap_get_int(&pb->options, "qos_max_rate", 0);
180 uint32_t burst = smap_get_int(&pb->options, "qos_burst", 0);
181 uint32_t queue_id = smap_get_int(&pb->options, "qdisc_queue_id", 0);
aef5f431 182
a6095f81
BS
183 if ((!max_rate && !burst) || !queue_id) {
184 /* Qos is not configured for this port. */
185 return;
186 }
187
188 struct qos_queue *node = xzalloc(sizeof *node);
189 hmap_insert(queue_map, &node->node, hash_int(queue_id, 0));
190 node->max_rate = max_rate;
191 node->burst = burst;
192 node->queue_id = queue_id;
193}
194
195static const struct ovsrec_qos *
196get_noop_qos(struct controller_ctx *ctx)
197{
198 const struct ovsrec_qos *qos;
199 OVSREC_QOS_FOR_EACH (qos, ctx->ovs_idl) {
200 if (!strcmp(qos->type, "linux-noop")) {
201 return qos;
202 }
203 }
204
205 if (!ctx->ovs_idl_txn) {
206 return NULL;
207 }
208 qos = ovsrec_qos_insert(ctx->ovs_idl_txn);
209 ovsrec_qos_set_type(qos, "linux-noop");
210 return qos;
211}
212
213static bool
214set_noop_qos(struct controller_ctx *ctx, struct sset *egress_ifaces)
215{
216 if (!ctx->ovs_idl_txn) {
217 return false;
218 }
219
220 const struct ovsrec_qos *noop_qos = get_noop_qos(ctx);
221 if (!noop_qos) {
222 return false;
223 }
224
225 const struct ovsrec_port *port;
226 size_t count = 0;
227
228 OVSREC_PORT_FOR_EACH (port, ctx->ovs_idl) {
229 if (sset_contains(egress_ifaces, port->name)) {
230 ovsrec_port_set_qos(port, noop_qos);
231 count++;
232 }
233 if (sset_count(egress_ifaces) == count) {
234 break;
235 }
236 }
237 return true;
238}
239
dc2dab6e
BP
240static void
241set_qos_type(struct netdev *netdev, const char *type)
242{
243 int error = netdev_set_qos(netdev, type, NULL);
244 if (error) {
245 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 1);
246 VLOG_WARN_RL(&rl, "%s: could not set qdisc type \"%s\" (%s)",
247 netdev_get_name(netdev), type, ovs_strerror(error));
248 }
249}
250
a6095f81
BS
251static void
252setup_qos(const char *egress_iface, struct hmap *queue_map)
253{
254 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5);
255 struct netdev *netdev_phy;
256
257 if (!egress_iface) {
258 /* Queues cannot be configured. */
259 return;
260 }
261
262 int error = netdev_open(egress_iface, NULL, &netdev_phy);
263 if (error) {
264 VLOG_WARN_RL(&rl, "%s: could not open netdev (%s)",
265 egress_iface, ovs_strerror(error));
266 return;
267 }
268
dc2dab6e 269 /* Check current qdisc. */
a6095f81
BS
270 const char *qdisc_type;
271 struct smap qdisc_details;
272
273 smap_init(&qdisc_details);
274 if (netdev_get_qos(netdev_phy, &qdisc_type, &qdisc_details) != 0 ||
275 qdisc_type[0] == '\0') {
6094fc7f 276 smap_destroy(&qdisc_details);
7f42ed63 277 netdev_close(netdev_phy);
a6095f81
BS
278 /* Qos is not supported. */
279 return;
280 }
6094fc7f
BP
281 smap_destroy(&qdisc_details);
282
dc2dab6e
BP
283 /* If we're not actually being requested to do any QoS:
284 *
285 * - If the current qdisc type is OVN_QOS_TYPE, then we clear the qdisc
286 * type to "". Otherwise, it's possible that our own leftover qdisc
287 * settings could cause strange behavior on egress. Also, QoS is
288 * expensive and may waste CPU time even if it's not really in use.
289 *
290 * OVN isn't the only software that can configure qdiscs, and
291 * physical interfaces are shared resources, so there is some risk in
292 * this strategy: we could disrupt some other program's QoS.
293 * Probably, to entirely avoid this possibility we would need to add
294 * a configuration setting.
295 *
296 * - Otherwise leave the qdisc alone. */
297 if (hmap_is_empty(queue_map)) {
298 if (!strcmp(qdisc_type, OVN_QOS_TYPE)) {
299 set_qos_type(netdev_phy, "");
a6095f81 300 }
7f42ed63 301 netdev_close(netdev_phy);
dc2dab6e
BP
302 return;
303 }
304
305 /* Configure qdisc. */
306 if (strcmp(qdisc_type, OVN_QOS_TYPE)) {
307 set_qos_type(netdev_phy, OVN_QOS_TYPE);
a6095f81
BS
308 }
309
310 /* Check and delete if needed. */
311 struct netdev_queue_dump dump;
312 unsigned int queue_id;
313 struct smap queue_details;
314 struct qos_queue *sb_info;
315 struct hmap consistent_queues;
316
317 smap_init(&queue_details);
318 hmap_init(&consistent_queues);
319 NETDEV_QUEUE_FOR_EACH (&queue_id, &queue_details, &dump, netdev_phy) {
320 bool is_queue_needed = false;
321
322 HMAP_FOR_EACH_WITH_HASH (sb_info, node, hash_int(queue_id, 0),
323 queue_map) {
324 is_queue_needed = true;
325 if (sb_info->max_rate ==
326 smap_get_int(&queue_details, "max-rate", 0)
327 && sb_info->burst == smap_get_int(&queue_details, "burst", 0)) {
328 /* This queue is consistent. */
329 hmap_insert(&consistent_queues, &sb_info->node,
330 hash_int(queue_id, 0));
331 break;
332 }
333 }
334
335 if (!is_queue_needed) {
336 error = netdev_delete_queue(netdev_phy, queue_id);
337 if (error) {
338 VLOG_WARN_RL(&rl, "%s: could not delete queue %u (%s)",
339 egress_iface, queue_id, ovs_strerror(error));
340 }
341 }
342 }
343
344 /* Create/Update queues. */
345 HMAP_FOR_EACH (sb_info, node, queue_map) {
346 if (hmap_contains(&consistent_queues, &sb_info->node)) {
347 hmap_remove(&consistent_queues, &sb_info->node);
348 continue;
349 }
350
351 smap_clear(&queue_details);
352 smap_add_format(&queue_details, "max-rate", "%d", sb_info->max_rate);
353 smap_add_format(&queue_details, "burst", "%d", sb_info->burst);
354 error = netdev_set_queue(netdev_phy, sb_info->queue_id,
355 &queue_details);
356 if (error) {
357 VLOG_WARN_RL(&rl, "%s: could not configure queue %u (%s)",
358 egress_iface, sb_info->queue_id, ovs_strerror(error));
359 }
360 }
361 smap_destroy(&queue_details);
362 hmap_destroy(&consistent_queues);
363 netdev_close(netdev_phy);
aef5f431
BP
364}
365
263064ae 366static void
6f783c06 367consider_local_datapath(struct controller_ctx *ctx,
1ea9b847
BP
368 const struct ldatapath_index *ldatapaths,
369 const struct lport_index *lports,
3475695e
VA
370 const struct chassis_index *chassis_index,
371 struct sset *active_tunnels,
263064ae
RM
372 const struct sbrec_chassis *chassis_rec,
373 const struct sbrec_port_binding *binding_rec,
a6095f81 374 struct hmap *qos_map,
6f783c06 375 struct hmap *local_datapaths,
c5f346a5 376 struct shash *lport_to_iface,
3a60b7cd 377 struct sset *local_lports)
263064ae
RM
378{
379 const struct ovsrec_interface *iface_rec
6f783c06 380 = shash_find_data(lport_to_iface, binding_rec->logical_port);
3475695e 381 struct ovs_list *gateway_chassis = NULL;
6f783c06 382
f1a8bd06 383 bool our_chassis = false;
263064ae
RM
384 if (iface_rec
385 || (binding_rec->parent_port && binding_rec->parent_port[0] &&
3a60b7cd 386 sset_contains(local_lports, binding_rec->parent_port))) {
926c34fd
RM
387 if (binding_rec->parent_port && binding_rec->parent_port[0]) {
388 /* Add child logical port to the set of all local ports. */
3a60b7cd 389 sset_add(local_lports, binding_rec->logical_port);
926c34fd 390 }
1ea9b847
BP
391 add_local_datapath(ldatapaths, lports, binding_rec->datapath,
392 false, local_datapaths);
a6095f81
BS
393 if (iface_rec && qos_map && ctx->ovs_idl_txn) {
394 get_qos_params(binding_rec, qos_map);
263064ae 395 }
2a38ef45
DA
396 /* This port is in our chassis unless it is a localport. */
397 if (strcmp(binding_rec->type, "localport")) {
398 our_chassis = true;
399 }
f1a8bd06
BP
400 } else if (!strcmp(binding_rec->type, "l2gateway")) {
401 const char *chassis_id = smap_get(&binding_rec->options,
402 "l2gateway-chassis");
403 our_chassis = chassis_id && !strcmp(chassis_id, chassis_rec->name);
404 if (our_chassis) {
3a60b7cd 405 sset_add(local_lports, binding_rec->logical_port);
f1a8bd06
BP
406 add_local_datapath(ldatapaths, lports, binding_rec->datapath,
407 false, local_datapaths);
408 }
41a15b71 409 } else if (!strcmp(binding_rec->type, "chassisredirect")) {
3475695e
VA
410 gateway_chassis = gateway_chassis_get_ordered(binding_rec,
411 chassis_index);
412 if (gateway_chassis &&
413 gateway_chassis_contains(gateway_chassis, chassis_rec)) {
508b7f96 414
415 our_chassis = gateway_chassis_is_active(
416 gateway_chassis, chassis_rec, active_tunnels);
417
41a15b71
MS
418 add_local_datapath(ldatapaths, lports, binding_rec->datapath,
419 false, local_datapaths);
420 }
3475695e 421 gateway_chassis_destroy(gateway_chassis);
f1a8bd06
BP
422 } else if (!strcmp(binding_rec->type, "l3gateway")) {
423 const char *chassis_id = smap_get(&binding_rec->options,
424 "l3gateway-chassis");
425 our_chassis = chassis_id && !strcmp(chassis_id, chassis_rec->name);
426 if (our_chassis) {
427 add_local_datapath(ldatapaths, lports, binding_rec->datapath,
428 true, local_datapaths);
263064ae 429 }
f1a8bd06 430 } else if (!strcmp(binding_rec->type, "localnet")) {
3a60b7cd 431 /* Add all localnet ports to local_lports so that we allocate ct zones
f1a8bd06 432 * for them. */
3a60b7cd 433 sset_add(local_lports, binding_rec->logical_port);
f1a8bd06
BP
434 our_chassis = false;
435 }
436
437 if (ctx->ovnsb_idl_txn) {
438 if (our_chassis) {
f90bb090
MS
439 if (binding_rec->chassis != chassis_rec) {
440 if (binding_rec->chassis) {
441 VLOG_INFO("Changing chassis for lport %s from %s to %s.",
442 binding_rec->logical_port,
443 binding_rec->chassis->name,
444 chassis_rec->name);
445 } else {
446 VLOG_INFO("Claiming lport %s for this chassis.",
447 binding_rec->logical_port);
448 }
449 for (int i = 0; i < binding_rec->n_mac; i++) {
450 VLOG_INFO("%s: Claiming %s",
451 binding_rec->logical_port, binding_rec->mac[i]);
452 }
453 sbrec_port_binding_set_chassis(binding_rec, chassis_rec);
62b87eab 454 }
f1a8bd06 455 } else if (binding_rec->chassis == chassis_rec) {
263064ae
RM
456 VLOG_INFO("Releasing lport %s from this chassis.",
457 binding_rec->logical_port);
458 sbrec_port_binding_set_chassis(binding_rec, NULL);
459 }
263064ae
RM
460 }
461}
462
41319fc9
HZ
463static void
464consider_localnet_port(const struct sbrec_port_binding *binding_rec,
465 struct hmap *local_datapaths)
466{
467 struct local_datapath *ld
468 = get_local_datapath(local_datapaths,
469 binding_rec->datapath->tunnel_key);
470 if (!ld) {
471 return;
472 }
473
474 if (ld->localnet_port && strcmp(ld->localnet_port->logical_port,
475 binding_rec->logical_port)) {
476 static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 1);
477 VLOG_WARN_RL(&rl, "localnet port '%s' already set for datapath "
478 "'%"PRId64"', skipping the new port '%s'.",
479 ld->localnet_port->logical_port,
480 binding_rec->datapath->tunnel_key,
481 binding_rec->logical_port);
482 return;
483 }
484 ld->localnet_port = binding_rec;
485}
486
717c7fc5 487void
4acc496e 488binding_run(struct controller_ctx *ctx, const struct ovsrec_bridge *br_int,
9211bbea
BP
489 const struct sbrec_chassis *chassis_rec,
490 const struct ldatapath_index *ldatapaths,
3475695e
VA
491 const struct lport_index *lports,
492 const struct chassis_index *chassis_index,
493 struct sset *active_tunnels,
494 struct hmap *local_datapaths, struct sset *local_lports)
717c7fc5 495{
9211bbea
BP
496 if (!chassis_rec) {
497 return;
498 }
499
dcda6e0d 500 const struct sbrec_port_binding *binding_rec;
6f783c06 501 struct shash lport_to_iface = SHASH_INITIALIZER(&lport_to_iface);
a6095f81
BS
502 struct sset egress_ifaces = SSET_INITIALIZER(&egress_ifaces);
503 struct hmap qos_map;
f1fd7657 504
a6095f81 505 hmap_init(&qos_map);
37c7a694 506 if (br_int) {
3a60b7cd 507 get_local_iface_ids(br_int, &lport_to_iface, local_lports,
a6095f81 508 &egress_ifaces);
37c7a694 509 }
aef5f431 510
7eccc741
JP
511 /* Run through each binding record to see if it is resident on this
512 * chassis and update the binding accordingly. This includes both
513 * directly connected logical ports and children of those ports. */
926c34fd 514 SBREC_PORT_BINDING_FOR_EACH(binding_rec, ctx->ovnsb_idl) {
3475695e
VA
515 consider_local_datapath(ctx, ldatapaths, lports, chassis_index,
516 active_tunnels, chassis_rec, binding_rec,
a6095f81
BS
517 sset_is_empty(&egress_ifaces) ? NULL :
518 &qos_map, local_datapaths, &lport_to_iface,
3a60b7cd 519 local_lports);
a6095f81
BS
520
521 }
522
41319fc9
HZ
523 /* Run through each binding record to see if it is a localnet port
524 * on local datapaths discovered from above loop, and update the
525 * corresponding local datapath accordingly. */
526 SBREC_PORT_BINDING_FOR_EACH (binding_rec, ctx->ovnsb_idl) {
527 if (!strcmp(binding_rec->type, "localnet")) {
528 consider_localnet_port(binding_rec, local_datapaths);
529 }
530 }
531
a6095f81
BS
532 if (!sset_is_empty(&egress_ifaces)
533 && set_noop_qos(ctx, &egress_ifaces)) {
534 const char *entry;
535 SSET_FOR_EACH (entry, &egress_ifaces) {
536 setup_qos(entry, &qos_map);
537 }
717c7fc5 538 }
6f783c06
RB
539
540 shash_destroy(&lport_to_iface);
a6095f81
BS
541 sset_destroy(&egress_ifaces);
542 hmap_destroy(&qos_map);
717c7fc5
JP
543}
544
f1fd7657
BP
545/* Returns true if the database is all cleaned up, false if more work is
546 * required. */
547bool
9211bbea
BP
548binding_cleanup(struct controller_ctx *ctx,
549 const struct sbrec_chassis *chassis_rec)
717c7fc5 550{
f1fd7657
BP
551 if (!ctx->ovnsb_idl_txn) {
552 return false;
553 }
71332231 554 if (!chassis_rec) {
f1fd7657 555 return true;
71332231
AW
556 }
557
dcda6e0d
BP
558 ovsdb_idl_txn_add_comment(
559 ctx->ovnsb_idl_txn,
9211bbea
BP
560 "ovn-controller: removing all port bindings for '%s'",
561 chassis_rec->name);
717c7fc5 562
dcda6e0d 563 const struct sbrec_port_binding *binding_rec;
f1fd7657 564 bool any_changes = false;
dcda6e0d 565 SBREC_PORT_BINDING_FOR_EACH(binding_rec, ctx->ovnsb_idl) {
f1fd7657 566 if (binding_rec->chassis == chassis_rec) {
dcda6e0d 567 sbrec_port_binding_set_chassis(binding_rec, NULL);
f1fd7657 568 any_changes = true;
717c7fc5 569 }
717c7fc5 570 }
f1fd7657 571 return !any_changes;
717c7fc5 572}