]> git.proxmox.com Git - mirror_ovs.git/blame - ovsdb/trigger.c
stream: Allow timeout configuration for open_block.
[mirror_ovs.git] / ovsdb / trigger.c
CommitLineData
b4e8d170 1/* Copyright (c) 2009, 2010, 2011, 2012 Nicira, Inc.
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 "trigger.h"
19
f85f8ebb 20#include <limits.h>
1b1d2e6d 21#include <string.h>
f85f8ebb 22
53178986 23#include "file.h"
ee89ea7b 24#include "openvswitch/json.h"
f85f8ebb
BP
25#include "jsonrpc.h"
26#include "ovsdb.h"
53178986 27#include "ovsdb-error.h"
fd016ae3 28#include "openvswitch/poll-loop.h"
e317253b 29#include "server.h"
1b1d2e6d
BP
30#include "transaction.h"
31#include "openvswitch/vlog.h"
fd016ae3 32#include "util.h"
f85f8ebb 33
1b1d2e6d 34VLOG_DEFINE_THIS_MODULE(trigger);
53178986 35
e317253b 36static bool ovsdb_trigger_try(struct ovsdb_trigger *, long long int now);
1b1d2e6d
BP
37static void ovsdb_trigger_complete(struct ovsdb_trigger *);
38static void trigger_convert_error(struct ovsdb_trigger *,
39 struct ovsdb_error *);
53178986 40static void trigger_success(struct ovsdb_trigger *, struct json *result);
f85f8ebb 41
53178986 42bool
b4e8d170 43ovsdb_trigger_init(struct ovsdb_session *session, struct ovsdb *db,
e317253b 44 struct ovsdb_trigger *trigger,
53178986
BP
45 struct jsonrpc_msg *request, long long int now,
46 bool read_only, const char *role, const char *id)
f85f8ebb 47{
53178986
BP
48 ovs_assert(!strcmp(request->method, "transact") ||
49 !strcmp(request->method, "convert"));
e317253b 50 trigger->session = session;
b4e8d170 51 trigger->db = db;
417e7e66 52 ovs_list_push_back(&trigger->db->triggers, &trigger->node);
f85f8ebb 53 trigger->request = request;
53178986 54 trigger->reply = NULL;
1b1d2e6d 55 trigger->progress = NULL;
f85f8ebb
BP
56 trigger->created = now;
57 trigger->timeout_msec = LLONG_MAX;
e51879e9 58 trigger->read_only = read_only;
d6db7b3c
LR
59 trigger->role = nullable_xstrdup(role);
60 trigger->id = nullable_xstrdup(id);
53178986 61 return ovsdb_trigger_try(trigger, now);
f85f8ebb
BP
62}
63
64void
65ovsdb_trigger_destroy(struct ovsdb_trigger *trigger)
66{
1b1d2e6d 67 ovsdb_txn_progress_destroy(trigger->progress);
417e7e66 68 ovs_list_remove(&trigger->node);
53178986
BP
69 jsonrpc_msg_destroy(trigger->request);
70 jsonrpc_msg_destroy(trigger->reply);
d6db7b3c
LR
71 free(trigger->role);
72 free(trigger->id);
f85f8ebb
BP
73}
74
75bool
76ovsdb_trigger_is_complete(const struct ovsdb_trigger *trigger)
77{
1b1d2e6d 78 return trigger->reply && !trigger->progress;
f85f8ebb
BP
79}
80
53178986
BP
81struct jsonrpc_msg *
82ovsdb_trigger_steal_reply(struct ovsdb_trigger *trigger)
f85f8ebb 83{
53178986
BP
84 struct jsonrpc_msg *reply = trigger->reply;
85 trigger->reply = NULL;
86 return reply;
f85f8ebb
BP
87}
88
1b1d2e6d
BP
89/* Cancels 'trigger'. 'reason' should be a human-readable reason for log
90 * messages etc. */
f85f8ebb 91void
1b1d2e6d 92ovsdb_trigger_cancel(struct ovsdb_trigger *trigger, const char *reason)
53178986 93{
1b1d2e6d
BP
94 if (trigger->progress) {
95 /* The transaction still might complete asynchronously, but we can stop
96 * tracking it. */
97 ovsdb_txn_progress_destroy(trigger->progress);
98 trigger->progress = NULL;
99 }
100
101 jsonrpc_msg_destroy(trigger->reply);
102 trigger->reply = NULL;
103
53178986 104 if (!strcmp(trigger->request->method, "transact")) {
1b1d2e6d
BP
105 /* There's no place to stick 'reason' into the error reply because RFC
106 * 7047 prescribes a fix form for these messages, see section 4.1.4. */
107 trigger->reply = jsonrpc_create_error(json_string_create("canceled"),
108 trigger->request->id);
109 ovsdb_trigger_complete(trigger);
53178986 110 } else if (!strcmp(trigger->request->method, "convert")) {
1b1d2e6d
BP
111 trigger_convert_error(
112 trigger,
113 ovsdb_error("canceled", "database conversion canceled because %s",
114 reason));
115 }
116}
117
118void
119ovsdb_trigger_prereplace_db(struct ovsdb_trigger *trigger)
120{
121 if (!ovsdb_trigger_is_complete(trigger)) {
122 if (!strcmp(trigger->request->method, "transact")) {
123 ovsdb_trigger_cancel(trigger, "database schema is changing");
124 } else if (!strcmp(trigger->request->method, "convert")) {
125 /* We don't cancel "convert" requests when a database is being
126 * replaced for two reasons. First, we expect the administrator to
127 * do some kind of sensible synchronization on conversion requests,
128 * that is, it only really makes sense for the admin to do a single
129 * conversion at a time at a scheduled point. Second, if we did
130 * then every "convert" request would end up getting canceled since
131 * "convert" itself causes the database to be replaced. */
132 } else {
133 OVS_NOT_REACHED();
134 }
53178986
BP
135 }
136}
137
138bool
f85f8ebb
BP
139ovsdb_trigger_run(struct ovsdb *db, long long int now)
140{
141 struct ovsdb_trigger *t, *next;
f85f8ebb 142
53178986 143 bool run_triggers = db->run_triggers;
f85f8ebb 144 db->run_triggers = false;
53178986
BP
145
146 bool disconnect_all = false;
147
4e8e4213 148 LIST_FOR_EACH_SAFE (t, next, node, &db->triggers) {
1b1d2e6d
BP
149 if (run_triggers
150 || now - t->created >= t->timeout_msec
151 || t->progress) {
53178986
BP
152 if (ovsdb_trigger_try(t, now)) {
153 disconnect_all = true;
154 }
f85f8ebb
BP
155 }
156 }
53178986 157 return disconnect_all;
f85f8ebb
BP
158}
159
160void
161ovsdb_trigger_wait(struct ovsdb *db, long long int now)
162{
163 if (db->run_triggers) {
164 poll_immediate_wake();
165 } else {
166 long long int deadline = LLONG_MAX;
167 struct ovsdb_trigger *t;
168
4e8e4213 169 LIST_FOR_EACH (t, node, &db->triggers) {
f85f8ebb
BP
170 if (t->created < LLONG_MAX - t->timeout_msec) {
171 long long int t_deadline = t->created + t->timeout_msec;
172 if (deadline > t_deadline) {
173 deadline = t_deadline;
174 if (now >= deadline) {
175 break;
176 }
177 }
178 }
179 }
180
181 if (deadline < LLONG_MAX) {
7cf8b266 182 poll_timer_wait_until(deadline);
f85f8ebb
BP
183 }
184 }
185}
186
187static bool
e317253b 188ovsdb_trigger_try(struct ovsdb_trigger *t, long long int now)
f85f8ebb 189{
1b1d2e6d
BP
190 /* Handle "initialized" state. */
191 if (!t->reply) {
192 ovs_assert(!t->progress);
193
194 struct ovsdb_txn *txn = NULL;
195 struct ovsdb *newdb = NULL;
196 if (!strcmp(t->request->method, "transact")) {
197 bool durable;
198
199 struct json *result;
200 txn = ovsdb_execute_compose(
201 t->db, t->session, t->request->params, t->read_only,
202 t->role, t->id, now - t->created, &t->timeout_msec,
203 &durable, &result);
204 if (!txn) {
205 if (result) {
206 /* Complete. There was an error but we still represent it
207 * in JSON-RPC as a successful result. */
208 trigger_success(t, result);
209 } else {
210 /* Unsatisfied "wait" condition. Take no action now, retry
211 * later. */
212 }
213 return false;
214 }
215
216 /* Transition to "committing" state. */
217 t->reply = jsonrpc_create_reply(result, t->request->id);
218 t->progress = ovsdb_txn_propose_commit(txn, durable);
219 } else if (!strcmp(t->request->method, "convert")) {
220 /* Permission check. */
221 if (t->role && *t->role) {
222 trigger_convert_error(
223 t, ovsdb_perm_error(
224 "RBAC rules for client \"%s\" role \"%s\" prohibit "
225 "\"convert\" of database %s "
226 "(only the root role may convert databases)",
227 t->id, t->role, t->db->schema->name));
228 return false;
229 }
230
231 /* Validate parameters. */
232 const struct json *params = t->request->params;
fa37affa 233 if (params->type != JSON_ARRAY || params->array.n != 2) {
1b1d2e6d
BP
234 trigger_convert_error(t, ovsdb_syntax_error(params, NULL,
235 "array expected"));
236 return false;
237 }
238
239 /* Parse new schema and make a converted copy. */
fa37affa 240 const struct json *new_schema_json = params->array.elems[1];
1b1d2e6d
BP
241 struct ovsdb_schema *new_schema;
242 struct ovsdb_error *error
243 = ovsdb_schema_from_json(new_schema_json, &new_schema);
244 if (!error && strcmp(new_schema->name, t->db->schema->name)) {
245 error = ovsdb_error("invalid parameters",
246 "new schema name (%s) does not match "
247 "database name (%s)",
248 new_schema->name, t->db->schema->name);
249 }
250 if (!error) {
251 error = ovsdb_convert(t->db, new_schema, &newdb);
252 }
253 if (error) {
254 ovsdb_schema_destroy(new_schema);
255 trigger_convert_error(t, error);
256 return false;
257 }
258
259 /* Make the new copy into a transaction log record. */
260 struct json *txn_json = ovsdb_to_txn_json(
261 newdb, "converted by ovsdb-server");
262
263 /* Propose the change. */
264 t->progress = ovsdb_txn_propose_schema_change(
265 t->db, new_schema_json, txn_json);
266 json_destroy(txn_json);
267 t->reply = jsonrpc_create_reply(json_object_create(),
268 t->request->id);
269 } else {
270 OVS_NOT_REACHED();
53178986
BP
271 }
272
1b1d2e6d
BP
273 /* If the transaction committed synchronously, complete it and
274 * transition to "complete". This is more than an optimization because
275 * the file-based storage isn't implemented to read back the
276 * transactions that we write (which is an ugly broken abstraction but
277 * it's what we have). */
278 if (ovsdb_txn_progress_is_complete(t->progress)
279 && !ovsdb_txn_progress_get_error(t->progress)) {
280 if (txn) {
281 ovsdb_txn_complete(txn);
282 }
283 ovsdb_txn_progress_destroy(t->progress);
284 t->progress = NULL;
285 ovsdb_trigger_complete(t);
286 if (newdb) {
287 ovsdb_replace(t->db, newdb);
288 return true;
289 }
53178986
BP
290 return false;
291 }
1b1d2e6d 292 ovsdb_destroy(newdb);
53178986 293
1b1d2e6d
BP
294 /* Fall through to the general handling for the "committing" state. We
295 * abort the transaction--if and when it eventually commits, we'll read
296 * it back from storage and replay it locally. */
297 if (txn) {
298 ovsdb_txn_abort(txn);
53178986 299 }
1b1d2e6d
BP
300 }
301
302 /* Handle "committing" state. */
303 if (t->progress) {
304 if (!ovsdb_txn_progress_is_complete(t->progress)) {
305 return false;
53178986 306 }
1b1d2e6d
BP
307
308 /* Transition to "complete". */
309 struct ovsdb_error *error
310 = ovsdb_error_clone(ovsdb_txn_progress_get_error(t->progress));
311 ovsdb_txn_progress_destroy(t->progress);
312 t->progress = NULL;
313
53178986 314 if (error) {
1b1d2e6d
BP
315 if (!strcmp(ovsdb_error_get_tag(error), "cluster error")) {
316 /* Temporary error. Transition back to "initialized" state to
317 * try again. */
318 jsonrpc_msg_destroy(t->reply);
319 t->reply = NULL;
320 t->db->run_triggers = true;
321 ovsdb_error_destroy(error);
322 } else {
323 /* Permanent error. Transition to "completed" state to report
324 * it. */
325 if (!strcmp(t->request->method, "transact")) {
326 json_array_add(t->reply->result,
327 ovsdb_error_to_json_free(error));
328 ovsdb_trigger_complete(t);
329 } else if (!strcmp(t->request->method, "convert")) {
330 jsonrpc_msg_destroy(t->reply);
331 t->reply = NULL;
332 trigger_convert_error(t, error);
333 }
334 }
335 } else {
336 /* Success. */
337 ovsdb_trigger_complete(t);
53178986
BP
338 }
339
1b1d2e6d 340 return false;
f85f8ebb 341 }
1b1d2e6d
BP
342
343 OVS_NOT_REACHED();
f85f8ebb
BP
344}
345
346static void
1b1d2e6d 347ovsdb_trigger_complete(struct ovsdb_trigger *t)
f85f8ebb 348{
1b1d2e6d 349 ovs_assert(t->reply);
417e7e66
BW
350 ovs_list_remove(&t->node);
351 ovs_list_push_back(&t->session->completions, &t->node);
f85f8ebb 352}
53178986 353
1b1d2e6d
BP
354/* Makes a "convert" request into an error.
355 *
356 * This is not suitable for "transact" requests because their replies should
357 * never be bare ovsdb_errors: RFC 7047 says that their replies must either be
358 * a JSON-RPC reply that contains an array of operation replies (which can be
359 * errors), or a JSON-RPC error whose "error" member is simply "canceled". */
53178986 360static void
1b1d2e6d 361trigger_convert_error(struct ovsdb_trigger *t, struct ovsdb_error *error)
53178986 362{
1b1d2e6d
BP
363 ovs_assert(!strcmp(t->request->method, "convert"));
364 ovs_assert(error && !t->reply);
365 t->reply = jsonrpc_create_error(
53178986 366 ovsdb_error_to_json_free(error), t->request->id);
1b1d2e6d 367 ovsdb_trigger_complete(t);
53178986
BP
368}
369
370static void
371trigger_success(struct ovsdb_trigger *t, struct json *result)
372{
1b1d2e6d
BP
373 ovs_assert(result && !t->reply);
374 t->reply = jsonrpc_create_reply(result, t->request->id);
375 ovsdb_trigger_complete(t);
53178986 376}