foreman_ansible 1.3.1 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of foreman_ansible might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/app/controllers/api/v2/ansible_roles_controller.rb +1 -2
- data/app/controllers/foreman_ansible/api/v2/hostgroups_controller_extensions.rb +76 -0
- data/app/controllers/foreman_ansible/api/v2/hosts_controller_extensions.rb +54 -0
- data/app/controllers/foreman_ansible/concerns/hostgroups_controller_extensions.rb +32 -0
- data/app/helpers/foreman_ansible/hosts_helper_extensions.rb +18 -5
- data/app/lib/actions/foreman_ansible/helpers/host_common.rb +41 -0
- data/app/lib/actions/foreman_ansible/helpers/play_roles_description.rb +19 -0
- data/app/lib/actions/foreman_ansible/play_host_roles.rb +23 -36
- data/app/lib/actions/foreman_ansible/play_hostgroup_roles.rb +57 -0
- data/app/models/concerns/foreman_ansible/host_managed_extensions.rb +13 -0
- data/app/models/setting/ansible.rb +81 -14
- data/app/overrides/hostgroup_play_roles.rb +7 -0
- data/app/services/foreman_ansible/fact_parser.rb +6 -1
- data/app/services/foreman_ansible/inventory_creator.rb +13 -3
- data/app/services/foreman_ansible/proxy_selector.rb +8 -0
- data/app/views/foreman_ansible/ansible_roles/_hostgroup_ansible_roles_button.erb +14 -0
- data/app/views/foreman_ansible/ansible_roles/_select_tab_content.html.erb +14 -4
- data/config/routes.rb +25 -0
- data/lib/foreman_ansible/engine.rb +20 -3
- data/lib/foreman_ansible/version.rb +1 -1
- data/test/functional/api/v2/hostgroups_controller_test.rb +49 -0
- data/test/functional/api/v2/hosts_controller_test.rb +48 -0
- metadata +15 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 840a69169eb96bfdb1a4c9ff9f85bb6cd6ea3b7c
|
4
|
+
data.tar.gz: 1b5f26981963dcdfe6682a97794eb24f2095ff48
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 27d7cb312d1d8b87e61cff2176d52c918132640cc4ba0cf159ad94fcd7771d12bd0a01af74302cb01a7fcfd1c53fd7d20a090097f2368111e93f82b256fd5a46
|
7
|
+
data.tar.gz: 45fdc84cdc1e0396f3e05976e9c40eb0a53af3561ea583fdaf64a3652fb4e9c8171aacc1271a65bbbbb7db13d580f95dcae6a837f6b3ab423169f8d55c8f17d5
|
@@ -10,8 +10,7 @@ module Api
|
|
10
10
|
|
11
11
|
api :GET, '/ansible/ansible_roles/:id', N_('Show role')
|
12
12
|
param :id, :identifier, :required => true
|
13
|
-
def show
|
14
|
-
end
|
13
|
+
def show; end
|
15
14
|
|
16
15
|
api :GET, '/ansible/ansible_roles', N_('List Ansible roles')
|
17
16
|
param_group :search_and_pagination, ::Api::V2::BaseController
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module ForemanAnsible
|
2
|
+
module Api
|
3
|
+
module V2
|
4
|
+
# Extends the hostgroups controller to support playing ansible roles
|
5
|
+
module HostgroupsControllerExtensions
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
include ForemanTasks::Triggers
|
8
|
+
|
9
|
+
# Included blocks shouldn't be bound by length, as otherwise concerns
|
10
|
+
# cannot extend the method properly.
|
11
|
+
# rubocop:disable BlockLength
|
12
|
+
included do
|
13
|
+
api :POST, '/hostgroups/play_roles',
|
14
|
+
N_('Plays Ansible roles on hostgroups')
|
15
|
+
param :id, Array, :required => true
|
16
|
+
|
17
|
+
def play_roles
|
18
|
+
find_resource
|
19
|
+
|
20
|
+
@result = {
|
21
|
+
:hostgroup => @hostgroup, :foreman_tasks => async_task(
|
22
|
+
::Actions::ForemanAnsible::PlayHostgroupRoles, @hostgroup
|
23
|
+
)
|
24
|
+
}
|
25
|
+
|
26
|
+
render_message @result
|
27
|
+
end
|
28
|
+
|
29
|
+
api :POST, '/hostgroups/play_roles',
|
30
|
+
N_('Plays Ansible roles on hostgroups')
|
31
|
+
param :id, Array, :required => true
|
32
|
+
|
33
|
+
def multiple_play_roles
|
34
|
+
find_multiple
|
35
|
+
|
36
|
+
@result = []
|
37
|
+
|
38
|
+
@hostgroups.uniq.each do |hostgroup|
|
39
|
+
@result.append(
|
40
|
+
:hostgroup => hostgroup, :foreman_tasks => async_task(
|
41
|
+
::Actions::ForemanAnsible::PlayHostgroupRoles, hostgroup
|
42
|
+
)
|
43
|
+
)
|
44
|
+
end
|
45
|
+
|
46
|
+
render_message @result
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
def find_multiple
|
53
|
+
hostgroup_ids = params.fetch(:hostgroup_ids, [])
|
54
|
+
hostgroup_names = params.fetch(:hostgroup_names, [])
|
55
|
+
|
56
|
+
@hostgroups = []
|
57
|
+
hostgroup_ids.uniq.each do |hostgroup_id|
|
58
|
+
@hostgroups.append(Hostgroup.find(hostgroup_id))
|
59
|
+
end
|
60
|
+
hostgroup_names.uniq.each do |hostgroup_name|
|
61
|
+
@hostgroups.append(Hostgroup.find_by(:name => hostgroup_name))
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
def action_permission
|
66
|
+
case params[:action]
|
67
|
+
when 'play_roles'
|
68
|
+
:view
|
69
|
+
else
|
70
|
+
super
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module ForemanAnsible
|
2
|
+
module Api
|
3
|
+
module V2
|
4
|
+
# Extends the hosts controller to support playing ansible roles
|
5
|
+
module HostsControllerExtensions
|
6
|
+
extend ActiveSupport::Concern
|
7
|
+
include ForemanTasks::Triggers
|
8
|
+
|
9
|
+
included do
|
10
|
+
api :POST, '/hosts/:id/play_roles', N_('Plays Ansible roles on hosts')
|
11
|
+
param :id, String, :required => true
|
12
|
+
|
13
|
+
def play_roles
|
14
|
+
@result = {
|
15
|
+
:host => @host, :foreman_tasks => async_task(
|
16
|
+
::Actions::ForemanAnsible::PlayHostRoles, @host
|
17
|
+
)
|
18
|
+
}
|
19
|
+
|
20
|
+
render_message @result
|
21
|
+
end
|
22
|
+
|
23
|
+
api :POST, '/hosts/play_roles', N_('Plays Ansible roles on hosts')
|
24
|
+
param :id, Array, :required => true
|
25
|
+
|
26
|
+
def multiple_play_roles
|
27
|
+
@result = []
|
28
|
+
|
29
|
+
@host.each do |item|
|
30
|
+
@result.append(
|
31
|
+
:host => item, :foreman_tasks => async_task(
|
32
|
+
::Actions::ForemanAnsible::PlayHostRoles, item
|
33
|
+
)
|
34
|
+
)
|
35
|
+
end
|
36
|
+
|
37
|
+
render_message @result
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def action_permission
|
44
|
+
case params[:action]
|
45
|
+
when 'play_roles', 'multiple_play_roles'
|
46
|
+
:view
|
47
|
+
else
|
48
|
+
super
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module ForemanAnsible
|
2
|
+
module Concerns
|
3
|
+
# Extra methods to enforce Ansible roles on a host or multiple hosts
|
4
|
+
module HostgroupsControllerExtensions
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
include ForemanTasks::Triggers
|
7
|
+
|
8
|
+
def play_roles
|
9
|
+
find_resource
|
10
|
+
task = async_task(
|
11
|
+
::Actions::ForemanAnsible::PlayHostgroupRoles,
|
12
|
+
@hostgroup
|
13
|
+
)
|
14
|
+
redirect_to task
|
15
|
+
rescue Foreman::Exception => e
|
16
|
+
error e.message
|
17
|
+
redirect_to hostgroups_path
|
18
|
+
end
|
19
|
+
|
20
|
+
private
|
21
|
+
|
22
|
+
def action_permission
|
23
|
+
case params[:action]
|
24
|
+
when 'play_roles'
|
25
|
+
:view
|
26
|
+
else
|
27
|
+
super
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -8,14 +8,27 @@ module ForemanAnsible
|
|
8
8
|
alias_method_chain(:multiple_actions, :run_ansible_roles)
|
9
9
|
end
|
10
10
|
|
11
|
-
def
|
12
|
-
|
11
|
+
def ansible_roles_present?(host)
|
12
|
+
host.ansible_roles.present? ||
|
13
|
+
host.inherited_ansible_roles.present?
|
14
|
+
end
|
15
|
+
|
16
|
+
def ansible_roles_button(host)
|
17
|
+
link_to(
|
13
18
|
icon_text('play', ' ' + _('Ansible roles'), :kind => 'fa'),
|
14
|
-
play_roles_host_path(:id =>
|
19
|
+
play_roles_host_path(:id => host.id),
|
15
20
|
:id => :ansible_roles_button,
|
16
|
-
:class => 'btn btn-default'
|
21
|
+
:class => 'btn btn-default',
|
22
|
+
:'data-no-turbolink' => true
|
17
23
|
)
|
18
|
-
|
24
|
+
end
|
25
|
+
|
26
|
+
def host_title_actions_with_run_ansible_roles(*args)
|
27
|
+
host = args.first
|
28
|
+
if ansible_roles_present?(host)
|
29
|
+
button = ansible_roles_button(host)
|
30
|
+
title_actions(button_group(button))
|
31
|
+
end
|
19
32
|
host_title_actions_without_run_ansible_roles(*args)
|
20
33
|
end
|
21
34
|
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Actions
|
2
|
+
module ForemanAnsible
|
3
|
+
module Helpers
|
4
|
+
# Shared task methods between hostgroup and host roles actions
|
5
|
+
module HostCommon
|
6
|
+
def finalize
|
7
|
+
return unless delegated_output[:exit_status].to_s != '0'
|
8
|
+
error! _('Playbook execution failed')
|
9
|
+
end
|
10
|
+
|
11
|
+
def rescue_strategy
|
12
|
+
::Dynflow::Action::Rescue::Fail
|
13
|
+
end
|
14
|
+
|
15
|
+
def humanized_name
|
16
|
+
_('Play Ansible roles')
|
17
|
+
end
|
18
|
+
|
19
|
+
def humanized_output
|
20
|
+
continuous_output.humanize
|
21
|
+
end
|
22
|
+
|
23
|
+
def continuous_output_providers
|
24
|
+
super << self
|
25
|
+
end
|
26
|
+
|
27
|
+
def fill_continuous_output(continuous_output)
|
28
|
+
delegated_output.fetch('result', []).each do |raw_output|
|
29
|
+
continuous_output.add_raw_output(raw_output)
|
30
|
+
end
|
31
|
+
rescue => e
|
32
|
+
continuous_output.add_exception(_('Error loading data from proxy'), e)
|
33
|
+
end
|
34
|
+
|
35
|
+
def find_options
|
36
|
+
{ :verbosity_level => Setting[:ansible_verbosity] }
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Actions
|
2
|
+
module ForemanAnsible
|
3
|
+
module Helpers
|
4
|
+
# Returns the name of the proxy running the specified action, or Foreman
|
5
|
+
# if it's the one running the action instead.
|
6
|
+
module PlayRolesDescription
|
7
|
+
def running_proxy_name
|
8
|
+
proxy = input.fetch(:host, {})[:proxy_used]
|
9
|
+
proxy ||= input.fetch(:hostgroup, {})[:proxy_used]
|
10
|
+
if [:not_defined, 'Foreman'].include? proxy
|
11
|
+
_('Foreman')
|
12
|
+
else
|
13
|
+
proxy
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -1,55 +1,42 @@
|
|
1
1
|
module Actions
|
2
2
|
module ForemanAnsible
|
3
|
-
#
|
4
|
-
# the host. It
|
3
|
+
# Action that initiates the playbook run for roles assigned to
|
4
|
+
# the host. It does that either locally or via a proxy when available.
|
5
5
|
class PlayHostRoles < Actions::EntryAction
|
6
6
|
include ::Actions::Helpers::WithContinuousOutput
|
7
7
|
include ::Actions::Helpers::WithDelegatedAction
|
8
|
+
include Helpers::PlayRolesDescription
|
9
|
+
include Helpers::HostCommon
|
8
10
|
|
9
|
-
def plan(host, proxy_selector = ::ForemanAnsible::ProxySelector.new
|
10
|
-
|
11
|
-
proxy =
|
12
|
-
inventory_creator = ::ForemanAnsible::InventoryCreator.new([host])
|
11
|
+
def plan(host, proxy_selector = ::ForemanAnsible::ProxySelector.new,
|
12
|
+
options = {})
|
13
|
+
proxy = find_host_and_proxy(host, proxy_selector)
|
13
14
|
role_names = host.all_ansible_roles.map(&:name)
|
15
|
+
inventory_creator = ::ForemanAnsible::InventoryCreator.new([host])
|
14
16
|
playbook_creator = ::ForemanAnsible::PlaybookCreator.new(role_names)
|
15
|
-
plan_delegated_action(proxy,
|
17
|
+
plan_delegated_action(proxy,
|
18
|
+
::ForemanAnsibleCore::Actions::RunPlaybook,
|
16
19
|
:inventory => inventory_creator.to_hash.to_json,
|
17
|
-
:playbook => playbook_creator.roles_playbook
|
20
|
+
:playbook => playbook_creator.roles_playbook,
|
21
|
+
:options => find_options.merge(options))
|
18
22
|
plan_self
|
19
23
|
end
|
20
24
|
|
21
|
-
def finalize
|
22
|
-
if delegated_output[:exit_status].to_s != '0'
|
23
|
-
error! _('Playbook execution failed')
|
24
|
-
end
|
25
|
-
end
|
26
|
-
|
27
|
-
def rescue_strategy
|
28
|
-
::Dynflow::Action::Rescue::Fail
|
29
|
-
end
|
30
|
-
|
31
25
|
def humanized_input
|
32
|
-
_('on host %{name}') % {
|
26
|
+
_('on host %{name} through %{proxy}') % {
|
27
|
+
:name => input.fetch(:host, {})[:name],
|
28
|
+
:proxy => running_proxy_name
|
29
|
+
}
|
33
30
|
end
|
34
31
|
|
35
|
-
|
36
|
-
_('Play Ansible roles')
|
37
|
-
end
|
38
|
-
|
39
|
-
def humanized_output
|
40
|
-
continuous_output.humanize
|
41
|
-
end
|
32
|
+
private
|
42
33
|
|
43
|
-
def
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
continuous_output.add_raw_output(raw_output)
|
50
|
-
end
|
51
|
-
rescue => e
|
52
|
-
continuous_output.add_exception(_('Error loading data from proxy'), e)
|
34
|
+
def find_host_and_proxy(host, proxy_selector)
|
35
|
+
proxy = proxy_selector.determine_proxy(host)
|
36
|
+
input[:host] = { :id => host.id,
|
37
|
+
:name => host.fqdn,
|
38
|
+
:proxy_used => proxy.try(:name) || :not_defined }
|
39
|
+
proxy
|
53
40
|
end
|
54
41
|
end
|
55
42
|
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
module Actions
|
2
|
+
module ForemanAnsible
|
3
|
+
# Action that initiates the playbook run for roles assigned to
|
4
|
+
# the hostgroup. It does that either locally or via a proxy when available.
|
5
|
+
class PlayHostgroupRoles < Actions::EntryAction
|
6
|
+
include ::Actions::Helpers::WithContinuousOutput
|
7
|
+
include ::Actions::Helpers::WithDelegatedAction
|
8
|
+
include Helpers::PlayRolesDescription
|
9
|
+
include Helpers::HostCommon
|
10
|
+
|
11
|
+
def plan(hostgroup, proxy_selector = ::ForemanAnsible::ProxySelector.new,
|
12
|
+
options = {})
|
13
|
+
proxy = find_hostgroup_and_proxy(hostgroup, proxy_selector)
|
14
|
+
inventory_creator = ::ForemanAnsible::
|
15
|
+
InventoryCreator.new(hostgroup.hosts)
|
16
|
+
playbook_creator = ::ForemanAnsible::
|
17
|
+
PlaybookCreator.new(hostgroup_ansible_roles(hostgroup))
|
18
|
+
plan_delegated_action(proxy, ::ForemanAnsibleCore::Actions::RunPlaybook,
|
19
|
+
:inventory => inventory_creator.to_hash.to_json,
|
20
|
+
:playbook => playbook_creator.roles_playbook,
|
21
|
+
:options => find_options.merge(options))
|
22
|
+
plan_self
|
23
|
+
end
|
24
|
+
|
25
|
+
def humanized_input
|
26
|
+
_('on host group %{name} through proxy %{proxy}') % {
|
27
|
+
:name => input.fetch(:hostgroup, {})[:name],
|
28
|
+
:proxy => running_proxy_name
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def hostgroup_ansible_roles(hostgroup)
|
35
|
+
role_names = []
|
36
|
+
hostgroup.hostgroup_ansible_roles.each do |ansible_role|
|
37
|
+
role_names.append(ansible_role.ansible_role_name)
|
38
|
+
end
|
39
|
+
role_names
|
40
|
+
end
|
41
|
+
|
42
|
+
def hostgroup_contains_hosts(hostgroup)
|
43
|
+
return unless hostgroup.hosts.empty?
|
44
|
+
raise ::Foreman::Exception.new(N_('host group is empty'))
|
45
|
+
end
|
46
|
+
|
47
|
+
def find_hostgroup_and_proxy(hostgroup, proxy_selector)
|
48
|
+
hostgroup_contains_hosts(hostgroup)
|
49
|
+
proxy = proxy_selector.determine_proxy(hostgroup.hosts[0])
|
50
|
+
input[:hostgroup] = { :id => hostgroup.id,
|
51
|
+
:name => hostgroup.name,
|
52
|
+
:proxy_used => proxy.try(:name) || :not_defined }
|
53
|
+
proxy
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -7,12 +7,25 @@ module ForemanAnsible
|
|
7
7
|
has_many :host_ansible_roles, :foreign_key => :host_id
|
8
8
|
has_many :ansible_roles, :through => :host_ansible_roles,
|
9
9
|
:dependent => :destroy
|
10
|
+
before_provision :play_ansible_roles
|
10
11
|
include ForemanAnsible::HasManyAnsibleRoles
|
11
12
|
|
12
13
|
def inherited_ansible_roles
|
13
14
|
return [] unless hostgroup
|
14
15
|
hostgroup.all_ansible_roles
|
15
16
|
end
|
17
|
+
|
18
|
+
def play_ansible_roles
|
19
|
+
return unless ansible_roles.present? || inherited_ansible_roles.present?
|
20
|
+
task = ::ForemanTasks.async_task(
|
21
|
+
::Actions::ForemanAnsible::PlayHostRoles,
|
22
|
+
self,
|
23
|
+
::ForemanAnsible::ProxySelector.new,
|
24
|
+
:timeout => Setting['ansible_post_provision_timeout']
|
25
|
+
)
|
26
|
+
logger.info("Task for Ansible roles on #{self} before_provision: "\
|
27
|
+
"#{Rails.application.routes.url_helpers.task_path(task)}.")
|
28
|
+
end
|
16
29
|
end
|
17
30
|
end
|
18
31
|
end
|
@@ -1,18 +1,85 @@
|
|
1
|
-
class Setting
|
2
|
-
|
3
|
-
|
4
|
-
self
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
1
|
+
class Setting
|
2
|
+
# Provide settings related with Ansible
|
3
|
+
class Ansible < ::Setting
|
4
|
+
class << self
|
5
|
+
# It would be more disadvantages than advantages to split up
|
6
|
+
# load_defaults into multiple methods, this way it's already very
|
7
|
+
# manageable.
|
8
|
+
# rubocop:disable AbcSize
|
9
|
+
# rubocop:disable MethodLength
|
10
|
+
# rubocop:disable BlockLength
|
11
|
+
def load_defaults
|
12
|
+
return unless super
|
13
|
+
transaction do
|
14
|
+
[
|
15
|
+
set(
|
16
|
+
'ansible_port',
|
17
|
+
N_('Use this port to connect to hosts '\
|
18
|
+
'and run Ansible. You can override this on hosts'\
|
19
|
+
' by adding a parameter "ansible_port"'),
|
20
|
+
22,
|
21
|
+
N_('Port')
|
22
|
+
),
|
23
|
+
set(
|
24
|
+
'ansible_user',
|
25
|
+
N_('Foreman will try to connect to hosts as this user by default'\
|
26
|
+
' when running Ansible playbooks. You can override this '\
|
27
|
+
' on hosts by adding a parameter "ansible_user"'),
|
28
|
+
'root',
|
29
|
+
N_('User')
|
30
|
+
),
|
31
|
+
set(
|
32
|
+
'ansible_ssh_pass',
|
33
|
+
N_('Use this password by default when running Ansible '\
|
34
|
+
'playbooks. You can override this on hosts '\
|
35
|
+
'by adding a parameter "ansible_ssh_pass"'),
|
36
|
+
'ansible',
|
37
|
+
N_('Password')
|
38
|
+
),
|
39
|
+
set(
|
40
|
+
'ansible_connection',
|
41
|
+
N_('Use this connection type by default when running '\
|
42
|
+
'Ansible playbooks. You can override this on hosts by '\
|
43
|
+
'adding a parameter "ansible_connection"'),
|
44
|
+
'ssh',
|
45
|
+
N_('Connection type')
|
46
|
+
),
|
47
|
+
set(
|
48
|
+
'ansible_winrm_server_cert_validation',
|
49
|
+
N_('Enable/disable WinRM server certificate '\
|
50
|
+
'validation when running Ansible playbooks. You can override '\
|
51
|
+
'this on hosts by adding a parameter '\
|
52
|
+
'"ansible_winrm_server_cert_validation"'),
|
53
|
+
'validate',
|
54
|
+
N_('WinRM cert Validation')
|
55
|
+
),
|
56
|
+
set(
|
57
|
+
'ansible_verbosity',
|
58
|
+
N_('Foreman will add the this level of verbosity for '\
|
59
|
+
'additional debugging output when running Ansible playbooks.'),
|
60
|
+
'0',
|
61
|
+
N_('Default verbosity level')
|
62
|
+
),
|
63
|
+
set(
|
64
|
+
'ansible_post_provision_timeout',
|
65
|
+
N_('Timeout (in seconds) to set when Foreman will trigger a '\
|
66
|
+
'play Ansible roles task after a host is fully provisioned. '\
|
67
|
+
'Set this to the maximum time you expect a host to take until'\
|
68
|
+
' it is ready after a reboot.'),
|
69
|
+
'360',
|
70
|
+
N_('Post-provision timeout')
|
71
|
+
)
|
72
|
+
].compact.each do |s|
|
73
|
+
create(s.update(:category => 'Setting::Ansible'))
|
74
|
+
end
|
75
|
+
end
|
11
76
|
|
12
|
-
|
13
|
-
|
77
|
+
true
|
78
|
+
end
|
14
79
|
|
15
|
-
|
16
|
-
|
80
|
+
def humanized_category
|
81
|
+
N_('Ansible')
|
82
|
+
end
|
83
|
+
end
|
17
84
|
end
|
18
85
|
end
|
@@ -0,0 +1,7 @@
|
|
1
|
+
# Displays Ansible roles button in host group action buttons
|
2
|
+
Deface::Override.new(
|
3
|
+
:virtual_path => 'hostgroups/index',
|
4
|
+
:name => 'hostgroup_ansible_roles_button',
|
5
|
+
:replace => "erb[loud]:contains('action_buttons')",
|
6
|
+
:partial => 'foreman_ansible/ansible_roles/hostgroup_ansible_roles_button'
|
7
|
+
)
|
@@ -49,7 +49,7 @@ module ForemanAnsible
|
|
49
49
|
if pref.present?
|
50
50
|
(facts[:ansible_interfaces] - [pref]).unshift(pref)
|
51
51
|
else
|
52
|
-
|
52
|
+
ansible_interfaces
|
53
53
|
end
|
54
54
|
end
|
55
55
|
|
@@ -64,6 +64,11 @@ module ForemanAnsible
|
|
64
64
|
|
65
65
|
private
|
66
66
|
|
67
|
+
def ansible_interfaces
|
68
|
+
return [] unless facts[:ansible_interfaces].present?
|
69
|
+
facts[:ansible_interfaces].sort
|
70
|
+
end
|
71
|
+
|
67
72
|
def ip_from_interface(interface)
|
68
73
|
return unless facts[:"ansible_#{interface}"]['ipv4'].present?
|
69
74
|
facts[:"ansible_#{interface}"]['ipv4']['address']
|
@@ -38,15 +38,25 @@ module ForemanAnsible
|
|
38
38
|
params = {
|
39
39
|
'ansible_port' => host_port(host),
|
40
40
|
'ansible_user' => host_user(host),
|
41
|
-
'ansible_ssh_pass' => host_ssh_pass(host)
|
41
|
+
'ansible_ssh_pass' => host_ssh_pass(host),
|
42
|
+
'ansible_connection' => connection_type(host),
|
43
|
+
'ansible_winrm_server_cert_validation' => winrm_cert_validation(host)
|
42
44
|
}
|
43
|
-
|
44
|
-
#Backward compatibility for Ansible 1.x
|
45
|
+
# Backward compatibility for Ansible 1.x
|
45
46
|
params['ansible_ssh_port'] = params['ansible_port']
|
46
47
|
params['ansible_ssh_user'] = params['ansible_user']
|
47
48
|
params
|
48
49
|
end
|
49
50
|
|
51
|
+
def winrm_cert_validation(host)
|
52
|
+
host.host_params['ansible_winrm_server_cert_validation'] ||
|
53
|
+
Setting['ansible_winrm_server_cert_validation']
|
54
|
+
end
|
55
|
+
|
56
|
+
def connection_type(host)
|
57
|
+
host.host_params['ansible_connection'] || Setting['ansible_connection']
|
58
|
+
end
|
59
|
+
|
50
60
|
def host_roles(host)
|
51
61
|
host.all_ansible_roles.map(&:name)
|
52
62
|
end
|
@@ -8,6 +8,14 @@ module ForemanAnsible
|
|
8
8
|
proxies
|
9
9
|
end
|
10
10
|
|
11
|
+
def determine_proxy(*args)
|
12
|
+
result = super
|
13
|
+
return result unless result == :not_available
|
14
|
+
# Always run roles in some way, even if there are no proxies, Foreman
|
15
|
+
# should take that role in that case.
|
16
|
+
:not_defined
|
17
|
+
end
|
18
|
+
|
11
19
|
private
|
12
20
|
|
13
21
|
def proxy_scope(host)
|
@@ -0,0 +1,14 @@
|
|
1
|
+
<%=
|
2
|
+
if hostgroup.all_ansible_roles.empty?
|
3
|
+
action_buttons(
|
4
|
+
display_link_if_authorized(_('Nest'), hash_for_nest_hostgroup_path(:id => hostgroup)),
|
5
|
+
display_link_if_authorized(_('Clone'), hash_for_clone_hostgroup_path(:id => hostgroup)),
|
6
|
+
display_delete_if_authorized(hash_for_hostgroup_path(:id => hostgroup).merge(:auth_object => hostgroup, :authorizer => authorizer), :data => { :confirm => warning_message(hostgroup) }))
|
7
|
+
else
|
8
|
+
action_buttons(
|
9
|
+
display_link_if_authorized(_('Nest'), hash_for_nest_hostgroup_path(:id => hostgroup)),
|
10
|
+
display_link_if_authorized(_('Clone'), hash_for_clone_hostgroup_path(:id => hostgroup)),
|
11
|
+
display_link_if_authorized(_('Play Roles'), hash_for_play_roles_hostgroup_path(:id => hostgroup), :'data-no-turbolink' => true),
|
12
|
+
display_delete_if_authorized(hash_for_hostgroup_path(:id => hostgroup).merge(:auth_object => hostgroup, :authorizer => authorizer), :data => { :confirm => warning_message(hostgroup) }))
|
13
|
+
end
|
14
|
+
%>
|
@@ -1,6 +1,16 @@
|
|
1
1
|
<div class='tab-pane' id='ansible_roles'>
|
2
|
-
<%= multiple_selects(
|
3
|
-
|
4
|
-
|
5
|
-
|
2
|
+
<%= multiple_selects(
|
3
|
+
f,
|
4
|
+
:ansible_roles,
|
5
|
+
AnsibleRole,
|
6
|
+
f.object.all_ansible_roles.map(&:id),
|
7
|
+
{
|
8
|
+
:disabled => f.object.inherited_ansible_roles.map(&:id),
|
9
|
+
:label => _('Available roles'),
|
10
|
+
:help_inline => popover('' ,
|
11
|
+
_('This list of roles will be applied when the host finishes '\
|
12
|
+
'provisioning. Users can also play these roles through the API '\
|
13
|
+
'or by clicking on the Play Roles button on the Host page '))
|
14
|
+
},
|
15
|
+
{ 'data-inheriteds' => f.object.inherited_ansible_roles.map(&:id).to_json }) %>
|
6
16
|
</div>
|
data/config/routes.rb
CHANGED
@@ -9,6 +9,11 @@ Rails.application.routes.draw do
|
|
9
9
|
get :multiple_play_roles
|
10
10
|
end
|
11
11
|
end
|
12
|
+
resources :hostgroups, :only => [] do
|
13
|
+
member do
|
14
|
+
get :play_roles
|
15
|
+
end
|
16
|
+
end
|
12
17
|
end
|
13
18
|
|
14
19
|
resources :ansible_roles, :only => [:index, :destroy] do
|
@@ -25,6 +30,26 @@ Rails.application.routes.draw do
|
|
25
30
|
:apiv => /v1|v2/,
|
26
31
|
:constraints => ApiConstraints.new(:version => 2) do
|
27
32
|
|
33
|
+
constraints(:id => %r{[^\/]+}) do
|
34
|
+
resources :hosts, :only => [] do
|
35
|
+
member do
|
36
|
+
post :play_roles
|
37
|
+
end
|
38
|
+
collection do
|
39
|
+
post :multiple_play_roles
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
resources :hostgroups, :only => [] do
|
44
|
+
member do
|
45
|
+
post :play_roles
|
46
|
+
end
|
47
|
+
collection do
|
48
|
+
post :multiple_play_roles
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
28
53
|
resources :ansible_roles, :only => [:show, :index, :destroy] do
|
29
54
|
collection do
|
30
55
|
put :import
|
@@ -35,9 +35,16 @@ module ForemanAnsible
|
|
35
35
|
requires_foreman '>= 1.12'
|
36
36
|
|
37
37
|
security_block :foreman_ansible do
|
38
|
-
permission :
|
39
|
-
{ :hosts => [:play_roles, :multiple_play_roles]
|
38
|
+
permission :play_roles_on_host,
|
39
|
+
{ :hosts => [:play_roles, :multiple_play_roles],
|
40
|
+
:'api/v2/hosts' => [:play_roles,
|
41
|
+
:multiple_play_roles] },
|
40
42
|
:resource_type => 'Host'
|
43
|
+
permission :play_roles_on_hostgroup,
|
44
|
+
{ :hostgroups => [:play_roles],
|
45
|
+
:'api/v2/hostgroups' => [:play_roles,
|
46
|
+
:multiple_play_roles] },
|
47
|
+
:resource_type => 'Hostgroup'
|
41
48
|
permission :view_ansible_roles,
|
42
49
|
{ :ansible_roles => [:index],
|
43
50
|
:'api/v2/ansible_roles' => [:index, :show] },
|
@@ -53,7 +60,8 @@ module ForemanAnsible
|
|
53
60
|
end
|
54
61
|
|
55
62
|
role 'Ansible Roles Manager',
|
56
|
-
[:
|
63
|
+
[:play_roles_on_host, :play_roles_on_hostgroup,
|
64
|
+
:view_ansible_roles, :destroy_ansible_roles,
|
57
65
|
:import_ansible_roles]
|
58
66
|
|
59
67
|
role_assignment_params = { :ansible_role_ids => [],
|
@@ -113,6 +121,15 @@ module ForemanAnsible
|
|
113
121
|
::HostsController.send(
|
114
122
|
:include, ForemanAnsible::Concerns::HostsControllerExtensions
|
115
123
|
)
|
124
|
+
::Api::V2::HostsController.send(
|
125
|
+
:include, ForemanAnsible::Api::V2::HostsControllerExtensions
|
126
|
+
)
|
127
|
+
::HostgroupsController.send(
|
128
|
+
:include, ForemanAnsible::Concerns::HostgroupsControllerExtensions
|
129
|
+
)
|
130
|
+
::Api::V2::HostgroupsController.send(
|
131
|
+
:include, ForemanAnsible::Api::V2::HostgroupsControllerExtensions
|
132
|
+
)
|
116
133
|
rescue => e
|
117
134
|
Rails.logger.warn "Foreman Ansible: skipping engine hook (#{e})"
|
118
135
|
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
require 'test_plugin_helper'
|
2
|
+
|
3
|
+
module Api
|
4
|
+
module V2
|
5
|
+
class HostgroupsControllerTest < ActionController::TestCase
|
6
|
+
include ::Dynflow::Testing
|
7
|
+
|
8
|
+
setup do
|
9
|
+
@host1 = FactoryGirl.create(:host, :with_hostgroup)
|
10
|
+
@host2 = FactoryGirl.create(:host, :with_hostgroup)
|
11
|
+
end
|
12
|
+
|
13
|
+
after do
|
14
|
+
::ForemanTasks::Task::DynflowTask.all.each do |task|
|
15
|
+
task.destroy
|
16
|
+
task.delete
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
test 'should return an not_found due to non-existent host_id' do
|
21
|
+
post :play_roles, :id => 'non-existent'
|
22
|
+
response = JSON.parse(@response.body)
|
23
|
+
refute_empty response
|
24
|
+
assert_response :not_found
|
25
|
+
end
|
26
|
+
|
27
|
+
test 'should trigger task on host group' do
|
28
|
+
post :play_roles, :id => @host1.hostgroup.id
|
29
|
+
response = JSON.parse(@response.body)
|
30
|
+
|
31
|
+
assert response['message']['foreman_tasks'].key?('id'),
|
32
|
+
'task id not contained in response'
|
33
|
+
assert_equal response['message']['hostgroup']['name'],
|
34
|
+
@host1.hostgroup.name,
|
35
|
+
'host group name not contained in response'
|
36
|
+
assert_response :success
|
37
|
+
end
|
38
|
+
|
39
|
+
test 'should trigger two host group tasks' do
|
40
|
+
post :multiple_play_roles,
|
41
|
+
:hostgroup_names => [@host1.hostgroup.name, @host2.hostgroup.name]
|
42
|
+
response = JSON.parse(@response.body)
|
43
|
+
|
44
|
+
assert response['message'].length == 2, 'should trigger two tasks'
|
45
|
+
assert_response :success
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'test_plugin_helper'
|
2
|
+
|
3
|
+
module Api
|
4
|
+
module V2
|
5
|
+
class HostsControllerTest < ActionController::TestCase
|
6
|
+
include ::Dynflow::Testing
|
7
|
+
|
8
|
+
setup do
|
9
|
+
@host1 = FactoryGirl.create(:host)
|
10
|
+
@host2 = FactoryGirl.create(:host)
|
11
|
+
end
|
12
|
+
|
13
|
+
after do
|
14
|
+
::ForemanTasks::Task::DynflowTask.all.each do |task|
|
15
|
+
task.destroy
|
16
|
+
task.delete
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
test 'should return an not_found due to non-existent host_id' do
|
21
|
+
post :play_roles, :id => 'non-existent'
|
22
|
+
response = JSON.parse(@response.body)
|
23
|
+
refute_empty response
|
24
|
+
assert_response :not_found
|
25
|
+
end
|
26
|
+
|
27
|
+
test 'should trigger task on host' do
|
28
|
+
post :play_roles, :id => @host1.id
|
29
|
+
response = JSON.parse(@response.body)
|
30
|
+
|
31
|
+
assert response['message']['foreman_tasks'].key?('id'),
|
32
|
+
'task id not contained in response'
|
33
|
+
assert_equal response['message']['host']['name'],
|
34
|
+
@host1.name,
|
35
|
+
'host name not contained in response'
|
36
|
+
assert_response :success
|
37
|
+
end
|
38
|
+
|
39
|
+
test 'should trigger two host tasks' do
|
40
|
+
post :multiple_play_roles, :id => [@host1.id, @host2.id]
|
41
|
+
response = JSON.parse(@response.body)
|
42
|
+
|
43
|
+
assert response['message'].length == 2, 'should trigger two tasks'
|
44
|
+
assert_response :success
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: foreman_ansible
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Daniel Lobato Garcia
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2017-01-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: rubocop
|
@@ -93,12 +93,18 @@ files:
|
|
93
93
|
- app/assets/images/Ansible.png
|
94
94
|
- app/controllers/ansible_roles_controller.rb
|
95
95
|
- app/controllers/api/v2/ansible_roles_controller.rb
|
96
|
+
- app/controllers/foreman_ansible/api/v2/hostgroups_controller_extensions.rb
|
97
|
+
- app/controllers/foreman_ansible/api/v2/hosts_controller_extensions.rb
|
98
|
+
- app/controllers/foreman_ansible/concerns/hostgroups_controller_extensions.rb
|
96
99
|
- app/controllers/foreman_ansible/concerns/hosts_controller_extensions.rb
|
97
100
|
- app/helpers/foreman_ansible/ansible_plugin_helper.rb
|
98
101
|
- app/helpers/foreman_ansible/ansible_reports_helper.rb
|
99
102
|
- app/helpers/foreman_ansible/ansible_roles_helper.rb
|
100
103
|
- app/helpers/foreman_ansible/hosts_helper_extensions.rb
|
104
|
+
- app/lib/actions/foreman_ansible/helpers/host_common.rb
|
105
|
+
- app/lib/actions/foreman_ansible/helpers/play_roles_description.rb
|
101
106
|
- app/lib/actions/foreman_ansible/play_host_roles.rb
|
107
|
+
- app/lib/actions/foreman_ansible/play_hostgroup_roles.rb
|
102
108
|
- app/lib/actions/foreman_ansible/play_hosts_roles.rb
|
103
109
|
- app/lib/proxy_api/ansible.rb
|
104
110
|
- app/models/ansible_role.rb
|
@@ -111,6 +117,7 @@ files:
|
|
111
117
|
- app/models/setting/ansible.rb
|
112
118
|
- app/overrides/ansible_roles_tab.rb
|
113
119
|
- app/overrides/hostgroup_ansible_roles_tab.rb
|
120
|
+
- app/overrides/hostgroup_play_roles.rb
|
114
121
|
- app/overrides/report_output.rb
|
115
122
|
- app/services/foreman_ansible/api_roles_importer.rb
|
116
123
|
- app/services/foreman_ansible/fact_importer.rb
|
@@ -129,6 +136,7 @@ files:
|
|
129
136
|
- app/views/api/v2/ansible_roles/index.json.rabl
|
130
137
|
- app/views/api/v2/ansible_roles/obsolete.json.rabl
|
131
138
|
- app/views/api/v2/ansible_roles/show.json.rabl
|
139
|
+
- app/views/foreman_ansible/ansible_roles/_hostgroup_ansible_roles_button.erb
|
132
140
|
- app/views/foreman_ansible/ansible_roles/_select_tab_content.html.erb
|
133
141
|
- app/views/foreman_ansible/ansible_roles/_select_tab_title.html.erb
|
134
142
|
- app/views/foreman_ansible/api/v2/ansible_roles/import.json.rabl
|
@@ -159,6 +167,8 @@ files:
|
|
159
167
|
- test/fixtures/sample_facts.json
|
160
168
|
- test/functional/ansible_roles_controller_test.rb
|
161
169
|
- test/functional/api/v2/ansible_roles_controller_test.rb
|
170
|
+
- test/functional/api/v2/hostgroups_controller_test.rb
|
171
|
+
- test/functional/api/v2/hosts_controller_test.rb
|
162
172
|
- test/functional/hosts_controller_test.rb
|
163
173
|
- test/support/fixture_support.rb
|
164
174
|
- test/support/foreman_tasks/task.rb
|
@@ -200,7 +210,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
200
210
|
version: '0'
|
201
211
|
requirements: []
|
202
212
|
rubyforge_project:
|
203
|
-
rubygems_version: 2.5.
|
213
|
+
rubygems_version: 2.4.5.1
|
204
214
|
signing_key:
|
205
215
|
specification_version: 4
|
206
216
|
summary: Ansible integration with Foreman (theforeman.org)
|
@@ -213,6 +223,8 @@ test_files:
|
|
213
223
|
- test/support/foreman_test_helper_additions.rb
|
214
224
|
- test/support/foreman_tasks/task.rb
|
215
225
|
- test/functional/api/v2/ansible_roles_controller_test.rb
|
226
|
+
- test/functional/api/v2/hostgroups_controller_test.rb
|
227
|
+
- test/functional/api/v2/hosts_controller_test.rb
|
216
228
|
- test/functional/ansible_roles_controller_test.rb
|
217
229
|
- test/functional/hosts_controller_test.rb
|
218
230
|
- test/test_plugin_helper.rb
|