mirror of
https://github.com/puppetlabs/vmpooler.git
synced 2026-01-26 10:08:40 -05:00
Merge pull request #382 from puppetlabs/pooler-167
(POOLER-167) Allow for network configuration at vm clone time
This commit is contained in:
commit
a2a3bb7dfd
3 changed files with 262 additions and 29 deletions
|
|
@ -298,7 +298,6 @@ module Vmpooler
|
|||
template_path = pool['template']
|
||||
target_folder_path = pool['folder']
|
||||
target_datastore = pool['datastore']
|
||||
target_cluster_name = get_target_cluster_from_config(pool_name)
|
||||
target_datacenter_name = get_target_datacenter_from_config(pool_name)
|
||||
|
||||
# Get the template VM object
|
||||
|
|
@ -320,31 +319,19 @@ module Vmpooler
|
|||
]
|
||||
)
|
||||
|
||||
# Put the VM in the specified folder and resource pool
|
||||
relocate_spec = RbVmomi::VIM.VirtualMachineRelocateSpec(
|
||||
datastore: find_datastore(target_datastore, connection, target_datacenter_name),
|
||||
diskMoveType: get_disk_backing(pool)
|
||||
)
|
||||
|
||||
manage_host_selection = @config[:config]['manage_host_selection'] if @config[:config].key?('manage_host_selection')
|
||||
if manage_host_selection
|
||||
run_select_hosts(pool_name, @provider_hosts)
|
||||
target_host = select_next_host(pool_name, @provider_hosts)
|
||||
host_object = find_host_by_dnsname(connection, target_host)
|
||||
relocate_spec.host = host_object
|
||||
else
|
||||
# Choose a cluster/host to place the new VM on
|
||||
target_cluster_object = find_cluster(target_cluster_name, connection, target_datacenter_name)
|
||||
relocate_spec.pool = target_cluster_object.resourcePool
|
||||
# Check if alternate network configuration is specified and add configuration
|
||||
if pool.key?('network')
|
||||
template_vm_network_device = template_vm_object.config.hardware.device.grep(RbVmomi::VIM::VirtualEthernetCard).first
|
||||
network_name = pool['network']
|
||||
network_device = set_network_device(target_datacenter_name, template_vm_network_device, network_name, connection)
|
||||
config_spec.deviceChange = [{ operation: 'edit', device: network_device }]
|
||||
end
|
||||
|
||||
# Put the VM in the specified folder and resource pool
|
||||
relocate_spec = create_relocate_spec(target_datastore, target_datacenter_name, pool_name, connection)
|
||||
|
||||
# Create a clone spec
|
||||
clone_spec = RbVmomi::VIM.VirtualMachineCloneSpec(
|
||||
location: relocate_spec,
|
||||
config: config_spec,
|
||||
powerOn: true,
|
||||
template: false
|
||||
)
|
||||
clone_spec = create_clone_spec(relocate_spec, config_spec)
|
||||
|
||||
begin
|
||||
vm_target_folder = find_vm_folder(pool_name, connection)
|
||||
|
|
@ -356,7 +343,7 @@ module Vmpooler
|
|||
raise
|
||||
end
|
||||
end
|
||||
raise ArgumentError, "Can not find the configured folder for #{pool_name} #{target_folder_path}" unless vm_target_folder
|
||||
raise ArgumentError, "Cannot find the configured folder for #{pool_name} #{target_folder_path}" unless vm_target_folder
|
||||
|
||||
# Create the new VM
|
||||
new_vm_object = template_vm_object.CloneVM_Task(
|
||||
|
|
@ -370,6 +357,81 @@ module Vmpooler
|
|||
vm_hash
|
||||
end
|
||||
|
||||
def create_relocate_spec(target_datastore, target_datacenter_name, pool_name, connection)
|
||||
pool = pool_config(pool_name)
|
||||
target_cluster_name = get_target_cluster_from_config(pool_name)
|
||||
|
||||
relocate_spec = RbVmomi::VIM.VirtualMachineRelocateSpec(
|
||||
datastore: find_datastore(target_datastore, connection, target_datacenter_name),
|
||||
diskMoveType: get_disk_backing(pool)
|
||||
)
|
||||
manage_host_selection = @config[:config]['manage_host_selection'] if @config[:config].key?('manage_host_selection')
|
||||
if manage_host_selection
|
||||
run_select_hosts(pool_name, @provider_hosts)
|
||||
target_host = select_next_host(pool_name, @provider_hosts)
|
||||
host_object = find_host_by_dnsname(connection, target_host)
|
||||
relocate_spec.host = host_object
|
||||
else
|
||||
# Choose a cluster/host to place the new VM on
|
||||
target_cluster_object = find_cluster(target_cluster_name, connection, target_datacenter_name)
|
||||
relocate_spec.pool = target_cluster_object.resourcePool
|
||||
end
|
||||
relocate_spec
|
||||
end
|
||||
|
||||
def create_clone_spec(relocate_spec, config_spec)
|
||||
RbVmomi::VIM.VirtualMachineCloneSpec(
|
||||
location: relocate_spec,
|
||||
config: config_spec,
|
||||
powerOn: true,
|
||||
template: false
|
||||
)
|
||||
end
|
||||
|
||||
def set_network_device(datacenter_name, template_vm_network_device, network_name, connection)
|
||||
# Retrieve network object
|
||||
datacenter = connection.serviceInstance.find_datacenter(datacenter_name)
|
||||
new_network = datacenter.network.find { |n| n.name == network_name }
|
||||
|
||||
raise("Cannot find network #{network_name} in datacenter #{datacenter_name}") unless new_network
|
||||
|
||||
# Determine network device type
|
||||
# All possible device type options here: https://vdc-download.vmware.com/vmwb-repository/dcr-public/98d63b35-d822-47fe-a87a-ddefd469df06/2e3c7b58-f2bd-486e-8bb1-a75eb0640bee/doc/vim.vm.device.VirtualEthernetCard.html
|
||||
network_device =
|
||||
if template_vm_network_device.is_a? RbVmomi::VIM::VirtualVmxnet2
|
||||
RbVmomi::VIM.VirtualVmxnet2
|
||||
elsif template_vm_network_device.is_a? RbVmomi::VIM::VirtualVmxnet3
|
||||
RbVmomi::VIM.VirtualVmxnet3
|
||||
elsif template_vm_network_device.is_a? RbVmomi::VIM::VirtualE1000
|
||||
RbVmomi::VIM.VirtualE1000
|
||||
elsif template_vm_network_device.is_a? RbVmomi::VIM::VirtualE1000e
|
||||
RbVmomi::VIM.VirtualE1000e
|
||||
elsif template_vm_network_device.is_a? RbVmomi::VIM::VirtualSriovEthernetCard
|
||||
RbVmomi::VIM.VirtualSriovEthernetCard
|
||||
else
|
||||
RbVmomi::VIM.VirtualPCNet32
|
||||
end
|
||||
|
||||
# Set up new network device attributes
|
||||
network_device.key = template_vm_network_device.key
|
||||
network_device.deviceInfo = RbVmomi::VIM.Description(
|
||||
label: template_vm_network_device.deviceInfo.label,
|
||||
summary: network_name
|
||||
)
|
||||
network_device.backing = RbVmomi::VIM.VirtualEthernetCardNetworkBackingInfo(
|
||||
deviceName: network_name,
|
||||
network: new_network,
|
||||
useAutoDetect: false
|
||||
)
|
||||
network_device.addressType = 'assigned'
|
||||
network_device.connectable = RbVmomi::VIM.VirtualDeviceConnectInfo(
|
||||
allowGuestControl: true,
|
||||
startConnected: true,
|
||||
connected: true
|
||||
)
|
||||
network_device
|
||||
end
|
||||
|
||||
def create_disk(pool_name, vm_name, disk_size)
|
||||
pool = pool_config(pool_name)
|
||||
raise("Pool #{pool_name} does not exist for the provider #{name}") if pool.nil?
|
||||
|
|
|
|||
|
|
@ -63,6 +63,20 @@ MockDatacenter = Struct.new(
|
|||
end
|
||||
end
|
||||
|
||||
MockNetwork = Struct.new(
|
||||
# https://www.vmware.com/support/developer/vc-sdk/visdk41pubs/ApiReference/vim.Network.html
|
||||
# From Network
|
||||
:host, :name, :summary, :vm
|
||||
)
|
||||
|
||||
MockVirtualVmxnet3 = Struct.new(
|
||||
# https://www.vmware.com/support/developer/vc-sdk/visdk400pubs/ReferenceGuide/vim.vm.device.VirtualVmxnet.html
|
||||
# From VirtualEthenetCard
|
||||
:addressType,
|
||||
# From VirtualDevice
|
||||
:key, :deviceInfo, :backing, :connectable
|
||||
)
|
||||
|
||||
MockDatastore = Struct.new(
|
||||
# https://www.vmware.com/support/developer/vc-sdk/visdk41pubs/ApiReference/vim.Datastore.html
|
||||
# From Datastore
|
||||
|
|
@ -208,6 +222,33 @@ MockDescription = Struct.new(
|
|||
:label, :summary
|
||||
)
|
||||
|
||||
MockVirtualEthernetCardNetworkBackingInfo = Struct.new(
|
||||
# https://www.vmware.com/support/developer/vc-sdk/visdk400pubs/ReferenceGuide/vim.vm.device.VirtualEthernetCard.NetworkBackingInfo.html
|
||||
# From VirtualEthernetCardNetworkBackingInfo
|
||||
:network,
|
||||
|
||||
# From VirtualDeviceBackingInfo
|
||||
:deviceName, :useAutoDetect
|
||||
)
|
||||
|
||||
MockVirtualDeviceConnectInfo = Struct.new(
|
||||
# https://www.vmware.com/support/developer/vc-sdk/visdk400pubs/ReferenceGuide/vim.vm.device.VirtualDevice.ConnectInfo.html
|
||||
# From VirtualDeviceConnectInfo
|
||||
:allowGuestControl, :connected, :startConnected
|
||||
)
|
||||
|
||||
MockVirtualMachineConfigSpec = Struct.new(
|
||||
# https://pubs.vmware.com/vi3/sdk/ReferenceGuide/vim.vm.ConfigSpec.html
|
||||
# From VirtualMachineConfigSpec
|
||||
:deviceChange, :annotation, :extraConfig
|
||||
)
|
||||
|
||||
MockVirtualMachineRelocateSpec = Struct.new(
|
||||
# https://pubs.vmware.com/vi3/sdk/ReferenceGuide/vim.vm.RelocateSpec.html
|
||||
# From VirtualMachineRelocateSpec
|
||||
:datastore, :diskMoveType, :pool
|
||||
)
|
||||
|
||||
MockDynamicProperty = Struct.new(
|
||||
# https://pubs.vmware.com/vsphere-55/index.jsp?topic=%2Fcom.vmware.wssdk.apiref.doc%2Fvmodl.DynamicProperty.html
|
||||
# From DynamicProperty
|
||||
|
|
@ -433,6 +474,7 @@ def mock_RbVmomi_VIM_Datacenter(options = {})
|
|||
# Currently don't support mocking datastore tree
|
||||
options[:datastores] = [] if options[:datastores].nil?
|
||||
options[:name] = 'Datacenter' + rand(65536).to_s if options[:name].nil?
|
||||
options[:networks] = [] if options[:networks].nil?
|
||||
|
||||
mock = MockDatacenter.new()
|
||||
|
||||
|
|
@ -440,6 +482,7 @@ def mock_RbVmomi_VIM_Datacenter(options = {})
|
|||
mock.hostFolder = mock_RbVmomi_VIM_Folder({ :name => 'hostFolderRoot'})
|
||||
mock.vmFolder = mock_RbVmomi_VIM_Folder({ :name => 'vmFolderRoot'})
|
||||
mock.datastore = []
|
||||
mock.network = []
|
||||
|
||||
# Create vmFolder hierarchy
|
||||
recurse_folder_tree(options[:vmfolder_tree],mock.vmFolder.childEntity)
|
||||
|
|
@ -453,6 +496,12 @@ def mock_RbVmomi_VIM_Datacenter(options = {})
|
|||
mock.datastore << mock_ds
|
||||
end
|
||||
|
||||
# Create mock Networks
|
||||
options[:networks].each do |networkname|
|
||||
mock_nw = mock_RbVmomi_VIM_Network({ :name => networkname })
|
||||
mock.network << mock_nw
|
||||
end
|
||||
|
||||
allow(mock).to receive(:is_a?) do |expected_type|
|
||||
expected_type == RbVmomi::VIM::Datacenter
|
||||
end
|
||||
|
|
@ -508,6 +557,72 @@ def mock_RbVmomi_VIM_Datastore(options = {})
|
|||
mock
|
||||
end
|
||||
|
||||
# https://www.vmware.com/support/developer/vc-sdk/visdk41pubs/ApiReference/vim.Network.html
|
||||
def mock_RbVmomi_VIM_Network(options = {})
|
||||
options[:name] = 'Network' + rand(65536).to_s if options[:name].nil?
|
||||
|
||||
mock = MockNetwork.new()
|
||||
|
||||
mock.name = options[:name]
|
||||
|
||||
allow(mock).to receive(:is_a?) do |expected_type|
|
||||
expected_type == RbVmomi::VIM::Network
|
||||
end
|
||||
|
||||
mock
|
||||
end
|
||||
|
||||
# https://www.vmware.com/support/developer/vc-sdk/visdk400pubs/ReferenceGuide/vim.vm.device.VirtualVmxnet3.html
|
||||
def mock_RbVmomi_VIM_VirtualVmxnet3(options = {})
|
||||
options[:key] = rand(65536) if options[:key].nil?
|
||||
options[:deviceInfo] = MockDescription.new()
|
||||
options[:backing] = MockVirtualEthernetCardNetworkBackingInfo.new()
|
||||
options[:addressType] = 'assigned'
|
||||
options[:connectable] = MockVirtualDeviceConnectInfo.new()
|
||||
|
||||
mock = MockVirtualVmxnet3.new()
|
||||
|
||||
mock.key = options[:key]
|
||||
mock.deviceInfo = options[:deviceInfo]
|
||||
mock.backing = options[:backing]
|
||||
mock.addressType = options[:addressType]
|
||||
mock.connectable = options[:connectable]
|
||||
|
||||
allow(mock).to receive(:is_a?) do |expected_type|
|
||||
expected_type == RbVmomi::VIM::VirtualVmxnet3
|
||||
end
|
||||
|
||||
mock
|
||||
end
|
||||
|
||||
# https://pubs.vmware.com/vi3/sdk/ReferenceGuide/vim.vm.RelocateSpec.html
|
||||
def mock_RbVmomi_VIM_VirtualMachineRelocateSpec(options = {})
|
||||
options[:datastore] = 'Datastore' + rand(65536).to_s if options[:datastore].nil?
|
||||
options[:diskMoveType] = :moveChildMostDiskBacking
|
||||
options[:pool] = 'Pool' + rand(65536).to_s if options[:pool].nil?
|
||||
|
||||
mock = MockVirtualMachineRelocateSpec.new
|
||||
|
||||
mock.datastore = mock_RbVmomi_VIM_Datastore({ :name => options[:datastore]})
|
||||
mock.diskMoveType = options[:diskMoveType]
|
||||
mock.pool = mock_RbVmomi_VIM_ResourcePool({:name => options[:pool]})
|
||||
allow(mock).to receive(:is_a?).and_return(RbVmomi::VIM::VirtualMachineRelocateSpec)
|
||||
mock
|
||||
end
|
||||
|
||||
# https://pubs.vmware.com/vi3/sdk/ReferenceGuide/vim.vm.ConfigSpec.html
|
||||
def mock_RbVmomi_VIM_VirtualMachineConfigSpec(options = {})
|
||||
options[:device] = mock_RbVmomi_VIM_VirtualVmxnet3()
|
||||
|
||||
|
||||
mock = MockVirtualMachineConfigSpec.new
|
||||
|
||||
mock.deviceChange = []
|
||||
mock.deviceChange << { operation: :edit, device: options[:device]}
|
||||
|
||||
mock
|
||||
end
|
||||
|
||||
# https://www.vmware.com/support/developer/vc-sdk/visdk41pubs/ApiReference/vim.Datastore.html
|
||||
def mock_RbVmomi_VIM_Folder(options = {})
|
||||
options[:name] = 'Folder' + rand(65536).to_s if options[:name].nil?
|
||||
|
|
|
|||
|
|
@ -648,7 +648,6 @@ EOT
|
|||
|
||||
context 'when create_vm_folder returns nil' do
|
||||
before(:each) do
|
||||
template_vm = new_template_object
|
||||
allow(subject).to receive(:find_template_vm).and_return(new_template_object)
|
||||
expect(subject).to receive(:find_vm_folder).and_return(nil)
|
||||
end
|
||||
|
|
@ -691,6 +690,43 @@ EOT
|
|||
end
|
||||
end
|
||||
|
||||
describe '#set_network_device' do
|
||||
let(:datacenter_object) { mock_RbVmomi_VIM_Datacenter({
|
||||
:datastores => ['datastore0'],
|
||||
:networks => ['network0'],
|
||||
})
|
||||
}
|
||||
let(:template_vm_network_device) { mock_RbVmomi_VIM_VirtualVmxnet3() }
|
||||
|
||||
before(:each) do
|
||||
allow(subject).to receive(:connect_to_vsphere).and_return(connection)
|
||||
allow(connection.serviceInstance).to receive(:find_datacenter).and_return(datacenter_object)
|
||||
end
|
||||
|
||||
context 'Given an invalid network name' do
|
||||
network_name = "invalid_network"
|
||||
|
||||
it 'should raise an error' do
|
||||
expect { subject.set_network_device("datacenter_name", template_vm_network_device, network_name, connection)}.to raise_error(/Cannot find network/)
|
||||
end
|
||||
end
|
||||
|
||||
context 'Given a valid network name' do
|
||||
network_name = "network0"
|
||||
it 'should return the network device' do
|
||||
result = subject.set_network_device("datacenter_name", template_vm_network_device, network_name, connection)
|
||||
expect(result).to be_instance_of(RbVmomi::VIM::VirtualVmxnet3)
|
||||
expect(result.deviceInfo).to be_instance_of(RbVmomi::VIM::Description)
|
||||
expect(result.deviceInfo.summary).to eq('network0')
|
||||
expect(result.backing).to be_instance_of(RbVmomi::VIM::VirtualEthernetCardNetworkBackingInfo)
|
||||
expect(result.backing.network.is_a?(RbVmomi::VIM::Network)).to be true
|
||||
expect(result.backing.network.name).to eq('network0')
|
||||
expect(result.connectable).to be_instance_of(RbVmomi::VIM::VirtualDeviceConnectInfo)
|
||||
expect(result.addressType).to eq('assigned')
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe '#create_disk' do
|
||||
let(:vm_object) { mock_RbVmomi_VIM_VirtualMachine({ :name => vmname }) }
|
||||
let(:datastorename) { 'datastore0' }
|
||||
|
|
@ -1144,7 +1180,7 @@ EOT
|
|||
|
||||
let (:credentials) { config[:providers][:vsphere] }
|
||||
|
||||
context 'succesful connection' do
|
||||
context 'successful connection' do
|
||||
it 'should use the supplied credentials' do
|
||||
expect(RbVmomi::VIM).to receive(:connect).with({
|
||||
:host => credentials['server'],
|
||||
|
|
@ -1436,7 +1472,7 @@ EOT
|
|||
allow(reconfig_vm_task).to receive(:wait_for_completion).and_return(true)
|
||||
end
|
||||
|
||||
context 'Succesfully addding disk' do
|
||||
context 'Successfully adding disk' do
|
||||
it 'should return true' do
|
||||
expect(subject.add_disk(vm_object,disk_size,datastorename,connection,datacenter_name)).to be true
|
||||
end
|
||||
|
|
@ -1501,6 +1537,26 @@ EOT
|
|||
end
|
||||
end
|
||||
|
||||
describe '#create_clone_spec' do
|
||||
let(:relocate_spec) { mock_RbVmomi_VIM_VirtualMachineRelocateSpec({
|
||||
:datastore => 'datastore0',
|
||||
:diskMoveType => :moveChildMostDiskBacking,
|
||||
:pool => 'pool0'
|
||||
})
|
||||
}
|
||||
|
||||
let(:config_spec) { mock_RbVmomi_VIM_VirtualMachineConfigSpec()}
|
||||
|
||||
it 'should return the configured clone spec' do
|
||||
result = subject.create_clone_spec(relocate_spec, config_spec)
|
||||
expect(result.location.pool.name).to eq('pool0')
|
||||
expect(result.location.datastore.name).to eq('datastore0')
|
||||
expect(result.location.diskMoveType).to eq(:moveChildMostDiskBacking)
|
||||
expect(result.config.deviceChange.first[:operation]).to eq(:edit)
|
||||
expect(result.config.deviceChange.first[:device].is_a?(RbVmomi::VIM::VirtualVmxnet3)).to be true
|
||||
end
|
||||
end
|
||||
|
||||
describe '#find_datastore' do
|
||||
let(:datastorename) { 'datastore' }
|
||||
let(:datastore_list) { [] }
|
||||
|
|
@ -2378,7 +2434,7 @@ EOT
|
|||
end
|
||||
|
||||
it 'should return the second host if called twice' do
|
||||
result = subject.select_next_host(poolname, hosts_hash)
|
||||
subject.select_next_host(poolname, hosts_hash)
|
||||
result2 = subject.select_next_host(poolname, hosts_hash)
|
||||
expect(result2).to eq('host2')
|
||||
end
|
||||
|
|
@ -2391,7 +2447,7 @@ EOT
|
|||
end
|
||||
|
||||
it 'should return the second host if called twice' do
|
||||
result = subject.select_next_host(poolname, hosts_hash, architecture)
|
||||
subject.select_next_host(poolname, hosts_hash, architecture)
|
||||
result2 = subject.select_next_host(poolname, hosts_hash, architecture)
|
||||
expect(result2).to eq('host4')
|
||||
end
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue