foreman_ansible 3.0.9 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. checksums.yaml +4 -4
  2. data/Rakefile +1 -1
  3. data/app/controllers/api/v2/ansible_inventories_controller.rb +50 -0
  4. data/app/models/ansible_variable.rb +1 -0
  5. data/app/models/foreman_ansible/ansible_provider.rb +25 -1
  6. data/app/models/foreman_ansible/fact_name.rb +5 -1
  7. data/app/models/setting/ansible.rb +81 -88
  8. data/app/services/foreman_ansible/fact_importer.rb +5 -8
  9. data/app/services/foreman_ansible/fact_parser.rb +16 -4
  10. data/app/services/foreman_ansible/inventory_creator.rb +1 -12
  11. data/app/views/foreman_ansible/job_templates/run_playbook-ansible_default.erb +17 -0
  12. data/config/routes.rb +1 -2
  13. data/db/migrate/20190328114657_remove_top_level_ansible_variables_setting.rb +1 -1
  14. data/db/migrate/20191010074208_remove_ansible_implementation_setting.rb +5 -0
  15. data/db/seeds.d/75_job_templates.rb +2 -2
  16. data/lib/foreman_ansible/register.rb +5 -2
  17. data/lib/foreman_ansible/remote_execution.rb +6 -0
  18. data/lib/foreman_ansible/version.rb +1 -1
  19. data/locale/Makefile +7 -0
  20. data/locale/de/LC_MESSAGES/foreman_ansible.mo +0 -0
  21. data/locale/de/foreman_ansible.po +1 -1
  22. data/locale/en/LC_MESSAGES/foreman_ansible.mo +0 -0
  23. data/locale/en/foreman_ansible.po +1 -1
  24. data/locale/es/LC_MESSAGES/foreman_ansible.mo +0 -0
  25. data/locale/es/foreman_ansible.po +1 -1
  26. data/locale/fr/LC_MESSAGES/foreman_ansible.mo +0 -0
  27. data/locale/fr/foreman_ansible.po +1 -1
  28. data/locale/it/LC_MESSAGES/foreman_ansible.mo +0 -0
  29. data/locale/it/foreman_ansible.po +1 -1
  30. data/locale/ja/LC_MESSAGES/foreman_ansible.mo +0 -0
  31. data/locale/ja/foreman_ansible.po +1 -1
  32. data/locale/ko/LC_MESSAGES/foreman_ansible.mo +0 -0
  33. data/locale/ko/foreman_ansible.po +1 -1
  34. data/locale/pt_BR/LC_MESSAGES/foreman_ansible.mo +0 -0
  35. data/locale/pt_BR/foreman_ansible.po +1 -1
  36. data/locale/ru/LC_MESSAGES/foreman_ansible.mo +0 -0
  37. data/locale/ru/foreman_ansible.po +1 -1
  38. data/locale/zh_CN/LC_MESSAGES/foreman_ansible.mo +0 -0
  39. data/locale/zh_CN/foreman_ansible.po +1 -1
  40. data/locale/zh_TW/LC_MESSAGES/foreman_ansible.mo +0 -0
  41. data/locale/zh_TW/foreman_ansible.po +1 -1
  42. data/package.json +3 -3
  43. data/test/unit/ansible_provider_test.rb +16 -0
  44. data/test/unit/ansible_variable_test.rb +10 -0
  45. data/test/unit/lib/foreman_ansible_core/ansible_runner_test.rb +51 -0
  46. data/test/unit/lib/foreman_ansible_core/playbook_runner_test.rb +34 -1
  47. data/test/unit/services/fact_importer_test.rb +4 -2
  48. data/test/unit/services/fact_parser_test.rb +57 -4
  49. data/test/unit/services/inventory_creator_test.rb +10 -25
  50. data/webpack/__mocks__/foremanReact/common/I18n.js +1 -0
  51. data/webpack/components/AnsibleRolesSwitcher/AnsibleRolesSwitcher.js +1 -0
  52. data/webpack/components/AnsibleRolesSwitcher/components/AnsiblePermissionDenied.js +1 -0
  53. data/webpack/components/AnsibleRolesSwitcher/components/AnsibleRole.js +1 -0
  54. data/webpack/components/AnsibleRolesSwitcher/components/AssignedRolesList.js +18 -23
  55. data/webpack/index.js +5 -4
  56. metadata +15 -11
  57. data/app/lib/actions/foreman_ansible/helpers/host_common.rb +0 -39
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 31a09bd7451b630e412be3bcb6c0c83c52b20df0
4
- data.tar.gz: ed465ebfdadc3b0f1e1f1f3f2f329be4e25511b2
3
+ metadata.gz: 75b0679707788dc074b4a0393b12f40e04129508
4
+ data.tar.gz: a94d06214e2975de3fe5db4e34a924f83f083a26
5
5
  SHA512:
6
- metadata.gz: 1cc4bce728dffab8e2fefae5205816c35a3bddbb517afd4f549cce4ad2071a8e0fea89eeac90b8b4aa40c5ccf6068b2901f74d85077f56b08c2fa12510b72900
7
- data.tar.gz: 0c1502b18beaed4ce8bb99b02f16ac9b0f93e8b1a04e34eddd17871d627b5e307e6d8ebefbb4ba45ce116e2022b21f1d3851455152ee479720d62e8eaf124226
6
+ metadata.gz: 44c06deccb3733c5e29bfbe294a0f5a31bf1005c3e1239397142c410224e06990a505d40a3d06c58686664b75e6aa2b0dfabf83b97fce7706d4afa3aa775e607
7
+ data.tar.gz: fd568be4b00eafbf89696d4a1468cd0996e5b0c9ecef42ed841982e67ee87bdbe3b3ff1265ebdecd2a402eb0abd4546ebf8ec2a92d5231374ae9dc29e51115f4
data/Rakefile CHANGED
@@ -19,7 +19,7 @@ end
19
19
  begin
20
20
  require 'rubocop/rake_task'
21
21
  RuboCop::RakeTask.new
22
- rescue StandardError => _
22
+ rescue StandardError => _e
23
23
  puts 'Rubocop not loaded.'
24
24
  end
25
25
 
@@ -31,10 +31,52 @@ module Api
31
31
  show_inventory :hostgroup_ids, :hostgroup_id
32
32
  end
33
33
 
34
+ api :POST, '/ansible_inventories/schedule',
35
+ N_('Schedule generating of Ansible Inventory report')
36
+ param :input_values, Hash, N_('Hash of input values of type input=>value')
37
+ param :report_format, ReportTemplateFormat.selectable.map(&:id),
38
+ N_("Report format, defaults to '%s'") % 'json'
39
+ example <<-EXAMPLE
40
+ POST /ansible/api/ansible_inventories/schedule
41
+ {
42
+ "input_values": {
43
+ "Organization": "yes",
44
+ "Location": "yes",
45
+ "IPv4": "yes",
46
+ "Facts": "no"
47
+ }
48
+ }
49
+ 200
50
+ {
51
+ "job_id": UNIQUE-REPORT-GENERATING-JOB-UUID
52
+ "data_url": "/api/v2/report_templates/1/report_data/UNIQUE-REPORT-GENERATING-JOB-UUID"
53
+ }
54
+ EXAMPLE
55
+
56
+ def schedule
57
+ @composer = ReportComposer.from_api_params(schedule_params)
58
+ if @composer.valid?
59
+ job = @composer.schedule_rendering
60
+ response = { :job_id => job.provider_job_id }
61
+ response[:data_url] = report_data_api_report_template_path(
62
+ @report_template, :job_id => job.provider_job_id
63
+ )
64
+ render :json => response
65
+ else
66
+ @ansible_inventory = @composer
67
+ process_resource_error(:resource => @ansible_inventory)
68
+ end
69
+ rescue StandardError => e
70
+ render_error 'standard_error', :status => :internal_error,
71
+ :locals => { :exception => e }
72
+ end
73
+
34
74
  def action_permission
35
75
  case params[:action]
36
76
  when 'hosts', 'hostgroups'
37
77
  :view
78
+ when 'schedule'
79
+ :generate
38
80
  else
39
81
  super
40
82
  end
@@ -42,6 +84,14 @@ module Api
42
84
 
43
85
  private
44
86
 
87
+ def schedule_params
88
+ template_name = Setting::Ansible.find_by(:name => 'ansible_inventory_template').value
89
+ @report_template = ReportTemplate.find_by!(:name => template_name)
90
+ params[:id] = @report_template.id
91
+ params[:report_format] = 'json' if params[:report_format].blank?
92
+ params
93
+ end
94
+
45
95
  def show_inventory(ids_key, condition_key)
46
96
  ids = params.fetch(ids_key, []).uniq
47
97
  render :json => ForemanAnsible::InventoryCreator.new(Host.where(condition_key => ids)).to_hash.to_json
@@ -4,6 +4,7 @@
4
4
  class AnsibleVariable < LookupKey
5
5
  belongs_to :ansible_role, :inverse_of => :ansible_variables
6
6
  validates :ansible_role_id, :presence => true
7
+ before_validation :cast_default_value, :if => :override?
7
8
  validates :key, :uniqueness => { :scope => :ansible_role_id }
8
9
  scoped_search :on => :key, :aliases => [:name], :complete_value => true
9
10
  scoped_search :on => :imported, :complete_value => { :true => true, :false => false }
@@ -23,6 +23,7 @@ if defined? ForemanRemoteExecution
23
23
  'ansible_inventory' => ::ForemanAnsible::InventoryCreator.new(
24
24
  [host], template_invocation
25
25
  ).to_hash.to_json,
26
+ :verbosity_level => Setting[:ansible_verbosity],
26
27
  :remote_execution_command => ansible_command?(
27
28
  template_invocation.template
28
29
  ),
@@ -30,12 +31,35 @@ if defined? ForemanRemoteExecution
30
31
  )
31
32
  end
32
33
 
34
+ def secrets(host)
35
+ {
36
+ 'per-host' => {
37
+ host.name => {
38
+ 'ansible_ssh_pass' => rex_ssh_password(host),
39
+ 'ansible_sudo_pass' => rex_sudo_password(host)
40
+ }
41
+ }
42
+ }
43
+ end
44
+
45
+ def rex_ssh_password(host)
46
+ host_setting(host, 'remote_execution_ssh_password')
47
+ end
48
+
49
+ def rex_sudo_password(host)
50
+ host_setting(host, 'remote_execution_sudo_password')
51
+ end
52
+
53
+ def host_setting(host, setting)
54
+ host.params[setting.to_s] || Setting[setting]
55
+ end
56
+
33
57
  def supports_effective_user?
34
58
  true
35
59
  end
36
60
 
37
61
  def proxy_operation_name
38
- Setting::Ansible[:ansible_implementation]
62
+ 'ansible-runner'
39
63
  end
40
64
 
41
65
  private
@@ -6,7 +6,11 @@ module ForemanAnsible
6
6
  # in the fact values table (/fact_values)
7
7
  class FactName < ::FactName
8
8
  def origin
9
- 'foreman_ansible/Ansible'
9
+ 'Ansible'
10
+ end
11
+
12
+ def icon_path
13
+ 'foreman_ansible/Ansible.png'
10
14
  end
11
15
  end
12
16
  end
@@ -5,101 +5,94 @@ class Setting
5
5
  class Ansible < ::Setting
6
6
  class << self
7
7
  # It would be more disadvantages than advantages to split up
8
- # load_defaults into multiple methods, this way it's already very
8
+ # default_settings into multiple methods, this way it's already very
9
9
  # manageable.
10
10
  # rubocop:disable AbcSize
11
11
  # rubocop:disable MethodLength
12
- # rubocop:disable BlockLength
13
- def load_defaults
14
- return unless super
15
- Setting::BLANK_ATTRS.push('ansible_ssh_private_key_file')
16
- transaction do
17
- [
18
- set(
19
- 'ansible_ssh_private_key_file',
20
- N_('Use this to supply a path to an SSH Private Key '\
21
- 'that Ansible will use in lieu of a password '\
22
- 'Override with "ansible_ssh_private_key_file" '\
23
- 'host parameter'),
24
- '',
25
- N_('Private Key Path')
26
- ),
27
- set(
28
- 'ansible_connection',
29
- N_('Use this connection type by default when running '\
30
- 'Ansible playbooks. You can override this on hosts by '\
31
- 'adding a parameter "ansible_connection"'),
32
- 'ssh',
33
- N_('Connection type')
34
- ),
35
- set(
36
- 'ansible_winrm_server_cert_validation',
37
- N_('Enable/disable WinRM server certificate '\
38
- 'validation when running Ansible playbooks. You can override '\
39
- 'this on hosts by adding a parameter '\
40
- '"ansible_winrm_server_cert_validation"'),
41
- 'validate',
42
- N_('WinRM cert Validation')
43
- ),
44
- set(
45
- 'ansible_verbosity',
46
- N_('Foreman will add this level of verbosity for '\
47
- 'additional debugging output when running Ansible playbooks.'),
48
- '0',
49
- N_('Default verbosity level'),
50
- nil,
51
- :collection => lambda do
52
- { '0' => N_('Disabled'),
53
- '1' => N_('Level 1 (-v)'),
54
- '2' => N_('Level 2 (-vv)'),
55
- '3' => N_('Level 3 (-vvv)'),
56
- '4' => N_('Level 4 (-vvvv)') }
57
- end
58
- # rubocop:enable BlockLength
59
- ),
60
- set(
61
- 'ansible_post_provision_timeout',
62
- N_('Timeout (in seconds) to set when Foreman will trigger a '\
63
- 'play Ansible roles task after a host is fully provisioned. '\
64
- 'Set this to the maximum time you expect a host to take '\
65
- 'until it is ready after a reboot.'),
66
- '360',
67
- N_('Post-provision timeout')
68
- ),
69
- set(
70
- 'ansible_interval',
71
- N_('Timeout (in minutes) when hosts should have reported.'),
72
- '30',
73
- N_('Ansible report timeout')
74
- ),
75
- set(
76
- 'ansible_out_of_sync_disabled',
77
- format(N_('Disable host configuration status turning to out of'\
78
- ' sync for %{cfgmgmt} after report does not arrive within'\
79
- ' configured interval'), :cfgmgmt => 'Ansible'),
80
- false,
81
- format(N_('%{cfgmgmt} out of sync disabled'),
82
- :cfgmgmt => 'Ansible')
83
- ),
84
- set(
85
- 'ansible_implementation',
86
- N_('Foreman will run Ansible playbooks using this implementation'),
87
- 'ansible-playbook',
88
- N_('Implementation for running Ansible'),
89
- nil,
90
- :collection => lambda do
91
- Hash[%w[ansible-playbook ansible-runner].map { |x| [x, x] }]
92
- end
93
- )
94
- ].compact.each do |s|
95
- create(s.update(:category => 'Setting::Ansible'))
96
- end
97
- end
98
- true
12
+ def default_settings
13
+ [
14
+ set(
15
+ 'ansible_ssh_private_key_file',
16
+ N_('Use this to supply a path to an SSH Private Key '\
17
+ 'that Ansible will use in lieu of a password '\
18
+ 'Override with "ansible_ssh_private_key_file" '\
19
+ 'host parameter'),
20
+ '',
21
+ N_('Private Key Path')
22
+ ),
23
+ set(
24
+ 'ansible_connection',
25
+ N_('Use this connection type by default when running '\
26
+ 'Ansible playbooks. You can override this on hosts by '\
27
+ 'adding a parameter "ansible_connection"'),
28
+ 'ssh',
29
+ N_('Connection type')
30
+ ),
31
+ set(
32
+ 'ansible_winrm_server_cert_validation',
33
+ N_('Enable/disable WinRM server certificate '\
34
+ 'validation when running Ansible playbooks. You can override '\
35
+ 'this on hosts by adding a parameter '\
36
+ '"ansible_winrm_server_cert_validation"'),
37
+ 'validate',
38
+ N_('WinRM cert Validation')
39
+ ),
40
+ set(
41
+ 'ansible_verbosity',
42
+ N_('Foreman will add this level of verbosity for '\
43
+ 'additional debugging output when running Ansible playbooks.'),
44
+ '0',
45
+ N_('Default verbosity level'),
46
+ nil,
47
+ :collection => lambda do
48
+ { '0' => N_('Disabled'),
49
+ '1' => N_('Level 1 (-v)'),
50
+ '2' => N_('Level 2 (-vv)'),
51
+ '3' => N_('Level 3 (-vvv)'),
52
+ '4' => N_('Level 4 (-vvvv)') }
53
+ end
54
+ ),
55
+ set(
56
+ 'ansible_post_provision_timeout',
57
+ N_('Timeout (in seconds) to set when Foreman will trigger a '\
58
+ 'play Ansible roles task after a host is fully provisioned. '\
59
+ 'Set this to the maximum time you expect a host to take '\
60
+ 'until it is ready after a reboot.'),
61
+ '360',
62
+ N_('Post-provision timeout')
63
+ ),
64
+ set(
65
+ 'ansible_interval',
66
+ N_('Timeout (in minutes) when hosts should have reported.'),
67
+ '30',
68
+ N_('Ansible report timeout')
69
+ ),
70
+ set(
71
+ 'ansible_out_of_sync_disabled',
72
+ format(N_('Disable host configuration status turning to out of'\
73
+ ' sync for %{cfgmgmt} after report does not arrive within'\
74
+ ' configured interval'), :cfgmgmt => 'Ansible'),
75
+ false,
76
+ format(N_('%{cfgmgmt} out of sync disabled'),
77
+ :cfgmgmt => 'Ansible')
78
+ ),
79
+ set(
80
+ 'ansible_inventory_template',
81
+ N_('Foreman will use this template to schedule the report '\
82
+ 'with Ansible inventory'),
83
+ 'Ansible Inventory',
84
+ N_('Default Ansible inventory report template')
85
+ )
86
+ ]
99
87
  end
100
88
  # rubocop:enable AbcSize
101
89
  # rubocop:enable MethodLength
102
90
 
91
+ def load_defaults
92
+ Setting::BLANK_ATTRS.push('ansible_ssh_private_key_file')
93
+ super
94
+ end
95
+
103
96
  def humanized_category
104
97
  N_('Ansible')
105
98
  end
@@ -55,16 +55,13 @@ module ForemanAnsible
55
55
  end
56
56
 
57
57
  def missing_facts
58
- db_fact_names = if db_facts.is_a? Hash
59
- db_facts.keys
60
- else
61
- db_facts
62
- end
63
- # In Foreman versions prior to 1.14, the db_facts key
64
- # used to be a hash. Now it's an ActiveRecord::AssociationRelation
65
58
  @missing_facts ||= facts.keys +
66
59
  FactSparser.sparse(@original_facts).keys -
67
- db_fact_names
60
+ host_fact_names
61
+ end
62
+
63
+ def host_fact_names
64
+ host.fact_names.where(:type => fact_name_class.to_s).pluck(:name)
68
65
  end
69
66
 
70
67
  # Returns pairs [id, fact_name]
@@ -51,19 +51,27 @@ module ForemanAnsible
51
51
  end
52
52
  end
53
53
 
54
- def get_facts_for_interface(interface)
55
- interface.tr!('-', '_') # virbr1-nic -> virbr1_nic
54
+ def get_facts_for_interface(iface_name)
55
+ interface = iface_name.tr('-', '_') # virbr1-nic -> virbr1_nic
56
56
  interface_facts = facts[:"ansible_#{interface}"]
57
57
  ipaddress = ip_from_interface(interface)
58
58
  ipaddress6 = ipv6_from_interface(interface)
59
- HashWithIndifferentAccess[
59
+ macaddress = mac_from_interface(interface)
60
+ iface_facts = HashWithIndifferentAccess[
60
61
  interface_facts.merge(:ipaddress => ipaddress,
61
- :ipaddress6 => ipaddress6)
62
+ :ipaddress6 => ipaddress6,
63
+ :macaddress => macaddress)
62
64
  ]
65
+ logger.debug { "Ansible interface #{interface} facts: #{iface_facts.inspect}" }
66
+ iface_facts
63
67
  end
64
68
 
65
69
  def ipmi_interface; end
66
70
 
71
+ def boot_timestamp
72
+ Time.zone.now.to_i - facts['ansible_uptime_seconds'].to_i
73
+ end
74
+
67
75
  private
68
76
 
69
77
  def ansible_interfaces
@@ -71,6 +79,10 @@ module ForemanAnsible
71
79
  facts[:ansible_interfaces].sort
72
80
  end
73
81
 
82
+ def mac_from_interface(interface)
83
+ facts[:"ansible_#{interface}"]['perm_macaddress'].presence || facts[:"ansible_#{interface}"]['macaddress']
84
+ end
85
+
74
86
  def ip_from_interface(interface)
75
87
  return if facts[:"ansible_#{interface}"]['ipv4'].blank?
76
88
  facts[:"ansible_#{interface}"]['ipv4']['address']
@@ -98,8 +98,7 @@ module ForemanAnsible
98
98
  def remote_execution_options(host)
99
99
  params = {
100
100
  'ansible_user' => host_setting(host, 'remote_execution_ssh_user'),
101
- 'ansible_ssh_pass' => rex_ssh_password(host),
102
- 'ansible_sudo_pass' => rex_sudo_password(host),
101
+ 'ansible_become_method' => host_setting(host, 'remote_execution_effective_user_method'),
103
102
  'ansible_ssh_private_key_file' => ansible_or_rex_ssh_private_key(host),
104
103
  'ansible_port' => host_setting(host, 'remote_execution_ssh_port'),
105
104
  'ansible_host' => AnsibleProvider.find_ip_or_hostname(host)
@@ -123,16 +122,6 @@ module ForemanAnsible
123
122
  result
124
123
  end
125
124
 
126
- def rex_ssh_password(host)
127
- @template_invocation.job_invocation.password ||
128
- host_setting(host, 'remote_execution_ssh_password')
129
- end
130
-
131
- def rex_sudo_password(host)
132
- @template_invocation.job_invocation.sudo_password ||
133
- host_setting(host, 'remote_execution_sudo_password')
134
- end
135
-
136
125
  def ansible_or_rex_ssh_private_key(host)
137
126
  ansible_private_file = host_setting(host, 'ansible_ssh_private_key_file')
138
127
  if !ansible_private_file.empty?
@@ -0,0 +1,17 @@
1
+ <%#
2
+ name: Ansible - Run playbook
3
+ snippet: false
4
+ job_category: Ansible Playbook
5
+ provider_type: Ansible
6
+ kind: job_template
7
+ feature: ansible_run_playbook
8
+ template_inputs:
9
+ - name: playbook
10
+ required: true
11
+ input_type: user
12
+ description: The playbook to run against given hosts
13
+ advanced: false
14
+ value_type: plain
15
+ %>
16
+
17
+ <%= input('playbook') %>