# Since there's only one glance type for now, # this probably could have all gone in the provider file. # But maybe this is good long-term. require 'puppet/util/inifile' class Puppet::Provider::Glance < Puppet::Provider def self.glance_credentials @glance_credentials ||= get_glance_credentials end def self.get_glance_credentials if glance_file and glance_file['keystone_authtoken'] and glance_file['keystone_authtoken']['auth_host'] and glance_file['keystone_authtoken']['auth_port'] and glance_file['keystone_authtoken']['auth_protocol'] and glance_file['keystone_authtoken']['admin_tenant_name'] and glance_file['keystone_authtoken']['admin_user'] and glance_file['keystone_authtoken']['admin_password'] and glance_file['DEFAULT']['os_region_name'] g = {} g['auth_host'] = glance_file['keystone_authtoken']['auth_host'].strip g['auth_port'] = glance_file['keystone_authtoken']['auth_port'].strip g['auth_protocol'] = glance_file['keystone_authtoken']['auth_protocol'].strip g['admin_tenant_name'] = glance_file['keystone_authtoken']['admin_tenant_name'].strip g['admin_user'] = glance_file['keystone_authtoken']['admin_user'].strip g['admin_password'] = glance_file['keystone_authtoken']['admin_password'].strip g['os_region_name'] = glance_file['DEFAULT']['os_region_name'].strip # auth_admin_prefix not required to be set. g['auth_admin_prefix'] = (glance_file['keystone_authtoken']['auth_admin_prefix'] || '').strip return g elsif glance_file and glance_file['keystone_authtoken'] and glance_file['keystone_authtoken']['identity_uri'] and glance_file['keystone_authtoken']['admin_tenant_name'] and glance_file['keystone_authtoken']['admin_user'] and glance_file['keystone_authtoken']['admin_password'] and glance_file['DEFAULT']['os_region_name'] g = {} g['identity_uri'] = glance_file['keystone_authtoken']['identity_uri'].strip g['admin_tenant_name'] = glance_file['keystone_authtoken']['admin_tenant_name'].strip g['admin_user'] = glance_file['keystone_authtoken']['admin_user'].strip g['admin_password'] = glance_file['keystone_authtoken']['admin_password'].strip g['os_region_name'] = glance_file['DEFAULT']['os_region_name'].strip return g else raise(Puppet::Error, 'File: /etc/glance/glance-api.conf does not contain all required sections.') end end def glance_credentials self.class.glance_credentials end def self.auth_endpoint @auth_endpoint ||= get_auth_endpoint end def self.get_auth_endpoint g = glance_credentials if g.key?('identity_uri') "#{g['identity_uri']}/" else "#{g['auth_protocol']}://#{g['auth_host']}:#{g['auth_port']}#{g['auth_admin_prefix']}/v2.0/" end end def self.glance_file return @glance_file if @glance_file @glance_file = Puppet::Util::IniConfig::File.new @glance_file.read('/etc/glance/glance-api.conf') @glance_file end def self.glance_hash @glance_hash ||= build_glance_hash end def self.reset @glance_hash = nil @glance_file = nil @glance_credentials = nil @auth_endpoint = nil end def glance_hash self.class.glance_hash end def self.auth_glance(*args) begin g = glance_credentials remove_warnings(glance('--os-tenant-name', g['admin_tenant_name'], '--os-username', g['admin_user'], '--os-password', g['admin_password'], '--os-region-name', g['os_region_name'], '--os-auth-url', auth_endpoint, args)) rescue Exception => e if (e.message =~ /\[Errno 111\] Connection refused/) or (e.message =~ /\(HTTP 400\)/) or (e.message =~ /HTTP Unable to establish connection/) sleep 10 remove_warnings(glance('--os-tenant-name', g['admin_tenant_name'], '--os-username', g['admin_user'], '--os-password', g['admin_password'], '--os-region-name', g['os_region_name'], '--os-auth-url', auth_endpoint, args)) else raise(e) end end end def auth_glance(*args) self.class.auth_glance(args) end def self.auth_glance_stdin(*args) begin g = glance_credentials command = "glance --os-tenant-name #{g['admin_tenant_name']} --os-username #{g['admin_user']} --os-password #{g['admin_password']} --os-region-name #{g['os_region_name']} --os-auth-url #{auth_endpoint} #{args.join(' ')}" # This is a horrible, horrible hack # Redirect stderr to stdout in order to report errors # Ignore good output err = `#{command} 3>&1 1>/dev/null 2>&3` if $? != 0 raise(Puppet::Error, err) end end end def auth_glance_stdin(*args) self.class.auth_glance_stdin(args) end private def self.list_glance_images ids = [] (auth_glance('image-list').split("\n")[3..-2] || []).collect do |line| ids << line.split('|')[1].strip() end return ids end def self.get_glance_image_attr(id, attr) (auth_glance('image-show', id).split("\n") || []).collect do |line| if line =~ /^#{attr}:/ return line.split(': ')[1..-1] end end end def self.get_glance_image_attrs(id) attrs = {} (auth_glance('image-show', id).split("\n")[3..-2] || []).collect do |line| attrs[line.split('|')[1].strip()] = line.split('|')[2].strip() end return attrs end def parse_table(table) # parse the table into an array of maps with a simplistic state machine found_header = false parsed_header = false keys = nil results = [] table.split("\n").collect do |line| # look for the header if not found_header if line =~ /^\+[-|+]+\+$/ found_header = true nil end # look for the key names in the table header elsif not parsed_header if line =~ /^(\|\s*[:alpha:]\s*)|$/ keys = line.split('|').map(&:strip) parsed_header = true end # parse the values in the rest of the table elsif line =~ /^|.*|$/ values = line.split('|').map(&:strip) result = Hash[keys.zip values] results << result end end results end # Remove warning from the output. This is a temporary hack until # things will be refactored to use the REST API def self.remove_warnings(results) found_header = false in_warning = false results.split("\n").collect do |line| unless found_header if line =~ /^\+[-\+]+\+$/ # Matches upper and lower box borders in_warning = false found_header = true line elsif line =~ /^WARNING/ or line =~ /UserWarning/ or in_warning # warnings can be multi line, we have to skip all of them in_warning = true nil else line end else line end end.compact.join("\n") end end