2 require 'puppet/util/inifile'
4 class Puppet::Provider::Neutron < Puppet::Provider
7 '/etc/neutron/neutron.conf'
10 def self.withenv(hash, &block)
12 hash.each do |name, val|
19 saved.each do |name, val|
24 def self.neutron_credentials
25 @neutron_credentials ||= get_neutron_credentials
28 def self.get_neutron_credentials
29 auth_keys = ['admin_tenant_name', 'admin_user', 'admin_password']
30 deprecated_auth_url = ['auth_host', 'auth_port', 'auth_protocol']
32 if conf and conf['keystone_authtoken'] and
33 auth_keys.all?{|k| !conf['keystone_authtoken'][k].nil?} and
34 ( deprecated_auth_url.all?{|k| !conf['keystone_authtoken'][k].nil?} or
35 !conf['keystone_authtoken']['auth_uri'].nil? )
36 creds = Hash[ auth_keys.map \
37 { |k| [k, conf['keystone_authtoken'][k].strip] } ]
38 if !conf['keystone_authtoken']['auth_uri'].nil?
39 creds['auth_uri'] = conf['keystone_authtoken']['auth_uri']
41 q = conf['keystone_authtoken']
42 creds['auth_uri'] = "#{q['auth_protocol']}://#{q['auth_host']}:#{q['auth_port']}/v2.0/"
44 if conf['DEFAULT'] and !conf['DEFAULT']['nova_region_name'].nil?
45 creds['nova_region_name'] = conf['DEFAULT']['nova_region_name']
49 raise(Puppet::Error, "File: #{conf_filename} does not contain all \
50 required sections. Neutron types will not work if neutron is not \
51 correctly configured.")
55 def neutron_credentials
56 self.class.neutron_credentials
59 def self.auth_endpoint
60 @auth_endpoint ||= get_auth_endpoint
63 def self.get_auth_endpoint
64 q = neutron_credentials
66 return "#{q['auth_protocol']}://#{q['auth_host']}:#{q['auth_port']}/v2.0/"
68 return "#{q['auth_uri']}".strip
73 return @neutron_conf if @neutron_conf
74 @neutron_conf = Puppet::Util::IniConfig::File.new
75 @neutron_conf.read(conf_filename)
79 def self.auth_neutron(*args)
80 q = neutron_credentials
82 :OS_AUTH_URL => self.auth_endpoint,
83 :OS_USERNAME => q['admin_user'],
84 :OS_TENANT_NAME => q['admin_tenant_name'],
85 :OS_PASSWORD => q['admin_password']
87 if q.key?('nova_region_name')
88 authenv[:OS_REGION_NAME] = q['nova_region_name']
92 end_time = Time.now.to_i + timeout
99 rescue Puppet::ExecutionFailure => e
100 if ! e.message =~ /(\(HTTP\s+400\))|
101 (400-\{\'message\'\:\s+\'\'\})|
102 (\[Errno 111\]\s+Connection\s+refused)|
103 (503\s+Service\s+Unavailable)|
104 (504\s+Gateway\s+Time-out)|
105 (\:\s+Maximum\s+attempts\s+reached)|
106 (Unauthorized\:\s+bad\s+credentials)|
107 (Max\s+retries\s+exceeded)/
110 current_time = Time.now.to_i
111 if current_time > end_time
114 wait = end_time - current_time
115 Puppet::debug("Non-fatal error: \"#{e.message}\"")
116 notice("Neutron API not avalaible. Wait up to #{wait} sec.")
119 # Note(xarses): Don't remove, we know that there is one of the
120 # Recoverable erros above, So we will retry a few more times
126 def auth_neutron(*args)
127 self.class.auth_neutron(args)
132 @neutron_credentials = nil
135 def self.list_neutron_resources(type)
137 list = auth_neutron("#{type}-list", '--format=csv',
138 '--column=id', '--quote=none')
140 raise(Puppet::ExecutionFailure, "Can't retrieve #{type}-list because Neutron or Keystone API is not avalaible.")
143 (list.split("\n")[1..-1] || []).compact.collect do |line|
149 def self.get_neutron_resource_attrs(type, id)
151 net = auth_neutron("#{type}-show", '--format=shell', id)
153 raise(Puppet::ExecutionFailure, "Can't retrieve #{type}-show because Neutron or Keystone API is not avalaible.")
157 (net.split("\n") || []).compact.collect do |line|
159 k, v = line.split('=', 2)
160 attrs[k] = v.gsub(/\A"|"\Z/, '')
163 # Handle the case of a list of values
164 v = line.gsub(/\A"|"\Z/, '')
165 attrs[last_key] = [attrs[last_key], v].flatten
171 def self.list_router_ports(router_name_or_id)
173 cmd_output = auth_neutron("router-port-list",
181 CSV.parse(cmd_output) do |row|
185 result = Hash[*headers.zip(row).flatten]
186 match_data = /.*"subnet_id": "(.*)", .*/.match(result['fixed_ips'])
188 result['subnet_id'] = match_data[1]
196 def self.get_tenant_id(catalog, name)
197 instance_type = 'keystone_tenant'
198 instance = catalog.resource("#{instance_type.capitalize!}[#{name}]")
200 instance = Puppet::Type.type(instance_type).instances.find do |i|
201 i.provider.name == name
205 return instance.provider.id
207 fail("Unable to find #{instance_type} for name #{name}")
211 def self.parse_creation_output(data)
213 data.split("\n").compact.each do |line|
215 hash[line.split('=').first] = line.split('=', 2)[1].gsub(/\A"|"\Z/, '')