blob: 95edf060f26e360bb2cd88bbf6f4332c17d9bad7 [file]
#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 <string_view>
#include <utility>
#include <vector>
#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/status/status.h"
#include "absl/status/statusor.h"
#include "absl/strings/string_view.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"
#include "tlbmc/true_hitless_metrics.pb.h"
#include "tlbmc/true_hitless/true_hitless_metrics_parser.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 constexpr std::string_view 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 constexpr std::string_view kNetFilterCmd(
"nft -j list table inet filter");
static constexpr std::string_view kTrueHitlessMetricsUpdateCmd(
"systemctl start --no-block true-hitless-status-update.service");
// Any implementation of this interface must be thread-safe regarding the listed
// functions.
class SoftwareMetrics {
public:
// Default base path for PSI metrics
static inline const absl::NoDestructor<std::string> kDefaultPsiBasePath{
"/sys/fs/cgroup"};
static absl::StatusOr<std::unique_ptr<SoftwareMetrics>> Create(
const SoftwareMetricsConfig& config);
// `root_path` is used to redirect filesystem access (e.g., to /run/bios_key)
// during integration testing. It defaults to "/" in production.
static absl::StatusOr<std::unique_ptr<SoftwareMetrics>> Create(
const SoftwareMetricsConfig& config, absl::string_view psi_base_path,
absl::string_view root_path = "/");
static absl::StatusOr<std::unique_ptr<SoftwareMetrics>>
CreateWithExecutorForUnitTest(
const SoftwareMetricsConfig& config,
absl::flat_hash_map<std::string, absl::StatusOr<std::string>>&);
static absl::StatusOr<std::unique_ptr<SoftwareMetrics>>
CreateWithExecutorForUnitTest(
const SoftwareMetricsConfig& config,
absl::flat_hash_map<std::string, absl::StatusOr<std::string>>&,
absl::string_view psi_base_path, absl::string_view root_path = "/");
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();
}
absl::StatusOr<PsiMetrics> GetPsiMetrics() const
ABSL_LOCKS_EXCLUDED(metric_data_mutex_);
// Get PSI metrics for a specific service
absl::StatusOr<PsiMetrics> GetPsiMetricsForService(
const std::string& service_name) const
ABSL_LOCKS_EXCLUDED(metric_data_mutex_);
// Get list of available services in system.slice
absl::StatusOr<std::vector<std::string>> GetAvailableServices() const
ABSL_LOCKS_EXCLUDED(metric_data_mutex_);
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_);
void RefreshTrueHitlessMetrics(absl::AnyInvocable<void()> callback);
void RefreshBiosKeyStatus() ABSL_LOCKS_EXCLUDED(metric_data_mutex_);
TrueHitlessMetrics GetTrueHitlessMetrics();
CommandExecutor* GetExecutorForTest() const { return executor_.get(); }
private:
friend class SoftwareMetricsTest;
SoftwareMetrics(
const SoftwareMetricsAttributesStatic& software_attributes_static,
absl::string_view psi_base_path, absl::string_view root_path)
: 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>()),
true_hitless_metrics_parser_(TrueHitlessMetricsParser(
metrics_attributes_static_.software_metrics_config()
.true_hitless_metrics_path())),
psi_base_path_(psi_base_path),
root_path_(root_path) {}
SoftwareMetrics(SoftwareMetricsAttributesStatic& software_attributes_static,
std::unique_ptr<CommandExecutor> executor,
absl::string_view psi_base_path, absl::string_view root_path)
: 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)),
true_hitless_metrics_parser_(TrueHitlessMetricsParser(
metrics_attributes_static_.software_metrics_config()
.true_hitless_metrics_path())),
psi_base_path_(psi_base_path),
root_path_(root_path) {}
absl::StatusOr<NetFilterStates> UpdateNetFilterStats();
absl::StatusOr<SocketStatStates> UpdateSocketStats();
absl::Status UpdatePsiData(SoftwareMetricsValue* value);
absl::StatusOr<PsiMetrics> ReadSystemPsiMetrics();
absl::StatusOr<std::vector<std::string>> ReadAvailableServices();
absl::StatusOr<PsiMetrics> ReadPsiMetricsForService(
const std::string& service_name);
absl::StatusOr<BiosKeyStatus> ReadBiosKeyStatus();
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_);
TrueHitlessMetricsParser true_hitless_metrics_parser_;
const std::string psi_base_path_;
// Root path for filesystem access, used for sandboxing in integration tests.
const std::string root_path_;
};
} // namespace milotic_tlbmc
#endif // THIRD_PARTY_MILOTIC_EXTERNAL_CC_TLBMC_METRICS_SOFTWARE_METRICS_H_