| /** @file |
| |
| traffic_ctl |
| |
| @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 "traffic_ctl.h" |
| #include <ctime> |
| #include "records/I_RecDefs.h" |
| #include "records/P_RecUtils.h" |
| #include "ts/apidefs.h" |
| #include "HTTP.h" |
| #include "HttpConnectionCount.h" |
| #include "shared/overridable_txn_vars.h" |
| #include <tscore/BufferWriter.h> |
| |
| struct RecordDescriptionPolicy { |
| using entry_type = TSConfigRecordDescription *; |
| |
| static void |
| free(entry_type e) |
| { |
| TSConfigRecordDescriptionDestroy(e); |
| } |
| |
| static entry_type |
| cast(void *ptr) |
| { |
| return (entry_type)ptr; |
| } |
| }; |
| |
| struct CtrlMgmtRecordDescriptionList : CtrlMgmtList<RecordDescriptionPolicy> { |
| TSMgmtError |
| match(const char *regex) |
| { |
| return TSConfigRecordDescribeMatchMlt(regex, 0u /* flags */, this->list); |
| } |
| }; |
| |
| // Record data type names, indexed by TSRecordT. |
| static const char * |
| rec_typeof(int rec_type) |
| { |
| switch (rec_type) { |
| case TS_REC_INT: |
| return "INT"; |
| case TS_REC_COUNTER: |
| return "COUNTER"; |
| case TS_REC_FLOAT: |
| return "FLOAT"; |
| case TS_REC_STRING: |
| return "STRING"; |
| case TS_REC_UNDEFINED: /* fallthrough */ |
| default: |
| return "UNDEFINED"; |
| } |
| } |
| |
| // Record type name, indexed by RecT. |
| static const char * |
| rec_classof(int rec_class) |
| { |
| switch (rec_class) { |
| case RECT_CONFIG: |
| return "standard config"; |
| case RECT_LOCAL: |
| return "local config"; |
| case RECT_PROCESS: |
| return "process metric"; |
| case RECT_NODE: |
| return "node metric"; |
| case RECT_PLUGIN: |
| return "plugin metric"; |
| default: |
| return "undefined"; |
| } |
| } |
| |
| // Record access control, indexed by RecAccessT. |
| static const char * |
| rec_accessof(int rec_access) |
| { |
| switch (rec_access) { |
| case RECA_NO_ACCESS: |
| return "no access"; |
| case RECA_READ_ONLY: |
| return "read only"; |
| case RECA_NULL: /* fallthrough */ |
| default: |
| return "default"; |
| } |
| } |
| |
| // Record access control, indexed by RecUpdateT. |
| static const char * |
| rec_updateof(int rec_updatetype) |
| { |
| switch (rec_updatetype) { |
| case RECU_DYNAMIC: |
| return "dynamic, no restart"; |
| case RECU_RESTART_TS: |
| return "static, restart traffic_server"; |
| case RECU_RESTART_TM: |
| return "static, restart traffic_manager"; |
| case RECU_NULL: /* fallthrough */ |
| default: |
| return "none"; |
| } |
| } |
| |
| // Record check type, indexed by RecCheckT. |
| static const char * |
| rec_checkof(int rec_checktype) |
| { |
| switch (rec_checktype) { |
| case RECC_STR: |
| return "string matching a regular expression"; |
| case RECC_INT: |
| return "integer with a specified range"; |
| case RECC_IP: |
| return "IP address"; |
| case RECC_NULL: /* fallthrough */ |
| default: |
| return "none"; |
| } |
| } |
| |
| static const char * |
| rec_sourceof(int rec_source) |
| { |
| switch (rec_source) { |
| case REC_SOURCE_DEFAULT: |
| return "built in default"; |
| case REC_SOURCE_EXPLICIT: |
| return "administratively set"; |
| case REC_SOURCE_PLUGIN: |
| return "plugin default"; |
| case REC_SOURCE_ENV: |
| return "environment"; |
| default: |
| return "unknown"; |
| } |
| } |
| |
| static const char * |
| rec_labelof(int rec_class) |
| { |
| switch (rec_class) { |
| case RECT_CONFIG: |
| return "CONFIG"; |
| case RECT_LOCAL: |
| return "LOCAL"; |
| default: |
| return nullptr; |
| } |
| } |
| |
| static const char * |
| rec_datatypeof(TSRecordDataType dt) |
| { |
| switch (dt) { |
| case TS_RECORDDATATYPE_INT: |
| return "int"; |
| case TS_RECORDDATATYPE_NULL: |
| return "null"; |
| case TS_RECORDDATATYPE_FLOAT: |
| return "float"; |
| case TS_RECORDDATATYPE_STRING: |
| return "string"; |
| case TS_RECORDDATATYPE_COUNTER: |
| return "counter"; |
| case TS_RECORDDATATYPE_STAT_CONST: |
| return "constant stat"; |
| case TS_RECORDDATATYPE_STAT_FX: |
| return "stat fx"; |
| case TS_RECORDDATATYPE_MAX: |
| return "*"; |
| } |
| return "?"; |
| } |
| |
| static std::string |
| timestr(time_t tm) |
| { |
| char buf[32]; |
| return std::string(ctime_r(&tm, buf)); |
| } |
| |
| static void |
| format_record(const CtrlMgmtRecord &record, bool recfmt) |
| { |
| CtrlMgmtRecordValue value(record); |
| |
| if (recfmt) { |
| std::cout << rec_labelof(record.rclass()) << ' ' << record.name() << ' ' << rec_typeof(record.type()) << ' ' << value.c_str() |
| << std::endl; |
| } else { |
| std::cout << record.name() << ": " << value.c_str() << std::endl; |
| } |
| } |
| |
| void |
| CtrlEngine::config_get() |
| { |
| for (const auto &it : arguments.get("get")) { |
| CtrlMgmtRecord record; |
| TSMgmtError error; |
| |
| error = record.fetch(it.c_str()); |
| if (error != TS_ERR_OKAY) { |
| CtrlMgmtError(error, "failed to fetch %s", it.c_str()); |
| status_code = CTRL_EX_ERROR; |
| return; |
| } |
| |
| if (REC_TYPE_IS_CONFIG(record.rclass())) { |
| format_record(record, arguments.get("records")); |
| } |
| } |
| } |
| |
| void |
| CtrlEngine::config_describe() |
| { |
| for (const auto &it : arguments.get("describe")) { |
| TSConfigRecordDescription desc; |
| TSMgmtError error; |
| |
| ink_zero(desc); |
| error = TSConfigRecordDescribe(it.c_str(), 0 /* flags */, &desc); |
| if (error != TS_ERR_OKAY) { |
| CtrlMgmtError(error, "failed to describe %s", it.c_str()); |
| status_code = CTRL_EX_ERROR; |
| return; |
| } |
| |
| auto ov_iter = ts::Overridable_Txn_Vars.find(it); |
| bool overridable_p = (ov_iter != ts::Overridable_Txn_Vars.end()); |
| |
| std::string text; |
| std::cout << ts::bwprint(text, "{:16s}: {}\n", "Name", desc.rec_name); |
| std::cout << ts::bwprint(text, "{:16s}: {}\n", "Current Value ", CtrlMgmtRecordValue(desc.rec_type, desc.rec_value).c_str()); |
| std::cout << ts::bwprint(text, "{:16s}: {}\n", "Default Value ", CtrlMgmtRecordValue(desc.rec_type, desc.rec_default).c_str()); |
| std::cout << ts::bwprint(text, "{:16s}: {}\n", "Record Type ", rec_classof(desc.rec_class)); |
| std::cout << ts::bwprint(text, "{:16s}: {}\n", "Data Type ", rec_typeof(desc.rec_type)); |
| std::cout << ts::bwprint(text, "{:16s}: {}\n", "Access Control ", rec_accessof(desc.rec_access)); |
| std::cout << ts::bwprint(text, "{:16s}: {}\n", "Update Type ", rec_updateof(desc.rec_updatetype)); |
| std::cout << ts::bwprint(text, "{:16s}: {}\n", "Update Status ", desc.rec_update); |
| std::cout << ts::bwprint(text, "{:16s}: {}\n", "Source ", rec_sourceof(desc.rec_source)); |
| std::cout << ts::bwprint(text, "{:16s}: {} {}\n", "Overridable", overridable_p ? "yes" : "no", |
| overridable_p ? rec_datatypeof(std::get<1>(ov_iter->second)) : ""); |
| |
| if (strlen(desc.rec_checkexpr)) { |
| std::cout << ts::bwprint(text, "{:16s}: {}\n", "Syntax Check ", rec_checkof(desc.rec_checktype), desc.rec_checkexpr); |
| } else { |
| std::cout << ts::bwprint(text, "{:16s}: {}\n", "Syntax Check ", rec_checkof(desc.rec_checktype)); |
| } |
| |
| std::cout << ts::bwprint(text, "{:16s}: {}\n", "Version ", desc.rec_version); |
| std::cout << ts::bwprint(text, "{:16s}: {}\n", "Order ", desc.rec_order); |
| std::cout << ts::bwprint(text, "{:16s}: {}\n", "Raw Stat Block ", desc.rec_rsb); |
| |
| TSConfigRecordDescriptionFree(&desc); |
| } |
| } |
| |
| void |
| CtrlEngine::config_set() |
| { |
| TSMgmtError error; |
| TSActionNeedT action; |
| auto set_data = arguments.get("set"); |
| const char *rec_name = set_data[0].c_str(); |
| const char *rec_val = set_data[1].c_str(); |
| error = TSRecordSet(rec_name, rec_val, &action); |
| if (error != TS_ERR_OKAY) { |
| CtrlMgmtError(error, "failed to set %s", rec_name); |
| status_code = CTRL_EX_ERROR; |
| return; |
| } |
| |
| switch (action) { |
| case TS_ACTION_SHUTDOWN: |
| std::cout << "set " << rec_name << ", full shutdown required" << std::endl; |
| break; |
| case TS_ACTION_RESTART: |
| std::cout << "set " << rec_name << ", restart required" << std::endl; |
| break; |
| case TS_ACTION_RECONFIGURE: |
| std::cout << "set " << rec_name << ", please wait 10 seconds for traffic server to sync configuration, restart is not required" |
| << std::endl; |
| break; |
| case TS_ACTION_DYNAMIC: |
| default: |
| printf("set %s\n", rec_name); |
| break; |
| } |
| } |
| |
| void |
| CtrlEngine::config_match() |
| { |
| for (const auto &it : arguments.get("match")) { |
| CtrlMgmtRecordList reclist; |
| TSMgmtError error; |
| |
| // XXX filter the results to only match configuration records. |
| |
| error = reclist.match(it.c_str()); |
| if (error != TS_ERR_OKAY) { |
| CtrlMgmtError(error, "failed to fetch %s", it.c_str()); |
| status_code = CTRL_EX_ERROR; |
| return; |
| } |
| |
| while (!reclist.empty()) { |
| CtrlMgmtRecord record(reclist.next()); |
| if (REC_TYPE_IS_CONFIG(record.rclass())) { |
| format_record(record, arguments.get("records")); |
| } |
| } |
| } |
| } |
| |
| void |
| CtrlEngine::config_reload() |
| { |
| TSMgmtError error = TSReconfigure(); |
| if (error != TS_ERR_OKAY) { |
| CtrlMgmtError(error, "configuration reload request failed"); |
| status_code = CTRL_EX_ERROR; |
| return; |
| } |
| } |
| |
| void |
| CtrlEngine::config_status() |
| { |
| CtrlMgmtRecord version; |
| CtrlMgmtRecord configtime; |
| CtrlMgmtRecord starttime; |
| CtrlMgmtRecord reconfig; |
| CtrlMgmtRecord proxy; |
| CtrlMgmtRecord manager; |
| |
| CTRL_MGMT_CHECK(version.fetch("proxy.process.version.server.long")); |
| CTRL_MGMT_CHECK(starttime.fetch("proxy.node.restarts.proxy.start_time")); |
| CTRL_MGMT_CHECK(configtime.fetch("proxy.node.config.reconfigure_time")); |
| CTRL_MGMT_CHECK(reconfig.fetch("proxy.node.config.reconfigure_required")); |
| CTRL_MGMT_CHECK(proxy.fetch("proxy.node.config.restart_required.proxy")); |
| CTRL_MGMT_CHECK(manager.fetch("proxy.node.config.restart_required.manager")); |
| |
| std::cout << CtrlMgmtRecordValue(version).c_str() << std::endl; |
| std::cout << "Started at " << timestr(static_cast<time_t>(starttime.as_int())).c_str(); |
| std::cout << "Last reconfiguration at " << timestr(static_cast<time_t>(configtime.as_int())).c_str(); |
| std::cout << (reconfig.as_int() ? "Reconfiguration required" : "Configuration is current") << std::endl; |
| |
| if (proxy.as_int()) { |
| std::cout << "traffic_server requires restarting" << std::endl; |
| } |
| if (manager.as_int()) { |
| std::cout << "traffic_manager requires restarting\n" << std::endl; |
| } |
| } |
| |
| void |
| CtrlEngine::config_defaults() |
| { |
| TSMgmtError error; |
| CtrlMgmtRecordDescriptionList descriptions; |
| |
| error = descriptions.match(".*"); |
| if (error != TS_ERR_OKAY) { |
| CtrlMgmtError(error, "failed to fetch record metadata"); |
| status_code = CTRL_EX_ERROR; |
| return; |
| } |
| |
| while (!descriptions.empty()) { |
| TSConfigRecordDescription *desc = descriptions.next(); |
| CtrlMgmtRecordValue deflt(desc->rec_type, desc->rec_default); |
| |
| if (arguments.get("records")) { |
| std::cout << rec_labelof(desc->rec_class) << ' ' << desc->rec_name << ' ' << rec_typeof(desc->rec_type) << ' ' |
| << deflt.c_str() << std::endl; |
| } else { |
| std::cout << desc->rec_name << ": " << deflt.c_str() << std::endl; |
| } |
| TSConfigRecordDescriptionDestroy(desc); |
| } |
| } |
| |
| void |
| CtrlEngine::config_diff() |
| { |
| TSMgmtError error; |
| CtrlMgmtRecordDescriptionList descriptions; |
| |
| error = descriptions.match(".*"); |
| if (error != TS_ERR_OKAY) { |
| CtrlMgmtError(error, "failed to fetch record metadata"); |
| status_code = CTRL_EX_ERROR; |
| return; |
| } |
| |
| while (!descriptions.empty()) { |
| TSConfigRecordDescription *desc; |
| bool changed = false; |
| |
| desc = descriptions.next(); |
| |
| switch (desc->rec_type) { |
| case TS_REC_INT: |
| changed = (desc->rec_value.int_val != desc->rec_default.int_val); |
| break; |
| case TS_REC_COUNTER: |
| changed = (desc->rec_value.counter_val != desc->rec_default.counter_val); |
| break; |
| case TS_REC_FLOAT: |
| changed = (desc->rec_value.float_val != desc->rec_default.float_val); |
| break; |
| case TS_REC_STRING: |
| changed = (strcmp(desc->rec_value.string_val, desc->rec_default.string_val) != 0); |
| break; |
| default: |
| break; |
| } |
| |
| if (changed) { |
| CtrlMgmtRecordValue current(desc->rec_type, desc->rec_value); |
| CtrlMgmtRecordValue deflt(desc->rec_type, desc->rec_default); |
| |
| if (arguments.get("records")) { |
| std::cout << rec_labelof(desc->rec_class) << ' ' << desc->rec_name << ' ' << rec_typeof(desc->rec_type) << ' ' |
| << current.c_str() << " # default: " << deflt.c_str() << std::endl; |
| } else { |
| std::cout << desc->rec_name << " has changed" << std::endl; |
| std::cout << "\tCurrent Value: " << current.c_str() << std::endl; |
| std::cout << "\tDefault Value: " << deflt.c_str() << std::endl; |
| } |
| } |
| |
| TSConfigRecordDescriptionDestroy(desc); |
| } |
| } |