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.
- checksums.yaml +4 -4
- data/app/controllers/api/v2/ansible_playbooks_controller.rb +66 -0
- data/app/controllers/foreman_ansible/api/v2/hostgroups_controller_extensions.rb +5 -1
- data/app/controllers/foreman_ansible/api/v2/hosts_controller_extensions.rb +3 -1
- data/app/graphql/presenters/ansible_role_presenter.rb +4 -0
- data/app/graphql/types/ansible_role.rb +1 -0
- data/app/graphql/types/inherited_ansible_role.rb +4 -0
- data/app/helpers/foreman_ansible/hosts_helper.rb +19 -0
- data/app/jobs/sync_playbooks.rb +25 -0
- data/app/lib/proxy_api/ansible.rb +13 -0
- data/app/services/foreman_ansible/import_playbooks_error_notification.rb +38 -0
- data/app/services/foreman_ansible/import_playbooks_success_notification.rb +33 -0
- data/app/services/foreman_ansible/playbooks_importer.rb +73 -0
- data/app/services/foreman_ansible/proxy_api.rb +3 -4
- data/app/services/foreman_ansible/variables_importer.rb +9 -9
- data/app/views/ansible_roles/index.html.erb +2 -0
- data/app/views/api/v2/ansible_playbooks/sync.json.rabl +5 -0
- data/app/views/api/v2/hostgroups/ansible_roles.json.rabl +9 -1
- data/app/views/api/v2/hosts/ansible_roles.json.rabl +9 -1
- data/app/views/foreman_ansible/job_templates/ansible_roles_-_install_from_git.erb +4 -1
- data/app/views/foreman_ansible/job_templates/ansible_windows_updates.erb +160 -0
- data/config/routes.rb +10 -3
- data/db/migrate/20200421201839_update_ansible_inv_template_name.rb +1 -5
- data/db/seeds.d/90_notification_blueprints.rb +14 -0
- data/lib/foreman_ansible/engine.rb +0 -1
- data/lib/foreman_ansible/register.rb +9 -3
- data/lib/foreman_ansible/version.rb +1 -1
- data/locale/action_names.rb +4 -3
- data/locale/ca/foreman_ansible.edit.po +1162 -0
- data/locale/ca/foreman_ansible.po +360 -45
- data/{webpack/components/withPagination.js → locale/ca/foreman_ansible.po.time_stamp} +0 -0
- data/locale/cs_CZ/foreman_ansible.edit.po +1207 -0
- data/locale/cs_CZ/foreman_ansible.po +372 -57
- data/locale/cs_CZ/foreman_ansible.po.time_stamp +0 -0
- data/locale/de/foreman_ansible.edit.po +1148 -0
- data/locale/de/foreman_ansible.po +355 -40
- data/locale/de/foreman_ansible.po.time_stamp +0 -0
- data/locale/en/foreman_ansible.edit.po +1146 -0
- data/locale/en/foreman_ansible.po +355 -40
- data/locale/en/foreman_ansible.po.time_stamp +0 -0
- data/locale/en_GB/foreman_ansible.edit.po +1155 -0
- data/locale/en_GB/foreman_ansible.po +357 -42
- data/locale/en_GB/foreman_ansible.po.time_stamp +0 -0
- data/locale/es/foreman_ansible.edit.po +1148 -0
- data/locale/es/foreman_ansible.po +355 -40
- data/locale/es/foreman_ansible.po.time_stamp +0 -0
- data/locale/foreman_ansible.pot +767 -263
- data/locale/fr/foreman_ansible.edit.po +1148 -0
- data/locale/fr/foreman_ansible.po +355 -40
- data/locale/fr/foreman_ansible.po.time_stamp +0 -0
- data/locale/gl/foreman_ansible.edit.po +1156 -0
- data/locale/gl/foreman_ansible.po +358 -43
- data/locale/gl/foreman_ansible.po.time_stamp +0 -0
- data/locale/it/foreman_ansible.edit.po +1148 -0
- data/locale/it/foreman_ansible.po +355 -40
- data/locale/it/foreman_ansible.po.time_stamp +0 -0
- data/locale/ja/foreman_ansible.edit.po +1148 -0
- data/locale/ja/foreman_ansible.po +355 -40
- data/locale/ja/foreman_ansible.po.time_stamp +0 -0
- data/locale/ko/foreman_ansible.edit.po +1148 -0
- data/locale/ko/foreman_ansible.po +355 -40
- data/locale/ko/foreman_ansible.po.time_stamp +0 -0
- data/locale/nl_NL/foreman_ansible.edit.po +1168 -0
- data/locale/nl_NL/foreman_ansible.po +359 -44
- data/locale/nl_NL/foreman_ansible.po.time_stamp +0 -0
- data/locale/pl/foreman_ansible.edit.po +1180 -0
- data/locale/pl/foreman_ansible.po +363 -48
- data/locale/pl/foreman_ansible.po.time_stamp +0 -0
- data/locale/pt_BR/foreman_ansible.edit.po +1148 -0
- data/locale/pt_BR/foreman_ansible.po +355 -40
- data/locale/pt_BR/foreman_ansible.po.time_stamp +0 -0
- data/locale/ru/foreman_ansible.edit.po +1149 -0
- data/locale/ru/foreman_ansible.po +355 -40
- data/locale/ru/foreman_ansible.po.time_stamp +0 -0
- data/locale/sv_SE/foreman_ansible.edit.po +1180 -0
- data/locale/sv_SE/foreman_ansible.po +363 -48
- data/locale/sv_SE/foreman_ansible.po.time_stamp +0 -0
- data/locale/zh_CN/foreman_ansible.edit.po +1148 -0
- data/locale/zh_CN/foreman_ansible.po +355 -40
- data/locale/zh_CN/foreman_ansible.po.time_stamp +0 -0
- data/locale/zh_TW/foreman_ansible.edit.po +1148 -0
- data/locale/zh_TW/foreman_ansible.po +355 -40
- data/locale/zh_TW/foreman_ansible.po.time_stamp +0 -0
- data/test/fixtures/playbooks_example_output.json +1 -0
- data/test/fixtures/sample_playbooks.json +10 -0
- data/test/functional/api/v2/ansible_playbooks_controller_test.rb +65 -0
- data/test/functional/hosts_controller_test.rb +2 -2
- data/test/graphql/queries/host_ansible_roles_query_test.rb +61 -0
- data/test/unit/import_playbooks_test.rb +51 -0
- data/test/unit/lib/proxy_api/ansible_test.rb +6 -0
- data/webpack/components/AnsibleHostDetail/components/AnsibleVariableOverrides/AnsibleVariableOverridesTable.js +58 -75
- data/webpack/components/AnsibleHostDetail/components/JobsTab/PreviousJobsTable.js +44 -58
- data/webpack/components/AnsibleHostDetail/components/JobsTab/PreviousJobsTable.js.orig +151 -0
- data/webpack/components/AnsibleHostDetail/components/RolesTab/AllRolesModal/AllRolesTable.js +43 -56
- data/webpack/components/AnsibleHostDetail/components/RolesTab/AllRolesModal/index.js +1 -1
- data/webpack/components/AnsibleHostDetail/components/RolesTab/EditRolesModal/EditRolesForm.js +26 -24
- data/webpack/components/AnsibleHostDetail/components/RolesTab/EditRolesModal/index.js +3 -2
- data/webpack/components/AnsibleHostDetail/components/RolesTab/RolesTable.js +39 -43
- data/webpack/components/AnsibleHostDetail/components/RolesTab/__test__/RolesTab.fixtures.js +30 -0
- data/webpack/components/AnsibleRolesAndVariables/AnsibleRolesAndVariables.js +27 -38
- data/webpack/components/AnsibleRolesAndVariables/AnsibleRolesAndVariablesActions.js +2 -1
- data/webpack/components/AnsibleRolesAndVariables/AnsibleRolesAndVariablesConstants.js +1 -0
- data/webpack/components/AnsibleRolesAndVariables/AnsibleRolesAndVariablesSelectors.js +6 -0
- data/webpack/components/AnsibleRolesAndVariables/index.js +7 -1
- data/webpack/components/AnsibleRolesSwitcher/__tests__/AnsibleRolesSwitcher.test.js +0 -2
- data/webpack/components/AnsibleRolesSwitcher/components/AnsibleRole.js +3 -12
- data/webpack/components/AnsibleRolesSwitcher/components/AvailableRolesList.js +2 -2
- data/webpack/components/AnsibleRolesSwitcher/components/OrderedRolesTooltip.js +11 -12
- data/webpack/components/AnsibleRolesSwitcher/components/__snapshots__/AnsibleRole.test.js.snap +6 -20
- data/webpack/components/AnsibleRolesSwitcher/components/__snapshots__/AvailableRolesList.test.js.snap +9 -6
- data/webpack/components/withLoading.js +7 -12
- data/webpack/graphql/queries/allAnsibleRoles.gql +3 -0
- data/webpack/graphql/queries/hostAnsibleRoles.gql +3 -0
- data/webpack/helpers/pageParamsHelper.js +3 -3
- metadata +87 -34
- data/app/helpers/foreman_ansible/hosts_helper_extensions.rb +0 -30
- data/webpack/helpers/paginationHelper.js +0 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3ae2b6a17514b657c6d7fa74889675aff19f214da4f923d8bca2bc02e62a8b45
|
4
|
+
data.tar.gz: c51baa9cc8ab0ae30c86e9dcc4353f9696ec4dc999d2a1b37a37d1196f8fdb53
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
@
|
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
|
-
@
|
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',
|
@@ -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
|
-
|
15
|
+
::ProxyAPI::Ansible.new(:url => ansible_proxy.url)
|
16
16
|
end
|
17
17
|
|
18
18
|
def proxy_api
|
19
|
-
|
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
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
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(:
|
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
|
<%
|
@@ -2,4 +2,12 @@
|
|
2
2
|
|
3
3
|
collection @ansible_roles
|
4
4
|
|
5
|
-
|
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
|
-
|
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
|
-
-
|
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
|
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
|