]>
Commit | Line | Data |
---|---|---|
0b1fae1b | 1 | /* Copyright (c) 2009, 2010 Nicira Networks |
f85f8ebb BP |
2 | * |
3 | * Licensed under the Apache License, Version 2.0 (the "License"); | |
4 | * you may not use this file except in compliance with the License. | |
5 | * You may obtain a copy of the License at: | |
6 | * | |
7 | * http://www.apache.org/licenses/LICENSE-2.0 | |
8 | * | |
9 | * Unless required by applicable law or agreed to in writing, software | |
10 | * distributed under the License is distributed on an "AS IS" BASIS, | |
11 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | |
12 | * See the License for the specific language governing permissions and | |
13 | * limitations under the License. | |
14 | */ | |
15 | ||
16 | #include <config.h> | |
17 | ||
18 | #include "jsonrpc-server.h" | |
19 | ||
10849616 | 20 | #include <assert.h> |
f85f8ebb BP |
21 | #include <errno.h> |
22 | ||
17d18afb | 23 | #include "bitmap.h" |
b93d3b6c | 24 | #include "column.h" |
f85f8ebb BP |
25 | #include "json.h" |
26 | #include "jsonrpc.h" | |
a8425c53 BP |
27 | #include "ovsdb-error.h" |
28 | #include "ovsdb-parser.h" | |
f85f8ebb | 29 | #include "ovsdb.h" |
6c2882f9 | 30 | #include "reconnect.h" |
a8425c53 | 31 | #include "row.h" |
f85f8ebb | 32 | #include "stream.h" |
a8425c53 | 33 | #include "table.h" |
f85f8ebb | 34 | #include "timeval.h" |
a8425c53 | 35 | #include "transaction.h" |
f85f8ebb | 36 | #include "trigger.h" |
f85f8ebb BP |
37 | #include "vlog.h" |
38 | ||
5136ce49 BP |
39 | VLOG_DEFINE_THIS_MODULE(ovsdb_jsonrpc_server) |
40 | ||
0b1fae1b | 41 | struct ovsdb_jsonrpc_remote; |
b93d3b6c | 42 | struct ovsdb_jsonrpc_session; |
f85f8ebb | 43 | |
0b1fae1b BP |
44 | /* Message rate-limiting. */ |
45 | struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5); | |
46 | ||
a8425c53 | 47 | /* Sessions. */ |
0b1fae1b BP |
48 | static struct ovsdb_jsonrpc_session *ovsdb_jsonrpc_session_create( |
49 | struct ovsdb_jsonrpc_remote *, struct jsonrpc_session *); | |
50 | static void ovsdb_jsonrpc_session_run_all(struct ovsdb_jsonrpc_remote *); | |
51 | static void ovsdb_jsonrpc_session_wait_all(struct ovsdb_jsonrpc_remote *); | |
52 | static void ovsdb_jsonrpc_session_close_all(struct ovsdb_jsonrpc_remote *); | |
31d0b6c9 | 53 | static void ovsdb_jsonrpc_session_reconnect_all(struct ovsdb_jsonrpc_remote *); |
b93d3b6c | 54 | |
a8425c53 | 55 | /* Triggers. */ |
b93d3b6c BP |
56 | static void ovsdb_jsonrpc_trigger_create(struct ovsdb_jsonrpc_session *, |
57 | struct json *id, struct json *params); | |
58 | static struct ovsdb_jsonrpc_trigger *ovsdb_jsonrpc_trigger_find( | |
59 | struct ovsdb_jsonrpc_session *, const struct json *id, size_t hash); | |
60 | static void ovsdb_jsonrpc_trigger_complete(struct ovsdb_jsonrpc_trigger *); | |
61 | static void ovsdb_jsonrpc_trigger_complete_all(struct ovsdb_jsonrpc_session *); | |
62 | static void ovsdb_jsonrpc_trigger_complete_done( | |
63 | struct ovsdb_jsonrpc_session *); | |
a8425c53 BP |
64 | |
65 | /* Monitors. */ | |
66 | static struct json *ovsdb_jsonrpc_monitor_create( | |
67 | struct ovsdb_jsonrpc_session *, struct json *params); | |
68 | static struct jsonrpc_msg *ovsdb_jsonrpc_monitor_cancel( | |
69 | struct ovsdb_jsonrpc_session *, | |
70 | struct json_array *params, | |
71 | const struct json *request_id); | |
72 | static void ovsdb_jsonrpc_monitor_remove_all(struct ovsdb_jsonrpc_session *); | |
b93d3b6c BP |
73 | \f |
74 | /* JSON-RPC database server. */ | |
f85f8ebb BP |
75 | |
76 | struct ovsdb_jsonrpc_server { | |
77 | struct ovsdb *db; | |
f85f8ebb | 78 | unsigned int n_sessions, max_sessions; |
0b1fae1b BP |
79 | struct shash remotes; /* Contains "struct ovsdb_jsonrpc_remote *"s. */ |
80 | }; | |
f85f8ebb | 81 | |
0b1fae1b BP |
82 | /* A configured remote. This is either a passive stream listener plus a list |
83 | * of the currently connected sessions, or a list of exactly one active | |
84 | * session. */ | |
85 | struct ovsdb_jsonrpc_remote { | |
86 | struct ovsdb_jsonrpc_server *server; | |
87 | struct pstream *listener; /* Listener, if passive. */ | |
88 | struct list sessions; /* List of "struct ovsdb_jsonrpc_session"s. */ | |
f85f8ebb BP |
89 | }; |
90 | ||
0b1fae1b BP |
91 | static void ovsdb_jsonrpc_server_add_remote(struct ovsdb_jsonrpc_server *, |
92 | const char *name); | |
93 | static void ovsdb_jsonrpc_server_del_remote(struct shash_node *); | |
94 | ||
b93d3b6c BP |
95 | struct ovsdb_jsonrpc_server * |
96 | ovsdb_jsonrpc_server_create(struct ovsdb *db) | |
f85f8ebb | 97 | { |
b93d3b6c | 98 | struct ovsdb_jsonrpc_server *server = xzalloc(sizeof *server); |
f85f8ebb BP |
99 | server->db = db; |
100 | server->max_sessions = 64; | |
0b1fae1b | 101 | shash_init(&server->remotes); |
b93d3b6c BP |
102 | return server; |
103 | } | |
f85f8ebb | 104 | |
23935e8b BP |
105 | void |
106 | ovsdb_jsonrpc_server_destroy(struct ovsdb_jsonrpc_server *svr) | |
107 | { | |
108 | struct shash_node *node, *next; | |
109 | ||
110 | SHASH_FOR_EACH_SAFE (node, next, &svr->remotes) { | |
111 | ovsdb_jsonrpc_server_del_remote(node); | |
112 | } | |
113 | shash_destroy(&svr->remotes); | |
114 | free(svr); | |
115 | } | |
116 | ||
0b1fae1b BP |
117 | /* Sets 'svr''s current set of remotes to the names in 'new_remotes'. The data |
118 | * values in 'new_remotes' are ignored. | |
119 | * | |
120 | * A remote is an active or passive stream connection method, e.g. "pssl:" or | |
121 | * "tcp:1.2.3.4". */ | |
6dea5eaf | 122 | void |
0b1fae1b BP |
123 | ovsdb_jsonrpc_server_set_remotes(struct ovsdb_jsonrpc_server *svr, |
124 | const struct shash *new_remotes) | |
b93d3b6c | 125 | { |
0b1fae1b BP |
126 | struct shash_node *node, *next; |
127 | ||
128 | SHASH_FOR_EACH_SAFE (node, next, &svr->remotes) { | |
129 | if (!shash_find(new_remotes, node->name)) { | |
130 | ovsdb_jsonrpc_server_del_remote(node); | |
131 | } | |
132 | } | |
133 | SHASH_FOR_EACH (node, new_remotes) { | |
134 | if (!shash_find(&svr->remotes, node->name)) { | |
135 | ovsdb_jsonrpc_server_add_remote(svr, node->name); | |
136 | } | |
f85f8ebb | 137 | } |
b93d3b6c | 138 | } |
f85f8ebb | 139 | |
0b1fae1b BP |
140 | static void |
141 | ovsdb_jsonrpc_server_add_remote(struct ovsdb_jsonrpc_server *svr, | |
142 | const char *name) | |
b93d3b6c | 143 | { |
0b1fae1b BP |
144 | struct ovsdb_jsonrpc_remote *remote; |
145 | struct pstream *listener; | |
146 | int error; | |
147 | ||
0d11f523 | 148 | error = jsonrpc_pstream_open(name, &listener); |
0b1fae1b BP |
149 | if (error && error != EAFNOSUPPORT) { |
150 | VLOG_ERR_RL(&rl, "%s: listen failed: %s", name, strerror(error)); | |
151 | return; | |
152 | } | |
153 | ||
154 | remote = xmalloc(sizeof *remote); | |
155 | remote->server = svr; | |
156 | remote->listener = listener; | |
157 | list_init(&remote->sessions); | |
158 | shash_add(&svr->remotes, name, remote); | |
159 | ||
160 | if (!listener) { | |
161 | ovsdb_jsonrpc_session_create(remote, jsonrpc_session_open(name)); | |
162 | } | |
163 | } | |
164 | ||
165 | static void | |
166 | ovsdb_jsonrpc_server_del_remote(struct shash_node *node) | |
167 | { | |
168 | struct ovsdb_jsonrpc_remote *remote = node->data; | |
169 | ||
170 | ovsdb_jsonrpc_session_close_all(remote); | |
171 | pstream_close(remote->listener); | |
172 | shash_delete(&remote->server->remotes, node); | |
173 | free(remote); | |
f85f8ebb BP |
174 | } |
175 | ||
31d0b6c9 BP |
176 | /* Forces all of the JSON-RPC sessions managed by 'svr' to disconnect and |
177 | * reconnect. */ | |
178 | void | |
179 | ovsdb_jsonrpc_server_reconnect(struct ovsdb_jsonrpc_server *svr) | |
180 | { | |
181 | struct shash_node *node; | |
182 | ||
183 | SHASH_FOR_EACH (node, &svr->remotes) { | |
184 | struct ovsdb_jsonrpc_remote *remote = node->data; | |
185 | ||
186 | ovsdb_jsonrpc_session_reconnect_all(remote); | |
187 | } | |
188 | } | |
189 | ||
f85f8ebb BP |
190 | void |
191 | ovsdb_jsonrpc_server_run(struct ovsdb_jsonrpc_server *svr) | |
192 | { | |
0b1fae1b BP |
193 | struct shash_node *node; |
194 | ||
195 | SHASH_FOR_EACH (node, &svr->remotes) { | |
196 | struct ovsdb_jsonrpc_remote *remote = node->data; | |
197 | ||
198 | if (remote->listener && svr->n_sessions < svr->max_sessions) { | |
199 | struct stream *stream; | |
200 | int error; | |
201 | ||
202 | error = pstream_accept(remote->listener, &stream); | |
203 | if (!error) { | |
204 | struct jsonrpc_session *js; | |
205 | js = jsonrpc_session_open_unreliably(jsonrpc_open(stream)); | |
206 | ovsdb_jsonrpc_session_create(remote, js); | |
207 | } else if (error != EAGAIN) { | |
208 | VLOG_WARN_RL(&rl, "%s: accept failed: %s", | |
209 | pstream_get_name(remote->listener), | |
210 | strerror(error)); | |
211 | } | |
f85f8ebb | 212 | } |
f85f8ebb | 213 | |
0b1fae1b BP |
214 | ovsdb_jsonrpc_session_run_all(remote); |
215 | } | |
f85f8ebb BP |
216 | } |
217 | ||
218 | void | |
219 | ovsdb_jsonrpc_server_wait(struct ovsdb_jsonrpc_server *svr) | |
220 | { | |
0b1fae1b BP |
221 | struct shash_node *node; |
222 | ||
223 | SHASH_FOR_EACH (node, &svr->remotes) { | |
224 | struct ovsdb_jsonrpc_remote *remote = node->data; | |
f85f8ebb | 225 | |
0b1fae1b BP |
226 | if (remote->listener && svr->n_sessions < svr->max_sessions) { |
227 | pstream_wait(remote->listener); | |
f85f8ebb | 228 | } |
f85f8ebb | 229 | |
0b1fae1b BP |
230 | ovsdb_jsonrpc_session_wait_all(remote); |
231 | } | |
f85f8ebb | 232 | } |
b93d3b6c BP |
233 | \f |
234 | /* JSON-RPC database server session. */ | |
f85f8ebb | 235 | |
b93d3b6c | 236 | struct ovsdb_jsonrpc_session { |
0b1fae1b BP |
237 | struct ovsdb_jsonrpc_remote *remote; |
238 | struct list node; /* Element in remote's sessions list. */ | |
f85f8ebb | 239 | |
b93d3b6c BP |
240 | /* Triggers. */ |
241 | struct hmap triggers; /* Hmap of "struct ovsdb_jsonrpc_trigger"s. */ | |
242 | struct list completions; /* Completed triggers. */ | |
f85f8ebb | 243 | |
a8425c53 BP |
244 | /* Monitors. */ |
245 | struct hmap monitors; /* Hmap of "struct ovsdb_jsonrpc_monitor"s. */ | |
246 | ||
4931f33a BP |
247 | /* Network connectivity. */ |
248 | struct jsonrpc_session *js; /* JSON-RPC session. */ | |
249 | unsigned int js_seqno; /* Last jsonrpc_session_get_seqno() value. */ | |
b93d3b6c | 250 | }; |
f85f8ebb | 251 | |
b93d3b6c | 252 | static void ovsdb_jsonrpc_session_close(struct ovsdb_jsonrpc_session *); |
b93d3b6c BP |
253 | static int ovsdb_jsonrpc_session_run(struct ovsdb_jsonrpc_session *); |
254 | static void ovsdb_jsonrpc_session_wait(struct ovsdb_jsonrpc_session *); | |
255 | static void ovsdb_jsonrpc_session_got_request(struct ovsdb_jsonrpc_session *, | |
256 | struct jsonrpc_msg *); | |
257 | static void ovsdb_jsonrpc_session_got_notify(struct ovsdb_jsonrpc_session *, | |
258 | struct jsonrpc_msg *); | |
f85f8ebb | 259 | |
6c2882f9 | 260 | static struct ovsdb_jsonrpc_session * |
0b1fae1b | 261 | ovsdb_jsonrpc_session_create(struct ovsdb_jsonrpc_remote *remote, |
4931f33a | 262 | struct jsonrpc_session *js) |
f85f8ebb BP |
263 | { |
264 | struct ovsdb_jsonrpc_session *s; | |
265 | ||
266 | s = xzalloc(sizeof *s); | |
0b1fae1b BP |
267 | s->remote = remote; |
268 | list_push_back(&remote->sessions, &s->node); | |
f85f8ebb | 269 | hmap_init(&s->triggers); |
a8425c53 | 270 | hmap_init(&s->monitors); |
f85f8ebb | 271 | list_init(&s->completions); |
4931f33a BP |
272 | s->js = js; |
273 | s->js_seqno = jsonrpc_session_get_seqno(js); | |
6c2882f9 | 274 | |
0b1fae1b | 275 | remote->server->n_sessions++; |
6c2882f9 BP |
276 | |
277 | return s; | |
f85f8ebb BP |
278 | } |
279 | ||
6c2882f9 BP |
280 | static void |
281 | ovsdb_jsonrpc_session_close(struct ovsdb_jsonrpc_session *s) | |
282 | { | |
e084f690 | 283 | ovsdb_jsonrpc_monitor_remove_all(s); |
4931f33a | 284 | jsonrpc_session_close(s->js); |
f85f8ebb | 285 | list_remove(&s->node); |
0b1fae1b | 286 | s->remote->server->n_sessions--; |
e084f690 | 287 | free(s); |
f85f8ebb BP |
288 | } |
289 | ||
4931f33a BP |
290 | static int |
291 | ovsdb_jsonrpc_session_run(struct ovsdb_jsonrpc_session *s) | |
6c2882f9 | 292 | { |
4931f33a BP |
293 | jsonrpc_session_run(s->js); |
294 | if (s->js_seqno != jsonrpc_session_get_seqno(s->js)) { | |
295 | s->js_seqno = jsonrpc_session_get_seqno(s->js); | |
b93d3b6c | 296 | ovsdb_jsonrpc_trigger_complete_all(s); |
a8425c53 | 297 | ovsdb_jsonrpc_monitor_remove_all(s); |
6c2882f9 | 298 | } |
6c2882f9 | 299 | |
4931f33a | 300 | ovsdb_jsonrpc_trigger_complete_done(s); |
6c2882f9 | 301 | |
4931f33a BP |
302 | if (!jsonrpc_session_get_backlog(s->js)) { |
303 | struct jsonrpc_msg *msg = jsonrpc_session_recv(s->js); | |
304 | if (msg) { | |
6c2882f9 BP |
305 | if (msg->type == JSONRPC_REQUEST) { |
306 | ovsdb_jsonrpc_session_got_request(s, msg); | |
307 | } else if (msg->type == JSONRPC_NOTIFY) { | |
308 | ovsdb_jsonrpc_session_got_notify(s, msg); | |
309 | } else { | |
310 | VLOG_WARN("%s: received unexpected %s message", | |
4931f33a | 311 | jsonrpc_session_get_name(s->js), |
6c2882f9 | 312 | jsonrpc_msg_type_to_string(msg->type)); |
4931f33a | 313 | jsonrpc_session_force_reconnect(s->js); |
6c2882f9 BP |
314 | jsonrpc_msg_destroy(msg); |
315 | } | |
316 | } | |
6c2882f9 | 317 | } |
4931f33a | 318 | return jsonrpc_session_is_alive(s->js) ? 0 : ETIMEDOUT; |
b93d3b6c | 319 | } |
6c2882f9 | 320 | |
b93d3b6c | 321 | static void |
0b1fae1b | 322 | ovsdb_jsonrpc_session_run_all(struct ovsdb_jsonrpc_remote *remote) |
b93d3b6c BP |
323 | { |
324 | struct ovsdb_jsonrpc_session *s, *next; | |
325 | ||
326 | LIST_FOR_EACH_SAFE (s, next, struct ovsdb_jsonrpc_session, node, | |
0b1fae1b | 327 | &remote->sessions) { |
b93d3b6c BP |
328 | int error = ovsdb_jsonrpc_session_run(s); |
329 | if (error) { | |
330 | ovsdb_jsonrpc_session_close(s); | |
331 | } | |
332 | } | |
6c2882f9 BP |
333 | } |
334 | ||
335 | static void | |
336 | ovsdb_jsonrpc_session_wait(struct ovsdb_jsonrpc_session *s) | |
337 | { | |
4931f33a BP |
338 | jsonrpc_session_wait(s->js); |
339 | if (!jsonrpc_session_get_backlog(s->js)) { | |
340 | jsonrpc_session_recv_wait(s->js); | |
6c2882f9 | 341 | } |
6c2882f9 BP |
342 | } |
343 | ||
b93d3b6c | 344 | static void |
0b1fae1b | 345 | ovsdb_jsonrpc_session_wait_all(struct ovsdb_jsonrpc_remote *remote) |
f85f8ebb | 346 | { |
b93d3b6c | 347 | struct ovsdb_jsonrpc_session *s; |
f85f8ebb | 348 | |
0b1fae1b | 349 | LIST_FOR_EACH (s, struct ovsdb_jsonrpc_session, node, &remote->sessions) { |
b93d3b6c | 350 | ovsdb_jsonrpc_session_wait(s); |
f85f8ebb | 351 | } |
b93d3b6c | 352 | } |
f85f8ebb | 353 | |
0b1fae1b BP |
354 | static void |
355 | ovsdb_jsonrpc_session_close_all(struct ovsdb_jsonrpc_remote *remote) | |
356 | { | |
357 | struct ovsdb_jsonrpc_session *s, *next; | |
358 | ||
359 | LIST_FOR_EACH_SAFE (s, next, struct ovsdb_jsonrpc_session, node, | |
360 | &remote->sessions) { | |
361 | ovsdb_jsonrpc_session_close(s); | |
362 | } | |
363 | } | |
364 | ||
31d0b6c9 BP |
365 | /* Forces all of the JSON-RPC sessions managed by 'remote' to disconnect and |
366 | * reconnect. */ | |
367 | static void | |
368 | ovsdb_jsonrpc_session_reconnect_all(struct ovsdb_jsonrpc_remote *remote) | |
369 | { | |
370 | struct ovsdb_jsonrpc_session *s, *next; | |
371 | ||
372 | LIST_FOR_EACH_SAFE (s, next, struct ovsdb_jsonrpc_session, node, | |
373 | &remote->sessions) { | |
374 | jsonrpc_session_force_reconnect(s->js); | |
375 | if (!jsonrpc_session_is_alive(s->js)) { | |
376 | ovsdb_jsonrpc_session_close(s); | |
377 | } | |
378 | } | |
379 | } | |
380 | ||
9cb53f26 BP |
381 | static const char * |
382 | get_db_name(const struct ovsdb_jsonrpc_session *s) | |
383 | { | |
384 | return s->remote->server->db->schema->name; | |
385 | } | |
386 | ||
387 | static struct jsonrpc_msg * | |
388 | ovsdb_jsonrpc_check_db_name(const struct ovsdb_jsonrpc_session *s, | |
389 | const struct jsonrpc_msg *request) | |
390 | { | |
391 | struct json_array *params; | |
392 | const char *want_db_name; | |
393 | const char *have_db_name; | |
394 | struct ovsdb_error *error; | |
395 | struct jsonrpc_msg *reply; | |
396 | ||
397 | params = json_array(request->params); | |
398 | if (!params->n || params->elems[0]->type != JSON_STRING) { | |
399 | error = ovsdb_syntax_error( | |
400 | request->params, NULL, | |
401 | "%s request params must begin with <db-name>", request->method); | |
402 | goto error; | |
403 | } | |
404 | ||
405 | want_db_name = params->elems[0]->u.string; | |
406 | have_db_name = get_db_name(s); | |
407 | if (strcmp(want_db_name, have_db_name)) { | |
408 | error = ovsdb_syntax_error( | |
409 | request->params, "unknown database", | |
410 | "%s request specifies unknown database %s", | |
411 | request->method, want_db_name); | |
412 | goto error; | |
413 | } | |
414 | ||
415 | return NULL; | |
416 | ||
417 | error: | |
418 | reply = jsonrpc_create_reply(ovsdb_error_to_json(error), request->id); | |
419 | ovsdb_error_destroy(error); | |
420 | return reply; | |
421 | } | |
422 | ||
b93d3b6c BP |
423 | static struct jsonrpc_msg * |
424 | execute_transaction(struct ovsdb_jsonrpc_session *s, | |
425 | struct jsonrpc_msg *request) | |
426 | { | |
427 | ovsdb_jsonrpc_trigger_create(s, request->id, request->params); | |
f85f8ebb BP |
428 | request->id = NULL; |
429 | request->params = NULL; | |
e084f690 | 430 | jsonrpc_msg_destroy(request); |
f85f8ebb BP |
431 | return NULL; |
432 | } | |
433 | ||
434 | static void | |
435 | ovsdb_jsonrpc_session_got_request(struct ovsdb_jsonrpc_session *s, | |
436 | struct jsonrpc_msg *request) | |
437 | { | |
438 | struct jsonrpc_msg *reply; | |
439 | ||
440 | if (!strcmp(request->method, "transact")) { | |
9cb53f26 BP |
441 | reply = ovsdb_jsonrpc_check_db_name(s, request); |
442 | if (!reply) { | |
443 | reply = execute_transaction(s, request); | |
444 | } | |
a8425c53 | 445 | } else if (!strcmp(request->method, "monitor")) { |
9cb53f26 BP |
446 | reply = ovsdb_jsonrpc_check_db_name(s, request); |
447 | if (!reply) { | |
448 | reply = jsonrpc_create_reply( | |
449 | ovsdb_jsonrpc_monitor_create(s, request->params), request->id); | |
450 | } | |
a8425c53 BP |
451 | } else if (!strcmp(request->method, "monitor_cancel")) { |
452 | reply = ovsdb_jsonrpc_monitor_cancel(s, json_array(request->params), | |
453 | request->id); | |
f85f8ebb | 454 | } else if (!strcmp(request->method, "get_schema")) { |
9cb53f26 BP |
455 | reply = ovsdb_jsonrpc_check_db_name(s, request); |
456 | if (!reply) { | |
457 | reply = jsonrpc_create_reply( | |
458 | ovsdb_schema_to_json(s->remote->server->db->schema), | |
459 | request->id); | |
460 | } | |
461 | } else if (!strcmp(request->method, "list_dbs")) { | |
f85f8ebb | 462 | reply = jsonrpc_create_reply( |
9cb53f26 BP |
463 | json_array_create_1(json_string_create(get_db_name(s))), |
464 | request->id); | |
6c2882f9 BP |
465 | } else if (!strcmp(request->method, "echo")) { |
466 | reply = jsonrpc_create_reply(json_clone(request->params), request->id); | |
f85f8ebb BP |
467 | } else { |
468 | reply = jsonrpc_create_error(json_string_create("unknown method"), | |
469 | request->id); | |
470 | } | |
471 | ||
472 | if (reply) { | |
473 | jsonrpc_msg_destroy(request); | |
4931f33a | 474 | jsonrpc_session_send(s->js, reply); |
f85f8ebb BP |
475 | } |
476 | } | |
477 | ||
478 | static void | |
479 | execute_cancel(struct ovsdb_jsonrpc_session *s, struct jsonrpc_msg *request) | |
480 | { | |
6e79e210 BP |
481 | if (json_array(request->params)->n == 1) { |
482 | struct ovsdb_jsonrpc_trigger *t; | |
483 | struct json *id; | |
f85f8ebb | 484 | |
6e79e210 BP |
485 | id = request->params->u.array.elems[0]; |
486 | t = ovsdb_jsonrpc_trigger_find(s, id, json_hash(id, 0)); | |
487 | if (t) { | |
488 | ovsdb_jsonrpc_trigger_complete(t); | |
489 | } | |
f85f8ebb BP |
490 | } |
491 | } | |
492 | ||
493 | static void | |
494 | ovsdb_jsonrpc_session_got_notify(struct ovsdb_jsonrpc_session *s, | |
495 | struct jsonrpc_msg *request) | |
496 | { | |
497 | if (!strcmp(request->method, "cancel")) { | |
498 | execute_cancel(s, request); | |
499 | } | |
500 | jsonrpc_msg_destroy(request); | |
501 | } | |
b93d3b6c BP |
502 | \f |
503 | /* JSON-RPC database server triggers. | |
504 | * | |
505 | * (Every transaction is treated as a trigger even if it doesn't actually have | |
506 | * any "wait" operations.) */ | |
507 | ||
508 | struct ovsdb_jsonrpc_trigger { | |
509 | struct ovsdb_trigger trigger; | |
510 | struct ovsdb_jsonrpc_session *session; | |
511 | struct hmap_node hmap_node; /* In session's "triggers" hmap. */ | |
512 | struct json *id; | |
513 | }; | |
514 | ||
515 | static void | |
516 | ovsdb_jsonrpc_trigger_create(struct ovsdb_jsonrpc_session *s, | |
517 | struct json *id, struct json *params) | |
518 | { | |
519 | struct ovsdb_jsonrpc_trigger *t; | |
520 | size_t hash; | |
521 | ||
522 | /* Check for duplicate ID. */ | |
523 | hash = json_hash(id, 0); | |
524 | t = ovsdb_jsonrpc_trigger_find(s, id, hash); | |
525 | if (t) { | |
4931f33a BP |
526 | struct jsonrpc_msg *msg; |
527 | ||
528 | msg = jsonrpc_create_error(json_string_create("duplicate request ID"), | |
529 | id); | |
530 | jsonrpc_session_send(s->js, msg); | |
b93d3b6c BP |
531 | json_destroy(id); |
532 | json_destroy(params); | |
533 | return; | |
534 | } | |
535 | ||
536 | /* Insert into trigger table. */ | |
537 | t = xmalloc(sizeof *t); | |
0b1fae1b | 538 | ovsdb_trigger_init(s->remote->server->db, |
b93d3b6c BP |
539 | &t->trigger, params, &s->completions, |
540 | time_msec()); | |
541 | t->session = s; | |
542 | t->id = id; | |
543 | hmap_insert(&s->triggers, &t->hmap_node, hash); | |
544 | ||
545 | /* Complete early if possible. */ | |
546 | if (ovsdb_trigger_is_complete(&t->trigger)) { | |
547 | ovsdb_jsonrpc_trigger_complete(t); | |
548 | } | |
549 | } | |
550 | ||
551 | static struct ovsdb_jsonrpc_trigger * | |
552 | ovsdb_jsonrpc_trigger_find(struct ovsdb_jsonrpc_session *s, | |
553 | const struct json *id, size_t hash) | |
554 | { | |
555 | struct ovsdb_jsonrpc_trigger *t; | |
556 | ||
557 | HMAP_FOR_EACH_WITH_HASH (t, struct ovsdb_jsonrpc_trigger, hmap_node, hash, | |
558 | &s->triggers) { | |
559 | if (json_equal(t->id, id)) { | |
560 | return t; | |
561 | } | |
562 | } | |
563 | ||
564 | return NULL; | |
565 | } | |
566 | ||
567 | static void | |
568 | ovsdb_jsonrpc_trigger_complete(struct ovsdb_jsonrpc_trigger *t) | |
569 | { | |
570 | struct ovsdb_jsonrpc_session *s = t->session; | |
571 | ||
4931f33a | 572 | if (jsonrpc_session_is_connected(s->js)) { |
b93d3b6c BP |
573 | struct jsonrpc_msg *reply; |
574 | struct json *result; | |
575 | ||
576 | result = ovsdb_trigger_steal_result(&t->trigger); | |
577 | if (result) { | |
578 | reply = jsonrpc_create_reply(result, t->id); | |
579 | } else { | |
580 | reply = jsonrpc_create_error(json_string_create("canceled"), | |
581 | t->id); | |
582 | } | |
4931f33a | 583 | jsonrpc_session_send(s->js, reply); |
b93d3b6c BP |
584 | } |
585 | ||
586 | json_destroy(t->id); | |
587 | ovsdb_trigger_destroy(&t->trigger); | |
588 | hmap_remove(&s->triggers, &t->hmap_node); | |
589 | free(t); | |
590 | } | |
591 | ||
592 | static void | |
593 | ovsdb_jsonrpc_trigger_complete_all(struct ovsdb_jsonrpc_session *s) | |
594 | { | |
595 | struct ovsdb_jsonrpc_trigger *t, *next; | |
596 | HMAP_FOR_EACH_SAFE (t, next, struct ovsdb_jsonrpc_trigger, hmap_node, | |
597 | &s->triggers) { | |
598 | ovsdb_jsonrpc_trigger_complete(t); | |
599 | } | |
600 | } | |
601 | ||
602 | static void | |
603 | ovsdb_jsonrpc_trigger_complete_done(struct ovsdb_jsonrpc_session *s) | |
604 | { | |
605 | while (!list_is_empty(&s->completions)) { | |
606 | struct ovsdb_jsonrpc_trigger *t | |
607 | = CONTAINER_OF(s->completions.next, | |
608 | struct ovsdb_jsonrpc_trigger, trigger.node); | |
609 | ovsdb_jsonrpc_trigger_complete(t); | |
610 | } | |
611 | } | |
a8425c53 BP |
612 | \f |
613 | /* JSON-RPC database table monitors. */ | |
614 | ||
615 | enum ovsdb_jsonrpc_monitor_selection { | |
616 | OJMS_INITIAL = 1 << 0, /* All rows when monitor is created. */ | |
617 | OJMS_INSERT = 1 << 1, /* New rows. */ | |
618 | OJMS_DELETE = 1 << 2, /* Deleted rows. */ | |
619 | OJMS_MODIFY = 1 << 3 /* Modified rows. */ | |
620 | }; | |
621 | ||
20aa445d BP |
622 | /* A particular column being monitored. */ |
623 | struct ovsdb_jsonrpc_monitor_column { | |
624 | const struct ovsdb_column *column; | |
625 | enum ovsdb_jsonrpc_monitor_selection select; | |
626 | }; | |
627 | ||
628 | /* A particular table being monitored. */ | |
a8425c53 BP |
629 | struct ovsdb_jsonrpc_monitor_table { |
630 | const struct ovsdb_table *table; | |
20aa445d BP |
631 | |
632 | /* This is the union (bitwise-OR) of the 'select' values in all of the | |
633 | * members of 'columns' below. */ | |
a8425c53 | 634 | enum ovsdb_jsonrpc_monitor_selection select; |
20aa445d BP |
635 | |
636 | /* Columns being monitored. */ | |
637 | struct ovsdb_jsonrpc_monitor_column *columns; | |
638 | size_t n_columns; | |
a8425c53 BP |
639 | }; |
640 | ||
20aa445d | 641 | /* A collection of tables being monitored. */ |
a8425c53 BP |
642 | struct ovsdb_jsonrpc_monitor { |
643 | struct ovsdb_replica replica; | |
644 | struct ovsdb_jsonrpc_session *session; | |
645 | struct hmap_node node; /* In ovsdb_jsonrpc_session's "monitors". */ | |
646 | ||
647 | struct json *monitor_id; | |
648 | struct shash tables; /* Holds "struct ovsdb_jsonrpc_monitor_table"s. */ | |
649 | }; | |
650 | ||
651 | static const struct ovsdb_replica_class ovsdb_jsonrpc_replica_class; | |
652 | ||
653 | struct ovsdb_jsonrpc_monitor *ovsdb_jsonrpc_monitor_find( | |
654 | struct ovsdb_jsonrpc_session *, const struct json *monitor_id); | |
655 | static void ovsdb_jsonrpc_monitor_destroy(struct ovsdb_replica *); | |
656 | static struct json *ovsdb_jsonrpc_monitor_get_initial( | |
657 | const struct ovsdb_jsonrpc_monitor *); | |
658 | ||
659 | static bool | |
660 | parse_bool(struct ovsdb_parser *parser, const char *name, bool default_value) | |
661 | { | |
662 | const struct json *json; | |
663 | ||
664 | json = ovsdb_parser_member(parser, name, OP_BOOLEAN | OP_OPTIONAL); | |
665 | return json ? json_boolean(json) : default_value; | |
666 | } | |
667 | ||
668 | struct ovsdb_jsonrpc_monitor * | |
669 | ovsdb_jsonrpc_monitor_find(struct ovsdb_jsonrpc_session *s, | |
670 | const struct json *monitor_id) | |
671 | { | |
672 | struct ovsdb_jsonrpc_monitor *m; | |
673 | ||
674 | HMAP_FOR_EACH_WITH_HASH (m, struct ovsdb_jsonrpc_monitor, node, | |
675 | json_hash(monitor_id, 0), &s->monitors) { | |
676 | if (json_equal(m->monitor_id, monitor_id)) { | |
677 | return m; | |
678 | } | |
679 | } | |
680 | ||
681 | return NULL; | |
682 | } | |
683 | ||
20aa445d BP |
684 | static void |
685 | ovsdb_jsonrpc_add_monitor_column(struct ovsdb_jsonrpc_monitor_table *mt, | |
686 | const struct ovsdb_column *column, | |
687 | enum ovsdb_jsonrpc_monitor_selection select, | |
688 | size_t *allocated_columns) | |
689 | { | |
690 | struct ovsdb_jsonrpc_monitor_column *c; | |
691 | ||
692 | if (mt->n_columns >= *allocated_columns) { | |
693 | mt->columns = x2nrealloc(mt->columns, allocated_columns, | |
694 | sizeof *mt->columns); | |
695 | } | |
696 | ||
697 | c = &mt->columns[mt->n_columns++]; | |
698 | c->column = column; | |
699 | c->select = select; | |
700 | } | |
701 | ||
702 | static int | |
703 | compare_ovsdb_jsonrpc_monitor_column(const void *a_, const void *b_) | |
704 | { | |
705 | const struct ovsdb_jsonrpc_monitor_column *a = a_; | |
706 | const struct ovsdb_jsonrpc_monitor_column *b = b_; | |
707 | ||
708 | return a->column < b->column ? -1 : a->column > b->column; | |
709 | } | |
710 | ||
711 | static struct ovsdb_error * WARN_UNUSED_RESULT | |
712 | ovsdb_jsonrpc_parse_monitor_request(struct ovsdb_jsonrpc_monitor_table *mt, | |
713 | const struct json *monitor_request, | |
714 | size_t *allocated_columns) | |
715 | { | |
716 | const struct ovsdb_table_schema *ts = mt->table->schema; | |
717 | enum ovsdb_jsonrpc_monitor_selection select; | |
718 | const struct json *columns, *select_json; | |
719 | struct ovsdb_parser parser; | |
720 | struct ovsdb_error *error; | |
721 | ||
722 | ovsdb_parser_init(&parser, monitor_request, "table %s", ts->name); | |
723 | columns = ovsdb_parser_member(&parser, "columns", OP_ARRAY | OP_OPTIONAL); | |
724 | select_json = ovsdb_parser_member(&parser, "select", | |
725 | OP_OBJECT | OP_OPTIONAL); | |
726 | error = ovsdb_parser_finish(&parser); | |
727 | if (error) { | |
728 | return error; | |
729 | } | |
730 | ||
731 | if (select_json) { | |
732 | select = 0; | |
733 | ovsdb_parser_init(&parser, select_json, "table %s select", ts->name); | |
734 | if (parse_bool(&parser, "initial", true)) { | |
735 | select |= OJMS_INITIAL; | |
736 | } | |
737 | if (parse_bool(&parser, "insert", true)) { | |
738 | select |= OJMS_INSERT; | |
739 | } | |
740 | if (parse_bool(&parser, "delete", true)) { | |
741 | select |= OJMS_DELETE; | |
742 | } | |
743 | if (parse_bool(&parser, "modify", true)) { | |
744 | select |= OJMS_MODIFY; | |
745 | } | |
746 | error = ovsdb_parser_finish(&parser); | |
747 | if (error) { | |
748 | return error; | |
749 | } | |
750 | } else { | |
751 | select = OJMS_INITIAL | OJMS_INSERT | OJMS_DELETE | OJMS_MODIFY; | |
752 | } | |
753 | mt->select |= select; | |
754 | ||
755 | if (columns) { | |
756 | size_t i; | |
757 | ||
758 | if (columns->type != JSON_ARRAY) { | |
759 | return ovsdb_syntax_error(columns, NULL, | |
760 | "array of column names expected"); | |
761 | } | |
762 | ||
763 | for (i = 0; i < columns->u.array.n; i++) { | |
764 | const struct ovsdb_column *column; | |
765 | const char *s; | |
766 | ||
767 | if (columns->u.array.elems[i]->type != JSON_STRING) { | |
768 | return ovsdb_syntax_error(columns, NULL, | |
769 | "array of column names expected"); | |
770 | } | |
771 | ||
772 | s = columns->u.array.elems[i]->u.string; | |
773 | column = shash_find_data(&mt->table->schema->columns, s); | |
774 | if (!column) { | |
775 | return ovsdb_syntax_error(columns, NULL, "%s is not a valid " | |
776 | "column name", s); | |
777 | } | |
778 | ovsdb_jsonrpc_add_monitor_column(mt, column, select, | |
779 | allocated_columns); | |
780 | } | |
781 | } else { | |
782 | struct shash_node *node; | |
783 | ||
784 | SHASH_FOR_EACH (node, &ts->columns) { | |
785 | const struct ovsdb_column *column = node->data; | |
786 | if (column->index != OVSDB_COL_UUID) { | |
787 | ovsdb_jsonrpc_add_monitor_column(mt, column, select, | |
788 | allocated_columns); | |
789 | } | |
790 | } | |
791 | } | |
792 | ||
793 | return NULL; | |
794 | } | |
795 | ||
a8425c53 BP |
796 | static struct json * |
797 | ovsdb_jsonrpc_monitor_create(struct ovsdb_jsonrpc_session *s, | |
798 | struct json *params) | |
799 | { | |
800 | struct ovsdb_jsonrpc_monitor *m = NULL; | |
801 | struct json *monitor_id, *monitor_requests; | |
802 | struct ovsdb_error *error = NULL; | |
803 | struct shash_node *node; | |
804 | struct json *json; | |
805 | ||
9cb53f26 | 806 | if (json_array(params)->n != 3) { |
a8425c53 BP |
807 | error = ovsdb_syntax_error(params, NULL, "invalid parameters"); |
808 | goto error; | |
809 | } | |
9cb53f26 BP |
810 | monitor_id = params->u.array.elems[1]; |
811 | monitor_requests = params->u.array.elems[2]; | |
a8425c53 BP |
812 | if (monitor_requests->type != JSON_OBJECT) { |
813 | error = ovsdb_syntax_error(monitor_requests, NULL, | |
814 | "monitor-requests must be object"); | |
815 | goto error; | |
816 | } | |
817 | ||
818 | if (ovsdb_jsonrpc_monitor_find(s, monitor_id)) { | |
819 | error = ovsdb_syntax_error(monitor_id, NULL, "duplicate monitor ID"); | |
820 | goto error; | |
821 | } | |
822 | ||
823 | m = xzalloc(sizeof *m); | |
824 | ovsdb_replica_init(&m->replica, &ovsdb_jsonrpc_replica_class); | |
0b1fae1b | 825 | ovsdb_add_replica(s->remote->server->db, &m->replica); |
a8425c53 BP |
826 | m->session = s; |
827 | hmap_insert(&s->monitors, &m->node, json_hash(monitor_id, 0)); | |
828 | m->monitor_id = json_clone(monitor_id); | |
829 | shash_init(&m->tables); | |
830 | ||
831 | SHASH_FOR_EACH (node, json_object(monitor_requests)) { | |
832 | const struct ovsdb_table *table; | |
833 | struct ovsdb_jsonrpc_monitor_table *mt; | |
20aa445d BP |
834 | size_t allocated_columns; |
835 | const struct json *mr_value; | |
836 | size_t i; | |
a8425c53 | 837 | |
0b1fae1b | 838 | table = ovsdb_get_table(s->remote->server->db, node->name); |
a8425c53 BP |
839 | if (!table) { |
840 | error = ovsdb_syntax_error(NULL, NULL, | |
841 | "no table named %s", node->name); | |
842 | goto error; | |
843 | } | |
844 | ||
845 | mt = xzalloc(sizeof *mt); | |
846 | mt->table = table; | |
a8425c53 BP |
847 | shash_add(&m->tables, table->schema->name, mt); |
848 | ||
20aa445d BP |
849 | /* Parse columns. */ |
850 | mr_value = node->data; | |
851 | allocated_columns = 0; | |
852 | if (mr_value->type == JSON_ARRAY) { | |
853 | const struct json_array *array = &mr_value->u.array; | |
854 | ||
855 | for (i = 0; i < array->n; i++) { | |
856 | error = ovsdb_jsonrpc_parse_monitor_request( | |
857 | mt, array->elems[i], &allocated_columns); | |
858 | if (error) { | |
859 | goto error; | |
860 | } | |
a8425c53 BP |
861 | } |
862 | } else { | |
20aa445d BP |
863 | error = ovsdb_jsonrpc_parse_monitor_request( |
864 | mt, mr_value, &allocated_columns); | |
865 | if (error) { | |
866 | goto error; | |
a8425c53 BP |
867 | } |
868 | } | |
869 | ||
20aa445d BP |
870 | /* Check for duplicate columns. */ |
871 | qsort(mt->columns, mt->n_columns, sizeof *mt->columns, | |
872 | compare_ovsdb_jsonrpc_monitor_column); | |
873 | for (i = 1; i < mt->n_columns; i++) { | |
874 | if (mt->columns[i].column == mt->columns[i - 1].column) { | |
875 | error = ovsdb_syntax_error(mr_value, NULL, "column %s " | |
876 | "mentioned more than once", | |
877 | mt->columns[i].column->name); | |
a8425c53 BP |
878 | goto error; |
879 | } | |
880 | } | |
881 | } | |
882 | ||
883 | return ovsdb_jsonrpc_monitor_get_initial(m); | |
884 | ||
885 | error: | |
23f37a97 | 886 | if (m) { |
0b1fae1b | 887 | ovsdb_remove_replica(s->remote->server->db, &m->replica); |
23f37a97 | 888 | } |
a8425c53 BP |
889 | |
890 | json = ovsdb_error_to_json(error); | |
891 | ovsdb_error_destroy(error); | |
892 | return json; | |
893 | } | |
894 | ||
895 | static struct jsonrpc_msg * | |
896 | ovsdb_jsonrpc_monitor_cancel(struct ovsdb_jsonrpc_session *s, | |
897 | struct json_array *params, | |
898 | const struct json *request_id) | |
899 | { | |
900 | if (params->n != 1) { | |
901 | return jsonrpc_create_error(json_string_create("invalid parameters"), | |
902 | request_id); | |
903 | } else { | |
904 | struct ovsdb_jsonrpc_monitor *m; | |
905 | ||
906 | m = ovsdb_jsonrpc_monitor_find(s, params->elems[0]); | |
907 | if (!m) { | |
908 | return jsonrpc_create_error(json_string_create("unknown monitor"), | |
909 | request_id); | |
910 | } else { | |
0b1fae1b | 911 | ovsdb_remove_replica(s->remote->server->db, &m->replica); |
a8425c53 BP |
912 | return jsonrpc_create_reply(json_object_create(), request_id); |
913 | } | |
914 | } | |
915 | } | |
916 | ||
917 | static void | |
918 | ovsdb_jsonrpc_monitor_remove_all(struct ovsdb_jsonrpc_session *s) | |
919 | { | |
920 | struct ovsdb_jsonrpc_monitor *m, *next; | |
921 | ||
922 | HMAP_FOR_EACH_SAFE (m, next, | |
923 | struct ovsdb_jsonrpc_monitor, node, &s->monitors) { | |
0b1fae1b | 924 | ovsdb_remove_replica(s->remote->server->db, &m->replica); |
a8425c53 BP |
925 | } |
926 | } | |
927 | ||
928 | static struct ovsdb_jsonrpc_monitor * | |
929 | ovsdb_jsonrpc_monitor_cast(struct ovsdb_replica *replica) | |
930 | { | |
931 | assert(replica->class == &ovsdb_jsonrpc_replica_class); | |
932 | return CONTAINER_OF(replica, struct ovsdb_jsonrpc_monitor, replica); | |
933 | } | |
934 | ||
935 | struct ovsdb_jsonrpc_monitor_aux { | |
936 | bool initial; /* Sending initial contents of table? */ | |
937 | const struct ovsdb_jsonrpc_monitor *monitor; | |
938 | struct json *json; /* JSON for the whole transaction. */ | |
939 | ||
940 | /* Current table. */ | |
941 | struct ovsdb_jsonrpc_monitor_table *mt; | |
942 | struct json *table_json; /* JSON for table's transaction. */ | |
943 | }; | |
944 | ||
945 | static bool | |
946 | ovsdb_jsonrpc_monitor_change_cb(const struct ovsdb_row *old, | |
947 | const struct ovsdb_row *new, | |
17d18afb | 948 | const unsigned long int *changed, |
a8425c53 BP |
949 | void *aux_) |
950 | { | |
951 | struct ovsdb_jsonrpc_monitor_aux *aux = aux_; | |
952 | const struct ovsdb_jsonrpc_monitor *m = aux->monitor; | |
953 | struct ovsdb_table *table = new ? new->table : old->table; | |
954 | enum ovsdb_jsonrpc_monitor_selection type; | |
955 | struct json *old_json, *new_json; | |
956 | struct json *row_json; | |
957 | char uuid[UUID_LEN + 1]; | |
958 | int n_changed; | |
959 | size_t i; | |
960 | ||
961 | if (!aux->mt || table != aux->mt->table) { | |
962 | aux->mt = shash_find_data(&m->tables, table->schema->name); | |
963 | aux->table_json = NULL; | |
964 | if (!aux->mt) { | |
965 | /* We don't care about rows in this table at all. Tell the caller | |
966 | * to skip it. */ | |
967 | return false; | |
968 | } | |
969 | } | |
970 | ||
971 | type = (aux->initial ? OJMS_INITIAL | |
972 | : !old ? OJMS_INSERT | |
973 | : !new ? OJMS_DELETE | |
974 | : OJMS_MODIFY); | |
975 | if (!(aux->mt->select & type)) { | |
976 | /* We don't care about this type of change (but do want to be called | |
977 | * back for changes to other rows in the same table). */ | |
978 | return true; | |
979 | } | |
980 | ||
981 | old_json = new_json = NULL; | |
982 | n_changed = 0; | |
20aa445d BP |
983 | for (i = 0; i < aux->mt->n_columns; i++) { |
984 | const struct ovsdb_jsonrpc_monitor_column *c = &aux->mt->columns[i]; | |
985 | const struct ovsdb_column *column = c->column; | |
986 | unsigned int idx = c->column->index; | |
17d18afb | 987 | bool column_changed = false; |
a8425c53 | 988 | |
20aa445d BP |
989 | if (!(type & c->select)) { |
990 | /* We don't care about this type of change for this particular | |
991 | * column (but we will care about it for some other column). */ | |
992 | continue; | |
993 | } | |
994 | ||
a8425c53 | 995 | if (type == OJMS_MODIFY) { |
17d18afb BP |
996 | column_changed = bitmap_is_set(changed, idx); |
997 | n_changed += column_changed; | |
a8425c53 | 998 | } |
17d18afb | 999 | if (column_changed || type == OJMS_DELETE) { |
a8425c53 BP |
1000 | if (!old_json) { |
1001 | old_json = json_object_create(); | |
1002 | } | |
1003 | json_object_put(old_json, column->name, | |
1004 | ovsdb_datum_to_json(&old->fields[idx], | |
1005 | &column->type)); | |
1006 | } | |
1007 | if (type & (OJMS_INITIAL | OJMS_INSERT | OJMS_MODIFY)) { | |
1008 | if (!new_json) { | |
1009 | new_json = json_object_create(); | |
1010 | } | |
1011 | json_object_put(new_json, column->name, | |
1012 | ovsdb_datum_to_json(&new->fields[idx], | |
1013 | &column->type)); | |
1014 | } | |
1015 | } | |
1016 | if ((type == OJMS_MODIFY && !n_changed) || (!old_json && !new_json)) { | |
1017 | /* No reportable changes. */ | |
1018 | json_destroy(old_json); | |
1019 | json_destroy(new_json); | |
1020 | return true; | |
1021 | } | |
1022 | ||
1023 | /* Create JSON object for transaction overall. */ | |
1024 | if (!aux->json) { | |
1025 | aux->json = json_object_create(); | |
1026 | } | |
1027 | ||
1028 | /* Create JSON object for transaction on this table. */ | |
1029 | if (!aux->table_json) { | |
1030 | aux->table_json = json_object_create(); | |
1031 | json_object_put(aux->json, aux->mt->table->schema->name, | |
1032 | aux->table_json); | |
1033 | } | |
1034 | ||
1035 | /* Create JSON object for transaction on this row. */ | |
1036 | row_json = json_object_create(); | |
1037 | if (old_json) { | |
1038 | json_object_put(row_json, "old", old_json); | |
1039 | } | |
1040 | if (new_json) { | |
1041 | json_object_put(row_json, "new", new_json); | |
1042 | } | |
1043 | ||
1044 | /* Add JSON row to JSON table. */ | |
1045 | snprintf(uuid, sizeof uuid, | |
1046 | UUID_FMT, UUID_ARGS(ovsdb_row_get_uuid(new ? new : old))); | |
1047 | json_object_put(aux->table_json, uuid, row_json); | |
1048 | ||
1049 | return true; | |
1050 | } | |
1051 | ||
1052 | static void | |
1053 | ovsdb_jsonrpc_monitor_init_aux(struct ovsdb_jsonrpc_monitor_aux *aux, | |
1054 | const struct ovsdb_jsonrpc_monitor *m, | |
1055 | bool initial) | |
1056 | { | |
1057 | aux->initial = initial; | |
1058 | aux->monitor = m; | |
1059 | aux->json = NULL; | |
1060 | aux->mt = NULL; | |
1061 | aux->table_json = NULL; | |
1062 | } | |
1063 | ||
1064 | static struct ovsdb_error * | |
1065 | ovsdb_jsonrpc_monitor_commit(struct ovsdb_replica *replica, | |
c69ee87c BP |
1066 | const struct ovsdb_txn *txn, |
1067 | bool durable OVS_UNUSED) | |
a8425c53 BP |
1068 | { |
1069 | struct ovsdb_jsonrpc_monitor *m = ovsdb_jsonrpc_monitor_cast(replica); | |
1070 | struct ovsdb_jsonrpc_monitor_aux aux; | |
1071 | ||
1072 | ovsdb_jsonrpc_monitor_init_aux(&aux, m, false); | |
1073 | ovsdb_txn_for_each_change(txn, ovsdb_jsonrpc_monitor_change_cb, &aux); | |
1074 | if (aux.json) { | |
1075 | struct jsonrpc_msg *msg; | |
1076 | struct json *params; | |
1077 | ||
1078 | params = json_array_create_2(json_clone(aux.monitor->monitor_id), | |
1079 | aux.json); | |
1080 | msg = jsonrpc_create_notify("update", params); | |
4931f33a | 1081 | jsonrpc_session_send(aux.monitor->session->js, msg); |
a8425c53 BP |
1082 | } |
1083 | ||
1084 | return NULL; | |
1085 | } | |
1086 | ||
1087 | static struct json * | |
1088 | ovsdb_jsonrpc_monitor_get_initial(const struct ovsdb_jsonrpc_monitor *m) | |
1089 | { | |
1090 | struct ovsdb_jsonrpc_monitor_aux aux; | |
1091 | struct shash_node *node; | |
1092 | ||
1093 | ovsdb_jsonrpc_monitor_init_aux(&aux, m, true); | |
1094 | SHASH_FOR_EACH (node, &m->tables) { | |
1095 | struct ovsdb_jsonrpc_monitor_table *mt = node->data; | |
1096 | ||
1097 | if (mt->select & OJMS_INITIAL) { | |
1098 | struct ovsdb_row *row; | |
1099 | ||
1100 | HMAP_FOR_EACH (row, struct ovsdb_row, hmap_node, | |
1101 | &mt->table->rows) { | |
17d18afb | 1102 | ovsdb_jsonrpc_monitor_change_cb(NULL, row, NULL, &aux); |
a8425c53 BP |
1103 | } |
1104 | } | |
1105 | } | |
1106 | return aux.json ? aux.json : json_object_create(); | |
1107 | } | |
1108 | ||
1109 | static void | |
1110 | ovsdb_jsonrpc_monitor_destroy(struct ovsdb_replica *replica) | |
1111 | { | |
1112 | struct ovsdb_jsonrpc_monitor *m = ovsdb_jsonrpc_monitor_cast(replica); | |
1113 | struct shash_node *node; | |
1114 | ||
1115 | json_destroy(m->monitor_id); | |
1116 | SHASH_FOR_EACH (node, &m->tables) { | |
1117 | struct ovsdb_jsonrpc_monitor_table *mt = node->data; | |
20aa445d | 1118 | free(mt->columns); |
a8425c53 BP |
1119 | free(mt); |
1120 | } | |
1121 | shash_destroy(&m->tables); | |
1122 | hmap_remove(&m->session->monitors, &m->node); | |
1123 | free(m); | |
1124 | } | |
1125 | ||
1126 | static const struct ovsdb_replica_class ovsdb_jsonrpc_replica_class = { | |
1127 | ovsdb_jsonrpc_monitor_commit, | |
1128 | ovsdb_jsonrpc_monitor_destroy | |
1129 | }; |