From ff66fba8c06ca8117f70044db0aa67c033af9fbe Mon Sep 17 00:00:00 2001 From: Fabien Dupont Date: Mon, 2 Sep 2019 18:37:31 +0200 Subject: [PATCH 1/2] Add restoring_vm_attributes state to InfraConversionJob --- app/models/infra_conversion_job.rb | 47 +++++++++- spec/models/infra_conversion_job_spec.rb | 107 ++++++++++++++++++++--- 2 files changed, 142 insertions(+), 12 deletions(-) diff --git a/app/models/infra_conversion_job.rb b/app/models/infra_conversion_job.rb index a52456d5007..a0f3e8d0775 100644 --- a/app/models/infra_conversion_job.rb +++ b/app/models/infra_conversion_job.rb @@ -45,9 +45,10 @@ def load_transitions 'waiting_for_inventory_refresh' => 'waiting_for_inventory_refresh' }, :apply_right_sizing => {'waiting_for_inventory_refresh' => 'applying_right_sizing'}, + :restore_vm_attributes => {'applying_right_sizing' => 'restoring_vm_attributes'}, :poll_automate_state_machine => { - 'applying_right_sizing' => 'running_in_automate', - 'running_in_automate' => 'running_in_automate' + 'restoring_vm_attributes' => 'running_in_automate', + 'running_in_automate' => 'running_in_automate' }, :finish => {'*' => 'finished'}, :abort_job => {'*' => 'aborting'}, @@ -98,6 +99,10 @@ def state_settings :description => "Apply Right-Sizing Recommendation", :weight => 1 }, + :restoring_vm_attributes => { + :description => "Restore VM Attributes", + :weight => 1 + }, :running_in_automate => { :max_retries => 36.hours / state_retry_interval } @@ -445,6 +450,44 @@ def apply_right_sizing send("apply_right_sizing_#{item}", right_sizing_mode) if right_sizing_mode.present? end + update_migration_task_progress(:on_exit) + queue_signal(:restore_vm_attributes) + rescue StandardError + update_migration_task_progress(:on_error) + queue_signal(:restore_vm_attributes) + end + + def restore_vm_attributes + update_migration_task_progress(:on_entry) + + # Transfer service link to destination VM + if source_vm.service + destination_vm.add_to_service(source_vm.service) + source_vm.direct_service.try(:remove_resource, source_vm) + end + + # Copy tags and custom attributes from source VM + source_vm.tags.each do |tag| + next if tag.name =~ /^\/managed\/folder_path_/ + tag_as_array = tag.name.split('/') + namespace = tag_as_array.shift + value = tag_as_array.pop + category = tag_as_array.join('/') + destination_vm.tag_add("#{category}/#{value}", :ns => namespace) + end + source_vm.miq_custom_keys.each { |ca| destination_vm.miq_custom_set(ca, source_vm.miq_custom_get(ca)) } + + # Copy ownership from source VM + destination_vm.evm_owner = source_vm.evm_owner if source_vm.present? + destination_vm.miq_group = source_vm.miq_group if source_vm.miq_group.present? + + # Copy retirement settings from source VM + destination_vm.retires_on = source_vm.retires_on if source_vm.retires_on.present? + destination_vm.retirement_warn = source_vm.retirement_warn if source_vm.retirement_warn.present? + + # Save destination_vm in VMDB + destination_vm.save + update_migration_task_progress(:on_exit) handover_to_automate queue_signal(:poll_automate_state_machine) diff --git a/spec/models/infra_conversion_job_spec.rb b/spec/models/infra_conversion_job_spec.rb index d04bca4245f..69eb3beee7f 100644 --- a/spec/models/infra_conversion_job_spec.rb +++ b/spec/models/infra_conversion_job_spec.rb @@ -1,5 +1,7 @@ RSpec.describe InfraConversionJob, :v2v do let(:user) { FactoryBot.create(:user_with_group) } + let(:user_admin) { FactoryBot.create(:user_admin) } + let(:group) { FactoryBot.create(:miq_group) } let(:zone) { FactoryBot.create(:zone) } let(:ems_vmware) { FactoryBot.create(:ems_vmware, :zone => zone) } @@ -15,13 +17,14 @@ :ems_cluster => ems_cluster_vmware, :host => host_vmware, :hardware => hardware_vmware, - :evm_owner => user) + :evm_owner => user, + :miq_group => group) end let(:ems_redhat) { FactoryBot.create(:ems_redhat, :zone => zone) } let(:ems_cluster_redhat) { FactoryBot.create(:ems_cluster, :ext_management_system => ems_redhat) } let(:host_redhat) { FactoryBot.create(:host, :ext_management_system => ems_redhat, :ems_cluster => ems_cluster_redhat) } - let(:vm_redhat) { FactoryBot.create(:vm_vmware, :ext_management_system => ems_redhat, :ems_cluster => ems_cluster_redhat, :host => host_redhat, :evm_owner => user) } + let(:vm_redhat) { FactoryBot.create(:vm_vmware, :ext_management_system => ems_redhat, :ems_cluster => ems_cluster_redhat, :host => host_redhat, :evm_owner => user_admin) } let(:embedded_ansible_auth) { FactoryBot.create(:embedded_ansible_credential) } let(:embedded_ansible_catalog_item_options) do @@ -349,7 +352,7 @@ end context 'state transitions' do - %w[start remove_snapshots poll_remove_snapshots_complete wait_for_ip_address run_migration_playbook poll_run_migration_playbook_complete shutdown_vm poll_shutdown_vm_complete transform_vm poll_transform_vm_complete poll_inventory_refresh_complete apply_right_sizing poll_automate_state_machine finish abort_job cancel error].each do |signal| + %w[start remove_snapshots poll_remove_snapshots_complete wait_for_ip_address run_migration_playbook poll_run_migration_playbook_complete shutdown_vm poll_shutdown_vm_complete transform_vm poll_transform_vm_complete poll_inventory_refresh_complete apply_right_sizing restore_vm_attributes poll_automate_state_machine finish abort_job cancel error].each do |signal| shared_examples_for "allows #{signal} signal" do it signal.to_s do expect(job).to receive(signal.to_sym) @@ -358,7 +361,7 @@ end end - %w[start remove_snapshots poll_remove_snapshots_complete wait_for_ip_address run_migration_playbook poll_run_migration_playbook_complete shutdown_vm poll_shutdown_vm_complete transform_vm poll_transform_vm_complete poll_inventory_refresh_complete apply_right_sizing poll_automate_state_machine].each do |signal| + %w[start remove_snapshots poll_remove_snapshots_complete wait_for_ip_address run_migration_playbook poll_run_migration_playbook_complete shutdown_vm poll_shutdown_vm_complete transform_vm poll_transform_vm_complete poll_inventory_refresh_complete apply_right_sizing restore_vm_attributes poll_automate_state_machine].each do |signal| shared_examples_for "doesn't allow #{signal} signal" do it signal.to_s do expect { job.signal(signal.to_sym) }.to raise_error(RuntimeError, /#{signal} is not permitted at state #{job.state}/) @@ -387,6 +390,8 @@ it_behaves_like 'doesn\'t allow transform_vm signal' it_behaves_like 'doesn\'t allow poll_transform_vm_complete signal' it_behaves_like 'doesn\'t allow poll_inventory_refresh_complete signal' + it_behaves_like 'doesn\'t allow apply_right_sizing signal' + it_behaves_like 'doesn\'t allow restore_vm_attributes signal' it_behaves_like 'doesn\'t allow poll_automate_state_machine signal' end @@ -411,6 +416,8 @@ it_behaves_like 'doesn\'t allow transform_vm signal' it_behaves_like 'doesn\'t allow poll_transform_vm_complete signal' it_behaves_like 'doesn\'t allow poll_inventory_refresh_complete signal' + it_behaves_like 'doesn\'t allow apply_right_sizing signal' + it_behaves_like 'doesn\'t allow restore_vm_attributes signal' it_behaves_like 'doesn\'t allow poll_automate_state_machine signal' end @@ -435,6 +442,8 @@ it_behaves_like 'doesn\'t allow transform_vm signal' it_behaves_like 'doesn\'t allow poll_transform_vm_complete signal' it_behaves_like 'doesn\'t allow poll_inventory_refresh_complete signal' + it_behaves_like 'doesn\'t allow apply_right_sizing signal' + it_behaves_like 'doesn\'t allow restore_vm_attributes signal' it_behaves_like 'doesn\'t allow poll_automate_state_machine signal' end @@ -459,6 +468,8 @@ it_behaves_like 'doesn\'t allow transform_vm signal' it_behaves_like 'doesn\'t allow poll_transform_vm_complete signal' it_behaves_like 'doesn\'t allow poll_inventory_refresh_complete signal' + it_behaves_like 'doesn\'t allow apply_right_sizing signal' + it_behaves_like 'doesn\'t allow restore_vm_attributes signal' it_behaves_like 'doesn\'t allow poll_automate_state_machine signal' end @@ -483,6 +494,8 @@ it_behaves_like 'doesn\'t allow transform_vm signal' it_behaves_like 'doesn\'t allow poll_transform_vm_complete signal' it_behaves_like 'doesn\'t allow poll_inventory_refresh_complete signal' + it_behaves_like 'doesn\'t allow apply_right_sizing signal' + it_behaves_like 'doesn\'t allow restore_vm_attributes signal' it_behaves_like 'doesn\'t allow poll_automate_state_machine signal' end @@ -507,6 +520,8 @@ it_behaves_like 'doesn\'t allow shutdown_vm signal' it_behaves_like 'doesn\'t allow poll_transform_vm_complete signal' it_behaves_like 'doesn\'t allow poll_inventory_refresh_complete signal' + it_behaves_like 'doesn\'t allow apply_right_sizing signal' + it_behaves_like 'doesn\'t allow restore_vm_attributes signal' it_behaves_like 'doesn\'t allow poll_automate_state_machine signal' end @@ -531,6 +546,8 @@ it_behaves_like 'doesn\'t allow shutdown_vm signal' it_behaves_like 'doesn\'t allow poll_shutdown_vm_complete signal' it_behaves_like 'doesn\'t allow transform_vm signal' + it_behaves_like 'doesn\'t allow apply_right_sizing signal' + it_behaves_like 'doesn\'t allow restore_vm_attributes signal' it_behaves_like 'doesn\'t allow poll_automate_state_machine signal' end @@ -556,6 +573,7 @@ it_behaves_like 'doesn\'t allow poll_shutdown_vm_complete signal' it_behaves_like 'doesn\'t allow transform_vm signal' it_behaves_like 'doesn\'t allow poll_transform_vm_complete signal' + it_behaves_like 'doesn\'t allow restore_vm_attributes signal' end context 'applying_right_sizing' do @@ -563,6 +581,31 @@ job.state = 'applying_right_sizing' end + it_behaves_like 'allows restore_vm_attributes signal' + it_behaves_like 'allows finish signal' + it_behaves_like 'allows abort_job signal' + it_behaves_like 'allows cancel signal' + it_behaves_like 'allows error signal' + + it_behaves_like 'doesn\'t allow start signal' + it_behaves_like 'doesn\'t allow remove_snapshots signal' + it_behaves_like 'doesn\'t allow poll_remove_snapshots_complete signal' + it_behaves_like 'doesn\'t allow wait_for_ip_address signal' + it_behaves_like 'doesn\'t allow run_migration_playbook signal' + it_behaves_like 'doesn\'t allow poll_run_migration_playbook_complete signal' + it_behaves_like 'doesn\'t allow shutdown_vm signal' + it_behaves_like 'doesn\'t allow poll_shutdown_vm_complete signal' + it_behaves_like 'doesn\'t allow transform_vm signal' + it_behaves_like 'doesn\'t allow poll_transform_vm_complete signal' + it_behaves_like 'doesn\'t allow poll_inventory_refresh_complete signal' + it_behaves_like 'doesn\'t allow apply_right_sizing signal' + end + + context 'restoring_vm_attributes' do + before do + job.state = 'restoring_vm_attributes' + end + it_behaves_like 'allows poll_automate_state_machine signal' it_behaves_like 'allows finish signal' it_behaves_like 'allows abort_job signal' @@ -581,6 +624,7 @@ it_behaves_like 'doesn\'t allow poll_transform_vm_complete signal' it_behaves_like 'doesn\'t allow poll_inventory_refresh_complete signal' it_behaves_like 'doesn\'t allow apply_right_sizing signal' + it_behaves_like 'doesn\'t allow restore_vm_attributes signal' end context 'running_in_automate' do @@ -606,6 +650,7 @@ it_behaves_like 'doesn\'t allow poll_transform_vm_complete signal' it_behaves_like 'doesn\'t allow poll_inventory_refresh_complete signal' it_behaves_like 'doesn\'t allow apply_right_sizing signal' + it_behaves_like 'doesn\'t allow restore_vm_attributes signal' end end @@ -1020,13 +1065,12 @@ task.update!(:destination => vm_redhat) end - it "exits to next state in case of failed" do + it "exits to next state in case of failure" do allow(job.migration_task).to receive(:cpu_right_sizing_mode).and_raise('Fake error message') expect(job).to receive(:update_migration_task_progress).once.ordered.with(:on_entry) expect(job).to receive(:update_migration_task_progress).once.ordered.with(:on_error) - expect(job).to receive(:queue_signal).with(:poll_automate_state_machine) + expect(job).to receive(:queue_signal).with(:restore_vm_attributes) job.signal(:apply_right_sizing) - expect(task.reload.options[:workflow_runner]).to eq('automate') end context 'without right_sizing mode' do @@ -1038,9 +1082,8 @@ it 'exits if no right-sizing is requested' do expect(job).to receive(:update_migration_task_progress).once.ordered.with(:on_entry) expect(job).to receive(:update_migration_task_progress).once.ordered.with(:on_exit) - expect(job).to receive(:queue_signal).with(:poll_automate_state_machine) + expect(job).to receive(:queue_signal).with(:restore_vm_attributes) job.signal(:apply_right_sizing) - expect(task.reload.options[:workflow_runner]).to eq('automate') end end @@ -1057,8 +1100,52 @@ expect(job.migration_task.destination).to receive(:set_number_of_cpus).with(1) expect(job.migration_task.destination).to receive(:set_memory).with(1024) expect(job).to receive(:update_migration_task_progress).once.ordered.with(:on_exit) - expect(job).to receive(:queue_signal).with(:poll_automate_state_machine) + expect(job).to receive(:queue_signal).with(:restore_vm_attributes) job.signal(:apply_right_sizing) + end + end + end + + context '#restore_vm_attributes' do + let(:service) { FactoryBot.create(:service) } + let(:parent_classification) { FactoryBot.create(:classification, :name => 'environment', :description => 'Environment') } + let(:classification) { FactoryBot.create(:classification, :name => 'prod', :description => 'Production', :parent => parent_classication) } + + before do + job.state = 'applying_right_sizing' + task.update!(:destination => vm_redhat) + end + + it "exits to next state in case of failure" do + allow(job.migration_task.source).to receive(:service).and_raise('Fake error message') + expect(job).to receive(:update_migration_task_progress).once.ordered.with(:on_entry) + expect(job).to receive(:update_migration_task_progress).once.ordered.with(:on_error) + expect(job).to receive(:queue_signal).with(:poll_automate_state_machine) + job.signal(:restore_vm_attributes) + expect(task.reload.options[:workflow_runner]).to eq('automate') + end + + it 'restore VM attributes' do + Timecop.freeze(2019, 2, 6) do + vm_vmware.add_to_service(service) + vm_vmware.tag_with('test', :ns => '/managed', :cat => 'folder_path_spec') + vm_vmware.tag_with('prod', :ns => '/managed', :cat => 'environment') + vm_vmware.miq_custom_set('attr', 'value') + vm_vmware.update!(:retires_on => Time.now.utc + 1.day) + vm_vmware.update!(:retirement_warn => 7) + expect(job).to receive(:update_migration_task_progress).once.ordered.with(:on_entry) + expect(job).to receive(:update_migration_task_progress).once.ordered.with(:on_exit) + expect(job).to receive(:queue_signal).with(:poll_automate_state_machine) + job.signal(:restore_vm_attributes) + vm_redhat.reload + expect(vm_vmware.service).to be_nil + expect(vm_redhat.service.id).to eq(service.id) + expect(vm_redhat.tags).to eq(['/managed/environment/prod']) + expect(vm_redhat.miq_custom_get('attr')).to eq('value') + expect(vm_redhat.evm_owner.id).to eq(user.id) + expect(vm_redhat.miq_group.id).to eq(group.id) + expect(vm_redhat.retires_on).to eq(Time.now.utc + 1.day) + expect(vm_redhat.retirement_warn).to eq(7) expect(task.reload.options[:workflow_runner]).to eq('automate') end end From 9f57a48bd864f2690ab43fbacfbc7636715bfa12 Mon Sep 17 00:00:00 2001 From: Fabien Dupont Date: Mon, 2 Sep 2019 18:56:25 +0200 Subject: [PATCH 2/2] Address rubocops --- app/models/infra_conversion_job.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/app/models/infra_conversion_job.rb b/app/models/infra_conversion_job.rb index a0f3e8d0775..f4e4dc1148c 100644 --- a/app/models/infra_conversion_job.rb +++ b/app/models/infra_conversion_job.rb @@ -469,6 +469,7 @@ def restore_vm_attributes # Copy tags and custom attributes from source VM source_vm.tags.each do |tag| next if tag.name =~ /^\/managed\/folder_path_/ + tag_as_array = tag.name.split('/') namespace = tag_as_array.shift value = tag_as_array.pop