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. 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. > 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