Skip to content

Commit 42d99eb

Browse files
almusiligsilya
authored andcommitted
ovsdb-idl: Add a way to assert IDL txn as read only.
Add a way to assert IDL txn as read only, that is useful in cases when we want to ensure that the program doesn't write anything into the txn. It is done via assert because this is considered a bug which should be fixed in the IDL caller. Acked-by: Dumitru Ceara <[email protected]> Signed-off-by: Ales Musil <[email protected]> Signed-off-by: Ilya Maximets <[email protected]>
1 parent 2e28fe3 commit 42d99eb

File tree

5 files changed

+101
-2
lines changed

5 files changed

+101
-2
lines changed

NEWS

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,10 @@ Post-v3.6.0
55
connections, instead of limiting these commands to IPv6 only.
66
- DPDK:
77
* OVS validated with DPDK 24.11.3.
8+
- OVSDB-IDL:
9+
* New ovsdb_idl_txn_assert_read_only() interface to mark transactions
10+
as read-only and trigger assertion failure when application attempts
11+
to modify the database data through this transaction.
812

913

1014
v3.6.0 - 18 Aug 2025

lib/ovsdb-idl.c

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,7 @@ struct ovsdb_idl_txn {
111111
enum ovsdb_idl_txn_status status;
112112
char *error;
113113
bool dry_run;
114+
bool assert_read_only;
114115
struct ds comment;
115116

116117
/* Increments. */
@@ -2771,6 +2772,7 @@ ovsdb_idl_txn_create(struct ovsdb_idl *idl)
27712772
txn->status = TXN_UNCOMMITTED;
27722773
txn->error = NULL;
27732774
txn->dry_run = false;
2775+
txn->assert_read_only = false;
27742776
ds_init(&txn->comment);
27752777

27762778
txn->inc_table = NULL;
@@ -2839,6 +2841,7 @@ ovsdb_idl_txn_increment(struct ovsdb_idl_txn *txn,
28392841
ovs_assert(!txn->inc_table);
28402842
ovs_assert(column->type.key.type == OVSDB_TYPE_INTEGER);
28412843
ovs_assert(column->type.value.type == OVSDB_TYPE_VOID);
2844+
ovs_assert(!txn->assert_read_only);
28422845

28432846
txn->inc_table = row->table->class_->name;
28442847
txn->inc_column = column->name;
@@ -3560,6 +3563,18 @@ ovsdb_idl_txn_abort(struct ovsdb_idl_txn *txn)
35603563
}
35613564
}
35623565

3566+
/* If 'assert' is true, configures the IDL to generate an assertion
3567+
* failure when a write operation is attempted on the transaction.
3568+
* Otherwise, write operations are allowed on the transaction.
3569+
* The check will turn into no-op when building with NDEBUG. */
3570+
void
3571+
ovsdb_idl_txn_assert_read_only(struct ovsdb_idl_txn *txn, bool assert)
3572+
{
3573+
if (txn) {
3574+
txn->assert_read_only = assert;
3575+
}
3576+
}
3577+
35633578
/* Returns a string that reports the error status for 'txn'. The caller must
35643579
* not modify or free the returned string. A call to ovsdb_idl_txn_destroy()
35653580
* for 'txn' may free the returned string.
@@ -3650,6 +3665,7 @@ ovsdb_idl_txn_write__(const struct ovsdb_idl_row *row_,
36503665
ovs_assert(row->new_datum != NULL);
36513666
ovs_assert(column_idx < class->n_columns);
36523667
ovs_assert(row->old_datum == NULL || column_mode & OVSDB_IDL_MONITOR);
3668+
ovs_assert(!row->table->idl->txn->assert_read_only);
36533669

36543670
if (row->table->idl->verify_write_only && !write_only) {
36553671
VLOG_ERR("Bug: Attempt to write to a read/write column (%s:%s) when"
@@ -3834,6 +3850,7 @@ ovsdb_idl_txn_delete(const struct ovsdb_idl_row *row_)
38343850

38353851
ovs_assert(row->new_datum != NULL);
38363852
ovs_assert(!is_index_row(row_));
3853+
ovs_assert(!row->table->idl->txn->assert_read_only);
38373854
ovsdb_idl_remove_from_indexes(row_);
38383855
if (!row->old_datum) {
38393856
ovsdb_idl_row_unparse(row);
@@ -3863,6 +3880,7 @@ ovsdb_idl_txn_insert__(struct ovsdb_idl_txn *txn,
38633880
struct ovsdb_idl_row *row = ovsdb_idl_row_create__(class);
38643881

38653882
ovs_assert(uuid || !persist_uuid);
3883+
ovs_assert(!txn->assert_read_only);
38663884
if (uuid) {
38673885
ovs_assert(!ovsdb_idl_txn_get_row(txn, uuid));
38683886
row->uuid = *uuid;
@@ -4221,6 +4239,8 @@ ovsdb_idl_txn_add_map_op(struct ovsdb_idl_row *row,
42214239
struct ovsdb_datum *datum,
42224240
enum map_op_type op_type)
42234241
{
4242+
ovs_assert(!row->table->idl->txn->assert_read_only);
4243+
42244244
const struct ovsdb_idl_table_class *class;
42254245
size_t column_idx;
42264246
struct map_op *map_op;
@@ -4257,6 +4277,8 @@ ovsdb_idl_txn_add_set_op(struct ovsdb_idl_row *row,
42574277
struct ovsdb_datum *datum,
42584278
enum set_op_type op_type)
42594279
{
4280+
ovs_assert(!row->table->idl->txn->assert_read_only);
4281+
42604282
const struct ovsdb_idl_table_class *class;
42614283
size_t column_idx;
42624284
struct set_op *set_op;

lib/ovsdb-idl.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -351,6 +351,7 @@ void ovsdb_idl_txn_wait(const struct ovsdb_idl_txn *);
351351
enum ovsdb_idl_txn_status ovsdb_idl_txn_commit(struct ovsdb_idl_txn *);
352352
enum ovsdb_idl_txn_status ovsdb_idl_txn_commit_block(struct ovsdb_idl_txn *);
353353
void ovsdb_idl_txn_abort(struct ovsdb_idl_txn *);
354+
void ovsdb_idl_txn_assert_read_only(struct ovsdb_idl_txn *, bool assert);
354355

355356
const char *ovsdb_idl_txn_get_error(const struct ovsdb_idl_txn *);
356357

tests/ovsdb-idl.at

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3000,3 +3000,60 @@ m4_define([OVSDB_CHECK_IDL_CHANGE_AWARE],
30003000

30013001
OVSDB_CHECK_IDL_CHANGE_AWARE([standalone])
30023002
OVSDB_CHECK_IDL_CHANGE_AWARE([clustered])
3003+
3004+
AT_SETUP([ovsdb-idl - read only insert - C])
3005+
AT_KEYWORDS([ovsdb server idl read only assert])
3006+
OVSDB_START_IDLTEST
3007+
3008+
AT_CHECK([[ovsdb-client transact unix:socket \
3009+
'["idltest",
3010+
{"op": "insert",
3011+
"table": "simple",
3012+
"row": {}}]']],
3013+
[0], [ignore], [ignore])
3014+
AT_CHECK([[test-ovsdb '-vPATTERN:console:test-ovsdb|%c|%m' -vjsonrpc -t10 \
3015+
--assert-read-only idl unix:socket 'insert 1']],
3016+
[ignore], [ignore], [stderr])
3017+
AT_SKIP_IF([grep -q "Assertion tests are not available due to NDEBUG" stderr])
3018+
AT_CHECK([grep -qE "assertion !.*txn->assert_read_only failed" stderr])
3019+
3020+
OVSDB_SERVER_SHUTDOWN
3021+
AT_CLEANUP
3022+
3023+
AT_SETUP([ovsdb-idl - read only set - C])
3024+
AT_KEYWORDS([ovsdb server idl read only assert])
3025+
OVSDB_START_IDLTEST
3026+
3027+
AT_CHECK([[ovsdb-client transact unix:socket \
3028+
'["idltest",
3029+
{"op": "insert",
3030+
"table": "simple",
3031+
"row": {}}]']],
3032+
[0], [ignore], [ignore])
3033+
AT_CHECK([[test-ovsdb '-vPATTERN:console:test-ovsdb|%c|%m' -vjsonrpc -t10 \
3034+
--assert-read-only idl unix:socket 'set 0 r 2.0']],
3035+
[ignore], [ignore], [stderr])
3036+
AT_SKIP_IF([grep -q "Assertion tests are not available due to NDEBUG" stderr])
3037+
AT_CHECK([grep -qE "assertion !.*txn->assert_read_only failed" stderr])
3038+
3039+
OVSDB_SERVER_SHUTDOWN
3040+
AT_CLEANUP
3041+
3042+
AT_SETUP([ovsdb-idl - read only delete - C])
3043+
AT_KEYWORDS([ovsdb server idl read only assert])
3044+
OVSDB_START_IDLTEST
3045+
3046+
AT_CHECK([[ovsdb-client transact unix:socket \
3047+
'["idltest",
3048+
{"op": "insert",
3049+
"table": "simple",
3050+
"row": {"i": 1}}]']],
3051+
[0], [ignore], [ignore])
3052+
AT_CHECK([[test-ovsdb '-vPATTERN:console:test-ovsdb|%c|%m' -vjsonrpc -t10 \
3053+
--assert-read-only idl unix:socket 'delete 1']],
3054+
[ignore], [ignore], [stderr])
3055+
AT_SKIP_IF([grep -q "Assertion tests are not available due to NDEBUG" stderr])
3056+
AT_CHECK([grep -qE "assertion !.*txn->assert_read_only failed" stderr])
3057+
3058+
OVSDB_SERVER_SHUTDOWN
3059+
AT_CLEANUP

tests/test-ovsdb.c

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,11 @@
1616

1717
#include <config.h>
1818

19+
#ifdef NDEBUG
20+
#define TEST_OVSDB_SKIP_ASSERT 1
21+
#undef NDEBUG
22+
#endif
23+
1924
#include <fcntl.h>
2025
#include <getopt.h>
2126
#include <inttypes.h>
@@ -57,6 +62,7 @@ VLOG_DEFINE_THIS_MODULE(test_ovsdb);
5762

5863
struct test_ovsdb_pvt_context {
5964
bool write_changed_only;
65+
bool assert_read_only;
6066
bool track;
6167
};
6268

@@ -93,6 +99,7 @@ parse_options(int argc, char *argv[], struct test_ovsdb_pvt_context *pvt)
9399
{"verbose", optional_argument, NULL, 'v'},
94100
{"change-track", optional_argument, NULL, 'c'},
95101
{"write-changed-only", optional_argument, NULL, 'w'},
102+
{"assert-read-only", optional_argument, NULL, 'r'},
96103
{"magic", required_argument, NULL, OPT_MAGIC},
97104
{"no-rename-open-files", no_argument, NULL, OPT_NO_RENAME_OPEN_FILES},
98105
{"help", no_argument, NULL, 'h'},
@@ -131,6 +138,13 @@ parse_options(int argc, char *argv[], struct test_ovsdb_pvt_context *pvt)
131138
pvt->write_changed_only = true;
132139
break;
133140

141+
case 'r':
142+
#ifdef TEST_OVSDB_SKIP_ASSERT
143+
ovs_fatal(0, "Assertion tests are not available due to NDEBUG");
144+
#endif
145+
pvt->assert_read_only = true;
146+
break;
147+
134148
case OPT_MAGIC:
135149
magic = optarg;
136150
break;
@@ -2459,7 +2473,7 @@ idltest_find_simple(struct ovsdb_idl *idl, int i)
24592473
}
24602474

24612475
static bool
2462-
idl_set(struct ovsdb_idl *idl, char *commands, int step)
2476+
idl_set(struct ovsdb_idl *idl, char *commands, int step, bool assert_read_only)
24632477
{
24642478
char *cmd, *save_ptr1 = NULL;
24652479
struct ovsdb_idl_txn *txn;
@@ -2472,6 +2486,7 @@ idl_set(struct ovsdb_idl *idl, char *commands, int step)
24722486

24732487
txn = ovsdb_idl_txn_create(idl);
24742488
ovsdb_idl_check_consistency(idl);
2489+
ovsdb_idl_txn_assert_read_only(txn, assert_read_only);
24752490
for (cmd = strtok_r(commands, ",", &save_ptr1); cmd;
24762491
cmd = strtok_r(NULL, ",", &save_ptr1)) {
24772492
char *save_ptr2 = NULL;
@@ -2911,7 +2926,7 @@ do_idl(struct ovs_cmdl_context *ctx)
29112926
next_cond_seqno = update_conditions(idl, arg + strlen(cond_s),
29122927
step++);
29132928
} else if (arg[0] != '[') {
2914-
if (!idl_set(idl, arg, step++)) {
2929+
if (!idl_set(idl, arg, step++, pvt->assert_read_only)) {
29152930
/* If idl_set() returns false, then no transaction
29162931
* was sent to the server and most likely 'seqno'
29172932
* would remain the same. And the above 'Wait for update'

0 commit comments

Comments
 (0)