app/models/manageiq/providers/vmware/infra_manager/provision/cloning.rb
module ManageIQ::Providers::Vmware::InfraManager::Provision::Cloning
def do_clone_task_check(clone_task_mor)
source.with_provider_connection do |vim|
begin
task_props = ["info.state", "info.error", "info.result", "info.progress", "info.completeTime"]
task = vim.getMoProp(clone_task_mor, task_props)
task_info = task&.info
task_state = task_info&.state
case task_state
when TaskInfoState::Success
phase_context[:new_vm_ems_ref] = task_info&.result&.to_s
phase_context[:clone_vm_task_completion_time] = task_info&.completeTime&.to_s
return true
when TaskInfoState::Error
raise "VM Clone Failed: #{task_info&.error&.localizedMessage}"
when TaskInfoState::Running
progress = task_info&.progress
return false, progress.nil? ? "beginning" : "#{progress}% complete"
else
return false, task_state
end
end
end
end
def find_destination_in_vmdb
# Check that the EMS inventory is as up to date as the CloneVM_Task completeTime
# to prevent issues with post-provision depending on data that isn't in VMDB yet
return if source.ext_management_system.last_inventory_date < phase_context[:clone_vm_task_completion_time]
source.ext_management_system&.vms_and_templates&.find_by(:ems_ref => phase_context[:new_vm_ems_ref])
end
def prepare_for_clone_task
raise MiqException::MiqProvisionError, "Provision Request's Destination VM Name=[#{dest_name}] cannot be blank" if dest_name.blank?
raise MiqException::MiqProvisionError, "A VM with name: [#{dest_name}] already exists" if source.ext_management_system.vms.where(:name => dest_name).any?
clone_options = {
:name => dest_name,
:cluster => dest_cluster,
:host => dest_host,
:datastore => dest_datastore,
:folder => dest_folder,
:pool => dest_resource_pool,
:storage_profile => dest_storage_profile,
:config => build_config_spec,
:customization => build_customization_spec
}
# Determine if we are doing a linked-clone provision
clone_options[:linked_clone] = get_option(:linked_clone).to_s == 'true'
clone_options[:snapshot] = get_selected_snapshot if clone_options[:linked_clone]
validate_customization_spec(clone_options[:customization])
clone_options
end
def dest_resource_pool
resource_pool = ResourcePool.find_by(:id => get_option(:placement_rp_name))
return resource_pool if resource_pool
dest_cluster.try(:default_resource_pool) || dest_host.default_resource_pool
end
def dest_storage_profile
storage_profile_id = get_option(:placement_storage_profile)
StorageProfile.find_by(:id => storage_profile_id) unless storage_profile_id.nil?
end
def dest_folder
ems_folder = EmsFolder.find_by(:id => get_option(:placement_folder_name))
return ems_folder if ems_folder
dc = dest_cluster.try(:parent_datacenter) || dest_host.parent_datacenter
# Pick the parent folder in the destination datacenter
find_folder("#{dc.folder_path}/vm", dc)
end
def find_folder(folder_path, datacenter)
EmsFolder.where(:name => File.basename(folder_path), :ems_id => source.ems_id).detect do |f|
f.folder_path == folder_path && f.parent_datacenter == datacenter
end
end
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 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})]")
_log.info("Power on after cloning: [#{clone_options[:power_on].inspect}]")
_log.info("Create Linked Clone: [#{clone_options[:linked_clone].inspect}]")
_log.info("Selected Source Snapshot: [#{clone_options[:snapshot].name} (#{clone_options[:snapshot].ems_ref})]") if clone_options[:linked_clone]
cust_dump = clone_options[:customization].try(:dup)
cust_dump.try(:delete, 'encryptionKey')
dump_obj(clone_options[:transform], "#{_log.prefix} Transform: ", $log, :info)
dump_obj(clone_options[:config], "#{_log.prefix} Config spec: ", $log, :info)
dump_obj(cust_dump, "#{_log.prefix} Customization spec: ", $log, :info, :protected => {:path => /[Pp]assword\]\[value\]/})
dump_obj(options, "#{_log.prefix} Prov Options: ", $log, :info, :protected => {:path => workflow_class.encrypted_options_field_regs})
end
def start_clone(clone_options)
vim_clone_options = {
:name => clone_options[:name],
:wait => MiqProvision::CLONE_SYNCHRONOUS,
:template => self.create_template?
}
[:config, :customization, :linked_clone].each { |key| vim_clone_options[key] = clone_options[key] }
%i[folder host pool snapshot].each do |key|
ci = clone_options[key]
next if ci.nil?
vim_clone_options[key] = ci.ems_ref_obj
end
vim_clone_options[:datastore] = datastore_ems_ref(clone_options)
vim_clone_options[:disk] = build_disk_relocate_spec(vim_clone_options[:datastore])
vim_clone_options[:storage_profile] = build_storage_profile(clone_options[:storage_profile]) unless clone_options[:storage_profile].nil?
task_mor = clone_vm(vim_clone_options)
_log.info("Provisioning completed for [#{vim_clone_options[:name]}] from source [#{source.name}]") if MiqProvision::CLONE_SYNCHRONOUS
task_mor
end
def clone_vm(vim_clone_options)
vim_clone_options = {:power_on => false, :template => false, :wait => true}.merge(vim_clone_options)
cspec = VimHash.new('VirtualMachineCloneSpec') do |cs|
cs.powerOn = vim_clone_options[:power_on].to_s
cs.template = vim_clone_options[:template].to_s
cs.config = vim_clone_options[:config] if vim_clone_options[:config]
cs.customization = vim_clone_options[:customization] if vim_clone_options[:customization]
cs.snapshot = vim_clone_options[:snapshot] if vim_clone_options[:snapshot]
cs.location = VimHash.new('VirtualMachineRelocateSpec') do |csl|
csl.datastore = vim_clone_options[:datastore] if vim_clone_options[:datastore]
csl.host = vim_clone_options[:host] if vim_clone_options[:host]
csl.pool = vim_clone_options[:pool] if vim_clone_options[:pool]
csl.disk = vim_clone_options[:disk] if vim_clone_options[:disk]
csl.transform = vim_clone_options[:transform] if vim_clone_options[:transform]
csl.diskMoveType = VimString.new('createNewChildDiskBacking', "VirtualMachineRelocateDiskMoveOptions") if vim_clone_options[:linked_clone] == true
csl.profile = vim_clone_options[:storage_profile] if vim_clone_options[:storage_profile]
end
end
task_mor = nil
source.with_provider_object do |vim_vm|
task_mor = vim_vm.cloneVM_raw(vim_clone_options[:folder], vim_clone_options[:name], cspec, vim_clone_options[:wait])
# task_mor is a VimString xsiType: ManagedObjectReference vimType: Task but
# we have to serialize just the String to the phase_context
task_mor = task_mor.to_s if task_mor
end
task_mor
end
def datastore_ems_ref(clone_opts)
datastore = Storage.find_by(:id => clone_opts[:datastore].id)
datastore.try(:ems_ref)
end
def get_selected_snapshot
selected_snapshot = get_option(:snapshot).to_s.downcase
if selected_snapshot.to_i > 0
ss = Snapshot.find_by_id(selected_snapshot)
raise MiqException::MiqProvisionError, "Unable to load requested snapshot <#{selected_snapshot}:#{get_option_last(:snapshot)}> for linked-clone processing." if ss.nil?
else
first = source.snapshots.first
ss = first.get_current_snapshot unless first.blank?
end
ss
end
def build_storage_profile(storage_profile)
VimArray.new('ArrayOfVirtualMachineProfileSpec') do |vm_profile_spec_array|
vm_profile_spec_array << VimHash.new('VirtualMachineDefinedProfileSpec') do |vm_profile_spec|
vm_profile_spec.profileId = storage_profile.ems_ref
end
end
end
end