diff --git a/lib/puppet/provider/neutron_bgpvpn_bagpipe_config/openstackconfig.rb b/lib/puppet/provider/neutron_bgpvpn_bagpipe_config/openstackconfig.rb new file mode 100644 index 000000000..5cfe16282 --- /dev/null +++ b/lib/puppet/provider/neutron_bgpvpn_bagpipe_config/openstackconfig.rb @@ -0,0 +1,15 @@ +Puppet::Type.type(:neutron_bgpvpn_bagpipe_config).provide( + :openstackconfig, + :parent => Puppet::Type.type(:openstack_config).provider(:ruby) +) do + + def self.file_path + '/etc/neutron/bagpipe-bgp/bgp.conf' + end + + # added for backwards compatibility with older versions of inifile + def file_path + self.class.file_path + end + +end diff --git a/lib/puppet/type/neutron_bgpvpn_bagpipe_config.rb b/lib/puppet/type/neutron_bgpvpn_bagpipe_config.rb new file mode 100644 index 000000000..46226a51f --- /dev/null +++ b/lib/puppet/type/neutron_bgpvpn_bagpipe_config.rb @@ -0,0 +1,28 @@ +Puppet::Type.newtype(:neutron_bgpvpn_bagpipe_config) do + + ensurable + + newparam(:name, :namevar => true) do + desc 'Section/setting name to manage from bagpipe agent config.' + newvalues(/\S+\/\S+/) + 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 + + newparam(:ensure_absent_val) do + desc 'A value that is specified as the value property will behave as if ensure => absent was specified' + defaultto('') + end + + autorequire(:package) do + ['python-networking-bagpipe'] + end + +end diff --git a/manifests/agents/bagpipe.pp b/manifests/agents/bagpipe.pp new file mode 100644 index 000000000..5ddecff38 --- /dev/null +++ b/manifests/agents/bagpipe.pp @@ -0,0 +1,133 @@ +# +# Copyright (C) 2017 Red Hat Inc. +# +# Author: Ricardo Noriega +# +# 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. +# +# == Class: neutron::agents::bagpipe +# +# Installs and configures the Neutron Bagpipe driver for BGPVPN +# +# === Parameters +# +# [*my_as*] +# (required) Private Autonomous System number +# Defaults to $::os_service_default +# +# [*api_port*] +# BGP component API port +# Defaults to $::os_service_default +# +# [*dataplane_driver_ipvpn*] +# IP VPN dataplane driver class +# Default to ovs +# +# [*enabled*] +# (optional) The state of the service +# Defaults to true +# +# [*enable_rtc*] +# Enable Route Target Constraint +# Defaults to $::os_service_default +# +# [*manage_service*] +# (optional) Whether to start/stop the service +# Defaults to true +# +# [*mpls_interface*] +# MPLS outgoing interface for Linux and OVS drivers +# Defaults to $::os_service_default +# +# [*ovs_bridge*] +# OVS bridge to use +# Defaults to $::os_service_default +# +# [*package_ensure*] +# (optional) The state of the package +# Defaults to present +# +# [*peers*] +# List of peers' IPs to establish p2p connections +# Defaults to $::os_service_default +# +# [*proxy_arp*] +# For OVS driver control if VRF will reply ARP messages +# Defaults to false +# +# [*purge_config*] +# (optional) Whether to set only the specified config options +# in the l2gateway config. +# Default to false. +# +# [*local_address*] +# (required) Local IP of the server to carry BGP traffic +# Defaults to $::ipaddress +# +class neutron::agents::bagpipe ( + $my_as, + $api_port = $::os_service_default, + $dataplane_driver_ipvpn = 'ovs', + $enabled = true, + $enable_rtc = $::os_service_default, + $manage_service = true, + $mpls_interface = $::os_service_default, + $ovs_bridge = $::os_service_default, + $package_ensure = 'present', + $peers = $::os_service_default, + $proxy_arp = false, + $purge_config = false, + $local_address = $::ipaddress, +) { + + include ::neutron::deps + include ::neutron::params + + resources { 'neutron_bgpvpn_bagpipe_config': + purge => $purge_config, + } + + neutron_bgpvpn_bagpipe_config { + 'api/port': value => $api_port; + 'bgp/local_address': value => $local_address; + 'bgp/peers': value => join(any2array($peers), ','); + 'bgp/my_as': value => $my_as; + 'bgp/enable_rtc': value => $enable_rtc; + 'dataplane_driver_ipvpn/dataplane_driver': value => $dataplane_driver_ipvpn; + 'dataplane_driver_ipvpn/ovs_bridge': value => $ovs_bridge; + 'dataplane_driver_ipvpn/proxy_arp': value => $proxy_arp; + 'dataplane_driver_ipvpn/mpls_interface': value => $mpls_interface; + } + + if $::neutron::params::bgpvpn_bagpipe_package { + package { 'python-networking-bagpipe': + ensure => $package_ensure, + name => $::neutron::params::bgpvpn_bagpipe_package, + tag => ['openstack', 'neutron-package'], + } + } + + if $manage_service { + if $enabled { + $service_ensure = 'running' + } else { + $service_ensure = 'stopped' + } + service { 'bagpipe-bgp': + ensure => $service_ensure, + name => $::neutron::params::bgpvpn_bagpipe_service, + enable => $enabled, + tag => 'neutron-service', + } + } +} diff --git a/manifests/config.pp b/manifests/config.pp index 923749332..991fc10e7 100644 --- a/manifests/config.pp +++ b/manifests/config.pp @@ -27,6 +27,9 @@ # [*api_config*] # (optional) Manage configuration of api-paste.ini # +# [*bgpvpn_bagpipe_config*] +# (optional) Manage configuration of bagpipe-bgp bgp.conf +# # [*bgpvpn_service_config*] # (optional) Manage configuration of networking_bgpvpn.conf # @@ -90,6 +93,7 @@ class neutron::config ( $server_config = {}, $api_config = {}, + $bgpvpn_bagpipe_config = {}, $bgpvpn_service_config = {}, $l2gw_agent_config = {}, $l2gw_service_config = {}, @@ -115,6 +119,7 @@ class neutron::config ( validate_hash($server_config) validate_hash($api_config) + validate_hash($bgpvpn_bagpipe_config) validate_hash($bgpvpn_service_config) validate_hash($l2gw_agent_config) validate_hash($l2gw_service_config) @@ -137,6 +142,7 @@ class neutron::config ( create_resources('neutron_config', $server_config) create_resources('neutron_api_config', $api_config) + create_resources('neutron_bgpvpn_bagpipe_config', $bgpvpn_bagpipe_config) create_resources('neutron_bgpvpn_service_config', $bgpvpn_service_config) create_resources('neutron_l2gw_agent_config', $l2gw_agent_config) create_resources('neutron_l3_agent_config', $l3_agent_config) diff --git a/manifests/deps.pp b/manifests/deps.pp index 38bb66422..da0b850fc 100644 --- a/manifests/deps.pp +++ b/manifests/deps.pp @@ -38,6 +38,7 @@ class neutron::deps { Anchor['neutron::config::begin'] -> Neutron_agent_vpp<||> ~> Anchor['neutron::config::end'] Anchor['neutron::config::begin'] -> Neutron_api_config<||> ~> Anchor['neutron::config::end'] Anchor['neutron::config::begin'] -> Neutron_api_paste_ini<||> ~> Anchor['neutron::config::end'] + Anchor['neutron::config::begin'] -> Neutron_bgpvpn_bagpipe_config<||> ~> Anchor['neutron::config::end'] Anchor['neutron::config::begin'] -> Neutron_bgpvpn_service_config<||> ~> Anchor['neutron::config::end'] Anchor['neutron::config::begin'] -> Neutron_config<||> ~> Anchor['neutron::config::end'] Anchor['neutron::config::begin'] -> Neutron_dhcp_agent_config<||> ~> Anchor['neutron::config::end'] diff --git a/manifests/params.pp b/manifests/params.pp index 55321b759..f3ecfe435 100644 --- a/manifests/params.pp +++ b/manifests/params.pp @@ -29,6 +29,8 @@ class neutron::params { $vpnaas_agent_service = 'neutron-vpn-agent' $l3_agent_service = 'neutron-l3-agent' $metadata_agent_service = 'neutron-metadata-agent' + $bgpvpn_bagpipe_package = 'python-networking-bagpipe' + $bgpvpn_bagpipe_service = 'bagpipe-bgp' $bgpvpn_plugin_package = 'python-networking-bgpvpn' $l2gw_agent_service = 'neutron-l2gw-agent' diff --git a/releasenotes/notes/bgpvpn-support-73886489d10d6301.yaml b/releasenotes/notes/bgpvpn-support-73886489d10d6301.yaml index 63cb1dc98..7e1a10b49 100644 --- a/releasenotes/notes/bgpvpn-support-73886489d10d6301.yaml +++ b/releasenotes/notes/bgpvpn-support-73886489d10d6301.yaml @@ -1,10 +1,9 @@ --- -prelude: > - BGP-based IP VPNs networks are widely used in the industry especially for - enterprises. This project aims at supporting inter-connection between - L3VPNs and Neutron resources, i.e. Networks, Routers and Ports. features: - Add support for BGPVPN Neutron service plugin - Add new type for BGPVPN Service config - Add new provider for BGPVPN Service config + - Add Bagpipe agent as reference driver + - Add new type for Bagpipe driver + - Add new provider for Bagpipe driver - Add spec and unit tests diff --git a/spec/classes/neutron_agents_bagpipe_spec.rb b/spec/classes/neutron_agents_bagpipe_spec.rb new file mode 100644 index 000000000..94faf82a3 --- /dev/null +++ b/spec/classes/neutron_agents_bagpipe_spec.rb @@ -0,0 +1,116 @@ +# Copyright (C) 2017 Red Hat Inc. +# +# Author: Ricardo Noriega +# +# 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. + +require 'spec_helper' + +describe 'neutron::agents::bagpipe' do + + let :params do + { :my_as => 64512, + :api_port => 8082, + :dataplane_driver_ipvpn => 'ovs', + :enabled => true, + :enable_rtc => true, + :manage_service => true, + :mpls_interface => '*gre*', + :ovs_bridge => 'br-mpls', + :package_ensure => 'present', + :peers => '192.168.0.101', + :proxy_arp => false, + :purge_config => false, + :local_address => '127.0.0.1' + } + end + + let :default_params do + {} + end + + let :test_facts do + { :operatingsystem => 'default', + :operatingsystemrelease => 'default' + } + end + shared_examples_for 'neutron bgpvpn bagpipe agent' do + let :p do + default_params.merge(params) + end + + it 'passes purge to resource' do + is_expected.to contain_resources('neutron_bgpvpn_bagpipe_config').with({ + :purge => false + }) + end + + it 'installs bgpvpn bagpipe package' do + is_expected.to contain_package('python-networking-bagpipe').with( + :ensure => p[:package_ensure], + :name => platform_params[:bgpvpn_bagpipe_package], + ) + end + + it 'configures bgp.conf' do + is_expected.to contain_neutron_bgpvpn_bagpipe_config('api/port').with_value(p[:api_port]) + is_expected.to contain_neutron_bgpvpn_bagpipe_config('bgp/local_address').with_value(p[:local_address]) + is_expected.to contain_neutron_bgpvpn_bagpipe_config('bgp/peers').with_value(p[:peers]) + is_expected.to contain_neutron_bgpvpn_bagpipe_config('bgp/my_as').with_value(p[:my_as]) + is_expected.to contain_neutron_bgpvpn_bagpipe_config('bgp/enable_rtc').with_value(p[:enable_rtc]) + is_expected.to contain_neutron_bgpvpn_bagpipe_config('dataplane_driver_ipvpn/dataplane_driver').with_value(p[:dataplane_driver_ipvpn]) + is_expected.to contain_neutron_bgpvpn_bagpipe_config('dataplane_driver_ipvpn/ovs_bridge').with_value(p[:ovs_bridge]) + is_expected.to contain_neutron_bgpvpn_bagpipe_config('dataplane_driver_ipvpn/proxy_arp').with_value(p[:proxy_arp]) + is_expected.to contain_neutron_bgpvpn_bagpipe_config('dataplane_driver_ipvpn/mpls_interface').with_value(p[:mpls_interface]) + end + + it 'bagpipe service running' do + is_expected.to contain_service('bagpipe-bgp').with_ensure('running') + end + + context 'with multiple peers' do + before :each do + params.merge!( + { :peers => ['peer1', 'peer2'] } + ) + end + + it 'configures multiple peers in bgp.conf' do + is_expected.to contain_neutron_bgpvpn_bagpipe_config( + 'bgp/peers' + ).with_value(p[:peers].join(',')) + end + end + end + + on_supported_os({ + :supported_os => OSDefaults.get_supported_os + }).each do |os,facts| + context "on #{os}" do + let (:facts) do + facts.merge(OSDefaults.get_facts()) + end + + let (:platform_params) do + case facts[:osfamily] + when 'RedHat' + { :bgpvpn_bagpipe_package => 'python-networking-bagpipe' } + when 'Debian' + { :bgpvpn_bagpipe_package => 'python-networking-bagpipe' } + end + end + + it_configures 'neutron bgpvpn bagpipe agent' + end + end +end diff --git a/spec/unit/provider/neutron_bgpvpn_bagpipe_config/openstackconfig_spec.rb b/spec/unit/provider/neutron_bgpvpn_bagpipe_config/openstackconfig_spec.rb new file mode 100644 index 000000000..b00122a98 --- /dev/null +++ b/spec/unit/provider/neutron_bgpvpn_bagpipe_config/openstackconfig_spec.rb @@ -0,0 +1,74 @@ +$LOAD_PATH.push( + File.join( + File.dirname(__FILE__), + '..', + '..', + '..', + 'fixtures', + 'modules', + 'inifile', + 'lib') +) +$LOAD_PATH.push( + File.join( + File.dirname(__FILE__), + '..', + '..', + '..', + 'fixtures', + 'modules', + 'openstacklib', + 'lib') +) + +require 'spec_helper' + +provider_class = Puppet::Type.type(:neutron_bgpvpn_bagpipe_config).provider(:openstackconfig) + +describe provider_class do + + it 'should default to the default setting when no other one is specified' do + resource = Puppet::Type::Neutron_bgpvpn_bagpipe_config.new( + { + :name => 'DEFAULT/foo', + :value => 'bar' + } + ) + provider = provider_class.new(resource) + expect(provider.section).to eq('DEFAULT') + expect(provider.setting).to eq('foo') + expect(provider.file_path).to eq('/etc/neutron/bagpipe-bgp/bgp.conf') + end + + it 'should allow setting to be set explicitly' do + resource = Puppet::Type::Neutron_bgpvpn_bagpipe_config.new( + { + :name => 'dude/foo', + :value => 'bar' + } + ) + provider = provider_class.new(resource) + expect(provider.section).to eq('dude') + expect(provider.setting).to eq('foo') + expect(provider.file_path).to eq('/etc/neutron/bagpipe-bgp/bgp.conf') + end + + it 'should ensure absent when is specified as a value' do + resource = Puppet::Type::Neutron_bgpvpn_bagpipe_config.new( + {:name => 'dude/foo', :value => ''} + ) + provider = provider_class.new(resource) + provider.exists? + expect(resource[:ensure]).to eq :absent + end + + it 'should ensure absent when value matches ensure_absent_val' do + resource = Puppet::Type::Neutron_bgpvpn_bagpipe_config.new( + {:name => 'dude/foo', :value => 'foo', :ensure_absent_val => 'foo' } + ) + provider = provider_class.new(resource) + provider.exists? + expect(resource[:ensure]).to eq :absent + end + +end diff --git a/spec/unit/type/neutron_bgpvpn_bagpipe_config_spec.rb b/spec/unit/type/neutron_bgpvpn_bagpipe_config_spec.rb new file mode 100644 index 000000000..7125da609 --- /dev/null +++ b/spec/unit/type/neutron_bgpvpn_bagpipe_config_spec.rb @@ -0,0 +1,20 @@ +require 'puppet' +require 'puppet/type/neutron_bgpvpn_bagpipe_config' + +describe 'Puppet::Type.type(:neutron_bgpvpn_bagpipe_config)' do + + before :each do + @neutron_bgpvpn_bagpipe_config = Puppet::Type.type(:neutron_bgpvpn_bagpipe_config).new(:name => 'DEFAULT/foo', :value => 'bar') + end + + it 'should autorequire the package that install the file' do + catalog = Puppet::Resource::Catalog.new + package = Puppet::Type.type(:package).new(:name => 'python-networking-bagpipe') + catalog.add_resource package, @neutron_bgpvpn_bagpipe_config + dependency = @neutron_bgpvpn_bagpipe_config.autorequire + expect(dependency.size).to eq(1) + expect(dependency[0].target).to eq(@neutron_bgpvpn_bagpipe_config) + expect(dependency[0].source).to eq(package) + end + +end