YugabyteDB (2.13.1.0-b60, 21121d69985fbf76aa6958d8f04a9bfa936293b5)

Coverage Report

Created: 2022-03-22 16:43

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