Files
puppet-nova/lib/puppet/provider/nova_aggregate/nova.rb
Matt Fischer 580f6289d9 Don't add non-existent hosts to host aggregates
nova will throw an error if you attempt to add a node to a host
aggergate that does not "exist" in terms of nova. Therefore
depending on the order in which your nodes come up, you can have puppet
attempting to add a node to a host aggregate when the node does not yet
have nova compute installed and "registered". This is especially true if
you manage your host list with a hiera list or other mechanism that is
not directly tied to nova-compute being installed. Instead of blowing up
with an error, we will instead print a warning and let eventual
consistency solve it as your nodes are installed.

Since it is inefficient to call nova host-list once for every member of
a host aggregate we also cache this information and similarly cache the
output of nova aggegrate-details which previously was also called
once for every member of every aggegrate.

Change-Id: I6249cd070ad804c626d880decb6767e2ff14dcbd
2015-10-21 21:03:38 -06:00

174 lines
5.1 KiB
Ruby

require File.join(File.dirname(__FILE__), '..','..','..',
'puppet/provider/nova')
Puppet::Type.type(:nova_aggregate).provide(
:nova,
:parent => Puppet::Provider::Nova
) do
desc "Manage nova aggregations"
commands :nova => 'nova'
mk_resource_methods
def self.instances
nova_aggregate_resources_ids().collect do |el|
attrs = nova_aggregate_resources_attr(el['Id'])
new(
:ensure => :present,
:name => attrs['Name'],
:id => attrs['Id'],
:availability_zone => attrs['Availability Zone'],
:metadata => attrs['Metadata'],
:hosts => attrs['Hosts']
)
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 destroy
#delete hosts first
if not @property_hash[:hosts].nil?
@property_hash[:hosts].each do |h|
auth_nova("aggregate-remove-host", name, h)
end
end
#now delete aggregate
auth_nova("aggregate-delete", name)
@property_hash[:ensure] = :absent
end
def create
extras = Array.new
#check for availability zone
if not @resource[:availability_zone].nil? and not @resource[:availability_zone].empty?
extras << "#{@resource[:availability_zone]}"
end
#run the command
result = auth_nova("aggregate-create", resource[:name], extras)
#get Id by Name
#force a refresh of the aggregate list on creation
id = self.class.nova_aggregate_resources_get_name_by_id(resource[:name], true)
@property_hash = {
:ensure => :present,
:name => resource[:name],
:id => id,
:availability_zone => resource[:availability_zone]
}
#add metadata
if not @resource[:metadata].nil? and not @resource[:metadata].empty?
@resource[:metadata].each do |key, value|
set_metadata_helper(id, key, value)
end
@property_hash[:metadata] = resource[:metadata]
end
#add hosts - This throws an error if the host is already attached to another aggregate!
if not @resource[:hosts].nil? and not @resource[:hosts].empty?
@resource[:hosts].each do |host|
# make sure the host exists in nova, or nova will fail the call
# this solves weird ordering issues with a compute node that's
# not 100% up being added to the host aggregate
if is_host_in_nova?(host)
auth_nova("aggregate-add-host", id, "#{host}")
else
warning("Cannot add #{host} to host aggregate, it's not available yet in nova host-list")
end
end
@property_hash[:hosts] = resource[:hosts]
end
end
def is_host_in_nova?(host)
return host==self.class.nova_get_host_by_name_and_type(host, "compute")
end
def hosts=(val)
#get current hosts
id = self.class.nova_aggregate_resources_get_name_by_id(name)
attrs = self.class.nova_aggregate_resources_attr(id)
#remove all hosts which are not in new value list
attrs['Hosts'].each do |h|
if not val.include? h
auth_nova("aggregate-remove-host", id, "#{h}")
end
end
#add hosts from the value list
val.each do |h|
if not attrs['Hosts'].include? h
if is_host_in_nova?(h)
auth_nova("aggregate-add-host", id, "#{h}")
else
warning("Cannot add #{h} to host aggregate, it's not available yet in nova host-list")
end
end
end
end
def set_metadata_helper(agg_id, key, value)
auth_nova("aggregate-set-metadata", agg_id, "#{key}=#{value}")
end
def metadata
#get current metadata
id = self.class.nova_aggregate_resources_get_name_by_id(name)
attrs = self.class.nova_aggregate_resources_attr(id)
#just ignore the availability_zone. that's handled directly by nova
attrs['Metadata'].delete('availability_zone')
return attrs['Metadata']
end
def metadata=(val)
#get current metadata
id = self.class.nova_aggregate_resources_get_name_by_id(name)
attrs = self.class.nova_aggregate_resources_attr(id)
#get keys which are in current metadata but not in val. Make sure it has data first!
if attrs['Metadata'].length > 0
obsolete_keys = attrs['Metadata'].keys - val.keys
end
# clear obsolete keys. If there are any!
if obsolete_keys
obsolete_keys.each do |key|
if not key.include? 'availability_zone'
auth_nova("aggregate-set-metadata", id, "#{key}")
end
end
#handle keys (with obsolete keys)
new_keys = val.keys - obsolete_keys
else
#handle keys (without obsolete keys)
new_keys = val.keys
end
#set new metadata if value changed
new_keys.each do |key|
if val[key] != attrs['Metadata'][key.to_s]
value = val[key]
set_metadata_helper(id, key, value)
end
end
end
def availability_zone=(val)
id = self.class.nova_aggregate_resources_get_name_by_id(name)
auth_nova("aggregate-set-metadata", id, "availability_zone=#{val}")
end
end