From 2a41927509398c84ad5175fac292e3fbce624b1c Mon Sep 17 00:00:00 2001 From: Maru Newby Date: Tue, 18 Jun 2013 13:25:22 +0000 Subject: [PATCH] Add quantum_router custom type. Change-Id: Ie00b839dc10c1e7b0a14801ad51fc483c7457c65 --- examples/base_provision.pp | 12 ++ lib/puppet/provider/quantum_router/quantum.rb | 146 ++++++++++++++++++ lib/puppet/type/quantum_router.rb | 91 +++++++++++ .../provider/quantum_router/quantum_spec.rb | 53 +++++++ 4 files changed, 302 insertions(+) create mode 100644 lib/puppet/provider/quantum_router/quantum.rb create mode 100644 lib/puppet/type/quantum_router.rb create mode 100644 spec/unit/provider/quantum_router/quantum_spec.rb diff --git a/examples/base_provision.pp b/examples/base_provision.pp index 60a6179a8..415c6bc33 100644 --- a/examples/base_provision.pp +++ b/examples/base_provision.pp @@ -3,6 +3,10 @@ # resources necessary to boot a vm with network connectivity provided # by quantum. # +# Note that a quantum_router resouce must declare a dependency on the +# first subnet of the gateway network. Other dependencies for the +# resources used in this example can be automatically determined. +# keystone_tenant { 'admin': ensure => present, @@ -36,3 +40,11 @@ quantum_subnet { 'private_subnet': network_name => 'private', tenant_name => 'demo', } + +# Tenant-private router - assumes network namespace isolation +quantum_router { 'demo_router': + ensure => present, + tenant_name => 'demo', + gateway_network_name => 'public', + require => Quantum_subnet['public_subnet'], +} diff --git a/lib/puppet/provider/quantum_router/quantum.rb b/lib/puppet/provider/quantum_router/quantum.rb new file mode 100644 index 000000000..2991599d0 --- /dev/null +++ b/lib/puppet/provider/quantum_router/quantum.rb @@ -0,0 +1,146 @@ +require File.join(File.dirname(__FILE__), '..','..','..', + 'puppet/provider/quantum') + +Puppet::Type.type(:quantum_router).provide( + :quantum, + :parent => Puppet::Provider::Quantum +) do + desc <<-EOT + Quantum provider to manage quantum_router type. + + Assumes that the quantum service is configured on the same host. + EOT + + commands :quantum => 'quantum' + + mk_resource_methods + + def self.instances + list_quantum_resources('router').collect do |id| + attrs = get_quantum_resource_attrs('router', id) + new( + :ensure => :present, + :name => attrs['name'], + :id => attrs['id'], + :admin_state_up => attrs['admin_state_up'], + :external_gateway_info => attrs['external_gateway_info'], + :status => attrs['status'], + :tenant_id => attrs['tenant_id'] + ) + end + 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 + opts = Array.new + + if @resource[:admin_state_up] == 'False' + opts << '--admin-state-down' + end + + if @resource[:tenant_name] + opts << "--tenant_id=#{get_tenant_id}" + elsif @resource[:tenant_id] + opts << "--tenant_id=#{@resource[:tenant_id]}" + end + + results = auth_quantum("router-create", '--format=shell', + opts, resource[:name]) + + if results =~ /Created a new router:/ + @router = Hash.new + results.split("\n").compact do |line| + @router[line.split('=').first] = \ + line.split('=', 2)[1].gsub(/\A"|"\Z/, '') + end + + @property_hash = { + :ensure => :present, + :name => resource[:name], + :id => @router[:id], + :admin_state_up => @router[:admin_state_up], + :external_gateway_info => @router[:external_gateway_info], + :status => @router[:status], + :tenant_id => @router[:tenant_id], + } + + if @resource[:gateway_network_name] + results = auth_quantum('router-gateway-set', + @resource[:name], + @resource[:gateway_network_name]) + if results =~ /Set gateway for router/ + attrs = self.class.get_quantum_resource_attrs('router', + @resource[:name]) + @property_hash[:external_gateway_info] = \ + attrs['external_gateway_info'] + else + fail(<<-EOT +did not get expected message on setting router gateway, got #{results} +EOT + ) + end + end + else + fail("did not get expected message on router creation, got #{results}") + end + end + + def get_tenant_id + @tenant_id ||= model.catalog.resource( \ + "Keystone_tenant[#{resource[:tenant_name]}]").provider.id + end + + def destroy + auth_quantum('router-delete', name) + @property_hash[:ensure] = :absent + end + + def gateway_network_name + if @gateway_network_name == nil and gateway_network_id + Puppet::Type.type('quantum_network').instances.each do |instance| + if instance.provider.id == gateway_network_id + @gateway_network_name = instance.provider.name + end + end + end + @gateway_network_name + end + + def gateway_network_name=(value) + if value == '' + auth_quantum('router-gateway-clear', name) + else + auth_quantum('router-gateway-set', name, value) + end + end + + def parse_gateway_network_id(external_gateway_info_) + match_data = /\{"network_id": "(.*)"\}/.match(external_gateway_info_) + if match_data + match_data[1] + else + '' + end + end + + def gateway_network_id + @gateway_network_id ||= parse_gateway_network_id(external_gateway_info) + end + + def admin_state_up=(value) + auth_quantum('router-update', "--admin-state-up=#{value}", name) + end + +end diff --git a/lib/puppet/type/quantum_router.rb b/lib/puppet/type/quantum_router.rb new file mode 100644 index 000000000..8b5a13f43 --- /dev/null +++ b/lib/puppet/type/quantum_router.rb @@ -0,0 +1,91 @@ +Puppet::Type.newtype(:quantum_router) do + + ensurable + + newparam(:name, :namevar => true) do + desc 'Symbolic name for the router' + newvalues(/.*/) + end + + newproperty(:id) do + desc 'The unique id of the router' + validate do |v| + raise(Puppet::Error, 'This is a read only property') + end + end + + newproperty(:admin_state_up) do + desc 'The administrative status of the router' + newvalues(/(t|T)rue/, /(f|F)alse/) + munge do |v| + v.to_s.capitalize + end + end + + newproperty(:external_gateway_info) do + desc <<-EOT + External network that this router connects to for gateway services + (e.g., NAT). + EOT + validate do |v| + raise(Puppet::Error, 'This is a read only property') + end + end + + newproperty(:gateway_network_name) do + desc <<-EOT + The name of the external network that this router connects to + for gateway services (e.g. NAT). + EOT + end + + newproperty(:gateway_network_id) do + desc <<-EOT + The uuid of the external network that this router connects to + for gateway services (e.g. NAT). + EOT + validate do |v| + raise(Puppet::Error, 'This is a read only property') + end + end + + newproperty(:status) do + desc 'Whether the router is currently operational or not.' + validate do |v| + raise(Puppet::Error, 'This is a read only property') + end + end + + newparam(:tenant_name) do + desc 'The name of the tenant which will own the router.' + end + + newproperty(:tenant_id) do + desc 'A uuid identifying the tenant which will own the router.' + end + + autorequire(:service) do + ['quantum-server'] + end + + autorequire(:keystone_tenant) do + [self[:tenant_name]] if self[:tenant_name] + end + + autorequire(:quantum_network) do + [self[:gateway_network_name]] if self[:gateway_network_name] + end + + validate do + if self[:ensure] != :present + return + end + if self[:tenant_id] && self[:tenant_name] + raise(Puppet::Error, <<-EOT +Please provide a value for only one of tenant_name and tenant_id. +EOT + ) + end + end + +end diff --git a/spec/unit/provider/quantum_router/quantum_spec.rb b/spec/unit/provider/quantum_router/quantum_spec.rb new file mode 100644 index 000000000..0971ed713 --- /dev/null +++ b/spec/unit/provider/quantum_router/quantum_spec.rb @@ -0,0 +1,53 @@ +require 'puppet' +require 'spec_helper' +require 'puppet/provider/quantum_router/quantum' + +provider_class = Puppet::Type.type(:quantum_router).provider(:quantum) + +describe provider_class do + + let :router_name do + 'router1' + end + + let :router_attrs do + { + :name => router_name, + :ensure => 'present', + :admin_state_up => 'True', + :tenant_id => '', + } + end + + describe 'when updating a router' do + let :resource do + Puppet::Type::Quantum_router.new(router_attrs) + end + + let :provider do + provider_class.new(resource) + end + + it 'should call router-update to change admin_state_up' do + provider.expects(:auth_quantum).with('router-update', + '--admin-state-up=False', + router_name) + provider.admin_state_up=('False') + end + + it 'should call router-gateway-clear for an empty network name' do + provider.expects(:auth_quantum).with('router-gateway-clear', + router_name) + provider.gateway_network_name=('') + end + + it 'should call router-gateway-set to configure an external network' do + provider.expects(:auth_quantum).with('router-gateway-set', + router_name, + 'net1') + provider.gateway_network_name=('net1') + end + + end + +end