cookbooks/fb_grub/recipes/validate.rb (128 lines of code) (raw):
#
# Cookbook Name:: fb_grub
# Recipe:: validate
#
# Copyright (c) 2016-present, Facebook, Inc.
# All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#
whyrun_safe_ruby_block 'initialize_grub_locations' do
block do
bootdisk_guess = 'hd0'
if Pathname.new('/boot').mountpoint?
boot_device = node.device_of_mount('/boot')
boot_label = node.filesystem_data['by_mountpoint']['/boot']['label']
boot_uuid = node.filesystem_data['by_mountpoint']['/boot']['uuid']
node.default['fb_grub']['path_prefix'] = ''
else
boot_device = node.device_of_mount('/')
boot_label = node.filesystem_data['by_mountpoint']['/']['label']
boot_uuid = node.filesystem_data['by_mountpoint']['/']['uuid']
node.default['fb_grub']['path_prefix'] = '/boot'
end
if node['fb_grub']['use_labels'] && node['fb_grub']['use_uuids']
fail 'fb_grub: must choose one of use_labels or use_uuids'
end
if node['fb_grub']['use_labels']
if node['fb_grub']['version'] < 2
fail 'fb_grub: Booting by label requires grub2.'
end
node.default['fb_grub']['_root_label'] = boot_label
# For tboot, we have to specify the full path to the modules.
# They are in /usr/lib/grub , so we need the label for the root disk
slash_label = node.filesystem_data['by_mountpoint']['/']['label']
if slash_label
node.default['fb_grub']['_module_label'] = slash_label
end
elsif node['fb_grub']['use_uuids']
if node['fb_grub']['version'] < 2
fail 'fb_grub: Booting by label requires grub2.'
end
node.default['fb_grub']['_root_uuid'] = boot_uuid
slash_uuid = node.filesystem_data['by_mountpoint']['/']['uuid']
if slash_uuid
node.default['fb_grub']['_module_uuid'] = slash_uuid
end
else
# We are apparently not using labels, so we have to do some detective
# work. If something did put a .before_chef file in place, we will
# extract the root_device from it. If the file does not exist (e.g. on
# older existing systems), we will use our old heuristics.
original_grub_config = '/root/grub.before_chef'
if File.exist?(original_grub_config)
content = File.read(original_grub_config)
original_root_device = FB::Grub.extract_root_device(content)
original_device_hints = FB::Grub.extract_device_hints(content)
if original_root_device
node.default['fb_grub']['root_device'] = original_root_device
Chef::Log.debug(
"fb_grub: Re-using existing root device: #{original_root_device}",
)
node.default['fb_grub']['_device_hints'] = original_device_hints
Chef::Log.debug(
"fb_grub: Found #{original_device_hints.size} grub device hints.",
)
else
Chef::Log.warn(
"fb_grub: Can't parse grub config: #{original_grub_config}",
)
end
end
# If nothing has set the root_device so far, fall back to the old logic
# and set it by using the hardcoded boot_disk parameter
unless node['fb_grub']['root_device']
# This is the old, somewhat broken logic to use a hardcoded root
# udev block device partitions start at 1
# grub disks start at 0
if boot_device
m = boot_device.match(/[0-9]+$/)
unless m
fail 'fb_grub: cannot parse the boot device!'
end
else
fail 'fb_grub: cannot find the boot device!'
end
grub_partition = m[0].to_i
grub_partition -= 1 if node['fb_grub']['version'] < 2
# In case somebody has set an override, just take whatever they set
# otherwise just use the default and hope for the best.
boot_disk = node['fb_grub']['boot_disk'] || bootdisk_guess
root_device = "#{boot_disk},#{grub_partition}"
Chef::Log.info("fb_grub: Using old root device logic: #{root_device}")
node.default['fb_grub']['root_device'] = root_device
end
end
# some provisioning configurations do not properly label the root filesystem
# Ensure grub is put down with the label matching the fs mounted at / that
# has a valid uuid or label. This will skip over things like rootfs mounts.
node.default['fb_grub']['rootfs_arg'] = 'LABEL=/'
label = node.filesystem_data['by_mountpoint']['/']['label']
uuid = node.filesystem_data['by_mountpoint']['/']['uuid']
if label && !label.empty?
node.default['fb_grub']['rootfs_arg'] = "LABEL=#{label}"
elsif uuid && !uuid.empty?
node.default['fb_grub']['rootfs_arg'] = "UUID=#{uuid}"
end
# Set the correct grub module path for e.g. the tboot modules
if node.efi? && node['fb_grub']['version'] == 2 &&
node['fb_grub']['tboot']['enable']
if node['fb_grub']['_module_label']
module_path = "/usr/lib/grub/#{node['kernel']['machine']}-efi"
else
os_device = node.device_of_mount('/')
if os_device
m = os_device.match(/[0-9]+$/)
unless m
fail 'fb_grub: cannot parse the OS device!'
end
else
fail 'fb_grub: cannot find the OS device!'
end
# People can override the boot_disk if they have a good reason.
if node['fb_grub']['boot_disk']
boot_disk = node['fb_grub']['boot_disk']
elsif node['fb_grub']['root_device']
boot_disk = node['fb_grub']['root_device'].split(',')[0]
else
# This basically just happens if someone enables labels
# but doesn't override the boot_disk param and we don't use our new
# logic to figure out the boot disk
boot_disk = bootdisk_guess
end
os_part = "(#{boot_disk},#{m[0].to_i})"
module_path = "#{os_part}/usr/lib/grub/#{node['kernel']['machine']}-efi"
end
node.default['fb_grub']['_grub2_module_path'] = module_path
# So that we can use btrfs subvolumes and still insmod filesystems
if node.root_btrfs?
node.default['fb_grub']['_grub2_copy_path'] = node['fb_grub'][
'_grub2_module_path']
node.default['fb_grub']['_module_label'] = node['fb_grub'][
'_root_label']
node.default['fb_grub']['_grub2_module_path'] = node['fb_grub'][
'path_prefix']
end
end
node.default['fb_grub']['_decided_boot_disk'] = boot_disk
end
end
if node.root_btrfs?
mount_opts = FB::Fstab.get_base_mount_opts(node, '/')
# in this case we'd be &.'ing all the way down, so the unless is actually
# cleaner
# rubocop:disable Style/SafeNavigation
unless mount_opts.nil?
mount_opts.split(',').each do |opt|
if opt.include?('subvolid=') || opt.include?('subvol=')
node.default['fb_grub']['_rootflags'] = opt
end
end
end
# rubocop:enable Style/SafeNavigation
end