On most switches, if STP is enabled and the port connected to OVS
is not in edge mode, when OVS service restart and negotiate LACP,
which will trigger STP recalculation.

During this time, traffic on the LACP interface will be blocked
for several seconds(up to 5s+).

Test in Mellanox Switch
OvS LACP: Fast Mode
Switch LACP |   STP Mode  | Downtime
------------|-------------|---------
Fast/Slow   | on - normal | 3.1s
Fast/Slow   | off / edge  | 0.2s

This patch propose a solution, similar to flow restore,
1. save LACP negotiation status
2. pause LACP process
3. restart OvS service
4. inject LACP status into vswitchd
5. resume LACP process

Test Results with this patch
Switch LACP |   STP Mode  | Downtime
------------|-------------|---------
Fast        | on - normal | 0.3s
Slow        | on - normal | 0.0s
Fast/Slow   | off / edge  | 0.0s

Signed-off-by: Changliang Wu <[email protected]>
---
 lib/lacp.c             | 272 +++++++++++++++++++++++++++++++++++++++++
 ofproto/ofproto-dpif.c |   5 +-
 ofproto/ofproto.c      |  14 +++
 ofproto/ofproto.h      |   2 +
 utilities/ovs-lib.in   |  46 ++++++-
 utilities/ovs-save     | 159 ++++++++++++++++++++++++
 vswitchd/bridge.c      |   5 +
 7 files changed, 499 insertions(+), 4 deletions(-)

diff --git a/lib/lacp.c b/lib/lacp.c
index 3252f17eb..251f63dbe 100644
--- a/lib/lacp.c
+++ b/lib/lacp.c
@@ -25,6 +25,7 @@
 #include "dp-packet.h"
 #include "ovs-atomic.h"
 #include "packets.h"
+#include "openvswitch/ofp-parse.h"
 #include "openvswitch/poll-loop.h"
 #include "seq.h"
 #include "openvswitch/shash.h"
@@ -168,6 +169,7 @@ static bool member_may_enable__(struct member *) 
OVS_REQUIRES(mutex);
 
 static unixctl_cb_func lacp_unixctl_show;
 static unixctl_cb_func lacp_unixctl_show_stats;
+static unixctl_cb_func lacp_unixctl_set;
 
 /* Populates 'pdu' with a LACP PDU comprised of 'actor' and 'partner'. */
 static void
@@ -229,6 +231,8 @@ lacp_init(void)
                              lacp_unixctl_show, NULL);
     unixctl_command_register("lacp/show-stats", "[port]", 0, 1,
                              lacp_unixctl_show_stats, NULL);
+        unixctl_command_register("lacp/set", "[port]", 3, INT_MAX,
+                             lacp_unixctl_set, NULL);
 }
 
 static void
@@ -940,6 +944,34 @@ lacp_find(const char *name) OVS_REQUIRES(mutex)
     return NULL;
 }
 
+static struct member *
+lacp_member_find(struct lacp *lacp, const char *name) OVS_REQUIRES(mutex)
+{
+    struct member *member;
+
+    HMAP_FOR_EACH_SAFE (member, node, &lacp->members) {
+        if (!strcmp(member->name, name)) {
+            return member;
+        }
+    }
+
+    return NULL;
+}
+
+static struct member *
+lacp_member_find_by_key(struct lacp *lacp, uint16_t key) OVS_REQUIRES(mutex)
+{
+    struct member *member;
+
+    HMAP_FOR_EACH_SAFE (member, node, &lacp->members) {
+        if (member->port_id == key) {
+            return member;
+        }
+    }
+
+    return NULL;
+}
+
 static void
 ds_put_lacp_state(struct ds *ds, uint8_t state)
 {
@@ -1219,5 +1251,245 @@ lacp_get_member_stats(const struct lacp *lacp, const 
void *member_,
     }
     ovs_mutex_unlock(&mutex);
     return ret;
+}
+
+static void
+lacp_port_set(struct unixctl_conn *conn, struct lacp *lacp, const char *parms)
+    OVS_REQUIRES(mutex)
+{
+    char *value;
+    char *key;
+    char **p = (char **)&parms;
+    while (ofputil_parse_key_value(p, &key, &value)) {
+        lacp->update = false;
+        uint32_t tmp;
+        if (nullable_string_is_equal(key, "status")) {
+            if (strstr(value, "active")) {
+                lacp->active = true;
+            }
+            if (strstr(value, "negotiated")) {
+                lacp->negotiated = true;
+            }
+
+        } else if (nullable_string_is_equal(key, "sys_id")) {
+            if (!eth_addr_from_string(value, &lacp->sys_id)) {
+                unixctl_command_reply_error(conn, "invalid port sys_id");
+                return;
+            }
+        } else if (nullable_string_is_equal(key, "sys_priority")) {
+            if (!str_to_uint(value, 10, &tmp)) {
+                unixctl_command_reply_error(conn, "invalid port sys_prority");
+                return;
+            }
+            lacp->sys_priority = tmp;
+        } else if (nullable_string_is_equal(key, "key")) {
+            if (!str_to_uint(value, 10, &tmp)) {
+                unixctl_command_reply_error(conn, "invalid port key");
+                return;
+            }
+            lacp->key_member = lacp_member_find_by_key(lacp, tmp);
+        }
+    }
+}
+
+static uint8_t
+lacp_state_from_str(const char *s)
+{
+    uint8_t state = 0;
+    if (strstr(s, "activity")) {
+        state |= LACP_STATE_ACT;
+    }
+    if (strstr(s, "timeout")) {
+        state |= LACP_STATE_TIME;
+    }
+    if (strstr(s, "aggregation")) {
+        state |= LACP_STATE_AGG;
+    }
+    if (strstr(s, "synchronized")) {
+        state |= LACP_STATE_SYNC;
+    }
+    if (strstr(s, "collecting")) {
+        state |= LACP_STATE_COL;
+    }
+    if (strstr(s, "distributing")) {
+        state |= LACP_STATE_DIST;
+    }
+    if (strstr(s, "defaulted")) {
+        state |= LACP_STATE_DEF;
+    }
+    if (strstr(s, "expired")) {
+        state |= LACP_STATE_EXP;
+    }
+    return state;
+}
 
+static void
+lacp_member_set(struct unixctl_conn *conn, struct member *member,
+                const char *op, const char *parms) OVS_REQUIRES(mutex)
+{
+    char *value;
+    char *key;
+    char **p = (char **)&parms;
+    if (nullable_string_is_equal(op, "member")) {
+        while (ofputil_parse_key_value(p, &key, &value)) {
+            uint32_t tmp;
+            if (nullable_string_is_equal(key, "port_id")) {
+                if (!str_to_uint(value, 10, &tmp)) {
+                    unixctl_command_reply_error(conn, "invalid member "
+                                                      "port_id");
+                    return;
+                }
+                member->port_id = tmp;
+            } else if (nullable_string_is_equal(key, "port_priority")) {
+                if (!str_to_uint(value, 10, &tmp)) {
+                    unixctl_command_reply_error(conn, "invalid member "
+                                                      "port_priority");
+                    return;
+                }
+                member->port_priority = tmp;
+            } else if (nullable_string_is_equal(key, "status")) {
+                if (strstr(value, "current")) {
+                    member->status = LACP_CURRENT;
+                    member->carrier_up = true;
+                } else if (strstr(value, "expired")) {
+                    member->status = LACP_EXPIRED;
+                } else if (strstr(value, "defaulted")) {
+                    member->status = LACP_DEFAULTED;
+                }
+
+                if (strstr(value, "attached")) {
+                    member->attached = true;
+                }
+            }
+        }
+    } else if (nullable_string_is_equal(op, "actor")) {
+        while (ofputil_parse_key_value(p, &key, &value)) {
+            uint32_t tmp;
+            if (nullable_string_is_equal(key, "sys_id")) {
+                struct eth_addr addr;
+                if (!eth_addr_from_string(value, &addr)) {
+                    unixctl_command_reply_error(conn, "invalid member "
+                                                      "actor sys_id");
+                    return;
+                }
+                member->ntt_actor.sys_id = addr;
+            } else if (nullable_string_is_equal(key, "sys_priority")) {
+                if (!str_to_uint(value, 10, &tmp)) {
+                    unixctl_command_reply_error(conn, "invalid member "
+                                                      "actor sys_priority");
+                    return;
+                }
+                member->ntt_actor.sys_priority = htons(tmp);
+            } else if (nullable_string_is_equal(key, "port_id")) {
+                if (!str_to_uint(value, 10, &tmp)) {
+                    unixctl_command_reply_error(conn, "invalid member "
+                                                      "actor port_id");
+                    return;
+                }
+                member->ntt_actor.port_id = htons(tmp);
+            } else if (nullable_string_is_equal(key, "port_priority")) {
+                if (!str_to_uint(value, 10, &tmp)) {
+                    unixctl_command_reply_error(conn, "invalid member "
+                                                      "actor port_priority");
+                    return;
+                }
+                member->ntt_actor.port_priority = htons(tmp);
+            } else if (nullable_string_is_equal(key, "key")) {
+                if (!str_to_uint(value, 10, &tmp)) {
+                    unixctl_command_reply_error(conn, "invalid member "
+                                                      "actor key");
+                    return;
+                }
+                member->ntt_actor.key = htons(tmp);
+            } else if (nullable_string_is_equal(key, "state")) {
+                member->ntt_actor.state = lacp_state_from_str(value);
+                VLOG_INFO("member->ntt_actor.state (%p)  (%d)",
+                          &(member->ntt_actor.state), member->ntt_actor.state);
+            }
+        }
+    } else if (nullable_string_is_equal(op, "partner")) {
+        while (ofputil_parse_key_value(p, &key, &value)) {
+            uint32_t tmp;
+            if (nullable_string_is_equal(key, "sys_id")) {
+                struct eth_addr addr;
+                if (!eth_addr_from_string(value, &addr)) {
+                    unixctl_command_reply_error(conn, "invalid member "
+                                                      "partner sys_id");
+                    return;
+                }
+                member->partner.sys_id = addr;
+            } else if (nullable_string_is_equal(key, "sys_priority")) {
+                if (!str_to_uint(value, 10, &tmp)) {
+                    unixctl_command_reply_error(conn, "invalid member "
+                                                      "partner sys_priority");
+                    return;
+                }
+                member->partner.sys_priority = htons(tmp);
+            } else if (nullable_string_is_equal(key, "port_id")) {
+                if (!str_to_uint(value, 10, &tmp)) {
+                    unixctl_command_reply_error(conn, "invalid member "
+                                                      "partner port_id");
+                    return;
+                }
+                member->partner.port_id = htons(tmp);
+            } else if (nullable_string_is_equal(key, "port_priority")) {
+                if (!str_to_uint(value, 10, &tmp)) {
+                    unixctl_command_reply_error(conn, "invalid member "
+                                                      "partner port_priority");
+                    return;
+                }
+                member->partner.port_priority = htons(tmp);
+            } else if (nullable_string_is_equal(key, "key")) {
+                if (!str_to_uint(value, 10, &tmp)) {
+                    unixctl_command_reply_error(conn, "invalid member "
+                                                      "partner key");
+                    return;
+                }
+                member->partner.key = htons(tmp);
+            } else if (nullable_string_is_equal(key, "state")) {
+                member->partner.state = lacp_state_from_str(value);
+            }
+        }
+    } else {
+        unixctl_command_reply_error(conn, "invalid member op");
+    }
+    timer_set_duration(&member->tx, LACP_FAST_TIME_TX);
+    timer_set_duration(&member->rx, LACP_FAST_TIME_TX);
+}
+
+static void
+lacp_unixctl_set(struct unixctl_conn *conn, int argc, const char *argv[],
+                 void *aux OVS_UNUSED) OVS_EXCLUDED(mutex)
+{
+    struct member *member;
+    struct lacp *lacp;
+
+    lacp_lock();
+    lacp = lacp_find(argv[1]);
+    if (!lacp) {
+        unixctl_command_reply_error(conn, "lacp port not found");
+        goto out;
+    }
+
+    if (nullable_string_is_equal(argv[2], "port")) {
+        lacp_port_set(conn, lacp, argv[3]);
+    } else if (nullable_string_is_equal(argv[2], "member")) {
+        member = lacp_member_find(lacp, argv[3]);
+        if (!member) {
+            unixctl_command_reply_error(conn, "lacp member not found");
+            goto out;
+        }
+        if (argc < 5) {
+            unixctl_command_reply_error(conn, "invalid member parms");
+            goto out;
+        }
+        lacp_member_set(conn, member, argv[4], argv[5]);
+    } else {
+        unixctl_command_reply_error(conn, "invalid op type");
+        goto out;
+    }
+
+out:
+    unixctl_command_reply(conn, NULL);
+    lacp_unlock();
 }
diff --git a/ofproto/ofproto-dpif.c b/ofproto/ofproto-dpif.c
index ed9e44ce2..28657f0e8 100644
--- a/ofproto/ofproto-dpif.c
+++ b/ofproto/ofproto-dpif.c
@@ -41,6 +41,7 @@
 #include "nx-match.h"
 #include "odp-util.h"
 #include "odp-execute.h"
+#include "ofproto/ofproto.h"
 #include "ofproto/ofproto-dpif.h"
 #include "ofproto/ofproto-provider.h"
 #include "ofproto-dpif-ipfix.h"
@@ -3702,7 +3703,6 @@ send_pdu_cb(void *port_, const void *pdu, size_t pdu_size)
     struct ofport_dpif *port = port_;
     struct eth_addr ea;
     int error;
-
     error = netdev_get_etheraddr(port->up.netdev, &ea);
     if (!error) {
         struct dp_packet packet;
@@ -3781,6 +3781,9 @@ bundle_send_learning_packets(struct ofbundle *bundle)
 static void
 bundle_run(struct ofbundle *bundle)
 {
+    if (bundle->lacp && ofproto_get_lacp_restore_wait()) {
+        return;
+    }
     if (bundle->lacp) {
         lacp_run(bundle->lacp, send_pdu_cb);
     }
diff --git a/ofproto/ofproto.c b/ofproto/ofproto.c
index 6fa18228b..6f587c21f 100644
--- a/ofproto/ofproto.c
+++ b/ofproto/ofproto.c
@@ -326,6 +326,8 @@ static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 
5);
 
 /* The default value of true waits for flow restore. */
 static bool flow_restore_wait = true;
+/* The default value of true waits for lacp restore. */
+static bool lacp_restore_wait = true;
 
 /* Must be called to initialize the ofproto library.
  *
@@ -997,6 +999,18 @@ ofproto_get_flow_restore_wait(void)
     return flow_restore_wait;
 }
 
+void
+ofproto_set_lacp_restore_wait(bool lacp_restore_wait_db)
+{
+    lacp_restore_wait = lacp_restore_wait_db;
+}
+
+bool
+ofproto_get_lacp_restore_wait(void)
+{
+    return lacp_restore_wait;
+}
+
 /* Retrieve datapath capabilities. */
 void
 ofproto_get_datapath_cap(const char *datapath_type, struct smap *dp_cap)
diff --git a/ofproto/ofproto.h b/ofproto/ofproto.h
index 3f85509a1..e8e69b0da 100644
--- a/ofproto/ofproto.h
+++ b/ofproto/ofproto.h
@@ -382,6 +382,8 @@ int ofproto_set_local_sample(struct ofproto *ofproto,
                              size_t n_options);
 void ofproto_set_flow_restore_wait(bool flow_restore_wait_db);
 bool ofproto_get_flow_restore_wait(void);
+void ofproto_set_lacp_restore_wait(bool lacp_restore_wait_db);
+bool ofproto_get_lacp_restore_wait(void);
 int ofproto_set_stp(struct ofproto *, const struct ofproto_stp_settings *);
 int ofproto_get_stp_status(struct ofproto *, struct ofproto_stp_status *);
 
diff --git a/utilities/ovs-lib.in b/utilities/ovs-lib.in
index dded0b7c7..a845ac915 100644
--- a/utilities/ovs-lib.in
+++ b/utilities/ovs-lib.in
@@ -587,12 +587,21 @@ ovs_save () {
     [ -z "${bridges}" ] && return 0
 }
 
+ovs_save_lacp () {
+    $datadir/scripts/ovs-save save-lacp > "${script_lacp}"
+    chmod +x "${script_lacp}"
+}
+
 save_flows_if_required () {
     if test X"$DELETE_BRIDGES" != Xyes; then
         action "Saving flows" ovs_save save-flows "${script_flows}"
     fi
 }
 
+save_lacp () {
+    action "Saving lacp" ovs_save_lacp
+}
+
 save_interfaces () {
     "$datadir/scripts/ovs-save" save-interfaces ${ifaces} \
         > "${script_interfaces}"
@@ -604,6 +613,12 @@ flow_restore_wait () {
     fi
 }
 
+lacp_restore_wait () {
+    if test X"${OVS_VSWITCHD:-yes}" = Xyes; then
+        ovs_vsctl set open_vswitch . other_config:lacp-restore-wait="true"
+    fi
+}
+
 flow_restore_complete () {
     if test X"${OVS_VSWITCHD:-yes}" = Xyes; then
         ovs_vsctl --if-exists remove open_vswitch . other_config \
@@ -611,11 +626,23 @@ flow_restore_complete () {
     fi
 }
 
+lacp_restore_complete () {
+    if test X"${OVS_VSWITCHD:-yes}" = Xyes; then
+        ovs_vsctl --if-exists remove open_vswitch . other_config \
+                  lacp-restore-wait="true"
+    fi
+}
+
 restore_flows () {
     [ -x "${script_flows}" ] && \
         action "Restoring saved flows" "${script_flows}"
 }
 
+restore_lacp () {
+    [ -x "${script_lacp}" ] && \
+        action "Restoring saved lacp" "${script_lacp}"
+}
+
 restore_interfaces () {
     [ ! -x "${script_interfaces}" ] && return 0
     action "Restoring interface configuration" "${script_interfaces}"
@@ -633,7 +660,8 @@ restore_interfaces () {
 init_restore_scripts () {
     script_interfaces=`mktemp`
     script_flows=`mktemp`
-    trap 'rm -f "${script_interfaces}" "${script_flows}"' 0
+    script_lacp=`mktemp`
+    trap 'rm -f "${script_interfaces}" "${script_flows}" "${script_lacp}"' 0
 }
 
 force_reload_kmod () {
@@ -648,6 +676,7 @@ force_reload_kmod () {
 
     init_restore_scripts
     save_flows_if_required
+    save_lacp
 
     # Restart the database first, since a large database may take a
     # while to load, and we want to minimize forwarding disruption.
@@ -682,11 +711,16 @@ force_reload_kmod () {
 
     # Start vswitchd by asking it to wait till flow restore is finished.
     flow_restore_wait
+    lacp_restore_wait
     start_forwarding || return 1
 
-    # Restore saved flows and inform vswitchd that we are done.
+    # Restore saved resources and inform vswitchd that we are done.
     restore_flows
+    restore_lacp
+
     flow_restore_complete
+    lacp_restore_complete
+
     add_managers
 
     restore_interfaces
@@ -704,6 +738,7 @@ restart () {
         init_restore_scripts
         if test X"${OVS_VSWITCHD:-yes}" = Xyes; then
             save_flows_if_required
+            save_lacp
         fi
     fi
 
@@ -716,10 +751,15 @@ restart () {
 
     # Start vswitchd by asking it to wait till flow restore is finished.
     flow_restore_wait
+    lacp_restore_wait
     start_forwarding || return 1
 
-    # Restore saved flows and inform vswitchd that we are done.
+    # Restore saved resources and inform vswitchd that we are done.
     restore_flows
+    restore_lacp
+
     flow_restore_complete
+    lacp_restore_complete
+
     add_managers
 }
diff --git a/utilities/ovs-save b/utilities/ovs-save
index 67092ecf7..792d5daee 100755
--- a/utilities/ovs-save
+++ b/utilities/ovs-save
@@ -33,6 +33,8 @@ Commands:
                         configuration.
  save-flows             Outputs a shell script on stdout that will restore
                         OpenFlow flows of each Open vSwitch bridge.
+ save-lacp              Outputs a shell script on stdout that will restore
+                        lacp info of each lacp bond port.
 This script is meant as a helper for the Open vSwitch init script commands.
 EOF
 }
@@ -159,6 +161,158 @@ save_flows () {
     echo "rm -rf \"$workdir\""
 }
 
+save_lacp () {
+    if (ovs-appctl --version) > /dev/null 2>&1; then :; else
+        echo "$0: ovs-ofctl not found in $PATH" >&2
+        exit 1
+    fi
+
+    case `ovs-appctl version | sed 1q` in
+        "ovs-vswitchd (Open vSwitch) 1."*.*)
+            return
+            ;;
+    esac
+
+    SP="[[:space:]]*"
+    BOND_NAME_PATTERN="s/^----${SP}([^[:space:]]+)${SP}----.*/\1/"
+    STATUS_PATTERN="s/^status:${SP}(.*)/\1/"
+    SYS_ID_PATTERN="s/^sys_id:${SP}(.*)/\1/"
+    SYS_PRIORITY_PATTERN="s/^sys_priority:${SP}(.*)/\1/"
+    AGGREGATION_KEY_PATTERN="s/^aggregation${SP}key:${SP}(.*)/\1/"
+    LACP_TIME_PATTERN="s/^lacp_time:${SP}(.*)/\1/"
+    MEMBER_PATTERN="s/^(member|slave):${SP}([^:]+):${SP}(.*)/\2/"
+    MEMBER_STATUS_PATTERN="s/^(member|slave):${SP}([^:]+):${SP}(.*)/\3/"
+    PORT_ID_PATTERN="s/^port_id:${SP}(.*)/\1/"
+    PORT_PRIORITY_PATTERN="s/^port_priority:${SP}(.*)/\1/"
+    MAY_ENABLE_PATTERN="s/^may_enable:${SP}(.*)/\1/"
+    ACTOR_SYS_ID_PATTERN="s/^actor${SP}sys_id:${SP}(.*)/\1/"
+    ACTOR_SYS_PRIORITY_PATTERN="s/^actor${SP}sys_priority:${SP}(.*)/\1/"
+    ACTOR_PORT_ID_PATTERN="s/^actor${SP}port_id:${SP}(.*)/\1/"
+    ACTOR_PORT_PRIORITY_PATTERN="s/^actor${SP}port_priority:${SP}(.*)/\1/"
+    ACTOR_KEY_PATTERN="s/^actor${SP}key:${SP}(.*)/\1/"
+    ACTOR_STATE_PATTERN="s/^actor${SP}state:${SP}(.*)/\1/"
+    PARTNER_SYS_ID_PATTERN="s/^partner${SP}sys_id:${SP}(.*)/\1/"
+    PARTNER_SYS_PRIORITY_PATTERN="s/^partner${SP}sys_priority:${SP}(.*)/\1/"
+    PARTNER_PORT_ID_PATTERN="s/^partner${SP}port_id:${SP}(.*)/\1/"
+    PARTNER_PORT_PRIORITY_PATTERN="s/^partner${SP}port_priority:${SP}(.*)/\1/"
+    PARTNER_KEY_PATTERN="s/^partner${SP}key:${SP}(.*)/\1/"
+    PARTNER_STATE_PATTERN="s/^partner${SP}state:${SP}(.*)/\1/"
+    tmpfile=$(mktemp)
+    ovs-appctl lacp/show | while IFS= read -r line; do
+        t=$(echo "$line" | sed -E 's/^[[:space:]]+//')
+        case "$t" in
+            ----*----*)
+                BOND_NAME=$(echo "$t" | sed -E "$BOND_NAME_PATTERN")
+                ;;
+            status:${SP})
+                STATUS=$(echo "$t" | sed -E "$STATUS_PATTERN" | sed 's/ /+/g')
+                ;;
+            sys_id:${SP})
+                SYS_ID=$(echo "$t" | sed -E "$SYS_ID_PATTERN")
+                ;;
+            sys_priority:${SP})
+                SYS_PRIORITY=$(echo "$t" | sed -E "$SYS_PRIORITY_PATTERN")
+                ;;
+            aggregation${SP}key:${SP})
+                AGGREGATION_KEY=$(echo "$t" |
+                    sed -E "$AGGREGATION_KEY_PATTERN")
+                ;;
+            lacp_time:${SP})
+                LACP_TIME=$(echo "$t" | sed -E "$LACP_TIME_PATTERN")
+                cmd="ovs-appctl lacp/set $BOND_NAME port status=$STATUS,"
+                cmd="${cmd}sys_id=$SYS_ID,sys_priority=$SYS_PRIORITY,"
+                cmd="${cmd}key=$AGGREGATION_KEY,lacp_time=$LACP_TIME"
+                echo "$cmd" >> "$tmpfile"
+                ;;
+            member:${SP}|slave:${SP})
+                # Extract member name and status
+                MEMBER_NAME=$(echo "$t" | sed -E "$MEMBER_PATTERN")
+                MEMBER_STATUS=$(echo "$t" |
+                    sed -E "$MEMBER_STATUS_PATTERN" | sed 's/ /+/g')
+                ;;
+            port_id:${SP})
+                PORT_ID=$(echo "$t" | sed -E "$PORT_ID_PATTERN")
+                ;;
+            port_priority:${SP})
+                PORT_PRIORITY=$(echo "$t" | sed -E "$PORT_PRIORITY_PATTERN")
+                ;;
+            may_enable:${SP})
+                MAY_ENABLE=$(echo "$t" | sed -E "$MAY_ENABLE_PATTERN")
+                # Print member information
+                cmd="ovs-appctl lacp/set $BOND_NAME member $MEMBER_NAME member"
+                cmd="${cmd} status=$MEMBER_STATUS,port_id=$PORT_ID,"
+                cmd="${cmd}port_priority=$PORT_PRIORITY,may_enable=$MAY_ENABLE"
+                echo "$cmd" >> "$tmpfile"
+                ;;
+            actor${SP}sys_id:${SP})
+                ACTOR_SYS_ID=$(echo "$t" | sed -E "$ACTOR_SYS_ID_PATTERN")
+                ;;
+            actor${SP}sys_priority:${SP})
+                ACTOR_SYS_PRIORITY=$(echo "$t" |
+                    sed -E "$ACTOR_SYS_PRIORITY_PATTERN")
+                ;;
+            actor${SP}port_id:${SP})
+                ACTOR_PORT_ID=$(echo "$t" | sed -E "$ACTOR_PORT_ID_PATTERN")
+                ;;
+            actor${SP}port_priority:${SP})
+                ACTOR_PORT_PRIORITY=$(echo "$t" |
+                    sed -E "$ACTOR_PORT_PRIORITY_PATTERN")
+                ;;
+            actor${SP}key:${SP})
+                ACTOR_KEY=$(echo "$t" | sed -E "$ACTOR_KEY_PATTERN")
+                ;;
+            actor${SP}state:${SP})
+                ACTOR_STATE=$(echo "$t" |
+                    sed -E "$ACTOR_STATE_PATTERN" | sed 's/ /+/g')
+                # Print actor information
+                cmd="ovs-appctl lacp/set $BOND_NAME member $MEMBER_NAME actor"
+                cmd="${cmd} sys_id=$ACTOR_SYS_ID,"
+                cmd="${cmd}sys_priority=$ACTOR_SYS_PRIORITY,"
+                cmd="${cmd}port_id=$ACTOR_PORT_ID,"
+                cmd="${cmd}port_priority=$ACTOR_PORT_PRIORITY,"
+                cmd="${cmd}key=$ACTOR_KEY,state=$ACTOR_STATE"
+                echo "$cmd" >> "$tmpfile"
+                ;;
+            partner${SP}sys_id:${SP})
+                PARTNER_SYS_ID=$(echo "$t" |
+                    sed -E "$PARTNER_SYS_ID_PATTERN")
+                ;;
+            partner${SP}sys_priority:${SP})
+                PARTNER_SYS_PRIORITY=$(echo "$t" |
+                    sed -E "$PARTNER_SYS_PRIORITY_PATTERN")
+                ;;
+            partner${SP}port_id:${SP})
+                PARTNER_PORT_ID=$(echo "$t" |
+                    sed -E "$PARTNER_PORT_ID_PATTERN")
+                ;;
+            partner${SP}port_priority:${SP})
+                PARTNER_PORT_PRIORITY=$(echo "$t" |
+                    sed -E "$PARTNER_PORT_PRIORITY_PATTERN")
+                ;;
+            partner${SP}key:${SP})
+                PARTNER_KEY=$(echo "$t" | sed -E "$PARTNER_KEY_PATTERN")
+                ;;
+            partner${SP}state:${SP})
+                PARTNER_STATE=$(echo "$t" |
+                    sed -E "$PARTNER_STATE_PATTERN" | sed 's/ /+/g')
+                # Print partner information
+                cmd="ovs-appctl lacp/set $BOND_NAME"
+                cmd="${cmd} member $MEMBER_NAME partner"
+                cmd="${cmd} sys_id=$PARTNER_SYS_ID,"
+                cmd="${cmd}sys_priority=$PARTNER_SYS_PRIORITY,"
+                cmd="${cmd}port_id=$PARTNER_PORT_ID,"
+                cmd="${cmd}port_priority=$PARTNER_PORT_PRIORITY,"
+                cmd="${cmd}key=$PARTNER_KEY,state=$PARTNER_STATE"
+                echo "$cmd" >> "$tmpfile"
+                ;;
+        esac
+    done
+    sed -i '1!G;h;$!d' "$tmpfile"
+    cat "$tmpfile"
+    rm -f "$tmpfile"
+}
+
+
 while [ $# -ne 0 ]
 do
     case $1 in
@@ -172,6 +326,11 @@ do
             save_interfaces "$@"
             exit 0
             ;;
+        "save-lacp")
+            shift
+            save_lacp "$@"
+            exit 0
+            ;;
         -h | --help)
             usage
             exit 0
diff --git a/vswitchd/bridge.c b/vswitchd/bridge.c
index 456b784c0..e2c523051 100644
--- a/vswitchd/bridge.c
+++ b/vswitchd/bridge.c
@@ -3415,6 +3415,11 @@ bridge_run(void)
                                         "flow-restore-wait", false));
     }
 
+    if (cfg && ofproto_get_lacp_restore_wait()) {
+        ofproto_set_lacp_restore_wait(smap_get_bool(&cfg->other_config,
+                                        "lacp-restore-wait", false));
+    }
+
     bridge_run__();
 
     /* Re-configure SSL/TLS.  We do this on every trip through the main loop,
-- 
2.43.5

_______________________________________________
dev mailing list
[email protected]
https://mail.openvswitch.org/mailman/listinfo/ovs-dev

Reply via email to