diff --git a/app/models/mixins/supports_feature_mixin.rb b/app/models/mixins/supports_feature_mixin.rb index c222d043632..19121ba0682 100644 --- a/app/models/mixins/supports_feature_mixin.rb +++ b/app/models/mixins/supports_feature_mixin.rb @@ -96,6 +96,7 @@ module SupportsFeatureMixin :launch_vmrc_console => 'Launch VMRC Console', :admin_ui => 'Open Admin UI for a Provider', :live_migrate => 'Live Migration', + :warm_migrate => 'Warm Migration', :migrate => 'Migration', :capture => 'Capture of Capacity & Utilization Metrics', :openscap_scan => 'OpenSCAP security scan', diff --git a/app/models/service_template_transformation_plan/validate_config_info.rb b/app/models/service_template_transformation_plan/validate_config_info.rb index d08e56a71bd..2f9b80b0d3d 100644 --- a/app/models/service_template_transformation_plan/validate_config_info.rb +++ b/app/models/service_template_transformation_plan/validate_config_info.rb @@ -63,6 +63,7 @@ def validate_config_info(options) raise _("Invalid VM found #{vm_obj.name}") if vm_obj.invalid? vm_options = {} + vm_options[:warm_migration_compatible] = vm_obj.supports_warm_migrate? vm_options[:pre_ansible_playbook_service_template_id] = pre_service_id if vm_hash[:pre_service] vm_options[:post_ansible_playbook_service_template_id] = post_service_id if vm_hash[:post_service] vm_options[:cpu_right_sizing_mode] = vm_hash[:cpu_right_sizing_mode] if vm_hash[:cpu_right_sizing_mode].present? diff --git a/spec/models/service_template_transformation_plan_spec.rb b/spec/models/service_template_transformation_plan_spec.rb index 0d0388537fc..89f7da8cad8 100644 --- a/spec/models/service_template_transformation_plan_spec.rb +++ b/spec/models/service_template_transformation_plan_spec.rb @@ -1,4 +1,5 @@ RSpec.describe ServiceTemplateTransformationPlan, :v2v do + before { EvmSpecHelper.local_miq_server } # required for creating snapshots needed for warm migration testing subject { FactoryBot.create(:service_template_transformation_plan) } describe '#request_class' do @@ -10,9 +11,9 @@ end let(:apst) { FactoryBot.create(:service_template_ansible_playbook) } - let(:vm1) { FactoryBot.create(:vm_or_template) } - let(:vm2) { FactoryBot.create(:vm_or_template) } - let(:vm3) { FactoryBot.create(:vm_or_template) } + let(:vm1) { FactoryBot.create(:vm_vmware) } + let(:vm2) { FactoryBot.create(:vm_vmware) } + let(:vm3) { FactoryBot.create(:vm_vmware) } let(:security_group1) { FactoryBot.create(:security_group, :name => "default") } let(:flavor1) { FactoryBot.create(:flavor, :name => "large") } let(:security_group2) { FactoryBot.create(:security_group, :name => "default") } @@ -277,9 +278,9 @@ expect(service_template.vm_resources.collect(&:resource)).to match_array([vm1, vm2]) expect(service_template.vm_resources.collect(&:status)).to eq([ServiceResource::STATUS_QUEUED, ServiceResource::STATUS_QUEUED]) expect(service_template.vm_resources.find_by(:resource_id => vm1.id).options) - .to eq("pre_ansible_playbook_service_template_id" => apst.id, "osp_security_group_id" => security_group1.id, "osp_flavor_id" => flavor1.id) + .to eq("pre_ansible_playbook_service_template_id" => apst.id, "osp_security_group_id" => security_group1.id, "osp_flavor_id" => flavor1.id, "warm_migration_compatible" => true) expect(service_template.vm_resources.find_by(:resource_id => vm2.id).options) - .to eq("pre_ansible_playbook_service_template_id" => apst.id, "post_ansible_playbook_service_template_id" => apst.id, "osp_security_group_id" => security_group1.id, "osp_flavor_id" => flavor1.id) + .to eq("pre_ansible_playbook_service_template_id" => apst.id, "post_ansible_playbook_service_template_id" => apst.id, "osp_security_group_id" => security_group1.id, "osp_flavor_id" => flavor1.id, "warm_migration_compatible" => true) expect(service_template.config_info).to eq(catalog_item_options[:config_info]) expect(service_template.resource_actions.first).to have_attributes( :action => 'Provision', @@ -287,6 +288,18 @@ ) end + it 'creates and returns a transformation plan with VMs containing snapshots' do + FactoryBot.create_list(:snapshot, 2, :create_time => 1.minute.ago, :vm_or_template => vm1) + FactoryBot.create_list(:snapshot, 2, :create_time => 1.minute.ago, :vm_or_template => vm2) + + service_template = described_class.create_catalog_item(catalog_item_options) + + expect(service_template.vm_resources.find_by(:resource_id => vm1.id).options) + .to eq("pre_ansible_playbook_service_template_id" => apst.id, "osp_security_group_id" => security_group1.id, "osp_flavor_id" => flavor1.id, "warm_migration_compatible" => false) + expect(service_template.vm_resources.find_by(:resource_id => vm2.id).options) + .to eq("pre_ansible_playbook_service_template_id" => apst.id, "post_ansible_playbook_service_template_id" => apst.id, "osp_security_group_id" => security_group1.id, "osp_flavor_id" => flavor1.id, "warm_migration_compatible" => false) + end + it 'requires a transformation mapping' do catalog_item_options[:config_info].delete(:transformation_mapping_id) @@ -355,11 +368,11 @@ expect(service_template.vm_resources.collect(&:resource)).to match_array([vm1, vm2, vm3]) expect(service_template.vm_resources.collect(&:status)).to eq([ServiceResource::STATUS_QUEUED, ServiceResource::STATUS_QUEUED, ServiceResource::STATUS_QUEUED]) expect(service_template.vm_resources.find_by(:resource_id => vm1.id).options) - .to eq("pre_ansible_playbook_service_template_id" => apst.id, "osp_security_group_id" => security_group1.id, "osp_flavor_id" => flavor1.id) + .to eq("pre_ansible_playbook_service_template_id" => apst.id, "osp_security_group_id" => security_group1.id, "osp_flavor_id" => flavor1.id, "warm_migration_compatible" => true) expect(service_template.vm_resources.find_by(:resource_id => vm2.id).options) - .to eq("pre_ansible_playbook_service_template_id" => apst.id, "post_ansible_playbook_service_template_id" => apst.id, "osp_security_group_id" => security_group1.id, "osp_flavor_id" => flavor1.id) + .to eq("pre_ansible_playbook_service_template_id" => apst.id, "post_ansible_playbook_service_template_id" => apst.id, "osp_security_group_id" => security_group1.id, "osp_flavor_id" => flavor1.id, "warm_migration_compatible" => true) expect(service_template.vm_resources.find_by(:resource_id => vm3.id).options) - .to eq("pre_ansible_playbook_service_template_id" => apst.id, "post_ansible_playbook_service_template_id" => apst.id, "osp_security_group_id" => security_group2.id, "osp_flavor_id" => flavor2.id) + .to eq("pre_ansible_playbook_service_template_id" => apst.id, "post_ansible_playbook_service_template_id" => apst.id, "osp_security_group_id" => security_group2.id, "osp_flavor_id" => flavor2.id, "warm_migration_compatible" => true) expect(service_template.config_info).to eq(updated_catalog_item_options_with_vms_added[:config_info]) expect(service_template.resource_actions.first).to have_attributes( :action => 'Provision', @@ -377,7 +390,7 @@ expect(service_template.vm_resources.collect(&:resource)).to match_array([vm1]) expect(service_template.vm_resources.collect(&:status)).to eq([ServiceResource::STATUS_QUEUED]) expect(service_template.vm_resources.find_by(:resource_id => vm1.id).options) - .to eq("pre_ansible_playbook_service_template_id" => apst.id) + .to eq("pre_ansible_playbook_service_template_id" => apst.id, "warm_migration_compatible" => true) expect(service_template.config_info).to eq(updated_catalog_item_options_with_vms_removed[:config_info]) expect(service_template.resource_actions.first).to have_attributes( :action => 'Provision', @@ -395,9 +408,9 @@ expect(service_template.vm_resources.collect(&:resource)).to match_array([vm1, vm3]) expect(service_template.vm_resources.collect(&:status)).to eq([ServiceResource::STATUS_QUEUED, ServiceResource::STATUS_QUEUED]) expect(service_template.vm_resources.find_by(:resource_id => vm1.id).options) - .to eq("pre_ansible_playbook_service_template_id" => apst.id) + .to eq("pre_ansible_playbook_service_template_id" => apst.id, "warm_migration_compatible" => true) expect(service_template.vm_resources.find_by(:resource_id => vm3.id).options) - .to eq("pre_ansible_playbook_service_template_id" => apst.id, "post_ansible_playbook_service_template_id" => apst.id) + .to eq("pre_ansible_playbook_service_template_id" => apst.id, "post_ansible_playbook_service_template_id" => apst.id, "warm_migration_compatible" => true) expect(service_template.config_info).to eq(updated_catalog_item_options_with_vms_added_and_removed[:config_info]) expect(service_template.resource_actions.first).to have_attributes( :action => 'Provision', @@ -416,9 +429,9 @@ expect(service_template.vm_resources.collect(&:resource)).to match_array([vm1, vm2]) expect(service_template.vm_resources.collect(&:status)).to eq([ServiceResource::STATUS_QUEUED, ServiceResource::STATUS_QUEUED]) expect(service_template.vm_resources.find_by(:resource_id => vm1.id).options) - .to eq("pre_ansible_playbook_service_template_id" => apst.id, "osp_security_group_id" => security_group1.id, "osp_flavor_id" => flavor1.id) + .to eq("pre_ansible_playbook_service_template_id" => apst.id, "osp_security_group_id" => security_group1.id, "osp_flavor_id" => flavor1.id, "warm_migration_compatible" => true) expect(service_template.vm_resources.find_by(:resource_id => vm2.id).options) - .to eq("pre_ansible_playbook_service_template_id" => apst.id, "post_ansible_playbook_service_template_id" => apst.id, "osp_security_group_id" => security_group1.id, "osp_flavor_id" => flavor1.id) + .to eq("pre_ansible_playbook_service_template_id" => apst.id, "post_ansible_playbook_service_template_id" => apst.id, "osp_security_group_id" => security_group1.id, "osp_flavor_id" => flavor1.id, "warm_migration_compatible" => true) expect(service_template.config_info).to eq(catalog_item_options[:config_info]) expect(service_template.resource_actions.first).to have_attributes( :action => 'Provision', @@ -436,9 +449,9 @@ expect(service_template.transformation_mapping).to eq(transformation_mapping) expect(service_template.vm_resources.collect(&:resource)).to match_array([vm1, vm2]) expect(service_template.vm_resources.find_by(:resource_id => vm1.id).options) - .to eq("pre_ansible_playbook_service_template_id" => apst.id, "osp_security_group_id" => security_group1.id, "osp_flavor_id" => flavor1.id) + .to eq("pre_ansible_playbook_service_template_id" => apst.id, "osp_security_group_id" => security_group1.id, "osp_flavor_id" => flavor1.id, "warm_migration_compatible" => true) expect(service_template.vm_resources.find_by(:resource_id => vm2.id).options) - .to eq("pre_ansible_playbook_service_template_id" => apst.id, "post_ansible_playbook_service_template_id" => apst.id, "osp_security_group_id" => security_group1.id, "osp_flavor_id" => flavor1.id) + .to eq("pre_ansible_playbook_service_template_id" => apst.id, "post_ansible_playbook_service_template_id" => apst.id, "osp_security_group_id" => security_group1.id, "osp_flavor_id" => flavor1.id, "warm_migration_compatible" => true) expect(service_template.config_info).to eq(catalog_item_options[:config_info]) expect(service_template.resource_actions.first).to have_attributes( :action => 'Provision',