From 75660de04e80131b804919ef3a28a4b4a1d6f007 Mon Sep 17 00:00:00 2001 From: Mike Dorman Date: Thu, 14 Jan 2016 17:08:45 -0700 Subject: [PATCH] Nova cells support for nova_aggregate provider Updates the nova_aggregate provider to support nova cells routing info in the naming of hosts and hosts aggregate. Change-Id: I29131e378184262a74b9e99a85a8215282787f65 Closes-bug: 1533423 --- lib/puppet/provider/nova.rb | 10 +- lib/puppet/provider/nova_aggregate/nova.rb | 26 ++--- spec/unit/provider/nova_spec.rb | 126 +++++++++++++++++++++ 3 files changed, 144 insertions(+), 18 deletions(-) diff --git a/lib/puppet/provider/nova.rb b/lib/puppet/provider/nova.rb index 1faa51bb7..77ff406ff 100644 --- a/lib/puppet/provider/nova.rb +++ b/lib/puppet/provider/nova.rb @@ -175,9 +175,10 @@ class Puppet::Provider::Nova < Puppet::Provider 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 + # (mdorman) Support api!cell_name@host_name -style output of nova host-list under nova cells + if entry["host_name"] =~ /^([a-zA-Z0-9\-_]+![a-zA-Z0-9\-_]+@)?#{Regexp.quote(host_name)}$/ if entry["service"] == service_type - return entry["host_name"] + return host_name end end end @@ -198,7 +199,10 @@ class Puppet::Provider::Nova < Puppet::Provider @nova_aggregate_resources_ids = cliout2list(cmd_output) #only interessted in Id and Name @nova_aggregate_resources_ids.map{ |e| e.delete("Availability Zone")} - @nova_aggregate_resources_ids.map{ |e| e['Id'] = e['Id'].to_i} + @nova_aggregate_resources_ids.map{ |e| + if e['Id'] =~ /^[0-9]+$/ + e['Id'] = e['Id'].to_i + end } @nova_aggregate_resources_ids end diff --git a/lib/puppet/provider/nova_aggregate/nova.rb b/lib/puppet/provider/nova_aggregate/nova.rb index 34a7b5bf0..222fa8572 100644 --- a/lib/puppet/provider/nova_aggregate/nova.rb +++ b/lib/puppet/provider/nova_aggregate/nova.rb @@ -14,7 +14,7 @@ Puppet::Type.type(:nova_aggregate).provide( def self.instances nova_aggregate_resources_ids().collect do |el| - attrs = nova_aggregate_resources_attr(el['Id']) + attrs = nova_aggregate_resources_attr(el['Name']) new( :ensure => :present, :name => attrs['Name'], @@ -74,7 +74,7 @@ Puppet::Type.type(:nova_aggregate).provide( #add metadata if not @resource[:metadata].nil? and not @resource[:metadata].empty? @resource[:metadata].each do |key, value| - set_metadata_helper(id, key, value) + set_metadata_helper(resource[:name], key, value) end @property_hash[:metadata] = resource[:metadata] end @@ -86,7 +86,7 @@ Puppet::Type.type(:nova_aggregate).provide( # 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}") + auth_nova("aggregate-add-host", resource[:name], "#{host}") else warning("Cannot add #{host} to host aggregate, it's not available yet in nova host-list") end @@ -101,12 +101,11 @@ Puppet::Type.type(:nova_aggregate).provide( 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) + attrs = self.class.nova_aggregate_resources_attr(name) #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}") + auth_nova("aggregate-remove-host", name, "#{h}") end end @@ -114,7 +113,7 @@ Puppet::Type.type(:nova_aggregate).provide( val.each do |h| if not attrs['Hosts'].include? h if is_host_in_nova?(h) - auth_nova("aggregate-add-host", id, "#{h}") + auth_nova("aggregate-add-host", name, "#{h}") else warning("Cannot add #{h} to host aggregate, it's not available yet in nova host-list") end @@ -128,8 +127,7 @@ Puppet::Type.type(:nova_aggregate).provide( def metadata #get current metadata - id = self.class.nova_aggregate_resources_get_name_by_id(name) - attrs = self.class.nova_aggregate_resources_attr(id) + attrs = self.class.nova_aggregate_resources_attr(name) #just ignore the availability_zone. that's handled directly by nova attrs['Metadata'].delete('availability_zone') return attrs['Metadata'] @@ -137,8 +135,7 @@ Puppet::Type.type(:nova_aggregate).provide( 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) + attrs = self.class.nova_aggregate_resources_attr(name) #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 @@ -147,7 +144,7 @@ Puppet::Type.type(:nova_aggregate).provide( if obsolete_keys obsolete_keys.each do |key| if not key.include? 'availability_zone' - auth_nova("aggregate-set-metadata", id, "#{key}") + auth_nova("aggregate-set-metadata", name, "#{key}") end end #handle keys (with obsolete keys) @@ -160,14 +157,13 @@ Puppet::Type.type(:nova_aggregate).provide( new_keys.each do |key| if val[key] != attrs['Metadata'][key.to_s] value = val[key] - set_metadata_helper(id, key, value) + set_metadata_helper(name, 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}") + auth_nova("aggregate-set-metadata", name, "availability_zone=#{val}") end end diff --git a/spec/unit/provider/nova_spec.rb b/spec/unit/provider/nova_spec.rb index acd3bd9ac..46b0db4fb 100644 --- a/spec/unit/provider/nova_spec.rb +++ b/spec/unit/provider/nova_spec.rb @@ -214,6 +214,76 @@ EOT end end + describe 'when parsing cli output with cells enabled' do + + it 'should return a list with hashes' do + output = <<-EOT ++-------------+----------------+-------------------+ +| Id | Name | Availability Zone | ++-------------+----------------+-------------------+ +| api!cell@1 | api!cell@haha | haha2 | +| api!cell@2 | api!cell@haha2 | - | ++-------------+----------------+-------------------+ + EOT + res = klass.cliout2list(output) + expect(res).to eq([{"Id"=>"api!cell@1", "Name"=>"api!cell@haha", "Availability Zone"=>"haha2"}, + {"Id"=>"api!cell@2", "Name"=>"api!cell@haha2", "Availability Zone"=>""}]) + end + + it 'should return a list with hashes' do + output = <<-EOT ++-------------+----------------+-------------------+-------+--------------------------------------------------+ +| Id | Name | Availability Zone | Hosts | Metadata | ++-------------+----------------+-------------------+-------+--------------------------------------------------+ +| api!cell@16 | api!cell@agg94 | my_-zone1 | | 'a=b', 'availability_zone= my_-zone1', 'x_q-r=y' | ++-------------+----------------+-------------------+-------+--------------------------------------------------+ +EOT + res = klass.cliout2list(output) + expect(res).to eq([{"Id"=>"api!cell@16", + "Name"=>"api!cell@agg94", + "Availability Zone"=>"my_-zone1", + "Hosts"=>"", + "Metadata"=> { + "a"=>"b", + "availability_zone"=>" my_-zone1", + "x_q-r"=>"y" + } + }]) + end + + it 'should return a empty list' do + output = <<-EOT ++----+------+-------------------+ +| Id | Name | Availability Zone | ++----+------+-------------------+ ++----+------+-------------------+ + EOT + res = klass.cliout2list(output) + expect(res).to eq([]) + end + + it 'should return a empty list because no input available' do + output = <<-EOT + EOT + res = klass.cliout2list(output) + expect(res).to eq([]) + end + + it 'should return a list with hashes' do + output = <<-EOT ++-------------+-------------------------+-------------------+ +| Id | Name | Availability Zone | ++-------------+-------------------------+-------------------+ +| api!cell@6 | api!cell@my | zone1 | +| api!cell@8 | api!cell@my2 | - | ++-------------+-------------------------+-------------------+ + EOT + res = klass.cliout2list(output) + expect(res).to eq([{"Id"=>"api!cell@6", "Name"=>"api!cell@my", "Availability Zone"=>"zone1"}, + {"Id"=>"api!cell@8", "Name"=>"api!cell@my2", "Availability Zone"=>""}]) + end + end + describe 'when handling cli output' do it 'should return the availble Id' do output = <<-EOT @@ -245,6 +315,37 @@ EOT end end + describe 'when handling cli output with cells enabled' do + it 'should return the availble Id' do + output = <<-EOT ++-------------+----------------+-------------------+ +| Id | Name | Availability Zone | ++-------------+----------------+-------------------+ +| api!cell@1 | api!cell@haha | haha2 | +| api!cell@2 | api!cell@haha2 | - | ++-------------+----------------+-------------------+ + EOT + klass.expects(:auth_nova).returns(output) + res = klass.nova_aggregate_resources_get_name_by_id("api!cell@haha2", true) + expect(res).to eq("api!cell@2") + end + + it 'should return nil because given name is not available' do + output = <<-EOT ++----+-------+-------------------+ +| Id | Name | Availability Zone | ++----+-------+-------------------+ +| api!cell@1 | api!cell@haha | haha2 | +| api!cell@2 | api!cell@haha2 | - | ++----+-------+-------------------+ + EOT + # used the cache copy, don't call nova again + klass.expects(:auth_nova).never() + res = klass.nova_aggregate_resources_get_name_by_id("notavailable") + expect(res).to eql(nil) + end + end + describe 'when getting details for given Id' do it 'should return a Hash with the details' do output = <<-EOT @@ -270,6 +371,31 @@ EOT end end + describe 'when getting details for given Id with cells enabled' do + it 'should return a Hash with the details' do + output = <<-EOT ++-------------+----------------+-------------------+-------+--------------------------------------------------+ +| Id | Name | Availability Zone | Hosts | Metadata | ++-------------+----------------+-------------------+-------+--------------------------------------------------+ +| api!cell@16 | api!cell@agg94 | my_-zone1 | | 'a=b', 'availability_zone= my_-zone1', 'x_q-r=y' | ++-------------+----------------+-------------------+-------+--------------------------------------------------+ + EOT + klass.expects(:auth_nova).returns(output) + res = klass.nova_aggregate_resources_attr(16) + expect(res).to eq({ + "Id"=>"api!cell@16", + "Name"=>"api!cell@agg94", + "Availability Zone"=>"my_-zone1", + "Hosts"=>[], + "Metadata"=>{ + "a"=>"b", + "availability_zone"=>" my_-zone1", + "x_q-r"=>"y" + } + }) + end + end + describe 'when searching for a host/type combo' do it 'should find the hostname if there is a match' do output = <<-EOT