Merge pull request #223 from glennsarti/pooler-83-specify-datacenter

(POOLER-83) Add ability to specify a datacenter for vsphere
This commit is contained in:
Rob Braden 2017-06-27 15:16:27 -07:00 committed by GitHub
commit e5d2844fcc
4 changed files with 489 additions and 175 deletions

View file

@ -45,7 +45,7 @@ module Vmpooler
@connection_pool.with_metrics do |pool_object| @connection_pool.with_metrics do |pool_object|
connection = ensured_vsphere_connection(pool_object) connection = ensured_vsphere_connection(pool_object)
foldername = pool_config(pool_name)['folder'] foldername = pool_config(pool_name)['folder']
folder_object = find_folder(foldername, connection) folder_object = find_folder(foldername, connection, get_target_datacenter_from_config(pool_name))
return vms if folder_object.nil? return vms if folder_object.nil?
@ -94,7 +94,7 @@ module Vmpooler
raise("VM #{vm_name} does not exist in Pool #{pool_name} for the provider #{name}") if vm_object.nil? raise("VM #{vm_name} does not exist in Pool #{pool_name} for the provider #{name}") if vm_object.nil?
target_cluster_name = get_target_cluster_from_config(pool_name) target_cluster_name = get_target_cluster_from_config(pool_name)
cluster = find_cluster(target_cluster_name, connection) cluster = find_cluster(target_cluster_name, connection, get_target_datacenter_from_config(pool_name))
raise("Pool #{pool_name} specifies cluster #{target_cluster_name} which does not exist for the provider #{name}") if cluster.nil? raise("Pool #{pool_name} specifies cluster #{target_cluster_name} which does not exist for the provider #{name}") if cluster.nil?
# Go through each host and initiate a migration when the correct host name is found # Go through each host and initiate a migration when the correct host name is found
@ -142,6 +142,7 @@ module Vmpooler
target_folder_path = pool['folder'] target_folder_path = pool['folder']
target_datastore = pool['datastore'] target_datastore = pool['datastore']
target_cluster_name = get_target_cluster_from_config(pool_name) target_cluster_name = get_target_cluster_from_config(pool_name)
target_datacenter_name = get_target_datacenter_from_config(pool_name)
# Extract the template VM name from the full path # Extract the template VM name from the full path
raise("Pool #{pool_name} did specify a full path for the template for the provider #{name}") unless template_path =~ /\// raise("Pool #{pool_name} did specify a full path for the template for the provider #{name}") unless template_path =~ /\//
@ -149,7 +150,7 @@ module Vmpooler
template_name = templatefolders.pop template_name = templatefolders.pop
# Get the actual objects from vSphere # Get the actual objects from vSphere
template_folder_object = find_folder(templatefolders.join('/'), connection) template_folder_object = find_folder(templatefolders.join('/'), connection, target_datacenter_name)
raise("Pool #{pool_name} specifies a template folder of #{templatefolders.join('/')} which does not exist for the provider #{name}") if template_folder_object.nil? raise("Pool #{pool_name} specifies a template folder of #{templatefolders.join('/')} which does not exist for the provider #{name}") if template_folder_object.nil?
template_vm_object = template_folder_object.find(template_name) template_vm_object = template_folder_object.find(template_name)
@ -170,11 +171,11 @@ module Vmpooler
) )
# Choose a cluster/host to place the new VM on # Choose a cluster/host to place the new VM on
target_host_object = find_least_used_host(target_cluster_name, connection) target_host_object = find_least_used_host(target_cluster_name, connection, target_datacenter_name)
# Put the VM in the specified folder and resource pool # Put the VM in the specified folder and resource pool
relocate_spec = RbVmomi::VIM.VirtualMachineRelocateSpec( relocate_spec = RbVmomi::VIM.VirtualMachineRelocateSpec(
datastore: find_datastore(target_datastore, connection), datastore: find_datastore(target_datastore, connection, target_datacenter_name),
host: target_host_object, host: target_host_object,
diskMoveType: :moveChildMostDiskBacking diskMoveType: :moveChildMostDiskBacking
) )
@ -189,7 +190,7 @@ module Vmpooler
# Create the new VM # Create the new VM
new_vm_object = template_vm_object.CloneVM_Task( new_vm_object = template_vm_object.CloneVM_Task(
folder: find_folder(target_folder_path, connection), folder: find_folder(target_folder_path, connection, target_datacenter_name),
name: new_vmname, name: new_vmname,
spec: clone_spec spec: clone_spec
).wait_for_completion ).wait_for_completion
@ -211,7 +212,7 @@ module Vmpooler
vm_object = find_vm(vm_name, connection) vm_object = find_vm(vm_name, connection)
raise("VM #{vm_name} in pool #{pool_name} does not exist for the provider #{name}") if vm_object.nil? raise("VM #{vm_name} in pool #{pool_name} does not exist for the provider #{name}") if vm_object.nil?
add_disk(vm_object, disk_size, datastore_name, connection) add_disk(vm_object, disk_size, datastore_name, connection, get_target_datacenter_from_config(pool_name))
end end
true true
end end
@ -287,6 +288,16 @@ module Vmpooler
nil nil
end end
def get_target_datacenter_from_config(pool_name)
pool = pool_config(pool_name)
return nil if pool.nil?
return pool['datacenter'] unless pool['datacenter'].nil?
return provider_config['datacenter'] unless provider_config['datacenter'].nil?
nil
end
def generate_vm_hash(vm_object, template_name, pool_name) def generate_vm_hash(vm_object, template_name, pool_name)
hash = { 'name' => nil, 'hostname' => nil, 'template' => nil, 'poolname' => nil, 'boottime' => nil, 'powerstate' => nil } hash = { 'name' => nil, 'hostname' => nil, 'template' => nil, 'poolname' => nil, 'boottime' => nil, 'powerstate' => nil }
@ -375,11 +386,12 @@ module Vmpooler
(full_path.reverse.map { |p| p[1] }).join('/') (full_path.reverse.map { |p| p[1] }).join('/')
end end
def add_disk(vm, size, datastore, connection) def add_disk(vm, size, datastore, connection, datacentername)
return false unless size.to_i > 0 return false unless size.to_i > 0
vmdk_datastore = find_datastore(datastore, connection) vmdk_datastore = find_datastore(datastore, connection, datacentername)
vmdk_file_name = "#{vm['name']}/#{vm['name']}_#{find_vmdks(vm['name'], datastore, connection).length + 1}.vmdk" raise("Datastore '#{datastore}' does not exist in datacenter '#{datacentername}'") if vmdk_datastore.nil?
vmdk_file_name = "#{vm['name']}/#{vm['name']}_#{find_vmdks(vm['name'], datastore, connection, datacentername).length + 1}.vmdk"
controller = find_disk_controller(vm) controller = find_disk_controller(vm)
@ -413,7 +425,7 @@ module Vmpooler
) )
connection.serviceContent.virtualDiskManager.CreateVirtualDisk_Task( connection.serviceContent.virtualDiskManager.CreateVirtualDisk_Task(
datacenter: connection.serviceInstance.find_datacenter, datacenter: connection.serviceInstance.find_datacenter(datacentername),
name: "[#{vmdk_datastore.name}] #{vmdk_file_name}", name: "[#{vmdk_datastore.name}] #{vmdk_file_name}",
spec: vmdk_spec spec: vmdk_spec
).wait_for_completion ).wait_for_completion
@ -423,8 +435,9 @@ module Vmpooler
true true
end end
def find_datastore(datastorename, connection) def find_datastore(datastorename, connection, datacentername)
datacenter = connection.serviceInstance.find_datacenter datacenter = connection.serviceInstance.find_datacenter(datacentername)
raise("Datacenter #{datacentername} does not exist") if datacenter.nil?
datacenter.find_datastore(datastorename) datacenter.find_datastore(datastorename)
end end
@ -497,8 +510,15 @@ module Vmpooler
available_unit_numbers.sort[0] available_unit_numbers.sort[0]
end end
def find_folder(foldername, connection) # Finds the first reference to and returns the folder object for a foldername and an optional datacenter
datacenter = connection.serviceInstance.find_datacenter # Params:
# +foldername+:: the folder to find (optionally with / in which case the foldername will be split and each element searched for)
# +connection+:: the vsphere connection object
# +datacentername+:: the datacenter where the folder resides, or nil to return the first datacenter found
# returns a ManagedObjectReference for the first folder found or nil if none found
def find_folder(foldername, connection, datacentername)
datacenter = connection.serviceInstance.find_datacenter(datacentername)
raise("Datacenter #{datacentername} does not exist") if datacenter.nil?
base = datacenter.vmFolder base = datacenter.vmFolder
folders = foldername.split('/') folders = foldername.split('/')
@ -558,16 +578,17 @@ module Vmpooler
(memory_usage.to_f / memory_size.to_f) * 100 (memory_usage.to_f / memory_size.to_f) * 100
end end
def find_least_used_host(cluster, connection) def find_least_used_host(cluster, connection, datacentername)
cluster_object = find_cluster(cluster, connection) cluster_object = find_cluster(cluster, connection, datacentername)
target_hosts = get_cluster_host_utilization(cluster_object) target_hosts = get_cluster_host_utilization(cluster_object)
raise("There is no host candidate in vcenter that meets all the required conditions, check that the cluster has available hosts in a 'green' status, not in maintenance mode and not overloaded CPU and memory'") if target_hosts.empty? raise("There is no host candidate in vcenter that meets all the required conditions, check that the cluster has available hosts in a 'green' status, not in maintenance mode and not overloaded CPU and memory'") if target_hosts.empty?
least_used_host = target_hosts.sort[0][1] least_used_host = target_hosts.sort[0][1]
least_used_host least_used_host
end end
def find_cluster(cluster, connection) def find_cluster(cluster, connection, datacentername)
datacenter = connection.serviceInstance.find_datacenter 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 } datacenter.hostFolder.children.find { |cluster_object| cluster_object.name == cluster }
end end
@ -590,8 +611,9 @@ module Vmpooler
[target_host, target_host.name] [target_host, target_host.name]
end end
def find_pool(poolname, connection) def find_pool(poolname, connection, datacentername)
datacenter = connection.serviceInstance.find_datacenter datacenter = connection.serviceInstance.find_datacenter(datacentername)
raise("Datacenter #{datacentername} does not exist") if datacenter.nil?
base = datacenter.hostFolder base = datacenter.hostFolder
pools = poolname.split('/') pools = poolname.split('/')
pools.each do |pool| pools.each do |pool|
@ -670,10 +692,10 @@ module Vmpooler
vms vms
end end
def find_vmdks(vmname, datastore, connection) def find_vmdks(vmname, datastore, connection, datacentername)
disks = [] disks = []
vmdk_datastore = find_datastore(datastore, connection) vmdk_datastore = find_datastore(datastore, connection,datacentername)
vm_files = connection.serviceContent.propertyCollector.collectMultiple vmdk_datastore.vm, 'layoutEx.file' vm_files = connection.serviceContent.propertyCollector.collectMultiple vmdk_datastore.vm, 'layoutEx.file'
vm_files.keys.each do |f| vm_files.keys.each do |f|

View file

@ -123,6 +123,7 @@ MockServiceInstance = Struct.new(
return child if path.nil? || child.name == path return child if path.nil? || child.name == path
end end
end end
nil
end end
end end

View file

@ -42,6 +42,7 @@ describe 'Vmpooler::PoolManager::Provider::VSphere' do
let(:metrics) { Vmpooler::DummyStatsd.new } let(:metrics) { Vmpooler::DummyStatsd.new }
let(:poolname) { 'pool1'} let(:poolname) { 'pool1'}
let(:provider_options) { { 'param' => 'value' } } let(:provider_options) { { 'param' => 'value' } }
let(:datacenter_name) { 'MockDC' }
let(:config) { YAML.load(<<-EOT let(:config) { YAML.load(<<-EOT
--- ---
:config: :config:
@ -55,6 +56,7 @@ describe 'Vmpooler::PoolManager::Provider::VSphere' do
insecure: true insecure: true
# Drop the connection pool timeout way down for spec tests so they fail fast # Drop the connection pool timeout way down for spec tests so they fail fast
connection_pool_timeout: 1 connection_pool_timeout: 1
datacenter: MockDC
:pools: :pools:
- name: '#{poolname}' - name: '#{poolname}'
alias: [ 'mockpool' ] alias: [ 'mockpool' ]
@ -95,7 +97,7 @@ EOT
context 'Given a pool folder that is missing' do context 'Given a pool folder that is missing' do
before(:each) do before(:each) do
expect(subject).to receive(:find_folder).with(pool_config['folder'],connection).and_return(nil) expect(subject).to receive(:find_folder).with(pool_config['folder'],connection,datacenter_name).and_return(nil)
end end
it 'should get a connection' do it 'should get a connection' do
@ -113,7 +115,7 @@ EOT
context 'Given an empty pool folder' do context 'Given an empty pool folder' do
before(:each) do before(:each) do
expect(subject).to receive(:find_folder).with(pool_config['folder'],connection).and_return(folder_object) expect(subject).to receive(:find_folder).with(pool_config['folder'],connection,datacenter_name).and_return(folder_object)
end end
it 'should get a connection' do it 'should get a connection' do
@ -142,7 +144,7 @@ EOT
folder_object.childEntity << mock_vm folder_object.childEntity << mock_vm
end end
expect(subject).to receive(:find_folder).with(pool_config['folder'],connection).and_return(folder_object) expect(subject).to receive(:find_folder).with(pool_config['folder'],connection,datacenter_name).and_return(folder_object)
end end
it 'should get a connection' do it 'should get a connection' do
@ -324,7 +326,7 @@ EOT
before(:each) do before(:each) do
config[:pools][0]['clone_target'] = cluster_name config[:pools][0]['clone_target'] = cluster_name
expect(subject).to receive(:find_cluster).with(cluster_name,connection).and_return(nil) expect(subject).to receive(:find_cluster).with(cluster_name,connection,datacenter_name).and_return(nil)
end end
it 'should raise an error' do it 'should raise an error' do
@ -338,7 +340,7 @@ EOT
before(:each) do before(:each) do
config[:pools][0]['clone_target'] = nil config[:pools][0]['clone_target'] = nil
config[:config]['clone_target'] = cluster_name config[:config]['clone_target'] = cluster_name
expect(subject).to receive(:find_cluster).with(cluster_name,connection).and_return(nil) expect(subject).to receive(:find_cluster).with(cluster_name,connection,datacenter_name).and_return(nil)
end end
it 'should raise an error' do it 'should raise an error' do
@ -352,7 +354,7 @@ EOT
mock_cluster = mock_RbVmomi_VIM_ComputeResource({ mock_cluster = mock_RbVmomi_VIM_ComputeResource({
:hosts => [ { :name => 'HOST001' },{ :name => dest_host_name} ] :hosts => [ { :name => 'HOST001' },{ :name => dest_host_name} ]
}) })
expect(subject).to receive(:find_cluster).with(cluster_name,connection).and_return(mock_cluster) expect(subject).to receive(:find_cluster).with(cluster_name,connection,datacenter_name).and_return(mock_cluster)
expect(subject).to receive(:migrate_vm_host).exactly(0).times expect(subject).to receive(:migrate_vm_host).exactly(0).times
end end
@ -367,7 +369,7 @@ EOT
mock_cluster = mock_RbVmomi_VIM_ComputeResource({ mock_cluster = mock_RbVmomi_VIM_ComputeResource({
:hosts => [ { :name => 'HOST001' },{ :name => dest_host_name} ] :hosts => [ { :name => 'HOST001' },{ :name => dest_host_name} ]
}) })
expect(subject).to receive(:find_cluster).with(cluster_name,connection).and_return(mock_cluster) expect(subject).to receive(:find_cluster).with(cluster_name,connection,datacenter_name).and_return(mock_cluster)
expect(subject).to receive(:migrate_vm_host).with(Object,Object).and_raise(RuntimeError,'MockMigrationError') expect(subject).to receive(:migrate_vm_host).with(Object,Object).and_raise(RuntimeError,'MockMigrationError')
end end
@ -382,7 +384,7 @@ EOT
mock_cluster = mock_RbVmomi_VIM_ComputeResource({ mock_cluster = mock_RbVmomi_VIM_ComputeResource({
:hosts => [ { :name => 'HOST001' },{ :name => dest_host_name} ] :hosts => [ { :name => 'HOST001' },{ :name => dest_host_name} ]
}) })
expect(subject).to receive(:find_cluster).with(cluster_name,connection).and_return(mock_cluster) expect(subject).to receive(:find_cluster).with(cluster_name,connection,datacenter_name).and_return(mock_cluster)
expect(subject).to receive(:migrate_vm_host).with(Object,Object).and_return(nil) expect(subject).to receive(:migrate_vm_host).with(Object,Object).and_return(nil)
end end
@ -558,7 +560,7 @@ EOT
context 'Given a successful creation' do context 'Given a successful creation' do
before(:each) do before(:each) do
template_vm = subject.find_folder('Templates',connection).find('pool1') template_vm = subject.find_folder('Templates',connection,datacenter_name).find('pool1')
allow(template_vm).to receive(:CloneVM_Task).and_return(clone_vm_task) allow(template_vm).to receive(:CloneVM_Task).and_return(clone_vm_task)
allow(clone_vm_task).to receive(:wait_for_completion).and_return(new_vm_object) allow(clone_vm_task).to receive(:wait_for_completion).and_return(new_vm_object)
end end
@ -570,7 +572,7 @@ EOT
end end
it 'should use the appropriate Create_VM spec' do it 'should use the appropriate Create_VM spec' do
template_vm = subject.find_folder('Templates',connection).find('pool1') template_vm = subject.find_folder('Templates',connection,datacenter_name).find('pool1')
expect(template_vm).to receive(:CloneVM_Task) expect(template_vm).to receive(:CloneVM_Task)
.with(create_vm_spec(vmname,'pool1','datastore0')) .with(create_vm_spec(vmname,'pool1','datastore0'))
.and_return(clone_vm_task) .and_return(clone_vm_task)
@ -633,7 +635,7 @@ EOT
context 'when adding the disk succeeds' do context 'when adding the disk succeeds' do
before(:each) do before(:each) do
expect(subject).to receive(:add_disk).with(vm_object, disk_size, datastorename, connection) expect(subject).to receive(:add_disk).with(vm_object, disk_size, datastorename, connection, datacenter_name)
end end
it 'should return true' do it 'should return true' do
@ -881,6 +883,92 @@ EOT
end end
end end
# vSphere helper methods
describe '#get_target_datacenter_from_config' do
let(:pool_dc) { 'PoolDC'}
let(:provider_dc) { 'ProvDC'}
context 'when not specified' do
let(:config) { YAML.load(<<-EOT
---
:config:
:providers:
:vsphere:
server: "vcenter.domain.local"
username: "vcenter_user"
password: "vcenter_password"
:pools:
- name: '#{poolname}'
EOT
)
}
it 'returns nil' do
expect(subject.get_target_datacenter_from_config(poolname)).to be_nil
end
end
context 'when specified only in the pool' do
let(:config) { YAML.load(<<-EOT
---
:config:
:providers:
:vsphere:
server: "vcenter.domain.local"
username: "vcenter_user"
password: "vcenter_password"
:pools:
- name: '#{poolname}'
datacenter: '#{pool_dc}'
EOT
)
}
it 'returns the pool datacenter' do
expect(subject.get_target_datacenter_from_config(poolname)).to eq(pool_dc)
end
end
context 'when specified only in the provider' do
let(:config) { YAML.load(<<-EOT
---
:config:
:providers:
:vsphere:
server: "vcenter.domain.local"
username: "vcenter_user"
password: "vcenter_password"
datacenter: '#{provider_dc}'
:pools:
- name: '#{poolname}'
EOT
)
}
it 'returns the provider datacenter' do
expect(subject.get_target_datacenter_from_config(poolname)).to eq(provider_dc)
end
end
context 'when specified in the provider and pool' do
let(:config) { YAML.load(<<-EOT
---
:config:
:providers:
:vsphere:
server: "vcenter.domain.local"
username: "vcenter_user"
password: "vcenter_password"
datacenter: '#{provider_dc}'
:pools:
- name: '#{poolname}'
datacenter: '#{pool_dc}'
EOT
)
}
it 'returns the pool datacenter' do
expect(subject.get_target_datacenter_from_config(poolname)).to eq(pool_dc)
end
end
end
# vSphere helper methods # vSphere helper methods
describe '#ensured_vsphere_connection' do describe '#ensured_vsphere_connection' do
let(:config) { YAML.load(<<-EOT let(:config) { YAML.load(<<-EOT
@ -1227,7 +1315,7 @@ EOT
let(:connection_options) {{ let(:connection_options) {{
:serviceContent => { :serviceContent => {
:datacenters => [ :datacenters => [
{ :name => 'MockDC', :datastores => [datastorename] } { :name => datacenter_name, :datastores => [datastorename] }
] ]
} }
}} }}
@ -1240,7 +1328,11 @@ EOT
allow(connection.serviceContent.propertyCollector).to receive(:collectMultiple).and_return(collectMultiple_response) allow(connection.serviceContent.propertyCollector).to receive(:collectMultiple).and_return(collectMultiple_response)
# Mocking for creating the disk # Mocking for creating the disk
allow(connection.serviceContent.virtualDiskManager).to receive(:CreateVirtualDisk_Task).and_return(create_virtual_disk_task) allow(connection.serviceContent.virtualDiskManager).to receive(:CreateVirtualDisk_Task) do |options|
if options[:datacenter][:name] == datacenter_name
create_virtual_disk_task
end
end
allow(create_virtual_disk_task).to receive(:wait_for_completion).and_return(true) allow(create_virtual_disk_task).to receive(:wait_for_completion).and_return(true)
# Mocking for adding disk to the VM # Mocking for adding disk to the VM
@ -1250,7 +1342,7 @@ EOT
context 'Succesfully addding disk' do context 'Succesfully addding disk' do
it 'should return true' do it 'should return true' do
expect(subject.add_disk(vm_object,disk_size,datastorename,connection)).to be true expect(subject.add_disk(vm_object,disk_size,datastorename,connection,datacenter_name)).to be true
end end
it 'should request a disk of appropriate size' do it 'should request a disk of appropriate size' do
@ -1259,13 +1351,13 @@ EOT
.and_return(create_virtual_disk_task) .and_return(create_virtual_disk_task)
subject.add_disk(vm_object,disk_size,datastorename,connection) subject.add_disk(vm_object,disk_size,datastorename,connection,datacenter_name)
end end
end end
context 'Requested disk size is 0' do context 'Requested disk size is 0' do
it 'should raise an error' do it 'should raise an error' do
expect(subject.add_disk(vm_object,0,datastorename,connection)).to be false expect(subject.add_disk(vm_object,0,datastorename,connection,datacenter_name)).to be false
end end
end end
@ -1273,13 +1365,28 @@ EOT
let(:connection_options) {{ let(:connection_options) {{
:serviceContent => { :serviceContent => {
:datacenters => [ :datacenters => [
{ :name => 'MockDC', :datastores => ['missing_datastore'] } { :name => datacenter_name, :datastores => ['missing_datastore'] }
] ]
} }
}} }}
it 'should return false' do it 'should raise error' do
expect{ subject.add_disk(vm_object,disk_size,datastorename,connection) }.to raise_error(NoMethodError) expect{ subject.add_disk(vm_object,disk_size,datastorename,connection,datacenter_name) }.to raise_error(/does not exist/)
end
end
context 'Multiple datacenters with multiple datastores' do
let(:connection_options) {{
:serviceContent => {
:datacenters => [
{ :name => 'AnotherDC', :datastores => ['dc1','dc2'] },
{ :name => datacenter_name, :datastores => ['dc3',datastorename,'dc4'] },
]
}
}}
it 'should return true' do
expect(subject.add_disk(vm_object,disk_size,datastorename,connection,datacenter_name)).to be true
end end
end end
@ -1293,7 +1400,7 @@ EOT
} }
it 'should raise an error' do it 'should raise an error' do
expect{ subject.add_disk(vm_object,disk_size,datastorename,connection) }.to raise_error(NoMethodError) expect{ subject.add_disk(vm_object,disk_size,datastorename,connection,datacenter_name) }.to raise_error(NoMethodError)
end end
end end
end end
@ -1306,13 +1413,13 @@ EOT
let(:connection_options) {{ let(:connection_options) {{
:serviceContent => { :serviceContent => {
:datacenters => [ :datacenters => [
{ :name => 'MockDC', :datastores => [] } { :name => datacenter_name, :datastores => [] }
] ]
} }
}} }}
it 'should return nil if the datastore is not found' do it 'should return nil if the datastore is not found' do
result = subject.find_datastore(datastorename,connection) result = subject.find_datastore(datastorename,connection,datacenter_name)
expect(result).to be_nil expect(result).to be_nil
end end
end end
@ -1321,18 +1428,42 @@ EOT
let(:connection_options) {{ let(:connection_options) {{
:serviceContent => { :serviceContent => {
:datacenters => [ :datacenters => [
{ :name => 'MockDC', :datastores => ['ds1','ds2',datastorename,'ds3'] } { :name => datacenter_name, :datastores => ['ds1','ds2',datastorename,'ds3'] }
] ]
} }
}} }}
it 'should return nil if the datastore is not found' do it 'should return nil if the datastore is not found' do
result = subject.find_datastore('missing_datastore',connection) result = subject.find_datastore('missing_datastore',connection,datacenter_name)
expect(result).to be_nil expect(result).to be_nil
end end
it 'should find the datastore in the datacenter' do it 'should find the datastore in the datacenter' do
result = subject.find_datastore(datastorename,connection) result = subject.find_datastore(datastorename,connection,datacenter_name)
expect(result).to_not be_nil
expect(result.is_a?(RbVmomi::VIM::Datastore)).to be true
expect(result.name).to eq(datastorename)
end
end
context 'Many datastores in many datacenters' do
let(:connection_options) {{
:serviceContent => {
:datacenters => [
{ :name => 'AnotherDC', :datastores => ['ds1','ds2','ds3'] },
{ :name => datacenter_name, :datastores => ['ds3','ds4',datastorename,'ds5'] },
]
}
}}
it 'should return nil if the datastore is not found' do
result = subject.find_datastore(datastorename,connection,'AnotherDC')
expect(result).to be_nil
end
it 'should find the datastore in the datacenter' do
result = subject.find_datastore(datastorename,connection,datacenter_name)
expect(result).to_not be_nil expect(result).to_not be_nil
expect(result.is_a?(RbVmomi::VIM::Datastore)).to be true expect(result.is_a?(RbVmomi::VIM::Datastore)).to be true
@ -1558,54 +1689,102 @@ EOT
let(:foldername) { 'folder'} let(:foldername) { 'folder'}
let(:missing_foldername) { 'missing_folder'} let(:missing_foldername) { 'missing_folder'}
before(:each) do
allow(connection.serviceInstance).to receive(:find_datacenter).and_return(datacenter_object)
end
context 'with no folder hierarchy' do context 'with no folder hierarchy' do
let(:datacenter_object) { mock_RbVmomi_VIM_Datacenter() } let(:connection_options) {{
:serviceContent => {
:datacenters => [
{ :name => datacenter_name }
]
}
}}
it 'should return nil if the folder is not found' do it 'should return nil if the folder is not found' do
expect(subject.find_folder(missing_foldername,connection)).to be_nil expect(subject.find_folder(missing_foldername,connection,datacenter_name)).to be_nil
end end
end end
context 'with a single layer folder hierarchy' do context 'with a single layer folder hierarchy' do
let(:datacenter_object) { mock_RbVmomi_VIM_Datacenter({ let(:connection_options) {{
:vmfolder_tree => { :serviceContent => {
'folder1' => nil, :datacenters => [
'folder2' => nil, { :name => datacenter_name,
foldername => nil, :vmfolder_tree => {
'folder3' => nil, 'folder1' => nil,
'folder2' => nil,
foldername => nil,
'folder3' => nil,
}
}
]
} }
}) } }}
it 'should return the folder when found' do it 'should return the folder when found' do
result = subject.find_folder(foldername,connection) result = subject.find_folder(foldername,connection,datacenter_name)
expect(result).to_not be_nil expect(result).to_not be_nil
expect(result.name).to eq(foldername) expect(result.name).to eq(foldername)
end end
it 'should return nil if the folder is not found' do it 'should return nil if the folder is not found' do
expect(subject.find_folder(missing_foldername,connection)).to be_nil expect(subject.find_folder(missing_foldername,connection,datacenter_name)).to be_nil
end
end
context 'with a single layer folder hierarchy in many datacenters' do
let(:connection_options) {{
:serviceContent => {
:datacenters => [
{ :name => 'AnotherDC',
:vmfolder_tree => {
'folder1' => nil,
'folder2' => nil,
'folder3' => nil,
}
},
{ :name => datacenter_name,
:vmfolder_tree => {
'folder4' => nil,
'folder5' => nil,
foldername => nil,
'folder6' => nil,
}
}
]
}
}}
it 'should return the folder when found' do
result = subject.find_folder(foldername,connection,datacenter_name)
expect(result).to_not be_nil
expect(result.name).to eq(foldername)
end
it 'should return nil if the folder is not found' do
expect(subject.find_folder(missing_foldername,connection,'AnotherDC')).to be_nil
end end
end end
context 'with a VM with the same name as a folder in a single layer folder hierarchy' do context 'with a VM with the same name as a folder in a single layer folder hierarchy' do
# The folder hierarchy should include a VM with same name as folder, and appear BEFORE the # The folder hierarchy should include a VM with same name as folder, and appear BEFORE the
# folder in the child list. # folder in the child list.
let(:datacenter_object) { mock_RbVmomi_VIM_Datacenter({ let(:connection_options) {{
:vmfolder_tree => { :serviceContent => {
'folder1' => nil, :datacenters => [
'vm1' => { :object_type => 'vm', :name => foldername }, { :name => datacenter_name,
foldername => nil, :vmfolder_tree => {
'folder3' => nil, 'folder1' => nil,
'vm1' => { :object_type => 'vm', :name => foldername },
foldername => nil,
'folder3' => nil,
}
}
]
} }
}) } }}
it 'should not return a VM' do it 'should not return a VM' do
pending('https://github.com/puppetlabs/vmpooler/issues/204') pending('https://github.com/puppetlabs/vmpooler/issues/204')
result = subject.find_folder(foldername,connection) result = subject.find_folder(foldername,connection,datacenter_name)
expect(result).to_not be_nil expect(result).to_not be_nil
expect(result.name).to eq(foldername) expect(result.name).to eq(foldername)
expect(result.is_a? RbVmomi::VIM::VirtualMachine).to be false expect(result.is_a? RbVmomi::VIM::VirtualMachine).to be false
@ -1615,31 +1794,37 @@ EOT
context 'with a multi layer folder hierarchy' do context 'with a multi layer folder hierarchy' do
let(:end_folder_name) { 'folder'} let(:end_folder_name) { 'folder'}
let(:foldername) { 'folder2/folder4/' + end_folder_name} let(:foldername) { 'folder2/folder4/' + end_folder_name}
let(:datacenter_object) { mock_RbVmomi_VIM_Datacenter({ let(:connection_options) {{
:vmfolder_tree => { :serviceContent => {
'folder1' => nil, :datacenters => [
'folder2' => { { :name => datacenter_name,
:children => { :vmfolder_tree => {
'folder3' => nil, 'folder1' => nil,
'folder4' => { 'folder2' => {
:children => { :children => {
end_folder_name => nil, 'folder3' => nil,
'folder4' => {
:children => {
end_folder_name => nil,
},
}
},
}, },
'folder5' => nil,
} }
}, }
}, ]
'folder5' => nil,
} }
}) } }}
it 'should return the folder when found' do it 'should return the folder when found' do
result = subject.find_folder(foldername,connection) result = subject.find_folder(foldername,connection,datacenter_name)
expect(result).to_not be_nil expect(result).to_not be_nil
expect(result.name).to eq(end_folder_name) expect(result.name).to eq(end_folder_name)
end end
it 'should return nil if the folder is not found' do it 'should return nil if the folder is not found' do
expect(subject.find_folder(missing_foldername,connection)).to be_nil expect(subject.find_folder(missing_foldername,connection,datacenter_name)).to be_nil
end end
end end
@ -1648,30 +1833,36 @@ EOT
# and appear BEFORE the folder in the child list. # and appear BEFORE the folder in the child list.
let(:end_folder_name) { 'folder'} let(:end_folder_name) { 'folder'}
let(:foldername) { 'folder2/folder4/' + end_folder_name} let(:foldername) { 'folder2/folder4/' + end_folder_name}
let(:datacenter_object) { mock_RbVmomi_VIM_Datacenter({ let(:connection_options) {{
:vmfolder_tree => { :serviceContent => {
'folder1' => nil, :datacenters => [
'folder2' => { { :name => datacenter_name,
:children => { :vmfolder_tree => {
'folder3' => nil, 'folder1' => nil,
'vm1' => { :object_type => 'vm', :name => 'folder4' }, 'folder2' => {
'folder4' => { :children => {
:children => { 'folder3' => nil,
end_folder_name => nil, 'vm1' => { :object_type => 'vm', :name => 'folder4' },
'folder4' => {
:children => {
end_folder_name => nil,
},
}
},
}, },
'folder5' => nil,
} }
}, }
}, ]
'folder5' => nil,
} }
}) } }}
it 'should not return a VM' do it 'should not return a VM' do
pending('https://github.com/puppetlabs/vmpooler/issues/204') pending('https://github.com/puppetlabs/vmpooler/issues/204')
result = subject.find_folder(foldername,connection) result = subject.find_folder(foldername,connection,datacenter_name)
expect(result).to_not be_nil expect(result).to_not be_nil
expect(result.name).to eq(foldername) expect(result.name).to eq(foldername)
expect(result.is_a? RbVmomi::VIM::VirtualMachine).to be false expect(result.is_a? RbVmomi::VIM::VirtualMachine,datacenter_name).to be false
end end
end end
end end
@ -1920,9 +2111,9 @@ EOT
:name => cluster_name, :name => cluster_name,
}]})} }]})}
let(:expected_host) { cluster_object.host[0] } let(:expected_host) { cluster_object.host[0] }
#,datacenter_name
it 'should raise an error' do it 'should raise an error' do
expect{subject.find_least_used_host(missing_cluster_name,connection)}.to raise_error(NoMethodError,/undefined method/) expect{subject.find_least_used_host(missing_cluster_name,connection,datacenter_name)}.to raise_error(NoMethodError,/undefined method/)
end end
end end
@ -1935,7 +2126,7 @@ EOT
let(:expected_host) { cluster_object.host[0] } let(:expected_host) { cluster_object.host[0] }
it 'should return the standalone host' do it 'should return the standalone host' do
result = subject.find_least_used_host(cluster_name,connection) result = subject.find_least_used_host(cluster_name,connection,datacenter_name)
expect(result).to be(expected_host) expect(result).to be(expected_host)
end end
@ -1951,7 +2142,7 @@ EOT
let(:expected_host) { cluster_object.host[0] } let(:expected_host) { cluster_object.host[0] }
it 'should raise an error' do it 'should raise an error' do
expect{subject.find_least_used_host(missing_cluster_name,connection)}.to raise_error(NoMethodError,/undefined method/) expect{subject.find_least_used_host(missing_cluster_name,connection,datacenter_name)}.to raise_error(NoMethodError,/undefined method/)
end end
end end
@ -1966,7 +2157,7 @@ EOT
let(:expected_host) { cluster_object.host[1] } let(:expected_host) { cluster_object.host[1] }
it 'should return the standalone host' do it 'should return the standalone host' do
result = subject.find_least_used_host(cluster_name,connection) result = subject.find_least_used_host(cluster_name,connection,datacenter_name)
expect(result).to be(expected_host) expect(result).to be(expected_host)
end end
@ -1983,7 +2174,7 @@ EOT
let(:expected_host) { cluster_object.host[1] } let(:expected_host) { cluster_object.host[1] }
it 'should raise an error' do it 'should raise an error' do
expect{subject.find_least_used_host(missing_cluster_name,connection)}.to raise_error(NoMethodError,/undefined method/) expect{subject.find_least_used_host(missing_cluster_name,connection,datacenter_name)}.to raise_error(NoMethodError,/undefined method/)
end end
end end
@ -2000,7 +2191,7 @@ EOT
let(:expected_host) { cluster_object.host[1] } let(:expected_host) { cluster_object.host[1] }
it 'should return the standalone host' do it 'should return the standalone host' do
result = subject.find_least_used_host(cluster_name,connection) result = subject.find_least_used_host(cluster_name,connection,datacenter_name)
expect(result).to be(expected_host) expect(result).to be(expected_host)
end end
@ -2018,7 +2209,7 @@ EOT
it 'should return a host' do it 'should return a host' do
pending('https://github.com/puppetlabs/vmpooler/issues/206') pending('https://github.com/puppetlabs/vmpooler/issues/206')
result = subject.find_least_used_host(missing_cluster_name,connection) result = subject.find_least_used_host(missing_cluster_name,connection,datacenter_name)
expect(result).to_not be_nil expect(result).to_not be_nil
end end
end end
@ -2028,66 +2219,109 @@ EOT
let(:cluster) {'cluster'} let(:cluster) {'cluster'}
let(:missing_cluster) {'missing_cluster'} let(:missing_cluster) {'missing_cluster'}
before(:each) do
allow(connection.serviceInstance).to receive(:find_datacenter).and_return(datacenter_object)
end
context 'no clusters in the datacenter' do context 'no clusters in the datacenter' do
let(:datacenter_object) { mock_RbVmomi_VIM_Datacenter() } let(:connection_options) {{
:serviceContent => {
before(:each) do :datacenters => [
end { :name => datacenter_name }
]
}
}}
it 'should return nil if the cluster is not found' do it 'should return nil if the cluster is not found' do
expect(subject.find_cluster(missing_cluster,connection)).to be_nil expect(subject.find_cluster(missing_cluster,connection,datacenter_name)).to be_nil
end end
end end
context 'with a single layer folder hierarchy' do context 'with a single layer folder hierarchy' do
let(:datacenter_object) { mock_RbVmomi_VIM_Datacenter({ let(:connection_options) {{
:hostfolder_tree => { :serviceContent => {
'cluster1' => {:object_type => 'compute_resource'}, :datacenters => [
'cluster2' => {:object_type => 'compute_resource'}, { :name => datacenter_name,
cluster => {:object_type => 'compute_resource'}, :hostfolder_tree => {
'cluster3' => {:object_type => 'compute_resource'}, 'cluster1' => {:object_type => 'compute_resource'},
'cluster2' => {:object_type => 'compute_resource'},
cluster => {:object_type => 'compute_resource'},
'cluster3' => {:object_type => 'compute_resource'},
}
}
]
} }
}) } }}
it 'should return the cluster when found' do it 'should return the cluster when found' do
result = subject.find_cluster(cluster,connection) result = subject.find_cluster(cluster,connection,datacenter_name)
expect(result).to_not be_nil expect(result).to_not be_nil
expect(result.name).to eq(cluster) expect(result.name).to eq(cluster)
end end
it 'should return nil if the cluster is not found' do it 'should return nil if the cluster is not found' do
expect(subject.find_cluster(missing_cluster,connection)).to be_nil expect(subject.find_cluster(missing_cluster,connection,datacenter_name)).to be_nil
end
end
context 'with a single layer folder hierarchy with multiple datacenters' do
let(:connection_options) {{
:serviceContent => {
:datacenters => [
{ :name => 'AnotherDC',
:hostfolder_tree => {
'cluster1' => {:object_type => 'compute_resource'},
'cluster2' => {:object_type => 'compute_resource'},
}
},
{ :name => datacenter_name,
:hostfolder_tree => {
cluster => {:object_type => 'compute_resource'},
'cluster3' => {:object_type => 'compute_resource'},
}
}
]
}
}}
it 'should return the cluster when found' do
result = subject.find_cluster(cluster,connection,datacenter_name)
expect(result).to_not be_nil
expect(result.name).to eq(cluster)
end
it 'should return nil if the cluster is not found' do
expect(subject.find_cluster(missing_cluster,connection,'AnotherDC')).to be_nil
end end
end end
context 'with a multi layer folder hierarchy' do context 'with a multi layer folder hierarchy' do
let(:datacenter_object) { mock_RbVmomi_VIM_Datacenter({ let(:connection_options) {{
:hostfolder_tree => { :serviceContent => {
'cluster1' => {:object_type => 'compute_resource'}, :datacenters => [
'folder2' => { { :name => datacenter_name,
:children => { :hostfolder_tree => {
cluster => {:object_type => 'compute_resource'}, 'cluster1' => {:object_type => 'compute_resource'},
'folder2' => {
:children => {
cluster => {:object_type => 'compute_resource'},
}
},
'cluster3' => {:object_type => 'compute_resource'},
}
} }
}, ]
'cluster3' => {:object_type => 'compute_resource'},
} }
}) } }}
it 'should return the cluster when found' do it 'should return the cluster when found' do
pending('https://github.com/puppetlabs/vmpooler/issues/205') pending('https://github.com/puppetlabs/vmpooler/issues/205')
result = subject.find_cluster(cluster,connection) result = subject.find_cluster(cluster,connection,datacenter_name)
expect(result).to_not be_nil expect(result).to_not be_nil
expect(result.name).to eq(cluster) expect(result.name).to eq(cluster)
end end
it 'should return nil if the cluster is not found' do it 'should return nil if the cluster is not found' do
expect(subject.find_cluster(missing_cluster,connection)).to be_nil expect(subject.find_cluster(missing_cluster,connection,datacenter_name)).to be_nil
end end
end end
end end
@ -2287,23 +2521,56 @@ EOT
let(:poolname) { 'pool'} let(:poolname) { 'pool'}
let(:missing_poolname) { 'missing_pool'} let(:missing_poolname) { 'missing_pool'}
before(:each) do
allow(connection.serviceInstance).to receive(:find_datacenter).and_return(datacenter_object)
end
context 'with empty folder hierarchy' do context 'with empty folder hierarchy' do
let(:datacenter_object) { mock_RbVmomi_VIM_Datacenter() } let(:connection_options) {{
:serviceContent => {
:datacenters => [
{ :name => datacenter_name }
]
}
}}
it 'should ensure the connection' do it 'should ensure the connection' do
pending('https://github.com/puppetlabs/vmpooler/issues/209') pending('https://github.com/puppetlabs/vmpooler/issues/209')
expect(subject).to receive(:ensure_connected) expect(subject).to receive(:ensure_connected)
subject.find_pool(poolname,connection) subject.find_pool(poolname,connection,datacenter_name)
end end
it 'should return nil if the pool is not found' do it 'should return nil if the pool is not found' do
pending('https://github.com/puppetlabs/vmpooler/issues/209') pending('https://github.com/puppetlabs/vmpooler/issues/209')
expect(subject.find_pool(missing_poolname,connection)).to be_nil expect(subject.find_pool(missing_poolname,connection,datacenter_name)).to be_nil
end
end
context 'with multiple datacenters' do
let(:poolpath) { 'pool' }
let(:connection_options) {{
:serviceContent => {
:datacenters => [
{ :name => 'AnotherDC',
:hostfolder_tree => {
'folder1' => nil,
'folder2' => nil,
},
},
{ :name => datacenter_name,
:hostfolder_tree => {
'folder3' => nil,
'pool' => {:object_type => 'resource_pool'},
'folder4' => nil,
},
}
]
}
}}
it 'should return the pool when found' do
result = subject.find_pool(poolpath,connection, datacenter_name)
expect(result).to_not be_nil
expect(result.name).to eq('pool')
expect(result.is_a?(RbVmomi::VIM::ResourcePool)).to be true
end end
end end
@ -2398,10 +2665,18 @@ EOT
}, },
].each do |testcase| ].each do |testcase|
context testcase[:context] do context testcase[:context] do
let(:datacenter_object) { mock_RbVmomi_VIM_Datacenter({ :hostfolder_tree => testcase[:hostfolder_tree]}) } let(:connection_options) {{
:serviceContent => {
:datacenters => [
{ :name => datacenter_name,
:hostfolder_tree => testcase[:hostfolder_tree],
}
]
}
}}
it 'should return the pool when found' do it 'should return the pool when found' do
result = subject.find_pool(testcase[:poolpath],connection) result = subject.find_pool(testcase[:poolpath],connection,datacenter_name)
expect(result).to_not be_nil expect(result).to_not be_nil
expect(result.name).to eq(testcase[:poolname]) expect(result.name).to eq(testcase[:poolname])
@ -2410,7 +2685,7 @@ EOT
it 'should return nil if the poolname is not found' do it 'should return nil if the poolname is not found' do
pending('https://github.com/puppetlabs/vmpooler/issues/209') pending('https://github.com/puppetlabs/vmpooler/issues/209')
expect(subject.find_pool(missing_poolname,connection)).to be_nil expect(subject.find_pool(missing_poolname,connection,datacenter_name)).to be_nil
end end
end end
end end
@ -2455,18 +2730,26 @@ EOT
}, },
].each do |testcase| ].each do |testcase|
context testcase[:context] do context testcase[:context] do
let(:datacenter_object) { mock_RbVmomi_VIM_Datacenter({ :hostfolder_tree => testcase[:hostfolder_tree]}) } let(:connection_options) {{
:serviceContent => {
:datacenters => [
{ :name => datacenter_name,
:hostfolder_tree => testcase[:hostfolder_tree],
}
]
}
}}
it 'should ensure the connection' do it 'should ensure the connection' do
pending('https://github.com/puppetlabs/vmpooler/issues/210') pending('https://github.com/puppetlabs/vmpooler/issues/210')
expect(subject).to receive(:ensure_connected) expect(subject).to receive(:ensure_connected)
subject.find_pool(testcase[:poolpath]) subject.find_pool(testcase[:poolpath],connection,datacenter_name)
end end
it 'should return the pool when found' do it 'should return the pool when found' do
pending('https://github.com/puppetlabs/vmpooler/issues/210') pending('https://github.com/puppetlabs/vmpooler/issues/210')
result = subject.find_pool(testcase[:poolpath]) result = subject.find_pool(testcase[:poolpath],connection,datacenter_name)
expect(result).to_not be_nil expect(result).to_not be_nil
expect(result.name).to eq(testcase[:poolname]) expect(result.name).to eq(testcase[:poolname])
@ -2688,7 +2971,7 @@ EOT
let(:connection_options) {{ let(:connection_options) {{
:serviceContent => { :serviceContent => {
:datacenters => [ :datacenters => [
{ :name => 'MockDC', :datastores => [datastorename] } { :name => datacenter_name, :datastores => [datastorename] }
] ]
} }
}} }}
@ -2719,11 +3002,11 @@ EOT
} } } }
it 'should return empty array if no VMDKs match the VM name' do it 'should return empty array if no VMDKs match the VM name' do
expect(subject.find_vmdks('missing_vm_name',datastorename,connection)).to eq([]) expect(subject.find_vmdks('missing_vm_name',datastorename,connection,datacenter_name)).to eq([])
end end
it 'should return matching VMDKs for the VM' do it 'should return matching VMDKs for the VM' do
result = subject.find_vmdks(vmname,datastorename,connection) result = subject.find_vmdks(vmname,datastorename,connection,datacenter_name)
expect(result).to_not be_nil expect(result).to_not be_nil
expect(result.count).to eq(2) expect(result.count).to eq(2)
# The keys for each VMDK should be less that 100 as per the mocks # The keys for each VMDK should be less that 100 as per the mocks

View file

@ -6,6 +6,27 @@
# The currently supported backing services are: # The currently supported backing services are:
# - vsphere # - vsphere
# - dummy # - dummy
#
# - provider_class
# For multiple providers, specify one of the supported backing services (vsphere or dummy)
# (optional: will default to it's parent :key: name eg. 'vsphere')
#
# If you want to support more than one provider with different parameters (server, username or passwords) you have to specify the
# backing service in the provider_class configuration parameter for example 'vsphere' or 'dummy'. Each pool can specify
# the provider to use.
#
# Multiple providers example:
:vsphere-pdx:
server: 'vsphere.pdx.company.com'
username: 'vmpooler-pdx'
password: 'swimsw1msw!m'
provider_class: 'vsphere'
:vsphere-bfs:
server: 'vsphere.bfs.company.com'
username: 'vmpooler-bfs'
password: 'swimsw1msw!m'
provider_class: 'vsphere'
# :vsphere: # :vsphere:
# #
@ -33,9 +54,9 @@
# Whether to ignore any HTTPS negotiation errors (e.g. untrusted self-signed certificates) # Whether to ignore any HTTPS negotiation errors (e.g. untrusted self-signed certificates)
# (optional: default true) # (optional: default true)
# #
# - provider_class # - datacenter
# For multiple providers, specify one of the supported backing services (vsphere or dummy) # The datacenter within vCenter to manage VMs. This can be overridden in the pool configuration
# (optional: will default to it's parent :key: name eg. 'vsphere') # (optional: default is the first datacenter in vSphere)
# #
# Example: # Example:
@ -44,23 +65,6 @@
username: 'vmpooler' username: 'vmpooler'
password: 'swimsw1msw!m' password: 'swimsw1msw!m'
# If you want to support more than one provider with different parameters (server, username or passwords) you have to specify the
# backing service in the provider_class configuration parameter for example 'vsphere' or 'dummy'. Each pool can specify
# the provider to use.
#
# Multiple providers example:
:vsphere-pdx:
server: 'vsphere.pdx.company.com'
username: 'vmpooler-pdx'
password: 'swimsw1msw!m'
provider_class: 'vsphere'
:vsphere-bfs:
server: 'vsphere.bfs.company.com'
username: 'vmpooler-bfs'
password: 'swimsw1msw!m'
provider_class: 'vsphere'
# :dummy: # :dummy:
# #
# The dummy backing service is a simple text file service that can be used # The dummy backing service is a simple text file service that can be used
@ -465,6 +469,10 @@
# The vSphere 'datastore' destination for spawned clones. # The vSphere 'datastore' destination for spawned clones.
# (required) # (required)
# #
# - datacenter
# The datacenter within vCenter to manage VMs.
# (optional: default is the first datacenter in vSphere)
#
# Example: # Example:
:pools: :pools: