Coverage Report

Created: 2026-07-16 06:13

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/openvswitch/lib/ovsdb-cs.c
Line
Count
Source
1
/* Copyright (c) 2009, 2010, 2011, 2012, 2013, 2014, 2015, 2016, 2017 Nicira, Inc.
2
 * Copyright (C) 2016 Hewlett Packard Enterprise Development LP
3
 *
4
 * Licensed under the Apache License, Version 2.0 (the "License");
5
 * you may not use this file except in compliance with the License.
6
 * You may obtain a copy of the License at:
7
 *
8
 *     http://www.apache.org/licenses/LICENSE-2.0
9
 *
10
 * Unless required by applicable law or agreed to in writing, software
11
 * distributed under the License is distributed on an "AS IS" BASIS,
12
 * WITHOUT WARRANTIES OR CONDITION OF ANY KIND, either express or implied.
13
 * See the License for the specific language governing permissions and
14
 * limitations under the License.
15
 */
16
17
#include <config.h>
18
19
#include "ovsdb-cs.h"
20
21
#include <errno.h>
22
23
#include "hash.h"
24
#include "jsonrpc.h"
25
#include "openvswitch/dynamic-string.h"
26
#include "openvswitch/hmap.h"
27
#include "openvswitch/json.h"
28
#include "openvswitch/poll-loop.h"
29
#include "openvswitch/shash.h"
30
#include "openvswitch/vlog.h"
31
#include "ovsdb-data.h"
32
#include "ovsdb-error.h"
33
#include "ovsdb-parser.h"
34
#include "ovsdb-session.h"
35
#include "ovsdb-types.h"
36
#include "svec.h"
37
#include "util.h"
38
#include "uuid.h"
39
40
VLOG_DEFINE_THIS_MODULE(ovsdb_cs);
41
42
/* Connection state machine.
43
 *
44
 * When a JSON-RPC session connects, the CS layer sends a "monitor_cond"
45
 * request for the Database table in the _Server database and transitions to
46
 * the CS_S_SERVER_MONITOR_REQUESTED state.  If the session drops and
47
 * reconnects, or if the CS receives a "monitor_canceled" notification for a
48
 * table it is monitoring, the CS starts over again in the same way. */
49
#define OVSDB_CS_STATES                                                 \
50
    /* Waits for "get_schema" reply, then sends "monitor_cond"          \
51
     * request for the Database table in the _Server database, whose    \
52
     * details are informed by the schema, and transitions to           \
53
     * CS_S_SERVER_MONITOR_REQUESTED. */                                \
54
0
    OVSDB_CS_STATE(SERVER_SCHEMA_REQUESTED)                             \
55
0
                                                                        \
56
0
    /* Waits for "monitor_cond" reply for the Database table:           \
57
0
     *                                                                  \
58
0
     * - If the reply indicates success, and the Database table has a   \
59
0
     *   row for the CS database:                                       \
60
0
     *                                                                  \
61
0
     *   * If the row indicates that this is a clustered database       \
62
0
     *     that is not connected to the cluster, closes the             \
63
0
     *     connection.  The next connection attempt has a chance at     \
64
0
     *     picking a connected server.                                  \
65
0
     *                                                                  \
66
0
     *   * Otherwise, sends a monitoring request for the CS             \
67
0
     *     database whose details are informed by the schema            \
68
0
     *     (obtained from the row), and transitions to                  \
69
0
     *     CS_S_DATA_MONITOR_(COND_(SINCE_))REQUESTED.                  \
70
0
     *                                                                  \
71
0
     * - If the reply indicates success, but the Database table does    \
72
0
     *   not have a row for the CS database, transitions to             \
73
0
     *   CS_S_ERROR.                                                    \
74
0
     *                                                                  \
75
0
     * - If the reply indicates failure, sends a "get_schema" request   \
76
0
     *   for the CS database and transitions to                         \
77
0
     *   CS_S_DATA_SCHEMA_REQUESTED. */                                 \
78
0
    OVSDB_CS_STATE(SERVER_MONITOR_REQUESTED)                            \
79
0
                                                                        \
80
0
    /* Waits for "get_schema" reply, then sends "monitor_cond"          \
81
0
     * request whose details are informed by the schema, and            \
82
0
     * transitions to CS_S_DATA_MONITOR_COND_REQUESTED. */              \
83
0
    OVSDB_CS_STATE(DATA_SCHEMA_REQUESTED)                               \
84
0
                                                                        \
85
0
    /* Waits for "monitor_cond_since" reply.  If successful, replaces   \
86
0
     * the CS contents by the data carried in the reply and             \
87
0
     * transitions to CS_S_MONITORING.  On failure, sends a             \
88
0
     * "monitor_cond" request and transitions to                        \
89
0
     * CS_S_DATA_MONITOR_COND_REQUESTED. */                             \
90
0
    OVSDB_CS_STATE(DATA_MONITOR_COND_SINCE_REQUESTED)                   \
91
0
                                                                        \
92
0
    /* Waits for "monitor_cond" reply.  If successful, replaces the     \
93
0
     * CS contents by the data carried in the reply and transitions     \
94
0
     * to CS_S_MONITORING.  On failure, sends a "monitor" request       \
95
0
     * and transitions to CS_S_DATA_MONITOR_REQUESTED. */               \
96
0
    OVSDB_CS_STATE(DATA_MONITOR_COND_REQUESTED)                         \
97
0
                                                                        \
98
0
    /* Waits for "monitor" reply.  If successful, replaces the CS       \
99
0
     * contents by the data carried in the reply and transitions to     \
100
0
     * CS_S_MONITORING.  On failure, transitions to CS_S_ERROR. */      \
101
0
    OVSDB_CS_STATE(DATA_MONITOR_REQUESTED)                              \
102
0
                                                                        \
103
0
    /* State that processes "update", "update2" or "update3"            \
104
0
     * notifications for the main database (and the Database table      \
105
0
     * in _Server if available).                                        \
106
0
     *                                                                  \
107
0
     * If we're monitoring the Database table and we get notified       \
108
0
     * that the CS database has been deleted, we close the              \
109
0
     * connection (which will restart the state machine). */            \
110
0
    OVSDB_CS_STATE(MONITORING)                                          \
111
0
                                                                        \
112
0
    /* Terminal error state that indicates that nothing useful can be   \
113
0
     * done, for example because the database server doesn't actually   \
114
0
     * have the desired database.  We maintain the session with the     \
115
0
     * database server anyway.  If it starts serving the database       \
116
0
     * that we want, or if someone fixes and restarts the database,     \
117
0
     * then it will kill the session and we will automatically          \
118
0
     * reconnect and try again. */                                      \
119
0
    OVSDB_CS_STATE(ERROR)                                               \
120
0
                                                                        \
121
0
    /* Terminal state that indicates we connected to a useless server   \
122
0
     * in a cluster, e.g. one that is partitioned from the rest of      \
123
0
     * the cluster. We're waiting to retry. */                          \
124
0
    OVSDB_CS_STATE(RETRY)
125
126
enum ovsdb_cs_state {
127
#define OVSDB_CS_STATE(NAME) CS_S_##NAME,
128
    OVSDB_CS_STATES
129
#undef OVSDB_CS_STATE
130
};
131
132
static const char *
133
ovsdb_cs_state_to_string(enum ovsdb_cs_state state)
134
0
{
135
0
    switch (state) {
136
0
#define OVSDB_CS_STATE(NAME) case CS_S_##NAME: return #NAME;
137
0
        OVSDB_CS_STATES
138
0
#undef OVSDB_CS_STATE
139
0
    default: return "<unknown>";
140
0
    }
141
0
}
142
143
/* A database being monitored.
144
 *
145
 * There are two instances of this data structure for each CS instance, one for
146
 * the _Server database used for working with clusters, and the other one for
147
 * the actual database that the client is interested in.  */
148
struct ovsdb_cs_db {
149
    struct ovsdb_cs *cs;
150
151
    /* Data. */
152
    const char *db_name;        /* Database's name. */
153
    struct hmap tables;         /* Contains "struct ovsdb_cs_db_table *"s.*/
154
    struct json *monitor_id;
155
    struct json *schema;
156
157
    /* Monitor version. */
158
    int max_version;            /* Maximum version of monitor request to use. */
159
    int monitor_version;        /* 0 if not monitoring, 1=monitor,
160
                                 * 2=monitor_cond, 3=monitor_cond_since. */
161
162
    /* Condition changes. */
163
    bool cond_changed;          /* Change not yet sent to server? */
164
    unsigned int cond_seqno;    /* Increments when condition changes. */
165
166
    /* Database locking. */
167
    char *lock_name;            /* Name of lock we need, NULL if none. */
168
    bool has_lock;              /* Has db server told us we have the lock? */
169
    bool is_lock_contended;     /* Has db server told us we can't get lock? */
170
    struct json *lock_request_id; /* JSON-RPC ID of in-flight lock request. */
171
172
    /* Last db txn id, used for fast resync through monitor_cond_since */
173
    struct uuid last_id;
174
175
    /* Client interface. */
176
    struct ovs_list events;
177
    const struct ovsdb_cs_ops *ops;
178
    void *ops_aux;
179
};
180
181
static const struct ovsdb_cs_ops ovsdb_cs_server_ops;
182
183
static void ovsdb_cs_db_destroy_tables(struct ovsdb_cs_db *);
184
static unsigned int ovsdb_cs_db_set_condition(
185
    struct ovsdb_cs_db *, const char *db_name, const struct json *condition);
186
187
static void ovsdb_cs_send_schema_request(struct ovsdb_cs *,
188
                                          struct ovsdb_cs_db *);
189
static void ovsdb_cs_send_db_change_aware(struct ovsdb_cs *);
190
static bool ovsdb_cs_check_server_db(struct ovsdb_cs *);
191
static void ovsdb_cs_clear_server_rows(struct ovsdb_cs *);
192
static void ovsdb_cs_send_monitor_request(struct ovsdb_cs *,
193
                                          struct ovsdb_cs_db *, int version);
194
static void ovsdb_cs_db_ack_condition(struct ovsdb_cs_db *db);
195
static void ovsdb_cs_db_sync_condition(struct ovsdb_cs_db *db);
196
197
struct ovsdb_cs {
198
    struct ovsdb_cs_db server;
199
    struct ovsdb_cs_db data;
200
201
    /* Session state.
202
     *
203
     * 'state_seqno' is a snapshot of the session's sequence number as returned
204
     * jsonrpc_session_get_seqno(session), so if it differs from the value that
205
     * function currently returns then the session has reconnected and the
206
     * state machine must restart.  */
207
    struct jsonrpc_session *session; /* Connection to the server. */
208
    char *remote;                    /* 'session' remote name. */
209
    enum ovsdb_cs_state state;       /* Current session state. */
210
    unsigned int state_seqno;        /* See above. */
211
    struct json *request_id;         /* JSON ID for request awaiting reply. */
212
213
    /* IDs of outstanding transactions. */
214
    struct json **txns;
215
    size_t n_txns, allocated_txns;
216
217
    /* Info for the _Server database. */
218
    struct uuid cid;
219
    struct hmap server_rows;
220
221
    /* Whether to send 'set_db_change_aware'. */
222
    bool set_db_change_aware;
223
224
    /* Clustered servers. */
225
    uint64_t min_index;      /* Minimum allowed index, to avoid regression. */
226
    bool leader_only;        /* If true, do not connect to Raft followers. */
227
    bool shuffle_remotes;    /* If true, connect to servers in random order. */
228
};
229
230
static void ovsdb_cs_transition_at(struct ovsdb_cs *, enum ovsdb_cs_state,
231
                                    const char *where);
232
#define ovsdb_cs_transition(CS, STATE) \
233
0
    ovsdb_cs_transition_at(CS, STATE, OVS_SOURCE_LOCATOR)
234
235
static void ovsdb_cs_retry_at(struct ovsdb_cs *, const char *where);
236
0
#define ovsdb_cs_retry(CS) ovsdb_cs_retry_at(CS, OVS_SOURCE_LOCATOR)
237
238
static struct vlog_rate_limit syntax_rl = VLOG_RATE_LIMIT_INIT(1, 5);
239
240
static void ovsdb_cs_db_parse_monitor_reply(struct ovsdb_cs_db *,
241
                                            const struct json *result,
242
                                            int version);
243
static bool ovsdb_cs_db_parse_update_rpc(struct ovsdb_cs_db *,
244
                                         const struct jsonrpc_msg *);
245
static bool ovsdb_cs_handle_monitor_canceled(struct ovsdb_cs *,
246
                                              struct ovsdb_cs_db *,
247
                                              const struct jsonrpc_msg *);
248
249
static bool ovsdb_cs_db_process_lock_replies(struct ovsdb_cs_db *,
250
                                              const struct jsonrpc_msg *);
251
static struct jsonrpc_msg *ovsdb_cs_db_compose_lock_request(
252
    struct ovsdb_cs_db *);
253
static struct jsonrpc_msg *ovsdb_cs_db_compose_unlock_request(
254
    struct ovsdb_cs_db *);
255
static void ovsdb_cs_db_parse_lock_reply(struct ovsdb_cs_db *,
256
                                          const struct json *);
257
static bool ovsdb_cs_db_parse_lock_notify(struct ovsdb_cs_db *,
258
                                           const struct json *params,
259
                                           bool new_has_lock);
260
static void ovsdb_cs_send_cond_change(struct ovsdb_cs *);
261
262
static bool ovsdb_cs_db_txn_process_reply(struct ovsdb_cs *,
263
                                          const struct jsonrpc_msg *reply);
264

265
/* Events. */
266
267
void
268
ovsdb_cs_event_destroy(struct ovsdb_cs_event *event)
269
0
{
270
0
    if (event) {
271
0
        switch (event->type) {
272
0
        case OVSDB_CS_EVENT_TYPE_RECONNECT:
273
0
        case OVSDB_CS_EVENT_TYPE_LOCKED:
274
0
            break;
275
276
0
        case OVSDB_CS_EVENT_TYPE_UPDATE:
277
0
            json_destroy(event->update.table_updates);
278
0
            break;
279
280
0
        case OVSDB_CS_EVENT_TYPE_TXN_REPLY:
281
0
            jsonrpc_msg_destroy(event->txn_reply);
282
0
            break;
283
0
        }
284
0
        free(event);
285
0
    }
286
0
}
287

288
/* Lifecycle. */
289
290
static void
291
ovsdb_cs_db_init(struct ovsdb_cs_db *db, const char *db_name,
292
                 struct ovsdb_cs *parent, int max_version,
293
                 const struct ovsdb_cs_ops *ops, void *ops_aux)
294
0
{
295
0
    *db = (struct ovsdb_cs_db) {
296
0
        .cs = parent,
297
0
        .db_name = db_name,
298
0
        .tables = HMAP_INITIALIZER(&db->tables),
299
0
        .max_version = max_version,
300
0
        .monitor_id = json_array_create_2(json_string_create("monid"),
301
0
                                          json_string_create(db_name)),
302
0
        .events = OVS_LIST_INITIALIZER(&db->events),
303
0
        .ops = ops,
304
0
        .ops_aux = ops_aux,
305
0
    };
306
0
}
307
308
/* Creates and returns a new client synchronization object.  The connection
309
 * will monitor remote database 'db_name'.  If 'retry' is true, then also
310
 * reconnect if the connection fails.
311
 *
312
 * XXX 'max_version' should ordinarily be 3, to allow use of the most efficient
313
 * "monitor_cond_since" method with the database.  Currently there's some kind
314
 * of bug in the DDlog Rust code that interfaces to that, so instead
315
 * ovn-northd-ddlog passes 1 to use plain 'monitor' instead.  Once the DDlog
316
 * Rust code gets fixed, we might as well just delete 'max_version'
317
 * entirely.
318
 *
319
 * 'ops' is a struct for northd_cs_run() to use, and 'ops_aux' is a pointer
320
 * that gets passed into each call.
321
 *
322
 * Use ovsdb_cs_set_remote() to configure the database to which to connect.
323
 * Until a remote is configured, no data can be retrieved.
324
 */
325
struct ovsdb_cs *
326
ovsdb_cs_create(const char *db_name, int max_version,
327
                const struct ovsdb_cs_ops *ops, void *ops_aux)
328
0
{
329
0
    struct ovsdb_cs *cs = xzalloc(sizeof *cs);
330
0
    ovsdb_cs_db_init(&cs->server, "_Server", cs, 2, &ovsdb_cs_server_ops, cs);
331
0
    ovsdb_cs_db_init(&cs->data, db_name, cs, max_version, ops, ops_aux);
332
0
    cs->state_seqno = UINT_MAX;
333
0
    cs->request_id = NULL;
334
0
    cs->leader_only = true;
335
0
    cs->shuffle_remotes = true;
336
0
    cs->set_db_change_aware = true;
337
0
    hmap_init(&cs->server_rows);
338
339
0
    return cs;
340
0
}
341
342
static void
343
ovsdb_cs_db_destroy(struct ovsdb_cs_db *db)
344
0
{
345
0
    ovsdb_cs_db_destroy_tables(db);
346
347
0
    json_destroy(db->monitor_id);
348
0
    json_destroy(db->schema);
349
350
0
    free(db->lock_name);
351
352
0
    json_destroy(db->lock_request_id);
353
354
    /* This list always gets flushed out at the end of ovsdb_cs_run(). */
355
0
    ovs_assert(ovs_list_is_empty(&db->events));
356
0
}
357
358
/* Destroys 'cs' and all of the data structures that it manages. */
359
void
360
ovsdb_cs_destroy(struct ovsdb_cs *cs)
361
0
{
362
0
    if (cs) {
363
0
        ovsdb_cs_db_destroy(&cs->server);
364
0
        ovsdb_cs_db_destroy(&cs->data);
365
0
        jsonrpc_session_close(cs->session);
366
0
        free(cs->remote);
367
0
        json_destroy(cs->request_id);
368
369
0
        for (size_t i = 0; i < cs->n_txns; i++) {
370
0
            json_destroy(cs->txns[i]);
371
0
        }
372
0
        free(cs->txns);
373
374
0
        ovsdb_cs_clear_server_rows(cs);
375
0
        hmap_destroy(&cs->server_rows);
376
377
0
        free(cs);
378
0
    }
379
0
}
380
381
static void
382
ovsdb_cs_transition_at(struct ovsdb_cs *cs, enum ovsdb_cs_state new_state,
383
                        const char *where)
384
0
{
385
0
    VLOG_DBG("%s: %s -> %s at %s",
386
0
             cs->session ? jsonrpc_session_get_name(cs->session) : "void",
387
0
             ovsdb_cs_state_to_string(cs->state),
388
0
             ovsdb_cs_state_to_string(new_state),
389
0
             where);
390
0
    cs->state = new_state;
391
0
}
392
393
static void
394
ovsdb_cs_send_request(struct ovsdb_cs *cs, struct jsonrpc_msg *request)
395
0
{
396
0
    json_destroy(cs->request_id);
397
0
    cs->request_id = json_clone(request->id);
398
0
    if (cs->session) {
399
0
        jsonrpc_session_send(cs->session, request);
400
0
    } else {
401
0
        jsonrpc_msg_destroy(request);
402
0
    }
403
0
}
404
405
static void
406
ovsdb_cs_retry_at(struct ovsdb_cs *cs, const char *where)
407
0
{
408
0
    ovsdb_cs_force_reconnect(cs);
409
0
    ovsdb_cs_transition_at(cs, CS_S_RETRY, where);
410
0
}
411
412
static void
413
ovsdb_cs_restart_fsm(struct ovsdb_cs *cs)
414
0
{
415
0
    ovsdb_cs_send_schema_request(cs, &cs->server);
416
0
    ovsdb_cs_transition(cs, CS_S_SERVER_SCHEMA_REQUESTED);
417
0
    cs->data.monitor_version = 0;
418
0
    cs->server.monitor_version = 0;
419
0
}
420
421
static void
422
ovsdb_cs_process_response(struct ovsdb_cs *cs, struct jsonrpc_msg *msg)
423
0
{
424
0
    bool ok = msg->type == JSONRPC_REPLY;
425
0
    if (!ok
426
0
        && cs->state != CS_S_SERVER_SCHEMA_REQUESTED
427
0
        && cs->state != CS_S_SERVER_MONITOR_REQUESTED
428
0
        && cs->state != CS_S_DATA_MONITOR_COND_REQUESTED
429
0
        && cs->state != CS_S_DATA_MONITOR_COND_SINCE_REQUESTED) {
430
0
        static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5);
431
0
        char *s = jsonrpc_msg_to_string(msg);
432
0
        VLOG_INFO_RL(&rl, "%s: received unexpected %s response in "
433
0
                     "%s state: %s", jsonrpc_session_get_name(cs->session),
434
0
                     jsonrpc_msg_type_to_string(msg->type),
435
0
                     ovsdb_cs_state_to_string(cs->state),
436
0
                     s);
437
0
        free(s);
438
0
        ovsdb_cs_retry(cs);
439
0
        return;
440
0
    }
441
442
0
    switch (cs->state) {
443
0
    case CS_S_SERVER_SCHEMA_REQUESTED:
444
0
        if (ok) {
445
0
            json_destroy(cs->server.schema);
446
0
            cs->server.schema = json_clone(msg->result);
447
0
            ovsdb_cs_send_monitor_request(cs, &cs->server,
448
0
                                          cs->server.max_version);
449
0
            ovsdb_cs_transition(cs, CS_S_SERVER_MONITOR_REQUESTED);
450
0
        } else {
451
0
            ovsdb_cs_send_schema_request(cs, &cs->data);
452
0
            ovsdb_cs_transition(cs, CS_S_DATA_SCHEMA_REQUESTED);
453
0
        }
454
0
        break;
455
456
0
    case CS_S_SERVER_MONITOR_REQUESTED:
457
0
        if (ok) {
458
0
            cs->server.monitor_version = cs->server.max_version;
459
0
            ovsdb_cs_db_parse_monitor_reply(&cs->server, msg->result,
460
0
                                            cs->server.monitor_version);
461
0
            if (ovsdb_cs_check_server_db(cs) && cs->set_db_change_aware) {
462
0
                ovsdb_cs_send_db_change_aware(cs);
463
0
            }
464
0
        } else {
465
0
            ovsdb_cs_send_schema_request(cs, &cs->data);
466
0
            ovsdb_cs_transition(cs, CS_S_DATA_SCHEMA_REQUESTED);
467
0
        }
468
0
        break;
469
470
0
    case CS_S_DATA_SCHEMA_REQUESTED:
471
0
        json_destroy(cs->data.schema);
472
0
        cs->data.schema = json_clone(msg->result);
473
0
        if (cs->data.max_version >= 2) {
474
0
            ovsdb_cs_send_monitor_request(cs, &cs->data, 2);
475
0
            ovsdb_cs_transition(cs, CS_S_DATA_MONITOR_COND_REQUESTED);
476
0
        } else {
477
0
            ovsdb_cs_send_monitor_request(cs, &cs->data, 1);
478
0
            ovsdb_cs_transition(cs, CS_S_DATA_MONITOR_REQUESTED);
479
0
        }
480
0
        break;
481
482
0
    case CS_S_DATA_MONITOR_COND_SINCE_REQUESTED:
483
0
        if (!ok) {
484
            /* "monitor_cond_since" not supported.  Try "monitor_cond". */
485
0
            ovsdb_cs_send_monitor_request(cs, &cs->data, 2);
486
0
            ovsdb_cs_transition(cs, CS_S_DATA_MONITOR_COND_REQUESTED);
487
0
        } else {
488
0
            cs->data.monitor_version = 3;
489
0
            ovsdb_cs_transition(cs, CS_S_MONITORING);
490
0
            ovsdb_cs_db_parse_monitor_reply(&cs->data, msg->result, 3);
491
0
        }
492
0
        break;
493
494
0
    case CS_S_DATA_MONITOR_COND_REQUESTED:
495
0
        if (!ok) {
496
            /* "monitor_cond" not supported.  Try "monitor". */
497
0
            ovsdb_cs_send_monitor_request(cs, &cs->data, 1);
498
0
            ovsdb_cs_transition(cs, CS_S_DATA_MONITOR_REQUESTED);
499
0
        } else {
500
0
            cs->data.monitor_version = 2;
501
0
            ovsdb_cs_transition(cs, CS_S_MONITORING);
502
0
            ovsdb_cs_db_parse_monitor_reply(&cs->data, msg->result, 2);
503
0
        }
504
0
        break;
505
506
0
    case CS_S_DATA_MONITOR_REQUESTED:
507
0
        cs->data.monitor_version = 1;
508
0
        ovsdb_cs_transition(cs, CS_S_MONITORING);
509
0
        ovsdb_cs_db_parse_monitor_reply(&cs->data, msg->result, 1);
510
0
        break;
511
512
0
    case CS_S_MONITORING:
513
        /* We don't normally have a request outstanding in this state.  If we
514
         * do, it's a "monitor_cond_change", which means that the conditional
515
         * monitor clauses were updated.
516
         *
517
         * Mark the last requested conditions as acked and if further
518
         * condition changes were pending, send them now. */
519
0
        ovsdb_cs_db_ack_condition(&cs->data);
520
0
        ovsdb_cs_send_cond_change(cs);
521
0
        cs->data.cond_seqno++;
522
0
        break;
523
524
0
    case CS_S_ERROR:
525
0
    case CS_S_RETRY:
526
        /* Nothing to do in this state. */
527
0
        break;
528
529
0
    default:
530
0
        OVS_NOT_REACHED();
531
0
    }
532
0
}
533
534
static void
535
ovsdb_cs_process_msg(struct ovsdb_cs *cs, struct jsonrpc_msg *msg)
536
0
{
537
0
    bool is_response = (msg->type == JSONRPC_REPLY ||
538
0
                        msg->type == JSONRPC_ERROR);
539
540
    /* Process a reply to an outstanding request. */
541
0
    if (is_response
542
0
        && cs->request_id && json_equal(cs->request_id, msg->id)) {
543
0
        json_destroy(cs->request_id);
544
0
        cs->request_id = NULL;
545
0
        ovsdb_cs_process_response(cs, msg);
546
0
        return;
547
0
    }
548
549
    /* Process database contents updates. */
550
0
    if (ovsdb_cs_db_parse_update_rpc(&cs->data, msg)) {
551
0
        return;
552
0
    }
553
0
    if (cs->server.monitor_version
554
0
        && ovsdb_cs_db_parse_update_rpc(&cs->server, msg)) {
555
0
        ovsdb_cs_check_server_db(cs);
556
0
        return;
557
0
    }
558
559
0
    if (ovsdb_cs_handle_monitor_canceled(cs, &cs->data, msg)
560
0
        || (cs->server.monitor_version
561
0
            && ovsdb_cs_handle_monitor_canceled(cs, &cs->server, msg))) {
562
0
        return;
563
0
    }
564
565
    /* Process "lock" replies and related notifications. */
566
0
    if (ovsdb_cs_db_process_lock_replies(&cs->data, msg)) {
567
0
        return;
568
0
    }
569
570
    /* Process response to a database transaction we submitted. */
571
0
    if (is_response && ovsdb_cs_db_txn_process_reply(cs, msg)) {
572
0
        return;
573
0
    }
574
575
    /* Unknown message.  Log at a low level because this can happen if
576
     * ovsdb_cs_txn_destroy() is called to destroy a transaction
577
     * before we receive the reply.
578
     *
579
     * (We could sort those out from other kinds of unknown messages by
580
     * using distinctive IDs for transactions, if it seems valuable to
581
     * do so, and then it would be possible to use different log
582
     * levels. XXX?) */
583
0
    char *s = jsonrpc_msg_to_string(msg);
584
0
    VLOG_DBG("%s: received unexpected %s message: %s",
585
0
             jsonrpc_session_get_name(cs->session),
586
0
             jsonrpc_msg_type_to_string(msg->type), s);
587
0
    free(s);
588
0
}
589
590
static struct ovsdb_cs_event *
591
ovsdb_cs_db_add_event(struct ovsdb_cs_db *db, enum ovsdb_cs_event_type type)
592
0
{
593
0
    struct ovsdb_cs_event *event = xmalloc(sizeof *event);
594
0
    event->type = type;
595
0
    ovs_list_push_back(&db->events, &event->list_node);
596
0
    return event;
597
0
}
598
599
/* Processes a batch of messages from the database server on 'cs'.  This may
600
 * cause the CS's contents to change.
601
 *
602
 * Initializes 'events' with a list of events that occurred on 'cs'.  The
603
 * caller must process and destroy all of the events. */
604
void
605
ovsdb_cs_run(struct ovsdb_cs *cs, struct ovs_list *events)
606
0
{
607
0
    ovs_list_init(events);
608
0
    if (!cs->session) {
609
0
        return;
610
0
    }
611
612
0
    ovsdb_cs_send_cond_change(cs);
613
614
0
    jsonrpc_session_run(cs->session);
615
616
0
    unsigned int seqno = jsonrpc_session_get_seqno(cs->session);
617
0
    if (cs->state_seqno != seqno) {
618
0
        cs->state_seqno = seqno;
619
0
        ovsdb_cs_restart_fsm(cs);
620
621
0
        for (size_t i = 0; i < cs->n_txns; i++) {
622
0
            json_destroy(cs->txns[i]);
623
0
        }
624
0
        cs->n_txns = 0;
625
626
0
        ovsdb_cs_db_add_event(&cs->data, OVSDB_CS_EVENT_TYPE_RECONNECT);
627
628
0
        if (cs->data.lock_name) {
629
0
            jsonrpc_session_send(
630
0
                cs->session,
631
0
                ovsdb_cs_db_compose_lock_request(&cs->data));
632
0
        }
633
0
    }
634
635
0
    for (int i = 0; i < 50; i++) {
636
0
        struct jsonrpc_msg *msg = jsonrpc_session_recv(cs->session);
637
0
        if (!msg) {
638
0
            break;
639
0
        }
640
0
        ovsdb_cs_process_msg(cs, msg);
641
0
        jsonrpc_msg_destroy(msg);
642
0
    }
643
0
    ovs_list_push_back_all(events, &cs->data.events);
644
0
}
645
646
/* Arranges for poll_block() to wake up when ovsdb_cs_run() has something to
647
 * do or when activity occurs on a transaction on 'cs'. */
648
void
649
ovsdb_cs_wait(struct ovsdb_cs *cs)
650
0
{
651
0
    if (!cs->session) {
652
0
        return;
653
0
    }
654
0
    jsonrpc_session_wait(cs->session);
655
0
    jsonrpc_session_recv_wait(cs->session);
656
0
}
657

658
/* Network connection. */
659
660
/* Changes the remote and creates a new session.  Keeps existing connection
661
 * if current remote is still valid.
662
 *
663
 * If 'retry' is true, the connection to the remote will automatically retry
664
 * when it fails.  If 'retry' is false, the connection is one-time. */
665
void
666
ovsdb_cs_set_remote(struct ovsdb_cs *cs, const char *remote, bool retry)
667
0
{
668
0
    if (cs
669
0
        && ((remote != NULL) != (cs->remote != NULL)
670
0
            || (remote && cs->remote && strcmp(remote, cs->remote)))) {
671
0
        struct jsonrpc *rpc = NULL;
672
673
        /* Close the old session, if any. */
674
0
        if (cs->session) {
675
            /* Save the current open connection and close the session. */
676
0
            rpc = jsonrpc_session_steal(cs->session);
677
0
            cs->session = NULL;
678
679
0
            free(cs->remote);
680
0
            cs->remote = NULL;
681
0
        }
682
683
        /* Open new session, if any. */
684
0
        if (remote) {
685
0
            struct svec remotes = SVEC_EMPTY_INITIALIZER;
686
0
            struct uuid old_cid = cs->cid;
687
688
0
            ovsdb_session_parse_remote(remote, &remotes, &cs->cid);
689
0
            if (cs->shuffle_remotes) {
690
0
                svec_shuffle(&remotes);
691
0
            }
692
0
            cs->session = jsonrpc_session_open_multiple(&remotes, retry);
693
694
            /* Use old connection, if cluster id didn't change and the remote
695
             * is still on the list, to avoid unnecessary re-connection. */
696
0
            if (rpc && uuid_equals(&old_cid, &cs->cid)
697
0
                && svec_contains_unsorted(&remotes, jsonrpc_get_name(rpc))) {
698
0
                jsonrpc_session_replace(cs->session, rpc);
699
0
                cs->state_seqno = jsonrpc_session_get_seqno(cs->session);
700
0
                rpc = NULL;
701
0
            } else {
702
0
                cs->state_seqno = UINT_MAX;
703
0
            }
704
705
0
            svec_destroy(&remotes);
706
0
            cs->remote = xstrdup(remote);
707
0
        }
708
709
0
        jsonrpc_close(rpc);
710
0
    }
711
0
}
712
713
/* Reconfigures 'cs' so that it would reconnect to the database, if
714
 * connection was dropped. */
715
void
716
ovsdb_cs_enable_reconnect(struct ovsdb_cs *cs)
717
0
{
718
0
    if (cs->session) {
719
0
        jsonrpc_session_enable_reconnect(cs->session);
720
0
    }
721
0
}
722
723
/* Forces 'cs' to drop its connection to the database and reconnect.  In the
724
 * meantime, the contents of 'cs' will not change. */
725
void
726
ovsdb_cs_force_reconnect(struct ovsdb_cs *cs)
727
0
{
728
0
    if (cs->session) {
729
0
        if (cs->state == CS_S_MONITORING) {
730
            /* The ovsdb-cs was in MONITORING state, so we either had data
731
             * inconsistency on this server, or it stopped being the cluster
732
             * leader, or the user requested to re-connect.  Avoiding backoff
733
             * in these cases, as we need to re-connect as soon as possible.
734
             * Connections that are not in MONITORING state should have their
735
             * backoff to avoid constant flood of re-connection attempts in
736
             * case there is no suitable database server. */
737
0
            jsonrpc_session_reset_backoff(cs->session);
738
0
        }
739
0
        jsonrpc_session_force_reconnect(cs->session);
740
0
    }
741
0
}
742
743
/* Drops 'cs''s current connection and the cached session.  This is useful if
744
 * the client notices some kind of inconsistency. */
745
void
746
ovsdb_cs_flag_inconsistency(struct ovsdb_cs *cs)
747
0
{
748
0
    cs->data.last_id = UUID_ZERO;
749
0
    ovsdb_cs_retry(cs);
750
0
}
751
752
/* Returns true if 'cs' is currently connected or will eventually try to
753
 * reconnect. */
754
bool
755
ovsdb_cs_is_alive(const struct ovsdb_cs *cs)
756
0
{
757
0
    return (cs->session
758
0
            && jsonrpc_session_is_alive(cs->session)
759
0
            && cs->state != CS_S_ERROR);
760
0
}
761
762
/* Returns true if 'cs' is currently connected to a server. */
763
bool
764
ovsdb_cs_is_connected(const struct ovsdb_cs *cs)
765
0
{
766
0
    return cs->session && jsonrpc_session_is_connected(cs->session);
767
0
}
768
769
/* Returns the last error reported on a connection by 'cs'.  The return value
770
 * is 0 only if no connection made by 'cs' has ever encountered an error and
771
 * a negative response to a schema request has never been received. See
772
 * jsonrpc_get_status() for jsonrpc_session_get_last_error() return value
773
 * interpretation. */
774
int
775
ovsdb_cs_get_last_error(const struct ovsdb_cs *cs)
776
0
{
777
0
    int err = cs->session ? jsonrpc_session_get_last_error(cs->session) : 0;
778
0
    if (err) {
779
0
        return err;
780
0
    } else if (cs->state == CS_S_ERROR) {
781
0
        return ENOENT;
782
0
    } else {
783
0
        return 0;
784
0
    }
785
0
}
786
787
/* Sets all the JSON-RPC session 'options' for 'cs''s current session. */
788
void
789
ovsdb_cs_set_jsonrpc_options(const struct ovsdb_cs *cs,
790
                             const struct jsonrpc_session_options *options)
791
0
{
792
0
    if (cs->session) {
793
0
        jsonrpc_session_set_options(cs->session, options);
794
0
    }
795
0
}
796
797
/* Sets the "probe interval" for 'cs''s current session to 'probe_interval', in
798
 * milliseconds. */
799
void
800
ovsdb_cs_set_probe_interval(const struct ovsdb_cs *cs, int probe_interval)
801
0
{
802
0
    if (cs->session) {
803
0
        jsonrpc_session_set_probe_interval(cs->session, probe_interval);
804
0
    }
805
0
}
806

807
/* Conditional monitoring. */
808
809
/* A table being monitored.
810
 *
811
 * At the CS layer, the only thing we care about, table-wise, is the conditions
812
 * we're using for monitoring them, so there's little here.  We only create
813
 * these table structures at all for tables that have conditions. */
814
struct ovsdb_cs_db_table {
815
    struct hmap_node hmap_node; /* Indexed by 'name'. */
816
    char *name;                 /* Table name. */
817
818
    /* Each of these is a null pointer if it is empty, or JSON [<condition>*]
819
     * or [true] or [false] otherwise.  [true] could be represented as a null
820
     * pointer, but we want to distinguish "empty slot" from "a condition that
821
     * is always true" in a slot. */
822
    struct json *ack_cond; /* Last condition acked by the server. */
823
    struct json *req_cond; /* Last condition requested to the server. */
824
    struct json *new_cond; /* Latest condition set by the IDL client. */
825
};
826
827
/* A kind of condition, so that we can treat equivalent JSON as equivalent. */
828
enum condition_type {
829
    COND_FALSE,                 /* [] or [false] */
830
    COND_TRUE,                  /* Null pointer or [true] */
831
    COND_OTHER                  /* Anything else. */
832
};
833
834
/* Returns the condition_type for 'condition'. */
835
static enum condition_type
836
condition_classify(const struct json *condition)
837
0
{
838
0
    if (condition) {
839
0
        switch (json_array_size(condition)) {
840
0
        case 0:
841
0
            return COND_FALSE;
842
843
0
        case 1: {
844
0
            const struct json *cond = json_array_at(condition, 0);
845
846
0
            return (cond->type == JSON_FALSE ? COND_FALSE
847
0
                    : cond->type == JSON_TRUE ? COND_TRUE
848
0
                    : COND_OTHER);
849
0
        }
850
851
0
        default:
852
0
            return COND_OTHER;
853
0
        }
854
0
    } else {
855
0
        return COND_TRUE;
856
0
    }
857
0
}
858
859
/* Returns true if 'a' and 'b' are the same condition (in an obvious way; we're
860
 * not going to compare for boolean equivalence or anything). */
861
static bool
862
condition_equal(const struct json *a, const struct json *b)
863
0
{
864
0
    enum condition_type type = condition_classify(a);
865
0
    return (type == condition_classify(b)
866
0
            && (type != COND_OTHER || json_equal(a, b)));
867
0
}
868
869
/* Returns a clone of 'condition', translating always-true and always-false to
870
 * [true] and [false], respectively. */
871
static struct json *
872
condition_clone(const struct json *condition)
873
0
{
874
0
    switch (condition_classify(condition)) {
875
0
    case COND_TRUE:
876
0
        return json_array_create_1(json_boolean_create(true));
877
878
0
    case COND_FALSE:
879
0
        return json_array_create_1(json_boolean_create(false));
880
881
0
    case COND_OTHER:
882
0
        return json_clone(condition);
883
0
    }
884
885
0
    OVS_NOT_REACHED();
886
0
}
887
888
/* Returns the ovsdb_cs_db_table associated with 'table' in 'db', creating an
889
 * empty one if necessary. */
890
static struct ovsdb_cs_db_table *
891
ovsdb_cs_db_get_table(struct ovsdb_cs_db *db, const char *table)
892
0
{
893
0
    uint32_t hash = hash_string(table, 0);
894
0
    struct ovsdb_cs_db_table *t;
895
896
0
    HMAP_FOR_EACH_WITH_HASH (t, hmap_node, hash, &db->tables) {
897
0
        if (!strcmp(t->name, table)) {
898
0
            return t;
899
0
        }
900
0
    }
901
902
0
    t = xzalloc(sizeof *t);
903
0
    t->name = xstrdup(table);
904
0
    t->ack_cond = json_array_create_1(json_boolean_create(true));
905
0
    hmap_insert(&db->tables, &t->hmap_node, hash);
906
0
    return t;
907
0
}
908
909
static void
910
ovsdb_cs_db_destroy_table(struct ovsdb_cs_db_table *table,
911
                          struct ovsdb_cs_db *db)
912
0
{
913
0
    json_destroy(table->ack_cond);
914
0
    json_destroy(table->req_cond);
915
0
    json_destroy(table->new_cond);
916
0
    hmap_remove(&db->tables, &table->hmap_node);
917
0
    free(table->name);
918
0
    free(table);
919
0
}
920
921
/* Destroy a given ovsdb_cs_db_table according to the table name. */
922
void
923
ovsdb_cs_clear_condition(struct ovsdb_cs *cs, const char *table)
924
0
{
925
0
    uint32_t hash = hash_string(table, 0);
926
0
    struct ovsdb_cs_db *db = &cs->data;
927
928
0
    struct ovsdb_cs_db_table *t;
929
0
    HMAP_FOR_EACH_WITH_HASH (t, hmap_node, hash, &db->tables) {
930
0
        if (!strcmp(t->name, table)) {
931
0
            ovsdb_cs_db_destroy_table(t, db);
932
0
            db->last_id = UUID_ZERO;
933
0
            return;
934
0
        }
935
0
    }
936
0
}
937
938
void
939
ovsdb_cs_reset_last_id(struct ovsdb_cs *cs)
940
0
{
941
0
    cs->data.last_id = UUID_ZERO;
942
0
}
943
944
static void
945
ovsdb_cs_db_destroy_tables(struct ovsdb_cs_db *db)
946
0
{
947
0
    struct ovsdb_cs_db_table *table;
948
0
    HMAP_FOR_EACH_SAFE (table, hmap_node, &db->tables) {
949
0
        ovsdb_cs_db_destroy_table(table, db);
950
0
    }
951
0
    hmap_destroy(&db->tables);
952
0
}
953
954
static unsigned int
955
ovsdb_cs_db_set_condition(struct ovsdb_cs_db *db, const char *table,
956
                          const struct json *condition)
957
0
{
958
    /* Compare the new condition to the last known condition which can be
959
     * either "new" (not sent yet), "requested" or "acked", in this order. */
960
0
    struct ovsdb_cs_db_table *t = ovsdb_cs_db_get_table(db, table);
961
0
    const struct json *table_cond = (t->new_cond ? t->new_cond
962
0
                                     : t->req_cond ? t->req_cond
963
0
                                     : t->ack_cond);
964
0
    if (!condition_equal(condition, table_cond)) {
965
0
        json_destroy(t->new_cond);
966
0
        t->new_cond = condition_clone(condition);
967
0
        db->cond_changed = true;
968
0
        poll_immediate_wake();
969
0
    }
970
971
    /* Conditions will be up to date when we receive replies for already
972
     * requested and new conditions, if any.  This includes condition change
973
     * requests for other tables too.
974
     */
975
0
    if (t->new_cond) {
976
        /* New condition will be sent out after all already requested ones
977
         * are acked.
978
         */
979
0
        bool any_req_cond = false;
980
0
        HMAP_FOR_EACH (t, hmap_node, &db->tables) {
981
0
            if (t->req_cond) {
982
0
                any_req_cond = true;
983
0
                break;
984
0
            }
985
0
        }
986
0
        return db->cond_seqno + any_req_cond + 1;
987
0
    } else {
988
        /* Already requested conditions should be up to date at
989
         * db->cond_seqno + 1 while acked conditions are already up to date.
990
         */
991
0
        return db->cond_seqno + !!t->req_cond;
992
0
    }
993
0
}
994
995
/* Sets the replication condition for 'tc' in 'cs' to 'condition' and arranges
996
 * to send the new condition to the database server.
997
 *
998
 * Return the next conditional update sequence number.  When this value and
999
 * ovsdb_cs_get_condition_seqno() matches, 'cs' contains rows that match the
1000
 * 'condition'. */
1001
unsigned int
1002
ovsdb_cs_set_condition(struct ovsdb_cs *cs, const char *table,
1003
                       const struct json *condition)
1004
0
{
1005
0
    return ovsdb_cs_db_set_condition(&cs->data, table, condition);
1006
0
}
1007
1008
/* Returns a "sequence number" that represents the number of conditional
1009
 * monitoring updates successfully received by the OVSDB server of a CS
1010
 * connection.
1011
 *
1012
 * ovsdb_cs_set_condition() sets a new condition that is different from the
1013
 * current condtion, the next expected "sequence number" is returned.
1014
 *
1015
 * Whenever ovsdb_cs_get_condition_seqno() returns a value that matches the
1016
 * return value of ovsdb_cs_set_condition(), the client is assured that:
1017
 *
1018
 *   - The ovsdb_cs_set_condition() changes has been acknowledged by the OVSDB
1019
 *     server.
1020
 *
1021
 *   -  'cs' now contains the content matches the new conditions.   */
1022
unsigned int
1023
ovsdb_cs_get_condition_seqno(const struct ovsdb_cs *cs)
1024
0
{
1025
0
    return cs->data.cond_seqno;
1026
0
}
1027
1028
static struct json *
1029
ovsdb_cs_create_cond_change_req(const struct json *cond)
1030
0
{
1031
0
    struct json *monitor_cond_change_request = json_object_create();
1032
0
    json_object_put(monitor_cond_change_request, "where", json_clone(cond));
1033
0
    return monitor_cond_change_request;
1034
0
}
1035
1036
static struct jsonrpc_msg *
1037
ovsdb_cs_db_compose_cond_change(struct ovsdb_cs_db *db)
1038
0
{
1039
0
    if (!db->cond_changed) {
1040
0
        return NULL;
1041
0
    }
1042
1043
0
    struct json *monitor_cond_change_requests = NULL;
1044
0
    struct ovsdb_cs_db_table *table;
1045
0
    HMAP_FOR_EACH (table, hmap_node, &db->tables) {
1046
        /* Always use the most recent conditions set by the CS client when
1047
         * requesting monitor_cond_change, i.e., table->new_cond.
1048
         */
1049
0
        if (table->new_cond) {
1050
0
            struct json *req =
1051
0
                ovsdb_cs_create_cond_change_req(table->new_cond);
1052
0
            if (req) {
1053
0
                if (!monitor_cond_change_requests) {
1054
0
                    monitor_cond_change_requests = json_object_create();
1055
0
                }
1056
0
                json_object_put(monitor_cond_change_requests,
1057
0
                                table->name,
1058
0
                                json_array_create_1(req));
1059
0
            }
1060
            /* Mark the new condition as requested by moving it to req_cond.
1061
             * If there's already requested condition that's a bug.
1062
             */
1063
0
            ovs_assert(table->req_cond == NULL);
1064
0
            table->req_cond = table->new_cond;
1065
0
            table->new_cond = NULL;
1066
0
        }
1067
0
    }
1068
1069
0
    if (!monitor_cond_change_requests) {
1070
0
        return NULL;
1071
0
    }
1072
1073
0
    db->cond_changed = false;
1074
0
    struct json *params = json_array_create_3(json_clone(db->monitor_id),
1075
0
                                              json_clone(db->monitor_id),
1076
0
                                              monitor_cond_change_requests);
1077
0
    return jsonrpc_create_request("monitor_cond_change", params, NULL);
1078
0
}
1079
1080
/* Marks all requested table conditions in 'db' as acked by the server.
1081
 * It should be called when the server replies to monitor_cond_change
1082
 * requests.
1083
 */
1084
static void
1085
ovsdb_cs_db_ack_condition(struct ovsdb_cs_db *db)
1086
0
{
1087
0
    struct ovsdb_cs_db_table *table;
1088
0
    HMAP_FOR_EACH (table, hmap_node, &db->tables) {
1089
0
        if (table->req_cond) {
1090
0
            json_destroy(table->ack_cond);
1091
0
            table->ack_cond = table->req_cond;
1092
0
            table->req_cond = NULL;
1093
0
        }
1094
0
    }
1095
0
}
1096
1097
/* Should be called when the CS fsm is restarted and resyncs table conditions
1098
 * based on the state the DB is in:
1099
 * - If a non-zero 'last_id' is available for the DB, then upon reconnect
1100
 *   the CS should first request acked conditions to avoid missing updates
1101
 *   about records that were added before the transaction with
1102
 *   txn-id == last_id.
1103
 *
1104
 * - If there were changes in flight then there are two cases:
1105
 *   a. either the server already processed the requested monitor condition
1106
 *      change but the FSM was restarted before the client was notified.
1107
 *      In this case the client should clear its local cache because it's
1108
 *      out of sync with the monitor view on the server side.
1109
 *
1110
 *   b. OR the server hasn't processed the requested monitor condition
1111
 *      change yet.
1112
 *
1113
 *   As there's no easy way to differentiate between the two, and given that
1114
 *   this condition should be rare, reset the 'last_id', essentially flushing
1115
 *   the local cached DB contents.
1116
 *
1117
 * - If there's no 'last_id' available for the DB or it was reset, then it's
1118
 *   safe to use the latest conditions set by the CS client even if they
1119
 *   weren't acked yet, since the local cache will be cleared anyway.
1120
 */
1121
static void
1122
ovsdb_cs_db_sync_condition(struct ovsdb_cs_db *db)
1123
0
{
1124
0
    struct ovsdb_cs_db_table *table;
1125
1126
0
    HMAP_FOR_EACH (table, hmap_node, &db->tables) {
1127
0
        if (table->req_cond) {
1128
            /* There was an in-flight condition change - reset. */
1129
0
            db->last_id = UUID_ZERO;
1130
0
            break;
1131
0
        }
1132
0
    }
1133
1134
0
    if (uuid_is_zero(&db->last_id)) {
1135
        /* No 'last_id' - use the latest conditions for the monitor request. */
1136
0
        HMAP_FOR_EACH (table, hmap_node, &db->tables) {
1137
0
            if (table->new_cond) {
1138
0
                json_destroy(table->req_cond);
1139
0
                table->req_cond = table->new_cond;
1140
0
                table->new_cond = NULL;
1141
0
            }
1142
1143
0
            if (table->req_cond) {
1144
0
                json_destroy(table->ack_cond);
1145
0
                table->ack_cond = table->req_cond;
1146
0
                table->req_cond = NULL;
1147
0
            }
1148
0
        }
1149
        /* Nothing to send after the initial monitor request. */
1150
0
        db->cond_changed = false;
1151
0
    } else {
1152
        /* No in-flight changes and a non-zero 'last_id'.  Send acknowledged
1153
         * first, then follow up with the new, if any. */
1154
0
        HMAP_FOR_EACH (table, hmap_node, &db->tables) {
1155
0
            if (table->new_cond) {
1156
0
                db->cond_changed = true;
1157
0
                break;
1158
0
            }
1159
0
        }
1160
0
    }
1161
0
}
1162
1163
static void
1164
ovsdb_cs_send_cond_change(struct ovsdb_cs *cs)
1165
0
{
1166
    /* When 'cs->request_id' is not NULL, there is an outstanding
1167
     * conditional monitoring update request that we have not heard
1168
     * from the server yet. Don't generate another request in this case. */
1169
0
    if (!jsonrpc_session_is_connected(cs->session)
1170
0
        || cs->data.monitor_version == 1
1171
0
        || cs->request_id) {
1172
0
        return;
1173
0
    }
1174
1175
0
    struct jsonrpc_msg *msg = ovsdb_cs_db_compose_cond_change(&cs->data);
1176
0
    if (msg) {
1177
0
        cs->request_id = json_clone(msg->id);
1178
0
        jsonrpc_session_send(cs->session, msg);
1179
0
    }
1180
0
}
1181

1182
/* Database change awareness. */
1183
1184
/* By default, or if 'set_db_change_aware' is true, 'cs' will send
1185
 * 'set_db_change_aware' request to the server after receiving the _SERVER data
1186
 * (when the server supports it), which is useful for clients that intends to
1187
 * keep long connections to the server.  Otherwise, 'cs' will not send the
1188
 * 'set_db_change_aware' request, which is more reasonable for short-lived
1189
 * connections to avoid unnecessary processing at the server side and possible
1190
 * error handling due to connections being closed by the clients before the
1191
 * responses are sent by the server. */
1192
void
1193
ovsdb_cs_set_db_change_aware(struct ovsdb_cs *cs, bool set_db_change_aware)
1194
0
{
1195
0
    cs->set_db_change_aware = set_db_change_aware;
1196
0
}
1197

1198
/* Clustered servers. */
1199
1200
/* By default, or if 'leader_only' is true, when 'cs' connects to a clustered
1201
 * database, the CS layer will avoid servers other than the cluster
1202
 * leader. This ensures that any data that it reads and reports is up-to-date.
1203
 * If 'leader_only' is false, the CS layer will accept any server in the
1204
 * cluster, which means that for read-only transactions it can report and act
1205
 * on stale data (transactions that modify the database are always serialized
1206
 * even with false 'leader_only').  Refer to Understanding Cluster Consistency
1207
 * in ovsdb(7) for more information. */
1208
void
1209
ovsdb_cs_set_leader_only(struct ovsdb_cs *cs, bool leader_only)
1210
0
{
1211
0
    cs->leader_only = leader_only;
1212
0
    if (leader_only && cs->server.monitor_version) {
1213
0
        ovsdb_cs_check_server_db(cs);
1214
0
    }
1215
0
}
1216
1217
/* Set whether the order of remotes should be shuffled, when there is more than
1218
 * one remote.  The setting doesn't take effect until the next time when
1219
 * ovsdb_cs_set_remote() is called. */
1220
void
1221
ovsdb_cs_set_shuffle_remotes(struct ovsdb_cs *cs, bool shuffle)
1222
0
{
1223
0
    cs->shuffle_remotes = shuffle;
1224
0
}
1225
1226
/* Reset min_index to 0. This prevents a situation where the client
1227
 * thinks all databases have stale data, when they actually have all
1228
 * been destroyed and rebuilt from scratch.
1229
 */
1230
void
1231
ovsdb_cs_reset_min_index(struct ovsdb_cs *cs)
1232
0
{
1233
0
    cs->min_index = 0;
1234
0
}
1235

1236
/* Database locks. */
1237
1238
static struct jsonrpc_msg *
1239
ovsdb_cs_db_set_lock(struct ovsdb_cs_db *db, const char *lock_name)
1240
0
{
1241
0
    if (db->lock_name
1242
0
        && (!lock_name || strcmp(lock_name, db->lock_name))) {
1243
        /* Release previous lock. */
1244
0
        struct jsonrpc_msg *msg = ovsdb_cs_db_compose_unlock_request(db);
1245
0
        free(db->lock_name);
1246
0
        db->lock_name = NULL;
1247
0
        db->is_lock_contended = false;
1248
0
        return msg;
1249
0
    }
1250
1251
0
    if (lock_name && !db->lock_name) {
1252
        /* Acquire new lock. */
1253
0
        db->lock_name = xstrdup(lock_name);
1254
0
        return ovsdb_cs_db_compose_lock_request(db);
1255
0
    }
1256
1257
0
    return NULL;
1258
0
}
1259
1260
/* If 'lock_name' is nonnull, configures 'cs' to obtain the named lock from the
1261
 * database server and to prevent modifying the database when the lock cannot
1262
 * be acquired (that is, when another client has the same lock).
1263
 *
1264
 * If 'lock_name' is NULL, drops the locking requirement and releases the
1265
 * lock. */
1266
void
1267
ovsdb_cs_set_lock(struct ovsdb_cs *cs, const char *lock_name)
1268
0
{
1269
0
    for (;;) {
1270
0
        struct jsonrpc_msg *msg = ovsdb_cs_db_set_lock(&cs->data, lock_name);
1271
0
        if (!msg) {
1272
0
            break;
1273
0
        }
1274
0
        if (cs->session) {
1275
0
            jsonrpc_session_send(cs->session, msg);
1276
0
        } else {
1277
0
            jsonrpc_msg_destroy(msg);
1278
0
        }
1279
0
    }
1280
0
}
1281
1282
/* Returns the name of the lock that 'cs' is trying to obtain, or NULL if none
1283
 * is configured. */
1284
const char *
1285
ovsdb_cs_get_lock(const struct ovsdb_cs *cs)
1286
0
{
1287
0
    return cs->data.lock_name;
1288
0
}
1289
1290
/* Returns true if 'cs' is configured to obtain a lock and owns that lock,
1291
 * false if it doesn't own the lock or isn't configured to obtain one.
1292
 *
1293
 * Locking and unlocking happens asynchronously from the database client's
1294
 * point of view, so the information is only useful for optimization (e.g. if
1295
 * the client doesn't have the lock then there's no point in trying to write to
1296
 * the database). */
1297
bool
1298
ovsdb_cs_has_lock(const struct ovsdb_cs *cs)
1299
0
{
1300
0
    return cs->data.has_lock;
1301
0
}
1302
1303
/* Returns true if 'cs' is configured to obtain a lock but the database server
1304
 * has indicated that some other client already owns the requested lock. */
1305
bool
1306
ovsdb_cs_is_lock_contended(const struct ovsdb_cs *cs)
1307
0
{
1308
0
    return cs->data.is_lock_contended;
1309
0
}
1310
1311
static void
1312
ovsdb_cs_db_update_has_lock(struct ovsdb_cs_db *db, bool new_has_lock)
1313
0
{
1314
0
    if (new_has_lock && !db->has_lock) {
1315
0
        ovsdb_cs_db_add_event(db, OVSDB_CS_EVENT_TYPE_LOCKED);
1316
0
        db->is_lock_contended = false;
1317
0
    }
1318
0
    db->has_lock = new_has_lock;
1319
0
}
1320
1321
static bool
1322
ovsdb_cs_db_process_lock_replies(struct ovsdb_cs_db *db,
1323
                                  const struct jsonrpc_msg *msg)
1324
0
{
1325
0
    if (msg->type == JSONRPC_REPLY
1326
0
        && db->lock_request_id
1327
0
        && json_equal(db->lock_request_id, msg->id)) {
1328
        /* Reply to our "lock" request. */
1329
0
        ovsdb_cs_db_parse_lock_reply(db, msg->result);
1330
0
        return true;
1331
0
    }
1332
1333
0
    if (msg->type == JSONRPC_NOTIFY) {
1334
0
        if (!strcmp(msg->method, "locked")) {
1335
            /* We got our lock. */
1336
0
            return ovsdb_cs_db_parse_lock_notify(db, msg->params, true);
1337
0
        } else if (!strcmp(msg->method, "stolen")) {
1338
            /* Someone else stole our lock. */
1339
0
            return ovsdb_cs_db_parse_lock_notify(db, msg->params, false);
1340
0
        }
1341
0
    }
1342
1343
0
    return false;
1344
0
}
1345
1346
static struct jsonrpc_msg *
1347
ovsdb_cs_db_compose_lock_request__(struct ovsdb_cs_db *db,
1348
                                    const char *method)
1349
0
{
1350
0
    ovsdb_cs_db_update_has_lock(db, false);
1351
1352
0
    json_destroy(db->lock_request_id);
1353
0
    db->lock_request_id = NULL;
1354
1355
0
    struct json *params = json_array_create_1(json_string_create(
1356
0
                                                  db->lock_name));
1357
0
    return jsonrpc_create_request(method, params, NULL);
1358
0
}
1359
1360
static struct jsonrpc_msg *
1361
ovsdb_cs_db_compose_lock_request(struct ovsdb_cs_db *db)
1362
0
{
1363
0
    struct jsonrpc_msg *msg = ovsdb_cs_db_compose_lock_request__(db, "lock");
1364
0
    db->lock_request_id = json_clone(msg->id);
1365
0
    return msg;
1366
0
}
1367
1368
static struct jsonrpc_msg *
1369
ovsdb_cs_db_compose_unlock_request(struct ovsdb_cs_db *db)
1370
0
{
1371
0
    return ovsdb_cs_db_compose_lock_request__(db, "unlock");
1372
0
}
1373
1374
static void
1375
ovsdb_cs_db_parse_lock_reply(struct ovsdb_cs_db *db,
1376
                              const struct json *result)
1377
0
{
1378
0
    bool got_lock;
1379
1380
0
    json_destroy(db->lock_request_id);
1381
0
    db->lock_request_id = NULL;
1382
1383
0
    if (result->type == JSON_OBJECT) {
1384
0
        const struct json *locked;
1385
1386
0
        locked = shash_find_data(json_object(result), "locked");
1387
0
        got_lock = locked && locked->type == JSON_TRUE;
1388
0
    } else {
1389
0
        got_lock = false;
1390
0
    }
1391
1392
0
    ovsdb_cs_db_update_has_lock(db, got_lock);
1393
0
    if (!got_lock) {
1394
0
        db->is_lock_contended = true;
1395
0
    }
1396
0
}
1397
1398
static bool
1399
ovsdb_cs_db_parse_lock_notify(struct ovsdb_cs_db *db,
1400
                               const struct json *params,
1401
                               bool new_has_lock)
1402
0
{
1403
0
    if (db->lock_name
1404
0
        && params->type == JSON_ARRAY
1405
0
        && json_array_size(params) > 0
1406
0
        && json_array_at(params, 0)->type == JSON_STRING) {
1407
0
        const char *lock_name = json_string(json_array_at(params, 0));
1408
1409
0
        if (!strcmp(db->lock_name, lock_name)) {
1410
0
            ovsdb_cs_db_update_has_lock(db, new_has_lock);
1411
0
            if (!new_has_lock) {
1412
0
                db->is_lock_contended = true;
1413
0
            }
1414
0
            return true;
1415
0
        }
1416
0
    }
1417
0
    return false;
1418
0
}
1419

1420
/* Transactions. */
1421
1422
static bool
1423
ovsdb_cs_db_txn_process_reply(struct ovsdb_cs *cs,
1424
                              const struct jsonrpc_msg *reply)
1425
0
{
1426
0
    bool found = ovsdb_cs_forget_transaction(cs, reply->id);
1427
0
    if (found) {
1428
0
        struct ovsdb_cs_event *event
1429
0
            = ovsdb_cs_db_add_event(&cs->data, OVSDB_CS_EVENT_TYPE_TXN_REPLY);
1430
0
        event->txn_reply = jsonrpc_msg_clone(reply);
1431
0
    }
1432
0
    return found;
1433
0
}
1434
1435
/* Returns true if 'cs' can be sent a transaction now, false otherwise.  This
1436
 * is useful for optimization: there is no point in composing and sending a
1437
 * transaction if it returns false. */
1438
bool
1439
ovsdb_cs_may_send_transaction(const struct ovsdb_cs *cs)
1440
0
{
1441
0
    return (cs->session != NULL
1442
0
            && cs->state == CS_S_MONITORING
1443
0
            && (!cs->data.lock_name || ovsdb_cs_has_lock(cs)));
1444
0
}
1445
1446
/* Attempts to send a transaction with the specified 'operations' to 'cs''s
1447
 * server.  On success, returns the request ID; the caller must eventually free
1448
 * it.  On failure, returns NULL. */
1449
struct json * OVS_WARN_UNUSED_RESULT
1450
ovsdb_cs_send_transaction(struct ovsdb_cs *cs, struct json *operations)
1451
0
{
1452
0
    if (!ovsdb_cs_may_send_transaction(cs)) {
1453
0
        json_destroy(operations);
1454
0
        return NULL;
1455
0
    }
1456
1457
0
    if (cs->data.lock_name) {
1458
0
        struct json *assertion = json_object_create();
1459
0
        json_object_put_string(assertion, "op", "assert");
1460
0
        json_object_put_string(assertion, "lock", cs->data.lock_name);
1461
0
        json_array_add(operations, assertion);
1462
0
    }
1463
1464
0
    struct json *request_id;
1465
0
    struct jsonrpc_msg *request = jsonrpc_create_request(
1466
0
        "transact", operations, &request_id);
1467
0
    int error = jsonrpc_session_send(cs->session, request);
1468
0
    if (error) {
1469
0
        json_destroy(request_id);
1470
0
        return NULL;
1471
0
    }
1472
1473
0
    if (cs->n_txns >= cs->allocated_txns) {
1474
0
        cs->txns = x2nrealloc(cs->txns, &cs->allocated_txns,
1475
0
                              sizeof *cs->txns);
1476
0
    }
1477
0
    cs->txns[cs->n_txns++] = request_id;
1478
0
    return json_clone(request_id);
1479
0
}
1480
1481
/* Makes 'cs' drop its record of transaction 'request_id'.  If a reply arrives
1482
 * for it later (which it will, unless the connection drops in the meantime),
1483
 * it won't be reported through an event.
1484
 *
1485
 * Returns true if 'request_id' was known, false otherwise. */
1486
bool
1487
ovsdb_cs_forget_transaction(struct ovsdb_cs *cs, const struct json *request_id)
1488
0
{
1489
0
    for (size_t i = 0; i < cs->n_txns; i++) {
1490
0
        if (json_equal(request_id, cs->txns[i])) {
1491
0
            json_destroy(cs->txns[i]);
1492
0
            cs->txns[i] = cs->txns[--cs->n_txns];
1493
0
            return true;
1494
0
        }
1495
0
    }
1496
0
    return false;
1497
0
}
1498

1499
static void
1500
ovsdb_cs_send_schema_request(struct ovsdb_cs *cs,
1501
                              struct ovsdb_cs_db *db)
1502
0
{
1503
0
    ovsdb_cs_send_request(cs, jsonrpc_create_request(
1504
0
                               "get_schema",
1505
0
                               json_array_create_1(json_string_create(
1506
0
                                                       db->db_name)),
1507
0
                               NULL));
1508
0
}
1509
1510
static void
1511
ovsdb_cs_send_db_change_aware(struct ovsdb_cs *cs)
1512
0
{
1513
0
    struct jsonrpc_msg *msg = jsonrpc_create_request(
1514
0
        "set_db_change_aware", json_array_create_1(json_boolean_create(true)),
1515
0
        NULL);
1516
0
    jsonrpc_session_send(cs->session, msg);
1517
0
}
1518
1519
static void
1520
ovsdb_cs_send_monitor_request(struct ovsdb_cs *cs, struct ovsdb_cs_db *db,
1521
                              int version)
1522
0
{
1523
0
    struct json *mrs = db->ops->compose_monitor_requests(
1524
0
        db->schema, db->ops_aux);
1525
    /* XXX handle failure */
1526
0
    ovs_assert(mrs->type == JSON_OBJECT);
1527
1528
0
    ovsdb_cs_db_sync_condition(db);
1529
1530
0
    if (version > 1) {
1531
0
        struct ovsdb_cs_db_table *table;
1532
0
        HMAP_FOR_EACH (table, hmap_node, &db->tables) {
1533
0
            if (table->ack_cond) {
1534
0
                struct json *mr = shash_find_data(json_object(mrs),
1535
0
                                                  table->name);
1536
0
                if (!mr) {
1537
0
                    mr = json_array_create_empty();
1538
0
                    json_object_put(mrs, table->name, mr);
1539
0
                }
1540
0
                ovs_assert(mr->type == JSON_ARRAY);
1541
1542
0
                struct json *mr0;
1543
0
                if (json_array_size(mr) == 0) {
1544
0
                    mr0 = json_object_create();
1545
0
                    json_object_put(mr0, "columns", json_array_create_empty());
1546
0
                    json_array_add(mr, mr0);
1547
0
                } else {
1548
0
                    mr0 = CONST_CAST(struct json *, json_array_at(mr, 0));
1549
0
                }
1550
0
                ovs_assert(mr0->type == JSON_OBJECT);
1551
1552
0
                json_object_put(mr0, "where",
1553
0
                                json_clone(table->ack_cond));
1554
0
            }
1555
0
        }
1556
0
    }
1557
1558
0
    const char *method = (version == 1 ? "monitor"
1559
0
                          : version == 2 ? "monitor_cond"
1560
0
                          : "monitor_cond_since");
1561
0
    struct json *params = json_array_create_3(
1562
0
                              json_string_create(db->db_name),
1563
0
                              json_clone(db->monitor_id),
1564
0
                              mrs);
1565
0
    if (version == 3) {
1566
0
        json_array_add(params, json_string_create_uuid(&db->last_id));
1567
0
    }
1568
0
    ovsdb_cs_send_request(cs, jsonrpc_create_request(method, params, NULL));
1569
0
}
1570
1571
static void
1572
log_parse_update_error(struct ovsdb_error *error)
1573
0
{
1574
0
    if (!VLOG_DROP_WARN(&syntax_rl)) {
1575
0
        char *s = ovsdb_error_to_string(error);
1576
0
        VLOG_WARN_RL(&syntax_rl, "%s", s);
1577
0
        free(s);
1578
0
    }
1579
0
    ovsdb_error_destroy(error);
1580
0
}
1581
1582
static void
1583
ovsdb_cs_db_add_update(struct ovsdb_cs_db *db,
1584
                       const struct json *table_updates, int version,
1585
                       bool clear, bool monitor_reply)
1586
0
{
1587
0
    struct ovsdb_cs_event *event = ovsdb_cs_db_add_event(
1588
0
        db, OVSDB_CS_EVENT_TYPE_UPDATE);
1589
0
    event->update = (struct ovsdb_cs_update_event) {
1590
0
        .table_updates = json_clone(table_updates),
1591
0
        .clear = clear,
1592
0
        .monitor_reply = monitor_reply,
1593
0
        .version = version,
1594
0
        .last_id = db->last_id,
1595
0
    };
1596
0
}
1597
1598
static void
1599
ovsdb_cs_db_parse_monitor_reply(struct ovsdb_cs_db *db,
1600
                                const struct json *result, int version)
1601
0
{
1602
0
    const struct json *table_updates;
1603
0
    bool clear;
1604
0
    if (version == 3) {
1605
0
        if (result->type != JSON_ARRAY || json_array_size(result) != 3
1606
0
            || (json_array_at(result, 0)->type != JSON_TRUE &&
1607
0
                json_array_at(result, 0)->type != JSON_FALSE)
1608
0
            || json_array_at(result, 1)->type != JSON_STRING
1609
0
            || !uuid_from_string(&db->last_id,
1610
0
                                 json_string(json_array_at(result, 1)))) {
1611
0
            struct ovsdb_error *error = ovsdb_syntax_error(
1612
0
                result, NULL, "bad monitor_cond_since reply format");
1613
0
            log_parse_update_error(error);
1614
0
            return;
1615
0
        }
1616
1617
0
        bool found = json_boolean(json_array_at(result, 0));
1618
0
        clear = !found;
1619
0
        table_updates = json_array_at(result, 2);
1620
0
    } else {
1621
0
        clear = true;
1622
0
        table_updates = result;
1623
0
    }
1624
1625
0
    ovsdb_cs_db_add_update(db, table_updates, version, clear, true);
1626
0
}
1627
1628
static bool
1629
ovsdb_cs_db_parse_update_rpc(struct ovsdb_cs_db *db,
1630
                             const struct jsonrpc_msg *msg)
1631
0
{
1632
0
    if (msg->type != JSONRPC_NOTIFY) {
1633
0
        return false;
1634
0
    }
1635
1636
0
    int version = (!strcmp(msg->method, "update") ? 1
1637
0
                   : !strcmp(msg->method, "update2") ? 2
1638
0
                   : !strcmp(msg->method, "update3") ? 3
1639
0
                   : 0);
1640
0
    if (!version) {
1641
0
        return false;
1642
0
    }
1643
1644
0
    struct json *params = msg->params;
1645
0
    int n = version == 3 ? 3 : 2;
1646
0
    if (params->type != JSON_ARRAY || json_array_size(params) != n) {
1647
0
        struct ovsdb_error *error = ovsdb_syntax_error(
1648
0
            params, NULL, "%s must be an array with %u elements.",
1649
0
            msg->method, n);
1650
0
        log_parse_update_error(error);
1651
0
        return false;
1652
0
    }
1653
1654
0
    if (!json_equal(json_array_at(params, 0), db->monitor_id)) {
1655
0
        return false;
1656
0
    }
1657
1658
0
    if (version == 3) {
1659
0
        const char *last_id = json_string(json_array_at(params, 1));
1660
0
        if (!uuid_from_string(&db->last_id, last_id)) {
1661
0
            struct ovsdb_error *error = ovsdb_syntax_error(
1662
0
                params, NULL, "Last-id %s is not in UUID format.", last_id);
1663
0
            log_parse_update_error(error);
1664
0
            return false;
1665
0
        }
1666
0
    }
1667
1668
0
    const struct json *table_updates = json_array_at(params,
1669
0
                                                     version == 3 ? 2 : 1);
1670
0
    ovsdb_cs_db_add_update(db, table_updates, version, false, false);
1671
0
    return true;
1672
0
}
1673
1674
static bool
1675
ovsdb_cs_handle_monitor_canceled(struct ovsdb_cs *cs,
1676
                                 struct ovsdb_cs_db *db,
1677
                                 const struct jsonrpc_msg *msg)
1678
0
{
1679
0
    if (msg->type != JSONRPC_NOTIFY
1680
0
        || strcmp(msg->method, "monitor_canceled")
1681
0
        || msg->params->type != JSON_ARRAY
1682
0
        || json_array_size(msg->params) != 1
1683
0
        || !json_equal(json_array_at(msg->params, 0), db->monitor_id)) {
1684
0
        return false;
1685
0
    }
1686
1687
0
    db->monitor_version = 0;
1688
1689
    /* Cancel the other monitor and restart the FSM from the top.
1690
     *
1691
     * Maybe a more sophisticated response would be better in some cases, but
1692
     * it doesn't seem worth optimizing yet.  (Although this is already more
1693
     * sophisticated than just dropping the connection and reconnecting.) */
1694
0
    struct ovsdb_cs_db *other_db
1695
0
        = db == &cs->data ? &cs->server : &cs->data;
1696
0
    if (other_db->monitor_version) {
1697
0
        jsonrpc_session_send(
1698
0
            cs->session,
1699
0
            jsonrpc_create_request(
1700
0
                "monitor_cancel",
1701
0
                json_array_create_1(json_clone(other_db->monitor_id)), NULL));
1702
0
        other_db->monitor_version = 0;
1703
0
    }
1704
0
    ovsdb_cs_restart_fsm(cs);
1705
1706
0
    return true;
1707
0
}
1708

1709
/* The _Server database.
1710
 *
1711
 * We replicate the Database table in the _Server database because this is the
1712
 * only way to find out properties we need to know for clustering, such as
1713
 * whether a database is clustered at all and whether this server is the
1714
 * leader.
1715
 *
1716
 * This code implements a kind of simple IDL-like layer. */
1717
1718
struct server_column {
1719
    const char *name;
1720
    struct ovsdb_type type;
1721
};
1722
enum server_column_index {
1723
    COL_NAME,
1724
    COL_MODEL,
1725
    COL_CONNECTED,
1726
    COL_LEADER,
1727
    COL_SCHEMA,
1728
    COL_CID,
1729
    COL_INDEX,
1730
};
1731
#define OPTIONAL_COLUMN(TYPE) \
1732
    {                                           \
1733
        .key = OVSDB_BASE_##TYPE##_INIT,        \
1734
        .value = OVSDB_BASE_VOID_INIT,          \
1735
        .n_min = 0,                             \
1736
        .n_max = 1                              \
1737
    }
1738
static const struct server_column server_columns[] = {
1739
    [COL_NAME] = {"name",  OPTIONAL_COLUMN(STRING) },
1740
    [COL_MODEL] = {"model", OPTIONAL_COLUMN(STRING) },
1741
    [COL_CONNECTED] = {"connected", OPTIONAL_COLUMN(BOOLEAN) },
1742
    [COL_LEADER] = {"leader", OPTIONAL_COLUMN(BOOLEAN) },
1743
    [COL_SCHEMA] = {"schema", OPTIONAL_COLUMN(STRING) },
1744
    [COL_CID] = {"cid", OPTIONAL_COLUMN(UUID) },
1745
    [COL_INDEX] = {"index", OPTIONAL_COLUMN(INTEGER) },
1746
};
1747
0
#define N_SERVER_COLUMNS ARRAY_SIZE(server_columns)
1748
struct server_row {
1749
    struct hmap_node hmap_node;
1750
    struct uuid uuid;
1751
    struct ovsdb_datum data[N_SERVER_COLUMNS];
1752
};
1753
1754
static void
1755
server_row_destroy(struct server_row *row)
1756
0
{
1757
0
    if (row) {
1758
0
        for (size_t i = 0; i < N_SERVER_COLUMNS; i++) {
1759
0
            ovsdb_datum_destroy(&row->data[i], &server_columns[i].type);
1760
0
        }
1761
0
        free(row);
1762
0
    }
1763
0
}
1764
1765
static struct server_row *
1766
ovsdb_cs_find_server_row(struct ovsdb_cs *cs, const struct uuid *uuid)
1767
0
{
1768
0
    struct server_row *row;
1769
0
    HMAP_FOR_EACH (row, hmap_node, &cs->server_rows) {
1770
0
        if (uuid_equals(uuid, &row->uuid)) {
1771
0
            return row;
1772
0
        }
1773
0
    }
1774
0
    return NULL;
1775
0
}
1776
1777
static void
1778
ovsdb_cs_delete_server_row(struct ovsdb_cs *cs, struct server_row *row)
1779
0
{
1780
0
    hmap_remove(&cs->server_rows, &row->hmap_node);
1781
0
    server_row_destroy(row);
1782
0
}
1783
1784
static struct server_row *
1785
ovsdb_cs_insert_server_row(struct ovsdb_cs *cs, const struct uuid *uuid)
1786
0
{
1787
0
    struct server_row *row = xmalloc(sizeof *row);
1788
0
    hmap_insert(&cs->server_rows, &row->hmap_node, uuid_hash(uuid));
1789
0
    row->uuid = *uuid;
1790
0
    for (size_t i = 0; i < N_SERVER_COLUMNS; i++) {
1791
0
        ovsdb_datum_init_default(&row->data[i], &server_columns[i].type);
1792
0
    }
1793
0
    return row;
1794
0
}
1795
1796
static void
1797
ovsdb_cs_update_server_row(struct server_row *row,
1798
                           const struct shash *update, bool xor)
1799
0
{
1800
0
    for (size_t i = 0; i < N_SERVER_COLUMNS; i++) {
1801
0
        const struct server_column *column = &server_columns[i];
1802
0
        struct shash_node *node = shash_find(update, column->name);
1803
0
        if (!node) {
1804
0
            continue;
1805
0
        }
1806
0
        const struct json *json = node->data;
1807
1808
0
        struct ovsdb_datum *old = &row->data[i];
1809
0
        struct ovsdb_datum new;
1810
0
        if (!xor) {
1811
0
            struct ovsdb_error *error = ovsdb_datum_from_json(
1812
0
                &new, &column->type, json, NULL);
1813
0
            if (error) {
1814
0
                ovsdb_error_destroy(error);
1815
0
                continue;
1816
0
            }
1817
0
        } else {
1818
0
            struct ovsdb_datum diff;
1819
0
            struct ovsdb_error *error = ovsdb_transient_datum_from_json(
1820
0
                &diff, &column->type, json);
1821
0
            if (error) {
1822
0
                ovsdb_error_destroy(error);
1823
0
                continue;
1824
0
            }
1825
1826
0
            error = ovsdb_datum_apply_diff(&new, old, &diff, &column->type);
1827
0
            if (error) {
1828
0
                ovsdb_error_destroy(error);
1829
0
                ovsdb_datum_destroy(&new, &column->type);
1830
0
                continue;
1831
0
            }
1832
0
            ovsdb_datum_destroy(&diff, &column->type);
1833
0
        }
1834
1835
0
        ovsdb_datum_destroy(&row->data[i], &column->type);
1836
0
        row->data[i] = new;
1837
0
    }
1838
0
}
1839
1840
static void
1841
ovsdb_cs_clear_server_rows(struct ovsdb_cs *cs)
1842
0
{
1843
0
    struct server_row *row;
1844
0
    HMAP_FOR_EACH_SAFE (row, hmap_node, &cs->server_rows) {
1845
0
        ovsdb_cs_delete_server_row(cs, row);
1846
0
    }
1847
0
}
1848
1849
static void log_parse_update_error(struct ovsdb_error *);
1850
1851
static void
1852
ovsdb_cs_process_server_event(struct ovsdb_cs *cs,
1853
                              const struct ovsdb_cs_event *event)
1854
0
{
1855
0
    ovs_assert(event->type == OVSDB_CS_EVENT_TYPE_UPDATE);
1856
1857
0
    const struct ovsdb_cs_update_event *update = &event->update;
1858
0
    struct ovsdb_cs_db_update *du;
1859
0
    struct ovsdb_error *error = ovsdb_cs_parse_db_update(
1860
0
        update->table_updates, update->version, &du);
1861
0
    if (error) {
1862
0
        log_parse_update_error(error);
1863
0
        return;
1864
0
    }
1865
1866
0
    if (update->clear) {
1867
0
        ovsdb_cs_clear_server_rows(cs);
1868
0
    }
1869
1870
0
    const struct ovsdb_cs_table_update *tu = ovsdb_cs_db_update_find_table(
1871
0
        du, "Database");
1872
0
    if (tu) {
1873
0
        for (size_t i = 0; i < tu->n; i++) {
1874
0
            const struct ovsdb_cs_row_update *ru = &tu->row_updates[i];
1875
0
            struct server_row *row
1876
0
                = ovsdb_cs_find_server_row(cs, &ru->row_uuid);
1877
0
            if (ru->type == OVSDB_CS_ROW_DELETE) {
1878
0
                ovsdb_cs_delete_server_row(cs, row);
1879
0
            } else {
1880
0
                if (!row) {
1881
0
                    row = ovsdb_cs_insert_server_row(cs, &ru->row_uuid);
1882
0
                }
1883
0
                ovsdb_cs_update_server_row(row, ru->columns,
1884
0
                                           ru->type == OVSDB_CS_ROW_XOR);
1885
0
            }
1886
0
        }
1887
0
    }
1888
1889
0
    ovsdb_cs_db_update_destroy(du);
1890
0
}
1891
1892
static const char *
1893
server_column_get_string(const struct server_row *row,
1894
                         enum server_column_index index,
1895
                         const char *default_value)
1896
0
{
1897
0
    ovs_assert(server_columns[index].type.key.type == OVSDB_TYPE_STRING);
1898
0
    const struct ovsdb_datum *d = &row->data[index];
1899
0
    return d->n == 1 ? json_string(d->keys[0].s) : default_value;
1900
0
}
1901
1902
static bool
1903
server_column_get_bool(const struct server_row *row,
1904
                       enum server_column_index index,
1905
                       bool default_value)
1906
0
{
1907
0
    ovs_assert(server_columns[index].type.key.type == OVSDB_TYPE_BOOLEAN);
1908
0
    const struct ovsdb_datum *d = &row->data[index];
1909
0
    return d->n == 1 ? d->keys[0].boolean : default_value;
1910
0
}
1911
1912
static uint64_t
1913
server_column_get_int(const struct server_row *row,
1914
                      enum server_column_index index,
1915
                      uint64_t default_value)
1916
0
{
1917
0
    ovs_assert(server_columns[index].type.key.type == OVSDB_TYPE_INTEGER);
1918
0
    const struct ovsdb_datum *d = &row->data[index];
1919
0
    return d->n == 1 ? d->keys[0].integer : default_value;
1920
0
}
1921
1922
static const struct uuid *
1923
server_column_get_uuid(const struct server_row *row,
1924
                       enum server_column_index index,
1925
                       const struct uuid *default_value)
1926
0
{
1927
0
    ovs_assert(server_columns[index].type.key.type == OVSDB_TYPE_UUID);
1928
0
    const struct ovsdb_datum *d = &row->data[index];
1929
0
    return d->n == 1 ? &d->keys[0].uuid : default_value;
1930
0
}
1931
1932
static const struct server_row *
1933
ovsdb_find_server_row(struct ovsdb_cs *cs)
1934
0
{
1935
0
    const struct server_row *row;
1936
0
    HMAP_FOR_EACH (row, hmap_node, &cs->server_rows) {
1937
0
        const struct uuid *cid = server_column_get_uuid(row, COL_CID, NULL);
1938
0
        const char *name = server_column_get_string(row, COL_NAME, NULL);
1939
0
        if (uuid_is_zero(&cs->cid)
1940
0
            ? (name && !strcmp(cs->data.db_name, name))
1941
0
            : (cid && uuid_equals(cid, &cs->cid))) {
1942
0
            return row;
1943
0
        }
1944
0
    }
1945
0
    return NULL;
1946
0
}
1947
1948
static void OVS_UNUSED
1949
ovsdb_log_server_rows(const struct ovsdb_cs *cs)
1950
0
{
1951
0
    int row_num = 0;
1952
0
    const struct server_row *row;
1953
0
    HMAP_FOR_EACH (row, hmap_node, &cs->server_rows) {
1954
0
        struct ds s = DS_EMPTY_INITIALIZER;
1955
0
        for (size_t i = 0; i < N_SERVER_COLUMNS; i++) {
1956
0
            ds_put_format(&s, " %s=", server_columns[i].name);
1957
0
            if (i == COL_SCHEMA) {
1958
0
                ds_put_format(&s, "...");
1959
0
            } else {
1960
0
                ovsdb_datum_to_string(&row->data[i], &server_columns[i].type,
1961
0
                                      &s);
1962
0
            }
1963
0
        }
1964
0
        VLOG_INFO("row %d:%s", row_num++, ds_cstr(&s));
1965
0
        ds_destroy(&s);
1966
0
    }
1967
0
}
1968
1969
static bool
1970
ovsdb_cs_check_server_db__(struct ovsdb_cs *cs)
1971
0
{
1972
0
    struct ovsdb_cs_event *event;
1973
0
    LIST_FOR_EACH_POP (event, list_node, &cs->server.events) {
1974
0
        ovsdb_cs_process_server_event(cs, event);
1975
0
        ovsdb_cs_event_destroy(event);
1976
0
    }
1977
1978
0
    const struct server_row *db_row = ovsdb_find_server_row(cs);
1979
0
    static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 5);
1980
0
    const char *server_name = jsonrpc_session_get_name(cs->session);
1981
0
    if (!db_row) {
1982
0
        VLOG_INFO_RL(&rl, "%s: server does not have %s database",
1983
0
                     server_name, cs->data.db_name);
1984
0
        return false;
1985
0
    }
1986
1987
0
    bool ok = false;
1988
0
    const char *model = server_column_get_string(db_row, COL_MODEL, "");
1989
0
    const char *schema = server_column_get_string(db_row, COL_SCHEMA, NULL);
1990
0
    bool connected = server_column_get_bool(db_row, COL_CONNECTED, false);
1991
0
    if (!strcmp(model, "clustered")) {
1992
0
        bool leader = server_column_get_bool(db_row, COL_LEADER, false);
1993
0
        uint64_t index = server_column_get_int(db_row, COL_INDEX, 0);
1994
1995
0
        if (!schema) {
1996
0
            VLOG_INFO("%s: clustered database server has not yet joined "
1997
0
                      "cluster; trying another server", server_name);
1998
0
        } else if (!connected) {
1999
0
            VLOG_INFO("%s: clustered database server is disconnected "
2000
0
                      "from cluster; trying another server", server_name);
2001
0
        } else if (cs->leader_only && !leader) {
2002
0
            VLOG_INFO("%s: clustered database server is not cluster "
2003
0
                      "leader; trying another server", server_name);
2004
0
        } else if (index < cs->min_index) {
2005
0
            VLOG_WARN("%s: clustered database server has stale data; "
2006
0
                      "trying another server", server_name);
2007
0
        } else {
2008
0
            cs->min_index = index;
2009
0
            ok = true;
2010
0
        }
2011
0
    } else if (!strcmp(model, "relay")) {
2012
0
        if (!schema) {
2013
0
            VLOG_INFO("%s: relay database server has not yet connected to the "
2014
0
                      "relay source; trying another server", server_name);
2015
0
        } else if (!connected) {
2016
0
            VLOG_INFO("%s: relay database server is disconnected from the "
2017
0
                      "relay source; trying another server", server_name);
2018
0
        } else if (cs->leader_only) {
2019
0
            VLOG_INFO("%s: relay database server cannot be a leader; "
2020
0
                      "trying another server", server_name);
2021
0
        } else {
2022
0
            ok = true;
2023
0
        }
2024
0
    } else {
2025
0
        if (!schema) {
2026
0
            VLOG_INFO("%s: missing database schema", server_name);
2027
0
        } else {
2028
0
            ok = true;
2029
0
        }
2030
0
    }
2031
0
    if (!ok) {
2032
0
        return false;
2033
0
    }
2034
2035
0
    if (cs->state == CS_S_SERVER_MONITOR_REQUESTED) {
2036
0
        json_destroy(cs->data.schema);
2037
0
        cs->data.schema = json_from_string(schema);
2038
0
        if (cs->data.max_version >= 3) {
2039
0
            ovsdb_cs_send_monitor_request(cs, &cs->data, 3);
2040
0
            ovsdb_cs_transition(cs, CS_S_DATA_MONITOR_COND_SINCE_REQUESTED);
2041
0
        } else if (cs->data.max_version >= 2) {
2042
0
            ovsdb_cs_send_monitor_request(cs, &cs->data, 2);
2043
0
            ovsdb_cs_transition(cs, CS_S_DATA_MONITOR_COND_REQUESTED);
2044
0
        } else {
2045
0
            ovsdb_cs_send_monitor_request(cs, &cs->data, 1);
2046
0
            ovsdb_cs_transition(cs, CS_S_DATA_MONITOR_REQUESTED);
2047
0
        }
2048
0
    }
2049
0
    return true;
2050
0
}
2051
2052
static bool
2053
ovsdb_cs_check_server_db(struct ovsdb_cs *cs)
2054
0
{
2055
0
    bool ok = ovsdb_cs_check_server_db__(cs);
2056
0
    if (!ok) {
2057
0
        ovsdb_cs_retry(cs);
2058
0
    }
2059
0
    return ok;
2060
0
}
2061
2062
static struct json *
2063
ovsdb_cs_compose_server_monitor_request(const struct json *schema_json,
2064
                                        void *cs_)
2065
0
{
2066
0
    struct ovsdb_cs *cs = cs_;
2067
0
    struct shash *schema = ovsdb_cs_parse_schema(schema_json);
2068
0
    struct json *monitor_requests = json_object_create();
2069
2070
0
    const char *table_name = "Database";
2071
0
    const struct shash *table_schema
2072
0
        = schema ? shash_find_data(schema, table_name) : NULL;
2073
0
    if (!table_schema) {
2074
0
        VLOG_WARN("%s database lacks %s table "
2075
0
                  "(database needs upgrade?)",
2076
0
                  cs->server.db_name, table_name);
2077
        /* XXX return failure? */
2078
0
    } else {
2079
0
        struct json *columns = json_array_create_empty();
2080
0
        for (size_t j = 0; j < N_SERVER_COLUMNS; j++) {
2081
0
            const struct server_column *column = &server_columns[j];
2082
0
            bool db_has_column = (table_schema &&
2083
0
                                  shash_find(table_schema, column->name));
2084
0
            if (table_schema && !db_has_column) {
2085
0
                VLOG_WARN("%s table in %s database lacks %s column "
2086
0
                          "(database needs upgrade?)",
2087
0
                          table_name, cs->server.db_name, column->name);
2088
0
                continue;
2089
0
            }
2090
0
            json_array_add(columns, json_string_create(column->name));
2091
0
        }
2092
2093
0
        struct json *monitor_request = json_object_create();
2094
0
        json_object_put(monitor_request, "columns", columns);
2095
0
        json_object_put(monitor_requests, table_name,
2096
0
                        json_array_create_1(monitor_request));
2097
0
    }
2098
0
    ovsdb_cs_free_schema(schema);
2099
2100
0
    return monitor_requests;
2101
0
}
2102
2103
static const struct ovsdb_cs_ops ovsdb_cs_server_ops = {
2104
    ovsdb_cs_compose_server_monitor_request
2105
};
2106

2107
static void
2108
log_error(struct ovsdb_error *error)
2109
0
{
2110
0
    char *s = ovsdb_error_to_string_free(error);
2111
0
    VLOG_WARN("error parsing database schema: %s", s);
2112
0
    free(s);
2113
0
}
2114
2115
/* Parses 'schema_json', an OVSDB schema in JSON format as described in RFC
2116
 * 7047, to obtain the names of its rows and columns.  If successful, returns
2117
 * an shash whose keys are table names and whose values are shashes, where each
2118
 * shash contains the names of its table's columns as keys and an ovsdb_type as
2119
 * data.  On failure (due to a parse error), returns NULL.
2120
 *
2121
 * It would also be possible to use the general-purpose OVSDB schema parser in
2122
 * ovsdb-server, but that's overkill, possibly too strict for the current use
2123
 * case, and would require restructuring ovsdb-server to separate the schema
2124
 * code from the rest. */
2125
struct shash *
2126
ovsdb_cs_parse_schema(const struct json *schema_json)
2127
0
{
2128
0
    struct ovsdb_parser parser;
2129
0
    const struct json *tables_json;
2130
0
    struct shash *schema = NULL;
2131
0
    struct ovsdb_error *error;
2132
0
    struct shash_node *node;
2133
2134
0
    ovsdb_parser_init(&parser, schema_json, "database schema");
2135
0
    tables_json = ovsdb_parser_member(&parser, "tables", OP_OBJECT);
2136
0
    error = ovsdb_parser_destroy(&parser);
2137
0
    if (error) {
2138
0
        log_error(error);
2139
0
        return NULL;
2140
0
    }
2141
2142
0
    schema = xmalloc(sizeof *schema);
2143
0
    shash_init(schema);
2144
0
    SHASH_FOR_EACH (node, json_object(tables_json)) {
2145
0
        const char *table_name = node->name;
2146
0
        const struct json *json = node->data;
2147
0
        const struct json *columns_json;
2148
2149
0
        ovsdb_parser_init(&parser, json, "table schema for table %s",
2150
0
                          table_name);
2151
0
        columns_json = ovsdb_parser_member(&parser, "columns", OP_OBJECT);
2152
0
        error = ovsdb_parser_destroy(&parser);
2153
0
        if (error) {
2154
0
            goto error;
2155
0
        }
2156
2157
0
        struct shash *columns = xmalloc(sizeof *columns);
2158
0
        shash_init(columns);
2159
0
        shash_add(schema, table_name, columns);
2160
2161
0
        struct shash_node *node2;
2162
0
        SHASH_FOR_EACH (node2, json_object(columns_json)) {
2163
0
            const struct json *column_json = node2->data;
2164
0
            const char *column_name = node2->name;
2165
0
            const struct json *type_json;
2166
0
            struct ovsdb_type *type;
2167
2168
0
            ovsdb_parser_init(&parser, column_json,
2169
0
                              "schema for column %s", column_name);
2170
0
            type_json = ovsdb_parser_member(&parser, "type",
2171
0
                                            OP_STRING | OP_OBJECT);
2172
0
            error = ovsdb_parser_destroy(&parser);
2173
0
            if (error) {
2174
0
                goto error;
2175
0
            }
2176
2177
0
            type = xzalloc(sizeof *type);
2178
0
            error = ovsdb_type_from_json(type, type_json);
2179
0
            if (error) {
2180
0
                free(type);
2181
0
                goto error;
2182
0
            }
2183
2184
0
            if (!shash_add_once(columns, column_name, type)) {
2185
                /* Should never happen, but just in case. */
2186
0
                ovsdb_type_destroy(type);
2187
0
                free(type);
2188
0
            }
2189
0
        }
2190
0
    }
2191
0
    return schema;
2192
2193
0
error:
2194
0
    log_error(error);
2195
0
    ovsdb_cs_free_schema(schema);
2196
0
    return NULL;
2197
0
}
2198
2199
/* Frees 'schema', which is in the format returned by
2200
 * ovsdb_cs_parse_schema(). */
2201
void
2202
ovsdb_cs_free_schema(struct shash *schema)
2203
0
{
2204
0
    if (schema) {
2205
0
        struct shash_node *node;
2206
2207
0
        SHASH_FOR_EACH_SAFE (node, schema) {
2208
0
            struct shash *columns = node->data;
2209
0
            struct shash_node *node2;
2210
2211
0
            SHASH_FOR_EACH (node2, columns) {
2212
0
                ovsdb_type_destroy(node2->data);
2213
0
            }
2214
0
            shash_destroy_free_data(columns);
2215
0
            free(columns);
2216
2217
0
            shash_delete(schema, node);
2218
0
        }
2219
0
        shash_destroy(schema);
2220
0
        free(schema);
2221
0
    }
2222
0
}
2223

2224
static struct ovsdb_error * OVS_WARN_UNUSED_RESULT
2225
ovsdb_cs_parse_row_update1(const struct json *in,
2226
                           struct ovsdb_cs_row_update *out)
2227
0
{
2228
0
    const struct json *old_json, *new_json;
2229
2230
0
    old_json = shash_find_data(json_object(in), "old");
2231
0
    new_json = shash_find_data(json_object(in), "new");
2232
0
    if (old_json && old_json->type != JSON_OBJECT) {
2233
0
        return ovsdb_syntax_error(old_json, NULL,
2234
0
                                  "\"old\" <row> is not object");
2235
0
    } else if (new_json && new_json->type != JSON_OBJECT) {
2236
0
        return ovsdb_syntax_error(new_json, NULL,
2237
0
                                  "\"new\" <row> is not object");
2238
0
    } else if ((old_json != NULL) + (new_json != NULL)
2239
0
               != shash_count(json_object(in))) {
2240
0
        return ovsdb_syntax_error(in, NULL,
2241
0
                                  "<row-update> contains "
2242
0
                                  "unexpected member");
2243
0
    } else if (!old_json && !new_json) {
2244
0
        return ovsdb_syntax_error(in, NULL,
2245
0
                                  "<row-update> missing \"old\" "
2246
0
                                  "and \"new\" members");
2247
0
    }
2248
2249
0
    if (!new_json) {
2250
0
        out->type = OVSDB_CS_ROW_DELETE;
2251
0
        out->columns = json_object(old_json);
2252
0
    } else if (!old_json) {
2253
0
        out->type = OVSDB_CS_ROW_INSERT;
2254
0
        out->columns = json_object(new_json);
2255
0
    } else {
2256
0
        out->type = OVSDB_CS_ROW_UPDATE;
2257
0
        out->columns = json_object(new_json);
2258
0
    }
2259
0
    return NULL;
2260
0
}
2261
2262
static struct ovsdb_error * OVS_WARN_UNUSED_RESULT
2263
ovsdb_cs_parse_row_update2(const struct json *in,
2264
                           struct ovsdb_cs_row_update *out)
2265
0
{
2266
0
    const struct shash *object = json_object(in);
2267
0
    if (shash_count(object) != 1) {
2268
0
        return ovsdb_syntax_error(
2269
0
            in, NULL, "<row-update2> has %"PRIuSIZE" members "
2270
0
            "instead of expected 1", shash_count(object));
2271
0
    }
2272
2273
0
    struct shash_node *node = shash_first(object);
2274
0
    const struct json *columns = node->data;
2275
0
    if (!strcmp(node->name, "insert") || !strcmp(node->name, "initial")) {
2276
0
        out->type = OVSDB_CS_ROW_INSERT;
2277
0
    } else if (!strcmp(node->name, "modify")) {
2278
0
        out->type = OVSDB_CS_ROW_XOR;
2279
0
    } else if (!strcmp(node->name, "delete")) {
2280
0
        out->type = OVSDB_CS_ROW_DELETE;
2281
0
        if (columns->type != JSON_NULL) {
2282
0
            return ovsdb_syntax_error(
2283
0
                in, NULL,
2284
0
                "<row-update2> delete operation has unexpected value");
2285
0
        }
2286
0
        return NULL;
2287
0
    } else {
2288
0
        return ovsdb_syntax_error(in, NULL,
2289
0
                                  "<row-update2> has unknown member \"%s\"",
2290
0
                                  node->name);
2291
0
    }
2292
2293
0
    if (columns->type != JSON_OBJECT) {
2294
0
        return ovsdb_syntax_error(
2295
0
            in, NULL,
2296
0
            "<row-update2> \"%s\" operation has unexpected value",
2297
0
            node->name);
2298
0
    }
2299
0
    out->columns = json_object(columns);
2300
2301
0
    return NULL;
2302
0
}
2303
2304
static struct ovsdb_error * OVS_WARN_UNUSED_RESULT
2305
ovsdb_cs_parse_row_update(const char *table_name,
2306
                          const struct json *in, int version,
2307
                          struct ovsdb_cs_row_update *out)
2308
0
{
2309
0
    if (in->type != JSON_OBJECT) {
2310
0
        const char *suffix = version > 1 ? "2" : "";
2311
0
        return ovsdb_syntax_error(
2312
0
            in, NULL,
2313
0
            "<table-update%s> for table \"%s\" contains <row-update%s> "
2314
0
            "that is not an object",
2315
0
            suffix, table_name, suffix);
2316
0
    }
2317
2318
0
    return (version == 1
2319
0
            ? ovsdb_cs_parse_row_update1(in, out)
2320
0
            : ovsdb_cs_parse_row_update2(in, out));
2321
0
}
2322
2323
static struct ovsdb_error * OVS_WARN_UNUSED_RESULT
2324
ovsdb_cs_parse_table_update(const char *table_name,
2325
                            const struct json *in, int version,
2326
                            struct ovsdb_cs_table_update *out)
2327
0
{
2328
0
    const char *suffix = version > 1 ? "2" : "";
2329
2330
0
    if (in->type != JSON_OBJECT) {
2331
0
        return ovsdb_syntax_error(
2332
0
            in, NULL, "<table-update%s> for table \"%s\" is not an object",
2333
0
            suffix, table_name);
2334
0
    }
2335
0
    struct shash *in_rows = json_object(in);
2336
2337
0
    out->row_updates = xmalloc(shash_count(in_rows) * sizeof *out->row_updates);
2338
2339
0
    const struct shash_node *node;
2340
0
    SHASH_FOR_EACH (node, in_rows) {
2341
0
        const char *row_uuid_string = node->name;
2342
0
        struct uuid row_uuid;
2343
0
        if (!uuid_from_string(&row_uuid, row_uuid_string)) {
2344
0
            return ovsdb_syntax_error(
2345
0
                in, NULL,
2346
0
                "<table-update%s> for table \"%s\" contains "
2347
0
                "bad UUID \"%s\" as member name",
2348
0
                suffix, table_name, row_uuid_string);
2349
0
        }
2350
2351
0
        const struct json *in_ru = node->data;
2352
0
        struct ovsdb_cs_row_update *out_ru = &out->row_updates[out->n++];
2353
0
        *out_ru = (struct ovsdb_cs_row_update) { .row_uuid = row_uuid };
2354
2355
0
        struct ovsdb_error *error = ovsdb_cs_parse_row_update(
2356
0
            table_name, in_ru, version, out_ru);
2357
0
        if (error) {
2358
0
            return error;
2359
0
        }
2360
0
    }
2361
2362
0
    return NULL;
2363
0
}
2364
2365
/* Parses OVSDB <table-updates> or <table-updates2> object 'in' into '*outp'.
2366
 * If successful, sets '*outp' to the new object and returns NULL.  On failure,
2367
 * returns the error and sets '*outp' to NULL.
2368
 *
2369
 * On success, the caller must eventually free '*outp', with
2370
 * ovsdb_cs_db_update_destroy().
2371
 *
2372
 * 'version' should be 1 if 'in' is a <table-updates>, 2 or 3 if it is a
2373
 * <table-updates2>. */
2374
struct ovsdb_error * OVS_WARN_UNUSED_RESULT
2375
ovsdb_cs_parse_db_update(const struct json *in, int version,
2376
                         struct ovsdb_cs_db_update **outp)
2377
0
{
2378
0
    const char *suffix = version > 1 ? "2" : "";
2379
2380
0
    *outp = NULL;
2381
0
    if (in->type != JSON_OBJECT) {
2382
0
        return ovsdb_syntax_error(in, NULL,
2383
0
                                  "<table-updates%s> is not an object", suffix);
2384
0
    }
2385
2386
0
    struct ovsdb_cs_db_update *out = xzalloc(sizeof *out);
2387
0
    out->table_updates = xmalloc(shash_count(json_object(in))
2388
0
                                 * sizeof *out->table_updates);
2389
0
    const struct shash_node *node;
2390
0
    SHASH_FOR_EACH (node, json_object(in)) {
2391
0
        const char *table_name = node->name;
2392
0
        const struct json *in_tu = node->data;
2393
2394
0
        struct ovsdb_cs_table_update *out_tu = &out->table_updates[out->n++];
2395
0
        *out_tu = (struct ovsdb_cs_table_update) { .table_name = table_name };
2396
2397
0
        struct ovsdb_error *error = ovsdb_cs_parse_table_update(
2398
0
            table_name, in_tu, version, out_tu);
2399
0
        if (error) {
2400
0
            ovsdb_cs_db_update_destroy(out);
2401
0
            return error;
2402
0
        }
2403
0
    }
2404
2405
0
    *outp = out;
2406
0
    return NULL;
2407
0
}
2408
2409
/* Frees 'du', which was presumably allocated by ovsdb_cs_parse_db_update(). */
2410
void
2411
ovsdb_cs_db_update_destroy(struct ovsdb_cs_db_update *du)
2412
0
{
2413
0
    if (!du) {
2414
0
        return;
2415
0
    }
2416
2417
0
    for (size_t i = 0; i < du->n; i++) {
2418
0
        struct ovsdb_cs_table_update *tu = &du->table_updates[i];
2419
0
        free(tu->row_updates);
2420
0
    }
2421
0
    free(du->table_updates);
2422
0
    free(du);
2423
0
}
2424
2425
const struct ovsdb_cs_table_update *
2426
ovsdb_cs_db_update_find_table(const struct ovsdb_cs_db_update *du,
2427
                              const char *table_name)
2428
0
{
2429
0
    for (size_t i = 0; i < du->n; i++) {
2430
0
        const struct ovsdb_cs_table_update *tu = &du->table_updates[i];
2431
0
        if (!strcmp(tu->table_name, table_name)) {
2432
0
            return tu;
2433
0
        }
2434
0
    }
2435
0
    return NULL;
2436
0
}
2437