Skip to content

Commit

Permalink
Merge pull request #11437 from agrare/vmware_infra_provision_no_host
Browse files Browse the repository at this point in the history
Allow VMware provision without needing to specify a host
  • Loading branch information
bdunne committed Sep 27, 2016
2 parents 9319534 + dca8127 commit de4f24f
Show file tree
Hide file tree
Showing 6 changed files with 122 additions and 51 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ def prepare_for_clone_task

clone_options = {
:name => dest_name,
:cluster => dest_cluster,
:host => dest_host,
:datastore => dest_datastore,
:folder => dest_folder,
Expand All @@ -54,18 +55,17 @@ def dest_resource_pool
resource_pool = ResourcePool.find_by(:id => respool_id) unless respool_id.nil?
return resource_pool unless resource_pool.nil?

cluster = dest_host.owning_cluster
cluster ? cluster.default_resource_pool : dest_host.default_resource_pool
dest_cluster.try(:default_resource_pool) || dest_host.default_resource_pool
end

def dest_folder
folder_id = get_option(:placement_folder_name)
return EmsFolder.find_by(:id => folder_id) if folder_id

host_dc = dest_host.parent_datacenter || dest_host.ems_cluster.parent_datacenter
dc = dest_cluster.try(:parent_datacenter) || dest_host.parent_datacenter

# Pick the parent folder in the destination datacenter
find_folder("#{host_dc.folder_path}/vm", host_dc)
find_folder("#{dc.folder_path}/vm", dc)
end

def find_folder(folder_path, datacenter)
Expand All @@ -78,7 +78,8 @@ def log_clone_options(clone_options)
_log.info("Provisioning [#{source.name}] to [#{clone_options[:name]}]")
_log.info("Source Template: [#{source.name}]")
_log.info("Destination VM Name: [#{clone_options[:name]}]")
_log.info("Destination Host: [#{clone_options[:host].name} (#{clone_options[:host].ems_ref})]")
_log.info("Destination Cluster: [#{clone_options[:cluster].name} (#{clone_options[:cluster].ems_ref})]") if clone_options[:cluster]
_log.info("Destination Host: [#{clone_options[:host].name} (#{clone_options[:host].ems_ref})]") if clone_options[:host]
_log.info("Destination Datastore: [#{clone_options[:datastore].name} (#{clone_options[:datastore].ems_ref})]")
_log.info("Destination Folder: [#{clone_options[:folder].name}] (#{clone_options[:folder].ems_ref})")
_log.info("Destination Resource Pool: [#{clone_options[:pool].name} (#{clone_options[:pool].ems_ref})]")
Expand Down Expand Up @@ -110,7 +111,7 @@ def start_clone(clone_options)
vim_clone_options[key] = ci.ems_ref_obj
end

vim_clone_options[:datastore] = clone_options[:host].host_storages.find_by(:storage_id => clone_options[:datastore].id).ems_ref
vim_clone_options[:datastore] = datastore_ems_ref(clone_options)

task_mor = clone_vm(vim_clone_options)
_log.info("Provisioning completed for [#{vim_clone_options[:name]}] from source [#{source.name}]") if MiqProvision::CLONE_SYNCHRONOUS
Expand Down Expand Up @@ -145,6 +146,20 @@ def clone_vm(vim_clone_options)
task_mor
end

def datastore_ems_ref(clone_opts)
host_ids = if clone_opts[:host]
clone_opts[:host].id
else
clone_opts[:cluster].hosts.pluck(:id)
end

# Find a host in the cluster that has this storage mounted to get the right ems_ref for this
# datastore in the datacenter
datastore = HostStorage.find_by(:storage_id => clone_opts[:datastore].id, :host_id => host_ids)

datastore.try(:ems_ref)
end

def get_selected_snapshot
selected_snapshot = get_option(:snapshot).to_s.downcase
if selected_snapshot.to_i > 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,41 +65,38 @@ def build_config_spec_vlan(network, vnicDev, vmcs)
end

def build_config_spec_dvs(network, vnicDev, vmcs)
source.with_provider_connection do |vim|
operation = vnicDev.nil? ? VirtualDeviceConfigSpecOperation::Add : VirtualDeviceConfigSpecOperation::Edit
add_device_config_spec(vmcs, operation) do |vdcs|
vdcs.device = vnicDev || create_vlan_device(network)
_log.info "Setting target network device to Device Name:<#{network[:network]}> Device:<#{vdcs.device.inspect}>"

#
# Change the port group of the target VM.
#

vdcs.device.backing = VimHash.new('VirtualEthernetCardDistributedVirtualPortBackingInfo') do |vecdvpbi|
vecdvpbi.port = VimHash.new('DistributedVirtualSwitchPortConnection') do |dvspc|
#
# Get the DVS info for a given host.
#
dvs = vim.queryDvsConfigTarget(vim.sic.dvSwitchManager, dest_host.ems_ref_obj, nil)
dpg = vim.applyFilter(dvs.distributedVirtualPortgroup, 'uplinkPortgroup' => 'false').detect { |nupg| URI.decode(nupg.portgroupName) == network[:network] }

raise MiqException::MiqProvisionError, "Port group [#{network[:network]}] is not available on target host [#{dest_host.name}]" if dpg.nil?
_log.info("portgroupName: #{dpg.portgroupName}, portgroupKey: #{dpg.portgroupKey}, switchUuid: #{dpg.switchUuid}")

dvspc.switchUuid = dpg.switchUuid
dvspc.portgroupKey = dpg.portgroupKey
end
end
# A DistributedVirtualPortgroup name is unique in a datacenter so look for a Lan with this name
# on all switches in the cluster
hosts = dest_cluster.try(:hosts) || dest_host
lan = Lan.find_by(:name => network[:network], :switch_id => HostSwitch.where(:host_id => hosts).pluck(:switch_id))

raise MiqException::MiqProvisionError, "Port group [#{network[:network]}] is not available on target" if lan.nil?
_log.info("portgroupName: #{lan.name}, portgroupKey: #{lan.uid_ems}, switchUuid: #{lan.switch.switch_uuid}")

operation = vnicDev.nil? ? VirtualDeviceConfigSpecOperation::Add : VirtualDeviceConfigSpecOperation::Edit
add_device_config_spec(vmcs, operation) do |vdcs|
vdcs.device = vnicDev || create_vlan_device(network)
_log.info "Setting target network device to Device Name:<#{network[:network]}> Device:<#{vdcs.device.inspect}>"

#
# Change the port group of the target VM.
#

#
# Manually assign MAC address to target VM.
#
mac_addr = network[:mac_address]
unless mac_addr.blank?
vdcs.device.macAddress = mac_addr
vdcs.device.addressType = 'Manual'
vdcs.device.backing = VimHash.new('VirtualEthernetCardDistributedVirtualPortBackingInfo') do |vecdvpbi|
vecdvpbi.port = VimHash.new('DistributedVirtualSwitchPortConnection') do |dvspc|
dvspc.switchUuid = lan.switch.switch_uuid
dvspc.portgroupKey = lan.uid_ems
end
end

#
# Manually assign MAC address to target VM.
#
mac_addr = network[:mac_address]
unless mac_addr.blank?
vdcs.device.macAddress = mac_addr
vdcs.device.addressType = 'Manual'
end
end
end

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,36 +4,53 @@ module ManageIQ::Providers::Vmware::InfraManager::Provision::Placement
protected

def placement
if get_option(:placement_auto) == true
host, cluster, datastore = if get_option(:placement_auto) == true
automatic_placement
else
manual_placement
end

raise MiqException::MiqProvisionError, "Destination placement_ds_name not provided" if datastore.nil?
raise MiqException::MiqProvisionError, "Destination placement_host_name and placement_cluster_name not provided" if host.nil? && cluster.nil?
raise MiqException::MiqProvisionError, "A Host must be selected on a non-DRS enabled cluster" if host.nil? && !cluster.drs_enabled

return host, cluster, datastore
end

private

def manual_placement
_log.info("Manual placement...")
return selected_placement_obj(:placement_host_name, Host),
selected_placement_obj(:placement_ds_name, Storage)

host = selected_placement_obj(:placement_host_name, Host)
cluster = selected_placement_obj(:placement_cluster_name, EmsCluster)
datastore = selected_placement_obj(:placement_ds_name, Storage)

if host && cluster.nil?
cluster = host.ems_cluster
end

return host, cluster, datastore
end

def automatic_placement
# get most suitable host and datastore for new VM
_log.info("Getting most suitable host and datastore for new VM from automate...")
host, datastore = get_most_suitable_host_and_storage

_log.info("Host Name: [#{host.name}] Id: [#{host.id}]") if host
_log.info("Datastore Name: [#{datastore.name}] ID : [#{datastore.id}]") if datastore
host ||= selected_placement_obj(:placement_host_name, Host)

host ||= selected_placement_obj(:placement_host_name, Host)
datastore ||= selected_placement_obj(:placement_ds_name, Storage)
return host, datastore
cluster = selected_placement_obj(:placement_cluster_name, EmsCluster) || host.try(:ems_cluster)

return host, cluster, datastore
end

def selected_placement_obj(key, klass)
klass.find_by(:id => get_option(key)).tap do |obj|
raise MiqException::MiqProvisionError, "Destination #{key} not provided" unless obj
_log.info("Using selected #{key} : [#{obj.name}] id : [#{obj.id}]")
_log.info("Using selected #{key} : [#{obj.name}] id : [#{obj.id}]") if obj
end
end
end
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,10 @@ def create_destination
end

def determine_placement
host, datastore = placement
host, cluster, datastore = placement

options[:dest_host] = [host.id, host.name]
options[:dest_host] = [host.id, host.name] if host
options[:dest_cluster] = [cluster.id, cluster.name] if cluster
options[:dest_storage] = [datastore.id, datastore.name]
signal :start_clone_task
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
template = FactoryGirl.create(:template_vmware, :ext_management_system => ems)
vm = FactoryGirl.create(:vm_vmware)
@storage = FactoryGirl.create(:storage)
@host = FactoryGirl.create(:host, :ext_management_system => ems, :storages => [@storage])
@cluster = FactoryGirl.create(:ems_cluster)
@host = FactoryGirl.create(:host, :ext_management_system => ems, :storages => [@storage], :ems_cluster => @cluster)
options = {:src_vm_id => template.id}

@task = FactoryGirl.create(:miq_provision_vmware, :source => template,
Expand All @@ -21,7 +22,13 @@
expect { @task.send(:placement) }.to raise_error(MiqException::MiqProvisionError)
end

it "#manual_placement" do
it "#manual_placement without host or cluster raises error" do
@task.options[:placement_ds_name] = @storage.id
@task.options[:placement_auto] = false
expect { @task.send(:placement) }.to raise_error(MiqException::MiqProvisionError)
end

it "#manual_placement with host and datastore" do
@task.options[:placement_host_name] = @host.id
@task.options[:placement_ds_name] = @storage.id
@task.options[:placement_auto] = false
Expand All @@ -43,8 +50,9 @@
end

def check
host, storage = @task.send(:placement)
host, cluster, storage = @task.send(:placement)
expect(host).to eql(@host)
expect(cluster).to eql(@cluster)
expect(storage).to eql(@storage)
end
end
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,8 @@
end

it "uses the resource pool from the cluster" do
@vm_prov.options[:dest_host] = [dest_host_with_cluster.id, dest_host_with_cluster.name]
@vm_prov.options[:dest_host] = [dest_host_with_cluster.id, dest_host_with_cluster.name]
@vm_prov.options[:dest_cluster] = [cluster.id, cluster.name]
expect(@vm_prov.dest_resource_pool).to eq(cluster.default_resource_pool)
end

Expand All @@ -211,9 +212,13 @@
storage = FactoryGirl.create(:storage_nfs, :ems_ref => ds_mor, :ems_ref_obj => ds_mor)

Array.new(2) do |i|
cluster_mor = "cluster-#{i}"
cluster = FactoryGirl.create(:ems_cluster, :ems_ref => cluster_mor)

host_mor = "host-#{i}"
host_props = {
:ext_management_system => @ems,
:ems_cluster => cluster,
:ems_ref => host_mor,
:ems_ref_obj => host_mor
}
Expand Down Expand Up @@ -255,6 +260,34 @@
result = @vm_prov.start_clone clone_opts
expect(result).to eq(task_mor)
end

it "uses the right ems_ref when given a cluster" do
dest_cluster_mor = "cluster-1"
dest_datastore_mor = "datastore-1"
task_mor = "task-1"

clone_opts = {
:name => @target_vm_name,
:cluster => EmsCluster.find_by(:ems_ref => dest_cluster_mor),
:datastore => Storage.first
}

expected_vim_clone_opts = {
:name => @target_vm_name,
:wait => false,
:template => false,
:transform => nil,
:config => nil,
:customization => nil,
:linked_clone => nil,
:datastore => dest_datastore_mor
}

allow(@vm_prov).to receive(:clone_vm).with(expected_vim_clone_opts).and_return(task_mor)

result = @vm_prov.start_clone clone_opts
expect(result).to eq(task_mor)
end
end
end
end
Expand Down

0 comments on commit de4f24f

Please sign in to comment.