VXLAN support

This patch is implementing support for VXLAN tunnel
type for OVS plugin.

Refactored neutron::plugins::ml2::validate_* to functions
so they can be used in other manifests in module.

Change-Id: Ic51ed794b27069809a151dc365ca108c8300c277
This commit is contained in:
Martin Magr 2013-11-07 17:05:18 +01:00
parent 37d1c5b810
commit 766561df83
12 changed files with 275 additions and 97 deletions

View File

@ -0,0 +1,44 @@
#
# Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
#
# Author: Emilien Macchi <emilien.macchi@enovance.com>
# Martin Magr <mmagr@redhat.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# Advanced validation for VLAN configuration
#
module Puppet::Parser::Functions
newfunction(:validate_network_vlan_ranges) do |args|
value = args[0]
if not value.kind_of?(Array)
value = [value]
end
value.each do |range|
if m = /^(.+:)?(\d+):(\d+)$/.match(range)
first_id = Integer(m[-2])
second_id = Integer(m[-1])
if (first_id > 4094) || (second_id > 4094)
raise Puppet::Error, "vlan id are invalid."
end
if ((second_id - first_id) < 0 )
raise Puppet::Error, "network vlan ranges are invalid."
end
elsif range
raise Puppet::Error, "network vlan ranges are invalid."
end
end
end
end

View File

@ -0,0 +1,44 @@
#
# Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
#
# Author: Emilien Macchi <emilien.macchi@enovance.com>
# Martin Magr <mmagr@redhat.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# Advanced validation when using GRE
#
module Puppet::Parser::Functions
newfunction(:validate_tunnel_id_ranges) do |args|
value = args[0]
if not value.kind_of?(Array)
value = [value]
end
value.each do |range|
if m = /^(\d+):(\d+)$/.match(range)
first_id = Integer(m[1])
second_id = Integer(m[2])
if ((second_id - first_id) > 1000000)
raise Puppet::Error, "tunnel id ranges are to large."
end
if ((second_id - first_id) < 0 )
raise Puppet::Error, "tunnel id ranges are invalid."
end
elsif range
raise Puppet::Error, "tunnel id ranges are invalid."
end
end
end
end

View File

@ -0,0 +1,47 @@
#
# Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
#
# Author: Emilien Macchi <emilien.macchi@enovance.com>
# Martin Magr <mmagr@redhat.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# Advanced validation when using VXLAN
#
module Puppet::Parser::Functions
newfunction(:validate_vni_ranges) do |args|
value = args[0]
if not value.kind_of?(Array)
value = [value]
end
value.each do |range|
if m = /^(\d+):(\d+)$/.match(range)
first_id = Integer(m[1])
second_id = Integer(m[2])
if not (0 <= first_id && first_id <= 16777215)
raise Puppet::Error, "vni ranges are invalid."
end
if not (0 <= second_id && second_id <= 16777215)
raise Puppet::Error, "vni ranges are invalid."
end
if (second_id < first_id)
raise Puppet::Error, "vni ranges are invalid."
end
elsif range
raise Puppet::Error, "vni ranges are invalid."
end
end
end
end

View File

@ -2,6 +2,7 @@
# Copyright (C) 2013 eNovance SAS <licensing@enovance.com> # Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
# #
# Author: Emilien Macchi <emilien.macchi@enovance.com> # Author: Emilien Macchi <emilien.macchi@enovance.com>
# Martin Magr <mmagr@redhat.com>
# #
# Licensed under the Apache License, Version 2.0 (the "License"); you may # Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain # not use this file except in compliance with the License. You may obtain
@ -15,16 +16,17 @@
# License for the specific language governing permissions and limitations # License for the specific language governing permissions and limitations
# under the License. # under the License.
# #
# Advanced testing for VLAN configuration # Advanced validation for VXLAN UDP port configuration
# #
define neutron::plugins::ml2::validate_network_vlan_ranges { module Puppet::Parser::Functions
$first_id = regsubst($name,'^(\d+):(\d+)$','\1') + 0 newfunction(:validate_vxlan_udp_port) do |args|
$second_id = regsubst($name,'^(\d+):(\d+)$','\2') + 0 value = Integer(args[0])
if ($first_id > 4094) or ($second_id > 4094) {
fail('vlan id are invalid.') # check if port is either default value or one of the private ports
} # according to http://tools.ietf.org/html/rfc6056
if (($second_id - $first_id) < 0 ) { if value != 4789 or (49151 >= value and value > 65535)
fail('network vlan ranges are invalid.') raise Puppet::Error, "vxlan udp port is invalid."
} end
} end
end

View File

@ -15,8 +15,10 @@ class neutron::agents::ovs (
$bridge_mappings = [], $bridge_mappings = [],
$integration_bridge = 'br-int', $integration_bridge = 'br-int',
$enable_tunneling = false, $enable_tunneling = false,
$tunnel_types = [],
$local_ip = false, $local_ip = false,
$tunnel_bridge = 'br-tun', $tunnel_bridge = 'br-tun',
$vxlan_udp_port = 4789,
$polling_interval = 2, $polling_interval = 2,
$firewall_driver = 'neutron.agent.linux.iptables_firewall.OVSHybridIptablesFirewallDriver' $firewall_driver = 'neutron.agent.linux.iptables_firewall.OVSHybridIptablesFirewallDriver'
) { ) {
@ -86,6 +88,18 @@ class neutron::agents::ovs (
'OVS/tunnel_bridge': value => $tunnel_bridge; 'OVS/tunnel_bridge': value => $tunnel_bridge;
'OVS/local_ip': value => $local_ip; 'OVS/local_ip': value => $local_ip;
} }
if $tunnel_types {
neutron_plugin_ovs {
'agent/tunnel_types': value => join($tunnel_types, ',');
}
}
if 'vxlan' in $tunnel_types {
validate_vxlan_udp_port($vxlan_udp_port)
neutron_plugin_ovs {
'agent/vxlan_udp_port': value => $vxlan_udp_port;
}
}
} else { } else {
neutron_plugin_ovs { neutron_plugin_ovs {
'OVS/enable_tunneling': value => false; 'OVS/enable_tunneling': value => false;

View File

@ -89,7 +89,7 @@ class neutron::plugins::ml2 (
$tenant_network_types = ['local', 'flat', 'vlan', 'gre', 'vxlan'], $tenant_network_types = ['local', 'flat', 'vlan', 'gre', 'vxlan'],
$mechanism_drivers = ['openvswitch', 'linuxbridge'], $mechanism_drivers = ['openvswitch', 'linuxbridge'],
$flat_networks = ['*'], $flat_networks = ['*'],
$network_vlan_ranges = ['10:50'], $network_vlan_ranges = ['physnet1:1000:2999'],
$tunnel_id_ranges = ['20:100'], $tunnel_id_ranges = ['20:100'],
$vxlan_group = '224.0.0.1', $vxlan_group = '224.0.0.1',
$vni_ranges = ['10:100'] $vni_ranges = ['10:100']

View File

@ -36,7 +36,7 @@ define neutron::plugins::ml2::driver (
fail('when gre is part of type_drivers, tunnel_id_ranges should be given.') fail('when gre is part of type_drivers, tunnel_id_ranges should be given.')
} }
neutron::plugins::ml2::validate_tunnel_id_ranges { $tunnel_id_ranges:; } validate_tunnel_id_ranges($tunnel_id_ranges)
neutron_plugin_ml2 { neutron_plugin_ml2 {
'ml2_type_gre/tunnel_id_ranges': value => join($tunnel_id_ranges, ','); 'ml2_type_gre/tunnel_id_ranges': value => join($tunnel_id_ranges, ',');
@ -48,7 +48,7 @@ define neutron::plugins::ml2::driver (
fail('when vlan is part of type_drivers, network_vlan_ranges should be given.') fail('when vlan is part of type_drivers, network_vlan_ranges should be given.')
} }
neutron::plugins::ml2::validate_network_vlan_ranges { $network_vlan_ranges:; } validate_network_vlan_ranges($network_vlan_ranges)
neutron_plugin_ml2 { neutron_plugin_ml2 {
'ml2_type_vlan/network_vlan_ranges': value => join($network_vlan_ranges, ','); 'ml2_type_vlan/network_vlan_ranges': value => join($network_vlan_ranges, ',');
@ -73,7 +73,7 @@ define neutron::plugins::ml2::driver (
} }
} }
neutron::plugins::ml2::validate_vni_ranges { $vni_ranges: } validate_vni_ranges($vni_ranges)
neutron_plugin_ml2 { neutron_plugin_ml2 {
'ml2_type_vxlan/vxlan_group': value => $vxlan_group; 'ml2_type_vxlan/vxlan_group': value => $vxlan_group;

View File

@ -1,30 +0,0 @@
#
# Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
#
# Author: Emilien Macchi <emilien.macchi@enovance.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# Advanced testing when using GRE
#
define neutron::plugins::ml2::validate_tunnel_id_ranges {
$first_id = regsubst($name,'^(\d+):(\d+)$','\1') + 0
$second_id = regsubst($name,'^(\d+):(\d+)$','\2') + 0
if (($second_id - $first_id) > 1000000) {
fail('tunnel id ranges are to large.')
}
if (($second_id - $first_id) < 0 ) {
fail('tunnel id ranges are invalid.')
}
}

View File

@ -1,34 +0,0 @@
#
# Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
#
# Author: Emilien Macchi <emilien.macchi@enovance.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
#
# Advanced testing when using VXLAN
#
define neutron::plugins::ml2::validate_vni_ranges {
if ($name !~ /^(\d+):(\d+)$/) {
fail('vni ranges are invalid.')
}
else {
$first_id = regsubst($name,'^(\d+):(\d+)$','\1') + 0
$second_id = regsubst($name,'^(\d+):(\d+)$','\2') + 0
if ( $first_id > 16777215 ) or ( $second_id > 16777215 )
or ( $first_id < 0 ) or ( $second_id < 0 )
or ( $second_id < $first_id ) {
fail('vni ranges are invalid.')
}
}
}

View File

@ -19,7 +19,8 @@ class neutron::plugins::ovs (
# if type is vlan or flat, a default of physnet1:1000:2000 is used # if type is vlan or flat, a default of physnet1:1000:2000 is used
# otherwise this will not be set by default. # otherwise this will not be set by default.
$network_vlan_ranges = undef, $network_vlan_ranges = undef,
$tunnel_id_ranges = '1:1000' $tunnel_id_ranges = '1:1000',
$vxlan_udp_port = 4789
) { ) {
include neutron::params include neutron::params
@ -56,31 +57,33 @@ class neutron::plugins::ovs (
'OVS/tenant_network_type': value => $tenant_network_type; 'OVS/tenant_network_type': value => $tenant_network_type;
} }
if($tenant_network_type == 'gre') { if $tenant_network_type in ['gre', 'vxlan'] {
validate_tunnel_id_ranges($tunnel_id_ranges)
neutron_plugin_ovs { neutron_plugin_ovs {
# this is set by the plugin and the agent - since the plugin node has the agent installed # this is set by the plugin and the agent - since the plugin node has the agent installed
# we rely on it setting it. # we rely on it setting it.
# TODO(ijw): do something with a virtualised node # TODO(ijw): do something with a virtualised node
# 'OVS/enable_tunneling': value => 'True'; # 'OVS/enable_tunneling': value => 'True';
'OVS/tunnel_id_ranges': value => $tunnel_id_ranges; 'OVS/tunnel_id_ranges': value => $tunnel_id_ranges;
'OVS/tunnel_type': value => $tenant_network_type;
} }
} }
# If the user hasn't specified vlan_ranges, fail for the modes where validate_vxlan_udp_port($vxlan_udp_port)
# it is required, otherwise keep it absent neutron_plugin_ovs { 'OVS/vxlan_udp_port': value => $vxlan_udp_port; }
if ($tenant_network_type == 'vlan') or ($tenant_network_type == 'flat') {
if ! $network_vlan_ranges { if ! $network_vlan_ranges {
# If the user hasn't specified vlan_ranges, fail for the modes where
# it is required, otherwise keep it absent
if $tenant_network_type in ['vlan', 'flat'] {
fail('When using the vlan network type, network_vlan_ranges is required') fail('When using the vlan network type, network_vlan_ranges is required')
} } else {
} else {
if ! $network_vlan_ranges {
neutron_plugin_ovs { 'OVS/network_vlan_ranges': ensure => absent } neutron_plugin_ovs { 'OVS/network_vlan_ranges': ensure => absent }
} }
} } else {
# This might be set by the user for the gre or vxlan case where
# This might be set by the user for the gre case where # provider networks are in use
# provider networks are in use validate_network_vlan_ranges($network_vlan_ranges)
if $network_vlan_ranges {
neutron_plugin_ovs { neutron_plugin_ovs {
'OVS/network_vlan_ranges': value => $network_vlan_ranges 'OVS/network_vlan_ranges': value => $network_vlan_ranges
} }

View File

@ -4,7 +4,7 @@ describe 'neutron::agents::ovs' do
let :pre_condition do let :pre_condition do
"class { 'neutron': rabbit_password => 'passw0rd' }\n" + "class { 'neutron': rabbit_password => 'passw0rd' }\n" +
"class { 'neutron::plugins::ovs': network_vlan_ranges => 'test' }" "class { 'neutron::plugins::ovs': network_vlan_ranges => 'physnet1:1000:2000' }"
end end
let :default_params do let :default_params do
@ -126,6 +126,20 @@ describe 'neutron::agents::ovs' do
) )
end end
end end
context 'with vxlan tunneling' do
before :each do
params.merge!(:enable_tunneling => true,
:local_ip => '127.0.0.1',
:tunnel_types => ['vxlan'],
:vxlan_udp_port => '4789')
end
it 'should perform vxlan network configuration' do
should contain_neutron_plugin_ovs('agent/tunnel_types').with_value(params[:tunnel_types])
should contain_neutron_plugin_ovs('agent/vxlan_udp_port').with_value(params[:vxlan_udp_port])
end
end
end end
end end

View File

@ -24,7 +24,7 @@ describe 'neutron::plugins::ovs' do
shared_examples_for 'neutron ovs plugin' do shared_examples_for 'neutron ovs plugin' do
before do before do
params.merge!(default_params) params.merge!(default_params) { |key, v1, v2| v1 }
end end
let :params do let :params do
@ -57,12 +57,13 @@ describe 'neutron::plugins::ovs' do
end end
before do before do
params.delete('network_vlan_ranges') params.delete(:network_vlan_ranges)
end end
it 'should perform gre network configuration' do it 'should perform gre network configuration' do
should contain_neutron_plugin_ovs('OVS/tenant_network_type').with_value(params[:tenant_network_type]) should contain_neutron_plugin_ovs('OVS/tenant_network_type').with_value(params[:tenant_network_type])
should contain_neutron_plugin_ovs('OVS/tunnel_id_ranges').with_value(params[:tunnel_id_ranges]) should contain_neutron_plugin_ovs('OVS/tunnel_id_ranges').with_value(params[:tunnel_id_ranges])
should contain_neutron_plugin_ovs('OVS/network_vlan_ranges').with_ensure('absent')
end end
end end
@ -80,6 +81,79 @@ describe 'neutron::plugins::ovs' do
end end
end end
context 'with vxlan tunneling' do
let :params do
{ :tenant_network_type => 'vxlan',
:vxlan_udp_port => '4789'}
end
before do
params.delete(:network_vlan_ranges)
end
it 'should perform vxlan network configuration' do
should contain_neutron_plugin_ovs('OVS/tenant_network_type').with_value(params[:tenant_network_type])
should contain_neutron_plugin_ovs('OVS/vxlan_udp_port').with_value(params[:vxlan_udp_port])
should contain_neutron_plugin_ovs('OVS/network_vlan_ranges').with_ensure('absent')
end
end
context 'with vxlan tunnelling using bad vxlan_udp_port' do
let :params do
{ :tenant_network_type => 'vxlan',
:vxlan_udp_port => '1',}
end
it 'should fail if invalid port is passed' do
expect { subject }.to raise_error(Puppet::Error, /vxlan udp port is invalid./)
end
end
context 'with vxlan tunnelling using bad tunnel_id_ranges' do
let :params do
{ :tenant_network_type => 'vxlan',
:tunnel_id_ranges => '100:9',}
end
it 'should fail if invalid id range is passed' do
expect { subject }.to raise_error(Puppet::Error, /tunnel id ranges are invalid./)
end
end
context 'with vxlan tunneling and provider networks using bad network_vlan_ranges' do
let :params do
{ :tenant_network_type => 'vxlan',
:network_vlan_ranges => 'physnet1:200:1'}
end
it 'should fail if invalid vlan range is passed' do
expect { subject }.to raise_error(Puppet::Error, /network vlan ranges are invalid./)
end
end
context 'with vxlan tunneling using bad multiple network_vlan_ranges' do
let :params do
{ :tenant_network_type => 'vxlan',
:network_vlan_ranges => ['physnet1:0:100', 'physnet2:1000:1']}
end
it 'should fail if invalid network vlan range is passed' do
expect { subject }.to raise_error(Puppet::Error, /network vlan ranges are invalid/)
end
end
context 'with vxlan tunneling and provider networks' do
let :params do
{ :tenant_network_type => 'vxlan',
:network_vlan_ranges => 'physnet1:1000:2000'}
end
it 'should perform vxlan network configuration' do
should contain_neutron_plugin_ovs('OVS/network_vlan_ranges').with_value(params[:network_vlan_ranges])
should contain_neutron_plugin_ovs('OVS/tenant_network_type').with_value(params[:tenant_network_type])
end
end
context 'with a flat network' do context 'with a flat network' do
let :params do let :params do
{ :tenant_network_type => 'flat'} { :tenant_network_type => 'flat'}
@ -106,7 +180,7 @@ describe 'neutron::plugins::ovs' do
end end
let :params do let :params do
{ :network_vlan_ranges => 'test' } { :network_vlan_ranges => 'physnet1:1000:2000' }
end end
let :platform_params do let :platform_params do