xref: /trafficserver/src/traffic_ctl/config.cc (revision 5efcddef)
1 /** @file
2 
3   traffic_ctl
4 
5   @section license License
6 
7   Licensed to the Apache Software Foundation (ASF) under one
8   or more contributor license agreements.  See the NOTICE file
9   distributed with this work for additional information
10   regarding copyright ownership.  The ASF licenses this file
11   to you under the Apache License, Version 2.0 (the
12   "License"); you may not use this file except in compliance
13   with the License.  You may obtain a copy of the License at
14 
15       http://www.apache.org/licenses/LICENSE-2.0
16 
17   Unless required by applicable law or agreed to in writing, software
18   distributed under the License is distributed on an "AS IS" BASIS,
19   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20   See the License for the specific language governing permissions and
21   limitations under the License.
22  */
23 
24 #include "traffic_ctl.h"
25 #include <ctime>
26 #include "records/I_RecDefs.h"
27 #include "records/P_RecUtils.h"
28 #include "ts/apidefs.h"
29 #include "HTTP.h"
30 #include "HttpConnectionCount.h"
31 #include "shared/overridable_txn_vars.h"
32 #include <tscore/BufferWriter.h>
33 
34 struct RecordDescriptionPolicy {
35   using entry_type = TSConfigRecordDescription *;
36 
37   static void
freeRecordDescriptionPolicy38   free(entry_type e)
39   {
40     TSConfigRecordDescriptionDestroy(e);
41   }
42 
43   static entry_type
castRecordDescriptionPolicy44   cast(void *ptr)
45   {
46     return (entry_type)ptr;
47   }
48 };
49 
50 struct CtrlMgmtRecordDescriptionList : CtrlMgmtList<RecordDescriptionPolicy> {
51   TSMgmtError
matchCtrlMgmtRecordDescriptionList52   match(const char *regex)
53   {
54     return TSConfigRecordDescribeMatchMlt(regex, 0u /* flags */, this->list);
55   }
56 };
57 
58 // Record data type names, indexed by TSRecordT.
59 static const char *
rec_typeof(int rec_type)60 rec_typeof(int rec_type)
61 {
62   switch (rec_type) {
63   case TS_REC_INT:
64     return "INT";
65   case TS_REC_COUNTER:
66     return "COUNTER";
67   case TS_REC_FLOAT:
68     return "FLOAT";
69   case TS_REC_STRING:
70     return "STRING";
71   case TS_REC_UNDEFINED: /* fallthrough */
72   default:
73     return "UNDEFINED";
74   }
75 }
76 
77 // Record type name, indexed by RecT.
78 static const char *
rec_classof(int rec_class)79 rec_classof(int rec_class)
80 {
81   switch (rec_class) {
82   case RECT_CONFIG:
83     return "standard config";
84   case RECT_LOCAL:
85     return "local config";
86   case RECT_PROCESS:
87     return "process metric";
88   case RECT_NODE:
89     return "node metric";
90   case RECT_PLUGIN:
91     return "plugin metric";
92   default:
93     return "undefined";
94   }
95 }
96 
97 // Record access control, indexed by RecAccessT.
98 static const char *
rec_accessof(int rec_access)99 rec_accessof(int rec_access)
100 {
101   switch (rec_access) {
102   case RECA_NO_ACCESS:
103     return "no access";
104   case RECA_READ_ONLY:
105     return "read only";
106   case RECA_NULL: /* fallthrough */
107   default:
108     return "default";
109   }
110 }
111 
112 // Record access control, indexed by RecUpdateT.
113 static const char *
rec_updateof(int rec_updatetype)114 rec_updateof(int rec_updatetype)
115 {
116   switch (rec_updatetype) {
117   case RECU_DYNAMIC:
118     return "dynamic, no restart";
119   case RECU_RESTART_TS:
120     return "static, restart traffic_server";
121   case RECU_RESTART_TM:
122     return "static, restart traffic_manager";
123   case RECU_NULL: /* fallthrough */
124   default:
125     return "none";
126   }
127 }
128 
129 // Record check type, indexed by RecCheckT.
130 static const char *
rec_checkof(int rec_checktype)131 rec_checkof(int rec_checktype)
132 {
133   switch (rec_checktype) {
134   case RECC_STR:
135     return "string matching a regular expression";
136   case RECC_INT:
137     return "integer with a specified range";
138   case RECC_IP:
139     return "IP address";
140   case RECC_NULL: /* fallthrough */
141   default:
142     return "none";
143   }
144 }
145 
146 static const char *
rec_sourceof(int rec_source)147 rec_sourceof(int rec_source)
148 {
149   switch (rec_source) {
150   case REC_SOURCE_DEFAULT:
151     return "built in default";
152   case REC_SOURCE_EXPLICIT:
153     return "administratively set";
154   case REC_SOURCE_PLUGIN:
155     return "plugin default";
156   case REC_SOURCE_ENV:
157     return "environment";
158   default:
159     return "unknown";
160   }
161 }
162 
163 static const char *
rec_labelof(int rec_class)164 rec_labelof(int rec_class)
165 {
166   switch (rec_class) {
167   case RECT_CONFIG:
168     return "CONFIG";
169   case RECT_LOCAL:
170     return "LOCAL";
171   default:
172     return nullptr;
173   }
174 }
175 
176 static const char *
rec_datatypeof(TSRecordDataType dt)177 rec_datatypeof(TSRecordDataType dt)
178 {
179   switch (dt) {
180   case TS_RECORDDATATYPE_INT:
181     return "int";
182   case TS_RECORDDATATYPE_NULL:
183     return "null";
184   case TS_RECORDDATATYPE_FLOAT:
185     return "float";
186   case TS_RECORDDATATYPE_STRING:
187     return "string";
188   case TS_RECORDDATATYPE_COUNTER:
189     return "counter";
190   case TS_RECORDDATATYPE_STAT_CONST:
191     return "constant stat";
192   case TS_RECORDDATATYPE_STAT_FX:
193     return "stat fx";
194   case TS_RECORDDATATYPE_MAX:
195     return "*";
196   }
197   return "?";
198 }
199 
200 static std::string
timestr(time_t tm)201 timestr(time_t tm)
202 {
203   char buf[32];
204   return std::string(ctime_r(&tm, buf));
205 }
206 
207 static void
format_record(const CtrlMgmtRecord & record,bool recfmt)208 format_record(const CtrlMgmtRecord &record, bool recfmt)
209 {
210   CtrlMgmtRecordValue value(record);
211 
212   if (recfmt) {
213     std::cout << rec_labelof(record.rclass()) << ' ' << record.name() << ' ' << rec_typeof(record.type()) << ' ' << value.c_str()
214               << std::endl;
215   } else {
216     std::cout << record.name() << ": " << value.c_str() << std::endl;
217   }
218 }
219 
220 void
config_get()221 CtrlEngine::config_get()
222 {
223   for (const auto &it : arguments.get("get")) {
224     CtrlMgmtRecord record;
225     TSMgmtError error;
226 
227     error = record.fetch(it.c_str());
228     if (error != TS_ERR_OKAY) {
229       CtrlMgmtError(error, "failed to fetch %s", it.c_str());
230       status_code = CTRL_EX_ERROR;
231       return;
232     }
233 
234     if (REC_TYPE_IS_CONFIG(record.rclass())) {
235       format_record(record, arguments.get("records"));
236     }
237   }
238 }
239 
240 void
config_describe()241 CtrlEngine::config_describe()
242 {
243   for (const auto &it : arguments.get("describe")) {
244     TSConfigRecordDescription desc;
245     TSMgmtError error;
246 
247     ink_zero(desc);
248     error = TSConfigRecordDescribe(it.c_str(), 0 /* flags */, &desc);
249     if (error != TS_ERR_OKAY) {
250       CtrlMgmtError(error, "failed to describe %s", it.c_str());
251       status_code = CTRL_EX_ERROR;
252       return;
253     }
254 
255     auto ov_iter       = ts::Overridable_Txn_Vars.find(it);
256     bool overridable_p = (ov_iter != ts::Overridable_Txn_Vars.end());
257 
258     std::string text;
259     std::cout << ts::bwprint(text, "{:16s}: {}\n", "Name", desc.rec_name);
260     std::cout << ts::bwprint(text, "{:16s}: {}\n", "Current Value ", CtrlMgmtRecordValue(desc.rec_type, desc.rec_value).c_str());
261     std::cout << ts::bwprint(text, "{:16s}: {}\n", "Default Value ", CtrlMgmtRecordValue(desc.rec_type, desc.rec_default).c_str());
262     std::cout << ts::bwprint(text, "{:16s}: {}\n", "Record Type ", rec_classof(desc.rec_class));
263     std::cout << ts::bwprint(text, "{:16s}: {}\n", "Data Type ", rec_typeof(desc.rec_type));
264     std::cout << ts::bwprint(text, "{:16s}: {}\n", "Access Control ", rec_accessof(desc.rec_access));
265     std::cout << ts::bwprint(text, "{:16s}: {}\n", "Update Type ", rec_updateof(desc.rec_updatetype));
266     std::cout << ts::bwprint(text, "{:16s}: {}\n", "Update Status ", desc.rec_update);
267     std::cout << ts::bwprint(text, "{:16s}: {}\n", "Source ", rec_sourceof(desc.rec_source));
268     std::cout << ts::bwprint(text, "{:16s}: {} {}\n", "Overridable", overridable_p ? "yes" : "no",
269                              overridable_p ? rec_datatypeof(std::get<1>(ov_iter->second)) : "");
270 
271     if (strlen(desc.rec_checkexpr)) {
272       std::cout << ts::bwprint(text, "{:16s}: {}\n", "Syntax Check ", rec_checkof(desc.rec_checktype), desc.rec_checkexpr);
273     } else {
274       std::cout << ts::bwprint(text, "{:16s}: {}\n", "Syntax Check ", rec_checkof(desc.rec_checktype));
275     }
276 
277     std::cout << ts::bwprint(text, "{:16s}: {}\n", "Version ", desc.rec_version);
278     std::cout << ts::bwprint(text, "{:16s}: {}\n", "Order ", desc.rec_order);
279     std::cout << ts::bwprint(text, "{:16s}: {}\n", "Raw Stat Block ", desc.rec_rsb);
280 
281     TSConfigRecordDescriptionFree(&desc);
282   }
283 }
284 
285 void
config_set()286 CtrlEngine::config_set()
287 {
288   TSMgmtError error;
289   TSActionNeedT action;
290   auto set_data        = arguments.get("set");
291   const char *rec_name = set_data[0].c_str();
292   const char *rec_val  = set_data[1].c_str();
293   error                = TSRecordSet(rec_name, rec_val, &action);
294   if (error != TS_ERR_OKAY) {
295     CtrlMgmtError(error, "failed to set %s", rec_name);
296     status_code = CTRL_EX_ERROR;
297     return;
298   }
299 
300   switch (action) {
301   case TS_ACTION_SHUTDOWN:
302     std::cout << "set " << rec_name << ", full shutdown required" << std::endl;
303     break;
304   case TS_ACTION_RESTART:
305     std::cout << "set " << rec_name << ", restart required" << std::endl;
306     break;
307   case TS_ACTION_RECONFIGURE:
308     std::cout << "set " << rec_name << ", please wait 10 seconds for traffic server to sync configuration, restart is not required"
309               << std::endl;
310     break;
311   case TS_ACTION_DYNAMIC:
312   default:
313     printf("set %s\n", rec_name);
314     break;
315   }
316 }
317 
318 void
config_match()319 CtrlEngine::config_match()
320 {
321   for (const auto &it : arguments.get("match")) {
322     CtrlMgmtRecordList reclist;
323     TSMgmtError error;
324 
325     // XXX filter the results to only match configuration records.
326 
327     error = reclist.match(it.c_str());
328     if (error != TS_ERR_OKAY) {
329       CtrlMgmtError(error, "failed to fetch %s", it.c_str());
330       status_code = CTRL_EX_ERROR;
331       return;
332     }
333 
334     while (!reclist.empty()) {
335       CtrlMgmtRecord record(reclist.next());
336       if (REC_TYPE_IS_CONFIG(record.rclass())) {
337         format_record(record, arguments.get("records"));
338       }
339     }
340   }
341 }
342 
343 void
config_reload()344 CtrlEngine::config_reload()
345 {
346   TSMgmtError error = TSReconfigure();
347   if (error != TS_ERR_OKAY) {
348     CtrlMgmtError(error, "configuration reload request failed");
349     status_code = CTRL_EX_ERROR;
350     return;
351   }
352 }
353 
354 void
config_status()355 CtrlEngine::config_status()
356 {
357   CtrlMgmtRecord version;
358   CtrlMgmtRecord configtime;
359   CtrlMgmtRecord starttime;
360   CtrlMgmtRecord reconfig;
361   CtrlMgmtRecord proxy;
362   CtrlMgmtRecord manager;
363 
364   CTRL_MGMT_CHECK(version.fetch("proxy.process.version.server.long"));
365   CTRL_MGMT_CHECK(starttime.fetch("proxy.node.restarts.proxy.start_time"));
366   CTRL_MGMT_CHECK(configtime.fetch("proxy.node.config.reconfigure_time"));
367   CTRL_MGMT_CHECK(reconfig.fetch("proxy.node.config.reconfigure_required"));
368   CTRL_MGMT_CHECK(proxy.fetch("proxy.node.config.restart_required.proxy"));
369   CTRL_MGMT_CHECK(manager.fetch("proxy.node.config.restart_required.manager"));
370 
371   std::cout << CtrlMgmtRecordValue(version).c_str() << std::endl;
372   std::cout << "Started at " << timestr((time_t)starttime.as_int()).c_str();
373   std::cout << "Last reconfiguration at " << timestr((time_t)configtime.as_int()).c_str();
374   std::cout << (reconfig.as_int() ? "Reconfiguration required" : "Configuration is current") << std::endl;
375 
376   if (proxy.as_int()) {
377     std::cout << "traffic_server requires restarting" << std::endl;
378   }
379   if (manager.as_int()) {
380     std::cout << "traffic_manager requires restarting\n" << std::endl;
381   }
382 }
383 
384 void
config_defaults()385 CtrlEngine::config_defaults()
386 {
387   TSMgmtError error;
388   CtrlMgmtRecordDescriptionList descriptions;
389 
390   error = descriptions.match(".*");
391   if (error != TS_ERR_OKAY) {
392     CtrlMgmtError(error, "failed to fetch record metadata");
393     status_code = CTRL_EX_ERROR;
394     return;
395   }
396 
397   while (!descriptions.empty()) {
398     TSConfigRecordDescription *desc = descriptions.next();
399     CtrlMgmtRecordValue deflt(desc->rec_type, desc->rec_default);
400 
401     if (arguments.get("records")) {
402       std::cout << rec_labelof(desc->rec_class) << ' ' << desc->rec_name << ' ' << rec_typeof(desc->rec_type) << ' '
403                 << deflt.c_str() << std::endl;
404     } else {
405       std::cout << desc->rec_name << ": " << deflt.c_str() << std::endl;
406     }
407     TSConfigRecordDescriptionDestroy(desc);
408   }
409 }
410 
411 void
config_diff()412 CtrlEngine::config_diff()
413 {
414   TSMgmtError error;
415   CtrlMgmtRecordDescriptionList descriptions;
416 
417   error = descriptions.match(".*");
418   if (error != TS_ERR_OKAY) {
419     CtrlMgmtError(error, "failed to fetch record metadata");
420     status_code = CTRL_EX_ERROR;
421     return;
422   }
423 
424   while (!descriptions.empty()) {
425     TSConfigRecordDescription *desc;
426     bool changed = false;
427 
428     desc = descriptions.next();
429 
430     switch (desc->rec_type) {
431     case TS_REC_INT:
432       changed = (desc->rec_value.int_val != desc->rec_default.int_val);
433       break;
434     case TS_REC_COUNTER:
435       changed = (desc->rec_value.counter_val != desc->rec_default.counter_val);
436       break;
437     case TS_REC_FLOAT:
438       changed = (desc->rec_value.float_val != desc->rec_default.float_val);
439       break;
440     case TS_REC_STRING:
441       changed = (strcmp(desc->rec_value.string_val, desc->rec_default.string_val) != 0);
442       break;
443     default:
444       break;
445     }
446 
447     if (changed) {
448       CtrlMgmtRecordValue current(desc->rec_type, desc->rec_value);
449       CtrlMgmtRecordValue deflt(desc->rec_type, desc->rec_default);
450 
451       if (arguments.get("records")) {
452         std::cout << rec_labelof(desc->rec_class) << ' ' << desc->rec_name << ' ' << rec_typeof(desc->rec_type) << ' '
453                   << current.c_str() << " # default: " << deflt.c_str() << std::endl;
454       } else {
455         std::cout << desc->rec_name << " has changed" << std::endl;
456         std::cout << "\tCurrent Value: " << current.c_str() << std::endl;
457         std::cout << "\tDefault Value: " << deflt.c_str() << std::endl;
458       }
459     }
460 
461     TSConfigRecordDescriptionDestroy(desc);
462   }
463 }
464