foreman_ansible 3.0.9 → 4.0.0

Sign up to get free protection for your applications and to get access to all the features.
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') %>