foreman_ansible 10.4.1 → 11.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.
- checksums.yaml +4 -4
- data/app/controllers/concerns/foreman/controller/parameters/job_template_extensions.rb +17 -0
- data/app/controllers/foreman_ansible/api/v2/job_templates_controller_extensions.rb +18 -0
- data/app/graphql/types/overriden_ansible_variable.rb +2 -4
- data/app/helpers/foreman_ansible/ansible_hostgroups_helper.rb +1 -7
- data/app/helpers/foreman_ansible/ansible_reports_helper.rb +20 -27
- data/app/models/foreman_ansible/ansible_provider.rb +1 -1
- data/app/views/api/v2/job_templates/job_templates.json.rabl +3 -0
- data/app/views/foreman_ansible/config_reports/_ansible.html.erb +6 -2
- data/app/views/job_templates/_job_template_callback_tab_content.html.erb +3 -0
- data/app/views/job_templates/_job_template_callback_tab_headers.html.erb +13 -0
- data/db/migrate/20210818083407_fix_ansible_setting_category_to_dsl.rb +1 -1
- data/db/migrate/20221003153000_add_ansible_callback_enabled_to_templates.rb +10 -0
- data/lib/foreman_ansible/engine.rb +3 -0
- data/lib/foreman_ansible/register.rb +15 -1
- data/lib/foreman_ansible/version.rb +1 -1
- data/package.json +1 -1
- data/test/fixtures/report.json +2 -106
- data/test/unit/ansible_provider_test.rb +3 -4
- data/test/unit/concerns/config_reports_extensions_test.rb +2 -4
- data/test/unit/helpers/ansible_reports_helper_test.rb +4 -50
- data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/AnsibleVariableOverridesTableHelper.js +2 -6
- data/webpack/graphql/queries/currentUserAttributes.gql +0 -5
- data/webpack/permissionsHelper.js +1 -4
- data/webpack/testHelper.js +0 -6
- metadata +14 -8
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 348701b380a514ccae60dda27305a137438dd4cd2812f9515e071a527a0b20c5
|
4
|
+
data.tar.gz: ab770d603437ed890a945381d28411e0caff547f6bef26b22c98191fe6317b98
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0a780ccd49ae7534e43de5ea1f50aab631b3f59c30c5793455e242568b77d2572fe187f7f0c75667cb1c22ccfd03666dd2ce7df2541cb974df1693ff58079250
|
7
|
+
data.tar.gz: ab9bdea79a470967e8c6592d7166317668a5d9f892887b8d715fc1daaa9a8ce99959cb5c4da2496dabf4b9e4d2ada5909c2e78bfa250bcd13d2250d7d32a1473
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Foreman
|
2
|
+
module Controller
|
3
|
+
module Parameters
|
4
|
+
module JobTemplateExtensions
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
class_methods do
|
8
|
+
def job_template_params_filter
|
9
|
+
super.tap do |filter|
|
10
|
+
filter.permit :ansible_callback_enabled
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module ForemanAnsible
|
4
|
+
module Api
|
5
|
+
module V2
|
6
|
+
# Extends the job_templates api controller to support creating/updating with ansible callback plugin
|
7
|
+
module JobTemplatesControllerExtensions
|
8
|
+
extend Apipie::DSL::Concern
|
9
|
+
|
10
|
+
update_api(:create, :update) do
|
11
|
+
param :job_template, Hash do
|
12
|
+
param :ansible_callback_enabled, :bool, :desc => N_('Enable the callback plugin for this template')
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
@@ -8,14 +8,12 @@ module Types
|
|
8
8
|
argument :match, String, required: false
|
9
9
|
end
|
10
10
|
|
11
|
-
field :meta, ::Types::Meta
|
12
|
-
|
13
|
-
def meta
|
11
|
+
field :meta, ::Types::Meta, resolve: (proc do |object|
|
14
12
|
{
|
15
13
|
:can_edit => ::User.current.can?(object.ansible_variable.permission_name(:edit), object.ansible_variable),
|
16
14
|
:can_destroy => ::User.current.can?(object.ansible_variable.permission_name(:destroy), object.ansible_variable)
|
17
15
|
}
|
18
|
-
end
|
16
|
+
end)
|
19
17
|
|
20
18
|
def lookup_values(match: nil)
|
21
19
|
return CollectionLoader.for(object.ansible_variable.class, :lookup_values).load(object.ansible_variable) unless match
|
@@ -3,19 +3,13 @@
|
|
3
3
|
module ForemanAnsible
|
4
4
|
module AnsibleHostgroupsHelper
|
5
5
|
def ansible_hostgroups_actions(hostgroup)
|
6
|
-
actions = []
|
7
6
|
play_roles = if hostgroup.all_ansible_roles.empty?
|
8
7
|
{ action: { content: (link_to _('Run all Ansible roles'), 'javascript:void(0);', disabled: true, title: 'No roles assigned', class: 'disabled'), options: { class: 'disabled' } }, priority: 31 }
|
9
8
|
else
|
10
9
|
{ action: display_link_if_authorized(_('Run all Ansible roles'), hash_for_play_roles_hostgroup_path(id: hostgroup), 'data-no-turbolink': true, title: _('Run all Ansible roles on hosts belonging to this host group')), priority: 31 }
|
11
10
|
end
|
12
11
|
|
13
|
-
|
14
|
-
|
15
|
-
actions.push play_roles if User.current.can?(:create_job_invocations)
|
16
|
-
actions.push assign_jobs if User.current.can?(:view_job_invocations) && User.current.can?(:view_recurring_logics)
|
17
|
-
|
18
|
-
actions
|
12
|
+
[play_roles] if User.current.can?(:create_job_invocations)
|
19
13
|
end
|
20
14
|
end
|
21
15
|
end
|
@@ -28,36 +28,26 @@ module ForemanAnsible
|
|
28
28
|
def ansible_module_message(log)
|
29
29
|
msg_json = parsed_message_json(log)
|
30
30
|
return _("Execution error: #{msg_json['msg']}") if msg_json['failed'].present?
|
31
|
-
return msg_json['censored'] if msg_json['censored'].present?
|
32
31
|
|
33
|
-
module_action = msg_json
|
32
|
+
module_action = msg_json['module']
|
34
33
|
case module_action
|
35
34
|
when 'package'
|
36
35
|
msg_json['results'].empty? ? msg_json['msg'] : msg_json['results']
|
37
36
|
when 'template'
|
38
|
-
|
39
|
-
|
40
|
-
end
|
37
|
+
module_args = msg_json['invocation']['module_args']
|
38
|
+
_("Rendered template #{module_args['_original_basename']} to #{msg_json['dest']}")
|
41
39
|
when 'service'
|
42
|
-
|
43
|
-
_("Service #{result['name']} #{result['state']} (enabled: #{result['enabled']})")
|
44
|
-
end
|
40
|
+
_("Service #{msg_json['name']} #{msg_json['state']} (enabled: #{msg_json['enabled']})")
|
45
41
|
when 'group'
|
46
|
-
|
47
|
-
_("User group #{result['name']} #{result['state']}, gid: #{result['gid']}")
|
48
|
-
end
|
42
|
+
_("User group #{msg_json['name']} #{msg_json['state']}, gid: #{msg_json['gid']}")
|
49
43
|
when 'user'
|
50
|
-
|
51
|
-
_("User #{result['name']} #{result['state']}, uid: #{result['uid']}")
|
52
|
-
end
|
44
|
+
_("User #{msg_json['name']} #{msg_json['state']}, uid: #{msg_json['uid']}")
|
53
45
|
when 'cron'
|
54
|
-
|
55
|
-
|
56
|
-
end
|
46
|
+
module_args = msg_json['invocation']['module_args']
|
47
|
+
_("Cron job: #{module_args['minute']} #{module_args['hour']} #{module_args['day']} #{module_args['month']} #{module_args['weekday']} #{module_args['job']} (disabled: #{module_args['disabled']})")
|
57
48
|
when 'copy'
|
58
|
-
|
59
|
-
|
60
|
-
end
|
49
|
+
module_args = msg_json['invocation']['module_args']
|
50
|
+
_("Copy #{module_args['_original_basename']} to #{msg_json['dest']}")
|
61
51
|
when 'command', 'shell'
|
62
52
|
msg_json['stdout_lines']
|
63
53
|
else
|
@@ -79,16 +69,19 @@ module ForemanAnsible
|
|
79
69
|
false
|
80
70
|
end
|
81
71
|
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
72
|
+
def show_full_error_message_value(message_value)
|
73
|
+
tag.div class: 'replace-hidden-value' do
|
74
|
+
link_to_function(icon_text('plus', '', class: 'small'), 'replace_value_control(this, "div")',
|
75
|
+
title: _('Show full value'),
|
76
|
+
class: 'replace-hidden-value pull-right') +
|
77
|
+
(tag.span class: 'full-value' do
|
78
|
+
message_value
|
79
|
+
end)
|
89
80
|
end
|
90
81
|
end
|
91
82
|
|
83
|
+
private
|
84
|
+
|
92
85
|
def parsed_message_json(log)
|
93
86
|
JSON.parse(log.message.value)
|
94
87
|
rescue StandardError => e
|
@@ -123,7 +123,7 @@ if defined? ForemanRemoteExecution
|
|
123
123
|
|
124
124
|
def ansible_command?(template)
|
125
125
|
template.remote_execution_features.
|
126
|
-
where(:label => 'ansible_run_host').empty?
|
126
|
+
where(:label => 'ansible_run_host').empty? && !template.ansible_callback_enabled
|
127
127
|
end
|
128
128
|
end
|
129
129
|
end
|
@@ -29,8 +29,12 @@
|
|
29
29
|
<% end %>
|
30
30
|
</ul>
|
31
31
|
<% else %>
|
32
|
-
|
33
|
-
|
32
|
+
<% allowed_length = 150 %>
|
33
|
+
<div class='pull-left'>
|
34
|
+
<%= truncate(log_message, length: allowed_length) %>
|
35
|
+
</div>
|
36
|
+
<%= show_full_error_message_value(log_message) if log_message.length > allowed_length %>
|
37
|
+
<% end %>
|
34
38
|
</td>
|
35
39
|
</tr>
|
36
40
|
<% end %>
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<li><a id="ansible_tab_header" href="#ansible_callback_enabled" data-toggle="tab"><%= _("Ansible") %></a></li>
|
2
|
+
<script type="text/javascript">
|
3
|
+
$(document).ready(function () {
|
4
|
+
var provider_type = $('#job_template_provider_type');
|
5
|
+
provider_type.change(setAnsibleTabVisibilityByProvider);
|
6
|
+
provider_type.change();
|
7
|
+
|
8
|
+
function setAnsibleTabVisibilityByProvider() {
|
9
|
+
var tab_header = $("#ansible_tab_header");
|
10
|
+
this.value === 'Ansible' ? tab_header.show() : tab_header.hide();
|
11
|
+
}
|
12
|
+
});
|
13
|
+
</script>
|
@@ -1,5 +1,5 @@
|
|
1
1
|
class FixAnsibleSettingCategoryToDsl < ActiveRecord::Migration[6.0]
|
2
2
|
def up
|
3
|
-
Setting.where(category: 'Setting::Ansible').update_all(category: 'Setting')
|
3
|
+
Setting.where(category: 'Setting::Ansible').update_all(category: 'Setting')
|
4
4
|
end
|
5
5
|
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
class AddAnsibleCallbackEnabledToTemplates < ActiveRecord::Migration[6.0]
|
4
|
+
def change
|
5
|
+
add_column :templates, :ansible_callback_enabled, :boolean, default: false
|
6
|
+
RemoteExecutionFeature.where(label: 'ansible_run_host').each do |rex_feature|
|
7
|
+
Template.find(rex_feature.job_template_id).update(ansible_callback_enabled: true)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
@@ -74,6 +74,9 @@ module ForemanAnsible
|
|
74
74
|
::Api::V2::HostgroupsController.include ForemanAnsible::Api::V2::HostgroupsControllerExtensions
|
75
75
|
::Api::V2::HostgroupsController.include ForemanAnsible::Api::V2::HostgroupsParamGroupExtensions
|
76
76
|
::ConfigReportImporter.include ForemanAnsible::AnsibleReportImporter
|
77
|
+
::Api::V2::JobTemplatesController.include ForemanAnsible::Api::V2::JobTemplatesControllerExtensions
|
78
|
+
::Api::V2::JobTemplatesController.include Foreman::Controller::Parameters::JobTemplateExtensions
|
79
|
+
::JobTemplatesController.include Foreman::Controller::Parameters::JobTemplateExtensions
|
77
80
|
ReportImporter.register_smart_proxy_feature('Ansible')
|
78
81
|
rescue StandardError => e
|
79
82
|
Rails.logger.warn "Foreman Ansible: skipping engine hook (#{e})"
|
@@ -1,7 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
Foreman::Plugin.register :foreman_ansible do
|
4
|
-
requires_foreman '>= 3.
|
4
|
+
requires_foreman '>= 3.6'
|
5
5
|
|
6
6
|
settings do
|
7
7
|
category :ansible, N_('Ansible') do
|
@@ -237,6 +237,8 @@ Foreman::Plugin.register :foreman_ansible do
|
|
237
237
|
register_report_origin 'Ansible', 'ConfigReport'
|
238
238
|
end
|
239
239
|
|
240
|
+
extend_rabl_template 'api/v2/job_templates/show', 'api/v2/job_templates/job_templates'
|
241
|
+
|
240
242
|
describe_host do
|
241
243
|
multiple_actions_provider :ansible_hosts_multiple_actions
|
242
244
|
end
|
@@ -257,4 +259,16 @@ Foreman::Plugin.register :foreman_ansible do
|
|
257
259
|
:partial => 'foreman/smart_proxies/update_smart_proxy',
|
258
260
|
:onlyif => ->(proxy, view) { view.can_update_proxy?(proxy) }
|
259
261
|
end
|
262
|
+
extend_page('templates/_form') do |context|
|
263
|
+
context.add_pagelet :tab_headers,
|
264
|
+
:name => _('Ansible'),
|
265
|
+
:partial => 'job_templates/job_template_callback_tab_headers',
|
266
|
+
:onlyif => ->(subject, _view) { subject.is_a? JobTemplate }
|
267
|
+
end
|
268
|
+
extend_page('templates/_form') do |context|
|
269
|
+
context.add_pagelet :tab_content,
|
270
|
+
:name => _('Ansible'),
|
271
|
+
:partial => 'job_templates/job_template_callback_tab_content',
|
272
|
+
:onlyif => ->(subject, _view) { subject.is_a? JobTemplate }
|
273
|
+
end
|
260
274
|
end
|
data/package.json
CHANGED
data/test/fixtures/report.json
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
|
1
|
+
{
|
2
2
|
"reporter": "ansible",
|
3
3
|
"reported_at":"2018-01-15 17:31:36 521275",
|
4
4
|
"metrics": {
|
@@ -24,108 +24,4 @@
|
|
24
24
|
}
|
25
25
|
}
|
26
26
|
]
|
27
|
-
}
|
28
|
-
{
|
29
|
-
"reporter": "ansible",
|
30
|
-
"reported_at":"2022-12-22 10:52:48 521275",
|
31
|
-
"metrics": {
|
32
|
-
"time":
|
33
|
-
{ "total":133 }
|
34
|
-
},
|
35
|
-
"host": "io.local",
|
36
|
-
"status": {
|
37
|
-
"applied": 8,
|
38
|
-
"failed": 0,
|
39
|
-
"skipped": 0
|
40
|
-
},
|
41
|
-
"logs": [
|
42
|
-
{
|
43
|
-
"log": {
|
44
|
-
"sources": {
|
45
|
-
"source": "Schedule multiple cronjobs"
|
46
|
-
},
|
47
|
-
"messages": {
|
48
|
-
"message": "{\"changed\": true, \"failed\": false, \"module\": \"cron\", \"msg\": \"All items completed\", \"results\": [{\"_ansible_item_label\": \"date\", \"_ansible_no_log\": false, \"ansible_loop_var\": \"item\", \"changed\": true, \"envs\": [], \"failed\": false, \"invocation\": {\"module_args\": {\"backup\": false, \"cron_file\": null, \"day\": \"*\", \"disabled\": false, \"env\": null, \"hour\": \"5,2\", \"insertafter\": null, \"insertbefore\": null, \"job\": \"date > /dev/null\", \"minute\": \"0\", \"month\": \"*\", \"name\": \"Cron date\", \"reboot\": false, \"special_time\": null, \"state\": \"present\", \"user\": null, \"weekday\": \"*\"}}, \"item\": \"date\", \"jobs\": [\"Cron date\"]}, {\"_ansible_item_label\": \"df\", \"_ansible_no_log\": false, \"ansible_loop_var\": \"item\", \"changed\": true, \"envs\": [], \"failed\": false, \"invocation\": {\"module_args\": {\"backup\": false, \"cron_file\": null, \"day\": \"*\", \"disabled\": false, \"env\": null, \"hour\": \"5,2\", \"insertafter\": null, \"insertbefore\": null, \"job\": \"df > /dev/null\", \"minute\": \"0\", \"month\": \"*\", \"name\": \"Cron df\", \"reboot\": false, \"special_time\": null, \"state\": \"present\", \"user\": null, \"weekday\": \"*\"}}, \"item\": \"df\", \"jobs\": [\"Cron date\", \"Cron df\"]}]}"
|
49
|
-
},
|
50
|
-
"level": "notice"
|
51
|
-
}
|
52
|
-
},
|
53
|
-
{
|
54
|
-
"log": {
|
55
|
-
"sources": {
|
56
|
-
"source": "Schedule one cronjob"
|
57
|
-
},
|
58
|
-
"messages": {
|
59
|
-
"message": "{\"_ansible_no_log\": false, \"changed\": true, \"envs\": [], \"failed\": false, \"invocation\": {\"module_args\": {\"backup\": false, \"cron_file\": null, \"day\": \"*\", \"disabled\": false, \"env\": null, \"hour\": \"5,2\", \"insertafter\": null, \"insertbefore\": null, \"job\": \"hostname > /dev/null\", \"minute\": \"0\", \"month\": \"*\", \"name\": \"Schedule hostname\", \"reboot\": false, \"special_time\": null, \"state\": \"present\", \"user\": null, \"weekday\": \"*\"}}, \"jobs\": [\"Cron date\", \"Cron df\", \"Schedule hostname\"], \"module\": \"cron\"}"
|
60
|
-
},
|
61
|
-
"level": "notice"
|
62
|
-
}
|
63
|
-
},
|
64
|
-
{
|
65
|
-
"log": {
|
66
|
-
"sources": {
|
67
|
-
"source": "Render multiple templates"
|
68
|
-
},
|
69
|
-
"messages": {
|
70
|
-
"message": "{\"changed\": true, \"failed\": false, \"module\": \"template\", \"msg\": \"All items completed\", \"results\": [{\"_ansible_item_label\": \"test1.txt\", \"_ansible_no_log\": false, \"ansible_loop_var\": \"item\", \"changed\": true, \"checksum\": \"dba7673010f19a94af4345453005933fd511bea9\", \"dest\": \"/tmp/test1.txt\", \"diff\": [], \"failed\": false, \"gid\": 0, \"group\": \"root\", \"invocation\": {\"module_args\": {\"_original_basename\": \"test1.txt.j2\", \"attributes\": null, \"backup\": false, \"checksum\": \"dba7673010f19a94af4345453005933fd511bea9\", \"content\": null, \"delimiter\": null, \"dest\": \"/tmp/test1.txt\", \"directory_mode\": null, \"follow\": false, \"force\": true, \"group\": null, \"local_follow\": null, \"mode\": null, \"owner\": null, \"regexp\": null, \"remote_src\": null, \"selevel\": null, \"serole\": null, \"setype\": null, \"seuser\": null, \"src\": \"/root/.ansible/tmp/ansible-tmp-1671670606.02-7241-175625077259447/source\", \"unsafe_writes\": false, \"validate\": null}}, \"item\": \"test1.txt\", \"md5sum\": null, \"mode\": \"0644\", \"owner\": \"root\", \"secontext\": \"unconfined_u:object_r:admin_home_t:s0\", \"size\": 6, \"src\": \"/root/.ansible/tmp/ansible-tmp-1671670606.02-7241-175625077259447/source\", \"state\": \"file\", \"uid\": 0}, {\"_ansible_item_label\": \"test2.txt\", \"_ansible_no_log\": false, \"ansible_loop_var\": \"item\", \"changed\": true, \"checksum\": \"9054fbe0b622c638224d50d20824d2ff6782e308\", \"dest\": \"/tmp/test2.txt\", \"diff\": [], \"failed\": false, \"gid\": 0, \"group\": \"root\", \"invocation\": {\"module_args\": {\"_original_basename\": \"test2.txt.j2\", \"attributes\": null, \"backup\": false, \"checksum\": \"9054fbe0b622c638224d50d20824d2ff6782e308\", \"content\": null, \"delimiter\": null, \"dest\": \"/tmp/test2.txt\", \"directory_mode\": null, \"follow\": false, \"force\": true, \"group\": null, \"local_follow\": null, \"mode\": null, \"owner\": null, \"regexp\": null, \"remote_src\": null, \"selevel\": null, \"serole\": null, \"setype\": null, \"seuser\": null, \"src\": \"/root/.ansible/tmp/ansible-tmp-1671670620.49-7241-225254470383476/source\", \"unsafe_writes\": false, \"validate\": null}}, \"item\": \"test2.txt\", \"md5sum\": null, \"mode\": \"0644\", \"owner\": \"root\", \"secontext\": \"unconfined_u:object_r:admin_home_t:s0\", \"size\": 6, \"src\": \"/root/.ansible/tmp/ansible-tmp-1671670620.49-7241-225254470383476/source\", \"state\": \"file\", \"uid\": 0}]}"
|
71
|
-
},
|
72
|
-
"level": "notice"
|
73
|
-
}
|
74
|
-
},
|
75
|
-
{
|
76
|
-
"log": {
|
77
|
-
"sources": {
|
78
|
-
"source": "Render one template"
|
79
|
-
},
|
80
|
-
"messages": {
|
81
|
-
"message": "{\"_ansible_no_log\": false, \"changed\": true, \"checksum\": \"41c5985fc771b6ecfe8feaa99f8fa9b77ac7d6ce\", \"dest\": \"/tmp/test3.txt\", \"diff\": [], \"failed\": false, \"gid\": 0, \"group\": \"root\", \"invocation\": {\"module_args\": {\"_original_basename\": \"test3.txt.j2\", \"attributes\": null, \"backup\": false, \"checksum\": \"41c5985fc771b6ecfe8feaa99f8fa9b77ac7d6ce\", \"content\": null, \"delimiter\": null, \"dest\": \"/tmp/test3.txt\", \"directory_mode\": null, \"follow\": false, \"force\": true, \"group\": null, \"local_follow\": null, \"mode\": null, \"owner\": null, \"regexp\": null, \"remote_src\": null, \"selevel\": null, \"serole\": null, \"setype\": null, \"seuser\": null, \"src\": \"/root/.ansible/tmp/ansible-tmp-1671670634.79-7306-243717749452063/source\", \"unsafe_writes\": false, \"validate\": null}}, \"md5sum\": null, \"mode\": \"0644\", \"module\": \"template\", \"owner\": \"root\", \"secontext\": \"unconfined_u:object_r:admin_home_t:s0\", \"size\": 6, \"src\": \"/root/.ansible/tmp/ansible-tmp-1671670634.79-7306-243717749452063/source\", \"state\": \"file\", \"uid\": 0}"
|
82
|
-
},
|
83
|
-
"level": "notice"
|
84
|
-
}
|
85
|
-
},
|
86
|
-
{
|
87
|
-
"log": {
|
88
|
-
"sources": {
|
89
|
-
"source": "Copy multiple local files"
|
90
|
-
},
|
91
|
-
"messages": {
|
92
|
-
"message": "{\"changed\": true, \"failed\": false, \"module\": \"copy\", \"msg\": \"All items completed\", \"results\": [{\"_ansible_item_label\": \"test4.txt\", \"_ansible_no_log\": false, \"ansible_loop_var\": \"item\", \"changed\": true, \"checksum\": \"ab2649b7e58f7e32b0c75be95d11e2979399d392\", \"dest\": \"/tmp/test4.txt\", \"diff\": [], \"failed\": false, \"gid\": 0, \"group\": \"root\", \"invocation\": {\"module_args\": {\"_original_basename\": \"test4.txt\", \"attributes\": null, \"backup\": false, \"checksum\": \"ab2649b7e58f7e32b0c75be95d11e2979399d392\", \"content\": null, \"delimiter\": null, \"dest\": \"/tmp/test4.txt\", \"directory_mode\": null, \"follow\": false, \"force\": true, \"group\": \"root\", \"local_follow\": null, \"mode\": 256, \"owner\": \"root\", \"regexp\": null, \"remote_src\": null, \"selevel\": null, \"serole\": null, \"setype\": null, \"seuser\": null, \"src\": \"/root/.ansible/tmp/ansible-tmp-1671670648.88-7343-91799014257533/source\", \"unsafe_writes\": false, \"validate\": null}}, \"item\": \"test4.txt\", \"md5sum\": null, \"mode\": \"0400\", \"owner\": \"root\", \"secontext\": \"unconfined_u:object_r:admin_home_t:s0\", \"size\": 6, \"src\": \"/root/.ansible/tmp/ansible-tmp-1671670648.88-7343-91799014257533/source\", \"state\": \"file\", \"uid\": 0}, {\"_ansible_item_label\": \"test5.txt\", \"_ansible_no_log\": false, \"ansible_loop_var\": \"item\", \"changed\": true, \"checksum\": \"4ea77484f3a1c7dde4c0cca2f5c40953388f19f5\", \"dest\": \"/tmp/test5.txt\", \"diff\": [], \"failed\": false, \"gid\": 0, \"group\": \"root\", \"invocation\": {\"module_args\": {\"_original_basename\": \"test5.txt\", \"attributes\": null, \"backup\": false, \"checksum\": \"4ea77484f3a1c7dde4c0cca2f5c40953388f19f5\", \"content\": null, \"delimiter\": null, \"dest\": \"/tmp/test5.txt\", \"directory_mode\": null, \"follow\": false, \"force\": true, \"group\": \"root\", \"local_follow\": null, \"mode\": 256, \"owner\": \"root\", \"regexp\": null, \"remote_src\": null, \"selevel\": null, \"serole\": null, \"setype\": null, \"seuser\": null, \"src\": \"/root/.ansible/tmp/ansible-tmp-1671670662.97-7343-50902792283881/source\", \"unsafe_writes\": false, \"validate\": null}}, \"item\": \"test5.txt\", \"md5sum\": null, \"mode\": \"0400\", \"owner\": \"root\", \"secontext\": \"unconfined_u:object_r:admin_home_t:s0\", \"size\": 6, \"src\": \"/root/.ansible/tmp/ansible-tmp-1671670662.97-7343-50902792283881/source\", \"state\": \"file\", \"uid\": 0}]}"
|
93
|
-
},
|
94
|
-
"level": "notice"
|
95
|
-
}
|
96
|
-
},
|
97
|
-
{
|
98
|
-
"log": {
|
99
|
-
"sources": {
|
100
|
-
"source": "Copy one local files"
|
101
|
-
},
|
102
|
-
"messages": {
|
103
|
-
"message": "{\"_ansible_no_log\": false, \"changed\": true, \"checksum\": \"ec4cddb45c3ce640bed61b3d8ab6c18e715dac78\", \"dest\": \"/tmp/test6.txt\", \"diff\": [], \"failed\": false, \"gid\": 0, \"group\": \"root\", \"invocation\": {\"module_args\": {\"_original_basename\": \"test6.txt\", \"attributes\": null, \"backup\": false, \"checksum\": \"ec4cddb45c3ce640bed61b3d8ab6c18e715dac78\", \"content\": null, \"delimiter\": null, \"dest\": \"/tmp/test6.txt\", \"directory_mode\": null, \"follow\": false, \"force\": true, \"group\": \"root\", \"local_follow\": null, \"mode\": 256, \"owner\": \"root\", \"regexp\": null, \"remote_src\": null, \"selevel\": null, \"serole\": null, \"setype\": null, \"seuser\": null, \"src\": \"/root/.ansible/tmp/ansible-tmp-1671670677.05-7408-75605497546833/source\", \"unsafe_writes\": false, \"validate\": null}}, \"md5sum\": null, \"mode\": \"0400\", \"module\": \"copy\", \"owner\": \"root\", \"secontext\": \"unconfined_u:object_r:admin_home_t:s0\", \"size\": 6, \"src\": \"/root/.ansible/tmp/ansible-tmp-1671670677.05-7408-75605497546833/source\", \"state\": \"file\", \"uid\": 0}"
|
104
|
-
},
|
105
|
-
"level": "notice"
|
106
|
-
}
|
107
|
-
},
|
108
|
-
{
|
109
|
-
"log": {
|
110
|
-
"sources": {
|
111
|
-
"source": "Restart multiple services"
|
112
|
-
},
|
113
|
-
"messages": {
|
114
|
-
"message": "{\"changed\":true,\"failed\":false,\"module\":\"service\",\"msg\":\"All items completed\",\"results\":[{\"_ansible_item_label\":\"chronyd\",\"_ansible_no_log\":false,\"ansible_loop_var\":\"item\",\"changed\":true,\"failed\":false,\"invocation\":{\"module_args\":{\"daemon_reexec\":false,\"daemon_reload\":false,\"enabled\":null,\"force\":null,\"masked\":null,\"name\":\"chronyd\",\"no_block\":false,\"scope\":null,\"state\":\"restarted\",\"user\":null}},\"item\":\"chronyd\",\"name\":\"chronyd\",\"state\":\"started\"},{\"_ansible_item_label\":\"firewalld\",\"_ansible_no_log\":false,\"ansible_loop_var\":\"item\",\"changed\":true,\"failed\":false,\"invocation\":{\"module_args\":{\"daemon_reexec\":false,\"daemon_reload\":false,\"enabled\":null,\"force\":null,\"masked\":null,\"name\":\"firewalld\",\"no_block\":false,\"scope\":null,\"state\":\"restarted\",\"user\":null}},\"item\":\"firewalld\",\"name\":\"firewalld\",\"state\":\"started\"}]}"
|
115
|
-
},
|
116
|
-
"level": "notice"
|
117
|
-
}
|
118
|
-
},
|
119
|
-
{
|
120
|
-
"log": {
|
121
|
-
"sources": {
|
122
|
-
"source": "Restart one service"
|
123
|
-
},
|
124
|
-
"messages": {
|
125
|
-
"message": "{\"_ansible_no_log\":false,\"changed\":true,\"failed\":false,\"invocation\":{\"module_args\":{\"daemon_reexec\":false,\"daemon_reload\":false,\"enabled\":null,\"force\":null,\"masked\":null,\"name\":\"chronyd\",\"no_block\":false,\"scope\":null,\"state\":\"restarted\",\"user\":null}},\"module\":\"service\",\"name\":\"chronyd\",\"state\":\"started\"}"
|
126
|
-
},
|
127
|
-
"level": "notice"
|
128
|
-
}
|
129
|
-
}
|
130
|
-
]
|
131
|
-
}]
|
27
|
+
}
|
@@ -12,16 +12,15 @@ class AnsibleProviderTest < ActiveSupport::TestCase
|
|
12
12
|
assert command_options['ansible_inventory']
|
13
13
|
end
|
14
14
|
|
15
|
-
context 'when
|
15
|
+
context 'when ansible_callback_enabled is set to false' do
|
16
16
|
it 'sets enables :remote_execution_command to true' do
|
17
17
|
assert command_options[:remote_execution_command]
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
|
-
context 'when
|
21
|
+
context 'when ansible_callback_enabled is set to true' do
|
22
22
|
it 'has remote_execution_command false' do
|
23
|
-
|
24
|
-
template_invocation.template.remote_execution_features << rex_feature
|
23
|
+
template_invocation.template.ansible_callback_enabled = true
|
25
24
|
assert_not command_options[:remote_execution_command]
|
26
25
|
end
|
27
26
|
end
|
@@ -4,15 +4,13 @@ require 'test_plugin_helper'
|
|
4
4
|
|
5
5
|
# Tests for the behavior of Host with roles, checks inheritance, etc
|
6
6
|
class ConfigReportExtensionsTest < ActiveSupport::TestCase
|
7
|
-
let(:
|
7
|
+
let(:example_report) do
|
8
8
|
JSON.parse(File.read(ansible_fixture_file('report.json')))
|
9
9
|
end
|
10
10
|
|
11
|
-
let(:example_report1) { example_reports.first }
|
12
|
-
|
13
11
|
describe '.import' do
|
14
12
|
it 'sets an origin for Ansible reports' do
|
15
|
-
report = ConfigReport.import(
|
13
|
+
report = ConfigReport.import(example_report)
|
16
14
|
assert_equal 'Ansible', report.origin
|
17
15
|
end
|
18
16
|
|
@@ -20,62 +20,16 @@ ANSIBLELOG
|
|
20
20
|
)
|
21
21
|
end
|
22
22
|
|
23
|
-
test 'module message extraction with
|
24
|
-
example_report = JSON.parse(File.read(ansible_fixture_file('report.json'))).second
|
25
|
-
report = ConfigReport.import(example_report)
|
26
|
-
expected_outputs = [
|
27
|
-
'No additional data',
|
28
|
-
['Cron job: 0 5,2 * * * date > /dev/null (disabled: false)', 'Cron job: 0 5,2 * * * df > /dev/null (disabled: false)'],
|
29
|
-
['Cron job: 0 5,2 * * * hostname > /dev/null (disabled: false)'],
|
30
|
-
['Rendered template test1.txt.j2 to /tmp/test1.txt', 'Rendered template test2.txt.j2 to /tmp/test2.txt'],
|
31
|
-
['Rendered template test3.txt.j2 to /tmp/test3.txt'],
|
32
|
-
['Copy test4.txt to /tmp/test4.txt', 'Copy test5.txt to /tmp/test5.txt'],
|
33
|
-
['Copy test6.txt to /tmp/test6.txt'],
|
34
|
-
['Service chronyd started (enabled: )', 'Service firewalld started (enabled: )'],
|
35
|
-
['Service chronyd started (enabled: )']
|
36
|
-
]
|
37
|
-
actual_outputs = []
|
38
|
-
report.logs.each do |log|
|
39
|
-
actual_outputs << ansible_module_message(log)
|
40
|
-
end
|
41
|
-
assert_equal expected_outputs, actual_outputs
|
42
|
-
end
|
43
|
-
|
44
|
-
test 'accepting an almost empty message' do
|
23
|
+
test 'module message extraction with error' do
|
45
24
|
log_value = <<-ANSIBLELOG.strip_heredoc
|
46
|
-
{"changed":
|
25
|
+
{"msg": "AnsibleUndefinedVariable", "changed": false, "_ansible_no_log": false, "failed": true, "module": "template", "exception": "raise AnsibleUndefinedVariable"}
|
47
26
|
ANSIBLELOG
|
48
27
|
message = FactoryBot.build(:message, value: log_value)
|
49
|
-
log = FactoryBot.build(:log)
|
28
|
+
log = FactoryBot.build(:log, message: message)
|
50
29
|
log.message = message
|
51
|
-
assert_match(
|
52
|
-
/Copy/,
|
53
|
-
ansible_module_message(log).to_s
|
54
|
-
)
|
55
|
-
end
|
56
30
|
|
57
|
-
test 'FQCN module message extraction' do
|
58
|
-
log_value = <<-ANSIBLELOG.strip_heredoc
|
59
|
-
{"msg": "Nothing to do", "changed": false, "results": [], "rc": 0, "invocation": {"module_args": {"name": ["openssh"], "state": "present", "allow_downgrade": false, "autoremove": false, "bugfix": false, "disable_gpg_check": false, "disable_plugin": [], "disablerepo": [], "download_only": false, "enable_plugin": [], "enablerepo": [], "exclude": [], "installroot": "/", "install_repoquery": true, "install_weak_deps": true, "security": false, "skip_broken": false, "update_cache": false, "update_only": false, "validate_certs": true, "lock_timeout": 30, "conf_file": null, "disable_excludes": null, "download_dir": null, "list": null, "releasever": null}}, "_ansible_no_log": false, "failed": false, "module": "ansible.builtin.package"}
|
60
|
-
ANSIBLELOG
|
61
|
-
message = FactoryBot.build(:message, value: log_value)
|
62
|
-
log = FactoryBot.build(:log)
|
63
|
-
log.message = message
|
64
|
-
assert_match(
|
65
|
-
/Nothing to do/,
|
66
|
-
ansible_module_message(log).to_s
|
67
|
-
)
|
68
|
-
end
|
69
|
-
|
70
|
-
test 'accepting a censored message' do
|
71
|
-
log_value = <<-ANSIBLELOG.strip_heredoc
|
72
|
-
{"censored": "the output has been hidden due to the fact that 'no_log: true' was specified for this result", "changed": true, "failed": false, "module": "copy"}
|
73
|
-
ANSIBLELOG
|
74
|
-
message = FactoryBot.build(:message, value: log_value)
|
75
|
-
log = FactoryBot.build(:log)
|
76
|
-
log.message = message
|
77
31
|
assert_match(
|
78
|
-
|
32
|
+
'Execution error: AnsibleUndefinedVariable',
|
79
33
|
ansible_module_message(log).to_s
|
80
34
|
)
|
81
35
|
end
|
@@ -86,16 +86,12 @@ const validateRegexp = (variable, value) => {
|
|
86
86
|
};
|
87
87
|
|
88
88
|
const validateList = (variable, value) => {
|
89
|
-
|
90
|
-
if (typeof validatorRule !== 'string') {
|
91
|
-
validatorRule = validatorRule.toString();
|
92
|
-
}
|
93
|
-
if (validatorRule.split(',').find(item => item.trim() === value)) {
|
89
|
+
if (variable.validatorRule.split(',').find(item => item.trim() === value)) {
|
94
90
|
return validationSuccess;
|
95
91
|
}
|
96
92
|
return {
|
97
93
|
key: 'error',
|
98
|
-
msg: sprintf(__('Invalid, expected one of: %s'), validatorRule),
|
94
|
+
msg: sprintf(__('Invalid, expected one of: %s'), variable.validatorRule),
|
99
95
|
};
|
100
96
|
};
|
101
97
|
|
data/webpack/testHelper.js
CHANGED
@@ -71,9 +71,6 @@ export const userFactory = (login, permissions = []) => ({
|
|
71
71
|
permissions: {
|
72
72
|
nodes: permissions,
|
73
73
|
},
|
74
|
-
usergroups: {
|
75
|
-
nodes: [],
|
76
|
-
},
|
77
74
|
});
|
78
75
|
|
79
76
|
export const admin = {
|
@@ -84,9 +81,6 @@ export const admin = {
|
|
84
81
|
permissions: {
|
85
82
|
nodes: [],
|
86
83
|
},
|
87
|
-
usergroups: {
|
88
|
-
nodes: [],
|
89
|
-
},
|
90
84
|
};
|
91
85
|
|
92
86
|
export const intruder = userFactory('intruder', [
|
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:
|
4
|
+
version: 11.0.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: 2023-
|
11
|
+
date: 2023-01-08 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: acts_as_list
|
@@ -44,28 +44,28 @@ dependencies:
|
|
44
44
|
requirements:
|
45
45
|
- - "~>"
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
47
|
+
version: '9.0'
|
48
48
|
type: :runtime
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
52
|
- - "~>"
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
54
|
+
version: '9.0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
56
|
name: foreman-tasks
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - "~>"
|
60
60
|
- !ruby/object:Gem::Version
|
61
|
-
version: '7.
|
61
|
+
version: '7.0'
|
62
62
|
type: :runtime
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
66
|
- - "~>"
|
67
67
|
- !ruby/object:Gem::Version
|
68
|
-
version: '7.
|
68
|
+
version: '7.0'
|
69
69
|
description: Ansible integration with Foreman
|
70
70
|
email:
|
71
71
|
- elobatocs@gmail.com
|
@@ -86,10 +86,12 @@ files:
|
|
86
86
|
- app/controllers/api/v2/ansible_variables_controller.rb
|
87
87
|
- app/controllers/concerns/foreman/controller/parameters/ansible_override_value.rb
|
88
88
|
- app/controllers/concerns/foreman/controller/parameters/ansible_variable.rb
|
89
|
+
- app/controllers/concerns/foreman/controller/parameters/job_template_extensions.rb
|
89
90
|
- app/controllers/foreman_ansible/api/v2/hostgroups_controller_extensions.rb
|
90
91
|
- app/controllers/foreman_ansible/api/v2/hostgroups_param_group_extensions.rb
|
91
92
|
- app/controllers/foreman_ansible/api/v2/hosts_controller_extensions.rb
|
92
93
|
- app/controllers/foreman_ansible/api/v2/hosts_param_group_extensions.rb
|
94
|
+
- app/controllers/foreman_ansible/api/v2/job_templates_controller_extensions.rb
|
93
95
|
- app/controllers/foreman_ansible/concerns/api_common.rb
|
94
96
|
- app/controllers/foreman_ansible/concerns/hostgroups_controller_extensions.rb
|
95
97
|
- app/controllers/foreman_ansible/concerns/hosts_controller_extensions.rb
|
@@ -172,6 +174,7 @@ files:
|
|
172
174
|
- app/views/api/v2/ansible_variables/show.json.rabl
|
173
175
|
- app/views/api/v2/hostgroups/ansible_roles.json.rabl
|
174
176
|
- app/views/api/v2/hosts/ansible_roles.json.rabl
|
177
|
+
- app/views/api/v2/job_templates/job_templates.json.rabl
|
175
178
|
- app/views/foreman/smart_proxies/_update_smart_proxy.html.erb
|
176
179
|
- app/views/foreman_ansible/ansible_roles/_select_tab_content.html.erb
|
177
180
|
- app/views/foreman_ansible/ansible_roles/_select_tab_title.html.erb
|
@@ -198,6 +201,8 @@ files:
|
|
198
201
|
- app/views/foreman_ansible/job_templates/run_playbook-ansible_default.erb
|
199
202
|
- app/views/foreman_ansible/job_templates/service_action_-_ansible_default.erb
|
200
203
|
- app/views/foreman_ansible/job_templates/service_action_-_enable_web_console.erb
|
204
|
+
- app/views/job_templates/_job_template_callback_tab_content.html.erb
|
205
|
+
- app/views/job_templates/_job_template_callback_tab_headers.html.erb
|
201
206
|
- app/views/ui_ansible_roles/index.json.rabl
|
202
207
|
- app/views/ui_ansible_roles/main.json.rabl
|
203
208
|
- app/views/ui_ansible_roles/show.json.rabl
|
@@ -217,6 +222,7 @@ files:
|
|
217
222
|
- db/migrate/20200421201839_update_ansible_inv_template_name.rb
|
218
223
|
- db/migrate/20210120150019_add_position_to_ansible_role.rb
|
219
224
|
- db/migrate/20210818083407_fix_ansible_setting_category_to_dsl.rb
|
225
|
+
- db/migrate/20221003153000_add_ansible_callback_enabled_to_templates.rb
|
220
226
|
- db/migrate/20221031114720_rename_capsule_upgrade_playbook.rb
|
221
227
|
- db/seeds.d/100_common_parameters.rb
|
222
228
|
- db/seeds.d/62_ansible_proxy_feature.rb
|
@@ -472,16 +478,16 @@ test_files:
|
|
472
478
|
- test/factories/host_ansible_enhancements.rb
|
473
479
|
- test/fixtures/insights_playbook.yaml
|
474
480
|
- test/fixtures/playbooks_example_output.json
|
481
|
+
- test/fixtures/report.json
|
475
482
|
- test/fixtures/sample_facts.json
|
476
483
|
- test/fixtures/sample_playbooks.json
|
477
|
-
- test/fixtures/report.json
|
478
484
|
- test/foreman_ansible/helpers/ansible_roles_helper_test.rb
|
479
485
|
- test/functional/ansible_roles_controller_test.rb
|
480
486
|
- test/functional/ansible_variables_controller_test.rb
|
487
|
+
- test/functional/api/v2/ansible_inventories_controller_test.rb
|
481
488
|
- test/functional/api/v2/ansible_playbooks_controller_test.rb
|
482
489
|
- test/functional/api/v2/ansible_roles_controller_test.rb
|
483
490
|
- test/functional/api/v2/ansible_variables_controller_test.rb
|
484
|
-
- test/functional/api/v2/ansible_inventories_controller_test.rb
|
485
491
|
- test/functional/api/v2/hostgroups_controller_test.rb
|
486
492
|
- test/functional/api/v2/hosts_controller_test.rb
|
487
493
|
- test/functional/hosts_controller_test.rb
|