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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 3462919b9025af83ed45745747a68e372c32d513
4
- data.tar.gz: ac580f584cbdbbe36805788fe2de9f7ab9b1c186
3
+ metadata.gz: 840a69169eb96bfdb1a4c9ff9f85bb6cd6ea3b7c
4
+ data.tar.gz: 1b5f26981963dcdfe6682a97794eb24f2095ff48
5
5
  SHA512:
6
- metadata.gz: fd6bcb7a20188b49fb6f26ebcba09d3e70439968883511db6b919f0e25d11afc97a2ed745e30036de7f40fcee1998f567c8611e20541c9c561381a00e9111d3c
7
- data.tar.gz: 5ff7b8a58b3dbe26db35ce4ebfc31d61c8e3c77b7e734f452cd43385d663e353186e2d4c93fe9f032db669092929219185e12010f30bff758b67662a866291dc
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 host_title_actions_with_run_ansible_roles(*args)
12
- button = link_to(
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 => args.first.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
- title_actions(button_group(button)) if args.first.ansible_roles.present?
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
- # Actions that initiaztes the playbook run for roles assigned to
4
- # the host. It doest that either locally or via a proxy when available.
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
- input[:host] = { :id => host.id, :name => host.fqdn }
11
- proxy = proxy_selector.determine_proxy(host)
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, ::ForemanAnsibleCore::Actions::RunPlaybook,
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}') % { :name => input.fetch(: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
- def humanized_name
36
- _('Play Ansible roles')
37
- end
38
-
39
- def humanized_output
40
- continuous_output.humanize
41
- end
32
+ private
42
33
 
43
- def continuous_output_providers
44
- super << self
45
- end
46
-
47
- def fill_continuous_output(continuous_output)
48
- delegated_output.fetch('result', []).each do |raw_output|
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::Ansible < ::Setting
2
- def self.load_defaults
3
- return unless super
4
- self.transaction do
5
- [
6
- self.set('ansible_port', N_('Foreman will use this port to ssh into hosts for running playbooks'), 22, N_('Default port')),
7
- self.set('ansible_user', N_('Foreman will try to connect as this user to hosts when running Ansible playbooks.'), 'root', N_('Default user')),
8
- self.set('ansible_ssh_pass', N_('Foreman will use this password when running Ansible playbooks.'), 'ansible', N_('Default password'))
9
- ].compact.each { |s| self.create s.update(:category => 'Setting::Ansible') }
10
- end
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
- true
13
- end
77
+ true
78
+ end
14
79
 
15
- def self.humanized_category
16
- N_('Ansible')
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
- (facts[:ansible_interfaces].sort unless facts[:ansible_interfaces].nil?) || []
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(f, :ansible_roles, AnsibleRole, f.object.all_ansible_roles.map(&:id),
3
- {:disabled => f.object.inherited_ansible_roles.map(&:id),
4
- :label => _('Available roles')},
5
- { 'data-inheriteds' => f.object.inherited_ansible_roles.map(&:id).to_json }) %>
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 :play_roles,
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
- [:play_roles, :view_ansible_roles, :destroy_ansible_roles,
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
@@ -2,5 +2,5 @@
2
2
  # This way other parts of Foreman can just call ForemanAnsible::VERSION
3
3
  # and detect what version the plugin is running.
4
4
  module ForemanAnsible
5
- VERSION = '1.3.1'.freeze
5
+ VERSION = '1.4.0'.freeze
6
6
  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.3.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: 2016-12-19 00:00:00.000000000 Z
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.0
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