]> git.openstreetmap.org Git - chef.git/blob - cookbooks/hardware/recipes/default.rb
2fbf301418faae2419c3387ef7da68dee84f8cff
[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   package "smartmontools"
469
470   template "/etc/cron.daily/update-smart-drivedb" do
471     source "update-smart-drivedb.erb"
472     owner "root"
473     group "root"
474     mode "755"
475   end
476
477   template "/usr/local/bin/smartd-mailer" do
478     source "smartd-mailer.erb"
479     owner "root"
480     group "root"
481     mode "755"
482   end
483
484   template "/etc/smartd.conf" do
485     source "smartd.conf.erb"
486     owner "root"
487     group "root"
488     mode "644"
489     variables :disks => disks
490   end
491
492   template "/etc/default/smartmontools" do
493     source "smartmontools.erb"
494     owner "root"
495     group "root"
496     mode "644"
497   end
498
499   service "smartmontools" do
500     action [:enable, :start]
501     subscribes :reload, "template[/etc/smartd.conf]"
502     subscribes :restart, "template[/etc/default/smartmontools]"
503   end
504
505   template "/etc/prometheus/collectors/smart.devices" do
506     source "smart.devices.erb"
507     owner "root"
508     group "root"
509     mode "644"
510     variables :disks => disks
511   end
512
513   prometheus_collector "smart" do
514     interval "15m"
515     user "root"
516     capability_bounding_set %w[CAP_DAC_OVERRIDE CAP_SYS_ADMIN CAP_SYS_RAWIO]
517     private_devices false
518     private_users false
519     protect_clock false
520   end
521 else
522   service "smartd" do
523     action [:stop, :disable]
524   end
525 end
526
527 if File.exist?("/etc/mdadm/mdadm.conf")
528   mdadm_conf = edit_file "/etc/mdadm/mdadm.conf" do |line|
529     line.gsub!(/^MAILADDR .*$/, "MAILADDR admins@openstreetmap.org")
530
531     line
532   end
533
534   file "/etc/mdadm/mdadm.conf" do
535     owner "root"
536     group "root"
537     mode "644"
538     content mdadm_conf
539   end
540
541   service "mdmonitor" do
542     action :nothing
543     subscribes :restart, "file[/etc/mdadm/mdadm.conf]"
544   end
545 end
546
547 file "/etc/modules" do
548   action :delete
549 end
550
551 node[:hardware][:modules].each do |module_name|
552   kernel_module module_name do
553     action :install
554     not_if { kitchen? }
555   end
556 end
557
558 node[:hardware][:blacklisted_modules].each do |module_name|
559   kernel_module module_name do
560     action :blacklist
561   end
562 end
563
564 if watchdog_module
565   kernel_module watchdog_module do
566     action :install
567   end
568
569   execute "systemctl-reload" do
570     action :nothing
571     command "systemctl daemon-reload"
572     user "root"
573     group "root"
574   end
575
576   directory "/etc/systemd/system.conf.d" do
577     owner "root"
578     group "root"
579     mode "755"
580   end
581
582   template "/etc/systemd/system.conf.d/watchdog.conf" do
583     source "watchdog.conf.erb"
584     owner "root"
585     group "root"
586     mode "644"
587     notifies :run, "execute[systemctl-reload]"
588   end
589 end
590
591 unless Dir.glob("/sys/class/hwmon/hwmon*").empty?
592   package "lm-sensors"
593
594   Dir.glob("/sys/devices/platform/coretemp.*").each do |coretemp|
595     cpu = File.basename(coretemp).sub("coretemp.", "").to_i
596     chip = format("coretemp-isa-%04d", cpu)
597
598     temps = if File.exist?("#{coretemp}/name")
599               Dir.glob("#{coretemp}/temp*_input").map do |temp|
600                 File.basename(temp).sub("temp", "").sub("_input", "").to_i
601               end.sort
602             else
603               Dir.glob("#{coretemp}/hwmon/hwmon*/temp*_input").map do |temp|
604                 File.basename(temp).sub("temp", "").sub("_input", "").to_i
605               end.sort
606             end
607
608     if temps.first == 1
609       node.default[:hardware][:sensors][chip][:temps][:temp1][:label] = "CPU #{cpu}"
610       temps.shift
611     end
612
613     temps.each_with_index do |temp, index|
614       node.default[:hardware][:sensors][chip][:temps]["temp#{temp}"][:label] = "CPU #{cpu} Core #{index}"
615     end
616   end
617
618   execute "/etc/sensors.d/chef.conf" do
619     action :nothing
620     command "/usr/bin/sensors -s"
621     user "root"
622     group "root"
623   end
624
625   template "/etc/sensors.d/chef.conf" do
626     source "sensors.conf.erb"
627     owner "root"
628     group "root"
629     mode "644"
630     notifies :run, "execute[/etc/sensors.d/chef.conf]"
631   end
632 end
633
634 if node[:hardware][:shm_size]
635   execute "remount-dev-shm" do
636     action :nothing
637     command "/bin/mount -o remount /dev/shm"
638     user "root"
639     group "root"
640   end
641
642   mount "/dev/shm" do
643     action :enable
644     device "tmpfs"
645     fstype "tmpfs"
646     options "rw,nosuid,nodev,size=#{node[:hardware][:shm_size]}"
647     notifies :run, "execute[remount-dev-shm]"
648   end
649 end
650
651 prometheus_collector "ohai" do
652   interval "15m"
653   user "root"
654   proc_subset "all"
655   capability_bounding_set %w[CAP_DAC_OVERRIDE CAP_SYS_ADMIN CAP_SYS_RAWIO]
656   private_devices false
657   private_users false
658   protect_clock false
659   protect_kernel_modules false
660 end