]> git.openstreetmap.org Git - chef.git/blob - cookbooks/hardware/recipes/default.rb
5492134dc50f1ed82eff62dc15f2f877a55edce9
[chef.git] / cookbooks / hardware / recipes / default.rb
1 #
2 # Cookbook:: hardware
3 # Recipe:: default
4 #
5 # Copyright:: 2012, OpenStreetMap Foundation
6 #
7 # Licensed under the Apache License, Version 2.0 (the "License");
8 # you may not use this file except in compliance with the License.
9 # You may obtain a copy of the License at
10 #
11 #     https://www.apache.org/licenses/LICENSE-2.0
12 #
13 # Unless required by applicable law or agreed to in writing, software
14 # distributed under the License is distributed on an "AS IS" BASIS,
15 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 # See the License for the specific language governing permissions and
17 # limitations under the License.
18 #
19
20 include_recipe "apt"
21 include_recipe "git"
22 include_recipe "prometheus"
23 include_recipe "sysfs"
24 include_recipe "tools"
25
26 ohai_plugin "hardware" do
27   template "ohai.rb.erb"
28 end
29
30 if platform?("debian")
31   package "firmware-linux"
32 end
33
34 if node[:cpu] && node[:cpu][:"0"] && node[:cpu][:"0"][:vendor_id]
35   case node[:cpu][:"0"][:vendor_id]
36   when "GenuineIntel"
37     package "intel-microcode"
38   when "AuthenticAMD"
39     package "amd64-microcode"
40   end
41 end
42
43 if node[:dmi] && node[:dmi][:system]
44   case node[:dmi][:system][:manufacturer]
45   when "empty"
46     manufacturer = node[:dmi][:base_board][:manufacturer]
47     product = node[:dmi][:base_board][:product_name]
48   else
49     manufacturer = node[:dmi][:system][:manufacturer]
50     product = node[:dmi][:system][:product_name]
51   end
52 else
53   manufacturer = "Unknown"
54   product = "Unknown"
55 end
56
57 units = []
58
59 if node[:roles].include?("bytemark") || node[:roles].include?("exonetric") || node[:roles].include?("prgmr")
60   units << "0"
61 end
62
63 case manufacturer
64 when "HP", "HPE"
65   include_recipe "apt::management-component-pack"
66
67   package "hponcfg"
68
69   execute "update-ilo" do
70     action :nothing
71     command "/usr/sbin/hponcfg -f /etc/ilo-defaults.xml"
72     not_if { kitchen? }
73   end
74
75   template "/etc/ilo-defaults.xml" do
76     source "ilo-defaults.xml.erb"
77     owner "root"
78     group "root"
79     mode "644"
80     notifies :run, "execute[update-ilo]"
81   end
82
83   package "hp-health" do
84     action :install
85     notifies :restart, "service[hp-health]"
86     only_if { platform?("ubuntu") && node[:lsb][:release].to_f < 22.04 }
87   end
88
89   service "hp-health" do
90     action [:enable, :start]
91     supports :status => true, :restart => true
92     only_if { platform?("ubuntu") && node[:lsb][:release].to_f < 22.04 }
93   end
94
95   if product.end_with?("Gen8", "Gen9")
96     package "hp-ams" do
97       action :install
98       notifies :restart, "service[hp-ams]"
99     end
100
101     service "hp-ams" do
102       action [:enable, :start]
103       supports :status => true, :restart => true
104     end
105   elsif product.end_with?("Gen10")
106     package "amsd" do
107       action :install
108       notifies :restart, "service[amsd]"
109     end
110
111     service "amsd" do
112       action [:enable, :start]
113       supports :status => true, :restart => true
114     end
115   end
116
117   units << if product.end_with?("Gen10")
118              "0"
119            else
120              "1"
121            end
122 when "TYAN"
123   units << "0"
124 when "TYAN Computer Corporation"
125   units << "0"
126 when "Supermicro"
127   units << "1"
128 when "IBM"
129   units << "0"
130 when "VMware, Inc."
131   package "open-vm-tools"
132
133   # Remove timeSync plugin completely
134   # https://github.com/vmware/open-vm-tools/issues/302
135   file "/usr/lib/open-vm-tools/plugins/vmsvc/libtimeSync.so" do
136     action :delete
137     notifies :restart, "service[open-vm-tools]"
138   end
139
140   # Attempt to tell Host we are not interested in timeSync
141   execute "vmware-toolbox-cmd-timesync-disable" do
142     command "/usr/bin/vmware-toolbox-cmd timesync disable"
143     ignore_failure true
144   end
145
146   service "open-vm-tools" do
147     action [:enable, :start]
148     supports :status => true, :restart => true
149   end
150 end
151
152 units.sort.uniq.each do |unit|
153   service "serial-getty@ttyS#{unit}" do
154     action [:enable, :start]
155     not_if { kitchen? }
156   end
157 end
158
159 # if we need a different / special kernel version to make the hardware
160 # work (e.g: https://github.com/openstreetmap/operations/issues/45) then
161 # ensure that we have the package installed. the grub template will
162 # make sure that this is the default on boot.
163 if node[:hardware][:grub][:kernel]
164   kernel_version = node[:hardware][:grub][:kernel]
165
166   package "linux-image-#{kernel_version}-generic"
167   package "linux-image-extra-#{kernel_version}-generic"
168   package "linux-headers-#{kernel_version}-generic"
169   package "linux-tools-#{kernel_version}-generic"
170
171   boot_device = IO.popen(["df", "/boot"]).readlines.last.split.first
172   boot_uuid = IO.popen(["blkid", "-o", "value", "-s", "UUID", boot_device]).readlines.first.chomp
173   grub_entry = "gnulinux-advanced-#{boot_uuid}>gnulinux-#{kernel_version}-advanced-#{boot_uuid}"
174 else
175   grub_entry = "0"
176 end
177
178 if File.exist?("/etc/default/grub")
179   execute "update-grub" do
180     action :nothing
181     command "/usr/sbin/update-grub"
182     not_if { kitchen? }
183   end
184
185   template "/etc/default/grub" do
186     source "grub.erb"
187     owner "root"
188     group "root"
189     mode "644"
190     variables :units => units, :entry => grub_entry
191     notifies :run, "execute[update-grub]"
192   end
193 end
194
195 package "initramfs-tools"
196
197 execute "update-initramfs" do
198   action :nothing
199   command "update-initramfs -u -k all"
200   user "root"
201   group "root"
202 end
203
204 template "/etc/initramfs-tools/conf.d/mdadm" do
205   source "initramfs-mdadm.erb"
206   owner "root"
207   group "root"
208   mode "644"
209   notifies :run, "execute[update-initramfs]"
210 end
211
212 # haveged is only required on older kernels
213 # /dev/random is not blocking anymore in 5.15+
214 if Chef::Util.compare_versions(node[:kernel][:release], [5, 15]).negative?
215   package "haveged"
216   service "haveged" do
217     action [:enable, :start]
218   end
219 else
220   service "haveged" do
221     action [:stop, :disable]
222   end
223   package "haveged" do
224     action :remove
225   end
226 end
227
228 watchdog_module = %w[hpwdt sp5100_tco].find do |module_name|
229   node[:hardware][:pci]&.any? { |_, pci| pci[:modules]&.any?(module_name) }
230 end
231
232 if node[:kernel][:modules].include?("ipmi_si")
233   package "ipmitool"
234   package "freeipmi-tools"
235
236   template "/etc/prometheus/ipmi_local.yml" do
237     source "ipmi_local.yml.erb"
238     owner "root"
239     group "root"
240     mode "644"
241   end
242
243   prometheus_exporter "ipmi" do
244     port 9290
245     user "root"
246     private_devices false
247     protect_clock false
248     system_call_filter ["@system-service", "@raw-io"]
249     options "--config.file=/etc/prometheus/ipmi_local.yml"
250     subscribes :restart, "template[/etc/prometheus/ipmi_local.yml]"
251   end
252
253   watchdog_module ||= "ipmi_watchdog"
254 end
255
256 package "irqbalance"
257
258 service "irqbalance" do
259   action [:start, :enable]
260   supports :status => false, :restart => true, :reload => false
261 end
262
263 package "lldpd"
264
265 service "lldpd" do
266   action [:start, :enable]
267   supports :status => true, :restart => true, :reload => true
268 end
269
270 ohai_plugin "lldp" do
271   template "lldp.rb.erb"
272 end
273
274 package %w[
275   rasdaemon
276   ruby-sqlite3
277 ]
278
279 service "rasdaemon" do
280   action [:enable, :start]
281 end
282
283 prometheus_exporter "rasdaemon" do
284   port 9797
285   user "root"
286 end
287
288 tools_packages = []
289 status_packages = {}
290
291 if node[:virtualization][:role] != "guest" ||
292    (node[:virtualization][:system] != "lxc" &&
293     node[:virtualization][:system] != "lxd" &&
294     node[:virtualization][:system] != "openvz")
295
296   node[:kernel][:modules].each_key do |modname|
297     case modname
298     when "cciss"
299       tools_packages << "ssacli"
300       status_packages["cciss-vol-status"] ||= []
301     when "hpsa"
302       tools_packages << "ssacli"
303       status_packages["cciss-vol-status"] ||= []
304     when "mptsas"
305       tools_packages << "lsiutil"
306       status_packages["mpt-status"] ||= []
307     when "mpt2sas", "mpt3sas"
308       tools_packages << "sas2ircu"
309       status_packages["sas2ircu-status"] ||= []
310     when "megaraid_sas"
311       tools_packages << "megacli"
312       status_packages["megaclisas-status"] ||= []
313     when "aacraid"
314       tools_packages << "arcconf"
315       status_packages["aacraid-status"] ||= []
316     when "arcmsr"
317       tools_packages << "areca"
318     end
319   end
320
321   node[:block_device].each do |name, attributes|
322     next unless attributes[:vendor] == "HP" && attributes[:model] == "LOGICAL VOLUME"
323
324     if name =~ /^cciss!(c[0-9]+)d[0-9]+$/
325       status_packages["cciss-vol-status"] |= ["cciss/#{Regexp.last_match[1]}d0"]
326     else
327       Dir.glob("/sys/block/#{name}/device/scsi_generic/*").each do |sg|
328         status_packages["cciss-vol-status"] |= [File.basename(sg)]
329       end
330     end
331   end
332 end
333
334 include_recipe "apt::hwraid" unless status_packages.empty?
335
336 %w[ssacli lsiutil sas2ircu megactl megacli arcconf].each do |tools_package|
337   if tools_packages.include?(tools_package)
338     package tools_package
339   else
340     package tools_package do
341       action :purge
342     end
343   end
344 end
345
346 if tools_packages.include?("areca")
347   include_recipe "git"
348
349   git "/opt/areca" do
350     action :sync
351     repository "https://git.openstreetmap.org/private/areca.git"
352     depth 1
353     user "root"
354     group "root"
355     not_if { kitchen? }
356   end
357 else
358   directory "/opt/areca" do
359     action :delete
360     recursive true
361   end
362 end
363
364 %w[cciss-vol-status mpt-status sas2ircu-status megaclisas-status aacraid-status].each do |status_package|
365   if status_packages.include?(status_package)
366     package status_package
367
368     service "#{status_package}d" do
369       action [:stop, :disable]
370     end
371
372     file "/etc/default/#{status_package}d" do
373       action :delete
374     end
375   else
376     package status_package do
377       action :purge
378     end
379   end
380 end
381
382 systemd_service "cciss-vol-statusd" do
383   action :delete
384 end
385
386 template "/usr/local/bin/cciss-vol-statusd" do
387   action :delete
388 end
389
390 disks = if node[:hardware][:disk]
391           node[:hardware][:disk][:disks]
392         else
393           []
394         end
395
396 intel_ssds = disks.select { |d| d[:vendor] == "INTEL" && d[:model] =~ /^SSD/ }
397
398 nvmes = if node[:hardware][:pci]
399           node[:hardware][:pci].values.select { |pci| pci[:driver] == "nvme" }
400         else
401           []
402         end
403
404 unless nvmes.empty?
405   package "nvme-cli"
406 end
407
408 intel_nvmes = nvmes.select { |pci| pci[:vendor_name] == "Intel Corporation" }
409
410 if !intel_ssds.empty? || !intel_nvmes.empty?
411   package "unzip"
412
413   sst_tool_version = "1.3"
414   sst_package_version = "#{sst_tool_version}.208-0"
415
416   # remote_file "#{Chef::Config[:file_cache_path]}/SST_CLI_Linux_#{sst_tool_version}.zip" do
417   #   source "https://downloadmirror.intel.com/743764/SST_CLI_Linux_#{sst_tool_version}.zip"
418   # end
419
420   execute "#{Chef::Config[:file_cache_path]}/SST_CLI_Linux_#{sst_tool_version}.zip" do
421     command "unzip SST_CLI_Linux_#{sst_tool_version}.zip sst_#{sst_package_version}_amd64.deb"
422     cwd Chef::Config[:file_cache_path]
423     user "root"
424     group "root"
425     not_if { ::File.exist?("#{Chef::Config[:file_cache_path]}/sst_#{sst_package_version}_amd64.deb") }
426   end
427
428   dpkg_package "sst" do
429     version "#{sst_package_version}"
430     source "#{Chef::Config[:file_cache_path]}/sst_#{sst_package_version}_amd64.deb"
431   end
432
433   dpkg_package "intelmas" do
434     action :purge
435   end
436 end
437
438 disks = disks.map do |disk|
439   next if disk[:state] == "spun_down" || %w[unconfigured failed].any?(disk[:status])
440
441   if disk[:smart_device]
442     controller = node[:hardware][:disk][:controllers][disk[:controller]]
443
444     if controller && controller[:device]
445       device = controller[:device].sub("/dev/", "")
446       smart = disk[:smart_device]
447     elsif disk[:device]
448       device = disk[:device].sub("/dev/", "")
449       smart = disk[:smart_device]
450     end
451   elsif disk[:device] =~ %r{^/dev/(nvme\d+)n\d+$}
452     device = Regexp.last_match(1)
453   elsif disk[:device]
454     device = disk[:device].sub("/dev/", "")
455   end
456
457   next if device.nil?
458
459   Hash[
460     :device => device,
461     :smart => smart
462   ]
463 end
464
465 disks = disks.compact.uniq
466
467 if disks.count.positive?
468   apt_preference "smartmontools" do
469     pin "release o=Debian Backports"
470     pin_priority "600"
471     only_if { platform?("debian") }
472   end
473
474   package "smartmontools"
475
476   template "/etc/cron.daily/update-smart-drivedb" do
477     source "update-smart-drivedb.erb"
478     owner "root"
479     group "root"
480     mode "755"
481   end
482
483   template "/usr/local/bin/smartd-mailer" do
484     source "smartd-mailer.erb"
485     owner "root"
486     group "root"
487     mode "755"
488   end
489
490   template "/etc/smartd.conf" do
491     source "smartd.conf.erb"
492     owner "root"
493     group "root"
494     mode "644"
495     variables :disks => disks
496   end
497
498   template "/etc/default/smartmontools" do
499     source "smartmontools.erb"
500     owner "root"
501     group "root"
502     mode "644"
503   end
504
505   service "smartmontools" do
506     action [:enable, :start]
507     subscribes :reload, "template[/etc/smartd.conf]"
508     subscribes :restart, "template[/etc/default/smartmontools]"
509   end
510
511   template "/etc/prometheus/collectors/smart.devices" do
512     source "smart.devices.erb"
513     owner "root"
514     group "root"
515     mode "644"
516     variables :disks => disks
517   end
518
519   prometheus_collector "smart" do
520     interval "15m"
521     user "root"
522     capability_bounding_set %w[CAP_DAC_OVERRIDE CAP_SYS_ADMIN CAP_SYS_RAWIO]
523     private_devices false
524     private_users false
525     protect_clock false
526   end
527 else
528   service "smartd" do
529     action [:stop, :disable]
530   end
531 end
532
533 if File.exist?("/etc/mdadm/mdadm.conf")
534   mdadm_conf = edit_file "/etc/mdadm/mdadm.conf" do |line|
535     line.gsub!(/^MAILADDR .*$/, "MAILADDR admins@openstreetmap.org")
536
537     line
538   end
539
540   file "/etc/mdadm/mdadm.conf" do
541     owner "root"
542     group "root"
543     mode "644"
544     content mdadm_conf
545   end
546
547   service "mdmonitor" do
548     action :nothing
549     subscribes :restart, "file[/etc/mdadm/mdadm.conf]"
550   end
551 end
552
553 file "/etc/modules" do
554   action :delete
555 end
556
557 node[:hardware][:modules].each do |module_name|
558   kernel_module module_name do
559     action :install
560     not_if { kitchen? }
561   end
562 end
563
564 node[:hardware][:blacklisted_modules].each do |module_name|
565   kernel_module module_name do
566     action :blacklist
567   end
568 end
569
570 if watchdog_module
571   kernel_module watchdog_module do
572     action :install
573   end
574
575   execute "systemctl-reload" do
576     action :nothing
577     command "systemctl daemon-reload"
578     user "root"
579     group "root"
580   end
581
582   directory "/etc/systemd/system.conf.d" do
583     owner "root"
584     group "root"
585     mode "755"
586   end
587
588   template "/etc/systemd/system.conf.d/watchdog.conf" do
589     source "watchdog.conf.erb"
590     owner "root"
591     group "root"
592     mode "644"
593     notifies :run, "execute[systemctl-reload]"
594   end
595 end
596
597 unless Dir.glob("/sys/class/hwmon/hwmon*").empty?
598   package "lm-sensors"
599
600   Dir.glob("/sys/devices/platform/coretemp.*").each do |coretemp|
601     cpu = File.basename(coretemp).sub("coretemp.", "").to_i
602     chip = format("coretemp-isa-%04d", cpu)
603
604     temps = if File.exist?("#{coretemp}/name")
605               Dir.glob("#{coretemp}/temp*_input").map do |temp|
606                 File.basename(temp).sub("temp", "").sub("_input", "").to_i
607               end.sort
608             else
609               Dir.glob("#{coretemp}/hwmon/hwmon*/temp*_input").map do |temp|
610                 File.basename(temp).sub("temp", "").sub("_input", "").to_i
611               end.sort
612             end
613
614     if temps.first == 1
615       node.default[:hardware][:sensors][chip][:temps][:temp1][:label] = "CPU #{cpu}"
616       temps.shift
617     end
618
619     temps.each_with_index do |temp, index|
620       node.default[:hardware][:sensors][chip][:temps]["temp#{temp}"][:label] = "CPU #{cpu} Core #{index}"
621     end
622   end
623
624   execute "/etc/sensors.d/chef.conf" do
625     action :nothing
626     command "/usr/bin/sensors -s"
627     user "root"
628     group "root"
629   end
630
631   template "/etc/sensors.d/chef.conf" do
632     source "sensors.conf.erb"
633     owner "root"
634     group "root"
635     mode "644"
636     notifies :run, "execute[/etc/sensors.d/chef.conf]"
637   end
638 end
639
640 if node[:hardware][:shm_size]
641   execute "remount-dev-shm" do
642     action :nothing
643     command "/bin/mount -o remount /dev/shm"
644     user "root"
645     group "root"
646   end
647
648   mount "/dev/shm" do
649     action :enable
650     device "tmpfs"
651     fstype "tmpfs"
652     options "rw,nosuid,nodev,size=#{node[:hardware][:shm_size]}"
653     notifies :run, "execute[remount-dev-shm]"
654   end
655 end
656
657 prometheus_collector "ohai" do
658   interval "15m"
659   user "root"
660   proc_subset "all"
661   capability_bounding_set %w[CAP_DAC_OVERRIDE CAP_SYS_ADMIN CAP_SYS_RAWIO]
662   private_devices false
663   private_users false
664   protect_clock false
665   protect_kernel_modules false
666 end