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

Directly run a playbook #16161

Merged
merged 1 commit into from
Oct 16, 2017
Merged

Directly run a playbook #16161

merged 1 commit into from
Oct 16, 2017

Conversation

bzwei
Copy link
Contributor

@bzwei bzwei commented Oct 10, 2017

Replaces #16060
Usage: ManageIQ::Providers::EmbeddedAnsible::AutomationManager::Playbook#run(options, userid)
The return is a task id that you can use to track the playbook execution progress.

Has dependency on ManageIQ/manageiq-providers-ansible_tower#25

@bzwei
Copy link
Contributor Author

bzwei commented Oct 10, 2017

@tinaafitz please test.

@bzwei bzwei changed the title Directly run a playbook [WIP]Directly run a playbook Oct 10, 2017
@bzwei
Copy link
Contributor Author

bzwei commented Oct 10, 2017

@miq-bot add_label wip

@miq-bot miq-bot added the wip label Oct 10, 2017
@bzwei bzwei force-pushed the ansible_runner branch 2 times, most recently from 98f6e89 to bba528e Compare October 10, 2017 16:12
@gmcculloug gmcculloug self-assigned this Oct 10, 2017
@gmcculloug gmcculloug requested review from syncrou and mkanoor October 10, 2017 17:51
Copy link
Contributor

@syncrou syncrou left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Over all this looks good. I was under the impression that Automate needed to track the status of the MiqTask and not necessarily the Job. If that isn't a problem then I only have a few concerns as noted in the PR.


private

def build_parameter_list(options)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This method is largely identical to the same method in ServiceTemplateAnsiblePlaybook. That code should be shared.

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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.


if options[:extra_vars]
params[:extra_vars] = options[:extra_vars].transform_values do |val|
val.kind_of?(String) ? val : val[:default] # TODO: support Hash only
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The call to transform_values nils out the manageiq key in :extra_vars. We found options[:extra_vars].deep_stringify_keys!.to_json off the raw :extra_vars hash solves the issue.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is old code. I am thinking of simplifying or refactoring this part. Thanks for the input.

if tower_job.raw_status.completed?
queue_signal(:finish)
else
queue_signal(:poll_ansible_tower_job_status, :deliver_on => Time.now.utc + 1.minute)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

At a minimal, if no inventory is passed in - each Runner 'Run' will take 3 minutes. Is the 1 minute time here to guarantee the synchronous saves complete?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. This is to requeue itself if the tower job is not completed. And yes, if the playbook runs quick enough, the 1 minute waiting time seems too long. But what's the right interval for best user experience?

if hosts == 'localhost'
tower.provider.default_inventory
else
inventory_name = "#{playbook.name}_#{Time.zone.now.to_i}"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Building out a shared naming method for Inventory and JobTemplate would make sense.

@bzwei bzwei force-pushed the ansible_runner branch 5 times, most recently from 9a73d87 to fa6a616 Compare October 13, 2017 18:53
@bzwei
Copy link
Contributor Author

bzwei commented Oct 13, 2017

@miq-bot remove_label wip
@miq-bot add_label enhancement

@miq-bot miq-bot changed the title [WIP]Directly run a playbook Directly run a playbook Oct 13, 2017
@miq-bot miq-bot added enhancement and removed wip labels Oct 13, 2017
def self.create_inventory_raw(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|
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bzwei Is this a quick process?
What happens if the name already exists, would we have to report that?

options[:playbook_id] = id
options[:userid] = userid || 'system'
options[:name] = "Playbook: #{name}"
miq_job = ManageIQ::Providers::EmbeddedAnsible::AutomationManager::PlaybookRunner.create_job(options)
Copy link
Contributor

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?

Copy link
Contributor Author

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.


# return provider raw object
def create_job_template_raw(options)
job_template_klass = ManageIQ::Providers::EmbeddedAnsible::AutomationManager::ConfigurationScript
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bzwei can we make this a constant?

def create_inventory
set_status('creating inventory')
tower = playbook.manager
hosts = options[:hosts] || options.fetch_path(:extra_vars, :hosts)
Copy link
Contributor

Choose a reason for hiding this comment

The 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.

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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

queue_signal(:post_ansible_run, err.message, 'error')
end

def poll_ansible_tower_job_status(interval)
Copy link
Member

Choose a reason for hiding this comment

The 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 launch_ansible_tower_job and poll_ansible_tower_job_status appear in this file.

Also, fix the rubocop warning on line 57 with the extra space.

if tower_job_status.succeeded?
queue_signal(:post_ansible_run, 'Playbook ran successfully', 'ok')
else
queue_signal(:post_ansible_run, 'Playbook failed at Ansible engine' , 'error')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bzwei This message seems incorrect. Could it say "Ansible Job failed"


tower_job_status = tower_job.raw_status
if tower_job_status.completed?
tower_job.refresh_ems
Copy link
Contributor

Choose a reason for hiding this comment

The 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

Copy link
Contributor Author

Choose a reason for hiding this comment

The 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 returns an error for the job', 'error')
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bzwei returned instead of returns.

A playbook can be run directly from the Playbook instance. It calls an instance of
PlaybooRunner which is a state machine.
@miq-bot
Copy link
Member

miq-bot commented Oct 16, 2017

Checked commit bzwei@fdc32bd with ruby 2.3.3, rubocop 0.47.1, and haml-lint 0.20.0
5 files checked, 5 offenses detected

app/models/manageiq/providers/embedded_ansible/automation_manager/playbook_runner.rb

Copy link
Contributor

@syncrou syncrou left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall it looks good, but I thought we needed to call more than simply to_json on the extra_vars hash to not remove the :manageiq nested hash. Has that been tested? If yes then I approve - if no - then that change would need to be put in.

:ask_credential_on_launch => true,
:limit => options[:limit],
:inventory => options[:inventory] || manager.provider.default_inventory,
:extra_vars => options[:extra_vars].try(:to_json)
Copy link
Contributor

@syncrou syncrou Oct 16, 2017

Choose a reason for hiding this comment

The 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 :manageiq key would be removed when we called to_json on the :extra_vars hash. -cc @mkanoor - To clarify - to_json works if all the keys are strings, it fails if there is a nested key that is passed as a symbol.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I cannot reproduce what you observed. For example:

irb(main):007:0> {:test => {:manageiq => {'a' => 'x'}}}.to_json
=> "{\"test\":{\"manageiq\":{\"a\":\"x\"}}}"
irb(main):008:0> JSON.parse(_)
=> {"test"=>{"manageiq"=>{"a"=>"x"}}}

@gmcculloug gmcculloug merged commit 04a20dd into ManageIQ:master Oct 16, 2017
@gmcculloug gmcculloug added this to the Sprint 71 Ending Oct 16, 2017 milestone Oct 16, 2017
@bzwei bzwei deleted the ansible_runner branch October 16, 2017 21:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants