foreman_ansible 7.0.4 → 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/app/views/foreman_ansible/job_templates/configure_cloud_connector_-_ansible_default.erb +37 -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/remote_execution.rb +6 -0
- 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 +46 -31
- 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 }}"
|
@@ -0,0 +1,37 @@
|
|
1
|
+
<%#
|
2
|
+
name: Configure Cloud Connector
|
3
|
+
snippet: false
|
4
|
+
template_inputs:
|
5
|
+
- name: satellite_user
|
6
|
+
required: true
|
7
|
+
input_type: user
|
8
|
+
advanced: false
|
9
|
+
value_type: plain
|
10
|
+
hidden_value: false
|
11
|
+
- name: satellite_password
|
12
|
+
required: true
|
13
|
+
input_type: user
|
14
|
+
advanced: false
|
15
|
+
value_type: plain
|
16
|
+
hidden_value: true
|
17
|
+
- name: http_proxy
|
18
|
+
required: false
|
19
|
+
input_type: user
|
20
|
+
advanced: true
|
21
|
+
value_type: plain
|
22
|
+
hidden_value: false
|
23
|
+
description: You can specify a HTTP proxy address that should be used for Cloud Connector connection to the cloud.redhat.com. Note that it must be HTTP proxy, not HTTPS. The tunelling of SSL (secured web socket connection) in SSL (HTTPS proxy) is currently unsupported.
|
24
|
+
model: JobTemplate
|
25
|
+
job_category: Maintenance Operations
|
26
|
+
description_format: "%{template_name}"
|
27
|
+
provider_type: Ansible
|
28
|
+
kind: job_template
|
29
|
+
feature: ansible_configure_cloud_connector
|
30
|
+
%>
|
31
|
+
|
32
|
+
---
|
33
|
+
- hosts: all
|
34
|
+
vars:
|
35
|
+
satellite_url: "<%= foreman_server_url %>"
|
36
|
+
roles:
|
37
|
+
- project-receptor.satellite_receptor_installer
|
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,
|
@@ -48,6 +48,12 @@ module ForemanAnsible
|
|
48
48
|
:description => N_('Upgrade Capsules on given Capsule server hosts'),
|
49
49
|
:proxy_selector_override => ::RemoteExecutionProxySelector::INTERNAL_PROXY
|
50
50
|
)
|
51
|
+
RemoteExecutionFeature.register(
|
52
|
+
:ansible_configure_cloud_connector,
|
53
|
+
N_('Configure Cloud Connector on given hosts'),
|
54
|
+
:description => N_('Configure Cloud Connector on given hosts'),
|
55
|
+
:proxy_selector_override => ::RemoteExecutionProxySelector::INTERNAL_PROXY
|
56
|
+
)
|
51
57
|
end
|
52
58
|
end
|
53
59
|
end
|
@@ -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
|