ohai 18.2.6 → 19.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/Gemfile +28 -28
- data/LICENSE +201 -201
- data/bin/ohai +25 -25
- data/lib/ohai/application.rb +189 -189
- data/lib/ohai/common/dmi.rb +167 -167
- data/lib/ohai/config.rb +51 -51
- data/lib/ohai/dsl/plugin/versionvii.rb +203 -203
- data/lib/ohai/dsl/plugin.rb +232 -232
- data/lib/ohai/dsl.rb +22 -22
- data/lib/ohai/exception.rb +36 -36
- data/lib/ohai/hints.rb +68 -68
- data/lib/ohai/loader.rb +178 -178
- data/lib/ohai/log.rb +34 -34
- data/lib/ohai/mash.rb +22 -22
- data/lib/ohai/mixin/alibaba_metadata.rb +83 -83
- data/lib/ohai/mixin/azure_metadata.rb +105 -105
- data/lib/ohai/mixin/chef_utils_wiring.rb +52 -52
- data/lib/ohai/mixin/command.rb +4 -4
- data/lib/ohai/mixin/constant_helper.rb +55 -55
- data/lib/ohai/mixin/dmi_decode.rb +54 -54
- data/lib/ohai/mixin/do_metadata.rb +48 -48
- data/lib/ohai/mixin/ec2_metadata.rb +264 -270
- data/lib/ohai/mixin/gce_metadata.rb +79 -79
- data/lib/ohai/mixin/http_helper.rb +64 -64
- data/lib/ohai/mixin/json_helper.rb +36 -36
- data/lib/ohai/mixin/network_helper.rb +92 -92
- data/lib/ohai/mixin/oci_metadata.rb +60 -60
- data/lib/ohai/mixin/os.rb +128 -128
- data/lib/ohai/mixin/scaleway_metadata.rb +51 -51
- data/lib/ohai/mixin/seconds_to_human.rb +52 -52
- data/lib/ohai/mixin/shell_out.rb +51 -51
- data/lib/ohai/mixin/softlayer_metadata.rb +74 -74
- data/lib/ohai/mixin/string.rb +31 -31
- data/lib/ohai/mixin/train_helpers.rb +36 -36
- data/lib/ohai/mixin/which.rb +39 -39
- data/lib/ohai/plugin_config.rb +47 -47
- data/lib/ohai/plugins/aix/kernel.rb +50 -50
- data/lib/ohai/plugins/aix/memory.rb +37 -37
- data/lib/ohai/plugins/aix/network.rb +142 -142
- data/lib/ohai/plugins/aix/platform.rb +30 -30
- data/lib/ohai/plugins/aix/uptime.rb +54 -54
- data/lib/ohai/plugins/aix/virtualization.rb +154 -154
- data/lib/ohai/plugins/alibaba.rb +72 -72
- data/lib/ohai/plugins/azure.rb +154 -154
- data/lib/ohai/plugins/bsd/virtualization.rb +121 -121
- data/lib/ohai/plugins/c.rb +178 -178
- data/lib/ohai/plugins/chef.rb +50 -50
- data/lib/ohai/plugins/cloud.rb +379 -379
- data/lib/ohai/plugins/command.rb +26 -26
- data/lib/ohai/plugins/cpu.rb +642 -642
- data/lib/ohai/plugins/darwin/hardware.rb +99 -99
- data/lib/ohai/plugins/darwin/memory.rb +62 -62
- data/lib/ohai/plugins/darwin/network.rb +207 -207
- data/lib/ohai/plugins/darwin/platform.rb +40 -40
- data/lib/ohai/plugins/darwin/virtualization.rb +104 -104
- data/lib/ohai/plugins/digital_ocean.rb +67 -67
- data/lib/ohai/plugins/dmi.rb +134 -134
- data/lib/ohai/plugins/docker.rb +58 -58
- data/lib/ohai/plugins/dragonflybsd/memory.rb +60 -60
- data/lib/ohai/plugins/dragonflybsd/network.rb +128 -128
- data/lib/ohai/plugins/dragonflybsd/platform.rb +28 -28
- data/lib/ohai/plugins/ec2.rb +148 -148
- data/lib/ohai/plugins/elixir.rb +36 -36
- data/lib/ohai/plugins/erlang.rb +60 -60
- data/lib/ohai/plugins/eucalyptus.rb +86 -86
- data/lib/ohai/plugins/filesystem.rb +753 -753
- data/lib/ohai/plugins/fips.rb +36 -36
- data/lib/ohai/plugins/freebsd/memory.rb +60 -60
- data/lib/ohai/plugins/freebsd/network.rb +128 -128
- data/lib/ohai/plugins/freebsd/platform.rb +28 -28
- data/lib/ohai/plugins/gce.rb +89 -89
- data/lib/ohai/plugins/go.rb +34 -34
- data/lib/ohai/plugins/groovy.rb +38 -38
- data/lib/ohai/plugins/grub2.rb +40 -40
- data/lib/ohai/plugins/habitat.rb +73 -73
- data/lib/ohai/plugins/haskell.rb +96 -96
- data/lib/ohai/plugins/hostname.rb +133 -133
- data/lib/ohai/plugins/init_package.rb +26 -26
- data/lib/ohai/plugins/java.rb +78 -78
- data/lib/ohai/plugins/kernel.rb +292 -292
- data/lib/ohai/plugins/keys.rb +27 -27
- data/lib/ohai/plugins/languages.rb +26 -26
- data/lib/ohai/plugins/libvirt.rb +114 -114
- data/lib/ohai/plugins/linode.rb +73 -73
- data/lib/ohai/plugins/linux/block_device.rb +48 -48
- data/lib/ohai/plugins/linux/hostnamectl.rb +34 -34
- data/lib/ohai/plugins/linux/interrupts.rb +84 -83
- data/lib/ohai/plugins/linux/ipc.rb +52 -52
- data/lib/ohai/plugins/linux/livepatch.rb +38 -38
- data/lib/ohai/plugins/linux/lsb.rb +46 -46
- data/lib/ohai/plugins/linux/lspci.rb +80 -80
- data/lib/ohai/plugins/linux/machineid.rb +36 -36
- data/lib/ohai/plugins/linux/mdadm.rb +120 -120
- data/lib/ohai/plugins/linux/memory.rb +106 -106
- data/lib/ohai/plugins/linux/network.rb +879 -879
- data/lib/ohai/plugins/linux/os_release.rb +38 -38
- data/lib/ohai/plugins/linux/platform.rb +314 -314
- data/lib/ohai/plugins/linux/selinux.rb +69 -69
- data/lib/ohai/plugins/linux/sessions.rb +54 -54
- data/lib/ohai/plugins/linux/sysctl.rb +39 -39
- data/lib/ohai/plugins/linux/systemd_paths.rb +36 -36
- data/lib/ohai/plugins/linux/tc.rb +61 -61
- data/lib/ohai/plugins/linux/virtualization.rb +300 -300
- data/lib/ohai/plugins/lua.rb +39 -39
- data/lib/ohai/plugins/mono.rb +50 -50
- data/lib/ohai/plugins/netbsd/memory.rb +99 -99
- data/lib/ohai/plugins/netbsd/network.rb +122 -122
- data/lib/ohai/plugins/netbsd/platform.rb +28 -28
- data/lib/ohai/plugins/network.rb +186 -186
- data/lib/ohai/plugins/nodejs.rb +40 -40
- data/lib/ohai/plugins/oci.rb +94 -94
- data/lib/ohai/plugins/ohai.rb +29 -29
- data/lib/ohai/plugins/ohai_time.rb +26 -26
- data/lib/ohai/plugins/openbsd/memory.rb +99 -99
- data/lib/ohai/plugins/openbsd/network.rb +122 -122
- data/lib/ohai/plugins/openbsd/platform.rb +28 -28
- data/lib/ohai/plugins/openstack.rb +84 -84
- data/lib/ohai/plugins/os.rb +55 -55
- data/lib/ohai/plugins/packages.rb +234 -234
- data/lib/ohai/plugins/passwd.rb +104 -104
- data/lib/ohai/plugins/perl.rb +45 -45
- data/lib/ohai/plugins/php.rb +52 -52
- data/lib/ohai/plugins/platform.rb +41 -41
- data/lib/ohai/plugins/powershell.rb +82 -82
- data/lib/ohai/plugins/ps.rb +35 -35
- data/lib/ohai/plugins/python.rb +43 -43
- data/lib/ohai/plugins/rackspace.rb +177 -177
- data/lib/ohai/plugins/root_group.rb +41 -41
- data/lib/ohai/plugins/rpm.rb +121 -121
- data/lib/ohai/plugins/ruby.rb +66 -66
- data/lib/ohai/plugins/rust.rb +34 -34
- data/lib/ohai/plugins/scala.rb +38 -38
- data/lib/ohai/plugins/scaleway.rb +58 -58
- data/lib/ohai/plugins/scsi.rb +52 -52
- data/lib/ohai/plugins/shard.rb +142 -142
- data/lib/ohai/plugins/shells.rb +32 -32
- data/lib/ohai/plugins/softlayer.rb +48 -48
- data/lib/ohai/plugins/solaris2/dmi.rb +191 -191
- data/lib/ohai/plugins/solaris2/memory.rb +32 -32
- data/lib/ohai/plugins/solaris2/network.rb +192 -192
- data/lib/ohai/plugins/solaris2/platform.rb +58 -58
- data/lib/ohai/plugins/solaris2/virtualization.rb +90 -90
- data/lib/ohai/plugins/ssh_host_key.rb +84 -84
- data/lib/ohai/plugins/sysconf.rb +46 -46
- data/lib/ohai/plugins/timezone.rb +45 -45
- data/lib/ohai/plugins/train.rb +35 -35
- data/lib/ohai/plugins/uptime.rb +95 -95
- data/lib/ohai/plugins/virtualbox.rb +197 -197
- data/lib/ohai/plugins/vmware.rb +109 -109
- data/lib/ohai/plugins/windows/dmi.rb +95 -95
- data/lib/ohai/plugins/windows/drivers.rb +52 -52
- data/lib/ohai/plugins/windows/memory.rb +39 -39
- data/lib/ohai/plugins/windows/network.rb +222 -222
- data/lib/ohai/plugins/windows/platform.rb +34 -34
- data/lib/ohai/plugins/windows/system_enclosure.rb +29 -29
- data/lib/ohai/plugins/windows/virtualization.rb +45 -45
- data/lib/ohai/plugins/zpools.rb +94 -94
- data/lib/ohai/provides_map.rb +208 -208
- data/lib/ohai/runner.rb +128 -126
- data/lib/ohai/system.rb +258 -258
- data/lib/ohai/train_transport.rb +29 -29
- data/lib/ohai/util/file_helper.rb +6 -6
- data/lib/ohai/util/ip_helper.rb +56 -56
- data/lib/ohai/util/win32.rb +47 -47
- data/lib/ohai/version.rb +23 -23
- data/lib/ohai.rb +23 -23
- data/ohai.gemspec +35 -35
- metadata +9 -15
@@ -1,879 +1,879 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
#
|
3
|
-
# Author:: Adam Jacob (<adam@chef.io>)
|
4
|
-
# Author:: Chris Read <chris.read@gmail.com>
|
5
|
-
# Copyright:: Copyright (c) Chef Software Inc.
|
6
|
-
# License:: Apache License, Version 2.0
|
7
|
-
#
|
8
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
9
|
-
# you may not use this file except in compliance with the License.
|
10
|
-
# You may obtain a copy of the License at
|
11
|
-
#
|
12
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
13
|
-
#
|
14
|
-
# Unless required by applicable law or agreed to in writing, software
|
15
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
16
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
17
|
-
# See the License for the specific language governing permissions and
|
18
|
-
# limitations under the License.
|
19
|
-
#
|
20
|
-
|
21
|
-
Ohai.plugin(:Network) do
|
22
|
-
provides "network", "network/interfaces"
|
23
|
-
provides "counters/network", "counters/network/interfaces"
|
24
|
-
provides "ipaddress", "ip6address", "macaddress"
|
25
|
-
|
26
|
-
def linux_encaps_lookup(encap)
|
27
|
-
return "Loopback" if encap.eql?("Local Loopback") || encap.eql?("loopback")
|
28
|
-
return "PPP" if encap.eql?("Point-to-Point Protocol")
|
29
|
-
return "SLIP" if encap.eql?("Serial Line IP")
|
30
|
-
return "VJSLIP" if encap.eql?("VJ Serial Line IP")
|
31
|
-
return "IPIP" if encap.eql?("IPIP Tunnel")
|
32
|
-
return "6to4" if encap.eql?("IPv6-in-IPv4")
|
33
|
-
return "Ethernet" if encap.eql?("ether")
|
34
|
-
|
35
|
-
encap
|
36
|
-
end
|
37
|
-
|
38
|
-
def ipv6_enabled?
|
39
|
-
file_exist? "/proc/net/if_inet6"
|
40
|
-
end
|
41
|
-
|
42
|
-
def ethtool_binary_path
|
43
|
-
@ethtool ||= which("ethtool")
|
44
|
-
end
|
45
|
-
|
46
|
-
def is_openvz?
|
47
|
-
@openvz ||= file_directory?("/proc/vz")
|
48
|
-
end
|
49
|
-
|
50
|
-
def is_openvz_host?
|
51
|
-
is_openvz? && file_directory?("/proc/bc")
|
52
|
-
end
|
53
|
-
|
54
|
-
def extract_neighbors(family, iface, neigh_attr)
|
55
|
-
so = shell_out("ip -f #{family[:name]} neigh show")
|
56
|
-
so.stdout.lines do |line|
|
57
|
-
if line =~ /^([a-f0-9\:\.]+)\s+dev\s+([^\s]+)\s+lladdr\s+([a-fA-F0-9\:]+)/
|
58
|
-
interface = iface[$2]
|
59
|
-
unless interface
|
60
|
-
logger.warn("neighbor list has entries for unknown interface #{interface}")
|
61
|
-
next
|
62
|
-
end
|
63
|
-
interface[neigh_attr] ||= Mash.new
|
64
|
-
interface[neigh_attr][$1] = $3.downcase
|
65
|
-
end
|
66
|
-
end
|
67
|
-
iface
|
68
|
-
end
|
69
|
-
|
70
|
-
# checking the routing tables
|
71
|
-
# why ?
|
72
|
-
# 1) to set the default gateway and default interfaces attributes
|
73
|
-
# 2) on some occasions, the best way to select node[:ipaddress] is to look at
|
74
|
-
# the routing table source field.
|
75
|
-
# 3) and since we're at it, let's populate some :routes attributes
|
76
|
-
# (going to do that for both inet and inet6 addresses)
|
77
|
-
def check_routing_table(family, iface, default_route_table)
|
78
|
-
so = shell_out("ip -o -f #{family[:name]} route show table #{default_route_table}")
|
79
|
-
so.stdout.lines do |line|
|
80
|
-
line.strip!
|
81
|
-
logger.trace("Plugin Network: Parsing #{line}")
|
82
|
-
if line.include?("\\")
|
83
|
-
# If we have multipath routing, then the first part will be a normal
|
84
|
-
# looking route:
|
85
|
-
# default proto ra metric 1024 <other options>
|
86
|
-
# Each successive part after that is a hop without those options.
|
87
|
-
# So the first thing we do is grab that first part, and split it into
|
88
|
-
# the route destination ("default"), and the route options.
|
89
|
-
parts = line.split("\\")
|
90
|
-
route_dest, dest_opts = parts.first.split(nil, 2)
|
91
|
-
# Then all the route endings, generally just nexthops.
|
92
|
-
route_endings = parts[1..]
|
93
|
-
if dest_opts && !dest_opts.empty?
|
94
|
-
# Route options like proto, metric, etc. only appear once for each
|
95
|
-
# multipath configuration. Prepend this information to the route
|
96
|
-
# endings so the code below will assign the fields properly.
|
97
|
-
route_endings.map! { |e| e.include?("nexthop") ? "#{dest_opts} #{e}" : e }
|
98
|
-
end
|
99
|
-
elsif line =~ /^([^\s]+)\s(.*)$/
|
100
|
-
route_dest = $1
|
101
|
-
route_endings = [$2]
|
102
|
-
else
|
103
|
-
next
|
104
|
-
end
|
105
|
-
route_endings.each do |route_ending|
|
106
|
-
route_entry = Mash.new(destination: route_dest,
|
107
|
-
|
108
|
-
route_int = nil
|
109
|
-
if route_ending =~ /\bdev\s+([^\s]+)\b/
|
110
|
-
route_int = $1
|
111
|
-
end
|
112
|
-
# does any known interface own the src address?
|
113
|
-
# we try to infer the interface/device from its address if it isn't specified
|
114
|
-
# we want to override the interface set via nexthop but only if possible
|
115
|
-
if line =~ /\bsrc\s+([^\s]+)\b/ && (!route_int || line.include?("nexthop"))
|
116
|
-
# only clobber previously set route_int if we find a match
|
117
|
-
if (match = iface.select { |name, intf| intf.fetch("addresses", {}).any? { |addr, _| addr == $1 } }.keys.first)
|
118
|
-
route_int = match
|
119
|
-
route_entry[:inferred] = true
|
120
|
-
end
|
121
|
-
end
|
122
|
-
|
123
|
-
unless route_int
|
124
|
-
logger.trace("Plugin Network: Skipping route entry without a device: '#{line}'")
|
125
|
-
next
|
126
|
-
end
|
127
|
-
route_int = "venet0:0" if is_openvz? && !is_openvz_host? && route_int == "venet0" && iface["venet0:0"]
|
128
|
-
|
129
|
-
unless iface[route_int]
|
130
|
-
logger.trace("Plugin Network: Skipping previously unseen interface from 'ip route show': #{route_int}")
|
131
|
-
next
|
132
|
-
end
|
133
|
-
|
134
|
-
%w{via scope metric proto src}.each do |k|
|
135
|
-
# http://rubular.com/r/pwTNp65VFf
|
136
|
-
route_entry[k] = $1 if route_ending =~ /\b#{k}\s+([^\s]+)/
|
137
|
-
end
|
138
|
-
# https://rubular.com/r/k1sMrRn5yLjgVi
|
139
|
-
route_entry["via"] = $1 if route_ending =~ /\bvia\s+inet6\s+([^\s]+)/
|
140
|
-
|
141
|
-
# a sanity check, especially for Linux-VServer, OpenVZ and LXC:
|
142
|
-
# don't report the route entry if the src address isn't set on the node
|
143
|
-
# unless the interface has no addresses of this type at all
|
144
|
-
if route_entry[:src]
|
145
|
-
addr = iface[route_int][:addresses]
|
146
|
-
unless addr.nil? || addr.key?(route_entry[:src]) ||
|
147
|
-
addr.values.all? { |a| a["family"] != family[:name] }
|
148
|
-
logger.trace("Plugin Network: Skipping route entry whose src does not match the interface IP")
|
149
|
-
next
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
|
-
iface[route_int][:routes] = [] unless iface[route_int][:routes]
|
154
|
-
iface[route_int][:routes] << route_entry
|
155
|
-
end
|
156
|
-
end
|
157
|
-
iface
|
158
|
-
end
|
159
|
-
|
160
|
-
# now looking at the routes to set the default attributes
|
161
|
-
# for information, default routes can be of this form :
|
162
|
-
# - default via 10.0.2.4 dev br0
|
163
|
-
# - default dev br0 scope link
|
164
|
-
# - default dev eth0 scope link src 1.1.1.1
|
165
|
-
# - default via 10.0.3.1 dev eth1 src 10.0.3.2 metric 10
|
166
|
-
# - default via 10.0.4.1 dev eth2 src 10.0.4.2 metric 20
|
167
|
-
|
168
|
-
# using a temporary var to hold routes and their interface name
|
169
|
-
def parse_routes(family, iface)
|
170
|
-
iface.filter_map do |i, iv|
|
171
|
-
next unless iv[:routes]
|
172
|
-
|
173
|
-
iv[:routes].filter_map do |r|
|
174
|
-
r.merge(dev: i) if r[:family] == family[:name]
|
175
|
-
end
|
176
|
-
end.flatten
|
177
|
-
end
|
178
|
-
|
179
|
-
# determine layer 1 details for the interface using ethtool
|
180
|
-
def ethernet_layer_one(iface)
|
181
|
-
return iface unless ethtool_binary_path
|
182
|
-
|
183
|
-
keys = %w{Speed Duplex Port Transceiver Auto-negotiation MDI-X}
|
184
|
-
iface.each_key do |tmp_int|
|
185
|
-
next unless iface[tmp_int][:encapsulation] == "Ethernet"
|
186
|
-
|
187
|
-
so = shell_out("#{ethtool_binary_path} #{tmp_int}")
|
188
|
-
so.stdout.lines do |line|
|
189
|
-
line.chomp!
|
190
|
-
logger.trace("Plugin Network: Parsing ethtool output: #{line}")
|
191
|
-
line.lstrip!
|
192
|
-
k, v = line.split(": ")
|
193
|
-
next unless keys.include? k
|
194
|
-
|
195
|
-
k.downcase!.tr!("-", "_")
|
196
|
-
if k == "speed"
|
197
|
-
k = "link_speed" # This is not necessarily the maximum speed the NIC supports
|
198
|
-
v = v[/\d+/].to_i
|
199
|
-
end
|
200
|
-
iface[tmp_int][k] = v
|
201
|
-
end
|
202
|
-
end
|
203
|
-
iface
|
204
|
-
end
|
205
|
-
|
206
|
-
# determine ring parameters for the interface using ethtool
|
207
|
-
def ethernet_ring_parameters(iface)
|
208
|
-
return iface unless ethtool_binary_path
|
209
|
-
|
210
|
-
iface.each_key do |tmp_int|
|
211
|
-
next unless iface[tmp_int][:encapsulation] == "Ethernet"
|
212
|
-
|
213
|
-
so = shell_out("#{ethtool_binary_path} -g #{tmp_int}")
|
214
|
-
logger.trace("Plugin Network: Parsing ethtool output: #{so.stdout}")
|
215
|
-
type = nil
|
216
|
-
iface[tmp_int]["ring_params"] = {}
|
217
|
-
so.stdout.lines.each do |line|
|
218
|
-
next if line.start_with?("Ring parameters for")
|
219
|
-
next if line.strip.nil?
|
220
|
-
|
221
|
-
if line.include?("Pre-set maximums")
|
222
|
-
type = "max"
|
223
|
-
next
|
224
|
-
end
|
225
|
-
if line.include?("Current hardware settings")
|
226
|
-
type = "current"
|
227
|
-
next
|
228
|
-
end
|
229
|
-
key, val = line.split(/:\s+/)
|
230
|
-
if type && val
|
231
|
-
ring_key = "#{type}_#{key.downcase.tr(" ", "_")}"
|
232
|
-
iface[tmp_int]["ring_params"][ring_key] = val.to_i
|
233
|
-
end
|
234
|
-
end
|
235
|
-
end
|
236
|
-
iface
|
237
|
-
end
|
238
|
-
|
239
|
-
# determine channel parameters for the interface using ethtool
|
240
|
-
def ethernet_channel_parameters(iface)
|
241
|
-
return iface unless ethtool_binary_path
|
242
|
-
|
243
|
-
iface.each_key do |tmp_int|
|
244
|
-
next unless iface[tmp_int][:encapsulation] == "Ethernet"
|
245
|
-
|
246
|
-
so = shell_out("#{ethtool_binary_path} -l #{tmp_int}")
|
247
|
-
logger.trace("Plugin Network: Parsing ethtool output: #{so.stdout}")
|
248
|
-
type = nil
|
249
|
-
iface[tmp_int]["channel_params"] = {}
|
250
|
-
so.stdout.lines.each do |line|
|
251
|
-
next if line.start_with?("Channel parameters for")
|
252
|
-
next if line.strip.nil?
|
253
|
-
|
254
|
-
if line.include?("Pre-set maximums")
|
255
|
-
type = "max"
|
256
|
-
next
|
257
|
-
end
|
258
|
-
if line.include?("Current hardware settings")
|
259
|
-
type = "current"
|
260
|
-
next
|
261
|
-
end
|
262
|
-
key, val = line.split(/:\s+/)
|
263
|
-
if type && val
|
264
|
-
channel_key = "#{type}_#{key.downcase.tr(" ", "_")}"
|
265
|
-
iface[tmp_int]["channel_params"][channel_key] = val.to_i
|
266
|
-
end
|
267
|
-
end
|
268
|
-
end
|
269
|
-
iface
|
270
|
-
end
|
271
|
-
|
272
|
-
# determine coalesce parameters for the interface using ethtool
|
273
|
-
def ethernet_coalesce_parameters(iface)
|
274
|
-
return iface unless ethtool_binary_path
|
275
|
-
|
276
|
-
iface.each_key do |tmp_int|
|
277
|
-
next unless iface[tmp_int][:encapsulation] == "Ethernet"
|
278
|
-
|
279
|
-
so = shell_out("#{ethtool_binary_path} -c #{tmp_int}")
|
280
|
-
logger.trace("Plugin Network: Parsing ethtool output: #{so.stdout}")
|
281
|
-
iface[tmp_int]["coalesce_params"] = {}
|
282
|
-
so.stdout.lines.each do |line|
|
283
|
-
next if line.start_with?("Coalesce parameters for")
|
284
|
-
next if line.strip.nil?
|
285
|
-
|
286
|
-
if line.start_with?("Adaptive")
|
287
|
-
_, adaptive_rx, _, adaptive_tx = line.split(/:\s+|\s+TX|\n/)
|
288
|
-
iface[tmp_int]["coalesce_params"]["adaptive_rx"] = adaptive_rx
|
289
|
-
iface[tmp_int]["coalesce_params"]["adaptive_tx"] = adaptive_tx
|
290
|
-
next
|
291
|
-
end
|
292
|
-
key, val = line.split(/:\s+/)
|
293
|
-
if val
|
294
|
-
coalesce_key = key.downcase.tr(" ", "_").to_s
|
295
|
-
iface[tmp_int]["coalesce_params"][coalesce_key] = val.to_i
|
296
|
-
end
|
297
|
-
end
|
298
|
-
end
|
299
|
-
iface
|
300
|
-
end
|
301
|
-
|
302
|
-
# determine offload features for the interface using ethtool
|
303
|
-
def ethernet_offload_parameters(iface)
|
304
|
-
return iface unless ethtool_binary_path
|
305
|
-
|
306
|
-
iface.each_key do |tmp_int|
|
307
|
-
next unless iface[tmp_int][:encapsulation] == "Ethernet"
|
308
|
-
|
309
|
-
so = shell_out("#{ethtool_binary_path} -k #{tmp_int}")
|
310
|
-
Ohai::Log.debug("Plugin Network: Parsing ethtool output: #{so.stdout}")
|
311
|
-
iface[tmp_int]["offload_params"] = {}
|
312
|
-
so.stdout.lines.each do |line|
|
313
|
-
next if line.start_with?("Features for")
|
314
|
-
next if line.strip.nil?
|
315
|
-
|
316
|
-
key, val = line.split(/:\s+/)
|
317
|
-
if val
|
318
|
-
offload_key = key.downcase.strip.tr(" ", "_").to_s
|
319
|
-
iface[tmp_int]["offload_params"][offload_key] = val.downcase.gsub(/\[.*\]/, "").strip.to_s
|
320
|
-
end
|
321
|
-
end
|
322
|
-
end
|
323
|
-
iface
|
324
|
-
end
|
325
|
-
|
326
|
-
# determine pause parameters for the interface using ethtool
|
327
|
-
def ethernet_pause_parameters(iface)
|
328
|
-
return iface unless ethtool_binary_path
|
329
|
-
|
330
|
-
iface.each_key do |tmp_int|
|
331
|
-
next unless iface[tmp_int][:encapsulation] == "Ethernet"
|
332
|
-
|
333
|
-
so = shell_out("#{ethtool_binary_path} -a #{tmp_int}")
|
334
|
-
logger.trace("Plugin Network: Parsing ethtool output: #{so.stdout}")
|
335
|
-
iface[tmp_int]["pause_params"] = {}
|
336
|
-
so.stdout.lines.each do |line|
|
337
|
-
next if line.start_with?("Pause parameters for")
|
338
|
-
next if line.strip.nil?
|
339
|
-
|
340
|
-
key, val = line.split(/:\s+/)
|
341
|
-
if val
|
342
|
-
pause_key = "#{key.downcase.tr(" ", "_")}"
|
343
|
-
iface[tmp_int]["pause_params"][pause_key] = val.strip.eql? "on"
|
344
|
-
end
|
345
|
-
end
|
346
|
-
end
|
347
|
-
iface
|
348
|
-
end
|
349
|
-
|
350
|
-
# determine driver info for the interface using ethtool
|
351
|
-
def ethernet_driver_info(iface)
|
352
|
-
return iface unless ethtool_binary_path
|
353
|
-
|
354
|
-
iface.each_key do |tmp_int|
|
355
|
-
next unless iface[tmp_int][:encapsulation] == "Ethernet"
|
356
|
-
|
357
|
-
so = shell_out("#{ethtool_binary_path} -i #{tmp_int}")
|
358
|
-
logger.trace("Plugin Network: Parsing ethtool output: #{so.stdout}")
|
359
|
-
iface[tmp_int]["driver_info"] = {}
|
360
|
-
so.stdout.lines.each do |line|
|
361
|
-
next if line.strip.nil?
|
362
|
-
|
363
|
-
key, val = line.split(/:\s+/)
|
364
|
-
if val.nil?
|
365
|
-
val = ""
|
366
|
-
end
|
367
|
-
driver_key = key.downcase.tr(" ", "_").to_s
|
368
|
-
iface[tmp_int]["driver_info"][driver_key] = val.chomp
|
369
|
-
end
|
370
|
-
end
|
371
|
-
iface
|
372
|
-
end
|
373
|
-
|
374
|
-
# determine link stats, vlans, queue length, and state for an interface using ip
|
375
|
-
def link_statistics(iface, net_counters)
|
376
|
-
so = shell_out("ip -d -s link")
|
377
|
-
tmp_int = nil
|
378
|
-
on_rx = true
|
379
|
-
xdp_mode = nil
|
380
|
-
so.stdout.lines do |line|
|
381
|
-
if line =~ IPROUTE_INT_REGEX
|
382
|
-
tmp_int = $2
|
383
|
-
iface[tmp_int] ||= Mash.new
|
384
|
-
net_counters[tmp_int] ||= Mash.new
|
385
|
-
end
|
386
|
-
|
387
|
-
if /^\s+(ip6tnl|ipip)/.match?(line)
|
388
|
-
iface[tmp_int][:tunnel_info] = {}
|
389
|
-
words = line.split
|
390
|
-
words.each_with_index do |word, index|
|
391
|
-
case word
|
392
|
-
when "external"
|
393
|
-
iface[tmp_int][:tunnel_info][word] = true
|
394
|
-
when "any", "ipip6", "ip6ip6"
|
395
|
-
iface[tmp_int][:tunnel_info][:proto] = word
|
396
|
-
when "remote",
|
397
|
-
"local",
|
398
|
-
"encaplimit",
|
399
|
-
"hoplimit",
|
400
|
-
"tclass",
|
401
|
-
"flowlabel",
|
402
|
-
"addrgenmode",
|
403
|
-
"numtxqueues",
|
404
|
-
"numrxqueues",
|
405
|
-
"gso_max_size",
|
406
|
-
"gso_max_segs"
|
407
|
-
iface[tmp_int][:tunnel_info][word] = words[index + 1]
|
408
|
-
end
|
409
|
-
end
|
410
|
-
end
|
411
|
-
|
412
|
-
if line =~ /(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)/
|
413
|
-
int = on_rx ? :rx : :tx
|
414
|
-
net_counters[tmp_int][int] ||= Mash.new
|
415
|
-
net_counters[tmp_int][int][:bytes] = $1
|
416
|
-
net_counters[tmp_int][int][:packets] = $2
|
417
|
-
net_counters[tmp_int][int][:errors] = $3
|
418
|
-
net_counters[tmp_int][int][:drop] = $4
|
419
|
-
if int == :rx
|
420
|
-
net_counters[tmp_int][int][:overrun] = $5
|
421
|
-
else
|
422
|
-
net_counters[tmp_int][int][:carrier] = $5
|
423
|
-
net_counters[tmp_int][int][:collisions] = $6
|
424
|
-
end
|
425
|
-
|
426
|
-
on_rx = !on_rx
|
427
|
-
end
|
428
|
-
|
429
|
-
if line =~ /qlen (\d+)/
|
430
|
-
net_counters[tmp_int][:tx] ||= Mash.new
|
431
|
-
net_counters[tmp_int][:tx][:queuelen] = $1
|
432
|
-
end
|
433
|
-
|
434
|
-
if line =~ /vlan id (\d+)/ || line =~ /vlan protocol ([\w\.]+) id (\d+)/
|
435
|
-
if $2
|
436
|
-
tmp_prot = $1
|
437
|
-
tmp_id = $2
|
438
|
-
else
|
439
|
-
tmp_id = $1
|
440
|
-
end
|
441
|
-
iface[tmp_int][:vlan] ||= Mash.new
|
442
|
-
iface[tmp_int][:vlan][:id] = tmp_id
|
443
|
-
iface[tmp_int][:vlan][:protocol] = tmp_prot if tmp_prot
|
444
|
-
|
445
|
-
vlan_flags = line.scan(/(REORDER_HDR|GVRP|LOOSE_BINDING)/)
|
446
|
-
if vlan_flags.length > 0
|
447
|
-
iface[tmp_int][:vlan][:flags] = vlan_flags.flatten.uniq
|
448
|
-
end
|
449
|
-
end
|
450
|
-
|
451
|
-
# https://rubular.com/r/JRp6lNANmpcLV5
|
452
|
-
if line =~ /\sstate (\w+)/
|
453
|
-
iface[tmp_int]["state"] = $1.downcase
|
454
|
-
end
|
455
|
-
|
456
|
-
if line.include?("xdp")
|
457
|
-
mode = line.scan(/\s(xdp|xdpgeneric|xdpoffload|xdpmulti)\s/).flatten[0]
|
458
|
-
# Fetches and sets the mode from the first line.
|
459
|
-
unless mode.nil?
|
460
|
-
iface[tmp_int][:xdp] = {}
|
461
|
-
if mode.eql?("xdp")
|
462
|
-
# In case of xdpdrv, mode is xdp,
|
463
|
-
# to keep it consistent, keeping mode as xdpdrv.
|
464
|
-
mode = "xdpdrv"
|
465
|
-
end
|
466
|
-
xdp_mode = mode
|
467
|
-
iface[tmp_int][:xdp][:attached] = []
|
468
|
-
end
|
469
|
-
|
470
|
-
if line =~ %r{prog/(\w+) id (\d+) tag (\w+)}
|
471
|
-
mode = $1.eql?("xdp") ? xdp_mode : $1
|
472
|
-
iface[tmp_int][:xdp][:attached] << {
|
473
|
-
mode: mode,
|
474
|
-
id: $2,
|
475
|
-
tag: $3,
|
476
|
-
}
|
477
|
-
end
|
478
|
-
end
|
479
|
-
end
|
480
|
-
iface
|
481
|
-
end
|
482
|
-
|
483
|
-
def match_iproute(iface, line, cint)
|
484
|
-
if line =~ IPROUTE_INT_REGEX
|
485
|
-
cint = $2
|
486
|
-
iface[cint] = Mash.new
|
487
|
-
if cint =~ /^(\w+?)(\d+.*)/
|
488
|
-
iface[cint][:type] = $1
|
489
|
-
iface[cint][:number] = $2
|
490
|
-
end
|
491
|
-
|
492
|
-
if line =~ /mtu (\d+)/
|
493
|
-
iface[cint][:mtu] = $1
|
494
|
-
end
|
495
|
-
|
496
|
-
flags = line.scan(/(UP|BROADCAST|DEBUG|LOOPBACK|POINTTOPOINT|NOTRAILERS|LOWER_UP|NOARP|PROMISC|ALLMULTI|SLAVE|MASTER|MULTICAST|DYNAMIC)/)
|
497
|
-
if flags.length > 1
|
498
|
-
iface[cint][:flags] = flags.flatten.uniq
|
499
|
-
end
|
500
|
-
end
|
501
|
-
cint
|
502
|
-
end
|
503
|
-
|
504
|
-
def parse_ip_addr(iface)
|
505
|
-
so = shell_out("ip addr")
|
506
|
-
cint = nil
|
507
|
-
so.stdout.lines do |line|
|
508
|
-
cint = match_iproute(iface, line, cint)
|
509
|
-
|
510
|
-
parse_ip_addr_link_line(cint, iface, line)
|
511
|
-
cint = parse_ip_addr_inet_line(cint, iface, line)
|
512
|
-
parse_ip_addr_inet6_line(cint, iface, line)
|
513
|
-
end
|
514
|
-
end
|
515
|
-
|
516
|
-
def parse_ip_addr_link_line(cint, iface, line)
|
517
|
-
if line =~ %r{link/(\w+) ([\da-f\:]+) }
|
518
|
-
iface[cint][:encapsulation] = linux_encaps_lookup($1)
|
519
|
-
unless $2 == "00:00:00:00:00:00"
|
520
|
-
iface[cint][:addresses] ||= Mash.new
|
521
|
-
iface[cint][:addresses][$2.upcase] = { "family" => "lladdr" }
|
522
|
-
end
|
523
|
-
end
|
524
|
-
end
|
525
|
-
|
526
|
-
def parse_ip_addr_inet_line(cint, iface, line)
|
527
|
-
if line =~ %r{inet (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})(/(\d{1,2}))?}
|
528
|
-
tmp_addr, tmp_prefix = $1, $3
|
529
|
-
tmp_prefix ||= "32"
|
530
|
-
original_int = nil
|
531
|
-
|
532
|
-
# Are we a formerly aliased interface?
|
533
|
-
if line =~ /#{cint}:(\d+)$/
|
534
|
-
sub_int = $1
|
535
|
-
alias_int = "#{cint}:#{sub_int}"
|
536
|
-
original_int = cint
|
537
|
-
cint = alias_int
|
538
|
-
end
|
539
|
-
|
540
|
-
iface[cint] ||= Mash.new # Create the fake alias interface if needed
|
541
|
-
iface[cint][:addresses] ||= Mash.new
|
542
|
-
iface[cint][:addresses][tmp_addr] = { "family" => "inet", "prefixlen" => tmp_prefix }
|
543
|
-
iface[cint][:addresses][tmp_addr][:netmask] = IPAddr.new("255.255.255.255").mask(tmp_prefix.to_i).to_s
|
544
|
-
|
545
|
-
if line =~ /peer (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/
|
546
|
-
iface[cint][:addresses][tmp_addr][:peer] = $1
|
547
|
-
end
|
548
|
-
|
549
|
-
if line =~ /brd (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/
|
550
|
-
iface[cint][:addresses][tmp_addr][:broadcast] = $1
|
551
|
-
end
|
552
|
-
|
553
|
-
if line =~ /scope (\w+)/
|
554
|
-
iface[cint][:addresses][tmp_addr][:scope] = ($1.eql?("host") ? "Node" : $1.capitalize)
|
555
|
-
end
|
556
|
-
|
557
|
-
# If we found we were an alias interface, restore cint to its original value
|
558
|
-
cint = original_int unless original_int.nil?
|
559
|
-
end
|
560
|
-
cint
|
561
|
-
end
|
562
|
-
|
563
|
-
def parse_ip_addr_inet6_line(cint, iface, line)
|
564
|
-
if line =~ %r{inet6 ([a-f0-9\:]+)/(\d+) scope (\w+)( .*)?}
|
565
|
-
iface[cint][:addresses] ||= Mash.new
|
566
|
-
tmp_addr = $1
|
567
|
-
tags = $4 || ""
|
568
|
-
tags = tags.split
|
569
|
-
|
570
|
-
iface[cint][:addresses][tmp_addr] = {
|
571
|
-
"family" => "inet6",
|
572
|
-
"prefixlen" => $2,
|
573
|
-
"scope" => ($3.eql?("host") ? "Node" : $3.capitalize),
|
574
|
-
"tags" => tags,
|
575
|
-
}
|
576
|
-
end
|
577
|
-
end
|
578
|
-
|
579
|
-
# returns the macaddress for interface from a hash of interfaces (iface elsewhere in this file)
|
580
|
-
def get_mac_for_interface(interfaces, interface)
|
581
|
-
interfaces[interface][:addresses].find { |k, v| v["family"] == "lladdr" }.first unless interfaces[interface][:addresses].nil? || interfaces[interface][:flags].include?("NOARP")
|
582
|
-
end
|
583
|
-
|
584
|
-
# returns the default route with the lowest metric (unspecified metric is 0)
|
585
|
-
def choose_default_route(routes)
|
586
|
-
routes.select do |r|
|
587
|
-
r[:destination] == "default"
|
588
|
-
end.min do |x, y|
|
589
|
-
(x[:metric].nil? ? 0 : x[:metric].to_i) <=> (y[:metric].nil? ? 0 : y[:metric].to_i)
|
590
|
-
end
|
591
|
-
end
|
592
|
-
|
593
|
-
def interface_has_no_addresses_in_family?(iface, family)
|
594
|
-
return true if iface[:addresses].nil?
|
595
|
-
|
596
|
-
iface[:addresses].values.all? { |addr| addr["family"] != family }
|
597
|
-
end
|
598
|
-
|
599
|
-
def interface_have_address?(iface, address)
|
600
|
-
return false if iface[:addresses].nil?
|
601
|
-
|
602
|
-
iface[:addresses].key?(address)
|
603
|
-
end
|
604
|
-
|
605
|
-
def interface_address_not_link_level?(iface, address)
|
606
|
-
!(iface[:addresses][address][:scope].casecmp("link") == 0)
|
607
|
-
end
|
608
|
-
|
609
|
-
def interface_valid_for_route?(iface, address, family)
|
610
|
-
return true if interface_has_no_addresses_in_family?(iface, family)
|
611
|
-
|
612
|
-
interface_have_address?(iface, address) && interface_address_not_link_level?(iface, address)
|
613
|
-
end
|
614
|
-
|
615
|
-
def route_is_valid_default_route?(route, default_route)
|
616
|
-
# if the route destination is a default route, it's good
|
617
|
-
return true if route[:destination] == "default"
|
618
|
-
|
619
|
-
return false if default_route[:via].nil?
|
620
|
-
|
621
|
-
dest_ipaddr = IPAddr.new(route[:destination])
|
622
|
-
default_route_via = IPAddr.new(default_route[:via])
|
623
|
-
|
624
|
-
# check if nexthop is the same address family
|
625
|
-
return false if dest_ipaddr.ipv4? != default_route_via.ipv4?
|
626
|
-
|
627
|
-
# the default route has a gateway and the route matches the gateway
|
628
|
-
dest_ipaddr.include?(default_route_via)
|
629
|
-
end
|
630
|
-
|
631
|
-
# ipv4/ipv6 routes are different enough that having a single algorithm to select the favored route for both creates unnecessary complexity
|
632
|
-
# this method attempts to deduce the route that is most important to the user, which is later used to deduce the favored values for {ip,mac,ip6}address
|
633
|
-
# we only consider routes that are default routes, or those routes that get us to the gateway for a default route
|
634
|
-
def favored_default_route_linux(routes, iface, default_route, family)
|
635
|
-
routes.select do |r|
|
636
|
-
if family[:name] == "inet"
|
637
|
-
# the route must have a source address
|
638
|
-
next if r[:src].nil? || r[:src].empty?
|
639
|
-
|
640
|
-
# the interface specified in the route must exist
|
641
|
-
route_interface = iface[r[:dev]]
|
642
|
-
next if route_interface.nil? # the interface specified in the route must exist
|
643
|
-
|
644
|
-
# the interface must have no addresses, or if it has the source address, the address must not
|
645
|
-
# be a link-level address
|
646
|
-
next unless interface_valid_for_route?(route_interface, r[:src], "inet")
|
647
|
-
|
648
|
-
# the route must either be a default route, or it must have a gateway which is accessible via the route
|
649
|
-
next unless route_is_valid_default_route?(r, default_route)
|
650
|
-
|
651
|
-
true
|
652
|
-
elsif family[:name] == "inet6"
|
653
|
-
iface[r[:dev]] &&
|
654
|
-
iface[r[:dev]][:state] == "up" &&
|
655
|
-
route_is_valid_default_route?(r, default_route)
|
656
|
-
end
|
657
|
-
end.min_by do |r|
|
658
|
-
# sorting the selected routes:
|
659
|
-
# - getting default routes first
|
660
|
-
# - then sort by metric
|
661
|
-
# - then sort by if the device was inferred or not (preferring explicit to inferred)
|
662
|
-
# - then by prefixlen
|
663
|
-
[
|
664
|
-
r[:destination] == "default" ? 0 : 1,
|
665
|
-
r[:metric].nil? ? 0 : r[:metric].to_i,
|
666
|
-
r[:inferred] ? 1 : 0,
|
667
|
-
# for some reason IPAddress doesn't accept "::/0", it doesn't like prefix==0
|
668
|
-
# just a quick workaround: use 0 if IPAddress fails
|
669
|
-
begin
|
670
|
-
IPAddress( r[:destination] == "default" ? family[:default_route] : r[:destination] ).prefix
|
671
|
-
rescue
|
672
|
-
0
|
673
|
-
end,
|
674
|
-
]
|
675
|
-
end
|
676
|
-
end
|
677
|
-
|
678
|
-
# Both the network plugin and this plugin (linux/network) are run on linux. This plugin runs first.
|
679
|
-
# If the 'ip' binary is available, this plugin may set {ip,mac,ip6}address. The network plugin should not overwrite these.
|
680
|
-
# The older code section below that relies on the deprecated net-tools, e.g. netstat and ifconfig, provides less functionality.
|
681
|
-
collect_data(:linux) do
|
682
|
-
require "ipaddr" unless defined?(IPAddr)
|
683
|
-
|
684
|
-
iface = Mash.new
|
685
|
-
net_counters = Mash.new
|
686
|
-
|
687
|
-
network Mash.new unless network
|
688
|
-
network[:interfaces] ||= Mash.new
|
689
|
-
counters Mash.new unless counters
|
690
|
-
counters[:network] ||= Mash.new
|
691
|
-
|
692
|
-
# ohai.plugin[:network][:default_route_table] = 'default'
|
693
|
-
if configuration(:default_route_table).nil? || configuration(:default_route_table).empty?
|
694
|
-
default_route_table = "main"
|
695
|
-
else
|
696
|
-
default_route_table = configuration(:default_route_table)
|
697
|
-
end
|
698
|
-
logger.trace("Plugin Network: default route table is '#{default_route_table}'")
|
699
|
-
|
700
|
-
# Match the lead line for an interface from iproute2
|
701
|
-
# 3: eth0.11@eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP
|
702
|
-
# The '@eth0:' portion doesn't exist on primary interfaces and thus is optional in the regex
|
703
|
-
IPROUTE_INT_REGEX ||= /^(\d+): ([0-9a-zA-Z@:\.\-_]*?)(@[0-9a-zA-Z\-_]+|):\s/.freeze
|
704
|
-
|
705
|
-
if which("ip")
|
706
|
-
# families to get default routes from
|
707
|
-
families = [{
|
708
|
-
name: "inet",
|
709
|
-
default_route: "0.0.0.0/0",
|
710
|
-
default_prefix: :default,
|
711
|
-
neighbour_attribute: :arp,
|
712
|
-
}]
|
713
|
-
|
714
|
-
if ipv6_enabled?
|
715
|
-
families << {
|
716
|
-
name: "inet6",
|
717
|
-
default_route: "::/0",
|
718
|
-
default_prefix: :default_inet6,
|
719
|
-
neighbour_attribute: :neighbour_inet6,
|
720
|
-
}
|
721
|
-
end
|
722
|
-
|
723
|
-
parse_ip_addr(iface)
|
724
|
-
|
725
|
-
iface = link_statistics(iface, net_counters)
|
726
|
-
|
727
|
-
families.each do |family|
|
728
|
-
neigh_attr = family[:neighbour_attribute]
|
729
|
-
default_prefix = family[:default_prefix]
|
730
|
-
|
731
|
-
iface = extract_neighbors(family, iface, neigh_attr)
|
732
|
-
|
733
|
-
iface = check_routing_table(family, iface, default_route_table)
|
734
|
-
|
735
|
-
routes = parse_routes(family, iface)
|
736
|
-
|
737
|
-
default_route = choose_default_route(routes)
|
738
|
-
|
739
|
-
if default_route.nil? || default_route.empty?
|
740
|
-
attribute_name = if family[:name] == "inet"
|
741
|
-
"default_interface"
|
742
|
-
else
|
743
|
-
"default_#{family[:name]}_interface"
|
744
|
-
end
|
745
|
-
logger.trace("Plugin Network: Unable to determine '#{attribute_name}' as no default routes were found for that interface family")
|
746
|
-
else
|
747
|
-
network["#{default_prefix}_interface"] = default_route[:dev]
|
748
|
-
logger.trace("Plugin Network: #{default_prefix}_interface set to #{default_route[:dev]}")
|
749
|
-
|
750
|
-
# setting gateway to 0.0.0.0 or :: if the default route is a link level one
|
751
|
-
network["#{default_prefix}_gateway"] = default_route[:via] || family[:default_route].chomp("/0")
|
752
|
-
logger.trace("Plugin Network: #{default_prefix}_gateway set to #{network["#{default_prefix}_gateway"]}")
|
753
|
-
|
754
|
-
# deduce the default route the user most likely cares about to pick {ip,mac,ip6}address below
|
755
|
-
favored_route = favored_default_route_linux(routes, iface, default_route, family)
|
756
|
-
|
757
|
-
# FIXME: This entire block should go away, and the network plugin should be the sole source of {ip,ip6,mac}address
|
758
|
-
|
759
|
-
# since we're at it, let's populate {ip,mac,ip6}address with the best values
|
760
|
-
# if we don't set these, the network plugin may set them afterwards
|
761
|
-
if favored_route && !favored_route.empty?
|
762
|
-
if family[:name] == "inet"
|
763
|
-
ipaddress favored_route[:src]
|
764
|
-
m = get_mac_for_interface(iface, favored_route[:dev])
|
765
|
-
logger.trace("Plugin Network: Overwriting macaddress #{macaddress} with #{m} from interface #{favored_route[:dev]}") if macaddress
|
766
|
-
macaddress m
|
767
|
-
elsif family[:name] == "inet6"
|
768
|
-
# this rarely does anything since we rarely have src for ipv6, so this usually falls back on the network plugin
|
769
|
-
ip6address favored_route[:src]
|
770
|
-
if macaddress
|
771
|
-
logger.trace("Plugin Network: Not setting macaddress from ipv6 interface #{favored_route[:dev]} because macaddress is already set")
|
772
|
-
else
|
773
|
-
macaddress get_mac_for_interface(iface, favored_route[:dev])
|
774
|
-
end
|
775
|
-
end
|
776
|
-
else
|
777
|
-
logger.trace("Plugin Network: Unable to deduce the favored default route for family '#{family[:name]}' despite finding a default route, and is not setting ipaddress/ip6address/macaddress. the network plugin may provide fallbacks.")
|
778
|
-
logger.trace("Plugin Network: This potential default route was excluded: #{default_route}")
|
779
|
-
end
|
780
|
-
end
|
781
|
-
end # end families.each
|
782
|
-
else # ip binary not available, falling back to net-tools, e.g. route, ifconfig
|
783
|
-
begin
|
784
|
-
so = shell_out("route -n")
|
785
|
-
route_result = so.stdout.split($/).grep( /^0.0.0.0/ )[0].split( /[ \t]+/ )
|
786
|
-
network[:default_gateway], network[:default_interface] = route_result.values_at(1, 7)
|
787
|
-
rescue Ohai::Exceptions::Exec
|
788
|
-
logger.trace("Plugin Network: Unable to determine default interface")
|
789
|
-
end
|
790
|
-
|
791
|
-
so = shell_out("ifconfig -a")
|
792
|
-
cint = nil
|
793
|
-
so.stdout.lines do |line|
|
794
|
-
tmp_addr = nil
|
795
|
-
# dev_valid_name in the kernel only excludes slashes, nulls, spaces
|
796
|
-
# http://git.kernel.org/?p=linux/kernel/git/stable/linux-stable.git;a=blob;f=net/core/dev.c#l851
|
797
|
-
if line =~ /^([0-9a-zA-Z@\.\:\-_]+)\s+/
|
798
|
-
cint = $1
|
799
|
-
iface[cint] = Mash.new
|
800
|
-
if cint =~ /^(\w+?)(\d+.*)/
|
801
|
-
iface[cint][:type] = $1
|
802
|
-
iface[cint][:number] = $2
|
803
|
-
end
|
804
|
-
end
|
805
|
-
if line =~ /Link encap:(Local Loopback)/ || line =~ /Link encap:(.+?)\s/
|
806
|
-
iface[cint][:encapsulation] = linux_encaps_lookup($1)
|
807
|
-
end
|
808
|
-
if line =~ /HWaddr (.+?)\s/
|
809
|
-
iface[cint][:addresses] ||= Mash.new
|
810
|
-
iface[cint][:addresses][$1] = { "family" => "lladdr" }
|
811
|
-
end
|
812
|
-
if line =~ /inet addr:(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/
|
813
|
-
iface[cint][:addresses] ||= Mash.new
|
814
|
-
iface[cint][:addresses][$1] = { "family" => "inet" }
|
815
|
-
tmp_addr = $1
|
816
|
-
end
|
817
|
-
if line =~ %r{inet6 addr: ([a-f0-9\:]+)/(\d+) Scope:(\w+)}
|
818
|
-
iface[cint][:addresses] ||= Mash.new
|
819
|
-
iface[cint][:addresses][$1] = { "family" => "inet6", "prefixlen" => $2, "scope" => ($3.eql?("Host") ? "Node" : $3) }
|
820
|
-
end
|
821
|
-
if line =~ /Bcast:(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/
|
822
|
-
iface[cint][:addresses][tmp_addr]["broadcast"] = $1
|
823
|
-
end
|
824
|
-
if line =~ /Mask:(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/
|
825
|
-
iface[cint][:addresses][tmp_addr]["netmask"] = $1
|
826
|
-
end
|
827
|
-
flags = line.scan(/(UP|BROADCAST|DEBUG|LOOPBACK|POINTTOPOINT|NOTRAILERS|RUNNING|NOARP|PROMISC|ALLMULTI|SLAVE|MASTER|MULTICAST|DYNAMIC)\s/)
|
828
|
-
if flags.length > 1
|
829
|
-
iface[cint][:flags] = flags.flatten
|
830
|
-
end
|
831
|
-
if line =~ /MTU:(\d+)/
|
832
|
-
iface[cint][:mtu] = $1
|
833
|
-
end
|
834
|
-
if line =~ /P-t-P:(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/
|
835
|
-
iface[cint][:peer] = $1
|
836
|
-
end
|
837
|
-
if line =~ /RX packets:(\d+) errors:(\d+) dropped:(\d+) overruns:(\d+) frame:(\d+)/
|
838
|
-
net_counters[cint] ||= Mash.new
|
839
|
-
net_counters[cint][:rx] = { "packets" => $1, "errors" => $2, "drop" => $3, "overrun" => $4, "frame" => $5 }
|
840
|
-
end
|
841
|
-
if line =~ /TX packets:(\d+) errors:(\d+) dropped:(\d+) overruns:(\d+) carrier:(\d+)/
|
842
|
-
net_counters[cint][:tx] = { "packets" => $1, "errors" => $2, "drop" => $3, "overrun" => $4, "carrier" => $5 }
|
843
|
-
end
|
844
|
-
if line =~ /collisions:(\d+)/
|
845
|
-
net_counters[cint][:tx]["collisions"] = $1
|
846
|
-
end
|
847
|
-
if line =~ /txqueuelen:(\d+)/
|
848
|
-
net_counters[cint][:tx]["queuelen"] = $1
|
849
|
-
end
|
850
|
-
if line =~ /RX bytes:(\d+) \((\d+?\.\d+ .+?)\)/
|
851
|
-
net_counters[cint][:rx]["bytes"] = $1
|
852
|
-
end
|
853
|
-
if line =~ /TX bytes:(\d+) \((\d+?\.\d+ .+?)\)/
|
854
|
-
net_counters[cint][:tx]["bytes"] = $1
|
855
|
-
end
|
856
|
-
end
|
857
|
-
|
858
|
-
so = shell_out("arp -an")
|
859
|
-
so.stdout.lines do |line|
|
860
|
-
if line =~ /^\S+ \((\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\) at ([a-fA-F0-9\:]+) \[(\w+)\] on ([0-9a-zA-Z\.\:\-]+)/
|
861
|
-
next unless iface[$4] # this should never happen
|
862
|
-
|
863
|
-
iface[$4][:arp] ||= Mash.new
|
864
|
-
iface[$4][:arp][$1] = $2.downcase
|
865
|
-
end
|
866
|
-
end
|
867
|
-
end # end "ip else net-tools" block
|
868
|
-
|
869
|
-
iface = ethernet_layer_one(iface)
|
870
|
-
iface = ethernet_ring_parameters(iface)
|
871
|
-
iface = ethernet_channel_parameters(iface)
|
872
|
-
iface = ethernet_coalesce_parameters(iface)
|
873
|
-
iface = ethernet_offload_parameters(iface)
|
874
|
-
iface = ethernet_driver_info(iface)
|
875
|
-
iface = ethernet_pause_parameters(iface)
|
876
|
-
counters[:network][:interfaces] = net_counters
|
877
|
-
network["interfaces"] = iface
|
878
|
-
end
|
879
|
-
end
|
1
|
+
# frozen_string_literal: true
|
2
|
+
#
|
3
|
+
# Author:: Adam Jacob (<adam@chef.io>)
|
4
|
+
# Author:: Chris Read <chris.read@gmail.com>
|
5
|
+
# Copyright:: Copyright (c) Chef Software Inc.
|
6
|
+
# License:: Apache License, Version 2.0
|
7
|
+
#
|
8
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
9
|
+
# you may not use this file except in compliance with the License.
|
10
|
+
# You may obtain a copy of the License at
|
11
|
+
#
|
12
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
13
|
+
#
|
14
|
+
# Unless required by applicable law or agreed to in writing, software
|
15
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
16
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
17
|
+
# See the License for the specific language governing permissions and
|
18
|
+
# limitations under the License.
|
19
|
+
#
|
20
|
+
|
21
|
+
Ohai.plugin(:Network) do
|
22
|
+
provides "network", "network/interfaces"
|
23
|
+
provides "counters/network", "counters/network/interfaces"
|
24
|
+
provides "ipaddress", "ip6address", "macaddress"
|
25
|
+
|
26
|
+
def linux_encaps_lookup(encap)
|
27
|
+
return "Loopback" if encap.eql?("Local Loopback") || encap.eql?("loopback")
|
28
|
+
return "PPP" if encap.eql?("Point-to-Point Protocol")
|
29
|
+
return "SLIP" if encap.eql?("Serial Line IP")
|
30
|
+
return "VJSLIP" if encap.eql?("VJ Serial Line IP")
|
31
|
+
return "IPIP" if encap.eql?("IPIP Tunnel")
|
32
|
+
return "6to4" if encap.eql?("IPv6-in-IPv4")
|
33
|
+
return "Ethernet" if encap.eql?("ether")
|
34
|
+
|
35
|
+
encap
|
36
|
+
end
|
37
|
+
|
38
|
+
def ipv6_enabled?
|
39
|
+
file_exist? "/proc/net/if_inet6"
|
40
|
+
end
|
41
|
+
|
42
|
+
def ethtool_binary_path
|
43
|
+
@ethtool ||= which("ethtool")
|
44
|
+
end
|
45
|
+
|
46
|
+
def is_openvz?
|
47
|
+
@openvz ||= file_directory?("/proc/vz")
|
48
|
+
end
|
49
|
+
|
50
|
+
def is_openvz_host?
|
51
|
+
is_openvz? && file_directory?("/proc/bc")
|
52
|
+
end
|
53
|
+
|
54
|
+
def extract_neighbors(family, iface, neigh_attr)
|
55
|
+
so = shell_out("ip -f #{family[:name]} neigh show")
|
56
|
+
so.stdout.lines do |line|
|
57
|
+
if line =~ /^([a-f0-9\:\.]+)\s+dev\s+([^\s]+)\s+lladdr\s+([a-fA-F0-9\:]+)/
|
58
|
+
interface = iface[$2]
|
59
|
+
unless interface
|
60
|
+
logger.warn("neighbor list has entries for unknown interface #{interface}")
|
61
|
+
next
|
62
|
+
end
|
63
|
+
interface[neigh_attr] ||= Mash.new
|
64
|
+
interface[neigh_attr][$1] = $3.downcase
|
65
|
+
end
|
66
|
+
end
|
67
|
+
iface
|
68
|
+
end
|
69
|
+
|
70
|
+
# checking the routing tables
|
71
|
+
# why ?
|
72
|
+
# 1) to set the default gateway and default interfaces attributes
|
73
|
+
# 2) on some occasions, the best way to select node[:ipaddress] is to look at
|
74
|
+
# the routing table source field.
|
75
|
+
# 3) and since we're at it, let's populate some :routes attributes
|
76
|
+
# (going to do that for both inet and inet6 addresses)
|
77
|
+
def check_routing_table(family, iface, default_route_table)
|
78
|
+
so = shell_out("ip -o -f #{family[:name]} route show table #{default_route_table}")
|
79
|
+
so.stdout.lines do |line|
|
80
|
+
line.strip!
|
81
|
+
logger.trace("Plugin Network: Parsing #{line}")
|
82
|
+
if line.include?("\\")
|
83
|
+
# If we have multipath routing, then the first part will be a normal
|
84
|
+
# looking route:
|
85
|
+
# default proto ra metric 1024 <other options>
|
86
|
+
# Each successive part after that is a hop without those options.
|
87
|
+
# So the first thing we do is grab that first part, and split it into
|
88
|
+
# the route destination ("default"), and the route options.
|
89
|
+
parts = line.split("\\")
|
90
|
+
route_dest, dest_opts = parts.first.split(nil, 2)
|
91
|
+
# Then all the route endings, generally just nexthops.
|
92
|
+
route_endings = parts[1..]
|
93
|
+
if dest_opts && !dest_opts.empty?
|
94
|
+
# Route options like proto, metric, etc. only appear once for each
|
95
|
+
# multipath configuration. Prepend this information to the route
|
96
|
+
# endings so the code below will assign the fields properly.
|
97
|
+
route_endings.map! { |e| e.include?("nexthop") ? "#{dest_opts} #{e}" : e }
|
98
|
+
end
|
99
|
+
elsif line =~ /^([^\s]+)\s(.*)$/
|
100
|
+
route_dest = $1
|
101
|
+
route_endings = [$2]
|
102
|
+
else
|
103
|
+
next
|
104
|
+
end
|
105
|
+
route_endings.each do |route_ending|
|
106
|
+
route_entry = Mash.new(destination: route_dest,
|
107
|
+
family: family[:name])
|
108
|
+
route_int = nil
|
109
|
+
if route_ending =~ /\bdev\s+([^\s]+)\b/
|
110
|
+
route_int = $1
|
111
|
+
end
|
112
|
+
# does any known interface own the src address?
|
113
|
+
# we try to infer the interface/device from its address if it isn't specified
|
114
|
+
# we want to override the interface set via nexthop but only if possible
|
115
|
+
if line =~ /\bsrc\s+([^\s]+)\b/ && (!route_int || line.include?("nexthop"))
|
116
|
+
# only clobber previously set route_int if we find a match
|
117
|
+
if (match = iface.select { |name, intf| intf.fetch("addresses", {}).any? { |addr, _| addr == $1 } }.keys.first)
|
118
|
+
route_int = match
|
119
|
+
route_entry[:inferred] = true
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
unless route_int
|
124
|
+
logger.trace("Plugin Network: Skipping route entry without a device: '#{line}'")
|
125
|
+
next
|
126
|
+
end
|
127
|
+
route_int = "venet0:0" if is_openvz? && !is_openvz_host? && route_int == "venet0" && iface["venet0:0"]
|
128
|
+
|
129
|
+
unless iface[route_int]
|
130
|
+
logger.trace("Plugin Network: Skipping previously unseen interface from 'ip route show': #{route_int}")
|
131
|
+
next
|
132
|
+
end
|
133
|
+
|
134
|
+
%w{via scope metric proto src}.each do |k|
|
135
|
+
# http://rubular.com/r/pwTNp65VFf
|
136
|
+
route_entry[k] = $1 if route_ending =~ /\b#{k}\s+([^\s]+)/
|
137
|
+
end
|
138
|
+
# https://rubular.com/r/k1sMrRn5yLjgVi
|
139
|
+
route_entry["via"] = $1 if route_ending =~ /\bvia\s+inet6\s+([^\s]+)/
|
140
|
+
|
141
|
+
# a sanity check, especially for Linux-VServer, OpenVZ and LXC:
|
142
|
+
# don't report the route entry if the src address isn't set on the node
|
143
|
+
# unless the interface has no addresses of this type at all
|
144
|
+
if route_entry[:src]
|
145
|
+
addr = iface[route_int][:addresses]
|
146
|
+
unless addr.nil? || addr.key?(route_entry[:src]) ||
|
147
|
+
addr.values.all? { |a| a["family"] != family[:name] }
|
148
|
+
logger.trace("Plugin Network: Skipping route entry whose src does not match the interface IP")
|
149
|
+
next
|
150
|
+
end
|
151
|
+
end
|
152
|
+
|
153
|
+
iface[route_int][:routes] = [] unless iface[route_int][:routes]
|
154
|
+
iface[route_int][:routes] << route_entry
|
155
|
+
end
|
156
|
+
end
|
157
|
+
iface
|
158
|
+
end
|
159
|
+
|
160
|
+
# now looking at the routes to set the default attributes
|
161
|
+
# for information, default routes can be of this form :
|
162
|
+
# - default via 10.0.2.4 dev br0
|
163
|
+
# - default dev br0 scope link
|
164
|
+
# - default dev eth0 scope link src 1.1.1.1
|
165
|
+
# - default via 10.0.3.1 dev eth1 src 10.0.3.2 metric 10
|
166
|
+
# - default via 10.0.4.1 dev eth2 src 10.0.4.2 metric 20
|
167
|
+
|
168
|
+
# using a temporary var to hold routes and their interface name
|
169
|
+
def parse_routes(family, iface)
|
170
|
+
iface.filter_map do |i, iv|
|
171
|
+
next unless iv[:routes]
|
172
|
+
|
173
|
+
iv[:routes].filter_map do |r|
|
174
|
+
r.merge(dev: i) if r[:family] == family[:name]
|
175
|
+
end
|
176
|
+
end.flatten
|
177
|
+
end
|
178
|
+
|
179
|
+
# determine layer 1 details for the interface using ethtool
|
180
|
+
def ethernet_layer_one(iface)
|
181
|
+
return iface unless ethtool_binary_path
|
182
|
+
|
183
|
+
keys = %w{Speed Duplex Port Transceiver Auto-negotiation MDI-X}
|
184
|
+
iface.each_key do |tmp_int|
|
185
|
+
next unless iface[tmp_int][:encapsulation] == "Ethernet"
|
186
|
+
|
187
|
+
so = shell_out("#{ethtool_binary_path} #{tmp_int}")
|
188
|
+
so.stdout.lines do |line|
|
189
|
+
line.chomp!
|
190
|
+
logger.trace("Plugin Network: Parsing ethtool output: #{line}")
|
191
|
+
line.lstrip!
|
192
|
+
k, v = line.split(": ")
|
193
|
+
next unless keys.include? k
|
194
|
+
|
195
|
+
k.downcase!.tr!("-", "_")
|
196
|
+
if k == "speed"
|
197
|
+
k = "link_speed" # This is not necessarily the maximum speed the NIC supports
|
198
|
+
v = v[/\d+/].to_i
|
199
|
+
end
|
200
|
+
iface[tmp_int][k] = v
|
201
|
+
end
|
202
|
+
end
|
203
|
+
iface
|
204
|
+
end
|
205
|
+
|
206
|
+
# determine ring parameters for the interface using ethtool
|
207
|
+
def ethernet_ring_parameters(iface)
|
208
|
+
return iface unless ethtool_binary_path
|
209
|
+
|
210
|
+
iface.each_key do |tmp_int|
|
211
|
+
next unless iface[tmp_int][:encapsulation] == "Ethernet"
|
212
|
+
|
213
|
+
so = shell_out("#{ethtool_binary_path} -g #{tmp_int}")
|
214
|
+
logger.trace("Plugin Network: Parsing ethtool output: #{so.stdout}")
|
215
|
+
type = nil
|
216
|
+
iface[tmp_int]["ring_params"] = {}
|
217
|
+
so.stdout.lines.each do |line|
|
218
|
+
next if line.start_with?("Ring parameters for")
|
219
|
+
next if line.strip.nil?
|
220
|
+
|
221
|
+
if line.include?("Pre-set maximums")
|
222
|
+
type = "max"
|
223
|
+
next
|
224
|
+
end
|
225
|
+
if line.include?("Current hardware settings")
|
226
|
+
type = "current"
|
227
|
+
next
|
228
|
+
end
|
229
|
+
key, val = line.split(/:\s+/)
|
230
|
+
if type && val
|
231
|
+
ring_key = "#{type}_#{key.downcase.tr(" ", "_")}"
|
232
|
+
iface[tmp_int]["ring_params"][ring_key] = val.to_i
|
233
|
+
end
|
234
|
+
end
|
235
|
+
end
|
236
|
+
iface
|
237
|
+
end
|
238
|
+
|
239
|
+
# determine channel parameters for the interface using ethtool
|
240
|
+
def ethernet_channel_parameters(iface)
|
241
|
+
return iface unless ethtool_binary_path
|
242
|
+
|
243
|
+
iface.each_key do |tmp_int|
|
244
|
+
next unless iface[tmp_int][:encapsulation] == "Ethernet"
|
245
|
+
|
246
|
+
so = shell_out("#{ethtool_binary_path} -l #{tmp_int}")
|
247
|
+
logger.trace("Plugin Network: Parsing ethtool output: #{so.stdout}")
|
248
|
+
type = nil
|
249
|
+
iface[tmp_int]["channel_params"] = {}
|
250
|
+
so.stdout.lines.each do |line|
|
251
|
+
next if line.start_with?("Channel parameters for")
|
252
|
+
next if line.strip.nil?
|
253
|
+
|
254
|
+
if line.include?("Pre-set maximums")
|
255
|
+
type = "max"
|
256
|
+
next
|
257
|
+
end
|
258
|
+
if line.include?("Current hardware settings")
|
259
|
+
type = "current"
|
260
|
+
next
|
261
|
+
end
|
262
|
+
key, val = line.split(/:\s+/)
|
263
|
+
if type && val
|
264
|
+
channel_key = "#{type}_#{key.downcase.tr(" ", "_")}"
|
265
|
+
iface[tmp_int]["channel_params"][channel_key] = val.to_i
|
266
|
+
end
|
267
|
+
end
|
268
|
+
end
|
269
|
+
iface
|
270
|
+
end
|
271
|
+
|
272
|
+
# determine coalesce parameters for the interface using ethtool
|
273
|
+
def ethernet_coalesce_parameters(iface)
|
274
|
+
return iface unless ethtool_binary_path
|
275
|
+
|
276
|
+
iface.each_key do |tmp_int|
|
277
|
+
next unless iface[tmp_int][:encapsulation] == "Ethernet"
|
278
|
+
|
279
|
+
so = shell_out("#{ethtool_binary_path} -c #{tmp_int}")
|
280
|
+
logger.trace("Plugin Network: Parsing ethtool output: #{so.stdout}")
|
281
|
+
iface[tmp_int]["coalesce_params"] = {}
|
282
|
+
so.stdout.lines.each do |line|
|
283
|
+
next if line.start_with?("Coalesce parameters for")
|
284
|
+
next if line.strip.nil?
|
285
|
+
|
286
|
+
if line.start_with?("Adaptive")
|
287
|
+
_, adaptive_rx, _, adaptive_tx = line.split(/:\s+|\s+TX|\n/)
|
288
|
+
iface[tmp_int]["coalesce_params"]["adaptive_rx"] = adaptive_rx
|
289
|
+
iface[tmp_int]["coalesce_params"]["adaptive_tx"] = adaptive_tx
|
290
|
+
next
|
291
|
+
end
|
292
|
+
key, val = line.split(/:\s+/)
|
293
|
+
if val
|
294
|
+
coalesce_key = key.downcase.tr(" ", "_").to_s
|
295
|
+
iface[tmp_int]["coalesce_params"][coalesce_key] = val.to_i
|
296
|
+
end
|
297
|
+
end
|
298
|
+
end
|
299
|
+
iface
|
300
|
+
end
|
301
|
+
|
302
|
+
# determine offload features for the interface using ethtool
|
303
|
+
def ethernet_offload_parameters(iface)
|
304
|
+
return iface unless ethtool_binary_path
|
305
|
+
|
306
|
+
iface.each_key do |tmp_int|
|
307
|
+
next unless iface[tmp_int][:encapsulation] == "Ethernet"
|
308
|
+
|
309
|
+
so = shell_out("#{ethtool_binary_path} -k #{tmp_int}")
|
310
|
+
Ohai::Log.debug("Plugin Network: Parsing ethtool output: #{so.stdout}")
|
311
|
+
iface[tmp_int]["offload_params"] = {}
|
312
|
+
so.stdout.lines.each do |line|
|
313
|
+
next if line.start_with?("Features for")
|
314
|
+
next if line.strip.nil?
|
315
|
+
|
316
|
+
key, val = line.split(/:\s+/)
|
317
|
+
if val
|
318
|
+
offload_key = key.downcase.strip.tr(" ", "_").to_s
|
319
|
+
iface[tmp_int]["offload_params"][offload_key] = val.downcase.gsub(/\[.*\]/, "").strip.to_s
|
320
|
+
end
|
321
|
+
end
|
322
|
+
end
|
323
|
+
iface
|
324
|
+
end
|
325
|
+
|
326
|
+
# determine pause parameters for the interface using ethtool
|
327
|
+
def ethernet_pause_parameters(iface)
|
328
|
+
return iface unless ethtool_binary_path
|
329
|
+
|
330
|
+
iface.each_key do |tmp_int|
|
331
|
+
next unless iface[tmp_int][:encapsulation] == "Ethernet"
|
332
|
+
|
333
|
+
so = shell_out("#{ethtool_binary_path} -a #{tmp_int}")
|
334
|
+
logger.trace("Plugin Network: Parsing ethtool output: #{so.stdout}")
|
335
|
+
iface[tmp_int]["pause_params"] = {}
|
336
|
+
so.stdout.lines.each do |line|
|
337
|
+
next if line.start_with?("Pause parameters for")
|
338
|
+
next if line.strip.nil?
|
339
|
+
|
340
|
+
key, val = line.split(/:\s+/)
|
341
|
+
if val
|
342
|
+
pause_key = "#{key.downcase.tr(" ", "_")}"
|
343
|
+
iface[tmp_int]["pause_params"][pause_key] = val.strip.eql? "on"
|
344
|
+
end
|
345
|
+
end
|
346
|
+
end
|
347
|
+
iface
|
348
|
+
end
|
349
|
+
|
350
|
+
# determine driver info for the interface using ethtool
|
351
|
+
def ethernet_driver_info(iface)
|
352
|
+
return iface unless ethtool_binary_path
|
353
|
+
|
354
|
+
iface.each_key do |tmp_int|
|
355
|
+
next unless iface[tmp_int][:encapsulation] == "Ethernet"
|
356
|
+
|
357
|
+
so = shell_out("#{ethtool_binary_path} -i #{tmp_int}")
|
358
|
+
logger.trace("Plugin Network: Parsing ethtool output: #{so.stdout}")
|
359
|
+
iface[tmp_int]["driver_info"] = {}
|
360
|
+
so.stdout.lines.each do |line|
|
361
|
+
next if line.strip.nil?
|
362
|
+
|
363
|
+
key, val = line.split(/:\s+/)
|
364
|
+
if val.nil?
|
365
|
+
val = ""
|
366
|
+
end
|
367
|
+
driver_key = key.downcase.tr(" ", "_").to_s
|
368
|
+
iface[tmp_int]["driver_info"][driver_key] = val.chomp
|
369
|
+
end
|
370
|
+
end
|
371
|
+
iface
|
372
|
+
end
|
373
|
+
|
374
|
+
# determine link stats, vlans, queue length, and state for an interface using ip
|
375
|
+
def link_statistics(iface, net_counters)
|
376
|
+
so = shell_out("ip -d -s link")
|
377
|
+
tmp_int = nil
|
378
|
+
on_rx = true
|
379
|
+
xdp_mode = nil
|
380
|
+
so.stdout.lines do |line|
|
381
|
+
if line =~ IPROUTE_INT_REGEX
|
382
|
+
tmp_int = $2
|
383
|
+
iface[tmp_int] ||= Mash.new
|
384
|
+
net_counters[tmp_int] ||= Mash.new
|
385
|
+
end
|
386
|
+
|
387
|
+
if /^\s+(ip6tnl|ipip)/.match?(line)
|
388
|
+
iface[tmp_int][:tunnel_info] = {}
|
389
|
+
words = line.split
|
390
|
+
words.each_with_index do |word, index|
|
391
|
+
case word
|
392
|
+
when "external"
|
393
|
+
iface[tmp_int][:tunnel_info][word] = true
|
394
|
+
when "any", "ipip6", "ip6ip6"
|
395
|
+
iface[tmp_int][:tunnel_info][:proto] = word
|
396
|
+
when "remote",
|
397
|
+
"local",
|
398
|
+
"encaplimit",
|
399
|
+
"hoplimit",
|
400
|
+
"tclass",
|
401
|
+
"flowlabel",
|
402
|
+
"addrgenmode",
|
403
|
+
"numtxqueues",
|
404
|
+
"numrxqueues",
|
405
|
+
"gso_max_size",
|
406
|
+
"gso_max_segs"
|
407
|
+
iface[tmp_int][:tunnel_info][word] = words[index + 1]
|
408
|
+
end
|
409
|
+
end
|
410
|
+
end
|
411
|
+
|
412
|
+
if line =~ /(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)\s+(\d+)/
|
413
|
+
int = on_rx ? :rx : :tx
|
414
|
+
net_counters[tmp_int][int] ||= Mash.new
|
415
|
+
net_counters[tmp_int][int][:bytes] = $1
|
416
|
+
net_counters[tmp_int][int][:packets] = $2
|
417
|
+
net_counters[tmp_int][int][:errors] = $3
|
418
|
+
net_counters[tmp_int][int][:drop] = $4
|
419
|
+
if int == :rx
|
420
|
+
net_counters[tmp_int][int][:overrun] = $5
|
421
|
+
else
|
422
|
+
net_counters[tmp_int][int][:carrier] = $5
|
423
|
+
net_counters[tmp_int][int][:collisions] = $6
|
424
|
+
end
|
425
|
+
|
426
|
+
on_rx = !on_rx
|
427
|
+
end
|
428
|
+
|
429
|
+
if line =~ /qlen (\d+)/
|
430
|
+
net_counters[tmp_int][:tx] ||= Mash.new
|
431
|
+
net_counters[tmp_int][:tx][:queuelen] = $1
|
432
|
+
end
|
433
|
+
|
434
|
+
if line =~ /vlan id (\d+)/ || line =~ /vlan protocol ([\w\.]+) id (\d+)/
|
435
|
+
if $2
|
436
|
+
tmp_prot = $1
|
437
|
+
tmp_id = $2
|
438
|
+
else
|
439
|
+
tmp_id = $1
|
440
|
+
end
|
441
|
+
iface[tmp_int][:vlan] ||= Mash.new
|
442
|
+
iface[tmp_int][:vlan][:id] = tmp_id
|
443
|
+
iface[tmp_int][:vlan][:protocol] = tmp_prot if tmp_prot
|
444
|
+
|
445
|
+
vlan_flags = line.scan(/(REORDER_HDR|GVRP|LOOSE_BINDING)/)
|
446
|
+
if vlan_flags.length > 0
|
447
|
+
iface[tmp_int][:vlan][:flags] = vlan_flags.flatten.uniq
|
448
|
+
end
|
449
|
+
end
|
450
|
+
|
451
|
+
# https://rubular.com/r/JRp6lNANmpcLV5
|
452
|
+
if line =~ /\sstate (\w+)/
|
453
|
+
iface[tmp_int]["state"] = $1.downcase
|
454
|
+
end
|
455
|
+
|
456
|
+
if line.include?("xdp")
|
457
|
+
mode = line.scan(/\s(xdp|xdpgeneric|xdpoffload|xdpmulti)\s/).flatten[0]
|
458
|
+
# Fetches and sets the mode from the first line.
|
459
|
+
unless mode.nil?
|
460
|
+
iface[tmp_int][:xdp] = {}
|
461
|
+
if mode.eql?("xdp")
|
462
|
+
# In case of xdpdrv, mode is xdp,
|
463
|
+
# to keep it consistent, keeping mode as xdpdrv.
|
464
|
+
mode = "xdpdrv"
|
465
|
+
end
|
466
|
+
xdp_mode = mode
|
467
|
+
iface[tmp_int][:xdp][:attached] = []
|
468
|
+
end
|
469
|
+
|
470
|
+
if line =~ %r{prog/(\w+) id (\d+) tag (\w+)}
|
471
|
+
mode = $1.eql?("xdp") ? xdp_mode : $1
|
472
|
+
iface[tmp_int][:xdp][:attached] << {
|
473
|
+
mode: mode,
|
474
|
+
id: $2,
|
475
|
+
tag: $3,
|
476
|
+
}
|
477
|
+
end
|
478
|
+
end
|
479
|
+
end
|
480
|
+
iface
|
481
|
+
end
|
482
|
+
|
483
|
+
def match_iproute(iface, line, cint)
|
484
|
+
if line =~ IPROUTE_INT_REGEX
|
485
|
+
cint = $2
|
486
|
+
iface[cint] = Mash.new
|
487
|
+
if cint =~ /^(\w+?)(\d+.*)/
|
488
|
+
iface[cint][:type] = $1
|
489
|
+
iface[cint][:number] = $2
|
490
|
+
end
|
491
|
+
|
492
|
+
if line =~ /mtu (\d+)/
|
493
|
+
iface[cint][:mtu] = $1
|
494
|
+
end
|
495
|
+
|
496
|
+
flags = line.scan(/(UP|BROADCAST|DEBUG|LOOPBACK|POINTTOPOINT|NOTRAILERS|LOWER_UP|NOARP|PROMISC|ALLMULTI|SLAVE|MASTER|MULTICAST|DYNAMIC)/)
|
497
|
+
if flags.length > 1
|
498
|
+
iface[cint][:flags] = flags.flatten.uniq
|
499
|
+
end
|
500
|
+
end
|
501
|
+
cint
|
502
|
+
end
|
503
|
+
|
504
|
+
def parse_ip_addr(iface)
|
505
|
+
so = shell_out("ip addr")
|
506
|
+
cint = nil
|
507
|
+
so.stdout.lines do |line|
|
508
|
+
cint = match_iproute(iface, line, cint)
|
509
|
+
|
510
|
+
parse_ip_addr_link_line(cint, iface, line)
|
511
|
+
cint = parse_ip_addr_inet_line(cint, iface, line)
|
512
|
+
parse_ip_addr_inet6_line(cint, iface, line)
|
513
|
+
end
|
514
|
+
end
|
515
|
+
|
516
|
+
def parse_ip_addr_link_line(cint, iface, line)
|
517
|
+
if line =~ %r{link/(\w+) ([\da-f\:]+) }
|
518
|
+
iface[cint][:encapsulation] = linux_encaps_lookup($1)
|
519
|
+
unless $2 == "00:00:00:00:00:00"
|
520
|
+
iface[cint][:addresses] ||= Mash.new
|
521
|
+
iface[cint][:addresses][$2.upcase] = { "family" => "lladdr" }
|
522
|
+
end
|
523
|
+
end
|
524
|
+
end
|
525
|
+
|
526
|
+
def parse_ip_addr_inet_line(cint, iface, line)
|
527
|
+
if line =~ %r{inet (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})(/(\d{1,2}))?}
|
528
|
+
tmp_addr, tmp_prefix = $1, $3
|
529
|
+
tmp_prefix ||= "32"
|
530
|
+
original_int = nil
|
531
|
+
|
532
|
+
# Are we a formerly aliased interface?
|
533
|
+
if line =~ /#{cint}:(\d+)$/
|
534
|
+
sub_int = $1
|
535
|
+
alias_int = "#{cint}:#{sub_int}"
|
536
|
+
original_int = cint
|
537
|
+
cint = alias_int
|
538
|
+
end
|
539
|
+
|
540
|
+
iface[cint] ||= Mash.new # Create the fake alias interface if needed
|
541
|
+
iface[cint][:addresses] ||= Mash.new
|
542
|
+
iface[cint][:addresses][tmp_addr] = { "family" => "inet", "prefixlen" => tmp_prefix }
|
543
|
+
iface[cint][:addresses][tmp_addr][:netmask] = IPAddr.new("255.255.255.255").mask(tmp_prefix.to_i).to_s
|
544
|
+
|
545
|
+
if line =~ /peer (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/
|
546
|
+
iface[cint][:addresses][tmp_addr][:peer] = $1
|
547
|
+
end
|
548
|
+
|
549
|
+
if line =~ /brd (\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/
|
550
|
+
iface[cint][:addresses][tmp_addr][:broadcast] = $1
|
551
|
+
end
|
552
|
+
|
553
|
+
if line =~ /scope (\w+)/
|
554
|
+
iface[cint][:addresses][tmp_addr][:scope] = ($1.eql?("host") ? "Node" : $1.capitalize)
|
555
|
+
end
|
556
|
+
|
557
|
+
# If we found we were an alias interface, restore cint to its original value
|
558
|
+
cint = original_int unless original_int.nil?
|
559
|
+
end
|
560
|
+
cint
|
561
|
+
end
|
562
|
+
|
563
|
+
def parse_ip_addr_inet6_line(cint, iface, line)
|
564
|
+
if line =~ %r{inet6 ([a-f0-9\:]+)/(\d+) scope (\w+)( .*)?}
|
565
|
+
iface[cint][:addresses] ||= Mash.new
|
566
|
+
tmp_addr = $1
|
567
|
+
tags = $4 || ""
|
568
|
+
tags = tags.split
|
569
|
+
|
570
|
+
iface[cint][:addresses][tmp_addr] = {
|
571
|
+
"family" => "inet6",
|
572
|
+
"prefixlen" => $2,
|
573
|
+
"scope" => ($3.eql?("host") ? "Node" : $3.capitalize),
|
574
|
+
"tags" => tags,
|
575
|
+
}
|
576
|
+
end
|
577
|
+
end
|
578
|
+
|
579
|
+
# returns the macaddress for interface from a hash of interfaces (iface elsewhere in this file)
|
580
|
+
def get_mac_for_interface(interfaces, interface)
|
581
|
+
interfaces[interface][:addresses].find { |k, v| v["family"] == "lladdr" }.first unless interfaces[interface][:addresses].nil? || interfaces[interface][:flags].include?("NOARP")
|
582
|
+
end
|
583
|
+
|
584
|
+
# returns the default route with the lowest metric (unspecified metric is 0)
|
585
|
+
def choose_default_route(routes)
|
586
|
+
routes.select do |r|
|
587
|
+
r[:destination] == "default"
|
588
|
+
end.min do |x, y|
|
589
|
+
(x[:metric].nil? ? 0 : x[:metric].to_i) <=> (y[:metric].nil? ? 0 : y[:metric].to_i)
|
590
|
+
end
|
591
|
+
end
|
592
|
+
|
593
|
+
def interface_has_no_addresses_in_family?(iface, family)
|
594
|
+
return true if iface[:addresses].nil?
|
595
|
+
|
596
|
+
iface[:addresses].values.all? { |addr| addr["family"] != family }
|
597
|
+
end
|
598
|
+
|
599
|
+
def interface_have_address?(iface, address)
|
600
|
+
return false if iface[:addresses].nil?
|
601
|
+
|
602
|
+
iface[:addresses].key?(address)
|
603
|
+
end
|
604
|
+
|
605
|
+
def interface_address_not_link_level?(iface, address)
|
606
|
+
!(iface[:addresses][address][:scope].casecmp("link") == 0)
|
607
|
+
end
|
608
|
+
|
609
|
+
def interface_valid_for_route?(iface, address, family)
|
610
|
+
return true if interface_has_no_addresses_in_family?(iface, family)
|
611
|
+
|
612
|
+
interface_have_address?(iface, address) && interface_address_not_link_level?(iface, address)
|
613
|
+
end
|
614
|
+
|
615
|
+
def route_is_valid_default_route?(route, default_route)
|
616
|
+
# if the route destination is a default route, it's good
|
617
|
+
return true if route[:destination] == "default"
|
618
|
+
|
619
|
+
return false if default_route[:via].nil?
|
620
|
+
|
621
|
+
dest_ipaddr = IPAddr.new(route[:destination])
|
622
|
+
default_route_via = IPAddr.new(default_route[:via])
|
623
|
+
|
624
|
+
# check if nexthop is the same address family
|
625
|
+
return false if dest_ipaddr.ipv4? != default_route_via.ipv4?
|
626
|
+
|
627
|
+
# the default route has a gateway and the route matches the gateway
|
628
|
+
dest_ipaddr.include?(default_route_via)
|
629
|
+
end
|
630
|
+
|
631
|
+
# ipv4/ipv6 routes are different enough that having a single algorithm to select the favored route for both creates unnecessary complexity
|
632
|
+
# this method attempts to deduce the route that is most important to the user, which is later used to deduce the favored values for {ip,mac,ip6}address
|
633
|
+
# we only consider routes that are default routes, or those routes that get us to the gateway for a default route
|
634
|
+
def favored_default_route_linux(routes, iface, default_route, family)
|
635
|
+
routes.select do |r|
|
636
|
+
if family[:name] == "inet"
|
637
|
+
# the route must have a source address
|
638
|
+
next if r[:src].nil? || r[:src].empty?
|
639
|
+
|
640
|
+
# the interface specified in the route must exist
|
641
|
+
route_interface = iface[r[:dev]]
|
642
|
+
next if route_interface.nil? # the interface specified in the route must exist
|
643
|
+
|
644
|
+
# the interface must have no addresses, or if it has the source address, the address must not
|
645
|
+
# be a link-level address
|
646
|
+
next unless interface_valid_for_route?(route_interface, r[:src], "inet")
|
647
|
+
|
648
|
+
# the route must either be a default route, or it must have a gateway which is accessible via the route
|
649
|
+
next unless route_is_valid_default_route?(r, default_route)
|
650
|
+
|
651
|
+
true
|
652
|
+
elsif family[:name] == "inet6"
|
653
|
+
iface[r[:dev]] &&
|
654
|
+
iface[r[:dev]][:state] == "up" &&
|
655
|
+
route_is_valid_default_route?(r, default_route)
|
656
|
+
end
|
657
|
+
end.min_by do |r|
|
658
|
+
# sorting the selected routes:
|
659
|
+
# - getting default routes first
|
660
|
+
# - then sort by metric
|
661
|
+
# - then sort by if the device was inferred or not (preferring explicit to inferred)
|
662
|
+
# - then by prefixlen
|
663
|
+
[
|
664
|
+
r[:destination] == "default" ? 0 : 1,
|
665
|
+
r[:metric].nil? ? 0 : r[:metric].to_i,
|
666
|
+
r[:inferred] ? 1 : 0,
|
667
|
+
# for some reason IPAddress doesn't accept "::/0", it doesn't like prefix==0
|
668
|
+
# just a quick workaround: use 0 if IPAddress fails
|
669
|
+
begin
|
670
|
+
IPAddress( r[:destination] == "default" ? family[:default_route] : r[:destination] ).prefix
|
671
|
+
rescue
|
672
|
+
0
|
673
|
+
end,
|
674
|
+
]
|
675
|
+
end
|
676
|
+
end
|
677
|
+
|
678
|
+
# Both the network plugin and this plugin (linux/network) are run on linux. This plugin runs first.
|
679
|
+
# If the 'ip' binary is available, this plugin may set {ip,mac,ip6}address. The network plugin should not overwrite these.
|
680
|
+
# The older code section below that relies on the deprecated net-tools, e.g. netstat and ifconfig, provides less functionality.
|
681
|
+
collect_data(:linux) do
|
682
|
+
require "ipaddr" unless defined?(IPAddr)
|
683
|
+
|
684
|
+
iface = Mash.new
|
685
|
+
net_counters = Mash.new
|
686
|
+
|
687
|
+
network Mash.new unless network
|
688
|
+
network[:interfaces] ||= Mash.new
|
689
|
+
counters Mash.new unless counters
|
690
|
+
counters[:network] ||= Mash.new
|
691
|
+
|
692
|
+
# ohai.plugin[:network][:default_route_table] = 'default'
|
693
|
+
if configuration(:default_route_table).nil? || configuration(:default_route_table).empty?
|
694
|
+
default_route_table = "main"
|
695
|
+
else
|
696
|
+
default_route_table = configuration(:default_route_table)
|
697
|
+
end
|
698
|
+
logger.trace("Plugin Network: default route table is '#{default_route_table}'")
|
699
|
+
|
700
|
+
# Match the lead line for an interface from iproute2
|
701
|
+
# 3: eth0.11@eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP
|
702
|
+
# The '@eth0:' portion doesn't exist on primary interfaces and thus is optional in the regex
|
703
|
+
IPROUTE_INT_REGEX ||= /^(\d+): ([0-9a-zA-Z@:\.\-_]*?)(@[0-9a-zA-Z\-_]+|):\s/.freeze
|
704
|
+
|
705
|
+
if which("ip")
|
706
|
+
# families to get default routes from
|
707
|
+
families = [{
|
708
|
+
name: "inet",
|
709
|
+
default_route: "0.0.0.0/0",
|
710
|
+
default_prefix: :default,
|
711
|
+
neighbour_attribute: :arp,
|
712
|
+
}]
|
713
|
+
|
714
|
+
if ipv6_enabled?
|
715
|
+
families << {
|
716
|
+
name: "inet6",
|
717
|
+
default_route: "::/0",
|
718
|
+
default_prefix: :default_inet6,
|
719
|
+
neighbour_attribute: :neighbour_inet6,
|
720
|
+
}
|
721
|
+
end
|
722
|
+
|
723
|
+
parse_ip_addr(iface)
|
724
|
+
|
725
|
+
iface = link_statistics(iface, net_counters)
|
726
|
+
|
727
|
+
families.each do |family|
|
728
|
+
neigh_attr = family[:neighbour_attribute]
|
729
|
+
default_prefix = family[:default_prefix]
|
730
|
+
|
731
|
+
iface = extract_neighbors(family, iface, neigh_attr)
|
732
|
+
|
733
|
+
iface = check_routing_table(family, iface, default_route_table)
|
734
|
+
|
735
|
+
routes = parse_routes(family, iface)
|
736
|
+
|
737
|
+
default_route = choose_default_route(routes)
|
738
|
+
|
739
|
+
if default_route.nil? || default_route.empty?
|
740
|
+
attribute_name = if family[:name] == "inet"
|
741
|
+
"default_interface"
|
742
|
+
else
|
743
|
+
"default_#{family[:name]}_interface"
|
744
|
+
end
|
745
|
+
logger.trace("Plugin Network: Unable to determine '#{attribute_name}' as no default routes were found for that interface family")
|
746
|
+
else
|
747
|
+
network["#{default_prefix}_interface"] = default_route[:dev]
|
748
|
+
logger.trace("Plugin Network: #{default_prefix}_interface set to #{default_route[:dev]}")
|
749
|
+
|
750
|
+
# setting gateway to 0.0.0.0 or :: if the default route is a link level one
|
751
|
+
network["#{default_prefix}_gateway"] = default_route[:via] || family[:default_route].chomp("/0")
|
752
|
+
logger.trace("Plugin Network: #{default_prefix}_gateway set to #{network["#{default_prefix}_gateway"]}")
|
753
|
+
|
754
|
+
# deduce the default route the user most likely cares about to pick {ip,mac,ip6}address below
|
755
|
+
favored_route = favored_default_route_linux(routes, iface, default_route, family)
|
756
|
+
|
757
|
+
# FIXME: This entire block should go away, and the network plugin should be the sole source of {ip,ip6,mac}address
|
758
|
+
|
759
|
+
# since we're at it, let's populate {ip,mac,ip6}address with the best values
|
760
|
+
# if we don't set these, the network plugin may set them afterwards
|
761
|
+
if favored_route && !favored_route.empty?
|
762
|
+
if family[:name] == "inet"
|
763
|
+
ipaddress favored_route[:src]
|
764
|
+
m = get_mac_for_interface(iface, favored_route[:dev])
|
765
|
+
logger.trace("Plugin Network: Overwriting macaddress #{macaddress} with #{m} from interface #{favored_route[:dev]}") if macaddress
|
766
|
+
macaddress m
|
767
|
+
elsif family[:name] == "inet6"
|
768
|
+
# this rarely does anything since we rarely have src for ipv6, so this usually falls back on the network plugin
|
769
|
+
ip6address favored_route[:src]
|
770
|
+
if macaddress
|
771
|
+
logger.trace("Plugin Network: Not setting macaddress from ipv6 interface #{favored_route[:dev]} because macaddress is already set")
|
772
|
+
else
|
773
|
+
macaddress get_mac_for_interface(iface, favored_route[:dev])
|
774
|
+
end
|
775
|
+
end
|
776
|
+
else
|
777
|
+
logger.trace("Plugin Network: Unable to deduce the favored default route for family '#{family[:name]}' despite finding a default route, and is not setting ipaddress/ip6address/macaddress. the network plugin may provide fallbacks.")
|
778
|
+
logger.trace("Plugin Network: This potential default route was excluded: #{default_route}")
|
779
|
+
end
|
780
|
+
end
|
781
|
+
end # end families.each
|
782
|
+
else # ip binary not available, falling back to net-tools, e.g. route, ifconfig
|
783
|
+
begin
|
784
|
+
so = shell_out("route -n")
|
785
|
+
route_result = so.stdout.split($/).grep( /^0.0.0.0/ )[0].split( /[ \t]+/ )
|
786
|
+
network[:default_gateway], network[:default_interface] = route_result.values_at(1, 7)
|
787
|
+
rescue Ohai::Exceptions::Exec
|
788
|
+
logger.trace("Plugin Network: Unable to determine default interface")
|
789
|
+
end
|
790
|
+
|
791
|
+
so = shell_out("ifconfig -a")
|
792
|
+
cint = nil
|
793
|
+
so.stdout.lines do |line|
|
794
|
+
tmp_addr = nil
|
795
|
+
# dev_valid_name in the kernel only excludes slashes, nulls, spaces
|
796
|
+
# http://git.kernel.org/?p=linux/kernel/git/stable/linux-stable.git;a=blob;f=net/core/dev.c#l851
|
797
|
+
if line =~ /^([0-9a-zA-Z@\.\:\-_]+)\s+/
|
798
|
+
cint = $1
|
799
|
+
iface[cint] = Mash.new
|
800
|
+
if cint =~ /^(\w+?)(\d+.*)/
|
801
|
+
iface[cint][:type] = $1
|
802
|
+
iface[cint][:number] = $2
|
803
|
+
end
|
804
|
+
end
|
805
|
+
if line =~ /Link encap:(Local Loopback)/ || line =~ /Link encap:(.+?)\s/
|
806
|
+
iface[cint][:encapsulation] = linux_encaps_lookup($1)
|
807
|
+
end
|
808
|
+
if line =~ /HWaddr (.+?)\s/
|
809
|
+
iface[cint][:addresses] ||= Mash.new
|
810
|
+
iface[cint][:addresses][$1] = { "family" => "lladdr" }
|
811
|
+
end
|
812
|
+
if line =~ /inet addr:(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/
|
813
|
+
iface[cint][:addresses] ||= Mash.new
|
814
|
+
iface[cint][:addresses][$1] = { "family" => "inet" }
|
815
|
+
tmp_addr = $1
|
816
|
+
end
|
817
|
+
if line =~ %r{inet6 addr: ([a-f0-9\:]+)/(\d+) Scope:(\w+)}
|
818
|
+
iface[cint][:addresses] ||= Mash.new
|
819
|
+
iface[cint][:addresses][$1] = { "family" => "inet6", "prefixlen" => $2, "scope" => ($3.eql?("Host") ? "Node" : $3) }
|
820
|
+
end
|
821
|
+
if line =~ /Bcast:(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/
|
822
|
+
iface[cint][:addresses][tmp_addr]["broadcast"] = $1
|
823
|
+
end
|
824
|
+
if line =~ /Mask:(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/
|
825
|
+
iface[cint][:addresses][tmp_addr]["netmask"] = $1
|
826
|
+
end
|
827
|
+
flags = line.scan(/(UP|BROADCAST|DEBUG|LOOPBACK|POINTTOPOINT|NOTRAILERS|RUNNING|NOARP|PROMISC|ALLMULTI|SLAVE|MASTER|MULTICAST|DYNAMIC)\s/)
|
828
|
+
if flags.length > 1
|
829
|
+
iface[cint][:flags] = flags.flatten
|
830
|
+
end
|
831
|
+
if line =~ /MTU:(\d+)/
|
832
|
+
iface[cint][:mtu] = $1
|
833
|
+
end
|
834
|
+
if line =~ /P-t-P:(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/
|
835
|
+
iface[cint][:peer] = $1
|
836
|
+
end
|
837
|
+
if line =~ /RX packets:(\d+) errors:(\d+) dropped:(\d+) overruns:(\d+) frame:(\d+)/
|
838
|
+
net_counters[cint] ||= Mash.new
|
839
|
+
net_counters[cint][:rx] = { "packets" => $1, "errors" => $2, "drop" => $3, "overrun" => $4, "frame" => $5 }
|
840
|
+
end
|
841
|
+
if line =~ /TX packets:(\d+) errors:(\d+) dropped:(\d+) overruns:(\d+) carrier:(\d+)/
|
842
|
+
net_counters[cint][:tx] = { "packets" => $1, "errors" => $2, "drop" => $3, "overrun" => $4, "carrier" => $5 }
|
843
|
+
end
|
844
|
+
if line =~ /collisions:(\d+)/
|
845
|
+
net_counters[cint][:tx]["collisions"] = $1
|
846
|
+
end
|
847
|
+
if line =~ /txqueuelen:(\d+)/
|
848
|
+
net_counters[cint][:tx]["queuelen"] = $1
|
849
|
+
end
|
850
|
+
if line =~ /RX bytes:(\d+) \((\d+?\.\d+ .+?)\)/
|
851
|
+
net_counters[cint][:rx]["bytes"] = $1
|
852
|
+
end
|
853
|
+
if line =~ /TX bytes:(\d+) \((\d+?\.\d+ .+?)\)/
|
854
|
+
net_counters[cint][:tx]["bytes"] = $1
|
855
|
+
end
|
856
|
+
end
|
857
|
+
|
858
|
+
so = shell_out("arp -an")
|
859
|
+
so.stdout.lines do |line|
|
860
|
+
if line =~ /^\S+ \((\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})\) at ([a-fA-F0-9\:]+) \[(\w+)\] on ([0-9a-zA-Z\.\:\-]+)/
|
861
|
+
next unless iface[$4] # this should never happen
|
862
|
+
|
863
|
+
iface[$4][:arp] ||= Mash.new
|
864
|
+
iface[$4][:arp][$1] = $2.downcase
|
865
|
+
end
|
866
|
+
end
|
867
|
+
end # end "ip else net-tools" block
|
868
|
+
|
869
|
+
iface = ethernet_layer_one(iface)
|
870
|
+
iface = ethernet_ring_parameters(iface)
|
871
|
+
iface = ethernet_channel_parameters(iface)
|
872
|
+
iface = ethernet_coalesce_parameters(iface)
|
873
|
+
iface = ethernet_offload_parameters(iface)
|
874
|
+
iface = ethernet_driver_info(iface)
|
875
|
+
iface = ethernet_pause_parameters(iface)
|
876
|
+
counters[:network][:interfaces] = net_counters
|
877
|
+
network["interfaces"] = iface
|
878
|
+
end
|
879
|
+
end
|