diff --git a/lib/puppet/provider/neutron_plugin_nsx/ini_setting.rb b/lib/puppet/provider/neutron_plugin_nsx/ini_setting.rb new file mode 100644 index 000000000..1a97f369b --- /dev/null +++ b/lib/puppet/provider/neutron_plugin_nsx/ini_setting.rb @@ -0,0 +1,15 @@ +Puppet::Type.type(:neutron_plugin_nsx).provide( + :ini_setting, + :parent => Puppet::Type.type(:openstack_config).provider(:ini_setting) +) do + + def self.file_path + '/etc/neutron/plugins/vmware/nsx.ini' + 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_plugin_nsx.rb b/lib/puppet/type/neutron_plugin_nsx.rb new file mode 100644 index 000000000..f6dd95d44 --- /dev/null +++ b/lib/puppet/type/neutron_plugin_nsx.rb @@ -0,0 +1,56 @@ +Puppet::Type.newtype(:neutron_plugin_nsx) do + + ensurable + + newparam(:name, :namevar => true) do + desc 'Section/setting name to manage from vmware/nsx.ini' + 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 + + def is_to_s( currentvalue ) + if resource.secret? + return '[old secret redacted]' + else + return currentvalue + end + end + + def should_to_s( newvalue ) + if resource.secret? + return '[new secret redacted]' + else + return newvalue + end + end + end + + newparam(:secret, :boolean => true) do + desc 'Whether to hide the value from Puppet logs. Defaults to `false`.' + + newvalues(:true, :false) + + defaultto false + 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(:file) do + '/etc/neutron/plugins/vmware' + end + + autorequire(:package) do + 'vmware-nsx' + end + +end diff --git a/manifests/config.pp b/manifests/config.pp index 991fc10e7..3fd6ff232 100644 --- a/manifests/config.pp +++ b/manifests/config.pp @@ -87,6 +87,9 @@ # [*plugin_ml2_config*] # (optional) Manage configuration of ml2_conf.ini # +# [*plugin_nsx_config*] +# (optional) Manage configuration of plugins/vmware/nsx.ini +# # NOTE: The configuration MUST NOT be already handled by this module # or Puppet catalog compilation will fail with duplicate resources. # @@ -113,6 +116,7 @@ class neutron::config ( $plugin_ovn_config = {}, $plugin_nuage_config = {}, $plugin_ml2_config = {}, + $plugin_nsx_config = {}, ) { include ::neutron::deps @@ -139,6 +143,7 @@ class neutron::config ( validate_hash($plugin_ovn_config) validate_hash($plugin_nuage_config) validate_hash($plugin_ml2_config) + validate_hash($plugin_nsx_config) create_resources('neutron_config', $server_config) create_resources('neutron_api_config', $api_config) @@ -161,4 +166,5 @@ class neutron::config ( create_resources('neutron_plugin_nuage', $plugin_nuage_config) create_resources('neutron_plugin_ml2', $plugin_ml2_config) create_resources('neutron_l2gw_service_config', $l2gw_service_config) + create_resources('neutron_plugin_nsx', $plugin_nsx_config) } diff --git a/manifests/deps.pp b/manifests/deps.pp index da0b850fc..8552d82f7 100644 --- a/manifests/deps.pp +++ b/manifests/deps.pp @@ -67,6 +67,7 @@ class neutron::deps { Anchor['neutron::config::begin'] -> Neutron_sriov_agent_config<||> ~> Anchor['neutron::config::end'] Anchor['neutron::config::begin'] -> Neutron_vpnaas_agent_config<||> ~> Anchor['neutron::config::end'] Anchor['neutron::config::begin'] -> Neutron_vpnaas_service_config<||> ~> Anchor['neutron::config::end'] + Anchor['neutron::config::begin'] -> Neutron_plugin_nsx<||> ~> Anchor['neutron::config::end'] # Support packages need to be installed in the install phase, but we don't # put them in the chain above because we don't want any false dependencies diff --git a/manifests/init.pp b/manifests/init.pp index c3d628833..e0ce11fb3 100644 --- a/manifests/init.pp +++ b/manifests/init.pp @@ -29,7 +29,7 @@ # (optional) Neutron plugin provider # Defaults to ml2 # Could be bigswitch, brocade, cisco, embrane, hyperv, midonet, -# ml2, mlnx, nec, nicira, plumgrid, ryu, nuage, opencontrail +# ml2, mlnx, nec, nicira, plumgrid, ryu, nuage, opencontrail, nsx # # Example for nuage: # diff --git a/manifests/params.pp b/manifests/params.pp index 133319ca4..3f14ba51e 100644 --- a/manifests/params.pp +++ b/manifests/params.pp @@ -34,6 +34,8 @@ class neutron::params { $bgpvpn_bagpipe_service = 'bagpipe-bgp' $bgpvpn_plugin_package = 'python-networking-bgpvpn' $l2gw_agent_service = 'neutron-l2gw-agent' + $nsx_plugin_package = 'vmware-nsx' + $nsx_config_file = '/etc/neutron/plugins/vmware/nsx.ini' if($::osfamily == 'Redhat') { $nobody_user_group = 'nobody' diff --git a/manifests/plugins/nsx.pp b/manifests/plugins/nsx.pp new file mode 100644 index 000000000..abedf3a58 --- /dev/null +++ b/manifests/plugins/nsx.pp @@ -0,0 +1,111 @@ +# +# Configure the VMware NSX plugin for neutron. +# +# === Parameters +# +# [*default_overlay_tz*] +# UUID of the default overlay Transport Zone to be used for creating +# tunneled isolated "Neutron" networks. This option MUST be specified. +# +# [*default_tier0_router*] +# UUID of the pre-created default tier0 (provider) router on NSX backend. +# This option is used to create external networks and MUST be specified. +# +# [*nsx_api_managers*] +# Comma separated NSX manager IP addresses. This option MUST be specified. +# +# [*nsx_api_user*] +# The username for NSX manager. +# +# [*nsx_api_password*] +# The password for NSX manager. +# +# [*dhcp_profile_uuid*] +# UUID of the pre-created DHCP profile on NSX backend to support native DHCP. +# This option MUST be specified if native_dhcp_metadata is True. +# +# [*metadata_proxy_uuid*] +# UUID of the pre-created Metadata Proxy on NSX backend. This option MUST +# be specified if native_dhcp_metadata is True. +# +# [*native_dhcp_metadata*] +# Flag to enable native DHCP and Metadata. +# +# [*package_ensure*] +# (optional) Ensure state for package. +# Defaults to 'present'. +# +# [*purge_config*] +# (optional) Whether to set only the specified config options +# in the nvp config. +# Defaults to false. +# +class neutron::plugins::nsx ( + $default_overlay_tz = $::os_service_default, + $default_tier0_router = $::os_service_default, + $nsx_api_managers = $::os_service_default, + $nsx_api_user = $::os_service_default, + $nsx_api_password = $::os_service_default, + $dhcp_profile_uuid = $::os_service_default, + $metadata_proxy_uuid = $::os_service_default, + $native_dhcp_metadata = $::os_service_default, + $package_ensure = 'present', + $purge_config = false, +) { + + include ::neutron::deps + include ::neutron::params + + file { '/etc/neutron/plugins/vmware': + ensure => directory, + tag => 'neutron-config-file', + } + + file { $::neutron::params::nsx_config_file: + ensure => file, + owner => 'root', + group => 'neutron', + require => File['/etc/neutron/plugins/vmware'], + mode => '0640', + tag => 'neutron-config-file', + } + + if $::osfamily == 'Debian' { + file_line { '/etc/default/neutron-server:NEUTRON_PLUGIN_CONFIG': + path => '/etc/default/neutron-server', + match => '^NEUTRON_PLUGIN_CONFIG=(.*)$', + line => "NEUTRON_PLUGIN_CONFIG=${::neutron::params::nsx_config_file}", + tag => 'neutron-file-line', + } + } + + if $::osfamily == 'Redhat' { + file { '/etc/neutron/plugin.ini': + ensure => link, + require => File[$::neutron::params::nsx_config_file], + target => $::neutron::params::nsx_config_file, + tag => 'neutron-config-file', + } + } + + + resources { 'neutron_plugin_nsx': + purge => $purge_config, + } + + neutron_plugin_nsx { + 'nsx_v3/default_overlay_tz': value => $default_overlay_tz; + 'nsx_v3/default_tier0_router': value => $default_tier0_router; + 'nsx_v3/nsx_api_managers': value => $nsx_api_managers; + 'nsx_v3/nsx_api_user': value => $nsx_api_user; + 'nsx_v3/nsx_api_password': value => $nsx_api_password; + 'nsx_v3/dhcp_profile_uuid': value => $dhcp_profile_uuid; + 'nsx_v3/metadata_proxy_uuid': value => $metadata_proxy_uuid; + 'nsx_v3/native_dhcp_metadata': value => $native_dhcp_metadata; + } + + if ($::neutron::core_plugin != 'vmware_nsx.plugin.NsxV3Plugin') and + ($::neutron::core_plugin != 'nsx') { + fail('VMware NSX plugin should be the core_plugin in neutron.conf') + } +} diff --git a/releasenotes/notes/nsx-support-1254839718d8df8c.yaml b/releasenotes/notes/nsx-support-1254839718d8df8c.yaml new file mode 100644 index 000000000..89487a8ac --- /dev/null +++ b/releasenotes/notes/nsx-support-1254839718d8df8c.yaml @@ -0,0 +1,3 @@ +--- +features: + - Add support to deploy VMware NSX plugin. diff --git a/spec/acceptance/neutron_config_spec.rb b/spec/acceptance/neutron_config_spec.rb index b7c09b77a..33c031a4c 100644 --- a/spec/acceptance/neutron_config_spec.rb +++ b/spec/acceptance/neutron_config_spec.rb @@ -23,6 +23,7 @@ describe 'basic neutron_config resource' do '/etc/neutron/plugins/midonet/midonet.ini', '/etc/neutron/plugins/opencontrail/ContrailPlugin.ini', '/etc/neutron/plugins/plumgrid/plumgrid.ini', + '/etc/neutron/plugins/vmware/nsx.ini', '/etc/neutron/plugins/ml2/ml2_conf_sriov.ini', '/etc/neutron/plugins/ml2/sriov_agent.ini', '/etc/neutron/plugins/ml2/vpp_agent.ini'] @@ -57,6 +58,7 @@ describe 'basic neutron_config resource' do File <||> -> Neutron_sriov_agent_config <||> File <||> -> Neutron_agent_vpp <||> File <||> -> Neutron_l2gw_agent_config <||> + File <||> -> Neutron_plugin_nsx <||> $neutron_directories = ['/etc/neutron', @@ -67,7 +69,8 @@ describe 'basic neutron_config resource' do '/etc/neutron/plugins/nicira', '/etc/neutron/plugins/midonet', '/etc/neutron/plugins/opencontrail', - '/etc/neutron/plugins/plumgrid'] + '/etc/neutron/plugins/plumgrid', + '/etc/neutron/plugins/vmware'] $neutron_files = [ '/etc/neutron/api-paste.ini', '/etc/neutron/neutron.conf', @@ -90,6 +93,7 @@ describe 'basic neutron_config resource' do '/etc/neutron/plugins/midonet/midonet.ini', '/etc/neutron/plugins/opencontrail/ContrailPlugin.ini', '/etc/neutron/plugins/plumgrid/plumgrid.ini', + '/etc/neutron/plugins/vmware/nsx.ini', '/etc/neutron/plugins/ml2/ml2_conf_sriov.ini', '/etc/neutron/plugins/ml2/sriov_agent.ini', '/etc/neutron/plugins/ml2/vpp_agent.ini'] @@ -496,6 +500,24 @@ describe 'basic neutron_config resource' do ensure_absent_val => 'toto', } + neutron_plugin_nsx { 'DEFAULT/thisshouldexist' : + value => 'foo', + } + + neutron_plugin_nsx { 'DEFAULT/thisshouldnotexist' : + value => '', + } + + neutron_plugin_nsx { 'DEFAULT/thisshouldexist2' : + value => '', + ensure_absent_val => 'toto', + } + + neutron_plugin_nsx { 'DEFAULT/thisshouldnotexist2' : + value => 'toto', + ensure_absent_val => 'toto', + } + neutron_plugin_sriov { 'DEFAULT/thisshouldexist' : value => 'foo', } diff --git a/spec/classes/neutron_config_spec.rb b/spec/classes/neutron_config_spec.rb index 359549dec..74947d4aa 100644 --- a/spec/classes/neutron_config_spec.rb +++ b/spec/classes/neutron_config_spec.rb @@ -91,6 +91,7 @@ describe 'neutron::config' do :plugin_cisco_config => config_hash, :plugin_midonet_config => config_hash, :plugin_plumgrid_config => config_hash, + :plugin_nsx_config => config_hash, :plugin_opencontrail_config => config_hash, :plugin_nuage_config => config_hash, :plugin_ml2_config => config_hash @@ -133,6 +134,12 @@ describe 'neutron::config' do is_expected.to contain_neutron_plugin_plumgrid('DEFAULT/baz').with_ensure('absent') end + it 'configures arbitrary neutron_plugin_nsx configurations' do + is_expected.to contain_neutron_plugin_nsx('DEFAULT/foo').with_value('fooValue') + is_expected.to contain_neutron_plugin_nsx('DEFAULT/bar').with_value('barValue') + is_expected.to contain_neutron_plugin_nsx('DEFAULT/baz').with_ensure('absent') + end + it 'configures arbitrary neutron_plugin_opencontrail configurations' do is_expected.to contain_neutron_plugin_opencontrail('DEFAULT/foo').with_value('fooValue') is_expected.to contain_neutron_plugin_opencontrail('DEFAULT/bar').with_value('barValue') diff --git a/spec/classes/neutron_plugins_nsx_spec.rb b/spec/classes/neutron_plugins_nsx_spec.rb new file mode 100644 index 000000000..9a8a581a8 --- /dev/null +++ b/spec/classes/neutron_plugins_nsx_spec.rb @@ -0,0 +1,142 @@ +require 'spec_helper' + +describe 'neutron::plugins::nsx' do + + let :pre_condition do + "class { 'neutron': + rabbit_password => 'passw0rd', + core_plugin => 'vmware_nsx.plugin.NsxV3Plugin' } + class { '::neutron::keystone::authtoken': + password => 'passw0rd', + } + class { 'neutron::server': }" + end + + let :default_params do + { + :default_overlay_tz => '', + :default_tier0_router => '', + :nsx_api_managers => '', + :nsx_api_user => '', + :nsx_api_password => '', + :dhcp_profile_uuid => '', + :metadata_proxy_uuid => '', + :native_dhcp_metadata => '', + :package_ensure => 'present', + :purge_config => false, + } + end + + shared_examples_for 'neutron plugin nsx' do + + context 'with defaults' do + it { is_expected.to contain_class('neutron::params') } + + it 'should have a nsx plugin ini file' do + is_expected.to contain_file('/etc/neutron/plugins/vmware/nsx.ini').with( + :ensure => 'file', + :owner => 'root', + :group => 'neutron', + :mode => '0640' + ) + end + + it 'should configure neutron.conf' do + is_expected.to contain_neutron_config('DEFAULT/core_plugin').with_value('vmware_nsx.plugin.NsxV3Plugin') + end + + it 'passes purge to resource' do + is_expected.to contain_resources('neutron_plugin_nsx').with({ + :purge => false + }) + end + + it 'should configure nsx.ini' do + is_expected.to contain_neutron_plugin_nsx('nsx_v3/default_overlay_tz').with_value(default_params[:default_overlay_tz]) + is_expected.to contain_neutron_plugin_nsx('nsx_v3/default_tier0_router').with_value(default_params[:default_tier0_router]) + is_expected.to contain_neutron_plugin_nsx('nsx_v3/nsx_api_managers').with_value(default_params[:nsx_api_managers]) + is_expected.to contain_neutron_plugin_nsx('nsx_v3/nsx_api_password').with_value(default_params[:nsx_api_password]) + is_expected.to contain_neutron_plugin_nsx('nsx_v3/dhcp_profile_uuid').with_value(default_params[:dhcp_profile_uuid]) + is_expected.to contain_neutron_plugin_nsx('nsx_v3/metadata_proxy_uuid').with_value(default_params[:metadata_proxy_uuid]) + is_expected.to contain_neutron_plugin_nsx('nsx_v3/native_dhcp_metadata').with_value(default_params[:native_dhcp_metadata]) + end + end + + context 'with parameters' do + let (:params) do + { + :default_overlay_tz => 'fake-tz-uuid', + :default_tier0_router => 'fake-tier0-uuid', + :nsx_api_managers => '127.0.0.1', + :nsx_api_user => 'admin', + :nsx_api_password => 'pasword', + :dhcp_profile_uuid => 'fake-dhcp-uuid', + :metadata_proxy_uuid => 'fake-metadata-uuid', + :native_dhcp_metadata => 'True', + :purge_config => true, + } + end + + it { + is_expected.to contain_neutron_plugin_nsx('nsx_v3/default_overlay_tz').with_value(params[:default_overlay_tz]) + is_expected.to contain_neutron_plugin_nsx('nsx_v3/default_tier0_router').with_value(params[:default_tier0_router]) + is_expected.to contain_neutron_plugin_nsx('nsx_v3/nsx_api_managers').with_value(params[:nsx_api_managers]) + is_expected.to contain_neutron_plugin_nsx('nsx_v3/nsx_api_password').with_value(params[:nsx_api_password]) + is_expected.to contain_neutron_plugin_nsx('nsx_v3/dhcp_profile_uuid').with_value(params[:dhcp_profile_uuid]) + is_expected.to contain_neutron_plugin_nsx('nsx_v3/metadata_proxy_uuid').with_value(params[:metadata_proxy_uuid]) + is_expected.to contain_neutron_plugin_nsx('nsx_v3/native_dhcp_metadata').with_value(params[:native_dhcp_metadata]) + is_expected.to contain_resources('neutron_plugin_nsx').with({ + :purge => true + }) + } + end + + context 'configure nsx with wrong core_plugin configure' do + let :pre_condition do + "class { 'neutron': + rabbit_password => 'passw0rd', + core_plugin => 'foo' }" + end + + it_raises 'a Puppet::Error', /NSX plugin should be the core_plugin in neutron.conf/ + end + end + + shared_examples_for 'neutron plugin nsx on Debian' do + context 'with defaults' do + it 'configures /etc/default/neutron-server' do + is_expected.to contain_file_line('/etc/default/neutron-server:NEUTRON_PLUGIN_CONFIG').with( + :path => '/etc/default/neutron-server', + :match => '^NEUTRON_PLUGIN_CONFIG=(.*)$', + :line => 'NEUTRON_PLUGIN_CONFIG=/etc/neutron/plugins/vmware/nsx.ini', + ) + is_expected.to contain_file_line('/etc/default/neutron-server:NEUTRON_PLUGIN_CONFIG').that_requires('Anchor[neutron::config::begin]') + is_expected.to contain_file_line('/etc/default/neutron-server:NEUTRON_PLUGIN_CONFIG').that_notifies('Anchor[neutron::config::end]') + end + end + end + + shared_examples_for 'neutron plugin nsx on RedHat' do + context 'with defaults' do + it 'should create plugin symbolic link' do + is_expected.to contain_file('/etc/neutron/plugin.ini').with( + :ensure => 'link', + :target => '/etc/neutron/plugins/vmware/nsx.ini') + 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 + + it_configures 'neutron plugin nsx' + it_configures "neutron plugin nsx on #{facts[:osfamily]}" + end + end + +end diff --git a/spec/unit/provider/neutron_plugin_nsx/ini_setting_spec.rb b/spec/unit/provider/neutron_plugin_nsx/ini_setting_spec.rb new file mode 100644 index 000000000..85d4322b1 --- /dev/null +++ b/spec/unit/provider/neutron_plugin_nsx/ini_setting_spec.rb @@ -0,0 +1,55 @@ +$LOAD_PATH.push( + File.join( + File.dirname(__FILE__), + '..', + '..', + '..', + 'fixtures', + 'modules', + 'inifile', + 'lib') +) + +require 'spec_helper' + +provider_class = Puppet::Type.type(:neutron_plugin_nsx).provider(:ini_setting) +describe provider_class do + let(:resource ) do + Puppet::Type::Neutron_plugin_nsx.new({ + :name => 'DEFAULT/foo', + :value => 'bar', + }) + end + + let (:provider) { resource.provider } + + [ 'RedHat', 'Debian' ].each do |os| + context "on #{os} with default setting" do + it 'it should fall back to default and use plugin.ini' do + Facter.fact(:operatingsystem).stubs(:value).returns("#{os}") + expect(provider.section).to eq('DEFAULT') + expect(provider.setting).to eq('foo') + expect(provider.file_path).to eq('/etc/neutron/plugins/vmware/nsx.ini') + end + end + end + + it 'should ensure absent when is specified as a value' do + resource = Puppet::Type::Neutron_plugin_nsx.new( + {:name => 'somename/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_plugin_nsx.new( + {:name => 'somename/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_plugin_nsx_spec.rb b/spec/unit/type/neutron_plugin_nsx_spec.rb new file mode 100644 index 000000000..9c6fa8efd --- /dev/null +++ b/spec/unit/type/neutron_plugin_nsx_spec.rb @@ -0,0 +1,20 @@ +require 'puppet' +require 'puppet/type/neutron_plugin_nsx' + +describe 'Puppet::Type.type(:neutron_plugin_nsx)' do + + before :each do + @neutron_plugin_nsx = Puppet::Type.type(:neutron_plugin_nsx).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 => 'vmware-nsx') + catalog.add_resource package, @neutron_plugin_nsx + dependency = @neutron_plugin_nsx.autorequire + expect(dependency.size).to eq(1) + expect(dependency[0].target).to eq(@neutron_plugin_nsx) + expect(dependency[0].source).to eq(package) + end + +end