First attempt at creating a glance_image type
This commit is contained in:
parent
9071ed1743
commit
242a424561
|
@ -0,0 +1,120 @@
|
|||
# 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['filter:authtoken'] and
|
||||
glance_file['filter:authtoken']['auth_host'] and
|
||||
glance_file['filter:authtoken']['auth_port'] and
|
||||
glance_file['filter:authtoken']['auth_protocol'] and
|
||||
glance_file['filter:authtoken']['admin_tenant_name'] and
|
||||
glance_file['filter:authtoken']['admin_user'] and
|
||||
glance_file['filter:authtoken']['admin_password']
|
||||
|
||||
g = {}
|
||||
g['auth_host'] = glance_file['filter:authtoken']['auth_host'].strip
|
||||
g['auth_port'] = glance_file['filter:authtoken']['auth_port'].strip
|
||||
g['auth_protocol'] = glance_file['filter:authtoken']['auth_protocol'].strip
|
||||
g['admin_tenant_name'] = glance_file['filter:authtoken']['admin_tenant_name'].strip
|
||||
g['admin_user'] = glance_file['filter:authtoken']['admin_user'].strip
|
||||
g['admin_password'] = glance_file['filter:authtoken']['admin_password'].strip
|
||||
return g
|
||||
else
|
||||
raise(Puppet::Error, 'File: /etc/glance/glance-api-paste.ini 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
|
||||
"#{g['auth_protocol']}://#{g['auth_host']}:#{g['auth_port']}/v2.0/"
|
||||
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-paste.ini')
|
||||
@glance_file
|
||||
end
|
||||
|
||||
def self.glance_hash
|
||||
@glance_hash ||= build_glance_hash
|
||||
end
|
||||
|
||||
def glance_hash
|
||||
self.class.glance_hash
|
||||
end
|
||||
|
||||
def self.auth_glance(*args)
|
||||
begin
|
||||
g = glance_credentials
|
||||
glance('-T', g['admin_tenant_name'], '-I', g['admin_user'], '-K', g['admin_password'], '-N', auth_endpoint, args)
|
||||
rescue Exception => e
|
||||
# Will probably add conditions later
|
||||
raise(e)
|
||||
end
|
||||
end
|
||||
|
||||
def auth_glance(*args)
|
||||
self.class.auth_glance(args)
|
||||
end
|
||||
|
||||
def self.auth_glance_stdin(*args)
|
||||
begin
|
||||
g = glance_credentials
|
||||
command = "glance --silent-upload -T #{g['admin_tenant_name']} -I #{g['admin_user']} -K #{g['admin_password']} -N #{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('index').split("\n")[2..-1] || []).collect do |line|
|
||||
ids << line.split[0]
|
||||
end
|
||||
return ids
|
||||
end
|
||||
|
||||
def self.get_glance_image_attr(id, attr)
|
||||
(auth_glance('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('show', id).split("\n") || []).collect do |line|
|
||||
attrs[line.split(': ').first.downcase] = line.split(': ')[1..-1].to_s
|
||||
end
|
||||
return attrs
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,115 @@
|
|||
$LOAD_PATH.push(File.join(File.dirname(__FILE__), '..', '..', '..'))
|
||||
|
||||
# Load the Glance provider library to help
|
||||
require 'puppet/provider/glance'
|
||||
|
||||
Puppet::Type.type(:glance_image).provide(
|
||||
:glance,
|
||||
:parent => Puppet::Provider::Glance
|
||||
) do
|
||||
desc <<-EOT
|
||||
Glance provider to manage glance_image type.
|
||||
|
||||
Assumes that the Keystone service is on the same host and is working.
|
||||
EOT
|
||||
|
||||
commands :glance => 'glance'
|
||||
|
||||
def self.prefetch(resource)
|
||||
# rebuild the cache for every puppet run
|
||||
@image_hash = nil
|
||||
end
|
||||
|
||||
def self.image_hash
|
||||
@image_hash ||= build_image_hash
|
||||
end
|
||||
|
||||
def image_hash
|
||||
self.class.image_hash
|
||||
end
|
||||
|
||||
def self.instances
|
||||
image_hash.collect do |k, v|
|
||||
new(:name => k)
|
||||
end
|
||||
end
|
||||
|
||||
def create
|
||||
stdin = nil
|
||||
if resource[:source]
|
||||
# copy_from cannot handle file://
|
||||
if resource[:source] =~ /^\// # local file
|
||||
location = "< #{resource[:source]}"
|
||||
stdin = true
|
||||
else
|
||||
location = "copy_from=#{resource[:source]}"
|
||||
end
|
||||
# location cannot handle file://
|
||||
# location does not import, so no sense in doing anything more than this
|
||||
elsif resource[:location]
|
||||
location = "location=#{resource[:location]}"
|
||||
else
|
||||
raise(Puppet::Error, "Must specify either source or location")
|
||||
end
|
||||
if stdin
|
||||
auth_glance_stdin('add', "name='#{resource[:name]}'", "is_public=#{resource[:is_public]}", "container_format=#{resource[:container_format]}", "disk_format=#{resource[:disk_format]}", location)
|
||||
else
|
||||
auth_glance('add', "name='#{resource[:name]}'", "is_public=#{resource[:is_public]}", "container_format=#{resource[:container_format]}", "disk_format=#{resource[:disk_format]}", location)
|
||||
end
|
||||
end
|
||||
|
||||
def exists?
|
||||
image_hash[resource[:name]]
|
||||
end
|
||||
|
||||
def destroy
|
||||
auth_glance('delete', '-f', image_hash[resource[:name]]['id'])
|
||||
end
|
||||
|
||||
def location
|
||||
image_hash[resource[:name]]['location']
|
||||
end
|
||||
|
||||
def location=(value)
|
||||
auth_glance('update', image_hash[resource[:name]]['id'], "location=#{value}")
|
||||
end
|
||||
|
||||
def is_public
|
||||
image_hash[resource[:name]]['public']
|
||||
end
|
||||
|
||||
def is_public=(value)
|
||||
auth_glance('update', image_hash[resource[:name]]['id'], "is_public=#{value}")
|
||||
end
|
||||
|
||||
def disk_format
|
||||
image_hash[resource[:name]]['disk format']
|
||||
end
|
||||
|
||||
def disk_format=(value)
|
||||
auth_glance('update', image_hash[resource[:name]]['id'], "disk_format=#{value}")
|
||||
end
|
||||
|
||||
def container_format
|
||||
image_hash[resource[:name]]['container format']
|
||||
end
|
||||
|
||||
def container_format=(value)
|
||||
auth_glance('update', image_hash[resource[:name]]['id'], "container_format=#{value}")
|
||||
end
|
||||
|
||||
def id
|
||||
image_hash[resource[:name]]['id']
|
||||
end
|
||||
|
||||
private
|
||||
def self.build_image_hash
|
||||
hash = {}
|
||||
list_glance_images.each do |image|
|
||||
attrs = get_glance_image_attrs(image)
|
||||
hash[attrs['name'].to_s] = attrs
|
||||
end
|
||||
hash
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
Puppet::Type.newtype(:glance_image) do
|
||||
desc <<-EOT
|
||||
This allows manifests to declare an image to be
|
||||
stored in glance.
|
||||
|
||||
glance_image {
|
||||
ensure => present,
|
||||
name => "Ubuntu 12.04 cloudimg amd64"
|
||||
is_public => true,
|
||||
container_format => ovf,
|
||||
disk_format => qcow',
|
||||
source => 'http://uec-images.ubuntu.com/releases/precise/release/ubuntu-12.04-server-cloudimg-amd64-disk1.img'
|
||||
}
|
||||
|
||||
Known problems / limitations:
|
||||
* All images are managed by the glance service.
|
||||
This means that since users are unable to manage their own images via this type,
|
||||
is_public is really of no use. You can probably hide images this way but that's all.
|
||||
* I really have no idea what I'm doing.
|
||||
|
||||
EOT
|
||||
|
||||
ensurable
|
||||
|
||||
newparam(:name, :namevar => true) do
|
||||
desc 'The image name'
|
||||
newvalues(/.*/)
|
||||
end
|
||||
|
||||
newproperty(:id) do
|
||||
desc 'The unique id of the image'
|
||||
validate do |v|
|
||||
raise(Puppet:Error, 'This is a read only property')
|
||||
end
|
||||
end
|
||||
|
||||
newproperty(:location) do
|
||||
desc "The permanent location of the image. Optional"
|
||||
newvalues(/\S+/)
|
||||
end
|
||||
|
||||
newproperty(:is_public) do
|
||||
desc "Whether the image is public or not. Default true"
|
||||
newvalues(/(y|Y)es/, /(n|N)o/)
|
||||
defaultto('Yes')
|
||||
munge do |v|
|
||||
v.to_s.capitalize
|
||||
end
|
||||
end
|
||||
|
||||
newproperty(:container_format) do
|
||||
desc "The format of the container"
|
||||
newvalues(:ami, :ari, :aki, :bare, :ovf)
|
||||
end
|
||||
|
||||
newproperty(:disk_format) do
|
||||
desc "The format of the disk"
|
||||
newvalues(:ami, :ari, :aki, :vhd, :vmd, :raw, :qcow2, :vdi)
|
||||
end
|
||||
|
||||
newparam(:source) do
|
||||
desc "The source of the image to import from"
|
||||
newvalues(/\S+/)
|
||||
end
|
||||
|
||||
# Require the Glance service to be running
|
||||
autorequire(:service) do
|
||||
['glance']
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
Loading…
Reference in New Issue