Puppet providers added

Change-Id: I67ad59731df43423dd210150c2b68dfec9cff8b4
This commit is contained in:
Nikita Koshikov 2015-04-10 14:10:06 +03:00
parent 5360254e4b
commit ea6188c9c0
33 changed files with 2013 additions and 0 deletions

View File

@ -0,0 +1,22 @@
Puppet::Type.type(:heat_api_cfn_config).provide(
:ini_setting,
:parent => Puppet::Type.type(:ini_setting).provider(:ruby)
) do
def section
resource[:name].split('/', 2).first
end
def setting
resource[:name].split('/', 2).last
end
def separator
'='
end
def file_path
'/etc/heat/heat-api-cfn.conf'
end
end

View File

@ -0,0 +1,23 @@
Puppet::Type.type(:heat_api_cfn_paste_ini).provide(
:ini_setting,
:parent => Puppet::Type.type(:ini_setting).provider(:ruby)
) do
def section
resource[:name].split('/', 2).first
end
def setting
resource[:name].split('/', 2).last
end
def separator
'='
end
def file_path
'/etc/heat/heat-api-cfn-paste.ini'
end
end

View File

@ -0,0 +1,22 @@
Puppet::Type.type(:heat_api_cloudwatch_config).provide(
:ini_setting,
:parent => Puppet::Type.type(:ini_setting).provider(:ruby)
) do
def section
resource[:name].split('/', 2).first
end
def setting
resource[:name].split('/', 2).last
end
def separator
'='
end
def file_path
'/etc/heat/heat-api-cloudwatch.conf'
end
end

View File

@ -0,0 +1,23 @@
Puppet::Type.type(:heat_api_cloudwatch_paste_ini).provide(
:ini_setting,
:parent => Puppet::Type.type(:ini_setting).provider(:ruby)
) do
def section
resource[:name].split('/', 2).first
end
def setting
resource[:name].split('/', 2).last
end
def separator
'='
end
def file_path
'/etc/heat/heat-api-cloudwatch-paste.ini'
end
end

View File

@ -0,0 +1,23 @@
Puppet::Type.type(:heat_api_config).provide(
:ini_setting,
:parent => Puppet::Type.type(:ini_setting).provider(:ruby)
) do
def section
resource[:name].split('/', 2).first
end
def setting
resource[:name].split('/', 2).last
end
def separator
'='
end
def file_path
'/etc/heat/heat-api.conf'
end
end

View File

@ -0,0 +1,23 @@
Puppet::Type.type(:heat_api_paste_ini).provide(
:ini_setting,
:parent => Puppet::Type.type(:ini_setting).provider(:ruby)
) do
def section
resource[:name].split('/', 2).first
end
def setting
resource[:name].split('/', 2).last
end
def separator
'='
end
def file_path
'/etc/heat/heat-api-paste.ini'
end
end

View File

@ -0,0 +1,22 @@
Puppet::Type.type(:heat_config).provide(
:ini_setting,
:parent => Puppet::Type.type(:ini_setting).provider(:ruby)
) do
def section
resource[:name].split('/', 2).first
end
def setting
resource[:name].split('/', 2).last
end
def separator
'='
end
def file_path
'/etc/heat/heat.conf'
end
end

View File

@ -0,0 +1,22 @@
Puppet::Type.type(:heat_engine_config).provide(
:ini_setting,
:parent => Puppet::Type.type(:ini_setting).provider(:ruby)
) do
def section
resource[:name].split('/', 2).first
end
def setting
resource[:name].split('/', 2).last
end
def separator
'='
end
def file_path
'/etc/heat/heat-engine.conf'
end
end

View File

@ -0,0 +1,19 @@
Puppet::Type.newtype(:heat_api_cfn_config) do
ensurable
newparam(:name, :namevar => true) do
desc 'Section/setting name to manage from heat_api_cfn.conf'
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
end
end

View File

@ -0,0 +1,19 @@
Puppet::Type.newtype(:heat_api_cfn_paste_ini) do
ensurable
newparam(:name, :namevar => true) do
desc 'Section/setting name to manage from heat-api.conf'
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
end
end

View File

@ -0,0 +1,19 @@
Puppet::Type.newtype(:heat_api_cloudwatch_config) do
ensurable
newparam(:name, :namevar => true) do
desc 'Section/setting name to manage from heat-api-cloudwatch.conf'
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
end
end

View File

@ -0,0 +1,19 @@
Puppet::Type.newtype(:heat_api_cloudwatch_paste_ini) do
ensurable
newparam(:name, :namevar => true) do
desc 'Section/setting name to manage from heat-api.conf'
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
end
end

View File

@ -0,0 +1,19 @@
Puppet::Type.newtype(:heat_api_config) do
ensurable
newparam(:name, :namevar => true) do
desc 'Section/setting name to manage from heat-api.conf'
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
end
end

View File

@ -0,0 +1,19 @@
Puppet::Type.newtype(:heat_api_paste_ini) do
ensurable
newparam(:name, :namevar => true) do
desc 'Section/setting name to manage from heat-api.conf'
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
end
end

View File

@ -0,0 +1,19 @@
Puppet::Type.newtype(:heat_config) do
ensurable
newparam(:name, :namevar => true) do
desc 'Section/setting name to manage from heat.conf'
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
end
end

View File

@ -0,0 +1,20 @@
Puppet::Type.newtype(:heat_engine_config) do
ensurable
newparam(:name, :namevar => true) do
desc 'Section/setting name to manage from heat-engine.conf'
newvalues(/\S+\/\S+/)
end
newproperty(:value) do
desc 'The value of the setting to be defined.'
munge do |value|
puts value
value = value.to_s.strip
value.capitalize! if value =~ /^(true|false)$/i
value
end
end
end

View File

@ -0,0 +1,272 @@
require 'csv'
require 'puppet/util/inifile'
class Puppet::Provider::Neutron < Puppet::Provider
#NOTE(xenolog): self.prefetch was removed with comment:
# FIXME:(xarses) needs to be abstraced from subresources and re-written here
def self.conf_filename
'/etc/neutron/neutron.conf'
end
def self.withenv(hash, &block)
saved = ENV.to_hash
hash.each do |name, val|
ENV[name.to_s] = val
end
yield
ensure
ENV.clear
saved.each do |name, val|
ENV[name] = val
end
end
def self.neutron_credentials
@neutron_credentials ||= get_neutron_credentials
end
def self.get_neutron_credentials
auth_keys = ['auth_host', 'auth_port', 'auth_protocol',
'admin_tenant_name', 'admin_user', 'admin_password']
conf = neutron_conf
if conf and conf['keystone_authtoken'] and
auth_keys.all?{|k| !conf['keystone_authtoken'][k].nil?}
creds = Hash[ auth_keys.map \
{ |k| [k, conf['keystone_authtoken'][k].strip] } ]
if conf['DEFAULT'] and !conf['DEFAULT']['nova_region_name'].nil?
creds['nova_region_name'] = conf['DEFAULT']['nova_region_name']
end
return creds
else
raise(Puppet::Error, "File: #{conf_filename} does not contain all \
required sections. Neutron types will not work if neutron is not \
correctly configured.")
end
end
def neutron_credentials
self.class.neutron_credentials
end
def self.auth_endpoint
@auth_endpoint ||= get_auth_endpoint
end
def self.get_auth_endpoint
q = neutron_credentials
"#{q['auth_protocol']}://#{q['auth_host']}:#{q['auth_port']}/v2.0/"
end
def self.neutron_conf
return @neutron_conf if @neutron_conf
@neutron_conf = Puppet::Util::IniConfig::File.new
@neutron_conf.read(conf_filename)
@neutron_conf
end
def self.auth_neutron(*args)
q = neutron_credentials
authenv = {
:OS_AUTH_URL => self.auth_endpoint,
:OS_USERNAME => q['admin_user'],
:OS_TENANT_NAME => q['admin_tenant_name'],
:OS_PASSWORD => q['admin_password']
}
if q.key?('nova_region_name')
authenv[:OS_REGION_NAME] = q['nova_region_name']
end
# NOTE(bogdando) contribute change to upstream #1384097:
# enhanced message checks within a given time frame
rv = nil
timeout = 120
end_time = Time.now.to_i + timeout
loop do
begin
withenv authenv do
rv = neutron(args)
end
break
rescue Puppet::ExecutionFailure => e
if ! e.message =~ /(\(HTTP\s+400\))|
(400-\{\'message\'\:\s+\'\'\})|
(\[Errno 111\]\s+Connection\s+refused)|
(503\s+Service\s+Unavailable)|
(504\s+Gateway\s+Time-out)|
(\:\s+Maximum\s+attempts\s+reached)|
(Unauthorized\:\s+bad\s+credentials)|
(Max\s+retries\s+exceeded)/
raise(e)
end
current_time = Time.now.to_i
if current_time > end_time
break
else
wait = end_time - current_time
Puppet::debug("Non-fatal error: \"#{e.message}\"")
notice("Neutron API not avalaible. Wait up to #{wait} sec.")
end
sleep(2)
# Note(xarses): Don't remove, we know that there is one of the
# Recoverable erros above, So we will retry a few more times
end
end
return rv
end
def auth_neutron(*args)
self.class.auth_neutron(args)
end
def self.reset
@neutron_conf = nil
@neutron_credentials = nil
end
def self.list_neutron_resources(type)
ids = []
list = auth_neutron("#{type}-list", '--format=csv',
'--column=id', '--quote=none')
# NOTE(bogdando) contribute change to upstream #1384101:
# raise Puppet exception, if resources list is empty
if list.nil?
raise(Puppet::ExecutionFailure, "Can't prefetch #{type}-list Neutron or Keystone API is not avalaible.")
end
(list.split("\n")[1..-1] || []).compact.collect do |line|
ids << line.strip
end
return ids
end
def self.get_neutron_resource_attrs(type, id)
attrs = {}
net = auth_neutron("#{type}-show", '--format=shell', id)
# NOTE(bogdando) contribute change to upstream #1384101:
# raise Puppet exception, if list of resources' attributes is empty
if net.nil?
raise(Puppet::ExecutionFailure, "Can't prefetch #{type}-show Neutron or Keystone API is not avalaible.")
end
last_key = nil
(net.split("\n") || []).compact.collect do |line|
if line.include? '='
k, v = line.split('=', 2)
attrs[k] = v.gsub(/\A"|"\Z/, '')
last_key = k
else
# Handle the case of a list of values
v = line.gsub(/\A"|"\Z/, '')
attrs[last_key] = [attrs[last_key], v].flatten
end
end
return attrs
end
def self.list_router_ports(router_name_or_id)
results = []
cmd_output = auth_neutron("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
def self.auth_keystone(*args)
q = neutron_credentials
authenv = {
:OS_AUTH_URL => self.auth_endpoint,
:OS_USERNAME => q['admin_user'],
:OS_TENANT_NAME => q['admin_tenant_name'],
:OS_PASSWORD => q['admin_password']
}
if q.key?('nova_region_name')
authenv[:OS_REGION_NAME] = q['nova_region_name']
end
rv = nil
timeout = 120
end_time = Time.now.to_i + timeout
loop do
begin
withenv authenv do
rv = keystone(args)
end
break
rescue Puppet::ExecutionFailure => e
if ! e.message =~ /(\(HTTP\s+400\))|
(400-\{\'message\'\:\s+\'\'\})|
(\[Errno 111\]\s+Connection\s+refused)|
(503\s+Service\s+Unavailable)|
(504\s+Gateway\s+Time-out)|
(\:\s+Maximum\s+attempts\s+reached)|
(Unauthorized\:\s+bad\s+credentials)|
(Max\s+retries\s+exceeded)/
raise(e)
end
current_time = Time.now.to_i
if current_time > end_time
#raise(e)
break
else
wait = end_time - current_time
Puppet::debug("Non-fatal error: \"#{e.message}\"")
notice("Keystone API not avalaible. Wait up to #{wait} sec.")
end
sleep(2)
# Note(xarses): Don't remove, we know that there is one of the
# Recoverable erros above, So we will retry a few more times
end
end
return rv
end
def auth_keystone(*args)
self.class.auth_neutron(args)
end
def self.get_tenant_id(catalog, name)
rv = nil
auth_keystone('tenant-list').each do |line|
fields=line.split(/\s*\|\s*/)
if fields[1] and fields[1].size == 32
if fields[2] == name
rv = fields[1]
break
end
end
end
if rv.nil?
fail("Unable to get tenant-ID for tenant '#{name}'")
end
return rv
end
def self.parse_creation_output(data)
hash = {}
data.split("\n").compact.each do |line|
if line.include? '='
hash[line.split('=').first] = line.split('=', 2)[1].gsub(/\A"|"\Z/, '')
end
end
hash
end
end

View File

@ -0,0 +1,22 @@
Puppet::Type.type(:neutron_config).provide(
:ini_setting,
:parent => Puppet::Type.type(:ini_setting).provider(:ruby)
) do
def section
resource[:name].split('/', 2).first
end
def setting
resource[:name].split('/', 2).last
end
def separator
'='
end
def file_path
'/etc/neutron/neutron.conf'
end
end

View File

@ -0,0 +1,140 @@
require File.join(File.dirname(__FILE__), '..','..','..',
'puppet/provider/neutron')
Puppet::Type.type(:neutron_network).provide(
:neutron,
:parent => Puppet::Provider::Neutron
) do
desc <<-EOT
Neutron provider to manage neutron_network type.
Assumes that the neutron service is configured on the same host.
EOT
commands :neutron => 'neutron'
commands :keystone => 'keystone'
mk_resource_methods
def self.neutron_type
'net'
end
def self.instances
list_neutron_resources(neutron_type).collect do |id|
attrs = get_neutron_resource_attrs(neutron_type, id)
new(
:ensure => :present,
:name => attrs['name'],
:id => attrs['id'],
:admin_state_up => attrs['admin_state_up'],
:provider_network_type => attrs['provider:network_type'],
:provider_physical_network => attrs['provider:physical_network'],
:provider_segmentation_id => attrs['provider:segmentation_id'],
:router_external => attrs['router:external'],
:shared => attrs['shared'],
:tenant_id => attrs['tenant_id']
)
end
end
def self.prefetch(resources)
networks = instances
resources.keys.each do |name|
if provider = networks.find{ |net| net.name == name }
resources[name].provider = provider
end
end
end
def exists?
@property_hash[:ensure] == :present
end
def create
network_opts = Array.new
if @resource[:shared] =~ /true/i
network_opts << '--shared'
end
if @resource[:tenant_name]
tenant_id = self.class.get_tenant_id(model.catalog,
@resource[:tenant_name])
notice("***N*** neutron_network::create *** tenant_id='#{tenant_id.inspect}'")
network_opts << "--tenant_id=#{tenant_id}"
elsif @resource[:tenant_id]
network_opts << "--tenant_id=#{@resource[:tenant_id]}"
end
if @resource[:provider_network_type]
network_opts << \
"--provider:network_type=#{@resource[:provider_network_type]}"
end
if @resource[:provider_physical_network]
network_opts << \
"--provider:physical_network=#{@resource[:provider_physical_network]}"
end
if @resource[:provider_segmentation_id]
network_opts << \
"--provider:segmentation_id=#{@resource[:provider_segmentation_id]}"
end
if @resource[:router_external]
network_opts << "--router:external=#{@resource[:router_external]}"
end
results = auth_neutron('net-create', '--format=shell',
network_opts, resource[:name])
if results =~ /Created a new network:/
attrs = self.class.parse_creation_output(results)
@property_hash = {
:ensure => :present,
:name => resource[:name],
:id => attrs['id'],
:admin_state_up => attrs['admin_state_up'],
:provider_network_type => attrs['provider:network_type'],
:provider_physical_network => attrs['provider:physical_network'],
:provider_segmentation_id => attrs['provider:segmentation_id'],
:router_external => attrs['router:external'],
:shared => attrs['shared'],
:tenant_id => attrs['tenant_id'],
}
else
fail("did not get expected message on network creation, got #{results}")
end
end
def destroy
auth_neutron('net-delete', name)
@property_hash[:ensure] = :absent
end
def admin_state_up=(value)
auth_neutron('net-update', "--admin_state_up=#{value}", name)
end
def shared=(value)
auth_neutron('net-update', "--shared=#{value}", name)
end
def router_external=(value)
auth_neutron('net-update', "--router:external=#{value}", name)
end
[
:provider_network_type,
:provider_physical_network,
:provider_segmentation_id,
:tenant_id,
].each do |attr|
define_method(attr.to_s + "=") do |value|
fail("Property #{attr.to_s} does not support being updated")
end
end
end

View File

@ -0,0 +1,22 @@
Puppet::Type.type(:neutron_plugin_ml2).provide(
:ini_setting,
:parent => Puppet::Type.type(:ini_setting).provider(:ruby)
) do
def section
resource[:name].split('/', 2).first
end
def setting
resource[:name].split('/', 2).last
end
def separator
'='
end
def file_path
'/etc/neutron/plugins/ml2/ml2_conf.ini'
end
end

View File

@ -0,0 +1,22 @@
Puppet::Type.type(:neutron_plugin_ml2_cisco).provide(
:ini_setting,
:parent => Puppet::Type.type(:ini_setting).provider(:ruby)
) do
def section
resource[:name].split('/', 2).first
end
def setting
resource[:name].split('/', 2).last
end
def separator
'='
end
def file_path
'/etc/neutron/plugins/ml2/ml2_conf_cisco.ini'
end
end

View File

@ -0,0 +1,192 @@
require File.join(File.dirname(__FILE__), "..","..","..",
"puppet/provider/neutron")
Puppet::Type.type(:neutron_port).provide(
:neutron,
:parent => Puppet::Provider::Neutron
) do
desc <<-EOT
Neutron provider to manage neutron_port type.
Assumes that the neutron service is configured on the same host.
EOT
#TODO No security group support
commands :neutron => "neutron"
commands :keystone => 'keystone'
mk_resource_methods
def self.instances
list_neutron_resources("port").collect do |id|
attrs = get_neutron_resource_attrs("port", id)
attrs["name"] = attrs["id"] if attrs["name"].empty?
new(
:ensure => :present,
:name => attrs["name"],
:id => attrs["id"],
:status => attrs["status"],
:tenant_id => attrs["tenant_id"],
:network_id => attrs["network_id"],
:admin_state_up => attrs["admin_state_up"],
:network_name => get_network_name(attrs["network_id"]),
:subnet_name => get_subnet_name(parse_subnet_id(attrs["fixed_ips"])),
:subnet_id => parse_subnet_id(attrs["fixed_ips"]),
:ip_address => parse_ip_address(attrs["fixed_ips"])
)
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[:ip_address]
# The spec says that multiple ip addresses may be specified, but this
# doesn't seem to work yet.
opts << "--fixed-ip"
opts << @resource[:ip_address].map{|ip|"ip_address=#{ip}"}.join(',')
end
if @resource[:subnet_name]
# The spec says that multiple subnets may be specified, but this doesn't
# seem to work yet.
opts << "--fixed-ip"
opts << @resource[:subnet_name].map{|s|"subnet_id=#{s}"}.join(',')
end
if @resource[:tenant_name]
tenant_id = self.class.get_tenant_id(
model.catalog,
@resource[:tenant_name]
)
opts << "--tenant_id=#{tenant_id}"
elsif @resource[:tenant_id]
opts << "--tenant_id=#{@resource[:tenant_id]}"
end
results = auth_neutron(
"port-create",
"--format=shell",
"--name=#{resource[:name]}",
opts,
resource[:network_name]
)
if results =~ /Created a new port:/
attrs = self.class.parse_creation_output(results)
@property_hash = {
:ensure => :present,
:name => resource[:name],
:id => attrs["id"],
:status => attrs["status"],
:tenant_id => attrs["tenant_id"],
:network_id => attrs["network_id"],
:admin_state_up => attrs["admin_state_up"],
:network_name => resource[:network_name],
:subnet_name => resource[:subnet_name],
:subnet_id => self.class.parse_subnet_id(attrs["fixed_ips"]),
:ip_address => self.class.parse_ip_address(attrs["fixed_ips"])
}
else
fail("did not get expected message on port creation, got #{results}")
end
end
def destroy
auth_neutron("port-delete", name)
@property_hash[:ensure] = :absent
end
def admin_state_up=(value)
auth_neutron("port-update", "--admin-state-up=#{value}", name)
end
private
def self.get_network_name(network_id_)
if network_id_
network_instances = Puppet::Type.type("neutron_network").instances
network_name = network_instances.find do |instance|
instance.provider.id == network_id_
end.provider.name
end
network_name
end
def get_network_name(network_id_)
@get_network_name ||= self.class.get_network_name(network_id_)
end
def self.get_subnet_name(subnet_id_)
if subnet_id_
subnet_ids = Array(subnet_id_)
subnet_instances = Puppet::Type.type("neutron_subnet").instances
subnet_names = subnet_instances.collect do |instance|
if subnet_ids.include?(instance.provider.id)
instance.provider.name
else
nil
end
end.compact
if subnet_names.length > 1
subnet_names
else
subnet_names.first
end
end
end
def get_subnet_name(subnet_id_)
@subnet_name ||= self.class.subnet_name(subnet_id_)
end
def self.parse_subnet_id(fixed_ips_)
subnet_ids = Array(fixed_ips_).collect do |json|
match_data = /\{"subnet_id": "(.*)", /.match(json)
if match_data
match_data[1]
else
nil
end
end.compact
if subnet_ids.length > 1
subnet_ids
else
subnet_ids.first
end
end
def self.parse_ip_address(fixed_ips_)
ip_addresses = Array(fixed_ips_).collect do |json|
match_data = /"ip_address": "(.*)"\}/.match(json)
if match_data
match_data[1]
else
nil
end
end.compact
if ip_addresses.length > 1
ip_addresses
else
ip_addresses.first
end
end
end

View File

@ -0,0 +1,139 @@
require File.join(File.dirname(__FILE__), '..','..','..',
'puppet/provider/neutron')
Puppet::Type.type(:neutron_router).provide(
:neutron,
:parent => Puppet::Provider::Neutron
) do
desc <<-EOT
Neutron provider to manage neutron_router type.
Assumes that the neutron service is configured on the same host.
EOT
commands :neutron => 'neutron'
commands :keystone => 'keystone'
mk_resource_methods
def self.instances
list_neutron_resources('router').collect do |id|
attrs = get_neutron_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]
tenant_id = self.class.get_tenant_id(model.catalog,
@resource[:tenant_name])
opts << "--tenant_id=#{tenant_id}"
elsif @resource[:tenant_id]
opts << "--tenant_id=#{@resource[:tenant_id]}"
end
results = auth_neutron("router-create", '--format=shell',
opts, resource[:name])
if results =~ /Created a new router:/
attrs = self.class.parse_creation_output(results)
@property_hash = {
:ensure => :present,
:name => resource[: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'],
}
if @resource[:gateway_network_name]
results = auth_neutron('router-gateway-set',
@resource[:name],
@resource[:gateway_network_name])
if results =~ /Set gateway for router/
attrs = self.class.get_neutron_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 destroy
auth_neutron('router-delete', name)
@property_hash[:ensure] = :absent
end
def gateway_network_name
if @gateway_network_name == nil and gateway_network_id
Puppet::Type.type('neutron_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_neutron('router-gateway-clear', name)
else
auth_neutron('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_neutron('router-update', "--admin-state-up=#{value}", name)
end
end

View File

@ -0,0 +1,93 @@
require File.join(File.dirname(__FILE__), '..','..','..',
'puppet/provider/neutron')
Puppet::Type.type(:neutron_router_interface).provide(
:neutron,
:parent => Puppet::Provider::Neutron
) do
desc <<-EOT
Neutron provider to manage neutron_router_interface type.
Assumes that the neutron 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 :neutron => 'neutron'
mk_resource_methods
def self.instances
subnet_name_hash = {}
Puppet::Type.type('neutron_subnet').instances.each do |instance|
subnet_name_hash[instance.provider.id] = instance.provider.name
end
instances_ = []
Puppet::Type.type('neutron_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'],
:port => port_hash['name']
)
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
router,subnet = resource[:name].split(':', 2)
port = resource[:port]
args = ["router-interface-add", "--format=shell", router]
if port
args << "port=#{port}"
else
args << "subnet=#{subnet}"
end
results = auth_neutron(args)
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 destroy
auth_neutron('router-interface-delete', router_name, subnet_name)
@property_hash[:ensure] = :absent
end
end

View File

@ -0,0 +1,220 @@
require File.join(File.dirname(__FILE__), '..','..','..',
'puppet/provider/neutron')
Puppet::Type.type(:neutron_subnet).provide(
:neutron,
:parent => Puppet::Provider::Neutron
) do
desc <<-EOT
Neutron provider to manage neutron_subnet type.
Assumes that the neutron service is configured on the same host.
EOT
commands :neutron => 'neutron'
commands :keystone => 'keystone'
mk_resource_methods
def self.neutron_type
'subnet'
end
def self.instances
list_neutron_resources(neutron_type).collect do |id|
attrs = get_neutron_resource_attrs(neutron_type, id)
new(
:ensure => :present,
:name => attrs['name'],
:id => attrs['id'],
:cidr => attrs['cidr'],
:ip_version => attrs['ip_version'],
:gateway_ip => parse_gateway_ip(attrs['gateway_ip']),
:allocation_pools => parse_allocation_pool(attrs['allocation_pools']),
:host_routes => parse_host_routes(attrs['host_routes']),
:dns_nameservers => parse_dns_nameservers(attrs['dns_nameservers']),
:enable_dhcp => attrs['enable_dhcp'],
:network_id => attrs['network_id'],
:tenant_id => attrs['tenant_id']
)
end
end
def self.prefetch(resources)
subnets = instances
resources.keys.each do |name|
if provider = subnets.find{ |subnet| subnet.name == name }
resources[name].provider = provider
end
end
end
def self.parse_gateway_ip(value)
return '' if value.nil?
return value
end
def self.parse_allocation_pool(values)
allocation_pools = []
return [] if values.empty?
for value in Array(values)
matchdata = /\{\s*"start"\s*:\s*"(.*)"\s*,\s*"end"\s*:\s*"(.*)"\s*\}/.match(value.gsub(/\\"/,'"'))
start_ip = matchdata[1]
end_ip = matchdata[2]
allocation_pools << "start=#{start_ip},end=#{end_ip}"
end
return allocation_pools
end
def self.parse_host_routes(values)
host_routes = []
return [] if values.empty?
for value in Array(values)
matchdata = /\{\s*"destination"\s*:\s*"(.*)"\s*,\s*"nexthop"\s*:\s*"(.*)"\s*\}/.match(value)
destination = matchdata[1]
nexthop = matchdata[2]
host_routes << "destination=#{destination},nexthop=#{nexthop}"
end
return host_routes
end
def self.parse_dns_nameservers(values)
# just enforce that this is actually an array
return Array(values)
end
def exists?
@property_hash[:ensure] == :present
end
def create
opts = ["--name=#{@resource[:name]}"]
if @resource[:ip_version]
opts << "--ip-version=#{@resource[:ip_version]}"
end
if @resource[:gateway_ip]
if @resource[:gateway_ip] == ''
opts << '--no-gateway'
else
opts << "--gateway-ip=#{@resource[:gateway_ip]}"
end
end
if @resource[:enable_dhcp] == 'False'
opts << "--disable-dhcp"
else
opts << "--enable-dhcp"
end
if @resource[:allocation_pools]
Array(@resource[:allocation_pools]).each do |allocation_pool|
opts << "--allocation-pool=#{allocation_pool}"
end
end
if @resource[:dns_nameservers]
Array(@resource[:dns_nameservers]).each do |nameserver|
opts << "--dns-nameserver=#{nameserver}"
end
end
if @resource[:host_routes]
Array(@resource[:host_routes]).each do |host_route|
opts << "--host-route=#{host_route}"
end
end
if @resource[:tenant_name]
tenant_id = self.class.get_tenant_id(model.catalog,
@resource[:tenant_name])
opts << "--tenant_id=#{tenant_id}"
elsif @resource[:tenant_id]
opts << "--tenant_id=#{@resource[:tenant_id]}"
end
if @resource[:network_name]
opts << resource[:network_name]
elsif @resource[:network_id]
opts << resource[:network_id]
end
results = auth_neutron('subnet-create', '--format=shell',
opts, resource[:cidr])
if results =~ /Created a new subnet:/
attrs = self.class.parse_creation_output(results)
@property_hash = {
:ensure => :present,
:name => resource[:name],
:id => attrs['id'],
:cidr => attrs['cidr'],
:ip_version => attrs['ip_version'],
:gateway_ip => self.class.parse_gateway_ip(attrs['gateway_ip']),
:allocation_pools => self.class.parse_allocation_pool(attrs['allocation_pools']),
:host_routes => self.class.parse_host_routes(attrs['host_routes']),
:dns_nameservers => self.class.parse_dns_nameservers(attrs['dns_nameservers']),
:enable_dhcp => attrs['enable_dhcp'],
:network_id => attrs['network_id'],
:tenant_id => attrs['tenant_id'],
}
else
fail("did not get expected message on subnet creation, got #{results}")
end
end
def destroy
auth_neutron('subnet-delete', name)
@property_hash[:ensure] = :absent
end
def gateway_ip=(value)
if value == ''
auth_neutron('subnet-update', '--no-gateway', name)
else
auth_neutron('subnet-update', "--gateway-ip=#{value}", name)
end
end
def enable_dhcp=(value)
if value == 'False'
auth_neutron('subnet-update', "--disable-dhcp", name)
else
auth_neutron('subnet-update', "--enable-dhcp", name)
end
end
def dns_nameservers=(values)
unless values.empty?
opts = ["#{name}", "--dns-nameservers", "list=true"]
for value in values
opts << value
end
auth_neutron('subnet-update', opts)
end
end
def host_routes=(values)
unless values.empty?
opts = ["#{name}", "--host-routes", "type=dict", "list=true"]
for value in values
opts << value
end
auth_neutron('subnet-update', opts)
end
end
[
:cidr,
:ip_version,
:network_id,
:allocation_pools,
:tenant_id,
].each do |attr|
define_method(attr.to_s + "=") do |value|
fail("Property #{attr.to_s} does not support being updated")
end
end
end

View File

@ -0,0 +1,47 @@
Puppet::Type.newtype(:neutron_config) do
ensurable
newparam(:name, :namevar => true) do
desc 'Section/setting name to manage from neutron.conf'
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
def create
provider.create
end
end

View File

@ -0,0 +1,90 @@
Puppet::Type.newtype(:neutron_network) do
ensurable
newparam(:name, :namevar => true) do
desc 'Symbolic name for the network'
newvalues(/.*/)
end
newproperty(:id) do
desc 'The unique id of the network'
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 network'
newvalues(/(t|T)rue/, /(f|F)alse/)
munge do |v|
v.to_s.capitalize
end
end
newproperty(:shared) do
desc 'Whether this network should be shared across all tenants or not'
newvalues(/(t|T)rue/, /(f|F)alse/)
munge do |v|
v.to_s.capitalize
end
end
newparam(:tenant_name) do
desc 'The name of the tenant which will own the network.'
end
newproperty(:tenant_id) do
desc 'A uuid identifying the tenant which will own the network.'
end
newproperty(:provider_network_type) do
desc 'The physical mechanism by which the virtual network is realized.'
newvalues(:flat, :vlan, :local, :gre)
end
newproperty(:provider_physical_network) do
desc <<-EOT
The name of the physical network over which the virtual network
is realized for flat and VLAN networks.
EOT
newvalues(/\S+/)
end
newproperty(:provider_segmentation_id) do
desc 'Identifies an isolated segment on the physical network.'
munge do |v|
Integer(v)
end
end
newproperty(:router_external) do
desc 'Whether this router will route traffic to an external network'
newvalues(/(t|T)rue/, /(f|F)alse/)
munge do |v|
v.to_s.capitalize
end
end
# Require the neutron-server service to be running
autorequire(:service) do
['neutron-server']
end
autorequire(:keystone_tenant) do
[self[:tenant_name]] if self[:tenant_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

View File

@ -0,0 +1,20 @@
Puppet::Type.newtype(:neutron_plugin_ml2) do
ensurable
newparam(:name, :namevar => true) do
desc 'Section/setting name to manage from ml2_conf.ini'
newvalues(/\S+\/\S+/)
end
autorequire(:package) do ['neutron'] 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
end
end

View File

@ -0,0 +1,47 @@
Puppet::Type.newtype(:neutron_plugin_ml2_cisco) do
ensurable
newparam(:name, :namevar => true) do
desc 'Section/setting name to manage from cisco_plugins.ini'
newvalues(/\S+\/\S+/)
end
autorequire(:file) do
['/etc/neutron/plugins/ml2/ml2_conf_cisco.ini']
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
end

View File

@ -0,0 +1,98 @@
Puppet::Type.newtype(:neutron_port) do
desc <<-EOT
This is currently used to model the creation of neutron ports.
Ports are used when associating a network and a router interface.
EOT
ensurable
newparam(:name, :namevar => true) do
desc 'Symbolic name for the port'
newvalues(/.*/)
end
newproperty(:id) do
desc 'The unique id of the port'
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(:network_name) do
desc <<-EOT
The name of the network that this port is assigned to on creation.
EOT
end
newproperty(:network_id) do
desc <<-EOT
The uuid of the network that this port is assigned to on creation.
EOT
validate do |v|
raise(Puppet::Error, 'This is a read only property')
end
end
newproperty(:subnet_name) do
desc 'A subnet to which the port is assigned on creation.'
end
newproperty(:subnet_id) do
desc <<-EOT
The uuid of the subnet on which this ports ip exists.
EOT
validate do |v|
raise(Puppet::Error, 'This is a read only property')
end
end
newproperty(:ip_address) do
desc 'A static ip address given to the port on creation.'
end
newproperty(:status) do
desc 'Whether the port 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 port.'
end
newproperty(:tenant_id) do
desc 'A uuid identifying the tenant which will own the port.'
end
autorequire(:service) do
['neutron-server']
end
autorequire(:keystone_tenant) do
[self[:tenant_name]] if self[:tenant_name]
end
autorequire(:neutron_network) do
[self[:name]]
end
validate do
if self[:tenant_id] && self[:tenant_name]
raise(Puppet::Error, 'Please provide a value for only one of tenant_name and tenant_id.')
end
if self[:ip_address] && self[:subnet_name]
raise(Puppet::Error, 'Please provide a value for only one of ip_address and subnet_name.')
end
end
end

View File

@ -0,0 +1,91 @@
Puppet::Type.newtype(:neutron_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
['neutron-server']
end
autorequire(:keystone_tenant) do
[self[:tenant_name]] if self[:tenant_name]
end
autorequire(:neutron_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

View File

@ -0,0 +1,51 @@
Puppet::Type.newtype(:neutron_router_interface) do
desc <<-EOT
This is currently used to model the creation of
neutron 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
newproperty(:port) do
desc 'An existing neutron port to which a rounter interface should be assigned'
end
autorequire(:service) do
['neutron-server']
end
autorequire(:neutron_router) do
self[:name].split(':', 2).first
end
autorequire(:neutron_subnet) do
self[:name].split(':', 2).last
end
end

View File

@ -0,0 +1,114 @@
Puppet::Type.newtype(:neutron_subnet) do
ensurable
newparam(:name, :namevar => true) do
desc 'Symbolic name for the subnet'
newvalues(/.*/)
end
newproperty(:id) do
desc 'The unique id of the subnet'
validate do |v|
raise(Puppet::Error, 'This is a read only property')
end
end
newproperty(:cidr) do
desc 'CIDR representing IP range for this subnet, based on IP version'
end
newproperty(:ip_version) do
desc 'The IP version of the CIDR'
newvalues('4', '6')
end
newproperty(:allocation_pools, :array_matching => :all) do
desc <<-EOT
Array of Sub-ranges of cidr available for dynamic allocation to ports.
Syntax:["start=IPADDR,end=IPADDR", ...]
EOT
end
newproperty(:gateway_ip) do
desc <<-EOT
The default gateway provided by DHCP to devices in this subnet. If set to
'' then no gateway IP address will be provided via DHCP.
EOT
end
newproperty(:enable_dhcp) do
desc 'Whether DHCP is enabled for this subnet or not.'
newvalues(/(t|T)rue/, /(f|F)alse/)
munge do |v|
v.to_s.capitalize
end
end
newproperty(:host_routes, :array_matching => :all) do
desc <<-EOT
Array of routes that should be used by devices with IPs from this subnet
(not including local subnet route).
Syntax:["destination=CIDR,nexhop=IP_ADDR", ...]
EOT
end
newproperty(:dns_nameservers, :array_matching => :all) do
desc <<-EOT
'Array of DNS name servers used by hosts in this subnet.'
EOT
end
newproperty(:network_id) do
desc 'A uuid identifying the network this subnet is associated with.'
end
newparam(:network_name) do
desc 'The name of the network this subnet is associated with.'
end
newparam(:tenant_name) do
desc 'The name of the tenant which will own the subnet.'
end
newproperty(:tenant_id) do
desc 'A uuid identifying the tenant which will own the subnet.'
end
autorequire(:service) do
['neutron-server']
end
autorequire(:keystone_tenant) do
[self[:tenant_name]] if self[:tenant_name]
end
autorequire(:neutron_network) do
[self[:network_name]] if self[:network_name]
end
validate do
if self[:ensure] != :present
return
end
if ! self[:cidr]
raise(Puppet::Error, 'Please provide a valid CIDR')
elsif ! (self[:network_id] || self[:network_name])
raise(Puppet::Error, <<-EOT
A value for one of network_name or network_id must be provided.
EOT
)
elsif self[:network_id] && self[:network_name]
raise(Puppet::Error, <<-EOT
Please provide a value for only one of network_name and network_id.
EOT
)
elsif 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