Merge "Add quantum_router_interface custom type."
This commit is contained in:
commit
5391172b55
|
@ -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,
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue