From efa9807bbcb592fcaf2ad559e1d7982f9cac147f Mon Sep 17 00:00:00 2001 From: Tim Smith Date: Wed, 14 Mar 2018 10:06:38 -0700 Subject: Add swap_file resource form the swap cookbook Copied as-is from the cookbook Signed-off-by: Tim Smith --- lib/chef/resource/swap_file.rb | 217 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 217 insertions(+) create mode 100644 lib/chef/resource/swap_file.rb (limited to 'lib/chef/resource/swap_file.rb') diff --git a/lib/chef/resource/swap_file.rb b/lib/chef/resource/swap_file.rb new file mode 100644 index 0000000000..813ad33113 --- /dev/null +++ b/lib/chef/resource/swap_file.rb @@ -0,0 +1,217 @@ +# +# Copyright 2012-2018, Seth Vargo +# Copyright 2017-2018, Chef Software, Inc. +# +# 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. +# + +require "chef/resource" + +class Chef + class Resource + class SwapFile < Chef::Resource + resource_name :swap_file + provides :swap_file + + description "Use the swap_file resource to create or delete swap files on Linux systems." + introduced "14.0" + + property :path, String, + description: "The path to put the swap file on the system.", + name_property: true + + property :size, Integer, + description: "The size (in MBs) for the swap file." + + property :persist, [TrueClass, FalseClass], + description: "Persist the swapon.", + default: false + + property :timeout, Integer, + description: "Timeout for dd/fallocate.", + default: 600 + + property :swappiness, Integer, + description: "The swappiness value to set on the system." + + action :create do + description "Create a swapfile." + + if swap_enabled? + Chef::Log.debug("#{new_resource} already created - nothing to do") + else + begin + Chef::Log.info "starting first create: #{node['virtualization']['system']}" + do_create(swap_creation_command) + rescue Mixlib::ShellOut::ShellCommandFailed => e + Chef::Log.warn("#{new_resource} Rescuing failed swapfile creation for #{new_resource.path}") + Chef::Log.debug("#{new_resource} Exception when creating swapfile #{new_resource.path}: #{e}") + do_create(dd_command) + end + end + if new_resource.swappiness + include_recipe "sysctl::default" + + sysctl_param "vm.swappiness" do + value new_resource.swappiness + end + end + end + + action :remove do + description "Remove a swapfile and disable swap." + + swapoff if swap_enabled? + remove_swapfile if ::File.exist?(new_resource.path) + end + + action_class do + def do_create(command) + create_swapfile(command) + set_permissions + mkswap + swapon + persist if persist? + end + + def create_swapfile(command) + converge_by "create empty swapfile at #{new_resource.path}" do # ~FC054 + shell_out!(command, timeout: new_resource.timeout) + end + end + + def set_permissions + permissions = "600" + converge_by "set permissions on #{new_resource.path} to #{permissions}" do + shell_out!("chmod #{permissions} #{new_resource.path}") + end + end + + def mkswap + converge_by "make #{new_resource.path} swappable" do + shell_out!("mkswap -f #{new_resource.path}") + end + end + + def swapon + converge_by "enable swap for #{new_resource.path}" do + shell_out!("swapon #{new_resource.path}") + end + end + + def swapoff + converge_by "turn off swap for #{new_resource.path}" do + shell_out!("swapoff #{new_resource.path}") + end + end + + def remove_swapfile + converge_by "remove swap file #{new_resource.path}" do + ::FileUtils.rm(new_resource.path) + end + end + + def swap_enabled? + enabled_swapfiles = shell_out("swapon --summary").stdout + # Regex for our resource path and only our resource path + # It will terminate on whitespace after the path it match + # /testswapfile would match + # /testswapfiledir/someotherfile will not + swapfile_regex = Regexp.new("^#{new_resource.path}[\\s\\t\\n\\f]+") + !swapfile_regex.match(enabled_swapfiles).nil? + end + + def swap_creation_command + command = if compatible_filesystem? && compatible_kernel && !docker? + fallocate_command + else + dd_command + end + Chef::Log.debug("#{new_resource} swap creation command is '#{command}'") + command + end + + def fallback_swap_creation_command + command = dd_command + Chef::Log.debug("#{new_resource} fallback swap creation command is '#{command}'") + command + end + + # The block size (1MB) + def block_size + 1_048_576 + end + + def fallocate_size + size = block_size * new_resource.size + Chef::Log.debug("#{new_resource} fallocate size is #{size}") + size + end + + def fallocate_command + size = fallocate_size + command = "fallocate -l #{size} #{new_resource.path}" + Chef::Log.debug("#{new_resource} fallocate command is '#{command}'") + command + end + + def dd_command + command = "dd if=/dev/zero of=#{new_resource.path} bs=#{block_size} count=#{new_resource.size}" + Chef::Log.debug("#{new_resource} dd command is '#{command}'") + command + end + + def compatible_kernel + fallocate_location = shell_out("which fallocate").stdout + Chef::Log.debug("#{new_resource} fallocate location is '#{fallocate_location}'") + ::File.exist?(fallocate_location.chomp) + end + + def compatible_filesystem? + compatible_filesystems = %w{xfs ext4} + parent_directory = ::File.dirname(new_resource.path) + # Get FS info, get second line as first is column headings + command = "df -PT #{parent_directory} | awk 'NR==2 {print $2}'" + result = shell_out(command).stdout + Chef::Log.debug("#{new_resource} filesystem listing is '#{result}'") + compatible_filesystems.any? { |fs| result.include? fs } + end + + # we can remove this when we only support Chef 13 + def docker?(node = run_context.nil? ? nil : run_context.node) + !!(node && node["virtualization"] && node["virtualization"]["systems"] && + node["virtualization"]["systems"]["docker"] && node["virtualization"]["systems"]["docker"] == "guest") + end + + def persist? + !!new_resource.persist + end + + def persist + fstab = "/etc/fstab" + contents = ::File.readlines(fstab) + addition = "#{new_resource.path} swap swap defaults 0 0" + + if contents.any? { |line| line.strip == addition } + Chef::Log.debug("#{new_resource} already added to /etc/fstab - skipping") + else + Chef::Log.info("#{new_resource} adding entry to #{fstab} for #{new_resource.path}") + + contents << "#{addition}\n" + ::File.open(fstab, "w") { |f| f.write(contents.join("")) } + end + end + end + end + end +end -- cgit v1.2.1