From patchwork Sun Apr 30 23:22:24 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Ben Pfaff X-Patchwork-Id: 756963 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from mail.linuxfoundation.org (mail.linuxfoundation.org [140.211.169.12]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3wGP4w5fthz9sCX for ; Mon, 1 May 2017 09:33:36 +1000 (AEST) Received: from mail.linux-foundation.org (localhost [127.0.0.1]) by mail.linuxfoundation.org (Postfix) with ESMTP id 8C438BE8; Sun, 30 Apr 2017 23:23:43 +0000 (UTC) X-Original-To: dev@openvswitch.org Delivered-To: ovs-dev@mail.linuxfoundation.org Received: from smtp2.linuxfoundation.org (smtp2.linux-foundation.org [172.17.192.36]) by mail.linuxfoundation.org (Postfix) with ESMTPS id AFBF6BDA for ; Sun, 30 Apr 2017 23:23:42 +0000 (UTC) X-Greylist: domain auto-whitelisted by SQLgrey-1.7.6 Received: from relay8-d.mail.gandi.net (relay8-d.mail.gandi.net [217.70.183.201]) by smtp2.linuxfoundation.org (Postfix) with ESMTPS id 92A3A1DEE0 for ; Sun, 30 Apr 2017 23:23:41 +0000 (UTC) Received: from relay5-d.mail.gandi.net (relay5-d.mail.gandi.net [217.70.183.197]) by relay8-d.mail.gandi.net (Postfix) with ESMTPS id 2E80D405B6; Mon, 1 May 2017 01:23:40 +0200 (CEST) Received: from mfilter14-d.gandi.net (mfilter14-d.gandi.net [217.70.178.142]) by relay5-d.mail.gandi.net (Postfix) with ESMTP id 1E12F41C089; Mon, 1 May 2017 01:23:40 +0200 (CEST) X-Virus-Scanned: Debian amavisd-new at mfilter14-d.gandi.net Received: from relay5-d.mail.gandi.net ([IPv6:::ffff:217.70.183.197]) by mfilter14-d.gandi.net (mfilter14-d.gandi.net [::ffff:10.0.15.180]) (amavisd-new, port 10024) with ESMTP id qJF8JF1CjnvR; Mon, 1 May 2017 01:23:38 +0200 (CEST) X-Originating-IP: 173.228.112.23 Received: from sigabrt.gateway.sonic.net (173-228-112-23.dsl.dynamic.fusionbroadband.com [173.228.112.23]) (Authenticated sender: blp@ovn.org) by relay5-d.mail.gandi.net (Postfix) with ESMTPSA id 4B1A941C084; Mon, 1 May 2017 01:23:34 +0200 (CEST) From: Ben Pfaff To: dev@openvswitch.org Date: Sun, 30 Apr 2017 16:22:24 -0700 Message-Id: <20170430232231.15151-21-blp@ovn.org> X-Mailer: git-send-email 2.10.2 In-Reply-To: <20170430232231.15151-1-blp@ovn.org> References: <20170430232231.15151-1-blp@ovn.org> X-Spam-Status: No, score=-2.6 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_LOW autolearn=ham version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on smtp2.linux-foundation.org Cc: Ben Pfaff Subject: [ovs-dev] [PATCH 20/27] ovn-sbctl: Add --ovs option to "lflow-list", for listing OpenFlow flows. X-BeenThere: ovs-dev@openvswitch.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Sender: ovs-dev-bounces@openvswitch.org Errors-To: ovs-dev-bounces@openvswitch.org This is like the --ovs option to ovn-trace, but it applies to every flow dumped, so it has different applications. Signed-off-by: Ben Pfaff --- NEWS | 2 + include/openvswitch/ofp-print.h | 4 +- lib/db-ctl-base.c | 19 ++++----- lib/db-ctl-base.h | 11 ++++- lib/ofp-print.c | 2 +- ovn/utilities/ovn-sbctl.8.in | 15 ++++++- ovn/utilities/ovn-sbctl.c | 90 ++++++++++++++++++++++++++++++++++++++++- 7 files changed, 124 insertions(+), 19 deletions(-) diff --git a/NEWS b/NEWS index 7a7e7fcd8a3a..ad3bdb162a7b 100644 --- a/NEWS +++ b/NEWS @@ -24,6 +24,8 @@ Post-v2.7.0 * ovn-trace now has basic support for tracing distributed firewalls. * In ovn-nbctl and ovn-sbctl, record UUIDs in commands may now be abbreviated to 4 hex digits. + * "ovn-sbctl lflow-list" can now print OpenFlow flows that correspond + to logical flows. - Add the command 'ovs-appctl stp/show' (see ovs-vswitchd(8)). - OpenFlow: * Increased support for OpenFlow 1.6 (draft). diff --git a/include/openvswitch/ofp-print.h b/include/openvswitch/ofp-print.h index 58fd4039d61f..863f7aaa2e52 100644 --- a/include/openvswitch/ofp-print.h +++ b/include/openvswitch/ofp-print.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2008, 2009, 2011, 2012, 2015 Nicira, Inc. + * Copyright (c) 2008, 2009, 2011, 2012, 2015, 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. @@ -43,7 +43,7 @@ char *ofp_to_string(const void *, size_t, int verbosity); char *ofp10_match_to_string(const struct ofp10_match *, int verbosity); char *ofp_packet_to_string(const void *data, size_t len); -void ofp_print_flow_stats(struct ds *, struct ofputil_flow_stats *); +void ofp_print_flow_stats(struct ds *, const struct ofputil_flow_stats *); void ofp_print_version(const struct ofp_header *, struct ds *); void ofp_print_table_features( struct ds *, const struct ofputil_table_features *features, diff --git a/lib/db-ctl-base.c b/lib/db-ctl-base.c index 3177d39472e9..d6fcbde41a28 100644 --- a/lib/db-ctl-base.c +++ b/lib/db-ctl-base.c @@ -1637,11 +1637,11 @@ parse_command(int argc, char *argv[], struct shash *local_options, const char *s = strstr(p->options, node->name); int end = s ? s[strlen(node->name)] : EOF; - if (end != '=' && end != ',' && end != ' ' && end != '\0') { + if (!strchr("=,? ", end)) { ctl_fatal("'%s' command has no '%s' option", argv[i], node->name); } - if ((end == '=') != (node->data != NULL)) { + if (end != '?' && (end == '=') != (node->data != NULL)) { if (end == '=') { ctl_fatal("missing argument to '%s' option on '%s' " "command", node->name, argv[i]); @@ -1913,19 +1913,14 @@ ctl_add_cmd_options(struct option **options_p, size_t *n_options_p, s = xstrdup(p->options); for (name = strtok_r(s, ",", &save_ptr); name != NULL; name = strtok_r(NULL, ",", &save_ptr)) { - char *equals; - int has_arg; - ovs_assert(name[0] == '-' && name[1] == '-' && name[2]); name += 2; - equals = strchr(name, '='); - if (equals) { - has_arg = required_argument; - *equals = '\0'; - } else { - has_arg = no_argument; - } + size_t n = strcspn(name, "=?"); + int has_arg = (name[n] == '\0' ? no_argument + : name[n] == '=' ? required_argument + : optional_argument); + name[n] = '\0'; o = find_option(name, *options_p, *n_options_p); if (o) { diff --git a/lib/db-ctl-base.h b/lib/db-ctl-base.h index 2838921044bd..f67f4345376a 100644 --- a/lib/db-ctl-base.h +++ b/lib/db-ctl-base.h @@ -128,7 +128,16 @@ struct ctl_command_syntax { void (*postprocess)(struct ctl_context *ctx); /* A comma-separated list of supported options, e.g. "--a,--b", or the - * empty string if the command does not support any options. */ + * empty string if the command does not support any options. + * + * Arguments are determined by appending special characters to option + * names: + * + * - Append "=" (e.g. "--id=") for a required argument. + * + * - Append "?" (e.g. "--ovs?") for an optional argument. + * + * - Otherwise an option does not accept an argument. */ const char *options; enum { RO, RW } mode; /* Does this command modify the database? */ diff --git a/lib/ofp-print.c b/lib/ofp-print.c index 10ac053ae54b..ab07434d024f 100644 --- a/lib/ofp-print.c +++ b/lib/ofp-print.c @@ -1659,7 +1659,7 @@ ofp_print_flow_stats_request(struct ds *string, const struct ofp_header *oh) } void -ofp_print_flow_stats(struct ds *string, struct ofputil_flow_stats *fs) +ofp_print_flow_stats(struct ds *string, const struct ofputil_flow_stats *fs) { ds_put_format(string, " %scookie=%s0x%"PRIx64", %sduration=%s", colors.param, colors.end, ntohll(fs->cookie), diff --git a/ovn/utilities/ovn-sbctl.8.in b/ovn/utilities/ovn-sbctl.8.in index 83953cf3cd60..4f8017e7ae8b 100644 --- a/ovn/utilities/ovn-sbctl.8.in +++ b/ovn/utilities/ovn-sbctl.8.in @@ -160,7 +160,7 @@ to unbind logical port that is not bound has no effect. . .SS "Logical Flow Commands" . -.IP "[\fB\-\-uuid\fR] \fBlflow\-list\fR [\fIlogical-datapath\fR] [\fIlflow\fR...]" +.IP "[\fB\-\-uuid\fR] [\fB\-\-ovs\fR[\fB=\fIremote\fR]] [\fB\-\-stats\fR] \fBlflow\-list\fR [\fIlogical-datapath\fR] [\fIlflow\fR...]" List logical flows. If \fIlogical-datapath\fR is specified, only list flows for that logical datapath. The \fIlogical-datapath\fR may be given as a UUID or as a datapath name (reporting an error if multiple @@ -177,6 +177,19 @@ flow.) If \fB\-\-uuid\fR is specified, the output includes the first 32 bits of each logical flow's UUID. This makes it easier to find the OpenFlow flows that correspond to a given logical flow. +.IP +If \fB\-\-ovs\fR is included, \fBovn\-sbctl\fR attempts to obtain and +display the OpenFlow flows that correspond to each OVN logical flow. +To do so, \fBovn\-sbctl\fR connects to \fIremote\fR (by default, +\fBunix:@RUNDIR@/br-int.mgmt\fR) over OpenFlow and retrieves the +flows. If \fIremote\fR is specified, it must be an active OpenFlow +connection method described in \fBovs\-ofctl\fR(8). Please see the +discussion of the similar \fB\-\-ovs\fR option in \fBovn-trace\fR(8) +for more information about the OpenFlow flow output. +.IP +By default, OpenFlow flow output includes only match and actions. Add +\fB\-\-stats\fR to include all OpenFlow information, such as packet +and byte counters, duration, and timeouts. . .IP "[\fB\-\-uuid\fR] \fBdump\-flows\fR [\fIlogical-datapath\fR]" Alias for \fBlflow\-list\fB. diff --git a/ovn/utilities/ovn-sbctl.c b/ovn/utilities/ovn-sbctl.c index 69f461176572..97354df2da94 100644 --- a/ovn/utilities/ovn-sbctl.c +++ b/ovn/utilities/ovn-sbctl.c @@ -27,6 +27,7 @@ #include #include +#include "colors.h" #include "command-line.h" #include "compiler.h" #include "db-ctl-base.h" @@ -34,7 +35,10 @@ #include "fatal-signal.h" #include "openvswitch/dynamic-string.h" #include "openvswitch/json.h" +#include "openvswitch/ofp-actions.h" +#include "openvswitch/ofp-print.h" #include "openvswitch/shash.h" +#include "openvswitch/vconn.h" #include "openvswitch/vlog.h" #include "ovn/lib/ovn-sb-idl.h" #include "ovn/lib/ovn-util.h" @@ -736,6 +740,81 @@ is_partial_uuid_match(const struct uuid *uuid, const char *match) return !strncmp(s1, s2, strlen(s2)); } +static char * +default_ovs(void) +{ + return xasprintf("unix:%s/br-int.mgmt", ovs_rundir()); +} + +static struct vconn * +sbctl_open_vconn(struct shash *options) +{ + struct shash_node *ovs = shash_find(options, "--ovs"); + if (!ovs) { + return NULL; + } + + char *remote = ovs->data ? xstrdup(ovs->data) : default_ovs(); + struct vconn *vconn; + int retval = vconn_open_block(remote, 1 << OFP13_VERSION, 0, &vconn); + if (retval) { + VLOG_WARN("%s: connection failed (%s)", remote, ovs_strerror(retval)); + } + free(remote); + return vconn; +} + +static void +sbctl_dump_openflow(struct vconn *vconn, const struct uuid *uuid, bool stats) +{ + struct ofputil_flow_stats_request fsr = { + .cookie = htonll(uuid->parts[0]), + .cookie_mask = OVS_BE64_MAX, + .out_port = OFPP_ANY, + .out_group = OFPG_ANY, + .table_id = OFPTT_ALL, + }; + + struct ofputil_flow_stats *fses; + size_t n_fses; + int error = vconn_dump_flows(vconn, &fsr, OFPUTIL_P_OF13_OXM, + &fses, &n_fses); + if (error) { + VLOG_WARN("%s: error obtaining flow stats (%s)", + vconn_get_name(vconn), ovs_strerror(error)); + return; + } + + if (n_fses) { + struct ds s = DS_EMPTY_INITIALIZER; + for (size_t i = 0; i < n_fses; i++) { + const struct ofputil_flow_stats *fs = &fses[i]; + + ds_clear(&s); + if (stats) { + ofp_print_flow_stats(&s, fs); + } else { + ds_put_format(&s, " %stable=%s%"PRIu8" ", + colors.special, colors.end, fs->table_id); + match_format(&fs->match, &s, OFP_DEFAULT_PRIORITY); + if (ds_last(&s) != ' ') { + ds_put_char(&s, ' '); + } + + ds_put_format(&s, "%sactions=%s", colors.actions, colors.end); + ofpacts_format(fs->ofpacts, fs->ofpacts_len, &s); + } + printf(" %s\n", ds_cstr(&s)); + } + ds_destroy(&s); + } + + for (size_t i = 0; i < n_fses; i++) { + free(CONST_CAST(struct ofpact *, fses[i].ofpacts)); + } + free(fses); +} + static void cmd_lflow_list(struct ctl_context *ctx) { @@ -759,6 +838,9 @@ cmd_lflow_list(struct ctl_context *ctx) ctx->argv[i] = s; } + struct vconn *vconn = sbctl_open_vconn(&ctx->options); + bool stats = shash_find(&ctx->options, "--stats") != NULL; + const struct sbrec_logical_flow **lflows = NULL; size_t n_flows = 0; size_t n_capacity = 0; @@ -824,9 +906,13 @@ cmd_lflow_list(struct ctl_context *ctx) lflow->table_id, smap_get_def(&lflow->external_ids, "stage-name", ""), lflow->priority, lflow->match, lflow->actions); + if (vconn) { + sbctl_dump_openflow(vconn, &lflow->header_.uuid, stats); + } prev = lflow; } + vconn_close(vconn); free(lflows); } @@ -1298,10 +1384,10 @@ static const struct ctl_command_syntax sbctl_commands[] = { /* Logical flow commands */ {"lflow-list", 0, INT_MAX, "[DATAPATH] [LFLOW...]", pre_get_info, cmd_lflow_list, NULL, - "--uuid", RO}, + "--uuid,--ovs?,--stats", RO}, {"dump-flows", 0, INT_MAX, "[DATAPATH] [LFLOW...]", pre_get_info, cmd_lflow_list, NULL, - "--uuid", RO}, /* Friendly alias for lflow-list */ + "--uuid,--ovs?,--stats", RO}, /* Friendly alias for lflow-list */ /* Connection commands. */ {"get-connection", 0, 0, "", pre_connection, cmd_get_connection, NULL, "", RO},