diff --git a/content/automate/ManageIQ/System/CommonMethods/MiqAe.class/__methods__/weighted_update_status.rb b/content/automate/ManageIQ/System/CommonMethods/MiqAe.class/__methods__/weightedupdatestatus.rb similarity index 92% rename from content/automate/ManageIQ/System/CommonMethods/MiqAe.class/__methods__/weighted_update_status.rb rename to content/automate/ManageIQ/System/CommonMethods/MiqAe.class/__methods__/weightedupdatestatus.rb index 5c12c6629..7ea2f7182 100644 --- a/content/automate/ManageIQ/System/CommonMethods/MiqAe.class/__methods__/weighted_update_status.rb +++ b/content/automate/ManageIQ/System/CommonMethods/MiqAe.class/__methods__/weightedupdatestatus.rb @@ -42,7 +42,6 @@ def on_entry(state_hash, _, _, state_weight, state_description) end def on_exit(state_hash, state_progress, state_name, _, _) - @handle.log(:info, "State Progress: #{state_progress.inspect}") # If the state is retrying, we leave the status to 'active'. if @handle.root['ae_result'] == 'retry' # If the method provides progress info, it is merged, otherwise we set @@ -68,12 +67,12 @@ def on_exit(state_hash, state_progress, state_name, _, _) state_hash end - def on_error(state_hash, state_progress, state_name, _, _) + def on_error(state_hash, state_progress, _, _, state_description) # The state has failed, so we consider it as finished and 100%. state_hash['status'] = 'failed' state_hash['percent'] = 100.0 # We merge the potential message from method and set the update time. - state_hash['message'] = state_progress.nil? ? "#{state_name} has failed." : state_progress['message'] + state_hash['message'] = state_progress.nil? ? "Failed to #{state_description}." : state_progress['message'] state_hash['updated_on'] = Time.now.utc state_hash end @@ -106,7 +105,6 @@ def main # We record the state hash in the task progress progress['states']["#{state_ancestry}/#{state_name}"] = state_hash - @handle.log(:info, "Progress: #{progress}") # If we enter the state, we update the task progress with current # state and description. if @handle.root['ae_status_state'] == 'on_entry' @@ -121,7 +119,12 @@ def main @handle.set_state_var('ae_state_progress', nil) # We record the progress as a task option. task.update_transformation_progress(progress) - @handle.log(:info, "Task: #{task.get_option(:progress)}") + # We set the task message. + if @handle.root['ae_state_step'] == 'on_error' + task.message = 'Failed' + else + task.message = @handle.inputs['task_message'] unless @handle.inputs['task_message'] == '_' + end end end end diff --git a/content/automate/ManageIQ/System/CommonMethods/MiqAe.class/__methods__/weighted_update_status.yaml b/content/automate/ManageIQ/System/CommonMethods/MiqAe.class/__methods__/weightedupdatestatus.yaml similarity index 72% rename from content/automate/ManageIQ/System/CommonMethods/MiqAe.class/__methods__/weighted_update_status.yaml rename to content/automate/ManageIQ/System/CommonMethods/MiqAe.class/__methods__/weightedupdatestatus.yaml index f6c867f96..ea013aa6c 100644 --- a/content/automate/ManageIQ/System/CommonMethods/MiqAe.class/__methods__/weighted_update_status.yaml +++ b/content/automate/ManageIQ/System/CommonMethods/MiqAe.class/__methods__/weightedupdatestatus.yaml @@ -51,3 +51,23 @@ object: on_error: max_retries: max_time: + - field: + aetype: + name: task_message + display_name: + datatype: + priority: 3 + owner: + default_value: _ + substitute: false + message: create + visibility: + collect: + scope: + description: + condition: + on_entry: + on_exit: + on_error: + max_retries: + max_time: diff --git a/content/automate/ManageIQ/Transformation/Common.class/__class__.yaml b/content/automate/ManageIQ/Transformation/Common.class/__class__.yaml new file mode 100644 index 000000000..b7fe6fdbf --- /dev/null +++ b/content/automate/ManageIQ/Transformation/Common.class/__class__.yaml @@ -0,0 +1,33 @@ +--- +object_type: class +version: 1.0 +object: + attributes: + description: + display_name: + name: Common + type: + inherits: + visibility: + owner: + schema: + - field: + aetype: method + name: execute + display_name: + datatype: + priority: 1 + owner: + default_value: + substitute: true + message: create + visibility: + collect: + scope: + description: + condition: + on_entry: + on_exit: + on_error: + max_retries: + max_time: diff --git a/content/automate/ManageIQ/Transformation/Common.class/__methods__/acquiretransformationhost.rb b/content/automate/ManageIQ/Transformation/Common.class/__methods__/acquiretransformationhost.rb new file mode 100644 index 000000000..5bde90efd --- /dev/null +++ b/content/automate/ManageIQ/Transformation/Common.class/__methods__/acquiretransformationhost.rb @@ -0,0 +1,47 @@ +module ManageIQ + module Automate + module Transformation + module Common + class AcquireTransformationHost + def initialize(handle = $evm) + @handle = handle + end + + def main + factory_config = @handle.get_state_var(:factory_config) + raise "No factory config found. Aborting." if factory_config.nil? + + task = @handle.root['service_template_transformation_plan_task'] + @handle.log(:info, "Task: #{task.inspect}") + source_vm = task.source + @handle.log(:info, "Source VM: #{source_vm.name}") + source_cluster = source_vm.ems_cluster + @handle.log(:info, "Source Cluster: #{source_cluster.name}") + @handle.log(:info, "Destination Cluster: #{task.transformation_destination(source_cluster).name}") + destination_ems = task.transformation_destination(source_cluster).ext_management_system + raise "Invalid destination EMS type: #{destination_ems.emstype}. Aborting." unless destination_ems.emstype == "rhevm" + + transformation_host = ManageIQ::Automate::Transformation::TransformationHosts::Common::Utils.get_transformation_host(destination_ems, @handle.get_state_var(:transformation_method), factory_config) + if transformation_host.nil? + @handle.log(:info, "No transformation host available. Retrying.") + @handle.root['ae_result'] = 'retry' + @handle.root['ae_retry_server_affinity'] = true + @handle.root['ae_retry_interval'] = $evm.object['check_convert_interval'] || '1.minutes' + else + @handle.log(:info, "Transformation Host: #{transformation_host.name}.") + task.set_option(:transformation_host_id, transformation_host.id) + task.set_option(:transformation_host_name, transformation_host.name) + end + rescue => e + @handle.set_state_var(:ae_state_progress, 'message' => e.message) + raise + end + end + end + end + end +end + +if $PROGRAM_NAME == __FILE__ + ManageIQ::Automate::Transformation::Common::AcquireTransformationHost.new.main +end diff --git a/content/automate/ManageIQ/Transformation/Common.class/__methods__/acquiretransformationhost.yaml b/content/automate/ManageIQ/Transformation/Common.class/__methods__/acquiretransformationhost.yaml new file mode 100644 index 000000000..7f20ae1d7 --- /dev/null +++ b/content/automate/ManageIQ/Transformation/Common.class/__methods__/acquiretransformationhost.yaml @@ -0,0 +1,15 @@ +--- +object_type: method +version: 1.0 +object: + attributes: + name: AcquireTransformationHost + display_name: + description: + scope: instance + language: ruby + location: inline + embedded_methods: + - "/Transformation/TransformationHosts/Common/Utils" + options: {} + inputs: [] diff --git a/content/automate/ManageIQ/Transformation/Common.class/__methods__/assesstransformation.rb b/content/automate/ManageIQ/Transformation/Common.class/__methods__/assesstransformation.rb new file mode 100644 index 000000000..eef6ef56d --- /dev/null +++ b/content/automate/ManageIQ/Transformation/Common.class/__methods__/assesstransformation.rb @@ -0,0 +1,87 @@ +module ManageIQ + module Automate + module Transformation + module Common + class AssessTransformation + SUPPORTED_SOURCE_EMS_TYPES = ['vmwarews'].freeze + SUPPORTED_DESTINATION_EMS_TYPES = ['rhevm'].freeze + REQUIRED_CUSTOM_ATTRIBUTES = { + 'rhevm' => %i(rhv_export_domain_id rhv_cluster_id rhv_storage_domain_id) + }.freeze + + def initialize(handle = $evm) + @handle = handle + end + + def main + task = @handle.root['service_template_transformation_plan_task'] + raise 'No task found. Exiting' if task.nil? + @handle.log(:info, "Task: #{task.inspect}") if @debug + + source_vm ||= task.source + raise 'No VM found. Exiting' if source_vm.nil? + + source_cluster = source_vm.ems_cluster + destination_cluster = task.transformation_destination(source_cluster) + raise "No destination cluster for '#{source_vm.name}'. Exiting." if destination_cluster.nil? + + source_ems = source_vm.ext_management_system + destination_ems = destination_cluster.ext_management_system + + virtv2v_networks = [] + source_vm.hardware.nics.select { |n| n.device_type == 'ethernet' }.each do |nic| + source_network = nic.lan + destination_network = task.transformation_destination(source_network) + raise "[#{source_vm.name}] NIC #{nic.device_name} [#{source_network.name}] has no mapping. Aborting." if destination_network.nil? + virtv2v_networks << { :source => source_network.name, :destination => destination_network.name } + end + @handle.log(:info, "Network mappings: #{virtv2v_networks}") + task.set_option(:virtv2v_networks, virtv2v_networks) + + virtv2v_disks = [] + source_vm.hardware.disks.select { |d| d.device_type == 'disk' }.each do |disk| + source_storage = disk.storage + destination_storage = task.transformation_destination(disk.storage) + raise "[#{source_vm.name}] Disk #{disk.device_name} [#{source_storage.name}] has no mapping. Aborting." if destination_storage.nil? + virtv2v_disks << { :path => disk.filename, :size => disk.size, :percent => 0, :weight => disk.size.to_f / source_vm.allocated_disk_storage.to_f * 100 } + end + @handle.log(:info, "Source VM Disks #{virtv2v_disks}") + task.set_option(:virtv2v_disks, virtv2v_disks) + + raise "Unsupported source EMS type: #{source_ems.emstype}." unless SUPPORTED_SOURCE_EMS_TYPES.include?(source_ems.emstype) + @handle.set_state_var(:source_ems_type, source_ems.emstype) + + raise "Unsupported destination EMS type: #{destination_ems.emstype}." unless SUPPORTED_DESTINATION_EMS_TYPES.include?(destination_ems.emstype) + @handle.set_state_var(:destination_ems_type, destination_ems.emstype) + + transformation_type = "#{source_ems.emstype}2#{destination_ems.emstype}" + @handle.set_state_var(:transformation_type, transformation_type) + + transformation_method = "vddk" + @handle.set_state_var(:transformation_method, transformation_method) + + transformation_host_type = "ovirt_host" + @handle.set_state_var(:transformation_host_type, transformation_host_type) + + factory_config = { + 'vmtransformation_check_interval' => @handle.object['vmtransformation_check_interval'] || '15.seconds', + 'vmpoweroff_check_interval' => @handle.object['vmpoweroff_check_interval'] || '30.seconds' + } + @handle.set_state_var(:factory_config, factory_config) + + # Force VM shutdown and snapshots collapse by default + task.set_option(:collapse_snapshots, true) + task.set_option(:power_off, true) + rescue => e + @handle.set_state_var(:ae_state_progress, 'message' => e.message) + raise + end + end + end + end + end +end + +if $PROGRAM_NAME == __FILE__ + ManageIQ::Automate::Transformation::Common::AssessTransformation.new.main +end diff --git a/content/automate/ManageIQ/Transformation/Common.class/__methods__/assesstransformation.yaml b/content/automate/ManageIQ/Transformation/Common.class/__methods__/assesstransformation.yaml new file mode 100644 index 000000000..bdbb37b38 --- /dev/null +++ b/content/automate/ManageIQ/Transformation/Common.class/__methods__/assesstransformation.yaml @@ -0,0 +1,13 @@ +--- +object_type: method +version: 1.0 +object: + attributes: + name: AssessTransformation + display_name: + description: + scope: instance + language: ruby + location: inline + options: {} + inputs: [] diff --git a/content/automate/ManageIQ/Transformation/Common.class/_missing.yaml b/content/automate/ManageIQ/Transformation/Common.class/_missing.yaml new file mode 100644 index 000000000..31e5fff8b --- /dev/null +++ b/content/automate/ManageIQ/Transformation/Common.class/_missing.yaml @@ -0,0 +1,12 @@ +--- +object_type: instance +version: 1.0 +object: + attributes: + display_name: + name: ".missing" + inherits: + description: + fields: + - execute: + value: "${#_missing_instance}" diff --git a/content/automate/ManageIQ/Transformation/Infrastructure/VM/Common.class/__class__.yaml b/content/automate/ManageIQ/Transformation/Infrastructure/VM/Common.class/__class__.yaml new file mode 100644 index 000000000..b7fe6fdbf --- /dev/null +++ b/content/automate/ManageIQ/Transformation/Infrastructure/VM/Common.class/__class__.yaml @@ -0,0 +1,33 @@ +--- +object_type: class +version: 1.0 +object: + attributes: + description: + display_name: + name: Common + type: + inherits: + visibility: + owner: + schema: + - field: + aetype: method + name: execute + display_name: + datatype: + priority: 1 + owner: + default_value: + substitute: true + message: create + visibility: + collect: + scope: + description: + condition: + on_entry: + on_exit: + on_error: + max_retries: + max_time: diff --git a/content/automate/ManageIQ/Transformation/Infrastructure/VM/Common.class/__methods__/poweroff.rb b/content/automate/ManageIQ/Transformation/Infrastructure/VM/Common.class/__methods__/poweroff.rb new file mode 100644 index 000000000..1a5032c04 --- /dev/null +++ b/content/automate/ManageIQ/Transformation/Infrastructure/VM/Common.class/__methods__/poweroff.rb @@ -0,0 +1,44 @@ +module ManageIQ + module Automate + module Transformation + module Infrastructure + module VM + module Common + class PowerOff + def initialize(handle = $evm) + @handle = handle + end + + def main + task = @handle.root['service_template_transformation_plan_task'] + source_vm = task.source + + if source_vm.power_state == 'off' + @handle.log(:info, "VM '#{source_vm.name}' is already off. Nothing to do.") + elsif task.get_option(:power_off) + @handle.log(:info, "VM '#{source_vm.name} is powered on. Let's shut it down.") + if @handle.state_var_exist?(:vm_shutdown_in_progress) + source_vm.stop if @handle.root['ae_state_retries'].to_i > 10 + else + source_vm.shutdown_guest + @handle.set_state_var(:vm_shutdown_in_progress, true) + end + @handle.root['ae_result'] = 'retry' + else + raise "VM '#{source_vm.name} is powered on, but we are not allowed to shut it down. Aborting." + end + rescue => e + @handle.set_state_var(:ae_state_progress, 'message' => e.message) + raise + end + end + end + end + end + end + end +end + +if $PROGRAM_NAME == __FILE__ + ManageIQ::Automate::Transformation::Infrastructure::VM::Common::PowerOff.new.main +end diff --git a/content/automate/ManageIQ/Transformation/Infrastructure/VM/Common.class/__methods__/poweroff.yaml b/content/automate/ManageIQ/Transformation/Infrastructure/VM/Common.class/__methods__/poweroff.yaml new file mode 100644 index 000000000..57c96587a --- /dev/null +++ b/content/automate/ManageIQ/Transformation/Infrastructure/VM/Common.class/__methods__/poweroff.yaml @@ -0,0 +1,13 @@ +--- +object_type: method +version: 1.0 +object: + attributes: + name: PowerOff + display_name: + description: + scope: instance + language: ruby + location: inline + options: {} + inputs: [] diff --git a/content/automate/ManageIQ/Transformation/Infrastructure/VM/Common.class/__methods__/poweron.rb b/content/automate/ManageIQ/Transformation/Infrastructure/VM/Common.class/__methods__/poweron.rb new file mode 100644 index 000000000..fbab12a95 --- /dev/null +++ b/content/automate/ManageIQ/Transformation/Infrastructure/VM/Common.class/__methods__/poweron.rb @@ -0,0 +1,30 @@ +module ManageIQ + module Automate + module Transformation + module Infrastructure + module VM + module Common + class PowerOn + def initialize(handle = $evm) + @handle = handle + end + + def main + task = @handle.root['service_template_transformation_plan_task'] + destination_vm = @handle.vmdb(:vm).find_by(:id => task.get_option(:destination_vm_id)) + destination_vm.start + rescue => e + @handle.set_state_var(:ae_state_progress, 'message' => e.message) + raise + end + end + end + end + end + end + end +end + +if $PROGRAM_NAME == __FILE__ + ManageIQ::Automate::Transformation::Infrastructure::VM::Common::PowerOn.new.main +end diff --git a/content/automate/ManageIQ/Transformation/Infrastructure/VM/Common.class/__methods__/poweron.yaml b/content/automate/ManageIQ/Transformation/Infrastructure/VM/Common.class/__methods__/poweron.yaml new file mode 100644 index 000000000..aa10bf7bb --- /dev/null +++ b/content/automate/ManageIQ/Transformation/Infrastructure/VM/Common.class/__methods__/poweron.yaml @@ -0,0 +1,13 @@ +--- +object_type: method +version: 1.0 +object: + attributes: + name: PowerOn + display_name: + description: + scope: instance + language: ruby + location: inline + options: {} + inputs: [] diff --git a/content/automate/ManageIQ/Transformation/Infrastructure/VM/Common.class/_missing.yaml b/content/automate/ManageIQ/Transformation/Infrastructure/VM/Common.class/_missing.yaml new file mode 100644 index 000000000..31e5fff8b --- /dev/null +++ b/content/automate/ManageIQ/Transformation/Infrastructure/VM/Common.class/_missing.yaml @@ -0,0 +1,12 @@ +--- +object_type: instance +version: 1.0 +object: + attributes: + display_name: + name: ".missing" + inherits: + description: + fields: + - execute: + value: "${#_missing_instance}" diff --git a/content/automate/ManageIQ/Transformation/Infrastructure/VM/__namespace__.yaml b/content/automate/ManageIQ/Transformation/Infrastructure/VM/__namespace__.yaml new file mode 100644 index 000000000..56812dc20 --- /dev/null +++ b/content/automate/ManageIQ/Transformation/Infrastructure/VM/__namespace__.yaml @@ -0,0 +1,10 @@ +--- +object_type: namespace +version: 1.0 +object: + attributes: + name: VM + description: + display_name: + priority: + enabled: diff --git a/content/automate/ManageIQ/Transformation/Infrastructure/VM/rhevm.class/__class__.yaml b/content/automate/ManageIQ/Transformation/Infrastructure/VM/rhevm.class/__class__.yaml new file mode 100644 index 000000000..9123cb0f1 --- /dev/null +++ b/content/automate/ManageIQ/Transformation/Infrastructure/VM/rhevm.class/__class__.yaml @@ -0,0 +1,33 @@ +--- +object_type: class +version: 1.0 +object: + attributes: + description: + display_name: Red Hat + name: rhevm + type: + inherits: + visibility: + owner: + schema: + - field: + aetype: method + name: execute + display_name: + datatype: + priority: 1 + owner: + default_value: + substitute: true + message: create + visibility: + collect: + scope: + description: + condition: + on_entry: + on_exit: + on_error: + max_retries: + max_time: diff --git a/content/automate/ManageIQ/Transformation/Infrastructure/VM/rhevm.class/__methods__/checkpoweredon.rb b/content/automate/ManageIQ/Transformation/Infrastructure/VM/rhevm.class/__methods__/checkpoweredon.rb new file mode 100644 index 000000000..ca09358f1 --- /dev/null +++ b/content/automate/ManageIQ/Transformation/Infrastructure/VM/rhevm.class/__methods__/checkpoweredon.rb @@ -0,0 +1,36 @@ +module ManageIQ + module Automate + module Transformation + module Infrastructure + module VM + module RedHat + class CheckPoweredOn + def initialize(handle = $evm) + @handle = handle + end + + def main + task = @handle.root['service_template_transformation_plan_task'] + destination_vm = @handle.vmdb(:vm).find_by(:id => task.get_option(:destination_vm_id)) + destination_ems = destination_vm.ext_management_system + destination_vm_sdk = ManageIQ::Automate::Transformation::Infrastructure::VM::RedHat::Utils.new(destination_ems).vm_find_by_name(destination_vm.name) + @handle.log(:info, "Status of VM '#{destination_vm.name}': #{destination_vm_sdk.status}") + unless destination_vm_sdk.status == OvirtSDK4::VmStatus::UP + @handle.root["ae_result"] = "retry" + @handle.root["ae_retry_interval"] = "15.seconds" + end + rescue => e + @handle.set_state_var(:ae_state_progress, 'message' => e.message) + raise + end + end + end + end + end + end + end +end + +if $PROGRAM_NAME == __FILE__ + ManageIQ::Automate::Transformation::Infrastructure::VM::RedHat::CheckPoweredOn.new.main +end diff --git a/content/automate/ManageIQ/Transformation/Infrastructure/VM/rhevm.class/__methods__/checkpoweredon.yaml b/content/automate/ManageIQ/Transformation/Infrastructure/VM/rhevm.class/__methods__/checkpoweredon.yaml new file mode 100644 index 000000000..4501d42b5 --- /dev/null +++ b/content/automate/ManageIQ/Transformation/Infrastructure/VM/rhevm.class/__methods__/checkpoweredon.yaml @@ -0,0 +1,15 @@ +--- +object_type: method +version: 1.0 +object: + attributes: + name: CheckPoweredOn + display_name: + description: + scope: instance + language: ruby + location: inline + embedded_methods: + - "/Transformation/Infrastructure/VM/rhevm/Utils" + options: {} + inputs: [] diff --git a/content/automate/ManageIQ/Transformation/Infrastructure/VM/rhevm.class/__methods__/checkvmininventory.rb b/content/automate/ManageIQ/Transformation/Infrastructure/VM/rhevm.class/__methods__/checkvmininventory.rb new file mode 100644 index 000000000..5f4aff3bf --- /dev/null +++ b/content/automate/ManageIQ/Transformation/Infrastructure/VM/rhevm.class/__methods__/checkvmininventory.rb @@ -0,0 +1,55 @@ +module ManageIQ + module Automate + module Transformation + module Infrastructure + module VM + module RedHat + class CheckVmInInventory + def initialize(handle = $evm) + @handle = handle + end + + def main + task = @handle.root['service_template_transformation_plan_task'] + source_vm = task.source + destination_ems = task.transformation_destination(source_vm.ems_cluster).ext_management_system + destination_vm = ManageIQ::Automate::Transformation::Infrastructure::VM::RedHat::Utils.new(destination_ems).vm_find_by_name(source_vm.name) + raise "VM #{source_vm.name} not found in destination provider #{destination_ems.name}" if destination_vm.nil? + + finished = false + + destination_vm_vmdb = @handle.vmdb(:vm).where(["ems_ref = ?", destination_vm.href.gsub(/^\/ovirt-engine/, '')]).first + if destination_vm_vmdb.blank? + @handle.log(:info, "VM '#{source_vm.name}' not found in VMDB.") + if @handle.state_var_exist?(:ems_refresh_in_progress) + @handle.log(:info, "Refresh of '#{destination_ems.name}' is in progress. Nothing to do.") + else + @handle.log(:info, "Forcing refresh of provider '#{destination_ems.name}'") + destination_ems.refresh + @handle.set_state_var(:ems_refresh_in_progress, true) + end + else + @handle.log(:info, "VM '#{source_vm.name}' found in VMDB with id '#{destination_vm_vmdb.id}'") + task.set_option(:destination_vm_id, destination_vm_vmdb.id) + finished = true + end + + unless finished + @handle.root['ae_result'] = 'retry' + @handle.root['ae_retry_interval'] = '15.seconds' + end + rescue => e + @handle.set_state_var(:ae_state_progress, 'message' => e.message) + raise + end + end + end + end + end + end + end +end + +if $PROGRAM_NAME == __FILE__ + ManageIQ::Automate::Transformation::Infrastructure::VM::RedHat::CheckVmInInventory.new.main +end diff --git a/content/automate/ManageIQ/Transformation/Infrastructure/VM/rhevm.class/__methods__/checkvmininventory.yaml b/content/automate/ManageIQ/Transformation/Infrastructure/VM/rhevm.class/__methods__/checkvmininventory.yaml new file mode 100644 index 000000000..93aa55ffc --- /dev/null +++ b/content/automate/ManageIQ/Transformation/Infrastructure/VM/rhevm.class/__methods__/checkvmininventory.yaml @@ -0,0 +1,15 @@ +--- +object_type: method +version: 1.0 +object: + attributes: + name: CheckVmInInventory + display_name: + description: + scope: instance + language: ruby + location: inline + embedded_methods: + - "/Transformation/Infrastructure/VM/rhevm/Utils" + options: {} + inputs: [] diff --git a/content/automate/ManageIQ/Transformation/Infrastructure/VM/rhevm.class/__methods__/setdescription.rb b/content/automate/ManageIQ/Transformation/Infrastructure/VM/rhevm.class/__methods__/setdescription.rb new file mode 100644 index 000000000..e36a625e2 --- /dev/null +++ b/content/automate/ManageIQ/Transformation/Infrastructure/VM/rhevm.class/__methods__/setdescription.rb @@ -0,0 +1,33 @@ +module ManageIQ + module Automate + module Transformation + module Infrastructure + module VM + module RedHat + class SetDescription + def initialize(handle = $evm) + @handle = handle + end + + def main + task = @handle.root['service_template_transformation_plan_task'] + destination_vm = @handle.vmdb(:vm).find_by(:id => task.get_option(:destination_vm_id)) + destination_ems = destination_vm.ext_management_system + + description = "Migrated by Cloudforms on #{Time.now.utc}." + ManageIQ::Automate::Transformation::Infrastructure::VM::RedHat::Utils.new(destination_ems).vm_set_description(destination_vm, description) + rescue => e + @handle.set_state_var(:ae_state_progress, 'message' => e.message) + raise + end + end + end + end + end + end + end +end + +if $PROGRAM_NAME == __FILE__ + ManageIQ::Automate::Transformation::Infrastructure::VM::RedHat::SetDescription.new.main +end diff --git a/content/automate/ManageIQ/Transformation/Infrastructure/VM/rhevm.class/__methods__/setdescription.yaml b/content/automate/ManageIQ/Transformation/Infrastructure/VM/rhevm.class/__methods__/setdescription.yaml new file mode 100644 index 000000000..095fa0e43 --- /dev/null +++ b/content/automate/ManageIQ/Transformation/Infrastructure/VM/rhevm.class/__methods__/setdescription.yaml @@ -0,0 +1,15 @@ +--- +object_type: method +version: 1.0 +object: + attributes: + name: SetDescription + display_name: + description: + scope: instance + language: ruby + location: inline + embedded_methods: + - "/Transformation/Infrastructure/VM/rhevm/Utils" + options: {} + inputs: [] diff --git a/content/automate/ManageIQ/Transformation/Infrastructure/VM/rhevm.class/__methods__/utils.rb b/content/automate/ManageIQ/Transformation/Infrastructure/VM/rhevm.class/__methods__/utils.rb new file mode 100644 index 000000000..3b0a8a96f --- /dev/null +++ b/content/automate/ManageIQ/Transformation/Infrastructure/VM/rhevm.class/__methods__/utils.rb @@ -0,0 +1,172 @@ +# +# Utility library for Red Hat Virtualization +# +require 'ovirtsdk4' + +module ManageIQ + module Automate + module Transformation + module Infrastructure + module VM + module RedHat + class Utils + def initialize(ems, handle = $evm) + @debug = true + @handle = handle + @ems = ems_to_service_model(ems) + @connection = connection(@ems) + end + + def ems_get_export_domain + storage_domains_service.list.select { |domain_service| domain_service.type == OvirtSDK4::StorageDomainType::EXPORT }.first + end + + def vm_import(vm_name, cluster, storage_domain) + target_domain = storage_domains_service.list(:search => "name=#{storage_domain}").first + raise "Can't find storage domain #{storage_domain}" if target_domain.blank? + target_cluster = clusters_service.list(:search => "name=#{cluster}").first + raise "Can't find cluster #{cluster}" if target_cluster.blank? + vm = vm_find_in_export_domain(vm_name) + raise "Can't find VM #{vm_name} on export domain" if vm.blank? + export_domain_vm_service(vm.id).import( + :storage_domain => OvirtSDK4::StorageDomain.new(:id => target_domain.id), + :cluster => OvirtSDK4::Cluster.new(:id => target_cluster.id), + :vm => OvirtSDK4::Vm.new(:id => vm.id) + ) + end + + def vm_delete_from_export_domain(vm_name) + vm = vm_find_in_export_domain(vm_name) + raise "Can't find VM #{vm_name} on export domain" if vm.blank? + @handle.log(:info, "About to remove VM: #{vm_name}") + export_domain_vm_service(vm.id).remove + end + + def vm_find_by_name(vm_name) + vms_service.list(:search => "name=#{vm_name}").first + end + + def vm_set_description(vm, description) + vm_sdk = vm_find_by_name(vm.name) + raise "Can't find VM #{vm_name} in RHV provider" if vm_sdk.blank? + vm_service(vm_sdk.id).update(:description => description) + true + end + + def vm_get_description(vm) + vm_sdk = vm_find_by_name(vm.name) + raise "Can't find VM #{vm_name} in RHV provider" if vm_sdk.blank? + vm_sdk.description + end + + def vm_enable_virtio_scsi(vm) + vm_sdk = vm_find_by_name(vm.name) + raise "Can't find VM #{vm_name}" if vm_sdk.blank? + # Enable virtio_scsi in the VM + vm_service(vm_sdk.id).update(:virtio_scsi => { :enabled => true }) + if vm_sdk.status == OvirtSDK4::VmStatus::DOWN + attachments_service = disk_attachments_service(vm_sdk.id) + attachments_service.list.each do |attachment| + attachments_service.attachment_service(attachment.id).update(:interface => OvirtSDK4::DiskInterface::VIRTIO_SCSI) + end + else + raise "VM must be down to enable virtio_scsi" + end + end + + def vm_set_nic_network(vm, nic, lan) + vm_sdk = vm_find_by_name(vm.name) + raise "Can't find VM #{vm_name} in RHV provider" if vm_sdk.blank? + target_network = vnic_profiles_service.list.select { |vnic_profile| vnic_profile.network.id == lan.uid_ems }.first + raise "Can't find network #{lan.name} in RHV provider" if target_network.blank? + nics_service = vm_nics_service(vm_sdk.id) + target_nic = nics_service.list.select { |nic_sdk| nic_sdk.name == nic.device_name }.first + raise "Can't find nic #{nic.name} for VM #{vm_name}" if target_nic.blank? + nics_service.nic_service(target_nic.id).update(:vnic_profile => target_network) + end + + def vm_get_disk_interfaces(vm) + disks = [] + vm_sdk = vm_find_by_name(vm.name) + raise "Can't find VM #{vm.name} in RHV provider" if vm_sdk.blank? + disk_attachments_service(vm_sdk.id).list.each do |attachment| + disks << attachment.interface + end + disks + end + + private + + def ems_to_service_model(ems) + raise "Invalid EMS" if ems.nil? + # ems could be a numeric id or the ems object itself + unless ems.kind_of?(DRb::DRbObject) && /Manager/.match(ems.type.demodulize) + if ems.to_s =~ /^\d{1,13}$/ + ems = @handle.vmdb(:ems, ems) + end + end + ems + end + + def vm_find_in_export_domain(vm_name) + export_domain_vms_service.list.select { |export_vm| export_vm.name == vm_name }.first + end + + def storage_domains_service + @connection.system_service.storage_domains_service + end + + def clusters_service + @connection.system_service.clusters_service + end + + def export_domain_vms_service + export_domain = ems_get_export_domain + raise "No export domain found!" if export_domain.blank? + storage_domains_service.storage_domain_service(export_domain.id).vms_service + end + + def export_domain_vm_service(id) + export_domain_vms_service.vm_service(id) + end + + def vms_service + @connection.system_service.vms_service + end + + def networks_service + @connection.system_service.networks_service + end + + def vnic_profiles_service + @connection.system_service.vnic_profiles_service + end + + def vm_nics_service(id) + vm_service(id).nics_service + end + + def vm_service(id) + vms_service.vm_service(id) + end + + def disk_attachments_service(id) + vm_service(id).disk_attachments_service + end + + def connection(ems) + connection = OvirtSDK4::Connection.new( + :url => "https://#{ems.hostname}/ovirt-engine/api", + :username => ems.authentication_userid, + :password => ems.authentication_password, + :insecure => true + ) + connection if connection.test(true) + end + end + end + end + end + end + end +end diff --git a/content/automate/ManageIQ/Transformation/Infrastructure/VM/rhevm.class/__methods__/utils.yaml b/content/automate/ManageIQ/Transformation/Infrastructure/VM/rhevm.class/__methods__/utils.yaml new file mode 100644 index 000000000..94612ce92 --- /dev/null +++ b/content/automate/ManageIQ/Transformation/Infrastructure/VM/rhevm.class/__methods__/utils.yaml @@ -0,0 +1,13 @@ +--- +object_type: method +version: 1.0 +object: + attributes: + name: Utils + display_name: + description: + scope: instance + language: ruby + location: inline + options: {} + inputs: [] diff --git a/content/automate/ManageIQ/Transformation/Infrastructure/VM/rhevm.class/_missing.yaml b/content/automate/ManageIQ/Transformation/Infrastructure/VM/rhevm.class/_missing.yaml new file mode 100644 index 000000000..31e5fff8b --- /dev/null +++ b/content/automate/ManageIQ/Transformation/Infrastructure/VM/rhevm.class/_missing.yaml @@ -0,0 +1,12 @@ +--- +object_type: instance +version: 1.0 +object: + attributes: + display_name: + name: ".missing" + inherits: + description: + fields: + - execute: + value: "${#_missing_instance}" diff --git a/content/automate/ManageIQ/Transformation/Infrastructure/VM/vmwarews.class/__class__.yaml b/content/automate/ManageIQ/Transformation/Infrastructure/VM/vmwarews.class/__class__.yaml new file mode 100644 index 000000000..6d886911f --- /dev/null +++ b/content/automate/ManageIQ/Transformation/Infrastructure/VM/vmwarews.class/__class__.yaml @@ -0,0 +1,33 @@ +--- +object_type: class +version: 1.0 +object: + attributes: + description: + display_name: VMware + name: vmwarews + type: + inherits: + visibility: + owner: + schema: + - field: + aetype: method + name: execute + display_name: + datatype: + priority: 1 + owner: + default_value: + substitute: true + message: create + visibility: + collect: + scope: + description: + condition: + on_entry: + on_exit: + on_error: + max_retries: + max_time: diff --git a/content/automate/ManageIQ/Transformation/Infrastructure/VM/vmwarews.class/__methods__/collapsesnapshots.rb b/content/automate/ManageIQ/Transformation/Infrastructure/VM/vmwarews.class/__methods__/collapsesnapshots.rb new file mode 100644 index 000000000..8c0aac0ae --- /dev/null +++ b/content/automate/ManageIQ/Transformation/Infrastructure/VM/vmwarews.class/__methods__/collapsesnapshots.rb @@ -0,0 +1,39 @@ +module ManageIQ + module Automate + module Transformation + module Infrastructure + module VM + module VMware + class CollapseSnapshots + def initialize(handle = $evm) + @handle = handle + end + + def main + task = @handle.root['service_template_transformation_plan_task'] + source_vm = task.source + + if source_vm.snapshots.empty? + @handle.log(:info, "VM '#{source_vm.name}' has no snapshot. Nothing to do.") + elsif task.get_option(:collapse_snapshots) + @handle.log(:info, "VM '#{source_vm.name}' has snapshots and we need to collapse them.") + @handle.log(:info, "Collapsing snapshots for #{source_vm.name}") + source_vm.remove_all_snapshots + else + raise "VM '#{source_vm.name}' has snapshots, but we are not allowed to collapse them. Exiting." + end + rescue => e + @handle.set_state_var(:ae_state_progress, 'message' => e.message) + raise + end + end + end + end + end + end + end +end + +if $PROGRAM_NAME == __FILE__ + ManageIQ::Automate::Transformation::Infrastructure::VM::VMware::CollapseSnapshots.new.main +end diff --git a/content/automate/ManageIQ/Transformation/Infrastructure/VM/vmwarews.class/__methods__/collapsesnapshots.yaml b/content/automate/ManageIQ/Transformation/Infrastructure/VM/vmwarews.class/__methods__/collapsesnapshots.yaml new file mode 100644 index 000000000..098568861 --- /dev/null +++ b/content/automate/ManageIQ/Transformation/Infrastructure/VM/vmwarews.class/__methods__/collapsesnapshots.yaml @@ -0,0 +1,15 @@ +--- +object_type: method +version: 1.0 +object: + attributes: + name: CollapseSnapshots + display_name: + description: + scope: instance + language: ruby + location: inline + embedded_methods: + - "/Transformation/Infrastructure/VM/vmwarews/Utils" + options: {} + inputs: [] diff --git a/content/automate/ManageIQ/Transformation/Infrastructure/VM/vmwarews.class/__methods__/setmigrated.rb b/content/automate/ManageIQ/Transformation/Infrastructure/VM/vmwarews.class/__methods__/setmigrated.rb new file mode 100644 index 000000000..f0a32294d --- /dev/null +++ b/content/automate/ManageIQ/Transformation/Infrastructure/VM/vmwarews.class/__methods__/setmigrated.rb @@ -0,0 +1,53 @@ +module ManageIQ + module Automate + module Transformation + module Infrastructure + module VM + module VMware + class SetMigrated + def initialize(handle = $evm) + @handle = handle + end + + # Taken from / Service / Provisioning / StateMachines / Methods / configure_vm_hostname. + def check_name_collisions(new_name) + name_collisions = Hash.new(0) + @handle.vmdb(:vm).where("name = '#{new_name}'").each do |vm| + name_collisions[:active] += 1 if vm.active + name_collisions[:template] += 1 if vm.template + name_collisions[:retired] += 1 if vm.retired? + name_collisions[:archived] += 1 if vm.archived + name_collisions[:orphaned] += 1 if vm.orphaned + end + name_collisions_summary = "" + name_collisions.each { |k, v| name_collisions_summary += " - #{k}: #{v}" } + name_collisions_summary + end + + def main + exit MIQ_OK + task = @handle.root['service_template_transformation_plan_task'] + source_vm = task.source + new_name = "#{source_vm.name}_migrated" + + name_collisions = check_name_collisions(new_name) + raise "ERROR: #{new_name} already exists #{name_collisions}." if name_collisions.present? + + @handle.log(:info, "Renaming VM #{source_vm.name} to #{new_name}") + result = ManageIQ::Automate::Transformation::Infrastructure::VM::VMware::Utils.vm_rename(source_vm, new_name) + raise "VM rename for #{source_vm.name} to #{new_name} failed" unless result + rescue => e + @handle.set_state_var(:ae_state_progress, 'message' => e.message) + raise + end + end + end + end + end + end + end +end + +if $PROGRAM_NAME == __FILE__ + ManageIQ::Automate::Transformation::Infrastructure::VM::VMware::SetMigrated.new.main +end diff --git a/content/automate/ManageIQ/Transformation/Infrastructure/VM/vmwarews.class/__methods__/setmigrated.yaml b/content/automate/ManageIQ/Transformation/Infrastructure/VM/vmwarews.class/__methods__/setmigrated.yaml new file mode 100644 index 000000000..51ff96a32 --- /dev/null +++ b/content/automate/ManageIQ/Transformation/Infrastructure/VM/vmwarews.class/__methods__/setmigrated.yaml @@ -0,0 +1,15 @@ +--- +object_type: method +version: 1.0 +object: + attributes: + name: SetMigrated + display_name: + description: + scope: instance + language: ruby + location: inline + embedded_methods: + - "/Transformation/Infrastructure/VM/vmwarews/Utils" + options: {} + inputs: [] diff --git a/content/automate/ManageIQ/Transformation/Infrastructure/VM/vmwarews.class/__methods__/utils.rb b/content/automate/ManageIQ/Transformation/Infrastructure/VM/vmwarews.class/__methods__/utils.rb new file mode 100644 index 000000000..c40e00b29 --- /dev/null +++ b/content/automate/ManageIQ/Transformation/Infrastructure/VM/vmwarews.class/__methods__/utils.rb @@ -0,0 +1,66 @@ +module ManageIQ + module Automate + module Transformation + module Infrastructure + module VM + module VMware + class Utils + require 'rbvmomi' + + def initialize(handle = $evm) + @handle = handle + end + + def main + end + + def connect_to_provider(ems) + ipaddress = ems.ipaddress + ipaddress ||= ems.hostname + RbVmomi::VIM.connect(:host => ipaddress, :user => ems.authentication_userid, :password => ems.authentication_password, :insecure => true) + end + + def vm_get_ref(vim, vm) + dc = vim.serviceInstance.find_datacenter(vm.v_owning_datacenter) + raise "Datacenter '#{vm.datacenter.name}' not found in vCenter" unless dc + vim.serviceInstance.content.searchIndex.FindByUuid(:datacenter => dc, :uuid => vm.uid_ems, :vmSearch => true, :instanceUuid => false) + end + + def self.get_vcenter_fingerprint(ems, handle = $evm) + command = "openssl s_client -connect #{ems.hostname}:443 2>\/dev\/null | openssl x509 -noout -fingerprint -sha1" + ssl_fingerprint = `#{command}` + fingerprint = ssl_fingerprint[17..ssl_fingerprint.size - 2] + handle.log(:info, "vCenter fingerprint: #{fingerprint}") + fingerprint + end + + def self.vm_rename(vm, new_name, handle = $evm) + ems = vm.ext_management_system + ems_endpoint = ems.ipaddress || ems.hostname + vim = RbVmomi::VIM.connect(:host => ems_endpoint, :user => ems.authentication_userid, :password => ems.authentication_password, :insecure => true) + + sleep 2 + dc = vim.serviceInstance.find_datacenter(vm.v_owning_datacenter) + raise "Datacenter '#{vm.v_owning_datacenter}' not found in vCenter" unless dc + + sleep 2 + vim_vm = dc.find_vm(vm.name) + raise "Unable to locate #{vm.name} in data center #{dc}" unless vim_vm + begin + sleep 2 + vim_vm.ReconfigVM_Task(:spec => RbVmomi::VIM::VirtualMachineConfigSpec(:name => new_name)).wait_for_completion + sleep 2 + dc.find_vm(new_name) + handle.log(:info, "Successfully renamed #{options[:source_vm]} to #{options[:new_name]}.") + rescue + return false + end + true + end + end + end + end + end + end + end +end diff --git a/content/automate/ManageIQ/Transformation/Infrastructure/VM/vmwarews.class/__methods__/utils.yaml b/content/automate/ManageIQ/Transformation/Infrastructure/VM/vmwarews.class/__methods__/utils.yaml new file mode 100644 index 000000000..94612ce92 --- /dev/null +++ b/content/automate/ManageIQ/Transformation/Infrastructure/VM/vmwarews.class/__methods__/utils.yaml @@ -0,0 +1,13 @@ +--- +object_type: method +version: 1.0 +object: + attributes: + name: Utils + display_name: + description: + scope: instance + language: ruby + location: inline + options: {} + inputs: [] diff --git a/content/automate/ManageIQ/Transformation/Infrastructure/VM/vmwarews.class/_missing.yaml b/content/automate/ManageIQ/Transformation/Infrastructure/VM/vmwarews.class/_missing.yaml new file mode 100644 index 000000000..31e5fff8b --- /dev/null +++ b/content/automate/ManageIQ/Transformation/Infrastructure/VM/vmwarews.class/_missing.yaml @@ -0,0 +1,12 @@ +--- +object_type: instance +version: 1.0 +object: + attributes: + display_name: + name: ".missing" + inherits: + description: + fields: + - execute: + value: "${#_missing_instance}" diff --git a/content/automate/ManageIQ/Transformation/Infrastructure/__namespace__.yaml b/content/automate/ManageIQ/Transformation/Infrastructure/__namespace__.yaml new file mode 100644 index 000000000..58e960d61 --- /dev/null +++ b/content/automate/ManageIQ/Transformation/Infrastructure/__namespace__.yaml @@ -0,0 +1,10 @@ +--- +object_type: namespace +version: 1.0 +object: + attributes: + name: Infrastructure + description: + display_name: + priority: + enabled: diff --git a/content/automate/ManageIQ/Transformation/StateMachines/VMTransformation.class/transformation.yaml b/content/automate/ManageIQ/Transformation/StateMachines/VMTransformation.class/transformation.yaml index 4a185ddbc..f83a8cc7f 100644 --- a/content/automate/ManageIQ/Transformation/StateMachines/VMTransformation.class/transformation.yaml +++ b/content/automate/ManageIQ/Transformation/StateMachines/VMTransformation.class/transformation.yaml @@ -11,35 +11,35 @@ object: - State1: value: "/Transformation/Common/AssessTransformation" on_entry: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 1, description - => "Assess Migration") + => "Assess Migration", task_message => "Validating") on_exit: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 1, description - => "Assess Migration") + => "Assess Migration", task_message => "Validating") on_error: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 1, description - => "Assess Migration") + => "Assess Migration", task_message => "Validating") - State2: - value: "/Transformation/Infrastructure/VM/${state_var#source_ems_type}/CollapseSnapshots" + value: "/Transformation/Common/AcquireTransformationHost" on_entry: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 1, description - => "Collapse Snapshots") + => "Acquire Transformation Host", task_message => "Pre-migration") on_exit: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 1, description - => "Collapse Snapshots") + => "Acquire Transformation Host", task_message => "Pre-migration") on_error: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 1, description - => "Collapse Snapshots") + => "Acquire Transformation Host", task_message => "Pre-migration") - State3: value: "/Transformation/Infrastructure/VM/Common/PowerOff" on_entry: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 1, description - => "Power off") + => "Power off", task_message => "Pre-migration") on_exit: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 1, description - => "Power off") + => "Power off", task_message => "Pre-migration") on_error: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 1, description - => "Power off") + => "Power off", task_message => "Pre-migration") - State4: - value: "/Transformation/Common/AcquireTransformationHost" + value: "/Transformation/Infrastructure/VM/${state_var#source_ems_type}/CollapseSnapshots" on_entry: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 1, description - => "Acquire Transformation Host") + => "Collapse Snapshots", task_message => "Pre-migration") on_exit: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 1, description - => "Acquire Transformation Host") + => "Collapse Snapshots", task_message => "Pre-migration") on_error: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 1, description - => "Acquire Transformation Host") + => "Collapse Snapshots", task_message => "Pre-migration") - State5: value: "/Transformation/StateMachines/VMTransformation/${state_var#transformation_type}_${state_var#transformation_method}?state_ancestry=${#state_ancestry}/${#ae_state}" on_entry: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 95, description @@ -51,8 +51,11 @@ object: - State6: value: "/Transformation/Infrastructure/VM/${state_var#source_ems_type}/SetMigrated" on_entry: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 1, description - => "Mark source as migrated") + => "Mark source as migrated", task_message => "Migrating") on_exit: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 1, description - => "Mark source as migrated") + => "Mark source as migrated", task_message => "Migrating") on_error: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 1, description - => "Mark source as migrated") + => "Mark source as migrated", task_message => "Migrating") + - State7: + on_entry: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 1, description + => "Virtual machine migrated", task_message => "Migration complete") diff --git a/content/automate/ManageIQ/Transformation/StateMachines/VMTransformation.class/vmwarews2rhevm_vddk.yaml b/content/automate/ManageIQ/Transformation/StateMachines/VMTransformation.class/vmwarews2rhevm_vddk.yaml index 06958e30c..7af947659 100644 --- a/content/automate/ManageIQ/Transformation/StateMachines/VMTransformation.class/vmwarews2rhevm_vddk.yaml +++ b/content/automate/ManageIQ/Transformation/StateMachines/VMTransformation.class/vmwarews2rhevm_vddk.yaml @@ -11,83 +11,51 @@ object: - State1: value: "/Transformation/TransformationHosts/${state_var#transformation_host_type}/VMTransform_${state_var#source_ems_type}2${state_var#destination_ems_type}_${state_var#transformation_method}" on_entry: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 1, description - => "Convert disks") + => "Convert disks", task_message => "Migrating") on_exit: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 1, description - => "Convert disks") + => "Convert disks", task_message => "Migrating") on_error: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 1, description - => "Convert disks") + => "Convert disks", task_message => "Migrating") - State2: value: "/Transformation/TransformationHosts/${state_var#transformation_host_type}/VMCheckTransformed_${state_var#source_ems_type}2${state_var#destination_ems_type}_${state_var#transformation_method}" - on_entry: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 70, description - => "Convert disks") - on_exit: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 70, description - => "Convert disks") - on_error: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 70, description - => "Convert disks") + on_entry: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 85, description + => "Convert disks", task_message => "Migrating") + on_exit: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 85, description + => "Convert disks", task_message => "Migrating") + on_error: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 85, description + => "Convert disks", task_message => "Migrating") max_retries: '1500' - State3: - value: "/Transformation/Infrastructure/VM/${state_var#destination_ems_type}/Import" - on_entry: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 1, description - => "Import VM") - on_exit: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 1, description - => "Import VM") - on_error: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 1, description - => "Import VM") + value: "/Transformation/Infrastructure/VM/${state_var#destination_ems_type}/CheckVmInInventory" + on_entry: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 4, description + => "Refresh inventory", task_message => "Migrating") + on_exit: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 4, description + => "Refresh inventory", task_message => "Migrating") + on_error: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 4, description + => "Refresh inventory", task_message => "Migrating") + max_retries: '200' - State4: - value: "/Transformation/Infrastructure/VM/${state_var#destination_ems_type}/CheckImported" - on_entry: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 18, description - => "Import VM") - on_exit: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 18, description - => "Import VM") - on_error: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 18, description - => "Import VM") - max_retries: '400' - - State5: value: "/Transformation/Infrastructure/VM/${state_var#destination_ems_type}/SetDescription" on_entry: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 1, description - => "Update description of VM") - on_exit: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 1, description - => "Update description of VM") - on_error: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 1, description - => "Update description of VM") - - State6: - value: "/Transformation/Infrastructure/VM/${state_var#destination_ems_type}/EnableVirtIOSCSI" - on_entry: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 1, description - => "Enable Virtio-SCSI for VM") + => "Update description of VM", task_message => "Migrating") on_exit: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 1, description - => "Enable Virtio-SCSI for VM") + => "Update description of VM", task_message => "Migrating") on_error: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 1, description - => "Enable Virtio-SCSI for VM") - - State7: - value: "/Transformation/Infrastructure/VM/${state_var#destination_ems_type}/RewireNetworks" - on_entry: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 1, description - => "Configure network of VM") - on_exit: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 1, description - => "Configure network of VM") - on_error: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 1, description - => "Configure network of VM") - - State8: + => "Update description of VM", task_message => "Migrating") + - State5: value: "/Transformation/Infrastructure/VM/Common/PowerOn" on_entry: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 1, description - => "Power-on VM") + => "Power-on VM", task_message => "Migrating") on_exit: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 1, description - => "Power-on VM") + => "Power-on VM", task_message => "Migrating") on_error: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 1, description - => "Power-on VM") - - State9: + => "Power-on VM", task_message => "Migrating") + - State6: value: "/Transformation/Infrastructure/VM/${state_var#destination_ems_type}/CheckPoweredOn" - on_entry: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 5, description - => "Power-on VM") - on_exit: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 5, description - => "Power-on VM") - on_error: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 5, description - => "Power-on VM") + on_entry: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 7, description + => "Power-on VM", task_message => "Migrating") + on_exit: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 7, description + => "Power-on VM", task_message => "Migrating") + on_error: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 7, description + => "Power-on VM", task_message => "Migrating") max_retries: '200' - - State10: - value: "/Transformation/Infrastructure/VM/${state_var#destination_ems_type}/DeleteFromExportDomain" - on_entry: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 1, description - => "Clean up temporary assets") - on_exit: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 1, description - => "Clean up temporary assets") - on_error: /System/CommonMethods/MiqAe.WeightedUpdateStatus(weight => 1, description - => "Clean up temporary assets") diff --git a/content/automate/ManageIQ/Transformation/TransformationHosts/Common.class/__methods__/utils.rb b/content/automate/ManageIQ/Transformation/TransformationHosts/Common.class/__methods__/utils.rb new file mode 100644 index 000000000..03158426d --- /dev/null +++ b/content/automate/ManageIQ/Transformation/TransformationHosts/Common.class/__methods__/utils.rb @@ -0,0 +1,57 @@ +module ManageIQ + module Automate + module Transformation + module TransformationHosts + module Common + class Utils + def initialize(handle = $evm) + @debug = true + @handle = handle + end + + def main + end + + def self.get_runners_count_by_host(host, handle = $evm) + handle.vmdb(:service_template_transformation_plan_task).where(:state => 'active').select { |task| task.get_option(:transformation_host) == host }.size + end + + def self.transformation_hosts(ems, method, factory_config) + thosts = [] + ems.hosts.each do |host| + next unless host.tagged_with?('v2v_transformation_host', 'true') && host.tagged_with?('v2v_transformation_method', method) + thosts << { + :host => host, + :runners => { + :current => get_runners_count_by_host(host), + :maximum => host.custom_get('Max Transformation Runners') || factory_config['transformation_host_max_runners'] || 1 + } + } + end + thosts.sort_by! { |th| th[:runners][:current] } + end + + def self.eligible_transformation_hosts(ems, method, factory_config) + transformation_hosts(ems, method, factory_config).select { |thost| thost[:runners][:current] < thost[:runners][:maximum] } + end + + def self.get_runners_count_by_ems(ems, method, factory_config) + transformation_hosts(ems, method, factory_config).inject(0) { |sum, thost| sum + thost[:runners][:current] } + end + + def self.get_transformation_host(ems, method, factory_config) + ems_max_runners = ems.custom_get('Max Transformation Runners') || factory_config['ems_max_runners'] || 1 + ems_cur_runners = get_runners_count_by_ems(ems, method, factory_config) + transformation_host = ems_cur_runners < ems_max_runners ? eligible_transformation_hosts(ems, method, factory_config).first[:host] : nil + transformation_host + end + end + end + end + end + end +end + +if $PROGRAM_NAME == __FILE__ + ManageIQ::Automate::Transformation::TransformationHosts::Common::Utils.new.main +end diff --git a/content/automate/ManageIQ/Transformation/TransformationHosts/Common.class/__methods__/utils.yaml b/content/automate/ManageIQ/Transformation/TransformationHosts/Common.class/__methods__/utils.yaml new file mode 100644 index 000000000..94612ce92 --- /dev/null +++ b/content/automate/ManageIQ/Transformation/TransformationHosts/Common.class/__methods__/utils.yaml @@ -0,0 +1,13 @@ +--- +object_type: method +version: 1.0 +object: + attributes: + name: Utils + display_name: + description: + scope: instance + language: ruby + location: inline + options: {} + inputs: [] diff --git a/content/automate/ManageIQ/Transformation/TransformationHosts/ovirt_host.class/__methods__/rolecheck.rb b/content/automate/ManageIQ/Transformation/TransformationHosts/ovirt_host.class/__methods__/rolecheck.rb new file mode 100644 index 000000000..0b6fa49e5 --- /dev/null +++ b/content/automate/ManageIQ/Transformation/TransformationHosts/ovirt_host.class/__methods__/rolecheck.rb @@ -0,0 +1,27 @@ +module ManageIQ + module Automate + module Transformation + module TransformationHost + module OVirtHost + class RoleCheck + def initialize(handle = $evm) + @handle = handle + end + + def main + playbook = "/usr/share/doc/ovirt-ansible-v2v-conversion-host-1.2.0/examples/conversion_host_check.yml" + extra_vars = { :v2v_manageiq_conversion_host_check => true } + + result = Transformation::TransformationHosts::OVirtHost::Utils.ansible_playbook(@handle.root['host'], playbook, extra_vars) + raise 'Conversion Host role is not enabled.' unless result[:rc].zero? + end + end + end + end + end + end +end + +if $PROGRAM_NAME == __FILE__ + ManageIQ::Automate::Transformation::TransformationHost::OVirtHost::RoleCheck.new.main +end diff --git a/content/automate/ManageIQ/Transformation/TransformationHosts/ovirt_host.class/__methods__/rolecheck.yaml b/content/automate/ManageIQ/Transformation/TransformationHosts/ovirt_host.class/__methods__/rolecheck.yaml index 432952a39..4f233d034 100644 --- a/content/automate/ManageIQ/Transformation/TransformationHosts/ovirt_host.class/__methods__/rolecheck.yaml +++ b/content/automate/ManageIQ/Transformation/TransformationHosts/ovirt_host.class/__methods__/rolecheck.yaml @@ -4,39 +4,12 @@ version: 1.0 object: attributes: name: RoleCheck - display_name: '' + display_name: description: scope: instance language: ruby - location: playbook - data: - options: - :repository_id: '1' - :playbook_id: '1' - :credential_id: '11' - :verbosity: '0' - :execution_ttl: '5' - :hosts: "${/#host.name}" - :log_output: on_error - :become_enabled: true - inputs: - - field: - aetype: - name: v2v_manageiq_conversion_host_check - display_name: - datatype: string - priority: 0 - owner: - default_value: 'true' - substitute: true - message: create - visibility: - collect: - scope: - description: - condition: - on_entry: - on_exit: - on_error: - max_retries: - max_time: + location: inline + embedded_methods: + - "/Transformation/TransformationHosts/ovirt_host/Utils" + options: {} + inputs: [] diff --git a/content/automate/ManageIQ/Transformation/TransformationHosts/ovirt_host.class/__methods__/roledisable.rb b/content/automate/ManageIQ/Transformation/TransformationHosts/ovirt_host.class/__methods__/roledisable.rb new file mode 100644 index 000000000..0c78a02fa --- /dev/null +++ b/content/automate/ManageIQ/Transformation/TransformationHosts/ovirt_host.class/__methods__/roledisable.rb @@ -0,0 +1,28 @@ +module ManageIQ + module Automate + module Transformation + module TransformationHost + module OVirtHost + class RoleDisable + def initialize(handle = $evm) + @handle = handle + end + + def main + host = @handle.root['host'] + playbook = "/usr/share/doc/ovirt-ansible-v2v-conversion-host-1.2.0/examples/conversion_host_disable.yml" + extra_vars = {} + + result = Transformation::TransformationHosts::OVirtHost::Utils.ansible_playbook(host, playbook, extra_vars) + @handle.log(:error, "Failed to disable Conversion Host role for '#{host.name}'.") unless result[:rc].zero? + end + end + end + end + end + end +end + +if $PROGRAM_NAME == __FILE__ + ManageIQ::Automate::Transformation::TransformationHost::OVirtHost::RoleDisable.new.main +end diff --git a/content/automate/ManageIQ/Transformation/TransformationHosts/ovirt_host.class/__methods__/roledisable.yaml b/content/automate/ManageIQ/Transformation/TransformationHosts/ovirt_host.class/__methods__/roledisable.yaml index 3ddac72ad..4a30eb7c6 100644 --- a/content/automate/ManageIQ/Transformation/TransformationHosts/ovirt_host.class/__methods__/roledisable.yaml +++ b/content/automate/ManageIQ/Transformation/TransformationHosts/ovirt_host.class/__methods__/roledisable.yaml @@ -4,19 +4,12 @@ version: 1.0 object: attributes: name: RoleDisable - display_name: '' + display_name: description: scope: instance language: ruby - location: playbook - data: - options: - :repository_id: '1' - :playbook_id: '2' - :credential_id: '11' - :verbosity: '' - :execution_ttl: '10' - :hosts: "${/#host.name}" - :log_output: on_error - :become_enabled: true + location: inline + embedded_methods: + - "/Transformation/TransformationHosts/ovirt_host/Utils" + options: {} inputs: [] diff --git a/content/automate/ManageIQ/Transformation/TransformationHosts/ovirt_host.class/__methods__/roleenable.rb b/content/automate/ManageIQ/Transformation/TransformationHosts/ovirt_host.class/__methods__/roleenable.rb new file mode 100644 index 000000000..067aef809 --- /dev/null +++ b/content/automate/ManageIQ/Transformation/TransformationHosts/ovirt_host.class/__methods__/roleenable.rb @@ -0,0 +1,35 @@ +module ManageIQ + module Automate + module Transformation + module TransformationHost + module OVirtHost + class RoleEnable + def initialize(handle = $evm) + @handle = handle + end + + def main + host = @handle.root['host'] + playbook = "/usr/share/doc/ovirt-ansible-v2v-conversion-host-1.2.0/examples/conversion_host_enable.yml" + extra_vars = {} + extra_vars[:v2v_vddk_package_name] = "VMware-vix-disklib-stable.tar.gz" + extra_vars[:v2v_vddk_package_url] = "http://#{host.ext_management_system.hostname}/vddk/#{extra_vars[:v2v_vddk_package_name]}" + # TODO: Remove the RPM repositories as they are provided by oVirt/RHV + extra_vars[:v2v_repo_rpms_name] = "v2v-nbdkit-rpms" + extra_vars[:v2v_repo_rpms_url] = "http://#{host.ext_management_system.hostname}/rpms/#{extra_vars[:v2v_repo_rpms_name]}" + extra_vars[:v2v_repo_srpms_name] = "v2v-nbdkit-src-rpms" + extra_vars[:v2v_repo_srpms_url] = "http://#{host.ext_management_system.hostname}/rpms/#{extra_vars[:v2v_repo_srpms_name]}" + + result = Transformation::TransformationHosts::OVirtHost::Utils.ansible_playbook(host, playbook, extra_vars) + @handle.log(:error, "Failed to enable Conversion Host role for '#{host.name}'.") unless result[:rc].zero? + end + end + end + end + end + end +end + +if $PROGRAM_NAME == __FILE__ + ManageIQ::Automate::Transformation::TransformationHost::OVirtHost::RoleEnable.new.main +end diff --git a/content/automate/ManageIQ/Transformation/TransformationHosts/ovirt_host.class/__methods__/roleenable.yaml b/content/automate/ManageIQ/Transformation/TransformationHosts/ovirt_host.class/__methods__/roleenable.yaml index 9a03cc4bd..78708cd5f 100644 --- a/content/automate/ManageIQ/Transformation/TransformationHosts/ovirt_host.class/__methods__/roleenable.yaml +++ b/content/automate/ManageIQ/Transformation/TransformationHosts/ovirt_host.class/__methods__/roleenable.yaml @@ -4,159 +4,12 @@ version: 1.0 object: attributes: name: RoleEnable - display_name: '' + display_name: description: scope: instance language: ruby - location: playbook - data: - options: - :repository_id: '1' - :playbook_id: '3' - :credential_id: '11' - :verbosity: '' - :execution_ttl: '20' - :hosts: "${/#host.name}" - :log_output: on_error - :become_enabled: true - inputs: - - field: - aetype: - name: v2v_content_server - display_name: - datatype: string - priority: 0 - owner: - default_value: - substitute: true - message: create - visibility: - collect: - scope: - description: - condition: - on_entry: - on_exit: - on_error: - max_retries: - max_time: - - field: - aetype: - name: v2v_vddk_package_url - display_name: - datatype: string - priority: 1 - owner: - default_value: - substitute: true - message: create - visibility: - collect: - scope: - description: - condition: - on_entry: - on_exit: - on_error: - max_retries: - max_time: - - field: - aetype: - name: v2v_vddk_package_name - display_name: - datatype: string - priority: 2 - owner: - default_value: - substitute: true - message: create - visibility: - collect: - scope: - description: - condition: - on_entry: - on_exit: - on_error: - max_retries: - max_time: - - field: - aetype: - name: v2v_repo_rpms_url - display_name: - datatype: string - priority: 3 - owner: - default_value: - substitute: true - message: create - visibility: - collect: - scope: - description: - condition: - on_entry: - on_exit: - on_error: - max_retries: - max_time: - - field: - aetype: - name: v2v_repo_rpms_name - display_name: - datatype: string - priority: 4 - owner: - default_value: - substitute: true - message: create - visibility: - collect: - scope: - description: - condition: - on_entry: - on_exit: - on_error: - max_retries: - max_time: - - field: - aetype: - name: v2v_repo_srpms_url - display_name: - datatype: string - priority: 5 - owner: - default_value: - substitute: true - message: create - visibility: - collect: - scope: - description: - condition: - on_entry: - on_exit: - on_error: - max_retries: - max_time: - - field: - aetype: - name: v2v_repo_srpms_name - display_name: - datatype: string - priority: 6 - owner: - default_value: - substitute: true - message: create - visibility: - collect: - scope: - description: - condition: - on_entry: - on_exit: - on_error: - max_retries: - max_time: + location: inline + embedded_methods: + - "/Transformation/TransformationHosts/ovirt_host/Utils" + options: {} + inputs: [] diff --git a/content/automate/ManageIQ/Transformation/TransformationHosts/ovirt_host.class/__methods__/utils.rb b/content/automate/ManageIQ/Transformation/TransformationHosts/ovirt_host.class/__methods__/utils.rb new file mode 100644 index 000000000..b7fad90ca --- /dev/null +++ b/content/automate/ManageIQ/Transformation/TransformationHosts/ovirt_host.class/__methods__/utils.rb @@ -0,0 +1,77 @@ +module ManageIQ + module Automate + module Transformation + module TransformationHosts + module OVirtHost + class Utils + def initialize(handle = $evm) + @handle = handle + end + + def self.remote_command(host, command, stdin = nil, run_as = nil) + require "net/ssh" + command = "sudo -u #{run_as} #{command}" unless run_as.nil? + success, stdout, stderr, exit_code = true, '', '', nil + Net::SSH.start(host.name, host.authentication_userid, :password => host.authentication_password) do |ssh| + channel = ssh.open_channel do |chan| + chan.request_pty unless run_as.nil? + chan.exec(command) do |ch, exec_success| + if exec_success + ch.on_data do |_, data| + stdout += data.to_s + end + ch.on_extended_data do |_, data| + stderr += data.to_s + end + ch.on_request("exit-status") do |_, data| + exit_code = data.read_long + end + unless stdin.nil? + ch.send_data(stdin) + ch.eof! + end + else + success = false + stderr = "Could not execute command." + end + end + end + channel.wait + end + { :success => success, :stdout => stdout, :stderr => stderr, :rc => exit_code } + end + + def self.ansible_playbook(host, playbook, extra_vars) + require "net/ssh" + command = "ansible-playbook -i #{host.name}, #{playbook}" + extra_vars.each { |k, v| command += " -e '#{k}=#{v}'" } + success, stdout, stderr, exit_code = true, '', '', nil + Net::SSH.start(host.ext_management_system.hostname, 'root') do |ssh| + channel = ssh.open_channel do |chan| + chan.exec(command) do |ch, exec_success| + if exec_success + ch.on_data do |_, data| + stdout += data.to_s + end + ch.on_extended_data do |_, data| + stderr += data.to_s + end + ch.on_request("exit-status") do |_, data| + exit_code = data.read_long + end + else + success = false + stderr = "Could not execute command." + end + end + end + channel.wait + end + { :success => success, :stdout => stdout, :stderr => stderr, :rc => exit_code } + end + end + end + end + end + end +end diff --git a/content/automate/ManageIQ/Transformation/TransformationHosts/ovirt_host.class/__methods__/utils.yaml b/content/automate/ManageIQ/Transformation/TransformationHosts/ovirt_host.class/__methods__/utils.yaml new file mode 100644 index 000000000..94612ce92 --- /dev/null +++ b/content/automate/ManageIQ/Transformation/TransformationHosts/ovirt_host.class/__methods__/utils.yaml @@ -0,0 +1,13 @@ +--- +object_type: method +version: 1.0 +object: + attributes: + name: Utils + display_name: + description: + scope: instance + language: ruby + location: inline + options: {} + inputs: [] diff --git a/content/automate/ManageIQ/Transformation/TransformationHosts/ovirt_host.class/__methods__/vmchecktransformed_vmwarews2rhevm_vddk.rb b/content/automate/ManageIQ/Transformation/TransformationHosts/ovirt_host.class/__methods__/vmchecktransformed_vmwarews2rhevm_vddk.rb new file mode 100644 index 000000000..7a4d43640 --- /dev/null +++ b/content/automate/ManageIQ/Transformation/TransformationHosts/ovirt_host.class/__methods__/vmchecktransformed_vmwarews2rhevm_vddk.rb @@ -0,0 +1,83 @@ +module ManageIQ + module Automate + module Transformation + module TransformationHost + module OVirtHost + class VMCheckTransformedVmwarews2rhevmVddk + def initialize(handle = $evm) + @debug = true + @handle = handle + end + + def main + require 'json' + + factory_config = @handle.get_state_var(:factory_config) + raise "No factory config found. Aborting." if factory_config.nil? + + task = @handle.root['service_template_transformation_plan_task'] + + # Retrieve transformation host + transformation_host = @handle.vmdb(:host).find_by(:id => task.get_option(:transformation_host_id)) + + # Retrieve state of virt-v2v + result = Transformation::TransformationHosts::OVirtHost::Utils.remote_command(transformation_host, "cat '#{task.get_option(:virtv2v_wrapper)['state_file']}'") + raise result[:stderr] unless result[:success] && !result[:stdout].empty? + virtv2v_state = JSON.parse(result[:stdout]) + @handle.log(:info, "VirtV2V State: #{virtv2v_state.inspect}") + + # Retrieve disks array + virtv2v_disks = task[:options][:virtv2v_disks] + @handle.log(:info, "Disks: #{virtv2v_disks.inspect}") + + if virtv2v_state['finished'].nil? + # Update the progress of each disk + virtv2v_disks.each do |disk| + matching_disks = virtv2v_state['disks'].select { |d| d['path'] == disk[:path] } + raise "No disk matches '#{disk[:path]}'. Aborting." if matching_disks.length.zero? + raise "More than one disk matches '#{disk[:path]}'. Aborting." if matching_disks.length > 1 + disk[:percent] = matching_disks.first['progress'] + end + converted_disks = virtv2v_disks.reject { |d| d[:percent].zero? } + @handle.log(:info, "Converted disks: #{converted_disks.inspect}") + if converted_disks.empty? + @handle.set_state_var(:ae_state_progress, 'message' => "Disks transformation is initializing.", 'percent' => 1) + else + percent = 0 + converted_disks.each { |disk| percent += (disk[:percent].to_f * disk[:weight].to_f / 100.0) } + message = "Converting disk #{converted_disks.length} / #{virtv2v_disks.length} [#{percent.round(2)}%]." + @handle.set_state_var(:ae_state_progress, 'message' => message, 'percent' => percent.round(2)) + end + else + task.set_option(:virtv2v_finished_on, Time.now.utc.strftime('%Y%m%d_%H%M')) + if virtv2v_state['return_code'].zero? + virtv2v_disks.each { |d| d[:percent] = 100 } + @handle.set_state_var(:ae_state_progress, 'message' => 'Disks transformation succeeded.', 'percent' => 100) + else + @handle.set_state_var(:ae_state_progress, 'message' => 'Disks transformation succeeded.') + raise "Disks transformation failed." + end + end + + task.set_option(:virtv2v_disks, virtv2v_disks) + + if task.get_option(:virtv2v_finished_on).nil? + @handle.root['ae_result'] = 'retry' + @handle.root['ae_retry_server_affinity'] = true + @handle.root['ae_retry_interval'] = factory_config['vmtransformation_check_interval'] || '15.seconds' + @handle.log(:info, "Disk transformation is not finished. Checking in #{@handle.root['ae_retry_interval']}") + end + rescue => e + @handle.set_state_var(:ae_state_progress, 'message' => e.message) + raise + end + end + end + end + end + end +end + +if $PROGRAM_NAME == __FILE__ + ManageIQ::Automate::Transformation::TransformationHost::OVirtHost::VMCheckTransformedVmwarews2rhevmVddk.new.main +end diff --git a/content/automate/ManageIQ/Transformation/TransformationHosts/ovirt_host.class/__methods__/vmchecktransformed_vmwarews2rhevm_vddk.yaml b/content/automate/ManageIQ/Transformation/TransformationHosts/ovirt_host.class/__methods__/vmchecktransformed_vmwarews2rhevm_vddk.yaml new file mode 100644 index 000000000..9ea4155ee --- /dev/null +++ b/content/automate/ManageIQ/Transformation/TransformationHosts/ovirt_host.class/__methods__/vmchecktransformed_vmwarews2rhevm_vddk.yaml @@ -0,0 +1,15 @@ +--- +object_type: method +version: 1.0 +object: + attributes: + name: VMCheckTransformed_vmwarews2rhevm_vddk + display_name: + description: + scope: instance + language: ruby + location: inline + embedded_methods: + - "/Transformation/TransformationHosts/ovirt_host/Utils" + options: {} + inputs: [] diff --git a/content/automate/ManageIQ/Transformation/TransformationHosts/ovirt_host.class/__methods__/vmtransform_vmwarews2rhevm_vddk.rb b/content/automate/ManageIQ/Transformation/TransformationHosts/ovirt_host.class/__methods__/vmtransform_vmwarews2rhevm_vddk.rb new file mode 100644 index 000000000..6e7e78f57 --- /dev/null +++ b/content/automate/ManageIQ/Transformation/TransformationHosts/ovirt_host.class/__methods__/vmtransform_vmwarews2rhevm_vddk.rb @@ -0,0 +1,103 @@ +module ManageIQ + module Automate + module Transformation + module TransformationHost + module OVirtHost + class VMTransformVmwarews2rhevmVddk + def initialize(handle = $evm) + @debug = false + @handle = handle + end + + def main + require 'json' + + factory_config = @handle.get_state_var(:factory_config) + raise "No factory config found. Aborting." if factory_config.nil? + + task = @handle.root['service_template_transformation_plan_task'] + source_vm = task.source + source_cluster = source_vm.ems_cluster + source_ems = source_vm.ext_management_system + destination_cluster = task.transformation_destination(source_cluster) + destination_ems = destination_cluster.ext_management_system + destination_storage = task.transformation_destination(source_vm.hardware.disks.select { |d| d.device_type == 'disk' }.first.storage) + raise "Invalid destination EMS type: #{destination_ems.emstype}. Aborting." unless destination_ems.emstype == "rhevm" + + # Get or create the virt-v2v start timestamp + start_timestamp = task.get_option(:virtv2v_started_on) || Time.now.utc.strftime('%Y-%m-%d %H:%M:%S') + + # Retrieve transformation host + transformation_host = @handle.vmdb(:host).find_by(:id => task.get_option(:transformation_host_id)) + + @handle.log(:info, "Transformation - Started On: #{start_timestamp}") + + max_runners = destination_ems.custom_get('Max Transformation Runners') || factory_config['max_transformation_runners_by_ems'] || 1 + if Transformation::TransformationHosts::Common::Utils.get_runners_count_by_ems(destination_ems, @handle.get_state_var(:transformation_method), factory_config) >= max_runners + @handle.log("Too many transformations running: (#{max_runners}). Retrying.") + else + # Collect the VMware connection information + vmware_uri = "vpx://" + vmware_uri += "#{source_ems.authentication_userid.gsub('@', '%40')}@#{source_ems.hostname}/" + vmware_uri += "#{source_cluster.v_parent_datacenter.gsub(' ', '%20')}/#{source_cluster.name.gsub(' ', '%20')}/#{source_vm.host.uid_ems}" + vmware_uri += "?no_verify=1" + + # Collect information about the disks to convert + virtv2v_disks = task[:options][:virtv2v_disks] + source_disks = virtv2v_disks.map { |disk| disk[:path] } + @handle.log(:info, "Source VM Disks: #{source_disks}") + + # Collect information about the network mappings + virtv2v_networks = task[:options][:virtv2v_networks] + + wrapper_options = { + :vm_name => source_vm.name, + :transport_method => 'vddk', + :vmware_fingerprint => Transformation::Infrastructure::VM::VMware::Utils.get_vcenter_fingerprint(source_ems), + :vmware_uri => vmware_uri, + :vmware_password => source_ems.authentication_password, + :rhv_url => "https://#{destination_ems.hostname}/ovirt-engine/api", + :rhv_cluster => destination_cluster.name, + :rhv_storage => destination_storage.name, + :rhv_password => destination_ems.authentication_password, + :source_disks => source_disks, + :network_mappings => virtv2v_networks, + :install_drivers => true + } + + # WARNING: Enable at your own risk, as it may lead to sensitive data leak + # @handle.log(:info, "JSON Input:\n#{JSON.pretty_generate(wrapper_options)}") if @debug + + @handle.log(:info, "Connecting to #{transformation_host.name} as #{transformation_host.authentication_userid}") if @debug + @handle.log(:info, "Executing '/usr/bin/virt-v2v-wrapper.py'") + result = Transformation::TransformationHosts::OVirtHost::Utils.remote_command(transformation_host, "/usr/bin/virt-v2v-wrapper.py", wrapper_options.to_json) + raise result[:stderr] unless result[:rc].zero? + + # Record the wrapper files path + @handle.log(:info, "Command stdout: #{result[:stdout]}") if @debug + task.set_option(:virtv2v_wrapper, JSON.parse(result[:stdout])) + + # Record the status in the task object + task.set_option(:virtv2v_started_on, start_timestamp) + task.set_option(:virtv2v_status, 'active') + end + + if task.get_option(:virtv2v_started_on).nil? + @handle.root['ae_result'] = 'retry' + @handle.root['ae_retry_server_affinity'] = true + @handle.root['ae_retry_interval'] = $evm.object['check_convert_interval'] || '1.minutes' + end + rescue => e + @handle.set_state_var(:ae_state_progress, 'message' => e.message) + raise + end + end + end + end + end + end +end + +if $PROGRAM_NAME == __FILE__ + ManageIQ::Automate::Transformation::TransformationHost::OVirtHost::VMTransformVmwarews2rhevmVddk.new.main +end diff --git a/content/automate/ManageIQ/Transformation/TransformationHosts/ovirt_host.class/__methods__/vmtransform_vmwarews2rhevm_vddk.yaml b/content/automate/ManageIQ/Transformation/TransformationHosts/ovirt_host.class/__methods__/vmtransform_vmwarews2rhevm_vddk.yaml new file mode 100644 index 000000000..7bac229bb --- /dev/null +++ b/content/automate/ManageIQ/Transformation/TransformationHosts/ovirt_host.class/__methods__/vmtransform_vmwarews2rhevm_vddk.yaml @@ -0,0 +1,17 @@ +--- +object_type: method +version: 1.0 +object: + attributes: + name: VMTransform_vmwarews2rhevm_vddk + display_name: + description: + scope: instance + language: ruby + location: inline + embedded_methods: + - "/Transformation/TransformationHosts/Common/Utils" + - "/Transformation/TransformationHosts/ovirt_host/Utils" + - "/Transformation/Infrastructure/VM/vmwarews/Utils" + options: {} + inputs: []