/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 | } |