From 7e7e848170117e2bb31840778e642132bef136e2 Mon Sep 17 00:00:00 2001 From: Maksym Yatsenko Date: Fri, 9 Oct 2015 14:31:31 +0300 Subject: [PATCH] Floating IP range support in Nova network This patch provides a possibility to add ip ranges of networks (i.e.: 192.168.1.210-192.168.1.224) to a floating ip pool. Depends-On: I802280e4e2912c9aed1c10d519ead84b1f22f900 Change-Id: Iffb0af32753708decb8d2b60f1075b66f7520553 --- Gemfile | 1 + .../provider/nova_floating/nova_manage.rb | 76 ++++++++++++-- lib/puppet/type/nova_floating.rb | 5 +- .../nova_floating/nova_manage_spec.rb | 99 +++++++++++++++++++ 4 files changed, 172 insertions(+), 9 deletions(-) create mode 100644 spec/unit/provider/nova_floating/nova_manage_spec.rb diff --git a/Gemfile b/Gemfile index 84917c5da..c22e44167 100644 --- a/Gemfile +++ b/Gemfile @@ -15,6 +15,7 @@ group :development, :test do gem 'json', :require => 'false' gem 'webmock', :require => 'false' gem 'r10k', :require => 'false' + gem 'netaddr', :require => 'false' end group :system_tests do diff --git a/lib/puppet/provider/nova_floating/nova_manage.rb b/lib/puppet/provider/nova_floating/nova_manage.rb index de3d71b67..37a4a6257 100644 --- a/lib/puppet/provider/nova_floating/nova_manage.rb +++ b/lib/puppet/provider/nova_floating/nova_manage.rb @@ -1,3 +1,5 @@ +require 'netaddr' + Puppet::Type.type(:nova_floating).provide(:nova_manage) do desc "Manage nova floating" @@ -5,19 +7,79 @@ Puppet::Type.type(:nova_floating).provide(:nova_manage) do optional_commands :nova_manage => 'nova-manage' def exists? - # Calculate num quads to grab for prefix - mask=resource[:network].sub(/.*\/([0-9][0-9]?)/, '\1').to_i - num_quads = 4 - mask / 8 - prefix=resource[:network].sub(/(\.[0-9]{1,3}){#{num_quads}}(\/[0-9]{1,2})?$/, '') + "." - return nova_manage("floating", "list").match(/#{prefix}/) + if resource[:network] =~ /^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\/[0-9]{1,2}$/ + # Calculate num quads to grab for prefix$$ + mask=resource[:network].sub(/.*\/([0-9][0-9]?)/, '\1').to_i + num_quads = 4 - mask / 8 + prefix=resource[:network].sub(/(\.[0-9]{1,3}){#{num_quads}}(\/[0-9]{1,2})?$/, '') + "." + return nova_manage("floating", "list").match(/#{prefix}/) + + elsif resource[:network].to_s =~ /^.{0,2}\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}-\d{1,3}\.\d{1,3}.\d{1,3}.\d{1,3}.{0,2}/ + if resource[:ensure] == :absent + operate_range.any? + else + operate_range.empty? + end + end end def create - nova_manage("floating", "create", '--pool', resource[:pool], resource[:network]) + if resource[:network] =~ /^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\/[0-9]{1,2}$/ + nova_manage("floating", "create", '--pool', resource[:pool], resource[:network]) + + elsif resource[:network].to_s =~ /^.{0,2}\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}-\d{1,3}\.\d{1,3}.\d{1,3}.\d{1,3}.{0,2}/ + mixed_range.each do |ip| + nova_manage("floating", "create", ip) + end + end end def destroy - nova_manage("floating", "delete", resource[:network]) + if resource[:network] =~ /^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\/[0-9]{1,2}$/ + nova_manage("floating", "delete", resource[:network]) + + elsif resource[:network].to_s =~ /^.{0,2}\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}-\d{1,3}\.\d{1,3}.\d{1,3}.\d{1,3}.{0,2}/ + mixed_range.each do |ip| + nova_manage("floating", "delete", ip ) + end + end end + # Create range in cidr, including first and last ip + # Nova will create this range, excluding network and broadcast IPs + def mixed_range + range = [] + NetAddr.merge(operate_range).each do |cidr| + tmp_range = NetAddr::CIDR.create(cidr).enumerate + range << tmp_range.first.to_s + range << tmp_range.last.to_s + end + range.uniq! + range += NetAddr.merge(operate_range).delete_if{ |part| part =~ /\/3[12]/} + end + + # Calculate existed IPs and provided ip ranges + def operate_range + exist_range = [] + output = nova_manage("floating", "list") + range_list = output.scan(/\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}/) + range_list.each do |element| + exist_range << element + end + if @resource[:ensure] == :absent + ip_range & exist_range + else + ip_range - exist_range + end + end + + def ip_range + ip_range = [] + Array(@resource[:network]).each do |rng| + ip = rng.split('-') + generated_range = NetAddr.range(NetAddr::CIDR.create(ip.first), NetAddr::CIDR.create(ip.last)) + ip_range += generated_range.unshift(ip.first).push(ip.last) + end + return ip_range + end end diff --git a/lib/puppet/type/nova_floating.rb b/lib/puppet/type/nova_floating.rb index 6da29ecce..2e6b67103 100644 --- a/lib/puppet/type/nova_floating.rb +++ b/lib/puppet/type/nova_floating.rb @@ -5,8 +5,9 @@ Puppet::Type.newtype(:nova_floating) do ensurable newparam(:network, :namevar => true) do - desc "Network (ie, 192.168.1.0/24 or 192.168.1.128/25 etc.)" - newvalues(/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\/[0-9]{1,2}$/) + desc "It can contain network (ie, 192.168.1.0/24 or 192.168.1.128/25 etc.), + ip range ('192.168.1.1-192.168.1.55' or list of ip ranges ['192.168.1.1-192.168.1.25', '192.168.1.30-192.168.1.55'])" + newvalues(/^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\/[0-9]{1,2}$ || ^(\d{1,3}\.){3}\d{1,3}-(\d{1,3}\.){3}\d{1,3}$/) end newparam(:pool) do diff --git a/spec/unit/provider/nova_floating/nova_manage_spec.rb b/spec/unit/provider/nova_floating/nova_manage_spec.rb new file mode 100644 index 000000000..f9b29b40c --- /dev/null +++ b/spec/unit/provider/nova_floating/nova_manage_spec.rb @@ -0,0 +1,99 @@ +require 'puppet' +require 'puppet/provider/nova_floating/nova_manage' +require 'tempfile' + +provider_class = Puppet::Type.type(:nova_floating).provider(:nova_manage) + +describe provider_class do + + let :range_attrs do + { + :ensure => 'present', + :pool => 'nova', + :network => '10.1.0.1-10.1.0.2', + } + end + + let :network_attrs do + { + :ensure => 'present', + :pool => 'nova', + :network => '11.1.0.0/30' + } + end + + let :resource_range do + Puppet::Type::Nova_floating.new(range_attrs) + end + + let :resource_network do + Puppet::Type::Nova_floating.new(network_attrs) + end + + let :provider_range do + provider_class.new(resource_range) + end + + let :provider_network do + provider_class.new(resource_network) + end + + shared_examples 'nova_floating' do + + describe '#exists?' do + + it 'should check if ips of a network are not existing' do + provider_network.expects(:nova_manage).with('floating', 'list').returns('No floating IP addresses have been defined.') + expect(provider_network.exists?).to be_falsey + end + it 'should check if ips of a network are existing' do + provider_network.expects(:nova_manage).with('floating', 'list').returns('None 11.1.0.1 None nova br-ex\nNone 11.1.0.2 None nova br-ex') + expect(provider_network.exists?).to be_truthy + end + + it 'should check if ips are not existing' do + provider_range.expects(:nova_manage).with('floating', 'list').returns('No floating IP addresses have been defined.') + expect(provider_range.exists?).to be_falsey + end + it 'should check if ips are existing' do + provider_range.expects(:nova_manage).with('floating', 'list').returns('None 10.1.0.1 None nova br-ex\nNone 10.1.0.2 None nova br-ex') + expect(provider_range.exists?).to be_truthy + end + + end + + describe '#create' do + it 'should check if ips of network were created' do + provider_network.expects(:nova_manage).with('floating', 'create', '--pool', 'nova', '11.1.0.0/30').returns('None 11.1.0.1 None nova br-ex\nNone 11.1.0.2 None nova br-ex') + expect(provider_network.create).to be_truthy + end + + it 'should check if ips of range were created' do + provider_range.stubs(:nova_manage).with('floating', 'create', '10.1.0.1') + provider_range.stubs(:nova_manage).with('floating', 'create', '10.1.0.2') + provider_range.stubs(:nova_manage).with('floating', 'list').returns('None 10.1.0.1 None nova br-ex\nNone 10.1.0.2 None nova br-ex') + expect(provider_range.create).to be_truthy + end + + end + + + describe '#destroy' do + it 'should check if ips of network were deleted' do + provider_network.expects(:nova_manage).with('floating', 'delete', '11.1.0.0/30').returns('No floating IP addresses have been defined.') + expect(provider_network.destroy).to be_truthy + end + + it 'should check if ips of range were deleted' do + provider_range.stubs(:nova_manage).with('floating', 'delete', '10.1.0.1') + provider_range.expects(:nova_manage).with('floating', 'list').returns('None 10.1.0.2 None nova br-ex') + provider_range.stubs(:nova_manage).with('floating', 'delete', '10.1.0.2') + provider_range.expects(:nova_manage).with('floating', 'list').returns('No floating IP addresses have been defined.') + expect(provider_range.destroy).to be_truthy + end + + end + end + +it_behaves_like('nova_floating') +end