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
This commit is contained in:
@@ -158,22 +158,46 @@ class Puppet::Provider::Nova < Puppet::Provider
|
|||||||
return hash_list
|
return hash_list
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.nova_aggregate_resources_ids
|
def self.nova_hosts
|
||||||
|
return @nova_hosts if @nova_hosts
|
||||||
|
cmd_output = auth_nova("host-list")
|
||||||
|
@nova_hosts = cliout2list(cmd_output)
|
||||||
|
@nova_hosts
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.nova_get_host_by_name_and_type(host_name, service_type)
|
||||||
|
#find the host by name and service type
|
||||||
|
nova_hosts.each do |entry|
|
||||||
|
if entry["host_name"] == host_name
|
||||||
|
if entry["service"] == service_type
|
||||||
|
return entry["host_name"]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
#name/service combo not found
|
||||||
|
return nil
|
||||||
|
end
|
||||||
|
|
||||||
|
def self.nova_aggregate_resources_ids(force_refresh=false)
|
||||||
|
# return the cached list unless requested
|
||||||
|
if not force_refresh
|
||||||
|
return @nova_aggregate_resources_ids if @nova_aggregate_resources_ids
|
||||||
|
end
|
||||||
#produce a list of hashes with Id=>Name pairs
|
#produce a list of hashes with Id=>Name pairs
|
||||||
lines = []
|
lines = []
|
||||||
#run command
|
#run command
|
||||||
cmd_output = auth_nova("aggregate-list")
|
cmd_output = auth_nova("aggregate-list")
|
||||||
#parse output
|
#parse output
|
||||||
hash_list = cliout2list(cmd_output)
|
@nova_aggregate_resources_ids = cliout2list(cmd_output)
|
||||||
#only interessted in Id and Name
|
#only interessted in Id and Name
|
||||||
hash_list.map{ |e| e.delete("Availability Zone")}
|
@nova_aggregate_resources_ids.map{ |e| e.delete("Availability Zone")}
|
||||||
hash_list.map{ |e| e['Id'] = e['Id'].to_i}
|
@nova_aggregate_resources_ids.map{ |e| e['Id'] = e['Id'].to_i}
|
||||||
return hash_list
|
@nova_aggregate_resources_ids
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.nova_aggregate_resources_get_name_by_id(name)
|
def self.nova_aggregate_resources_get_name_by_id(name, force_refresh=false)
|
||||||
#find the id by the given name
|
#find the id by the given name
|
||||||
nova_aggregate_resources_ids.each do |entry|
|
nova_aggregate_resources_ids(force_refresh).each do |entry|
|
||||||
if entry["Name"] == name
|
if entry["Name"] == name
|
||||||
return entry["Id"]
|
return entry["Id"]
|
||||||
end
|
end
|
||||||
|
@@ -61,7 +61,8 @@ Puppet::Type.type(:nova_aggregate).provide(
|
|||||||
result = auth_nova("aggregate-create", resource[:name], extras)
|
result = auth_nova("aggregate-create", resource[:name], extras)
|
||||||
|
|
||||||
#get Id by Name
|
#get Id by Name
|
||||||
id = self.class.nova_aggregate_resources_get_name_by_id(resource[: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 = {
|
@property_hash = {
|
||||||
:ensure => :present,
|
:ensure => :present,
|
||||||
@@ -81,12 +82,23 @@ Puppet::Type.type(:nova_aggregate).provide(
|
|||||||
#add hosts - This throws an error if the host is already attached to another aggregate!
|
#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?
|
if not @resource[:hosts].nil? and not @resource[:hosts].empty?
|
||||||
@resource[:hosts].each do |host|
|
@resource[:hosts].each do |host|
|
||||||
auth_nova("aggregate-add-host", id, "#{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
|
end
|
||||||
@property_hash[:hosts] = resource[:hosts]
|
@property_hash[:hosts] = resource[:hosts]
|
||||||
end
|
end
|
||||||
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)
|
def hosts=(val)
|
||||||
#get current hosts
|
#get current hosts
|
||||||
id = self.class.nova_aggregate_resources_get_name_by_id(name)
|
id = self.class.nova_aggregate_resources_get_name_by_id(name)
|
||||||
@@ -101,7 +113,11 @@ Puppet::Type.type(:nova_aggregate).provide(
|
|||||||
#add hosts from the value list
|
#add hosts from the value list
|
||||||
val.each do |h|
|
val.each do |h|
|
||||||
if not attrs['Hosts'].include? h
|
if not attrs['Hosts'].include? h
|
||||||
auth_nova("aggregate-add-host", id, "#{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
|
end
|
||||||
end
|
end
|
||||||
|
@@ -240,7 +240,8 @@ EOT
|
|||||||
| 2 | haha2 | - |
|
| 2 | haha2 | - |
|
||||||
+----+-------+-------------------+
|
+----+-------+-------------------+
|
||||||
EOT
|
EOT
|
||||||
klass.expects(:auth_nova).returns(output)
|
# used the cache copy, don't call nova again
|
||||||
|
klass.expects(:auth_nova).never()
|
||||||
res = klass.nova_aggregate_resources_get_name_by_id("notavailable")
|
res = klass.nova_aggregate_resources_get_name_by_id("notavailable")
|
||||||
expect(res).to eql(nil)
|
expect(res).to eql(nil)
|
||||||
end
|
end
|
||||||
@@ -269,6 +270,38 @@ EOT
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
end
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'when searching for a host/type combo' do
|
||||||
|
it 'should find the hostname if there is a match' do
|
||||||
|
output = <<-EOT
|
||||||
|
+-------------------+-------------+----------+
|
||||||
|
| host_name | service | zone |
|
||||||
|
+-------------------+-------------+----------+
|
||||||
|
| node-control-001 | consoleauth | internal |
|
||||||
|
| node-control-001 | cert | internal |
|
||||||
|
| node-compute-002 | compute | nova |
|
||||||
|
+-------------------+-------------+----------+
|
||||||
|
EOT
|
||||||
|
klass.expects(:auth_nova).returns(output)
|
||||||
|
res = klass.nova_get_host_by_name_and_type("node-compute-002","compute")
|
||||||
|
expect(res).to eq("node-compute-002")
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'should return nil because there is no host/type combo match' do
|
||||||
|
output = <<-EOT
|
||||||
|
+-------------------+-------------+----------+
|
||||||
|
| host_name | service | zone |
|
||||||
|
+-------------------+-------------+----------+
|
||||||
|
| node-control-001 | consoleauth | internal |
|
||||||
|
| node-control-001 | cert | internal |
|
||||||
|
| node-compute-002 | compute | nova |
|
||||||
|
+-------------------+-------------+----------+
|
||||||
|
EOT
|
||||||
|
# used the cache copy, don't call nova again
|
||||||
|
klass.expects(:auth_nova).never()
|
||||||
|
res = klass.nova_get_host_by_name_and_type("node-compute-002","internal")
|
||||||
|
expect(res).to eql(nil)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
Reference in New Issue
Block a user