Added sriov provider for port

New provider `sriov` introduced for add-port action.
It accepts two vendor-specific arguments:
* sriov_numvfs: number of VF's to enable
* physnet: physnet to setup in nova-compute

virtfn network interfaces is ignored now.

Change-Id: Id42e09ef2cf955301cd64c28dcfcd60f61ae72af
Implements: blueprint support-sriov
This commit is contained in:
Vladimir Eremin 2016-02-02 20:33:07 +03:00
parent da53d1421c
commit c9ad6112e1
22 changed files with 688 additions and 15 deletions

View File

@ -0,0 +1,16 @@
#!/bin/bash
. /etc/init.d/functions
cd /etc/sysconfig/network-scripts
. ./network-functions
CONFIG="${1}"
source_config
SYSFSPATH="/sys/class/net/${SYSCTLDEVICE}/device/sriov_numvfs"
if [ ${SRIOV_NUMFS:-0} -gt 0 -a -f "${SYSFSPATH}" ]
then
echo 0 > "${SYSFSPATH}"
echo "${SRIOV_NUMFS:-0}" > "${SYSFSPATH}"
fi
exec ./ifup-eth "${@}"

View File

@ -351,9 +351,9 @@ Puppet::Parser::Functions::newfunction(:generate_network_config, :type => :rvalu
trans = L23network.sanitize_transformation(t, default_provider)
#debug("TTT: '#{trans[:name]}' => '#{trans.to_yaml.gsub('!ruby/sym ','')}'.")
if !ports_properties[trans[:name].to_sym()].nil?
trans.merge! ports_properties[trans[:name].to_sym()]
end
# merge interface properties with transformations and vendor_specific
port_props = ports_properties[trans[:name].to_sym()] || {}
trans = trans.merge(port_props) { |k,a,b| (k == :vendor_specific and a.is_a?(Hash)) ? a.merge(b) : b }
if trans.has_key?(:mtu) && !trans[:name].nil? && trans[:name] != 'unnamed'
# MTU should be adjusted to phys_dev if it is possible

View File

@ -0,0 +1,26 @@
require 'puppetx/l23_network_scheme'
Puppet::Parser::Functions::newfunction(:get_nic_passthrough_whitelist, :type => :rvalue, :arity => 1, :doc => <<-EOS
This function gets pci_passthrough_whitelist mapping from transformations
Returns NIL if no transformations with this provider found or list
ex: pci_passthrough_whitelist('sriov')
EOS
) do |argv|
provider = argv[0].to_s.upcase
cfg = L23network::Scheme.get_config(lookupvar('l3_fqdn_hostname'))
transformations = cfg[:transformations]
rv = []
transformations.each do |transform|
if transform[:provider].to_s.upcase == provider and\
transform[:action] == "add-port" and\
transform[:vendor_specific][:physnet]
rv.push({"devname" => transform[:name], "physical_network" => transform[:vendor_specific][:physnet]})
end
end
rv unless rv.empty?
end
# vim: set ts=2 sw=2 et :

View File

@ -0,0 +1,16 @@
require 'puppetx/filemapper'
require File.join(File.dirname(__FILE__), '..','..','..','puppet/provider/l23_stored_config_sriov_centos')
Puppet::Type.type(:l23_stored_config).provide(:sriov_centos6, :parent => Puppet::Provider::L23_stored_config_sriov_centos) do
include PuppetX::FileMapper
confine :l23_os => :centos6
has_feature :provider_options
self.unlink_empty_files = true
end
# vim: set ts=2 sw=2 et :

View File

@ -0,0 +1,16 @@
require 'puppetx/filemapper'
require File.join(File.dirname(__FILE__), '..','..','..','puppet/provider/l23_stored_config_sriov_centos')
Puppet::Type.type(:l23_stored_config).provide(:sriov_centos7, :parent => Puppet::Provider::L23_stored_config_sriov_centos) do
include PuppetX::FileMapper
confine :l23_os => :centos7
has_feature :provider_options
self.unlink_empty_files = true
end
# vim: set ts=2 sw=2 et :

View File

@ -0,0 +1,16 @@
require 'puppetx/filemapper'
require File.join(File.dirname(__FILE__), '..','..','..','puppet/provider/l23_stored_config_sriov_centos')
Puppet::Type.type(:l23_stored_config).provide(:sriov_redhat7, :parent => Puppet::Provider::L23_stored_config_sriov_centos) do
include PuppetX::FileMapper
confine :l23_os => :redhat7
has_feature :provider_options
self.unlink_empty_files = true
end
# vim: set ts=2 sw=2 et :

View File

@ -0,0 +1,50 @@
require 'puppetx/filemapper'
require File.join(File.dirname(__FILE__), '..','..','..','puppet/provider/l23_stored_config_ubuntu')
Puppet::Type.type(:l23_stored_config).provide(:sriov_ubuntu, :parent => Puppet::Provider::L23_stored_config_ubuntu) do
include PuppetX::FileMapper
confine :l23_os => :ubuntu
has_feature :provider_options
self.unlink_empty_files = true
def self.check_if_provider(if_data)
if if_data[:sriov_numvfs]
if_data[:if_provider] = :sriov
true
else
if_data[:if_provider] = nil
end
end
def self.property_mappings
super.merge({
:sriov_numvfs => 'sriov_numvfs',
})
end
def self.iface_file_header(provider)
rv = []
rv << self.puppet_header
if provider.onboot
rv << "auto #{provider.name}"
end
rv << "iface #{provider.name} inet #{provider.method}"
rv << "up echo 0 > /sys/class/net/#{provider.name}/device/sriov_numvfs"
rv << "up echo ${IF_SRIOV_NUMVFS:-0} > /sys/class/net/#{provider.name}/device/sriov_numvfs"
return [rv, {}]
end
def self.mangle__sriov_numvfs(data)
data.to_i
end
end
# vim: set ts=2 sw=2 et :

View File

@ -0,0 +1,28 @@
require File.join(File.dirname(__FILE__), '..','..','puppet/provider/l23_stored_config_centos')
class Puppet::Provider::L23_stored_config_sriov_centos < Puppet::Provider::L23_stored_config_centos
def self.property_mappings
super.merge({
:sriov_numvfs => 'SRIOV_NUMFS',
:device_type => 'DEVICETYPE',
})
end
def device_type
:sriov
end
def self.mangle__sriov_numvfs(data)
data.to_i
end
def self.unmangle_properties(provider, props)
rv = super
rv.merge({
'TYPE' => rv['DEVICETYPE'],
})
end
end
# vim: set ts=2 sw=2 et :

View File

@ -55,6 +55,7 @@ class Puppet::Provider::L2_base < Puppet::Provider::InterfaceToolset
# Fetch information about interfaces, visible in network namespace from /sys/class/net
interfaces = Dir['/sys/class/net/*'].select{ |f| File.symlink? f}
interfaces.each do |if_dir|
next if File.exists? "#{if_dir}/device/physfn"
if_name = if_dir.split('/')[-1]
port[if_name] = {
:name => if_name,

View File

@ -0,0 +1,120 @@
require File.join(File.dirname(__FILE__), '..','..','..','puppet/provider/lnx_base')
Puppet::Type.type(:l2_port).provide(:sriov, :parent => Puppet::Provider::Lnx_base) do
commands :ethtool_cmd => 'ethtool',
:pkill => 'pkill'
def self.instances
rv = []
ports = get_lnx_ports
ports.each_pair do |if_name, if_props|
debug("prefetching interface '#{if_name}'")
props = {
:ensure => :present,
:name => if_name,
:vendor_specific => {}
}
props.merge! if_props
sriov_numvfs = self.get_sriov_numvfs(if_name)
if sriov_numvfs and sriov_numvfs > 0
props[:provider] = 'sriov'
props[:vendor_specific] = {'sriov_numvfs' => sriov_numvfs}
end
props[:port_type] = props[:port_type].insert(0, props[:provider]).join(':')
if props[:provider] == 'sriov'
rv << new(props)
debug("PREFETCH properties for '#{if_name}': #{props}")
else
debug("SKIP properties for '#{if_name}': #{props}")
end
end
return rv
end
def self.sriov_numvfs_file(iface)
"/sys/class/net/#{iface}/device/sriov_numvfs"
end
def self.get_sriov_numvfs(iface)
File.read(self.sriov_numvfs_file(iface)).to_i if File.exists? self.sriov_numvfs_file(iface)
end
def sriov_numvfs
self.class.get_sriov_numvfs(@resource[:interface])
end
def sriov_numvfs=(val)
sriov_numvfs_file = self.class.sriov_numvfs_file(@resource[:interface])
raise "'#{@resource[:interface]}' does not support sriov." unless File.exists? sriov_numvfs_file
File.open(sriov_numvfs_file, "a") {|f| f << val}
end
def create
debug("CREATE resource: #{@resource}")
@old_property_hash = {}
@property_flush = {}.merge! @resource
self.class.interface_up(@resource[:interface])
end
def destroy
debug("DESTROY resource: #{@resource}")
self.sriov_numvfs = 0
self.class.interface_down(@resource[:interface], true)
end
def flush
unless @property_flush.empty?
unless ['', 'absent'].include? @property_flush[:mtu].to_s
self.class.set_mtu(@resource[:interface], @property_flush[:mtu])
end
vendor_specific = (@property_flush[:vendor_specific] || {})
sriov_numvfs = vendor_specific["sriov_numvfs"].to_i
if self.sriov_numvfs != sriov_numvfs
self.sriov_numvfs = 0
self.sriov_numvfs = sriov_numvfs
end
@property_hash = resource.to_hash
end
end
def vendor_specific
@property_hash[:vendor_specific] || :absent
end
def vendor_specific=(val)
old = @property_hash[:vendor_specific] || {}
@property_flush[:vendor_specific] = val.reject{|(k,v)| old[k.to_sym] == v }
end
def vlan_dev
@property_hash[:vlan_dev] || :absent
end
def vlan_dev=(val)
@property_flush[:vlan_dev] = val
end
def vlan_id
@property_hash[:vlan_id] || :absent
end
def vlan_id=(val)
@property_flush[:vlan_id] = val
end
def vlan_mode
@property_hash[:vlan_mode] || :absent
end
def vlan_mode=(val)
@property_flush[:vlan_mode] = val
end
def bond_master
@property_hash[:bond_master] || :absent
end
def bond_master=(val)
@property_flush[:bond_master] = val
end
end
# vim: set ts=2 sw=2 et :

View File

@ -346,6 +346,17 @@ Puppet::Type.newtype(:l23_stored_config) do
defaultto :absent
end
newproperty(:sriov_numvfs) do
desc "SR-IOV VFs number"
newvalues(/^\d+$/, 0, "", :none, :undef, :nil, :absent)
aliasvalue(0, :absent)
aliasvalue("", :absent)
aliasvalue(:none, :absent)
aliasvalue(:undef, :absent)
aliasvalue(:nil, :absent)
defaultto :absent
end
newproperty(:vendor_specific) do
desc "Hash of vendor specific properties"
#defaultto {} # no default value should be!!!

View File

@ -18,5 +18,11 @@ class l23network::l2::centos_upndown_scripts {
mode => '0755',
source => 'puppet:///modules/l23network/centos_ifup-pre-local',
} ->
file {'/etc/sysconfig/network-scripts/ifup-sriov':
ensure => present,
owner => 'root',
mode => '0755',
source => 'puppet:///modules/l23network/centos_ifup-sriov',
} ->
anchor { 'l23network::l2::centos_upndown_scripts': }
}

View File

@ -43,7 +43,7 @@ define l23network::l2::port (
$master = undef, # used for bonds automatically
$slave = undef, # used for bonds automatically
# $type = undef, # was '',
$vendor_specific = undef,
$vendor_specific = {},
$provider = undef,
# deprecated parameters, in the future ones will be moved to the vendor_specific hash
# $skip_existing = undef,
@ -142,7 +142,8 @@ define l23network::l2::port (
ethtool => $ethtool,
delay_while_up => $delay_while_up,
vendor_specific => $vendor_specific,
provider => $config_provider
provider => $config_provider,
sriov_numvfs => $vendor_specific['sriov_numvfs']
}
l2_port { $port_name :

View File

@ -29,11 +29,12 @@ describe 'l23network::l3::ifconfig', :type => :define do
it do
should contain_l23_stored_config('eth4').only_with({
'ensure' => 'present',
'name' => 'eth4',
'method' => 'dhcp',
'ipaddr' => 'dhcp',
'gateway' => nil,
'ensure' => 'present',
'name' => 'eth4',
'method' => 'dhcp',
'ipaddr' => 'dhcp',
'gateway' => nil,
'vendor_specific' => {},
})
end

View File

@ -29,11 +29,12 @@ describe 'l23network::l3::ifconfig', :type => :define do
it do
should contain_l23_stored_config('eth4').only_with({
'ensure' => 'present',
'name' => 'eth4',
'method' => 'manual',
'ipaddr' => 'none',
'ipaddr_aliases' => nil,
'ensure' => 'present',
'name' => 'eth4',
'method' => 'manual',
'ipaddr' => 'none',
'ipaddr_aliases' => nil,
'vendor_specific' => {},
})
end

View File

@ -0,0 +1,67 @@
require 'spec_helper'
describe 'l23network::l2::port', :type => :define do
let(:title) { 'Spec for l23network::l2::port for SRIOV' }
let(:facts) { {
:osfamily => 'Debian',
:operatingsystem => 'Ubuntu',
:kernel => 'Linux',
:l23_os => 'ubuntu',
:l3_fqdn_hostname => 'stupid_hostname',
} }
let(:pre_condition) { [
"class {'l23network': }"
] }
context 'Create SRIOV port' do
let(:params) do
{
:name => 'eth0',
:provider => 'sriov',
:vendor_specific => {
'sriov_numvfs' => 7,
'physnet' => 'physnet2'
}
}
end
before(:each) do
puppet_debug_override()
end
it do
should compile.with_all_deps
end
it do
should contain_l23_stored_config('eth0').with(
{
'ensure' => 'present',
'use_ovs' => nil,
'if_type' => nil,
'method' => nil,
'ipaddr' => nil,
'gateway' => nil,
'vendor_specific' => {
'sriov_numvfs' => 7,
'physnet' => 'physnet2'
}
}
)
end
it do
should contain_l2_port('eth0').with(
{
'ensure' => 'present',
'vendor_specific' => {
'sriov_numvfs' => 7,
'physnet' => 'physnet2'
}
}
).that_requires('L23_stored_config[eth0]')
end
end
end
# vim: set ts=2 sw=2 et

View File

@ -0,0 +1,6 @@
DEVICE=eth0
ONBOOT=yes
BOOTPROTO=none
TYPE=sriov
DEVICETYPE=sriov
SRIOV_NUMFS=63

View File

@ -0,0 +1,5 @@
auto p2p4
iface p2p4 inet manual
up echo 0 > /sys/class/net/p2p4/device/sriov_numvfs
up echo ${IF_SRIOV_NUMVFS:-0} > /sys/class/net/enp1s0f0/device/sriov_numvfs
sriov_numvfs 7

View File

@ -0,0 +1,73 @@
require 'spec_helper'
require 'yaml'
require 'puppetx/l23_hash_tools'
describe Puppet::Parser::Functions.function(:get_nic_passthrough_whitelist) do
let(:network_scheme) do
<<eof
---
version: 1.1
provider: lnx
interfaces:
enp1s0f0:
mtu: 1500
enp1s0f1:
mtu: 1500
transformations:
- action: add-port
name: enp1s0f0
provider: sriov
vendor_specific:
sriov_numvfs: 63
physnet: physnet1
- action: add-port
name: enp1s0f1
provider: sriov
vendor_specific:
sriov_numvfs: 63
physnet: physnet2
- action: add-port
name: eth0
provider: sriov
vendor_specific:
sriov_numvfs: 4
endpoints:
roles:
eof
end
let(:scope) { PuppetlabsSpec::PuppetInternals.scope }
subject do
function_name = Puppet::Parser::Functions.function(:get_nic_passthrough_whitelist)
scope.method(function_name)
end
context "get_nic_passthrough_whitelist() usage" do
before(:each) do
scope.stubs(:lookupvar).with('l3_fqdn_hostname').returns('node1.tld')
L23network::Scheme.set_config(
scope.lookupvar('l3_fqdn_hostname'),
L23network.sanitize_keys_in_hash(YAML.load(network_scheme))
)
end
it 'should exist' do
subject == Puppet::Parser::Functions.function(:get_pci_passthrough_whitelist)
end
it 'should return sriov mappings from transformations' do
should run.with_params('sriov').and_return([
{"devname" => "enp1s0f0", "physical_network" => "physnet1"},
{"devname" => "enp1s0f1", "physical_network" => "physnet2"}
])
end
it 'should return empty mapping from transformations' do
should run.with_params('dumb').and_return(nil)
end
end
end

View File

@ -0,0 +1,100 @@
require 'spec_helper'
require 'yaml'
describe Puppet::Type.type(:l23_stored_config).provider(:sriov_centos7) do
let(:facts) do
{
:osfamily => 'Redhat',
:operatingsystem => 'CentOS',
:l23_os => 'centos7',
}
end
let(:input_data) do
{
:eth0 => {
:name => "eth0",
:provider => 'sriov_centos7',
:sriov_numvfs => 63
}
}
end
let(:resources) do
resources = {}
input_data.each do |name, res|
resources.store name, Puppet::Type.type(:l23_stored_config).new(res)
end
return resources
end
let(:providers) do
providers = {}
resources.each do |name, resource|
provider = resource.provider
if ENV['SPEC_PUPPET_DEBUG']
class << provider
def debug(msg)
puts msg
end
end
end
provider.create
providers.store name, provider
end
providers
end
before(:each) do
puppet_debug_override()
end
def fixture_path
File.join(PROJECT_ROOT, 'spec', 'fixtures', 'provider', 'l23_stored_config', 'sriov_centos7_spec')
end
def fixture_file(file)
File.join(fixture_path, file)
end
def fixture_data(file)
File.read(fixture_file(file))
end
context "parsing config files" do
let(:res) { subject.class.parse_file('eth0', fixture_data('ifcfg-eth0'))[0] }
it { expect(res[:method]).to eq 'manual' }
it { expect(res[:onboot]).to eq true }
it { expect(res[:if_type]).to eq :sriov }
it { expect(res[:name]).to eq 'eth0' }
it { expect(res[:sriov_numvfs]).to eq 63 }
it { expect(res[:provider]).to eq :sriov_centos7 }
end
context "when formatting resources" do
subject { providers[:eth0] }
let(:res) { subject.class.format_file('filepath', [subject]) }
it { expect(res).to match %r(DEVICE=eth0) }
it { expect(res).to match %r(ONBOOT=yes) }
it { expect(res).to match %r(BOOTPROTO=none) }
it { expect(res).to match %r(^TYPE=sriov) }
it { expect(res).to match %r(DEVICETYPE=sriov) }
it { expect(res).to match %r(SRIOV_NUMFS=63) }
it { expect(res).not_to match %r(IPADDR=.*) }
it { expect(res).not_to match %r(GATEWAY=.*) }
it { expect(res).not_to match %r(PREFIX=.*) }
it { expect(res.split(/\n/).reject{|x| x=~/(^\s*$)|(^#.*$)/}.length). to eq(6) }
it 'should not remove GATEWAY from /etc/sysconfig/network' do
subject.class.expects(:write_file).times(0)
res
end
it 'should not write route to /etc/sysconfig/network-scripts/route-eth0' do
subject.class.expects(:write_file).times(0)
res
end
end
end

View File

@ -0,0 +1,79 @@
require 'spec_helper'
resources_map = {
:'p2p4' => {
:name => 'p2p4',
:provider => 'sriov_ubuntu',
:sriov_numvfs => 7
},
}
describe Puppet::Type.type(:l23_stored_config).provider(:sriov_ubuntu) do
let(:input_data) { resources_map}
let(:resources) do
resources = {}
input_data.each do |name, res|
resources.store name, Puppet::Type.type(:l23_stored_config).new(res)
end
return resources
end
let(:providers) do
providers = {}
resources.each do |name, resource|
provider = resource.provider
if ENV['SPEC_PUPPET_DEBUG']
class << provider
def debug(msg)
puts msg
end
end
end
provider.create
providers.store name, provider
end
return providers
end
before(:each) do
puppet_debug_override()
end
def fixture_path
File.join(PROJECT_ROOT, 'spec', 'fixtures', 'provider', 'l23_stored_config', 'ubuntu_ports')
end
def fixture_file(file)
File.join(fixture_path, file)
end
def fixture_data(file)
File.read(fixture_file(file))
end
context "formatting config files" do
context 'SRIOV port p2p4' do
subject { providers[:'p2p4'] }
let(:cfg_file) { subject.class.format_file('filepath', [subject]) }
it { expect(cfg_file).to match(/auto\s+p2p4/) }
it { expect(cfg_file).to match(/iface\s+p2p4\s+inet\s+manual/) }
it { expect(cfg_file).to match(/up\s+echo\s+0\s+>\s*\/sys\/class\/net\/p2p4\/device\/sriov_numvfs/) }
it { expect(cfg_file).to match(/sriov_numvfs\s+7/) }
it { expect(cfg_file.split(/\n/).reject{|x| x=~/(^\s*$)|(^#.*$)/}.length). to eq(5) }
end
end
context "parsing config files" do
let(:res) { subject.class.parse_file('p2p4', fixture_data('ifcfg-p2p4'))[0] }
it { expect(res[:method]).to eq :manual }
it { expect(res[:onboot]).to eq true }
it { expect(res[:name]).to eq 'p2p4' }
it { expect(res[:sriov_numvfs]).to eq 7 }
it { expect(res[:provider]).to eq :sriov_ubuntu }
end
end

View File

@ -0,0 +1,34 @@
require 'spec_helper'
describe Puppet::Type.type(:l2_port).provider(:sriov) do
let(:resource_port) {
Puppet::Type.type(:l2_port).new(
:provider => 'sriov',
:name => 'eth0',
:vendor_specific => {
:sriov_numvfs => 63,
:physnet => 'physnet2'
},
)
}
let(:provider_port) { resource_port.provider }
describe "sriov port" do
before(:each) do
puppet_debug_override()
end
it "create" do
File.stubs(:exists?).with('/sys/class/net/eth0/device/sriov_numvfs').returns(true)
File.stubs(:open).with('/sys/class/net/eth0/device/sriov_numvfs', 'a').returns(true)
File.stubs(:read).with('/sys/class/net/eth0/device/sriov_numvfs').returns(63)
provider_port.class.stubs(:interface_up).with('eth0').returns(true)
provider_port.create
provider_port.flush
end
end
end