[ovs-dev] [PATCH] ovn: Support for taas(tap-as-a-service) function

wang.qianyu at zte.com.cn wang.qianyu at zte.com.cn
Thu Aug 3 08:44:42 UTC 2017


Taas was designed to provide tenants and service providers a means of 
monitoring the traffic flowing in their Neutron provisioned virtual 
networks. It is useful for network trouble-shooting, security and 
analytics. The taas presentations could be found from 
https://github.com/openstack/tap-as-a-service/blob/master/doc/source/presentations.rst
, and the api reference could be found from 
https://github.com/openstack/tap-as-a-service/blob/master/API_REFERENCE.rst

To support taas function, this patch add a new logical switch 
"logica_mirror_switch" which represents a taas_service in ovn.
This patch also add logica_mirror_switch_port with type of "mirror" and 
"taas". port with type "mirror" is used as inport for monitor flow in 
logica_mirror_switch, and port with type "taas" is used as outport for 
monitor flow in logica_mirror_switch.

The ovn-controller will make the relation between the logical_switch_port 
and logica_mirror_switch_port.

Signed-off-by: wang qianyu <wang.qianyu at zte.com.cn>
---
 ovn/controller/binding.c        |  11 +-
 ovn/controller/ovn-controller.c |   4 +-
 ovn/controller/physical.c       | 125 +++++++++++++++-
 ovn/northd/ovn-northd.c         | 317 
++++++++++++++++++++++++++++++++++++----
 ovn/ovn-nb.ovsschema            |  42 +++++-
 ovn/ovn-nb.xml                  | 167 +++++++++++++++++++++
 ovn/ovn-sb.xml                  |  85 ++++++++++-
 ovn/utilities/ovn-nbctl.c       | 152 ++++++++++++++++++-
 ovn/utilities/ovn-trace.c       |   5 +-
 9 files changed, 865 insertions(+), 43 deletions(-)

diff --git a/ovn/controller/binding.c b/ovn/controller/binding.c
index a80671a..70b33f4 100644
--- a/ovn/controller/binding.c
+++ b/ovn/controller/binding.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015, 2016, 2017 Nicira, Inc.
+/* Copyright (c) 2015, 2016, 2017 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -432,6 +432,15 @@ consider_local_datapath(struct controller_ctx *ctx,
          * for them. */
         sset_add(local_lports, binding_rec->logical_port);
         our_chassis = false;
+    } else if (!strcmp(binding_rec->type, "mirror") ||
+               !strcmp(binding_rec->type, "taas")) {
+        const char *chassis_id = smap_get(&binding_rec->options,
+                                          "taas-chassis");
+        our_chassis = chassis_id && !strcmp(chassis_id, 
chassis_rec->name);
+
+        //sset_add(local_lports, binding_rec->logical_port);
+        add_local_datapath(ldatapaths, lports, binding_rec->datapath,
+                               false, local_datapaths);
     }
 
     if (ctx->ovnsb_idl_txn) {
diff --git a/ovn/controller/ovn-controller.c 
b/ovn/controller/ovn-controller.c
index da3e83a..31fd411 100644
--- a/ovn/controller/ovn-controller.c
+++ b/ovn/controller/ovn-controller.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015, 2016, 2017 Nicira, Inc.
+/* Copyright (c) 2015, 2016, 2017 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -150,6 +150,8 @@ update_sb_monitors(struct ovsdb_idl *ovnsb_idl,
     struct ovsdb_idl_condition mg = OVSDB_IDL_CONDITION_INIT(&mg);
     struct ovsdb_idl_condition dns = OVSDB_IDL_CONDITION_INIT(&dns);
     sbrec_port_binding_add_clause_type(&pb, OVSDB_F_EQ, "patch");
+    sbrec_port_binding_add_clause_type(&pb, OVSDB_F_EQ, "mirror");
+    sbrec_port_binding_add_clause_type(&pb, OVSDB_F_EQ, "taas");
     /* XXX: We can optimize this, if we find a way to only monitor
      * ports that have a Gateway_Chassis that point's to our own
      * chassis */
diff --git a/ovn/controller/physical.c b/ovn/controller/physical.c
index 719b020..dfe2a05 100644
--- a/ovn/controller/physical.c
+++ b/ovn/controller/physical.c
@@ -1,4 +1,4 @@
-/* Copyright (c) 2015, 2016, 2017 Nicira, Inc.
+/* Copyright (c) 2015, 2016, 2017 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -291,6 +291,59 @@ load_logical_ingress_metadata(const struct 
sbrec_port_binding *binding,
 }
 
 static void
+taas_port_handle(const struct simap *ct_zones,
+                 const struct lport_index *lports,
+                 const struct sbrec_port_binding *binding,
+                 struct ofpbuf *ofpacts_p,
+                 struct hmap *flow_table,
+                 uint32_t dp_key,
+                 uint32_t port_key)
+{
+    if (strlen(binding->logical_port) < strlen("taas-")) {
+        VLOG_INFO("logical_port is %s len lt taas-", 
binding->logical_port);
+        return;
+    }
+
+    char *logical_port_name = binding->logical_port + strlen("taas-");
+    const struct sbrec_port_binding *taas_port = lport_lookup_by_name(
+        lports, logical_port_name);
+    if (!taas_port) {
+        VLOG_INFO("can not find taas port %s in this switch",
+            logical_port_name);
+        return;
+    }
+
+    ofp_port_t ofport = u16_to_ofp(simap_get(&localvif_to_ofport,
+                                              logical_port_name));
+    if (!ofport) {
+        VLOG_INFO("can not find ofport of %s in this switch",
+            logical_port_name);
+        return;
+    }
+
+    /* Packets that arrive from a vif can belong to a VM or
+     * to a container located inside that VM. Packets that
+     * arrive from containers have a tag (vlan) associated with them.
+     */
+    struct zone_ids zone_ids = get_zone_ids(binding, ct_zones);
+    put_local_common_flows(dp_key, port_key, false, &zone_ids,
+                           ofpacts_p, flow_table);
+
+    /* Table 65, Priority 100.
+     * =======================
+     *
+     * Deliver the packet to the local vif. */
+     struct match match;
+    match_init_catchall(&match);
+    ofpbuf_clear(ofpacts_p);
+    match_set_metadata(&match, htonll(dp_key));
+    match_set_reg(&match, MFF_LOG_OUTPORT - MFF_REG0, port_key);
+    ofpact_put_OUTPUT(ofpacts_p)->port = ofport;
+    ofctrl_add_flow(flow_table, OFTABLE_LOG_TO_PHY, 100, 0,
+                    &match, ofpacts_p);
+}
+
+static void
 consider_port_binding(enum mf_field_id mff_ovn_geneve,
                       const struct simap *ct_zones,
                       const struct lport_index *lports,
@@ -360,6 +413,14 @@ consider_port_binding(enum mf_field_id 
mff_ovn_geneve,
         return;
     }
 
+    if (!strcmp(binding->type, "taas")
+        && binding->chassis == chassis) {
+
+        taas_port_handle(ct_zones, lports, binding, ofpacts_p, 
flow_table,
+                         dp_key, port_key);
+        return;
+    }
+
     struct ovs_list *gateway_chassis
         = gateway_chassis_get_ordered(binding, chassis_index);
 
@@ -531,13 +592,42 @@ consider_port_binding(enum mf_field_id 
mff_ovn_geneve,
         ofpbuf_clear(ofpacts_p);
         match_init_catchall(&match);
         match_set_in_port(&match, ofport);
+        if (tag || !strcmp(binding->type, "localnet")
+            || !strcmp(binding->type, "l2gateway")) {
+            match_set_dl_vlan(&match, htons(tag));
+        }
+
+        /* add mirror action of flow mirrored port in table 0 */
+        const char *mirror_port_name = smap_get(&binding->options,
+                                                "mirror-port");
+        const char *direction = smap_get(&binding->options,
+                                         "mirror-direction");
+        if (mirror_port_name && direction) {
+            const struct sbrec_port_binding *mirror_port
+            = lport_lookup_by_name(lports, mirror_port_name);
+            if (mirror_port &&
+                (!strcmp(direction, "from-port") ||
+                 !strcmp(direction, "all"))) {
+                size_t clone_ofs = ofpacts_p->size;
+                struct ofpact_nest *clone = ofpact_put_CLONE(ofpacts_p);
+
+                put_load(mirror_port->datapath->tunnel_key, 
MFF_LOG_DATAPATH,
+                    0, 64, ofpacts_p);
+                put_load(mirror_port->tunnel_key, MFF_LOG_INPORT, 0, 32,
+                    ofpacts_p);
+                put_resubmit(OFTABLE_LOG_INGRESS_PIPELINE, ofpacts_p);
+
+                clone = ofpbuf_at_assert(ofpacts_p, clone_ofs, sizeof 
*clone);
+                ofpacts_p->header = clone;
+                ofpact_finish_CLONE(ofpacts_p, &clone);
+            }
+        }
 
         /* Match a VLAN tag and strip it, including stripping priority 
tags
          * (e.g. VLAN ID 0).  In the latter case we'll add a second flow
          * for frames that lack any 802.1Q header later. */
         if (tag || !strcmp(binding->type, "localnet")
             || !strcmp(binding->type, "l2gateway")) {
-            match_set_dl_vlan(&match, htons(tag));
             if (nested_container) {
                 /* When a packet comes from a container sitting behind a
                  * parent_port, we should let it loopback to other 
containers
@@ -586,6 +676,37 @@ consider_port_binding(enum mf_field_id 
mff_ovn_geneve,
             vlan_vid->push_vlan_if_needed = true;
         }
         ofpact_put_OUTPUT(ofpacts_p)->port = ofport;
+
+        /* add mirror action of flow mirrored port in table 65 */
+        if (mirror_port_name && direction) {
+            const struct sbrec_port_binding *mirror_port
+            = lport_lookup_by_name(lports, mirror_port_name);
+            if (mirror_port &&
+                (!strcmp(direction, "to-port") || !strcmp(direction, 
"all"))) {
+                size_t clone_ofs = ofpacts_p->size;
+                struct ofpact_nest *clone = ofpact_put_CLONE(ofpacts_p);
+                ofpact_put_CT_CLEAR(ofpacts_p);
+                put_load(0, MFF_LOG_DNAT_ZONE, 0, 32, ofpacts_p);
+                put_load(0, MFF_LOG_SNAT_ZONE, 0, 32, ofpacts_p);
+                put_load(0, MFF_LOG_CT_ZONE, 0, 32, ofpacts_p);
+                put_load(0, MFF_LOG_FLAGS, 0, 32, ofpacts_p);
+                put_load(0, MFF_LOG_OUTPORT, 0, 32, ofpacts_p);
+                for (int i = 0; i < MFF_N_LOG_REGS; i++) {
+                    put_load(0, MFF_LOG_REG0 + i, 0, 32, ofpacts_p);
+                }
+
+                put_load(mirror_port->datapath->tunnel_key, 
MFF_LOG_DATAPATH,
+                    0, 64, ofpacts_p);
+                put_load(mirror_port->tunnel_key, MFF_LOG_INPORT,
+                    0, 32, ofpacts_p);
+                put_resubmit(OFTABLE_LOG_INGRESS_PIPELINE, ofpacts_p);
+
+                clone = ofpbuf_at_assert(ofpacts_p, clone_ofs, sizeof 
*clone);
+                ofpacts_p->header = clone;
+                ofpact_finish_CLONE(ofpacts_p, &clone);
+            }
+        }
+
         if (tag) {
             /* Revert the tag added to the packets headed to containers
              * in the previous step. If we don't do this, the packets
diff --git a/ovn/northd/ovn-northd.c b/ovn/northd/ovn-northd.c
index 10e0c7c..fac679f 100644
--- a/ovn/northd/ovn-northd.c
+++ b/ovn/northd/ovn-northd.c
@@ -1,4 +1,4 @@
-/*
+/*
  * 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:
@@ -80,7 +80,8 @@ enum ovn_pipeline {
 /* The two purposes for which ovn-northd uses OVN logical datapaths. */
 enum ovn_datapath_type {
     DP_SWITCH,                  /* OVN logical switch. */
-    DP_ROUTER                   /* OVN logical router. */
+    DP_ROUTER,                  /* OVN logical router. */
+    DP_MSWITCH                  /* OVN mirror switch. */
 };
 
 /* Returns an "enum ovn_stage" built from the arguments.
@@ -143,7 +144,16 @@ enum ovn_stage {
     PIPELINE_STAGE(ROUTER, OUT, UNDNAT,    0, "lr_out_undnat")        \
     PIPELINE_STAGE(ROUTER, OUT, SNAT,      1, "lr_out_snat")          \
     PIPELINE_STAGE(ROUTER, OUT, EGR_LOOP,  2, "lr_out_egr_loop")      \
-    PIPELINE_STAGE(ROUTER, OUT, DELIVERY,  3, "lr_out_delivery")
+    PIPELINE_STAGE(ROUTER, OUT, DELIVERY,  3, "lr_out_delivery")      \
+                                                                      \
+/* Logical mirror switch ingress stages. */                           \
+    PIPELINE_STAGE(MSWITCH, IN,  MIRROR_IN,   0, "lms_in_port")       \
+    PIPELINE_STAGE(MSWITCH, IN,  FLOW_FILTER, 1, "lms_in_flow_filter")\
+    PIPELINE_STAGE(MSWITCH, IN,  OUT_LK,      2, "lms_in_out_lk")     \
+                                                                      \
+    /* Logical mirror switch egress stages. */                        \
+    PIPELINE_STAGE(MSWITCH, OUT, FLOW_FILTER, 0, "lms_out_flow_filter")\
+    PIPELINE_STAGE(MSWITCH, OUT, DELIVERY,    1, "lms_out_delivery")
 
 #define PIPELINE_STAGE(DP_TYPE, PIPELINE, STAGE, TABLE, NAME)   \
     S_##DP_TYPE##_##PIPELINE##_##STAGE                          \
@@ -391,9 +401,10 @@ struct ovn_datapath {
     struct hmap_node key_node;  /* Index on 'key'. */
     struct uuid key;            /* (nbs/nbr)->header_.uuid. */
 
-    const struct nbrec_logical_switch *nbs;  /* May be NULL. */
-    const struct nbrec_logical_router *nbr;  /* May be NULL. */
-    const struct sbrec_datapath_binding *sb; /* May be NULL. */
+    const struct nbrec_logical_switch *nbs;          /* May be NULL. */
+    const struct nbrec_logical_router *nbr;          /* May be NULL. */
+    const struct nbrec_logical_mirror_switch *nbms;  /* May be NULL. */
+    const struct sbrec_datapath_binding *sb;         /* May be NULL. */
 
     struct ovs_list list;       /* In list of similar records. */
 
@@ -438,6 +449,7 @@ static struct ovn_datapath *
 ovn_datapath_create(struct hmap *datapaths, const struct uuid *key,
                     const struct nbrec_logical_switch *nbs,
                     const struct nbrec_logical_router *nbr,
+                    const struct nbrec_logical_mirror_switch *nbms,
                     const struct sbrec_datapath_binding *sb)
 {
     struct ovn_datapath *od = xzalloc(sizeof *od);
@@ -445,6 +457,7 @@ ovn_datapath_create(struct hmap *datapaths, const 
struct uuid *key,
     od->sb = sb;
     od->nbs = nbs;
     od->nbr = nbr;
+    od->nbms = nbms;
     hmap_init(&od->port_tnlids);
     od->port_key_hint = 0;
     hmap_insert(datapaths, &od->key_node, uuid_hash(&od->key));
@@ -473,7 +486,13 @@ ovn_datapath_destroy(struct hmap *datapaths, struct 
ovn_datapath *od)
 static enum ovn_datapath_type
 ovn_datapath_get_type(const struct ovn_datapath *od)
 {
-    return od->nbs ? DP_SWITCH : DP_ROUTER;
+    if (od->nbs) {
+        return DP_SWITCH;
+    } else if (od->nbr) {
+        return  DP_ROUTER;
+    } else {
+        return DP_MSWITCH;
+    }
 }
 
 static struct ovn_datapath *
@@ -496,7 +515,8 @@ ovn_datapath_from_sbrec(struct hmap *datapaths,
     struct uuid key;
 
     if (!smap_get_uuid(&sb->external_ids, "logical-switch", &key) &&
-        !smap_get_uuid(&sb->external_ids, "logical-router", &key)) {
+        !smap_get_uuid(&sb->external_ids, "logical-router", &key) &&
+        !smap_get_uuid(&sb->external_ids, "logical-taas-switch", &key)) {
         return NULL;
     }
     return ovn_datapath_find(datapaths, &key);
@@ -606,15 +626,33 @@ ovn_datapath_update_external_ids(struct ovn_datapath 
*od)
      * external-ids. */
     char uuid_s[UUID_LEN + 1];
     sprintf(uuid_s, UUID_FMT, UUID_ARGS(&od->key));
-    const char *key = od->nbs ? "logical-switch" : "logical-router";
+    const char *key;
+    if (od->nbs) {
+        key = "logical-switch";
+    } else if (od->nbr) {
+        key = "logical-router";
+    } else {
+        key = "logical-taas-switch";
+    }
 
     /* Get names to set in external-ids. */
-    const char *name = od->nbs ? od->nbs->name : od->nbr->name;
-    const char *name2 = (od->nbs
-                         ? smap_get(&od->nbs->external_ids,
-                                    "neutron:network_name")
-                         : smap_get(&od->nbr->external_ids,
-                                    "neutron:router_name"));
+    const char *name ;
+    if (od->nbs) {
+        name =  od->nbs->name;
+    } else if (od->nbr) {
+       name =  od->nbr->name;
+    } else {
+        name =  od->nbms->name;
+    }
+
+    const char *name2 ;
+    if (od->nbs) {
+        name2 =  smap_get(&od->nbs->external_ids, 
"neutron:network_name");
+    } else if (od->nbr) {
+       name2 =  smap_get(&od->nbr->external_ids, "neutron:router_name");
+    } else {
+        name2 = NULL;
+    }
 
     /* Set external-ids. */
     struct smap ids = SMAP_INITIALIZER(&ids);
@@ -641,12 +679,14 @@ join_datapaths(struct northd_context *ctx, struct 
hmap *datapaths,
     SBREC_DATAPATH_BINDING_FOR_EACH_SAFE (sb, sb_next, ctx->ovnsb_idl) {
         struct uuid key;
         if (!smap_get_uuid(&sb->external_ids, "logical-switch", &key) &&
-            !smap_get_uuid(&sb->external_ids, "logical-router", &key)) {
+            !smap_get_uuid(&sb->external_ids, "logical-router", &key) &&
+          !smap_get_uuid(&sb->external_ids, "logical-taas-switch", &key)) 
{
             ovsdb_idl_txn_add_comment(
                 ctx->ovnsb_txn,
                 "deleting Datapath_Binding "UUID_FMT" that lacks "
                 "external-ids:logical-switch and "
-                "external-ids:logical-router",
+                "external-ids:logical-router and"
+                "external-ids:logical-taas-switch",
                 UUID_ARGS(&sb->header_.uuid));
             sbrec_datapath_binding_delete(sb);
             continue;
@@ -663,7 +703,7 @@ join_datapaths(struct northd_context *ctx, struct hmap 
*datapaths,
         }
 
         struct ovn_datapath *od = ovn_datapath_create(datapaths, &key,
-                                                      NULL, NULL, sb);
+                                                      NULL, NULL, NULL, 
sb);
         ovs_list_push_back(sb_only, &od->list);
     }
 
@@ -678,13 +718,28 @@ join_datapaths(struct northd_context *ctx, struct 
hmap *datapaths,
             ovn_datapath_update_external_ids(od);
         } else {
             od = ovn_datapath_create(datapaths, &nbs->header_.uuid,
-                                     nbs, NULL, NULL);
+                                     nbs, NULL, NULL, NULL);
             ovs_list_push_back(nb_only, &od->list);
         }
 
         init_ipam_info_for_datapath(od);
     }
 
+    const struct nbrec_logical_mirror_switch *nbms;
+    NBREC_LOGICAL_MIRROR_SWITCH_FOR_EACH (nbms, ctx->ovnnb_idl) {
+        struct ovn_datapath *od = ovn_datapath_find(datapaths,
+                                                    &nbms->header_.uuid);
+        if (od) {
+            od->nbms = nbms;
+            ovs_list_remove(&od->list);
+            ovs_list_push_back(both, &od->list);
+        } else {
+            od = ovn_datapath_create(datapaths, &nbms->header_.uuid,
+                                     NULL, NULL, nbms, NULL);
+            ovs_list_push_back(nb_only, &od->list);
+        }
+    }
+
     const struct nbrec_logical_router *nbr;
     NBREC_LOGICAL_ROUTER_FOR_EACH (nbr, ctx->ovnnb_idl) {
         if (!lrouter_is_enabled(nbr)) {
@@ -709,7 +764,7 @@ join_datapaths(struct northd_context *ctx, struct hmap 
*datapaths,
             }
         } else {
             od = ovn_datapath_create(datapaths, &nbr->header_.uuid,
-                                     NULL, nbr, NULL);
+                                     NULL, nbr, NULL, NULL);
             ovs_list_push_back(nb_only, &od->list);
         }
     }
@@ -775,6 +830,9 @@ struct ovn_port {
     /* Logical switch port data. */
     const struct nbrec_logical_switch_port *nbsp; /* May be NULL. */
 
+    /*  mirror port data. */
+    const struct nbrec_logical_mirror_switch_port *nbmsp; /* May be NULL. 
*/
+
     struct lport_addresses *lsp_addrs;  /* Logical switch port addresses. 
*/
     unsigned int n_lsp_addrs;
 
@@ -806,6 +864,7 @@ static struct ovn_port *
 ovn_port_create(struct hmap *ports, const char *key,
                 const struct nbrec_logical_switch_port *nbsp,
                 const struct nbrec_logical_router_port *nbrp,
+                const struct nbrec_logical_mirror_switch_port *nbmsp,
                 const struct sbrec_port_binding *sb)
 {
     struct ovn_port *op = xzalloc(sizeof *op);
@@ -818,6 +877,7 @@ ovn_port_create(struct hmap *ports, const char *key,
     op->sb = sb;
     op->nbsp = nbsp;
     op->nbrp = nbrp;
+    op->nbmsp = nbmsp;
     op->derived = false;
     hmap_insert(ports, &op->key_node, hash_string(op->key, 0));
     return op;
@@ -1313,7 +1373,7 @@ join_logical_ports(struct northd_context *ctx,
     const struct sbrec_port_binding *sb;
     SBREC_PORT_BINDING_FOR_EACH (sb, ctx->ovnsb_idl) {
         struct ovn_port *op = ovn_port_create(ports, sb->logical_port,
-                                              NULL, NULL, sb);
+                                              NULL, NULL, NULL, sb);
         ovs_list_push_back(sb_only, &op->list);
     }
 
@@ -1325,7 +1385,7 @@ join_logical_ports(struct northd_context *ctx,
                     = od->nbs->ports[i];
                 struct ovn_port *op = ovn_port_find(ports, nbsp->name);
                 if (op) {
-                    if (op->nbsp || op->nbrp) {
+                    if (op->nbsp || op->nbrp || op->nbmsp) {
                         static struct vlog_rate_limit rl
                             = VLOG_RATE_LIMIT_INIT(5, 1);
                         VLOG_WARN_RL(&rl, "duplicate logical port %s",
@@ -1349,7 +1409,8 @@ join_logical_ports(struct northd_context *ctx,
                      * not have been initialized fully. */
                     ovs_assert(!op->n_lsp_addrs && !op->n_ps_addrs);
                 } else {
-                    op = ovn_port_create(ports, nbsp->name, nbsp, NULL, 
NULL);
+                    op = ovn_port_create(ports, nbsp->name, nbsp,
+                                         NULL, NULL, NULL);
                     ovs_list_push_back(nb_only, &op->list);
                 }
 
@@ -1414,7 +1475,7 @@ join_logical_ports(struct northd_context *ctx,
                 ipam_add_port_addresses(od, op);
                 tag_alloc_add_existing_tags(tag_alloc_table, nbsp);
             }
-        } else {
+        } else if (od->nbr) {
             for (size_t i = 0; i < od->nbr->n_ports; i++) {
                 const struct nbrec_logical_router_port *nbrp
                     = od->nbr->ports[i];
@@ -1433,7 +1494,7 @@ join_logical_ports(struct northd_context *ctx,
 
                 struct ovn_port *op = ovn_port_find(ports, nbrp->name);
                 if (op) {
-                    if (op->nbsp || op->nbrp) {
+                    if (op->nbsp || op->nbrp || op->nbmsp) {
                         static struct vlog_rate_limit rl
                             = VLOG_RATE_LIMIT_INIT(5, 1);
                         VLOG_WARN_RL(&rl, "duplicate logical router port 
%s",
@@ -1449,7 +1510,8 @@ join_logical_ports(struct northd_context *ctx,
                     ovs_assert(!op->lrp_networks.n_ipv4_addrs
                                && !op->lrp_networks.n_ipv6_addrs);
                 } else {
-                    op = ovn_port_create(ports, nbrp->name, NULL, nbrp, 
NULL);
+                    op = ovn_port_create(ports, nbrp->name, NULL,
+                                         nbrp, NULL, NULL);
                     ovs_list_push_back(nb_only, &op->list);
                 }
 
@@ -1490,7 +1552,7 @@ join_logical_ports(struct northd_context *ctx,
                         ovs_list_push_back(both, &crp->list);
                     } else {
                         crp = ovn_port_create(ports, redirect_name,
-                                              NULL, nbrp, NULL);
+                                              NULL, nbrp, NULL, NULL);
                         crp->derived = true;
                         ovs_list_push_back(nb_only, &crp->list);
                     }
@@ -1503,6 +1565,31 @@ join_logical_ports(struct northd_context *ctx,
                     od->l3redirect_port = crp;
                 }
             }
+        } else {
+            for (size_t i = 0; i < od->nbms->n_ports; i++) {
+                const struct nbrec_logical_mirror_switch_port *nbmsp
+                    = od->nbms->ports[i];
+                struct ovn_port *op = ovn_port_find(ports, nbmsp->name);
+                if (op) {
+                    if (op->nbsp || op->nbrp || op->nbmsp) {
+                        static struct vlog_rate_limit rl
+                            = VLOG_RATE_LIMIT_INIT(5, 1);
+                        VLOG_WARN_RL(&rl, "duplicate logical port %s",
+                                     nbmsp->name);
+                        continue;
+                    }
+                    op->nbmsp = nbmsp;
+                    ovs_list_remove(&op->list);
+                    ovs_list_push_back(both, &op->list);
+                }
+                else {
+                    op = ovn_port_create(ports, nbmsp->name, NULL,
+                                         NULL, nbmsp, NULL);
+                    ovs_list_push_back(nb_only, &op->list);
+                }
+                op->od = od;
+            }
+
         }
     }
 
@@ -1938,7 +2025,7 @@ ovn_port_update_sbrec(struct northd_context *ctx,
 
         struct smap ids = SMAP_INITIALIZER(&ids);
         sbrec_port_binding_set_external_ids(op->sb, &ids);
-    } else {
+    } else if (op->nbsp) {
         if (strcmp(op->nbsp->type, "router")) {
             uint32_t queue_id = smap_get_int(
                     &op->sb->options, "qdisc_queue_id", 0);
@@ -2039,6 +2126,12 @@ ovn_port_update_sbrec(struct northd_context *ctx,
         }
         sbrec_port_binding_set_external_ids(op->sb, &ids);
         smap_destroy(&ids);
+    } else {
+        struct smap options;
+        smap_clone(&options, &op->nbmsp->options);
+        sbrec_port_binding_set_options(op->sb, &options);
+        smap_destroy(&options);
+        sbrec_port_binding_set_type(op->sb, op->nbmsp->type);
     }
 }
 
@@ -4029,6 +4122,127 @@ build_lswitch_flows(struct hmap *datapaths, struct 
hmap *ports,
     ds_destroy(&actions);
 }
 
+static void
+build_mirror_filters(struct ovn_datapath *od, struct hmap *lflows)
+{
+    ovn_lflow_add(lflows, od, S_MSWITCH_IN_FLOW_FILTER, 0, "1", "next;");
+    ovn_lflow_add(lflows, od, S_MSWITCH_OUT_FLOW_FILTER, 0, "1", 
"next;");
+
+    /* Ingress or Egress FLOW_FILTER Table (Various priorities). */
+    for (size_t i = 0; i < od->nbms->n_acls; i++) {
+        struct nbrec_acl *acl = od->nbms->acls[i];
+        bool ingress = !strcmp(acl->direction, "from-lport") ? true 
:false;
+        enum ovn_stage stage = ingress ?
+                S_MSWITCH_IN_FLOW_FILTER : S_MSWITCH_OUT_FLOW_FILTER;
+
+        if (!strcmp(acl->action, "allow")) {
+
+            ovn_lflow_add(lflows, od, stage,
+                          acl->priority + OVN_ACL_PRI_OFFSET,
+                          acl->match, "next;");
+        } else if (!strcmp(acl->action, "drop")
+                   || !strcmp(acl->action, "reject")) {
+
+            /* XXX Need to support "reject", treat it as "drop;" for now. 
*/
+            if (!strcmp(acl->action, "reject")) {
+                VLOG_INFO("reject is not a supported action");
+            }
+            ovn_lflow_add(lflows, od, stage,
+                          acl->priority + OVN_ACL_PRI_OFFSET,
+                          acl->match, "drop;");
+        }
+    }
+}
+
+
+static void
+build_lmirror_flows(struct hmap *datapaths, struct hmap *ports,
+                    struct hmap *lflows)
+{
+    /* This flow table structure is documented in ovn-northd(8), so 
please
+     * update ovn-northd.8.xml if you change anything. */
+
+    struct ds match = DS_EMPTY_INITIALIZER;
+    struct ds actions = DS_EMPTY_INITIALIZER;
+
+    /* Build pre-ACL and ACL tables for both ingress and egress.
+     * Ingress tables 3 through 9.  Egress tables 0 through 6. */
+    struct ovn_datapath *od;
+    HMAP_FOR_EACH (od, key_node, datapaths) {
+        if (!od->nbms) {
+            continue;
+        }
+        build_mirror_filters(od, lflows);
+    }
+
+
+    /* miss table.  (priority 0)*/
+    HMAP_FOR_EACH (od, key_node, datapaths) {
+        if (!od->nbms) {
+            continue;
+        }
+        ovn_lflow_add(lflows, od, S_MSWITCH_IN_MIRROR_IN, 0, "1", 
"drop;");
+        ovn_lflow_add(lflows, od, S_MSWITCH_IN_OUT_LK, 0, "1", "drop;");
+        ovn_lflow_add(lflows, od, S_MSWITCH_OUT_DELIVERY, 0, "1", 
"output;");
+    }
+
+    /* Ingress table*/
+     struct ovn_port *op;
+    HMAP_FOR_EACH (op, key_node, ports) {
+        if (!op->nbmsp) {
+            continue;
+        }
+        if (strcmp(op->nbmsp->type, "mirror")) {
+            continue;
+        }
+         /*ingress table = 0*/
+        ds_clear(&match);
+        ds_clear(&actions);
+        ds_put_format(&match, "inport == %s", op->json_key);
+        ovn_lflow_add(lflows, op->od, S_MSWITCH_IN_MIRROR_IN, 100,
+                      ds_cstr(&match), "next;");
+        const char *tass_port_name =
+                  smap_get(&op->nbmsp->options, "taas-port");
+        if (!tass_port_name) {
+            continue;
+        }
+        struct ovn_port *tass_port =
+            ovn_port_find(ports, tass_port_name);
+        if (!tass_port) {
+            continue;
+        }
+         /*ingress table = 2*/
+        ds_clear(&match);
+        ds_clear(&actions);
+        ds_put_format(&match, "inport == %s", op->json_key);
+        ds_put_format(&actions, "outport = %s; output;", 
tass_port->json_key);
+        ovn_lflow_add(lflows, op->od, S_MSWITCH_IN_OUT_LK, 100,
+              ds_cstr(&match), ds_cstr(&actions));
+
+    }
+
+    /* egress table*/
+    HMAP_FOR_EACH (op, key_node, ports) {
+        if (!op->nbmsp) {
+            continue;
+        }
+        if (strcmp(op->nbmsp->type, "taas")) {
+            continue;
+        }
+
+        /*egress table = 1*/
+        ds_clear(&match);
+        ds_clear(&actions);
+        ds_put_format(&match, "outport == %s", op->json_key);
+        ovn_lflow_add(lflows, op->od, S_MSWITCH_OUT_DELIVERY, 100,
+                      ds_cstr(&match), "output;");
+
+    }
+
+    ds_destroy(&match);
+    ds_destroy(&actions);
+}
+
 static bool
 lrport_is_enabled(const struct nbrec_logical_router_port *lrport)
 {
@@ -5409,7 +5623,9 @@ build_lrouter_flows(struct hmap *datapaths, struct 
hmap *ports,
                                   100, ds_cstr(&match), 
ds_cstr(&actions));
                 }
             }
-        } else if (op->od->n_router_ports && strcmp(op->nbsp->type, 
"router")) {
+        } else if (op->nbsp &&
+                   op->od->n_router_ports &&
+                   strcmp(op->nbsp->type, "router")) {
             /* This is a logical switch port that backs a VM or a 
container.
              * Extract its addresses. For each of the address, go through 
all
              * the router ports attached to the switch (to which this 
port
@@ -5486,7 +5702,7 @@ build_lrouter_flows(struct hmap *datapaths, struct 
hmap *ports,
                     }
                 }
             }
-        } else if (!strcmp(op->nbsp->type, "router")) {
+        } else if (op->nbsp && !strcmp(op->nbsp->type, "router")) {
             /* This is a logical switch port that connects to a router. 
*/
 
             /* The peer of this switch port is the router port for which
@@ -5666,6 +5882,7 @@ build_lflows(struct northd_context *ctx, struct hmap 
*datapaths,
 
     build_lswitch_flows(datapaths, ports, &lflows, &mcgroups);
     build_lrouter_flows(datapaths, ports, &lflows);
+    build_lmirror_flows(datapaths, ports, &lflows);
 
     /* Push changes to the Logical_Flow table to database. */
     const struct sbrec_logical_flow *sbflow, *next_sbflow;
@@ -5677,7 +5894,7 @@ build_lflows(struct northd_context *ctx, struct hmap 
*datapaths,
             continue;
         }
 
-        enum ovn_datapath_type dp_type = od->nbs ? DP_SWITCH : DP_ROUTER;
+        enum ovn_datapath_type dp_type = ovn_datapath_get_type(od);
         enum ovn_pipeline pipeline
             = !strcmp(sbflow->pipeline, "ingress") ? P_IN : P_OUT;
         struct ovn_lflow *lflow = ovn_lflow_find(
@@ -6000,6 +6217,43 @@ update_logical_port_status(struct northd_context 
*ctx)
     hmap_destroy(&lports_hmap);
 }
 
+/* if taas_port is not configured with taas-chassis, set the taas-chassis 
with
+ * the corresponding port chassis in sourthbound DB*/
+static void
+update_taas_port_chassis(struct northd_context *ctx)
+{
+    const struct sbrec_port_binding *sb;
+    struct shash taas_ports = SHASH_INITIALIZER(&taas_ports);
+
+    SBREC_PORT_BINDING_FOR_EACH (sb, ctx->ovnsb_idl) {
+        if (!strcmp(sb->type, "taas")) {
+            if (strlen(sb->logical_port) < strlen("taas-")) {
+                VLOG_INFO("sb is %s is invlalid", sb->logical_port);
+                continue;
+            }
+            shash_add(&taas_ports, sb->logical_port+strlen("taas-"), sb);
+        }
+    }
+
+    SBREC_PORT_BINDING_FOR_EACH (sb, ctx->ovnsb_idl) {
+        struct sbrec_port_binding *taas_port  = 
shash_find_data(&taas_ports,
+                                                 sb->logical_port);
+        if (taas_port) {
+            const char *chassis_name = smap_get(&taas_port->options,
+                                                "taas-chassis");
+            struct sbrec_chassis *chassis = sb->chassis;
+            if (!chassis_name && chassis) {
+                struct smap options;
+                smap_clone(&options, &taas_port->options);
+                smap_add(&options, "taas-chassis", chassis->name);
+                sbrec_port_binding_set_options(taas_port, &options);
+                smap_destroy(&options);
+            }
+        }
+    }
+    shash_destroy(&taas_ports);
+}
+
 static struct dhcp_opts_map supported_dhcp_opts[] = {
     OFFERIP,
     DHCP_OPT_NETMASK,
@@ -6322,6 +6576,7 @@ ovnsb_db_run(struct northd_context *ctx, struct 
ovsdb_idl_loop *sb_loop)
     }
 
     update_logical_port_status(ctx);
+    update_taas_port_chassis(ctx);
     update_northbound_cfg(ctx, sb_loop);
 }
 
diff --git a/ovn/ovn-nb.ovsschema b/ovn/ovn-nb.ovsschema
index a077bfb..2ade868 100644
--- a/ovn/ovn-nb.ovsschema
+++ b/ovn/ovn-nb.ovsschema
@@ -1,7 +1,7 @@
 {
-    "name": "OVN_Northbound",
-    "version": "5.8.0",
-    "cksum": "2812300190 16766",
+   "name": "OVN_Northbound",
+    "version": "5.9.0",
+    "cksum": "2027606117 18549",
     "tables": {
         "NB_Global": {
             "columns": {
@@ -323,5 +323,41 @@
                     "type": {"key": "string", "value": "string",
                              "min": 0, "max": "unlimited"}}},
             "indexes": [["name"]],
+            "isRoot": false},
+        "Logical_Mirror_Switch": {
+            "columns": {
+                "name": {"type": "string"},
+                "ports": {"type": {"key": {"type": "uuid",
+                                     "refTable": 
"Logical_Mirror_Switch_Port",
+                                           "refType": "strong"},
+                                   "min": 0,
+                                   "max": "unlimited"}},
+                "acls": {"type": {"key": {"type": "uuid",
+                                          "refTable": "ACL",
+                                          "refType": "strong"},
+                                  "min": 0,
+                                  "max": "unlimited"}},
+                "other_config": {
+                    "type": {"key": "string", "value": "string",
+                             "min": 0, "max": "unlimited"}},
+                "external_ids": {
+                    "type": {"key": "string", "value": "string",
+                             "min": 0, "max": "unlimited"}}},
+            "isRoot": true},
+        "Logical_Mirror_Switch_Port": {
+            "columns": {
+                "name": {"type": "string"},
+                "type": {"type": "string"},
+                "options": {
+                    "type": {"key": "string",
+                             "value": "string",
+                             "min": 0,
+                             "max": "unlimited"}},
+                "peer": {"type": {"key": "string", "min": 0, "max": 1}},
+                "enabled": {"type": {"key": "boolean", "min": 0, "max": 
1}},
+                "external_ids": {
+                    "type": {"key": "string", "value": "string",
+                             "min": 0, "max": "unlimited"}}},
+            "indexes": [["name"]],
             "isRoot": false}}
     }
diff --git a/ovn/ovn-nb.xml b/ovn/ovn-nb.xml
index 31303a8..891f13a 100644
--- a/ovn/ovn-nb.xml
+++ b/ovn/ovn-nb.xml
@@ -444,6 +444,39 @@
           If set, indicates the maximum burst size for data sent from 
this
           interface, in bits.
         </column>
+
+        <column name="options" key="mirror-port">
+          If set, indicates the packet send to/from this port will be 
cloned
+          to the <code>logical_mirror_switch_port</code> which 
mirror-port
+          indicated. In order to make this column sense, the
+          <ref column="options" 
table="Port_Binding"/>:<code>mirror-direction
+          </code> must also be defined.
+        </column>
+
+        <column name="options" key="mirror-direction">
+         <p>
+            This option indicates whitch direction(from-port/to-port/all) 
of
+            packet will be cloned to the <code>logical_mirror_switch_port
+            </code>. the directions are defined as follow:
+          </p>
+          <dl>
+            <dt><code>from-port</code></dt>
+            <dd>
+              The packets from this port will be cloned to specified 
mirror
+              port.
+            </dd>
+            <dt><code>to-port</code></dt>
+            <dd>
+              The packets to this port will be cloned to specified mirror
+              port.
+            </dd>
+            <dt><code>both</code></dt>
+            <dd>
+              The packets both from and to this port will be cloned to
+              specified mirror port.
+            </dd>
+          </dl>
+        </column>
       </group>
     </group>
 
@@ -2216,4 +2249,138 @@
     </group>
   </table>
 
+  <table name="Logical_Mirror_Switch">
+    <p>
+      There is a special logical switch which is used for taas function 
as
+      the mirror flow forward transfer, each Logical_Mirror_Switch 
represent
+      a tap_service.
+    </p>
+
+    <column name="name">
+      <p>
+        A name for the logical mirror switch.  This name has no special
+        meaning or purpose other than to provide convenience for human
+        interaction with the ovn-nb database.  There is no requirement 
for
+        the name to be unique.  The uuid of logical switch which taas 
service
+        port in with prefix of "taas-" should be used as the unique 
identifier.
+      </p>
+    </column>
+
+    <column name="ports">
+      <p>
+        The logical mirror switch ports connected to the logical mirror
+        switch.
+      </p>
+      <p>
+        It is an error for multiple logical mirror switches to include 
the
+        same logical mirror switch port.
+      </p>
+    </column>
+
+    <column name="acls">
+      Access control rules that apply to packets within the logical 
switch.
+      Here, acls are used as flow filter..
+    </column>
+
+    <column name="other_config">
+      <p>
+        Additional configuration options for the logical mirror switch.
+      </p>
+    </column>
+
+    <group title="Common Columns">
+      <column name="external_ids">
+        See <em>External IDs</em> at the beginning of this document.
+      </column>
+    </group>
+  </table>
+
+  <table name="Logical_Mirror_Switch_Port">
+    <p>
+      A port within an L2 logical mirror switch.
+    </p>
+
+    <column name="name">
+      <p>
+        The logical mirror switch port name, prefix of "mirror-" or 
"taas-"
+        according to the port type.
+      </p>
+
+      <p>
+        port name start with "mirror-", has the source port uuid of taas
+        flows as the postfix.
+      </p>
+
+      <p>
+        port name start with "taas-", has the port uuid of taas service 
as
+        the postfix.
+      </p>
+    </column>
+
+    <column name="type">
+      <p>
+        Specify a type for this logical mirror port. The following types 
are
+        defined:
+      </p>
+
+      <dl>
+        <dt>mirror</dt>
+        <dd>
+          indicates inport of flow in logical mirror switch. related to
+          source_port of taas flows.
+        </dd>
+
+        <dt><code>taas</code></dt>
+        <dd>
+          indicates destination port of flow in logical mirror switch. 
related
+          to port_id of taas services.
+        </dd>
+      </dl>
+    </column>
+
+    <group title="Options">
+      <column name="options">
+        This column provides key/value settings specific to the logical 
port
+        <ref column="type"/>.  The type-specific options are described
+        individually below.
+      </column>
+
+      <group title="Options for mirror ports">
+        <p>
+          These options apply when <ref column="type"/> is 
<code>mirror</code>.
+        </p>
+
+        <column name="options" key="taas-port">
+          Required.  The <ref column="name"/> of the <ref
+          table="Logical_mirror_Port"/>  with type <code>taas</code>which 
the
+          cloned flows transfered to.
+        </column>
+      </group>
+
+      <group title="Options for taas ports">
+        <p>
+          These options apply when <ref column="type"/> is 
<code>taas</code>.
+        </p>
+
+        <column name="options" key="taas-chassis">
+          Required.  The <ref column="name"/> of the <ref
+          table="chassis"/> which the cloned flows transfered to.
+        </column>
+      </group>
+    </group>
+
+    <column name="peer">
+      not used
+    </column>
+
+    <column name="enabled">
+      not used
+    </column>
+
+    <group title="Common Columns">
+      <column name="external_ids">
+        See <em>External IDs</em> at the beginning of this document.
+      </column>
+    </group>
+  </table>
 </database>
diff --git a/ovn/ovn-sb.xml b/ovn/ovn-sb.xml
index 08d784a..9b0d90d 100644
--- a/ovn/ovn-sb.xml
+++ b/ovn/ovn-sb.xml
@@ -1743,10 +1743,17 @@ tcp.flags = RST;
         the <ref db="OVN_Northbound"/> database.
       </column>
 
+      <column name="external_ids" key="logical-taas-switch" 
type='{"type":
+        "uuid"}'> For a logical datapath that represents a logical mirror
+        switch, <code>ovn-northd</code> stores in this key the UUID of 
the
+        corresponding <ref table="Logical_Mirror_Switch" 
db="OVN_Northbound"/>
+        row in the <ref db="OVN_Northbound"/> database.
+      </column>
+
       <group title="Naming">
         <p>
           <code>ovn-northd</code> copies these from the name fields in 
the <ref
-          db="OVN_Northbound"/> database, either from <ref
+          db="OVN_Northbound"/> database, from <ref
           table="Logical_Router" db="OVN_Northbound" column="name"/> and 
<ref
           table="Logical_Router" db="OVN_Northbound" 
column="external_ids"
           key="neutron:router_name"/> in the <ref table="Logical_Router"
@@ -1754,7 +1761,10 @@ tcp.flags = RST;
           db="OVN_Northbound" column="name"/> and <ref 
table="Logical_Switch"
           db="OVN_Northbound" column="external_ids"
           key="neutron:network_name"/> in the <ref table="Logical_Switch"
-          db="OVN_Northbound"/> table.
+          db="OVN_Northbound"/> table or from <ref
+          table="Logical_Mirror_Switch" db="OVN_Northbound" 
column="name"/>
+          in the <ref table="Logical_Mirror_Switch" db="OVN_Northbound"/>
+          table.
         </p>
 
         <column name="external_ids" key="name">
@@ -1996,6 +2006,20 @@ tcp.flags = RST;
             the <code>outport</code> will be reset to the value of the
             distributed port.
           </dd>
+
+          <dt><code>mirror</code></dt>
+          <dd>
+            A port in a logical mirror switch which related the
+            <code>source_port</code> in <code>tap_flow</code>. This port 
is
+            configured as the source port of monitored flows.
+          </dd>
+
+          <dt><code>taas</code></dt>
+          <dd>
+            A port in a logical mirror switch which related the
+            <code>port</code> in <code>tap_service</code>. This port is
+            configured as the dst port of monitored flows.
+          </dd>
         </dl>
       </column>
     </group>
@@ -2175,6 +2199,63 @@ tcp.flags = RST;
         <code>queue_id</code> used in OpenFlow in <code>struct
         ofp_action_enqueue</code>.
       </column>
+
+      <column name="options" key="mirror-port">
+        If set, indicates the packet send to/from this port will be 
cloned to
+        the <code>logical_mirror_switch</code>, The value of mirror-port
+        indicates the inport in <code>logical_mirror_switch</code>. In 
order
+        to make this column sense, the <ref column="options"
+        table="Port_Binding"/>:<code>mirror-direction</code> must also be
+        defined.
+      </column>
+
+      <column name="options" key="mirror-direction">
+        <p>
+          This option indicates whitch direction(from-port/to-port/both) 
of
+          packet will be cloned to the
+          <code>logical_mirror_switch_port</code>. the directions are 
defined:
+        </p>
+        <dl>
+          <dt><code>from-port</code></dt>
+          <dd>
+            The packets from this port will be cloned to specified mirror
+            port.
+          </dd>
+          <dt><code>to-port</code></dt>
+          <dd>
+            The packets to this port will be cloned to specified mirror 
port.
+          </dd>
+          <dt><code>both</code></dt>
+          <dd>
+            The packets both from and to this port will be cloned to 
specified
+            mirror port.
+          </dd>
+        </dl>
+      </column>
+    </group>
+
+    <group title="mirror Options">
+      <p>
+        These options apply to logical ports with <ref column="type"/>
+        of <code>mirror</code>.
+      </p>
+
+      <column name="options" key="taas-port">
+        The name of the taas port which is used to indicate the 
destination
+        of the monitored flows.
+      </column>
+    </group>
+
+    <group title="taas Options">
+      <p>
+        These options apply to logical ports with <ref column="type"/>
+        of <code>taas</code>.
+      </p>
+
+      <column name="options" key="taas-chassis">
+        The name of the taas chassis which is used to indicate the 
location of
+        this port
+      </column>
     </group>
 
     <group title="Chassis Redirect Options">
diff --git a/ovn/utilities/ovn-nbctl.c b/ovn/utilities/ovn-nbctl.c
index 46ede4e..088b889 100644
--- a/ovn/utilities/ovn-nbctl.c
+++ b/ovn/utilities/ovn-nbctl.c
@@ -1,4 +1,4 @@
-/*
+/*
  * 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:
@@ -373,6 +373,9 @@ Logical switch port commands:\n\
                             set dhcpv6 options for PORT\n\
   lsp-get-dhcpv6-options PORT  get the dhcpv6 options for PORT\n\
 \n\
+Logical mirror_switch port commands:\n\
+  lmsp-add SWITCH PORT       add logical port PORT on mirror SWITCH\n\
+\n\
 Logical router commands:\n\
   lr-add [ROUTER]           create a logical router named ROUTER\n\
   lr-del ROUTER             delete ROUTER and all its ports\n\
@@ -941,6 +944,142 @@ nbctl_lsp_add(struct ctl_context *ctx)
     free(new_ports);
 }
 
+static const struct nbrec_logical_mirror_switch_port *
+lmsp_by_name_or_uuid(struct ctl_context *ctx, const char *id,
+                    bool must_exist)
+{
+    const struct nbrec_logical_mirror_switch_port *lmsp = NULL;
+
+    struct uuid lmsp_uuid;
+    bool is_uuid = uuid_from_string(&lmsp_uuid, id);
+    if (is_uuid) {
+        lmsp = nbrec_logical_mirror_switch_port_get_for_uuid(ctx->idl,
+                                                          &lmsp_uuid);
+    }
+
+    if (!lmsp) {
+        NBREC_LOGICAL_MIRROR_SWITCH_PORT_FOR_EACH (lmsp, ctx->idl) {
+            if (!strcmp(lmsp->name, id)) {
+                break;
+            }
+        }
+    }
+
+    if (!lmsp && must_exist) {
+        ctl_fatal("%s: port %s not found", id, is_uuid ? "UUID" : 
"name");
+    }
+
+    return lmsp;
+}
+
+/* Returns the logical switch that contains 'lsp'. */
+static const struct nbrec_logical_mirror_switch *
+lmsp_to_ls(const struct ovsdb_idl *idl,
+               const struct nbrec_logical_mirror_switch_port *lmsp)
+{
+    const struct nbrec_logical_mirror_switch *lms;
+    NBREC_LOGICAL_MIRROR_SWITCH_FOR_EACH (lms, idl) {
+        for (size_t i = 0; i < lms->n_ports; i++) {
+            if (lms->ports[i] == lmsp) {
+                return lms;
+            }
+        }
+    }
+
+    /* Can't happen because of the database schema */
+    ctl_fatal("logical port %s is not part of any logical switch",
+              lmsp->name);
+}
+
+static const struct nbrec_logical_mirror_switch *
+lms_by_name_or_uuid(struct ctl_context *ctx, const char *id, bool 
must_exist)
+{
+    const struct nbrec_logical_mirror_switch *lms = NULL;
+
+    struct uuid lms_uuid;
+    bool is_uuid = uuid_from_string(&lms_uuid, id);
+    if (is_uuid) {
+        lms = nbrec_logical_mirror_switch_get_for_uuid(ctx->idl, 
&lms_uuid);
+    }
+
+    if (!lms) {
+        const struct nbrec_logical_mirror_switch *iter;
+
+        NBREC_LOGICAL_MIRROR_SWITCH_FOR_EACH (iter, ctx->idl) {
+            if (strcmp(iter->name, id)) {
+                continue;
+            }
+            if (lms) {
+                ctl_fatal("Multiple logical switches named '%s'.  "
+                          "Use a UUID.", id);
+            }
+            lms = iter;
+        }
+    }
+
+    if (!lms && must_exist) {
+        ctl_fatal("%s: switch %s not found", id, is_uuid ? "UUID" : 
"name");
+    }
+
+    return lms;
+}
+
+static const char *
+lms_get_name(const struct nbrec_logical_mirror_switch *lms,
+                 char uuid_s[UUID_LEN + 1], size_t uuid_s_size)
+{
+    if (lms->name[0]) {
+        return lms->name;
+    }
+    snprintf(uuid_s, uuid_s_size, UUID_FMT, 
UUID_ARGS(&lms->header_.uuid));
+    return uuid_s;
+}
+
+
+static void
+nbctl_lmsp_add(struct ctl_context *ctx)
+{
+    bool may_exist = shash_find(&ctx->options, "--may-exist") != NULL;
+
+    const struct nbrec_logical_mirror_switch *lms;
+    lms = lms_by_name_or_uuid(ctx, ctx->argv[1], true);
+
+    const char *lmsp_name = ctx->argv[2];
+    const struct nbrec_logical_mirror_switch_port *lmsp;
+    lmsp = lmsp_by_name_or_uuid(ctx, lmsp_name, false);
+    if (lmsp) {
+        if (!may_exist) {
+            ctl_fatal("%s: a port with this name already exists",
+                      lmsp_name);
+        }
+
+        const struct nbrec_logical_mirror_switch *lmsw;
+        lmsw = lmsp_to_ls(ctx->idl, lmsp);
+        if (lmsw != lms) {
+            char uuid_s[UUID_LEN + 1];
+            ctl_fatal("%s: port already exists but in switch %s", 
lmsp_name,
+                      lms_get_name(lmsw, uuid_s, sizeof uuid_s));
+        }
+        return;
+    }
+
+    /* Create the logical port. */
+    lmsp = nbrec_logical_mirror_switch_port_insert(ctx->txn);
+    nbrec_logical_mirror_switch_port_set_name(lmsp, lmsp_name);
+
+    /* Insert the logical port into the logical switch. */
+    nbrec_logical_mirror_switch_verify_ports(lms);
+    struct nbrec_logical_mirror_switch_port **new_ports = xmalloc(
+                                 sizeof *new_ports * (lms->n_ports + 1));
+    memcpy(new_ports, lms->ports, sizeof *new_ports * lms->n_ports);
+    new_ports[lms->n_ports] = CONST_CAST(
+             struct nbrec_logical_mirror_switch_port *,
+             lmsp);
+    nbrec_logical_mirror_switch_set_ports(lms, new_ports, lms->n_ports + 
1);
+    free(new_ports);
+}
+
+
 /* Removes logical switch port 'ls->ports[idx]'. */
 static void
 remove_lsp(const struct nbrec_logical_switch *ls, size_t idx)
@@ -3363,6 +3502,13 @@ static const struct ctl_table_class 
tables[NBREC_N_TABLES] = {
     = {&nbrec_address_set_col_name, NULL, NULL},
 
     [NBREC_TABLE_ACL].row_ids[0] = {&nbrec_acl_col_name, NULL, NULL},
+
+    [NBREC_TABLE_LOGICAL_MIRROR_SWITCH].row_ids[0]
+    = {&nbrec_logical_mirror_switch_col_name, NULL, NULL},
+
+    [NBREC_TABLE_LOGICAL_MIRROR_SWITCH_PORT].row_ids[0]
+    = {&nbrec_logical_mirror_switch_port_col_name, NULL, NULL},
+
 };
 
 static void
@@ -3657,6 +3803,10 @@ static const struct ctl_command_syntax 
nbctl_commands[] = {
     { "lsp-get-dhcpv6-options", 1, 1, "PORT", NULL,
       nbctl_lsp_get_dhcpv6_options, NULL, "", RO },
 
+    /* logical mirror switch port commands. */
+    { "lmsp-add", 2, 2, "SWITCH PORT", NULL, nbctl_lmsp_add,
+      NULL, "--may-exist", RW },
+
     /* logical router commands. */
     { "lr-add", 0, 1, "[ROUTER]", NULL, nbctl_lr_add, NULL,
       "--may-exist,--add-duplicate", RW },
diff --git a/ovn/utilities/ovn-trace.c b/ovn/utilities/ovn-trace.c
index 0fe05f8..6757d14 100644
--- a/ovn/utilities/ovn-trace.c
+++ b/ovn/utilities/ovn-trace.c
@@ -1,4 +1,4 @@
-/*
+/*
  * Copyright (c) 2016, 2017 Nicira, Inc.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
@@ -545,7 +545,8 @@ read_datapaths(void)
 
         dp->sb_uuid = sbdb->header_.uuid;
         if (!smap_get_uuid(ids, "logical-switch", &dp->nb_uuid) &&
-            !smap_get_uuid(ids, "logical-router", &dp->nb_uuid)) {
+            !smap_get_uuid(ids, "logical-router", &dp->nb_uuid) &&
+            !smap_get_uuid(ids, "logical-taas-switch", &dp->nb_uuid)) {
             dp->nb_uuid = dp->sb_uuid;
         }
 
-- 
1.8.3.1




More information about the dev mailing list