| #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_ |