blob: ffe551150d4cca81ec7a1288ffacf1489a344e6a [file] [log] [blame] [edit]
#ifndef THIRD_PARTY_MILOTIC_EXTERNAL_CC_TLBMC_METRICS_SOFTWARE_METRICS_H_
#define THIRD_PARTY_MILOTIC_EXTERNAL_CC_TLBMC_METRICS_SOFTWARE_METRICS_H_
#include <cstdint>
#include <memory>
#include <string>
#include <utility>
#include "absl/base/no_destructor.h"
#include "absl/base/thread_annotations.h"
#include "absl/container/flat_hash_map.h"
#include "absl/container/flat_hash_set.h"
#include "absl/functional/any_invocable.h"
#include "absl/log/log.h"
#include "absl/synchronization/mutex.h"
#include "software_metrics_config.pb.h"
#include "resource.pb.h"
#include "software_metrics.pb.h"
#include "tlbmc/utils/command_executor.h"
#include "tlbmc/utils/shell_command_executor.h"
namespace milotic_tlbmc {
/*
The ss linux command is 'socketstats'.
-t displays only TCP sockets
-a displays all sockets (listening and non-listening)
-n displays numeric host names
This is piped into awk which does the following:
1. Skip the first line since it's just headers and we don't care about them
For each row after that:
2. Target column number 4 as part of the split command and cuts it into two
using the colon as a delimiter (Looks like ${IP}:${PORT})
3. Prints TCP socket state column (column 1 or $1 in the script) as well as the
last portion of the column 4 split in step 2 which happens to be the port.
*/
// clang-format off
/*
ss -tan output will be:
State Recv-Q Send-Q Local Address:Port Peer Address:Port
LISTEN 0 10 *:23 *:*
LISTEN 0 4096 *:22 *:*
TIME-WAIT 0 0 [2002:a05:613a:84d1:fd01::]:22 [2001:4860:f803:3fe::b]:47104
ESTAB 0 0 [2002:a05:613a:84d1:fd01::]:443 [2002:a05:613a:84d1:1000::]:48022
*/
// clang-format on
/*
after being piped into the awk script becomes:
LISTEN 23
LISTEN 22
TIME-WAIT 22
ESTAB 443
*/
static const absl::NoDestructor<std::string> kSocketStatCmd(
"ss -tan | awk 'NR > 1 { split($4, a, \":\"); print $1, a[length(a)] }'");
/*
The nft command is used to interact with the netfilter subsystem
list chain inet filter output displays all output chain rules
-j ensures the output is in JSON format
The JSON is then processed by the software_metrics object.
Example:
{
"nftables": [
{
"metainfo": {
"version": "1.0.9",
"release_name": "Old Doc Yak #3",
"json_schema_version": 1
}
},
{
"table": {
"family": "inet",
"name": "filter",
"handle": 25
}
},
{
"rule": {
"family": "inet",
"table": "filter",
"chain": "count_port_22",
"handle": 18,
"comment": "tcp-server-22-synack",
"expr": [
{
"counter": {
"packets": 41,
"bytes": 3200
}
}
]
}
}
]
}
*/
static const absl::NoDestructor<std::string> kNetFilterCmd(
"nft -j list table inet filter");
// Any implementation of this interface must be thread-safe regarding the listed
// functions.
class SoftwareMetrics {
public:
static absl::StatusOr<std::unique_ptr<SoftwareMetrics>> Create(
const SoftwareMetricsConfig& config);
static absl::StatusOr<std::unique_ptr<SoftwareMetrics>>
CreateWithExecutorForUnitTest(
const SoftwareMetricsConfig& config,
absl::flat_hash_map<std::string, absl::StatusOr<std::string>>&);
static SoftwareMetricsAttributesStatic CreateStaticAttributes(
const SoftwareMetricsConfig& software_metrics_config);
SoftwareMetricsValue GetSoftwareMetricsValues() const
ABSL_LOCKS_EXCLUDED(metric_data_mutex_) {
absl::MutexLock lock(&metric_data_mutex_);
return values_;
}
virtual ~SoftwareMetrics() = default;
SocketStatStates GetSocketStatStates() const
ABSL_LOCKS_EXCLUDED(metric_data_mutex_) {
absl::MutexLock lock(&metric_data_mutex_);
return values_.socket_stat_state();
}
NetFilterStates GetNetFilterStates() const
ABSL_LOCKS_EXCLUDED(metric_data_mutex_) {
absl::MutexLock lock(&metric_data_mutex_);
return values_.netfilter_state();
}
SoftwareMetricsAttributesDynamic GetSoftwareMetricsAttributesDynamic() const {
absl::MutexLock lock(&metric_attributes_dynamic_mutex_);
return metrics_attributes_dynamic_;
}
const SoftwareMetricsAttributesStatic& GetSoftwareMetricsAttributesStatic()
const {
return metrics_attributes_static_;
}
// Refreshes the metric data once asynchronously. The metric data will be
// updated when the refresh is done.
// The `callback` will be called when the refresh is done. The callback is
// supposed to be blocking.
void RefreshOnce(absl::AnyInvocable<void()> callback)
ABSL_LOCKS_EXCLUDED(metric_data_mutex_);
CommandExecutor* GetExecutorForTest() const {
return executor_.get();
}
private:
explicit SoftwareMetrics(
const SoftwareMetricsAttributesStatic& software_attributes_static)
: metrics_attributes_static_(software_attributes_static),
ss_port_set_(metrics_attributes_static_.software_metrics_config()
.socket_stat_ports()
.begin(),
metrics_attributes_static_.software_metrics_config()
.socket_stat_ports()
.end()),
nf_port_set_(metrics_attributes_static_.software_metrics_config()
.netfilter_ports()
.begin(),
metrics_attributes_static_.software_metrics_config()
.netfilter_ports()
.end()),
executor_(std::make_unique<ShellExecutor>()) {}
SoftwareMetrics(SoftwareMetricsAttributesStatic& software_attributes_static,
std::unique_ptr<CommandExecutor> executor)
: metrics_attributes_static_(software_attributes_static),
ss_port_set_(metrics_attributes_static_.software_metrics_config()
.socket_stat_ports()
.begin(),
metrics_attributes_static_.software_metrics_config()
.socket_stat_ports()
.end()),
nf_port_set_(metrics_attributes_static_.software_metrics_config()
.netfilter_ports()
.begin(),
metrics_attributes_static_.software_metrics_config()
.netfilter_ports()
.end()),
executor_(std::move(executor)) {}
void StoreData(SoftwareMetricsValue* metric_data)
ABSL_LOCKS_EXCLUDED(metric_data_mutex_);
absl::StatusOr<NetFilterStates> UpdateNetFilterStats();
absl::StatusOr<SocketStatStates> UpdateSocketStats();
const SoftwareMetricsAttributesStatic metrics_attributes_static_;
const absl::flat_hash_set<int32_t> ss_port_set_;
const absl::flat_hash_set<int32_t> nf_port_set_;
mutable absl::Mutex metric_attributes_dynamic_mutex_;
SoftwareMetricsAttributesDynamic metrics_attributes_dynamic_
ABSL_GUARDED_BY(metric_attributes_dynamic_mutex_);
std::unique_ptr<CommandExecutor> executor_;
mutable absl::Mutex metric_data_mutex_;
SoftwareMetricsValue values_ ABSL_GUARDED_BY(metric_data_mutex_);
};
} // namespace milotic_tlbmc
#endif // THIRD_PARTY_MILOTIC_EXTERNAL_CC_TLBMC_METRICS_SOFTWARE_METRICS_H_