Implement sysfs values management resource
Change-Id: I84cd799630a5b930f7e22cad735205d122fa0fb1 Closes-Bug: 1456587 Signed-off-by: Sergii Golovatiuk <sgolovatiuk@mirantis.com>
This commit is contained in:
parent
47491e08d6
commit
5cde6f1668
|
@ -30,3 +30,20 @@ class { 'openstack::keepalive' :
|
|||
tcpka_intvl => '3',
|
||||
tcp_retries2 => '5',
|
||||
}
|
||||
|
||||
# increase network backlog for performance on fast networks
|
||||
sysctl::value { 'net.core.netdev_max_backlog': value => '261144' }
|
||||
|
||||
L2_port<||> -> Sysfs_config_value<||>
|
||||
L3_ifconfig<||> -> Sysfs_config_value<||>
|
||||
L3_route<||> -> Sysfs_config_value<||>
|
||||
|
||||
class { 'sysfs' :}
|
||||
|
||||
sysfs_config_value { 'rps_cpus' :
|
||||
ensure => 'present',
|
||||
name => "/etc/sysfs.d/rps_cpus.conf",
|
||||
value => cpu_affinity_hex($::processorcount),
|
||||
sysfs => '/sys/class/net/*/queues/rx-*/rps_cpus',
|
||||
exclude => '/sys/class/net/lo/*',
|
||||
}
|
||||
|
|
|
@ -30,4 +30,32 @@ class NetconfigPostTest < Test::Unit::TestCase
|
|||
assert TestCommon::Network.ping?(ip), "Cannot ping the master node '#{ip}'!"
|
||||
end
|
||||
|
||||
def processor_count
|
||||
File.read('/proc/cpuinfo').split("\n").count { |line| line.start_with? 'processor' }
|
||||
end
|
||||
|
||||
def hex_mask
|
||||
return @hex_mask if @hex_mask
|
||||
@hex_mask = ((2 ** processor_count) -1 ).to_s(16)
|
||||
end
|
||||
|
||||
def rps_cpus
|
||||
Dir.glob('/sys/class/net/*/queues/rx-*/rps_cpus').reject { |node| node.start_with? '/sys/class/net/lo' }
|
||||
end
|
||||
|
||||
def test_rps_cpus_set
|
||||
rps_cpus.each do |node|
|
||||
assert File.read(node).chomp.end_with?(hex_mask), "Sysfs node: '#{node}' is not '#{hex_mask}'!"
|
||||
end
|
||||
end
|
||||
|
||||
def test_rps_cpus_config
|
||||
assert File.exists?('/etc/sysfs.d/rps_cpus.conf'), 'RPS_CPUS sysfs config is missing!'
|
||||
rps_cpus.each do |line|
|
||||
line.gsub! %r(/sys/), ''
|
||||
line = "#{line} = #{hex_mask}"
|
||||
assert TestCommon::Config.has_line?('/etc/sysfs.d/rps_cpus.conf', line), "Line '#{line}' is missing in the rps_cpus.conf!"
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
|
|
@ -0,0 +1,80 @@
|
|||
#!/bin/bash
|
||||
#
|
||||
# sysfs Apply sysfs values from the config files
|
||||
#
|
||||
# Based on Debian 'sysfsutils' init script
|
||||
#
|
||||
# chkconfig: 345 15 85
|
||||
# description: Sets sysfs values from the config file to the system on boot
|
||||
### BEGIN INIT INFO
|
||||
# Short-Description: Apply sysfs values from the config files
|
||||
# Description: Apply sysfs values from the config files
|
||||
### END INIT INFO
|
||||
|
||||
. '/etc/init.d/functions'
|
||||
|
||||
if [ -f '/etc/sysconfig/sysfs' ]; then
|
||||
. '/etc/sysconfig/sysfs'
|
||||
fi
|
||||
|
||||
if [ -z "${CONFIG_FILE}" ]; then
|
||||
CONFIG_FILE='/etc/sysfs.conf'
|
||||
fi
|
||||
|
||||
if [ -z "${CONFIG_DIR}" ]; then
|
||||
CONFIG_DIR='/etc/sysfs.d'
|
||||
fi
|
||||
|
||||
load_conffile() {
|
||||
FILE="$1"
|
||||
echo "Load sysfs file: ${FILE}"
|
||||
sed 's/#.*$//; /^[[:space:]]*$/d;
|
||||
s/^[[:space:]]*\([^=[:space:]]*\)[[:space:]]*\([^=[:space:]]*\)[[:space:]]*=[[:space:]]*\(.*\)/\1 \2 \3/' \
|
||||
"${FILE}" | {
|
||||
while read f1 f2 f3; do
|
||||
if [ "$f1" = "mode" -a -n "$f2" -a -n "$f3" ]; then
|
||||
if [ -f "/sys/$f2" ] || [ -d "/sys/$f2" ]; then
|
||||
chmod "$f3" "/sys/$f2"
|
||||
else
|
||||
failure "unknown attribute $f2"
|
||||
fi
|
||||
elif [ "$f1" = "owner" -a -n "$f2" -a -n "$f3" ]; then
|
||||
if [ -f "/sys/$f2" ]; then
|
||||
chown "$f3" "/sys/$f2"
|
||||
else
|
||||
failure "unknown attribute $f2"
|
||||
fi
|
||||
elif [ "$f1" -a -n "$f2" -a -z "$f3" ]; then
|
||||
if [ -f "/sys/$f1" ]; then
|
||||
# Some fields need a terminating newline, others
|
||||
# need the terminating newline to be absent :-(
|
||||
echo -n "$f2" > "/sys/$f1" 2>/dev/null ||
|
||||
echo "$f2" > "/sys/$f1"
|
||||
else
|
||||
echo "unknown attribute $f1"
|
||||
fi
|
||||
else
|
||||
failure "syntax error: '$f1' '$f2' '$f3'"
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
}
|
||||
}
|
||||
|
||||
######################################################################
|
||||
|
||||
case "$1" in
|
||||
start|restart|reload)
|
||||
echo "Settings sysfs values..."
|
||||
for file in ${CONFIG_FILE} ${CONFIG_DIR}/*.conf; do
|
||||
[ -r "${file}" ] || continue
|
||||
load_conffile "${file}"
|
||||
done
|
||||
;;
|
||||
stop)
|
||||
exit 0
|
||||
;;
|
||||
*)
|
||||
echo "Usage: $0 {start|stop|restart|reload}"
|
||||
exit 2
|
||||
esac
|
|
@ -0,0 +1,10 @@
|
|||
module Puppet::Parser::Functions
|
||||
newfunction(:cpu_affinity_hex, :type => :rvalue, :doc => <<-EOS
|
||||
Generate a HEX value used to set network device rsp_cpus value
|
||||
EOS
|
||||
) do |argv|
|
||||
number = argv[0].to_i
|
||||
fail "Argument should be the CPU number - integer value!" unless number.to_s == argv[0]
|
||||
((2 ** number) - 1).to_s(16)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,140 @@
|
|||
require 'fileutils'
|
||||
require 'digest/md5'
|
||||
|
||||
Puppet::Type.type(:sysfs_config_value).provide(:ruby) do
|
||||
|
||||
def glob(path)
|
||||
Dir.glob path
|
||||
end
|
||||
|
||||
def included_sysfs_nodes
|
||||
return @included_sysfs_nodes if @included_sysfs_nodes
|
||||
return [] unless @resource[:sysfs]
|
||||
@included_sysfs_nodes = []
|
||||
return @included_sysfs_nodes unless @resource[:sysfs]
|
||||
@resource[:sysfs].each do |path|
|
||||
@included_sysfs_nodes += glob path
|
||||
end
|
||||
@included_sysfs_nodes
|
||||
end
|
||||
|
||||
def excluded_sysfs_nodes
|
||||
return @excluded_sysfs_nodes if @excluded_sysfs_nodes
|
||||
return [] unless @resource[:exclude]
|
||||
@excluded_sysfs_nodes = []
|
||||
return @excluded_sysfs_nodes unless @resource[:exclude]
|
||||
@resource[:exclude].each do |path|
|
||||
@excluded_sysfs_nodes += glob path
|
||||
end
|
||||
@excluded_sysfs_nodes
|
||||
end
|
||||
|
||||
def sysfs_nodes
|
||||
return @sysfs_nodes if @sysfs_nodes
|
||||
@sysfs_nodes = included_sysfs_nodes.reject do |included_node|
|
||||
excluded_sysfs_nodes.find do |excluded_node|
|
||||
included_node.start_with? excluded_node
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def sysfs_node_value(node)
|
||||
value = @resource[:value]
|
||||
if @resource[:value].is_a? Hash
|
||||
value = @resource[:value].fetch 'default', nil
|
||||
@resource[:value].each do |override_node, override_value|
|
||||
if node.include? override_node
|
||||
value = override_value
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
value
|
||||
end
|
||||
|
||||
def generate_file_content
|
||||
return unless @resource.generate_content?
|
||||
content = ''
|
||||
sysfs_nodes.each do |node|
|
||||
node = node.gsub %r(^/sys/), ''
|
||||
content += "#{node} = #{sysfs_node_value node}\n"
|
||||
end
|
||||
debug 'Generated config content'
|
||||
@resource[:content] = content
|
||||
end
|
||||
|
||||
def reset
|
||||
@included_sysfs_nodes = nil
|
||||
@excluded_sysfs_nodes = nil
|
||||
@sysfs_nodes = nil
|
||||
end
|
||||
|
||||
######################################################
|
||||
|
||||
def file_name
|
||||
@resource[:name]
|
||||
end
|
||||
|
||||
def file_base_dir
|
||||
File.dirname file_name
|
||||
end
|
||||
|
||||
def file_mkdir
|
||||
FileUtils.mkdir_p file_base_dir unless File.directory? file_base_dir
|
||||
fail "Could not create the base directory for file: '#{file_name}'" unless File.directory? file_base_dir
|
||||
end
|
||||
|
||||
def file_write(value)
|
||||
File.open(file_name, 'w') do |f|
|
||||
f.write value
|
||||
end
|
||||
fail "Error writing file: '#{file_name}'!" unless file_read == value
|
||||
end
|
||||
|
||||
def file_exists?
|
||||
File.file? file_name
|
||||
end
|
||||
|
||||
def file_remove
|
||||
File.delete file_name if file_exists?
|
||||
end
|
||||
|
||||
def file_read
|
||||
return unless file_exists?
|
||||
File.read file_name
|
||||
end
|
||||
|
||||
######################################################
|
||||
|
||||
def exists?
|
||||
debug 'Call: exists?'
|
||||
generate_file_content
|
||||
out = file_exists?
|
||||
debug "Return: '#{out}'"
|
||||
out
|
||||
end
|
||||
|
||||
def create
|
||||
debug 'Call: create'
|
||||
file_mkdir
|
||||
file_write @resource[:content]
|
||||
end
|
||||
|
||||
def destroy
|
||||
debug 'Call: destroy'
|
||||
file_remove
|
||||
end
|
||||
|
||||
def content
|
||||
debug 'Call: content'
|
||||
out = file_read
|
||||
debug "Return: '(md5)#{Digest::MD5.hexdigest out}'"
|
||||
out
|
||||
end
|
||||
|
||||
def content=(value)
|
||||
debug "Call: content='(md5)#{Digest::MD5.hexdigest value}'"
|
||||
file_write value
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,87 @@
|
|||
require 'digest/md5'
|
||||
|
||||
Puppet::Type.newtype(:sysfs_config_value, :doc => <<-eos
|
||||
This resource creates a single file in the sysfs config directory. The content
|
||||
of this files can either be passed directly by the "content" property the same
|
||||
way as usual "file" resource works or it can be autogenerated.
|
||||
|
||||
For example, it you want to set the scheduler of all your SSDs to "noop" you
|
||||
can specify "/sys/block/sd*/queue/scheduler" as the "sysfs" property and "noop"
|
||||
as the "value" property. The configuration file will contain:
|
||||
|
||||
block/sda/queue/scheduler = noop
|
||||
block/sdb/queue/scheduler = noop
|
||||
|
||||
The globulation will be opened to match every drive and the value will be set
|
||||
for every line.
|
||||
|
||||
You can exclude sysfs path elements by specifying "sdb" to the "exclude"
|
||||
parameter to exclude matching lines from the file.
|
||||
|
||||
If you need to pass different values for different lines you can pass a hash
|
||||
to the "value" parameter instead of a string:
|
||||
|
||||
value => { 'sdb' => 'deadline', 'default' => 'noop' }
|
||||
|
||||
The matching lines will be set to their values and the "default" values will be
|
||||
used for the lines that have not matched any of the hash keys.
|
||||
|
||||
Parameters "sysfs" and "exclude" can actually accept several patterns as an
|
||||
array, but, perhaps, it would be better to use several instances of this
|
||||
resources if you need to set a lot of different values.
|
||||
eos
|
||||
) do
|
||||
|
||||
ensurable
|
||||
|
||||
newparam :name, :namevar => true do
|
||||
desc 'The path to the config file'
|
||||
end
|
||||
|
||||
newparam :sysfs do
|
||||
desc 'Path to the SysFS nodes to be updated'
|
||||
munge do |value|
|
||||
break unless value
|
||||
break value if value.is_a? Array
|
||||
[value]
|
||||
end
|
||||
end
|
||||
|
||||
newparam :exclude do
|
||||
desc 'Path to the SysFS nodes to be excluded'
|
||||
munge do |value|
|
||||
break unless value
|
||||
break value if value.is_a? Array
|
||||
[value]
|
||||
end
|
||||
end
|
||||
|
||||
newparam :value do
|
||||
desc 'Set the SysFS nodes to this value'
|
||||
validate do |value|
|
||||
fail "The value should be either a string or a hash!" unless value.is_a? String or value.is_a? Hash
|
||||
end
|
||||
end
|
||||
|
||||
newproperty :content do
|
||||
desc 'Content of the config file. Should be autogenerated unless overriden.'
|
||||
|
||||
def should_to_s(value)
|
||||
"(md5)#{Digest::MD5.hexdigest value}"
|
||||
end
|
||||
|
||||
def is_to_s(value)
|
||||
"(md5)#{Digest::MD5.hexdigest value}"
|
||||
end
|
||||
end
|
||||
|
||||
def generate_content?
|
||||
return false if self[:content]
|
||||
!self[:sysfs].nil? and !self[:value].nil?
|
||||
end
|
||||
|
||||
def validate
|
||||
fail 'You should privide either "sysfs" and "value" to generate content or the "content" itself!' unless self[:content] or generate_content?
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,12 @@
|
|||
# == Class: sysfs
|
||||
#
|
||||
# This module manages Linux sysfs values using sysfsutils init script which
|
||||
# can take values stored in /etc/sysfs.conf and /etc/sysfs.d/*.conf snipplets.
|
||||
#
|
||||
|
||||
class sysfs {
|
||||
class { 'sysfs::install' :}
|
||||
class { 'sysfs::service' :}
|
||||
|
||||
Class['sysfs::install'] ~> Class['sysfs::service']
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
# == Class: sysfs::install
|
||||
#
|
||||
# This class installs the sysfsutils packages and prepares the config directory
|
||||
#
|
||||
|
||||
class sysfs::install inherits sysfs::params {
|
||||
|
||||
File {
|
||||
owner => 'root',
|
||||
group => 'root',
|
||||
mode => '0755',
|
||||
}
|
||||
|
||||
#TODO: should be moved to the fuel-library package or sysfsutils package
|
||||
if $::osfamily == 'RedHat' {
|
||||
file { 'sysfsutils.init' :
|
||||
ensure => 'present',
|
||||
name => "/etc/init.d/${service}",
|
||||
source => 'puppet:///modules/sysfs/centos-sysfsutils.init.sh',
|
||||
}
|
||||
}
|
||||
|
||||
package { 'sysfsutils' :
|
||||
ensure => 'installed',
|
||||
name => $package,
|
||||
}
|
||||
|
||||
tweaks::ubuntu_service_override { 'sysfsutils' :
|
||||
package_name => $package,
|
||||
}
|
||||
|
||||
file { 'sysfs.d' :
|
||||
ensure => 'directory',
|
||||
name => $config_dir,
|
||||
}
|
||||
|
||||
Class['sysfs::install'] -> Sysfs_config_value <||>
|
||||
}
|
|
@ -0,0 +1,5 @@
|
|||
class sysfs::params {
|
||||
$package = 'sysfsutils'
|
||||
$service = 'sysfsutils'
|
||||
$config_dir = '/etc/sysfs.d'
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
# == Class: sysfs::service
|
||||
#
|
||||
# This class actually enables and runs the sysfsutils service to
|
||||
# apply any configuration found in the config files
|
||||
#
|
||||
class sysfs::service inherits sysfs::params {
|
||||
service { 'sysfsutils' :
|
||||
ensure => 'running',
|
||||
enable => true,
|
||||
hasstatus => false,
|
||||
hasrestart => true,
|
||||
}
|
||||
|
||||
Sysfs_config_value <||> ~> Service['sysfsutils']
|
||||
}
|
|
@ -0,0 +1,33 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe 'the cpu_affinity_hex function' do
|
||||
let(:scope) { PuppetlabsSpec::PuppetInternals.scope }
|
||||
|
||||
it 'should exist' do
|
||||
expect(
|
||||
Puppet::Parser::Functions.function('cpu_affinity_hex')
|
||||
).to eq('function_cpu_affinity_hex')
|
||||
end
|
||||
|
||||
it 'should calculate HEX affinity value' do
|
||||
expect(
|
||||
scope.function_cpu_affinity_hex(%w(12))
|
||||
).to eq 'fff'
|
||||
expect(
|
||||
scope.function_cpu_affinity_hex(%w(2))
|
||||
).to eq '3'
|
||||
end
|
||||
|
||||
it 'should raise an error if there is less than 1 arguments' do
|
||||
expect {
|
||||
scope.function_cpu_affinity_hexs([])
|
||||
}.to raise_error
|
||||
end
|
||||
|
||||
it 'should raise an error if value is not integer' do
|
||||
expect {
|
||||
scope.function_cpu_affinity_hex(%w(abc))
|
||||
}.to raise_error
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,7 @@
|
|||
require 'puppetlabs_spec_helper/module_spec_helper'
|
||||
|
||||
RSpec.configure do |config|
|
||||
config.mock_with :rspec do |mock|
|
||||
mock.syntax = :expect
|
||||
end
|
||||
end
|
|
@ -0,0 +1,167 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Puppet::Type.type(:sysfs_config_value).provider(:ruby) do
|
||||
|
||||
let(:resource) do
|
||||
Puppet::Type.type(:sysfs_config_value).new(
|
||||
:name => '/etc/sysfs.d/scheduler.conf',
|
||||
:sysfs => '/sys/block/sd*/queue/scheduler',
|
||||
:exclude => '/sys/block/sdc/queue/scheduler',
|
||||
:value => 'noop',
|
||||
)
|
||||
end
|
||||
|
||||
let(:provider) do
|
||||
provider = resource.provider
|
||||
if ENV['SPEC_PUPPET_DEBUG']
|
||||
class << provider
|
||||
def debug(str)
|
||||
puts str
|
||||
end
|
||||
end
|
||||
end
|
||||
provider
|
||||
end
|
||||
|
||||
subject { provider }
|
||||
|
||||
before(:each) do
|
||||
allow(subject).to receive(:file_mkdir)
|
||||
allow(subject).to receive(:file_write)
|
||||
allow(subject).to receive(:file_exists?)
|
||||
allow(subject).to receive(:file_remove)
|
||||
allow(subject).to receive(:file_read)
|
||||
end
|
||||
|
||||
it 'should exist' do
|
||||
expect(subject).to be_a Puppet::Provider
|
||||
end
|
||||
|
||||
context 'ensurable' do
|
||||
it 'should check if file exists' do
|
||||
expect(subject).to receive(:file_exists?).and_return(true)
|
||||
expect(subject.exists?).to be true
|
||||
end
|
||||
|
||||
it 'can create a file and parent directory' do
|
||||
resource[:content] = '123'
|
||||
expect(subject).to receive(:file_write).with('123')
|
||||
expect(subject).to receive(:file_mkdir)
|
||||
expect(subject.file_base_dir).to eq '/etc/sysfs.d'
|
||||
subject.create
|
||||
end
|
||||
|
||||
it 'can remove a file' do
|
||||
expect(subject).to receive(:file_remove)
|
||||
subject.destroy
|
||||
end
|
||||
end
|
||||
|
||||
context 'content' do
|
||||
it 'can read file content' do
|
||||
expect(subject).to receive(:file_read).and_return('123')
|
||||
expect(subject.content).to eq '123'
|
||||
end
|
||||
|
||||
it 'can write file content' do
|
||||
expect(subject).to receive(:file_write).with('123')
|
||||
subject.content = '123'
|
||||
end
|
||||
end
|
||||
|
||||
context 'content generation' do
|
||||
before(:each) do
|
||||
allow(subject).to receive(:glob).with('/sys/block/sd*/queue/scheduler').and_return %w(
|
||||
/sys/block/sda/queue/scheduler
|
||||
/sys/block/sdb/queue/scheduler
|
||||
/sys/block/sdc/queue/scheduler
|
||||
)
|
||||
allow(subject).to receive(:glob).with('/sys/block/sda/queue/scheduler').and_return %w(
|
||||
/sys/block/sda/queue/scheduler
|
||||
)
|
||||
allow(subject).to receive(:glob).with('/sys/block/sdb/queue/scheduler').and_return %w(
|
||||
/sys/block/sdb/queue/scheduler
|
||||
)
|
||||
allow(subject).to receive(:glob).with('/sys/block/sdc/queue/scheduler').and_return %w(
|
||||
/sys/block/sdc/queue/scheduler
|
||||
)
|
||||
end
|
||||
|
||||
it 'can get a list of sysfs nodes using "sysfs" and "exclude"' do
|
||||
expect(subject.sysfs_nodes).to match_array %w(
|
||||
/sys/block/sda/queue/scheduler
|
||||
/sys/block/sdb/queue/scheduler
|
||||
)
|
||||
end
|
||||
|
||||
it 'can get a sysfs node value either as a string or as a hash' do
|
||||
resource[:value] = '123'
|
||||
expect(subject.sysfs_node_value '/sys/block/sda/queue/scheduler').to eq '123'
|
||||
resource[:value] = { 'sda' => '234' }
|
||||
expect(subject.sysfs_node_value '/sys/block/sda/queue/scheduler').to eq '234'
|
||||
resource[:value] = { 'default' => '345' }
|
||||
expect(subject.sysfs_node_value '/sys/block/sda/queue/scheduler').to eq '345'
|
||||
end
|
||||
|
||||
it 'can generate new config file content' do
|
||||
expect(resource).to receive(:generate_content?).and_return(true)
|
||||
resource[:content] = ''
|
||||
subject.generate_file_content
|
||||
expect(resource[:content]).to eq <<-eos
|
||||
block/sda/queue/scheduler = noop
|
||||
block/sdb/queue/scheduler = noop
|
||||
eos
|
||||
end
|
||||
|
||||
it 'can use values provided as a hash' do
|
||||
expect(resource).to receive(:generate_content?).and_return(true)
|
||||
resource[:content] = ''
|
||||
resource[:value] = { 'sdb' => 'deadline', 'default' => 'noop' }
|
||||
subject.generate_file_content
|
||||
expect(resource[:content]).to eq <<-eos
|
||||
block/sda/queue/scheduler = noop
|
||||
block/sdb/queue/scheduler = deadline
|
||||
eos
|
||||
end
|
||||
|
||||
it 'can use and array of "sysfs" values' do
|
||||
expect(resource).to receive(:generate_content?).and_return(true)
|
||||
resource[:content] = ''
|
||||
resource[:sysfs] = %w(
|
||||
/sys/block/sda/queue/scheduler
|
||||
/sys/block/sdb/queue/scheduler
|
||||
)
|
||||
subject.generate_file_content
|
||||
expect(resource[:content]).to eq <<-eos
|
||||
block/sda/queue/scheduler = noop
|
||||
block/sdb/queue/scheduler = noop
|
||||
eos
|
||||
end
|
||||
|
||||
it 'can use and array of "exclude" values' do
|
||||
expect(resource).to receive(:generate_content?).and_return(true)
|
||||
resource[:content] = ''
|
||||
resource[:sysfs] = %w(
|
||||
/sys/block/sda/queue/scheduler
|
||||
/sys/block/sdb/queue/scheduler
|
||||
/sys/block/sdc/queue/scheduler
|
||||
)
|
||||
resource[:exclude] = %w(
|
||||
/sys/block/sdc/queue/scheduler
|
||||
)
|
||||
subject.generate_file_content
|
||||
expect(resource[:content]).to eq <<-eos
|
||||
block/sda/queue/scheduler = noop
|
||||
block/sdb/queue/scheduler = noop
|
||||
eos
|
||||
end
|
||||
|
||||
it 'will not generate content if the type tells not to' do
|
||||
expect(resource).to receive(:generate_content?).and_return(false)
|
||||
resource[:content] = ''
|
||||
subject.generate_file_content
|
||||
expect(resource[:content]).to eq ''
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,54 @@
|
|||
require 'spec_helper'
|
||||
|
||||
describe Puppet::Type.type(:sysfs_config_value) do
|
||||
subject do
|
||||
Puppet::Type.type(:sysfs_config_value).new(
|
||||
:name => '/etc/sysfs.d/scheduler.conf',
|
||||
:sysfs => '/sys/block/sd*/queue/scheduler',
|
||||
:value => 'noop',
|
||||
)
|
||||
end
|
||||
|
||||
it 'should exist' do
|
||||
expect(subject).to be_a Puppet::Type
|
||||
end
|
||||
|
||||
[:name, :sysfs, :exclude, :value, :content].each do |param|
|
||||
it "should have '#{param}' parameter" do
|
||||
expect { subject[param] }.not_to raise_error
|
||||
end
|
||||
end
|
||||
|
||||
it 'should permit the new content generation if there is no content and sysfs and value are present' do
|
||||
expect(subject.generate_content?).to be true
|
||||
subject[:content] = 'test'
|
||||
expect(subject.generate_content?).to be false
|
||||
end
|
||||
|
||||
[:sysfs, :exclude].each do |param|
|
||||
it "should convert '#{param}' from a string to an array" do
|
||||
subject[param] = '123'
|
||||
expect(subject[param]).to eq ['123']
|
||||
end
|
||||
|
||||
it "should pass '#{param}' array values as is" do
|
||||
subject[param] = ['123']
|
||||
expect(subject[param]).to eq ['123']
|
||||
end
|
||||
end
|
||||
|
||||
it 'should accept "value" only as a string or a hash' do
|
||||
expect {
|
||||
subject[:value] = ['123']
|
||||
}.to raise_error
|
||||
end
|
||||
|
||||
it 'should not allow to use the resource without either content or sysfs and value' do
|
||||
expect {
|
||||
Puppet::Type.type(:sysfs_config_value).new(
|
||||
:name => '/etc/sysfs/scheduler.conf',
|
||||
)
|
||||
}.to raise_error
|
||||
end
|
||||
|
||||
end
|
Loading…
Reference in New Issue