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.qia...@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 _______________________________________________ dev mailing list d...@openvswitch.org https://mail.openvswitch.org/mailman/listinfo/ovs-dev