diff --git a/lib/puppet/provider/sriov_vf_config/numvfs.rb b/lib/puppet/provider/sriov_vf_config/numvfs.rb new file mode 100644 index 000000000..cfa663cc2 --- /dev/null +++ b/lib/puppet/provider/sriov_vf_config/numvfs.rb @@ -0,0 +1,57 @@ +Puppet::Type.type(:sriov_vf_config).provide(:numvfs) do + desc <<-EOT + The file /sys/class/net//device/sriov_numvfs will be + present when a physical PCIe device supports SR-IOV. A number written to + this file will enable the specified number of VFs. This provider shall read + the file and ensure that the value is zero, before writing the number of + VFs that should be enabled. If the VFs needs to be disabled then we shall + write a zero to this file. + EOT + + def create + if File.file?(sriov_numvfs_path) + _set_numvfs + else + fail("#{sriov_numvfs_path} doesn't exist. Check if #{sriov_get_interface} is a valid network interface supporting SR-IOV") + end + end + + def destroy + if File.file?(sriov_numvfs_path) + File.write(sriov_numvfs_path,"0") + end + end + + def exists? + if File.file?(sriov_numvfs_path) + cur_value = File.read(sriov_numvfs_path) + if cur_value.to_i == sriov_numvfs_value + return true + end + end + return false + end + + def _set_numvfs + # During an update, the content of file sriov_numvfs_path has to be set + # to 0 (ZERO), before writing the actual value + cur_value = File.read(sriov_numvfs_path) + if cur_value != 0 + File.write(sriov_numvfs_path,"0") + end + File.write(sriov_numvfs_path,sriov_numvfs_value) + end + + def sriov_numvfs_path + "/sys/class/net/#{sriov_get_interface}/device/sriov_numvfs" + end + + def sriov_get_interface + resource[:name].split(':', 2).first + end + + def sriov_numvfs_value + resource[:name].split(':', 2).last.to_i + end + +end diff --git a/lib/puppet/type/sriov_vf_config.rb b/lib/puppet/type/sriov_vf_config.rb new file mode 100644 index 000000000..09a3671d1 --- /dev/null +++ b/lib/puppet/type/sriov_vf_config.rb @@ -0,0 +1,10 @@ +Puppet::Type.newtype(:sriov_vf_config) do + + ensurable + + newparam(:name) do + desc "sriov_numvfs conf as : format" + newvalues(/^[a-z0-9\-_]+:[0-9]+$/) + end + +end diff --git a/manifests/host/sriov.pp b/manifests/host/sriov.pp new file mode 100644 index 000000000..a30db4295 --- /dev/null +++ b/manifests/host/sriov.pp @@ -0,0 +1,27 @@ +# == Class: tripleo::host::sriov +# +# Configures host configuration for the SR-IOV interfaces +# +# === Parameters +# +# [*number_of_vfs*] +# (optional) List of : specifying the number +# VFs to be exposed per physical interface. +# For example, to configure two interface with number of VFs, specify +# it as ['eth1:4','eth2:10'] +# Defaults to [] +# +class tripleo::host::sriov ( + $number_of_vfs = [], +) { + + if !empty($number_of_vfs) { + sriov_vf_config { $number_of_vfs: ensure => present } + + # the numvfs configuration needs to be persisted for every boot + tripleo::host::sriov::numvfs_persistence {'persistent_numvfs': + vf_defs => $number_of_vfs, + content_string => "#!/bin/bash\n" + } + } +} diff --git a/manifests/host/sriov/numvfs_persistence.pp b/manifests/host/sriov/numvfs_persistence.pp new file mode 100644 index 000000000..1ee402cd1 --- /dev/null +++ b/manifests/host/sriov/numvfs_persistence.pp @@ -0,0 +1,55 @@ +# +# tripleo::host::sriov::numvfs_persistence used by tripleo::host::sriov +# +# === Parameters: +# +# [*vf_defs*] +# (required) Array of of :. +# Example: ['eth1:10','eth2:8'] +# +# [*content_string*] +# (required) String which shall be written to the script file. +# +define tripleo::host::sriov::numvfs_persistence( + $vf_defs, + $content_string +){ + # Since reduce isn't available, we use recursion to iterate each entries of + # "physical_interface:vfs" and accumulate the content that needs to be + # written to the script file. + include ::stdlib + + if empty($vf_defs) { + file { '/etc/sysconfig/allocate_vfs': + ensure => file, + content => $content_string, + group => 'root', + mode => '0755', + owner => 'root', + } + + file { '/sbin/ifup-local': + group => 'root', + mode => '0755', + owner => 'root', + content => '#!/bin/bash', + replace => false + } + + file_line { 'call_ifup-local': + path => '/sbin/ifup-local', + line => '/etc/sysconfig/allocate_vfs $1', + require => File['/sbin/ifup-local'], + } + } else { + $vfspec = split($vf_defs[0], ':') + $interface = $vfspec[0] + $count = $vfspec[1] + $vfdef_str = "${content_string}[ \"${interface}\" == \"\$1\" ] && echo ${count} > /sys/class/net/${interface}/device/sriov_numvfs\n" + tripleo::host::sriov::numvfs_persistence{"mapped ${interface}": + vf_defs => delete_at($vf_defs, 0), + content_string => $vfdef_str + } + } +} + diff --git a/manifests/profile/base/neutron/sriov.pp b/manifests/profile/base/neutron/sriov.pp index 9b5f34c48..00ecc21ae 100644 --- a/manifests/profile/base/neutron/sriov.pp +++ b/manifests/profile/base/neutron/sriov.pp @@ -36,6 +36,7 @@ class tripleo::profile::base::neutron::sriov( if $step >= 4 { if 'sriovnicswitch' in $mechanism_drivers { include ::neutron::agents::ml2::sriov + include ::tripleo::host::sriov } } diff --git a/releasenotes/notes/sriov_numvfs-40564db9e1be589b.yaml b/releasenotes/notes/sriov_numvfs-40564db9e1be589b.yaml new file mode 100644 index 000000000..85cbdec69 --- /dev/null +++ b/releasenotes/notes/sriov_numvfs-40564db9e1be589b.yaml @@ -0,0 +1,4 @@ +--- +features: + - Added a provider to configure VFs for SR-IOV interface. + Added a define for persistence of the VFs configuratin. diff --git a/spec/classes/tripleo_host_sriov_spec.rb b/spec/classes/tripleo_host_sriov_spec.rb new file mode 100644 index 000000000..15d381338 --- /dev/null +++ b/spec/classes/tripleo_host_sriov_spec.rb @@ -0,0 +1,39 @@ +require 'spec_helper' + +describe 'tripleo::host::sriov' do + + shared_examples_for 'sriov vfs configuration for Red Hat distributions' do + + let :facts do + { + :osfamily => 'RedHat', + :operatingsystemmajrelease => 7, + } + end + + let :params do + {:number_of_vfs => []} + end + + it 'does not configure numvfs by default' do + is_expected.not_to contain_sriov_vf_config([]) + end + + context 'when number_of_vfs is configured' do + let :params do + {:number_of_vfs => ['eth0:4','eth1:5']} + end + + it 'configures numvfs' do + is_expected.to contain_sriov_vf_config('eth0:4').with( :ensure => 'present' ) + is_expected.to contain_sriov_vf_config('eth1:5').with( :ensure => 'present') + is_expected.to contain_tripleo__host__sriov__numvfs_persistence('persistent_numvfs').with( + :vf_defs => ['eth0:4','eth1:5'], + :content_string => "#!/bin/bash\n" + ) + end + end + end + + it_configures 'sriov vfs configuration for Red Hat distributions' +end diff --git a/spec/defines/tripleo_host_sriov_numvfs_persistence_spec.rb b/spec/defines/tripleo_host_sriov_numvfs_persistence_spec.rb new file mode 100644 index 000000000..57559a2a5 --- /dev/null +++ b/spec/defines/tripleo_host_sriov_numvfs_persistence_spec.rb @@ -0,0 +1,40 @@ +require 'spec_helper' + +describe 'tripleo::host::sriov::numvfs_persistence' do + + describe 'confugure numvfs for persistence' do + + let :title do + 'numvfs' + end + + let :params do + { + :name => 'persistence', + :vf_defs => ['eth0:10','eth1:8'], + :content_string => "Hashbang\n" + } + end + + it 'configures persistence' do + is_expected.to contain_file('/etc/sysconfig/allocate_vfs').with( + :ensure => 'file', + :content => "Hashbang\n[ \"eth0\" == \"\$1\" ] && echo 10 > /sys/class/net/eth0/device/sriov_numvfs\n[ \"eth1\" == \"\$1\" ] && echo 8 > /sys/class/net/eth1/device/sriov_numvfs\n", + :group => 'root', + :mode => '0755', + :owner => 'root', + ) + is_expected.to contain_file('/sbin/ifup-local').with( + :group => 'root', + :mode => '0755', + :owner => 'root', + :content => '#!/bin/bash', + :replace => false, + ) + is_expected.to contain_file_line('call_ifup-local').with( + :path => '/sbin/ifup-local', + :line => '/etc/sysconfig/allocate_vfs $1', + ) + end + end +end diff --git a/spec/unit/provider/sriov_vf_config/numvfs_spec.rb b/spec/unit/provider/sriov_vf_config/numvfs_spec.rb new file mode 100644 index 000000000..ac1a398c1 --- /dev/null +++ b/spec/unit/provider/sriov_vf_config/numvfs_spec.rb @@ -0,0 +1,40 @@ +require 'puppet' +require 'spec_helper' +require 'puppet/provider/sriov_vf_config/numvfs' + +provider_class = Puppet::Type.type(:sriov_vf_config). + provider(:numvfs) + +describe provider_class do + + let(:test_cfg_path) { "/tmp/test-ifup-local.txt" } + let :numvfs_conf do + { + :name => 'eth0:10', + :ensure => 'present', + } + end + + describe 'when setting the attributes' do + let :resource do + Puppet::Type::Sriov_vf_config.new(numvfs_conf) + end + + let :provider do + provider_class.new(resource) + end + + it 'should return the correct interface name' do + expect(provider.sriov_get_interface).to eql('eth0') + end + + it 'should return the correct numvfs value' do + expect(provider.sriov_numvfs_value).to eql(10) + end + + it 'should return path of the file to enable vfs' do + expect(provider.sriov_numvfs_path).to eql('/sys/class/net/eth0/device/sriov_numvfs') + end + end + +end diff --git a/spec/unit/type/sriov_vf_config_spec.rb b/spec/unit/type/sriov_vf_config_spec.rb new file mode 100644 index 000000000..9a911f604 --- /dev/null +++ b/spec/unit/type/sriov_vf_config_spec.rb @@ -0,0 +1,47 @@ +require 'puppet' +require 'puppet/type/sriov_vf_config' + +describe 'Puppet::Type.type(:sriov_vf_config)' do + it 'should allow name to be passed' do + expect{Puppet::Type.type(:sriov_vf_config).new( + :name => 'eth0:10', + :ensure => 'present' + )}.not_to raise_error + end + it 'should allow name to be passed with -' do + expect{Puppet::Type.type(:sriov_vf_config).new( + :name => 'eth-0:10', + :ensure => 'present' + )}.not_to raise_error + end + it 'should allow name to be passed with _' do + expect{Puppet::Type.type(:sriov_vf_config).new( + :name => 'eth_0:10', + :ensure => 'present' + )}.not_to raise_error + end + it 'should throw error for invalid format' do + expect{Puppet::Type.type(:sriov_vf_config).new( + :name => 'eth0', + :ensure => 'present' + )}.to raise_error(Puppet::ResourceError) + end + it 'should throw error for invalid format without interface name' do + expect{Puppet::Type.type(:sriov_vf_config).new( + :name => ':9', + :ensure => 'present' + )}.to raise_error(Puppet::ResourceError) + end + it 'should throw error for invalid format for numvfs' do + expect{Puppet::Type.type(:sriov_vf_config).new( + :name => 'eth8:none', + :ensure => 'present' + )}.to raise_error(Puppet::ResourceError) + end + it 'should throw error for invalid format without numvfs' do + expect{Puppet::Type.type(:sriov_vf_config).new( + :name => 'eth0:', + :ensure => 'present' + )}.to raise_error(Puppet::ResourceError) + end +end