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.
- checksums.yaml +4 -4
- data/Rakefile +1 -1
- data/app/controllers/api/v2/ansible_inventories_controller.rb +50 -0
- data/app/models/ansible_variable.rb +1 -0
- data/app/models/foreman_ansible/ansible_provider.rb +25 -1
- data/app/models/foreman_ansible/fact_name.rb +5 -1
- data/app/models/setting/ansible.rb +81 -88
- data/app/services/foreman_ansible/fact_importer.rb +5 -8
- data/app/services/foreman_ansible/fact_parser.rb +16 -4
- data/app/services/foreman_ansible/inventory_creator.rb +1 -12
- data/app/views/foreman_ansible/job_templates/run_playbook-ansible_default.erb +17 -0
- data/config/routes.rb +1 -2
- data/db/migrate/20190328114657_remove_top_level_ansible_variables_setting.rb +1 -1
- data/db/migrate/20191010074208_remove_ansible_implementation_setting.rb +5 -0
- data/db/seeds.d/75_job_templates.rb +2 -2
- data/lib/foreman_ansible/register.rb +5 -2
- data/lib/foreman_ansible/remote_execution.rb +6 -0
- data/lib/foreman_ansible/version.rb +1 -1
- data/locale/Makefile +7 -0
- data/locale/de/LC_MESSAGES/foreman_ansible.mo +0 -0
- data/locale/de/foreman_ansible.po +1 -1
- data/locale/en/LC_MESSAGES/foreman_ansible.mo +0 -0
- data/locale/en/foreman_ansible.po +1 -1
- data/locale/es/LC_MESSAGES/foreman_ansible.mo +0 -0
- data/locale/es/foreman_ansible.po +1 -1
- data/locale/fr/LC_MESSAGES/foreman_ansible.mo +0 -0
- data/locale/fr/foreman_ansible.po +1 -1
- data/locale/it/LC_MESSAGES/foreman_ansible.mo +0 -0
- data/locale/it/foreman_ansible.po +1 -1
- data/locale/ja/LC_MESSAGES/foreman_ansible.mo +0 -0
- data/locale/ja/foreman_ansible.po +1 -1
- data/locale/ko/LC_MESSAGES/foreman_ansible.mo +0 -0
- data/locale/ko/foreman_ansible.po +1 -1
- data/locale/pt_BR/LC_MESSAGES/foreman_ansible.mo +0 -0
- data/locale/pt_BR/foreman_ansible.po +1 -1
- data/locale/ru/LC_MESSAGES/foreman_ansible.mo +0 -0
- data/locale/ru/foreman_ansible.po +1 -1
- data/locale/zh_CN/LC_MESSAGES/foreman_ansible.mo +0 -0
- data/locale/zh_CN/foreman_ansible.po +1 -1
- data/locale/zh_TW/LC_MESSAGES/foreman_ansible.mo +0 -0
- data/locale/zh_TW/foreman_ansible.po +1 -1
- data/package.json +3 -3
- data/test/unit/ansible_provider_test.rb +16 -0
- data/test/unit/ansible_variable_test.rb +10 -0
- data/test/unit/lib/foreman_ansible_core/ansible_runner_test.rb +51 -0
- data/test/unit/lib/foreman_ansible_core/playbook_runner_test.rb +34 -1
- data/test/unit/services/fact_importer_test.rb +4 -2
- data/test/unit/services/fact_parser_test.rb +57 -4
- data/test/unit/services/inventory_creator_test.rb +10 -25
- data/webpack/__mocks__/foremanReact/common/I18n.js +1 -0
- data/webpack/components/AnsibleRolesSwitcher/AnsibleRolesSwitcher.js +1 -0
- data/webpack/components/AnsibleRolesSwitcher/components/AnsiblePermissionDenied.js +1 -0
- data/webpack/components/AnsibleRolesSwitcher/components/AnsibleRole.js +1 -0
- data/webpack/components/AnsibleRolesSwitcher/components/AssignedRolesList.js +18 -23
- data/webpack/index.js +5 -4
- metadata +15 -11
- 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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 75b0679707788dc074b4a0393b12f40e04129508
|
4
|
+
data.tar.gz: a94d06214e2975de3fe5db4e34a924f83f083a26
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 44c06deccb3733c5e29bfbe294a0f5a31bf1005c3e1239397142c410224e06990a505d40a3d06c58686664b75e6aa2b0dfabf83b97fce7706d4afa3aa775e607
|
7
|
+
data.tar.gz: fd568be4b00eafbf89696d4a1468cd0996e5b0c9ecef42ed841982e67ee87bdbe3b3ff1265ebdecd2a402eb0abd4546ebf8ec2a92d5231374ae9dc29e51115f4
|
data/Rakefile
CHANGED
@@ -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
|
-
|
62
|
+
'ansible-runner'
|
39
63
|
end
|
40
64
|
|
41
65
|
private
|
@@ -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
|
-
#
|
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
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
'0',
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
)
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
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
|
-
|
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(
|
55
|
-
interface.tr
|
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
|
-
|
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
|
-
'
|
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') %>
|