From 16f279f1e4662ffeb9c2187a9e0ace9d9b022ef1 Mon Sep 17 00:00:00 2001 From: Emilien Macchi Date: Wed, 25 Sep 2013 18:02:53 +0200 Subject: [PATCH] Add ML2 plugin support This patch aims to support: - ml2 configuration for Open-vSwitch & Linux Bridge plugins - mechanism drivers: flat, gre, vlan vxlan - l2 population support for OVS & linux bridge plugins implement blueprint ml2-support Change-Id: I9a4224f899e95411c509f583bf04dfc4dd0de25b Signed-off-by: Emilien Macchi --- examples/neutron.pp | 8 + .../neutron_plugin_ml2/ini_setting.rb | 22 ++ lib/puppet/type/neutron_plugin_ml2.rb | 20 ++ manifests/plugins/ml2.pp | 146 ++++++++++++++ manifests/plugins/ml2/driver.pp | 90 +++++++++ .../ml2/validate_network_vlan_ranges.pp | 30 +++ .../plugins/ml2/validate_tunnel_id_ranges.pp | 30 +++ manifests/plugins/ml2/validate_vni_ranges.pp | 34 ++++ spec/classes/neutron_plugins_ml2_spec.rb | 189 ++++++++++++++++++ 9 files changed, 569 insertions(+) create mode 100644 lib/puppet/provider/neutron_plugin_ml2/ini_setting.rb create mode 100644 lib/puppet/type/neutron_plugin_ml2.rb create mode 100644 manifests/plugins/ml2.pp create mode 100644 manifests/plugins/ml2/driver.pp create mode 100644 manifests/plugins/ml2/validate_network_vlan_ranges.pp create mode 100644 manifests/plugins/ml2/validate_tunnel_id_ranges.pp create mode 100644 manifests/plugins/ml2/validate_vni_ranges.pp create mode 100644 spec/classes/neutron_plugins_ml2_spec.rb diff --git a/examples/neutron.pp b/examples/neutron.pp index 6a62b6022..b6a8e7930 100644 --- a/examples/neutron.pp +++ b/examples/neutron.pp @@ -33,6 +33,14 @@ class { 'neutron::plugins::ovs': tenant_network_type => 'gre', } +# ml2 plugin with vxlan as ml2 driver and ovs as mechanism driver +class { 'neutron::plugins::ml2': + type_drivers => ['vxlan'], + tenant_network_types => ['vxlan'], + vxlan_group => '239.1.1.1', + mechanism_drivers => ['openvswitch'], + vni_ranges => ['0:300'] +} ### Compute Nodes: # Generally, any machine with a neutron element running on it talks diff --git a/lib/puppet/provider/neutron_plugin_ml2/ini_setting.rb b/lib/puppet/provider/neutron_plugin_ml2/ini_setting.rb new file mode 100644 index 000000000..9f724b517 --- /dev/null +++ b/lib/puppet/provider/neutron_plugin_ml2/ini_setting.rb @@ -0,0 +1,22 @@ +Puppet::Type.type(:neutron_plugin_ml2).provide( + :ini_setting, + :parent => Puppet::Type.type(:ini_setting).provider(:ruby) +) do + + def section + resource[:name].split('/', 2).first + end + + def setting + resource[:name].split('/', 2).last + end + + def separator + '=' + end + + def file_path + '/etc/neutron/plugins/ml2/ml2_conf.ini' + end + +end diff --git a/lib/puppet/type/neutron_plugin_ml2.rb b/lib/puppet/type/neutron_plugin_ml2.rb new file mode 100644 index 000000000..e121a11c5 --- /dev/null +++ b/lib/puppet/type/neutron_plugin_ml2.rb @@ -0,0 +1,20 @@ +Puppet::Type.newtype(:neutron_plugin_ml2) do + + ensurable + + newparam(:name, :namevar => true) do + desc 'Section/setting name to manage from ml2_conf.ini' + newvalues(/\S+\/\S+/) + end + + autorequire(:package) do ['neutron'] end + + newproperty(:value) do + desc 'The value of the setting to be defined.' + munge do |value| + value = value.to_s.strip + value.capitalize! if value =~ /^(true|false)$/i + value + end + end +end diff --git a/manifests/plugins/ml2.pp b/manifests/plugins/ml2.pp new file mode 100644 index 000000000..426bfd46f --- /dev/null +++ b/manifests/plugins/ml2.pp @@ -0,0 +1,146 @@ +# +# Copyright (C) 2013 eNovance SAS +# +# Author: Emilien Macchi +# +# 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. + +# Configure the neutron server to use the ML2 plugin. +# This configures the plugin for the API server, but does nothing +# about configuring the agents that must also run and share a config +# file with the OVS plugin if both are on the same machine. +# +# === Parameters +# +# [*type_drivers*] +# (optional) List of network type driver entrypoints to be loaded +# from the neutron.ml2.type_drivers namespace. +# Could be an array that can have these elements: +# local, flat, vlan, gre, vxlan +# Defaults to ['local', 'flat', 'vlan', 'gre', 'vxlan']. +# +# [*tenant_network_types*] +# (optional) Ordered list of network_types to allocate as tenant networks. +# The value 'local' is only useful for single-box testing +# but provides no connectivity between hosts. +# Should be an array that can have these elements: +# local, flat, vlan, gre, vxlan +# Defaults to ['local', 'flat', 'vlan', 'gre', 'vxlan']. +# +# [*mechanism_drivers*] +# (optional) An ordered list of networking mechanism driver +# entrypoints to be loaded from the neutron.ml2.mechanism_drivers namespace. +# Should be an array that can have these elements: +# logger, test, linuxbridge, openvswitch, hyperv, ncs, arista, cisco_nexus, +# l2population. +# Default to ['openvswitch', 'linuxbridge']. +# +# [*flat_networks*] +# (optional) List of physical_network names with which flat networks +# can be created. Use * to allow flat networks with arbitrary +# physical_network names. +# Should be an array. +# Default to *. +# +# [*network_vlan_ranges*] +# (optional) List of :: or +# specifying physical_network names +# usable for VLAN provider and tenant networks, as +# well as ranges of VLAN tags on each available for +# allocation to tenant networks. +# Should be an array with vlan_min = 1 & vlan_max = 4094 (IEEE 802.1Q) +# Default to empty. +# +# [*tunnel_id_ranges*] +# (optional) Comma-separated list of : tuples +# enumerating ranges of GRE tunnel IDs that are +# available for tenant network allocation +# Should be an array with tun_max +1 - tun_min > 1000000 +# Default to empty. +# +# [*vxlan_group*] +# (optional) Multicast group for VXLAN. +# Multicast group for VXLAN. If unset, disables VXLAN enable sending allocate +# broadcast traffic to this multicast group. When left unconfigured, will +# disable multicast VXLAN mode +# Should be an Multicast IP (v4 or v6) address. +# Default to 'None'. +# +# [*vni_ranges*] +# (optional) Comma-separated list of : tuples +# enumerating ranges of VXLAN VNI IDs that are +# available for tenant network allocation. +# Min value is 0 and Max value is 16777215. +# Default to empty. +# + +class neutron::plugins::ml2 ( + $type_drivers = ['local', 'flat', 'vlan', 'gre', 'vxlan'], + $tenant_network_types = ['local', 'flat', 'vlan', 'gre', 'vxlan'], + $mechanism_drivers = ['openvswitch', 'linuxbridge'], + $flat_networks = ['*'], + $network_vlan_ranges = ['10:50'], + $tunnel_id_ranges = ['20:100'], + $vxlan_group = '224.0.0.1', + $vni_ranges = ['10:100'] +) { + + include neutron::params + + # test mechanism drivers + validate_array($mechanism_drivers) + if ! $mechanism_drivers { + warning('Without networking mechanism driver, ml2 will not communicate with L2 agents') + } + + neutron::plugins::ml2::driver { $type_drivers: + flat_networks => $flat_networks, + tunnel_id_ranges => $tunnel_id_ranges, + network_vlan_ranges => $network_vlan_ranges, + vni_ranges => $vni_ranges, + vxlan_group => $vxlan_group, + } + + # Configure ml2_conf.ini + neutron_plugin_ml2 { + 'ml2/type_drivers': value => join($type_drivers, ','); + 'ml2/tenant_network_types': value => join($tenant_network_types, ','); + 'ml2/mechanism_drivers': value => join($mechanism_drivers, ','); + } + + # Specific plugin configuration + if ('openvswitch' in $mechanism_drivers) and ('l2population' in $mechanism_drivers) { + neutron_plugin_ovs { + 'agent/l2_population': value => true; + } + } else { + neutron_plugin_ovs { + 'agent/l2_population': value => false; + } + } + if ('linuxbridge' in $mechanism_drivers) and ('l2population' in $mechanism_drivers) { + neutron_plugin_linuxbridge { + 'vxlan/enable_vxlan': value => true; + 'vxlan/l2_population': value => true; + } + } + + if $::osfamily == 'Redhat' { + file {'/etc/neutron/plugin.ini': + ensure => link, + target => '/etc/neutron/plugins/ml2/ml2_conf.ini', + require => Package['openstack-neutron'] + } + } + +} diff --git a/manifests/plugins/ml2/driver.pp b/manifests/plugins/ml2/driver.pp new file mode 100644 index 000000000..65532591d --- /dev/null +++ b/manifests/plugins/ml2/driver.pp @@ -0,0 +1,90 @@ +# +# Copyright (C) 2013 eNovance SAS +# +# Author: Emilien Macchi +# +# 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. +# +# neutron::plugins::ml2::drivers used by neutron::plugins::ml2 +# + +define neutron::plugins::ml2::driver ( + $flat_networks, + $tunnel_id_ranges, + $network_vlan_ranges, + $vni_ranges, + $vxlan_group +){ + if ($name == 'flat') { + neutron_plugin_ml2 { + 'ml2_type_flat/flat_networks': value => join($flat_networks, ','); + } + } + elsif ($name == 'gre') { + # tunnel_id_ranges is required in gre + if ! $tunnel_id_ranges { + fail('when gre is part of type_drivers, tunnel_id_ranges should be given.') + } + + neutron::plugins::ml2::validate_tunnel_id_ranges { $tunnel_id_ranges:; } + + neutron_plugin_ml2 { + 'ml2_type_gre/tunnel_id_ranges': value => join($tunnel_id_ranges, ','); + } + } + elsif ($name == 'vlan') { + # network_vlan_ranges is required in vlan + if ! $network_vlan_ranges { + fail('when vlan is part of type_drivers, network_vlan_ranges should be given.') + } + + neutron::plugins::ml2::validate_network_vlan_ranges { $network_vlan_ranges:; } + + neutron_plugin_ml2 { + 'ml2_type_vlan/network_vlan_ranges': value => join($network_vlan_ranges, ','); + } + } + elsif ($name == 'vxlan') { + # vni_ranges and vxlan_group are required in vxlan + if (! $vni_ranges) or (! $vxlan_group) { + fail('when vxlan is part of type_drivers, vni_ranges and vxlan_group should be given.') + } + # test multicast ip address (ipv4 else ipv6): + case $vxlan_group { + /^2[\d.]+$/: { + case $vxlan_group { + /^(22[4-9]|23[0-9])\.(\d+)\.(\d+)\.(\d+)$/: { } + default: { } + } + } + /^ff[\d.]+$/: { } + default: { + fail("${vxlan_group} is not valid for vxlan_group.") + } + } + + neutron::plugins::ml2::validate_vni_ranges { $vni_ranges: } + + neutron_plugin_ml2 { + 'ml2_type_vxlan/vxlan_group': value => $vxlan_group; + 'ml2_type_vxlan/vni_ranges': value => join($vni_ranges, ','); + } + } + elsif ($name == 'local') { + warning('local type_driver is useful only for single-box, because it provides no connectivity between hosts') + } + else { + # detect an invalid type_drivers value + fail('type_driver unknown.') + } +} diff --git a/manifests/plugins/ml2/validate_network_vlan_ranges.pp b/manifests/plugins/ml2/validate_network_vlan_ranges.pp new file mode 100644 index 000000000..1e86d14aa --- /dev/null +++ b/manifests/plugins/ml2/validate_network_vlan_ranges.pp @@ -0,0 +1,30 @@ +# +# Copyright (C) 2013 eNovance SAS +# +# Author: Emilien Macchi +# +# 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 for VLAN configuration +# + +define neutron::plugins::ml2::validate_network_vlan_ranges { + $first_id = regsubst($name,'^(\d+):(\d+)$','\1') + 0 + $second_id = regsubst($name,'^(\d+):(\d+)$','\2') + 0 + if ($first_id > 4094) or ($second_id > 4094) { + fail('vlan id are invalid.') + } + if (($second_id - $first_id) < 0 ) { + fail('network vlan ranges are invalid.') + } +} diff --git a/manifests/plugins/ml2/validate_tunnel_id_ranges.pp b/manifests/plugins/ml2/validate_tunnel_id_ranges.pp new file mode 100644 index 000000000..e55e46c6f --- /dev/null +++ b/manifests/plugins/ml2/validate_tunnel_id_ranges.pp @@ -0,0 +1,30 @@ +# +# Copyright (C) 2013 eNovance SAS +# +# Author: Emilien Macchi +# +# 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.') + } +} diff --git a/manifests/plugins/ml2/validate_vni_ranges.pp b/manifests/plugins/ml2/validate_vni_ranges.pp new file mode 100644 index 000000000..125e25bb0 --- /dev/null +++ b/manifests/plugins/ml2/validate_vni_ranges.pp @@ -0,0 +1,34 @@ +# +# Copyright (C) 2013 eNovance SAS +# +# Author: Emilien Macchi +# +# 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.') + } + } +} diff --git a/spec/classes/neutron_plugins_ml2_spec.rb b/spec/classes/neutron_plugins_ml2_spec.rb new file mode 100644 index 000000000..140d95a70 --- /dev/null +++ b/spec/classes/neutron_plugins_ml2_spec.rb @@ -0,0 +1,189 @@ +# +# Copyright (C) 2013 eNovance SAS +# +# Author: Emilien Macchi +# +# 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. +# +# Unit tests for neutron::plugins::ml2 class +# + +require 'spec_helper' + +describe 'neutron::plugins::ml2' do + + let :default_params do + { :type_drivers => ['local', 'flat', 'vlan', 'gre', 'vxlan'], + :tenant_network_types => ['local', 'flat', 'vlan', 'gre', 'vxlan'], + :mechanism_drivers => ['openvswitch', 'linuxbridge'], + :flat_networks => ['*'], + :network_vlan_ranges => ['10:50'], + :tunnel_id_ranges => ['20:100'], + :vxlan_group => '224.0.0.1', + :vni_ranges => ['10:100'] } + end + + let :params do + {} + end + + shared_examples_for 'neutron plugin ml2' do + let :p do + default_params.merge(params) + end + + it { should include_class('neutron::params') } + + it 'configures ml2_conf.ini' do + should contain_neutron_plugin_ml2('ml2/type_drivers').with_value(p[:type_drivers].join(',')) + should contain_neutron_plugin_ml2('ml2/tenant_network_types').with_value(p[:tenant_network_types].join(',')) + should contain_neutron_plugin_ml2('ml2/mechanism_drivers').with_value(p[:mechanism_drivers].join(',')) + end + + it 'configures ovs plugin' do + should_not contain_neutron_plugin_ovs('agent/l2_population').with('value' => true) + end + + it 'configures linux bridge plugin' do + should_not contain_neutron_plugin_linuxbridge('vxlan/enable_vxlan').with('value' => true) + should_not contain_neutron_plugin_linuxbridge('vxlan/l2_population').with('value' => true) + end + + context 'configure ml2 with bad driver value' do + before :each do + params.merge!(:type_drivers => ['foobar']) + end + it 'should fails to configure ml2 because foobar is not a valid driver' do + expect { subject }.to raise_error(Puppet::Error, /type_driver unknown./) + end + end + + context 'when using flat driver' do + before :each do + params.merge!(:flat_networks => ['eth1', 'eth2']) + end + it 'should configure flat_networks' do + should contain_neutron_plugin_ml2('ml2_type_flat/flat_networks').with_value(p[:flat_networks].join(',')) + end + end + + context 'when using gre driver with valid values' do + before :each do + params.merge!(:tunnel_id_ranges => ['0:20', '40:60']) + end + it 'should configure gre_networks with valid ranges' do + should contain_neutron_plugin_ml2('ml2_type_gre/tunnel_id_ranges').with_value(p[:tunnel_id_ranges].join(',')) + end + end + + context 'when using gre driver with invalid values' do + before :each do + params.merge!(:tunnel_id_ranges => ['0:20', '40:100000000']) + end + it 'should fails to configure gre_networks because of too big range' do + expect { subject }.to raise_error(Puppet::Error, /tunnel id ranges are to large./) + end + end + + context 'when using vlan driver with valid values' do + before :each do + params.merge!(:network_vlan_ranges => ['1:20', '400:4094']) + end + it 'should configure vlan_networks with 1:20 and 400:4094 VLAN ranges' do + should contain_neutron_plugin_ml2('ml2_type_vlan/network_vlan_ranges').with_value(p[:network_vlan_ranges].join(',')) + end + end + + context 'when using vlan driver with invalid vlan id' do + before :each do + params.merge!(:network_vlan_ranges => ['1:20', '400:4099']) + end + it 'should fails to configure vlan_networks because of 400:4099 VLAN range' do + expect { subject }.to raise_error(Puppet::Error, /vlan id are invalid./) + end + end + + context 'when using vlan driver with invalid vlan range' do + before :each do + params.merge!(:network_vlan_ranges => ['2938:1']) + end + it 'should fails to configure network_vlan_ranges with 2938:1 range' do + expect { subject }.to raise_error(Puppet::Error, /vlan ranges are invalid./) + end + end + + context 'when using vxlan driver with valid values' do + before :each do + params.merge!(:vni_ranges => ['40:300', '500:1000'], :vxlan_group => '224.1.1.1') + end + it 'should configure vxlan_networks with 224.1.1.1 vxlan group' do + should contain_neutron_plugin_ml2('ml2_type_vxlan/vni_ranges').with_value(p[:vni_ranges].join(',')) + should contain_neutron_plugin_ml2('ml2_type_vxlan/vxlan_group').with_value(p[:vxlan_group]) + end + end + + context 'when using vxlan driver with invalid vxlan group' do + before :each do + params.merge!(:vxlan_group => '192.1.1.1') + end + it 'should fails to configure vxlan_group with 192.1.1.1 vxlan group' do + expect { subject }.to raise_error(Puppet::Error, /is not valid for vxlan_group./) + end + end + + context 'when using vxlan driver with invalid vni_range' do + before :each do + params.merge!(:vni_ranges => ['2938:1']) + end + it 'should fails to configure vni_ranges with 2938:1 range' do + expect { subject }.to raise_error(Puppet::Error, /vni ranges are invalid./) + end + end + + context 'when using l2population with ovs' do + before :each do + params.merge!(:mechanism_drivers => ['openvswitch','l2population']) + end + it 'should set l2_population flag as true' do + should contain_neutron_plugin_ovs('agent/l2_population').with_value('true') + end + end + + context 'when using l2population with linuxbridge' do + before :each do + params.merge!(:mechanism_drivers => ['linuxbridge','l2population']) + end + it 'should set l2_population flag as true' do + should contain_neutron_plugin_linuxbridge('vxlan/enable_vxlan').with_value('true') + should contain_neutron_plugin_linuxbridge('vxlan/l2_population').with_value('true') + end + end + end + + context 'on Debian platforms' do + let :facts do + { :osfamily => 'Debian' } + end + + it_configures 'neutron plugin ml2' + end + + context 'on RedHat platforms' do + let :facts do + { :osfamily => 'RedHat' } + end + + it_configures 'neutron plugin ml2' + end + +end