-
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
Directly run a playbook #16161
Directly run a playbook #16161
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,12 @@ | ||
class ManageIQ::Providers::EmbeddedAnsible::AutomationManager::Inventory < ManageIQ::Providers::EmbeddedAutomationManager::InventoryRootGroup | ||
def self.raw_create_inventory(tower, inventory_name, hosts) | ||
miq_org = tower.provider.default_organization | ||
tower.with_provider_connection do |connection| | ||
connection.api.inventories.create!(:name => inventory_name, :organization => miq_org).tap do |inventory| | ||
hosts.split(',').each do |host| | ||
connection.api.hosts.create!(:name => host, :inventory => inventory.id) | ||
end | ||
end | ||
end | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,4 +2,46 @@ class ManageIQ::Providers::EmbeddedAnsible::AutomationManager::Playbook < | |
ManageIQ::Providers::EmbeddedAutomationManager::ConfigurationScriptPayload | ||
|
||
has_many :jobs, :class_name => 'OrchestrationStack', :foreign_key => :configuration_script_base_id | ||
|
||
def run(options, userid = nil) | ||
options[:playbook_id] = id | ||
options[:userid] = userid || 'system' | ||
options[:name] = "Playbook: #{name}" | ||
miq_job = ManageIQ::Providers::EmbeddedAnsible::AutomationManager::PlaybookRunner.create_job(options) | ||
miq_job.signal(:start) | ||
miq_job.miq_task.id | ||
end | ||
|
||
# return provider raw object | ||
def raw_create_job_template(options) | ||
job_template_klass = ManageIQ::Providers::EmbeddedAnsible::AutomationManager::ConfigurationScript | ||
job_template_klass.raw_create_in_provider(manager, build_parameter_list(options)) | ||
end | ||
|
||
private | ||
|
||
def build_parameter_list(options) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This method is largely identical to the same method in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes. I planned to have another PR to reuse code introduced in this PR. |
||
params = { | ||
:name => options[:template_name] || "#{name}_#{Time.zone.now.to_i}", | ||
:description => options[:description] || '', | ||
:project => configuration_script_source.manager_ref, | ||
:playbook => name, | ||
:become_enabled => options[:become_enabled].present?, | ||
:verbosity => options[:verbosity].presence || 0, | ||
:ask_variables_on_launch => true, | ||
:ask_limit_on_launch => true, | ||
:ask_inventory_on_launch => true, | ||
:ask_credential_on_launch => true, | ||
:limit => options[:limit], | ||
:inventory => options[:inventory] || manager.provider.default_inventory, | ||
:extra_vars => options[:extra_vars].try(:to_json) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @bzwei - have you been able to verify that this call will deep stringify nested hashes? We had issues with testing where the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I cannot reproduce what you observed. For example:
|
||
} | ||
|
||
%i(credential cloud_credential network_credential).each do |credential| | ||
cred_sym = "#{credential}_id".to_sym | ||
params[credential] = Authentication.find(options[cred_sym]).manager_ref if options[cred_sym] | ||
end | ||
|
||
params.compact | ||
end | ||
end |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,140 @@ | ||
class ManageIQ::Providers::EmbeddedAnsible::AutomationManager::PlaybookRunner < ::Job | ||
# options are job table columns, including options column which is the playbook context info | ||
def self.create_job(options) | ||
super(name, options) | ||
end | ||
|
||
def start | ||
time = Time.zone.now | ||
update_attributes(:started_on => time) | ||
miq_task.update_attributes(:started_on => time) | ||
if options[:inventory] | ||
queue_signal(:create_job_template) | ||
else | ||
queue_signal(:create_inventory) | ||
end | ||
end | ||
|
||
def create_inventory | ||
set_status('creating inventory') | ||
tower = playbook.manager | ||
hosts = options[:hosts] || options.fetch_path(:extra_vars, :hosts) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @bzwei Should this default to localhost if its not available in either the config options or in the method input parameters (coming via extra vars). So if someone wants to run it on localhost they dont have to add a hosts method input parameter. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It has been taken cared of. Yes, if the user does not provide any hosts, default (localhost) is used in https://github.com/ManageIQ/manageiq/pull/16161/files#diff-713a4162657cdeb3c36e2194e0181ab7R36 |
||
options[:inventory] = | ||
if hosts == 'localhost' || hosts.nil? | ||
tower.provider.default_inventory | ||
else | ||
inventory_name = "#{playbook.name}_#{Time.zone.now.to_i}" | ||
ManageIQ::Providers::EmbeddedAnsible::AutomationManager::Inventory.raw_create_inventory(tower, inventory_name, hosts).id | ||
end | ||
save! | ||
queue_signal(:create_job_template) | ||
rescue => err | ||
_log.log_backtrace(err) | ||
queue_signal(:post_ansible_run, err.message, 'error') | ||
end | ||
|
||
def create_job_template | ||
set_status('creating job template') | ||
raw_job_template = playbook.raw_create_job_template(options) | ||
options[:job_template_ref] = raw_job_template.id | ||
save! | ||
|
||
queue_signal(:launch_ansible_tower_job) | ||
rescue => err | ||
_log.log_backtrace(err) | ||
queue_signal(:post_ansible_run, err.message, 'error') | ||
end | ||
|
||
def launch_ansible_tower_job | ||
set_status('launching tower job') | ||
job_template = ManageIQ::Providers::EmbeddedAnsible::AutomationManager::ConfigurationScript.new( | ||
:manager => playbook.manager, | ||
:manager_ref => options[:job_template_ref], | ||
:variables => {} | ||
) | ||
launch_options = options.slice(:extra_vars, :limit) | ||
tower_job = ManageIQ::Providers::EmbeddedAnsible::AutomationManager::Job.create_job(job_template, launch_options) | ||
options[:tower_job_id] = tower_job.id | ||
self.name = "#{name}, Job ID: #{tower_job.id}" | ||
miq_task.update_attributes(:name => name) | ||
save! | ||
|
||
queue_signal(:poll_ansible_tower_job_status, 10) | ||
rescue => err | ||
_log.log_backtrace(err) | ||
queue_signal(:post_ansible_run, err.message, 'error') | ||
end | ||
|
||
def poll_ansible_tower_job_status(interval) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @bzwei Minor, but can we swap the order the methods are listed in the file to match the "happy path" the methods would take. This would mean swapping the order that Also, fix the rubocop warning on line 57 with the extra space. |
||
set_status('waiting for tower job to complete') | ||
|
||
tower_job_status = tower_job.raw_status | ||
if tower_job_status.completed? | ||
tower_job.refresh_ems | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @bzwei is this a synchronous refresh. Should it be a different state in the state machine There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No need. It is very quick, just updates the job record in our DB. |
||
if tower_job_status.succeeded? | ||
queue_signal(:post_ansible_run, 'Playbook ran successfully', 'ok') | ||
else | ||
queue_signal(:post_ansible_run, 'Ansible engine returned an error for the job', 'error') | ||
end | ||
else | ||
interval = 60 if interval > 60 | ||
queue_signal(:poll_ansible_tower_job_status, interval * 2, :deliver_on => Time.now.utc + interval) | ||
end | ||
rescue => err | ||
_log.log_backtrace(err) | ||
queue_signal(:post_ansible_run, err.message, 'error') | ||
end | ||
|
||
def post_ansible_run(*args) | ||
# delete inventory, job_template, job? | ||
queue_signal(:finish, *args) | ||
end | ||
|
||
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 | ||
|
||
private | ||
|
||
def load_transitions | ||
self.state ||= 'initialize' | ||
|
||
{ | ||
:initializing => {'initialize' => 'waiting_to_start'}, | ||
:start => {'waiting_to_start' => 'running'}, | ||
:create_inventory => {'running' => 'inventory'}, | ||
:create_job_template => {'inventory' => 'job_template', 'running' => 'job_template'}, | ||
:launch_ansible_tower_job => {'job_template' => 'ansible_job'}, | ||
:poll_ansible_tower_job_status => {'ansible_job' => 'ansible_job'}, | ||
:post_ansible_run => {'inventory' => 'ansible_done', 'job_template' => 'ansible_done', 'ansible_job' => 'ansible_done'}, | ||
:finish => {'*' => 'finished'}, | ||
:abort_job => {'*' => 'aborting'}, | ||
:cancel => {'*' => 'canceling'}, | ||
:error => {'*' => '*'} | ||
} | ||
end | ||
|
||
def queue_signal(*args, deliver_on: nil) | ||
priority = options[:priority] || MiqQueue::NORMAL_PRIORITY | ||
|
||
MiqQueue.put( | ||
:class_name => self.class.name, | ||
:method_name => "signal", | ||
:instance_id => id, | ||
:priority => priority, | ||
:role => 'embedded_ansible', | ||
:args => args, | ||
:deliver_on => deliver_on | ||
) | ||
end | ||
|
||
def playbook | ||
ManageIQ::Providers::EmbeddedAnsible::AutomationManager::Playbook.find(options[:playbook_id]) | ||
end | ||
|
||
def tower_job | ||
ManageIQ::Providers::EmbeddedAnsible::AutomationManager::Job.find(options[:tower_job_id]) | ||
end | ||
end |
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.
@bzwei
Do we need try to make sure we have a miq_job?
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.
I don't think so.
create_job
is a straight forward method.