foreman_ansible 7.0.3 → 7.1.0
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/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/views/ansible_roles/index.html.erb +2 -0
- data/app/views/api/v2/ansible_playbooks/sync.json.rabl +5 -0
- data/app/views/foreman_ansible/job_templates/ansible_windows_updates.erb +160 -0
- data/config/routes.rb +7 -0
- data/db/seeds.d/90_notification_blueprints.rb +14 -0
- data/lib/foreman_ansible/register.rb +3 -1
- data/lib/foreman_ansible/version.rb +1 -1
- 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/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/RolesTab/AllRolesModal/AllRolesTable.js +32 -55
- data/webpack/components/AnsibleHostDetail/components/RolesTab/AllRolesModal/index.js +1 -1
- data/webpack/components/AnsibleHostDetail/components/RolesTab/RolesTable.js +29 -42
- 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/components/AvailableRolesList.js +2 -2
- data/webpack/components/AnsibleRolesSwitcher/components/__snapshots__/AvailableRolesList.test.js.snap +9 -6
- data/webpack/helpers/pageParamsHelper.js +3 -3
- metadata +42 -29
- data/webpack/components/AnsibleHostDetail/components/JobsTab/PreviousJobsTable.js.orig +0 -151
- data/webpack/components/withPagination.js +0 -0
- 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: dc6d250a74d7e811600edfd0f78551286d8800644512d4346fd6d5794fe451f3
|
4
|
+
data.tar.gz: f7057a56a7e5fda90510ab9aa13b70b59efa90f07a8e7fb3460140102e36d390
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 99dbfada521e9075ae164312cdf3eecb85e663beca47e558b5d3fd61cde68bb2a632e29f80ca9dcbf5519be3122a15b6c2777f4400b6bd685c84f2488a02baf4
|
7
|
+
data.tar.gz: b8a5fde3a9bb62aaf578b5d08ef24afe6bd9ddc2ac7af33b732a6c8eadfeb077dc92f0db86496975f44d3b9ad0c59acdb176f1722e964de5a4afb7a7c826182c
|
@@ -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
|
@@ -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
|
@@ -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,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
@@ -85,6 +85,13 @@ Rails.application.routes.draw do
|
|
85
85
|
end
|
86
86
|
end
|
87
87
|
|
88
|
+
resources :ansible_playbooks, :only => [] do
|
89
|
+
collection do
|
90
|
+
get :fetch
|
91
|
+
put :sync
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
88
95
|
resources :ansible_variables, :only => [:show, :index, :destroy, :update, :create] do
|
89
96
|
collection do
|
90
97
|
put :import
|
@@ -31,7 +31,21 @@ blueprints = [
|
|
31
31
|
:name => 'Sync_roles_and_variables_failed',
|
32
32
|
:message => 'DYNAMIC',
|
33
33
|
:level => 'error'
|
34
|
+
},
|
35
|
+
{
|
36
|
+
:group => N_('Playbooks'),
|
37
|
+
:name => 'Sync_playbooks_successfully',
|
38
|
+
:message => N_('Import playbooks has finished successfully'),
|
39
|
+
:level => 'success'
|
40
|
+
|
41
|
+
},
|
42
|
+
{
|
43
|
+
:group => N_('Playbooks'),
|
44
|
+
:name => 'Sync_playbooks_failed',
|
45
|
+
:message => 'DYNAMIC',
|
46
|
+
:level => 'error'
|
34
47
|
}
|
35
48
|
|
36
49
|
]
|
50
|
+
|
37
51
|
blueprints.each { |blueprint| UINotifications::Seed.new(blueprint).configure }
|
@@ -158,6 +158,8 @@ Foreman::Plugin.register :foreman_ansible do
|
|
158
158
|
:resource_type => 'Hostgroup'
|
159
159
|
permission :generate_ansible_inventory,
|
160
160
|
{ :'api/v2/ansible_inventories' => [:schedule] }
|
161
|
+
permission :import_ansible_playbooks,
|
162
|
+
{ :'api/v2/ansible_playbooks' => [:sync, :fetch] }
|
161
163
|
end
|
162
164
|
|
163
165
|
role 'Ansible Roles Manager',
|
@@ -167,7 +169,7 @@ Foreman::Plugin.register :foreman_ansible do
|
|
167
169
|
:view_ansible_roles, :destroy_ansible_roles,
|
168
170
|
:import_ansible_roles, :view_ansible_variables,
|
169
171
|
:create_ansible_variables, :import_ansible_variables,
|
170
|
-
:edit_ansible_variables, :destroy_ansible_variables]
|
172
|
+
:edit_ansible_variables, :destroy_ansible_variables, :import_ansible_playbooks]
|
171
173
|
|
172
174
|
role 'Ansible Tower Inventory Reader',
|
173
175
|
[:view_hosts, :view_hostgroups, :view_facts, :generate_report_templates, :generate_ansible_inventory,
|
@@ -0,0 +1 @@
|
|
1
|
+
{ "template": "<%#\n name: xprazak2.forklift_collection.foreman_provisioning.yml\n snippet: false\n job_category: Ansible Playbook - Imported\n provider_type: Ansible\n kind: job_template\n model: JobTemplate\n%>\n- hosts: all\n become: true\n vars:\n libvirt_tftp: true\n roles:\n - foreman\n - libvirt\n - foreman_provisioning\n"}
|
@@ -0,0 +1,10 @@
|
|
1
|
+
[
|
2
|
+
{
|
3
|
+
"name": "xprazak2.forklift_collection.foreman_provisioning.yml",
|
4
|
+
"playbooks_content": "- hosts: all\n become: true\n vars:\n libvirt_tftp: true\n roles:\n - foreman\n - libvirt\n - foreman_provisioning\n"
|
5
|
+
},
|
6
|
+
{
|
7
|
+
"name": "xprazak2.forklift_collection.collect_debug_draft.yml",
|
8
|
+
"playbooks_content": "---\n- hosts: all\n become: true\n vars:\n bats_output_dir: '/root/bats_results'\n remote_dir: \"/tmp/debug-{{ pipeline_type | default('foreman') }}-{{ pipeline_version | default('nightly') }}-{{ pipeline_os | default('el7') }}\"\n roles:\n - sos_report\n tasks:\n - name: \"Find bats files\"\n find:\n paths: \"{{ bats_output_dir }}\"\n patterns: \"*.tap\"\n register: bats_results\n\n - name: \"Copy bats results\"\n fetch:\n src: \"{{ item.path }}\"\n dest: \"{{ remote_dir }}\"\n with_items: \"{{ bats_results.files }}\"\n\n - name: \"Find smoker files\"\n find:\n paths: \"{{ smoker_output_dir }}\"\n patterns: \"*.xml\"\n register: smoker_results\n\n - name: \"Copy smoker results\"\n fetch:\n src: \"{{ item.path }}\"\n dest: \"{{ remote_dir }}\"\n with_items: \"{{ smoker_results.files }}\"\n"
|
9
|
+
}
|
10
|
+
]
|
@@ -0,0 +1,65 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'test_plugin_helper'
|
4
|
+
|
5
|
+
module Api
|
6
|
+
module V2
|
7
|
+
# Tests for the ansible playbooks controller
|
8
|
+
class AnsiblePlaybooksControllerTest < ActionController::TestCase
|
9
|
+
let(:ansible_proxy) { FactoryBot.create(:smart_proxy, :with_ansible) }
|
10
|
+
let(:proxy_api) { ::ProxyAPI::Ansible.new(url: ansible_proxy.url) }
|
11
|
+
let(:playbooks_names) { %w[xprazak2.forklift_collection.foreman_provisioning.yml xprazak2.forklift_collection.collect_debug_draft.yml] }
|
12
|
+
let(:importer_test) { mock('PlaybooksImporter') }
|
13
|
+
|
14
|
+
test 'should fetch with nil' do
|
15
|
+
AnsiblePlaybooksController.any_instance.stubs(:fetch_playbooks_names).returns(nil)
|
16
|
+
|
17
|
+
get :fetch, :params => {
|
18
|
+
:proxy_id => ansible_proxy.id
|
19
|
+
}, :session => set_session_user
|
20
|
+
response = JSON.parse(@response.body)
|
21
|
+
assert_empty response['results']['playbooks_names']
|
22
|
+
assert_response :success
|
23
|
+
end
|
24
|
+
|
25
|
+
test 'should fetch playbooks names' do
|
26
|
+
AnsiblePlaybooksController.any_instance.stubs(:fetch_playbooks_names).returns(playbooks_names)
|
27
|
+
|
28
|
+
get :fetch, :params => {
|
29
|
+
:proxy_id => ansible_proxy.id
|
30
|
+
}, :session => set_session_user
|
31
|
+
response = JSON.parse(@response.body)
|
32
|
+
assert_not_empty response['results']
|
33
|
+
assert_equal response['results']['playbooks_names'], playbooks_names
|
34
|
+
end
|
35
|
+
|
36
|
+
test 'should use correct proxy' do
|
37
|
+
AnsiblePlaybooksController.any_instance.expects(:find_proxy_api).with(ansible_proxy).returns(proxy_api)
|
38
|
+
proxy_api.expects(:playbooks_names).returns(playbooks_names)
|
39
|
+
|
40
|
+
get :fetch, :params => {
|
41
|
+
:proxy_id => ansible_proxy.id
|
42
|
+
}, :session => set_session_user
|
43
|
+
response = JSON.parse(@response.body)
|
44
|
+
assert_not_empty response['results']
|
45
|
+
assert_not_empty playbooks_names = response['results']['playbooks_names']
|
46
|
+
assert_equal 2, playbooks_names.count
|
47
|
+
end
|
48
|
+
|
49
|
+
test 'check plan and sync' do
|
50
|
+
task = mock('Foreman Task')
|
51
|
+
AnsiblePlaybooksController.any_instance.expects(:plan_ansible_sync).with(ansible_proxy.id, playbooks_names).returns(task)
|
52
|
+
task.stubs(:id).returns('123')
|
53
|
+
task.stubs(:action).returns('Import Playbooks')
|
54
|
+
|
55
|
+
put :sync, :params => {
|
56
|
+
:proxy_id => ansible_proxy.id,
|
57
|
+
:playbooks_names => playbooks_names
|
58
|
+
}, :session => set_session_user
|
59
|
+
response = JSON.parse(@response.body)
|
60
|
+
assert_equal '123', response['id']
|
61
|
+
assert_equal 'Import Playbooks', response['action']
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -20,7 +20,7 @@ class HostsControllerExtensionsTest < ActionController::TestCase
|
|
20
20
|
0 => { :ansible_role_id => @role.id, :position => 0 }
|
21
21
|
} }
|
22
22
|
post :create, :params => { :host => host }, :session => set_session_user
|
23
|
-
assert_redirected_to
|
23
|
+
assert_redirected_to host_details_page_url(assigns('host'))
|
24
24
|
assert assigns('host').ansible_roles, [@role]
|
25
25
|
end
|
26
26
|
|
@@ -34,7 +34,7 @@ class HostsControllerExtensionsTest < ActionController::TestCase
|
|
34
34
|
} }
|
35
35
|
},
|
36
36
|
:session => set_session_user
|
37
|
-
assert_redirected_to
|
37
|
+
assert_redirected_to host_details_page_url(assigns('host'))
|
38
38
|
assert_equal assigns('host').ansible_roles, [@role]
|
39
39
|
end
|
40
40
|
|