blob: 6bd568492ed9d524b7a9c2626faa87b9bdc1dfb1 [file] [log] [blame]
/** @file
@section license License
Licensed to the Apache Software Foundation (ASF) under one
or more contributor license agreements. See the NOTICE file
distributed with this work for additional information
regarding copyright ownership. The ASF licenses this file
to you under the Apache License, Version 2.0 (the
"License"); you may not use this file except in compliance
with the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
#include "CtrlCommands.h"
#include <fstream>
#include <unordered_map>
#include <chrono>
#include <thread>
#include <csignal>
#include <unistd.h>
#include <swoc/TextView.h>
#include <swoc/BufferWriter.h>
#include <swoc/bwf_base.h>
#include "jsonrpc/CtrlRPCRequests.h"
#include "jsonrpc/ctrl_yaml_codecs.h"
#include "TrafficCtlStatus.h"
namespace
{
/// We use yamlcpp as codec implementation.
using Codec = yamlcpp_json_emitter;
using StringToFormatFlagsMap = std::unordered_map<std::string_view, BasePrinter::Options::FormatFlags>;
const StringToFormatFlagsMap _Fmt_str_to_enum = {
{"json", BasePrinter::Options::FormatFlags::JSON},
{"rpc", BasePrinter::Options::FormatFlags::RPC }
};
constexpr std::string_view YAML_PREFIX{"records."};
constexpr std::string_view RECORD_PREFIX{"proxy.config."};
/// Convert YAML-style path (records.diags.debug) to record name format (proxy.config.diags.debug).
/// If the path doesn't start with "records.", it's returned unchanged.
std::string
yaml_to_record_name(std::string_view path)
{
swoc::TextView tv{path};
if (tv.starts_with(YAML_PREFIX)) {
return std::string{RECORD_PREFIX} + std::string{path.substr(YAML_PREFIX.size())};
}
return std::string{path};
}
} // namespace
BasePrinter::Options::FormatFlags
parse_print_opts(ts::Arguments *args)
{
BasePrinter::Options::FormatFlags val{BasePrinter::Options::FormatFlags::NOT_SET};
if (args->get("default")) {
val |= BasePrinter::Options::FormatFlags::SHOW_DEFAULT;
}
if (args->get("records")) { // records overrule the rest of the formats.
val |= BasePrinter::Options::FormatFlags::RECORDS;
return val;
}
if (auto data = args->get("format"); data) {
StringToFormatFlagsMap::const_iterator search = _Fmt_str_to_enum.find(data.value());
if (search != std::end(_Fmt_str_to_enum)) {
val |= search->second;
}
}
return val;
}
std::atomic_int CtrlCommand::Signal_Flagged{0};
//------------------------------------------------------------------------------------------------------------------------------------
CtrlCommand::CtrlCommand(ts::Arguments *args) : _arguments(args) {}
void
CtrlCommand::execute()
{
if (_invoked_func) {
_invoked_func();
} else {
throw std::logic_error("CtrlCommand::execute(): Internal error. There should be a function to invoke. (_invoked_func not set)");
}
}
std::string
RPCAccessor::invoke_rpc(std::string const &request, std::chrono::milliseconds timeout_ms, int attempts)
{
if (_printer->print_rpc_message()) {
std::string text;
swoc::bwprint(text, "--> {}", request);
_printer->write_debug(std::string_view{text});
}
if (auto resp = _rpcClient.invoke(request, timeout_ms, attempts); !resp.empty()) {
// all good.
if (_printer->print_rpc_message()) {
std::string text;
swoc::bwprint(text, "<-- {}", resp);
_printer->write_debug(std::string_view{text});
}
return resp;
}
return {};
}
shared::rpc::JSONRPCResponse
RPCAccessor::invoke_rpc(shared::rpc::ClientRequest const &request, std::chrono::milliseconds timeout_ms, int attempts)
{
std::string encodedRequest = Codec::encode(request);
std::string resp = invoke_rpc(encodedRequest, timeout_ms, attempts);
return Codec::decode(resp);
}
void
RPCAccessor::invoke_rpc(shared::rpc::ClientRequest const &request, std::string &resp, std::chrono::milliseconds timeout_ms,
int attempts)
{
std::string encodedRequest = Codec::encode(request);
resp = invoke_rpc(encodedRequest, timeout_ms, attempts);
}
// -----------------------------------------------------------------------------------------------------------------------------------
ConfigCommand::ConfigCommand(ts::Arguments *args) : RecordCommand(args)
{
BasePrinter::Options printOpts(parse_print_opts(args));
if (args->get(MATCH_STR)) {
_printer = std::make_unique<RecordPrinter>(printOpts);
_invoked_func = [&]() { config_match(); };
} else if (args->get(GET_STR)) {
_printer = std::make_unique<RecordPrinter>(printOpts);
_invoked_func = [&]() { config_get(); };
} else if (args->get(DIFF_STR)) {
_printer = std::make_unique<DiffConfigPrinter>(printOpts);
_invoked_func = [&]() { config_diff(); };
} else if (args->get(DESCRIBE_STR)) {
_printer = std::make_unique<RecordDescribePrinter>(printOpts);
_invoked_func = [&]() { config_describe(); };
} else if (args->get(DEFAULTS_STR)) {
_printer = std::make_unique<RecordPrinter>(printOpts);
_invoked_func = [&]() { config_defaults(); };
} else if (args->get(SET_STR)) {
_printer = std::make_unique<ConfigSetPrinter>(printOpts);
_invoked_func = [&]() { config_set(); };
} else if (args->get(RESET_STR)) {
_printer = std::make_unique<ConfigSetPrinter>(printOpts);
_invoked_func = [&]() { config_reset(); };
} else if (args->get(STATUS_STR)) {
_printer = std::make_unique<ConfigStatusPrinter>(printOpts);
_invoked_func = [&]() { config_status(); };
} else if (args->get(RELOAD_STR)) {
_printer = std::make_unique<ConfigReloadPrinter>(printOpts);
_invoked_func = [&]() { config_reload(); };
} else if (args->get(REGISTRY_STR)) {
_printer = std::make_unique<ConfigShowFileRegistryPrinter>(printOpts);
_invoked_func = [&]() { config_show_file_registry(); };
} else {
// work in here.
}
}
shared::rpc::JSONRPCResponse
RecordCommand::record_fetch(ts::ArgumentData argData, bool isRegex, RecordQueryType recQueryType)
{
shared::rpc::RecordLookupRequest request;
for (auto &&it : argData) {
request.emplace_rec(it, isRegex,
recQueryType == RecordQueryType::CONFIG ? shared::rpc::CONFIG_REC_TYPES : shared::rpc::METRIC_REC_TYPES);
}
return invoke_rpc(request);
}
std::string
CtrlCommand::invoke_rpc(std::string const &request)
{
auto timeout = std::chrono::milliseconds(std::stoi(get_parsed_arguments()->get("read-timeout").value()));
auto attempts = std::stoi(get_parsed_arguments()->get("read-attempts").value());
return RPCAccessor::invoke_rpc(request, timeout, attempts);
}
shared::rpc::JSONRPCResponse
CtrlCommand::invoke_rpc(shared::rpc::ClientRequest const &request)
{
auto timeout = std::chrono::milliseconds(std::stoi(get_parsed_arguments()->get("read-timeout").value()));
auto attempts = std::stoi(get_parsed_arguments()->get("read-attempts").value());
return RPCAccessor::invoke_rpc(request, timeout, attempts);
}
void
ConfigCommand::config_match()
{
_printer->write_output(record_fetch(get_parsed_arguments()->get(MATCH_STR), shared::rpc::REGEX, RecordQueryType::CONFIG));
}
void
ConfigCommand::config_get()
{
_printer->write_output(record_fetch(get_parsed_arguments()->get(GET_STR), shared::rpc::NOT_REGEX, RecordQueryType::CONFIG));
}
void
ConfigCommand::config_describe()
{
_printer->write_output(record_fetch(get_parsed_arguments()->get(DESCRIBE_STR), shared::rpc::NOT_REGEX, RecordQueryType::CONFIG));
}
void
ConfigCommand::config_defaults()
{
const bool configs{true};
shared::rpc::JSONRPCResponse response = invoke_rpc(GetAllRecordsRequest{configs});
_printer->write_output(response);
}
void
ConfigCommand::config_diff()
{
GetAllRecordsRequest request{true};
shared::rpc::JSONRPCResponse response = invoke_rpc(request);
_printer->write_output(response);
}
void
ConfigCommand::config_status()
{
ConfigStatusRequest request;
shared::rpc::JSONRPCResponse response = invoke_rpc(request);
_printer->write_output(response);
}
void
ConfigCommand::config_set()
{
auto const &data = get_parsed_arguments()->get(SET_STR);
ConfigSetRecordRequest request{
{data[0], data[1]}
};
shared::rpc::JSONRPCResponse response = invoke_rpc(request);
_printer->write_output(response);
}
void
ConfigCommand::config_reset()
{
auto const &paths = get_parsed_arguments()->get(RESET_STR);
// Build lookup request - always use REGEX to support partial path matching
shared::rpc::RecordLookupRequest lookup_request;
if (paths.empty() || (paths.size() == 1 && paths[0] == "records")) {
lookup_request.emplace_rec(".*", shared::rpc::REGEX, shared::rpc::CONFIG_REC_TYPES);
} else {
for (auto const &path : paths) {
// Convert YAML-style path (records.*) to record name format (proxy.config.*)
auto record_path = yaml_to_record_name(path);
lookup_request.emplace_rec(record_path, shared::rpc::REGEX, shared::rpc::CONFIG_REC_TYPES);
}
}
// Lookup matching records
auto lookup_response = invoke_rpc(lookup_request);
if (lookup_response.is_error()) {
_printer->write_output(lookup_response);
return;
}
// Build reset request from modified records (current != default)
auto const &records = lookup_response.result.as<shared::rpc::RecordLookUpResponse>();
ConfigSetRecordRequest set_request;
for (auto const &rec : records.recordList) {
if (rec.currentValue != rec.defaultValue) {
set_request.params.push_back(ConfigSetRecordRequest::Params{rec.name, rec.defaultValue});
}
}
if (set_request.params.size() == 0) {
std::cout << "No records to reset (all matching records are already at default values)\n";
return;
}
_printer->write_output(invoke_rpc(set_request));
}
void
ConfigCommand::config_reload()
{
_printer->write_output(invoke_rpc(ConfigReloadRequest{}));
}
void
ConfigCommand::config_show_file_registry()
{
_printer->write_output(invoke_rpc(ConfigShowFileRegistryRequest{}));
}
//------------------------------------------------------------------------------------------------------------------------------------
MetricCommand::MetricCommand(ts::Arguments *args) : RecordCommand(args)
{
BasePrinter::Options printOpts{parse_print_opts(args)};
if (args->get(MATCH_STR)) {
_printer = std::make_unique<MetricRecordPrinter>(printOpts);
_invoked_func = [&]() { metric_match(); };
} else if (args->get(GET_STR)) {
_printer = std::make_unique<MetricRecordPrinter>(printOpts);
_invoked_func = [&]() { metric_get(); };
} else if (args->get(DESCRIBE_STR)) {
_printer = std::make_unique<RecordDescribePrinter>(printOpts);
_invoked_func = [&]() { metric_describe(); };
} else if (args->get(MONITOR_STR)) {
_printer = std::make_unique<MetricRecordPrinter>(printOpts);
_invoked_func = [&]() { metric_monitor(); };
}
}
void
MetricCommand::metric_get()
{
_printer->write_output(record_fetch(get_parsed_arguments()->get(GET_STR), shared::rpc::NOT_REGEX, RecordQueryType::METRIC));
}
void
MetricCommand::metric_match()
{
_printer->write_output(record_fetch(get_parsed_arguments()->get(MATCH_STR), shared::rpc::REGEX, RecordQueryType::METRIC));
}
void
MetricCommand::metric_describe()
{
_printer->write_output(record_fetch(get_parsed_arguments()->get(DESCRIBE_STR), shared::rpc::NOT_REGEX, RecordQueryType::METRIC));
}
void
MetricCommand::metric_monitor()
{
ts::ArgumentData const &arg = get_parsed_arguments()->get(MONITOR_STR);
std::string err_text;
//
// Note: if any of the string->number fails, the exception will be caught by the invoke function from the ArgParser.
//
const int32_t count = std::stoi(get_parsed_arguments()->get("count").value());
int32_t query_count{0};
const int32_t interval = std::stoi(get_parsed_arguments()->get("interval").value());
// default count is 0.
if (count < 0 || interval <= 0) {
throw std::runtime_error(swoc::bwprint(err_text, "monitor: invalid input, count: {}(>=0), interval: {}(>=1)", count, interval));
}
// keep track of each metric
struct ctx {
float min{std::numeric_limits<float>::max()};
float max{std::numeric_limits<float>::lowest()};
float sum{0.0f};
float last{0.0f};
};
// Keep track of the requested metric(s), we support more than one at the same time.
// To be used to print all the stats. This is a lambda function as this could
// be called when SIGINT is invoked, so we dump what we have before exit.
auto dump = [&](std::unordered_map<std::string, ctx> const &_summary) {
if (_summary.size() == 0) {
// nothing to report.
return;
}
_printer->write_output(swoc::bwprint(err_text, "--- metric monitor statistics({}) ---", query_count));
for (auto const &item : _summary) {
ctx const &s = item.second;
const int avg = s.sum / query_count;
_printer->write_output(swoc::bwprint(err_text, "┌ {}\n└─ min/avg/max = {:.5}/{}/{:.5}", item.first, s.min, avg, s.max));
}
};
std::unordered_map<std::string, ctx> summary;
while (!Signal_Flagged.load()) {
// Request will hold all metrics in a single message.
shared::rpc::JSONRPCResponse const &resp = record_fetch(arg, shared::rpc::NOT_REGEX, RecordQueryType::METRIC);
if (resp.is_error()) { // something went wrong in the server, report it.
_printer->write_output(resp);
return;
}
auto const &response = resp.result.as<shared::rpc::RecordLookUpResponse>();
if (response.errorList.size() && response.recordList.size() == 0) {
// nothing to be done or report, use '-f rpc' for details.
break;
}
for (auto &&rec : response.recordList) { // requested metric(s)
auto &s = summary[rec.name]; // We will update it.
const float val = std::stof(rec.currentValue);
s.sum += val;
s.max = std::max<float>(s.max, val);
s.min = std::min<float>(s.min, val);
std::string symbol;
if (query_count > 0) {
if (val > s.last) {
symbol = "+";
} else if (val < s.last) {
symbol = "-";
}
}
s.last = val;
_printer->write_output(swoc::bwprint(err_text, "{}: {} {}", rec.name, rec.currentValue, symbol));
}
if ((query_count++ == count - 1) && count > 0 /* could be a forever loop*/) {
break;
}
sleep(interval);
}
// all done, print summary.
dump(summary);
}
//------------------------------------------------------------------------------------------------------------------------------------
// TODO, let call the super const
HostCommand::HostCommand(ts::Arguments *args) : CtrlCommand(args)
{
BasePrinter::Options printOpts{parse_print_opts(args)};
if (get_parsed_arguments()->get(STATUS_STR)) {
_printer = std::make_unique<GetHostStatusPrinter>(printOpts);
_invoked_func = [&]() { status_get(); };
} else if (get_parsed_arguments()->get(DOWN_STR)) {
_printer = std::make_unique<SetHostStatusPrinter>(printOpts);
_invoked_func = [&]() { status_down(); };
} else if (get_parsed_arguments()->get(UP_STR)) {
_printer = std::make_unique<SetHostStatusPrinter>(printOpts);
_invoked_func = [&]() { status_up(); };
}
}
void
HostCommand::status_get()
{
auto const &data = get_parsed_arguments()->get(STATUS_STR);
HostGetStatusRequest request{
{std::begin(data), std::end(data)}
};
auto response = invoke_rpc(request);
_printer->write_output(response);
}
void
HostCommand::status_down()
{
auto hosts = get_parsed_arguments()->get(DOWN_STR);
HostSetStatusRequest request{
{HostSetStatusRequest::Params::Op::DOWN,
{std::begin(hosts), std::end(hosts)},
get_parsed_arguments()->get(REASON_STR).value(),
"0"}
};
auto response = invoke_rpc(request);
_printer->write_output(response);
}
void
HostCommand::status_up()
{
auto hosts = get_parsed_arguments()->get(UP_STR);
HostSetStatusRequest request{
{HostSetStatusRequest::Params::Op::UP,
{std::begin(hosts), std::end(hosts)},
get_parsed_arguments()->get(REASON_STR).value(),
"0"}
};
auto response = invoke_rpc(request);
_printer->write_output(response);
}
//------------------------------------------------------------------------------------------------------------------------------------
HostDBCommand::HostDBCommand(ts::Arguments *args) : CtrlCommand(args)
{
BasePrinter::Options printOpts{parse_print_opts(args)};
if (get_parsed_arguments()->get(STATUS_STR)) {
_printer = std::make_unique<HostDBStatusPrinter>(printOpts);
_invoked_func = [&]() { status_get(); };
}
}
void
HostDBCommand::status_get()
{
HostDBGetStatusRequest::Params params;
auto const &data = get_parsed_arguments()->get(STATUS_STR);
if (data.size() >= 1) {
params = {
data[0],
};
}
HostDBGetStatusRequest request{params};
auto response = invoke_rpc(request);
_printer->write_output(response);
}
//------------------------------------------------------------------------------------------------------------------------------------
PluginCommand::PluginCommand(ts::Arguments *args) : CtrlCommand(args)
{
if (get_parsed_arguments()->get(MSG_STR)) {
_invoked_func = [&]() { plugin_msg(); };
}
_printer = std::make_unique<GenericPrinter>(parse_print_opts(args));
}
void
PluginCommand::plugin_msg()
{
auto msgs = get_parsed_arguments()->get(MSG_STR);
BasicPluginMessageRequest::Params params;
params.tag = msgs[0];
if (msgs.size() > 1) {
// have a value
params.str = msgs[1];
}
BasicPluginMessageRequest request{params};
auto response = invoke_rpc(request);
_printer->write_output(response);
}
//------------------------------------------------------------------------------------------------------------------------------------
DirectRPCCommand::DirectRPCCommand(ts::Arguments *args) : CtrlCommand(args)
{
BasePrinter::Options printOpts{parse_print_opts(args)};
if (get_parsed_arguments()->get(GET_API_STR)) {
_printer = std::make_unique<RPCAPIPrinter>(printOpts);
_invoked_func = [&]() { get_rpc_api(); };
return;
} else if (get_parsed_arguments()->get(FILE_STR)) {
_invoked_func = [&]() { from_file_request(); };
} else if (get_parsed_arguments()->get(INPUT_STR)) {
_invoked_func = [&]() { read_from_input(); };
} else if (get_parsed_arguments()->get(INVOKE_STR)) {
_invoked_func = [&]() { invoke_method(); };
if (printOpts._format & BasePrinter::Options::FormatFlags::NOT_SET) {
// overwrite this and let it drop json instead.
printOpts._format |= BasePrinter::Options::FormatFlags::RPC;
}
}
_printer = std::make_unique<GenericPrinter>(printOpts);
}
bool
DirectRPCCommand::validate_input(std::string const &in) const
{
// validate the input
YAML::Node content = YAML::Load(in);
if (content.Type() != YAML::NodeType::Map && content.Type() != YAML::NodeType::Sequence) {
return false;
}
return true;
}
void
DirectRPCCommand::from_file_request()
{
// TODO: remove all the output messages from here if possible
auto filenames = get_parsed_arguments()->get(FILE_STR);
for (auto &&filename : filenames) {
std::string text;
// run some basic validation on the passed files, they should
try {
std::ifstream file(filename);
std::string content((std::istreambuf_iterator<char>(file)), std::istreambuf_iterator<char>());
if (!validate_input(content)) {
_printer->write_output(
swoc::bwprint(text, "Content not accepted. expecting a valid sequence or structure. {} skipped.\n", filename));
continue;
}
std::string const &response = invoke_rpc(content);
if (_printer->is_json_format()) {
// as we have the raw json in here, we cna just directly print it
_printer->write_output(response);
} else {
_printer->write_output(swoc::bwprint(text, "\n[ {} ]\n --> \n{}\n", filename, content));
_printer->write_output(swoc::bwprint(text, "<--\n{}\n", response));
}
} catch (std::exception const &ex) {
App_Exit_Status_Code = CTRL_EX_ERROR;
_printer->write_output(swoc::bwprint(text, "Error found: {}\n", ex.what()));
}
}
}
void
DirectRPCCommand::get_rpc_api()
{
auto response = invoke_rpc(ShowRegisterHandlersRequest{});
_printer->write_output(response);
}
void
DirectRPCCommand::read_from_input()
{
// TODO: remove all the output messages from here if possible
std::string text;
try {
_printer->write_output(">> Ctrl-D to fire the request. Ctrl-C to exit\n");
std::cin >> std::noskipws;
// read cin.
std::string content((std::istreambuf_iterator<char>(std::cin)), std::istreambuf_iterator<char>());
if (!get_parsed_arguments()->get(RAW_STR) && !validate_input(content)) {
_printer->write_output(swoc::bwprint(text, "Content not accepted. expecting a valid sequence or structure\n"));
return;
}
std::string const &response = invoke_rpc(content);
_printer->write_output("--> Request sent.\n");
_printer->write_output(swoc::bwprint(text, "\n<-- {}\n", response));
} catch (std::exception const &ex) {
App_Exit_Status_Code = CTRL_EX_ERROR;
_printer->write_output(swoc::bwprint(text, "Error found: {}\n", ex.what()));
}
}
void
DirectRPCCommand::invoke_method()
{
shared::rpc::ClientRequest request;
if (auto method = get_parsed_arguments()->get(INVOKE_STR); method) {
request.method = method.value();
// We build up the parameter content if passed.
if (auto params = get_parsed_arguments()->get(PARAMS_STR); params) {
std::ostringstream ss;
for (auto &&param : params) {
ss << param;
ss << '\n';
}
request.params = YAML::Load(ss.str()); // let if fail if this is bad.
}
_printer->write_output(invoke_rpc(request));
}
}
//------------------------------------------------------------------------------------------------------------------------------------
ServerCommand::ServerCommand(ts::Arguments *args) : CtrlCommand(args)
{
BasePrinter::Options printOpts{parse_print_opts(args)};
if (get_parsed_arguments()->get(DRAIN_STR)) {
_printer = std::make_unique<GenericPrinter>(printOpts);
_invoked_func = [&]() { server_drain(); };
} else if (get_parsed_arguments()->get(DEBUG_STR)) {
_printer = std::make_unique<GenericPrinter>(printOpts);
_invoked_func = [&]() { server_debug(); };
} else if (get_parsed_arguments()->get(STATUS_STR)) {
_printer = std::make_unique<ServerStatusPrinter>(printOpts);
_invoked_func = [&]() { server_status(); };
}
}
void
ServerCommand::server_drain()
{
shared::rpc::JSONRPCResponse response;
// TODO, can call_request take a && ?? if needed in the cmd just pass by ref.
if (get_parsed_arguments()->get(UNDO_STR)) {
response = invoke_rpc(ServerStopDrainRequest{});
} else {
const bool newConn = get_parsed_arguments()->get(NO_NEW_CONN_STR);
ServerStartDrainRequest request{{newConn}};
response = invoke_rpc(request);
}
_printer->write_output(response);
}
void
ServerCommand::server_debug()
{
// Set ATS to enable or disable debug at runtime.
const bool enable = get_parsed_arguments()->get(ENABLE_STR);
const bool append = get_parsed_arguments()->get(APPEND_STR);
// If the following is not passed as options then the request will ignore them as default values
// will be set.
std::string tags = get_parsed_arguments()->get(TAGS_STR).value();
const std::string client_ip = get_parsed_arguments()->get(CLIENT_IP_STR).value();
// If append mode is enabled and tags are provided, fetch current tags and combine
if (append && !tags.empty()) {
shared::rpc::RecordLookupRequest lookup_request;
lookup_request.emplace_rec("proxy.config.diags.debug.tags", shared::rpc::NOT_REGEX, shared::rpc::CONFIG_REC_TYPES);
auto lookup_response = invoke_rpc(lookup_request);
if (!lookup_response.is_error()) {
auto const &records = lookup_response.result.as<shared::rpc::RecordLookUpResponse>();
if (!records.recordList.empty()) {
std::string current_tags = records.recordList[0].currentValue;
if (!current_tags.empty()) {
// Combine: current|new
tags = current_tags + "|" + tags;
}
}
}
}
const SetDebugServerRequest request{enable, tags, client_ip};
shared::rpc::JSONRPCResponse const &response = invoke_rpc(request);
swoc::LocalBufferWriter<512> bw;
bw.print("■ TS Runtime debug set to »{}({})«", enable ? "ON" : "OFF", enable ? (!client_ip.empty() ? "2" : "1") : "0");
if (enable) {
bw.print(" - tags »\"{}\"«, client_ip »{}«", !tags.empty() ? tags : "unchanged", !client_ip.empty() ? client_ip : "unchanged");
}
if (response.is_error()) {
_printer->write_output(response);
} else {
_printer->write_output(bw.view());
}
}
void
ServerCommand::server_status()
{
shared::rpc::JSONRPCResponse response = invoke_rpc(GetServerStatusRequest{});
_printer->write_output(response);
}
// //------------------------------------------------------------------------------------------------------------------------------------
StorageCommand::StorageCommand(ts::Arguments *args) : CtrlCommand(args)
{
BasePrinter::Options printOpts{parse_print_opts(args)};
if (get_parsed_arguments()->get(STATUS_STR)) {
_printer = std::make_unique<CacheDiskStoragePrinter>(printOpts);
_invoked_func = [&]() { get_storage_status(); };
} else if (get_parsed_arguments()->get(OFFLINE_STR)) {
_printer = std::make_unique<CacheDiskStorageOfflinePrinter>(printOpts);
_invoked_func = [&]() { set_storage_offline(); };
}
}
void
StorageCommand::get_storage_status()
{
auto disks = get_parsed_arguments()->get(STATUS_STR);
GetStorageDeviceStatusRequest request{{{std::begin(disks), std::end(disks)}}};
auto response = invoke_rpc(request);
_printer->write_output(response);
}
void
StorageCommand::set_storage_offline()
{
auto disks = get_parsed_arguments()->get(OFFLINE_STR);
SetStorageDeviceOfflineRequest request{{{std::begin(disks), std::end(disks)}}};
auto response = invoke_rpc(request);
_printer->write_output(response);
}
//------------------------------------------------------------------------------------------------------------------------------------