Hi Xavier,
On 5/12/22 11:04, Xavier Simonart wrote:
> When VIF ports are claimed on a chassis, SBDB Port_Binding table is updated.
> If the SBDB IDL is still is read-only ("in transaction") when such a update
> is required, the update is not possible and recompute is triggered through
> I+P failure.
>
> This situation can happen:
> - after updating Port_Binding->chassis to SBDB for one port, in a following
> iteration, ovn-controller handles Interface:external_ids:ovn-installed
> (for the same port) while SBDB is still read-only.
> - after updating Port_Binding->chassis to SBDB for one port, in a following
> iteration, ovn-controller updates Port_Binding->chassis for another port,
> while SBDB is still read-only.
>
> This patch prevent the recompute, by having the if-status module
> updating the Port_Binding chassis (if needed), when possible.
> This does not delay Port_Binding chassis update compared to before this
> patch: Port_Binding chassis will be updated as soon as SBDB is again
> writable, as it was the case, through a recompute, before this patch.
Just to confirm, with your patch the Port_Binding chassis will be
updated when the SBDB is again writable and will *not* cause a
recompute, right?
>
> Reported-at: https://bugzilla.redhat.com/show_bug.cgi?id=2059253
> Signed-off-by: Xavier Simonart <[email protected]>
> ---
> controller/binding.c | 124 ++++++++++++++++++++++++++----------
> controller/binding.h | 9 ++-
> controller/if-status.c | 31 +++++++--
> controller/if-status.h | 6 +-
> controller/ovn-controller.c | 6 +-
> tests/ovn.at | 55 +++++++++++++++-
> 6 files changed, 184 insertions(+), 47 deletions(-)
>
> diff --git a/controller/binding.c b/controller/binding.c
> index e5ba56b25..d917d8775 100644
> --- a/controller/binding.c
> +++ b/controller/binding.c
> @@ -657,11 +657,15 @@ local_binding_get_lport_ofport(const struct shash
> *local_bindings,
> }
>
> bool
> -local_binding_is_up(struct shash *local_bindings, const char *pb_name)
> +local_binding_is_up(struct shash *local_bindings, const char *pb_name,
> + const struct sbrec_chassis *chassis_rec)
> {
> struct local_binding *lbinding =
> local_binding_find(local_bindings, pb_name);
> struct binding_lport *b_lport =
> local_binding_get_primary_lport(lbinding);
> + if (b_lport && b_lport->pb->chassis != chassis_rec) {
> + return false;
> + }
> if (lbinding && b_lport && lbinding->iface) {
> if (b_lport->pb->n_up && !b_lport->pb->up[0]) {
> return false;
> @@ -673,13 +677,23 @@ local_binding_is_up(struct shash *local_bindings, const
> char *pb_name)
> }
>
> bool
> -local_binding_is_down(struct shash *local_bindings, const char *pb_name)
> +local_binding_is_down(struct shash *local_bindings, const char *pb_name,
> + const struct sbrec_chassis *chassis_rec)
> {
> struct local_binding *lbinding =
> local_binding_find(local_bindings, pb_name);
>
> struct binding_lport *b_lport =
> local_binding_get_primary_lport(lbinding);
>
> + if (b_lport) {
> + if (b_lport->pb->chassis == chassis_rec) {
> + return false;
> + } else if (b_lport->pb->chassis) {
> + VLOG_DBG("lport %s already claimed by other chassis",
> + b_lport->pb->logical_port);
> + }
> + }
> +
> if (!lbinding) {
> return true;
> }
> @@ -894,6 +908,48 @@ get_lport_type_str(enum en_lport_type lport_type)
> OVS_NOT_REACHED();
> }
>
> +static void set_pb_chassis_in_sbrec(const struct sbrec_port_binding *pb,
Nit: this should be:
static void
set_pb_chassis_in_sbrec(const struct sbrec_port_binding *pb,
> + const struct sbrec_chassis *chassis_rec)
> +{
> + if (pb->chassis != chassis_rec) {
> + if (pb->chassis) {
> + VLOG_INFO("Changing chassis for lport %s from %s to %s.",
> + pb->logical_port, pb->chassis->name,
> + chassis_rec->name);
Nit: indentation.
> + } else {
> + VLOG_INFO("Claiming lport %s for this chassis.",
> pb->logical_port);
> + }
> + for (int i = 0; i < pb->n_mac; i++) {
> + VLOG_INFO("%s: Claiming %s", pb->logical_port, pb->mac[i]);
> + }
> + sbrec_port_binding_set_chassis(pb, chassis_rec);
> + }
> +}
> +
> +static void clear_pb_chassis_in_sbrec(const struct sbrec_port_binding *pb)
Here too, the function name should start on a new line.
> +{
> + if (pb->chassis) {
> + sbrec_port_binding_set_chassis(pb, NULL);
> + }
> +}
> +
> +void
> +local_binding_set_pb(struct shash *local_bindings, const char *pb_name,
> + const struct sbrec_chassis *chassis_rec)
Nit: indentation.
> +{
> + struct local_binding *lbinding =
> + local_binding_find(local_bindings, pb_name);
> + struct binding_lport *b_lport =
> local_binding_get_primary_lport(lbinding);
> +
> + if (b_lport) {
> + if (chassis_rec) {
> + set_pb_chassis_in_sbrec(b_lport->pb, chassis_rec);
> + } else {
> + clear_pb_chassis_in_sbrec(b_lport->pb);
> + }
> + }
> +}
> +
> /* For newly claimed ports, if 'notify_up' is 'false':
> * - set the 'pb.up' field to true if 'pb' has no 'parent_pb'.
> * - set the 'pb.up' field to true if 'parent_pb.up' is 'true' (e.g., for
> @@ -906,23 +962,37 @@ get_lport_type_str(enum en_lport_type lport_type)
> * it's explicitly set to 'false'.
> * This is to ensure compatibility with older versions of ovn-northd.
> */
> -static void
> +static bool
> claimed_lport_set_up(const struct sbrec_port_binding *pb,
> const struct sbrec_port_binding *parent_pb,
> const struct sbrec_chassis *chassis_rec,
> - bool notify_up, struct if_status_mgr *if_mgr)
> + bool notify_up, struct if_status_mgr *if_mgr,
> + bool sb_readonly)
> {
> +
Extra line not needed.
> if (!notify_up) {
> bool up = true;
> if (!parent_pb || (parent_pb->n_up && parent_pb->up[0])) {
> - sbrec_port_binding_set_up(pb, &up, 1);
> + if (!sb_readonly) {
> + sbrec_port_binding_set_up(pb, &up, 1);
> + } else if (pb->n_up && !pb->up[0]) {
> + return false;
> + }
> }
> - return;
> + if (sb_readonly && (pb->chassis != chassis_rec)) {
> + /* Handle the cases where if_status_mgr does not claim the
> + * interface.In those cases, if we do not set pb->chassis in sb
> + * now (as readonly), we might not do it later ...
> + */
> + return false;
> + }
> + return true;
> }
>
> if (pb->chassis != chassis_rec || (pb->n_up && !pb->up[0])) {
> if_status_mgr_claim_iface(if_mgr, pb->logical_port);
> }
> + return true;
> }
>
> /* Returns false if lport is not claimed due to 'sb_readonly'.
> @@ -937,27 +1007,15 @@ claim_lport(const struct sbrec_port_binding *pb,
> struct hmap *tracked_datapaths,
> struct if_status_mgr *if_mgr)
> {
> - if (!sb_readonly) {
> - claimed_lport_set_up(pb, parent_pb, chassis_rec, notify_up, if_mgr);
> - }
>
> + if (!claimed_lport_set_up(pb, parent_pb, chassis_rec, notify_up,
> + if_mgr, sb_readonly)) {
> + return false;
> + }
> if (pb->chassis != chassis_rec) {
> - if (sb_readonly) {
> - return false;
> - }
> -
> - if (pb->chassis) {
> - VLOG_INFO("Changing chassis for lport %s from %s to %s.",
> - pb->logical_port, pb->chassis->name,
> - chassis_rec->name);
> - } else {
> - VLOG_INFO("Claiming lport %s for this chassis.",
> pb->logical_port);
> + if (!sb_readonly) {
> + set_pb_chassis_in_sbrec(pb, chassis_rec);
> }
> - for (int i = 0; i < pb->n_mac; i++) {
> - VLOG_INFO("%s: Claiming %s", pb->logical_port, pb->mac[i]);
> - }
> -
> - sbrec_port_binding_set_chassis(pb, chassis_rec);
>
> if (tracked_datapaths) {
> update_lport_tracking(pb, tracked_datapaths, true);
> @@ -974,7 +1032,6 @@ claim_lport(const struct sbrec_port_binding *pb,
> }
> sbrec_port_binding_set_encap(pb, encap_rec);
> }
> -
Unrelated?
> return true;
> }
>
> @@ -986,7 +1043,8 @@ claim_lport(const struct sbrec_port_binding *pb,
> * Caller should make sure that this is the case.
> */
> static bool
> -release_lport_(const struct sbrec_port_binding *pb, bool sb_readonly)
> +release_lport_(const struct sbrec_port_binding *pb, bool sb_readonly,
> + struct if_status_mgr *if_mgr)
> {
> if (pb->encap) {
> if (sb_readonly) {
> @@ -995,11 +1053,13 @@ release_lport_(const struct sbrec_port_binding *pb,
> bool sb_readonly)
> sbrec_port_binding_set_encap(pb, NULL);
> }
>
> + /* if sb readonly, pb->chassis unset through if-status if present */
> if (pb->chassis) {
> - if (sb_readonly) {
> + if (!sb_readonly) {
> + sbrec_port_binding_set_chassis(pb, NULL);
> + } else if (!if_status_mgr_iface_is_present(if_mgr,
> pb->logical_port)) {
> return false;
> }
> - sbrec_port_binding_set_chassis(pb, NULL);
> }
>
> if (pb->virtual_parent) {
> @@ -1009,7 +1069,8 @@ release_lport_(const struct sbrec_port_binding *pb,
> bool sb_readonly)
> sbrec_port_binding_set_virtual_parent(pb, NULL);
> }
>
> - VLOG_INFO("Releasing lport %s from this chassis.", pb->logical_port);
> + VLOG_INFO("Releasing lport %s from this chassis (sb_readonly=%d)",
> + pb->logical_port, sb_readonly);
> return true;
> }
>
> @@ -1017,7 +1078,7 @@ static bool
> release_lport(const struct sbrec_port_binding *pb, bool sb_readonly,
> struct hmap *tracked_datapaths, struct if_status_mgr *if_mgr)
> {
> - if (!release_lport_(pb, sb_readonly)) {
> + if (!release_lport_(pb, sb_readonly, if_mgr)) {
> return false;
> }
>
> @@ -1342,10 +1403,9 @@ consider_localport(const struct sbrec_port_binding *pb,
> /* If the port binding is claimed, then release it as localport is
> claimed
> * by any ovn-controller. */
> if (pb->chassis == b_ctx_in->chassis_rec) {
> - if (!release_lport_(pb, !b_ctx_in->ovnsb_idl_txn)) {
> + if (!release_lport_(pb, !b_ctx_in->ovnsb_idl_txn,
> b_ctx_out->if_mgr)) {
> return false;
> }
> -
Unrelated?
> remove_related_lport(pb, b_ctx_out);
> }
>
> diff --git a/controller/binding.h b/controller/binding.h
> index 430a8d9b1..45202a321 100644
> --- a/controller/binding.h
> +++ b/controller/binding.h
> @@ -151,14 +151,17 @@ const struct sbrec_port_binding
> *local_binding_get_primary_pb(
> ofp_port_t local_binding_get_lport_ofport(const struct shash *local_bindings,
> const char *pb_name);
>
> -bool local_binding_is_up(struct shash *local_bindings, const char *pb_name);
> -bool local_binding_is_down(struct shash *local_bindings, const char
> *pb_name);
> +bool local_binding_is_up(struct shash *local_bindings, const char *pb_name,
> + const struct sbrec_chassis *);
> +bool local_binding_is_down(struct shash *local_bindings, const char *pb_name,
> + const struct sbrec_chassis *);
> void local_binding_set_up(struct shash *local_bindings, const char *pb_name,
> const char *ts_now_str, bool sb_readonly,
> bool ovs_readonly);
> void local_binding_set_down(struct shash *local_bindings, const char
> *pb_name,
> bool sb_readonly, bool ovs_readonly);
> -
> +void local_binding_set_pb(struct shash *local_bindings, const char *pb_name,
> + const struct sbrec_chassis *chassis_rec);
> void binding_register_ovs_idl(struct ovsdb_idl *);
> void binding_run(struct binding_ctx_in *, struct binding_ctx_out *);
> bool binding_cleanup(struct ovsdb_idl_txn *ovnsb_idl_txn,
> diff --git a/controller/if-status.c b/controller/if-status.c
> index dbdf8b66f..e36df22a0 100644
> --- a/controller/if-status.c
> +++ b/controller/if-status.c
> @@ -115,6 +115,7 @@ static void ovs_iface_set_state(struct if_status_mgr *,
> struct ovs_iface *,
>
> static void if_status_mgr_update_bindings(
> struct if_status_mgr *mgr, struct local_binding_data *binding_data,
> + const struct sbrec_chassis *chassis_rec,
> bool sb_readonly, bool ovs_readonly);
>
> struct if_status_mgr *
> @@ -181,6 +182,13 @@ if_status_mgr_claim_iface(struct if_status_mgr *mgr,
> const char *iface_id)
> }
> }
>
> +bool
> +if_status_mgr_iface_is_present(struct if_status_mgr *mgr, const char
> *iface_id)
> +{
> + struct ovs_iface *iface = shash_find_data(&mgr->ifaces, iface_id);
> + return (!!iface);
> +}
> +
> void
> if_status_mgr_release_iface(struct if_status_mgr *mgr, const char *iface_id)
> {
> @@ -247,7 +255,8 @@ if_status_mgr_delete_iface(struct if_status_mgr *mgr,
> const char *iface_id)
>
> void
> if_status_mgr_update(struct if_status_mgr *mgr,
> - struct local_binding_data *binding_data)
> + struct local_binding_data *binding_data,
> + const struct sbrec_chassis *chassis_rec)
> {
> if (!binding_data) {
> return;
> @@ -262,7 +271,7 @@ if_status_mgr_update(struct if_status_mgr *mgr,
> HMAPX_FOR_EACH_SAFE (node, &mgr->ifaces_per_state[OIF_MARK_UP]) {
> struct ovs_iface *iface = node->data;
>
> - if (local_binding_is_up(bindings, iface->id)) {
> + if (local_binding_is_up(bindings, iface->id, chassis_rec)) {
> ovs_iface_set_state(mgr, iface, OIF_INSTALLED);
> }
> }
> @@ -273,7 +282,7 @@ if_status_mgr_update(struct if_status_mgr *mgr,
> HMAPX_FOR_EACH_SAFE (node, &mgr->ifaces_per_state[OIF_MARK_DOWN]) {
> struct ovs_iface *iface = node->data;
>
> - if (local_binding_is_down(bindings, iface->id)) {
> + if (local_binding_is_down(bindings, iface->id, chassis_rec)) {
> ovs_iface_destroy(mgr, iface);
> }
> }
> @@ -306,6 +315,7 @@ if_status_mgr_update(struct if_status_mgr *mgr,
> void
> if_status_mgr_run(struct if_status_mgr *mgr,
> struct local_binding_data *binding_data,
> + const struct sbrec_chassis *chassis_rec,
> bool sb_readonly, bool ovs_readonly)
> {
> struct ofctrl_acked_seqnos *acked_seqnos =
> @@ -328,8 +338,8 @@ if_status_mgr_run(struct if_status_mgr *mgr,
> ofctrl_acked_seqnos_destroy(acked_seqnos);
>
> /* Update binding states. */
> - if_status_mgr_update_bindings(mgr, binding_data, sb_readonly,
> - ovs_readonly);
> + if_status_mgr_update_bindings(mgr, binding_data, chassis_rec,
> + sb_readonly, ovs_readonly);
> }
>
> static void
> @@ -390,6 +400,7 @@ ovs_iface_set_state(struct if_status_mgr *mgr, struct
> ovs_iface *iface,
> static void
> if_status_mgr_update_bindings(struct if_status_mgr *mgr,
> struct local_binding_data *binding_data,
> + const struct sbrec_chassis *chassis_rec,
> bool sb_readonly, bool ovs_readonly)
> {
> if (!binding_data) {
> @@ -404,6 +415,9 @@ if_status_mgr_update_bindings(struct if_status_mgr *mgr,
> */
> HMAPX_FOR_EACH (node, &mgr->ifaces_per_state[OIF_INSTALL_FLOWS]) {
> struct ovs_iface *iface = node->data;
> + if (!sb_readonly) {
> + local_binding_set_pb(bindings, iface->id, chassis_rec);
> + }
>
> local_binding_set_down(bindings, iface->id, sb_readonly,
> ovs_readonly);
> }
> @@ -415,7 +429,9 @@ if_status_mgr_update_bindings(struct if_status_mgr *mgr,
> char *ts_now_str = xasprintf("%lld", time_wall_msec());
> HMAPX_FOR_EACH (node, &mgr->ifaces_per_state[OIF_MARK_UP]) {
> struct ovs_iface *iface = node->data;
> -
> + if (!sb_readonly) {
> + local_binding_set_pb(bindings, iface->id, chassis_rec);
> + }
> local_binding_set_up(bindings, iface->id, ts_now_str,
> sb_readonly, ovs_readonly);
> }
> @@ -426,6 +442,9 @@ if_status_mgr_update_bindings(struct if_status_mgr *mgr,
> */
> HMAPX_FOR_EACH (node, &mgr->ifaces_per_state[OIF_MARK_DOWN]) {
> struct ovs_iface *iface = node->data;
> + if (!sb_readonly) {
> + local_binding_set_pb(bindings, iface->id, NULL);
> + }
>
> local_binding_set_down(bindings, iface->id, sb_readonly,
> ovs_readonly);
> }
> diff --git a/controller/if-status.h b/controller/if-status.h
> index ff4aa760e..2c55eb18e 100644
> --- a/controller/if-status.h
> +++ b/controller/if-status.h
> @@ -31,10 +31,14 @@ void if_status_mgr_claim_iface(struct if_status_mgr *,
> const char *iface_id);
> void if_status_mgr_release_iface(struct if_status_mgr *, const char
> *iface_id);
> void if_status_mgr_delete_iface(struct if_status_mgr *, const char
> *iface_id);
>
> -void if_status_mgr_update(struct if_status_mgr *, struct local_binding_data
> *);
> +void if_status_mgr_update(struct if_status_mgr *, struct local_binding_data
> *,
> + const struct sbrec_chassis *chassis);
> void if_status_mgr_run(struct if_status_mgr *mgr, struct local_binding_data
> *,
> + const struct sbrec_chassis *chassis,
> bool sb_readonly, bool ovs_readonly);
> void if_status_mgr_get_memory_usage(struct if_status_mgr *mgr,
> struct simap *usage);
> +bool if_status_mgr_iface_is_present(struct if_status_mgr *mgr,
> + const char *iface_id);
>
> # endif /* controller/if-status.h */
> diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
> index 5a6274eb2..994aebdfb 100644
> --- a/controller/ovn-controller.c
> +++ b/controller/ovn-controller.c
> @@ -3912,7 +3912,7 @@ main(int argc, char *argv[])
> runtime_data ? &runtime_data->lbinding_data : NULL;
> stopwatch_start(IF_STATUS_MGR_UPDATE_STOPWATCH_NAME,
> time_msec());
> - if_status_mgr_update(if_mgr, binding_data);
> + if_status_mgr_update(if_mgr, binding_data, chassis);
> stopwatch_stop(IF_STATUS_MGR_UPDATE_STOPWATCH_NAME,
> time_msec());
>
> @@ -3938,8 +3938,8 @@ main(int argc, char *argv[])
> time_msec());
> stopwatch_start(IF_STATUS_MGR_RUN_STOPWATCH_NAME,
> time_msec());
> - if_status_mgr_run(if_mgr, binding_data, !ovnsb_idl_txn,
> - !ovs_idl_txn);
> + if_status_mgr_run(if_mgr, binding_data, chassis,
> + !ovnsb_idl_txn, !ovs_idl_txn);
> stopwatch_stop(IF_STATUS_MGR_RUN_STOPWATCH_NAME,
> time_msec());
> }
> diff --git a/tests/ovn.at b/tests/ovn.at
> index 6a0a169c1..403fbc85f 100644
> --- a/tests/ovn.at
> +++ b/tests/ovn.at
> @@ -13934,6 +13934,7 @@ ovn_attach n1 br-phys 192.168.0.11
> ovs-vsctl -- add-port br-int hv1-vif0 -- \
> set Interface hv1-vif0 ofport-request=1
>
> +
Unrelated?
> sim_add hv2
> as hv2
> ovs-vsctl add-br br-phys
> @@ -13983,7 +13984,10 @@ AT_CHECK([as hv1 ovs-ofctl dump-flows br-int
> table=65 | grep actions=output:1],
> echo "verifying that lsp0 binding moves when requested-chassis is changed"
>
> ovn-nbctl lsp-set-options lsp0 requested-chassis=hv2
> -OVS_WAIT_UNTIL([test 1 = $(grep -c "Releasing lport lsp0 from this chassis"
> hv1/ovn-controller.log)])
> +
> +# We might see multiple "Releasing lport ...", when sb is read only
> +OVS_WAIT_UNTIL([test 1 -le $(grep -c "Releasing lport lsp0 from this
> chassis" hv1/ovn-controller.log)])
> +
> wait_column "$hv2_uuid" Port_Binding chassis logical_port=lsp0
>
> # (6) Chassis hv2 should add flows and hv1 should not.
> @@ -14029,7 +14033,7 @@ AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=0
> | grep in_port=1], [0], [ig
> AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=65 | grep
> actions=output:1], [0], [ignore])
>
> check ovn-nbctl --wait=hv lsp-set-options lsp0
> requested-chassis=non-existant-chassis
> -OVS_WAIT_UNTIL([test 1 = $(grep -c "Releasing lport lsp0 from this chassis"
> hv1/ovn-controller.log)])
> +OVS_WAIT_UNTIL([test 1 -le $(grep -c "Releasing lport lsp0 from this
> chassis" hv1/ovn-controller.log)])
> check ovn-nbctl --wait=hv sync
> wait_column '' Port_Binding chasssi logical_port=lsp0
> AT_CHECK([as hv1 ovs-ofctl dump-flows br-int table=0 | grep in_port=1], [1],
> [])
> @@ -30595,3 +30599,50 @@ test_mac_binding_flows hv2 00\:00\:00\:00\:10\:10 1
> OVN_CLEANUP([hv1], [hv2])
> AT_CLEANUP
> ])
> +
> +OVN_FOR_EACH_NORTHD([
> +AT_SETUP([recomputes])
> +ovn_start
> +
> +# Add chassis
> +net_add n1
> +sim_add hv1
> +as hv1
> +ovs-vsctl add-br br-phys
> +ovs-vsctl set open . external-ids:ovn-bridge-mappings=phys:br-phys
> +ovn_attach n1 br-phys 192.168.0.1
> +
> +add_switch_ports() {
> + for port in $(seq $1 $2); do
> + logical_switch_port=lsp${port}
> + check ovn-nbctl lsp-add ls1 $logical_switch_port
> + check ovn-nbctl lsp-set-addresses $logical_switch_port dynamic
> +
> + as hv1 ovs-vsctl \
> + -- add-port br-int vif${port} \
> + -- set Interface vif${port}
> external_ids:iface-id=$logical_switch_port
> + done
Don't we need a wait_for_ports_up here to make sure the new ports are
claimed and processed?
> +
> + check ovn-nbctl --wait=hv sync
> + AT_CHECK([test $(ovn-appctl -t ovn-controller coverage/read-counter
> lflow_run) == $lflow_run])
> +}
> +
> +check ovn-nbctl ls-add ls1
> +check ovn-nbctl set Logical_Switch ls1 other_config:subnet=10.1.0.0/16
> +check ovn-nbctl set Logical_Switch ls1 other_config:exclude_ips=10.1.255.254
> +
> +check ovn-nbctl lr-add lr1
> +check ovn-nbctl lsp-add ls1 lsp0 -- set Logical_Switch_Port lsp0 type=router
> options:router-port=lrp0 addresses=dynamic
> +check ovn-nbctl lrp-add lr1 lrp0 "f0:00:00:01:00:01" 10.1.255.254/16
> +check ovn-nbctl lr-nat-add lr1 snat 10.2.0.1 10.1.0.0/16
> +
> +check ovn-nbctl --wait=hv sync
> +lflow_run=$(ovn-appctl -t ovn-controller coverage/read-counter lflow_run)
> +
> +add_switch_ports 1 50
> +add_switch_ports 51 100
> +add_switch_ports 101 150
> +
> +OVN_CLEANUP([hv1])
> +AT_CLEANUP
> +])
Thanks,
Dumitru
_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev