]> git.openstreetmap.org Git - chef.git/blob - cookbooks/hardware/recipes/default.rb
Simplify kernel pinning
[chef.git] / cookbooks / hardware / recipes / default.rb
1 #
2 # Cookbook Name:: 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 #     http://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 case node[:cpu][:"0"][:vendor_id]
24 when "GenuineIntel"
25   package "intel-microcode"
26 end
27
28 case node[:cpu][:"0"][:vendor_id]
29 when "AuthenticAMD"
30   if node[:lsb][:release].to_f >= 14.04
31     package "amd64-microcode"
32   end
33 end
34
35 if node[:dmi] && node[:dmi][:system]
36   case node[:dmi][:system][:manufacturer]
37   when "empty"
38     manufacturer = node[:dmi][:base_board][:manufacturer]
39     product = node[:dmi][:base_board][:product_name]
40   else
41     manufacturer = node[:dmi][:system][:manufacturer]
42     product = node[:dmi][:system][:product_name]
43   end
44 else
45   manufacturer = "Unknown"
46   product = "Unknown"
47 end
48
49 case manufacturer
50 when "HP"
51   package "hponcfg"
52   package "hp-health"
53   unit = "1"
54   speed = "115200"
55 when "TYAN"
56   unit = "0"
57   speed = "115200"
58 when "TYAN Computer Corporation"
59   unit = "0"
60   speed = "115200"
61 when "Supermicro"
62   case product
63   when "H8DGU", "X9SCD", "X7DBU", "X7DW3", "X9DR7/E-(J)LN4F", "X9DR3-F", "X9DRW"
64     unit = "1"
65     speed = "115200"
66   else
67     unit = "0"
68     speed = "115200"
69   end
70 when "IBM"
71   unit = "0"
72   speed = "115200"
73 end
74
75 if manufacturer == "HP" && node[:lsb][:release].to_f > 11.10
76   include_recipe "git"
77
78   git "/opt/hp/hp-legacy" do
79     action :sync
80     repository "git://chef.openstreetmap.org/hp-legacy.git"
81     user "root"
82     group "root"
83   end
84
85   link "/opt/hp/hp-health/bin/hpasmd" do
86     to "/opt/hp/hp-legacy/hpasmd"
87   end
88
89   link "/usr/lib/libhpasmintrfc.so.3.0" do
90     to "/opt/hp/hp-legacy/libhpasmintrfc.so.3.0"
91   end
92
93   link "/usr/lib/libhpasmintrfc.so.3" do
94     to "libhpasmintrfc.so.3.0"
95   end
96
97   link "/usr/lib/libhpasmintrfc.so" do
98     to "libhpasmintrfc.so.3.0"
99   end
100 end
101
102 unless unit.nil?
103   file "/etc/init/ttySttyS#{unit}.conf" do
104     action :delete
105   end
106
107   template "/etc/init/ttyS#{unit}.conf" do
108     source "tty.conf.erb"
109     owner "root"
110     group "root"
111     mode 0644
112     variables :unit => unit, :speed => speed
113   end
114
115   service "ttyS#{unit}" do
116     provider Chef::Provider::Service::Upstart
117     action [:enable, :start]
118     supports :status => true, :restart => true, :reload => false
119     subscribes :restart, "template[/etc/init/ttyS#{unit}.conf]"
120   end
121 end
122
123 # if we need a different / special kernel version to make the hardware
124 # work (e.g: https://github.com/openstreetmap/operations/issues/45) then
125 # ensure that we have the package installed. the grub template will
126 # make sure that this is the default on boot.
127 if node[:hardware][:grub][:kernel]
128   kernel_version = node[:hardware][:grub][:kernel]
129
130   package "linux-image-#{kernel_version}-generic"
131   package "linux-image-extra-#{kernel_version}-generic"
132   package "linux-headers-#{kernel_version}-generic"
133
134   boot_device = IO.popen(["df", "/boot"]).readlines.last.split.first
135   boot_uuid = IO.popen(["blkid", "-o", "value", "-s", "UUID", boot_device]).readlines.first.chomp
136   grub_entry = "gnulinux-advanced-#{boot_uuid}>gnulinux-#{kernel_version}-advanced-#{boot_uuid}"
137 else
138   grub_entry = "0"
139 end
140
141 if File.exist?("/etc/default/grub")
142   execute "update-grub" do
143     action :nothing
144     command "/usr/sbin/update-grub"
145   end
146
147   template "/etc/default/grub" do
148     source "grub.erb"
149     owner "root"
150     group "root"
151     mode 0644
152     variables :unit => unit, :speed => speed, :entry => grub_entry
153     notifies :run, "execute[update-grub]"
154   end
155 end
156
157 execute "update-initramfs" do
158   action :nothing
159   command "update-initramfs -u -k all"
160   user "root"
161   group "root"
162 end
163
164 template "/etc/initramfs-tools/conf.d/mdadm" do
165   source "initramfs-mdadm.erb"
166   owner "root"
167   group "root"
168   mode 0644
169   notifies :run, "execute[update-initramfs]"
170 end
171
172 package "haveged"
173 service "haveged" do
174   action [:enable, :start]
175 end
176
177 if node[:kernel][:modules].include?("ipmi_si")
178   package "ipmitool"
179 end
180
181 if node[:lsb][:release].to_f >= 12.10
182   package "irqbalance"
183
184   template "/etc/default/irqbalance" do
185     source "irqbalance.erb"
186     owner "root"
187     group "root"
188     mode 0644
189   end
190
191   service "irqbalance" do
192     action [:start, :enable]
193     supports :status => false, :restart => true, :reload => false
194     subscribes :restart, "template[/etc/default/irqbalance]"
195   end
196 end
197
198 tools_packages = []
199 status_packages = {}
200
201 node[:kernel][:modules].each_key do |modname|
202   case modname
203   when "cciss"
204     tools_packages << "hpacucli"
205     status_packages["cciss-vol-status"] ||= []
206   when "hpsa"
207     tools_packages << "hpacucli"
208     status_packages["cciss-vol-status"] ||= []
209   when "mptsas"
210     tools_packages << "lsiutil"
211     # status_packages["mpt-status"] ||= []
212   when "mpt2sas", "mpt3sas"
213     tools_packages << "sas2ircu"
214     status_packages["sas2ircu-status"] ||= []
215   when "megaraid_mm"
216     tools_packages << "megactl"
217     status_packages["megaraid-status"] ||= []
218   when "megaraid_sas"
219     tools_packages << "megacli"
220     status_packages["megaclisas-status"] ||= []
221   when "aacraid"
222     tools_packages << "arcconf"
223     status_packages["aacraid-status"] ||= []
224   when "arcmsr"
225     tools_packages << "areca"
226   end
227 end
228
229 node[:block_device].each do |name, attributes|
230   next unless attributes[:vendor] == "HP" && attributes[:model] == "LOGICAL VOLUME"
231
232   if name =~ /^cciss!(c[0-9]+)d[0-9]+$/
233     status_packages["cciss-vol-status"] |= ["cciss/#{Regexp.last_match[1]}d0"]
234   else
235     Dir.glob("/sys/block/#{name}/device/scsi_generic/*").each do |sg|
236       status_packages["cciss-vol-status"] |= [File.basename(sg)]
237     end
238   end
239 end
240
241 %w(hpacucli lsiutil sas2ircu megactl megacli arcconf).each do |tools_package|
242   if tools_packages.include?(tools_package)
243     package tools_package
244   else
245     package tools_package do
246       action :purge
247     end
248   end
249 end
250
251 if tools_packages.include?("areca")
252   include_recipe "git"
253
254   git "/opt/areca" do
255     action :sync
256     repository "git://chef.openstreetmap.org/areca.git"
257     user "root"
258     group "root"
259   end
260 else
261   directory "/opt/areca" do
262     action :delete
263     recursive true
264   end
265 end
266
267 ["cciss-vol-status", "mpt-status", "sas2ircu-status", "megaraid-status", "megaclisas-status", "aacraid-status"].each do |status_package|
268   if status_packages.include?(status_package)
269     package status_package
270
271     template "/etc/default/#{status_package}d" do
272       source "raid.default.erb"
273       owner "root"
274       group "root"
275       mode 0644
276       variables :devices => status_packages[status_package]
277     end
278
279     service "#{status_package}d" do
280       action [:start, :enable]
281       supports :status => false, :restart => true, :reload => false
282       subscribes :restart, "template[/etc/default/#{status_package}d]"
283     end
284   else
285     package status_package do
286       action :purge
287     end
288
289     file "/etc/default/#{status_package}d" do
290       action :delete
291     end
292   end
293 end
294
295 disks = []
296
297 node[:block_device].each do |name, attributes|
298   disks << { :device => name } if attributes[:vendor] == "ATA"
299 end
300
301 if status_packages["cciss-vol-status"] && File.exist?("/usr/sbin/cciss_vol_status")
302   status_packages["cciss-vol-status"].each do |device|
303     IO.popen(["cciss_vol_status", "-V", "/dev/#{device}"]).each do |line|
304       disks << { :device => device, :driver => "cciss", :id => Regexp.last_match[1].to_i - 1 } if line =~ / bay ([0-9]+) +HP /
305     end
306   end
307 end
308
309 if status_packages["megaclisas-status"]
310   controller = 0
311
312   Dir.glob("/sys/class/scsi_host/host*") do |host|
313     driver = File.new("#{host}/proc_name").read.chomp
314
315     next unless driver == "megaraid_sas"
316
317     bus = host.sub("/sys/class/scsi_host/host", "")
318     device = File.basename(Dir.glob("/sys/bus/scsi/devices/#{bus}:*/scsi_generic/*").first)
319
320     IO.popen(["megacli", "-PDList", "-a#{controller}", "-NoLog"]).each do |line|
321       disks << { :device => device, :driver => "megaraid",  :id => Regexp.last_match[1] } if line =~ /^Device Id: ([0-9]+)$/
322
323       disks.pop if line =~ /^Firmware state: Hotspare, Spun down$/
324     end
325
326     controller += 1
327   end
328 end
329
330 if tools_packages.include?("lsiutil")
331   Dir.glob("/sys/class/scsi_host/host*") do |host|
332     driver = File.new("#{host}/proc_name").read.chomp
333
334     next unless driver == "mptsas"
335
336     bus = host.sub("/sys/class/scsi_host/host", "")
337
338     Dir.glob("/sys/bus/scsi/devices/#{bus}:0:*/scsi_generic/*").each do |sg|
339       disks << { :device => File.basename(sg) }
340     end
341   end
342 end
343
344 if status_packages["sas2ircu-status"]
345   Dir.glob("/sys/class/scsi_host/host*") do |host|
346     driver = File.new("#{host}/proc_name").read.chomp
347
348     next unless driver == "mpt2sas" || driver == "mpt3sas"
349
350     bus = host.sub("/sys/class/scsi_host/host", "")
351
352     Dir.glob("/sys/bus/scsi/devices/#{bus}:0:*/scsi_generic/*").each do |sg|
353       next if File.directory?("#{sg}/../../block")
354
355       disks << { :device => File.basename(sg) }
356     end
357   end
358 end
359
360 if status_packages["aacraid-status"]
361   Dir.glob("/sys/class/scsi_host/host*") do |host|
362     driver = File.new("#{host}/proc_name").read.chomp
363
364     next unless driver == "aacraid"
365
366     bus = host.sub("/sys/class/scsi_host/host", "")
367
368     Dir.glob("/sys/bus/scsi/devices/#{bus}:1:*/scsi_generic/*").each do |sg|
369       disks << { :device => File.basename(sg) }
370     end
371   end
372 end
373
374 if tools_packages.include?("areca") && File.exist?("/opt/areca/x86_64/cli64")
375   device = IO.popen(["lsscsi", "-g"]).grep(%r{Areca +RAID controller .*/dev/(sg[0-9]+)}) do
376     Regexp.last_match[1]
377   end.first
378
379   IO.popen(["/opt/areca/x86_64/cli64", "disk", "info"]).each do |line|
380     next if line =~ /N\.A\./
381
382     if line =~ /^ +[0-9]+ +0*([0-9]+) +(?:Slot#|SLOT )0*([0-9]+) +/
383       enc = Regexp.last_match[1]
384       slot = Regexp.last_match[2]
385
386       disks << { :device => device, :driver => "areca", :id => "#{slot}/#{enc}" }
387     elsif line =~ /^ +([0-9]+) +[0-9]+ +/
388       disks << { :device => device, :driver => "areca", :id => Regexp.last_match[1] }
389     end
390   end
391 end
392
393 disks.each do |disk|
394   if disk[:device] =~ %r{^cciss/(.*)$}
395     id = File.read("/sys/bus/cciss/devices/#{Regexp.last_match[1]}/unique_id").chomp
396
397     disk[:munin] = "cciss-3#{id.downcase}"
398   else
399     disk[:munin] = disk[:device]
400   end
401
402   if disk[:id]
403     disk[:munin] = "#{disk[:munin]}-#{disk[:id].to_s.tr('/', ':')}"
404   end
405
406   disk[:hddtemp] = disk[:munin].tr("-:", "_")
407 end
408
409 if disks.count > 0
410   package "smartmontools"
411
412   template "/etc/smartd.conf" do
413     source "smartd.conf.erb"
414     owner "root"
415     group "root"
416     mode 0644
417     variables :disks => disks
418     notifies :reload, "service[smartmontools]"
419   end
420
421   template "/etc/default/smartmontools" do
422     source "smartmontools.erb"
423     owner "root"
424     group "root"
425     mode 0644
426     notifies :restart, "service[smartmontools]"
427   end
428
429   service "smartmontools" do
430     action [:enable, :start]
431     supports :status => true, :restart => true, :reload => true
432   end
433
434   # Don't try and do munin monitoring of disks behind
435   # an Areca controller as they only allow one thing to
436   # talk to the controller at a time and smartd will
437   # throw errors if it clashes with munin
438   disks = disks.reject { |disk| disk[:driver] == "areca" }
439
440   disks.each do |disk|
441     munin_plugin "smart_#{disk[:munin]}" do
442       target "smart_"
443       conf "munin.smart.erb"
444       conf_variables :disk => disk
445     end
446   end
447
448   munin_plugin "hddtemp_smartctl" do
449     conf "munin.hddtemp.erb"
450     conf_variables :disks => disks
451   end
452 else
453   service "smartmontools" do
454     action [:stop, :disable]
455   end
456
457   munin_plugin "hddtemp_smartctl" do
458     action :delete
459   end
460 end
461
462 plugins = Dir.glob("/etc/munin/plugins/smart_*").map { |p| File.basename(p) } -
463           disks.map { |d| "smart_#{d[:munin]}" }
464
465 plugins.each do |plugin|
466   munin_plugin plugin do
467     action :delete
468   end
469 end
470
471 if File.exist?("/etc/mdadm/mdadm.conf")
472   mdadm_conf = edit_file "/etc/mdadm/mdadm.conf" do |line|
473     line.gsub!(/^MAILADDR .*$/, "MAILADDR admins@openstreetmap.org")
474
475     line
476   end
477
478   file "/etc/mdadm/mdadm.conf" do
479     owner "root"
480     group "root"
481     mode 0644
482     content mdadm_conf
483   end
484
485   service "mdadm" do
486     action :nothing
487     subscribes :restart, "file[/etc/mdadm/mdadm.conf]"
488   end
489 end
490
491 template "/etc/modules" do
492   source "modules.erb"
493   owner "root"
494   group "root"
495   mode 0644
496 end
497
498 if node[:lsb][:release].to_f <= 12.10
499   service "module-init-tools" do
500     provider Chef::Provider::Service::Upstart
501     action :nothing
502     subscribes :start, "template[/etc/modules]"
503   end
504 else
505   service "kmod" do
506     provider Chef::Provider::Service::Upstart
507     action :nothing
508     subscribes :start, "template[/etc/modules]"
509   end
510 end
511
512 if node[:hardware][:watchdog]
513   package "watchdog"
514
515   template "/etc/default/watchdog" do
516     source "watchdog.erb"
517     owner "root"
518     group "root"
519     mode 0644
520     variables :module => node[:hardware][:watchdog]
521   end
522
523   service "watchdog" do
524     action [:enable, :start]
525   end
526 end
527
528 unless Dir.glob("/sys/class/hwmon/hwmon*").empty?
529   package "lm-sensors"
530
531   execute "/etc/sensors.d/chef.conf" do
532     action :nothing
533     command "/usr/bin/sensors -s"
534     user "root"
535     group "root"
536   end
537
538   template "/etc/sensors.d/chef.conf" do
539     source "sensors.conf.erb"
540     owner "root"
541     group "root"
542     mode 0644
543     notifies :run, "execute[/etc/sensors.d/chef.conf]"
544   end
545 end