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.
Files changed (26) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/concerns/foreman/controller/parameters/job_template_extensions.rb +17 -0
  3. data/app/controllers/foreman_ansible/api/v2/job_templates_controller_extensions.rb +18 -0
  4. data/app/graphql/types/overriden_ansible_variable.rb +2 -4
  5. data/app/helpers/foreman_ansible/ansible_hostgroups_helper.rb +1 -7
  6. data/app/helpers/foreman_ansible/ansible_reports_helper.rb +20 -27
  7. data/app/models/foreman_ansible/ansible_provider.rb +1 -1
  8. data/app/views/api/v2/job_templates/job_templates.json.rabl +3 -0
  9. data/app/views/foreman_ansible/config_reports/_ansible.html.erb +6 -2
  10. data/app/views/job_templates/_job_template_callback_tab_content.html.erb +3 -0
  11. data/app/views/job_templates/_job_template_callback_tab_headers.html.erb +13 -0
  12. data/db/migrate/20210818083407_fix_ansible_setting_category_to_dsl.rb +1 -1
  13. data/db/migrate/20221003153000_add_ansible_callback_enabled_to_templates.rb +10 -0
  14. data/lib/foreman_ansible/engine.rb +3 -0
  15. data/lib/foreman_ansible/register.rb +15 -1
  16. data/lib/foreman_ansible/version.rb +1 -1
  17. data/package.json +1 -1
  18. data/test/fixtures/report.json +2 -106
  19. data/test/unit/ansible_provider_test.rb +3 -4
  20. data/test/unit/concerns/config_reports_extensions_test.rb +2 -4
  21. data/test/unit/helpers/ansible_reports_helper_test.rb +4 -50
  22. data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/AnsibleVariableOverridesTableHelper.js +2 -6
  23. data/webpack/graphql/queries/currentUserAttributes.gql +0 -5
  24. data/webpack/permissionsHelper.js +1 -4
  25. data/webpack/testHelper.js +0 -6
  26. metadata +14 -8
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6036f9a1d90705a4459c56093084b63c953a7c657a125f4f04ca878fb9bc7552
4
- data.tar.gz: 5354e5227c4eccf6fcee0f721b3e03a10d0d119354d3dc3b2aa742ad44105023
3
+ metadata.gz: 348701b380a514ccae60dda27305a137438dd4cd2812f9515e071a527a0b20c5
4
+ data.tar.gz: ab770d603437ed890a945381d28411e0caff547f6bef26b22c98191fe6317b98
5
5
  SHA512:
6
- metadata.gz: a29b28a5c665e8af6f52b9b5b2e7ff94f88f87912795ef54c5eedbb7347c666796a42c09456bcd6b50ed30e1d17a1e28f25ab23929fad6b34a4b51ceb87f5131
7
- data.tar.gz: 482d9f6ee4f9c1367a45d87cd4158c29ef88ca2860b6183cdf159123a5a9cf88c7bcca18549b7d448cfff9a5094a3eda24f782f7cd1339e0d0d1fd9abfb1b28c
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
- assign_jobs = { action: { content: (link_to _('Configure Ansible Job'), "/ansible/hostgroups/#{hostgroup.id}", class: 'la') }, priority: 32 }
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.fetch('module', '').delete_prefix('ansible.builtin.').delete_prefix('ansible.legacy.')
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
- get_results(msg_json) do |module_args, result|
39
- _("Rendered template #{module_args['_original_basename']} to #{result['dest']}")
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
- get_results(msg_json) do |_, result|
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
- get_results(msg_json) do |_, result|
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
- get_results(msg_json) do |_, result|
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
- get_results(msg_json) do |module_args, _|
55
- _("Cron job: #{module_args['minute']} #{module_args['hour']} #{module_args['day']} #{module_args['month']} #{module_args['weekday']} #{module_args['job']} (disabled: #{module_args['disabled']})")
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
- get_results(msg_json) do |module_args, result|
59
- _("Copy #{module_args['_original_basename']} to #{result['dest']}")
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
- private
83
-
84
- def get_results(msg_json)
85
- results = msg_json.key?('results') ? msg_json['results'] : [msg_json]
86
- results.map do |result|
87
- module_args = result.fetch('invocation', {}).fetch('module_args', {})
88
- yield module_args, result
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
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ attributes :ansible_callback_enabled
@@ -29,8 +29,12 @@
29
29
  <% end %>
30
30
  </ul>
31
31
  <% else %>
32
- <%= log_message %>
33
- <% end %>
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,3 @@
1
+ <div class="tab-pane" id="ansible_callback_enabled">
2
+ <%= checkbox_f f, :ansible_callback_enabled, :label => _('Enable Ansible Callback'), :disabled => @template.locked? %>
3
+ </div>
@@ -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') if column_exists?(:settings, :category)
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.5'
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
@@ -4,5 +4,5 @@
4
4
  # This way other parts of Foreman can just call ForemanAnsible::VERSION
5
5
  # and detect what version the plugin is running.
6
6
  module ForemanAnsible
7
- VERSION = '10.4.1'
7
+ VERSION = '11.0.0'
8
8
  end
data/package.json CHANGED
@@ -7,7 +7,7 @@
7
7
  "test": "test"
8
8
  },
9
9
  "peerDependencies": {
10
- "@theforeman/vendor": "^10.0"
10
+ "@theforeman/vendor": ">= 8.16.0"
11
11
  },
12
12
  "dependencies": {
13
13
  "react-json-tree": "^0.11.0"
@@ -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 it is not using the ansible_run_host feature' do
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 it is using the ansible_run_host feature' do
21
+ context 'when ansible_callback_enabled is set to true' do
22
22
  it 'has remote_execution_command false' do
23
- rex_feature = RemoteExecutionFeature.where(:label => 'ansible_run_host', :name => 'Run Ansible roles').first_or_create
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(:example_reports) do
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(example_report1)
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 action' do
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": true, "failed": false, "module": "copy"}
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
- /output has been hidden/,
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
- let { validatorRule } = variable;
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
 
@@ -8,9 +8,4 @@ fragment CurrentUserAttributes on User {
8
8
  name
9
9
  }
10
10
  }
11
- usergroups {
12
- nodes {
13
- admin
14
- }
15
- }
16
11
  }
@@ -11,10 +11,7 @@ export const permissionCheck = (user, permissionsRequired) => {
11
11
  );
12
12
  }
13
13
 
14
- if (
15
- user.admin ||
16
- user.usergroups.nodes.find(usergroup => usergroup.admin === true)
17
- ) {
14
+ if (user.admin) {
18
15
  return { allowed: true };
19
16
  }
20
17
 
@@ -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: 10.4.1
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-02-27 00:00:00.000000000 Z
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: '8.2'
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: '8.2'
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.1'
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.1'
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