YugabyteDB (2.13.0.0-b42, bfc6a6643e7399ac8a0e81d06a3ee6d6571b33ab)

Coverage Report

Created: 2022-03-09 17:30

/Users/deen/code/yugabyte-db/src/yb/tools/yb-admin_cli.cc
Line
Count
Source (jump to first uncovered line)
1
// Licensed to the Apache Software Foundation (ASF) under one
2
// or more contributor license agreements.  See the NOTICE file
3
// distributed with this work for additional information
4
// regarding copyright ownership.  The ASF licenses this file
5
// to you under the Apache License, Version 2.0 (the
6
// "License"); you may not use this file except in compliance
7
// with the License.  You may obtain a copy of the License at
8
//
9
//   http://www.apache.org/licenses/LICENSE-2.0
10
//
11
// Unless required by applicable law or agreed to in writing,
12
// software distributed under the License is distributed on an
13
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
// KIND, either express or implied.  See the License for the
15
// specific language governing permissions and limitations
16
// under the License.
17
//
18
// The following only applies to changes made to this file as part of YugaByte development.
19
//
20
// Portions Copyright (c) YugaByte, Inc.
21
//
22
// Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
23
// in compliance with the License.  You may obtain a copy of the License at
24
//
25
// http://www.apache.org/licenses/LICENSE-2.0
26
//
27
// Unless required by applicable law or agreed to in writing, software distributed under the License
28
// is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express
29
// or implied.  See the License for the specific language governing permissions and limitations
30
// under the License.
31
//
32
33
#include "yb/tools/yb-admin_cli.h"
34
35
#include <memory>
36
#include <utility>
37
38
#include <boost/lexical_cast.hpp>
39
40
#include "yb/common/json_util.h"
41
42
#include "yb/master/master_defaults.h"
43
44
#include "yb/tools/yb-admin_client.h"
45
46
#include "yb/util/flags.h"
47
#include "yb/util/logging.h"
48
#include "yb/util/status_format.h"
49
#include "yb/util/stol_utils.h"
50
#include "yb/util/string_case.h"
51
52
DEFINE_string(master_addresses, "localhost:7100",
53
              "Comma-separated list of YB Master server addresses");
54
DEFINE_string(init_master_addrs, "",
55
              "host:port of any yb-master in a cluster");
56
DEFINE_int64(timeout_ms, 1000 * 60, "RPC timeout in milliseconds");
57
DEFINE_bool(exclude_dead, false, "Exclude dead tservers from output");
58
59
60
using std::cerr;
61
using std::endl;
62
using std::ostringstream;
63
using std::make_pair;
64
using std::next;
65
using std::pair;
66
using std::string;
67
68
using yb::client::YBTableName;
69
using strings::Substitute;
70
71
using namespace std::placeholders;
72
73
namespace yb {
74
namespace tools {
75
76
const Status ClusterAdminCli::kInvalidArguments = STATUS(
77
                                  InvalidArgument, "Invalid arguments for operation");
78
79
namespace {
80
81
constexpr auto kBlacklistAdd = "ADD";
82
constexpr auto kBlacklistRemove = "REMOVE";
83
constexpr int32 kDefaultRpcPort = 9100;
84
85
CHECKED_STATUS GetUniverseConfig(ClusterAdminClientClass* client,
86
0
                                 const ClusterAdminCli::CLIArguments&) {
87
0
  RETURN_NOT_OK_PREPEND(client->GetUniverseConfig(), "Unable to get universe config");
88
0
  return Status::OK();
89
0
}
90
91
CHECKED_STATUS ChangeBlacklist(ClusterAdminClientClass* client,
92
                               const ClusterAdminCli::CLIArguments& args, bool blacklist_leader,
93
0
                               const std::string& errStr) {
94
0
  if (args.size() < 2) {
95
0
    return ClusterAdminCli::kInvalidArguments;
96
0
  }
97
0
  const auto change_type = args[0];
98
0
  if (change_type != kBlacklistAdd && change_type != kBlacklistRemove) {
99
0
    return ClusterAdminCli::kInvalidArguments;
100
0
  }
101
0
  std::vector<HostPort> hostports;
102
0
  for (const auto& arg : boost::make_iterator_range(args.begin() + 1, args.end())) {
103
0
    hostports.push_back(VERIFY_RESULT(HostPort::FromString(arg, kDefaultRpcPort)));
104
0
  }
105
106
0
  RETURN_NOT_OK_PREPEND(client->ChangeBlacklist(hostports, change_type == kBlacklistAdd,
107
0
        blacklist_leader), errStr);
108
0
  return Status::OK();
109
0
}
110
111
CHECKED_STATUS MasterLeaderStepDown(
112
    ClusterAdminClientClass* client,
113
0
    const ClusterAdminCli::CLIArguments& args) {
114
0
  const auto leader_uuid = VERIFY_RESULT(client->GetMasterLeaderUuid());
115
0
  return client->MasterLeaderStepDown(
116
0
      leader_uuid, args.size() > 0 ? args[0] : std::string());
117
0
}
118
119
CHECKED_STATUS LeaderStepDown(
120
    ClusterAdminClientClass* client,
121
0
    const ClusterAdminCli::CLIArguments& args) {
122
0
  if (args.size() < 1) {
123
0
    return ClusterAdminCli::kInvalidArguments;
124
0
  }
125
0
  std::string dest_uuid = (args.size() > 1) ? args[1] : "";
126
0
  RETURN_NOT_OK_PREPEND(
127
0
      client->LeaderStepDownWithNewLeader(args[0], dest_uuid), "Unable to step down leader");
128
0
  return Status::OK();
129
0
}
130
131
0
bool IsEqCaseInsensitive(const string& check, const string& expected) {
132
0
  string upper_check, upper_expected;
133
0
  ToUpperCase(check, &upper_check);
134
0
  ToUpperCase(expected, &upper_expected);
135
0
  return upper_check == upper_expected;
136
0
}
137
138
template <class Enum>
139
Result<std::pair<int, EnumBitSet<Enum>>> GetValueAndFlags(
140
    const CLIArgumentsIterator& begin,
141
    const CLIArgumentsIterator& end,
142
0
    const std::initializer_list<Enum>& flags_list) {
143
0
  std::pair<int, EnumBitSet<Enum>> result;
144
0
  bool seen_value = false;
145
0
  for (auto iter = begin; iter != end; iter = ++iter) {
146
0
    bool found_flag = false;
147
0
    for (auto flag : flags_list) {
148
0
      if (IsEqCaseInsensitive(*iter, ToString(flag))) {
149
0
        if (result.second.Test(flag)) {
150
0
          return STATUS_FORMAT(InvalidArgument, "Duplicate flag: $0", flag);
151
0
        }
152
0
        result.second.Set(flag);
153
0
        found_flag = true;
154
0
        break;
155
0
      }
156
0
    }
157
0
    if (found_flag) {
158
0
      continue;
159
0
    }
160
161
0
    if (seen_value) {
162
0
      return STATUS_FORMAT(InvalidArgument, "Multiple values: $0 and $1", result.first, *iter);
163
0
    }
164
165
0
    result.first = VERIFY_RESULT(CheckedStoi(*iter));
166
0
    seen_value = true;
167
0
  }
168
169
0
  return result;
170
0
}
Unexecuted instantiation: yb-admin_cli.cc:_ZN2yb5tools12_GLOBAL__N_116GetValueAndFlagsINS1_16ListTabletsFlagsEEENS_6ResultINSt3__14pairIiNS_10EnumBitSetIT_EEEEEERKNS5_11__wrap_iterIPKNS5_12basic_stringIcNS5_11char_traitsIcEENS5_9allocatorIcEEEEEESN_RKSt16initializer_listIS8_E
Unexecuted instantiation: yb-admin_cli.cc:_ZN2yb5tools12_GLOBAL__N_116GetValueAndFlagsINS1_10AddIndexesEEENS_6ResultINSt3__14pairIiNS_10EnumBitSetIT_EEEEEERKNS5_11__wrap_iterIPKNS5_12basic_stringIcNS5_11char_traitsIcEENS5_9allocatorIcEEEEEESN_RKSt16initializer_listIS8_E
171
172
YB_DEFINE_ENUM(AddIndexes, (ADD_INDEXES));
173
174
Result<pair<int, bool>> GetTimeoutAndAddIndexesFlag(
175
    CLIArgumentsIterator begin,
176
0
    const CLIArgumentsIterator& end) {
177
0
  auto temp_pair = VERIFY_RESULT(GetValueAndFlags(begin, end, kAddIndexesList));
178
0
  return std::make_pair(temp_pair.first, temp_pair.second.Test(AddIndexes::ADD_INDEXES));
179
0
}
180
181
YB_DEFINE_ENUM(ListTabletsFlags, (JSON)(INCLUDE_FOLLOWERS));
182
183
} // namespace
184
185
1
Status ClusterAdminCli::Run(int argc, char** argv) {
186
1
  const string prog_name = argv[0];
187
1
  FLAGS_logtostderr = 1;
188
1
  FLAGS_minloglevel = 2;
189
1
  ParseCommandLineFlags(&argc, &argv, true);
190
1
  InitGoogleLoggingSafe(prog_name.c_str());
191
192
1
  std::unique_ptr<ClusterAdminClientClass> client;
193
1
  const string addrs = FLAGS_master_addresses;
194
1
  if (!FLAGS_init_master_addrs.empty()) {
195
0
    std::vector<HostPort> init_master_addrs;
196
0
    RETURN_NOT_OK(HostPort::ParseStrings(
197
0
        FLAGS_init_master_addrs,
198
0
        master::kMasterDefaultPort,
199
0
        &init_master_addrs));
200
0
    client.reset(new ClusterAdminClientClass(
201
0
        init_master_addrs[0],
202
0
        MonoDelta::FromMilliseconds(FLAGS_timeout_ms)));
203
1
  } else {
204
1
    client.reset(new ClusterAdminClientClass(
205
1
        addrs,
206
1
        MonoDelta::FromMilliseconds(FLAGS_timeout_ms)));
207
1
  }
208
209
1
  RegisterCommandHandlers(client.get());
210
1
  SetUsage(prog_name);
211
212
1
  CLIArguments args;
213
4
  for (int i = 0; i < argc; ++i) {
214
3
    args.push_back(argv[i]);
215
3
  }
216
217
1
  if (args.size() < 2) {
218
0
    return ClusterAdminCli::kInvalidArguments;
219
0
  }
220
221
  // Find operation handler by operation name.
222
1
  const string op = args[1];
223
1
  auto cmd = command_indexes_.find(op);
224
225
1
  if (cmd == command_indexes_.end()) {
226
0
    cerr << "Invalid operation: " << op << endl;
227
0
    return ClusterAdminCli::kInvalidArguments;
228
0
  }
229
230
  // Init client.
231
1
  Status s = client->Init();
232
233
1
  if (PREDICT_FALSE(!s.ok())) {
234
0
    cerr << s.CloneAndPrepend("Unable to establish connection to leader master at [" + addrs + "]."
235
0
                              " Please verify the addresses.\n\n").ToString() << endl;
236
0
    return STATUS(RuntimeError, "Error connecting to cluster");
237
0
  }
238
239
  // Run found command.
240
1
  CLIArguments command_args(args.begin() + 2, args.end());
241
1
  s = commands_[cmd->second].action_(command_args);
242
1
  if (!s.ok()) {
243
0
    cerr << "Error running " << cmd->first << ": " << s << endl;
244
0
    return STATUS(RuntimeError, "Error running command");
245
0
  }
246
1
  return Status::OK();
247
1
}
248
249
85
void ClusterAdminCli::Register(string&& cmd_name, string&& cmd_args, Action&& action) {
250
85
  command_indexes_[cmd_name] = commands_.size();
251
85
  commands_.push_back({std::move(cmd_name), std::move(cmd_args), std::move(action)});
252
85
}
253
254
6
void ClusterAdminCli::RegisterJson(string&& cmd_name, string&& cmd_args, JsonAction&& action) {
255
0
  Register(std::move(cmd_name), std::move(cmd_args), [action](const CLIArguments& args) -> Status {
256
0
    auto result = VERIFY_RESULT(action(args));
257
0
    std::cout << common::PrettyWriteRapidJsonToString(result) << std::endl;
258
0
    return Status::OK();
259
0
  });
260
6
}
261
262
1
void ClusterAdminCli::SetUsage(const string& prog_name) {
263
1
  ostringstream str;
264
265
1
  str << prog_name << " [-master_addresses server1:port,server2:port,server3:port,...] "
266
1
      << " [-timeout_ms <millisec>] [-certs_dir_name <dir_name>] <operation>" << endl
267
1
      << "<operation> must be one of:" << endl;
268
269
86
  for (size_t i = 0; i < commands_.size(); ++i) {
270
85
    str << ' ' << i + 1 << ". " << commands_[i].name_ << commands_[i].usage_arguments_ << endl;
271
85
  }
272
273
1
  str << endl;
274
1
  str << "<namespace>:" << endl;
275
1
  str << "  [(ycql|ysql).]<namespace_name>" << endl;
276
1
  str << "<table>:" << endl;
277
1
  str << "  <namespace> <table_name> | tableid.<table_id>" << endl;
278
1
  str << "<index>:" << endl;
279
1
  str << "  <namespace> <index_name> | tableid.<index_id>" << endl;
280
281
1
  google::SetUsageMessage(str.str());
282
1
}
283
284
Result<rapidjson::Document> DdlLog(
285
0
    ClusterAdminClientClass* client, const ClusterAdminCli::CLIArguments& args) {
286
0
  RETURN_NOT_OK(CheckArgumentsCount(args.size(), 0, 0));
287
0
  return client->DdlLog();
288
0
}
289
290
1
void ClusterAdminCli::RegisterCommandHandlers(ClusterAdminClientClass* client) {
291
1
  DCHECK_ONLY_NOTNULL(client);
292
293
1
  Register(
294
1
      "change_config",
295
1
      " <tablet_id> <ADD_SERVER|REMOVE_SERVER> <peer_uuid> [PRE_VOTER|PRE_OBSERVER]",
296
0
      [client](const CLIArguments& args) -> Status {
297
0
        if (args.size() < 3) {
298
0
          return ClusterAdminCli::kInvalidArguments;
299
0
        }
300
0
        const string tablet_id = args[0];
301
0
        const string change_type = args[1];
302
0
        const string peer_uuid = args[2];
303
0
        boost::optional<string> member_type;
304
0
        if (args.size() > 3) {
305
0
          member_type = args[3];
306
0
        }
307
0
        RETURN_NOT_OK_PREPEND(client->ChangeConfig(tablet_id, change_type, peer_uuid, member_type),
308
0
                              "Unable to change config");
309
0
        return Status::OK();
310
0
      });
311
312
1
  Register(
313
1
      "list_tablet_servers", " <tablet_id>",
314
0
      [client](const CLIArguments& args) -> Status {
315
0
        if (args.size() < 1) {
316
0
          return ClusterAdminCli::kInvalidArguments;
317
0
        }
318
0
        const string tablet_id = args[0];
319
0
        RETURN_NOT_OK_PREPEND(client->ListPerTabletTabletServers(tablet_id),
320
0
                              Substitute("Unable to list tablet servers of tablet $0", tablet_id));
321
0
        return Status::OK();
322
0
      });
323
324
1
  Register(
325
1
      "backfill_indexes_for_table", " <table>",
326
0
      [client](const CLIArguments& args) -> Status {
327
0
        const auto table_name = VERIFY_RESULT(
328
0
            ResolveSingleTableName(client, args.begin(), args.end()));
329
0
        RETURN_NOT_OK_PREPEND(client->LaunchBackfillIndexForTable(table_name),
330
0
                              yb::Format("Unable to launch backfill for indexes in $0",
331
0
                                         table_name));
332
0
        return Status::OK();
333
0
      });
334
335
1
  static const auto kIncludeDBType = "include_db_type";
336
1
  static const auto kIncludeTableId = "include_table_id";
337
1
  static const auto kIncludeTableType = "include_table_type";
338
339
1
  Register(
340
1
      "list_tables", Format(" [$0] [$1] [$2]", kIncludeDBType, kIncludeTableId, kIncludeTableType),
341
0
      [client](const CLIArguments& args) -> Status {
342
0
        bool include_db_type = false;
343
0
        bool include_table_id = false;
344
0
        bool include_table_type = false;
345
0
        const std::array<std::pair<const char*, bool*>, 3> flags{
346
0
            std::make_pair(kIncludeDBType, &include_db_type),
347
0
            std::make_pair(kIncludeTableId, &include_table_id),
348
0
            std::make_pair(kIncludeTableType, &include_table_type)
349
0
        };
350
0
        for (const auto& arg : args) {
351
0
          for (const auto& flag : flags) {
352
0
            if (flag.first == arg) {
353
0
              *flag.second = true;
354
0
            }
355
0
          }
356
0
        }
357
0
        RETURN_NOT_OK_PREPEND(client->ListTables(include_db_type,
358
0
                                                 include_table_id,
359
0
                                                 include_table_type),
360
0
                              "Unable to list tables");
361
0
        return Status::OK();
362
0
      });
363
364
  // Deprecated list_tables commands with arguments should be used instead.
365
1
  Register(
366
1
      "list_tables_with_db_types", "",
367
0
      [client](const CLIArguments&) -> Status {
368
0
        RETURN_NOT_OK_PREPEND(
369
0
            client->ListTables(true /* include_db_type */,
370
0
                               false /* include_table_id*/,
371
0
                               false /* include_table_type*/),
372
0
            "Unable to list tables");
373
0
        return Status::OK();
374
0
      });
375
376
1
  Register(
377
1
      "list_tablets",
378
1
      " <table> [max_tablets] (default 10, set 0 for max) [JSON] [include_followers]",
379
0
      [client](const CLIArguments& args) -> Status {
380
0
        std::pair<int, EnumBitSet<ListTabletsFlags>> arguments;
381
0
        const auto table_name  = VERIFY_RESULT(ResolveSingleTableName(
382
0
            client, args.begin(), args.end(),
383
0
            [&arguments](auto i, const auto& end) -> Status {
384
0
              arguments = VERIFY_RESULT(GetValueAndFlags(i, end, kListTabletsFlagsList));
385
0
              return Status::OK();
386
0
            }));
387
0
        RETURN_NOT_OK_PREPEND(
388
0
            client->ListTablets(
389
0
                table_name, arguments.first, arguments.second.Test(ListTabletsFlags::JSON),
390
0
                arguments.second.Test(ListTabletsFlags::INCLUDE_FOLLOWERS)),
391
0
            Format("Unable to list tablets of table $0", table_name));
392
0
        return Status::OK();
393
0
      });
394
395
1
  static const auto kTableName = "<(<keyspace> <table_name>)|tableid.<table_id>>";
396
1
  static const auto kPlacementInfo = "placement_info";
397
1
  static const auto kReplicationFactor = "replication_factor";
398
1
  static const auto kPlacementUuid = "placement_uuid";
399
400
1
  Register(
401
1
      "modify_table_placement_info",
402
1
        Format(" [$0] [$1] [$2] [$3]", kTableName, kPlacementInfo, kReplicationFactor,
403
1
          kPlacementUuid),
404
0
      [client](const CLIArguments& args) -> Status {
405
0
        if (args.size() < 3 || args.size() > 5) {
406
0
          return ClusterAdminCli::kInvalidArguments;
407
0
        }
408
0
        std::string placement_info;
409
0
        int rf = -1;
410
0
        std::string placement_uuid;
411
0
        const auto table_name  = VERIFY_RESULT(ResolveSingleTableName(
412
0
            client, args.begin(), args.end(),
413
0
            [&placement_info, &rf, &placement_uuid](
414
0
        auto i, const auto& end) -> Status {
415
          // Get placement info.
416
0
          placement_info = *i;
417
0
          i = std::next(i);
418
          // Get replication factor.
419
0
          rf = VERIFY_RESULT(CheckedStoi(*i));
420
0
          i = std::next(i);
421
          // Get optional placement uuid.
422
0
          if (i != end) {
423
0
            placement_uuid = *i;
424
0
          }
425
0
          return Status::OK();
426
0
        }
427
0
        ));
428
0
        RETURN_NOT_OK_PREPEND(
429
0
            client->ModifyTablePlacementInfo(table_name, placement_info, rf, placement_uuid),
430
0
            Substitute("Unable to modify placement info for table $0", table_name.ToString()));
431
0
        return Status::OK();
432
0
      });
433
434
1
  Register(
435
1
      "modify_placement_info", " <placement_info> <replication_factor> [placement_uuid]",
436
0
      [client](const CLIArguments& args) -> Status {
437
0
        if (args.size() != 2 && args.size() != 3) {
438
0
          return ClusterAdminCli::kInvalidArguments;
439
0
        }
440
0
        int rf = boost::lexical_cast<int>(args[1]);
441
0
        string placement_uuid = args.size() == 3 ? args[2] : "";
442
0
        RETURN_NOT_OK_PREPEND(client->ModifyPlacementInfo(args[0], rf, placement_uuid),
443
0
                              Substitute("Unable to modify placement info."));
444
0
        return Status::OK();
445
0
      });
446
447
1
  Register(
448
1
      "clear_placement_info", "",
449
0
      [client](const CLIArguments& args) -> Status {
450
0
        if (args.size() != 0) {
451
0
          return ClusterAdminCli::kInvalidArguments;
452
0
        }
453
0
        RETURN_NOT_OK_PREPEND(client->ClearPlacementInfo(),
454
0
                              Substitute("Unable to clear placement info."));
455
0
        return Status::OK();
456
0
      });
457
458
1
  Register(
459
1
      "add_read_replica_placement_info", " <placement_info> <replication_factor> [placement_uuid]",
460
0
      [client](const CLIArguments& args) -> Status {
461
0
        if (args.size() != 2 && args.size() != 3) {
462
0
          return ClusterAdminCli::kInvalidArguments;
463
0
        }
464
0
        int rf = boost::lexical_cast<int>(args[1]);
465
0
        string placement_uuid = args.size() == 3 ? args[2] : "";
466
0
        RETURN_NOT_OK_PREPEND(client->AddReadReplicaPlacementInfo(args[0], rf, placement_uuid),
467
0
                              Substitute("Unable to add read replica placement info."));
468
0
        return Status::OK();
469
0
      });
470
471
1
  Register(
472
1
      "modify_read_replica_placement_info",
473
1
      " <placement_info> <replication_factor> [placement_uuid]",
474
0
      [client](const CLIArguments& args) -> Status {
475
0
        if (args.size() != 2 && args.size() != 3) {
476
0
          return ClusterAdminCli::kInvalidArguments;
477
0
        }
478
0
        int rf = boost::lexical_cast<int>(args[1]);
479
0
        string placement_uuid = args.size() == 3 ? args[2] : "";
480
0
        RETURN_NOT_OK_PREPEND(client->ModifyReadReplicaPlacementInfo(placement_uuid, args[0], rf),
481
0
                              Substitute("Unable to modify read replica placement info."));
482
0
        return Status::OK();
483
0
      });
484
485
1
  Register(
486
1
      "delete_read_replica_placement_info", "",
487
0
      [client](const CLIArguments& args) -> Status {
488
0
        if (args.size() != 0) {
489
0
          return ClusterAdminCli::kInvalidArguments;
490
0
        }
491
0
        RETURN_NOT_OK_PREPEND(client->DeleteReadReplicaPlacementInfo(),
492
0
                              Substitute("Unable to delete read replica placement info."));
493
0
        return Status::OK();
494
0
      });
495
496
1
  Register(
497
1
      "delete_namespace", " <namespace>",
498
1
      [client](const CLIArguments& args) -> Status {
499
1
        if (args.size() != 1) {
500
0
          return ClusterAdminCli::kInvalidArguments;
501
0
        }
502
1
        auto namespace_name = VERIFY_RESULT(ParseNamespaceName(args[0]));
503
1
        RETURN_NOT_OK_PREPEND(client->DeleteNamespace(namespace_name),
504
1
                              Substitute("Unable to delete namespace $0", namespace_name.name));
505
1
        return Status::OK();
506
1
      });
507
508
1
  Register(
509
1
      "delete_namespace_by_id", " <namespace_id>",
510
0
      [client](const CLIArguments& args) -> Status {
511
0
        if (args.size() != 1) {
512
0
          return ClusterAdminCli::kInvalidArguments;
513
0
        }
514
0
        RETURN_NOT_OK_PREPEND(client->DeleteNamespaceById(args[0]),
515
0
                              Substitute("Unable to delete namespace $0", args[0]));
516
0
        return Status::OK();
517
0
      });
518
519
1
  Register(
520
1
      "delete_table", " <table>",
521
0
      [client](const CLIArguments& args) -> Status {
522
0
        const auto table_name = VERIFY_RESULT(
523
0
            ResolveSingleTableName(client, args.begin(), args.end()));
524
0
        RETURN_NOT_OK_PREPEND(client->DeleteTable(table_name),
525
0
                              Substitute("Unable to delete table $0", table_name.ToString()));
526
0
        return Status::OK();
527
0
      });
528
529
1
  Register(
530
1
      "delete_table_by_id", " <table_id>",
531
0
      [client](const CLIArguments& args) -> Status {
532
0
        if (args.size() != 1) {
533
0
          return ClusterAdminCli::kInvalidArguments;
534
0
        }
535
0
        RETURN_NOT_OK_PREPEND(client->DeleteTableById(args[0]),
536
0
                              Substitute("Unable to delete table $0", args[0]));
537
0
        return Status::OK();
538
0
      });
539
540
1
  Register(
541
1
      "delete_index", " <index>",
542
0
      [client](const CLIArguments& args) -> Status {
543
0
        const auto table_name = VERIFY_RESULT(
544
0
            ResolveSingleTableName(client, args.begin(), args.end()));
545
0
        RETURN_NOT_OK_PREPEND(client->DeleteIndex(table_name),
546
0
                              Substitute("Unable to delete index $0", table_name.ToString()));
547
0
        return Status::OK();
548
0
      });
549
550
1
  Register(
551
1
      "delete_index_by_id", " <index_id>",
552
0
      [client](const CLIArguments& args) -> Status {
553
0
        if (args.size() != 1) {
554
0
          return ClusterAdminCli::kInvalidArguments;
555
0
        }
556
0
        RETURN_NOT_OK_PREPEND(client->DeleteIndexById(args[0]),
557
0
                              Substitute("Unable to delete index $0", args[0]));
558
0
        return Status::OK();
559
0
      });
560
561
1
  Register(
562
1
      "flush_table",
563
1
      " <table> [timeout_in_seconds] (default 20)"
564
1
      " [ADD_INDEXES] (default false)",
565
0
      [client](const CLIArguments& args) -> Status {
566
0
        bool add_indexes = false;
567
0
        int timeout_secs = 20;
568
0
        const auto table_name = VERIFY_RESULT(ResolveSingleTableName(
569
0
            client, args.begin(), args.end(),
570
0
            [&add_indexes, &timeout_secs](auto i, const auto& end) -> Status {
571
0
              std::tie(timeout_secs, add_indexes) = VERIFY_RESULT(
572
0
                  GetTimeoutAndAddIndexesFlag(i, end));
573
0
              return Status::OK();
574
0
            }));
575
0
        RETURN_NOT_OK_PREPEND(client->FlushTables({table_name},
576
0
                                                  add_indexes,
577
0
                                                  timeout_secs,
578
0
                                                  false /* is_compaction */),
579
0
                              Substitute("Unable to flush table $0", table_name.ToString()));
580
0
        return Status::OK();
581
0
      });
582
583
1
  Register(
584
1
      "flush_table_by_id", " <table_id> [timeout_in_seconds] (default 20)"
585
1
      " [ADD_INDEXES] (default false)",
586
0
      [client](const CLIArguments& args) -> Status {
587
0
        bool add_indexes = false;
588
0
        int timeout_secs = 20;
589
0
        if (args.size() < 1) {
590
0
          return ClusterAdminCli::kInvalidArguments;
591
0
        }
592
0
        std::tie(timeout_secs, add_indexes) = VERIFY_RESULT(GetTimeoutAndAddIndexesFlag(
593
0
          args.begin() + 1, args.end()));
594
0
        RETURN_NOT_OK_PREPEND(client->FlushTablesById({args[0]},
595
0
                                                      add_indexes,
596
0
                                                      timeout_secs,
597
0
                                                      false /* is_compaction */),
598
0
                              Substitute("Unable to flush table $0", args[0]));
599
0
        return Status::OK();
600
0
      });
601
602
1
  Register(
603
1
      "flush_sys_catalog", "",
604
0
      [client](const CLIArguments& args) -> Status {
605
0
        RETURN_NOT_OK_PREPEND(client->FlushSysCatalog(), "Unable to flush table sys_catalog");
606
0
        return Status::OK();
607
0
      });
608
609
1
  Register(
610
1
      "compact_sys_catalog", "",
611
0
      [client](const CLIArguments& args) -> Status {
612
0
        RETURN_NOT_OK_PREPEND(client->CompactSysCatalog(), "Unable to compact table sys_catalog");
613
0
        return Status::OK();
614
0
      });
615
616
1
  Register(
617
1
      "compact_table",
618
1
      " <table> [timeout_in_seconds] (default 20)"
619
1
      " [ADD_INDEXES] (default false)",
620
0
      [client](const CLIArguments& args) -> Status {
621
0
        bool add_indexes = false;
622
0
        int timeout_secs = 20;
623
0
        const auto table_name = VERIFY_RESULT(ResolveSingleTableName(
624
0
            client, args.begin(), args.end(),
625
0
            [&add_indexes, &timeout_secs](auto i, const auto& end) -> Status {
626
0
              std::tie(timeout_secs, add_indexes) = VERIFY_RESULT(
627
0
                  GetTimeoutAndAddIndexesFlag(i, end));
628
0
              return Status::OK();
629
0
            }));
630
        // We use the same FlushTables RPC to trigger compaction.
631
0
        RETURN_NOT_OK_PREPEND(client->FlushTables({table_name},
632
0
                                                  add_indexes,
633
0
                                                  timeout_secs,
634
0
                                                  true /* is_compaction */),
635
0
                              Substitute("Unable to compact table $0", table_name.ToString()));
636
0
        return Status::OK();
637
0
      });
638
639
1
  Register(
640
1
      "compact_table_by_id", " <table_id> [timeout_in_seconds] (default 20)"
641
1
      " [ADD_INDEXES] (default false)",
642
0
      [client](const CLIArguments& args) -> Status {
643
0
        bool add_indexes = false;
644
0
        int timeout_secs = 20;
645
0
        if (args.size() < 1) {
646
0
          return ClusterAdminCli::kInvalidArguments;
647
0
        }
648
0
        std::tie(timeout_secs, add_indexes) = VERIFY_RESULT(
649
0
            GetTimeoutAndAddIndexesFlag(args.begin() + 1, args.end()));
650
        // We use the same FlushTables RPC to trigger compaction.
651
0
        RETURN_NOT_OK_PREPEND(client->FlushTablesById({args[0]},
652
0
                                                      add_indexes,
653
0
                                                      timeout_secs,
654
0
                                                      true /* is_compaction */),
655
0
                              Substitute("Unable to compact table $0", args[0]));
656
0
        return Status::OK();
657
0
      });
658
659
1
  Register(
660
1
      "list_all_tablet_servers", "",
661
0
      [client](const CLIArguments&) -> Status {
662
0
        RETURN_NOT_OK_PREPEND(client->ListAllTabletServers(FLAGS_exclude_dead),
663
0
                              "Unable to list tablet servers");
664
0
        return Status::OK();
665
0
      });
666
667
1
  Register(
668
1
      "list_all_masters", "",
669
0
      [client](const CLIArguments&) -> Status {
670
0
        RETURN_NOT_OK_PREPEND(client->ListAllMasters(),
671
0
                              "Unable to list masters");
672
0
        return Status::OK();
673
0
      });
674
675
1
  Register(
676
1
      "change_master_config", " <ADD_SERVER|REMOVE_SERVER> <ip_addr> <port> [<uuid>]",
677
0
      [client](const CLIArguments& args) -> Status {
678
0
        uint16_t new_port = 0;
679
0
        string new_host;
680
681
0
        if (args.size() < 3 || args.size() > 4) {
682
0
          return ClusterAdminCli::kInvalidArguments;
683
0
        }
684
685
0
        const string change_type = args[0];
686
0
        if (change_type != "ADD_SERVER" && change_type != "REMOVE_SERVER") {
687
0
          return ClusterAdminCli::kInvalidArguments;
688
0
        }
689
690
0
        new_host = args[1];
691
0
        new_port = VERIFY_RESULT(CheckedStoi(args[2]));
692
693
0
        string given_uuid;
694
0
        if (args.size() == 4) {
695
0
          given_uuid = args[3];
696
0
        }
697
0
        RETURN_NOT_OK_PREPEND(
698
0
            client->ChangeMasterConfig(change_type, new_host, new_port, given_uuid),
699
0
            "Unable to change master config");
700
0
        return Status::OK();
701
0
      });
702
703
1
  Register(
704
1
      "dump_masters_state", " [CONSOLE]",
705
0
      [client](const CLIArguments& args) -> Status {
706
0
        bool to_console = false;
707
0
        if (args.size() > 0) {
708
0
          if (IsEqCaseInsensitive(args[0], "CONSOLE")) {
709
0
            to_console = true;
710
0
          } else {
711
0
            return ClusterAdminCli::kInvalidArguments;
712
0
          }
713
0
        }
714
0
        RETURN_NOT_OK_PREPEND(client->DumpMasterState(to_console),
715
0
                              "Unable to dump master state");
716
0
        return Status::OK();
717
0
      });
718
719
1
  Register(
720
1
      "list_tablet_server_log_locations", "",
721
0
      [client](const CLIArguments&) -> Status {
722
0
        RETURN_NOT_OK_PREPEND(client->ListTabletServersLogLocations(),
723
0
                              "Unable to list tablet server log locations");
724
0
        return Status::OK();
725
0
      });
726
727
1
  Register(
728
1
      "list_tablets_for_tablet_server", " <ts_uuid>",
729
0
      [client](const CLIArguments& args) -> Status {
730
0
        if (args.size() != 1) {
731
0
          return ClusterAdminCli::kInvalidArguments;
732
0
        }
733
0
        const string& ts_uuid = args[0];
734
0
        RETURN_NOT_OK_PREPEND(client->ListTabletsForTabletServer(ts_uuid),
735
0
                              "Unable to list tablet server tablets");
736
0
        return Status::OK();
737
0
      });
738
739
1
  Register(
740
1
      "set_load_balancer_enabled", " <0|1>",
741
0
      [client](const CLIArguments& args) -> Status {
742
0
        if (args.size() != 1) {
743
0
          return ClusterAdminCli::kInvalidArguments;
744
0
        }
745
746
0
        const bool is_enabled = VERIFY_RESULT(CheckedStoi(args[0])) != 0;
747
0
        RETURN_NOT_OK_PREPEND(client->SetLoadBalancerEnabled(is_enabled),
748
0
                              "Unable to change load balancer state");
749
0
        return Status::OK();
750
0
      });
751
752
1
  Register(
753
1
      "get_load_balancer_state", "",
754
0
      [client](const CLIArguments& args) -> Status {
755
0
        if (args.size() != 0) {
756
0
          return ClusterAdminCli::kInvalidArguments;
757
0
        }
758
759
0
        RETURN_NOT_OK_PREPEND(client->GetLoadBalancerState(),
760
0
                              "Unable to get the load balancer state");
761
0
        return Status::OK();
762
0
      });
763
764
1
  Register(
765
1
      "get_load_move_completion", "",
766
0
      [client](const CLIArguments&) -> Status {
767
0
        RETURN_NOT_OK_PREPEND(client->GetLoadMoveCompletion(),
768
0
                              "Unable to get load completion");
769
0
        return Status::OK();
770
0
      });
771
772
1
  Register(
773
1
      "get_leader_blacklist_completion", "",
774
0
      [client](const CLIArguments&) -> Status {
775
0
        RETURN_NOT_OK_PREPEND(client->GetLeaderBlacklistCompletion(),
776
0
                              "Unable to get leader blacklist completion");
777
0
        return Status::OK();
778
0
      });
779
780
1
  Register(
781
1
      "get_is_load_balancer_idle", "",
782
0
      [client](const CLIArguments&) -> Status {
783
0
        RETURN_NOT_OK_PREPEND(client->GetIsLoadBalancerIdle(),
784
0
                              "Unable to get is load balancer idle");
785
0
        return Status::OK();
786
0
      });
787
788
1
  Register(
789
1
      "list_leader_counts", " <table>",
790
0
      [client](const CLIArguments& args) -> Status {
791
0
        const auto table_name = VERIFY_RESULT(
792
0
            ResolveSingleTableName(client, args.begin(), args.end()));
793
0
        RETURN_NOT_OK_PREPEND(client->ListLeaderCounts(table_name),
794
0
                              "Unable to get leader counts");
795
0
        return Status::OK();
796
0
      });
797
798
1
  Register(
799
1
      "setup_redis_table", "",
800
0
      [client](const CLIArguments&) -> Status {
801
0
        RETURN_NOT_OK_PREPEND(client->SetupRedisTable(),
802
0
                              "Unable to setup Redis keyspace and table");
803
0
        return Status::OK();
804
0
      });
805
806
1
  Register(
807
1
      "drop_redis_table", "",
808
0
      [client](const CLIArguments&) -> Status {
809
0
        RETURN_NOT_OK_PREPEND(client->DropRedisTable(),
810
0
                              "Unable to drop Redis table");
811
0
        return Status::OK();
812
0
      });
813
814
1
  Register(
815
1
      "get_universe_config", "",
816
1
      std::bind(&GetUniverseConfig, client, _1));
817
818
1
  Register(
819
1
      "change_blacklist", Format(" <$0|$1> <ip_addr>:<port> [<ip_addr>:<port>]...",
820
1
          kBlacklistAdd, kBlacklistRemove),
821
1
      std::bind(&ChangeBlacklist, client, _1, false, "Unable to change blacklist"));
822
823
1
  Register(
824
1
      "change_leader_blacklist", Format(" <$0|$1> <ip_addr>:<port> [<ip_addr>:<port>]...",
825
1
          kBlacklistAdd, kBlacklistRemove),
826
1
      std::bind(&ChangeBlacklist, client, _1, true, "Unable to change leader blacklist"));
827
828
1
  Register(
829
1
      "master_leader_stepdown", " [dest_uuid]",
830
1
      std::bind(&MasterLeaderStepDown, client, _1));
831
832
1
  Register(
833
1
      "leader_stepdown", " <tablet_id> [dest_ts_uuid]",
834
1
      std::bind(&LeaderStepDown, client, _1));
835
836
1
  Register(
837
1
      "split_tablet", " <tablet_id>",
838
0
      [client](const CLIArguments& args) -> Status {
839
0
        if (args.size() < 1) {
840
0
          return ClusterAdminCli::kInvalidArguments;
841
0
        }
842
0
        const string tablet_id = args[0];
843
0
        RETURN_NOT_OK_PREPEND(client->SplitTablet(tablet_id),
844
0
                              Format("Unable to start split of tablet $0", tablet_id));
845
0
        return Status::OK();
846
0
      });
847
848
1
  Register(
849
1
      "create_transaction_table", " <table_name>",
850
0
      [client](const CLIArguments& args) -> Status {
851
0
        if (args.size() < 1) {
852
0
          return ClusterAdminCli::kInvalidArguments;
853
0
        }
854
0
        const string table_name = args[0];
855
0
        RETURN_NOT_OK_PREPEND(client->CreateTransactionsStatusTable(table_name),
856
0
                              Format("Unable to create transaction table named $0", table_name));
857
0
        return Status::OK();
858
0
      });
859
860
1
  Register(
861
1
      "ysql_catalog_version", "",
862
0
      [client](const CLIArguments&) -> Status {
863
0
        RETURN_NOT_OK_PREPEND(client->GetYsqlCatalogVersion(),
864
0
                              "Unable to get catalog version");
865
0
        return Status::OK();
866
0
      });
867
868
1
  RegisterJson("ddl_log", "", std::bind(&DdlLog, client, _1));
869
870
1
  Register(
871
1
      "upgrade_ysql", "",
872
0
      [client](const CLIArguments&) -> Status {
873
0
        RETURN_NOT_OK_PREPEND(client->UpgradeYsql(),
874
0
                              "Unable to upgrade YSQL cluster");
875
0
        return Status::OK();
876
0
      });
877
878
1
  Register(
879
      // Today we have a weird pattern recognization for table name.
880
      // The expected input argument for the <table> is:
881
      // <db type>.<namespace> <table name>
882
      // (with a space in between).
883
      // So the expected arguement size is 3 (= 2 for the table name + 1 for the retention time).
884
0
      "set_wal_retention_secs", " <table> <seconds>", [client](const CLIArguments& args) -> Status {
885
0
        RETURN_NOT_OK(CheckArgumentsCount(args.size(), 3, 3));
886
887
0
        uint32_t wal_ret_secs = 0;
888
0
        const auto table_name = VERIFY_RESULT(
889
0
            ResolveSingleTableName(client, args.begin(), args.end(),
890
0
            [&wal_ret_secs] (auto i, const auto& end) -> Status {
891
0
              if (PREDICT_FALSE(i == end)) {
892
0
                return STATUS(InvalidArgument, "Table name not found in the command");
893
0
              }
894
895
0
              const auto raw_time = VERIFY_RESULT(CheckedStoi(*i));
896
0
              if (raw_time < 0) {
897
0
                return STATUS(
898
0
                    InvalidArgument, "WAL retention time must be non-negative integer in seconds");
899
0
              }
900
0
              wal_ret_secs = static_cast<uint32_t>(raw_time);
901
0
              return Status::OK();
902
0
            }
903
0
            ));
904
0
        RETURN_NOT_OK_PREPEND(
905
0
            client->SetWalRetentionSecs(table_name, wal_ret_secs),
906
0
            "Unable to set WAL retention time (sec) for the cluster");
907
0
        return Status::OK();
908
0
      });
909
910
0
  Register("get_wal_retention_secs", " <table>", [client](const CLIArguments& args) -> Status {
911
0
    RETURN_NOT_OK(CheckArgumentsCount(args.size(), 2, 2));
912
0
    const auto table_name = VERIFY_RESULT(ResolveSingleTableName(client, args.begin(), args.end()));
913
0
    RETURN_NOT_OK(client->GetWalRetentionSecs(table_name));
914
0
    return Status::OK();
915
0
  });
916
1
} // NOLINT, prevents long function message
917
918
Result<std::vector<client::YBTableName>> ResolveTableNames(
919
    ClusterAdminClientClass* client,
920
    CLIArgumentsIterator i,
921
    const CLIArgumentsIterator& end,
922
    const TailArgumentsProcessor& tail_processor,
923
0
    bool allow_namespace_only) {
924
0
  auto resolver = VERIFY_RESULT(client->BuildTableNameResolver());
925
0
  auto tail = i;
926
  // Greedy algorithm of taking as much tables as possible.
927
0
  for (; i != end; ++i) {
928
0
    const auto result = resolver.Feed(*i);
929
0
    if (!result.ok()) {
930
      // If tail arguments were not processed suppose it is bad table
931
      // and return its parsing error instead.
932
0
      if (tail_processor && tail_processor(tail, end).ok()) {
933
0
        break;
934
0
      }
935
0
      return result.status();
936
0
    }
937
0
    if (*result) {
938
0
      tail = std::next(i);
939
0
    }
940
0
  }
941
942
0
  auto& tables = resolver.values();
943
  // Handle case when no table name is followed keyspace.
944
0
  if (tail != end) {
945
0
    if (tail_processor) {
946
0
      RETURN_NOT_OK(tail_processor(tail, end));
947
0
    } else {
948
0
      if (allow_namespace_only && tables.empty()) {
949
0
        auto last_namespace = resolver.last_namespace();
950
0
        if (!last_namespace.name().empty()) {
951
0
          client::YBTableName table_name;
952
0
          table_name.GetFromNamespaceIdentifierPB(last_namespace);
953
0
          return std::vector<client::YBTableName>{table_name};
954
0
        }
955
0
      }
956
0
      return STATUS(InvalidArgument, "Table name is missed");
957
0
    }
958
0
  }
959
0
  if (tables.empty()) {
960
0
    return STATUS(InvalidArgument, "Empty list of tables");
961
0
  }
962
0
  return std::move(tables);
963
0
}
964
965
Result<client::YBTableName> ResolveSingleTableName(ClusterAdminClientClass* client,
966
                                                   CLIArgumentsIterator i,
967
                                                   const CLIArgumentsIterator& end,
968
0
                                                   TailArgumentsProcessor tail_processor) {
969
0
  auto tables = VERIFY_RESULT(ResolveTableNames(client, i, end, tail_processor));
970
0
  if (tables.size() != 1) {
971
0
    return STATUS_FORMAT(InvalidArgument, "Single table expected, $0 found", tables.size());
972
0
  }
973
0
  return std::move(tables.front());
974
0
}
975
976
0
Status CheckArgumentsCount(size_t count, size_t min, size_t max) {
977
0
  if (count < min) {
978
0
    return STATUS_FORMAT(
979
0
        InvalidArgument, "Too few arguments $0, should be in range [$1, $2]", count, min, max);
980
0
  }
981
982
0
  if (count > max) {
983
0
    return STATUS_FORMAT(
984
0
        InvalidArgument, "Too many arguments $0, should be in range [$1, $2]", count, min, max);
985
0
  }
986
987
0
  return Status::OK();
988
0
}
989
990
}  // namespace tools
991
}  // namespace yb
992
993
1
int main(int argc, char** argv) {
994
1
  yb::Status s = yb::tools::enterprise::ClusterAdminCli().Run(argc, argv);
995
1
  if (s.ok()) {
996
1
    return 0;
997
1
  }
998
999
0
  if (s.IsInvalidArgument()) {
1000
0
    google::ShowUsageWithFlagsRestrict(argv[0], __FILE__);
1001
0
  }
1002
1003
0
  return 1;
1004
0
}