foreman_ansible 7.0.4 → 8.0.1

Sign up to get free protection for your applications and to get access to all the features.
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/migrate/20200421201839_update_ansible_inv_template_name.rb +1 -5
  24. data/db/seeds.d/90_notification_blueprints.rb +14 -0
  25. data/lib/foreman_ansible/engine.rb +0 -1
  26. data/lib/foreman_ansible/register.rb +9 -3
  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/JobsTab/PreviousJobsTable.js.orig +151 -0
  94. data/webpack/components/AnsibleHostDetail/components/RolesTab/AllRolesModal/AllRolesTable.js +43 -56
  95. data/webpack/components/AnsibleHostDetail/components/RolesTab/AllRolesModal/index.js +1 -1
  96. data/webpack/components/AnsibleHostDetail/components/RolesTab/EditRolesModal/EditRolesForm.js +26 -24
  97. data/webpack/components/AnsibleHostDetail/components/RolesTab/EditRolesModal/index.js +3 -2
  98. data/webpack/components/AnsibleHostDetail/components/RolesTab/RolesTable.js +39 -43
  99. data/webpack/components/AnsibleHostDetail/components/RolesTab/__test__/RolesTab.fixtures.js +30 -0
  100. data/webpack/components/AnsibleRolesAndVariables/AnsibleRolesAndVariables.js +27 -38
  101. data/webpack/components/AnsibleRolesAndVariables/AnsibleRolesAndVariablesActions.js +2 -1
  102. data/webpack/components/AnsibleRolesAndVariables/AnsibleRolesAndVariablesConstants.js +1 -0
  103. data/webpack/components/AnsibleRolesAndVariables/AnsibleRolesAndVariablesSelectors.js +6 -0
  104. data/webpack/components/AnsibleRolesAndVariables/index.js +7 -1
  105. data/webpack/components/AnsibleRolesSwitcher/__tests__/AnsibleRolesSwitcher.test.js +0 -2
  106. data/webpack/components/AnsibleRolesSwitcher/components/AnsibleRole.js +3 -12
  107. data/webpack/components/AnsibleRolesSwitcher/components/AvailableRolesList.js +2 -2
  108. data/webpack/components/AnsibleRolesSwitcher/components/OrderedRolesTooltip.js +11 -12
  109. data/webpack/components/AnsibleRolesSwitcher/components/__snapshots__/AnsibleRole.test.js.snap +6 -20
  110. data/webpack/components/AnsibleRolesSwitcher/components/__snapshots__/AvailableRolesList.test.js.snap +9 -6
  111. data/webpack/components/withLoading.js +7 -12
  112. data/webpack/graphql/queries/allAnsibleRoles.gql +3 -0
  113. data/webpack/graphql/queries/hostAnsibleRoles.gql +3 -0
  114. data/webpack/helpers/pageParamsHelper.js +3 -3
  115. metadata +87 -34
  116. data/app/helpers/foreman_ansible/hosts_helper_extensions.rb +0 -30
  117. data/webpack/helpers/paginationHelper.js +0 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6974b31e097d2cb11b28ed665e3346b9b6b2b08111cc3a7701c062aeb41c6e09
4
- data.tar.gz: 13d80c394e6e050d0b9009e86ce1a8b0f500e65c678e6d7b8596b286ae70489a
3
+ metadata.gz: 3ae2b6a17514b657c6d7fa74889675aff19f214da4f923d8bca2bc02e62a8b45
4
+ data.tar.gz: c51baa9cc8ab0ae30c86e9dcc4353f9696ec4dc999d2a1b37a37d1196f8fdb53
5
5
  SHA512:
6
- metadata.gz: df9ff9f909efd6602b11ac585fe8b3768b3168a869232038d4c92a5b1d2acdf635a9ce0834bfff2385aed1475e63c8361894b06b297eb38ec41adbccf8ea67ba
7
- data.tar.gz: 68dc31b736c3ff6b787bf49fb4d24b1563b042d9845b8e8e122cfc87f50b8db97981add7b8b3ba06f91792c04460ccc0a08c6fc0193cc36fe5daaf6bba0e6e80
6
+ metadata.gz: 21cd91f053a0f88fd02e8b312bba23da2b3d0d8f28b578f8a14c6b66294198c336cbdf6eb69ea31c3f75e762c503b36710629870fce900a772ddfbe1fcb60fd3
7
+ data.tar.gz: 373026d1e7cfea4c30f715dfbb409ce6159081cc321fe315c35c89de5bc3a9b132b1e317b192caab40f24238e22cbfa331cec5d4ef26e3f19de56cf5b901e6fa
@@ -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
@@ -1,9 +1,5 @@
1
1
  class UpdateAnsibleInvTemplateName < ActiveRecord::Migration[5.2]
2
2
  def up
3
- Setting.where(:name => 'ansible_inventory_template').update_all(:default => 'Ansible - Ansible Inventory')
4
- end
5
-
6
- def down
7
- Setting.where(:name => 'ansible_inventory_template').update_all(:default => 'Ansible Inventory')
3
+ # Setting defaults are updated automatically now
8
4
  end
9
5
  end