foreman_ansible 7.0.3 → 8.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 (117) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/api/v2/ansible_playbooks_controller.rb +66 -0
  3. data/app/controllers/foreman_ansible/api/v2/hostgroups_controller_extensions.rb +5 -1
  4. data/app/controllers/foreman_ansible/api/v2/hosts_controller_extensions.rb +3 -1
  5. data/app/graphql/presenters/ansible_role_presenter.rb +4 -0
  6. data/app/graphql/types/ansible_role.rb +1 -0
  7. data/app/graphql/types/inherited_ansible_role.rb +4 -0
  8. data/app/helpers/foreman_ansible/hosts_helper.rb +19 -0
  9. data/app/jobs/sync_playbooks.rb +25 -0
  10. data/app/lib/proxy_api/ansible.rb +13 -0
  11. data/app/services/foreman_ansible/import_playbooks_error_notification.rb +38 -0
  12. data/app/services/foreman_ansible/import_playbooks_success_notification.rb +33 -0
  13. data/app/services/foreman_ansible/playbooks_importer.rb +73 -0
  14. data/app/services/foreman_ansible/proxy_api.rb +3 -4
  15. data/app/services/foreman_ansible/variables_importer.rb +9 -9
  16. data/app/views/ansible_roles/index.html.erb +2 -0
  17. data/app/views/api/v2/ansible_playbooks/sync.json.rabl +5 -0
  18. data/app/views/api/v2/hostgroups/ansible_roles.json.rabl +9 -1
  19. data/app/views/api/v2/hosts/ansible_roles.json.rabl +9 -1
  20. data/app/views/foreman_ansible/job_templates/ansible_roles_-_install_from_git.erb +4 -1
  21. data/app/views/foreman_ansible/job_templates/ansible_windows_updates.erb +160 -0
  22. data/config/routes.rb +10 -3
  23. data/db/seeds.d/90_notification_blueprints.rb +14 -0
  24. data/lib/foreman_ansible/engine.rb +0 -1
  25. data/lib/foreman_ansible/register.rb +9 -3
  26. data/lib/foreman_ansible/remote_execution.rb +0 -6
  27. data/lib/foreman_ansible/version.rb +1 -1
  28. data/locale/action_names.rb +4 -3
  29. data/locale/ca/foreman_ansible.edit.po +1162 -0
  30. data/locale/ca/foreman_ansible.po +360 -45
  31. data/{webpack/components/withPagination.js → locale/ca/foreman_ansible.po.time_stamp} +0 -0
  32. data/locale/cs_CZ/foreman_ansible.edit.po +1207 -0
  33. data/locale/cs_CZ/foreman_ansible.po +372 -57
  34. data/locale/cs_CZ/foreman_ansible.po.time_stamp +0 -0
  35. data/locale/de/foreman_ansible.edit.po +1148 -0
  36. data/locale/de/foreman_ansible.po +355 -40
  37. data/locale/de/foreman_ansible.po.time_stamp +0 -0
  38. data/locale/en/foreman_ansible.edit.po +1146 -0
  39. data/locale/en/foreman_ansible.po +355 -40
  40. data/locale/en/foreman_ansible.po.time_stamp +0 -0
  41. data/locale/en_GB/foreman_ansible.edit.po +1155 -0
  42. data/locale/en_GB/foreman_ansible.po +357 -42
  43. data/locale/en_GB/foreman_ansible.po.time_stamp +0 -0
  44. data/locale/es/foreman_ansible.edit.po +1148 -0
  45. data/locale/es/foreman_ansible.po +355 -40
  46. data/locale/es/foreman_ansible.po.time_stamp +0 -0
  47. data/locale/foreman_ansible.pot +767 -263
  48. data/locale/fr/foreman_ansible.edit.po +1148 -0
  49. data/locale/fr/foreman_ansible.po +355 -40
  50. data/locale/fr/foreman_ansible.po.time_stamp +0 -0
  51. data/locale/gl/foreman_ansible.edit.po +1156 -0
  52. data/locale/gl/foreman_ansible.po +358 -43
  53. data/locale/gl/foreman_ansible.po.time_stamp +0 -0
  54. data/locale/it/foreman_ansible.edit.po +1148 -0
  55. data/locale/it/foreman_ansible.po +355 -40
  56. data/locale/it/foreman_ansible.po.time_stamp +0 -0
  57. data/locale/ja/foreman_ansible.edit.po +1148 -0
  58. data/locale/ja/foreman_ansible.po +355 -40
  59. data/locale/ja/foreman_ansible.po.time_stamp +0 -0
  60. data/locale/ko/foreman_ansible.edit.po +1148 -0
  61. data/locale/ko/foreman_ansible.po +355 -40
  62. data/locale/ko/foreman_ansible.po.time_stamp +0 -0
  63. data/locale/nl_NL/foreman_ansible.edit.po +1168 -0
  64. data/locale/nl_NL/foreman_ansible.po +359 -44
  65. data/locale/nl_NL/foreman_ansible.po.time_stamp +0 -0
  66. data/locale/pl/foreman_ansible.edit.po +1180 -0
  67. data/locale/pl/foreman_ansible.po +363 -48
  68. data/locale/pl/foreman_ansible.po.time_stamp +0 -0
  69. data/locale/pt_BR/foreman_ansible.edit.po +1148 -0
  70. data/locale/pt_BR/foreman_ansible.po +355 -40
  71. data/locale/pt_BR/foreman_ansible.po.time_stamp +0 -0
  72. data/locale/ru/foreman_ansible.edit.po +1149 -0
  73. data/locale/ru/foreman_ansible.po +355 -40
  74. data/locale/ru/foreman_ansible.po.time_stamp +0 -0
  75. data/locale/sv_SE/foreman_ansible.edit.po +1180 -0
  76. data/locale/sv_SE/foreman_ansible.po +363 -48
  77. data/locale/sv_SE/foreman_ansible.po.time_stamp +0 -0
  78. data/locale/zh_CN/foreman_ansible.edit.po +1148 -0
  79. data/locale/zh_CN/foreman_ansible.po +355 -40
  80. data/locale/zh_CN/foreman_ansible.po.time_stamp +0 -0
  81. data/locale/zh_TW/foreman_ansible.edit.po +1148 -0
  82. data/locale/zh_TW/foreman_ansible.po +355 -40
  83. data/locale/zh_TW/foreman_ansible.po.time_stamp +0 -0
  84. data/test/fixtures/playbooks_example_output.json +1 -0
  85. data/test/fixtures/sample_playbooks.json +10 -0
  86. data/test/functional/api/v2/ansible_playbooks_controller_test.rb +65 -0
  87. data/test/functional/hosts_controller_test.rb +2 -2
  88. data/test/graphql/queries/host_ansible_roles_query_test.rb +61 -0
  89. data/test/unit/import_playbooks_test.rb +51 -0
  90. data/test/unit/lib/proxy_api/ansible_test.rb +6 -0
  91. data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/AnsibleVariableOverridesTable.js +58 -75
  92. data/webpack/components/AnsibleHostDetail/components/JobsTab/PreviousJobsTable.js +44 -58
  93. data/webpack/components/AnsibleHostDetail/components/RolesTab/AllRolesModal/AllRolesTable.js +43 -56
  94. data/webpack/components/AnsibleHostDetail/components/RolesTab/AllRolesModal/index.js +1 -1
  95. data/webpack/components/AnsibleHostDetail/components/RolesTab/EditRolesModal/EditRolesForm.js +22 -22
  96. data/webpack/components/AnsibleHostDetail/components/RolesTab/EditRolesModal/index.js +2 -1
  97. data/webpack/components/AnsibleHostDetail/components/RolesTab/RolesTable.js +39 -43
  98. data/webpack/components/AnsibleHostDetail/components/RolesTab/__test__/RolesTab.fixtures.js +30 -0
  99. data/webpack/components/AnsibleRolesAndVariables/AnsibleRolesAndVariables.js +27 -38
  100. data/webpack/components/AnsibleRolesAndVariables/AnsibleRolesAndVariablesActions.js +2 -1
  101. data/webpack/components/AnsibleRolesAndVariables/AnsibleRolesAndVariablesConstants.js +1 -0
  102. data/webpack/components/AnsibleRolesAndVariables/AnsibleRolesAndVariablesSelectors.js +6 -0
  103. data/webpack/components/AnsibleRolesAndVariables/index.js +7 -1
  104. data/webpack/components/AnsibleRolesSwitcher/__tests__/AnsibleRolesSwitcher.test.js +0 -2
  105. data/webpack/components/AnsibleRolesSwitcher/components/AnsibleRole.js +3 -12
  106. data/webpack/components/AnsibleRolesSwitcher/components/AvailableRolesList.js +2 -2
  107. data/webpack/components/AnsibleRolesSwitcher/components/OrderedRolesTooltip.js +11 -12
  108. data/webpack/components/AnsibleRolesSwitcher/components/__snapshots__/AnsibleRole.test.js.snap +6 -20
  109. data/webpack/components/AnsibleRolesSwitcher/components/__snapshots__/AvailableRolesList.test.js.snap +9 -6
  110. data/webpack/components/withLoading.js +7 -12
  111. data/webpack/graphql/queries/allAnsibleRoles.gql +3 -0
  112. data/webpack/graphql/queries/hostAnsibleRoles.gql +3 -0
  113. data/webpack/helpers/pageParamsHelper.js +3 -3
  114. metadata +57 -6
  115. data/app/helpers/foreman_ansible/hosts_helper_extensions.rb +0 -30
  116. data/app/views/foreman_ansible/job_templates/configure_cloud_connector_-_ansible_default.erb +0 -37
  117. data/webpack/helpers/paginationHelper.js +0 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 7a8c4f263992d803c31386bc301ce1e032c680ada845db8bf07e71162c003384
4
- data.tar.gz: 628c7b6952499ef5dfe912370bec8d3e936926a004acf5b7693d7706fe461ea9
3
+ metadata.gz: 47b85dc5fe69b87ea05703f75380a2387deb64674755903ec03c3cb55dcc3bdb
4
+ data.tar.gz: ece7521c8b3f6da727abd6abad8be7384701a1598f6601dc5baf0b1f579931df
5
5
  SHA512:
6
- metadata.gz: '09f3fd7b961edcb7abee1f6dd6de4fbf0bb80130668c39724d6c8658c2ca1570109ab6b7d172d2f9b963c6e4e5aebaa3280ebf199f9fe5ff86580ab1d8f06c7e'
7
- data.tar.gz: 924b85ee81562a848c07417fd0788520c000292901f63ef45f8adbfc99ec32dae21858589c83688f461d19e49a4c6491801d53f611032a1c291ba941e9068133
6
+ metadata.gz: f72ecf4fc7949815787bd4b93c1da3ee7df5baf2c2266950ddc6bd01b701ae38559896ae45b11995fa3574c84f041e01d7caf41ba1e18ba4b9c0c2cea397b752
7
+ data.tar.gz: cee830b49ad5443db42d34e6e0910e0783b2d116f91b02745a8556e184b4e66535c3620b4e9c15b4329a35286d117df51a0bba3e7d7ae818ab1016a63636e61c
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Api
4
+ module V2
5
+ # API controller for Ansible Roles
6
+ class AnsiblePlaybooksController < ::Api::V2::BaseController
7
+ include ::Api::Version2
8
+ include ::ForemanAnsible::ProxyAPI
9
+
10
+ before_action :find_proxy, only: [:fetch, :sync]
11
+
12
+ resource_description do
13
+ api_version 'v2'
14
+ api_base_url '/ansible/api'
15
+ end
16
+
17
+ api :PUT, '/ansible_playbooks/sync', N_('Sync Ansible playbooks')
18
+ param :proxy_id, :identifier, :required => true, :desc => N_('Smart Proxy to sync from')
19
+ param :playbooks_names, Array, N_('Ansible playbooks names to be synced')
20
+ def sync
21
+ @task = plan_ansible_sync(@proxy.id, playbooks_names)
22
+ end
23
+
24
+ api :GET, '/ansible_playbooks/fetch', N_('Fetch Ansible playbooks available to be synced')
25
+ param :proxy_id, :identifier, N_('Smart Proxy to fetch from'),
26
+ :required => true
27
+ def fetch
28
+ fetched = fetch_playbooks_names
29
+ render :json => { :results => { :playbooks_names => fetched } }
30
+ end
31
+
32
+ def action_permission
33
+ case params[:action]
34
+ when 'sync', 'fetch'
35
+ :import
36
+ else
37
+ super
38
+ end
39
+ end
40
+
41
+ def plan_ansible_sync(proxy_id, playbooks_names)
42
+ ForemanTasks.async_task(ImportPlaybooksJob::Async::SyncPlaybooks, proxy_id, playbooks_names)
43
+ end
44
+
45
+ private
46
+
47
+ def find_proxy
48
+ unless params[:proxy_id]
49
+ msg = _('Smart proxy id is required')
50
+ render_error('custom_error', :status => :unprocessable_entity, :locals => { :message => msg })
51
+ return false
52
+ end
53
+ @proxy = SmartProxy.find(params[:proxy_id])
54
+ end
55
+
56
+ def fetch_playbooks_names
57
+ proxy_api = find_proxy_api(@proxy)
58
+ proxy_api.playbooks_names if @proxy
59
+ end
60
+
61
+ def playbooks_names
62
+ params.fetch(:playbooks_names, [])
63
+ end
64
+ end
65
+ end
66
+ end
@@ -46,7 +46,11 @@ module ForemanAnsible
46
46
  find_resource
47
47
  return unless @hostgroup
48
48
 
49
- @ansible_roles = @hostgroup.all_ansible_roles
49
+ @inherited_ansible_roles = @hostgroup.inherited_ansible_roles
50
+ @directly_assigned_roles = @hostgroup.ansible_roles
51
+ @ansible_roles = (
52
+ @directly_assigned_roles + @inherited_ansible_roles + @hostgroup.host_ansible_roles
53
+ ).uniq
50
54
  end
51
55
 
52
56
  api :POST, '/hostgroups/:id/assign_ansible_roles',
@@ -49,7 +49,9 @@ module ForemanAnsible
49
49
  def ansible_roles
50
50
  return unless @host
51
51
 
52
- @ansible_roles = @host.all_ansible_roles
52
+ @inherited_ansible_roles = @host.inherited_ansible_roles
53
+ @directly_assigned_roles = @host.ansible_roles
54
+ @ansible_roles = (@directly_assigned_roles + @inherited_ansible_roles).uniq
53
55
  end
54
56
 
55
57
  api :POST, '/hosts/:id/assign_ansible_roles',
@@ -4,6 +4,10 @@ module Presenters
4
4
 
5
5
  delegate :id, :name, :association, :to => :ansible_role
6
6
 
7
+ def self.graphql_type
8
+ 'Types::InheritedAnsibleRole'
9
+ end
10
+
7
11
  def initialize(ansible_role, inherited)
8
12
  @ansible_role = ansible_role
9
13
  @inherited = inherited
@@ -6,5 +6,6 @@ module Types
6
6
 
7
7
  field :name, String, :null => false
8
8
  field :path, resolver: Resolvers::AnsibleRole::Path
9
+ has_many :ansible_variables, ::Types::AnsibleVariable
9
10
  end
10
11
  end
@@ -2,6 +2,10 @@ module Types
2
2
  class InheritedAnsibleRole < ::Types::AnsibleRole
3
3
  field :inherited, Boolean, :null => false
4
4
 
5
+ def self.record_for(object)
6
+ object.ansible_role
7
+ end
8
+
5
9
  def object_class
6
10
  object.ansible_role.class
7
11
  end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ForemanAnsible
4
+ module HostsHelper
5
+ def ansible_hosts_multiple_actions
6
+ return [] unless User.current.can?(:create_job_invocations) &&
7
+ User.current.can?(:play_roles_on_host)
8
+
9
+ [{ action: [_('Run all Ansible roles'),
10
+ multiple_play_roles_hosts_path,
11
+ false], priority: 1000 }]
12
+ end
13
+
14
+ def ansible_roles_present?(host)
15
+ host.ansible_roles.present? ||
16
+ host.inherited_ansible_roles.present?
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,25 @@
1
+ module ImportPlaybooksJob
2
+ module Async
3
+ class SyncPlaybooks < ::Actions::EntryAction
4
+ def plan(proxy_id, playbooks_names)
5
+ plan_self(proxy_id: proxy_id, playbooks_names: playbooks_names)
6
+ end
7
+
8
+ def run
9
+ playbooks_importer = ForemanAnsible::PlaybooksImporter.new(proxy)
10
+ output[:result] = playbooks_importer.import_playbooks(playbooks_names)
11
+ ForemanAnsible::ImportPlaybooksSuccessNotification.deliver!(task)
12
+ rescue StandardError => e
13
+ ForemanAnsible::ImportPlaybooksErrorNotification.new(e, task).deliver!
14
+ end
15
+
16
+ def proxy
17
+ SmartProxy.find(input[:proxy_id])
18
+ end
19
+
20
+ def playbooks_names
21
+ input[:playbooks_names]
22
+ end
23
+ end
24
+ end
25
+ end
@@ -40,5 +40,18 @@ module ProxyAPI
40
40
  raise ProxyException.new(url, e,
41
41
  N_('Unable to get roles/variables from Ansible'))
42
42
  end
43
+
44
+ def playbooks_names
45
+ parse(get('playbooks_names'))
46
+ rescue *PROXY_ERRORS => e
47
+ raise ProxyException.new(url, e, N_('Unable to get playbook\'s names from Ansible'))
48
+ end
49
+
50
+ def playbooks(playbooks_names = [])
51
+ playbooks_names = playbooks_names.join(',')
52
+ parse(get("playbooks/#{playbooks_names}"))
53
+ rescue *PROXY_ERRORS => e
54
+ raise ProxyException.new(url, e, N_('Unable to get playbooks from Ansible'))
55
+ end
43
56
  end
44
57
  end
@@ -0,0 +1,38 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ForemanAnsible
4
+ class ImportPlaybooksErrorNotification < ::UINotifications::Base
5
+ private
6
+
7
+ def initialize(error, task)
8
+ @job_error = error
9
+ @subject = task
10
+ end
11
+
12
+ def create
13
+ ::Notification.create!(
14
+ :audience => Notification::AUDIENCE_USER,
15
+ :notification_blueprint => blueprint,
16
+ :initiator => initiator,
17
+ :message => message,
18
+ :subject => subject,
19
+ :actions => {
20
+ :links => links
21
+ }
22
+ )
23
+ end
24
+
25
+ def blueprint
26
+ name = 'Sync_playbooks_failed'
27
+ @blueprint ||= NotificationBlueprint.unscoped.find_by(:name => name)
28
+ end
29
+
30
+ def message
31
+ _("Failed to import playbooks Due to: #{@job_error}")
32
+ end
33
+
34
+ def links
35
+ [{ :href => Rails.application.routes.url_helpers.foreman_tasks_task_path(:id => subject.id), :title => N_('Task Details') }]
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ForemanAnsible
4
+ class ImportPlaybooksSuccessNotification < ::UINotifications::Base
5
+ private
6
+
7
+ def create
8
+ ::Notification.create!(
9
+ :audience => Notification::AUDIENCE_USER,
10
+ :notification_blueprint => blueprint,
11
+ :initiator => initiator,
12
+ :message => message,
13
+ :subject => subject,
14
+ :actions => {
15
+ :links => links
16
+ }
17
+ )
18
+ end
19
+
20
+ def blueprint
21
+ name = 'Sync_playbooks_successfully'
22
+ @blueprint ||= NotificationBlueprint.unscoped.find_by(:name => name)
23
+ end
24
+
25
+ def message
26
+ blueprint.message
27
+ end
28
+
29
+ def links
30
+ [{ :href => Rails.application.routes.url_helpers.foreman_tasks_task_path(:id => subject.id), :title => N_('Task Details') }]
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,73 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ForemanAnsible
4
+ # Imports playbooks from smart proxy
5
+ class PlaybooksImporter
6
+ include ::ForemanAnsible::ProxyAPI
7
+ delegate :playbooks, :playbooks_names, :to => :proxy_api
8
+
9
+ def initialize(proxy = nil)
10
+ @ansible_proxy = proxy
11
+ end
12
+
13
+ def import_playbooks(playbooks_names)
14
+ playbooks = playbooks(playbooks_names)
15
+ result = { created: {}, updated: {} }
16
+ playbooks.each do |playbook|
17
+ parsed_playbook = parse_playbook playbook
18
+ job_template_id = JobTemplate.where(name: parsed_playbook[:name]).pick(:id)
19
+ if job_template_id.present?
20
+ updated = update_job_template(job_template_id, parsed_playbook)
21
+ result[:updated].merge!(updated) unless updated.nil?
22
+ else
23
+ result[:created].merge!(create_job_template(parsed_playbook))
24
+ end
25
+ end
26
+ result
27
+ end
28
+
29
+ def parse_playbook(playbook)
30
+ content = playbook['playbooks_content']
31
+ {
32
+ name: playbook['name'],
33
+ playbook_content: metadata(playbook['name']) + content,
34
+ vars: get_vars(content)
35
+ }
36
+ end
37
+
38
+ def metadata(playbook_name)
39
+ <<~END_HEREDOC
40
+ <%#
41
+ name: #{playbook_name}
42
+ snippet: false
43
+ job_category: Ansible Playbook - Imported
44
+ provider_type: Ansible
45
+ kind: job_template
46
+ model: JobTemplate
47
+ %>
48
+ END_HEREDOC
49
+ end
50
+
51
+ def get_vars(playbook_content)
52
+ YAML.safe_load(playbook_content).map { |play| play['vars'] }
53
+ end
54
+
55
+ def create_job_template(playbook)
56
+ job_template = JobTemplate.create(name: playbook[:name], template: playbook[:playbook_content], job_category: 'Ansible Playbook - Imported', provider_type: 'Ansible')
57
+ # TODO: Add support for creating template inputs
58
+ job_template.organizations = Organization.unscoped.all
59
+ job_template.locations = Location.unscoped.all
60
+ job_template.save
61
+ { job_template.id => job_template.name }
62
+ end
63
+
64
+ def update_job_template(job_template_id, playbook)
65
+ # TODO: Add support for updating template inputs
66
+ inputs = []
67
+ job_template = JobTemplate.find(job_template_id)
68
+ should_update = !playbook[:playbook_content].eql?(job_template.template)
69
+ job_template.template = playbook[:playbook_content] if should_update
70
+ { job_template.id => job_template.name } unless inputs.empty? && !should_update
71
+ end
72
+ end
73
+ end
@@ -8,16 +8,15 @@ module ForemanAnsible
8
8
  included do
9
9
  attr_reader :ansible_proxy
10
10
 
11
- def find_proxy_api
11
+ def find_proxy_api(ansible_proxy)
12
12
  if ansible_proxy.blank?
13
13
  raise ::Foreman::Exception.new(N_('Proxy not found'))
14
14
  end
15
- @proxy_api = ::ProxyAPI::Ansible.new(:url => ansible_proxy.url)
15
+ ::ProxyAPI::Ansible.new(:url => ansible_proxy.url)
16
16
  end
17
17
 
18
18
  def proxy_api
19
- return @proxy_api if @proxy_api
20
- find_proxy_api
19
+ @proxy_api ||= find_proxy_api(ansible_proxy)
21
20
  end
22
21
  end
23
22
  end
@@ -31,14 +31,12 @@ module ForemanAnsible
31
31
  end
32
32
 
33
33
  def import_variables(role_variables, new_roles)
34
- detect_changes(
35
- role_variables.map do |role_name, variables|
36
- next if variables.blank?
37
- role = import_new_role(role_name, new_roles)
38
- next if role.blank?
39
- initialize_variables(variables, role)
40
- end.select(&:present?).flatten.compact
41
- )
34
+ detect_changes(role_variables.map do |role_name, variables|
35
+ next if variables.blank?
36
+ role = import_new_role(role_name, new_roles)
37
+ next if role.blank?
38
+ initialize_variables(variables, role)
39
+ end.select(&:present?).flatten.compact)
42
40
  end
43
41
 
44
42
  def import_variables_roles(roles)
@@ -96,7 +94,9 @@ module ForemanAnsible
96
94
  persisted, changes[:new] = imported.partition { |var| var.id.present? }
97
95
  changed, _old = persisted.partition(&:changed?)
98
96
  _overriden, changes[:update] = changed.partition(&:override?)
99
- changes[:obsolete] = AnsibleVariable.where.not(:id => persisted.pluck(:id), :imported => false)
97
+ changes[:obsolete] = AnsibleVariable.where.not(id: persisted.pluck(:id)).
98
+ where.not(imported: false)
99
+
100
100
  changes
101
101
  end
102
102
 
@@ -9,6 +9,7 @@
9
9
  <th class="col-md-6"><%= sort :name, :as => s_("Role|Name") %></th>
10
10
  <th class="col-md-2"><%= _("Hostgroups") %></th>
11
11
  <th class="col-md-2"><%= _("Hosts") %></th>
12
+ <th class="col-md-2"><%= _("Variables") %></th>
12
13
  <th class="col-md-2"><%= sort :updated_at, :as => _("Imported at") %></th>
13
14
  <th class="col-md-2"><%= _("Actions") %></th>
14
15
  </tr>
@@ -19,6 +20,7 @@
19
20
  <td class="ellipsis"><%= role.name %></td>
20
21
  <td class="ellipsis"><%= role.hostgroups.count %></td>
21
22
  <td class="ellipsis"><%= link_to role.hosts.count, hosts_path(:search => "ansible_role = #{role.name}")%></td>
23
+ <td class="ellipsis"><%= link_to(role.ansible_variables.count, ansible_variables_path(:search => "ansible_role = #{role}")) %></td>
22
24
  <td class="ellipsis"><%= import_time role %></td>
23
25
  <td>
24
26
  <%
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ object @task
4
+
5
+ attributes :id, :action, :state, :result, :start_at
@@ -2,4 +2,12 @@
2
2
 
3
3
  collection @ansible_roles
4
4
 
5
- attributes :id, :name, :created_at, :updated_at
5
+ extends 'api/v2/ansible_roles/show'
6
+
7
+ node :inherited do |role|
8
+ @inherited_ansible_roles.include?(role)
9
+ end
10
+
11
+ node :directly_assigned do |role|
12
+ @directly_assigned_roles.include?(role)
13
+ end
@@ -2,4 +2,12 @@
2
2
 
3
3
  collection @ansible_roles
4
4
 
5
- attributes :id, :name, :created_at, :updated_at
5
+ extends 'api/v2/ansible_roles/show'
6
+
7
+ node :inherited do |role|
8
+ @inherited_ansible_roles.include?(role)
9
+ end
10
+
11
+ node :directly_assigned do |role|
12
+ @directly_assigned_roles.include?(role)
13
+ end
@@ -23,6 +23,9 @@ model: JobTemplate
23
23
  ---
24
24
  - hosts: all
25
25
  tasks:
26
- - command: git clone <%= input('git_repository') %> <%= input('location') %>
26
+ - git:
27
+ repo: "<%= input('git_repository') %>"
28
+ dest: "<%= input('location') %>"
27
29
  register: out
30
+
28
31
  - debug: var=out
@@ -0,0 +1,160 @@
1
+ <%#
2
+ name: Manage Windows Updates - Ansible Default
3
+ snippet: false
4
+ template_inputs:
5
+ - name: reject_list
6
+ required: false
7
+ input_type: user
8
+ description: "A list of update titles or KB numbers that can be used to specify
9
+ which updates are to be excluded from installation.\r\nIf an available update
10
+ does match one of the entries, then it is skipped and not installed.\r\nEach entry
11
+ can either be the KB article or Update title as a regex according to the PowerShell
12
+ regex rules."
13
+ advanced: false
14
+ - name: category_names
15
+ required: false
16
+ input_type: user
17
+ description: "A scalar or list of categories to install updates from. To get the
18
+ list of categories, run the module with state=searched. The category must be the
19
+ full category string, but is case insensitive.\r\nSome possible categories are
20
+ Application, Connectors, Critical Updates, Definition Updates, Developer Kits,
21
+ Feature Packs, Guidance, Security Updates, Service Packs, Tools, Update Rollups
22
+ and Updates.\r\n\r\nDefault is '[\"CriticalUpdates\", \"SecurityUpdates\", \"UpdateRollups\"]'"
23
+ advanced: false
24
+ - name: log_path
25
+ required: false
26
+ input_type: user
27
+ description: If set, win_updates will append update progress to the specified file.
28
+ The directory must already exist.
29
+ advanced: false
30
+ - name: reboot
31
+ required: false
32
+ input_type: user
33
+ description: "Ansible will automatically reboot the remote host if it is required
34
+ and continue to install updates after the reboot.\r\nThis can be used instead
35
+ of using a win_reboot task after this one and ensures all updates for that category
36
+ is installed in one go.\r\nAsync does not work when reboot=true.\r\n\r\nDefault
37
+ is 'false'"
38
+ options: "false\r\ntrue"
39
+ advanced: false
40
+ - name: reboot_timeout
41
+ required: false
42
+ input_type: user
43
+ description: "The time in seconds to wait until the host is back online from a reboot.\r\nThis
44
+ is only used if reboot=true and a reboot is required.\r\n\r\nDefault is '1200'"
45
+ advanced: false
46
+ - name: server_selection
47
+ required: false
48
+ input_type: user
49
+ description: "Defines the Windows Update source catalog.\r\ndefault Use the default
50
+ search source. For many systems default is set to the Microsoft Windows Update
51
+ catalog. Systems participating in Windows Server Update Services (WSUS), Systems
52
+ Center Configuration Manager (SCCM), or similar corporate update server environments
53
+ may default to those managed update sources instead of the Windows Update catalog.\r\nmanaged_server
54
+ Use a managed server catalog. For environments utilizing Windows Server Update
55
+ Services (WSUS), Systems Center Configuration Manager (SCCM), or similar corporate
56
+ update servers, this option selects the defined corporate update source.\r\nwindows_update
57
+ Use the Microsoft Windows Update catalog.\r\n\r\nDefault is 'default'"
58
+ options: "default\r\nmanaged_server\r\nwindows_update"
59
+ advanced: false
60
+ - name: state
61
+ required: false
62
+ input_type: user
63
+ description: "Controls whether found updates are downloaded or installed or listed\r\nThis
64
+ module also supports Ansible check mode, which has the same effect as setting
65
+ state=searched\r\n\r\nDefault is 'searched'"
66
+ options: "installed\r\nsearched\r\ndownloaded"
67
+ advanced: false
68
+ - name: use_scheduled_task
69
+ required: false
70
+ input_type: user
71
+ description: "Will not auto elevate the remote process with become and use a scheduled
72
+ task instead.\r\nSet this to true when using this module with async on Server
73
+ 2008, 2008 R2, or Windows 7, or on Server 2008 that is not authenticated with
74
+ basic or credssp.\r\nCan also be set to true on newer hosts where become does
75
+ not work due to further privilege restrictions from the OS defaults."
76
+ options: "false\r\ntrue"
77
+ advanced: false
78
+ - name: whitelist
79
+ required: false
80
+ input_type: user
81
+ description: "A list of update titles or KB numbers that can be used to specify
82
+ which updates are to be searched or installed.\r\nIf an available update does
83
+ not match one of the entries, then it is skipped and not installed.\r\nEach entry
84
+ can either be the KB article or Update title as a regex according to the PowerShell
85
+ regex rules.\r\nThe whitelist is only validated on updates that were found based
86
+ on category_names. It will not force the module to install an update if it was
87
+ not in the category specified."
88
+ advanced: false
89
+ model: JobTemplate
90
+ job_category: Ansible Playbook
91
+ provider_type: Ansible
92
+ kind: job_template
93
+ organizations:
94
+ - My_Organization
95
+ locations:
96
+ - My_Location
97
+ %>
98
+
99
+ - hosts: all
100
+ vars:
101
+ updates: []
102
+ <% if input('state').blank? %>
103
+ state: searched
104
+ <% else %>
105
+ state: <%= input('state') %>
106
+ <% end %>
107
+
108
+ tasks:
109
+ - name: "{{ state | replace('ed','') | capitalize }} Windows Updates"
110
+ win_updates:
111
+ <% unless input('reject_list').blank? %>
112
+ blacklist: <%= input('reject_list') %>
113
+ <% end -%>
114
+ <% if input('category_names').blank? %>
115
+ category_names: ["CriticalUpdates", "SecurityUpdates", "UpdateRollups"]
116
+ <% else %>
117
+ category_names: <%= input('category_names') %>
118
+ <% end %>
119
+ <% unless input('log_path').blank? %>
120
+ log_path: <%= input('log_path') %>
121
+ <% end -%>
122
+ <% if input('reboot').blank? %>
123
+ reboot: false
124
+ <% else %>
125
+ reboot: <%= input('reboot') %>
126
+ <% end %>
127
+ <% if input('reboot_timeout').blank? %>
128
+ reboot_timeout: 1200
129
+ <% else %>
130
+ reboot_timeout: <%= input('reboot_timeout') %>
131
+ <% end -%>
132
+ <% if input('server_selection').blank? %>
133
+ server_selection: default
134
+ <% else %>
135
+ server_selection: <%= input('server_selection') %>
136
+ <% end %>
137
+ state: "{{ state }}"
138
+ <% if input('use_scheduled_task').blank? %>
139
+ use_scheduled_task: false
140
+ <% else %>
141
+ use_scheduled_task: <%= input('use_scheduled_task') %>
142
+ <% end %>
143
+ <% if !input('whitelist').blank? %>
144
+ whitelist: <%= input('whitelist') %>
145
+ <% end -%>
146
+
147
+ register: found_updates
148
+
149
+ - name: "Get all {{ state }} updates"
150
+ set_fact:
151
+ updates: "{{ updates + [ current_update ] }}"
152
+ vars:
153
+ current_update: "{{ item.value.title }}"
154
+ loop: "{{ found_updates.updates | dict2items }}"
155
+ no_log: true
156
+
157
+ - name: "List {{ state }} Windows Updates"
158
+ debug:
159
+ msg: "{{ item }}"
160
+ loop: "{{ updates }}"
data/config/routes.rb CHANGED
@@ -1,9 +1,6 @@
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
-
7
4
  namespace :api, defaults: { format: 'json' } do
8
5
  scope '(:apiv)',
9
6
  :module => :v2,
@@ -85,6 +82,13 @@ Rails.application.routes.draw do
85
82
  end
86
83
  end
87
84
 
85
+ resources :ansible_playbooks, :only => [] do
86
+ collection do
87
+ get :fetch
88
+ put :sync
89
+ end
90
+ end
91
+
88
92
  resources :ansible_variables, :only => [:show, :index, :destroy, :update, :create] do
89
93
  collection do
90
94
  put :import
@@ -106,4 +110,7 @@ Rails.application.routes.draw do
106
110
  end
107
111
  end
108
112
  end
113
+
114
+ match '/ansible/hostgroups' => 'react#index', :via => [:get]
115
+ match '/ansible/hostgroups/*page' => 'react#index', :via => [:get]
109
116
  end