[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