Skip to content

Commit eea653d

Browse files
author
Dylan Ratcliffe
committed
Added tasks and plans
1 parent aa5bb5c commit eea653d

20 files changed

+1005
-1
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@
33
Gemfile.lock
44
.bundle/
55
.fixtures/modules
6-
.fixtures/manifests
6+
.fixtures/manifests
7+
spec/fixtures

functions/bytes_to_size.pp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
function lvm::bytes_to_size (
2+
Numeric $size,
3+
) {
4+
$units = {
5+
'k' => 1024,
6+
'm' => 1048576,
7+
'g' => 1073741824,
8+
't' => 1099511627776,
9+
'p' => 1.12589991e15,
10+
'e' => 1.1529215e18,
11+
}
12+
$remaining_units = $units.filter |$name, $number| {
13+
# Run all the calculation and return only units that are greater than one
14+
$size_as_unit = $size / $number
15+
$size_as_unit >= 1
16+
}
17+
18+
# Use the last unit
19+
$largest_unit = $remaining_units.keys[-1]
20+
21+
$value = ($size / $units[$largest_unit])
22+
23+
# # Return the string
24+
"${value}${largest_unit}"
25+
}

functions/size_to_bytes.pp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
function lvm::size_to_bytes (
2+
String $size,
3+
) {
4+
$units = {
5+
'K' => 1024,
6+
'M' => 1048576,
7+
'G' => 1073741824,
8+
'T' => 1099511627776,
9+
'P' => 1.12589991e15,
10+
'E' => 1.1529215e18,
11+
}
12+
# Check if the size is valid and if so, extract the units
13+
if $size =~ /^([0-9]+(\.[0-9]+)?)([KMGTPEkmgtpe])/ {
14+
$unit = String($3, '%u') # Store the units in uppercase
15+
$number = Float($1) # Store the number as a float
16+
17+
# Multiply the number by the units to get bytes
18+
$number * $units[$unit]
19+
} else {
20+
fail("${size} is not a valid LVM size")
21+
}
22+
}

plans/expand.pp

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
# lvm::expand
2+
#
3+
# This plan implements an opinionated method for expanding storage on servers
4+
# that use LVM. If this doesn't fit your needs, simply tie the tasks together
5+
# in some way that does.
6+
#
7+
# @param server The target for the plan
8+
# @param volume_group The volume group to which the logical volume belongs
9+
# @param logical_volume The logical volume which is to be expanded
10+
# @param additional_size How much size to add to the LV. This should be
11+
# specified in LVM format i.e. "200m" or "2.5g"
12+
# @param disks Any physical disks that should be added to the volume group as
13+
# part of the expand process
14+
# @param resize_fs Wheather or not to resize the filesystem
15+
plan lvm::expand (
16+
String $server,
17+
String $volume_group,
18+
String $logical_volume,
19+
String $additional_size,
20+
Array[String] $disks = [],
21+
Boolean $resize_fs = true,
22+
) {
23+
$targets = get_targets($server)
24+
25+
# Fail if we are trying to run on many servers
26+
if $targets.length > 1 {
27+
fail('This plan should only be run against one server at a time')
28+
}
29+
30+
# The target should be the first server
31+
$target = $targets[0]
32+
33+
# Refresh facts for this server. Ideally we would call directly to the
34+
# `facts` plan. But there seems to be a bug preventing this.
35+
$result_set = run_task('facts::ruby', $target, '_catch_errors' => true)
36+
37+
$result_set.each |$result| {
38+
# Store facts for nodes from which they were succefully retrieved
39+
if ($result.ok) {
40+
add_facts($result.target, $result.value)
41+
}
42+
}
43+
44+
unless $disks.empty {
45+
# If we have passed disks then we want to create a PV for each of these
46+
# disks, then add them to the LV
47+
$disks.each |$disk| {
48+
# Ensure that the PV exists
49+
run_task('lvm::ensure_pv', $target, {
50+
'ensure' => 'present',
51+
'name' => $disk,
52+
})
53+
}
54+
55+
# Extend the volume group to also contain the new disks
56+
run_task('lvm::extend_vg', $target, {
57+
'volume_group' => $volume_group,
58+
'physical_volumes' => $disks,
59+
})
60+
}
61+
62+
# Now we need to extend the logical volume
63+
# Get the current size in bytes
64+
$current_size_bytes = lvm::size_to_bytes($target.facts['logical_volumes'][$logical_volume]['size'])
65+
# Get the additonal size in bytes
66+
$additional_size_bytes = lvm::size_to_bytes($additional_size)
67+
# Add them together
68+
$new_size_bytes = $current_size_bytes + $additional_size_bytes
69+
# Convert back to a fromat that LVM wants i.e. "150g"
70+
$new_size = lvm::bytes_to_size($new_size_bytes)
71+
72+
$expand_result = run_task('lvm::ensure_lv', $target, {
73+
'ensure' => 'present',
74+
'name' => $logical_volume,
75+
'volume_group' => $volume_group,
76+
'size' => $new_size,
77+
'resize_fs' => true,
78+
})
79+
80+
return $expand_result.first.message
81+
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
require 'spec_helper'
2+
3+
describe 'lvm::bytes_to_size' do
4+
context 'with incorrect parameters' do
5+
it 'should fail' do
6+
is_expected.to run.with_params('foo').and_raise_error(
7+
ArgumentError,
8+
/expects a Numeric value/
9+
)
10+
end
11+
end
12+
13+
context 'with lower case parameters' do
14+
it 'should return the correct values' do
15+
is_expected.to run.with_params(1024).and_return('1k')
16+
is_expected.to run.with_params(1_048_576).and_return('1m')
17+
is_expected.to run.with_params(1_073_741_824).and_return('1g')
18+
is_expected.to run.with_params(1_099_511_627_776).and_return('1t')
19+
is_expected.to run.with_params(1.12589991e15).and_return('1.0p')
20+
is_expected.to run.with_params(1.1529215e18).and_return('1.0e')
21+
is_expected.to run.with_params(214_748_364_800).and_return('200g')
22+
is_expected.to run.with_params(1536.0).and_return('1.5k')
23+
end
24+
end
25+
end
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
require 'spec_helper'
2+
3+
describe 'lvm::size_to_bytes' do
4+
context 'with incorrect parameters' do
5+
it 'should fail' do
6+
is_expected.to run.with_params('foo').and_raise_error(
7+
Puppet::PreformattedError,
8+
/foo is not a valid LVM size/
9+
)
10+
end
11+
end
12+
13+
context 'with lower case parameters' do
14+
it 'should return the correct values' do
15+
is_expected.to run.with_params('1k').and_return(1024)
16+
is_expected.to run.with_params('1m').and_return(1_048_576)
17+
is_expected.to run.with_params('1g').and_return(1_073_741_824)
18+
is_expected.to run.with_params('1t').and_return(1_099_511_627_776)
19+
is_expected.to run.with_params('1.0p').and_return(1.12589991e15)
20+
is_expected.to run.with_params('1.0e').and_return(1.1529215e18)
21+
is_expected.to run.with_params('200.0g').and_return(214_748_364_800.0)
22+
is_expected.to run.with_params('1.5k').and_return(1536.0)
23+
end
24+
end
25+
end

tasks/ensure_fs.json

Lines changed: 122 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,122 @@
1+
{
2+
"description": "Ensures settings on a filesystem using the type & provider",
3+
"input_method": "stdin",
4+
"parameters": {
5+
"fs_type": {
6+
"description": "The file system type. eg. ext3.",
7+
"type": "String"
8+
},
9+
"name": {
10+
"description": "Fully qualified name",
11+
"type": "String"
12+
},
13+
"mkfs_cmd": {
14+
"description": "Command to use to create the file system. Defaults to mkswap for fs_type=swap, otherwise mkfs.{{fs_type}}",
15+
"type": "Optional[String]"
16+
},
17+
"options": {
18+
"description": "Params for the mkfs command. eg. -l internal,agcount=x",
19+
"type": "Optional[String]"
20+
},
21+
"initial_size": {
22+
"description": "Initial size of the filesystem, Used only for resource creation, when using this option Puppet will not manage or maintain the size. To resize filesystems see the size property. AIX only.",
23+
"type": "Optional[String]"
24+
},
25+
"size": {
26+
"description": "Configures the size of the filesystem. Supports filesystem resizing. The size will be rounded up to the nearest multiple of the partition size. AIX only.",
27+
"type": "Optional[String]"
28+
},
29+
"ag_size": {
30+
"description": "Specify the allocation group size in megabytes, AIX only.",
31+
"type": "Optional[Integer]"
32+
},
33+
"large_files": {
34+
"description": "Large file enabled file system. AIX only",
35+
"type": "Optional[Boolean]"
36+
},
37+
"compress": {
38+
"description": "Data compression, LZ or no. AIX only",
39+
"type": "Optional[Enum[LG,no]]"
40+
},
41+
"frag": {
42+
"description": "JFS fragment size in bytes. AIX only",
43+
"type": "Optional[Integer]"
44+
},
45+
"nbpi": {
46+
"description": "Bytes per inode. AIX only",
47+
"type": "Optional[Integer]"
48+
},
49+
"logname": {
50+
"description": "Configure the log logical volume. AIX only",
51+
"type": "Optional[String]"
52+
},
53+
"logsize": {
54+
"description": "Size for an inline log in MB, AIX only",
55+
"type": "Optional[Integer]"
56+
},
57+
"maxext": {
58+
"description": "Size of a file extent in file system blocks, AIX only",
59+
"type": "Optional[Integer]"
60+
},
61+
"mountguard": {
62+
"description": "Enable the mountguard. AIX only",
63+
"type": "Optional[Boolean]"
64+
},
65+
"agblksize": {
66+
"description": "JFS2 block size in bytes, AIX only",
67+
"type": "Optional[Integer]"
68+
},
69+
"extended_attributes": {
70+
"description": "Format to be used to store extended attributes. AIX only",
71+
"type": "Optional[Enum[v1,v2]]"
72+
},
73+
"encrypted": {
74+
"description": "Specify and encrypted filesystem. AIX only",
75+
"type": "Optional[Boolean]"
76+
},
77+
"isnapshot": {
78+
"description": "Specify whether the filesystem supports internal snapshots, AIX only",
79+
"type": "Optional[Boolean]"
80+
},
81+
"mount_options": {
82+
"description": "Specify the options to be passed to the mount command. AIX only",
83+
"type": "Optional[String]"
84+
},
85+
"vix": {
86+
"description": "Specify that the file system can allocate inode extents smaller than the default, AIX only",
87+
"type": "Optional[Boolean]"
88+
},
89+
"log_partitions": {
90+
"description": "Specify the size of the log logical volume as number of logical partitions, AIX only",
91+
"type": "Optional[String]"
92+
},
93+
"nodename": {
94+
"description": "Specify the remote host where the filesystem resides. AIX only",
95+
"type": "Optional[String]"
96+
},
97+
"accounting": {
98+
"description": "Specify accounting subsystem support, AIX only",
99+
"type": "Optional[Boolean]"
100+
},
101+
"mountgroup": {
102+
"description": "Mount group for the filesystem, AIX only",
103+
"type": "Optional[String]"
104+
},
105+
"atboot": {
106+
"description": "Specify whether the file system is mounted at boot time, AIX only",
107+
"type": "Optional[Boolean]"
108+
},
109+
"perms": {
110+
"description": "Permissions for the filesystem, AIX only",
111+
"type": "Optional[Enum[ro,rw]]"
112+
},
113+
"device": {
114+
"description": "Device to create the filesystem on, this can be a device or a logical volume. AIX only",
115+
"type": "Optional[String]"
116+
},
117+
"volume_group": {
118+
"description": "Volume group that the file system should be greated on. AIX only.",
119+
"type": "Optional[String]"
120+
}
121+
}
122+
}

tasks/ensure_fs.rb

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
#!/opt/puppetlabs/puppet/bin/ruby
2+
require 'json'
3+
require 'puppet'
4+
5+
# Parse the parameters
6+
params = JSON.parse(STDIN.read)
7+
8+
# Load all of Puppet's settings
9+
Puppet.initialize_settings
10+
11+
# Set Puppet's user to root
12+
Puppet.settings[:user] = '0'
13+
Puppet.settings[:group] = '0'
14+
15+
# Create an empty resource object
16+
filesystem = Puppet::Resource.new(
17+
"Filesystem[#{params['name']}]"
18+
)
19+
20+
# Prune parameters that we don't need
21+
filesystem.prune_parameters
22+
23+
# Set the settings we need
24+
filesystem[:ensure] = params['ensure'] if params['ensure']
25+
filesystem[:fs_type] = params['fs_type'] if params['fs_type']
26+
filesystem[:name] = params['name'] if params['name']
27+
filesystem[:mkfs_cmd] = params['mkfs_cmd'] if params['mkfs_cmd']
28+
filesystem[:options] = params['options'] if params['options']
29+
filesystem[:initial_size] = params['initial_size'] if params['initial_size']
30+
filesystem[:size] = params['size'] if params['size']
31+
filesystem[:ag_size] = params['ag_size'] if params['ag_size']
32+
filesystem[:large_files] = params['large_files'] if params['large_files']
33+
filesystem[:compress] = params['compress'] if params['compress']
34+
filesystem[:frag] = params['frag'] if params['frag']
35+
filesystem[:nbpi] = params['nbpi'] if params['nbpi']
36+
filesystem[:logname] = params['logname'] if params['logname']
37+
filesystem[:logsize] = params['logsize'] if params['logsize']
38+
filesystem[:maxext] = params['maxext'] if params['maxext']
39+
filesystem[:mountguard] = params['mountguard'] if params['mountguard']
40+
filesystem[:agblksize] = params['agblksize'] if params['agblksize']
41+
filesystem[:extended_attributes] = params['extended_attributes'] if params['extended_attributes']
42+
filesystem[:encrypted] = params['encrypted'] if params['encrypted']
43+
filesystem[:isnapshot] = params['isnapshot'] if params['isnapshot']
44+
filesystem[:mount_options] = params['mount_options'] if params['mount_options']
45+
filesystem[:vix] = params['vix'] if params['vix']
46+
filesystem[:log_partitions] = params['log_partitions'] if params['log_partitions']
47+
filesystem[:nodename] = params['nodename'] if params['nodename']
48+
filesystem[:accounting] = params['accounting'] if params['accounting']
49+
filesystem[:mountgroup] = params['mountgroup'] if params['mountgroup']
50+
filesystem[:atboot] = params['atboot'] if params['atboot']
51+
filesystem[:perms] = params['perms'] if params['perms']
52+
filesystem[:device] = params['device'] if params['device']
53+
filesystem[:volume_group] = params['volume_group'] if params['volume_group']
54+
55+
# Save the result
56+
_resource, report = Puppet::Resource.indirection.save(filesystem)
57+
58+
# Print the logs
59+
resource_status = report.resource_statuses.values[0]
60+
61+
exit_code = if resource_status.failed
62+
1
63+
else
64+
0
65+
end
66+
67+
if resource_status.events.empty?
68+
puts 'unchanged'
69+
else
70+
report.logs.each do |log|
71+
puts log.to_report
72+
end
73+
end
74+
75+
exit exit_code

0 commit comments

Comments
 (0)