[ovs-dev] [PATCH v2 1/3] ovsdb-idl: Add support for on-demand columns

Rodriguez Betancourt, Esteban estebarb at hpe.com
Thu Mar 17 19:42:16 UTC 2016


From: Sebastian Arguello <sebastian.arguello at hpe.com>

The IDL only supports reading from columns that are being monitored.
In the case where the column represent a frequently changing entity (e.g. counter),
and the reads are relatively infrequent (e.g. CLI client), there is a
significant overhead in replication.

This patch introduces a new column mode called OVSDB_IDL_ON_DEMAND.
An on-demand column will have its default value if it has never been updated.
This can happen if: 1) the user has not called explicitly a fetch operation
over it or 2) the server reply with the actual value has not been
received/processed (i.e. ovsdb_idl_run() has not been called). The on-demand
columns will keep the last value received from the OVSDB server.

The on-demand columns are not updated by the IDL automatically, they are
updated when the IDL user asks it by the calling one of the new fetching
functions that were added to the IDL. When this happens, the IDL sends a select
operation to request the data from the server. After calling ovsdb_idl_run(),
the IDL updates the replica with the information received from the server.

With this new column mode, the state of the replica could diverge from the
state of the database, as some of the columns could be outdated. The process
using the IDL is responsible for requesting the information before using it.

The main user visible changes in this patch are:
  - There is a new function that adds on-demand columns:
    ovsdb_idl_add_on_demand_column()
  - Functions for fetching a cells (columns for specific rows),
    columns, and table were added: ovsdb_idl_fetch_row(),
    ovsdb_idl_fetch_column(), and ovsdb_idl_fetch_table()
  - Functions for verifying if the fetch requests of on-demand columns
    were processed were added: ovsdb_idl_is_row_fetch_pending(),
    ovsdb_idl_is_column_fetch_pending(), ovsdb_idl_is_table_fetch_pending()
  - When an on-demand column is updated, the IDL seqno is changed as well

Note that the Python IDL already has a feature similar to this called
Read-only columns.

Signed-off-by: Sebastian Arguello <sebastian.arguello at hpe.com>
Signed-off-by: Esteban Rodriguez Betancourt <estebarb at hpe.com>
---
This is a change over the first patch sent. The other two patches remain 
without changes.
The pull request https://github.com/openvswitch/ovs/pull/110 was also updated.
 lib/ovsdb-idl-provider.h |  11 ++
 lib/ovsdb-idl.c          | 470 ++++++++++++++++++++++++++++++++++++++++++++++-
 lib/ovsdb-idl.h          |  32 ++++
 3 files changed, 511 insertions(+), 2 deletions(-)

diff --git a/lib/ovsdb-idl-provider.h b/lib/ovsdb-idl-provider.h
index 190acca..b1e18d7 100644
--- a/lib/ovsdb-idl-provider.h
+++ b/lib/ovsdb-idl-provider.h
@@ -1,4 +1,5 @@
 /* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
+ * Copyright (C) 2016 Hewlett Packard Enterprise Development LP
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -41,6 +42,10 @@ struct ovsdb_idl_row {
     unsigned int change_seqno[OVSDB_IDL_CHANGE_MAX];
     struct ovs_list track_node; /* Rows modified/added/deleted by IDL */
     unsigned long int *updated; /* Bitmap of columns updated by IDL */
+
+    size_t outstanding_fetch_reqs; /* Number of on-demand columns in this row
+                                      with on-going fetch operations */
+
 };
 
 struct ovsdb_idl_column {
@@ -70,6 +75,12 @@ struct ovsdb_idl_table {
     struct ovsdb_idl *idl;   /* Containing idl. */
     unsigned int change_seqno[OVSDB_IDL_CHANGE_MAX];
     struct ovs_list track_list; /* Tracked rows (ovsdb_idl_row.track_node). */
+    bool has_pending_fetch;     /* Indicates if the table has a pending fetch
+                                operation */
+    struct shash outstanding_col_fetch_reqs; /* Contains the name of the
+                                                columns with on-demand fetch
+                                                request pending. It does not
+                                                store any data, just keys */
 };
 
 struct ovsdb_idl_class {
diff --git a/lib/ovsdb-idl.c b/lib/ovsdb-idl.c
index 4cb1c81..b29ca57 100644
--- a/lib/ovsdb-idl.c
+++ b/lib/ovsdb-idl.c
@@ -1,4 +1,5 @@
 /* Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016 Nicira, Inc.
+ * Copyright (C) 2016 Hewlett Packard Enterprise Development LP
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -83,6 +84,15 @@ enum ovsdb_idl_state {
     IDL_S_MONITORING2
 };
 
+/* Keeps the information of fetch request for on-demand fetch columns. */
+struct ovsdb_idl_fetch_node {
+   struct hmap_node hmap_node;      /* To store this structure in hmaps */
+   struct shash columns;            /* Contains the columns requested */
+   struct ovsdb_idl_table *table;   /* Pointer to the requested table */
+   enum ovsdb_idl_fetch_type fetch_type; /* Type of the request: row, column
+                                            or table */
+};
+
 struct ovsdb_idl {
     const struct ovsdb_idl_class *class;
     struct jsonrpc_session *session;
@@ -106,6 +116,10 @@ struct ovsdb_idl {
     /* Transaction support. */
     struct ovsdb_idl_txn *txn;
     struct hmap outstanding_txns;
+
+    /* On-demand fetch. */
+    struct hmap outstanding_fetch_reqs; /* Contains the data of the
+                                           on-going fetch operations */
 };
 
 struct ovsdb_idl_txn {
@@ -198,10 +212,19 @@ static void ovsdb_idl_parse_lock_reply(struct ovsdb_idl *,
 static void ovsdb_idl_parse_lock_notify(struct ovsdb_idl *,
                                         const struct json *params,
                                         bool new_has_lock);
+static void ovsdb_idl_parse_fetch_reply(struct ovsdb_idl *,
+                                        struct hmap_node *,
+                                        const struct json *);
+static struct ovsdb_error *ovsdb_idl_parse_fetch_reply__(
+                                     struct ovsdb_idl *,
+                                     struct ovsdb_idl_fetch_node *,
+                                     const struct json *);
 static struct ovsdb_idl_table *
 ovsdb_idl_table_from_class(const struct ovsdb_idl *,
                            const struct ovsdb_idl_table_class *);
 static bool ovsdb_idl_track_is_set(struct ovsdb_idl_table *table);
+static struct json *
+where_uuid_equals(const struct uuid *uuid);
 
 /* Creates and returns a connection to database 'remote', which should be in a
  * form acceptable to jsonrpc_session_open().  The connection will maintain an
@@ -256,6 +279,7 @@ ovsdb_idl_create(const char *remote, const struct ovsdb_idl_class *class,
         }
         hmap_init(&table->rows);
         list_init(&table->track_list);
+        shash_init(&table->outstanding_col_fetch_reqs);
         table->change_seqno[OVSDB_IDL_CHANGE_INSERT]
             = table->change_seqno[OVSDB_IDL_CHANGE_MODIFY]
             = table->change_seqno[OVSDB_IDL_CHANGE_DELETE] = 0;
@@ -267,6 +291,7 @@ ovsdb_idl_create(const char *remote, const struct ovsdb_idl_class *class,
     idl->schema = NULL;
 
     hmap_init(&idl->outstanding_txns);
+    hmap_init(&idl->outstanding_fetch_reqs);
 
     return idl;
 }
@@ -286,6 +311,7 @@ ovsdb_idl_destroy(struct ovsdb_idl *idl)
             struct ovsdb_idl_table *table = &idl->tables[i];
             shash_destroy(&table->columns);
             hmap_destroy(&table->rows);
+            shash_destroy(&table->outstanding_col_fetch_reqs);
             free(table->modes);
         }
         shash_destroy(&idl->table_by_name);
@@ -295,6 +321,7 @@ ovsdb_idl_destroy(struct ovsdb_idl *idl)
         json_destroy(idl->lock_request_id);
         json_destroy(idl->schema);
         hmap_destroy(&idl->outstanding_txns);
+        hmap_destroy(&idl->outstanding_fetch_reqs);
         free(idl);
     }
 }
@@ -348,6 +375,7 @@ void
 ovsdb_idl_run(struct ovsdb_idl *idl)
 {
     int i;
+    struct hmap_node *fetch_node;
 
     ovs_assert(!idl->txn);
     jsonrpc_session_run(idl->session);
@@ -383,6 +411,13 @@ ovsdb_idl_run(struct ovsdb_idl *idl)
             ovsdb_idl_parse_update(idl, msg->params->u.array.elems[1],
                                    OVSDB_UPDATE2);
         } else if (msg->type == JSONRPC_REPLY
+                   && msg->result->type == JSON_ARRAY
+                   && (fetch_node = hmap_first_with_hash(
+                                        &idl->outstanding_fetch_reqs,
+                                        json_hash(msg->id, 0))) != NULL) {
+            /* On-demand fetch reply received. */
+            ovsdb_idl_parse_fetch_reply(idl, fetch_node, msg->result->u.array.elems[0]);
+        } else if (msg->type == JSONRPC_REPLY
                    && idl->request_id
                    && json_equal(idl->request_id, msg->id)) {
             json_destroy(idl->request_id);
@@ -603,6 +638,26 @@ add_ref_table(struct ovsdb_idl *idl, const struct ovsdb_base_type *base)
     }
 }
 
+/*
+ * Turns on OVSDB_IDL_MANUAL_FETCH for 'column' in 'idl'. Columns in this mode
+ * are not synchronized automatically.
+ *
+ * In order to get its value from the database, it is necessary to explicity
+ * ask them from the server by calling one of this functions:
+ * ovsdb_idl_fetch_row, ovsdb_idl_fetch_column, or ovsdb_idl_fetch_table.
+ *
+ * This function adds the table with table class 'tc' to the replica (see
+ * ovsdb_idl_add_table() for more information)
+ */
+void
+ovsdb_idl_add_on_demand_column(struct ovsdb_idl *idl,
+                               struct ovsdb_idl_table_class *tc,
+                               const struct ovsdb_idl_column *column)
+{
+    ovsdb_idl_add_table(idl, tc);
+    *ovsdb_idl_get_mode(idl, column) = OVSDB_IDL_ON_DEMAND;
+}
+
 /* Turns on OVSDB_IDL_MONITOR and OVSDB_IDL_ALERT for 'column' in 'idl'.  Also
  * ensures that any tables referenced by 'column' will be replicated, even if
  * no columns in that table are selected for replication (see
@@ -1162,6 +1217,151 @@ ovsdb_idl_parse_update__(struct ovsdb_idl *idl,
     return NULL;
 }
 
+/* Processes an on-demand fetch request.
+ * 'pending_node' is a pointer to the request associated to the 'fetch_reply'.
+ * This function changes the IDL seqno.
+ */
+static void
+ovsdb_idl_parse_fetch_reply(struct ovsdb_idl *idl,
+                            struct hmap_node *pending_node,
+                            const struct json *fetch_reply)
+{
+
+    struct ovsdb_error *error = NULL;
+    struct ovsdb_idl_fetch_node *fetch_node;
+    /* Retrive the fetch node from the pending fetch hash */
+    fetch_node = CONTAINER_OF(pending_node, struct ovsdb_idl_fetch_node,
+            hmap_node);
+
+    if (fetch_reply->type != JSON_OBJECT) {
+        error = ovsdb_syntax_error(fetch_reply, NULL,
+                "<fetch_reply> is not an object");
+    } else {
+        error = ovsdb_idl_parse_fetch_reply__(idl, fetch_node, fetch_reply);
+    }
+
+    hmap_remove(&idl->outstanding_fetch_reqs, pending_node);
+    shash_destroy(&fetch_node->columns);
+    free(pending_node);
+
+    if (error) {
+        if (!VLOG_DROP_WARN(&syntax_rl)) {
+            char *s = ovsdb_error_to_string(error);
+            VLOG_WARN_RL(&syntax_rl, "%s", s);
+            free(s);
+        }
+        ovsdb_error_destroy(error);
+        return;
+    }
+
+    idl->change_seqno++;
+}
+
+/* Parses the JSON reply and updates the local replica.
+ * It clears the pending fetch flag of the row, column, or table fetched.
+ */
+static struct ovsdb_error *
+ovsdb_idl_parse_fetch_reply__(struct ovsdb_idl *idl,
+                              struct ovsdb_idl_fetch_node *fetch_node,
+                              const struct json* fetch_reply)
+{
+    struct uuid uuid;
+    const struct json_array *array;
+    const struct json *uuid_array;
+    const struct json *column_value;
+    struct ovsdb_datum column_data;
+    struct shash_node *shash_node;
+    struct ovsdb_idl_row *row;
+    struct ovsdb_idl_column *column = NULL;
+    struct ovsdb_idl_table *table = fetch_node->table;
+    unsigned int column_idx;
+    struct ovsdb_datum *old;
+
+    if (fetch_reply->type != JSON_OBJECT) {
+        return ovsdb_syntax_error(fetch_reply, NULL,
+               "<fetch_reply> is not an object");
+    }
+
+    /* Parse the json reply and get the UUID and value of the fetched column */
+
+    struct json_array *rows_array = json_array(shash_find_data(json_object(fetch_reply), "rows"));
+    for (int i = 0; i < rows_array->n; ++i) {
+        /* Read the uuid of the fetched row */
+        uuid_array = shash_find_data(json_object(rows_array->elems[i]), "_uuid");
+        if (!uuid_array || uuid_array->type != JSON_ARRAY) {
+            return ovsdb_syntax_error(fetch_reply, NULL,
+                    "Fetch reply for table %s does not include "
+                    "the UUID of the row", table->class->name);
+        }
+
+        array = json_array(uuid_array);
+        if (array->n != 2 || array->elems[1]->type != JSON_STRING
+                || !uuid_from_string(&uuid, array->elems[1]->u.string)) {
+            return ovsdb_syntax_error(fetch_reply, NULL,
+                    "Fetch reply for table %s contains bad UUID", table->class->name);
+        }
+
+        row = CONST_CAST(struct ovsdb_idl_row *,
+                ovsdb_idl_get_row_for_uuid(idl, fetch_node->table->class,
+                    &uuid));
+
+        /* If the row was deleted before the fetch reply was received,
+         * then there is no need to process the reply */
+        if (row != NULL) {
+            SHASH_FOR_EACH(shash_node, &fetch_node->columns) {
+                column = shash_node->data;
+                /* Read the fetched value */
+                column_value = shash_find_data(json_object(rows_array->elems[i]),
+                                               column->name);
+                if (!column_value) {
+                    return ovsdb_syntax_error(fetch_reply, NULL,
+                            "Fetch reply for table %s does not include "
+                            "the requested table value", table->class->name);
+                }
+
+                if (ovsdb_datum_from_json(&column_data, &column->type,
+                            column_value, NULL) != NULL) {
+                    /* In case this was a column request, remove it from the
+                     * outstanding_col_fetch_reqs */
+                    if (fetch_node->fetch_type == OVSDB_IDL_COLUMN_FETCH) {
+                        shash_find_and_delete(&table->outstanding_col_fetch_reqs,
+                                              column->name);
+                    }
+
+                    return ovsdb_syntax_error(fetch_reply, NULL,
+                            "Fetch reply for column %s contains bad column value",
+                            column->name);
+                }
+
+                /* Update the row */
+                column_idx = column - table->class->columns;
+                old = &row->old[column_idx];
+
+                if (!ovsdb_datum_equals(old, &column_data, &column->type)) {
+                    column->parse(row, &column_data);
+                    ovsdb_datum_swap(old, &column_data);
+                }
+
+                ovsdb_datum_destroy(&column_data, &column->type);
+                if (fetch_node->fetch_type == OVSDB_IDL_ROW_FETCH) {
+                    row->outstanding_fetch_reqs--;
+                }
+            }
+        }
+    }
+
+    if (fetch_node->fetch_type == OVSDB_IDL_COLUMN_FETCH && column) {
+        shash_find_and_delete(&table->outstanding_col_fetch_reqs, column->name);
+    }
+
+
+    if (fetch_node->fetch_type == OVSDB_IDL_TABLE_FETCH && table) {
+        table->has_pending_fetch = false;
+    }
+
+    return NULL;
+}
+
 static struct ovsdb_idl_row *
 ovsdb_idl_get_row(struct ovsdb_idl_table *table, const struct uuid *uuid)
 {
@@ -1547,6 +1747,7 @@ ovsdb_idl_row_create__(const struct ovsdb_idl_table_class *class)
     list_init(&row->dst_arcs);
     hmap_node_nullify(&row->txn_node);
     list_init(&row->track_node);
+    row->outstanding_fetch_reqs = 0;
     return row;
 }
 
@@ -1836,6 +2037,270 @@ ovsdb_idl_get(const struct ovsdb_idl_row *row,
     return ovsdb_idl_read(row, column);
 }
 
+/* Return true if any column of 'row' has a pending fetch operation.
+ *
+ * This function only considers on-demand requests done explicitly over 'row'
+ * i.e. after calling ovsdb_idl_fetch_row().
+ */
+bool
+ovsdb_idl_is_row_fetch_pending(const struct ovsdb_idl_row *row)
+{
+    return row->outstanding_fetch_reqs > 0;
+}
+
+/* Return true if 'column' has a pending fetch operation
+ *
+ * This function only considers on-demand requests done explicitly over
+ * 'column' i.e. after calling ovsdb_idl_fetch_column().
+ *
+ * It does not take into account rows requests.
+ */
+bool
+ovsdb_idl_is_column_fetch_pending(struct ovsdb_idl *idl,
+                                  const struct ovsdb_idl_table_class *tc,
+                                  const struct ovsdb_idl_column *column) {
+    struct shash_node *shash_node;
+    struct ovsdb_idl_table *table;
+    shash_node = shash_find(&idl->table_by_name, tc->name);
+    table = shash_node->data;
+
+    return shash_find(&table->outstanding_col_fetch_reqs, column->name) != NULL;
+}
+
+/* Return true if 'table' has a pending fetch operation.
+ *
+ * This function only considers on-demand requests done explicitly over 'table'
+ * i.e. after calling ovsdb_idl_fetch_table().
+ *
+ * It does not take into account columns nor rows requests.
+ */
+bool
+ovsdb_idl_is_table_fetch_pending(struct ovsdb_idl *idl,
+                                 const struct ovsdb_idl_table_class *tc) {
+    struct shash_node *shash_node;
+    struct ovsdb_idl_table *table;
+    shash_node = shash_find(&idl->table_by_name, tc->name);
+    table = shash_node->data;
+
+    return table->has_pending_fetch;
+}
+
+/* This function fetches the value of 'column' for the especified 'row'.
+ *
+ * After calling this function, the IDL requests the required value to the
+ * ovsdb server and updates it in the local replica.
+ *
+ * The function ovsdb_idl_is_row_fetch_pending can be used to verify if the
+ * fetch request has been processed.
+ */
+void
+ovsdb_idl_fetch_row(struct ovsdb_idl *idl,
+                    struct ovsdb_idl_row *row,
+                    const struct ovsdb_idl_column *column)
+{
+    struct json *request;
+    struct json *op;
+    struct json *columns;
+    struct json *fetch_id;
+    int status;
+    struct ovsdb_idl_fetch_node *fetch_node;
+
+    if (!(row->table->modes[column - row->table->class->columns] &
+          OVSDB_IDL_ON_DEMAND)) {
+        VLOG_WARN_RL(&syntax_rl,
+                "Error attempting to fetch a monitored column");
+        return;
+    }
+
+    request = json_array_create_1(
+            json_string_create(idl->class->database));
+    op = json_object_create();
+    json_object_put_string(op, "op", "select");
+    json_object_put_string(op, "table", row->table->class->name);
+    json_object_put(op, "where", where_uuid_equals(&row->uuid));
+    columns = json_array_create_2(json_string_create("_uuid"),
+                                  json_string_create(column->name));
+    json_object_put(op, "columns", columns);
+
+    json_array_add(request, op);
+    status = jsonrpc_session_send(idl->session,
+                jsonrpc_create_request("transact", request, &fetch_id));
+
+    if (status) {
+        VLOG_WARN_RL(&syntax_rl,
+                "Error while sending row fetch request (%d)", status);
+
+        json_destroy(fetch_id);
+        return;
+    }
+
+    fetch_node = xmalloc(sizeof *fetch_node);
+    shash_init(&fetch_node->columns);
+    shash_add_assert(&fetch_node->columns, column->name,
+                     CONST_CAST(struct ovsdb_idl_column *, column));
+
+    fetch_node->table = row->table;
+    fetch_node->fetch_type = OVSDB_IDL_ROW_FETCH;
+
+    hmap_insert(&idl->outstanding_fetch_reqs, &fetch_node->hmap_node,
+                json_hash(fetch_id, 0));
+
+    json_destroy(fetch_id);
+    row->outstanding_fetch_reqs++;
+}
+
+/* This function fetches the value 'column' for all the rows in the table.
+ *
+ * After calling this function, the IDL requests the required values to the
+ * ovsdb server and updates it in the local replica.
+ *
+ * The function ovsdb_idl_is_column_fetch_pending can be used to verify if the
+ * fetch request has been processed.
+ */
+void
+ovsdb_idl_fetch_column(struct ovsdb_idl *idl,
+                       const struct ovsdb_idl_table_class *table_class,
+                       struct ovsdb_idl_column *column)
+{
+    struct json *request;
+    struct json *op;
+    struct json *columns;
+    struct json *fetch_id;
+    int status;
+    struct ovsdb_idl_fetch_node *fetch_node;
+    struct ovsdb_idl_table *table;
+    struct shash_node *shash_node;
+
+    shash_node = shash_find(&idl->table_by_name, table_class->name);
+
+    if (!shash_node) {
+        VLOG_WARN_RL(&syntax_rl, "error attempting to fetch an unknown table");
+        return;
+    }
+
+    table = shash_node->data;
+
+    if (!(table->modes[column - table_class->columns] &
+          OVSDB_IDL_ON_DEMAND)) {
+        VLOG_WARN_RL(&syntax_rl,
+                "error attempting to fetch a monitored column");
+        return;
+    }
+
+    request = json_array_create_1(
+            json_string_create(idl->class->database));
+    op = json_object_create();
+    json_object_put_string(op, "op", "select");
+    json_object_put_string(op, "table", table_class->name);
+    json_object_put(op, "where", json_array_create_empty());
+    columns = json_array_create_2(json_string_create("_uuid"),
+                                  json_string_create(column->name));
+    json_object_put(op, "columns", columns);
+
+    json_array_add(request, op);
+    status = jsonrpc_session_send(idl->session,
+                jsonrpc_create_request("transact", request, &fetch_id));
+
+    if (status) {
+        VLOG_WARN_RL(&syntax_rl,
+                "Error while sending column fetch request (%d)", status);
+
+        json_destroy(fetch_id);
+        return;
+    }
+
+    fetch_node = xmalloc(sizeof *fetch_node);
+    shash_init(&fetch_node->columns);
+    shash_add_assert(&fetch_node->columns, column->name,
+                     CONST_CAST(struct ovsdb_idl_column *, column));
+    fetch_node->table = table;
+    fetch_node->fetch_type = OVSDB_IDL_COLUMN_FETCH;
+
+    hmap_insert(&idl->outstanding_fetch_reqs, &fetch_node->hmap_node,
+                json_hash(fetch_id, 0));
+
+    json_destroy(fetch_id);
+    shash_add(&table->outstanding_col_fetch_reqs, column->name, NULL);
+}
+
+/* This function fetches the value of all the on-demand columns in 'table'
+ *
+ * After calling this function, the IDL requests the required values to the
+ * ovsdb server and updates it in the local replica.
+ *
+ * The function ovsdb_idl_is_table_fetch_pending can be used to verify if the
+ * fetch request has been processed.
+ */
+void
+ovsdb_idl_fetch_table(struct ovsdb_idl *idl,
+                      struct ovsdb_idl_table_class *table_class)
+{
+    struct json *request;
+    struct json *op;
+    struct json *columns;
+    struct json *fetch_id;
+    int status;
+    struct ovsdb_idl_column *column;
+    struct ovsdb_idl_fetch_node *fetch_node;
+    struct ovsdb_idl_table *table;
+    struct shash_node *shash_node;
+
+    shash_node = shash_find(&idl->table_by_name, table_class->name);
+
+    if (!shash_node) {
+        VLOG_WARN_RL(&syntax_rl, "error attempting to fetch an unknown table");
+        return;
+    }
+
+    table = shash_node->data;
+
+    /* Create the fetch node and store the columns*/
+    fetch_node = xmalloc(sizeof *fetch_node);
+    shash_init(&fetch_node->columns);
+    fetch_node->table = table;
+    fetch_node->fetch_type = OVSDB_IDL_TABLE_FETCH;
+
+
+    request = json_array_create_1(json_string_create(idl->class->database));
+
+    op = json_object_create();
+    json_object_put_string(op, "op", "select");
+    json_object_put_string(op, "table", table->class->name);
+    json_object_put(op, "where", json_array_create_empty());
+
+    columns = json_array_create_1(json_string_create("_uuid"));
+
+    /* Add the on-demand columns to the request */
+    SHASH_FOR_EACH(shash_node, &table->columns) {
+        column = (struct ovsdb_idl_column*)shash_node->data;
+        if ((table->modes[column - table->class->columns] &
+                    OVSDB_IDL_ON_DEMAND)) {
+            json_array_add(columns, json_string_create(column->name));
+            shash_add(&fetch_node->columns, column->name, column);
+        }
+    }
+
+    json_object_put(op, "columns", columns);
+
+    json_array_add(request, op);
+    status = jsonrpc_session_send(idl->session,
+                 jsonrpc_create_request("transact", request, &fetch_id));
+
+    if (status) {
+        VLOG_WARN_RL(&syntax_rl,
+                     "Error while sending column fetch request (%d)", status);
+        json_destroy(fetch_id);
+        return;
+    }
+
+    hmap_insert(&idl->outstanding_fetch_reqs, &fetch_node->hmap_node,
+                json_hash(fetch_id, 0));
+
+    json_destroy(fetch_id);
+    table->has_pending_fetch = true;
+}
+
+
 /* Returns true if the field represented by 'column' in 'row' may be modified,
  * false if it is immutable.
  *
@@ -2541,8 +3006,9 @@ ovsdb_idl_txn_write__(const struct ovsdb_idl_row *row_,
 
     ovs_assert(row->new != NULL);
     ovs_assert(column_idx < class->n_columns);
-    ovs_assert(row->old == NULL ||
-               row->table->modes[column_idx] & OVSDB_IDL_MONITOR);
+    ovs_assert(row->old == NULL
+               || row->table->modes[column_idx] & OVSDB_IDL_MONITOR
+               || row->table->modes[column_idx] & OVSDB_IDL_ON_DEMAND);
 
     if (row->table->idl->verify_write_only && !write_only) {
         VLOG_ERR("Bug: Attempt to write to a read/write column (%s:%s) when"
diff --git a/lib/ovsdb-idl.h b/lib/ovsdb-idl.h
index 136c38c..b287dbf 100644
--- a/lib/ovsdb-idl.h
+++ b/lib/ovsdb-idl.h
@@ -1,4 +1,5 @@
 /* Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc.
+ * Copyright (C) 2016 Hewlett Packard Enterprise Development LP
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -44,6 +45,7 @@ struct ovsdb_datum;
 struct ovsdb_idl_class;
 struct ovsdb_idl_row;
 struct ovsdb_idl_column;
+struct ovsdb_idl_table;
 struct ovsdb_idl_table_class;
 struct uuid;
 
@@ -102,8 +104,12 @@ int ovsdb_idl_get_last_error(const struct ovsdb_idl *);
 #define OVSDB_IDL_MONITOR (1 << 0) /* Monitor this column? */
 #define OVSDB_IDL_ALERT   (1 << 1) /* Alert client when column updated? */
 #define OVSDB_IDL_TRACK   (1 << 2)
+#define OVSDB_IDL_ON_DEMAND (1 << 3) /* Manually update columns */
 
 void ovsdb_idl_add_column(struct ovsdb_idl *, const struct ovsdb_idl_column *);
+void ovsdb_idl_add_on_demand_column(struct ovsdb_idl *,
+                                    struct ovsdb_idl_table_class *,
+                                    const struct ovsdb_idl_column *);
 void ovsdb_idl_add_table(struct ovsdb_idl *,
                          const struct ovsdb_idl_table_class *);
 
@@ -118,6 +124,16 @@ enum ovsdb_idl_change {
     OVSDB_IDL_CHANGE_MAX
 };
 
+/* On-demand fetch columns.
+ * Fetch request types.
+ */
+enum ovsdb_idl_fetch_type {
+    OVSDB_IDL_ROW_FETCH,    /* Fetch a column value for a given row. */
+    OVSDB_IDL_COLUMN_FETCH, /* Fetch the value of a whole column. */
+    OVSDB_IDL_TABLE_FETCH   /* Fetch the value of all on-demand
+                               columns that are part of given table. */
+};
+
 /* Row, table sequence numbers */
 unsigned int ovsdb_idl_table_get_seqno(
     const struct ovsdb_idl *idl,
@@ -152,6 +168,22 @@ const struct ovsdb_datum *ovsdb_idl_get(const struct ovsdb_idl_row *,
                                         const struct ovsdb_idl_column *,
                                         enum ovsdb_atomic_type key_type,
                                         enum ovsdb_atomic_type value_type);
+void ovsdb_idl_fetch_row(struct ovsdb_idl *,
+                         struct ovsdb_idl_row *,
+                         const struct ovsdb_idl_column *);
+void ovsdb_idl_fetch_column(struct ovsdb_idl *,
+                            const struct ovsdb_idl_table_class *,
+                            struct ovsdb_idl_column *);
+void ovsdb_idl_fetch_table(struct ovsdb_idl *,
+                           struct ovsdb_idl_table_class *);
+
+bool ovsdb_idl_is_row_fetch_pending(const struct ovsdb_idl_row *);
+bool ovsdb_idl_is_column_fetch_pending(struct ovsdb_idl *,
+                                       const struct ovsdb_idl_table_class *,
+                                       const struct ovsdb_idl_column *);
+bool ovsdb_idl_is_table_fetch_pending(struct ovsdb_idl *,
+                                      const struct ovsdb_idl_table_class *);
+
 bool ovsdb_idl_is_mutable(const struct ovsdb_idl_row *,
                           const struct ovsdb_idl_column *);
 
-- 
1.9.1



More information about the dev mailing list