1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
|
#
# 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_relative "../resource"
class Chef
class Resource
class SwapFile < Chef::Resource
resource_name :swap_file
provides(:swap_file) { true }
description "Use the swap_file resource to create or delete swap files on Linux systems, and optionally to manage the swappiness configuration for a host."
introduced "14.0"
property :path, String,
description: "The path where the swap file will be created on the system if it differs from the resource block's name.",
name_property: true
property :size, Integer,
description: "The size (in MBs) of the swap file."
property :persist, [TrueClass, FalseClass],
description: "Persist the swapon.",
default: false
property :timeout, Integer,
description: "Timeout for 'dd' / 'fallocate' commands.",
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
declare_resource(:sysctl, "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
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
|