diff options
author | Matthew Massey <matthewmassey@fb.com> | 2021-09-30 10:54:25 -0600 |
---|---|---|
committer | Matthew Massey <matthewmassey@fb.com> | 2021-09-30 13:40:26 -0600 |
commit | 31325ca63b4a853a4caec518ad4b27b33150f674 (patch) | |
tree | 02c49a5dcc52ae2360b3ffa7cdbfc964b036db59 | |
parent | c31cfa6d54ff3ae94bc1b6438a5b3ac21f6627a4 (diff) | |
download | ohai-31325ca63b4a853a4caec518ad4b27b33150f674.tar.gz |
initial commit of plugin to parse tc qdiscs
Signed-off-by: Matthew Massey <matthewmassey@fb.com>
-rw-r--r-- | lib/ohai/plugins/linux/tc.rb | 61 | ||||
-rw-r--r-- | spec/unit/plugins/linux/tc_spec.rb | 124 |
2 files changed, 185 insertions, 0 deletions
diff --git a/lib/ohai/plugins/linux/tc.rb b/lib/ohai/plugins/linux/tc.rb new file mode 100644 index 00000000..0471bb59 --- /dev/null +++ b/lib/ohai/plugins/linux/tc.rb @@ -0,0 +1,61 @@ +# +# Author:: Matthew Massey <matthewmassey@fb.com> +# Copyright:: Copyright (c) 2021 Facebook +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +Ohai.plugin(:Tc) do + provides "tc" + optional true + + collect_data(:linux) do + tc_path = which("tc") + if tc_path + cmd = "#{tc_path} qdisc show" + tc_output = shell_out(cmd) + + tc_data = Mash.new + tc_data[:qdisc] = Mash.new + + tc_output.stdout.split("\n").each do |line| + line = line.strip + if /dev (\w+)/ =~ line + dev = $1 + tc_data[:qdisc][dev] ||= Mash.new + else + next + end + if /qdisc (\w+)/ =~ line + qdisc = $1 + tc_data[:qdisc][dev][:qdiscs] ||= [] + tc_data[:qdisc][dev][:qdiscs] << Mash.new + qdisc_idx = tc_data[:qdisc][dev][:qdiscs].length - 1 + tc_data[:qdisc][dev][:qdiscs][qdisc_idx] ||= Mash.new + tc_data[:qdisc][dev][:qdiscs][qdisc_idx][:type] = qdisc + tc_data[:qdisc][dev][:qdiscs][qdisc_idx][:parms] ||= Mash.new + else + next + end + if qdisc == "fq" && /buckets (\d+)/ =~ line + buckets = $1.to_i + tc_data[:qdisc][dev][:qdiscs][qdisc_idx][:parms][:buckets] = buckets + end + end + tc tc_data + else + logger.trace("Plugin Tc: Could not find tc. Skipping plugin.") + end + end +end diff --git a/spec/unit/plugins/linux/tc_spec.rb b/spec/unit/plugins/linux/tc_spec.rb new file mode 100644 index 00000000..d04e963e --- /dev/null +++ b/spec/unit/plugins/linux/tc_spec.rb @@ -0,0 +1,124 @@ +# +# Author:: Matthew Massey <matthewmassey@fb.com> +# Copyright:: Copyright (c) 2021 Facebook +# License:: Apache License, Version 2.0 +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +require "spec_helper" + +describe Ohai::System, "Linux tc plugin" do + let(:plugin) { get_plugin("linux/tc") } + + before do + allow(plugin).to receive(:collect_os).and_return(:linux) + end + + it "populates tc if tc is found" do + tc_out = <<-TC_OUT + qdisc noqueue 0: dev lo root refcnt 2 + qdisc mq 1234: dev eth0 root + qdisc fq 8001: dev eth0 parent 1234:4 limit 10000p flow_limit 100p buckets 2048 orphan_mask 1023 quantum 3028b initial_quantum 15140b low_rate_threshold 550Kbit refill_delay 40.0ms + qdisc fq_codel 0: dev eth0 parent 1234:7 limit 10240p flows 1024 quantum 1514 target 5.0ms interval 100.0ms memory_limit 32Mb ecn + qdisc fq_codel 0: dev eth0 parent 1234:5 limit 10240p flows 1024 quantum 1514 target 5.0ms interval 100.0ms memory_limit 32Mb ecn + qdisc fq_codel 0: dev eth0 parent 1234:3 limit 10240p flows 1024 quantum 1514 target 5.0ms interval 100.0ms memory_limit 32Mb ecn + qdisc fq_codel 0: dev eth0 parent 1234:1 limit 10240p flows 1024 quantum 1514 target 5.0ms interval 100.0ms memory_limit 32Mb ecn + qdisc fq 8003: dev eth0 parent 1234:6 limit 10000p flow_limit 100p buckets 8192 orphan_mask 1023 quantum 3028b initial_quantum 15140b low_rate_threshold 550Kbit refill_delay 40.0ms + qdisc fq 8002: dev eth0 parent 1234:2 limit 10000p flow_limit 100p buckets 4096 orphan_mask 1023 nopacing quantum 3028b initial_quantum 15140b low_rate_threshold 550Kbit refill_delay 40.0ms + qdisc pfifo_fast 8004: dev eth0 parent 1234:8 bands 3 priomap 1 2 2 2 1 2 0 0 1 1 1 1 1 1 1 1 + qdisc clsact ffff: dev eth0 parent ffff:fff1 + garbage line, this won't parse and should not break anything + dev eth99 garbage line but with a device name + qdisc fq 1234: dev eth0 fq but with no parms + TC_OUT + allow(plugin).to receive(:which).with("tc").and_return("/sbin/tc") + cmd = "/sbin/tc qdisc show" + allow(plugin).to receive(:shell_out).with(cmd).and_return(mock_shell_out(0, tc_out, "")) + plugin.run + + expect(plugin[:tc].to_hash).to eq({ + "qdisc" => { + "lo" => { + "qdiscs" => [ + { + "type" => "noqueue", + "parms" => {}, + }, + ], + }, + "eth0" => { + "qdiscs" => [ + { + "type" => "mq", + "parms" => {}, + }, + { + "type" => "fq", + "parms" => { + "buckets" => 2048, + }, + }, + { + "type" => "fq_codel", + "parms" => {}, + }, + { + "type" => "fq_codel", + "parms" => {}, + }, + { + "type" => "fq_codel", + "parms" => {}, + }, + { + "type" => "fq_codel", + "parms" => {}, + }, + { + "type" => "fq", + "parms" => { + "buckets" => 8192, + }, + }, + { + "type" => "fq", + "parms" => { + "buckets" => 4096, + }, + }, + { + "type" => "pfifo_fast", + "parms" => {}, + }, + { + "type" => "clsact", + "parms" => {}, + }, + { + "type" => "fq", + "parms" => {}, + }, + ], + }, + "eth99" => {}, + }, + }) + end + + it "does not populate tc if tc is not found" do + allow(plugin).to receive(:which).with("tc").and_return(false) + plugin.run + expect(plugin[:tc]).to be(nil) + end +end |