4 Copyright (C) Martin Schwenke 2019
6 Parts based on ctdb.c, event_tool.c:
8 Copyright (C) Amitay Isaacs 2015, 2018
10 This program is free software; you can redistribute it and/or modify
11 it under the terms of the GNU General Public License as published by
12 the Free Software Foundation; either version 3 of the License, or
13 (at your option) any later version.
15 This program is distributed in the hope that it will be useful,
16 but WITHOUT ANY WARRANTY; without even the implied warranty of
17 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 GNU General Public License for more details.
20 You should have received a copy of the GNU General Public License
21 along with this program; if not, see <http://www.gnu.org/licenses/>.
25 #include "system/filesys.h"
26 #include "system/network.h"
27 #include "system/time.h"
34 #include "lib/util/debug.h"
35 #include "lib/util/sys_rw.h"
36 #include "lib/util/util.h"
37 #include "lib/util/smb_strtox.h"
38 #include "lib/tdb_wrap/tdb_wrap.h"
40 #include "common/cmdline.h"
41 #include "common/logging.h"
42 #include "common/path.h"
43 #include "common/event_script.h"
44 #include "common/system_socket.h"
46 #include "protocol/protocol.h"
47 #include "protocol/protocol_api.h"
48 #include "protocol/protocol_util.h"
50 #include "client/client.h"
51 #include "client/client_sync.h"
53 struct tdb_context *client_db_tdb(struct ctdb_db_context *db);
55 #define TIMEOUT() tevent_timeval_current_ofs(ctx->timelimit, 0)
57 struct db_test_tool_context {
58 struct cmdline_context *cmdline;
59 struct tevent_context *ev;
60 struct ctdb_client_context *client;
66 * If this is ever consolodated into a larger test tool then these
67 * forward declarations can be moved to an include file
69 int db_test_tool_init(TALLOC_CTX *mem_ctx,
71 struct poptOption *options,
75 struct db_test_tool_context **result);
76 int db_test_tool_run(struct db_test_tool_context *ctx, int *result);
78 static int db_test_get_lmaster(TALLOC_CTX *mem_ctx,
83 struct db_test_tool_context *ctx = talloc_get_type_abort(
84 private_data, struct db_test_tool_context);
85 struct ctdb_vnn_map *vnnmap;
87 uint32_t idx, lmaster;
92 cmdline_usage(ctx->cmdline, "get-lmaster");
96 ret = ctdb_ctrl_getvnnmap(mem_ctx,
103 D_ERR("Control GETVNN_MAP failed, ret=%d\n", ret);
107 key.dsize = strlen(argv[0]);
108 key.dptr = (uint8_t *)discard_const(argv[0]);
110 hash = tdb_jenkins_hash(&key);
111 idx = hash % vnnmap->size;
112 lmaster = vnnmap->map[idx];
114 printf("%"PRId32"\n", lmaster);
119 static struct ctdb_dbid *db_find(TALLOC_CTX *mem_ctx,
120 struct db_test_tool_context *ctx,
121 struct ctdb_dbid_map *dbmap,
124 struct ctdb_dbid *db = NULL;
129 for (i=0; i<dbmap->num; i++) {
130 ret = ctdb_ctrl_get_dbname(mem_ctx,
141 if (strcmp(db_name, name) == 0) {
142 talloc_free(discard_const(name));
151 static bool db_exists(TALLOC_CTX *mem_ctx,
152 struct db_test_tool_context *ctx,
155 const char **db_name,
158 struct ctdb_dbid_map *dbmap;
159 struct ctdb_dbid *db = NULL;
161 const char *name = NULL;
165 ret = ctdb_ctrl_get_dbmap(mem_ctx,
175 if (strncmp(db_arg, "0x", 2) == 0) {
176 id = smb_strtoul(db_arg, NULL, 0, &ret, SMB_STR_STANDARD);
180 for (i=0; i<dbmap->num; i++) {
181 if (id == dbmap->dbs[i].db_id) {
188 db = db_find(mem_ctx, ctx, dbmap, name);
192 fprintf(stderr, "No database matching '%s' found\n", db_arg);
197 ret = ctdb_ctrl_get_dbname(mem_ctx,
212 if (db_name != NULL) {
213 *db_name = talloc_strdup(mem_ctx, name);
215 if (db_flags != NULL) {
216 *db_flags = db->flags;
221 static int db_test_fetch_local_delete(TALLOC_CTX *mem_ctx,
226 struct db_test_tool_context *ctx = talloc_get_type_abort(
227 private_data, struct db_test_tool_context);
228 struct ctdb_db_context *db = NULL;
229 struct ctdb_record_handle *h = NULL;
230 struct tdb_context *tdb;
231 struct ctdb_ltdb_header header;
242 cmdline_usage(ctx->cmdline, "fetch-local-delete");
246 if (! db_exists(mem_ctx, ctx, argv[0], &db_id, &db_name, &db_flags)) {
250 if (db_flags & (CTDB_DB_FLAGS_PERSISTENT | CTDB_DB_FLAGS_REPLICATED)) {
251 D_ERR("DB %s is not a volatile database\n", db_name);
255 ret = ctdb_attach(ctx->ev,
262 D_ERR("Failed to attach to DB %s\n", db_name);
266 key.dsize = strlen(argv[1]);
267 key.dptr = (uint8_t *)discard_const(argv[1]);
269 ret = ctdb_fetch_lock(mem_ctx,
279 D_ERR("Failed to fetch record for key %s\n", argv[1]);
283 len = ctdb_ltdb_header_len(&header);
284 buf = talloc_size(mem_ctx, len);
286 D_ERR("Memory allocation error\n");
291 ctdb_ltdb_header_push(&header, buf, &np);
296 tdb = client_db_tdb(db);
298 ret = tdb_store(tdb, key, data, TDB_REPLACE);
301 D_ERR("fetch_lock delete: %s tdb_store failed, %s\n",
312 #define ISASCII(x) (isprint(x) && ! strchr("\"\\", (x)))
314 static void dump(const char *name, uint8_t *dptr, size_t dsize)
318 fprintf(stdout, "%s(%zu) = \"", name, dsize);
319 for (i = 0; i < dsize; i++) {
320 if (ISASCII(dptr[i])) {
321 fprintf(stdout, "%c", dptr[i]);
323 fprintf(stdout, "\\%02X", dptr[i]);
326 fprintf(stdout, "\"\n");
329 static void dump_ltdb_header(struct ctdb_ltdb_header *header)
331 fprintf(stdout, "dmaster: %u\n", header->dmaster);
332 fprintf(stdout, "rsn: %" PRIu64 "\n", header->rsn);
333 fprintf(stdout, "flags: 0x%08x", header->flags);
334 if (header->flags & CTDB_REC_FLAG_MIGRATED_WITH_DATA) {
335 fprintf(stdout, " MIGRATED_WITH_DATA");
337 if (header->flags & CTDB_REC_FLAG_VACUUM_MIGRATED) {
338 fprintf(stdout, " VACUUM_MIGRATED");
340 if (header->flags & CTDB_REC_FLAG_AUTOMATIC) {
341 fprintf(stdout, " AUTOMATIC");
343 if (header->flags & CTDB_REC_RO_HAVE_DELEGATIONS) {
344 fprintf(stdout, " RO_HAVE_DELEGATIONS");
346 if (header->flags & CTDB_REC_RO_HAVE_READONLY) {
347 fprintf(stdout, " RO_HAVE_READONLY");
349 if (header->flags & CTDB_REC_RO_REVOKING_READONLY) {
350 fprintf(stdout, " RO_REVOKING_READONLY");
352 if (header->flags & CTDB_REC_RO_REVOKE_COMPLETE) {
353 fprintf(stdout, " RO_REVOKE_COMPLETE");
355 fprintf(stdout, "\n");
359 static int db_test_local_lock(TALLOC_CTX *mem_ctx,
364 struct db_test_tool_context *ctx = talloc_get_type_abort(
365 private_data, struct db_test_tool_context);
366 struct ctdb_db_context *db;
376 cmdline_usage(ctx->cmdline, "local-lock");
381 if (! db_exists(mem_ctx, ctx, argv[0], &db_id, &db_name, &db_flags)) {
382 D_ERR("DB %s not attached\n", db_name);
386 if (db_flags & (CTDB_DB_FLAGS_PERSISTENT | CTDB_DB_FLAGS_REPLICATED)) {
387 D_ERR("DB %s is not a volatile database\n", db_name);
391 ret = ctdb_attach(ctx->ev,
398 D_ERR("Failed to attach to DB %s\n", db_name);
404 DBG_ERR("Failed to create pipe\n");
410 DBG_ERR("Failed to fork()\n");
420 nread = sys_read(pipefd[0], &status, sizeof(status));
421 if (nread < 0 || (size_t)nread != sizeof(status)) {
426 printf("OK %d\n", pid);
428 printf("FAIL %d\n", status);
437 key.dsize = strlen(argv[1]);
438 key.dptr = (uint8_t *)discard_const(argv[1]);
440 ret = tdb_chainlock(client_db_tdb(db), key);
442 D_ERR("Failed to lock chain for key %s\n", argv[1]);
446 sys_write(pipefd[1], &ret, sizeof(ret));
452 /* Hold the lock- the caller should SIGTERM to release the lock */
457 sys_write(pipefd[1], &ret, sizeof(ret));
461 static int db_test_local_read(TALLOC_CTX *mem_ctx,
466 struct db_test_tool_context *ctx = talloc_get_type_abort(
467 private_data, struct db_test_tool_context);
468 struct ctdb_db_context *db;
469 struct ctdb_ltdb_header header;
478 cmdline_usage(ctx->cmdline, "local-read");
482 if (! db_exists(mem_ctx, ctx, argv[0], &db_id, &db_name, &db_flags)) {
486 if (db_flags & (CTDB_DB_FLAGS_PERSISTENT | CTDB_DB_FLAGS_REPLICATED)) {
487 D_ERR("DB %s is not a volatile database\n", db_name);
491 ret = ctdb_attach(ctx->ev,
498 D_ERR("Failed to attach to DB %s\n", db_name);
502 key.dsize = strlen(argv[1]);
503 key.dptr = (uint8_t *)discard_const(argv[1]);
505 data = tdb_fetch(client_db_tdb(db), key);
507 if (data.dptr == NULL) {
508 D_ERR("No record for key %s\n", argv[1]);
512 if (data.dsize < sizeof(struct ctdb_ltdb_header)) {
513 D_ERR("Invalid record for key %s\n", argv[1]);
518 ret = ctdb_ltdb_header_pull(data.dptr, data.dsize, &header, &np);
520 D_ERR("Failed to parse header from data\n");
525 dump_ltdb_header(&header);
526 dump("data", data.dptr + np, data.dsize - np);
533 static int db_test_vacuum(TALLOC_CTX *mem_ctx,
538 struct db_test_tool_context *ctx = talloc_get_type_abort(
539 private_data, struct db_test_tool_context);
540 struct ctdb_db_vacuum db_vacuum;
541 struct ctdb_req_control request;
542 struct ctdb_reply_control *reply;
549 if (argc != 1 && argc != 2) {
550 cmdline_usage(ctx->cmdline, "vacuum");
556 db_vacuum.full_vacuum_run = false;
558 if (strcmp(argv[1], "full") == 0) {
559 db_vacuum.full_vacuum_run = true;
561 cmdline_usage(ctx->cmdline, "vacuum");
566 if (! db_exists(mem_ctx, ctx, db_arg, &db_id, &db_name, &db_flags)) {
570 if (db_flags & (CTDB_DB_FLAGS_PERSISTENT | CTDB_DB_FLAGS_REPLICATED)) {
571 D_ERR("DB %s is not a volatile database\n", db_name);
575 db_vacuum.db_id = db_id;
577 ctdb_req_control_db_vacuum(&request, &db_vacuum);
579 ret = ctdb_client_control(mem_ctx,
587 D_ERR("Control DB_VACUUM failed to node %u, ret=%d\n",
594 ret = ctdb_reply_control_db_vacuum(reply);
596 D_ERR("Control DB_VACUUM failed, ret=%d\n", ret);
603 struct cmdline_command db_test_commands[] = {
605 .name = "get-lmaster",
606 .fn = db_test_get_lmaster,
607 .msg_help = "Print lmaster for key",
611 .name = "fetch-local-delete",
612 .fn = db_test_fetch_local_delete,
613 .msg_help = "Fetch record and delete from local database",
614 .msg_args = "<dbname|dbid> <key>"
617 .name = "local-lock",
618 .fn = db_test_local_lock,
619 .msg_help = "Lock a record in a local database",
620 .msg_args = "<dbname|dbid> <key>"
623 .name = "local-read",
624 .fn = db_test_local_read,
625 .msg_help = "Read a record from local database",
626 .msg_args = "<dbname|dbid> <key>"
630 .fn = db_test_vacuum,
631 .msg_help = "Vacuum a database",
632 .msg_args = "<dbname|dbid> [full]"
637 int db_test_tool_init(TALLOC_CTX *mem_ctx,
639 struct poptOption *options,
643 struct db_test_tool_context **result)
645 struct db_test_tool_context *ctx;
648 ctx = talloc_zero(mem_ctx, struct db_test_tool_context);
650 D_ERR("Memory allocation error\n");
654 ret = cmdline_init(mem_ctx,
661 D_ERR("Failed to initialize cmdline, ret=%d\n", ret);
666 ret = cmdline_parse(ctx->cmdline, argc, argv, parse_options);
668 cmdline_usage(ctx->cmdline, NULL);
677 int db_test_tool_run(struct db_test_tool_context *ctx, int *result)
682 ctx->ev = tevent_context_init(ctx);
683 if (ctx->ev == NULL) {
684 D_ERR("Failed to initialize tevent\n");
688 ctdb_socket = path_socket(ctx, "ctdbd");
689 if (ctdb_socket == NULL) {
690 fprintf(stderr, "Memory allocation error\n");
694 ret = ctdb_client_init(ctx, ctx->ev, ctdb_socket, &ctx->client);
696 D_ERR("Failed to connect to CTDB daemon (%s)\n", ctdb_socket);
700 ret = cmdline_run(ctx->cmdline, ctx, result);
704 #ifdef CTDB_DB_TEST_TOOL
712 .destnode = CTDB_CURRENT_NODE,
716 struct poptOption db_test_options[] = {
720 .argInfo = POPT_ARG_STRING,
721 .arg = &db_test_data.debug,
723 .descrip = "debug level",
724 .argDescrip = "ERROR|WARNING|NOTICE|INFO|DEBUG"
729 .argInfo = POPT_ARG_INT,
730 .arg = &db_test_data.destnode,
732 .descrip = "node number",
736 .longName = "timelimit",
738 .argInfo = POPT_ARG_INT,
739 .arg = &db_test_data.timelimit,
741 .descrip = "control time limit",
742 .argDescrip = "SECONDS"
747 int main(int argc, const char **argv)
750 struct db_test_tool_context *ctx;
755 mem_ctx = talloc_new(NULL);
756 if (mem_ctx == NULL) {
757 fprintf(stderr, "Memory allocation error\n");
761 ret = db_test_tool_init(mem_ctx,
769 talloc_free(mem_ctx);
773 setup_logging("ctdb-db-test", DEBUG_STDERR);
774 ok = debug_level_parse(db_test_data.debug, &level);
778 debuglevel_set(level);
780 ctx->destnode = db_test_data.destnode;
781 ctx->timelimit = db_test_data.timelimit;
783 ret = db_test_tool_run(ctx, &result);
788 talloc_free(mem_ctx);
792 #endif /* CTDB_DB_TEST_TOOL */