From 017a25e158a641f9f88ecd9f2a2475cb3b6c7cdf Mon Sep 17 00:00:00 2001 From: Bryan Seay <42394778+bryanseay@users.noreply.github.com> Date: Mon, 19 Aug 2019 14:01:39 -0400 Subject: [PATCH] [SMAGENT-1855] Docker engine support for cpuset usage (#1473) Parses the CpusetCpus value from the docker socket, parses it to count the number of cpus and stores the value with the other container data. --- userspace/libsinsp/cgroup_list_counter.h | 136 ++++++++++++++++++ userspace/libsinsp/container.cpp | 1 + .../container_engine/docker_common.cpp | 9 +- userspace/libsinsp/container_info.h | 2 + userspace/libsinsp/parsers.cpp | 6 + 5 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 userspace/libsinsp/cgroup_list_counter.h diff --git a/userspace/libsinsp/cgroup_list_counter.h b/userspace/libsinsp/cgroup_list_counter.h new file mode 100644 index 0000000000..f4121a8886 --- /dev/null +++ b/userspace/libsinsp/cgroup_list_counter.h @@ -0,0 +1,136 @@ +#pragma once + +#include +#include "logger.h" + +namespace libsinsp +{ + +/** + * Simple helper to read a comma-separated list that includes ranges and + * determine the total count of the values within. + * Examples: 1,4-5 = 3; 0-15 = 16; 3,7,11 = 3 + * + * See the "List Format" section of + * http://man7.org/linux/man-pages/man7/cpuset.7.html + * + * Returns -1 if string is invalid. + */ +class cgroup_list_counter +{ +public: + const int INVALID_CPU_COUNT = -1; + + /** + * Return the number of elements given by the buffer. If needed, log at the + * given log-level. + */ + int operator ()(const char *buffer, sinsp_logger::severity log_level) + { + reset(); + + int cpu_count = 0; + + try + { + const char *position = buffer; + for(; '\0' != *position; ++position) + { + if ('-' == *position) + { + if (nullptr == m_section_start) + { + throw std::runtime_error("duplicate range indicator before start"); + } + if (nullptr != m_range_indicator) + { + throw std::runtime_error("duplicate range indicators"); + } + + m_range_indicator = position; + } + else if (',' == *position) + { + cpu_count += process_section(m_section_start, position, m_range_indicator); + reset(); + } + else if (nullptr == m_section_start) + { + m_section_start = position; + } + + } + + // There is never a trailing comma so always process the + // final section + cpu_count += process_section(m_section_start, position, m_range_indicator); + + } + catch (const std::exception& ex) + { + g_logger.format(log_level, + "Invalid List Format: %s. Detail: %s", + buffer, + ex.what()); + return INVALID_CPU_COUNT; + } + + return cpu_count; + } + +private: + + static int process_number(const char *section_start, const char *section_end) + { + std::string section(section_start, section_end - section_start); + return std::stoi(section.c_str()); + + } + + static int process_section(const char *section_start, const char *section_end, const char *range_indicator) + { + if (nullptr == section_start) + { + throw std::runtime_error("invalid end of section before start of section"); + } + + if (nullptr == section_end) + { + throw std::runtime_error("invalid end of section"); + } + + if (section_end <= section_start) + { + throw std::runtime_error("invalid section"); + } + + if (range_indicator) + { + // Split into two sections + int first = process_number(section_start, range_indicator); + int second = process_number(range_indicator + 1, section_end); + + if (second <= first) + { + throw std::runtime_error("invalid range"); + } + + return second - first + 1; + } + + // We don't care what the value is, we just want to know that it is a number + (void)process_number(section_start, section_end); + return 1; + } + + void reset() + { + m_section_start = nullptr; + m_range_indicator = nullptr; + } + + const char *m_section_start = nullptr; + const char *m_range_indicator = nullptr; +}; + +} \ No newline at end of file diff --git a/userspace/libsinsp/container.cpp b/userspace/libsinsp/container.cpp index f7fb7edc22..062f187900 100644 --- a/userspace/libsinsp/container.cpp +++ b/userspace/libsinsp/container.cpp @@ -219,6 +219,7 @@ string sinsp_container_manager::container_to_json(const sinsp_container_info& co container["cpu_shares"] = (Json::Value::Int64) container_info.m_cpu_shares; container["cpu_quota"] = (Json::Value::Int64) container_info.m_cpu_quota; container["cpu_period"] = (Json::Value::Int64) container_info.m_cpu_period; + container["cpuset_cpu_count"] = (Json::Value::Int) container_info.m_cpuset_cpu_count; if(!container_info.m_mesos_task_id.empty()) { diff --git a/userspace/libsinsp/container_engine/docker_common.cpp b/userspace/libsinsp/container_engine/docker_common.cpp index f1995c469c..f5b576371d 100644 --- a/userspace/libsinsp/container_engine/docker_common.cpp +++ b/userspace/libsinsp/container_engine/docker_common.cpp @@ -18,6 +18,7 @@ limitations under the License. */ #include "container_engine/docker.h" +#include "cgroup_list_counter.h" #include "sinsp.h" #include "sinsp_int.h" #include "container.h" @@ -739,7 +740,13 @@ bool docker_async_source::parse_docker(std::string &container_id, sinsp_containe { container->m_cpu_period = cpu_period; } - const Json::Value &privileged = host_config_obj["Privileged"]; + const auto cpuset_cpus = host_config_obj["CpusetCpus"].asString(); + if (!cpuset_cpus.empty()) + { + libsinsp::cgroup_list_counter counter; + container->m_cpuset_cpu_count = counter(cpuset_cpus.c_str(), sinsp_logger::SEV_DEBUG); + } + const Json::Value& privileged = host_config_obj["Privileged"]; if(!privileged.isNull() && privileged.isBool()) { container->m_privileged = privileged.asBool(); diff --git a/userspace/libsinsp/container_info.h b/userspace/libsinsp/container_info.h index 6550f5301a..357d522703 100644 --- a/userspace/libsinsp/container_info.h +++ b/userspace/libsinsp/container_info.h @@ -182,6 +182,7 @@ class sinsp_container_info m_cpu_shares(1024), m_cpu_quota(0), m_cpu_period(100000), + m_cpuset_cpu_count(0), m_is_pod_sandbox(false), m_metadata_complete(true), m_metadata_deadline(0) @@ -223,6 +224,7 @@ class sinsp_container_info int64_t m_cpu_shares; int64_t m_cpu_quota; int64_t m_cpu_period; + int32_t m_cpuset_cpu_count; std::list m_health_probes; bool m_is_pod_sandbox; diff --git a/userspace/libsinsp/parsers.cpp b/userspace/libsinsp/parsers.cpp index 6b98059337..0f7c7530e3 100644 --- a/userspace/libsinsp/parsers.cpp +++ b/userspace/libsinsp/parsers.cpp @@ -4708,6 +4708,12 @@ void sinsp_parser::parse_container_json_evt(sinsp_evt *evt) container_info.m_cpu_period = cpu_period.asInt64(); } + const Json::Value& cpuset_cpu_count = container["cpuset_cpu_count"]; + if(check_json_val_is_convertible(cpuset_cpu_count, Json::intValue, "cpuset_cpu_count")) + { + container_info.m_cpuset_cpu_count = cpuset_cpu_count.asInt(); + } + const Json::Value& mesos_task_id = container["mesos_task_id"]; if(check_json_val_is_convertible(mesos_task_id, Json::stringValue, "mesos_task_id")) {