From f94e9957c341b9350b75a43778276a5030055628 Mon Sep 17 00:00:00 2001 From: Maru Newby Date: Tue, 18 Jun 2013 13:25:22 +0000 Subject: [PATCH] Add quantum_router_interface custom type. Change-Id: I0f3eb0ec30247932564eb1b65bd08bd75d87c481 --- examples/base_provision.pp | 4 + lib/puppet/provider/quantum.rb | 27 ++++++ .../quantum_router_interface/quantum.rb | 90 +++++++++++++++++++ lib/puppet/type/quantum_router_interface.rb | 47 ++++++++++ .../quantum_router_interface/quantum_spec.rb | 36 ++++++++ spec/unit/provider/quantum_spec.rb | 44 +++++++++ 6 files changed, 248 insertions(+) create mode 100644 lib/puppet/provider/quantum_router_interface/quantum.rb create mode 100644 lib/puppet/type/quantum_router_interface.rb create mode 100644 spec/unit/provider/quantum_router_interface/quantum_spec.rb diff --git a/examples/base_provision.pp b/examples/base_provision.pp index 415c6bc33..5186198ce 100644 --- a/examples/base_provision.pp +++ b/examples/base_provision.pp @@ -48,3 +48,7 @@ quantum_router { 'demo_router': gateway_network_name => 'public', require => Quantum_subnet['public_subnet'], } + +quantum_router_interface { 'demo_router:private_subnet': + ensure => present, +} diff --git a/lib/puppet/provider/quantum.rb b/lib/puppet/provider/quantum.rb index b9380b208..2f632e730 100644 --- a/lib/puppet/provider/quantum.rb +++ b/lib/puppet/provider/quantum.rb @@ -1,4 +1,6 @@ +require 'csv' require 'puppet/util/inifile' + class Puppet::Provider::Quantum < Puppet::Provider def self.conf_filename @@ -140,4 +142,29 @@ correctly configured.") return exts end + def self.list_router_ports(router_name_or_id) + results = [] + cmd_output = auth_quantum("router-port-list", + '--format=csv', + router_name_or_id) + if ! cmd_output + return results + end + + headers = nil + CSV.parse(cmd_output) do |row| + if headers == nil + headers = row + else + result = Hash[*headers.zip(row).flatten] + match_data = /.*"subnet_id": "(.*)", .*/.match(result['fixed_ips']) + if match_data + result['subnet_id'] = match_data[1] + end + results << result + end + end + return results + end + end diff --git a/lib/puppet/provider/quantum_router_interface/quantum.rb b/lib/puppet/provider/quantum_router_interface/quantum.rb new file mode 100644 index 000000000..17253625d --- /dev/null +++ b/lib/puppet/provider/quantum_router_interface/quantum.rb @@ -0,0 +1,90 @@ +require File.join(File.dirname(__FILE__), '..','..','..', + 'puppet/provider/quantum') + +Puppet::Type.type(:quantum_router_interface).provide( + :quantum, + :parent => Puppet::Provider::Quantum +) do + desc <<-EOT + Quantum provider to manage quantum_router_interface type. + + Assumes that the quantum service is configured on the same host. + + It is not possible to manage an interface for the subnet used by + the gateway network, and such an interface will appear in the list + of resources ('puppet resource [type]'). Attempting to manage the + gateway interfae will result in an error. + + EOT + + commands :quantum => 'quantum' + + mk_resource_methods + + def self.instances + subnet_name_hash = {} + Puppet::Type.type('quantum_subnet').instances.each do |instance| + subnet_name_hash[instance.provider.id] = instance.provider.name + end + instances_ = [] + Puppet::Type.type('quantum_router').instances.each do |instance| + list_router_ports(instance.provider.id).each do |port_hash| + router_name = instance.provider.name + subnet_name = subnet_name_hash[port_hash['subnet_id']] + name = "#{router_name}:#{subnet_name}" + instances_ << new( + :ensure => :present, + :name => name, + :id => port_hash['id'] + ) + end + end + return instances_ + end + + def self.prefetch(resources) + instances_ = instances + resources.keys.each do |name| + if provider = instances_.find{ |instance| instance.name == name } + resources[name].provider = provider + end + end + end + + def exists? + @property_hash[:ensure] == :present + end + + def create + results = auth_quantum("router-interface-add", '--format=shell', + resource[:name].split(':', 2)) + + if results =~ /Added interface to router/ + @property_hash = { + :ensure => :present, + :name => resource[:name], + } + else + fail("did not get expected message on interface addition, got #{results}") + end + end + + def router_name + name.split(':', 2).first + end + + def subnet_name + name.split(':', 2).last + end + + def id + # TODO: Need to look this up for newly-added resources since it is + # not returned on creation + end + + def destroy + auth_quantum('router-interface-delete', router_name, subnet_name) + @property_hash[:ensure] = :absent + end + +end diff --git a/lib/puppet/type/quantum_router_interface.rb b/lib/puppet/type/quantum_router_interface.rb new file mode 100644 index 000000000..048792ed8 --- /dev/null +++ b/lib/puppet/type/quantum_router_interface.rb @@ -0,0 +1,47 @@ +Puppet::Type.newtype(:quantum_router_interface) do + + desc <<-EOT + This is currently used to model the creation of + quantum router interfaces. + + Router interfaces are an association between a router and a + subnet. + EOT + + ensurable + + newparam(:name, :namevar => true) do + newvalues(/^\S+:\S+$/) + end + + newproperty(:id) do + validate do |v| + raise(Puppet::Error, 'This is a read only property') + end + end + + newproperty(:router_name) do + validate do |v| + raise(Puppet::Error, 'This is a read only property') + end + end + + newproperty(:subnet_name) do + validate do |v| + raise(Puppet::Error, 'This is a read only property') + end + end + + autorequire(:service) do + ['quantum-server'] + end + + autorequire(:quantum_router) do + self[:name].split(':', 2).first + end + + autorequire(:quantum_subnet) do + self[:name].split(':', 2).last + end + +end diff --git a/spec/unit/provider/quantum_router_interface/quantum_spec.rb b/spec/unit/provider/quantum_router_interface/quantum_spec.rb new file mode 100644 index 000000000..a897f8357 --- /dev/null +++ b/spec/unit/provider/quantum_router_interface/quantum_spec.rb @@ -0,0 +1,36 @@ +require 'puppet' +require 'spec_helper' +require 'puppet/provider/quantum_router_interface/quantum' + +provider_class = Puppet::Type.type(:quantum_router_interface). + provider(:quantum) + +describe provider_class do + + let :interface_attrs do + { + :name => 'router:subnet', + :ensure => 'present', + } + end + + describe 'when accessing attributes of an interface' do + let :resource do + Puppet::Type::Quantum_router_interface.new(interface_attrs) + end + + let :provider do + provider_class.new(resource) + end + + it 'should return the correct router name' do + provider.router_name.should eql('router') + end + + it 'should return the correct subnet name' do + provider.subnet_name.should eql('subnet') + end + + end + +end diff --git a/spec/unit/provider/quantum_spec.rb b/spec/unit/provider/quantum_spec.rb index a5e907297..6a2ee50f4 100644 --- a/spec/unit/provider/quantum_spec.rb +++ b/spec/unit/provider/quantum_spec.rb @@ -128,4 +128,48 @@ subnet2" end + describe 'when listing router ports' do + + let :router do + 'router1' + end + + it 'should handle an empty port list' do + klass.expects(:auth_quantum).with('router-port-list', + '--format=csv', + router) + result = klass.list_router_ports(router) + result.should eql([]) + end + + it 'should handle several ports' do + output = <<-EOT +"id","name","mac_address","fixed_ips" +"1345e576-a21f-4c2e-b24a-b245639852ab","","fa:16:3e:e3:e6:38","{""subnet_id"": ""839a1d2d-2c6e-44fb-9a2b-9b011dce8c2f"", ""ip_address"": ""10.0.0.1""}" +"de0dc526-02b2-467c-9832-2c3dc69ac2b4","","fa:16:3e:f6:b5:72","{""subnet_id"": ""e4db0abd-276a-4f69-92ea-8b9e4eacfd43"", ""ip_address"": ""172.24.4.226""}" + EOT + expected = + [{ "fixed_ips"=> + "{\"subnet_id\": \"839a1d2d-2c6e-44fb-9a2b-9b011dce8c2f\", \ +\"ip_address\": \"10.0.0.1\"}", + "name"=>"", + "subnet_id"=>"839a1d2d-2c6e-44fb-9a2b-9b011dce8c2f", + "id"=>"1345e576-a21f-4c2e-b24a-b245639852ab", + "mac_address"=>"fa:16:3e:e3:e6:38"}, + {"fixed_ips"=> + "{\"subnet_id\": \"e4db0abd-276a-4f69-92ea-8b9e4eacfd43\", \ +\"ip_address\": \"172.24.4.226\"}", + "name"=>"", + "subnet_id"=>"e4db0abd-276a-4f69-92ea-8b9e4eacfd43", + "id"=>"de0dc526-02b2-467c-9832-2c3dc69ac2b4", + "mac_address"=>"fa:16:3e:f6:b5:72"}] + klass.expects(:auth_quantum). + with('router-port-list', '--format=csv', router). + returns(output) + result = klass.list_router_ports(router) + result.should eql(expected) + end + + end + end