Fix ovs2ovs saving problem during reboot

* Implement saving configs for ovs2ovs patches
* Implement vlan_ids as well
* Test coverage

Change-Id: I288da3b859d8a25f2428cfa785eeedb72c472eaa
Closes-bug: #1495534
This commit is contained in:
Stanislav Makar 2015-09-24 11:56:27 +00:00
parent 704b60496f
commit 4a2aaff731
14 changed files with 463 additions and 85 deletions

View File

@ -89,11 +89,18 @@ Puppet::Type.type(:l23_stored_config).provide(:ovs_ubuntu, :parent => Puppet::Pr
header << "allow-#{bridge} #{provider.name}"
props[:ovs_type] = 'OVSBond'
props[:bridge] = bridge
elsif provider.if_type.to_s == 'patch'
header << "auto #{provider.name}" if provider.onboot
header << "allow-#{bridge} #{provider.name}"
props[:bridge] = bridge
props[:ovs_type] = 'OVSPort'
provider.mtu = nil
else
header << "auto #{provider.name}" if provider.onboot
header << "allow-#{bridge} #{provider.name}"
props[:ovs_type] = 'OVSIntPort'
props[:bridge] = bridge
provider.jacks = nil
end
# Add iface header
header << "iface #{provider.name} inet #{provider.method}"
@ -101,5 +108,38 @@ Puppet::Type.type(:l23_stored_config).provide(:ovs_ubuntu, :parent => Puppet::Pr
return header, props
end
def self.collected_properties
rv = super
rv.merge!({
:jacks => {
:detect_re => /(ovs_)?extra\s+--\s+set\s+Interface\s+(p_.*-[0 1])\s+type=patch\s+options:peer=(p_.*-[0 1])/,
:detect_shift => 3,
},
:vlan_id => {
:detect_re => /(ovs_)?extra\s+--\s+set\s+Port\s+(p_.*-[0 1])\s+tag=(\d+)/,
:detect_shift => 3,
},
})
return rv
end
def self.unmangle__jacks(provider, data)
rv = []
rv << "ovs_extra -- set Interface #{provider.name} type=patch options:peer=#{data.join()}"
end
def self.unmangle__vlan_id(provider, data)
rv = []
rv << "ovs_extra -- set Port #{provider.name} tag=#{provider.vlan_id}"
end
def self.mangle__jacks(data)
[data.join()]
end
def self.mangle__vlan_id(data)
data.join()
end
end
# vim: set ts=2 sw=2 et :
# vim: set ts=2 sw=2 et :

View File

@ -404,7 +404,7 @@ class Puppet::Provider::L23_stored_config_ubuntu < Puppet::Provider::L23_stored_
#add to content unmangled collected-properties
collected_properties.keys.each do |type_name|
data = provider.send(type_name)
if !(data.nil? or data.empty?)
if ((!data.nil? or !data.empty?) and data.to_s != 'absent')
mangle_method_name="unmangle__#{type_name}"
if self.respond_to?(mangle_method_name)
rv = self.send(mangle_method_name, provider, data)

View File

@ -80,11 +80,11 @@ Puppet::Type.type(:l2_patch).provide(:ovs, :parent => Puppet::Provider::Ovs_base
@old_property_hash = {}
@property_flush = {}.merge! @resource
bridges = self.class.get_bridges_order_for_patch(@resource[:bridges])
@property_flush[:bridges] = bridges
#
debug("Bridges: '#{bridges.join(', ')}'.")
debug("Bridges: '#{@resource[:bridges].join(', ')}'.")
if File.directory?("/sys/class/net/#{bridges[1]}/bridge")
# creating 'cross' OVS-to-lnx patchcord
@property_flush[:bridges] = bridges
@resource[:cross] = true
lnx_port_br_mapping = self.class.get_lnx_port_bridges_pairs()
jack = L23network.get_jack_name(bridges,0)
@ -109,6 +109,7 @@ Puppet::Type.type(:l2_patch).provide(:ovs, :parent => Puppet::Provider::Ovs_base
self.class.interface_up(jack)
else
# creating OVS-to-OVS patchcord
bridges = @resource[:bridges]
jacks = []
jacks << L23network.get_jack_name(bridges,0)
jacks << L23network.get_jack_name(bridges,1)
@ -218,4 +219,4 @@ debug(cmds)
end
end
# vim: set ts=2 sw=2 et :
# vim: set ts=2 sw=2 et :

View File

@ -36,7 +36,7 @@ Puppet::Type.newtype(:l23_stored_config) do
newproperty(:if_type) do
desc "Device type. Service property, shouldn't be setting by puppet"
newvalues(:ethernet, :bridge, :bond)
newvalues(:ethernet, :bridge, :bond, :patch)
end
newproperty(:if_provider) do
@ -127,14 +127,14 @@ Puppet::Type.newtype(:l23_stored_config) do
aliasvalue(0, :absent)
defaultto :absent
validate do |val|
min_vid = 1
min_vid = 0
max_vid = 4094
if ! (val.to_s == 'absent' or (min_vid .. max_vid).include?(val.to_i))
raise ArgumentError, "'#{val}' is not a valid 802.1q NALN_ID (must be a integer value in range (#{min_vid} .. #{max_vid})"
end
end
munge do |val|
((val == :absent) ? :absent : val.to_i)
((val == :absent or val.to_s == '0') ? :absent : val.to_i)
end
end

View File

@ -32,12 +32,21 @@ define l23network::l2::patch (
$bridge2_provider = get_provider_for('L2_bridge', $bridges[1])
if $bridge1_provider == 'ovs' and $bridge2_provider == 'ovs' {
$act_bridges = sort($bridges)
$do_not_create_stored_config = true
$act_bridges = sort($bridges)
$ovs2ovs = true
if $vlan_ids == undef {
$act_vlan_ids = [0,0]
} elsif $act_bridges == $bridges {
$act_vlan_ids = $vlan_ids
} else {
$act_vlan_ids = [$vlan_ids[1], $vlan_ids[0]]
}
} elsif $bridge1_provider == 'ovs' and $bridge2_provider == 'lnx' {
$act_bridges = [$bridges[0], $bridges[1]]
$act_bridges = [$bridges[0], $bridges[1]]
$act_vlan_ids = undef
} elsif $bridge1_provider == 'lnx' and $bridge2_provider == 'ovs' {
$act_bridges = [$bridges[1], $bridges[0]]
$act_bridges = [$bridges[1], $bridges[0]]
$act_vlan_ids = undef
} else {
$act_bridges = $bridges
}
@ -52,11 +61,37 @@ define l23network::l2::patch (
$config_provider = undef
}
if ! $do_not_create_stored_config {
# we do not create any configs for ovs2ovs patchcords, because
# neither CenOS5 nor Ubuntu with OVS < 2.4 supports creating patch resources
# from network config files. But OVSDB stores patch configuration and this is
# enough to restore after reboot
if $ovs2ovs {
if ! defined(L23_stored_config[$patch_jacks_names[0]]) {
l23_stored_config { $patch_jacks_names[0]: }
}
L23_stored_config <| title == $patch_jacks_names[0] |> {
ensure => $ensure,
if_type => 'patch',
bridge => $act_bridges[0],
vlan_id => $act_vlan_ids[0],
jacks => $patch_jacks_names[1],
onboot => true,
vendor_specific => $vendor_specific,
provider => $config_provider
}
L23_stored_config[$patch_jacks_names[0]] -> L2_patch[$patch_name]
if ! defined(L23_stored_config[$patch_jacks_names[1]]) {
l23_stored_config { $patch_jacks_names[1]: }
}
L23_stored_config <| title == $patch_jacks_names[1] |> {
ensure => $ensure,
if_type => 'patch',
bridge => $act_bridges[1],
vlan_id => $act_vlan_ids[1],
jacks => $patch_jacks_names[0],
onboot => true,
vendor_specific => $vendor_specific,
provider => $config_provider
}
L23_stored_config[$patch_jacks_names[1]] -> L2_patch[$patch_name]
} else {
if ! defined(L23_stored_config[$patch_jacks_names[0]]) {
# we use only one (first) patch jack name here and later,
# because a both jacks for patch are created by
@ -81,7 +116,7 @@ define l23network::l2::patch (
bridges => $act_bridges,
use_ovs => $use_ovs,
jacks => $patch_jacks_names,
vlan_ids => $vlan_ids,
vlan_ids => $act_vlan_ids,
mtu => $mtu,
vendor_specific => $vendor_specific,
provider => $provider

View File

@ -92,7 +92,7 @@ end
should contain_l2_patch('patch__br-ovs--br1').with({
'ensure' => 'present',
'bridges' => ['br-ovs', 'br1'],
'vlan_ids' => ['0', '0'],
'vlan_ids' => nil,
'provider' => 'ovs'
})
end

View File

@ -20,6 +20,9 @@ network_scheme:
bridges:
- br-ovs2
- br-ovs1
vlan_ids:
- 200
- 100
provider: ovs
endpoints: {}
roles: {}
@ -60,7 +63,7 @@ end
it do
should contain_l23_stored_config('br-ovs1').with({
'ensure' => 'present',
'provider' => 'ovs_ubuntu'
'provider' => 'ovs_ubuntu',
})
end
@ -89,7 +92,7 @@ end
should contain_l2_patch('patch__br-ovs1--br-ovs2').with({
'ensure' => 'present',
'bridges' => ['br-ovs1', 'br-ovs2'],
'vlan_ids' => ['0', '0'],
'vlan_ids' => ['100', '200'],
'provider' => 'ovs'
})
end
@ -99,9 +102,107 @@ end
end
it do
should_not contain_l23_stored_config('p_f277dc2b-0')
should contain_l23_stored_config('p_f277dc2b-0').with({
'bridge' => 'br-ovs1',
'vlan_id' => '100',
'jacks' => 'p_f277dc2b-1',
})
end
it do
should contain_l23_stored_config('p_f277dc2b-1').with({
'bridge' => 'br-ovs2',
'vlan_id' => '200',
'jacks' => 'p_f277dc2b-0',
})
end
end
end
# vim: set ts=2 sw=2 et
describe 'l23network::examples::run_network_scheme', :type => :class do
let(:network_scheme) do
<<eof
---
network_scheme:
version: 1.1
provider: lnx
interfaces:
eth1: {}
transformations:
- action: add-br
name: br-ovs2
provider: ovs
- action: add-br
name: br-ovs1
provider: ovs
- action: add-patch
bridges:
- br-ovs1
- br-ovs2
vlan_ids:
- 200
- 100
provider: ovs
endpoints: {}
roles: {}
eof
end
context 'Patch between two OVS bridges and vlan ids' do
let(:facts) {
{
:osfamily => 'Debian',
:operatingsystem => 'Ubuntu',
:kernel => 'Linux',
:l23_os => 'ubuntu',
:l3_fqdn_hostname => 'stupid_hostname',
}
}
let(:params) do {
:settings_yaml => network_scheme,
} end
get_provider_for = {}
before(:each) do
puppet_debug_override()
Puppet::Parser::Functions.newfunction(:get_provider_for, :type => :rvalue) {
|args| get_provider_for.call(args[0], args[1])
}
get_provider_for.stubs(:call).with('L2_bridge', 'br-ovs1').returns('ovs')
get_provider_for.stubs(:call).with('L2_bridge', 'br-ovs2').returns('ovs')
end
it do
should compile.with_all_deps
end
it do
should contain_l2_patch('patch__br-ovs1--br-ovs2').with({
'bridges' => ['br-ovs1', 'br-ovs2'],
'vlan_ids' => ['200', '100'],
})
end
it do
should contain_l23_stored_config('p_f277dc2b-0').with({
'bridge' => 'br-ovs1',
'vlan_id' => '200',
})
end
it do
should contain_l23_stored_config('p_f277dc2b-1').with({
'bridge' => 'br-ovs2',
'vlan_id' => '100',
})
end
end
end
# vim: set ts=2 sw=2 et

View File

@ -89,36 +89,6 @@ describe 'l23network::l2::patch', :type => :define do
end
end
context 'Patch between two bridges, with explicitly defined OVS provider.' do
let(:params) do
{
:bridges => ['br1', 'br2'],
:provider => 'ovs'
}
end
it do
should compile.with_all_deps
end
it do
should contain_l23_stored_config('p_39a440c1-0').with({
'ipaddr' => nil,
'gateway' => nil,
'onboot' => true,
'bridge' => ['br1', 'br2'],
'jacks' => ['p_39a440c1-0', 'p_39a440c1-1']
})
end
it do
should contain_l2_patch('patch__br1--br2').with({
'ensure' => 'present',
'bridges' => ['br1', 'br2'],
'jacks' => ['p_39a440c1-0', 'p_39a440c1-1']
}).that_requires('L23_stored_config[p_39a440c1-0]')
end
end
context 'Patch, which has jumbo frames' do
let(:params) do
@ -196,34 +166,6 @@ describe 'l23network::l2::patch', :type => :define do
end
end
context 'Tagged patchcord with explicitly defined OVS provider.' do
let(:params) do
{
:bridges => ['br1', 'br2'],
:vlan_ids => ['101', '202'],
:provider => 'ovs'
}
end
it do
should compile.with_all_deps
end
it do
should contain_l23_stored_config('p_39a440c1-0').with({
'bridge' => ['br1', 'br2'],
'jacks' => ['p_39a440c1-0', 'p_39a440c1-1']
})
end
it do
should contain_l2_patch('patch__br1--br2').with({
'ensure' => 'present',
'vlan_ids' => ['101', '202'],
'bridges' => ['br1', 'br2'],
}).that_requires('L23_stored_config[p_39a440c1-0]')
end
end
end
@ -265,12 +207,22 @@ describe 'l23network::l2::patch', :type => :define do
end
it do
should_not contain_l23_stored_config('p_39a440c1-0').with({
should contain_l23_stored_config('p_39a440c1-0').with({
'ipaddr' => nil,
'gateway' => nil,
'onboot' => true,
'bridge' => ['br1', 'br2'],
'jacks' => ['p_39a440c1-0', 'p_39a440c1-1']
'bridge' => ['br1'],
'jacks' => ['p_39a440c1-1']
})
end
it do
should contain_l23_stored_config('p_39a440c1-1').with({
'ipaddr' => nil,
'gateway' => nil,
'onboot' => true,
'bridge' => ['br2'],
'jacks' => ['p_39a440c1-0']
})
end
@ -282,6 +234,45 @@ describe 'l23network::l2::patch', :type => :define do
end
end
context 'Tagged patchcord with explicitly defined OVS provider.' do
let(:params) do
{
:bridges => ['br1', 'br2'],
:vlan_ids => ['101', '202'],
:provider => 'ovs'
}
end
it do
should compile.with_all_deps
end
it do
should contain_l23_stored_config('p_39a440c1-0').with({
'bridge' => ['br1'],
'jacks' => ['p_39a440c1-1'],
'vlan_id' => '101',
})
end
it do
should contain_l23_stored_config('p_39a440c1-1').with({
'bridge' => ['br2'],
'jacks' => ['p_39a440c1-0'],
'vlan_id' => '202',
})
end
it do
should contain_l2_patch('patch__br1--br2').with({
'ensure' => 'present',
'vlan_ids' => ['101', '202'],
'bridges' => ['br1', 'br2'],
}).that_requires('L23_stored_config[p_39a440c1-0]')
end
end
end

View File

@ -0,0 +1,5 @@
auto br-ovs1
allow-ovs br-ovs1
iface br-ovs1 inet manual
ovs_type OVSBridge
ovs_ports p_33470efd-0

View File

@ -0,0 +1,5 @@
auto br-ovs2
allow-ovs br-ovs2
iface br-ovs2 inet manual
ovs_type OVSBridge
ovs_ports p_33470efd-1

View File

@ -0,0 +1,7 @@
auto p_33470efd-0
allow-br-ovs1 p_33470efd-0
iface p_33470efd-0 inet manual
ovs_type OVSPort
ovs_bridge br-ovs1
ovs_extra -- set Interface p_33470efd-0 type=patch options:peer=p_33470efd-1
ovs_extra -- set Port p_33470efd-0 tag=100

View File

@ -0,0 +1,7 @@
auto p_33470efd-1
allow-br-ovs2 p_33470efd-1
iface p_33470efd-1 inet manual
ovs_type OVSPort
ovs_bridge br-ovs2
ovs_extra -- set Interface p_33470efd-1 type=patch options:peer=p_33470efd-0
ovs_extra -- set Port p_33470efd-1 tag=200

View File

@ -103,6 +103,7 @@ describe Puppet::Type.type(:l23_stored_config).provider(:ovs_ubuntu) do
it { expect(cfg_file).to match(/iface\s+p_33470efd-0\s+inet\s+manual/) }
it { expect(cfg_file).to match(/ovs_type\s+OVSIntPort/) }
it { expect(cfg_file).to match(/ovs_bridge\s+br-ovs/) }
it { expect(cfg_file).not_to match(/ovs_extra/) }
it { expect(cfg_file.split(/\n/).reject{|x| x=~/^\s*$/}.length). to eq(5) }
end
end
@ -192,4 +193,4 @@ describe Puppet::Type.type(:l23_stored_config).provider(:lnx_ubuntu) do
it { expect(res[:bridge_ports]).to eq ['p_33470efd-0'] }
it { expect(res[:if_provider].to_s).to eq 'lnx' }
end
end
end

View File

@ -0,0 +1,185 @@
require 'spec_helper'
resources_map = {
:'br-ovs1' => {
:name => "br-ovs1",
:onboot => "yes",
:if_type => "bridge",
:bridge_ports => ['p_33470efd-0'],
:provider => "ovs_ubuntu",
},
:'br-ovs2' => {
:name => "br-ovs2",
:onboot => "yes",
:method => "static",
:if_type => "bridge",
:ipaddr => "192.168.88.2/24",
:bridge_ports => ['p_33470efd-0'],
:provider => "lnx_ubuntu",
},
:'p_33470efd-0' => {
:name => "p_33470efd-0",
:if_type => "patch",
:bridge => "br-ovs1",
:vlan_id => '100',
:jacks => 'p_33470efd-1',
:provider => "ovs_ubuntu",
},
:'p_33470efd-1' => {
:name => "p_33470efd-1",
:if_type => "patch",
:bridge => "br-ovs2",
:vlan_id => '200',
:jacks => 'p_33470efd-0',
:provider => "ovs_ubuntu",
},
}
# This test is functional continue of .spec/classes/ovs2ovs_patch__spec.rb
describe Puppet::Type.type(:l23_stored_config).provider(:ovs_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', 'ovs_ubuntu__ovs2ovs_patch__spec')
end
def fixture_file(file)
File.join(fixture_path, file)
end
def fixture_data(file)
File.read(fixture_file(file))
end
context "when formating config files" do
context 'for OVS bridge br-ovs1' do
subject { providers[:'br-ovs1'] }
let(:cfg_file) { subject.class.format_file('filepath', [subject]) }
it { expect(cfg_file).to match(/auto\s+br-ovs1/) }
it { expect(cfg_file).to match(/allow-ovs\s+br-ovs1/) }
it { expect(cfg_file).to match(/iface\s+br-ovs1\s+inet\s+manual/) }
it { expect(cfg_file).to match(/ovs_type\s+OVSBridge/) }
it { expect(cfg_file).to match(/ovs_ports\s+p_33470efd-0/) }
it { expect(cfg_file.split(/\n/).reject{|x| x=~/^\s*$/}.length). to eq(5) }
end
context 'for OVS bridge br-ovs2' do
subject { providers[:'br-ovs2'] }
let(:cfg_file) { subject.class.format_file('filepath', [subject]) }
it { expect(cfg_file).to match(/auto\s+br-ovs2/) }
it { expect(cfg_file).to match(/iface\s+br-ovs2\s+inet\s+static/) }
it { expect(cfg_file).to match(/bridge_ports\s+p_33470efd-0/) }
it { expect(cfg_file).to match(/address\s+192.168.88.2\/24/) }
it { expect(cfg_file.split(/\n/).reject{|x| x=~/^\s*$/}.length). to eq(4) }
end
context 'for ovs2ovs patchcord p_33470efd-0' do
subject { providers[:'p_33470efd-0'] }
let(:cfg_file) { subject.class.format_file('filepath', [subject]) }
it { expect(cfg_file).to match(/auto\s+p_33470efd-0/) }
it { expect(cfg_file).to match(/allow-br-ovs1\s+p_33470efd-0/) }
it { expect(cfg_file).to match(/iface\s+p_33470efd-0\s+inet\s+manual/) }
it { expect(cfg_file).to match(/ovs_type\s+OVSPort/) }
it { expect(cfg_file).to match(/ovs_bridge\s+br-ovs1/) }
it { expect(cfg_file).to match(/ovs_extra\s+--\s+set\s+Interface\s+p_33470efd-0\s+type=patch\s+options:peer=p_33470efd-1/) }
it { expect(cfg_file).to match(/ovs_extra\s+--\s+set\s+Port\s+p_33470efd-0\s+tag=100/) }
it { expect(cfg_file.split(/\n/).reject{|x| x=~/^\s*$/}.length). to eq(7) }
end
context 'for ovs2ovs patchcord p_33470efd-1' do
subject { providers[:'p_33470efd-1'] }
let(:cfg_file) { subject.class.format_file('filepath', [subject]) }
it { expect(cfg_file).to match(/auto\s+p_33470efd-1/) }
it { expect(cfg_file).to match(/allow-br-ovs2\s+p_33470efd-1/) }
it { expect(cfg_file).to match(/iface\s+p_33470efd-1\s+inet\s+manual/) }
it { expect(cfg_file).to match(/ovs_type\s+OVSPort/) }
it { expect(cfg_file).to match(/ovs_bridge\s+br-ovs2/) }
it { expect(cfg_file).to match(/ovs_extra\s+--\s+set\s+Interface\s+p_33470efd-1\s+type=patch\s+options:peer=p_33470efd-0/) }
it { expect(cfg_file).to match(/ovs_extra\s+--\s+set\s+Port\s+p_33470efd-1\s+tag=200/) }
it { expect(cfg_file.split(/\n/).reject{|x| x=~/^\s*$/}.length). to eq(7) }
end
end
context "when parsing config files" do
context 'for OVS bridge br-ovs1' do
let(:res) { subject.class.parse_file('br-ovs1', fixture_data('ifcfg-br-ovs1'))[0] }
it { expect(res[:method]).to eq :manual }
it { expect(res[:onboot]).to eq true }
it { expect(res[:name]).to eq 'br-ovs1' }
it { expect(res[:bridge_ports]).to eq ['p_33470efd-0'] }
it { expect(res[:if_type].to_s).to eq 'bridge' }
it { expect(res[:if_provider].to_s).to eq 'ovs' }
end
context 'for OVS bridge br-ovs2' do
let(:res) { subject.class.parse_file('br-ovs2', fixture_data('ifcfg-br-ovs2'))[0] }
it { expect(res[:method]).to eq :manual }
it { expect(res[:onboot]).to eq true }
it { expect(res[:name]).to eq 'br-ovs2' }
it { expect(res[:bridge_ports]).to eq ['p_33470efd-1'] }
it { expect(res[:if_type].to_s).to eq 'bridge' }
it { expect(res[:if_provider].to_s).to eq 'ovs' }
end
context 'for ovs2ovs patchcord p_33470efd-0' do
let(:res) { subject.class.parse_file('p_33470efd-0', fixture_data('ifcfg-p_33470efd-0'))[0] }
it { expect(res[:method]).to eq :manual }
it { expect(res[:onboot]).to eq true }
it { expect(res[:name]).to eq 'p_33470efd-0' }
it { expect(res[:bridge]).to eq "br-ovs1" }
it { expect(res[:vlan_id]).to eq '100' }
it { expect(res[:jacks]).to eq ['p_33470efd-1'] }
it { expect(res[:if_provider].to_s).to eq 'ovs' }
end
context 'for ovs2ovs patchcord p_33470efd-1' do
let(:res) { subject.class.parse_file('p_33470efd-1', fixture_data('ifcfg-p_33470efd-1'))[0] }
it { expect(res[:method]).to eq :manual }
it { expect(res[:onboot]).to eq true }
it { expect(res[:name]).to eq 'p_33470efd-1' }
it { expect(res[:bridge]).to eq "br-ovs2" }
it { expect(res[:vlan_id]).to eq '200' }
it { expect(res[:jacks]).to eq ['p_33470efd-0'] }
it { expect(res[:if_provider].to_s).to eq 'ovs' }
end
end
end