diff --git a/lib/vmpooler/providers/vsphere.rb b/lib/vmpooler/providers/vsphere.rb index d0ca8c618..53524fef0 100644 --- a/lib/vmpooler/providers/vsphere.rb +++ b/lib/vmpooler/providers/vsphere.rb @@ -839,7 +839,20 @@ def find_least_used_host(cluster, connection, datacentername) def find_cluster(cluster, connection, datacentername) datacenter = connection.serviceInstance.find_datacenter(datacentername) raise("Datacenter #{datacentername} does not exist") if datacenter.nil? - datacenter.hostFolder.children.find { |cluster_object| cluster_object.name == cluster } + + # In the event the cluster is not a direct descendent of the + # datacenter, we use a ContainerView to leverage its recursive + # search. This will find clusters which are, for example, in + # folders under the datacenter. This will also find standalone + # hosts which are not part of a cluster. + cv = connection.serviceContent.viewManager.CreateContainerView( + container: datacenter.hostFolder, + type: ['ComputeResource', 'ClusterComputeResource'], + recursive: true, + ) + cluster = cv.view.find { |cluster_object| cluster_object.name == cluster } + cv.DestroyView + cluster end def get_cluster_host_utilization(cluster, model = nil) diff --git a/spec/rbvmomi_helper.rb b/spec/rbvmomi_helper.rb index 88da34308..05754d912 100644 --- a/spec/rbvmomi_helper.rb +++ b/spec/rbvmomi_helper.rb @@ -24,7 +24,29 @@ # https://www.vmware.com/support/developer/vc-sdk/visdk400pubs/ReferenceGuide/vim.view.ContainerView.html # From ContainerView :container, :recursive, :type -) +) do + def _search_tree(layer) + results = [] + + layer.children.each do |child| + if type.any? { |t| child.is_a?(RbVmomi::VIM.const_get(t)) } + results << child + end + + if recursive && child.respond_to?(:children) + results += _search_tree(child) + end + end + results + end + + def view + _search_tree(container) + end + + def DestroyView + end +end MockDatacenter = Struct.new( # https://www.vmware.com/support/developer/vc-sdk/visdk41pubs/ApiReference/vim.Datacenter.html @@ -384,6 +406,10 @@ def mock_RbVmomi_VIM_ComputeResource(options = {}) mock.host << mock_host end + allow(mock).to receive(:is_a?) do |expected_type| + expected_type == RbVmomi::VIM::ComputeResource + end + mock end diff --git a/spec/unit/providers/vsphere_spec.rb b/spec/unit/providers/vsphere_spec.rb index 7e445c2b6..1d166d25f 100644 --- a/spec/unit/providers/vsphere_spec.rb +++ b/spec/unit/providers/vsphere_spec.rb @@ -2887,6 +2887,7 @@ describe '#find_cluster' do let(:cluster) {'cluster'} + let(:host) { 'host' } let(:missing_cluster) {'missing_cluster'} context 'no clusters in the datacenter' do @@ -2909,10 +2910,11 @@ :datacenters => [ { :name => datacenter_name, :hostfolder_tree => { - 'cluster1' => {:object_type => 'compute_resource'}, - 'cluster2' => {:object_type => 'compute_resource'}, - cluster => {:object_type => 'compute_resource'}, - 'cluster3' => {:object_type => 'compute_resource'}, + 'cluster1' => {:object_type => 'cluster_compute_resource'}, + 'cluster2' => {:object_type => 'cluster_compute_resource'}, + cluster => {:object_type => 'cluster_compute_resource'}, + 'cluster3' => {:object_type => 'cluster_compute_resource'}, + host => {:object_type => 'compute_resource'}, } } ] @@ -2926,6 +2928,13 @@ expect(result.name).to eq(cluster) end + it 'should return the single host when found' do + result = subject.find_cluster(host,connection,datacenter_name) + + expect(result).to_not be_nil + expect(result.name).to eq(host) + end + it 'should return nil if the cluster is not found' do expect(subject.find_cluster(missing_cluster,connection,datacenter_name)).to be_nil end @@ -2937,14 +2946,15 @@ :datacenters => [ { :name => 'AnotherDC', :hostfolder_tree => { - 'cluster1' => {:object_type => 'compute_resource'}, - 'cluster2' => {:object_type => 'compute_resource'}, + 'cluster1' => {:object_type => 'cluster_compute_resource'}, + 'cluster2' => {:object_type => 'cluster_compute_resource'}, } }, { :name => datacenter_name, :hostfolder_tree => { - cluster => {:object_type => 'compute_resource'}, - 'cluster3' => {:object_type => 'compute_resource'}, + cluster => {:object_type => 'cluster_compute_resource'}, + 'cluster3' => {:object_type => 'cluster_compute_resource'}, + host => {:object_type => 'compute_resource'} } } ] @@ -2958,6 +2968,13 @@ expect(result.name).to eq(cluster) end + it 'should return the single host when found' do + result = subject.find_cluster(host,connection,datacenter_name) + + expect(result).to_not be_nil + expect(result.name).to eq(host) + end + it 'should return nil if the cluster is not found' do expect(subject.find_cluster(missing_cluster,connection,'AnotherDC')).to be_nil end @@ -2969,13 +2986,18 @@ :datacenters => [ { :name => datacenter_name, :hostfolder_tree => { - 'cluster1' => {:object_type => 'compute_resource'}, + 'cluster1' => {:object_type => 'cluster_compute_resource'}, 'folder2' => { :children => { - cluster => {:object_type => 'compute_resource'}, + cluster => {:object_type => 'cluster_compute_resource'}, } }, - 'cluster3' => {:object_type => 'compute_resource'}, + 'cluster3' => {:object_type => 'cluster_compute_resource'}, + 'folder4' => { + :children => { + host => {:object_type => 'compute_resource'}, + } + } } } ] @@ -2983,13 +3005,19 @@ }} it 'should return the cluster when found' do - pending('https://github.com/puppetlabs/vmpooler/issues/205') result = subject.find_cluster(cluster,connection,datacenter_name) expect(result).to_not be_nil expect(result.name).to eq(cluster) end + it 'should return the host when found' do + result = subject.find_cluster(host,connection,datacenter_name) + + expect(result).to_not be_nil + expect(result.name).to eq(host) + end + it 'should return nil if the cluster is not found' do expect(subject.find_cluster(missing_cluster,connection,datacenter_name)).to be_nil end