-
Notifications
You must be signed in to change notification settings - Fork 897
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] Run the playbook on the appliance with the conversion host in inventory #18613
[V2V] Run the playbook on the appliance with the conversion host in inventory #18613
Conversation
app/models/conversion_host.rb
Outdated
def ansible_playbook(playbook, extra_vars = {}, auth_type = nil) | ||
host = hostname || ipaddress | ||
|
||
command = "ansible-playbook #{playbook} --inventory #{host}, --become -vvv" |
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.
Should the comma be there after the host?
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.
Yes, that's how you define the inventory from a list of hostnames / IP addresses. Otherwise, it looks for a file.
app/models/conversion_host.rb
Outdated
command = "ansible-playbook #{playbook} --inventory #{host}, --become -vvv" | ||
|
||
auth = authentication_type(auth_type) || authentications.first | ||
command += " --user #{auth.userid}" |
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.
Minor, but prefer <<
over +=
for String building to avoid intermediate strings.
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.
Done
app/models/conversion_host.rb
Outdated
|
||
connect_ssh { |ssu| ssu.shell_exec(command) } | ||
_log.info("FDUPONT - Calling Ansible playbook: #{command}") |
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.
Don't forget to remove these FDUPONT debug lines
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.
Yes, and also because they might contain some sensitive data.
@miq-bot add-label transformation, bug, hammer/yes, wip |
17134e5
to
30cb4db
Compare
55e6b92
to
2565bc5
Compare
@miq-bot remove-label wip |
@fdupont-redhat now that this is intended for backport to hammer, I thought we were going to continue to use ansible-playbook on hammer and refactor to use ansible::runner with added inventory support on master. |
Spoke offline to @fdupont-redhat and there seems to be an issue with becoming root over ssh, it should be working and isn't. I think we should be fixing that instead of replacing with ansible-runner for a backport to fix an issue that isn't understood. |
@agrare @fdupont-redhat What's the issue with root over ssh? Does it happen for both userid/password and private key? |
@agrare @fdupont-redhat refactoring to use runner is substantial, I'd prefer not to see such a large refactor (and change for QE) in a Z-Stream and instead understand the failure of something that is fully expected to work (root over ssh) and fix the underlying issue. |
@fdupont-redhat yeah I'm good with that (minus the logs obviously) |
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.
@fdupont-redhat I'm really worried about that MiqTask
lookup, and I think updating the task is making the ansible_playbook
method more complex than it needs to be. It would be better IMO to just return the awesome_spawn CommandResult
to the caller and let it handle updating the task.
Is there any way to get the miq_task_id to these methods calling ansible_playbook
so that we don't need to do a loop over every task looking for ones on this conversion host?
app/models/conversion_host.rb
Outdated
def ansible_playbook(playbook, extra_vars = {}) | ||
command = "ansible-playbook #{playbook} -i #{ipaddress}" | ||
def ansible_playbook(playbook, extra_vars = {}, auth_type = nil) | ||
task = MiqTask.all.select { |t| t.context_data.present? && t.context_data[:conversion_host_id] == id }.sort_by(&:created_on).last |
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.
This is where having proper associations would help, this is bringing back every miq_task and loading up the context_data hash in ruby 😱
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.
We could use a .where
clause here instead couldn't we? Totally untested, but this is the general idea:
MiqTask.where.not(:context_data => nil)
.where(:context_data[:conversion_host_id]) => id)
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'd rather pass the task_id to the calling method and update the task there so we don't have to do any searching at all
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.
Indeed, we have the task id, so I changed the code to pass it to the *_conversion_host_role
methods, that in turn pass it to ansible_playbook
.
app/models/conversion_host.rb
Outdated
rescue => e | ||
_log.error("Ansible playbook '#{playbook}' failed for '#{resource.name}' with [#{e.class}: #{e}]") | ||
errormsg = "Ansible playbook '#{playbook}' failed for '#{resource.name}' with [#{e.class}: #{e}]" |
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.
Hm if you just raise I don't think there's going to be much meaningful information in the exception, it is just going to be a RuntimeError
and you won't see the result stdout.
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.
Good point. And we capture the result.output
in the ensure
part.
app/models/conversion_host.rb
Outdated
ansible_output_name = playbook.split('/').last.split('.').first | ||
task&.update_context(task.context_data.merge!(ansible_output_name => result.output)) | ||
end | ||
if !ssh_private_key_file.nil? && File.exist?(ssh_private_key_file.path) |
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.
Just a style thing but we usually prefer ssh_private_key_file.present?
instead of !.nil?
app/models/conversion_host.rb
Outdated
task&.update_context(task.context_data.merge!(ansible_output_name => result.output)) | ||
end | ||
if !ssh_private_key_file.nil? && File.exist?(ssh_private_key_file.path) | ||
ssh_private_key_file.close unless ssh_private_key_file.closed? |
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.
What case is this handling? If there was an exception writing the file before it was closed? Probably better to handle this with a begin/rescue block right around writing the file then.
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.
In case the SSH private key file creation fails on line 301.
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.
Yeah that's what I thought so in that case I think it'd be better to have a more localized rescue like:
ssh_private_key_file = Tempfile.new('ansible_key')
begin
ssh_private_key_file.write(auth.auth_key)
ensure
ssh_private_key_file.close
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.
Done.
app/models/conversion_host.rb
Outdated
ansible_output_name = playbook.split('/').last.split('.').first | ||
task&.update_context(task.context_data.merge!(ansible_output_name => result.output)) | ||
end | ||
File.delete(ssh_private_key_file) if ssh_private_key_file.present? && File.exist?(ssh_private_key_file.path) |
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.
This can be simplified to ssh_private_key_file&.unlink
app/models/conversion_host.rb
Outdated
raise unless result.exit_status.zero? | ||
ensure | ||
unless result.nil? | ||
ansible_output_name = playbook.split('/').last.split('.').first |
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.
Pretty sure you can just do File.basename(playbook, ".yml")
>> File.basename("/usr/share/v2v-conversion-host-ansible/playbooks/conversion_host_check.yml", ".yml")
=> "conversion_host_check"
raise unless result.exit_status.zero? | ||
ensure | ||
task&.update_context(task.context_data.merge!(File.basename(playbook, '.yml') => result.output)) unless result.nil? | ||
ssh_private_key_file&.unlink |
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.
Nice! much better
Checked commits fabiendupont/manageiq@2b1a60c~...4d18a99 with ruby 2.3.3, rubocop 0.52.1, haml-lint 0.20.0, and yamllint 1.10.0 |
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.
👍 LGTM
…ybook [V2V] Run the playbook on the appliance with the conversion host in inventory (cherry picked from commit 1a2f07f) https://bugzilla.redhat.com/show_bug.cgi?id=1694229
Hammer backport details:
|
@fdupont-redhat does this PR obviate the requirement to change to running ansible-runner on the appliance as I was previously investigating? It was my impression that we were calling ansible-playbook on the remote host via ssh (not ansible-runner as mentioned in the top-level description here. Thanks in advance. |
@jerryk55 we still want to use ansible-runner, this was just intended for backport so we wanted to minimize the changes made |
@agrare maybe I'm just lost at sea but intending this for backport seems orthogonal to the question. Maybe we can talk face-to-face about this Monday? |
No it doesn't
This change calls ansible-playbook on the appliance passing the target host as inventory instead of calling ansible-playbook on the target host over ssh |
@agrare I understand that. What had been explained to me by @jameswnl was that the reason I was supposed to look at migrating to ansible-runner, was so that we could run it on the appliance instead of calling ansible-playbook on the target host over ssh. Now that we're calling ansible-playbook on the appliance, what is the justification for migrating to ansible-runner? Thanks. |
@jerryk55 Because writing our own ansible-playbook wrapper goes against using ansible-runner which is itself an ansible-playbook wrapper that does all the right stuff and is owned by the ansible team. |
In existing implementation, CloudForms connect to the conversion host via SSH to execute the
ansible-runner
command. The Ansible playbooks are now shipped in the appliance to keep the playbooks in sync with the backend capabilities. This PR updates theextra_vars
hash to matchv2v-conversion-host-ansible-1.12
requirements and callsansible-runner
on the CloudForms appliance.To do so, it generates the runtime directory for the playbook:
ansible-runner
endsThe
ansible-runner
command is then run viaAwesomeSpawn.run
.Note: this PR is dedicated to Hammer. Another one will be created to use
Ansible::Runner
. This will require some changes toAnsible::Runner
class to allow specifying the inventory and the credentials.RHBZ: https://bugzilla.redhat.com/show_bug.cgi?id=1622728
Depends on: ManageIQ/manageiq-api#535, #18541