|
| 1 | +%% ------------------------------------------------------------------- |
| 2 | +%% |
| 3 | +%% Copyright (c) 2016 Basho Technologies, Inc. All Rights Reserved. |
| 4 | +%% |
| 5 | +%% This file is provided to you under the Apache License, |
| 6 | +%% Version 2.0 (the "License"); you may not use this file |
| 7 | +%% except in compliance with the License. You may obtain |
| 8 | +%% a copy of the License at |
| 9 | +%% |
| 10 | +%% http://www.apache.org/licenses/LICENSE-2.0 |
| 11 | +%% |
| 12 | +%% Unless required by applicable law or agreed to in writing, |
| 13 | +%% software distributed under the License is distributed on an |
| 14 | +%% "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| 15 | +%% KIND, either express or implied. See the License for the |
| 16 | +%% specific language governing permissions and limitations |
| 17 | +%% under the License. |
| 18 | +%% |
| 19 | +%% ------------------------------------------------------------------- |
| 20 | +%% @doc This module encapsulates the command line interface for the |
| 21 | +%% sweeper commands: |
| 22 | +%% |
| 23 | +%% status |
| 24 | +%% |
| 25 | +-module(riak_kv_sweeper_cli). |
| 26 | + |
| 27 | +-behaviour(clique_handler). |
| 28 | + |
| 29 | +-include("riak_kv_sweeper.hrl"). |
| 30 | + |
| 31 | +-export([ |
| 32 | + format_sweeps/1, |
| 33 | + register_cli/0, |
| 34 | + status/3 |
| 35 | + ]). |
| 36 | + |
| 37 | +register_cli() -> |
| 38 | + register_all_usage(), |
| 39 | + register_all_commands(). |
| 40 | + |
| 41 | +register_all_usage() -> |
| 42 | + clique:register_usage(["riak-admin", "sweeper"], sweeper_usage()), |
| 43 | + clique:register_usage(["riak-admin", "sweeper", "status"], status_usage()). |
| 44 | + |
| 45 | +register_all_commands() -> |
| 46 | + lists:foreach(fun(Args) -> apply(clique, register_command, Args) end, |
| 47 | + [status_register()]). |
| 48 | + |
| 49 | +status_register() -> |
| 50 | + [["riak-admin", "sweeper", "status"], % Cmd |
| 51 | + [], % KeySpecs |
| 52 | + [], % FlagSpecs |
| 53 | + fun status/3]. % Implementation callback. |
| 54 | + |
| 55 | + |
| 56 | +sweeper_usage() -> |
| 57 | + [ |
| 58 | + "riak-admin sweeper <sub-command>\n\n", |
| 59 | + " Sub-commands:\n", |
| 60 | + " status Display sweeper status\n", |
| 61 | + " Use --help after a sub-command for more details.\n" |
| 62 | + ]. |
| 63 | + |
| 64 | +status_usage() -> |
| 65 | + ["riak-admin sweeper status\n\n", |
| 66 | + " Display sweeper status\n"]. |
| 67 | + |
| 68 | +status(_CmdBase, [], []) -> |
| 69 | + {Participants, Sweeps} = riak_kv_sweeper:status(), |
| 70 | + ParticipantsTable = format_participants(Participants), |
| 71 | + SweepsTable = format_sweeps(Sweeps), |
| 72 | + ActiveSweeps = format_active_sweeps(Sweeps), |
| 73 | + [ParticipantsTable, |
| 74 | + SweepsTable] |
| 75 | + ++ ActiveSweeps. |
| 76 | + |
| 77 | +format_sweeps(Sweeps) -> |
| 78 | + Now = os:timestamp(), |
| 79 | + SortedSweeps = lists:keysort(#sweep.index, Sweeps), |
| 80 | + Rows = |
| 81 | + [begin |
| 82 | + StartTime = Sweep#sweep.start_time, |
| 83 | + EndTime = Sweep#sweep.end_time, |
| 84 | + LastSweep = format_timestamp(Now, StartTime), |
| 85 | + Duration = format_timestamp(EndTime, StartTime), |
| 86 | + ResultsString = format_results(Now, Sweep#sweep.results), |
| 87 | + [{"Index", Sweep#sweep.index}, |
| 88 | + {"Last sweep", LastSweep}, |
| 89 | + {"Duration", Duration}, |
| 90 | + {"Results", ResultsString} |
| 91 | + ] |
| 92 | + end |
| 93 | + || Sweep <- SortedSweeps], |
| 94 | + clique_status:table(Rows). |
| 95 | + |
| 96 | +format_active_sweeps(Sweeps) -> |
| 97 | + Header = io_lib:format("~n~s~n", [string:centre(" Active sweeps ", 79, $=)]), |
| 98 | + Rows = [begin |
| 99 | + format_progress(Sweep) |
| 100 | + end |
| 101 | + || #sweep{state = State} = Sweep <- Sweeps, State == running], |
| 102 | + case Rows of |
| 103 | + [] -> |
| 104 | + []; |
| 105 | + Rows -> |
| 106 | + [clique_status:text(Header), |
| 107 | + clique_status:table(Rows)] |
| 108 | + end. |
| 109 | + |
| 110 | +format_progress(#sweep{index = Index, |
| 111 | + active_participants = AcitvePart, |
| 112 | + estimated_keys = {EstimatedKeys, _TS}, |
| 113 | + swept_keys = SweptKeys}) -> |
| 114 | + case EstimatedKeys of |
| 115 | + EstimatedKeys when is_integer(EstimatedKeys) andalso EstimatedKeys > 0 -> |
| 116 | + [{"Index", Index}, |
| 117 | + {"Swept keys", SweptKeys}, |
| 118 | + {"Estimated Keys", EstimatedKeys}, |
| 119 | + {"Active", format_active_participants(AcitvePart)}]; |
| 120 | + _ -> |
| 121 | + [{"Index", Index}, |
| 122 | + {"Swept keys", SweptKeys}, |
| 123 | + {"Active", format_active_participants(AcitvePart)}] |
| 124 | + end; |
| 125 | + |
| 126 | +format_progress(_) -> |
| 127 | + "". |
| 128 | + |
| 129 | +format_active_participants(AcitvePart) -> |
| 130 | + [io_lib:format("| ~s", [atom_to_list(Mod)]) |
| 131 | + || #sweep_participant{module = Mod} <- AcitvePart]. |
| 132 | + |
| 133 | +format_results(Now, Results) -> |
| 134 | + ResultList = dict:to_list(Results), |
| 135 | + SortedResultList = lists:keysort(1, ResultList), |
| 136 | + [begin |
| 137 | + LastResult = format_timestamp(Now, TimeStamp), |
| 138 | + OutcomeString = string:to_upper(atom_to_list(Outcome)), |
| 139 | + io_lib:format(" ~s ~-4s ~-8s", [atom_to_list(Mod), OutcomeString, LastResult]) |
| 140 | + end |
| 141 | + || {Mod, {TimeStamp, Outcome}} <- SortedResultList, lists:member(Outcome, [succ, fail])]. |
| 142 | + |
| 143 | +format_participants(SweepParticipants) -> |
| 144 | + SortedSweepParticipants = lists:keysort(1, SweepParticipants), |
| 145 | + Rows = [format_sweep_participant(SweepParticipant) |
| 146 | + || SweepParticipant <- SortedSweepParticipants], |
| 147 | + clique_status:table(Rows). |
| 148 | + |
| 149 | +format_sweep_participant(#sweep_participant{module = Mod, |
| 150 | + description = Desciption, |
| 151 | + run_interval = Interval}) -> |
| 152 | + IntervalValue = riak_kv_sweeper:get_run_interval(Interval), |
| 153 | + IntervalString = format_interval(IntervalValue * 1000000), |
| 154 | + [{"Module" ,atom_to_list(Mod)}, |
| 155 | + {"Desciption" ,Desciption}, |
| 156 | + {"Interval", IntervalString}]. |
| 157 | + |
| 158 | +format_interval(Interval) -> |
| 159 | + riak_core_format:human_time_fmt("~.1f", Interval). |
| 160 | + |
| 161 | +format_timestamp(_Now, undefined) -> |
| 162 | + "--"; |
| 163 | +format_timestamp(undefined, _) -> |
| 164 | + "--"; |
| 165 | +format_timestamp(Now, TS) -> |
| 166 | + riak_core_format:human_time_fmt("~.1f", timer:now_diff(Now, TS)). |
0 commit comments