diff --git a/REFERENCE.md b/REFERENCE.md index 7dd1d9a1..9b769ab2 100644 --- a/REFERENCE.md +++ b/REFERENCE.md @@ -871,6 +871,20 @@ systemd::manage_dropin { 'userlimits.conf': } ``` +##### set IO limits on two devices + +```puppet +systemd::manage_dropin { 'devicelimits.conf': + unit => 'special.service', + service_entry => { + 'IOReadIOPSMax' => [ + ['/dev/afs',100], + ['/dev/gluster','1000K'], + ], + }, +} +``` + #### Parameters The following parameters are available in the `systemd::manage_dropin` defined type: @@ -2649,11 +2663,11 @@ Struct[{ Optional['IOAccounting'] => Boolean, Optional['IOWeight'] => Integer[1,10000], Optional['StartupIOWeight'] => Integer[1,10000], - Optional['IODeviceWeight'] => Array[Hash[Stdlib::Absolutepath, Integer[1,10000], 1, 1]], - Optional['IOReadBandwidthMax'] => Array[Hash[Stdlib::Absolutepath, Systemd::Unit::Amount, 1, 1]], - Optional['IOWriteBandwidthMax'] => Array[Hash[Stdlib::Absolutepath, Systemd::Unit::Amount, 1, 1]], - Optional['IOReadIOPSMax'] => Array[Hash[Stdlib::Absolutepath, Systemd::Unit::Amount, 1, 1]], - Optional['IOWriteIOPSMax'] => Array[Hash[Stdlib::Absolutepath, Systemd::Unit::Amount, 1, 1]], + Optional['IODeviceWeight'] => Variant[Tuple[Stdlib::Absolutepath, Integer[1,10000]],Array[Tuple[Stdlib::Absolutepath, Integer[1,10000]]]], + Optional['IOReadBandwidthMax'] => Variant[Tuple[Stdlib::Absolutepath, Systemd::Unit::Amount],Array[Tuple[Stdlib::Absolutepath, Systemd::Unit::Amount]]], + Optional['IOWriteBandwidthMax'] => Variant[Tuple[Stdlib::Absolutepath, Systemd::Unit::Amount],Array[Tuple[Stdlib::Absolutepath, Systemd::Unit::Amount]]], + Optional['IOReadIOPSMax'] => Variant[Tuple[Stdlib::Absolutepath, Systemd::Unit::Amount],Array[Tuple[Stdlib::Absolutepath, Systemd::Unit::Amount]]], + Optional['IOWriteIOPSMax'] => Variant[Tuple[Stdlib::Absolutepath, Systemd::Unit::Amount],Array[Tuple[Stdlib::Absolutepath, Systemd::Unit::Amount]]], Optional['DeviceAllow'] => String[1], Optional['DevicePolicy'] => Enum['auto','closed','strict'], Optional['Slice'] => String[1], @@ -2748,12 +2762,12 @@ Struct[{ Optional['DeviceAllow'] => Pattern['^(/dev/)|(char-)|(block-).*$'], Optional['DevicePolicy'] => Enum['auto','closed','strict'], Optional['IOAccounting'] => Boolean, - Optional['IODeviceWeight'] => Array[Hash[Stdlib::Absolutepath, Integer[1,10000], 1, 1]], - Optional['IOReadBandwidthMax'] => Array[Hash[Stdlib::Absolutepath, Systemd::Unit::Amount], 1, 1], - Optional['IOReadIOPSMax'] => Array[Hash[Stdlib::Absolutepath, Systemd::Unit::Amount], 1, 1], + Optional['IODeviceWeight'] => Variant[Tuple[Stdlib::Absolutepath, Integer[1,10000]],Array[Tuple[Stdlib::Absolutepath, Integer[1,10000]]]], + Optional['IOReadBandwidthMax'] => Variant[Tuple[Stdlib::Absolutepath, Systemd::Unit::Amount],Array[Tuple[Stdlib::Absolutepath, Systemd::Unit::Amount]]], + Optional['IOReadIOPSMax'] => Variant[Tuple[Stdlib::Absolutepath, Systemd::Unit::Amount],Array[Tuple[Stdlib::Absolutepath, Systemd::Unit::Amount]]], Optional['IOWeight'] => Integer[1,10000], - Optional['IOWriteBandwidthMax'] => Array[Hash[Stdlib::Absolutepath, Systemd::Unit::Amount], 1, 1], - Optional['IOWriteIOPSMax'] => Array[Hash[Stdlib::Absolutepath, Systemd::Unit::Amount], 1, 1], + Optional['IOWriteBandwidthMax'] => Variant[Tuple[Stdlib::Absolutepath, Systemd::Unit::Amount],Array[Tuple[Stdlib::Absolutepath, Systemd::Unit::Amount]]], + Optional['IOWriteIOPSMax'] => Variant[Tuple[Stdlib::Absolutepath, Systemd::Unit::Amount],Array[Tuple[Stdlib::Absolutepath, Systemd::Unit::Amount]]], Optional['IPAccounting'] => Boolean, Optional['MemoryAccounting'] => Boolean, Optional['MemoryHigh'] => Systemd::Unit::AmountOrPercent, diff --git a/manifests/manage_dropin.pp b/manifests/manage_dropin.pp index 41761a7d..140c79bb 100644 --- a/manifests/manage_dropin.pp +++ b/manifests/manage_dropin.pp @@ -59,6 +59,17 @@ # } # } # +# @example set IO limits on two devices +# systemd::manage_dropin { 'devicelimits.conf': +# unit => 'special.service', +# service_entry => { +# 'IOReadIOPSMax' => [ +# ['/dev/afs',100], +# ['/dev/gluster','1000K'], +# ], +# }, +# } +# # @param unit The unit to create a dropfile for # @param filename The target unit file to create. The filename of the drop in. The full path is determined using the path, unit and this filename. # @param ensure The state of this dropin file diff --git a/spec/defines/manage_dropin_spec.rb b/spec/defines/manage_dropin_spec.rb index 14110aba..fcf333c9 100644 --- a/spec/defines/manage_dropin_spec.rb +++ b/spec/defines/manage_dropin_spec.rb @@ -20,6 +20,24 @@ it { is_expected.to compile.with_all_deps } it { is_expected.to contain_systemd__dropin_file('foobar.conf').with_content(%r{^# Deployed with puppet$}) } + context 'with empty sections' do + let(:params) do + super().merge( + unit_entry: {}, + service_entry: {} + ) + end + + it { is_expected.to compile.with_all_deps } + + it { + is_expected.to contain_systemd__dropin_file('foobar.conf'). + with_content(%r{^\[Unit\]$}). + with_content(%r{^\[Service\]$}). + without_content(%r{^\[Slice\]$}) + } + end + context 'setting some parameters simply' do let(:params) do super().merge( @@ -29,6 +47,12 @@ service_entry: { SyslogIdentifier: 'simple', LimitCORE: 'infinity', + IODeviceWeight: + [ + ['/dev/weight', 10], + ['/dev/weight2', 20], + ], + IOReadBandwidthMax: ['/dev/weight3', '50K'], } ) end @@ -38,6 +62,9 @@ with_content(%r{^LimitCORE=infinity$}). with_content(%r{^DefaultDependencies=true$}). with_content(%r{^SyslogIdentifier=simple$}). + with_content(%r{^IODeviceWeight=/dev/weight 10$}). + with_content(%r{^IODeviceWeight=/dev/weight2 20$}). + with_content(%r{^IOReadBandwidthMax=/dev/weight3 50K$}). without_content(%r{^\[Slice\]$}) } end @@ -133,6 +160,10 @@ slice_entry: { 'MemoryMax' => '10G', 'MemoryAccounting' => true, + 'IOWriteBandwidthMax' => [ + ['/dev/afs', '50P'], + ['/dev/gluster', 50], + ] } } end @@ -142,8 +173,11 @@ it { is_expected.to contain_systemd__dropin_file('foobar.conf'). with_unit('user-.slice'). + with_content(%r{^IOWriteBandwidthMax=/dev/afs 50P$}). + with_content(%r{^IOWriteBandwidthMax=/dev/gluster 50$}). with_content(%r{^MemoryMax=10G$}). - with_content(%r{^MemoryAccounting=true$}) + with_content(%r{^MemoryAccounting=true$}). + without_content(%r{^\[Service\]$}) } end diff --git a/spec/defines/manage_unit_spec.rb b/spec/defines/manage_unit_spec.rb index c72020d9..fb404b37 100644 --- a/spec/defines/manage_unit_spec.rb +++ b/spec/defines/manage_unit_spec.rb @@ -21,7 +21,8 @@ Type: 'oneshot', ExecStart: '/usr/bin/doit.sh', SyslogIdentifier: 'doit-backwards.sh', - Environment: ['bla=foo', 'foo=bla'] + Environment: ['bla=foo', 'foo=bla'], + IOReadIOPSMax: ['/dev/afs', '1K'], }, install_entry: { WantedBy: 'multi-user.target', @@ -43,6 +44,7 @@ with_content(%r{^Description=My great service$}). with_content(%r{^Description=has two lines of description$}). with_content(%r{^Type=oneshot$}). + with_content(%r{^IOReadIOPSMax=/dev/afs 1K$}). without_content(%r{^\[Slice\]$}) } @@ -180,7 +182,11 @@ slice_entry: { 'MemoryMax' => '10G', 'IOAccounting' => true, - } + 'IOWriteIOPSMax' => [ + ['/dev/gluster', 20], + ['/dev/afs', '50K'], + ], + }, } end @@ -190,7 +196,10 @@ is_expected.to contain_systemd__unit_file('myslice.slice'). with_content(%r{^\[Slice\]$}). with_content(%r{^MemoryMax=10G$}). - with_content(%r{^IOAccounting=true$}) + with_content(%r{^IOAccounting=true$}). + with_content(%r{^IOWriteIOPSMax=/dev/gluster 20$}). + with_content(%r{^IOWriteIOPSMax=/dev/afs 50K$}). + without_content(%r{^\[Service\]$}) } end diff --git a/spec/type_aliases/systemd_unit_service_spec.rb b/spec/type_aliases/systemd_unit_service_spec.rb index c40ed1f7..fa31128e 100644 --- a/spec/type_aliases/systemd_unit_service_spec.rb +++ b/spec/type_aliases/systemd_unit_service_spec.rb @@ -106,4 +106,21 @@ it { is_expected.not_to allow_value({ 'CPUQuota' => 50 }) } it { is_expected.not_to allow_value({ 'CPUQuota' => '0%' }) } it { is_expected.not_to allow_value({ 'MemoryHigh' => '1Y' }) } + + it { is_expected.to allow_value({ 'IODeviceWeight' => ['/dev/afs', 1000] }) } + it { is_expected.to allow_value({ 'IODeviceWeight' => [['/dev/afs', 1000], ['/dev/gluster', 10]] }) } + it { is_expected.not_to allow_value({ 'IODeviceWeight' => ['/dev/afs', 10_001] }) } + it { is_expected.not_to allow_value({ 'IODeviceWeight' => ['absolute/path', 10_001] }) } + it { is_expected.not_to allow_value({ 'IODeviceWeight' => '/dev/afs 1000' }) } + + %w[IOReadBandwidthMax IOWriteBandwidthMax IOWriteIOPSMax IOWriteIOPSMax].each do |device_size| + context "with a key of #{device_size} can have a typle and size" do + it { + is_expected.to allow_value({ device_size => ['/dev/afs', 1000] }) + is_expected.to allow_value({ device_size => [['/dev/afs', 1000], ['/dev/gluster', '12G']] }) + is_expected.not_to allow_value({ device_size => '/dev/afs 1000' }) + is_expected.not_to allow_value({ device_size => [['absolute/path', 1000], ['/dev/gluster', '12G']] }) + } + end + end end diff --git a/templates/unit_file.epp b/templates/unit_file.epp index 7c606a7c..483281f0 100644 --- a/templates/unit_file.epp +++ b/templates/unit_file.epp @@ -22,6 +22,15 @@ 'Install', ] +# Directives which are pair of items to be expressed as a space seperated pair. +$_tupled_values = [ + 'IODeviceWeight', + 'IOReadBandwidthMax', + 'IOWriteBandwidthMax', + 'IOReadIOPSMax', + 'IOWriteIOPSMax', +] + -%> # Deployed with puppet # @@ -29,14 +38,31 @@ $_unit_sections.each | $_section | { $_values = getvar("${downcase($_section)}_entry") if $_values { --%> + -%> [<%= $_section %>] -<%- - $_values.each | $_key, $_value | { - Array($_value, true).each | $_subvalue | { -%> + <%- + + # De-tuple the tupled values to space seperation + $_myvalues = $_values.map | $_key, $_value | { + if $_key in $_tupled_values { + if $_value =~ Array[Tuple[String[1],Variant[Integer,String[1]]]] { + { $_key => $_value.map | $_inner_value | { "${_inner_value[0]} ${_inner_value[1]}" } } + } else { + {$_key => "${_value[0]} ${_value[1]}"} + } + } else { + {$_key => $_value} + } + }.reduce | $_memo, $_value | { $_memo + $_value } + + if $_myvalues { + $_myvalues.each | $_key, $_value | { + Array($_value, true).each | $_subvalue | { + -%> <%= $_key %>=<%= $_subvalue %> -<%- + <%- + } } } } diff --git a/types/unit/service.pp b/types/unit/service.pp index e2521abe..83479df9 100644 --- a/types/unit/service.pp +++ b/types/unit/service.pp @@ -63,11 +63,11 @@ Optional['IOAccounting'] => Boolean, Optional['IOWeight'] => Integer[1,10000], Optional['StartupIOWeight'] => Integer[1,10000], - Optional['IODeviceWeight'] => Array[Hash[Stdlib::Absolutepath, Integer[1,10000], 1, 1]], - Optional['IOReadBandwidthMax'] => Array[Hash[Stdlib::Absolutepath, Systemd::Unit::Amount, 1, 1]], - Optional['IOWriteBandwidthMax'] => Array[Hash[Stdlib::Absolutepath, Systemd::Unit::Amount, 1, 1]], - Optional['IOReadIOPSMax'] => Array[Hash[Stdlib::Absolutepath, Systemd::Unit::Amount, 1, 1]], - Optional['IOWriteIOPSMax'] => Array[Hash[Stdlib::Absolutepath, Systemd::Unit::Amount, 1, 1]], + Optional['IODeviceWeight'] => Variant[Tuple[Stdlib::Absolutepath, Integer[1,10000]],Array[Tuple[Stdlib::Absolutepath, Integer[1,10000]]]], + Optional['IOReadBandwidthMax'] => Variant[Tuple[Stdlib::Absolutepath, Systemd::Unit::Amount],Array[Tuple[Stdlib::Absolutepath, Systemd::Unit::Amount]]], + Optional['IOWriteBandwidthMax'] => Variant[Tuple[Stdlib::Absolutepath, Systemd::Unit::Amount],Array[Tuple[Stdlib::Absolutepath, Systemd::Unit::Amount]]], + Optional['IOReadIOPSMax'] => Variant[Tuple[Stdlib::Absolutepath, Systemd::Unit::Amount],Array[Tuple[Stdlib::Absolutepath, Systemd::Unit::Amount]]], + Optional['IOWriteIOPSMax'] => Variant[Tuple[Stdlib::Absolutepath, Systemd::Unit::Amount],Array[Tuple[Stdlib::Absolutepath, Systemd::Unit::Amount]]], Optional['DeviceAllow'] => String[1], Optional['DevicePolicy'] => Enum['auto','closed','strict'], Optional['Slice'] => String[1], diff --git a/types/unit/slice.pp b/types/unit/slice.pp index 4439cc00..9e60a719 100644 --- a/types/unit/slice.pp +++ b/types/unit/slice.pp @@ -12,12 +12,12 @@ Optional['DeviceAllow'] => Pattern['^(/dev/)|(char-)|(block-).*$'], Optional['DevicePolicy'] => Enum['auto','closed','strict'], Optional['IOAccounting'] => Boolean, - Optional['IODeviceWeight'] => Array[Hash[Stdlib::Absolutepath, Integer[1,10000], 1, 1]], - Optional['IOReadBandwidthMax'] => Array[Hash[Stdlib::Absolutepath, Systemd::Unit::Amount], 1, 1], - Optional['IOReadIOPSMax'] => Array[Hash[Stdlib::Absolutepath, Systemd::Unit::Amount], 1, 1], + Optional['IODeviceWeight'] => Variant[Tuple[Stdlib::Absolutepath, Integer[1,10000]],Array[Tuple[Stdlib::Absolutepath, Integer[1,10000]]]], + Optional['IOReadBandwidthMax'] => Variant[Tuple[Stdlib::Absolutepath, Systemd::Unit::Amount],Array[Tuple[Stdlib::Absolutepath, Systemd::Unit::Amount]]], + Optional['IOReadIOPSMax'] => Variant[Tuple[Stdlib::Absolutepath, Systemd::Unit::Amount],Array[Tuple[Stdlib::Absolutepath, Systemd::Unit::Amount]]], Optional['IOWeight'] => Integer[1,10000], - Optional['IOWriteBandwidthMax'] => Array[Hash[Stdlib::Absolutepath, Systemd::Unit::Amount], 1, 1], - Optional['IOWriteIOPSMax'] => Array[Hash[Stdlib::Absolutepath, Systemd::Unit::Amount], 1, 1], + Optional['IOWriteBandwidthMax'] => Variant[Tuple[Stdlib::Absolutepath, Systemd::Unit::Amount],Array[Tuple[Stdlib::Absolutepath, Systemd::Unit::Amount]]], + Optional['IOWriteIOPSMax'] => Variant[Tuple[Stdlib::Absolutepath, Systemd::Unit::Amount],Array[Tuple[Stdlib::Absolutepath, Systemd::Unit::Amount]]], Optional['IPAccounting'] => Boolean, Optional['MemoryAccounting'] => Boolean, Optional['MemoryHigh'] => Systemd::Unit::AmountOrPercent,