]> git.openstreetmap.org Git - chef.git/blob - cookbooks/hardware/recipes/default.rb
Disable host time sync on VMware guests
[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 "tools"
21 include_recipe "munin"
22
23 ohai_plugin "hardware" do
24   template "ohai.rb.erb"
25 end
26
27 case node[:cpu][:"0"][:vendor_id]
28 when "GenuineIntel"
29   package "intel-microcode"
30 when "AuthenticAMD"
31   package "amd64-microcode"
32 end
33
34 if node[:dmi] && node[:dmi][:system]
35   case node[:dmi][:system][:manufacturer]
36   when "empty"
37     manufacturer = node[:dmi][:base_board][:manufacturer]
38     product = node[:dmi][:base_board][:product_name]
39   else
40     manufacturer = node[:dmi][:system][:manufacturer]
41     product = node[:dmi][:system][:product_name]
42   end
43 else
44   manufacturer = "Unknown"
45   product = "Unknown"
46 end
47
48 units = []
49
50 if node[:roles].include?("bytemark") || node[:roles].include?("exonetric")
51   units << "0"
52 end
53
54 case manufacturer
55 when "HP"
56   package "hponcfg"
57
58   package "hp-health" do
59     action :install
60     notifies :restart, "service[hp-health]"
61   end
62
63   service "hp-health" do
64     action [:enable, :start]
65     supports :status => true, :restart => true
66   end
67
68   if product.end_with?("Gen8", "Gen9")
69     package "hp-ams" do
70       action :install
71       notifies :restart, "service[hp-ams]"
72     end
73
74     service "hp-ams" do
75       action [:enable, :start]
76       supports :status => true, :restart => true
77     end
78   end
79
80   units << "1"
81 when "TYAN"
82   units << "0"
83 when "TYAN Computer Corporation"
84   units << "0"
85 when "Supermicro"
86   case product
87   when "H8DGU", "X9SCD", "X7DBU", "X7DW3", "X9DR7/E-(J)LN4F", "X9DR3-F", "X9DRW", "SYS-1028U-TN10RT+", "SYS-2028U-TN24R4T+", "SYS-1029P-WTRT", "Super Server"
88     units << "1"
89   else
90     units << "0"
91   end
92 when "IBM"
93   units << "0"
94 when "VMware, Inc."
95   package "open-vm-tools"
96
97   service "open-vm-tools" do
98     action [:enable, :start]
99     supports :status => true, :restart => true
100   end
101
102   # May need additional work per:
103   # https://github.com/vmware/open-vm-tools/issues/302
104   execute "vmware-toolbox-cmd-timesync-disable" do
105     command "/usr/bin/vmware-toolbox-cmd timesync disable"
106   end
107 end
108
109 units.sort.uniq.each do |unit|
110   service "serial-getty@ttyS#{unit}" do
111     action [:enable, :start]
112   end
113 end
114
115 # if we need a different / special kernel version to make the hardware
116 # work (e.g: https://github.com/openstreetmap/operations/issues/45) then
117 # ensure that we have the package installed. the grub template will
118 # make sure that this is the default on boot.
119 if node[:hardware][:grub][:kernel]
120   kernel_version = node[:hardware][:grub][:kernel]
121
122   package "linux-image-#{kernel_version}-generic"
123   package "linux-image-extra-#{kernel_version}-generic"
124   package "linux-headers-#{kernel_version}-generic"
125   package "linux-tools-#{kernel_version}-generic"
126
127   boot_device = IO.popen(["df", "/boot"]).readlines.last.split.first
128   boot_uuid = IO.popen(["blkid", "-o", "value", "-s", "UUID", boot_device]).readlines.first.chomp
129   grub_entry = "gnulinux-advanced-#{boot_uuid}>gnulinux-#{kernel_version}-advanced-#{boot_uuid}"
130 else
131   grub_entry = "0"
132 end
133
134 if File.exist?("/etc/default/grub")
135   execute "update-grub" do
136     action :nothing
137     command "/usr/sbin/update-grub"
138   end
139
140   template "/etc/default/grub" do
141     source "grub.erb"
142     owner "root"
143     group "root"
144     mode 0o644
145     variables :units => units, :entry => grub_entry
146     notifies :run, "execute[update-grub]"
147   end
148 end
149
150 execute "update-initramfs" do
151   action :nothing
152   command "update-initramfs -u -k all"
153   user "root"
154   group "root"
155 end
156
157 template "/etc/initramfs-tools/conf.d/mdadm" do
158   source "initramfs-mdadm.erb"
159   owner "root"
160   group "root"
161   mode 0o644
162   notifies :run, "execute[update-initramfs]"
163 end
164
165 package "haveged"
166 service "haveged" do
167   action [:enable, :start]
168 end
169
170 package "ipmitool" if node[:kernel][:modules].include?("ipmi_si")
171
172 package "irqbalance"
173
174 template "/etc/default/irqbalance" do
175   source "irqbalance.erb"
176   owner "root"
177   group "root"
178   mode 0o644
179 end
180
181 service "irqbalance" do
182   action [:start, :enable]
183   supports :status => false, :restart => true, :reload => false
184   subscribes :restart, "template[/etc/default/irqbalance]"
185 end
186
187 # Link Layer Discovery Protocol Daemon
188 package "lldpd"
189 service "lldpd" do
190   action [:start, :enable]
191   supports :status => true, :restart => true, :reload => true
192 end
193
194 tools_packages = []
195 status_packages = {}
196
197 node[:kernel][:modules].each_key do |modname|
198   case modname
199   when "cciss"
200     tools_packages << "ssacli"
201     status_packages["cciss-vol-status"] ||= []
202   when "hpsa"
203     tools_packages << "ssacli"
204     status_packages["cciss-vol-status"] ||= []
205   when "mptsas"
206     tools_packages << "lsiutil"
207     status_packages["mpt-status"] ||= []
208   when "mpt2sas", "mpt3sas"
209     tools_packages << "sas2ircu"
210     status_packages["sas2ircu-status"] ||= []
211   when "megaraid_mm"
212     tools_packages << "megactl"
213     status_packages["megaraid-status"] ||= []
214   when "megaraid_sas"
215     tools_packages << "megacli"
216     status_packages["megaclisas-status"] ||= []
217   when "aacraid"
218     tools_packages << "arcconf"
219     status_packages["aacraid-status"] ||= []
220   when "arcmsr"
221     tools_packages << "areca"
222   end
223 end
224
225 node[:block_device].each do |name, attributes|
226   next unless attributes[:vendor] == "HP" && attributes[:model] == "LOGICAL VOLUME"
227
228   if name =~ /^cciss!(c[0-9]+)d[0-9]+$/
229     status_packages["cciss-vol-status"] |= ["cciss/#{Regexp.last_match[1]}d0"]
230   else
231     Dir.glob("/sys/block/#{name}/device/scsi_generic/*").each do |sg|
232       status_packages["cciss-vol-status"] |= [File.basename(sg)]
233     end
234   end
235 end
236
237 %w[ssacli lsiutil sas2ircu megactl megacli arcconf].each do |tools_package|
238   if tools_packages.include?(tools_package)
239     package tools_package
240   else
241     package tools_package do
242       action :purge
243     end
244   end
245 end
246
247 if tools_packages.include?("areca")
248   include_recipe "git"
249
250   git "/opt/areca" do
251     action :sync
252     repository "https://git.openstreetmap.org/private/areca.git"
253     user "root"
254     group "root"
255   end
256 else
257   directory "/opt/areca" do
258     action :delete
259     recursive true
260   end
261 end
262
263 if status_packages.include?("cciss-vol-status")
264   template "/usr/local/bin/cciss-vol-statusd" do
265     source "cciss-vol-statusd.erb"
266     owner "root"
267     group "root"
268     mode 0o755
269     notifies :restart, "service[cciss-vol-statusd]"
270   end
271
272   systemd_service "cciss-vol-statusd" do
273     description "Check cciss_vol_status values in the background"
274     exec_start "/usr/local/bin/cciss-vol-statusd"
275     private_tmp true
276     protect_system "full"
277     protect_home true
278     no_new_privileges true
279     notifies :restart, "service[cciss-vol-statusd]"
280   end
281 end
282
283 %w[cciss-vol-status mpt-status sas2ircu-status megaraid-status megaclisas-status aacraid-status].each do |status_package|
284   if status_packages.include?(status_package)
285     package status_package
286
287     template "/etc/default/#{status_package}d" do
288       source "raid.default.erb"
289       owner "root"
290       group "root"
291       mode 0o644
292       variables :devices => status_packages[status_package]
293     end
294
295     service "#{status_package}d" do
296       action [:start, :enable]
297       supports :status => false, :restart => true, :reload => false
298       subscribes :restart, "template[/etc/default/#{status_package}d]"
299     end
300   else
301     package status_package do
302       action :purge
303     end
304
305     file "/etc/default/#{status_package}d" do
306       action :delete
307     end
308   end
309 end
310
311 disks = if node[:hardware][:disk]
312           node[:hardware][:disk][:disks]
313         else
314           []
315         end
316
317 # intel_ssds = disks.select { |d| d[:vendor] == "INTEL" && d[:model] =~ /^SSD/ }
318 #
319 # nvmes = if node[:hardware][:pci]
320 #           node[:hardware][:pci].values.select { |pci| pci[:driver] == "nvme" }
321 #         else
322 #           []
323 #         end
324 #
325 # intel_nvmes = nvmes.select { |pci| pci[:vendor_name] == "Intel Corporation" }
326 #
327 # if !intel_ssds.empty? || !intel_nvmes.empty?
328 #   package "unzip"
329 #
330 #   intel_ssd_tool_version = "3.0.21"
331 #
332 #   remote_file "#{Chef::Config[:file_cache_path]}/Intel_SSD_Data_Center_Tool_#{intel_ssd_tool_version}_Linux.zip" do
333 #     source "https://downloadmirror.intel.com/29115/eng/Intel_SSD_Data_Center_Tool_#{intel_ssd_tool_version}_Linux.zip"
334 #   end
335 #
336 #   execute "#{Chef::Config[:file_cache_path]}/Intel_SSD_Data_Center_Tool_#{intel_ssd_tool_version}_Linux.zip" do
337 #     command "unzip Intel_SSD_Data_Center_Tool_#{intel_ssd_tool_version}_Linux.zip isdct_#{intel_ssd_tool_version}-1_amd64.deb"
338 #     cwd Chef::Config[:file_cache_path]
339 #     user "root"
340 #     group "root"
341 #     not_if { File.exist?("#{Chef::Config[:file_cache_path]}/isdct_#{intel_ssd_tool_version}-1_amd64.deb") }
342 #   end
343 #
344 #   dpkg_package "isdct" do
345 #     version "#{intel_ssd_tool_version}-1"
346 #     source "#{Chef::Config[:file_cache_path]}/isdct_#{intel_ssd_tool_version}-1_amd64.deb"
347 #   end
348 # end
349
350 disks = disks.map do |disk|
351   next if disk[:state] == "spun_down" || %w[unconfigured failed].any?(disk[:status])
352
353   if disk[:smart_device]
354     controller = node[:hardware][:disk][:controllers][disk[:controller]]
355
356     if controller && controller[:device]
357       device = controller[:device].sub("/dev/", "")
358       smart = disk[:smart_device]
359
360       if device.start_with?("cciss/") && smart =~ /^cciss,(\d+)$/
361         array = node[:hardware][:disk][:arrays][disk[:arrays].first]
362         munin = "cciss-3#{array[:wwn]}-#{Regexp.last_match(1)}"
363       elsif smart =~ /^.*,(\d+)$/
364         munin = "#{device}-#{Regexp.last_match(1)}"
365       elsif smart =~ %r{^.*,(\d+)/(\d+)$}
366         munin = "#{device}-#{Regexp.last_match(1)}:#{Regexp.last_match(2)}"
367       end
368     end
369   elsif disk[:device] =~ %r{^/dev/(nvme\d+)n\d+$}
370     device = Regexp.last_match(1)
371     munin = device
372   elsif disk[:device]
373     device = disk[:device].sub("/dev/", "")
374     munin = device
375   end
376
377   next if device.nil?
378
379   Hash[
380     :device => device,
381     :smart => smart,
382     :munin => munin,
383     :hddtemp => munin.tr("-:", "_")
384   ]
385 end
386
387 disks = disks.compact.uniq
388
389 if disks.count.positive?
390   package "smartmontools"
391
392   template "/usr/local/bin/smartd-mailer" do
393     source "smartd-mailer.erb"
394     owner "root"
395     group "root"
396     mode 0o755
397   end
398
399   template "/etc/smartd.conf" do
400     source "smartd.conf.erb"
401     owner "root"
402     group "root"
403     mode 0o644
404     variables :disks => disks
405   end
406
407   template "/etc/default/smartmontools" do
408     source "smartmontools.erb"
409     owner "root"
410     group "root"
411     mode 0o644
412   end
413
414   service "smartd" do
415     action [:enable, :start]
416     subscribes :reload, "template[/etc/smartd.conf]"
417     subscribes :restart, "template[/etc/default/smartmontools]"
418   end
419
420   # Don't try and do munin monitoring of disks behind
421   # an Areca controller as they only allow one thing to
422   # talk to the controller at a time and smartd will
423   # throw errors if it clashes with munin
424   disks = disks.reject { |disk| disk[:smart]&.start_with?("areca,") }
425
426   disks.each do |disk|
427     munin_plugin "smart_#{disk[:munin]}" do
428       target "smart_"
429       conf "munin.smart.erb"
430       conf_variables :disk => disk
431     end
432   end
433 else
434   service "smartd" do
435     action [:stop, :disable]
436   end
437 end
438
439 if disks.count.positive?
440   munin_plugin "hddtemp_smartctl" do
441     conf "munin.hddtemp.erb"
442     conf_variables :disks => disks
443   end
444 else
445   munin_plugin "hddtemp_smartctl" do
446     action :delete
447     conf "munin.hddtemp.erb"
448   end
449 end
450
451 plugins = Dir.glob("/etc/munin/plugins/smart_*").map { |p| File.basename(p) } -
452           disks.map { |d| "smart_#{d[:munin]}" }
453
454 plugins.each do |plugin|
455   munin_plugin plugin do
456     action :delete
457     conf "munin.smart.erb"
458   end
459 end
460
461 if File.exist?("/etc/mdadm/mdadm.conf")
462   mdadm_conf = edit_file "/etc/mdadm/mdadm.conf" do |line|
463     line.gsub!(/^MAILADDR .*$/, "MAILADDR admins@openstreetmap.org")
464
465     line
466   end
467
468   file "/etc/mdadm/mdadm.conf" do
469     owner "root"
470     group "root"
471     mode 0o644
472     content mdadm_conf
473   end
474
475   service "mdadm" do
476     action :nothing
477     subscribes :restart, "file[/etc/mdadm/mdadm.conf]"
478   end
479 end
480
481 template "/etc/modules" do
482   source "modules.erb"
483   owner "root"
484   group "root"
485   mode 0o644
486 end
487
488 service "kmod" do
489   action :nothing
490   subscribes :start, "template[/etc/modules]"
491 end
492
493 if node[:hardware][:watchdog]
494   package "watchdog"
495
496   template "/etc/default/watchdog" do
497     source "watchdog.erb"
498     owner "root"
499     group "root"
500     mode 0o644
501     variables :module => node[:hardware][:watchdog]
502   end
503
504   service "watchdog" do
505     action [:enable, :start]
506   end
507 end
508
509 unless Dir.glob("/sys/class/hwmon/hwmon*").empty?
510   package "lm-sensors"
511
512   Dir.glob("/sys/devices/platform/coretemp.*").each do |coretemp|
513     cpu = File.basename(coretemp).sub("coretemp.", "").to_i
514     chip = format("coretemp-isa-%04d", cpu)
515
516     temps = if File.exist?("#{coretemp}/name")
517               Dir.glob("#{coretemp}/temp*_input").map do |temp|
518                 File.basename(temp).sub("temp", "").sub("_input", "").to_i
519               end.sort
520             else
521               Dir.glob("#{coretemp}/hwmon/hwmon*/temp*_input").map do |temp|
522                 File.basename(temp).sub("temp", "").sub("_input", "").to_i
523               end.sort
524             end
525
526     if temps.first == 1
527       node.default[:hardware][:sensors][chip][:temps][:temp1][:label] = "CPU #{cpu}"
528       temps.shift
529     end
530
531     temps.each_with_index do |temp, index|
532       node.default[:hardware][:sensors][chip][:temps]["temp#{temp}"][:label] = "CPU #{cpu} Core #{index}"
533     end
534   end
535
536   execute "/etc/sensors.d/chef.conf" do
537     action :nothing
538     command "/usr/bin/sensors -s"
539     user "root"
540     group "root"
541   end
542
543   template "/etc/sensors.d/chef.conf" do
544     source "sensors.conf.erb"
545     owner "root"
546     group "root"
547     mode 0o644
548     notifies :run, "execute[/etc/sensors.d/chef.conf]"
549   end
550 end
551
552 if node[:hardware][:shm_size]
553   execute "remount-dev-shm" do
554     action :nothing
555     command "/bin/mount -o remount /dev/shm"
556     user "root"
557     group "root"
558   end
559
560   mount "/dev/shm" do
561     action :enable
562     device "tmpfs"
563     fstype "tmpfs"
564     options "rw,nosuid,nodev,size=#{node[:hardware][:shm_size]}"
565     notifies :run, "execute[remount-dev-shm]"
566   end
567 end