Line data Source code
1 : #include "source/server/admin/clusters_handler.h"
2 :
3 : #include "envoy/admin/v3/clusters.pb.h"
4 :
5 : #include "source/common/http/headers.h"
6 : #include "source/common/http/utility.h"
7 : #include "source/common/network/utility.h"
8 : #include "source/common/upstream/host_utility.h"
9 : #include "source/server/admin/utils.h"
10 :
11 : namespace Envoy {
12 : namespace Server {
13 :
14 : namespace {
15 :
16 : void addCircuitBreakerSettingsAsText(const std::string& cluster_name,
17 : const std::string& priority_str,
18 : Upstream::ResourceManager& resource_manager,
19 0 : Buffer::Instance& response) {
20 0 : response.add(fmt::format("{}::{}_priority::max_connections::{}\n", cluster_name, priority_str,
21 0 : resource_manager.connections().max()));
22 0 : response.add(fmt::format("{}::{}_priority::max_pending_requests::{}\n", cluster_name,
23 0 : priority_str, resource_manager.pendingRequests().max()));
24 0 : response.add(fmt::format("{}::{}_priority::max_requests::{}\n", cluster_name, priority_str,
25 0 : resource_manager.requests().max()));
26 0 : response.add(fmt::format("{}::{}_priority::max_retries::{}\n", cluster_name, priority_str,
27 0 : resource_manager.retries().max()));
28 0 : }
29 :
30 : void addCircuitBreakerSettingsAsJson(const envoy::config::core::v3::RoutingPriority& priority,
31 : Upstream::ResourceManager& resource_manager,
32 0 : envoy::admin::v3::ClusterStatus& cluster_status) {
33 0 : auto& thresholds = *cluster_status.mutable_circuit_breakers()->add_thresholds();
34 0 : thresholds.set_priority(priority);
35 0 : thresholds.mutable_max_connections()->set_value(resource_manager.connections().max());
36 0 : thresholds.mutable_max_pending_requests()->set_value(resource_manager.pendingRequests().max());
37 0 : thresholds.mutable_max_requests()->set_value(resource_manager.requests().max());
38 0 : thresholds.mutable_max_retries()->set_value(resource_manager.retries().max());
39 0 : }
40 :
41 : } // namespace
42 :
43 134 : ClustersHandler::ClustersHandler(Server::Instance& server) : HandlerContextBase(server) {}
44 :
45 : Http::Code ClustersHandler::handlerClusters(Http::ResponseHeaderMap& response_headers,
46 0 : Buffer::Instance& response, AdminStream& admin_stream) {
47 0 : const auto format_value = Utility::formatParam(admin_stream.queryParams());
48 :
49 0 : if (format_value.has_value() && format_value.value() == "json") {
50 0 : writeClustersAsJson(response);
51 0 : response_headers.setReferenceContentType(Http::Headers::get().ContentTypeValues.Json);
52 0 : } else {
53 0 : writeClustersAsText(response);
54 0 : }
55 :
56 0 : return Http::Code::OK;
57 0 : }
58 :
59 : // Helper method that ensures that we've setting flags based on all the health flag values on the
60 : // host.
61 : void setHealthFlag(Upstream::Host::HealthFlag flag, const Upstream::Host& host,
62 0 : envoy::admin::v3::HostHealthStatus& health_status) {
63 0 : switch (flag) {
64 0 : case Upstream::Host::HealthFlag::FAILED_ACTIVE_HC:
65 0 : health_status.set_failed_active_health_check(
66 0 : host.healthFlagGet(Upstream::Host::HealthFlag::FAILED_ACTIVE_HC));
67 0 : break;
68 0 : case Upstream::Host::HealthFlag::FAILED_OUTLIER_CHECK:
69 0 : health_status.set_failed_outlier_check(
70 0 : host.healthFlagGet(Upstream::Host::HealthFlag::FAILED_OUTLIER_CHECK));
71 0 : break;
72 0 : case Upstream::Host::HealthFlag::FAILED_EDS_HEALTH:
73 0 : case Upstream::Host::HealthFlag::DEGRADED_EDS_HEALTH:
74 0 : if (host.healthFlagGet(Upstream::Host::HealthFlag::FAILED_EDS_HEALTH)) {
75 0 : health_status.set_eds_health_status(envoy::config::core::v3::UNHEALTHY);
76 0 : } else if (host.healthFlagGet(Upstream::Host::HealthFlag::DEGRADED_EDS_HEALTH)) {
77 0 : health_status.set_eds_health_status(envoy::config::core::v3::DEGRADED);
78 0 : } else {
79 0 : health_status.set_eds_health_status(envoy::config::core::v3::HEALTHY);
80 0 : }
81 0 : break;
82 0 : case Upstream::Host::HealthFlag::DEGRADED_ACTIVE_HC:
83 0 : health_status.set_failed_active_degraded_check(
84 0 : host.healthFlagGet(Upstream::Host::HealthFlag::DEGRADED_ACTIVE_HC));
85 0 : break;
86 0 : case Upstream::Host::HealthFlag::PENDING_DYNAMIC_REMOVAL:
87 0 : health_status.set_pending_dynamic_removal(
88 0 : host.healthFlagGet(Upstream::Host::HealthFlag::PENDING_DYNAMIC_REMOVAL));
89 0 : break;
90 0 : case Upstream::Host::HealthFlag::PENDING_ACTIVE_HC:
91 0 : health_status.set_pending_active_hc(
92 0 : host.healthFlagGet(Upstream::Host::HealthFlag::PENDING_ACTIVE_HC));
93 0 : break;
94 0 : case Upstream::Host::HealthFlag::EXCLUDED_VIA_IMMEDIATE_HC_FAIL:
95 0 : health_status.set_excluded_via_immediate_hc_fail(
96 0 : host.healthFlagGet(Upstream::Host::HealthFlag::EXCLUDED_VIA_IMMEDIATE_HC_FAIL));
97 0 : break;
98 0 : case Upstream::Host::HealthFlag::ACTIVE_HC_TIMEOUT:
99 0 : health_status.set_active_hc_timeout(
100 0 : host.healthFlagGet(Upstream::Host::HealthFlag::ACTIVE_HC_TIMEOUT));
101 0 : break;
102 0 : }
103 0 : }
104 :
105 : // TODO(efimki): Add support of text readouts stats.
106 0 : void ClustersHandler::writeClustersAsJson(Buffer::Instance& response) {
107 0 : envoy::admin::v3::Clusters clusters;
108 : // TODO(mattklein123): Add ability to see warming clusters in admin output.
109 0 : auto all_clusters = server_.clusterManager().clusters();
110 0 : for (const auto& [name, cluster_ref] : all_clusters.active_clusters_) {
111 0 : UNREFERENCED_PARAMETER(name);
112 0 : const Upstream::Cluster& cluster = cluster_ref.get();
113 0 : Upstream::ClusterInfoConstSharedPtr cluster_info = cluster.info();
114 :
115 0 : envoy::admin::v3::ClusterStatus& cluster_status = *clusters.add_cluster_statuses();
116 0 : cluster_status.set_name(cluster_info->name());
117 0 : cluster_status.set_observability_name(cluster_info->observabilityName());
118 0 : if (const auto& name = cluster_info->edsServiceName(); !name.empty()) {
119 0 : cluster_status.set_eds_service_name(name);
120 0 : }
121 0 : addCircuitBreakerSettingsAsJson(
122 0 : envoy::config::core::v3::RoutingPriority::DEFAULT,
123 0 : cluster.info()->resourceManager(Upstream::ResourcePriority::Default), cluster_status);
124 0 : addCircuitBreakerSettingsAsJson(
125 0 : envoy::config::core::v3::RoutingPriority::HIGH,
126 0 : cluster.info()->resourceManager(Upstream::ResourcePriority::High), cluster_status);
127 :
128 0 : const Upstream::Outlier::Detector* outlier_detector = cluster.outlierDetector();
129 0 : if (outlier_detector != nullptr &&
130 0 : outlier_detector->successRateEjectionThreshold(
131 0 : Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin) > 0.0) {
132 0 : cluster_status.mutable_success_rate_ejection_threshold()->set_value(
133 0 : outlier_detector->successRateEjectionThreshold(
134 0 : Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin));
135 0 : }
136 0 : if (outlier_detector != nullptr &&
137 0 : outlier_detector->successRateEjectionThreshold(
138 0 : Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin) > 0.0) {
139 0 : cluster_status.mutable_local_origin_success_rate_ejection_threshold()->set_value(
140 0 : outlier_detector->successRateEjectionThreshold(
141 0 : Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin));
142 0 : }
143 :
144 0 : cluster_status.set_added_via_api(cluster_info->addedViaApi());
145 :
146 0 : for (auto& host_set : cluster.prioritySet().hostSetsPerPriority()) {
147 0 : for (auto& host : host_set->hosts()) {
148 0 : envoy::admin::v3::HostStatus& host_status = *cluster_status.add_host_statuses();
149 0 : Network::Utility::addressToProtobufAddress(*host->address(),
150 0 : *host_status.mutable_address());
151 0 : host_status.set_hostname(host->hostname());
152 0 : host_status.mutable_locality()->MergeFrom(host->locality());
153 :
154 0 : for (const auto& [counter_name, counter] : host->counters()) {
155 0 : auto& metric = *host_status.add_stats();
156 0 : metric.set_name(std::string(counter_name));
157 0 : metric.set_value(counter.get().value());
158 0 : metric.set_type(envoy::admin::v3::SimpleMetric::COUNTER);
159 0 : }
160 :
161 0 : for (const auto& [gauge_name, gauge] : host->gauges()) {
162 0 : auto& metric = *host_status.add_stats();
163 0 : metric.set_name(std::string(gauge_name));
164 0 : metric.set_value(gauge.get().value());
165 0 : metric.set_type(envoy::admin::v3::SimpleMetric::GAUGE);
166 0 : }
167 :
168 0 : envoy::admin::v3::HostHealthStatus& health_status = *host_status.mutable_health_status();
169 :
170 : // Invokes setHealthFlag for each health flag.
171 0 : #define SET_HEALTH_FLAG(name, notused) \
172 0 : setHealthFlag(Upstream::Host::HealthFlag::name, *host, health_status);
173 0 : HEALTH_FLAG_ENUM_VALUES(SET_HEALTH_FLAG)
174 0 : #undef SET_HEALTH_FLAG
175 :
176 0 : double success_rate = host->outlierDetector().successRate(
177 0 : Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin);
178 0 : if (success_rate >= 0.0) {
179 0 : host_status.mutable_success_rate()->set_value(success_rate);
180 0 : }
181 :
182 0 : host_status.set_weight(host->weight());
183 :
184 0 : host_status.set_priority(host->priority());
185 0 : success_rate = host->outlierDetector().successRate(
186 0 : Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin);
187 0 : if (success_rate >= 0.0) {
188 0 : host_status.mutable_local_origin_success_rate()->set_value(success_rate);
189 0 : }
190 0 : }
191 0 : }
192 0 : }
193 0 : response.add(MessageUtil::getJsonStringFromMessageOrError(clusters, true)); // pretty-print
194 0 : }
195 :
196 : // TODO(efimki): Add support of text readouts stats.
197 0 : void ClustersHandler::writeClustersAsText(Buffer::Instance& response) {
198 : // TODO(mattklein123): Add ability to see warming clusters in admin output.
199 0 : auto all_clusters = server_.clusterManager().clusters();
200 0 : for (const auto& [name, cluster_ref] : all_clusters.active_clusters_) {
201 0 : UNREFERENCED_PARAMETER(name);
202 0 : const Upstream::Cluster& cluster = cluster_ref.get();
203 0 : const std::string& cluster_name = cluster.info()->name();
204 0 : response.add(fmt::format("{}::observability_name::{}\n", cluster_name,
205 0 : cluster.info()->observabilityName()));
206 0 : addOutlierInfo(cluster_name, cluster.outlierDetector(), response);
207 :
208 0 : addCircuitBreakerSettingsAsText(
209 0 : cluster_name, "default",
210 0 : cluster.info()->resourceManager(Upstream::ResourcePriority::Default), response);
211 0 : addCircuitBreakerSettingsAsText(
212 0 : cluster_name, "high", cluster.info()->resourceManager(Upstream::ResourcePriority::High),
213 0 : response);
214 :
215 0 : response.add(
216 0 : fmt::format("{}::added_via_api::{}\n", cluster_name, cluster.info()->addedViaApi()));
217 0 : if (const auto& name = cluster.info()->edsServiceName(); !name.empty()) {
218 0 : response.add(fmt::format("{}::eds_service_name::{}\n", cluster_name, name));
219 0 : }
220 0 : for (auto& host_set : cluster.prioritySet().hostSetsPerPriority()) {
221 0 : for (auto& host : host_set->hosts()) {
222 0 : const std::string& host_address = host->address()->asString();
223 0 : std::map<absl::string_view, uint64_t> all_stats;
224 0 : for (const auto& [counter_name, counter] : host->counters()) {
225 0 : all_stats[counter_name] = counter.get().value();
226 0 : }
227 :
228 0 : for (const auto& [gauge_name, gauge] : host->gauges()) {
229 0 : all_stats[gauge_name] = gauge.get().value();
230 0 : }
231 :
232 0 : for (const auto& [stat_name, stat] : all_stats) {
233 0 : response.add(
234 0 : fmt::format("{}::{}::{}::{}\n", cluster_name, host_address, stat_name, stat));
235 0 : }
236 :
237 0 : response.add(
238 0 : fmt::format("{}::{}::hostname::{}\n", cluster_name, host_address, host->hostname()));
239 0 : response.add(fmt::format("{}::{}::health_flags::{}\n", cluster_name, host_address,
240 0 : Upstream::HostUtility::healthFlagsToString(*host)));
241 0 : response.add(
242 0 : fmt::format("{}::{}::weight::{}\n", cluster_name, host_address, host->weight()));
243 0 : response.add(fmt::format("{}::{}::region::{}\n", cluster_name, host_address,
244 0 : host->locality().region()));
245 0 : response.add(
246 0 : fmt::format("{}::{}::zone::{}\n", cluster_name, host_address, host->locality().zone()));
247 0 : response.add(fmt::format("{}::{}::sub_zone::{}\n", cluster_name, host_address,
248 0 : host->locality().sub_zone()));
249 0 : response.add(
250 0 : fmt::format("{}::{}::canary::{}\n", cluster_name, host_address, host->canary()));
251 0 : response.add(
252 0 : fmt::format("{}::{}::priority::{}\n", cluster_name, host_address, host->priority()));
253 0 : response.add(fmt::format(
254 0 : "{}::{}::success_rate::{}\n", cluster_name, host_address,
255 0 : host->outlierDetector().successRate(
256 0 : Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin)));
257 0 : response.add(fmt::format(
258 0 : "{}::{}::local_origin_success_rate::{}\n", cluster_name, host_address,
259 0 : host->outlierDetector().successRate(
260 0 : Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin)));
261 0 : }
262 0 : }
263 0 : }
264 0 : }
265 :
266 : void ClustersHandler::addOutlierInfo(const std::string& cluster_name,
267 : const Upstream::Outlier::Detector* outlier_detector,
268 0 : Buffer::Instance& response) {
269 0 : if (outlier_detector) {
270 0 : response.add(fmt::format(
271 0 : "{}::outlier::success_rate_average::{:g}\n", cluster_name,
272 0 : outlier_detector->successRateAverage(
273 0 : Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin)));
274 0 : response.add(fmt::format(
275 0 : "{}::outlier::success_rate_ejection_threshold::{:g}\n", cluster_name,
276 0 : outlier_detector->successRateEjectionThreshold(
277 0 : Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::ExternalOrigin)));
278 0 : response.add(fmt::format(
279 0 : "{}::outlier::local_origin_success_rate_average::{:g}\n", cluster_name,
280 0 : outlier_detector->successRateAverage(
281 0 : Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin)));
282 0 : response.add(fmt::format(
283 0 : "{}::outlier::local_origin_success_rate_ejection_threshold::{:g}\n", cluster_name,
284 0 : outlier_detector->successRateEjectionThreshold(
285 0 : Upstream::Outlier::DetectorHostMonitor::SuccessRateMonitorType::LocalOrigin)));
286 0 : }
287 0 : }
288 :
289 : } // namespace Server
290 : } // namespace Envoy
|