Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[V2V] Add transforming vm state to InfraConversionJob #19222

Merged
merged 3 commits into from
Aug 29, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 51 additions & 4 deletions app/models/infra_conversion_job.rb
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,10 @@ def load_transitions
:poll_run_migration_playbook_complete => {'running_migration_playbook' => 'running_migration_playbook'},
:shutdown_vm => {'running_migration_playbook' => 'shutting_down_vm' },
:poll_shutdown_vm_complete => {'shutting_down_vm' => 'shutting_down_vm'},
:transform_vm => {'shutting_down_vm' => 'transforming_vm'},
:poll_transform_vm_complete => {'transforming_vm' => 'transforming_vm'},
:poll_automate_state_machine => {
'shutting_down_vm' => 'running_in_automate',
'transforming_vm' => 'running_in_automate',
'running_in_automate' => 'running_in_automate'
},
:finish => {'*' => 'finished'},
Expand Down Expand Up @@ -81,6 +83,11 @@ def state_settings
:weight => 1,
:max_retries => 15.minutes / state_retry_interval
},
:transforming_vm => {
:description => "Converting disks",
:weight => 60,
:max_retries => 1.day / state_retry_interval
},
:running_in_automate => {
:max_retries => 36.hours / state_retry_interval
}
Expand Down Expand Up @@ -332,7 +339,7 @@ def shutdown_vm

update_migration_task_progress(:on_exit)
handover_to_automate
queue_signal(:poll_automate_state_machine)
queue_signal(:transform_vm)
rescue StandardError => error
update_migration_task_progress(:on_error)
abort_conversion(error.message, 'error')
Expand All @@ -344,8 +351,7 @@ def poll_shutdown_vm_complete

if target_vm.power_state == 'off'
update_migration_task_progress(:on_exit)
handover_to_automate
return queue_signal(:poll_automate_state_machine)
return queue_signal(:transform_vm)
end

update_migration_task_progress(:on_retry)
Expand All @@ -355,6 +361,47 @@ def poll_shutdown_vm_complete
abort_conversion(error.message, 'error')
end

def transform_vm
update_migration_task_progress(:on_entry)
migration_task.run_conversion
update_migration_task_progress(:on_exit)
queue_signal(:poll_transform_vm_complete, :deliver_on => Time.now.utc + state_retry_interval)
rescue StandardError => error
update_migration_task_progress(:on_error)
abort_conversion(error.message, 'error')
end

def poll_transform_vm_complete
update_migration_task_progress(:on_entry)
return abort_conversion('Converting disks timed out', 'error') if polling_timeout

migration_task.get_conversion_state
case migration_task.options[:virtv2v_status]
when 'active'
virtv2v_disks = migration_task.options[:virtv2v_disks]
converted_disks = virtv2v_disks.reject { |disk| disk[:percent].zero? }
if converted_disks.empty?
message = 'Disk 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)}%]."
end
update_migration_task_progress(:on_retry, :message => message, :percent => percent)
queue_signal(:poll_transform_vm_complete, :deliver_on => Time.now.utc + state_retry_interval)
when 'failed'
raise migration_task.options[:virtv2v_message]
when 'succeeded'
update_migration_task_progress(:on_exit)
handover_to_automate
queue_signal(:poll_automate_state_machine)
end
rescue StandardError => error
update_migration_task_progress(:on_error)
abort_conversion(error.message, 'error')
end

def poll_automate_state_machine
return abort_conversion('Polling Automate state machine timed out', 'error') if polling_timeout

Expand Down
138 changes: 133 additions & 5 deletions spec/models/infra_conversion_job_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -348,7 +348,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 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_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)
Expand All @@ -357,7 +357,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 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_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}/)
Expand All @@ -383,6 +383,8 @@
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_automate_state_machine signal'
end

Expand All @@ -404,6 +406,8 @@
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_automate_state_machine signal'
end

Expand All @@ -425,6 +429,8 @@
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_automate_state_machine signal'
end

Expand All @@ -446,6 +452,8 @@
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_automate_state_machine signal'
end

Expand All @@ -467,6 +475,8 @@
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_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_automate_state_machine signal'
end

Expand All @@ -475,7 +485,30 @@
job.state = 'shutting_down_vm'
end

it_behaves_like 'allows transform_vm signal'
it_behaves_like 'allows poll_shutdown_vm_complete 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_transform_vm_complete signal'
it_behaves_like 'doesn\'t allow poll_automate_state_machine signal'
end

context 'transforming_vm' do
before do
job.state = 'transforming_vm'
end

it_behaves_like 'allows poll_transform_vm_complete signal'
it_behaves_like 'allows poll_automate_state_machine signal'
it_behaves_like 'allows finish signal'
it_behaves_like 'allows abort_job signal'
Expand All @@ -489,6 +522,8 @@
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'
end

context 'running_in_automate' do
Expand All @@ -510,6 +545,8 @@
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'
end
end

Expand Down Expand Up @@ -735,7 +772,7 @@
vm_vmware.update!(:raw_power_state => 'poweredOff')
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(:transform_vm)
job.signal(:shutdown_vm)
expect(task.reload.options[:workflow_runner]).to eq('automate')
end
Expand Down Expand Up @@ -789,9 +826,100 @@
vm_vmware.update!(:raw_power_state => 'poweredOff')
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(:transform_vm)
job.signal(:poll_shutdown_vm_complete)
expect(task.reload.options[:workflow_runner]).to eq('automate')
end
end

context '#transform_vm' do
before do
task.update_options(:migration_phase => 'pre')
job.state = 'shutting_down_vm'
end

it 'sends run_conversion to migration task and exits' do
Timecop.freeze(2019, 2, 6) 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.migration_task).to receive(:run_conversion)
expect(job).to receive(:queue_signal).with(:poll_transform_vm_complete, :deliver_on => Time.now.utc + job.state_retry_interval)
job.signal(:transform_vm)
end
end
end

context '#poll_transform_vm_complete' do
before do
job.state = 'transforming_vm'
allow(job.migration_task).to receive(:get_conversion_state)
end

it 'abort_conversion when shutting_down_vm times out' do
job.context[:retries_transforming_vm] = 5760
expect(job).to receive(:abort_conversion).with('Converting disks timed out', 'error')
job.signal(:poll_transform_vm_complete)
end

context 'virt-v2v has not started conversion' do
let(:virtv2v_disks) do
[
{ :path => '[datastore] test_vm/test_vm.vmdk', :size => 1_234_567, :percent => 0, :weight => 25 },
{ :path => '[datastore] test_vm/test_vm-2.vmdk', :size => 3_703_701, :percent => 0, :weight => 75 }
]
end

it 'returns a message stating conversion has not started' do
task.update_options(:virtv2v_status => 'active', :virtv2v_disks => virtv2v_disks)
Timecop.freeze(2019, 2, 6) do
expect(job).to receive(:update_migration_task_progress).once.ordered.with(:on_entry).and_call_original
expect(job).to receive(:update_migration_task_progress).once.ordered.with(:on_retry, :message => 'Disk transformation is initializing.', :percent => 1).and_call_original
expect(job).to receive(:queue_signal).with(:poll_transform_vm_complete, :deliver_on => Time.now.utc + job.state_retry_interval)
job.signal(:poll_transform_vm_complete)
expect(task.reload.options[:progress][:states][job.state.to_sym]).to include(
:message => 'Disk transformation is initializing.',
:percent => 1
)
end
end
end

context "conversion is still running" do
let(:virtv2v_disks) do
[
{ :path => '[datastore] test_vm/test_vm.vmdk', :size => 1_234_567, :percent => 100, :weight => 25 },
{ :path => '[datastore] test_vm/test_vm-2.vmdk', :size => 3_703_701, :percent => 25, :weight => 75 }
]
end

it "updates message and percentage, and retries if conversion is not finished" do
task.update_options(:virtv2v_status => 'active', :virtv2v_disks => virtv2v_disks)
Timecop.freeze(2019, 2, 6) do
expect(job).to receive(:update_migration_task_progress).once.ordered.with(:on_entry).and_call_original
expect(job).to receive(:update_migration_task_progress).once.ordered.with(:on_retry, :message => 'Converting disk 2 / 2 [43.75%].', :percent => 43.75).and_call_original
expect(job).to receive(:queue_signal).with(:poll_transform_vm_complete, :deliver_on => Time.now.utc + job.state_retry_interval)
job.signal(:poll_transform_vm_complete)
expect(task.reload.options[:progress][:states][job.state.to_sym]).to include(
:message => 'Converting disk 2 / 2 [43.75%].',
:percent => 43.75
)
end
end

it "aborts if conversion failed" do
task.update_options(:virtv2v_status => 'failed', :virtv2v_message => 'virt-v2v failed for some reason')
expect(job).to receive(:abort_conversion).with('virt-v2v failed for some reason', 'error').and_call_original
job.signal(:poll_transform_vm_complete)
end

it "exits if conversion succeeded" do
task.update_options(:virtv2v_status => 'succeeded')
expect(job).to receive(:update_migration_task_progress).once.ordered.with(:on_entry).and_call_original
expect(job).to receive(:update_migration_task_progress).once.ordered.with(:on_exit).and_call_original
expect(job).to receive(:queue_signal).with(:poll_automate_state_machine)
job.signal(:poll_transform_vm_complete)
expect(task.reload.options[:progress][:states][job.state.to_sym]).to include(:percent => 100.0)
expect(task.options[:workflow_runner]).to eq('automate')
end
end
end

Expand Down