main.cpp 16.9 KB
Newer Older
Q
qinzuoyan 已提交
1 2 3 4 5
// Copyright (c) 2017, Xiaomi, Inc.  All rights reserved.
// This source code is licensed under the Apache License Version 2.0, which
// can be found in the LICENSE file in the root directory of this source tree.

#include <pegasus/version.h>
6
#include <dsn/utility/strings.h>
Q
qinzuoyan 已提交
7 8 9 10 11 12 13
#include <setjmp.h>
#include <signal.h>
#include <algorithm>
#include "args.h"
#include "command_executor.h"
#include "commands.h"

14 15 16 17 18 19
std::string g_last_history;
const int s_max_params_count = 10000;
std::map<std::string, command_executor *> s_commands_map;
shell_context s_global_context;
size_t s_max_name_length = 0;
size_t s_option_width = 70;
Q
qinzuoyan 已提交
20

21 22 23 24 25 26 27
void print_help();
bool help_info(command_executor *e, shell_context *sc, arguments args)
{
    print_help();
    return true;
}

Q
qinzuoyan 已提交
28
command_executor commands[] = {
29 30 31
    {
        "help", "print help info", "", help_info,
    },
Q
qinzuoyan 已提交
32 33 34 35 36 37 38 39 40
    {
        "version", "get the shell version", "", version,
    },
    {
        "cluster_info", "get the informations for the cluster", "", query_cluster_info,
    },
    {
        "app",
        "get the partition information for some specific app",
41
        "<app_name> [-d|--detailed] [-o|--output file_name]",
Q
qinzuoyan 已提交
42 43 44 45 46
        query_app,
    },
    {
        "app_disk",
        "get the disk usage information for some specific app",
47
        "<app_name> [-d|--detailed] [-o|--output file_name]",
Q
qinzuoyan 已提交
48 49 50 51 52
        app_disk,
    },
    {
        "ls",
        "list all apps",
53 54
        "[-a|-all] [-d|--detailed] [-o|--output file_name] "
        "[-s|--status all|available|creating|dropping|dropped]",
Q
qinzuoyan 已提交
55 56 57 58 59
        ls_apps,
    },
    {
        "nodes",
        "get the node status for this cluster",
60
        "[-d|--detailed] [-o|--output file_name] [-s|--status all|alive|unalive]",
Q
qinzuoyan 已提交
61 62 63 64 65
        ls_nodes,
    },
    {
        "create",
        "create an app",
66 67
        "<app_name> [-p|--partition_count num] [-r|--replica_count num] "
        "[-e|--envs k1=v1,k2=v2...]",
Q
qinzuoyan 已提交
68 69 70
        create_app,
    },
    {
71
        "drop", "drop an app", "<app_name> [-r|--reserve_seconds num]", drop_app,
Q
qinzuoyan 已提交
72 73 74 75 76 77 78
    },
    {
        "recall", "recall an app", "<app_id> [new_app_name]", recall_app,
    },
    {
        "set_meta_level",
        "set the meta function level: stopped, blind, freezed, steady, lively",
79
        "<stopped|blind|freezed|steady|lively>",
Q
qinzuoyan 已提交
80 81 82 83 84 85 86 87
        set_meta_level,
    },
    {
        "get_meta_level", "get the meta function level", "", get_meta_level,
    },
    {
        "balance",
        "send explicit balancer request for the cluster",
88 89
        "<-g|--gpid appid.pidx> <-p|--type move_pri|copy_pri|copy_sec> <-f|--from from_address> "
        "<-t|--to to_address>",
Q
qinzuoyan 已提交
90 91 92 93 94 95
        balance,
    },
    {
        "propose",
        "send configuration proposals to cluster",
        "[-f|--force] "
96 97
        "<-g|--gpid appid.pidx> <-p|--type ASSIGN_PRIMARY|ADD_SECONDARY|DOWNGRADE_TO_INACTIVE...> "
        "<-t|--target node_to_exec_command> <-n|--node node_to_be_affected> ",
Q
qinzuoyan 已提交
98 99 100 101 102 103 104 105
        propose,
    },
    {
        "use",
        "set the current app used for the data access commands",
        "[app_name]",
        use_app_as_current,
    },
Q
qinzuoyan 已提交
106 107 108 109 110 111
    {
        "escape_all",
        "if escape all characters when printing key/value bytes",
        "[true|false]",
        process_escape_all,
    },
112 113 114 115 116 117
    {
        "timeout",
        "default timeout in milliseconds for read/write operations",
        "[time_in_ms]",
        process_timeout,
    },
Q
qinzuoyan 已提交
118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
    {
        "hash",
        "calculate the hash result for some hash key",
        "<hash_key> <sort_key>",
        calculate_hash_value,
    },
    {
        "set", "set value", "<hash_key> <sort_key> <value> [ttl_in_seconds]", data_operations,
    },
    {
        "multi_set",
        "set multiple values for a single hash key",
        "<hash_key> <sort_key> <value> [sort_key value...]",
        data_operations,
    },
    {
        "get", "get value", "<hash_key> <sort_key>", data_operations,
    },
    {
        "multi_get",
        "get multiple values for a single hash key",
        "<hash_key> [sort_key...]",
        data_operations,
    },
142 143 144 145
    {
        "multi_get_range",
        "get multiple values under sort key range for a single hash key",
        "<hash_key> <start_sort_key> <stop_sort_key> "
146 147 148 149
        "[-a|--start_inclusive true|false] [-b|--stop_inclusive true|false] "
        "[-s|--sort_key_filter_type anywhere|prefix|postfix] "
        "[-y|--sort_key_filter_pattern str] "
        "[-n|--max_count num] [-i|--no_value] [-r|--reverse]",
150 151
        data_operations,
    },
Q
qinzuoyan 已提交
152 153 154 155 156 157 158
    {
        "multi_get_sortkeys",
        "get multiple sort keys for a single hash key",
        "<hash_key>",
        data_operations,
    },
    {
159
        "del", "delete a key", "<hash_key> <sort_key>", data_operations,
Q
qinzuoyan 已提交
160 161 162 163 164 165 166
    },
    {
        "multi_del",
        "delete multiple values for a single hash key",
        "<hash_key> <sort_key> [sort_key...]",
        data_operations,
    },
167 168 169 170
    {
        "multi_del_range",
        "delete multiple values under sort key range for a single hash key",
        "<hash_key> <start_sort_key> <stop_sort_key> "
171 172 173 174
        "[-a|--start_inclusive true|false] [-b|--stop_inclusive true|false] "
        "[-s|--sort_key_filter_type anywhere|prefix|postfix] "
        "[-y|--sort_key_filter_pattern str] "
        "[-o|--output file_name] [-i|--silent]",
175 176
        data_operations,
    },
Q
qinzuoyan 已提交
177 178 179 180 181 182 183 184 185 186
    {
        "exist", "check value exist", "<hash_key> <sort_key>", data_operations,
    },
    {
        "count", "get sort key count for a single hash key", "<hash_key>", data_operations,
    },
    {
        "ttl", "query ttl for a specific key", "<hash_key> <sort_key>", data_operations,
    },
    {
187
        "hash_scan",
Q
qinzuoyan 已提交
188
        "scan all sorted keys for a single hash key",
189 190 191 192 193 194
        "<hash_key> <start_sort_key> <stop_sort_key> "
        "[-a|--start_inclusive true|false] [-b|--stop_inclusive true|false] "
        "[-s|--sort_key_filter_type anywhere|prefix|postfix] "
        "[-y|--sort_key_filter_pattern str] "
        "[-o|--output file_name] [-n|--max_count num] [-t|--timeout_ms num] "
        "[-d|--detailed] [-i|--no_value]",
Q
qinzuoyan 已提交
195 196 197
        data_operations,
    },
    {
198 199
        "full_scan",
        "scan all hash keys",
200 201 202 203 204 205
        "[-h|--hash_key_filter_type anywhere|prefix|postfix] "
        "[-x|--hash_key_filter_pattern str] "
        "[-s|--sort_key_filter_type anywhere|prefix|postfix] "
        "[-y|--sort_key_filter_pattern str] "
        "[-o|--output file_name] [-n|--max_count num] [-t|--timeout_ms num] "
        "[-d|--detailed] [-i|--no_value] [-p|--partition num]",
Q
qinzuoyan 已提交
206 207 208 209 210
        data_operations,
    },
    {
        "copy_data",
        "copy app data",
211 212
        "<-c|--target_cluster_name str> <-a|--target_app_name str> "
        "[-s|--max_split_count num] [-b|--max_batch_count num] [-t|--timeout_ms num]",
Q
qinzuoyan 已提交
213 214 215 216 217
        data_operations,
    },
    {
        "clear_data",
        "clear app data",
218 219
        "[-f|--force] [-s|--max_split_count num] [-b|--max_batch_count num] "
        "[-t|--timeout_ms num]",
Q
qinzuoyan 已提交
220 221 222 223 224
        data_operations,
    },
    {
        "count_data",
        "get app row count",
225 226
        "[-s|--max_split_count num] [-b|--max_batch_count num] "
        "[-t|--timeout_ms num] [-z|--stat_size] [-c|--top_count num]",
Q
qinzuoyan 已提交
227 228 229 230 231
        data_operations,
    },
    {
        "remote_command",
        "send remote command to servers",
232 233
        "[-t all|meta-server|replica-server] [-l ip:port,ip:port...] "
        "<command> [arguments...]",
Q
qinzuoyan 已提交
234 235 236 237 238
        remote_command,
    },
    {
        "server_info",
        "get info of servers",
239
        "[-t all|meta-server|replica-server] [-l ip:port,ip:port...]",
Q
qinzuoyan 已提交
240 241 242 243 244
        server_info,
    },
    {
        "server_stat",
        "get stat of servers",
245
        "[-t all|meta-server|replica-server] [-l ip:port,ip:port...]",
Q
qinzuoyan 已提交
246 247 248
        server_stat,
    },
    {
249
        "app_stat", "get stat of apps", "[-a|--app_name str] [-o|--output file_name]", app_stat,
Q
qinzuoyan 已提交
250 251 252 253
    },
    {
        "flush_log",
        "flush log of servers",
254
        "[-t all|meta-server|replica-server] [-l ip:port,ip:port...]",
Q
qinzuoyan 已提交
255 256 257 258 259 260 261 262
        flush_log,
    },
    {
        "local_get", "get value from local db", "<db_path> <hash_key> <sort_key>", local_get,
    },
    {
        "sst_dump",
        "dump sstable dir or files",
263 264
        "[--command=check|scan|none|raw] <--file=data_dir_OR_sst_file> "
        "[--from=user_key] [--to=user_key] [--read_num=num] [--show_properties]",
Q
qinzuoyan 已提交
265 266 267 268 269
        sst_dump,
    },
    {
        "mlog_dump",
        "dump mutation log dir",
270
        "<-i|--input log_dir> [-o|--output file_name] [-d|--detailed]",
Q
qinzuoyan 已提交
271 272 273 274 275
        mlog_dump,
    },
    {
        "recover",
        "control the meta to recover the system from given nodes",
276 277 278
        "[-f|--node_list_file file_name] [-s|--node_list_str str] "
        "[-w|--wait_seconds num] "
        "[-b|--skip_bad_nodes] [-l|--skip_lost_partitions] [-o|--output file_name]",
Q
qinzuoyan 已提交
279 280
        recover,
    },
C
cailiuyang 已提交
281 282 283
    {
        "add_backup_policy",
        "add new cold backup policy",
284 285 286
        "<-p|--policy_name str> <-b|--backup_provider_type str> <-a|--app_ids 1,2...> "
        "<-i|--backup_interval_seconds num> <-s|--start_time hour:minute> "
        "<-c|--backup_history_cnt num>",
C
cailiuyang 已提交
287 288
        add_backup_policy,
    },
289
    {"ls_backup_policy", "list the names of the subsistent backup policies", "", ls_backup_policy},
C
cailiuyang 已提交
290 291
    {
        "query_backup_policy",
292
        "query subsistent backup policy and last backup infos",
293
        "<-p|--policy_name p1,p2...> [-b|--backup_info_cnt num]",
C
cailiuyang 已提交
294 295 296 297 298
        query_backup_policy,
    },
    {
        "modify_backup_policy",
        "modify the backup policy",
299 300
        "<-p|--policy_name str> [-a|--add_app 1,2...] [-r|--remove_app 1,2...] "
        "[-i|--backup_interval_seconds num] [-c|--backup_history_count num] "
C
cailiuyang 已提交
301
        "[-s|--start_time hour:minute]",
C
cailiuyang 已提交
302 303 304 305 306
        modify_backup_policy,
    },
    {
        "disable_backup_policy",
        "stop policy continue backup",
307
        "<-p|--policy_name str>",
C
cailiuyang 已提交
308 309 310 311 312
        disable_backup_policy,
    },
    {
        "enable_backup_policy",
        "start backup policy to backup again",
313
        "<-p|--policy_name str>",
C
cailiuyang 已提交
314 315 316 317 318
        enable_backup_policy,
    },
    {
        "restore_app",
        "restore app from backup media",
319
        "<-c|--old_cluster_name str> <-p|--old_policy_name str> <-a|--old_app_name str> "
C
cailiuyang 已提交
320
        "<-i|--old_app_id id> <-t|--timestamp/backup_id timestamp> "
321
        "<-b|--backup_provider_type str> [-n|--new_app_name str] [-s|--skip_bad_partition]",
C
cailiuyang 已提交
322 323 324
        restore,
    },
    {
325 326
        "query_restore_status",
        "query restore status",
C
cailiuyang 已提交
327
        "<restore_app_id> [-d|--detailed]",
328
        query_restore_status,
C
cailiuyang 已提交
329
    },
330 331 332
    {
        "get_app_envs", "get current app envs", "", get_app_envs,
    },
333
    {
334
        "set_app_envs", "set current app envs", "<key> <value> [key value...]", set_app_envs,
335 336
    },
    {
337
        "del_app_envs", "delete current app envs", "<key> [key...]", del_app_envs,
338 339 340 341
    },
    {
        "clear_app_envs", "clear current app envs", "<-a|--all> <-p|--prefix str>", clear_app_envs,
    },
Q
qinzuoyan 已提交
342 343 344 345 346 347 348
    {
        "exit", "exit shell", "", exit_shell,
    },
    {
        nullptr, nullptr, nullptr, nullptr,
    }};

349
void print_help(command_executor *e, size_t name_width, size_t option_width)
Q
qinzuoyan 已提交
350
{
351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386
    std::vector<std::string> lines;
    std::string options(e->option_usage);
    int line_start = 0;
    int line_end = -1;
    int i;
    for (i = 0; i < options.size(); i++) {
        if (i - line_start >= option_width && line_end >= line_start) {
            std::string s = options.substr(line_start, line_end - line_start + 1);
            std::string r = dsn::utils::trim_string((char *)s.c_str());
            if (!r.empty())
                lines.push_back(r);
            line_start = line_end + 2;
        }
        if ((options[i] == ']' || options[i] == '>') && i < options.size() - 1 &&
            options[i + 1] == ' ') {
            line_end = i;
        }
    }
    line_end = i - 1;
    if (line_end >= line_start) {
        std::string s = options.substr(line_start, line_end - line_start + 1);
        std::string r = dsn::utils::trim_string((char *)s.c_str());
        if (!r.empty())
            lines.push_back(r);
    }

    std::cout << "\t" << e->name << std::string(name_width + 2 - strlen(e->name), ' ');
    if (lines.empty()) {
        std::cout << std::endl;
    } else {
        for (int k = 0; k < lines.size(); k++) {
            if (k != 0)
                std::cout << "\t" << std::string(name_width + 2, ' ');
            std::cout << lines[k] << std::endl;
        }
    }
Q
qinzuoyan 已提交
387 388 389 390 391 392
}

void print_help()
{
    std::cout << "Usage:" << std::endl;
    for (int i = 0; commands[i].name != nullptr; ++i) {
393
        print_help(&commands[i], s_max_name_length, s_option_width);
Q
qinzuoyan 已提交
394 395 396 397 398 399
    }
}

void register_all_commands()
{
    for (int i = 0; commands[i].name != nullptr; ++i) {
400
        auto pr = s_commands_map.emplace(commands[i].name, &commands[i]);
Q
qinzuoyan 已提交
401
        dassert(pr.second, "the command '%s' is already registered!!!", commands[i].name);
402
        s_max_name_length = std::max(s_max_name_length, strlen(commands[i].name));
Q
qinzuoyan 已提交
403 404 405 406 407
    }
}

void execute_command(command_executor *e, int argc, std::string str_args[])
{
408 409 410
    static char buffer[s_max_params_count][512]; // 512*32
    static char *argv[s_max_params_count];
    for (int i = 0; i < s_max_params_count; ++i) {
Q
qinzuoyan 已提交
411 412 413
        argv[i] = buffer[i];
    }

414
    for (int i = 0; i < argc && i < s_max_params_count; ++i) {
Q
qinzuoyan 已提交
415 416 417 418 419 420 421
        if (!str_args[i].empty()) {
            strcpy(argv[i], str_args[i].c_str());
        } else {
            memset(argv[i], 0, sizeof(512));
        }
    }

422
    if (!e->exec(e, &s_global_context, {argc, argv})) {
Q
qinzuoyan 已提交
423
        printf("USAGE: ");
424
        print_help(e, s_max_name_length, s_option_width);
Q
qinzuoyan 已提交
425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459
    }
}

static sigjmp_buf s_ctrlc_buf;
void handle_signals(int signo)
{
    if (signo == SIGINT) {
        std::cout << std::endl << "Type \"Ctrl-D\" to exit the shell." << std::endl;
        siglongjmp(s_ctrlc_buf, 1);
    }
}

void initialize(int argc, char **argv)
{
    if (signal(SIGINT, handle_signals) == SIG_ERR) {
        std::cout << "ERROR: register signal handler failed" << std::endl;
        dsn_exit(-1);
    }

    std::cout << "Pegasus Shell " << PEGASUS_VERSION << std::endl;
    std::cout << "Type \"help\" for more information." << std::endl;
    std::cout << "Type \"Ctrl-D\" to exit the shell." << std::endl;
    std::cout << std::endl;

    std::string config_file = argc > 1 ? argv[1] : "config.ini";
    if (!pegasus::pegasus_client_factory::initialize(config_file.c_str())) {
        std::cout << "ERROR: init pegasus failed: " << config_file << std::endl;
        dsn_exit(-1);
    } else {
        std::cout << "The config file is: " << config_file << std::endl;
    }

    std::string cluster_name = argc > 2 ? argv[2] : "mycluster";
    std::cout << "The cluster name is: " << cluster_name << std::endl;

460 461
    s_global_context.current_cluster_name = cluster_name;
    std::string section = "uri-resolver.dsn://" + s_global_context.current_cluster_name;
Q
qinzuoyan 已提交
462 463 464 465 466
    std::string key = "arguments";
    std::string server_list = dsn_config_get_value_string(section.c_str(), key.c_str(), "", "");
    std::cout << "The cluster meta list is: " << server_list << std::endl;

    dsn::replication::replica_helper::load_meta_servers(
467 468 469
        s_global_context.meta_list, section.c_str(), key.c_str());
    s_global_context.ddl_client =
        new dsn::replication::replication_ddl_client(s_global_context.meta_list);
Q
qinzuoyan 已提交
470 471 472 473 474 475 476 477 478 479 480

    register_all_commands();
}

void run()
{
    while (sigsetjmp(s_ctrlc_buf, 1) != 0)
        ;

    while (true) {
        int arg_count;
481 482
        std::string args[s_max_params_count];
        scanfCommand(arg_count, args, s_max_params_count);
Q
qinzuoyan 已提交
483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499
        if (arg_count > 0) {
            int i = 0;
            for (; i < arg_count; ++i) {
                std::string &s = args[i];
                int j = 0;
                for (; j < s.size(); ++j) {
                    if (!isprint(s.at(j))) {
                        std::cout << "ERROR: found unprintable character in '"
                                  << pegasus::utils::c_escape_string(s) << "'" << std::endl;
                        break;
                    }
                }
                if (j < s.size())
                    break;
            }
            if (i < arg_count)
                continue;
500 501
            auto iter = s_commands_map.find(args[0]);
            if (iter != s_commands_map.end()) {
Q
qinzuoyan 已提交
502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535
                execute_command(iter->second, arg_count, args);
            } else {
                std::cout << "ERROR: invalid subcommand '" << args[0] << "'" << std::endl;
                print_help();
            }
        }
    }
}

int main(int argc, char **argv)
{
    initialize(argc, argv);
    run();
    return 0;
}

#if defined(__linux__)
#include <dsn/git_commit.h>
#include <dsn/version.h>
#include <pegasus/git_commit.h>
#include <pegasus/version.h>
static char const rcsid[] =
    "$Version: Pegasus Shell " PEGASUS_VERSION " (" PEGASUS_GIT_COMMIT ")"
#if defined(DSN_BUILD_TYPE)
    " " STR(DSN_BUILD_TYPE)
#endif
        ", built with rDSN " DSN_CORE_VERSION " (" DSN_GIT_COMMIT ")"
        ", built by gcc " STR(__GNUC__) "." STR(__GNUC_MINOR__) "." STR(__GNUC_PATCHLEVEL__)
#if defined(DSN_BUILD_HOSTNAME)
            ", built on " STR(DSN_BUILD_HOSTNAME)
#endif
                ", built at " __DATE__ " " __TIME__ " $";
const char *pegasus_shell_rcsid() { return rcsid; }
#endif