From: Ben Pfaff Date: Tue, 29 Jun 2010 16:54:10 +0000 (-0700) Subject: ovs-vsctl: Add "wait-until" command. X-Git-Tag: v1.1.0pre1~210 X-Git-Url: http://git.onelab.eu/?a=commitdiff_plain;h=7db03f7c0e1d2b917aec5af0613f6d8e228aa639;p=sliver-openvswitch.git ovs-vsctl: Add "wait-until" command. The "wait-until" command causes ovs-vsctl to wait until a specified condition becomes true. Requested-by: Sajjad Lateef --- diff --git a/AUTHORS b/AUTHORS index 03ff4ee77..eda90e0e0 100644 --- a/AUTHORS +++ b/AUTHORS @@ -40,6 +40,7 @@ Paulo Cravero pcravero@as2594.net Peter Balland peter@nicira.com Ram Jothikumar rjothikumar@nicira.com Rob Hoes rob.hoes@citrix.com +Sajjad Lateef slateef@nicira.com Sean Brady sbrady@gtfservices.com Srini Seetharaman seethara@stanford.edu Takayuki HAMA t-hama@cb.jp.nec.com diff --git a/tests/ovs-vsctl.at b/tests/ovs-vsctl.at index 66c375c94..4f179b691 100644 --- a/tests/ovs-vsctl.at +++ b/tests/ovs-vsctl.at @@ -661,6 +661,12 @@ AT_CHECK([RUN_OVS_VSCTL([get b br0 :y=z])], AT_CHECK([RUN_OVS_VSCTL([get b br0 datapath_id:y=z])], [1], [], [ovs-vsctl: datapath_id:y=z: trailing garbage "=z" in argument ], [OVS_VSCTL_CLEANUP]) +AT_CHECK([RUN_OVS_VSCTL([set b br0 'datapath_id:y>=z'])], + [1], [], [ovs-vsctl: datapath_id:y>=z: argument does not end in "=" followed by a value. +], [OVS_VSCTL_CLEANUP]) +AT_CHECK([RUN_OVS_VSCTL([wait-until b br0 datapath_id:y,z])], + [1], [], [ovs-vsctl: datapath_id:y,z: argument does not end in "=", "!=", "<", ">", "<=", or ">=" followed by a value. +], [OVS_VSCTL_CLEANUP]) AT_CHECK([RUN_OVS_VSCTL([get b br0 datapath_id::])], [1], [], [ovs-vsctl: datapath_id::: trailing garbage ":" in argument ], [OVS_VSCTL_CLEANUP]) @@ -697,6 +703,58 @@ AT_CHECK([RUN_OVS_VSCTL([destroy b br2])], OVS_VSCTL_CLEANUP AT_CLEANUP +AT_SETUP([database commands -- wait-until immediately true]) +AT_KEYWORDS([ovs-vsctl]) +OVS_VSCTL_SETUP +AT_CHECK([RUN_OVS_VSCTL( + [add-br br0], + [add-bond br0 bond0 eth0 eth1], + [set port bond0 bond_updelay=500 other-config:abc=def])], + [0], [], [], [OVS_VSCTL_CLEANUP]) +AT_CHECK([RUN_OVS_VSCTL([[wait-until Open_vSwitch . managers=[]]])], + [0], [], [], [OVS_VSCTL_CLEANUP]) +AT_CHECK([RUN_OVS_VSCTL([[wait-until Open_vSwitch . bridges!=[]]])], + [0], [], [], [OVS_VSCTL_CLEANUP]) +AT_CHECK([RUN_OVS_VSCTL([[wait-until Port bond0 other-config:abc=def]])], + [0], [], [], [OVS_VSCTL_CLEANUP]) +AT_CHECK([RUN_OVS_VSCTL([[wait-until port bond0 'bond_updelay>50' 'other-config:abc>d' 'other-config:abc stdout1 & +(RUN_OVS_VSCTL([[wait-until bridge br1 -- get bridge br1 other-config:abc]])) > stdout2 & +(RUN_OVS_VSCTL([[wait-until b br1 other-config={abc=def} -- get bridge br1 other-config]])) > stdout3 & +(RUN_OVS_VSCTL([[wait-until port bond0 'bond_updelay>50' -- get port bond0 bond-updelay]])) > stdout4 & + +# Give the ovs-vsctls a chance to read the database +sleep 1 + +AT_CHECK([RUN_OVS_VSCTL([add-br br10 -- set bridge br1 other-config:abc=quux]) +RUN_OVS_VSCTL([add-br br1 -- set bridge br1 other-config:abc=def -- add-bond br1 bond0 eth0 eth1 -- set port bond0 bond_updelay=500])], + [0], [], [], [OVS_VSCTL_CLEANUP]) + +# Wait for the ovs-vsctls to finish. +wait + +# Check output +AT_CHECK([cat stdout1], [0], [quux +], [], [OVS_VSCTL_CLEANUP]) +AT_CHECK([cat stdout2], [0], [def +], [], [OVS_VSCTL_CLEANUP]) +AT_CHECK([cat stdout3], [0], [{abc=def} +], [], [OVS_VSCTL_CLEANUP]) +AT_CHECK([cat stdout4], [0], [500 +], [], [OVS_VSCTL_CLEANUP]) + +OVS_VSCTL_CLEANUP +AT_CLEANUP + dnl This test really shows a bug -- "create" followed by "list" in dnl the same execution shows the wrong UUID on the "list" command. dnl The bug is documented in ovs-vsctl.8. diff --git a/utilities/ovs-vsctl.8.in b/utilities/ovs-vsctl.8.in index cde912d7e..cba0e5a06 100644 --- a/utilities/ovs-vsctl.8.in +++ b/utilities/ovs-vsctl.8.in @@ -577,6 +577,38 @@ precede or follow the \fBcreate\fR command. .IP "\fR[\fB\-\-if\-exists\fR] \fBdestroy \fItable record\fR..." Deletes each specified \fIrecord\fR from \fItable\fR. Unless \fB\-\-if\-exists\fR is specified, each \fIrecord\fRs must exist. +. +.IP "\fBwait\-until \fItable record \fR[\fIcolumn\fR[\fB:\fIkey\fR]\fB=\fIvalue\fR]..." +Waits until \fItable\fR contains a record named \fIrecord\fR whose +\fIcolumn\fR equals \fIvalue\fR or, if \fIkey\fR is specified, whose +\fIcolumn\fR contains a \fIkey\fR with the specified \fIvalue\fR. Any +of the operators \fB!=\fR, \fB<\fR, \fB>\fR, \fB<=\fR, or \fB>=\fR may +be substituted for \fB=\fR to test for inequality, less than, greater +than, less than or equal to, or greater than or equal to, +respectively. (Don't forget to escape \fB<\fR or \fB>\fR from +interpretation by the shell.) +.IP +If no \fIcolumn\fR[\fB:\fIkey\fR]\fB=\fIvalue\fR arguments are given, +this command waits only until \fIrecord\fR exists. If more than one +such argument is given, the command waits until all of them are +satisfied. +.IP +Usually \fBwait\-until\fR should be placed at the beginning of a set +of \fBovs\-vsctl\fR commands. For example, \fBwait\-until bridge br0 +\-\- get bridge br0 datapath_id\fR waits until a bridge named +\fBbr0\fR is created, then prints its \fBdatapath_id\fR column, +whereas \fBget bridge br0 datapath_id \-\- wait\-until bridge br0\fR +will abort if no bridge named \fBbr0\fR exists when \fBovs\-vsctl\fR +initially connects to the database. +.IP +Unlike all the other database commands, \fBwait\-until\fR treats its +\fIrecord\fR argument as case-sensitive and does not allow it to be +abbreviated. Otherwise, \fBwait\-until br1\fR would be satisfied by a +bridge named \fBbr10\fR. +.IP +Consider specifying \fB\-\-timeout=0\fR along with +\fB\-\-wait\-until\fR, to prevent \fBovs\-vsctl\fR from terminating +after waiting only at most 5 seconds. .SH "EXAMPLES" Create a new bridge named br0 and add port eth0 to it: .IP diff --git a/utilities/ovs-vsctl.c b/utilities/ovs-vsctl.c index b5502fdfb..0291fe813 100644 --- a/utilities/ovs-vsctl.c +++ b/utilities/ovs-vsctl.c @@ -465,6 +465,7 @@ Database commands:\n\ clear TBL REC COL clear values from COLumn in RECord in TBL\n\ create TBL COL[:KEY]=VALUE create and initialize new record\n\ destroy TBL REC delete REC from TBL\n\ + wait-until TBL REC [COL[:KEY]=VALUE] wait until condition is true\n\ Potentially unsafe database commands require --force option.\n\ \n\ Options:\n\ @@ -2237,7 +2238,7 @@ parse_column_key_value(const char *arg, const char *op = allowed_operators[i]; size_t op_len = strlen(op); - if (op_len > best_len && !strncmp(op, p, op_len)) { + if (op_len > best_len && !strncmp(op, p, op_len) && p[op_len]) { best_len = op_len; best = op; } @@ -2250,13 +2251,7 @@ parse_column_key_value(const char *arg, if (operatorp) { *operatorp = best; } - - p += best_len; - if (p[0] == '\0') { - error = missing_operator_error(arg, allowed_operators, n_allowed); - goto error; - } - *valuep = xstrdup(p); + *valuep = xstrdup(p + best_len); } else { if (valuep) { *valuep = NULL; @@ -2675,6 +2670,105 @@ cmd_destroy(struct vsctl_context *ctx) } } } + +static bool +is_condition_satified(const struct vsctl_table_class *table, + const struct ovsdb_idl_row *row, const char *arg, + struct ovsdb_symbol_table *symtab) +{ + static const char *operators[] = { + "=", "!=", "<", ">", "<=", ">=" + }; + + const struct ovsdb_idl_column *column; + char *key_string, *value_string; + struct ovsdb_datum have_datum; + const char *operator; + unsigned int idx; + char *error; + int cmp; + + error = parse_column_key_value(arg, table, &column, &key_string, + &operator, operators, ARRAY_SIZE(operators), + &value_string); + die_if_error(error); + if (!value_string) { + vsctl_fatal("%s: missing value", arg); + } + + ovsdb_idl_txn_read(row, column, &have_datum); + if (key_string) { + union ovsdb_atom want_key, want_value; + + if (column->type.value.type == OVSDB_TYPE_VOID) { + vsctl_fatal("cannot specify key to check for non-map column %s", + column->name); + } + + die_if_error(ovsdb_atom_from_string(&want_key, &column->type.key, + key_string, symtab)); + die_if_error(ovsdb_atom_from_string(&want_value, &column->type.value, + value_string, symtab)); + + idx = ovsdb_datum_find_key(&have_datum, + &want_key, column->type.key.type); + if (idx != UINT_MAX) { + cmp = ovsdb_atom_compare_3way(&have_datum.values[idx], + &want_value, + column->type.value.type); + } + + ovsdb_atom_destroy(&want_key, column->type.key.type); + ovsdb_atom_destroy(&want_value, column->type.value.type); + } else { + struct ovsdb_datum want_datum; + + die_if_error(ovsdb_datum_from_string(&want_datum, &column->type, + value_string, symtab)); + idx = 0; + cmp = ovsdb_datum_compare_3way(&have_datum, &want_datum, + &column->type); + ovsdb_datum_destroy(&want_datum, &column->type); + } + ovsdb_datum_destroy(&have_datum, &column->type); + + free(key_string); + free(value_string); + + return (idx == UINT_MAX ? false + : !strcmp(operator, "=") ? cmp == 0 + : !strcmp(operator, "!=") ? cmp != 0 + : !strcmp(operator, "<") ? cmp < 0 + : !strcmp(operator, ">") ? cmp > 0 + : !strcmp(operator, "<=") ? cmp <= 0 + : !strcmp(operator, ">=") ? cmp >= 0 + : (abort(), 0)); +} + +static void +cmd_wait_until(struct vsctl_context *ctx) +{ + const char *table_name = ctx->argv[1]; + const char *record_id = ctx->argv[2]; + const struct vsctl_table_class *table; + const struct ovsdb_idl_row *row; + int i; + + table = get_table(table_name); + + row = get_row__(ctx, table, record_id, false); + if (!row) { + ctx->try_again = true; + return; + } + + for (i = 3; i < ctx->argc; i++) { + if (!is_condition_satified(table, row, ctx->argv[i], ctx->symtab)) { + ctx->try_again = true; + return; + } + } +} static struct json * where_uuid_equals(const struct uuid *uuid) @@ -2930,6 +3024,7 @@ static const struct vsctl_command_syntax all_commands[] = { {"clear", 3, INT_MAX, cmd_clear, NULL, ""}, {"create", 2, INT_MAX, cmd_create, post_create, "--id="}, {"destroy", 1, INT_MAX, cmd_destroy, NULL, "--if-exists"}, + {"wait-until", 2, INT_MAX, cmd_wait_until, NULL, ""}, {NULL, 0, 0, NULL, NULL, NULL}, };