V2:
- rebased (fixed conflicts in NEWS).
---
NEWS | 4 ++++
ovsdb/ovsdb-server.c | 38 +++++++++++++++++++++++++++++++++
ovsdb/row.c | 17 +++++++++++++++
ovsdb/row.h | 1 +
ovsdb/table.c | 7 ++++++
ovsdb/table.h | 3 +++
ovsdb/transaction.c | 51
++++++++++++++++++++++++++++++++++++++++++++
7 files changed, 121 insertions(+)
diff --git a/NEWS b/NEWS
index 90f4b15902b8..d56329772276 100644
--- a/NEWS
+++ b/NEWS
@@ -10,6 +10,10 @@ Post-v2.16.0
limiting behavior.
* Add hardware offload support for matching IPv4/IPv6 frag
types
(experimental).
+ - OVSDB:
+ * New unixctl command 'ovsdb-server/log-db-ops DB TABLE on|off".
+ If turned on, ovsdb-server will log (at level INFO and rate
limited)
+ all operations that are committed to table TABLE in the DB
database.
v2.16.0 - 16 Aug 2021
diff --git a/ovsdb/ovsdb-server.c b/ovsdb/ovsdb-server.c
index 0b3d2bb71432..c48645f7e255 100644
--- a/ovsdb/ovsdb-server.c
+++ b/ovsdb/ovsdb-server.c
@@ -115,6 +115,7 @@ static unixctl_cb_func ovsdb_server_list_remotes;
static unixctl_cb_func ovsdb_server_add_database;
static unixctl_cb_func ovsdb_server_remove_database;
static unixctl_cb_func ovsdb_server_list_databases;
+static unixctl_cb_func ovsdb_server_log_db_ops;
static void read_db(struct server_config *, struct db *);
static struct ovsdb_error *open_db(struct server_config *,
@@ -443,6 +444,8 @@ main(int argc, char *argv[])
ovsdb_server_remove_database,
&server_config);
unixctl_command_register("ovsdb-server/list-dbs", "", 0, 0,
ovsdb_server_list_databases, &all_dbs);
+ unixctl_command_register("ovsdb-server/log-db-ops", "DB TABLE
on|off",
+ 3, 3, ovsdb_server_log_db_ops, &all_dbs);
unixctl_command_register("ovsdb-server/perf-counters-show", "",
0, 0,
ovsdb_server_perf_counters_show, NULL);
unixctl_command_register("ovsdb-server/perf-counters-clear", "",
0, 0,
@@ -1769,6 +1772,41 @@ ovsdb_server_list_databases(struct unixctl_conn
*conn, int argc OVS_UNUSED,
ds_destroy(&s);
}
+static void
+ovsdb_server_log_db_ops(struct unixctl_conn *conn, int argc
OVS_UNUSED,
+ const char *argv[], void *all_dbs_)
+{
+ struct shash *all_dbs = all_dbs_;
+ const char *db_name = argv[1];
+ const char *tbl_name = argv[2];
+ const char *command = argv[3];
+ bool log;
+
+ if (!strcmp(command, "on")) {
+ log = true;
+ } else if (!strcmp(command, "off")) {
+ log = false;
+ } else {
+ unixctl_command_reply_error(conn, "invalid argument");
+ return;
+ }
+
+ struct db *db = shash_find_data(all_dbs, db_name);
+ if (!db) {
+ unixctl_command_reply_error(conn, "no such database");
+ return;
+ }
+
+ struct ovsdb_table *table = ovsdb_get_table(db->db, tbl_name);
+ if (!table) {
+ unixctl_command_reply_error(conn, "no such table");
+ return;
+ }
+
+ ovsdb_table_log_ops(table, log);
+ unixctl_command_reply(conn, NULL);
+}
+
static void
ovsdb_server_get_sync_status(struct unixctl_conn *conn, int argc
OVS_UNUSED,
const char *argv[] OVS_UNUSED, void
*config_)
diff --git a/ovsdb/row.c b/ovsdb/row.c
index 65a0546211c8..5e31716506bc 100644
--- a/ovsdb/row.c
+++ b/ovsdb/row.c
@@ -278,6 +278,23 @@ ovsdb_row_to_json(const struct ovsdb_row *row,
}
return json;
}
+
+void
+ovsdb_row_to_string(const struct ovsdb_row *row, struct ds *out)
+{
+ struct shash_node *node;
+
+ SHASH_FOR_EACH (node, &row->table->schema->columns) {
+ const struct ovsdb_column *column = node->data;
+
+ ds_put_format(out, "%s:", column->name);
+ ovsdb_datum_to_string(&row->fields[column->index],
&column->type, out);
+ ds_put_cstr(out, ",");
+ }
+ if (shash_count(&row->table->schema->columns)) {
+ ds_chomp(out, ',');
+ }
+}
void
ovsdb_row_set_init(struct ovsdb_row_set *set)
diff --git a/ovsdb/row.h b/ovsdb/row.h
index 394ac8eb49b6..f22a08ecd197 100644
--- a/ovsdb/row.h
+++ b/ovsdb/row.h
@@ -95,6 +95,7 @@ struct ovsdb_error *ovsdb_row_from_json(struct
ovsdb_row *,
OVS_WARN_UNUSED_RESULT;
struct json *ovsdb_row_to_json(const struct ovsdb_row *,
const struct ovsdb_column_set
*include);
+void ovsdb_row_to_string(const struct ovsdb_row *, struct ds *);
static inline const struct uuid *
ovsdb_row_get_uuid(const struct ovsdb_row *row)
diff --git a/ovsdb/table.c b/ovsdb/table.c
index 455a3663fe89..b7b41d139914 100644
--- a/ovsdb/table.c
+++ b/ovsdb/table.c
@@ -301,10 +301,17 @@ ovsdb_table_create(struct ovsdb_table_schema *ts)
hmap_init(&table->indexes[i]);
}
hmap_init(&table->rows);
+ table->log = false;
return table;
}
+void
+ovsdb_table_log_ops(struct ovsdb_table *table, bool enabled)
+{
+ table->log = enabled;
+}
+
void
ovsdb_table_destroy(struct ovsdb_table *table)
{
diff --git a/ovsdb/table.h b/ovsdb/table.h
index ce69a5d130bf..be88b7a59279 100644
--- a/ovsdb/table.h
+++ b/ovsdb/table.h
@@ -63,10 +63,13 @@ struct ovsdb_table {
* ovsdb_row"s. Each of the hmap_nodes in indexes[i] are at
index 'i' at
* the end of struct ovsdb_row, following the 'fields'
member. */
struct hmap *indexes;
+
+ bool log; /* True if logging is enabled for this table. */
};
struct ovsdb_table *ovsdb_table_create(struct
ovsdb_table_schema *);
void ovsdb_table_destroy(struct ovsdb_table *);
+void ovsdb_table_log_ops(struct ovsdb_table *, bool);
const struct ovsdb_row *ovsdb_table_get_row(const struct
ovsdb_table *,
const struct uuid *);
diff --git a/ovsdb/transaction.c b/ovsdb/transaction.c
index 8ffefcf7c9d0..dc07e9c00a4b 100644
--- a/ovsdb/transaction.c
+++ b/ovsdb/transaction.c
@@ -29,6 +29,7 @@
#include "openvswitch/vlog.h"
#include "ovsdb-error.h"
#include "ovsdb.h"
+#include "ovs-thread.h"
#include "row.h"
#include "storage.h"
#include "table.h"
@@ -95,6 +96,7 @@ struct ovsdb_txn_row {
static struct ovsdb_error * OVS_WARN_UNUSED_RESULT
delete_garbage_row(struct ovsdb_txn *txn, struct ovsdb_txn_row *r);
static void ovsdb_txn_row_prefree(struct ovsdb_txn_row *);
+static void ovsdb_txn_row_log(const struct ovsdb_txn_row *);
static struct ovsdb_error * OVS_WARN_UNUSED_RESULT
for_each_txn_row(struct ovsdb_txn *txn,
struct ovsdb_error *(*)(struct ovsdb_txn *,
@@ -104,6 +106,11 @@ for_each_txn_row(struct ovsdb_txn *txn,
* processed. */
static unsigned int serial;
+/* Used by ovsdb_txn_row_log() to avoid reallocating dynamic
strings
+ * every time a row operation is logged.
+ */
+DEFINE_STATIC_PER_THREAD_DATA(struct ds, row_log_str,
DS_EMPTY_INITIALIZER);
+
struct ovsdb_txn *
ovsdb_txn_create(struct ovsdb *db)
{
@@ -422,6 +429,49 @@ update_ref_counts(struct ovsdb_txn *txn)
return for_each_txn_row(txn, check_ref_count);
}
+static void
+ovsdb_txn_row_log(const struct ovsdb_txn_row *txn_row)
+{
+ static struct vlog_rate_limit rl_insert =
VLOG_RATE_LIMIT_INIT(30, 60);
+ static struct vlog_rate_limit rl_update =
VLOG_RATE_LIMIT_INIT(30, 60);
+ static struct vlog_rate_limit rl_delete =
VLOG_RATE_LIMIT_INIT(30, 60);
+
+ if (!txn_row->table->log) {
+ return;
+ }