foreman_ansible 6.3.4 → 7.0.1

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 (151) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/api/v2/ansible_inventories_controller.rb +1 -1
  3. data/app/graphql/mutations/ansible_variable_overrides/create.rb +26 -0
  4. data/app/graphql/mutations/ansible_variable_overrides/delete.rb +38 -0
  5. data/app/graphql/mutations/ansible_variable_overrides/update.rb +26 -0
  6. data/app/graphql/mutations/hosts/assign_ansible_roles.rb +37 -0
  7. data/app/graphql/presenters/ansible_role_presenter.rb +12 -0
  8. data/app/graphql/presenters/overriden_ansible_variable_presenter.rb +19 -0
  9. data/app/graphql/types/ansible_role.rb +9 -0
  10. data/app/graphql/types/ansible_variable.rb +23 -0
  11. data/app/graphql/types/ansible_variable_override.rb +9 -0
  12. data/app/graphql/types/inherited_ansible_role.rb +13 -0
  13. data/app/graphql/types/overriden_ansible_variable.rb +27 -0
  14. data/app/helpers/foreman_ansible/ansible_reports_helper.rb +35 -54
  15. data/app/models/concerns/foreman_ansible/host_managed_extensions.rb +23 -4
  16. data/app/models/concerns/foreman_ansible/hostgroup_extensions.rb +1 -0
  17. data/app/models/foreman_ansible/ansible_provider.rb +56 -2
  18. data/app/services/foreman_ansible/ansible_report_importer.rb +2 -2
  19. data/app/services/foreman_ansible/inventory_creator.rb +1 -1
  20. data/app/services/foreman_ansible/override_resolver.rb +22 -0
  21. data/app/views/api/v2/ansible_override_values/index.json.rabl +3 -0
  22. data/app/views/api/v2/ansible_variables/show.json.rabl +1 -1
  23. data/app/views/foreman_ansible/ansible_roles/_hostgroup_ansible_roles_button.erb +3 -0
  24. data/app/views/foreman_ansible/config_reports/_ansible.html.erb +14 -5
  25. data/app/views/foreman_ansible/job_templates/ansible_roles_-_ansible_default.erb +4 -0
  26. data/app/views/foreman_ansible/job_templates/convert_to_rhel.erb +6 -2
  27. data/app/views/foreman_ansible/job_templates/run_openscap_scans_-_ansible_default.erb +20 -0
  28. data/config/routes.rb +3 -0
  29. data/db/migrate/20210818083407_fix_ansible_setting_category_to_dsl.rb +5 -0
  30. data/lib/foreman_ansible/engine.rb +0 -18
  31. data/lib/foreman_ansible/register.rb +114 -2
  32. data/lib/foreman_ansible/version.rb +1 -1
  33. data/package.json +10 -6
  34. data/test/functional/api/v2/ansible_inventories_controller_test.rb +1 -2
  35. data/test/graphql/mutations/hosts/assign_ansible_roles_mutation_test.rb +96 -0
  36. data/test/graphql/queries/ansible_roles_query_test.rb +35 -0
  37. data/test/unit/concerns/host_managed_extensions_test.rb +8 -0
  38. data/test/unit/concerns/hostgroup_extensions_test.rb +6 -0
  39. data/test/unit/helpers/ansible_reports_helper_test.rb +4 -30
  40. data/test/unit/services/override_resolver_test.rb +34 -0
  41. data/webpack/components/AnsibleHostDetail/AnsibleHostDetail.js +59 -0
  42. data/webpack/components/AnsibleHostDetail/AnsibleHostDetail.scss +6 -0
  43. data/webpack/components/AnsibleHostDetail/AnsibleHostDetail.test.js +20 -0
  44. data/webpack/components/AnsibleHostDetail/components/AnsibleHostInventory/AnsibleHostInventory.js +22 -0
  45. data/webpack/components/AnsibleHostDetail/components/AnsibleHostInventory/AnsibleHostInventory.scss +4 -0
  46. data/webpack/components/AnsibleHostDetail/components/AnsibleHostInventory/AnsibleHostInventory.test.js +104 -0
  47. data/webpack/components/AnsibleHostDetail/components/AnsibleHostInventory/index.js +38 -0
  48. data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/AnsibleVariableOverrides.scss +3 -0
  49. data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/AnsibleVariableOverridesTable.js +238 -0
  50. data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/AnsibleVariableOverridesTableHelper.js +111 -0
  51. data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/EditableAction.js +161 -0
  52. data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/EditableAction.scss +7 -0
  53. data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/EditableActionHelper.js +49 -0
  54. data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/EditableValue.js +70 -0
  55. data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/EditableValueHelper.js +35 -0
  56. data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/__test__/AnsibleVariableOverrides.fixtures.js +429 -0
  57. data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/__test__/AnsibleVariableOverrides.test.js +71 -0
  58. data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/__test__/AnsibleVariableOverridesDelete.test.js +74 -0
  59. data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/__test__/AnsibleVariableOverridesUpdate.test.js +188 -0
  60. data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/index.js +58 -0
  61. data/webpack/components/AnsibleHostDetail/components/JobsTab/JobsTabHelper.js +79 -0
  62. data/webpack/components/AnsibleHostDetail/components/JobsTab/NewRecurringJobHelper.js +106 -0
  63. data/webpack/components/AnsibleHostDetail/components/JobsTab/NewRecurringJobModal.js +129 -0
  64. data/webpack/components/AnsibleHostDetail/components/JobsTab/NewRecurringJobModal.scss +7 -0
  65. data/webpack/components/AnsibleHostDetail/components/JobsTab/PreviousJobsTable.js +103 -0
  66. data/webpack/components/AnsibleHostDetail/components/JobsTab/RecurringJobsTable.js +96 -0
  67. data/webpack/components/AnsibleHostDetail/components/JobsTab/__test__/JobsTab.fixtures.js +184 -0
  68. data/webpack/components/AnsibleHostDetail/components/JobsTab/__test__/JobsTab.test.js +195 -0
  69. data/webpack/components/AnsibleHostDetail/components/JobsTab/index.js +88 -0
  70. data/webpack/components/AnsibleHostDetail/components/RolesTab/AllRolesModal/AllRolesTable.js +89 -0
  71. data/webpack/components/AnsibleHostDetail/components/RolesTab/AllRolesModal/index.js +80 -0
  72. data/webpack/components/AnsibleHostDetail/components/RolesTab/EditRolesModal/EditRolesForm.js +90 -0
  73. data/webpack/components/AnsibleHostDetail/components/RolesTab/EditRolesModal/EditRolesModal.scss +3 -0
  74. data/webpack/components/AnsibleHostDetail/components/RolesTab/EditRolesModal/EditRolesModalHelper.js +40 -0
  75. data/webpack/components/AnsibleHostDetail/components/RolesTab/EditRolesModal/index.js +82 -0
  76. data/webpack/components/AnsibleHostDetail/components/RolesTab/RolesTable.js +129 -0
  77. data/webpack/components/AnsibleHostDetail/components/RolesTab/__test__/EditRoles.test.js +85 -0
  78. data/webpack/components/AnsibleHostDetail/components/RolesTab/__test__/RolesTab.fixtures.js +180 -0
  79. data/webpack/components/AnsibleHostDetail/components/RolesTab/__test__/RolesTab.test.js +75 -0
  80. data/webpack/components/AnsibleHostDetail/components/RolesTab/index.js +51 -0
  81. data/webpack/components/AnsibleHostDetail/components/SecondaryTabRoutes.js +60 -0
  82. data/webpack/components/AnsibleHostDetail/components/TabLayout.js +12 -0
  83. data/webpack/components/AnsibleHostDetail/constants.js +9 -0
  84. data/webpack/components/AnsibleHostDetail/helpers.js +4 -0
  85. data/webpack/components/AnsibleHostDetail/index.js +6 -0
  86. data/webpack/components/AnsibleRolesAndVariables/__test__/AnsibleRolesAndVariablesImport.test.js +15 -10
  87. data/webpack/components/AnsibleRolesSwitcher/components/AnsibleRole.js +29 -0
  88. data/webpack/components/AnsibleRolesSwitcher/components/AnsibleRole.test.js +3 -0
  89. data/webpack/components/AnsibleRolesSwitcher/components/AvailableRolesList.js +2 -1
  90. data/webpack/components/AnsibleRolesSwitcher/components/__snapshots__/AnsiblePermissionDenied.test.js.snap +2 -0
  91. data/webpack/components/AnsibleRolesSwitcher/components/__snapshots__/AnsibleRole.test.js.snap +3 -3
  92. data/webpack/components/AnsibleRolesSwitcher/components/__snapshots__/AssignedRolesList.test.js.snap +4 -4
  93. data/webpack/components/AnsibleRolesSwitcher/components/__snapshots__/AvailableRolesList.test.js.snap +9 -0
  94. data/webpack/components/DualList/DualList.scss +3 -0
  95. data/webpack/components/DualList/ListControls.js +65 -0
  96. data/webpack/components/DualList/ListHeader.js +16 -0
  97. data/webpack/components/DualList/ListItem.js +69 -0
  98. data/webpack/components/DualList/ListPane.js +95 -0
  99. data/webpack/components/DualList/SelectedStatus.js +21 -0
  100. data/webpack/components/DualList/index.js +103 -0
  101. data/webpack/components/ErrorState.js +16 -0
  102. data/webpack/components/withLoading.js +135 -0
  103. data/webpack/components/withPagination.js +0 -0
  104. data/webpack/formHelper.js +131 -0
  105. data/webpack/globalIdHelper.js +13 -0
  106. data/webpack/global_index.js +18 -0
  107. data/webpack/graphql/mutations/assignAnsibleRoles.gql +17 -0
  108. data/webpack/graphql/mutations/cancelRecurringLogic.gql +12 -0
  109. data/webpack/graphql/mutations/createAnsibleVariableOverride.gql +28 -0
  110. data/webpack/graphql/mutations/createJobInvocation.gql +11 -0
  111. data/webpack/graphql/mutations/deleteAnsibleVariableOverride.gql +17 -0
  112. data/webpack/graphql/mutations/updateAnsibleVariableOverride.gql +29 -0
  113. data/webpack/graphql/queries/allAnsibleRoles.gql +13 -0
  114. data/webpack/graphql/queries/ansibleRoles.gql +13 -0
  115. data/webpack/graphql/queries/currentUserAttributes.gql +11 -0
  116. data/webpack/graphql/queries/hostAnsibleRoles.gql +17 -0
  117. data/webpack/graphql/queries/hostAvailableAnsibleRoles.gql +11 -0
  118. data/webpack/graphql/queries/hostVariableOverrides.gql +39 -0
  119. data/webpack/graphql/queries/recurringJobs.gql +28 -0
  120. data/webpack/helpers/pageParamsHelper.js +40 -0
  121. data/webpack/helpers/paginationHelper.js +9 -0
  122. data/webpack/permissionsHelper.js +58 -0
  123. data/webpack/routes/HostgroupJobs/__test__/HostgroupJobs.fixtures.js +63 -0
  124. data/webpack/routes/HostgroupJobs/__test__/HostgroupJobs.test.js +112 -0
  125. data/webpack/routes/HostgroupJobs/index.js +26 -0
  126. data/webpack/routes/routes.js +10 -0
  127. data/webpack/testHelper.js +165 -0
  128. data/webpack/toastHelper.js +4 -0
  129. metadata +135 -83
  130. data/app/assets/images/foreman_ansible/Ansible.png +0 -0
  131. data/app/models/foreman_ansible/fact_name.rb +0 -16
  132. data/app/models/setting/ansible.rb +0 -106
  133. data/app/services/foreman_ansible/fact_importer.rb +0 -99
  134. data/app/services/foreman_ansible/fact_parser.rb +0 -126
  135. data/app/services/foreman_ansible/fact_sparser.rb +0 -37
  136. data/app/services/foreman_ansible/operating_system_parser.rb +0 -102
  137. data/app/services/foreman_ansible/structured_fact_importer.rb +0 -25
  138. data/test/unit/lib/foreman_ansible_core/ansible_runner_test.rb +0 -51
  139. data/test/unit/lib/foreman_ansible_core/command_creator_test.rb +0 -64
  140. data/test/unit/lib/foreman_ansible_core/playbook_runner_test.rb +0 -110
  141. data/test/unit/services/fact_importer_test.rb +0 -52
  142. data/test/unit/services/fact_parser_test.rb +0 -281
  143. data/test/unit/services/fact_sparser_test.rb +0 -24
  144. data/test/unit/services/structured_fact_importer_test.rb +0 -30
  145. data/webpack/__mocks__/foremanReact/common/I18n.js +0 -1
  146. data/webpack/__mocks__/foremanReact/common/helpers.js +0 -13
  147. data/webpack/__mocks__/foremanReact/components/Pagination/PaginationWrapper.js +0 -2
  148. data/webpack/__mocks__/foremanReact/components/common/EmptyState.js +0 -5
  149. data/webpack/__mocks__/foremanReact/components/common/forms/OrderableSelect/helpers.js +0 -5
  150. data/webpack/__mocks__/foremanReact/redux/API.js +0 -7
  151. data/webpack/components/AnsibleRolesAndVariables/__test__/__snapshots__/AnsibleRolesAndVariablesImport.test.js.snap +0 -177
@@ -15,10 +15,14 @@ model: JobTemplate
15
15
  - name: Display all parameters known for the Foreman host
16
16
  debug:
17
17
  var: foreman
18
+ tags:
19
+ - always
18
20
  tasks:
19
21
  - name: Apply roles
20
22
  include_role:
21
23
  name: "{{ role }}"
24
+ tags:
25
+ - always
22
26
  loop: "{{ foreman_ansible_roles }}"
23
27
  loop_control:
24
28
  loop_var: role
@@ -5,13 +5,17 @@ template_inputs:
5
5
  - name: Activation Key
6
6
  required: true
7
7
  input_type: user
8
+ description: Set the activation key to assign the desired RHEL subscription and
9
+ life cycle environment to the converted machine at the registration step.
8
10
  advanced: false
9
- value_type: plain
11
+ value_type: resource
10
12
  resource_type: Katello::ActivationKey
11
13
  hidden_value: false
12
14
  - name: Restart
13
15
  required: true
14
16
  input_type: user
17
+ description: Restart the system when it is successfully converted to RHEL to boot
18
+ the new RHEL kernel.
15
19
  options: "yes\r\nno"
16
20
  advanced: false
17
21
  value_type: plain
@@ -34,7 +38,7 @@ kind: job_template
34
38
  url: <%= subscription_manager_configuration_url(@host) %>
35
39
  dest: /usr/share/convert2rhel/subscription-manager/katello-ca-consumer-latest.noarch.rpm
36
40
  - name: Start convert2rhel
37
- command: convert2rhel -y --activationkey "<%= input('Activation Key') %>" --org "<%= @host.organization.label %>" --keep-rhsm
41
+ command: convert2rhel -y --activationkey "<%= input_resource('Activation Key').name %>" --org "<%= @host.organization.label %>" --keep-rhsm
38
42
  <%- if input('Restart') == "yes" -%>
39
43
  - name: Reboot the machine
40
44
  reboot:
@@ -0,0 +1,20 @@
1
+ <%#
2
+ name: Run OpenSCAP scans - Ansible Default
3
+ job_category: OpenSCAP Ansible Commands
4
+ description_format: Run scan for all OpenSCAP policies on given hosts
5
+ snippet: false
6
+ provider_type: Ansible
7
+ kind: job_template
8
+ model: JobTemplate
9
+ %>
10
+
11
+ <% raise "Create and assign a policy to this host before proceeding" if @host.policies_enc_raw.empty? -%>
12
+ ---
13
+ - hosts: all
14
+ tasks:
15
+ <% @host.policies_enc_raw.each do |policy| -%>
16
+ - shell: /usr/bin/foreman_scap_client <%= policy['id'] %>
17
+ register: out
18
+ - debug: var=out
19
+ <% end -%>
20
+
data/config/routes.rb CHANGED
@@ -1,6 +1,9 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  Rails.application.routes.draw do
4
+ match '/ansible/hostgroups' => 'react#index', :via => [:get]
5
+ match '/ansible/hostgroups/*page' => 'react#index', :via => [:get]
6
+
4
7
  namespace :api, defaults: { format: 'json' } do
5
8
  scope '(:apiv)',
6
9
  :module => :v2,
@@ -0,0 +1,5 @@
1
+ class FixAnsibleSettingCategoryToDsl < ActiveRecord::Migration[6.0]
2
+ def up
3
+ Setting.where(category: 'Setting::Ansible').update_all(category: 'Setting')
4
+ end
5
+ end
@@ -4,7 +4,6 @@ require 'deface'
4
4
  require 'acts_as_list'
5
5
  require 'fast_gettext'
6
6
  require 'gettext_i18n_rails'
7
- require 'foreman_ansible_core' if Rails.env.test?
8
7
  require 'foreman_ansible/remote_execution'
9
8
 
10
9
  module ForemanAnsible
@@ -20,12 +19,6 @@ module ForemanAnsible
20
19
  config.autoload_paths += Dir["#{config.root}/app/views"]
21
20
  config.autoload_paths += Dir["#{config.root}/app/lib"]
22
21
 
23
- initializer 'foreman_ansible.load_default_settings',
24
- :before => :load_config_initializers do
25
- require_dependency(File.join(ForemanAnsible::Engine.root,
26
- 'app/models/setting/ansible.rb'))
27
- end
28
-
29
22
  initializer 'foreman_ansible.register_gettext',
30
23
  :after => :load_config_initializers do
31
24
  locale_dir = File.join(File.expand_path('../..', __dir__), 'locale')
@@ -72,17 +65,6 @@ module ForemanAnsible
72
65
  end
73
66
 
74
67
  config.to_prepare do
75
- foreman_version = ::Foreman::Version.new
76
- if Rails.env.test? ||
77
- foreman_version.major.to_i == 1 && foreman_version.minor.to_i < 13
78
- ::Foreman::Plugin.fact_importer_registry.register(:ansible, ForemanAnsible::FactImporter)
79
- else
80
- ::Foreman::Plugin.fact_importer_registry.register(
81
- :ansible,
82
- ForemanAnsible::StructuredFactImporter
83
- )
84
- end
85
- ::FactParser.register_fact_parser(:ansible, ForemanAnsible::FactParser)
86
68
  ::Host::Managed.prepend ForemanAnsible::HostManagedExtensions
87
69
  ::Hostgroup.include ForemanAnsible::HostgroupExtensions
88
70
  ::HostsHelper.include ForemanAnsible::HostsHelperExtensions
@@ -1,7 +1,89 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  Foreman::Plugin.register :foreman_ansible do
4
- requires_foreman '>= 2.2'
4
+ requires_foreman '>= 3.0'
5
+
6
+ settings do
7
+ category :ansible, N_('Ansible') do
8
+ setting 'ansible_ssh_private_key_file',
9
+ type: :string,
10
+ description: N_('Use this to supply a path to an SSH Private Key '\
11
+ 'that Ansible will use in lieu of a password '\
12
+ 'Override with "ansible_ssh_private_key_file" '\
13
+ 'host parameter'),
14
+ default: '',
15
+ full_name: N_('Private Key Path')
16
+ setting 'ansible_connection',
17
+ type: :string,
18
+ description: N_('Use this connection type by default when running '\
19
+ 'Ansible playbooks. You can override this on hosts by '\
20
+ 'adding a parameter "ansible_connection"'),
21
+ default: 'ssh',
22
+ full_name: N_('Connection type')
23
+ setting 'ansible_winrm_server_cert_validation',
24
+ type: :string,
25
+ description: N_('Enable/disable WinRM server certificate '\
26
+ 'validation when running Ansible playbooks. You can override '\
27
+ 'this on hosts by adding a parameter '\
28
+ '"ansible_winrm_server_cert_validation"'),
29
+ default: 'validate',
30
+ full_name: N_('WinRM cert Validation')
31
+ setting 'ansible_verbosity',
32
+ type: :integer,
33
+ description: N_('Foreman will add this level of verbosity for '\
34
+ 'additional debugging output when running Ansible playbooks.'),
35
+ default: '0',
36
+ full_name: N_('Default verbosity level'),
37
+ value: nil,
38
+ collection: proc {
39
+ { '0' => N_('Disabled'),
40
+ '1' => N_('Level 1 (-v)'),
41
+ '2' => N_('Level 2 (-vv)'),
42
+ '3' => N_('Level 3 (-vvv)'),
43
+ '4' => N_('Level 4 (-vvvv)') }
44
+ }
45
+ setting 'ansible_post_provision_timeout',
46
+ type: :integer,
47
+ description: N_('Timeout (in seconds) to set when Foreman will trigger a '\
48
+ 'play Ansible roles task after a host is fully provisioned. '\
49
+ 'Set this to the maximum time you expect a host to take '\
50
+ 'until it is ready after a reboot.'),
51
+ default: '360',
52
+ full_name: N_('Post-provision timeout')
53
+ setting 'ansible_interval',
54
+ type: :integer,
55
+ description: N_('Timeout (in minutes) when hosts should have reported.'),
56
+ default: '30',
57
+ full_name: N_('Ansible report timeout')
58
+ setting 'ansible_out_of_sync_disabled',
59
+ type: :boolean,
60
+ description: format(N_('Disable host configuration status turning to out of'\
61
+ ' sync for %{cfgmgmt} after report does not arrive within'\
62
+ ' configured interval'), :cfgmgmt => 'Ansible'),
63
+ default: false,
64
+ full_name: format(N_('%{cfgmgmt} out of sync disabled'), :cfgmgmt => 'Ansible')
65
+ setting 'ansible_inventory_template',
66
+ type: :string,
67
+ description: N_('Foreman will use this template to schedule the report '\
68
+ 'with Ansible inventory'),
69
+ default: 'Ansible - Ansible Inventory',
70
+ full_name: N_('Default Ansible inventory report template')
71
+ setting 'ansible_roles_to_ignore',
72
+ type: :array,
73
+ description: N_('Those roles will be excluded when importing roles from smart proxy, '\
74
+ 'The expected input is comma separated values and you can use * wildcard metacharacters'\
75
+ 'For example: foo*, *b*,*bar'),
76
+ default: [],
77
+ full_name: N_('Ansible roles to ignore')
78
+ setting 'foreman_ansible_proxy_batch_size',
79
+ type: :integer,
80
+ description: N_('Number of tasks which should be sent to the smart proxy in one request, '\
81
+ 'if foreman_tasks_proxy_batch_trigger is enabled. '\
82
+ 'If set, overrides foreman_tasks_proxy_batch_size setting for Ansible jobs.'),
83
+ default: nil,
84
+ full_name: N_('Proxy tasks batch size for Ansible')
85
+ end
86
+ end
5
87
 
6
88
  security_block :foreman_ansible do
7
89
  permission :play_roles_on_host,
@@ -29,23 +111,27 @@ Foreman::Plugin.register :foreman_ansible do
29
111
  :resource_type => 'AnsibleRole'
30
112
  permission :view_ansible_variables,
31
113
  {
114
+ :lookup_values => [:index],
32
115
  :ansible_variables => [:index, :auto_complete_search],
33
116
  :'api/v2/ansible_variables' => [:index, :show]
34
117
  },
35
118
  :resource_type => 'AnsibleVariable'
36
119
  permission :edit_ansible_variables,
37
- { :ansible_variables => [:edit, :update],
120
+ { :lookup_values => [:update],
121
+ :ansible_variables => [:edit, :update],
38
122
  :'api/v2/ansible_variables' => [:update],
39
123
  :'api/v2/ansible_override_values' => [:create, :destroy] },
40
124
  :resource_type => 'AnsibleVariable'
41
125
  permission :destroy_ansible_variables,
42
126
  {
127
+ :lookup_values => [:destroy],
43
128
  :ansible_variables => [:destroy],
44
129
  :'api/v2/ansible_variables' => [:destroy, :obsolete]
45
130
  },
46
131
  :resource_type => 'AnsibleVariable'
47
132
  permission :create_ansible_variables,
48
133
  {
134
+ :lookup_values => [:create],
49
135
  :ansible_variables => [:new, :create],
50
136
  :'api/v2/ansible_variables' => [:create]
51
137
  },
@@ -97,6 +183,32 @@ Foreman::Plugin.register :foreman_ansible do
97
183
  parameter_filter Host::Managed, base_role_assignment_params.merge(:host_ansible_roles_attributes => {})
98
184
  parameter_filter Hostgroup, base_role_assignment_params.merge(:hostgroup_ansible_roles_attributes => {})
99
185
 
186
+ register_global_js_file 'global'
187
+
188
+ extend_graphql_type :type => ::Types::Host do
189
+ field :all_ansible_roles, ::Types::InheritedAnsibleRole.connection_type, :null => true, :method => :present_all_ansible_roles
190
+ field :own_ansible_roles, ::Types::AnsibleRole.connection_type, :null => true
191
+ field :available_ansible_roles, ::Types::AnsibleRole.connection_type, :null => true
192
+ field :ansible_variables_with_overrides, Types::OverridenAnsibleVariable.connection_type, :null => false
193
+
194
+ def present_all_ansible_roles
195
+ inherited_ansible_roles = object.inherited_ansible_roles.map { |role| ::Presenters::AnsibleRolePresenter.new(role, true) }
196
+ ansible_roles = object.ansible_roles.map { |role| ::Presenters::AnsibleRolePresenter.new(role, false) }
197
+ (inherited_ansible_roles + ansible_roles).uniq
198
+ end
199
+
200
+ def ansible_variables_with_overrides
201
+ resolver = ::ForemanAnsible::OverrideResolver.new(object)
202
+ AnsibleVariable.where(:ansible_role_id => object.all_ansible_roles.pluck(:id), :override => true).map { |variable| ::Presenters::OverridenAnsibleVariablePresenter.new variable, resolver }
203
+ end
204
+ end
205
+
206
+ register_graphql_query_field :ansible_roles, '::Types::AnsibleRole', :collection_field
207
+ register_graphql_mutation_field :assign_ansible_roles, '::Mutations::Hosts::AssignAnsibleRoles'
208
+ register_graphql_mutation_field :delete_ansible_variable_override, ::Mutations::AnsibleVariableOverrides::Delete
209
+ register_graphql_mutation_field :update_ansible_variable_override, ::Mutations::AnsibleVariableOverrides::Update
210
+ register_graphql_mutation_field :create_ansible_variable_override, ::Mutations::AnsibleVariableOverrides::Create
211
+
100
212
  divider :top_menu, :caption => N_('Ansible'), :parent => :configure_menu
101
213
  menu :top_menu, :ansible_roles,
102
214
  :caption => N_('Roles'),
@@ -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 = '6.3.4'
7
+ VERSION = '7.0.1'
8
8
  end
data/package.json CHANGED
@@ -7,23 +7,27 @@
7
7
  "test": "test"
8
8
  },
9
9
  "peerDependencies": {
10
- "@theforeman/vendor": ">= 6.0.0"
10
+ "@theforeman/vendor": ">= 8.16.0"
11
11
  },
12
12
  "dependencies": {
13
13
  "react-json-tree": "^0.11.0"
14
14
  },
15
15
  "devDependencies": {
16
16
  "@babel/core": "^7.7.0",
17
- "@theforeman/builder": "^6.0.0",
18
- "@theforeman/eslint-plugin-foreman": "6.0.0",
19
- "@theforeman/test": "^6.0.0",
20
- "@theforeman/vendor-dev": "^6.0.0",
17
+ "@testing-library/user-event": "^13.2.1",
18
+ "@theforeman/builder": "^8.4.1",
19
+ "@theforeman/eslint-plugin-foreman": "^8.4.1",
20
+ "@theforeman/find-foreman": "^8.4.1",
21
+ "@theforeman/stories": "^8.4.1",
22
+ "@theforeman/test": "^8.9.0",
23
+ "@theforeman/vendor-dev": "^8.4.1",
24
+ "@testing-library/user-event": "^13.2.1",
21
25
  "babel-eslint": "^10.0.3",
22
26
  "eslint": "^6.7.2",
23
27
  "prettier": "^1.13.5"
24
28
  },
25
29
  "scripts": {
26
- "test": "tfm-test --plugin",
30
+ "test": "tfm-test --plugin --config jest.config.js",
27
31
  "lint": "tfm-lint --plugin -d webpack"
28
32
  },
29
33
  "repository": {
@@ -34,8 +34,7 @@ module Api
34
34
 
35
35
  test 'schedule inventory by user' do
36
36
  report = FactoryBot.create(:report_template)
37
- Setting::Ansible.create! :name => 'ansible_inventory_template', :value => report.name,
38
- :default => report.name, :description => 'inventory'
37
+ Setting['ansible_inventory_template'] = report.name
39
38
  user = FactoryBot.create(:user)
40
39
  user.roles << Role.find_by(:name => 'Ansible Tower Inventory Reader')
41
40
  post :schedule, { :session => set_session_user(user) }
@@ -0,0 +1,96 @@
1
+ require 'test_plugin_helper'
2
+
3
+ module Mutations
4
+ module Hosts
5
+ class CreateMutationTest < GraphQLQueryTestCase
6
+ let(:tax_location) { FactoryBot.create(:location) }
7
+ let(:location_id) { Foreman::GlobalId.for(tax_location) }
8
+ let(:organization) { FactoryBot.create(:organization) }
9
+ let(:organization_id) { Foreman::GlobalId.for(organization) }
10
+
11
+ let(:role1) { FactoryBot.create(:ansible_role) }
12
+ let(:role2) { FactoryBot.create(:ansible_role) }
13
+ let(:role3) { FactoryBot.create(:ansible_role) }
14
+ let(:host) { FactoryBot.create(:host, :ansible_roles => [role1, role2, role3], :organization => organization, :location => tax_location) }
15
+
16
+ let(:variables) { { id: Foreman::GlobalId.for(host), ansibleRoleIds: [role3.id, role2.id, role1.id] } }
17
+ let(:query) do
18
+ <<-GRAPHQL
19
+ mutation AssignAnsibleRoles($id: ID!, $ansibleRoleIds: [Int!]!) {
20
+ assignAnsibleRoles(input: { id: $id, ansibleRoleIds: $ansibleRoleIds }) {
21
+ host {
22
+ id
23
+ ownAnsibleRoles {
24
+ nodes {
25
+ id
26
+ name
27
+ }
28
+ }
29
+ }
30
+ errors {
31
+ path
32
+ message
33
+ }
34
+ }
35
+ }
36
+ GRAPHQL
37
+ end
38
+
39
+ context 'with admin permissions' do
40
+ let(:context_user) { FactoryBot.create(:user, :admin) }
41
+ let(:data) { result['data']['assignAnsibleRoles']['host'] }
42
+
43
+ it 'reorderes ansible roles' do
44
+ assert_empty result['errors']
45
+ assert_not_nil data
46
+ assert_empty result['data']['assignAnsibleRoles']['errors']
47
+
48
+ assert_equal([role3, role2, role1].map { |role| Foreman::GlobalId.for role }, data['ownAnsibleRoles']['nodes'].map { |node| node['id'] })
49
+ end
50
+ end
51
+
52
+ context 'with edit permission' do
53
+ let(:context_user) do
54
+ setup_user('edit', 'hosts') do |user|
55
+ user.roles << Role.find_by(name: 'Viewer')
56
+ end
57
+ end
58
+ let(:data) { result['data']['assignAnsibleRoles']['host'] }
59
+ let(:variables) { { id: Foreman::GlobalId.for(host), ansibleRoleIds: [role3.id, role2.id] } }
60
+
61
+ before do
62
+ Location.current = tax_location
63
+ Organization.current = organization
64
+ end
65
+
66
+ it 'reorderes ansible roles' do
67
+ assert_empty result['errors']
68
+ assert_not_nil data
69
+ assert_empty result['data']['assignAnsibleRoles']['errors']
70
+
71
+ assert_equal([role3, role2].map { |role| Foreman::GlobalId.for role }, data['ownAnsibleRoles']['nodes'].map { |node| node['id'] })
72
+ end
73
+ end
74
+
75
+ context 'with view only permissions' do
76
+ let(:context_user) do
77
+ setup_user('show', 'hosts') do |user|
78
+ user.roles << Role.find_by(name: 'Viewer')
79
+ end
80
+ end
81
+
82
+ before do
83
+ Location.current = tax_location
84
+ Organization.current = organization
85
+ end
86
+
87
+ test 'cannot create a host' do
88
+ expected_error = 'Unauthorized. You do not have the required permission edit_hosts.'
89
+
90
+ assert_not_empty result['errors']
91
+ assert_includes result['errors'].map { |e| e['message'] }, expected_error
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,35 @@
1
+ require 'test_plugin_helper'
2
+
3
+ module Queries
4
+ class AnsibleRolesQueryTest < GraphQLQueryTestCase
5
+ let(:query) do
6
+ <<-GRAPHQL
7
+ query {
8
+ ansibleRoles {
9
+ totalCount
10
+ nodes {
11
+ id
12
+ name
13
+ }
14
+ }
15
+ }
16
+ GRAPHQL
17
+ end
18
+
19
+ let(:data) { result['data']['ansibleRoles'] }
20
+
21
+ setup do
22
+ FactoryBot.create_list(:ansible_role, 2)
23
+ end
24
+
25
+ test 'should fetch Ansible roles' do
26
+ assert_empty result['errors']
27
+
28
+ expected_count = AnsibleRole.count
29
+
30
+ assert_not_equal 0, expected_count
31
+ assert_equal expected_count, data['totalCount']
32
+ assert_equal expected_count, data['nodes'].count
33
+ end
34
+ end
35
+ end
@@ -62,4 +62,12 @@ class HostManagedExtensionsTest < ActiveSupport::TestCase
62
62
  FactoryBot.create(:host_ansible_role, :ansible_role_id => @role1.id, :position => 0, :host_id => host.id)
63
63
  host.all_ansible_roles.must_equal [@role2, @role1]
64
64
  end
65
+
66
+ test 'should find hosts with role2' do
67
+ host1 = FactoryBot.create(:host, :ansible_roles => [@role2])
68
+ host2 = FactoryBot.create(:host, :hostgroup => @hostgroup)
69
+ result = Host::Managed.search_for("ansible_role= #{@role2.name}").pluck(:id)
70
+ assert_include result, host1.id
71
+ assert_include result, host2.id
72
+ end
65
73
  end
@@ -55,4 +55,10 @@ class HostgroupExtensionsTest < ActiveSupport::TestCase
55
55
  @hostgroup.parent = @hostgroup_parent
56
56
  @hostgroup.inherited_and_own_ansible_roles.must_equal [@role2, @role1]
57
57
  end
58
+
59
+ describe '#cloned_ansibe_roles' do
60
+ test 'clone ansible roles from hostgroup parent' do
61
+ @hostgroup_parent.clone.all_ansible_roles.must_equal @hostgroup_parent.all_ansible_roles
62
+ end
63
+ end
58
64
  end
@@ -6,43 +6,17 @@ class AnsibleReportsHelperTest < ActiveSupport::TestCase
6
6
  include ForemanAnsible::AnsibleReportsHelper
7
7
  include ActionView::Helpers::TagHelper
8
8
 
9
- test 'is able to print a string instead of a hash' do
9
+ test 'module message extraction' do
10
10
  log_value = <<-ANSIBLELOG.strip_heredoc
11
- {"_ansible_parsed": true, "_ansible_no_log": false, "changed": false, "results": ["ntp-4.2.8p10-3.fc27.x86_64 providing ntp is already installed"], "rc": 0, "invocation": {"module_args": {"allow_downgrade": false, "name": ["ntp"], "list": null, "disable_gpg_check": false, "conf_file": null, "install_repoquery": true, "state": "installed", "disablerepo": null, "update_cache": false, "enablerepo": null, "exclude": null, "security": false, "validate_certs": true, "installroot": "/", "skip_broken": false}}, "msg": ""}
11
+ {"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": "package"}
12
12
  ANSIBLELOG
13
13
  message = FactoryBot.build(:message)
14
14
  message.value = log_value
15
15
  log = FactoryBot.build(:log)
16
16
  log.message = message
17
17
  assert_match(
18
- /ntp-4.2.8p10-3.fc27.x86_64 providing ntp is already installed/,
19
- module_invocations(parsed_message_json(log)).to_s
20
- )
21
- end
22
-
23
- test 'pretty print is able to print a hash' do
24
- hash = {
25
- 'allow_downgrade' => false,
26
- 'name' => ['ntp'],
27
- 'list' => nil,
28
- 'disable_gpg_check' => false,
29
- 'conf_file' => nil,
30
- 'install_repoquery' => true,
31
- 'state' => 'installed',
32
- 'disablerepo' => nil,
33
- 'update_cache' => false,
34
- 'enablerepo' => nil,
35
- 'exclude' => nil,
36
- 'security' => false,
37
- 'validate_certs' => true,
38
- 'installroot' => '/',
39
- 'skip_broken' => false
40
- }
41
- assert_equal(
42
- hash,
43
- remove_keys(
44
- hash
45
- )
18
+ /Nothing to do/,
19
+ ansible_module_message(log).to_s
46
20
  )
47
21
  end
48
22
  end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_plugin_helper'
4
+
5
+ module ForemanAnsible
6
+ class OverrideResolverTest < ActiveSupport::TestCase
7
+ test 'should return no overrides when no roles assigned to host' do
8
+ assert_empty OverrideResolver.new(FactoryBot.build(:host)).overrides
9
+ end
10
+
11
+ test 'should return overrides for host' do
12
+ first_role = FactoryBot.create(:ansible_role)
13
+ first_var = FactoryBot.create(:ansible_variable, :override => true, :ansible_role => first_role)
14
+ second_role = FactoryBot.create(:ansible_role)
15
+ second_var = FactoryBot.create(:ansible_variable, :override => true, :ansible_role => second_role)
16
+ host = FactoryBot.create(:host, :ansible_roles => [first_role])
17
+ another_host = FactoryBot.create(:host)
18
+
19
+ FactoryBot.create(:lookup_value, :match => "fqdn=#{host.name}", :lookup_key_id => first_var.id)
20
+ FactoryBot.create(:lookup_value, :match => "fqdn=#{another_host.name}", :lookup_key_id => first_var.id)
21
+ FactoryBot.create(:lookup_value, :match => "fqdn=#{host.name}", :lookup_key_id => second_var.id)
22
+
23
+ assert_not_nil OverrideResolver.new(host).resolve(first_var)
24
+ assert_nil OverrideResolver.new(another_host).resolve(first_var)
25
+ assert_nil OverrideResolver.new(host).resolve(second_var)
26
+ end
27
+
28
+ test 'should raise when no host specified' do
29
+ assert_raises Foreman::Exception do
30
+ OverrideResolver.new(nil)
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,59 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { Tabs, Tab, TabTitleText } from '@patternfly/react-core';
4
+ import { useHistory } from 'react-router-dom';
5
+ import SkeletonLoader from 'foremanReact/components/common/SkeletonLoader';
6
+
7
+ import SecondaryTabRoutes from './components/SecondaryTabRoutes';
8
+ import { SECONDARY_TABS } from './constants';
9
+ import './AnsibleHostDetail.scss';
10
+
11
+ const AnsibleHostDetail = ({
12
+ response,
13
+ status,
14
+ router,
15
+ location: { pathname },
16
+ history,
17
+ }) => {
18
+ const hashHistory = useHistory();
19
+ return (
20
+ <SkeletonLoader status={status} skeletonProps={{ count: 5 }}>
21
+ {response?.id && (
22
+ <>
23
+ <Tabs
24
+ onSelect={(evt, subTab) => hashHistory.push(subTab)}
25
+ activeKey={pathname?.split('/')[2]}
26
+ isSecondary
27
+ >
28
+ {SECONDARY_TABS.map(({ key, title }) => (
29
+ <Tab
30
+ key={key}
31
+ eventKey={key}
32
+ title={<TabTitleText>{title}</TabTitleText>}
33
+ />
34
+ ))}
35
+ </Tabs>
36
+ <SecondaryTabRoutes
37
+ response={response}
38
+ router={router}
39
+ history={history}
40
+ />
41
+ </>
42
+ )}
43
+ </SkeletonLoader>
44
+ );
45
+ };
46
+
47
+ AnsibleHostDetail.propTypes = {
48
+ response: PropTypes.object.isRequired,
49
+ status: PropTypes.string.isRequired,
50
+ location: PropTypes.object,
51
+ router: PropTypes.object.isRequired,
52
+ history: PropTypes.object.isRequired,
53
+ };
54
+
55
+ AnsibleHostDetail.defaultProps = {
56
+ location: { pathname: '' },
57
+ };
58
+
59
+ export default AnsibleHostDetail;
@@ -0,0 +1,6 @@
1
+ @import '~@theforeman/vendor/scss/variables';
2
+
3
+ .ansible-host-detail {
4
+ background-color: $pf-color-white;
5
+ padding: 1.8rem;
6
+ }
@@ -0,0 +1,20 @@
1
+ import React from 'react';
2
+ import { render } from '@testing-library/react';
3
+ import '@testing-library/jest-dom';
4
+ import AnsibleHostDetail from './';
5
+
6
+ describe('AnsibleHostDetail', () => {
7
+ it('should show skeleton when loading', () => {
8
+ const { container } = render(
9
+ <AnsibleHostDetail
10
+ status="PENDING"
11
+ response={{ id: 5, name: 'test.example.com' }}
12
+ router={{}}
13
+ history={{}}
14
+ />
15
+ );
16
+ expect(
17
+ container.getElementsByClassName('react-loading-skeleton')
18
+ ).toHaveLength(5);
19
+ });
20
+ });