-
Notifications
You must be signed in to change notification settings - Fork 900
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 Throttling #18415
Merged
Merged
v2v Throttling #18415
Changes from all commits
Commits
Show all changes
18 commits
Select commit
Hold shift + click to select a range
4709864
v2v migration as a Job
jameswnl 6831cf0
lock to update option
jameswnl 7a8d844
Doing TransformCleanup
jameswnl f444e66
use set_options
jameswnl aafc4bd
wait for PreTransform
jameswnl 4c0d810
rename InfraMigration to InfraConversion
jameswnl 49dc8aa
migration terminalogy changed to conversion
jameswnl 3c6c490
start_post_stage signal
jameswnl 9cbbf88
constant monitoring/throttling
jameswnl 04acf66
infra conversion job messages updated
jameswnl 324a8f5
refactor preflight_check and set_options
jameswnl c2f9fe8
specs added
jameswnl dd66882
polling timeout to abort conversion
jameswnl 91cc174
refactor for easier tests
jameswnl df4d626
test
jameswnl 8e7029f
move InfraConversionJob to app/models, etc
jameswnl 327625f
updated on code review
jameswnl 0b1a8c5
code review feedbacks implemented
jameswnl File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,155 @@ | ||
class InfraConversionJob < Job | ||
def self.create_job(options) | ||
# TODO: from settings/user plan settings | ||
options[:conversion_polling_interval] ||= 60 # in seconds | ||
options[:poll_conversion_max] ||= 24 * 60 # i.e. default 24 hour (with 60s per-interval) | ||
options[:poll_post_stage_max] ||= 30 # i.e. default 30 minutes | ||
super(name, options) | ||
end | ||
|
||
# | ||
# State-transition diagram: | ||
# :poll_conversion :poll_post_stage | ||
# * /-------------\ /---------------\ | ||
# | :initialize | | | | | ||
# v :start v | v | | ||
# waiting_to_start --------> running ------------------------------> post_conversion --/ | ||
# | | :start_post_stage | | ||
# | :abort_job | :abort_job | | ||
# \------------------------>| | :finish | ||
# v | | ||
# aborting --------------------------------->| | ||
# :finish v | ||
# finished | ||
# | ||
|
||
alias_method :initializing, :dispatch_start | ||
alias_method :finish, :process_finished | ||
alias_method :abort_job, :process_abort | ||
alias_method :cancel, :process_cancel | ||
alias_method :error, :process_error | ||
|
||
def load_transitions | ||
self.state ||= 'initialize' | ||
|
||
{ | ||
:initializing => {'initialize' => 'waiting_to_start'}, | ||
:start => {'waiting_to_start' => 'running'}, | ||
:poll_conversion => {'running' => 'running'}, | ||
:start_post_stage => {'running' => 'post_conversion'}, | ||
:poll_post_stage => {'post_conversion' => 'post_conversion'}, | ||
:finish => {'*' => 'finished'}, | ||
:abort_job => {'*' => 'aborting'}, | ||
:cancel => {'*' => 'canceling'}, | ||
:error => {'*' => '*'} | ||
} | ||
end | ||
|
||
def migration_task | ||
@migration_task ||= target_entity | ||
# valid states: %w(migrated pending finished active queued) | ||
end | ||
|
||
def start | ||
# TransformationCleanup 3 things: | ||
# - kill v2v: ignored because no converion_host is there yet in the original automate-based logic | ||
# - power_on: ignored | ||
# - check_power_on: ignore | ||
|
||
migration_task.preflight_check | ||
_log.info(prep_message("Preflight check passed, task.state=#{migration_task.state}. continue ...")) | ||
queue_signal(:poll_conversion) | ||
rescue => error | ||
message = prep_message("Preflight check has failed: #{error}") | ||
_log.info(message) | ||
abort_conversion(message, 'error') | ||
end | ||
|
||
def abort_conversion(message, status) | ||
migration_task.cancel | ||
queue_signal(:abort_job, message, status) | ||
end | ||
|
||
def polling_timeout(poll_type) | ||
count = "#{poll_type}_count".to_sym | ||
max = "#{poll_type}_max".to_sym | ||
context[count] = (context[count] || 0) + 1 | ||
context[count] > options[max] | ||
end | ||
|
||
def poll_conversion | ||
return abort_conversion("Polling times out", 'error') if polling_timeout(:poll_conversion) | ||
|
||
message = "Getting conversion state" | ||
_log.info(prep_message(message)) | ||
|
||
unless migration_task.options.fetch_path(:virtv2v_wrapper, 'state_file') | ||
message = "Virt v2v state file not available, continuing poll_conversion" | ||
_log.info(prep_message(message)) | ||
return queue_signal(:poll_conversion, :deliver_on => Time.now.utc + options[:conversion_polling_interval]) | ||
end | ||
|
||
begin | ||
migration_task.get_conversion_state # migration_task.options will be updated | ||
rescue => exception | ||
_log.log_backtrace(exception) | ||
return abort_conversion("Conversion error: #{exception}", 'error') | ||
end | ||
|
||
v2v_status = migration_task.options[:virtv2v_status] | ||
message = "virtv2v_status=#{v2v_status}" | ||
_log.info(prep_message(message)) | ||
|
||
case v2v_status | ||
when 'active' | ||
queue_signal(:poll_conversion, :deliver_on => Time.now.utc + options[:conversion_polling_interval]) | ||
when 'failed' | ||
message = "disk conversion failed" | ||
abort_conversion(prep_message(message), 'error') | ||
when 'succeeded' | ||
message = "disk conversion succeeded" | ||
_log.info(prep_message(message)) | ||
queue_signal(:start_post_stage) | ||
else | ||
message = prep_message("Unknown converstion status: #{v2v_status}") | ||
abort_conversion(message, 'error') | ||
end | ||
end | ||
|
||
def start_post_stage | ||
# once we refactor Automate's PostTransformation into a job, we kick start it here | ||
message = "To wait for Post-Transformation progress" | ||
_log.info(prep_message(message)) | ||
queue_signal(:poll_post_stage, :deliver_on => Time.now.utc + options[:conversion_polling_interval]) | ||
end | ||
|
||
def poll_post_stage | ||
return abort_conversion("Polling times out", 'error') if polling_timeout(:poll_post_stage) | ||
|
||
message = "PostTransformation state=#{migration_task.state}, status=#{migration_task.status}" | ||
_log.info(prep_message(message)) | ||
if migration_task.state == 'finished' | ||
self.status = migration_task.status | ||
queue_signal(:finish) | ||
else | ||
queue_signal(:poll_post_stage, :deliver_on => Time.now.utc + options[:conversion_polling_interval]) | ||
end | ||
end | ||
|
||
def queue_signal(*args, deliver_on: nil) | ||
MiqQueue.put( | ||
:class_name => self.class.name, | ||
:method_name => "signal", | ||
:instance_id => id, | ||
:role => "ems_operations", | ||
:zone => zone, | ||
:task_id => guid, | ||
:args => args, | ||
:deliver_on => deliver_on | ||
) | ||
end | ||
|
||
def prep_message(contents) | ||
"MiqRequestTask id=#{migration_task.id}, InfraConversionJob id=#{id}. #{contents}" | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
class InfraConversionThrottler | ||
DEFAULT_EMS_MAX_RUNNERS = 10 | ||
|
||
def self.start_conversions | ||
pending_conversion_jobs.each do |ems, jobs| | ||
running = ems.conversion_hosts.inject(0) { |sum, ch| sum + ch.active_tasks.size } | ||
slots = (ems.miq_custom_get('Max Transformation Runners') || DEFAULT_EMS_MAX_RUNNERS).to_i - running | ||
jobs.each do |job| | ||
eligible_hosts = ems.conversion_hosts.select(&:eligible?).sort_by { |ch| ch.active_tasks.size } | ||
break if slots <= 0 || eligible_hosts.empty? | ||
job.migration_task.update_attributes!(:conversion_host => eligible_hosts.first) | ||
job.queue_signal(:start) | ||
_log.info("Pening InfraConversionJob: id=#{job.id} signaled to start") | ||
slots -= 1 | ||
end | ||
end | ||
end | ||
|
||
def self.pending_conversion_jobs | ||
pending = InfraConversionJob.where(:state => 'waiting_to_start') | ||
_log.info("Pening InfraConversionJob: #{pending.count}") | ||
pending.group_by { |job| job.migration_task.destination_ems } | ||
end | ||
end |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
FactoryBot.define do | ||
factory :job do | ||
sequence(:name) { |n| "job_#{seq_padded_for_sorting(n)}" } | ||
end | ||
|
||
factory :infra_conversion_job, :parent => :job | ||
end |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The target_entity is known at initialization time right? This looks like it could just be an
attr_reader
that is set in initializeThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is caching the query result of
target_entity
. Shall I rely on rails to take care of caching, and just do aalias_method :migration_task, :target_entity
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh no problem with the caching, I was saying in
initialize
you could do@migration_task = target_entity
and makemigration_task
anattr_reader
which is basically what this duplicates the behavior of.