On Wed, Dec 14, 2022 at 7:48 PM Ihar Hrachyshka <[email protected]> wrote:
>
> There's one code branch left for the now-killed "both" filter. (See
> below.) With this branch cleaned up,
>
> Acked-By: Ihar Hrachyshka <[email protected]>

I applied this patch to the main and branch-22,12 with a few changes.

Please see below


>
> On Tue, Dec 13, 2022 at 2:34 PM Abhiram R N <[email protected]> wrote:
> >
> > Mirror creation just creates the mirror. The lsp-attach-mirror
> > triggers the sequence to create Mirror in OVS DB on compute node.
> > OVS already supports Port Mirroring.
> >
> > Further added test cases in ovn.at to verify end to end
> > the functioning of Port Mirror and also verify bulk updates
> > to mirrors.
> >
> > Note: This is targeted to mirror to destinations anywhere outside the
> > cluster where the analyser resides and it need not be an OVN node.
> >
> > Example commands are as below:
> >
> > Mirror creation
> > ovn-nbctl mirror-add mirror1 gre 0 from-lport 10.10.10.2
> >
> > Attach a logical port to the mirror.
> > ovn-nbctl lsp-attach-mirror sw0-port1 mirror1
> >
> > Detach a source from Mirror
> > ovn-nbctl lsp-detach-mirror sw0-port1 mirror1
> >
> > Mirror deletion
> > ovn-nbctl mirror-del mirror1
> >
> > Co-authored-by: Veda Barrenkala <[email protected]>
> > Signed-off-by: Veda Barrenkala <[email protected]>
> > Signed-off-by: Abhiram R N <[email protected]>
> > Acked-By: Ihar Hrachyshka <[email protected]>
> > ---
> >  NEWS                        |   1 +
> >  controller/automake.mk      |   4 +-
> >  controller/mirror.c         | 418 +++++++++++++++++++++++++++++
> >  controller/mirror.h         |  33 +++
> >  controller/ovn-controller.c |   9 +
> >  tests/ovn.at                | 514 ++++++++++++++++++++++++++++++++++++
> >  6 files changed, 978 insertions(+), 1 deletion(-)
> >  create mode 100644 controller/mirror.c
> >  create mode 100644 controller/mirror.h
> >
> > diff --git a/NEWS b/NEWS
> > index 7d1e49cf1..566d6731b 100644
> > --- a/NEWS
> > +++ b/NEWS
> > @@ -10,6 +10,7 @@ OVN v22.12.0 - xx xxx xxxx
> >      per-flow IPFIX sampling.
> >    - Add support for component templates within logical flows and load
> >      balancers.
> > +  - Added Support for Remote Port Mirroring.
> >
> >  OVN v22.09.0 - 16 Sep 2022
> >  --------------------------
> > diff --git a/controller/automake.mk b/controller/automake.mk
> > index c2ab1bbe6..334672b4d 100644
> > --- a/controller/automake.mk
> > +++ b/controller/automake.mk
> > @@ -41,7 +41,9 @@ controller_ovn_controller_SOURCES = \
> >         controller/ovsport.h \
> >         controller/ovsport.c \
> >         controller/vif-plug.h \
> > -       controller/vif-plug.c
> > +       controller/vif-plug.c \
> > +       controller/mirror.h \
> > +       controller/mirror.c
> >
> >  controller_ovn_controller_LDADD = lib/libovn.la 
> > $(OVS_LIBDIR)/libopenvswitch.la
> >  man_MANS += controller/ovn-controller.8
> > diff --git a/controller/mirror.c b/controller/mirror.c
> > new file mode 100644
> > index 000000000..938ec3542
> > --- /dev/null
> > +++ b/controller/mirror.c
> > @@ -0,0 +1,418 @@
> > +/* Copyright (c) 2022 Red Hat, Inc.
> > + *
> > + * Licensed under the Apache License, Version 2.0 (the "License");
> > + * you may not use this file except in compliance with the License.
> > + * You may obtain a copy of the License at:
> > + *
> > + *     http://www.apache.org/licenses/LICENSE-2.0
> > + *
> > + * Unless required by applicable law or agreed to in writing, software
> > + * distributed under the License is distributed on an "AS IS" BASIS,
> > + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> > + * See the License for the specific language governing permissions and
> > + * limitations under the License.
> > + */
> > +
> > +#include <config.h>
> > +#include <unistd.h>
> > +
> > +/* library headers */
> > +#include "lib/sset.h"
> > +#include "lib/util.h"
> > +
> > +/* OVS includes. */
> > +#include "lib/vswitch-idl.h"
> > +#include "include/openvswitch/shash.h"
> > +#include "openvswitch/vlog.h"
> > +
> > +/* OVN includes. */
> > +#include "binding.h"
> > +#include "lib/ovn-sb-idl.h"
> > +#include "mirror.h"
> > +
> > +VLOG_DEFINE_THIS_MODULE(port_mirror);
> > +
> > +struct ovn_mirror {
> > +    char *name;
> > +    const struct sbrec_mirror *sb_mirror;
> > +    const struct ovsrec_mirror *ovs_mirror;
> > +    struct ovs_list mirror_src_lports;
> > +    struct ovs_list mirror_dst_lports;
> > +};
> > +
> > +struct mirror_lport {
> > +    struct ovs_list list_node;
> > +
> > +    struct local_binding *lbinding;
> > +};
> > +
> > +static struct ovn_mirror *ovn_mirror_create(char *mirror_name);
> > +static void ovn_mirror_add(struct shash *ovn_mirrors,
> > +                           struct ovn_mirror *);
> > +static struct ovn_mirror *ovn_mirror_find(struct shash *ovn_mirrors,
> > +                                          const char *mirror_name);
> > +static void ovn_mirror_delete(struct ovn_mirror *);
> > +static void ovn_mirror_add_lport(struct ovn_mirror *, struct local_binding 
> > *);
> > +static void sync_ovn_mirror(struct ovn_mirror *, struct ovsdb_idl_txn *,
> > +                            const struct ovsrec_bridge *);
> > +
> > +static void create_ovs_mirror(struct ovn_mirror *, struct ovsdb_idl_txn *,
> > +                              const struct ovsrec_bridge *);
> > +static void sync_ovs_mirror_ports(struct ovn_mirror *,
> > +                                  const struct ovsrec_bridge *);
> > +static void delete_ovs_mirror(struct ovn_mirror *,
> > +                              const struct ovsrec_bridge *);
> > +static bool should_delete_ovs_mirror(struct ovn_mirror *);
> > +static void set_mirror_iface_options(struct ovsrec_interface *,
> > +                                     const struct sbrec_mirror *);
> > +
> > +static const struct ovsrec_port *get_iface_port(
> > +    const struct ovsrec_interface *, const struct ovsrec_bridge *);
> > +
> > +
> > +void
> > +mirror_register_ovs_idl(struct ovsdb_idl *ovs_idl)
> > +{
> > +    ovsdb_idl_add_column(ovs_idl, &ovsrec_bridge_col_mirrors);
> > +
> > +    ovsdb_idl_add_table(ovs_idl, &ovsrec_table_mirror);
> > +    ovsdb_idl_add_column(ovs_idl, &ovsrec_mirror_col_name);
> > +    ovsdb_idl_add_column(ovs_idl, &ovsrec_mirror_col_output_port);
> > +    ovsdb_idl_add_column(ovs_idl, &ovsrec_mirror_col_select_dst_port);
> > +    ovsdb_idl_add_column(ovs_idl, &ovsrec_mirror_col_select_src_port);
> > +    ovsdb_idl_add_column(ovs_idl, &ovsrec_mirror_col_external_ids);
> > +}
> > +
> > +void
> > +mirror_init(void)
> > +{
> > +}
> > +
> > +void
> > +mirror_destroy(void)
> > +{
> > +}
> > +
> > +void
> > +mirror_run(struct ovsdb_idl_txn *ovs_idl_txn,
> > +           const struct ovsrec_mirror_table *ovs_mirror_table,
> > +           const struct sbrec_mirror_table *sb_mirror_table,
> > +           const struct ovsrec_bridge *br_int,
> > +           struct shash *local_bindings)
> > +{
> > +    if (!ovs_idl_txn) {
> > +        return;
> > +    }
> > +
> > +    struct shash ovn_mirrors = SHASH_INITIALIZER(&ovn_mirrors);
> > +    struct shash tmp_mirrors = SHASH_INITIALIZER(&tmp_mirrors);
> > +
> > +    /* Iterate through sb mirrors and build the 'ovn_mirrors'. */
> > +    const struct sbrec_mirror *sb_mirror;
> > +    SBREC_MIRROR_TABLE_FOR_EACH (sb_mirror, sb_mirror_table) {
> > +        struct ovn_mirror *m = ovn_mirror_create(sb_mirror->name);
> > +        m->sb_mirror = sb_mirror;
> > +        ovn_mirror_add(&ovn_mirrors, m);
> > +    }
> > +
> > +    /* Iterate through ovs mirrors and add to the 'tmp_mirrors'. */
> > +    const struct ovsrec_mirror *ovs_mirror;
> > +    OVSREC_MIRROR_TABLE_FOR_EACH (ovs_mirror, ovs_mirror_table) {
> > +        bool ovn_owned_mirror = smap_get_bool(&ovs_mirror->external_ids,
> > +                                              "ovn-owned", false);
> > +        if (!ovn_owned_mirror) {
> > +            continue;
> > +        }
> > +
> > +        struct ovn_mirror *m = ovn_mirror_find(&ovn_mirrors, 
> > ovs_mirror->name);
> > +        if (!m) {
> > +            m = ovn_mirror_create(ovs_mirror->name);
> > +            shash_add(&tmp_mirrors, ovs_mirror->name, m);
> > +        }
> > +        m->ovs_mirror = ovs_mirror;
> > +    }
> > +
> > +    if (shash_is_empty(&ovn_mirrors)) {
> > +        shash_destroy(&ovn_mirrors);
> > +        if (shash_is_empty(&tmp_mirrors)) {
> > +            shash_destroy(&tmp_mirrors);
> > +            return;
> > +        } else {
> > +            goto cleanup;
> > +        }
> > +    }

There is no need to have a shash map 'tmp_mirrors' to store the mirrors which
are in the local OVS db but not in SB DB.

Also there was a bug for the scenario where the user deletes the NB mirror.
In this case, ovn-controller was not deleting the OVS mirrors.  It's because
the OVS mirrors were  not added to the shash map 'ovn_mirrors' and
because of the above 'goto cleanup'.

So I fixed this issue,  updated the bulk mirror test to cover this scenario and
applied the patch with the below changes.

-------------------------------------------------------------------------------------------------------
diff --git a/controller/mirror.c b/controller/mirror.c
index 938ec35429..39d30adf0d 100644
--- a/controller/mirror.c
+++ b/controller/mirror.c
@@ -115,7 +115,7 @@ mirror_run(struct ovsdb_idl_txn *ovs_idl_txn,
         ovn_mirror_add(&ovn_mirrors, m);
     }

-    /* Iterate through ovs mirrors and add to the 'tmp_mirrors'. */
+    /* Iterate through ovs mirrors and add to the 'ovn_mirrors'. */
     const struct ovsrec_mirror *ovs_mirror;
     OVSREC_MIRROR_TABLE_FOR_EACH (ovs_mirror, ovs_mirror_table) {
         bool ovn_owned_mirror = smap_get_bool(&ovs_mirror->external_ids,
@@ -127,19 +127,14 @@ mirror_run(struct ovsdb_idl_txn *ovs_idl_txn,
         struct ovn_mirror *m = ovn_mirror_find(&ovn_mirrors, ovs_mirror->name);
         if (!m) {
             m = ovn_mirror_create(ovs_mirror->name);
-            shash_add(&tmp_mirrors, ovs_mirror->name, m);
+            ovn_mirror_add(&ovn_mirrors, m);
         }
         m->ovs_mirror = ovs_mirror;
     }

     if (shash_is_empty(&ovn_mirrors)) {
         shash_destroy(&ovn_mirrors);
-        if (shash_is_empty(&tmp_mirrors)) {
-            shash_destroy(&tmp_mirrors);
-            return;
-        } else {
-            goto cleanup;
-        }
+        return;
     }

     /* Iterate through the local bindings and if the local binding's 'pb' has
@@ -175,14 +170,6 @@ mirror_run(struct ovsdb_idl_txn *ovs_idl_txn,
     }

     shash_destroy(&ovn_mirrors);
-
-cleanup:
-    SHASH_FOR_EACH_SAFE (node, &tmp_mirrors) {
-        ovn_mirror_delete(node->data);
-        shash_delete(&tmp_mirrors, node);
-    }
-
-    shash_destroy(&tmp_mirrors);
 }

 /* Static functions. */
@@ -199,7 +186,7 @@ ovn_mirror_create(char *mirror_name)
 static void
 ovn_mirror_add(struct shash *ovn_mirrors, struct ovn_mirror *m)
 {
-    shash_add(ovn_mirrors, m->sb_mirror->name, m);
+    shash_add(ovn_mirrors, m->name, m);
 }

 static struct ovn_mirror *

diff --git a/controller/mirror.c b/controller/mirror.c
index 39d30adf0d..6657369665 100644
--- a/controller/mirror.c
+++ b/controller/mirror.c
@@ -218,12 +218,7 @@ ovn_mirror_add_lport(struct ovn_mirror *m, struct
local_binding *lbinding)
     m_lport->lbinding = lbinding;
     if (!strcmp(m->sb_mirror->filter, "from-lport")) {
         ovs_list_push_back(&m->mirror_src_lports, &m_lport->list_node);
-    } else if (!strcmp(m->sb_mirror->filter, "to-lport")) {
-        ovs_list_push_back(&m->mirror_dst_lports, &m_lport->list_node);
     } else {
-        ovs_list_push_back(&m->mirror_src_lports, &m_lport->list_node);
-        m_lport = xzalloc(sizeof *m_lport);
-        m_lport->lbinding = lbinding;
         ovs_list_push_back(&m->mirror_dst_lports, &m_lport->list_node);
     }
 }

diff --git a/tests/ovn.at b/tests/ovn.at
index 305a0e45d5..af7810b130 100644
--- a/tests/ovn.at
+++ b/tests/ovn.at
@@ -16753,6 +16753,16 @@ OVS_WAIT_UNTIL([test "gre" = "$type"])
 index=$(ovs-vsctl get Interface ovn-mirror0 options:key | grep 123 | wc -l)
 OVS_WAIT_UNTIL([test "1" = "$index"])

+# pause ovn-controller and delete the mirror and make sure that
+# ovn-controller deletes the ovs mirror when it is resumed.
+
+check as hv1 ovn-appctl -t ovn-controller debug/pause
+check ovn-nbctl --wait=sb mirror-del
+check as hv1 ovn-appctl -t ovn-controller debug/resume
+check ovn-nbctl --wait=hv sync
+
+AT_CHECK([as hv1 ovs-vsctl list mirror], [0], [])
+
 OVN_CLEANUP([hv1],[hv2])
 AT_CLEANUP
 ])
diff --git a/NEWS b/NEWS
index 566d6731b3..d46f049435 100644
--- a/NEWS
+++ b/NEWS
@@ -10,7 +10,7 @@ OVN v22.12.0 - xx xxx xxxx
     per-flow IPFIX sampling.
   - Add support for component templates within logical flows and load
     balancers.
-  - Added Support for Remote Port Mirroring.
+  - Add support for remote port mirroring (Experimental).

 OVN v22.09.0 - 16 Sep 2022
 
--------------------------------------------------------------------------------------



> > +
> > +    /* Iterate through the local bindings and if the local binding's 'pb' 
> > has
> > +     * mirrors associated, add it to the ovn_mirror. */
> > +    struct shash_node *node;
> > +    SHASH_FOR_EACH (node, local_bindings) {
> > +        struct local_binding *lbinding = node->data;
> > +        const struct sbrec_port_binding *pb =
> > +            local_binding_get_primary_pb(local_bindings, lbinding->name);
> > +        if (!pb || !pb->n_mirror_rules) {
> > +            continue;
> > +        }
> > +
> > +        for (size_t i = 0; i < pb->n_mirror_rules; i++) {
> > +            struct ovn_mirror *m = ovn_mirror_find(&ovn_mirrors,
> > +                                                   
> > pb->mirror_rules[i]->name);
> > +            ovs_assert(m);
> > +            ovn_mirror_add_lport(m, lbinding);
> > +        }
> > +    }
> > +
> > +    /* Iterate through the built 'ovn_mirrors' and
> > +     * sync with the local ovsdb i.e.
> > +     * create/update or delete the ovsrec mirror(s). */
> > +     SHASH_FOR_EACH (node, &ovn_mirrors) {
> > +        struct ovn_mirror *m = node->data;
> > +        sync_ovn_mirror(m, ovs_idl_txn, br_int);
> > +     }
> > +
> > +    SHASH_FOR_EACH_SAFE (node, &ovn_mirrors) {
> > +        ovn_mirror_delete(node->data);
> > +        shash_delete(&ovn_mirrors, node);
> > +    }
> > +
> > +    shash_destroy(&ovn_mirrors);
> > +
> > +cleanup:
> > +    SHASH_FOR_EACH_SAFE (node, &tmp_mirrors) {
> > +        ovn_mirror_delete(node->data);
> > +        shash_delete(&tmp_mirrors, node);
> > +    }
> > +
> > +    shash_destroy(&tmp_mirrors);
> > +}
> > +
> > +/* Static functions. */
> > +static struct ovn_mirror *
> > +ovn_mirror_create(char *mirror_name)
> > +{
> > +    struct ovn_mirror *m = xzalloc(sizeof *m);
> > +    m->name = xstrdup(mirror_name);
> > +    ovs_list_init(&m->mirror_src_lports);
> > +    ovs_list_init(&m->mirror_dst_lports);
> > +    return m;
> > +}
> > +
> > +static void
> > +ovn_mirror_add(struct shash *ovn_mirrors, struct ovn_mirror *m)
> > +{
> > +    shash_add(ovn_mirrors, m->sb_mirror->name, m);
> > +}
> > +
> > +static struct ovn_mirror *
> > +ovn_mirror_find(struct shash *ovn_mirrors, const char *mirror_name)
> > +{
> > +    return shash_find_data(ovn_mirrors, mirror_name);
> > +}
> > +
> > +static void
> > +ovn_mirror_delete(struct ovn_mirror *m)
> > +{
> > +    free(m->name);
> > +    struct mirror_lport *m_lport;
> > +    LIST_FOR_EACH_POP (m_lport, list_node, &m->mirror_src_lports) {
> > +        free(m_lport);
> > +    }
> > +
> > +    LIST_FOR_EACH_POP (m_lport, list_node, &m->mirror_dst_lports) {
> > +        free(m_lport);
> > +    }
> > +
> > +    free(m);
> > +}
> > +
> > +static void
> > +ovn_mirror_add_lport(struct ovn_mirror *m, struct local_binding *lbinding)
> > +{
> > +    struct mirror_lport *m_lport = xzalloc(sizeof *m_lport);
> > +    m_lport->lbinding = lbinding;
> > +    if (!strcmp(m->sb_mirror->filter, "from-lport")) {
> > +        ovs_list_push_back(&m->mirror_src_lports, &m_lport->list_node);
> > +    } else if (!strcmp(m->sb_mirror->filter, "to-lport")) {
> > +        ovs_list_push_back(&m->mirror_dst_lports, &m_lport->list_node);
> > +    } else {
> > +        ovs_list_push_back(&m->mirror_src_lports, &m_lport->list_node);
> > +        m_lport = xzalloc(sizeof *m_lport);
> > +        m_lport->lbinding = lbinding;
> > +        ovs_list_push_back(&m->mirror_dst_lports, &m_lport->list_node);
>
> This branch seems to be a remnant from when the series supported
> "both" filter. Should be removed until we are ready to introduce
> support for "both" filter.

Ack.  Fixed this before applying.

Thanks
Numan

>
> > +    }
> > +}
> > +
> > +static void
> > +set_mirror_iface_options(struct ovsrec_interface *iface,
> > +                         const struct sbrec_mirror *sb_mirror)
> > +{
> > +    struct smap options = SMAP_INITIALIZER(&options);
> > +    char *key;
> > +
> > +    key = xasprintf("%ld", (long int) sb_mirror->index);
> > +    smap_add(&options, "remote_ip", sb_mirror->sink);
> > +    smap_add(&options, "key", key);
> > +    if (!strcmp(sb_mirror->type, "erspan")) {
> > +        /* Set the ERSPAN index */
> > +        smap_add(&options, "erspan_idx", key);
> > +        smap_add(&options, "erspan_ver", "1");
> > +    }
> > +    ovsrec_interface_set_options(iface, &options);
> > +
> > +    free(key);
> > +    smap_destroy(&options);
> > +}
> > +
> > +static void
> > +check_and_update_interface_table(const struct sbrec_mirror *sb_mirror,
> > +                                 const struct ovsrec_mirror *ovs_mirror)
> > +{
> > +    char *type;
> > +    struct ovsrec_interface *iface =
> > +                          ovs_mirror->output_port->interfaces[0];
> > +    struct smap *opts = &iface->options;
> > +    const char *erspan_ver = smap_get(opts, "erspan_ver");
> > +    if (erspan_ver) {
> > +        type = "erspan";
> > +    } else {
> > +        type = "gre";
> > +    }
> > +    if (strcmp(type, sb_mirror->type)) {
> > +        ovsrec_interface_set_type(iface, sb_mirror->type);
> > +    }
> > +    set_mirror_iface_options(iface, sb_mirror);
> > +}
> > +
> > +static void
> > +sync_ovn_mirror(struct ovn_mirror *m, struct ovsdb_idl_txn *ovs_idl_txn,
> > +                const struct ovsrec_bridge *br_int)
> > +{
> > +    if (should_delete_ovs_mirror(m)) {
> > +        /* Delete the ovsrec mirror. */
> > +        delete_ovs_mirror(m, br_int);
> > +        return;
> > +    }
> > +
> > +    if (ovs_list_is_empty(&m->mirror_src_lports) &&
> > +            ovs_list_is_empty(&m->mirror_dst_lports)) {
> > +        /* Nothing to do. */
> > +        return;
> > +    }
> > +
> > +    if (m->sb_mirror && !m->ovs_mirror) {
> > +        create_ovs_mirror(m, ovs_idl_txn, br_int);
> > +    } else {
> > +        check_and_update_interface_table(m->sb_mirror, m->ovs_mirror);
> > +    }
> > +
> > +    sync_ovs_mirror_ports(m, br_int);
> > +}
> > +
> > +static bool
> > +should_delete_ovs_mirror(struct ovn_mirror *m)
> > +{
> > +    if (!m->ovs_mirror) {
> > +        return false;
> > +    }
> > +
> > +    if (m->ovs_mirror && !m->sb_mirror) {
> > +        return true;
> > +    }
> > +
> > +    return (ovs_list_is_empty(&m->mirror_src_lports) &&
> > +            ovs_list_is_empty(&m->mirror_dst_lports));
> > +}
> > +
> > +static const struct ovsrec_port *
> > +get_iface_port(const struct ovsrec_interface *iface,
> > +               const struct ovsrec_bridge *br_int)
> > +{
> > +    for (size_t i = 0; i < br_int->n_ports; i++) {
> > +        const struct ovsrec_port *p = br_int->ports[i];
> > +        for (size_t j = 0; j < p->n_interfaces; j++) {
> > +            if (!strcmp(iface->name, p->interfaces[j]->name)) {
> > +                return p;
> > +            }
> > +        }
> > +    }
> > +    return NULL;
> > +}
> > +
> > +static void
> > +create_ovs_mirror(struct ovn_mirror *m, struct ovsdb_idl_txn *ovs_idl_txn,
> > +                  const struct ovsrec_bridge *br_int)
> > +{
> > +    struct ovsrec_interface *iface = ovsrec_interface_insert(ovs_idl_txn);
> > +    char *port_name = xasprintf("ovn-%s", m->name);
> > +
> > +    ovsrec_interface_set_name(iface, port_name);
> > +    ovsrec_interface_set_type(iface, m->sb_mirror->type);
> > +    set_mirror_iface_options(iface, m->sb_mirror);
> > +
> > +    struct ovsrec_port *port = ovsrec_port_insert(ovs_idl_txn);
> > +    ovsrec_port_set_name(port, port_name);
> > +    ovsrec_port_set_interfaces(port, &iface, 1);
> > +    ovsrec_bridge_update_ports_addvalue(br_int, port);
> > +
> > +    free(port_name);
> > +
> > +    m->ovs_mirror = ovsrec_mirror_insert(ovs_idl_txn);
> > +    ovsrec_mirror_set_name(m->ovs_mirror, m->name);
> > +    ovsrec_mirror_set_output_port(m->ovs_mirror, port);
> > +
> > +    const struct smap external_ids =
> > +        SMAP_CONST1(&external_ids, "ovn-owned", "true");
> > +    ovsrec_mirror_set_external_ids(m->ovs_mirror, &external_ids);
> > +
> > +    ovsrec_bridge_update_mirrors_addvalue(br_int, m->ovs_mirror);
> > +}
> > +
> > +static void
> > +sync_ovs_mirror_ports(struct ovn_mirror *m, const struct ovsrec_bridge 
> > *br_int)
> > +{
> > +    struct mirror_lport *m_lport;
> > +
> > +    if (ovs_list_is_empty(&m->mirror_src_lports)) {
> > +        ovsrec_mirror_set_select_src_port(m->ovs_mirror, NULL, 0);
> > +    } else {
> > +        size_t n_lports = ovs_list_size(&m->mirror_src_lports);
> > +        struct ovsrec_port **ovs_ports = xmalloc(sizeof *ovs_ports * 
> > n_lports);
> > +
> > +        size_t i = 0;
> > +        LIST_FOR_EACH (m_lport, list_node, &m->mirror_src_lports) {
> > +            const struct ovsrec_port *p =
> > +                get_iface_port(m_lport->lbinding->iface, br_int);
> > +            ovs_assert(p);
> > +            ovs_ports[i++] = (struct ovsrec_port *) p;
> > +        }
> > +
> > +        ovsrec_mirror_set_select_src_port(m->ovs_mirror, ovs_ports, 
> > n_lports);
> > +        free(ovs_ports);
> > +    }
> > +
> > +    if (ovs_list_is_empty(&m->mirror_dst_lports)) {
> > +        ovsrec_mirror_set_select_dst_port(m->ovs_mirror, NULL, 0);
> > +    } else {
> > +        size_t n_lports = ovs_list_size(&m->mirror_dst_lports);
> > +        struct ovsrec_port **ovs_ports = xmalloc(sizeof *ovs_ports * 
> > n_lports);
> > +
> > +        size_t i = 0;
> > +        LIST_FOR_EACH (m_lport, list_node, &m->mirror_dst_lports) {
> > +            const struct ovsrec_port *p =
> > +                get_iface_port(m_lport->lbinding->iface, br_int);
> > +            ovs_assert(p);
> > +            ovs_ports[i++] = (struct ovsrec_port *) p;
> > +        }
> > +
> > +        ovsrec_mirror_set_select_dst_port(m->ovs_mirror, ovs_ports, 
> > n_lports);
> > +        free(ovs_ports);
> > +    }
> > +}
> > +
> > +static void
> > +delete_ovs_mirror(struct ovn_mirror *m, const struct ovsrec_bridge *br_int)
> > +{
> > +    ovsrec_bridge_update_ports_delvalue(br_int, 
> > m->ovs_mirror->output_port);
> > +    ovsrec_bridge_update_mirrors_delvalue(br_int, m->ovs_mirror);
> > +    ovsrec_port_delete(m->ovs_mirror->output_port);
> > +    ovsrec_mirror_delete(m->ovs_mirror);
> > +}
> > diff --git a/controller/mirror.h b/controller/mirror.h
> > new file mode 100644
> > index 000000000..a79de109d
> > --- /dev/null
> > +++ b/controller/mirror.h
> > @@ -0,0 +1,33 @@
> > +/* Copyright (c) 2022 Red Hat, Inc.
> > + *
> > + * Licensed under the Apache License, Version 2.0 (the "License");
> > + * you may not use this file except in compliance with the License.
> > + * You may obtain a copy of the License at:
> > + *
> > + *     http://www.apache.org/licenses/LICENSE-2.0
> > + *
> > + * Unless required by applicable law or agreed to in writing, software
> > + * distributed under the License is distributed on an "AS IS" BASIS,
> > + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
> > + * See the License for the specific language governing permissions and
> > + * limitations under the License.
> > + */
> > +
> > +#ifndef OVN_MIRROR_H
> > +#define OVN_MIRROR_H 1
> > +
> > +struct ovsdb_idl_txn;
> > +struct ovsrec_mirror_table;
> > +struct sbrec_mirror_table;
> > +struct ovsrec_bridge;
> > +struct shash;
> > +
> > +void mirror_register_ovs_idl(struct ovsdb_idl *);
> > +void mirror_init(void);
> > +void mirror_destroy(void);
> > +void mirror_run(struct ovsdb_idl_txn *ovs_idl_txn,
> > +                const struct ovsrec_mirror_table *,
> > +                const struct sbrec_mirror_table *,
> > +                const struct ovsrec_bridge *,
> > +                struct shash *local_bindings);
> > +#endif
> > diff --git a/controller/ovn-controller.c b/controller/ovn-controller.c
> > index f0fd24820..470891bb2 100644
> > --- a/controller/ovn-controller.c
> > +++ b/controller/ovn-controller.c
> > @@ -78,6 +78,7 @@
> >  #include "lib/inc-proc-eng.h"
> >  #include "lib/ovn-l7.h"
> >  #include "hmapx.h"
> > +#include "mirror.h"
> >
> >  VLOG_DEFINE_THIS_MODULE(main);
> >
> > @@ -1004,6 +1005,7 @@ ctrl_register_ovs_idl(struct ovsdb_idl *ovs_idl)
> >      ovsdb_idl_track_add_column(ovs_idl, &ovsrec_port_col_name);
> >      ovsdb_idl_track_add_column(ovs_idl, &ovsrec_port_col_interfaces);
> >      ovsdb_idl_track_add_column(ovs_idl, &ovsrec_port_col_external_ids);
> > +    mirror_register_ovs_idl(ovs_idl);
> >  }
> >
> >  #define SB_NODES \
> > @@ -3926,6 +3928,7 @@ main(int argc, char *argv[])
> >      patch_init();
> >      pinctrl_init();
> >      lflow_init();
> > +    mirror_init();
> >      vif_plug_provider_initialize();
> >
> >      /* Connect to OVS OVSDB instance. */
> > @@ -4638,6 +4641,11 @@ main(int argc, char *argv[])
> >                                      &runtime_data->local_active_ports_ras);
> >                          stopwatch_stop(PINCTRL_RUN_STOPWATCH_NAME,
> >                                         time_msec());
> > +                        mirror_run(ovs_idl_txn,
> > +                                   
> > ovsrec_mirror_table_get(ovs_idl_loop.idl),
> > +                                   
> > sbrec_mirror_table_get(ovnsb_idl_loop.idl),
> > +                                   br_int,
> > +                                   &runtime_data->lbinding_data.bindings);
> >                          /* Updating monitor conditions if runtime data or
> >                           * logical datapath goups changed. */
> >                          if (engine_node_changed(&en_runtime_data)
> > @@ -4884,6 +4892,7 @@ loop_done:
> >      pinctrl_destroy();
> >      binding_destroy();
> >      patch_destroy();
> > +    mirror_destroy();
> >      if_status_mgr_destroy(if_mgr);
> >      shash_destroy(&vif_plug_deleted_iface_ids);
> >      shash_destroy(&vif_plug_changed_iface_ids);
> > diff --git a/tests/ovn.at b/tests/ovn.at
> > index f3bd53242..305a0e45d 100644
> > --- a/tests/ovn.at
> > +++ b/tests/ovn.at
> > @@ -16242,6 +16242,520 @@ OVN_CLEANUP([hv1], [hv2])
> >  AT_CLEANUP
> >  ])
> >
> > +OVN_FOR_EACH_NORTHD([
> > +AT_SETUP([Mirror])
> > +AT_KEYWORDS([Mirror])
> > +ovn_start
> > +
> > +# Logical network:
> > +# One LR - R1 has switch ls1 (191.168.1.0/24) connected to it,
> > +# and has switch ls2 (172.16.1.0/24) connected to it.
> > +
> > +ovn-nbctl lr-add R1
> > +
> > +ovn-nbctl ls-add ls1
> > +ovn-nbctl ls-add ls2
> > +
> > +# Connect ls1 to R1
> > +ovn-nbctl lrp-add R1 ls1 00:00:00:01:02:f1 192.168.1.1/24
> > +ovn-nbctl lsp-add ls1 rp-ls1 -- set Logical_Switch_Port rp-ls1 \
> > +    type=router options:router-port=ls1 addresses=\"00:00:00:01:02:f1\"
> > +
> > +# Connect ls2 to R1
> > +ovn-nbctl lrp-add R1 ls2 00:00:00:01:02:f2 172.16.1.1/24
> > +ovn-nbctl lsp-add ls2 rp-ls2 -- set Logical_Switch_Port rp-ls2 \
> > +    type=router options:router-port=ls2 addresses=\"00:00:00:01:02:f2\"
> > +
> > +# Create logical port ls1-lp1 in ls1
> > +ovn-nbctl lsp-add ls1 ls1-lp1 \
> > +-- lsp-set-addresses ls1-lp1 "00:00:00:01:02:03 192.168.1.2"
> > +
> > +# Create logical port ls2-lp1 in ls2
> > +ovn-nbctl lsp-add ls2 ls2-lp1 \
> > +-- lsp-set-addresses ls2-lp1 "00:00:00:01:02:04 172.16.1.2"
> > +
> > +ovn-nbctl lsp-add ls1 ln-public
> > +ovn-nbctl lsp-set-type ln-public localnet
> > +ovn-nbctl lsp-set-addresses ln-public unknown
> > +ovn-nbctl lsp-set-options ln-public network_name=public
> > +
> > +# Create one hypervisor and create OVS ports corresponding to logical 
> > ports.
> > +net_add n1
> > +
> > +sim_add hv1
> > +as hv1
> > +ovs-vsctl add-br br-phys -- set bridge br-phys 
> > other-config:hwaddr=\"00:00:00:01:02:00\"
> > +ovn_attach n1 br-phys 192.168.1.11
> > +
> > +ovs-vsctl -- add-port br-int vif1 -- \
> > +    set interface vif1 external-ids:iface-id=ls1-lp1 \
> > +    options:tx_pcap=hv1/vif1-tx.pcap \
> > +    options:rxq_pcap=hv1/vif1-rx.pcap \
> > +    ofport-request=1
> > +
> > +ovs-vsctl -- add-port br-int vif2 -- \
> > +    set interface vif2 external-ids:iface-id=ls2-lp1 \
> > +    options:tx_pcap=hv1/vif2-tx.pcap \
> > +    options:rxq_pcap=hv1/vif2-rx.pcap \
> > +    ofport-request=1
> > +
> > +ovs-vsctl set open . external-ids:ovn-bridge-mappings=public:br-phys
> > +
> > +# Allow some time for ovn-northd and ovn-controller to catch up.
> > +wait_for_ports_up
> > +check ovn-nbctl --wait=hv sync
> > +ovn-nbctl dump-flows > sbflows
> > +AT_CAPTURE_FILE([sbflows])
> > +
> > +for i in 1 2; do
> > +    : > vif$i.expected
> > +done
> > +
> > +net_add n2
> > +
> > +sim_add hv2
> > +as hv2
> > +ovs-vsctl add-br br-phys -- set bridge br-phys 
> > other-config:hwaddr=\"00:00:00:02:02:00\"
> > +ovn_attach n2 br-phys 192.168.1.12
> > +
> > +OVN_POPULATE_ARP
> > +
> > +as hv1
> > +
> > +# test_ipv4_icmp_request INPORT ETH_SRC ETH_DST IPV4_SRC IPV4_DST 
> > IP_CHKSUM ICMP_CHKSUM [EXP_IP_CHKSUM EXP_ICMP_CHKSUM] ENCAP_TYPE FILTER
> > +#
> > +# Causes a packet to be received on INPORT.  The packet is an ICMPv4
> > +# request with ETH_SRC, ETH_DST, IPV4_SRC, IPV4_DST, IP_CHSUM and
> > +# ICMP_CHKSUM as specified.  If EXP_IP_CHKSUM and EXP_ICMP_CHKSUM are
> > +# provided, then it should be the ip and icmp checksums of the packet
> > +# responded; otherwise, no reply is expected.
> > +# In the absence of an ip checksum calculation helpers, this relies
> > +# on the caller to provide the checksums for the ip and icmp headers.
> > +# XXX This should be more systematic.
> > +#
> > +# INPORT is an lport number, e.g. 11 for vif11.
> > +# ETH_SRC and ETH_DST are each 12 hex digits.
> > +# IPV4_SRC and IPV4_DST are each 8 hex digits.
> > +# IP_CHSUM and ICMP_CHKSUM are each 4 hex digits.
> > +# EXP_IP_CHSUM and EXP_ICMP_CHKSUM are each 4 hex digits.
> > +# ENCAP_TYPE - gre or erspan
> > +# FILTER - Mirror Filter - to-lport / from-lport
> > +test_ipv4_icmp_request() {
> > +    local inport=$1 eth_src=$2 eth_dst=$3 ipv4_src=$4 ipv4_dst=$5 
> > ip_chksum=$6 icmp_chksum=$7
> > +    local exp_ip_chksum=$8 exp_icmp_chksum=$9 mirror_encap_type=${10} 
> > mirror_filter=${11}
> > +    shift; shift; shift; shift; shift; shift; shift
> > +    shift; shift; shift; shift;
> > +
> > +    # Use ttl to exercise section 4.2.2.9 of RFC1812
> > +    local ip_ttl=02
> > +    local icmp_id=5fbf
> > +    local icmp_seq=0001
> > +    local icmp_data=$(seq 1 56 | xargs printf "%02x")
> > +    local icmp_type_code_request=0800
> > +    local 
> > icmp_payload=${icmp_type_code_request}${icmp_chksum}${icmp_id}${icmp_seq}${icmp_data}
> > +    local 
> > packet=${eth_dst}${eth_src}08004500005400004000${ip_ttl}01${ip_chksum}${ipv4_src}${ipv4_dst}${icmp_payload}
> > +
> > +    as hv1 ovs-appctl netdev-dummy/receive vif$inport $packet
> > +
> > +    # Expect to receive the reply, if any. In same port where packet was 
> > sent.
> > +    # Note: src and dst fields are expected to be reversed.
> > +    local icmp_type_code_response=0000
> > +    local reply_icmp_ttl=fe
> > +    local 
> > reply_icmp_payload=${icmp_type_code_response}${exp_icmp_chksum}${icmp_id}${icmp_seq}${icmp_data}
> > +    local 
> > reply=${eth_src}${eth_dst}08004500005400004000${reply_icmp_ttl}01${exp_ip_chksum}${ipv4_dst}${ipv4_src}${reply_icmp_payload}
> > +    echo $reply >> vif$inport.expected
> > +    local remote_mac=000000020200
> > +    local local_mac=000000010200
> > +    if test ${mirror_encap_type} = "gre" ; then
> > +        local 
> > ipv4_gre=4500007e00004000402fb6e9c0a8010bc0a8010c2000655800000000
> > +        if test ${mirror_filter} = "to-lport" ; then
> > +            local mirror=${remote_mac}${local_mac}0800${ipv4_gre}${reply}
> > +        elif test ${mirror_filter} = "from-lport" ; then
> > +            local mirror=${remote_mac}${local_mac}0800${ipv4_gre}${packet}
> > +        fi
> > +    elif test ${mirror_encap_type} = "erspan" ; then
> > +        local ipv4_erspan=4500008600004000402fb6e1c0a8010bc0a8010c
> > +        local erspan_seq0=100088be000000001000000000000000
> > +        local erspan_seq1=100088be000000011000000000000000
> > +        if test ${mirror_filter} = "to-lport" ; then
> > +            local 
> > mirror=${remote_mac}${local_mac}0800${ipv4_erspan}${erspan_seq0}${reply}
> > +        elif test ${mirror_filter} = "from-lport" ; then
> > +            local 
> > mirror=${remote_mac}${local_mac}0800${ipv4_erspan}${erspan_seq1}${packet}
> > +        fi
> > +    fi
> > +    echo $mirror >> br-phys_n1.expected
> > +
> > +}
> > +
> > +# Set IPs
> > +rtr_l2_ip=$(ip_to_hex 172 16 1 1)
> > +l1_ip=$(ip_to_hex 192 168 1 2)
> > +
> > +check ovn-nbctl mirror-add mirror0 gre 0 to-lport 192.168.1.12
> > +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror0
> > +
> > +# Send ping packet and check for mirrored packet of the reply
> > +test_ipv4_icmp_request 1 000000010203 0000000102f1 $l1_ip $rtr_l2_ip 0000 
> > 8510 03ff 8d10 "gre" "to-lport"
> > +
> > +OVN_CHECK_PACKETS_CONTAIN([hv1/br-phys_n1-tx.pcap], [br-phys_n1.expected])
> > +OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected])
> > +
> > +as hv1 reset_pcap_file vif1 hv1/vif1
> > +as hv1 reset_pcap_file br-phys_n1 hv1/br-phys_n1
> > +rm -f br-phys_n1.expected
> > +rm -f vif1.expected
> > +
> > +check ovn-nbctl set mirror . type=erspan
> > +
> > +# Send ping packet and check for mirrored packet of the reply
> > +test_ipv4_icmp_request 1 000000010203 0000000102f1 $l1_ip $rtr_l2_ip 0000 
> > 8510 03ff 8d10 "erspan" "to-lport"
> > +
> > +OVN_CHECK_PACKETS_CONTAIN([hv1/br-phys_n1-tx.pcap], [br-phys_n1.expected])
> > +OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected])
> > +
> > +as hv1 reset_pcap_file vif1 hv1/vif1
> > +as hv1 reset_pcap_file br-phys_n1 hv1/br-phys_n1
> > +rm -f br-phys_n1.expected
> > +rm -f vif1.expected
> > +
> > +check ovn-nbctl set mirror . filter=from-lport
> > +
> > +# Send ping packet and check for mirrored packet of the request
> > +test_ipv4_icmp_request 1 000000010203 0000000102f1 $l1_ip $rtr_l2_ip 0000 
> > 8510 03ff 8d10 "erspan" "from-lport"
> > +
> > +OVN_CHECK_PACKETS_CONTAIN([hv1/br-phys_n1-tx.pcap], [br-phys_n1.expected])
> > +OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected])
> > +
> > +as hv1 reset_pcap_file vif1 hv1/vif1
> > +as hv1 reset_pcap_file br-phys_n1 hv1/br-phys_n1
> > +rm -f br-phys_n1.expected
> > +rm -f vif1.expected
> > +
> > +check ovn-nbctl set mirror . type=gre
> > +
> > +# Send ping packet and check for mirrored packet of the request
> > +test_ipv4_icmp_request 1 000000010203 0000000102f1 $l1_ip $rtr_l2_ip 0000 
> > 8510 03ff 8d10 "gre" "from-lport"
> > +
> > +OVN_CHECK_PACKETS_CONTAIN([hv1/br-phys_n1-tx.pcap], [br-phys_n1.expected])
> > +OVN_CHECK_PACKETS([hv1/vif1-tx.pcap], [vif1.expected])
> > +
> > +echo "---------OVN NB Mirror-----"
> > +ovn-nbctl mirror-list
> > +
> > +echo "---------OVS Mirror----"
> > +ovs-vsctl list Mirror
> > +
> > +echo "-----------------------"
> > +
> > +echo "Verifying Mirror deletion in OVS"
> > +# Set vif1 iface-id such that OVN releases port binding
> > +check ovs-vsctl set interface vif1 external_ids:iface-id=foo
> > +check ovn-nbctl --wait=hv sync
> > +
> > +AT_CHECK([as hv1 ovs-vsctl list Mirror], [0], [dnl
> > +])
> > +
> > +# Set vif1 iface-id back to ls1-lp1
> > +check ovs-vsctl set interface vif1 external_ids:iface-id=ls1-lp1
> > +check ovn-nbctl --wait=hv sync
> > +
> > +OVS_WAIT_UNTIL([test $(as hv1 ovs-vsctl get Mirror mirror0 name) = 
> > "mirror0"])
> > +
> > +# Delete vif1 so that OVN releases port binding
> > +check ovs-vsctl del-port br-int vif1
> > +check ovn-nbctl --wait=hv sync
> > +
> > +OVS_WAIT_UNTIL([test 0 = $(as hv1 ovs-vsctl list Mirror | wc -l)])
> > +
> > +OVN_CLEANUP([hv1], [hv2])
> > +AT_CLEANUP
> > +])
> > +
> > +OVN_FOR_EACH_NORTHD([
> > +AT_SETUP([Mirror test bulk updates])
> > +AT_KEYWORDS([Mirror test bulk updates])
> > +ovn_start
> > +
> > +# Logical network:
> > +# One LR - R1 has switch ls1 (191.168.1.0/24) connected to it,
> > +# and has switch ls2 (172.16.1.0/24) connected to it.
> > +
> > +ovn-nbctl lr-add R1
> > +
> > +ovn-nbctl ls-add ls1
> > +ovn-nbctl ls-add ls2
> > +
> > +# Connect ls1 to R1
> > +ovn-nbctl lrp-add R1 ls1 00:00:00:01:02:f1 192.168.1.1/24
> > +ovn-nbctl lsp-add ls1 rp-ls1 -- set Logical_Switch_Port rp-ls1 \
> > +    type=router options:router-port=ls1 addresses=\"00:00:00:01:02:f1\"
> > +
> > +# Connect ls2 to R1
> > +ovn-nbctl lrp-add R1 ls2 00:00:00:01:02:f2 172.16.1.1/24
> > +ovn-nbctl lsp-add ls2 rp-ls2 -- set Logical_Switch_Port rp-ls2 \
> > +    type=router options:router-port=ls2 addresses=\"00:00:00:01:02:f2\"
> > +
> > +# Create logical port ls1-lp1 in ls1
> > +ovn-nbctl lsp-add ls1 ls1-lp1 \
> > +-- lsp-set-addresses ls1-lp1 "00:00:00:01:02:03 192.168.1.2"
> > +
> > +# Create logical port ls1-lp2 in ls1
> > +ovn-nbctl lsp-add ls1 ls1-lp2 \
> > +-- lsp-set-addresses ls1-lp2 "00:00:00:01:03:03 192.168.1.3"
> > +
> > +# Create logical port ls2-lp1 in ls2
> > +ovn-nbctl lsp-add ls2 ls2-lp1 \
> > +-- lsp-set-addresses ls2-lp1 "00:00:00:01:02:04 172.16.1.2"
> > +
> > +# Create logical port ls2-lp2 in ls2
> > +ovn-nbctl lsp-add ls2 ls2-lp2 \
> > +-- lsp-set-addresses ls2-lp2 "00:00:00:01:03:04 172.16.1.3"
> > +
> > +ovn-nbctl lsp-add ls1 ln-public
> > +ovn-nbctl lsp-set-type ln-public localnet
> > +ovn-nbctl lsp-set-addresses ln-public unknown
> > +ovn-nbctl lsp-set-options ln-public network_name=public
> > +
> > +# Create 2 hypervisors and create OVS ports corresponding to logical ports 
> > for hv1.
> > +net_add n1
> > +
> > +sim_add hv1
> > +as hv1
> > +ovs-vsctl add-br br-phys -- set bridge br-phys 
> > other-config:hwaddr=\"00:00:00:01:02:00\"
> > +ovn_attach n1 br-phys 192.168.1.11
> > +
> > +net_add n2
> > +
> > +sim_add hv2
> > +as hv2
> > +ovs-vsctl add-br br-phys -- set bridge br-phys 
> > other-config:hwaddr=\"00:00:00:02:02:00\"
> > +ovn_attach n2 br-phys 192.168.1.12
> > +
> > +OVN_POPULATE_ARP
> > +
> > +as hv1
> > +
> > +ovs-vsctl -- add-port br-int vif1 -- \
> > +    set interface vif1 external-ids:iface-id=ls1-lp1
> > +
> > +ovs-vsctl -- add-port br-int vif2 -- \
> > +    set interface vif2 external-ids:iface-id=ls2-lp1
> > +
> > +ovs-vsctl -- add-port br-int vif3 -- \
> > +    set interface vif3 external-ids:iface-id=ls1-lp2
> > +
> > +ovs-vsctl -- add-port br-int vif4 -- \
> > +    set interface vif4 external-ids:iface-id=ls2-lp2
> > +
> > +ovs-vsctl set open . external-ids:ovn-bridge-mappings=public:br-phys
> > +
> > +# Allow some time for ovn-northd and ovn-controller to catch up.
> > +wait_for_ports_up
> > +check ovn-nbctl --wait=hv sync
> > +ovn-nbctl dump-flows > sbflows
> > +AT_CAPTURE_FILE([sbflows])
> > +
> > +check ovn-nbctl mirror-add mirror0 gre 0 to-lport 192.168.1.12
> > +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror0
> > +check ovn-nbctl --wait=hv sync
> > +origA=$(ovs-vsctl get Mirror mirror0 select_dst_port)
> > +check ovn-nbctl lsp-attach-mirror ls2-lp1 mirror0
> > +
> > +check ovn-nbctl mirror-add mirror1 gre 1 to-lport 192.168.1.12
> > +check ovn-nbctl lsp-attach-mirror ls1-lp2 mirror1
> > +check ovn-nbctl --wait=hv sync
> > +origB=$(ovs-vsctl get Mirror mirror1 select_dst_port)
> > +check ovn-nbctl lsp-attach-mirror ls2-lp2 mirror1
> > +
> > +check ovn-nbctl --wait=hv sync
> > +
> > +as hv1
> > +orig1=$(ovs-vsctl get Mirror mirror0 select_dst_port)
> > +orig2=$(ovs-vsctl get Mirror mirror1 select_dst_port)
> > +
> > +check ovn-nbctl mirror-del
> > +check ovn-nbctl --wait=hv sync
> > +
> > +check ovn-nbctl mirror-add mirror0 gre 0 to-lport 192.168.1.12
> > +check ovn-nbctl mirror-add mirror1 gre 1 to-lport 192.168.1.12
> > +
> > +# Attaches multiple mirrors
> > +
> > +check as hv1 ovn-appctl -t ovn-controller debug/pause
> > +check ovn-nbctl lsp-attach-mirror ls2-lp1 mirror0
> > +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror0
> > +check ovn-nbctl lsp-attach-mirror ls2-lp2 mirror1
> > +check ovn-nbctl lsp-attach-mirror ls1-lp2 mirror1
> > +check as hv1 ovn-appctl -t ovn-controller debug/resume
> > +
> > +check ovn-nbctl --wait=hv sync
> > +
> > +as hv1
> > +new1=$(ovs-vsctl get Mirror mirror0 select_dst_port)
> > +new2=$(ovs-vsctl get Mirror mirror1 select_dst_port)
> > +
> > +AT_CHECK([test "$orig1" = "$new1"], [0], [])
> > +
> > +AT_CHECK([test "$orig2" = "$new2"], [0], [])
> > +
> > +# Equal detaches and attaches
> > +check as hv1 ovn-appctl -t ovn-controller debug/pause
> > +check ovn-nbctl lsp-detach-mirror ls1-lp1 mirror0
> > +check ovn-nbctl lsp-detach-mirror ls2-lp1 mirror0
> > +check ovn-nbctl lsp-attach-mirror ls1-lp2 mirror0
> > +check ovn-nbctl lsp-attach-mirror ls2-lp2 mirror0
> > +
> > +check ovn-nbctl lsp-detach-mirror ls1-lp2 mirror1
> > +check ovn-nbctl lsp-detach-mirror ls2-lp2 mirror1
> > +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror1
> > +check ovn-nbctl lsp-attach-mirror ls2-lp1 mirror1
> > +check as hv1 ovn-appctl -t ovn-controller debug/resume
> > +
> > +check ovn-nbctl --wait=hv sync
> > +
> > +# Make sure that ovn-controller has not asserted.
> > +AT_CHECK([kill -0 $(cat hv1/ovn-controller.pid)])
> > +
> > +as hv1
> > +new1=$(ovs-vsctl get Mirror mirror0 select_dst_port)
> > +new2=$(ovs-vsctl get Mirror mirror1 select_dst_port)
> > +
> > +AT_CHECK([test "$orig1" = "$new2"], [0], [])
> > +
> > +AT_CHECK([test "$orig2" = "$new1"], [0], [])
> > +
> > +# Detaches more than attaches
> > +check as hv1 ovn-appctl -t ovn-controller debug/pause
> > +check ovn-nbctl lsp-detach-mirror ls1-lp1 mirror1
> > +check ovn-nbctl lsp-detach-mirror ls2-lp1 mirror1
> > +check ovn-nbctl lsp-detach-mirror ls1-lp2 mirror0
> > +check ovn-nbctl lsp-detach-mirror ls2-lp2 mirror0
> > +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror0
> > +check ovn-nbctl lsp-attach-mirror ls1-lp2 mirror1
> > +check as hv1 ovn-appctl -t ovn-controller debug/resume
> > +
> > +check ovn-nbctl --wait=hv sync
> > +# Make sure that ovn-controller has not asserted.
> > +AT_CHECK([kill -0 $(cat hv1/ovn-controller.pid)])
> > +
> > +as hv1
> > +new1=$(ovs-vsctl get Mirror mirror0 select_dst_port)
> > +new2=$(ovs-vsctl get Mirror mirror1 select_dst_port)
> > +
> > +AT_CHECK([test "$origA" = "$new1"], [0], [])
> > +
> > +AT_CHECK([test "$origB" = "$new2"], [0], [])
> > +
> > +# Attaches more than detaches
> > +check as hv1 ovn-appctl -t ovn-controller debug/pause
> > +check ovn-nbctl lsp-detach-mirror ls1-lp1 mirror0
> > +check ovn-nbctl lsp-detach-mirror ls1-lp2 mirror1
> > +check ovn-nbctl lsp-attach-mirror ls1-lp2 mirror0
> > +check ovn-nbctl lsp-attach-mirror ls2-lp2 mirror0
> > +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror1
> > +check ovn-nbctl lsp-attach-mirror ls2-lp1 mirror1
> > +check as hv1 ovn-appctl -t ovn-controller debug/resume
> > +
> > +check ovn-nbctl --wait=hv sync
> > +# Make sure that ovn-controller has not asserted.
> > +AT_CHECK([kill -0 $(cat hv1/ovn-controller.pid)])
> > +
> > +as hv1
> > +new1=$(ovs-vsctl get Mirror mirror0 select_dst_port)
> > +new2=$(ovs-vsctl get Mirror mirror1 select_dst_port)
> > +
> > +AT_CHECK([test "$orig1" = "$new2"], [0], [])
> > +
> > +AT_CHECK([test "$orig2" = "$new1"], [0], [])
> > +
> > +# Detaches all
> > +check as hv1 ovn-appctl -t ovn-controller debug/pause
> > +check ovn-nbctl lsp-detach-mirror ls1-lp2 mirror0
> > +check ovn-nbctl lsp-detach-mirror ls2-lp2 mirror0
> > +check ovn-nbctl lsp-detach-mirror ls1-lp1 mirror1
> > +check ovn-nbctl lsp-detach-mirror ls2-lp1 mirror1
> > +check as hv1 ovn-appctl -t ovn-controller debug/resume
> > +check ovn-nbctl --wait=hv sync
> > +
> > +OVS_WAIT_UNTIL([test 0 = $(as hv1 ovs-vsctl list Mirror | wc -l)])
> > +
> > +check ovn-nbctl mirror-add mirror2 gre 2 to-lport 192.168.1.12
> > +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror2
> > +check ovn-nbctl --wait=hv sync
> > +
> > +# Attaches SAME port to multiple mirrors
> > +# and detaches it from existing mirror.
> > +# More attach than detach
> > +
> > +check as hv1 ovn-appctl -t ovn-controller debug/pause
> > +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror0
> > +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror1
> > +check ovn-nbctl lsp-detach-mirror ls1-lp1 mirror2
> > +check as hv1 ovn-appctl -t ovn-controller debug/resume
> > +
> > +check ovn-nbctl --wait=hv sync
> > +
> > +as hv1
> > +new1=$(ovs-vsctl get Mirror mirror0 select_dst_port)
> > +new2=$(ovs-vsctl get Mirror mirror1 select_dst_port)
> > +
> > +AT_CHECK([test "$origA" = "$new1"], [0], [])
> > +AT_CHECK([test "$origA" = "$new2"], [0], [])
> > +OVS_WAIT_UNTIL([test 0 = $(as hv1 ovs-vsctl list Mirror mirror2 | wc -l)])
> > +
> > +check ovn-nbctl mirror-add mirror3 gre 3 to-lport 192.168.1.12
> > +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror3
> > +check ovn-nbctl --wait=hv sync
> > +
> > +# Detaches SAME port from multiple mirrors
> > +# and attaches it to existing mirror.
> > +# More detach than attach
> > +
> > +check as hv1 ovn-appctl -t ovn-controller debug/pause
> > +check ovn-nbctl lsp-detach-mirror ls1-lp1 mirror0
> > +check ovn-nbctl lsp-detach-mirror ls1-lp1 mirror3
> > +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror2
> > +check as hv1 ovn-appctl -t ovn-controller debug/resume
> > +
> > +check ovn-nbctl --wait=hv sync
> > +
> > +as hv1
> > +new1=$(ovs-vsctl get Mirror mirror1 select_dst_port)
> > +new2=$(ovs-vsctl get Mirror mirror2 select_dst_port)
> > +
> > +AT_CHECK([test "$origA" = "$new1"], [0], [])
> > +AT_CHECK([test "$origA" = "$new2"], [0], [])
> > +OVS_WAIT_UNTIL([test 0 = $(as hv1 ovs-vsctl list Mirror mirror0 | wc -l)])
> > +OVS_WAIT_UNTIL([test 0 = $(as hv1 ovs-vsctl list Mirror mirror3 | wc -l)])
> > +
> > +check ovn-nbctl mirror-del
> > +check ovn-nbctl --wait=hv sync
> > +check ovn-nbctl mirror-add mirror0 erspan 0 to-lport 192.168.1.12
> > +check ovn-nbctl lsp-attach-mirror ls1-lp1 mirror0
> > +check ovn-nbctl --wait=hv sync
> > +
> > +# Make sure different fields of mirror resource set from OVN
> > +# propagates to OVS correctly
> > +
> > +check as hv1 ovn-appctl -t ovn-controller debug/pause
> > +check ovn-nbctl set mirror . filter=from-lport
> > +check ovn-nbctl set mirror . type=gre
> > +check ovn-nbctl set mirror . index=123
> > +check as hv1 ovn-appctl -t ovn-controller debug/resume
> > +
> > +check ovn-nbctl --wait=hv sync
> > +
> > +as hv1
> > +new1=$(ovs-vsctl get Mirror mirror0 select_src_port)
> > +AT_CHECK([test "$origA" = "$new1"], [0], [])
> > +type=$(ovs-vsctl get Interface ovn-mirror0 type)
> > +OVS_WAIT_UNTIL([test "gre" = "$type"])
> > +index=$(ovs-vsctl get Interface ovn-mirror0 options:key | grep 123 | wc -l)
> > +OVS_WAIT_UNTIL([test "1" = "$index"])
> > +
> > +OVN_CLEANUP([hv1],[hv2])
> > +AT_CLEANUP
> > +])
> >
> >  OVN_FOR_EACH_NORTHD([
> >  AT_SETUP([Port Groups])
> > --
> > 2.31.1
> >
>

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


Reply via email to