On May 15, 2015 at 7:45:31 AM, Jarno Rajahalme 
(jrajaha...@nicira.com(mailto:jrajaha...@nicira.com)) wrote:

>  
> > On May 14, 2015, at 23:44, Romain Lenglet wrote:
> >
> > Thanks Jarno!
> >
> >> On May 14, 2015 at 2:11:50 PM, Jarno Rajahalme 
> >> (jrajaha...@nicira.com(mailto:jrajaha...@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.
> >
> > I would suggest a different design for the ovs-ofctl support: add a 
> > --bundle option to the ovs-ofctl’s add-flows, del-flows and replace-flows, 
> > so that all the flow mods generated by each execution would be sent in a 
> > bundle. This option would be accepted only when OF1.4 or later is used.
> >
>  
> Unfortunately this does not work, as bundles are controller (connection) 
> specific, and each ovs-ofctl invocation is seen as a separate controller.

Yes, that’s what I had in mind: one bundle per invocation of add-flows, 
del-flows, diff-flows, or replace-flows.

>  
> This said, it could be possible to come up with an extension that would 
> circumvent this restriction between ovs-ofctl and OVS.
>  
> > This design would allow using the current flows file format unmodified, and 
> > seems much more useful to me. I understand that this design wouldn’t 
> > support port mods, but adding support for bundles to replace-flows would be 
> > extremely useful.
> >
>  
> This should be doable, as replace-flows runs as a single ovs-ofctl 
> invocation. I'll look into this.

Thanks, I’m looking forward to this!
--
Romain Lenglet

>  
> Jarno
>  
> >> 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' keyword is not yet supported.
> >>
> >> Signed-off-by: Jarno Rajahalme
> >> ---
> >> NEWS | 10 +-
> >> include/openvswitch/vconn.h | 6 +-
> >> lib/ofp-parse.c | 95 +++++++++++++++++-
> >> lib/ofp-parse.h | 12 ++-
> >> lib/ofp-util.c | 30 ++++++
> >> lib/ofp-util.h | 2 +
> >> lib/vconn.c | 27 ++++--
> >> tests/ofproto.at | 110 +++++++++++++++++++++
> >> utilities/ovs-ofctl.8.in | 32 +++++++
> >> utilities/ovs-ofctl.c | 222 +++++++++++++++++++++++++++++++++++++++++--
> >> 10 files changed, 523 insertions(+), 23 deletions(-)
> >>
> >> diff --git a/NEWS b/NEWS
> >> index a480607..a7f9369 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,9 @@ 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-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..a8d7297 100644
> >> --- a/include/openvswitch/vconn.h
> >> +++ b/include/openvswitch/vconn.h
> >> @@ -50,8 +50,10 @@ void vconn_set_recv_any_version(struct vconn *);
> >> int vconn_connect(struct vconn *);
> >> int vconn_recv(struct vconn *, struct ofpbuf **);
> >> int vconn_send(struct vconn *, struct ofpbuf *);
> >> -int vconn_recv_xid(struct vconn *, ovs_be32 xid, struct ofpbuf **);
> >> -int vconn_transact(struct vconn *, struct ofpbuf *, struct ofpbuf **);
> >> +int vconn_recv_xid(struct vconn *, ovs_be32 xid, struct ofpbuf **,
> >> + void (*error_reporter)(const struct ofp_header *));
> >> +int vconn_transact(struct vconn *, struct ofpbuf *, struct ofpbuf **,
> >> + void (*error_reporter)(const struct ofp_header *));
> >> int vconn_transact_noreply(struct vconn *, struct ofpbuf *, struct ofpbuf 
> >> **);
> >> int vconn_transact_multiple_noreply(struct vconn *, struct ovs_list 
> >> *requests,
> >> struct ofpbuf **replyp);
> >> diff --git a/lib/ofp-parse.c b/lib/ofp-parse.c
> >> index 126980c..3e746ea 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",
> >> + * "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",
> >> + * "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
> >> + * ("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)
> >> {
> >> @@ -1547,3 +1583,58 @@ parse_ofp_group_mod_file(const char *file_name, 
> >> uint16_t command,
> >> }
> >> return NULL;
> >> }
> >> +
> >> +static char * OVS_WARN_UNUSED_RESULT
> >> +parse_ofp_bundle_control_str__(struct ofputil_bundle_ctrl_msg *bc,
> >> + char *string,
> >> + enum ofputil_protocol *usable_protocols)
> >> +{
> >> + char *save_ptr = NULL;
> >> + char *name;
> >> +
> >> + /* Bundles require at least OF 1.4. */
> >> + *usable_protocols = OFPUTIL_P_OF14_UP;
> >> +
> >> + memset(bc, 0, sizeof *bc);
> >> +
> >> + for (name = strtok_r(string, "=, \t\r\n", &save_ptr); name;
> >> + name = strtok_r(NULL, "=, \t\r\n", &save_ptr)) {
> >> +
> >> + if (!strcmp(name, "atomic")) {
> >> + bc->flags |= OFPBF_ATOMIC;
> >> + } else if (!strcmp(name, "ordered")) {
> >> + bc->flags |= OFPBF_ORDERED;
> >> + } else {
> >> + char *value;
> >> +
> >> + value = strtok_r(NULL, ", \t\r\n", &save_ptr);
> >> + if (!value) {
> >> + return xasprintf("field %s missing value", name);
> >> + }
> >> +
> >> + if (!strcmp(name, "bundle")) {
> >> + char *error = str_to_u32(value, &bc->bundle_id);
> >> +
> >> + if (error) {
> >> + return error;
> >> + }
> >> + } else {
> >> + return xasprintf("unknown keyword %s", name);
> >> + }
> >> + }
> >> + }
> >> +
> >> + return NULL;
> >> +}
> >> +
> >> +char * OVS_WARN_UNUSED_RESULT
> >> +parse_ofp_bundle_control_str(struct ofputil_bundle_ctrl_msg *bc,
> >> + const char *str_,
> >> + enum ofputil_protocol *usable_protocols)
> >> +{
> >> + char *string = xstrdup(str_);
> >> + char *error = parse_ofp_bundle_control_str__(bc, string, 
> >> usable_protocols);
> >> +
> >> + free(string);
> >> + return error;
> >> +}
> >> diff --git a/lib/ofp-parse.h b/lib/ofp-parse.h
> >> index db30f43..f39c323 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.
> >> @@ -32,6 +32,7 @@ struct ofputil_flow_mod;
> >> struct ofputil_flow_monitor_request;
> >> struct ofputil_flow_stats_request;
> >> struct ofputil_group_mod;
> >> +struct ofputil_bundle_ctrl_msg;
> >> struct ofputil_meter_mod;
> >> struct ofputil_table_mod;
> >> struct simap;
> >> @@ -42,7 +43,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 +52,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;
> >> @@ -84,6 +85,11 @@ char *parse_ofp_group_mod_str(struct ofputil_group_mod 
> >> *, uint16_t command,
> >> enum ofputil_protocol *usable_protocols)
> >> OVS_WARN_UNUSED_RESULT;
> >>
> >> +char *parse_ofp_bundle_control_str(struct ofputil_bundle_ctrl_msg *,
> >> + const char *string,
> >> + enum ofputil_protocol *usable_protocols)
> >> + OVS_WARN_UNUSED_RESULT;
> >> +
> >> char *str_to_u8(const char *str, const char *name, uint8_t *valuep)
> >> OVS_WARN_UNUSED_RESULT;
> >> char *str_to_u16(const char *str, const char *name, uint16_t *valuep)
> >> 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/vconn.c b/lib/vconn.c
> >> index c033b48..e0329ec 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.
> >> @@ -751,11 +751,14 @@ vconn_recv_block(struct vconn *vconn, struct ofpbuf 
> >> **msgp)
> >> *
> >> * 'request' is always destroyed, regardless of the return value. */
> >> int
> >> -vconn_recv_xid(struct vconn *vconn, ovs_be32 xid, struct ofpbuf **replyp)
> >> +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,15 +766,21 @@ 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);
> >> }
> >> }
> >> @@ -788,7 +797,8 @@ vconn_recv_xid(struct vconn *vconn, ovs_be32 xid, 
> >> struct ofpbuf **replyp)
> >> * 'request' is always destroyed, regardless of the return value. */
> >> int
> >> vconn_transact(struct vconn *vconn, struct ofpbuf *request,
> >> - struct ofpbuf **replyp)
> >> + struct ofpbuf **replyp,
> >> + void (*error_reporter)(const struct ofp_header *))
> >> {
> >> ovs_be32 send_xid = ((struct ofp_header *) request->data)->xid;
> >> int error;
> >> @@ -798,7 +808,8 @@ vconn_transact(struct vconn *vconn, struct ofpbuf 
> >> *request,
> >> if (error) {
> >> ofpbuf_delete(request);
> >> }
> >> - return error ? error : vconn_recv_xid(vconn, send_xid, replyp);
> >> + return error ? error : vconn_recv_xid(vconn, send_xid, replyp,
> >> + error_reporter);
> >> }
> >>
> >> /* Sends 'request' followed by a barrier request to 'vconn', then blocks 
> >> until
> >> diff --git a/tests/ofproto.at b/tests/ofproto.at
> >> index 9729a7c..20bf5d6 100644
> >> --- a/tests/ofproto.at
> >> +++ b/tests/ofproto.at
> >> @@ -3450,3 +3450,113 @@ 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-ofctl -O OpenFlow14 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 -O OpenFlow14 bundle br0 flows.txt])
> >> +
> >> +AT_CHECK([ovs-ofctl -O OpenFlow14 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
> >> +OFPST_FLOW reply (OF1.4):
> >> +])
> >> +
> >> +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 -O OpenFlow14 bundle br0 flows.txt])
> >> +
> >> +AT_CHECK([ovs-ofctl -O OpenFlow14 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
> >> +OFPST_FLOW reply (OF1.4):
> >> +])
> >> +
> >> +# 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 -O OpenFlow14 bundle br0 flows.txt])
> >> +
> >> +AT_CHECK([ovs-ofctl -O OpenFlow14 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
> >> +OFPST_FLOW reply (OF1.4):
> >> +])
> >> +
> >> +OVS_VSWITCHD_STOP
> >> +AT_CLEANUP
> >> +
> >> +
> >> +AT_SETUP([ofproto - failing bundle commit (OpenFlow 1.4)])
> >> +AT_KEYWORDS([monitor])
> >> +OVS_VSWITCHD_START
> >> +
> >> +AT_CHECK([ovs-ofctl -O OpenFlow14 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 -O OpenFlow14 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
> >> +OFPST_FLOW reply (OF1.4):
> >> +])
> >> +
> >> +# 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 -O OpenFlow14 bundle br0 flows.txt], [1], [], [dnl
> >> +OFPT_ERROR (OF1.4) (xid=0x15): OFPBRC_EPERM
> >> +OFPT_FLOW_MOD (OF1.4) (xid=0x15): ADD table:254 actions=drop
> >> +OFPT_ERROR (OF1.4) (xid=0x17): OFPBFC_MSG_FAILED
> >> +OFPT_BUNDLE_CONTROL (OF1.4) (xid=0x17):
> >> + bundle_id=0 type=COMMIT_REQUEST flags=0
> >> +ovs-ofctl: Bundle commit failed (OFPBFC_MSG_FAILED).
> >> +])
> >> +
> >> +AT_CHECK([ovs-ofctl -O OpenFlow14 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
> >> +OFPST_FLOW reply (OF1.4):
> >> +])
> >> +
> >> +OVS_VSWITCHD_STOP
> >> +AT_CLEANUP
> >> diff --git a/utilities/ovs-ofctl.8.in b/utilities/ovs-ofctl.8.in
> >> index c667aa4..95341ac 100644
> >> --- a/utilities/ovs-ofctl.8.in
> >> +++ b/utilities/ovs-ofctl.8.in
> >> @@ -318,6 +318,38 @@ Deletes entries from \fIswitch\fR's flow table. With 
> >> only a
> >> entries that match the specified flows. With \fB\-\-strict\fR,
> >> wildcards are not treated as active for matching purposes.
> >> .
> >> +.IQ "\fBbundle \fIswitch [\fBatomic\fR] [\fBordered\fR] \fIfile\fR"
> >> +.IQ "\fBbundle \fIswitch [\fBatomic\fR] [\fBordered\fR] \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
> >> +All flow mods are processed 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. The \fIswitch\fR may
> >> +perform resource management, such as evicting flows, that may not be
> >> +unrolled even when the transaction fails.
> >> +.
> >> +.IP
> >> +If an optional \fBatomic\fR keyword is given, the switch is requested
> >> +to make the modifications so that no datapath packet sees an
> >> +intermediate configuration of the flow tables. The optional
> >> +\fBordered\fR keyword requests the modifications to be performed in
> >> +the order given in the file. Without this keyword the switch may
> >> +apply the modifications in any order.
> >> +.
> >> +.IP
> >> +\fBbundle\fR command requires OpenFlow 1.4 or higher. You may need to
> >> +supply an explicit \fB-O OpenFlow14\fR option, and you may need to enable
> >> +OVS OpenFlow 1.4 support by setting the OVSDB \fIprotocols\fR column
> >> +in the \fIbridge\fR table.
> >> +.
> >> .IP "[\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
> >> diff --git a/utilities/ovs-ofctl.c b/utilities/ovs-ofctl.c
> >> index 54a5bb8..20abed7 100644
> >> --- a/utilities/ovs-ofctl.c
> >> +++ b/utilities/ovs-ofctl.c
> >> @@ -506,7 +506,7 @@ dump_transaction(struct vconn *vconn, struct ofpbuf 
> >> *request)
> >> struct ofpbuf *reply;
> >>
> >> ofpmsg_update_length(request);
> >> - run(vconn_transact(vconn, request, &reply), "talking to %s",
> >> + run(vconn_transact(vconn, request, &reply, NULL), "talking to %s",
> >> vconn_get_name(vconn));
> >> ofp_print(stdout, reply->data, reply->size, verbosity + 1);
> >> ofpbuf_delete(reply);
> >> @@ -627,7 +627,7 @@ fetch_switch_config(struct vconn *vconn, struct 
> >> ofp_switch_config *config_)
> >>
> >> request = ofpraw_alloc(OFPRAW_OFPT_GET_CONFIG_REQUEST,
> >> vconn_get_version(vconn), 0);
> >> - run(vconn_transact(vconn, request, &reply),
> >> + run(vconn_transact(vconn, request, &reply, NULL),
> >> "talking to %s", vconn_get_name(vconn));
> >>
> >> if (ofptype_pull(&type, reply) || type != OFPTYPE_GET_CONFIG_REPLY) {
> >> @@ -664,7 +664,8 @@ ofctl_show(struct ovs_cmdl_context *ctx)
> >> open_vconn(vconn_name, &vconn);
> >> version = vconn_get_version(vconn);
> >> request = ofpraw_alloc(OFPRAW_OFPT_FEATURES_REQUEST, version, 0);
> >> - run(vconn_transact(vconn, request, &reply), "talking to %s", vconn_name);
> >> + run(vconn_transact(vconn, request, &reply, NULL), "talking to %s",
> >> + vconn_name);
> >>
> >> has_ports = ofputil_switch_features_has_ports(reply);
> >> ofp_print(stdout, reply->data, reply->size, verbosity + 1);
> >> @@ -731,7 +732,7 @@ fetch_port_by_features(struct vconn *vconn,
> >> /* Fetch the switch's ofp_switch_features. */
> >> request = ofpraw_alloc(OFPRAW_OFPT_FEATURES_REQUEST,
> >> vconn_get_version(vconn), 0);
> >> - run(vconn_transact(vconn, request, &reply),
> >> + run(vconn_transact(vconn, request, &reply, NULL),
> >> "talking to %s", vconn_get_name(vconn));
> >>
> >> oh = reply->data;
> >> @@ -1667,7 +1668,8 @@ ofctl_probe(struct ovs_cmdl_context *ctx)
> >>
> >> open_vconn(ctx->argv[1], &vconn);
> >> request = make_echo_request(vconn_get_version(vconn));
> >> - run(vconn_transact(vconn, request, &reply), "talking to %s", 
> >> ctx->argv[1]);
> >> + run(vconn_transact(vconn, request, &reply, NULL), "talking to %s",
> >> + ctx->argv[1]);
> >> if (reply->size != sizeof(struct ofp_header)) {
> >> ovs_fatal(0, "reply does not match request");
> >> }
> >> @@ -2022,7 +2024,8 @@ ofctl_ping(struct ovs_cmdl_context *ctx)
> >> random_bytes(ofpbuf_put_uninit(request, payload), payload);
> >>
> >> xgettimeofday(&start);
> >> - run(vconn_transact(vconn, ofpbuf_clone(request), &reply), "transact");
> >> + run(vconn_transact(vconn, ofpbuf_clone(request), &reply, NULL),
> >> + "transact");
> >> xgettimeofday(&end);
> >>
> >> rpy_hdr = reply->data;
> >> @@ -2075,7 +2078,7 @@ ofctl_benchmark(struct ovs_cmdl_context *ctx)
> >> request = ofpraw_alloc(OFPRAW_OFPT_ECHO_REQUEST,
> >> vconn_get_version(vconn), payload_size);
> >> ofpbuf_put_zeros(request, payload_size);
> >> - run(vconn_transact(vconn, request, &reply), "transact");
> >> + run(vconn_transact(vconn, request, &reply, NULL), "transact");
> >> ofpbuf_delete(reply);
> >> }
> >> xgettimeofday(&end);
> >> @@ -2261,6 +2264,210 @@ ofctl_dump_group_features(struct ovs_cmdl_context 
> >> *ctx)
> >> vconn_close(vconn);
> >> }
> >>
> >> +static enum ofperr
> >> +bundle_reply_validate(struct ofpbuf *reply, enum ofp_version version,
> >> + struct ofputil_bundle_ctrl_msg *request)
> >> +{
> >> + 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) {
> >> + ofp_print(stderr, reply->data, reply->size, verbosity + 1);
> >> + fflush(stderr);
> >> + error = ofperr_decode_msg(oh, NULL);
> >> + return error;
> >> + }
> >> + if (type != OFPTYPE_BUNDLE_CONTROL) {
> >> + ofp_print(stderr, reply->data, reply->size, verbosity + 1);
> >> + fflush(stderr);
> >> + return OFPERR_OFPBRC_BAD_TYPE;
> >> + }
> >> +
> >> + error = ofputil_decode_bundle_ctrl(oh, &rbc);
> >> + if (error) {
> >> + return error;
> >> + }
> >> +
> >> + if (rbc.bundle_id != request->bundle_id) {
> >> + ofp_print(stderr, reply->data, reply->size, verbosity + 1);
> >> + fflush(stderr);
> >> + return OFPERR_OFPBFC_BAD_ID;
> >> + }
> >> +
> >> + if (rbc.type != request->type + 1) {
> >> + ofp_print(stderr, reply->data, reply->size, verbosity + 1);
> >> + fflush(stderr);
> >> + return OFPERR_OFPBFC_BAD_TYPE;
> >> + }
> >> +
> >> + return 0;
> >> +}
> >> +
> >> +static void
> >> +bundle_error_reporter(const struct ofp_header *oh)
> >> +{
> >> + ofp_print(stderr, oh, ntohs(oh->length), verbosity + 1);
> >> + fflush(stderr);
> >> +}
> >> +
> >> +static enum ofperr
> >> +bundle_control_transact(struct vconn *vconn, enum ofp_version version,
> >> + struct ofputil_bundle_ctrl_msg *bc,
> >> + uint16_t type)
> >> +{
> >> + struct ofpbuf *request, *reply;
> >> + enum ofperr error;
> >> +
> >> + bc->type = type;
> >> + request = ofputil_encode_bundle_ctrl_request(version, bc);
> >> + ofpmsg_update_length(request);
> >> + run(vconn_transact(vconn, request, &reply, bundle_error_reporter),
> >> + "talking to %s", vconn_get_name(vconn));
> >> +
> >> + error = bundle_reply_validate(reply, version, bc);
> >> + ofpbuf_delete(reply);
> >> +
> >> + return error;
> >> +}
> >> +
> >> +static enum ofperr
> >> +bundle_add_msg(struct vconn *vconn, enum ofp_version version,
> >> + struct ofputil_bundle_ctrl_msg *bc, struct ofpbuf *msg)
> >> +{
> >> + struct ofputil_bundle_add_msg bam;
> >> + struct ofpbuf *request, *reply;
> >> +
> >> + ofpmsg_update_length(msg);
> >> +
> >> + bam.bundle_id = bc->bundle_id;
> >> + bam.flags = bc->flags;
> >> + bam.msg = msg->data;
> >> +
> >> + request = ofputil_encode_bundle_add(version, &bam);
> >> + ofpbuf_delete(msg);
> >> + ofpmsg_update_length(request);
> >> +
> >> + run(vconn_transact_noreply(vconn, request, &reply), "talking to %s",
> >> + vconn_get_name(vconn));
> >> +
> >> + if (reply) {
> >> + const struct ofp_header *oh;
> >> + enum ofptype type;
> >> + enum ofperr error;
> >> +
> >> + oh = reply->data;
> >> + error = ofptype_decode(&type, oh);
> >> + if (error) {
> >> + goto error_out;
> >> + }
> >> +
> >> + if (type == OFPTYPE_ERROR) {
> >> + ofp_print(stderr, reply->data, reply->size, verbosity + 1);
> >> + fflush(stderr);
> >> + error = ofperr_decode_msg(oh, NULL);
> >> + goto error_out;
> >> + }
> >> + error = OFPERR_OFPBFC_UNKNOWN;
> >> +error_out:
> >> + ofpbuf_delete(reply);
> >> + return error;
> >> + }
> >> + return 0;
> >> +}
> >> +
> >> +static void
> >> +ofctl_bundle(struct ovs_cmdl_context *ctx)
> >> +{
> >> + enum ofputil_protocol usable_protocols, usable_protocols2;
> >> + enum ofputil_protocol protocol;
> >> + enum ofp_version version;
> >> + struct vconn *vconn;
> >> + const char *file;
> >> + struct ofputil_flow_mod *fms = NULL;
> >> + size_t n_fms = 0;
> >> + struct ofputil_bundle_ctrl_msg bc;
> >> + char *error;
> >> + enum ofperr ofperr;
> >> + size_t i;
> >> +
> >> + /* Parse bundle options, if any. */
> >> + if (ctx->argc > 3) {
> >> + error = parse_ofp_bundle_control_str(&bc, ctx->argv[2],
> >> + &usable_protocols);
> >> + if (error) {
> >> + ovs_fatal(0, "%s", error);
> >> + }
> >> + file = ctx->argv[3]; /* File name after the options. */
> >> + } else {
> >> + memset(&bc, 0, sizeof bc);
> >> + file = ctx->argv[2];
> >> + usable_protocols = OFPUTIL_P_OF14_UP;
> >> + }
> >> +
> >> + /* Parse flow mods from the file. */
> >> + error = parse_ofp_flow_mod_file(file, -2, &fms, &n_fms,
> >> + &usable_protocols2);
> >> + if (error) {
> >> + ovs_fatal(0, "%s", error);
> >> + }
> >> +
> >> + usable_protocols &= usable_protocols2;
> >> +
> >> + protocol = open_vconn_for_flow_mod(ctx->argv[1], &vconn, 
> >> usable_protocols);
> >> + version = ofputil_protocol_to_ofp_version(protocol);
> >> +
> >> + ofperr = bundle_control_transact(vconn, version, &bc, 
> >> OFPBCT_OPEN_REQUEST);
> >> + if (ofperr) {
> >> + ovs_fatal(0, "Bundle open failed (%s).", ofperr_to_string(ofperr));
> >> + }
> >> +
> >> + for (i = 0; i < n_fms; i++) {
> >> + struct ofputil_flow_mod *fm = &fms[i];
> >> +
> >> + if (!ofperr) {
> >> + ofperr = bundle_add_msg(vconn, version, &bc,
> >> + ofputil_encode_flow_mod(fm, protocol));
> >> + if (ofperr) {
> >> + fprintf(stderr, "Bundle flow mod #%"PRIuSIZE
> >> + " failed (%s), discarding bundle.",
> >> + i, ofperr_to_string(ofperr));
> >> + fflush(stderr);
> >> + }
> >> + }
> >> + free(CONST_CAST(struct ofpact *, fm->ofpacts));
> >> + }
> >> + free(fms);
> >> +
> >> + if (!ofperr) {
> >> + ofperr = bundle_control_transact(vconn, version, &bc,
> >> + OFPBCT_COMMIT_REQUEST);
> >> + if (ofperr) {
> >> + ovs_fatal(0, "Bundle commit failed (%s).",
> >> + ofperr_to_string(ofperr));
> >> + }
> >> + } else {
> >> + ofperr = bundle_control_transact(vconn, version, &bc,
> >> + OFPBCT_DISCARD_REQUEST);
> >> + if (ofperr) {
> >> + ovs_fatal(0, "Bundle discard failed (%s).",
> >> + ofperr_to_string(ofperr));
> >> + }
> >> + }
> >> + vconn_close(vconn);
> >> +}
> >> +
> >> static void
> >> ofctl_help(struct ovs_cmdl_context *ctx OVS_UNUSED)
> >> {
> >> @@ -3578,6 +3785,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 [options] file", 2, 3, ofctl_bundle },
> >> { "help", NULL, 0, INT_MAX, ofctl_help },
> >> { "list-commands", NULL, 0, INT_MAX, ofctl_list_commands },
> >>
> >> --
> >> 1.7.10.4
> >
> > --
> > Romain Lenglet
> >

_______________________________________________
dev mailing list
dev@openvswitch.org
http://openvswitch.org/mailman/listinfo/dev

Reply via email to