+static int
+compare_link1(const void *a_, const void *b_)
+{
+ const struct idltest_link1 *const *ap = a_;
+ const struct idltest_link1 *const *bp = b_;
+ const struct idltest_link1 *a = *ap;
+ const struct idltest_link1 *b = *bp;
+
+ return a->i < b->i ? -1 : a->i > b->i;
+}
+
+static void
+print_idl(struct ovsdb_idl *idl, int step)
+{
+ const struct idltest_simple *s;
+ const struct idltest_link1 *l1;
+ const struct idltest_link2 *l2;
+ int n = 0;
+
+ IDLTEST_SIMPLE_FOR_EACH (s, idl) {
+ size_t i;
+
+ printf("%03d: i=%"PRId64" r=%g b=%s s=%s u="UUID_FMT" ia=[",
+ step, s->i, s->r, s->b ? "true" : "false",
+ s->s, UUID_ARGS(&s->u));
+ for (i = 0; i < s->n_ia; i++) {
+ printf("%s%"PRId64, i ? " " : "", s->ia[i]);
+ }
+ printf("] ra=[");
+ for (i = 0; i < s->n_ra; i++) {
+ printf("%s%g", i ? " " : "", s->ra[i]);
+ }
+ printf("] ba=[");
+ for (i = 0; i < s->n_ba; i++) {
+ printf("%s%s", i ? " " : "", s->ba[i] ? "true" : "false");
+ }
+ printf("] sa=[");
+ for (i = 0; i < s->n_sa; i++) {
+ printf("%s%s", i ? " " : "", s->sa[i]);
+ }
+ printf("] ua=[");
+ for (i = 0; i < s->n_ua; i++) {
+ printf("%s"UUID_FMT, i ? " " : "", UUID_ARGS(&s->ua[i]));
+ }
+ printf("] uuid="UUID_FMT"\n", UUID_ARGS(&s->header_.uuid));
+ n++;
+ }
+ IDLTEST_LINK1_FOR_EACH (l1, idl) {
+ struct idltest_link1 **links;
+ size_t i;
+
+ printf("%03d: i=%"PRId64" k=", step, l1->i);
+ if (l1->k) {
+ printf("%"PRId64, l1->k->i);
+ }
+ printf(" ka=[");
+ links = xmemdup(l1->ka, l1->n_ka * sizeof *l1->ka);
+ qsort(links, l1->n_ka, sizeof *links, compare_link1);
+ for (i = 0; i < l1->n_ka; i++) {
+ printf("%s%"PRId64, i ? " " : "", links[i]->i);
+ }
+ free(links);
+ printf("] l2=");
+ if (l1->l2) {
+ printf("%"PRId64, l1->l2->i);
+ }
+ printf(" uuid="UUID_FMT"\n", UUID_ARGS(&l1->header_.uuid));
+ n++;
+ }
+ IDLTEST_LINK2_FOR_EACH (l2, idl) {
+ printf("%03d: i=%"PRId64" l1=", step, l2->i);
+ if (l2->l1) {
+ printf("%"PRId64, l2->l1->i);
+ }
+ printf(" uuid="UUID_FMT"\n", UUID_ARGS(&l2->header_.uuid));
+ n++;
+ }
+ if (!n) {
+ printf("%03d: empty\n", step);
+ }
+}
+
+static void
+parse_uuids(const struct json *json, struct ovsdb_symbol_table *symtab,
+ size_t *n)
+{
+ struct uuid uuid;
+
+ if (json->type == JSON_STRING && uuid_from_string(&uuid, json->u.string)) {
+ char *name = xasprintf("#%"PRIuSIZE"#", *n);
+ fprintf(stderr, "%s = "UUID_FMT"\n", name, UUID_ARGS(&uuid));
+ ovsdb_symbol_table_put(symtab, name, &uuid, false);
+ free(name);
+ *n += 1;
+ } else if (json->type == JSON_ARRAY) {
+ size_t i;
+
+ for (i = 0; i < json->u.array.n; i++) {
+ parse_uuids(json->u.array.elems[i], symtab, n);
+ }
+ } else if (json->type == JSON_OBJECT) {
+ const struct shash_node *node;
+
+ SHASH_FOR_EACH (node, json_object(json)) {
+ parse_uuids(node->data, symtab, n);
+ }
+ }
+}
+
+static void
+substitute_uuids(struct json *json, const struct ovsdb_symbol_table *symtab)
+{
+ if (json->type == JSON_STRING) {
+ const struct ovsdb_symbol *symbol;
+
+ symbol = ovsdb_symbol_table_get(symtab, json->u.string);
+ if (symbol) {
+ free(json->u.string);
+ json->u.string = xasprintf(UUID_FMT, UUID_ARGS(&symbol->uuid));
+ }
+ } else if (json->type == JSON_ARRAY) {
+ size_t i;
+
+ for (i = 0; i < json->u.array.n; i++) {
+ substitute_uuids(json->u.array.elems[i], symtab);
+ }
+ } else if (json->type == JSON_OBJECT) {
+ const struct shash_node *node;
+
+ SHASH_FOR_EACH (node, json_object(json)) {
+ substitute_uuids(node->data, symtab);
+ }
+ }
+}
+
+static const struct idltest_simple *
+idltest_find_simple(struct ovsdb_idl *idl, int i)
+{
+ const struct idltest_simple *s;
+
+ IDLTEST_SIMPLE_FOR_EACH (s, idl) {
+ if (s->i == i) {
+ return s;
+ }
+ }
+ return NULL;
+}
+
+static void
+idl_set(struct ovsdb_idl *idl, char *commands, int step)
+{
+ char *cmd, *save_ptr1 = NULL;
+ struct ovsdb_idl_txn *txn;
+ enum ovsdb_idl_txn_status status;
+ bool increment = false;
+
+ txn = ovsdb_idl_txn_create(idl);
+ for (cmd = strtok_r(commands, ",", &save_ptr1); cmd;
+ cmd = strtok_r(NULL, ",", &save_ptr1)) {
+ char *save_ptr2 = NULL;
+ char *name, *arg1, *arg2, *arg3;
+
+ name = strtok_r(cmd, " ", &save_ptr2);
+ arg1 = strtok_r(NULL, " ", &save_ptr2);
+ arg2 = strtok_r(NULL, " ", &save_ptr2);
+ arg3 = strtok_r(NULL, " ", &save_ptr2);
+
+ if (!strcmp(name, "set")) {
+ const struct idltest_simple *s;
+
+ if (!arg3) {
+ ovs_fatal(0, "\"set\" command requires 3 arguments");
+ }
+
+ s = idltest_find_simple(idl, atoi(arg1));
+ if (!s) {
+ ovs_fatal(0, "\"set\" command asks for nonexistent "
+ "i=%d", atoi(arg1));
+ }
+
+ if (!strcmp(arg2, "b")) {
+ idltest_simple_set_b(s, atoi(arg3));
+ } else if (!strcmp(arg2, "s")) {
+ idltest_simple_set_s(s, arg3);
+ } else if (!strcmp(arg2, "u")) {
+ struct uuid uuid;
+ if (!uuid_from_string(&uuid, arg3)) {
+ ovs_fatal(0, "\"%s\" is not a valid UUID", arg3);
+ }
+ idltest_simple_set_u(s, uuid);
+ } else if (!strcmp(arg2, "r")) {
+ idltest_simple_set_r(s, atof(arg3));
+ } else {
+ ovs_fatal(0, "\"set\" command asks for unknown column %s",
+ arg2);
+ }
+ } else if (!strcmp(name, "insert")) {
+ struct idltest_simple *s;
+
+ if (!arg1 || arg2) {
+ ovs_fatal(0, "\"insert\" command requires 1 argument");
+ }
+
+ s = idltest_simple_insert(txn);
+ idltest_simple_set_i(s, atoi(arg1));
+ } else if (!strcmp(name, "delete")) {
+ const struct idltest_simple *s;
+
+ if (!arg1 || arg2) {
+ ovs_fatal(0, "\"delete\" command requires 1 argument");
+ }
+
+ s = idltest_find_simple(idl, atoi(arg1));
+ if (!s) {
+ ovs_fatal(0, "\"delete\" command asks for nonexistent "
+ "i=%d", atoi(arg1));
+ }
+ idltest_simple_delete(s);
+ } else if (!strcmp(name, "verify")) {
+ const struct idltest_simple *s;
+
+ if (!arg2 || arg3) {
+ ovs_fatal(0, "\"verify\" command requires 2 arguments");
+ }
+
+ s = idltest_find_simple(idl, atoi(arg1));
+ if (!s) {
+ ovs_fatal(0, "\"verify\" command asks for nonexistent "
+ "i=%d", atoi(arg1));
+ }
+
+ if (!strcmp(arg2, "i")) {
+ idltest_simple_verify_i(s);
+ } else if (!strcmp(arg2, "b")) {
+ idltest_simple_verify_b(s);
+ } else if (!strcmp(arg2, "s")) {
+ idltest_simple_verify_s(s);
+ } else if (!strcmp(arg2, "u")) {
+ idltest_simple_verify_s(s);
+ } else if (!strcmp(arg2, "r")) {
+ idltest_simple_verify_r(s);
+ } else {
+ ovs_fatal(0, "\"verify\" command asks for unknown column %s",
+ arg2);
+ }
+ } else if (!strcmp(name, "increment")) {
+ const struct idltest_simple *s;
+
+ if (!arg1 || arg2) {
+ ovs_fatal(0, "\"increment\" command requires 1 argument");
+ }
+
+ s = idltest_find_simple(idl, atoi(arg1));
+ if (!s) {
+ ovs_fatal(0, "\"set\" command asks for nonexistent "
+ "i=%d", atoi(arg1));
+ }
+
+ ovsdb_idl_txn_increment(txn, &s->header_, &idltest_simple_col_i);
+ increment = true;
+ } else if (!strcmp(name, "abort")) {
+ ovsdb_idl_txn_abort(txn);
+ break;
+ } else if (!strcmp(name, "destroy")) {
+ printf("%03d: destroy\n", step);
+ ovsdb_idl_txn_destroy(txn);
+ return;
+ } else {
+ ovs_fatal(0, "unknown command %s", name);
+ }
+ }
+
+ status = ovsdb_idl_txn_commit_block(txn);
+ printf("%03d: commit, status=%s",
+ step, ovsdb_idl_txn_status_to_string(status));
+ if (increment) {
+ printf(", increment=%"PRId64,
+ ovsdb_idl_txn_get_increment_new_value(txn));
+ }
+ putchar('\n');
+ ovsdb_idl_txn_destroy(txn);
+}
+
+static void
+do_idl(int argc, char *argv[])
+{
+ struct jsonrpc *rpc;
+ struct ovsdb_idl *idl;
+ unsigned int seqno = 0;
+ struct ovsdb_symbol_table *symtab;
+ size_t n_uuids = 0;
+ int step = 0;
+ int error;
+ int i;
+
+ idltest_init();
+
+ idl = ovsdb_idl_create(argv[1], &idltest_idl_class, true, true);
+ if (argc > 2) {
+ struct stream *stream;
+
+ error = stream_open_block(jsonrpc_stream_open(argv[1], &stream,
+ DSCP_DEFAULT), &stream);
+ if (error) {
+ ovs_fatal(error, "failed to connect to \"%s\"", argv[1]);
+ }
+ rpc = jsonrpc_open(stream);
+ } else {
+ rpc = NULL;
+ }
+
+ setvbuf(stdout, NULL, _IOLBF, 0);
+
+ symtab = ovsdb_symbol_table_create();
+ for (i = 2; i < argc; i++) {
+ char *arg = argv[i];
+ struct jsonrpc_msg *request, *reply;
+
+ if (*arg == '+') {
+ /* The previous transaction didn't change anything. */
+ arg++;
+ } else {
+ /* Wait for update. */
+ for (;;) {
+ ovsdb_idl_run(idl);
+ if (ovsdb_idl_get_seqno(idl) != seqno) {
+ break;
+ }
+ jsonrpc_run(rpc);
+
+ ovsdb_idl_wait(idl);
+ jsonrpc_wait(rpc);
+ poll_block();
+ }
+
+ /* Print update. */
+ print_idl(idl, step++);
+ }
+ seqno = ovsdb_idl_get_seqno(idl);
+
+ if (!strcmp(arg, "reconnect")) {
+ printf("%03d: reconnect\n", step++);
+ ovsdb_idl_force_reconnect(idl);
+ } else if (arg[0] != '[') {
+ idl_set(idl, arg, step++);
+ } else {
+ struct json *json = parse_json(arg);
+ substitute_uuids(json, symtab);
+ request = jsonrpc_create_request("transact", json, NULL);
+ error = jsonrpc_transact_block(rpc, request, &reply);
+ if (error || reply->error) {
+ ovs_fatal(error, "jsonrpc transaction failed");
+ }
+ printf("%03d: ", step++);
+ if (reply->result) {
+ parse_uuids(reply->result, symtab, &n_uuids);
+ }
+ json_destroy(reply->id);
+ reply->id = NULL;
+ print_and_free_json(jsonrpc_msg_to_json(reply));
+ }
+ }
+ ovsdb_symbol_table_destroy(symtab);
+
+ if (rpc) {
+ jsonrpc_close(rpc);
+ }
+ for (;;) {
+ ovsdb_idl_run(idl);
+ if (ovsdb_idl_get_seqno(idl) != seqno) {
+ break;
+ }
+ ovsdb_idl_wait(idl);
+ poll_block();
+ }
+ print_idl(idl, step++);
+ ovsdb_idl_destroy(idl);
+ printf("%03d: done\n", step);
+}
+