[ovs-dev] [PATCH v2 16/19] ovs-ofctl: Add 'bundle' command and unit testing.

Jarno Rajahalme jrajahalme at nicira.com
Fri Jun 5 19:47:01 UTC 2015


Thanks for the suggestons, all applied in the v3 series I’ll post soon.

  Jarno

> On May 20, 2015, at 11:26 AM, Romain Lenglet <romain.lenglet at oracle.com> wrote:
> 
> Jarno,
> Thanks a lot for this updated patch, this will be very useful.
> Sorry, I don’t feel comfortable reviewing the other patches.
> 
> Acked-by: Romain Lenglet <romain.lenglet at oracle.com>
> --  
> Romain Lenglet  
> 
> On May 18, 2015 at 4:26:12 PM, Jarno Rajahalme (jrajahalme at nicira.com(mailto:jrajahalme at nicira.com)) wrote:
> 
>> The new ovs-ofctl 'bundle' command accepts files similar to
>> 'add-flows', but each line can optionally start with 'add', 'modify',
>> 'delete', 'modify_strict', or 'delete_strict' keyword, so that
>> arbitrary flow table modifications may be specified in a single file.
>> The modifications in the file are executed as a single transaction
>> using a OpenFlow 1.4 bundle.
>> 
>> Similarly, all existing ovs-ofctl flow mod commands now take an
>> optional '--bundle' argument, which executes the flow mods as a single
>> transaction.
>> 
>> OpenFlow 1.4 requires bundles to support at least flow and port mods.
>> This implementation does not yet support port mods in bundles.
>> 
>> Another restriction is that the atomic transactions are not yet
>> supported.
>> 
>> Signed-off-by: Jarno Rajahalme  
>> ---
>> NEWS | 14 ++-
>> include/openvswitch/vconn.h | 3 +
>> lib/ofp-parse.c | 40 ++++++-
>> lib/ofp-parse.h | 6 +-
>> lib/ofp-util.c | 30 ++++++
>> lib/ofp-util.h | 2 +
>> lib/ofp-version-opt.c | 7 ++
>> lib/ofp-version-opt.h | 1 +
>> lib/vconn.c | 236 +++++++++++++++++++++++++++++++++++++----
>> tests/ofproto-macros.at | 10 ++
>> tests/ofproto.at | 244 +++++++++++++++++++++++++++++++++++++++++++
>> tests/ovs-ofctl.at | 107 +++++++++++++++++++
>> utilities/ovs-ofctl.8.in | 67 +++++++++---
>> utilities/ovs-ofctl.c | 110 ++++++++++++++++++-
>> 14 files changed, 834 insertions(+), 43 deletions(-)
>> 
>> diff --git a/NEWS b/NEWS
>> index a480607..ac60451 100644
>> --- a/NEWS
>> +++ b/NEWS
>> @@ -1,6 +1,6 @@
>> Post-v2.3.0
>> ---------------------
>> - - Added support for SFQ, FQ_CoDel and CoDel qdiscs.
>> + - Added support for SFQ, FQ_CoDel and CoDel qdiscs.
>> - Add bash command-line completion support for ovs-vsctl Please check
>> utilities/ovs-command-compgen.INSTALL.md for how to use.
>> - The MAC learning feature now includes per-port fairness to mitigate
>> @@ -27,6 +27,11 @@ Post-v2.3.0
>> commands are now redundant and will be removed in a future
>> release. See ovs-vswitchd(8) for details.
>> - OpenFlow:
>> + * OpenFlow 1.4 bundles are now supported, but for flow mod
>> + messages only. 'atomic' bundles are not yet supported, and
>> + 'ordered' bundles are trivially supported, as all bundled
>> + messages are executed in the order they were added to the
>> + bundle regardless of the presence of the 'ordered' flag.
>> * IPv6 flow label and neighbor discovery fields are now modifiable.
>> * OpenFlow 1.5 extended registers are now supported.
>> * The OpenFlow 1.5 actset_output field is now supported.
>> @@ -41,6 +46,13 @@ Post-v2.3.0
>> * A new Netronome extension to OpenFlow 1.5+ allows control over the
>> fields hashed for OpenFlow select groups. See "selection_method" and
>> related options in ovs-ofctl(8) for details.
>> + - ovs-ofctl has a new 'bundle' command that accepts a file of
>> + arbitrary flow mods as an input that are executed as a single
>> + transaction using the new OpenFlow 1.4 bundles support.
>> + - ovs-ofctl has a new '--bundle' option that makes the flow mod
>> + commands (add-flow, add-flows, mod-flows, del-flows, and
>> + replace-flows) use an OpenFlow 1.4 bundle to operate the
>> + modifications as a single transaction.
>> - ovs-pki: Changed message digest algorithm from MD5 to SHA-1 because
>> MD5 is no longer secure and some operating systems have started to disable
>> it in OpenSSL.
>> diff --git a/include/openvswitch/vconn.h b/include/openvswitch/vconn.h
>> index 3b157e1..f8b6655 100644
>> --- a/include/openvswitch/vconn.h
>> +++ b/include/openvswitch/vconn.h
>> @@ -55,6 +55,9 @@ int vconn_transact(struct vconn *, struct ofpbuf *, struct ofpbuf **);
>> int vconn_transact_noreply(struct vconn *, struct ofpbuf *, struct ofpbuf **);
>> int vconn_transact_multiple_noreply(struct vconn *, struct ovs_list *requests,
>> struct ofpbuf **replyp);
>> +int vconn_bundle_transact(struct vconn *, struct ovs_list *requests,
>> + uint16_t bundle_flags,
>> + void (*error_reporter)(const struct ofp_header *));
>> 
>> void vconn_run(struct vconn *);
>> void vconn_run_wait(struct vconn *);
>> diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c
>> index 126980c..3ed9937 100644
>> --- a/lib/ofp-parse.c
>> +++ b/lib/ofp-parse.c
>> @@ -257,6 +257,29 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string,
>> 
>> *usable_protocols = OFPUTIL_P_ANY;
>> 
>> + if (command == -2) {
>> + size_t len;
>> +
>> + string += strspn(string, " \t\r\n"); /* Skip white space. */
>> + len = strcspn(string, ", \t\r\n"); /* Get length of the first token. */
>> +
>> + if (!strncmp(string, "add", len)) {
>> + command = OFPFC_ADD;
>> + } else if (!strncmp(string, "delete", len)) {
>> + command = OFPFC_DELETE;
>> + } else if (!strncmp(string, "delete_strict", len)) {
>> + command = OFPFC_DELETE_STRICT;
>> + } else if (!strncmp(string, "modify", len)) {
>> + command = OFPFC_MODIFY;
>> + } else if (!strncmp(string, "modify_strict", len)) {
>> + command = OFPFC_MODIFY_STRICT;
>> + } else {
>> + len = 0;
>> + command = OFPFC_ADD;
>> + }
>> + string += len;
>> + }
>> +
>> switch (command) {
>> case -1:
>> fields = 0;
>> @@ -485,6 +508,10 @@ parse_ofp_str__(struct ofputil_flow_mod *fm, int command, char *string,
>> * constant for 'command'. To parse syntax for an OFPST_FLOW or
>> * OFPST_AGGREGATE (or NXST_FLOW or NXST_AGGREGATE), use -1 for 'command'.
>> *
>> + * if 'command' is given as -2, 'str_' may begin with a command name ("add”,
> 
> Nitpick: capitalize If.
> 
>> + * "modify", "delete", "modify_strict", or "delete_strict"). A missing command
>> + * name is treated as "add".
>> + *
>> * Returns NULL if successful, otherwise a malloc()'d string describing the
>> * error. The caller is responsible for freeing the returned string. */
>> char * OVS_WARN_UNUSED_RESULT
>> @@ -817,14 +844,19 @@ parse_flow_monitor_request(struct ofputil_flow_monitor_request *fmr,
>> /* Parses 'string' as an OFPT_FLOW_MOD or NXT_FLOW_MOD with command 'command'
>> * (one of OFPFC_*) into 'fm'.
>> *
>> + * if 'command' is given as -2, 'string' may begin with a command name ("add”,
> 
> Nitpick: capitalize If.
> 
>> + * "modify", "delete", "modify_strict", or "delete_strict"). A missing command
>> + * name is treated as "add".
>> + *
>> * Returns NULL if successful, otherwise a malloc()'d string describing the
>> * error. The caller is responsible for freeing the returned string. */
>> char * OVS_WARN_UNUSED_RESULT
>> parse_ofp_flow_mod_str(struct ofputil_flow_mod *fm, const char *string,
>> - uint16_t command,
>> + int command,
>> enum ofputil_protocol *usable_protocols)
>> {
>> char *error = parse_ofp_str(fm, command, string, usable_protocols);
>> +
>> if (!error) {
>> /* Normalize a copy of the match. This ensures that non-normalized
>> * flows get logged but doesn't affect what gets sent to the switch, so
>> @@ -882,10 +914,14 @@ parse_ofp_table_mod(struct ofputil_table_mod *tm, const char *table_id,
>> * type (one of OFPFC_*). Stores each flow_mod in '*fm', an array allocated
>> * on the caller's behalf, and the number of flow_mods in '*n_fms'.
>> *
>> + * if 'command' is given as -2, each line may start with a command name
> 
> Nitpick: capitalize If.
> 
>> + * ("add", "modify", "delete", "modify_strict", or "delete_strict"). A missing
>> + * command name is treated as "add".
>> + *
>> * Returns NULL if successful, otherwise a malloc()'d string describing the
>> * error. The caller is responsible for freeing the returned string. */
>> char * OVS_WARN_UNUSED_RESULT
>> -parse_ofp_flow_mod_file(const char *file_name, uint16_t command,
>> +parse_ofp_flow_mod_file(const char *file_name, int command,
>> struct ofputil_flow_mod **fms, size_t *n_fms,
>> enum ofputil_protocol *usable_protocols)
>> {
>> diff --git a/lib/ofp-parse.h b/lib/ofp-parse.h
>> index db30f43..f112603 100644
>> --- a/lib/ofp-parse.h
>> +++ b/lib/ofp-parse.h
>> @@ -1,5 +1,5 @@
>> /*
>> - * Copyright (c) 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
>> + * Copyright (c) 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc.
>> *
>> * Licensed under the Apache License, Version 2.0 (the "License");
>> * you may not use this file except in compliance with the License.
>> @@ -42,7 +42,7 @@ char *parse_ofp_str(struct ofputil_flow_mod *, int command, const char *str_,
>> OVS_WARN_UNUSED_RESULT;
>> 
>> char *parse_ofp_flow_mod_str(struct ofputil_flow_mod *, const char *string,
>> - uint16_t command,
>> + int command,
>> enum ofputil_protocol *usable_protocols)
>> OVS_WARN_UNUSED_RESULT;
>> 
>> @@ -51,7 +51,7 @@ char *parse_ofp_table_mod(struct ofputil_table_mod *,
>> enum ofputil_protocol *usable_protocols)
>> OVS_WARN_UNUSED_RESULT;
>> 
>> -char *parse_ofp_flow_mod_file(const char *file_name, uint16_t command,
>> +char *parse_ofp_flow_mod_file(const char *file_name, int command,
>> struct ofputil_flow_mod **fms, size_t *n_fms,
>> enum ofputil_protocol *usable_protocols)
>> OVS_WARN_UNUSED_RESULT;
>> diff --git a/lib/ofp-util.c b/lib/ofp-util.c
>> index 17a0c41..d3251ed 100644
>> --- a/lib/ofp-util.c
>> +++ b/lib/ofp-util.c
>> @@ -8748,6 +8748,36 @@ ofputil_decode_bundle_ctrl(const struct ofp_header *oh,
>> }
>> 
>> struct ofpbuf *
>> +ofputil_encode_bundle_ctrl_request(enum ofp_version ofp_version,
>> + struct ofputil_bundle_ctrl_msg *bc)
>> +{
>> + struct ofpbuf *request;
>> + struct ofp14_bundle_ctrl_msg *m;
>> +
>> + switch (ofp_version) {
>> + case OFP10_VERSION:
>> + case OFP11_VERSION:
>> + case OFP12_VERSION:
>> + case OFP13_VERSION:
>> + ovs_fatal(0, "bundles need OpenFlow 1.4 or later "
>> + "(\'-O OpenFlow14\')");
>> + case OFP14_VERSION:
>> + case OFP15_VERSION:
>> + request = ofpraw_alloc(OFPRAW_OFPT14_BUNDLE_CONTROL, ofp_version, 0);
>> + m = ofpbuf_put_zeros(request, sizeof *m);
>> +
>> + m->bundle_id = htonl(bc->bundle_id);
>> + m->type = htons(bc->type);
>> + m->flags = htons(bc->flags);
>> + break;
>> + default:
>> + OVS_NOT_REACHED();
>> + }
>> +
>> + return request;
>> +}
>> +
>> +struct ofpbuf *
>> ofputil_encode_bundle_ctrl_reply(const struct ofp_header *oh,
>> struct ofputil_bundle_ctrl_msg *msg)
>> {
>> diff --git a/lib/ofp-util.h b/lib/ofp-util.h
>> index efb5b18..644a679 100644
>> --- a/lib/ofp-util.h
>> +++ b/lib/ofp-util.h
>> @@ -1114,6 +1114,8 @@ enum ofptype;
>> enum ofperr ofputil_decode_bundle_ctrl(const struct ofp_header *,
>> struct ofputil_bundle_ctrl_msg *);
>> 
>> +struct ofpbuf *ofputil_encode_bundle_ctrl_request(enum ofp_version,
>> + struct ofputil_bundle_ctrl_msg *);
>> struct ofpbuf *ofputil_encode_bundle_ctrl_reply(const struct ofp_header *,
>> struct ofputil_bundle_ctrl_msg *);
>> 
>> diff --git a/lib/ofp-version-opt.c b/lib/ofp-version-opt.c
>> index 46fb45a..1cf57e1 100644
>> --- a/lib/ofp-version-opt.c
>> +++ b/lib/ofp-version-opt.c
>> @@ -27,6 +27,13 @@ mask_allowed_ofp_versions(uint32_t bitmap)
>> }
>> 
>> void
>> +add_allowed_ofp_versions(uint32_t bitmap)
> 
> Do you think it would be useful to use this in other places, e.g. limit to OF1.4+ if a flow mod contains a non-zero importance, OF1.1+ if out_group is used, etc.?
> 
>> +{
>> + assert_single_threaded();
>> + allowed_versions |= bitmap;
>> +}
>> +
>> +void
>> ofp_version_usage(void)
>> {
>> struct ds msg = DS_EMPTY_INITIALIZER;
>> diff --git a/lib/ofp-version-opt.h b/lib/ofp-version-opt.h
>> index 6bf5eed..82b4ccc 100644
>> --- a/lib/ofp-version-opt.h
>> +++ b/lib/ofp-version-opt.h
>> @@ -21,6 +21,7 @@
>> uint32_t get_allowed_ofp_versions(void);
>> void set_allowed_ofp_versions(const char *string);
>> void mask_allowed_ofp_versions(uint32_t);
>> +void add_allowed_ofp_versions(uint32_t);
>> void ofp_version_usage(void);
>> 
>> #endif
>> diff --git a/lib/vconn.c b/lib/vconn.c
>> index c033b48..ee6fb9e 100644
>> --- a/lib/vconn.c
>> +++ b/lib/vconn.c
>> @@ -1,5 +1,5 @@
>> /*
>> - * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014 Nicira, Inc.
>> + * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc.
>> *
>> * Licensed under the Apache License, Version 2.0 (the "License");
>> * you may not use this file except in compliance with the License.
>> @@ -744,18 +744,15 @@ vconn_recv_block(struct vconn *vconn, struct ofpbuf **msgp)
>> return retval;
>> }
>> 
>> -/* Waits until a message with a transaction ID matching 'xid' is received on
>> - * 'vconn'. Returns 0 if successful, in which case the reply is stored in
>> - * '*replyp' for the caller to examine and free. Otherwise returns a positive
>> - * errno value, or EOF, and sets '*replyp' to null.
>> - *
>> - * 'request' is always destroyed, regardless of the return value. */
>> -int
>> -vconn_recv_xid(struct vconn *vconn, ovs_be32 xid, struct ofpbuf **replyp)
>> +static int
>> +vconn_recv_xid__(struct vconn *vconn, ovs_be32 xid, struct ofpbuf **replyp,
>> + void (*error_reporter)(const struct ofp_header *))
>> {
>> for (;;) {
>> ovs_be32 recv_xid;
>> struct ofpbuf *reply;
>> + const struct ofp_header *oh;
>> + enum ofptype type;
>> int error;
>> 
>> error = vconn_recv_block(vconn, &reply);
>> @@ -763,19 +760,54 @@ vconn_recv_xid(struct vconn *vconn, ovs_be32 xid, struct ofpbuf **replyp)
>> *replyp = NULL;
>> return error;
>> }
>> - recv_xid = ((struct ofp_header *) reply->data)->xid;
>> + oh = reply->data;
>> + recv_xid = oh->xid;
>> if (xid == recv_xid) {
>> *replyp = reply;
>> return 0;
>> }
>> 
>> - VLOG_DBG_RL(&bad_ofmsg_rl, "%s: received reply with xid %08"PRIx32
>> - " != expected %08"PRIx32,
>> - vconn->name, ntohl(recv_xid), ntohl(xid));
>> + error = ofptype_decode(&type, oh);
>> + if (!error && type == OFPTYPE_ERROR && error_reporter) {
>> + error_reporter(oh);
>> + } else {
>> + VLOG_DBG_RL(&bad_ofmsg_rl, "%s: received reply with xid %08"PRIx32
>> + " != expected %08"PRIx32,
>> + vconn->name, ntohl(recv_xid), ntohl(xid));
>> + }
>> ofpbuf_delete(reply);
>> }
>> }
>> 
>> +/* Waits until a message with a transaction ID matching 'xid' is received on
>> + * 'vconn'. Returns 0 if successful, in which case the reply is stored in
>> + * '*replyp' for the caller to examine and free. Otherwise returns a positive
>> + * errno value, or EOF, and sets '*replyp' to null.
>> + *
>> + * 'request' is always destroyed, regardless of the return value. */
>> +int
>> +vconn_recv_xid(struct vconn *vconn, ovs_be32 xid, struct ofpbuf **replyp)
>> +{
>> + return vconn_recv_xid__(vconn, xid, replyp, NULL);
>> +}
>> +
>> +static int
>> +vconn_transact__(struct vconn *vconn, struct ofpbuf *request,
>> + struct ofpbuf **replyp,
>> + void (*error_reporter)(const struct ofp_header *))
>> +{
>> + ovs_be32 send_xid = ((struct ofp_header *) request->data)->xid;
>> + int error;
>> +
>> + *replyp = NULL;
>> + error = vconn_send_block(vconn, request);
>> + if (error) {
>> + ofpbuf_delete(request);
>> + }
>> + return error ? error : vconn_recv_xid__(vconn, send_xid, replyp,
>> + error_reporter);
>> +}
>> +
>> /* Sends 'request' to 'vconn' and blocks until it receives a reply with a
>> * matching transaction ID. Returns 0 if successful, in which case the reply
>> * is stored in '*replyp' for the caller to examine and free. Otherwise
>> @@ -790,15 +822,7 @@ int
>> vconn_transact(struct vconn *vconn, struct ofpbuf *request,
>> struct ofpbuf **replyp)
>> {
>> - ovs_be32 send_xid = ((struct ofp_header *) request->data)->xid;
>> - int error;
>> -
>> - *replyp = NULL;
>> - error = vconn_send_block(vconn, request);
>> - if (error) {
>> - ofpbuf_delete(request);
>> - }
>> - return error ? error : vconn_recv_xid(vconn, send_xid, replyp);
>> + return vconn_transact__(vconn, request, replyp, NULL);
>> }
>> 
>> /* Sends 'request' followed by a barrier request to 'vconn', then blocks until
>> @@ -897,6 +921,174 @@ vconn_transact_multiple_noreply(struct vconn *vconn, struct ovs_list *requests,
>> return 0;
>> }
>> 
>> +static enum ofperr
>> +vconn_bundle_reply_validate(struct ofpbuf *reply, enum ofp_version version,
>> + struct ofputil_bundle_ctrl_msg *request,
>> + void (*error_reporter)(const struct ofp_header *))
>> +{
>> + const struct ofp_header *oh;
>> + enum ofptype type;
>> + enum ofperr error;
>> + struct ofputil_bundle_ctrl_msg rbc;
>> +
>> + oh = reply->data;
>> + if (oh->version != version) {
>> + return OFPERR_OFPBRC_BAD_VERSION;
>> + }
>> +
>> + error = ofptype_decode(&type, oh);
>> + if (error) {
>> + return error;
>> + }
>> +
>> + if (type == OFPTYPE_ERROR) {
>> + error_reporter(oh);
>> + error = ofperr_decode_msg(oh, NULL);
>> + return error;
>> + }
>> + if (type != OFPTYPE_BUNDLE_CONTROL) {
>> + return OFPERR_OFPBRC_BAD_TYPE;
>> + }
>> +
>> + error = ofputil_decode_bundle_ctrl(oh, &rbc);
>> + if (error) {
>> + return error;
>> + }
>> +
>> + if (rbc.bundle_id != request->bundle_id) {
>> + return OFPERR_OFPBFC_BAD_ID;
>> + }
>> +
>> + if (rbc.type != request->type + 1) {
>> + return OFPERR_OFPBFC_BAD_TYPE;
>> + }
>> +
>> + return 0;
>> +}
>> +
>> +static int
>> +vconn_bundle_control_transact(struct vconn *vconn,
>> + struct ofputil_bundle_ctrl_msg *bc,
>> + uint16_t type,
>> + void (*error_reporter)(const struct ofp_header *))
>> +{
>> + struct ofpbuf *request, *reply;
>> + int error;
>> + enum ofperr ofperr;
>> +
>> + bc->type = type;
>> + request = ofputil_encode_bundle_ctrl_request(vconn->version, bc);
>> + ofpmsg_update_length(request);
>> + error = vconn_transact__(vconn, request, &reply, error_reporter);
>> + if (error) {
>> + return error;
>> + }
>> +
>> + ofperr = vconn_bundle_reply_validate(reply, vconn->version, bc,
>> + error_reporter);
>> + if (ofperr) {
>> + VLOG_WARN_RL(&bad_ofmsg_rl, "Bundle %s failed (%s).",
>> + type == OFPBCT_OPEN_REQUEST ? "open"
>> + : type == OFPBCT_CLOSE_REQUEST ? "close"
>> + : type == OFPBCT_COMMIT_REQUEST ? "commit"
>> + : type == OFPBCT_DISCARD_REQUEST ? "discard"
>> + : "control message",
>> + ofperr_to_string(ofperr));
>> + }
>> + ofpbuf_delete(reply);
>> +
>> + return ofperr ? EPROTO : 0;
>> +}
>> +
>> +/* Checks if error responses can be received on
>> + * 'vconn'. */
>> +static void
>> +vconn_recv_error(struct vconn *vconn,
>> + void (*error_reporter)(const struct ofp_header *))
>> +{
>> + struct ofpbuf *reply;
>> + int error;
>> +
>> + error = vconn_recv(vconn, &reply);
>> + if (!error) {
>> + const struct ofp_header *oh;
>> + enum ofptype type;
>> + enum ofperr ofperr;
>> +
>> + oh = reply->data;
>> + ofperr = ofptype_decode(&type, oh);
>> + if (!ofperr && type == OFPTYPE_ERROR) {
>> + error_reporter(oh);
>> + } else {
>> + VLOG_DBG_RL(&bad_ofmsg_rl,
>> + "%s: received unexpected reply with xid %08"PRIx32,
>> + vconn->name, ntohl(oh->xid));
>> + }
>> + ofpbuf_delete(reply);
>> + }
>> +}
>> +
>> +static int
>> +vconn_bundle_add_msg(struct vconn *vconn, struct ofputil_bundle_ctrl_msg *bc,
>> + struct ofpbuf *msg,
>> + void (*error_reporter)(const struct ofp_header *))
>> +{
>> + struct ofputil_bundle_add_msg bam;
>> + struct ofpbuf *request;
>> + int error;
>> +
>> + bam.bundle_id = bc->bundle_id;
>> + bam.flags = bc->flags;
>> + bam.msg = msg->data;
>> +
>> + request = ofputil_encode_bundle_add(vconn->version, &bam);
>> + ofpmsg_update_length(request);
>> +
>> + error = vconn_send_block(vconn, request);
>> + if (!error) {
>> + /* Check for an error return, so that the socket buffer does not become
>> + * full of errors. */
>> + vconn_recv_error(vconn, error_reporter);
>> + }
>> + return error;
>> +}
>> +
>> +int
>> +vconn_bundle_transact(struct vconn *vconn, struct ovs_list *requests,
>> + uint16_t flags,
>> + void (*error_reporter)(const struct ofp_header *))
>> +{
>> + struct ofputil_bundle_ctrl_msg bc;
>> + struct ofpbuf *request;
>> + int error;
>> +
>> + memset(&bc, 0, sizeof bc);
>> + bc.flags = flags;
>> + error = vconn_bundle_control_transact(vconn, &bc, OFPBCT_OPEN_REQUEST,
>> + error_reporter);
>> + if (error) {
>> + return error;
>> + }
>> +
>> + LIST_FOR_EACH (request, list_node, requests) {
>> + error = vconn_bundle_add_msg(vconn, &bc, request, error_reporter);
>> + if (error) {
>> + break;
>> + }
>> + }
>> +
>> + if (!error) {
>> + error = vconn_bundle_control_transact(vconn, &bc,
>> + OFPBCT_COMMIT_REQUEST,
>> + error_reporter);
>> + } else {
>> + error = vconn_bundle_control_transact(vconn, &bc,
>> + OFPBCT_DISCARD_REQUEST,
>> + error_reporter);
>> + }
>> + return error;
>> +}
>> +
>> void
>> vconn_wait(struct vconn *vconn, enum vconn_wait_type wait)
>> {
>> diff --git a/tests/ofproto-macros.at b/tests/ofproto-macros.at
>> index fd915ef..65bfe40 100644
>> --- a/tests/ofproto-macros.at
>> +++ b/tests/ofproto-macros.at
>> @@ -15,6 +15,16 @@ s/ hard_age=[0-9]*,//
>> '
>> }
>> 
>> +# Filter (multiline) vconn debug messages from ovs-vswitchd.log.
>> +# Use with ofctl_strip()
>> +print_vconn_debug () { awk -F\| < ovs-vswitchd.log '
>> +BEGIN { prt=0 }
>> +/\|vconn\|DBG\|/ { sub(/[ \t]*$/, ""); print $3 "|" $4 "|" $5; prt=1; next }
>> +$4 != "" { prt=0; next }
>> +prt==1 { sub(/[ \t]*$/, ""); print $0 }
>> +'
>> +}
>> +
>> # parse_listening_port [SERVER]
>> #
>> # Parses the TCP or SSL port on which a server is listening from the
>> diff --git a/tests/ofproto.at b/tests/ofproto.at
>> index 9729a7c..e6ede1a 100644
>> --- a/tests/ofproto.at
>> +++ b/tests/ofproto.at
>> @@ -3450,3 +3450,247 @@ OFPT_BARRIER_REPLY (OF1.4):
>> 
>> OVS_VSWITCHD_STOP
>> AT_CLEANUP
>> +
>> +
>> +AT_SETUP([ofproto - bundle with multiple flow mods (OpenFlow 1.4)])
>> +AT_KEYWORDS([monitor])
>> +OVS_VSWITCHD_START
>> +
>> +AT_CHECK([ovs-appctl vlog/set vconn:dbg])
>> +
>> +AT_CHECK([ovs-ofctl del-flows br0])
>> +
>> +AT_DATA([flows.txt], [dnl
>> +add idle_timeout=50 in_port=2 dl_src=00:66:77:88:99:aa actions=1
>> +add idle_timeout=60 in_port=2 dl_src=00:77:88:99:aa:bb actions=2
>> +add idle_timeout=70 in_port=2 dl_src=00:88:99:aa:bb:cc actions=3
>> +add idle_timeout=50 in_port=2 dl_src=00:66:77:88:99:aa actions=4
>> +delete
>> +add idle_timeout=50 in_port=2 dl_src=00:66:77:88:99:aa actions=5
>> +add idle_timeout=60 in_port=2 dl_src=00:77:88:99:aa:bb actions=6
>> +add idle_timeout=70 in_port=2 dl_src=00:88:99:aa:bb:cc actions=7
>> +delete in_port=2 dl_src=00:88:99:aa:bb:cc
>> +])
>> +
>> +AT_CHECK([ovs-ofctl bundle br0 flows.txt])
>> +
>> +AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
>> + idle_timeout=50, in_port=2,dl_src=00:66:77:88:99:aa actions=output:5
>> + idle_timeout=60, in_port=2,dl_src=00:77:88:99:aa:bb actions=output:6
>> +NXST_FLOW reply:
>> +])
>> +
>> +AT_DATA([flows.txt], [dnl
>> +modify actions=drop
>> +modify_strict in_port=2 dl_src=00:77:88:99:aa:bb actions=7
>> +])
>> +
>> +AT_CHECK([ovs-ofctl bundle br0 flows.txt])
>> +
>> +AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
>> + idle_timeout=50, in_port=2,dl_src=00:66:77:88:99:aa actions=drop
>> + idle_timeout=60, in_port=2,dl_src=00:77:88:99:aa:bb actions=output:7
>> +NXST_FLOW reply:
>> +])
>> +
>> +# Adding an existing flow acts as a modify, and delete_strict also works.
>> +AT_DATA([flows.txt], [dnl
>> +add idle_timeout=60 in_port=2 dl_src=00:77:88:99:aa:bb actions=8
>> +delete_strict in_port=2 dl_src=00:66:77:88:99:aa
>> +add in_port=2 dl_src=00:66:77:88:99:aa actions=drop
>> +])
>> +
>> +AT_CHECK([ovs-ofctl bundle br0 flows.txt])
>> +
>> +AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
>> + idle_timeout=60, in_port=2,dl_src=00:77:88:99:aa:bb actions=output:8
>> + in_port=2,dl_src=00:66:77:88:99:aa actions=drop
>> +NXST_FLOW reply:
>> +])
>> +
>> +dnl Check logs for OpenFlow trace
>> +# Prevent race.
>> +OVS_WAIT_UNTIL([test `grep -- "|vconn|DBG|unix: sent (Success): NXST_FLOW reply" ovs-vswitchd.log | wc -l` -ge 3])
>> +AT_CHECK([print_vconn_debug | ofctl_strip], [0], [dnl
>> +vconn|DBG|unix: sent (Success): OFPT_HELLO (OF1.5):
>> + version bitmap: 0x01, 0x02, 0x03, 0x04, 0x05, 0x06
>> +vconn|DBG|unix: received: OFPT_HELLO:
>> + version bitmap: 0x01
>> +vconn|DBG|unix: negotiated OpenFlow version 0x01 (we support version 0x06 and earlier, peer supports version 0x01)
>> +vconn|DBG|unix: received: OFPT_FLOW_MOD: DEL actions=drop
>> +vconn|DBG|unix: received: OFPT_BARRIER_REQUEST:
>> +vconn|DBG|unix: sent (Success): OFPT_BARRIER_REPLY:
>> +vconn|DBG|unix: sent (Success): OFPT_HELLO (OF1.5):
>> + version bitmap: 0x01, 0x02, 0x03, 0x04, 0x05, 0x06
>> +vconn|DBG|unix: received: OFPT_HELLO (OF1.4):
>> + version bitmap: 0x01, 0x05
>> +vconn|DBG|unix: negotiated OpenFlow version 0x05 (we support version 0x06 and earlier, peer supports versions 0x01, 0x05)
>> +vconn|DBG|unix: received: OFPT_BUNDLE_CONTROL (OF1.4):
>> + bundle_id=0 type=OPEN_REQUEST flags=ordered
>> +vconn|DBG|unix: sent (Success): OFPT_BUNDLE_CONTROL (OF1.4):
>> + bundle_id=0 type=OPEN_REPLY flags=0
>> +vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4):
>> + bundle_id=0 flags=ordered
>> +OFPT_FLOW_MOD (OF1.4): ADD in_port=2,dl_src=00:66:77:88:99:aa idle:50 actions=output:1
>> +vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4):
>> + bundle_id=0 flags=ordered
>> +OFPT_FLOW_MOD (OF1.4): ADD in_port=2,dl_src=00:77:88:99:aa:bb idle:60 actions=output:2
>> +vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4):
>> + bundle_id=0 flags=ordered
>> +OFPT_FLOW_MOD (OF1.4): ADD in_port=2,dl_src=00:88:99:aa:bb:cc idle:70 actions=output:3
>> +vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4):
>> + bundle_id=0 flags=ordered
>> +OFPT_FLOW_MOD (OF1.4): ADD in_port=2,dl_src=00:66:77:88:99:aa idle:50 actions=output:4
>> +vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4):
>> + bundle_id=0 flags=ordered
>> +OFPT_FLOW_MOD (OF1.4): DEL table:255 actions=drop
>> +vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4):
>> + bundle_id=0 flags=ordered
>> +OFPT_FLOW_MOD (OF1.4): ADD in_port=2,dl_src=00:66:77:88:99:aa idle:50 actions=output:5
>> +vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4):
>> + bundle_id=0 flags=ordered
>> +OFPT_FLOW_MOD (OF1.4): ADD in_port=2,dl_src=00:77:88:99:aa:bb idle:60 actions=output:6
>> +vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4):
>> + bundle_id=0 flags=ordered
>> +OFPT_FLOW_MOD (OF1.4): ADD in_port=2,dl_src=00:88:99:aa:bb:cc idle:70 actions=output:7
>> +vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4):
>> + bundle_id=0 flags=ordered
>> +OFPT_FLOW_MOD (OF1.4): DEL table:255 in_port=2,dl_src=00:88:99:aa:bb:cc actions=drop
>> +vconn|DBG|unix: received: OFPT_BUNDLE_CONTROL (OF1.4):
>> + bundle_id=0 type=COMMIT_REQUEST flags=ordered
>> +vconn|DBG|unix: sent (Success): OFPT_BUNDLE_CONTROL (OF1.4):
>> + bundle_id=0 type=COMMIT_REPLY flags=0
>> +vconn|DBG|unix: sent (Success): OFPT_HELLO (OF1.5):
>> + version bitmap: 0x01, 0x02, 0x03, 0x04, 0x05, 0x06
>> +vconn|DBG|unix: received: OFPT_HELLO:
>> + version bitmap: 0x01
>> +vconn|DBG|unix: negotiated OpenFlow version 0x01 (we support version 0x06 and earlier, peer supports version 0x01)
>> +vconn|DBG|unix: received: NXT_SET_FLOW_FORMAT: format=nxm
>> +vconn|DBG|unix: received: OFPT_BARRIER_REQUEST:
>> +vconn|DBG|unix: sent (Success): OFPT_BARRIER_REPLY:
>> +vconn|DBG|unix: received: NXST_FLOW request:
>> +vconn|DBG|unix: sent (Success): NXST_FLOW reply:
>> + idle_timeout=50, in_port=2,dl_src=00:66:77:88:99:aa actions=output:5
>> + idle_timeout=60, in_port=2,dl_src=00:77:88:99:aa:bb actions=output:6
>> +vconn|DBG|unix: sent (Success): OFPT_HELLO (OF1.5):
>> + version bitmap: 0x01, 0x02, 0x03, 0x04, 0x05, 0x06
>> +vconn|DBG|unix: received: OFPT_HELLO (OF1.4):
>> + version bitmap: 0x01, 0x05
>> +vconn|DBG|unix: negotiated OpenFlow version 0x05 (we support version 0x06 and earlier, peer supports versions 0x01, 0x05)
>> +vconn|DBG|unix: received: OFPT_BUNDLE_CONTROL (OF1.4):
>> + bundle_id=0 type=OPEN_REQUEST flags=ordered
>> +vconn|DBG|unix: sent (Success): OFPT_BUNDLE_CONTROL (OF1.4):
>> + bundle_id=0 type=OPEN_REPLY flags=0
>> +vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4):
>> + bundle_id=0 flags=ordered
>> +OFPT_FLOW_MOD (OF1.4): MOD actions=drop
>> +vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4):
>> + bundle_id=0 flags=ordered
>> +OFPT_FLOW_MOD (OF1.4): MOD_STRICT in_port=2,dl_src=00:77:88:99:aa:bb actions=output:7
>> +vconn|DBG|unix: received: OFPT_BUNDLE_CONTROL (OF1.4):
>> + bundle_id=0 type=COMMIT_REQUEST flags=ordered
>> +vconn|DBG|unix: sent (Success): OFPT_BUNDLE_CONTROL (OF1.4):
>> + bundle_id=0 type=COMMIT_REPLY flags=0
>> +vconn|DBG|unix: sent (Success): OFPT_HELLO (OF1.5):
>> + version bitmap: 0x01, 0x02, 0x03, 0x04, 0x05, 0x06
>> +vconn|DBG|unix: received: OFPT_HELLO:
>> + version bitmap: 0x01
>> +vconn|DBG|unix: negotiated OpenFlow version 0x01 (we support version 0x06 and earlier, peer supports version 0x01)
>> +vconn|DBG|unix: received: NXT_SET_FLOW_FORMAT: format=nxm
>> +vconn|DBG|unix: received: OFPT_BARRIER_REQUEST:
>> +vconn|DBG|unix: sent (Success): OFPT_BARRIER_REPLY:
>> +vconn|DBG|unix: received: NXST_FLOW request:
>> +vconn|DBG|unix: sent (Success): NXST_FLOW reply:
>> + idle_timeout=50, in_port=2,dl_src=00:66:77:88:99:aa actions=drop
>> + idle_timeout=60, in_port=2,dl_src=00:77:88:99:aa:bb actions=output:7
>> +vconn|DBG|unix: sent (Success): OFPT_HELLO (OF1.5):
>> + version bitmap: 0x01, 0x02, 0x03, 0x04, 0x05, 0x06
>> +vconn|DBG|unix: received: OFPT_HELLO (OF1.4):
>> + version bitmap: 0x01, 0x05
>> +vconn|DBG|unix: negotiated OpenFlow version 0x05 (we support version 0x06 and earlier, peer supports versions 0x01, 0x05)
>> +vconn|DBG|unix: received: OFPT_BUNDLE_CONTROL (OF1.4):
>> + bundle_id=0 type=OPEN_REQUEST flags=ordered
>> +vconn|DBG|unix: sent (Success): OFPT_BUNDLE_CONTROL (OF1.4):
>> + bundle_id=0 type=OPEN_REPLY flags=0
>> +vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4):
>> + bundle_id=0 flags=ordered
>> +OFPT_FLOW_MOD (OF1.4): ADD in_port=2,dl_src=00:77:88:99:aa:bb idle:60 actions=output:8
>> +vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4):
>> + bundle_id=0 flags=ordered
>> +OFPT_FLOW_MOD (OF1.4): DEL_STRICT table:255 in_port=2,dl_src=00:66:77:88:99:aa actions=drop
>> +vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4):
>> + bundle_id=0 flags=ordered
>> +OFPT_FLOW_MOD (OF1.4): ADD in_port=2,dl_src=00:66:77:88:99:aa actions=drop
>> +vconn|DBG|unix: received: OFPT_BUNDLE_CONTROL (OF1.4):
>> + bundle_id=0 type=COMMIT_REQUEST flags=ordered
>> +vconn|DBG|unix: sent (Success): OFPT_BUNDLE_CONTROL (OF1.4):
>> + bundle_id=0 type=COMMIT_REPLY flags=0
>> +vconn|DBG|unix: sent (Success): OFPT_HELLO (OF1.5):
>> + version bitmap: 0x01, 0x02, 0x03, 0x04, 0x05, 0x06
>> +vconn|DBG|unix: received: OFPT_HELLO:
>> + version bitmap: 0x01
>> +vconn|DBG|unix: negotiated OpenFlow version 0x01 (we support version 0x06 and earlier, peer supports version 0x01)
>> +vconn|DBG|unix: received: NXT_SET_FLOW_FORMAT: format=nxm
>> +vconn|DBG|unix: received: OFPT_BARRIER_REQUEST:
>> +vconn|DBG|unix: sent (Success): OFPT_BARRIER_REPLY:
>> +vconn|DBG|unix: received: NXST_FLOW request:
>> +vconn|DBG|unix: sent (Success): NXST_FLOW reply:
>> + idle_timeout=60, in_port=2,dl_src=00:77:88:99:aa:bb actions=output:8
>> + in_port=2,dl_src=00:66:77:88:99:aa actions=drop
>> +])
>> +
>> +OVS_VSWITCHD_STOP
>> +AT_CLEANUP
>> +
>> +
>> +AT_SETUP([ofproto - failing bundle commit (OpenFlow 1.4)])
>> +AT_KEYWORDS([monitor])
>> +OVS_VSWITCHD_START
>> +
>> +AT_CHECK([ovs-ofctl del-flows br0])
>> +
>> +ovs-ofctl add-flows br0 - <> +idle_timeout=50 in_port=2 dl_src=00:66:77:88:99:aa actions=11
>> +idle_timeout=60 in_port=2 dl_src=00:77:88:99:aa:bb actions=22
>> +idle_timeout=70 in_port=2 dl_src=00:88:99:aa:bb:cc actions=33
>> +EOF
>> +AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
>> + idle_timeout=50, in_port=2,dl_src=00:66:77:88:99:aa actions=output:11
>> + idle_timeout=60, in_port=2,dl_src=00:77:88:99:aa:bb actions=output:22
>> + idle_timeout=70, in_port=2,dl_src=00:88:99:aa:bb:cc actions=output:33
>> +NXST_FLOW reply:
>> +])
>> +
>> +# last line uses illegal table number (OVS internal table)
>> +AT_DATA([flows.txt], [dnl
>> +add idle_timeout=50 in_port=2 dl_src=00:66:77:88:99:aa actions=1
>> +add idle_timeout=60 in_port=2 dl_src=00:77:88:99:aa:bb actions=2
>> +add idle_timeout=70 in_port=2 dl_src=00:88:99:aa:bb:cc actions=3
>> +modify idle_timeout=50 in_port=2 dl_src=00:66:77:88:99:aa actions=4
>> +delete
>> +add idle_timeout=50 in_port=2 dl_src=00:66:77:88:99:aa actions=5
>> +add idle_timeout=60 in_port=2 dl_src=00:77:88:99:aa:bb actions=6
>> +add idle_timeout=70 in_port=2 dl_src=00:88:99:aa:bb:cc actions=7
>> +delete in_port=2 dl_src=00:88:99:aa:bb:cc
>> +add table=254 actions=drop
>> +])
>> +
>> +AT_CHECK([ovs-ofctl bundle br0 flows.txt 2>&1 | sed '/|WARN|/d
>> +s/unix:.*br0\.mgmt/unix:br0.mgmt/'], [0], [dnl
>> +OFPT_ERROR (OF1.4) (xid=0xb): OFPBRC_EPERM
>> +OFPT_FLOW_MOD (OF1.4) (xid=0xb): ADD table:254 actions=drop
>> +OFPT_ERROR (OF1.4) (xid=0xd): OFPBFC_MSG_FAILED
>> +OFPT_BUNDLE_CONTROL (OF1.4) (xid=0xd):
>> + bundle_id=0 type=COMMIT_REQUEST flags=ordered
>> +ovs-ofctl: talking to unix:br0.mgmt (Protocol error)
>> +])
>> +
>> +AT_CHECK([ovs-ofctl dump-flows br0 | ofctl_strip | sort], [0], [dnl
>> + idle_timeout=50, in_port=2,dl_src=00:66:77:88:99:aa actions=output:11
>> + idle_timeout=60, in_port=2,dl_src=00:77:88:99:aa:bb actions=output:22
>> + idle_timeout=70, in_port=2,dl_src=00:88:99:aa:bb:cc actions=output:33
>> +NXST_FLOW reply:
>> +])
>> +
>> +OVS_VSWITCHD_STOP
>> +AT_CLEANUP
>> diff --git a/tests/ovs-ofctl.at b/tests/ovs-ofctl.at
>> index 42be8f0..d2164bb 100644
>> --- a/tests/ovs-ofctl.at
>> +++ b/tests/ovs-ofctl.at
>> @@ -2813,3 +2813,110 @@ AT_CHECK([ovs-ofctl -O OpenFlow14 dump-flows br0 | ofctl_strip | sed '/OFPST_FLO
>> 
>> OVS_VSWITCHD_STOP
>> AT_CLEANUP
>> +
>> +AT_SETUP([ovs-ofctl replace-flows with --bundle])
>> +OVS_VSWITCHD_START
>> +
>> +AT_CHECK([ovs-appctl vlog/set vconn:dbg])
>> +
>> +dnl Add flows to br0 with importance via OF1.4+, using an OF1.4+ bundle. For more details refer "ovs-ofctl rule with importance" test case.
>> +for i in 1 2 3 4 5 6 7 8; do echo "dl_vlan=$i,importance=$i,actions=drop"; done > add-flows.txt
>> +AT_CHECK([ovs-ofctl --bundle add-flows br0 add-flows.txt])
>> +
>> +dnl Replace some flows in the bridge.
>> +for i in 1 3 5 7; do echo "dl_vlan=$i,importance=`expr $i + 10`,actions=drop"; done > replace-flows.txt
>> +AT_CHECK([ovs-ofctl --bundle replace-flows br0 replace-flows.txt])
>> +
>> +dnl Dump them and compare the dump flows output against the expected output.
>> +for i in 1 2 3 4 5 6 7 8; do if [[ `expr $i % 2` -eq 1 ]]; then importance=`expr $i + 10`; else importance=$i; fi; echo " importance=$importance, dl_vlan=$i actions=drop"; done | sort > expout
>> +AT_CHECK([ovs-ofctl -O OpenFlow14 dump-flows br0 | ofctl_strip | sed '/OFPST_FLOW/d' | sort],
>> + [0], [expout])
>> +
>> +dnl Check logs for OpenFlow trace
>> +# Prevent race.
>> +OVS_WAIT_UNTIL([test `grep -- "|vconn|DBG|unix: sent (Success): OFPST_FLOW reply" ovs-vswitchd.log | wc -l` -ge 2])
>> +# AT_CHECK([sed -n "s/^.*\(|vconn|DBG|.*xid=.*:\).*$/\1/p" ovs-vswitchd.log], [0], [dnl
>> +AT_CHECK([print_vconn_debug | ofctl_strip], [0], [dnl
>> +vconn|DBG|unix: sent (Success): OFPT_HELLO (OF1.5):
>> + version bitmap: 0x01, 0x02, 0x03, 0x04, 0x05, 0x06
>> +vconn|DBG|unix: received: OFPT_HELLO (OF1.4):
>> + version bitmap: 0x01, 0x05
>> +vconn|DBG|unix: negotiated OpenFlow version 0x05 (we support version 0x06 and earlier, peer supports versions 0x01, 0x05)
>> +vconn|DBG|unix: received: OFPT_BUNDLE_CONTROL (OF1.4):
>> + bundle_id=0 type=OPEN_REQUEST flags=ordered
>> +vconn|DBG|unix: sent (Success): OFPT_BUNDLE_CONTROL (OF1.4):
>> + bundle_id=0 type=OPEN_REPLY flags=0
>> +vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4):
>> + bundle_id=0 flags=ordered
>> +OFPT_FLOW_MOD (OF1.4): ADD dl_vlan=1 importance:1 actions=drop
>> +vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4):
>> + bundle_id=0 flags=ordered
>> +OFPT_FLOW_MOD (OF1.4): ADD dl_vlan=2 importance:2 actions=drop
>> +vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4):
>> + bundle_id=0 flags=ordered
>> +OFPT_FLOW_MOD (OF1.4): ADD dl_vlan=3 importance:3 actions=drop
>> +vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4):
>> + bundle_id=0 flags=ordered
>> +OFPT_FLOW_MOD (OF1.4): ADD dl_vlan=4 importance:4 actions=drop
>> +vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4):
>> + bundle_id=0 flags=ordered
>> +OFPT_FLOW_MOD (OF1.4): ADD dl_vlan=5 importance:5 actions=drop
>> +vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4):
>> + bundle_id=0 flags=ordered
>> +OFPT_FLOW_MOD (OF1.4): ADD dl_vlan=6 importance:6 actions=drop
>> +vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4):
>> + bundle_id=0 flags=ordered
>> +OFPT_FLOW_MOD (OF1.4): ADD dl_vlan=7 importance:7 actions=drop
>> +vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4):
>> + bundle_id=0 flags=ordered
>> +OFPT_FLOW_MOD (OF1.4): ADD dl_vlan=8 importance:8 actions=drop
>> +vconn|DBG|unix: received: OFPT_BUNDLE_CONTROL (OF1.4):
>> + bundle_id=0 type=COMMIT_REQUEST flags=ordered
>> +vconn|DBG|unix: sent (Success): OFPT_BUNDLE_CONTROL (OF1.4):
>> + bundle_id=0 type=COMMIT_REPLY flags=0
>> +vconn|DBG|unix: sent (Success): OFPT_HELLO (OF1.5):
>> + version bitmap: 0x01, 0x02, 0x03, 0x04, 0x05, 0x06
>> +vconn|DBG|unix: received: OFPT_HELLO (OF1.4):
>> + version bitmap: 0x01, 0x05
>> +vconn|DBG|unix: negotiated OpenFlow version 0x05 (we support version 0x06 and earlier, peer supports versions 0x01, 0x05)
>> +vconn|DBG|unix: received: OFPST_FLOW request (OF1.4):
>> +vconn|DBG|unix: sent (Success): OFPST_FLOW reply (OF1.4):
>> +vconn|DBG|unix: received: OFPT_BUNDLE_CONTROL (OF1.4):
>> + bundle_id=0 type=OPEN_REQUEST flags=ordered
>> +vconn|DBG|unix: sent (Success): OFPT_BUNDLE_CONTROL (OF1.4):
>> + bundle_id=0 type=OPEN_REPLY flags=0
>> +vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4):
>> + bundle_id=0 flags=ordered
>> +OFPT_FLOW_MOD (OF1.4): ADD dl_vlan=1 importance:11 actions=drop
>> +vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4):
>> + bundle_id=0 flags=ordered
>> +OFPT_FLOW_MOD (OF1.4): ADD dl_vlan=3 importance:13 actions=drop
>> +vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4):
>> + bundle_id=0 flags=ordered
>> +OFPT_FLOW_MOD (OF1.4): ADD dl_vlan=5 importance:15 actions=drop
>> +vconn|DBG|unix: received: OFPT_BUNDLE_ADD_MESSAGE (OF1.4):
>> + bundle_id=0 flags=ordered
>> +OFPT_FLOW_MOD (OF1.4): ADD dl_vlan=7 importance:17 actions=drop
>> +vconn|DBG|unix: received: OFPT_BUNDLE_CONTROL (OF1.4):
>> + bundle_id=0 type=COMMIT_REQUEST flags=ordered
>> +vconn|DBG|unix: sent (Success): OFPT_BUNDLE_CONTROL (OF1.4):
>> + bundle_id=0 type=COMMIT_REPLY flags=0
>> +vconn|DBG|unix: sent (Success): OFPT_HELLO (OF1.5):
>> + version bitmap: 0x01, 0x02, 0x03, 0x04, 0x05, 0x06
>> +vconn|DBG|unix: received: OFPT_HELLO (OF1.4):
>> + version bitmap: 0x05
>> +vconn|DBG|unix: negotiated OpenFlow version 0x05 (we support version 0x06 and earlier, peer supports version 0x05)
>> +vconn|DBG|unix: received: OFPST_FLOW request (OF1.4):
>> +vconn|DBG|unix: sent (Success): OFPST_FLOW reply (OF1.4):
>> + importance=11, dl_vlan=1 actions=drop
>> + importance=2, dl_vlan=2 actions=drop
>> + importance=13, dl_vlan=3 actions=drop
>> + importance=4, dl_vlan=4 actions=drop
>> + importance=15, dl_vlan=5 actions=drop
>> + importance=6, dl_vlan=6 actions=drop
>> + importance=17, dl_vlan=7 actions=drop
>> + importance=8, dl_vlan=8 actions=drop
>> +])
>> +
>> +OVS_VSWITCHD_STOP
>> +AT_CLEANUP
>> diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in
>> index c667aa4..1805a64 100644
>> --- a/utilities/ovs-ofctl.8.in
>> +++ b/utilities/ovs-ofctl.8.in
>> @@ -296,29 +296,31 @@ Print meter features.
>> .
>> These commands manage the flow table in an OpenFlow switch. In each
>> case, \fIflow\fR specifies a flow entry in the format described in
>> -\fBFlow Syntax\fR, below, and \fIfile\fR is a text file that contains
>> -zero or more flows in the same syntax, one per line.
>> -.
>> -.IP "\fBadd\-flow \fIswitch flow\fR"
>> -.IQ "\fBadd\-flow \fIswitch \fB\- < \fIfile\fR"
>> -.IQ "\fBadd\-flows \fIswitch file\fR"
>> +\fBFlow Syntax\fR, below, \fIfile\fR is a text file that contains zero
>> +or more flows in the same syntax, one per line, and the optional
>> +\fB\-\-bundle\fR option operates the command as a single transation,
>> +see command \fBbundle\fR, below.
>> +.
>> +.IP "[\fB\-\-bundle\fR] \fBadd\-flow \fIswitch flow\fR"
>> +.IQ "[\fB\-\-bundle\fR] \fBadd\-flow \fIswitch \fB\- < \fIfile\fR"
>> +.IQ "[\fB\-\-bundle\fR] \fBadd\-flows \fIswitch file\fR"
>> Add each flow entry to \fIswitch\fR's tables.
>> .
>> -.IP "[\fB\-\-strict\fR] \fBmod\-flows \fIswitch flow\fR"
>> -.IQ "[\fB\-\-strict\fR] \fBmod\-flows \fIswitch \fB\- < \fIfile\fR"
>> +.IP "[\fB\-\-bundle\fR] [\fB\-\-strict\fR] \fBmod\-flows \fIswitch flow\fR"
>> +.IQ "[\fB\-\-bundle\fR] [\fB\-\-strict\fR] \fBmod\-flows \fIswitch \fB\- < \fIfile\fR"
>> Modify the actions in entries from \fIswitch\fR's tables that match
>> the specified flows. With \fB\-\-strict\fR, wildcards are not treated
>> as active for matching purposes.
>> .
>> -.IP "\fBdel\-flows \fIswitch\fR"
>> -.IQ "[\fB\-\-strict\fR] \fBdel\-flows \fIswitch \fR[\fIflow\fR]"
>> -.IQ "[\fB\-\-strict\fR] \fBdel\-flows \fIswitch \fB\- < \fIfile\fR"
>> +.IP "[\fB\-\-bundle\fR] \fBdel\-flows \fIswitch\fR"
>> +.IQ "[\fB\-\-bundle\fR] [\fB\-\-strict\fR] \fBdel\-flows \fIswitch \fR[\fIflow\fR]"
>> +.IQ "[\fB\-\-bundle\fR] [\fB\-\-strict\fR] \fBdel\-flows \fIswitch \fB\- < \fIfile\fR"
>> Deletes entries from \fIswitch\fR's flow table. With only a
>> \fIswitch\fR argument, deletes all flows. Otherwise, deletes flow
>> entries that match the specified flows. With \fB\-\-strict\fR,
>> wildcards are not treated as active for matching purposes.
>> .
>> -.IP "[\fB\-\-readd\fR] \fBreplace\-flows \fIswitch file\fR"
>> +.IP "[\fB\-\-bundle\fR] [\fB\-\-readd\fR] \fBreplace\-flows \fIswitch file\fR"
>> Reads flow entries from \fIfile\fR (or \fBstdin\fR if \fIfile\fR is
>> \fB\-\fR) and queries the flow table from \fIswitch\fR. Then it fixes
>> up any differences, adding flows from \fIflow\fR that are missing on
>> @@ -332,6 +334,44 @@ With \fB\-\-readd\fR, \fBovs\-ofctl\fR adds all the flows from
>> timeout in \fIswitch\fR. This resets all the flow packet and byte
>> counters to 0, which can be useful for debugging.
>> .
>> +.IP "\fBbundle \fIswitch \fIfile\fR"
>> +.IQ "\fBbundle \fIswitch \fB\- < \fIfile\fR"
>> +Adds, modifies, and/or deletes entries from \fIswitch\fR's flow table.
>> +\fIfile\fR is a text file where each line contains a flow in the same
>> +syntax as with \fBadd\-flows\fR, \fBmod\-flows\fR, and
>> +\fBdel\-flows\fR, except that the line may start with \fBadd\fR,
>> +\fBmodify\fR, \fBdelete\fR, \fBmodify_strict\fR, or
>> +\fBdelete_strict\fR keyword to specify whether a flow is to be added,
>> +modified, or deleted, and whether the modify or delete is strict or
>> +not. A line without one of these keywords is treated as a flow add.
>> +.
>> +.IP
>> +The following applies to the flow table modification commands with the
>> +\fB\-\-bundle\fR option as well:
>> +.
>> +.RS
>> +.IP -
>> +Within a bundle, all flow mods are processed in the order they appear
>> +and as a single transaction, meaning that if one of them fails, the
>> +whole transaction fails and none of the changes are made to the
>> +\fIswitch\fR's flow table. However, the \fIswitch\fR may perform
>> +resource management, such as evicting flows, that may not be unrolled
>> +even when the transaction fails.
>> +.
>> +.IP -
>> +The beginning and the end of the flow table modification commands in a
>> +bundle are delimited with OpenFlow 1.4 bundle control messages, which
>> +makes it possible to stream the included commands without explicit
>> +OpenFlow barriers, which are otherwise used after each flow table
>> +modification command. This may make large modifications execute
>> +faster as a bundle.
>> +.
>> +.IP -
>> +Bundles require OpenFlow 1.4 or higher. An explicit \fB-O
>> +OpenFlow14\fR option is not needed, but you may need to enable
>> +OpenFlow 1.4 support for OVS by setting the OVSDB \fIprotocols\fR
>> +column in the \fIbridge\fR table.
> 
> Maybe add a paragraph about the types of bundle that are supported?
> E.g. document that all bundles are ordered, but not atomic?
> 
>> +.RE
>> .IP "\fBdiff\-flows \fIsource1 source2\fR"
>> Reads flow entries from \fIsource1\fR and \fIsource2\fR and prints the
>> differences. A flow that is in \fIsource1\fR but not in \fIsource2\fR
>> @@ -2386,6 +2426,9 @@ depending on its configuration.
>> \fB\-\-strict\fR
>> Uses strict matching when running flow modification commands.
>> .
>> +.IP "\fB\-\-bundle\fR"
>> +Execute flow mods as an OpenFlow 1.4 bundle transaction.
>> +.
>> .so lib/ofp-version.man
>> .
>> .IP "\fB\-F \fIformat\fR[\fB,\fIformat\fR...]"
>> diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
>> index 54a5bb8..0b5f002 100644
>> --- a/utilities/ovs-ofctl.c
>> +++ b/utilities/ovs-ofctl.c
>> @@ -67,6 +67,12 @@
>> 
>> VLOG_DEFINE_THIS_MODULE(ofctl);
>> 
>> +/* --bundle: Use OpenFlow 1.4 bundle for making the flow table change atomic.
>> + * NOTE: Also the flow mod will use OpenFlow 1.4, so the semantics may be
>> + * different (see the comment in parse_options() for details).
>> + */
>> +static bool bundle = false;
>> +
>> /* --strict: Use strict matching for flow mod commands? Additionally governs
>> * use of nx_pull_match() instead of nx_pull_match_loose() in parse-nx-match.
>> */
>> @@ -159,6 +165,7 @@ parse_options(int argc, char *argv[])
>> OPT_SORT,
>> OPT_RSORT,
>> OPT_UNIXCTL,
>> + OPT_BUNDLE,
>> DAEMON_OPTION_ENUMS,
>> OFP_VERSION_OPTION_ENUMS,
>> VLOG_OPTION_ENUMS
>> @@ -176,6 +183,7 @@ parse_options(int argc, char *argv[])
>> {"unixctl", required_argument, NULL, OPT_UNIXCTL},
>> {"help", no_argument, NULL, 'h'},
>> {"option", no_argument, NULL, 'o'},
>> + {"bundle", no_argument, NULL, OPT_BUNDLE},
>> DAEMON_LONG_OPTIONS,
>> OFP_VERSION_LONG_OPTIONS,
>> VLOG_LONG_OPTIONS,
>> @@ -249,6 +257,10 @@ parse_options(int argc, char *argv[])
>> ovs_cmdl_print_options(long_options);
>> exit(EXIT_SUCCESS);
>> 
>> + case OPT_BUNDLE:
>> + bundle = true;
>> + break;
>> +
>> case OPT_STRICT:
>> strict = true;
>> break;
>> @@ -293,6 +305,12 @@ parse_options(int argc, char *argv[])
>> 
>> free(short_options);
>> 
>> + /* Implicit OpenFlow 1.4 with the '--bundle' option. */
>> + if (bundle) {
>> + /* Add implicit allowance for OpenFlow 1.4. */
>> + add_allowed_ofp_versions(ofputil_protocols_to_version_bitmap(
>> + OFPUTIL_P_OF14_OXM));
>> + }
>> versions = get_allowed_ofp_versions();
>> version_protocols = ofputil_protocols_from_version_bitmap(versions);
>> if (!(allowed_protocols & version_protocols)) {
>> @@ -602,6 +620,26 @@ transact_multiple_noreply(struct vconn *vconn, struct ovs_list *requests)
>> ofpbuf_delete(reply);
>> }
>> 
>> +static void
>> +bundle_error_reporter(const struct ofp_header *oh)
>> +{
>> + ofp_print(stderr, oh, ntohs(oh->length), verbosity + 1);
>> + fflush(stderr);
>> +}
>> +
>> +static void
>> +bundle_transact(struct vconn *vconn, struct ovs_list *requests, uint16_t flags)
>> +{
>> + struct ofpbuf *request;
>> +
>> + LIST_FOR_EACH (request, list_node, requests) {
>> + ofpmsg_update_length(request);
>> + }
>> +
>> + run(vconn_bundle_transact(vconn, requests, flags, bundle_error_reporter),
>> + "talking to %s", vconn_get_name(vconn));
>> +}
>> +
>> /* Sends 'request', which should be a request that only has a reply if an error
>> * occurs, and waits for it to succeed or fail. If an error does occur, prints
>> * it and exits with an error.
>> @@ -1194,6 +1232,10 @@ ofctl_flow_mod__(const char *remote, struct ofputil_flow_mod *fms,
>> }
>> 
>> static void
>> +bundle_flow_mod__(const char *remote, struct ofputil_flow_mod *fms,
>> + size_t n_fms, enum ofputil_protocol);
>> +
>> +static void
>> ofctl_flow_mod_file(int argc OVS_UNUSED, char *argv[], uint16_t command)
>> {
>> enum ofputil_protocol usable_protocols;
>> @@ -1206,7 +1248,11 @@ ofctl_flow_mod_file(int argc OVS_UNUSED, char *argv[], uint16_t command)
>> if (error) {
>> ovs_fatal(0, "%s", error);
>> }
>> - ofctl_flow_mod__(argv[1], fms, n_fms, usable_protocols);
>> + if (bundle) {
>> + bundle_flow_mod__(argv[1], fms, n_fms, usable_protocols);
>> + } else {
>> + ofctl_flow_mod__(argv[1], fms, n_fms, usable_protocols);
>> + }
>> free(fms);
>> }
>> 
>> @@ -1225,7 +1271,11 @@ ofctl_flow_mod(int argc, char *argv[], uint16_t command)
>> if (error) {
>> ovs_fatal(0, "%s", error);
>> }
>> - ofctl_flow_mod__(argv[1], &fm, 1, usable_protocols);
>> + if (bundle) {
>> + bundle_flow_mod__(argv[1], &fm, 1, usable_protocols);
>> + } else {
>> + ofctl_flow_mod__(argv[1], &fm, 1, usable_protocols);
>> + }
>> }
>> }
>> 
>> @@ -2262,6 +2312,55 @@ ofctl_dump_group_features(struct ovs_cmdl_context *ctx)
>> }
>> 
>> static void
>> +bundle_flow_mod__(const char *remote, struct ofputil_flow_mod *fms,
>> + size_t n_fms, enum ofputil_protocol usable_protocols)
>> +{
>> + enum ofputil_protocol protocol;
>> + struct vconn *vconn;
>> + struct ovs_list requests;
>> + size_t i;
>> +
>> + list_init(&requests);
>> +
>> + /* Add implicit allowance for OpenFlow 1.4. */
>> + allowed_protocols |= OFPUTIL_P_OF14_OXM;
>> + add_allowed_ofp_versions(ofputil_protocols_to_version_bitmap(
>> + OFPUTIL_P_OF14_OXM));
>> + usable_protocols &= OFPUTIL_P_OF14_UP;
>> + protocol = open_vconn_for_flow_mod(remote, &vconn, usable_protocols);
>> +
>> + for (i = 0; i < n_fms; i++) {
>> + struct ofputil_flow_mod *fm = &fms[i];
>> + struct ofpbuf *request = ofputil_encode_flow_mod(fm, protocol);
>> +
>> + list_push_back(&requests, &request->list_node);
>> + free(CONST_CAST(struct ofpact *, fm->ofpacts));
>> + }
>> +
>> + bundle_transact(vconn, &requests, OFPBF_ORDERED);
>> + vconn_close(vconn);
>> +}
>> +
>> +static void
>> +ofctl_bundle(struct ovs_cmdl_context *ctx)
>> +{
>> + enum ofputil_protocol usable_protocols;
>> + struct ofputil_flow_mod *fms = NULL;
>> + size_t n_fms = 0;
>> + char *error;
>> +
>> + /* Parse flow mods from the file. */
>> + error = parse_ofp_flow_mod_file(ctx->argv[2], -2, &fms, &n_fms,
>> + &usable_protocols);
>> + if (error) {
>> + ovs_fatal(0, "%s", error);
>> + }
>> +
>> + bundle_flow_mod__(ctx->argv[1], fms, n_fms, usable_protocols);
>> + free(fms);
>> +}
>> +
>> +static void
>> ofctl_help(struct ovs_cmdl_context *ctx OVS_UNUSED)
>> {
>> usage();
>> @@ -2636,7 +2735,11 @@ ofctl_replace_flows(struct ovs_cmdl_context *ctx)
>> fte_make_flow_mod(fte, FILE_IDX, OFPFC_ADD, protocol, &requests);
>> }
>> }
>> - transact_multiple_noreply(vconn, &requests);
>> + if (bundle) {
>> + bundle_transact(vconn, &requests, OFPBF_ORDERED);
>> + } else {
>> + transact_multiple_noreply(vconn, &requests);
>> + }
>> vconn_close(vconn);
>> 
>> fte_free_all(&cls);
>> @@ -3578,6 +3681,7 @@ static const struct ovs_cmdl_command all_commands[] = {
>> 1, 2, ofctl_dump_group_stats },
>> { "dump-group-features", "switch",
>> 1, 1, ofctl_dump_group_features },
>> + { "bundle", "switch file", 2, 2, ofctl_bundle },
>> { "help", NULL, 0, INT_MAX, ofctl_help },
>> { "list-commands", NULL, 0, INT_MAX, ofctl_list_commands },
>> 
>> --
>> 1.7.10.4
>> 
>> _______________________________________________
>> dev mailing list
>> dev at openvswitch.org
>> http://openvswitch.org/mailman/listinfo/dev
> 




More information about the dev mailing list