foreman_wreckingball 3.2.0 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/assets/javascripts/foreman_wreckingball/modal.js +26 -17
- data/app/assets/javascripts/foreman_wreckingball/status_hosts_table.js +34 -2
- data/app/assets/javascripts/foreman_wreckingball/status_managed_hosts_dashboard.js +8 -0
- data/app/assets/stylesheets/foreman_wreckingball/status_managed_hosts_dashboard.css.scss +8 -0
- data/app/controllers/foreman_wreckingball/hosts_controller.rb +47 -31
- data/app/helpers/foreman_wreckingball/statuses_helper.rb +21 -0
- data/app/lib/actions/foreman_wreckingball/bulk_remediate.rb +27 -0
- data/app/models/concerns/foreman_wreckingball/host_status_extensions.rb +1 -1
- data/app/views/foreman_wreckingball/hosts/_hosts.json.rabl +5 -11
- data/app/views/foreman_wreckingball/hosts/_status_dashboard_content.erb +2 -0
- data/app/views/foreman_wreckingball/hosts/_status_managed_hosts_dashboard_cards.html.erb +16 -0
- data/app/views/foreman_wreckingball/hosts/_status_managed_hosts_dashboard_cards_card.html.erb +11 -0
- data/app/views/foreman_wreckingball/hosts/_status_row.html.erb +8 -12
- data/app/views/foreman_wreckingball/hosts/_status_row_actions.html.erb +22 -0
- data/app/views/foreman_wreckingball/hosts/_status_row_hosts_table.html.erb +7 -0
- data/app/views/foreman_wreckingball/hosts/_status_row_hosts_table_actions.html.erb +9 -0
- data/app/views/foreman_wreckingball/hosts/schedule_remediate.html.erb +44 -26
- data/app/views/foreman_wreckingball/hosts/status_dashboard.html.erb +6 -6
- data/app/views/foreman_wreckingball/hosts/status_managed_hosts_dashboard.html.erb +81 -30
- data/config/routes.rb +2 -4
- data/lib/foreman_wreckingball/version.rb +1 -1
- data/test/actions/foreman_wreckingball/bulk_remediate_test.rb +31 -0
- data/test/controllers/foreman_wreckingball/hosts_controller_test.rb +83 -29
- data/test/helpers/foreman_wreckingball/status_helper.rb +10 -0
- data/test/integration/hosts_status_managed_hosts_test.rb +1 -1
- data/test/test_plugin_helper.rb +2 -0
- metadata +14 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 5038fc9d12985cb5fb7ee83a5dc72567d63a04fc11ff5d8d17c09d4cc59ea6b9
|
|
4
|
+
data.tar.gz: 1579d2bb8bb7c35188b76861b84611a190103fdef76f3135f2b3776d86a34003
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: ca6aefe61f1d908461e3f527b8384046923cbe3ecc20864165394532b4735587d1bab362836d2ae044c1c3b1a4d4e48e433bbeec34c7beaba18d20c62cc9f4bf
|
|
7
|
+
data.tar.gz: cfe558ecd67daf5a1b76943396df76626461023f80bae31662aad1cdbc6e4786206ee0307879b5e4d4725b9ecf75fa8a963bbf1f0f98dd7995e3bbe29330a653
|
|
@@ -9,29 +9,38 @@ function submit_modal_form() {
|
|
|
9
9
|
$('#confirmation-modal').modal('hide');
|
|
10
10
|
}
|
|
11
11
|
|
|
12
|
-
function show_modal(element
|
|
13
|
-
if (
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
12
|
+
function show_modal(element) {
|
|
13
|
+
if ($(element).attr('disabled')) { return; }
|
|
14
|
+
|
|
15
|
+
const url = $(element).attr('href');
|
|
16
|
+
const title = $(element).data('title');
|
|
17
|
+
const hostAssociation = $(element).data('host-association');
|
|
18
|
+
|
|
19
|
+
if (title) { $('#confirmation-modal .modal-title').text(title); }
|
|
20
|
+
|
|
20
21
|
$('#confirmation-modal .modal-body')
|
|
21
22
|
.empty()
|
|
22
23
|
.append('<div class="modal-spinner spinner spinner-lg"></div>');
|
|
23
24
|
$('#confirmation-modal').modal();
|
|
24
|
-
$('#confirmation-modal .modal-body').load(url + ' #content',
|
|
25
|
-
function(response, status, xhr) {
|
|
26
|
-
$('#confirmation-modal .form-actions').remove();
|
|
27
25
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
|
|
26
|
+
let params;
|
|
27
|
+
if (!!hostAssociation) {
|
|
28
|
+
params = Object.assign({}, { host_association: hostAssociation },
|
|
29
|
+
!!$(element).data('owned-only') ? { owned_only: true } : null);
|
|
30
|
+
} else if($(element).data('status-id')) {
|
|
31
|
+
params = { status_ids: [$(element).data('status-id')] };
|
|
32
|
+
} else {
|
|
33
|
+
const statusIds = $(element).closest('.status-row')
|
|
34
|
+
.find(':checkbox[name="status-id"]:checked')
|
|
35
|
+
.map((_, e) => { return e.value; })
|
|
36
|
+
.toArray();
|
|
37
|
+
params = { status_ids: statusIds };
|
|
38
|
+
}
|
|
34
39
|
|
|
40
|
+
$('#confirmation-modal .modal-body').load(`${url}?${$.param(params)} #content`,
|
|
41
|
+
(response, status, xhr) => {
|
|
42
|
+
$('#confirmation-modal .form-actions').remove();
|
|
43
|
+
$('#confirmation-modal button[data-action="submit"]').attr('class', 'btn btn-primary');
|
|
35
44
|
$('#confirmation-modal a[rel="popover"]').popover();
|
|
36
45
|
|
|
37
46
|
trigger_form_selector_binds('schedule_remediate_form', url);
|
|
@@ -10,7 +10,8 @@ $(document).ready(() => {
|
|
|
10
10
|
processing: true,
|
|
11
11
|
serverSide: true,
|
|
12
12
|
columnDefs: [
|
|
13
|
-
{ className: 'ellipsis', targets: [
|
|
13
|
+
{ className: 'ellipsis', targets: [1, 2, 3, 4] },
|
|
14
|
+
{ width: '10px', targets: 0 }
|
|
14
15
|
],
|
|
15
16
|
ajax: {
|
|
16
17
|
url: $(element).data('hosts-url'),
|
|
@@ -26,13 +27,23 @@ $(document).ready(() => {
|
|
|
26
27
|
dataSrc: (json) => {
|
|
27
28
|
return json.data.map((e) => {
|
|
28
29
|
let row = [
|
|
30
|
+
`<input name='status-id' type='checkbox' value='${e.status.id}' data-remediate='${!!e.remediate}' >`,
|
|
29
31
|
`<a href="${e.path}">${e.name}</a>`,
|
|
30
32
|
`<span class="${e.status.icon_class}"></span><span class="${e.status.status_class}">${e.status.label}</span>`,
|
|
31
33
|
(e.owner || {}).name || '',
|
|
32
34
|
(e.environment || {}).name || ''
|
|
33
35
|
];
|
|
34
36
|
if(e.remediate) {
|
|
35
|
-
row.push(
|
|
37
|
+
row.push(`
|
|
38
|
+
<span class="btn btn-sm btn-default">
|
|
39
|
+
<a data-title="${e.remediate.title}"
|
|
40
|
+
data-id="aid_wreckingball_hosts_${e.name}_schedule_remediate"
|
|
41
|
+
data-status-id="${e.status.id}"
|
|
42
|
+
onclick="show_modal(this); return false;"
|
|
43
|
+
href="${e.remediate.path}">
|
|
44
|
+
${e.remediate.label}
|
|
45
|
+
</a>
|
|
46
|
+
</span>`);
|
|
36
47
|
} else {
|
|
37
48
|
row.push(null);
|
|
38
49
|
}
|
|
@@ -44,6 +55,27 @@ $(document).ready(() => {
|
|
|
44
55
|
$(element).on('error.dt', (_, settings) => {
|
|
45
56
|
$(settings.nTable).closest('.status-hosts-container').addClass('ajax-error');
|
|
46
57
|
});
|
|
58
|
+
$(element).on('page.dt', () => {
|
|
59
|
+
$selectAll.prop('checked', false).change();
|
|
60
|
+
$remediateSelected.attr('disabled', true);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
const $selectAll = $(this).find(':checkbox[name="select-all"]');
|
|
64
|
+
const $remediateSelected = $(this).find('a.remediate-selected');
|
|
65
|
+
|
|
66
|
+
$selectAll.prop('checked', false);
|
|
67
|
+
|
|
68
|
+
$selectAll.change(() => {
|
|
69
|
+
const $selectHost = $(this).find(':checkbox[name="status-id"]');
|
|
70
|
+
$selectHost.prop('checked', $selectAll.is(':checked'));
|
|
71
|
+
$remediateSelected.attr('disabled', !$selectAll.is(':checked'));
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
$(this).on('change', ':checkbox[name=status-id]', () => {
|
|
75
|
+
const isAnyHostChecked = $(this).find(':checkbox[name="status-id"]').is(':checked');
|
|
76
|
+
$selectAll.prop('checked', isAnyHostChecked);
|
|
77
|
+
$remediateSelected.attr('disabled', !isAnyHostChecked);
|
|
78
|
+
});
|
|
47
79
|
});
|
|
48
80
|
});
|
|
49
81
|
});
|
|
@@ -12,8 +12,7 @@ module ForemanWreckingball
|
|
|
12
12
|
|
|
13
13
|
AJAX_REQUESTS = [:status_hosts].freeze
|
|
14
14
|
before_action :ajax_request, :only => AJAX_REQUESTS
|
|
15
|
-
before_action :
|
|
16
|
-
before_action :find_status, :only => [:submit_remediate, :schedule_remediate]
|
|
15
|
+
before_action :find_statuses, :only => [:schedule_remediate, :submit_remediate]
|
|
17
16
|
|
|
18
17
|
def status_dashboard
|
|
19
18
|
@newest_data = Host.authorized(:view_hosts).joins(:vmware_facet).maximum('vmware_facets.updated_at')
|
|
@@ -43,27 +42,24 @@ module ForemanWreckingball
|
|
|
43
42
|
end
|
|
44
43
|
|
|
45
44
|
def status_managed_hosts_dashboard
|
|
46
|
-
@hosts = Host::Managed.authorized(:view_hosts, Host)
|
|
47
|
-
.try { |query| params[:owned_only] ? query.owned_by_current_user_or_group_with_current_user : query }
|
|
48
|
-
|
|
49
|
-
compute_resources = ComputeResource.where(:type => 'Foreman::Model::Vmware')
|
|
50
|
-
|
|
51
|
-
# get all vms by compute resource id
|
|
52
|
-
vms_by_compute_resource_id = {}
|
|
53
45
|
# NOTE The call to ComputeResource#vms may slow things down
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
46
|
+
vms_by_compute_resource_id = Foreman::Model::Vmware.all.each_with_object({}) do |cr, memo|
|
|
47
|
+
memo[cr.id] = cr.vms(eager_loading: true)
|
|
48
|
+
end
|
|
57
49
|
|
|
58
50
|
# Find all hosts with duplicate VMs
|
|
59
|
-
@duplicate_vms =
|
|
51
|
+
@duplicate_vms = vms_by_compute_resource_id.values
|
|
52
|
+
.flatten
|
|
53
|
+
.group_by(&:uuid)
|
|
54
|
+
.select { |_uuid, vms| vms.size > 1 }
|
|
60
55
|
|
|
61
56
|
@missing_hosts = []
|
|
62
57
|
@different_hosts = []
|
|
63
58
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
59
|
+
Host::Managed.authorized(:view_hosts, Host)
|
|
60
|
+
.where.not(compute_resource_id: nil)
|
|
61
|
+
.try { |query| params[:owned_only] ? query.owned_by_current_user_or_group_with_current_user : query }
|
|
62
|
+
.each do |host|
|
|
67
63
|
# find the compute resource id of the host in the vm map
|
|
68
64
|
cr_id, _vms = vms_by_compute_resource_id.find { |_cr_id, vms| vms.find { |vm| vm.uuid == host.uuid } }
|
|
69
65
|
|
|
@@ -79,7 +75,7 @@ module ForemanWreckingball
|
|
|
79
75
|
|
|
80
76
|
# ajax method
|
|
81
77
|
def status_hosts
|
|
82
|
-
@status = HostStatus.find_wreckingball_status_by_host_association(params.fetch(:status)
|
|
78
|
+
@status = HostStatus.find_wreckingball_status_by_host_association(params.fetch(:status))
|
|
83
79
|
|
|
84
80
|
all_hosts = Host.authorized(:view_hosts, Host)
|
|
85
81
|
.joins(@status.host_association)
|
|
@@ -119,20 +115,21 @@ module ForemanWreckingball
|
|
|
119
115
|
end
|
|
120
116
|
|
|
121
117
|
def submit_remediate
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
118
|
+
return not_found unless @statuses.any?
|
|
119
|
+
|
|
120
|
+
triggering = ::ForemanTasks::Triggering.new_from_params(triggering_params)
|
|
121
|
+
if triggering.future?
|
|
122
|
+
triggering.parse_start_at!
|
|
123
|
+
triggering.parse_start_before!
|
|
124
|
+
else
|
|
125
|
+
triggering.start_at ||= Time.zone.now
|
|
126
|
+
end
|
|
131
127
|
|
|
132
|
-
|
|
128
|
+
task = User.as_anonymous_admin do
|
|
129
|
+
triggering.trigger(::Actions::ForemanWreckingball::BulkRemediate, @statuses)
|
|
133
130
|
end
|
|
134
|
-
|
|
135
|
-
redirect_to
|
|
131
|
+
|
|
132
|
+
redirect_to foreman_tasks_task_path(task.id)
|
|
136
133
|
end
|
|
137
134
|
|
|
138
135
|
private
|
|
@@ -143,8 +140,27 @@ module ForemanWreckingball
|
|
|
143
140
|
end
|
|
144
141
|
end
|
|
145
142
|
|
|
146
|
-
def
|
|
147
|
-
@
|
|
143
|
+
def statuses_params
|
|
144
|
+
@statuses_params ||= params.permit(:host_association, :owned_only, status_ids: [])
|
|
145
|
+
end
|
|
146
|
+
|
|
147
|
+
def find_statuses
|
|
148
|
+
@statuses = begin
|
|
149
|
+
host_association = statuses_params[:host_association]
|
|
150
|
+
status_class = HostStatus.find_wreckingball_status_by_host_association(host_association)
|
|
151
|
+
if status_class
|
|
152
|
+
Host.authorized(:remediate_vmware_status_hosts, Host)
|
|
153
|
+
.joins(status_class.host_association)
|
|
154
|
+
.includes(status_class.host_association)
|
|
155
|
+
.try { |query| statuses_params[:owned_only] ? query.owned_by_current_user_or_group_with_current_user : query }
|
|
156
|
+
.where.not('host_status.status': status_class.global_ok_list)
|
|
157
|
+
.map { |host| host.send(status_class.host_association) }
|
|
158
|
+
else
|
|
159
|
+
HostStatus::Status.includes(:host).where(id: statuses_params[:status_ids]).select do |status|
|
|
160
|
+
User.current.can?(:remediate_vmware_status_hosts, status.host)
|
|
161
|
+
end
|
|
162
|
+
end
|
|
163
|
+
end
|
|
148
164
|
end
|
|
149
165
|
|
|
150
166
|
def action_permission
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module ForemanWreckingball
|
|
4
|
+
module StatusesHelper
|
|
5
|
+
def status_actions(host_association, owned_only, supports_remediate)
|
|
6
|
+
actions = []
|
|
7
|
+
actions << display_link_if_authorized(_('Refresh'),
|
|
8
|
+
hash_for_refresh_status_dashboard_hosts_path,
|
|
9
|
+
title: _('Refresh Data'),
|
|
10
|
+
method: :put)
|
|
11
|
+
if supports_remediate
|
|
12
|
+
actions << display_link_if_authorized(_('Remediate All'),
|
|
13
|
+
hash_for_schedule_remediate_hosts_path,
|
|
14
|
+
'data-host-association': host_association,
|
|
15
|
+
'data-owned-only': owned_only,
|
|
16
|
+
onClick: 'show_modal(this); return false;')
|
|
17
|
+
end
|
|
18
|
+
actions.reject(&:blank?)
|
|
19
|
+
end
|
|
20
|
+
end
|
|
21
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Actions
|
|
4
|
+
module ForemanWreckingball
|
|
5
|
+
class BulkRemediate < Actions::Base
|
|
6
|
+
def plan(statuses)
|
|
7
|
+
sequence do
|
|
8
|
+
statuses.group_by(&:class).each do |statuses_klass, statuses_list|
|
|
9
|
+
plan_action(::Actions::BulkAction, statuses_klass.remediate_action, statuses_list.map(&:host)) if statuses_klass.respond_to?(:remediate_action)
|
|
10
|
+
end
|
|
11
|
+
end
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def run
|
|
15
|
+
# dummy run phase to save input
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def resource_locks
|
|
19
|
+
:link
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def humanized_name
|
|
23
|
+
_('Bulk remediate')
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -7,7 +7,7 @@ module ForemanWreckingball
|
|
|
7
7
|
end
|
|
8
8
|
|
|
9
9
|
def find_wreckingball_status_by_host_association(host_association)
|
|
10
|
-
wreckingball_statuses.find { |s| s.host_association == host_association }
|
|
10
|
+
wreckingball_statuses.find { |s| s.host_association.to_s == host_association.to_s }
|
|
11
11
|
end
|
|
12
12
|
end
|
|
13
13
|
end
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
collection @hosts
|
|
4
4
|
|
|
5
|
-
attributes :name
|
|
5
|
+
attributes :id, :name
|
|
6
6
|
|
|
7
7
|
child owner: :owner do
|
|
8
8
|
attribute :name
|
|
@@ -17,6 +17,7 @@ node(:path) { |host| host_path(host) }
|
|
|
17
17
|
node(:status) do |host|
|
|
18
18
|
status = host.public_send(locals[:host_association])
|
|
19
19
|
{
|
|
20
|
+
id: status.id,
|
|
20
21
|
label: status.to_label,
|
|
21
22
|
icon_class: host_global_status_icon_class(status.to_global),
|
|
22
23
|
status_class: host_global_status_class(status.to_global)
|
|
@@ -24,18 +25,11 @@ node(:status) do |host|
|
|
|
24
25
|
end
|
|
25
26
|
|
|
26
27
|
node(:remediate, if: lambda do |host|
|
|
27
|
-
locals[:supports_remediate] &&
|
|
28
|
-
|
|
29
|
-
status_id: host.public_send(locals[:host_association]).id)
|
|
30
|
-
.merge(auth_object: host,
|
|
31
|
-
permission: :remediate_vmware_status_hosts)
|
|
32
|
-
authorized_for(options)
|
|
33
|
-
end
|
|
34
|
-
end) do |host|
|
|
35
|
-
status_id = host.public_send(locals[:host_association]).id
|
|
28
|
+
locals[:supports_remediate] && User.current.can?(:remediate_vmware_status_hosts, host)
|
|
29
|
+
end) do
|
|
36
30
|
{
|
|
37
31
|
label: _('Remediate'),
|
|
38
32
|
title: _('Remediate Host OS'),
|
|
39
|
-
path:
|
|
33
|
+
path: schedule_remediate_hosts_path
|
|
40
34
|
}
|
|
41
35
|
end
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
<div class='status-managed-hosts-dashboard-cards container-fluid container-cards-pf'>
|
|
2
|
+
<div class='row row-cards-pf'>
|
|
3
|
+
<%= render partial: 'status_managed_hosts_dashboard_cards_card', locals: {
|
|
4
|
+
title: _('List of Hosts not found in vSphere'),
|
|
5
|
+
count: missing_hosts_count
|
|
6
|
+
} %>
|
|
7
|
+
<%= render partial: 'status_managed_hosts_dashboard_cards_card', locals: {
|
|
8
|
+
title: _('List of VMs with same uuid'),
|
|
9
|
+
count: duplicate_vms_count
|
|
10
|
+
} %>
|
|
11
|
+
<%= render partial: 'status_managed_hosts_dashboard_cards_card', locals: {
|
|
12
|
+
title: _('List of VMs associated with different compute resources'),
|
|
13
|
+
count: different_hosts_count
|
|
14
|
+
} %>
|
|
15
|
+
</div>
|
|
16
|
+
</div>
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<div class='col-xs-12 col-sm-4'>
|
|
2
|
+
<div class='card-pf card-pf-accented card-pf-aggregate-status'>
|
|
3
|
+
<%= content_tag :h2, title, class: 'card-pf-title' %>
|
|
4
|
+
<div class='card-pf-body'>
|
|
5
|
+
<p class='card-pf-aggregate-status-notifications'>
|
|
6
|
+
<%= content_tag :span, nil, class: "pficon pficon-#{count > 0 ? 'error-circle-o' : 'ok'}" %>
|
|
7
|
+
<%= count if count > 0 %>
|
|
8
|
+
</p>
|
|
9
|
+
</div>
|
|
10
|
+
</div>
|
|
11
|
+
</div>
|
|
@@ -3,18 +3,14 @@
|
|
|
3
3
|
<div class="list-view-pf-expand">
|
|
4
4
|
<span class="fa fa-angle-right"></span>
|
|
5
5
|
</div>
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
</ul>
|
|
15
|
-
</div>
|
|
16
|
-
<% end %>
|
|
17
|
-
</div>
|
|
6
|
+
<%=
|
|
7
|
+
render :partial => 'status_row_actions', locals: {
|
|
8
|
+
id: id,
|
|
9
|
+
supports_remediate: supports_remediate,
|
|
10
|
+
host_association: host_association,
|
|
11
|
+
owned_only: owned_only
|
|
12
|
+
}
|
|
13
|
+
%>
|
|
18
14
|
<div class="list-view-pf-main-info">
|
|
19
15
|
<div class="list-view-pf-left">
|
|
20
16
|
<span class="pficon list-view-pf-icon-md <%= classes_for_vmware_status_row(counter) %>"></span>
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
<% actions = status_actions(host_association, owned_only, supports_remediate) %>
|
|
2
|
+
|
|
3
|
+
<% if actions.any? %>
|
|
4
|
+
<div class='list-view-pf-actions'>
|
|
5
|
+
<div class='dropdown pull-right dropdown-kebab-pf'>
|
|
6
|
+
<%= content_tag :button, class: 'btn btn-link dropdown-toggle',
|
|
7
|
+
type: 'button',
|
|
8
|
+
id: "dropdownKebabRight#{id}",
|
|
9
|
+
'data-toggle': 'dropdown',
|
|
10
|
+
'aria-haspopup': true,
|
|
11
|
+
'aria-expanded': true do %>
|
|
12
|
+
<span class='fa fa-ellipsis-v' />
|
|
13
|
+
<% end %>
|
|
14
|
+
|
|
15
|
+
<%= content_tag :ul, class: 'dropdown-menu dropdown-menu-right', 'aria-labelledby': "dropdownKebabRight#{id}" do %>
|
|
16
|
+
<% actions.each do |action| %>
|
|
17
|
+
<%= content_tag :li, action %>
|
|
18
|
+
<% end %>
|
|
19
|
+
<% end %>
|
|
20
|
+
</div>
|
|
21
|
+
</div>
|
|
22
|
+
<% end %>
|
|
@@ -1,8 +1,13 @@
|
|
|
1
|
+
<%= render :partial => 'status_row_hosts_table_actions', locals: { supports_remediate: supports_remediate } %>
|
|
2
|
+
|
|
1
3
|
<%= content_tag :table, id: status,
|
|
2
4
|
class: 'table table-striped table-fixed status-hosts',
|
|
3
5
|
'data-hosts-url': ajax_status_dashboard_hosts_path(status, owned_only: params[:owned_only]) do %>
|
|
4
6
|
<%= content_tag :thead do %>
|
|
5
7
|
<%= content_tag :tr do %>
|
|
8
|
+
<%= content_tag :th do %>
|
|
9
|
+
<%= check_box_tag 'select-all' %>
|
|
10
|
+
<% end %>
|
|
6
11
|
<%= content_tag :th, _('Hostname') %>
|
|
7
12
|
<%= content_tag :th, _('Status') %>
|
|
8
13
|
<%= content_tag :th, _('Owner') %>
|
|
@@ -13,5 +18,7 @@
|
|
|
13
18
|
<%= content_tag(:tbody) {} %>
|
|
14
19
|
<% end %>
|
|
15
20
|
|
|
21
|
+
<%= render :partial => 'status_row_hosts_table_actions', locals: { supports_remediate: supports_remediate } %>
|
|
22
|
+
|
|
16
23
|
<%= alert header: _("Oops, we're sorry but something went wrong"), text: '',
|
|
17
24
|
class: 'alert-danger', close: false %>
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
<%= content_tag :div, class: 'text-right' do %>
|
|
2
|
+
<% if supports_remediate %>
|
|
3
|
+
<%= content_tag :a, _('Remediate selected'),
|
|
4
|
+
href: schedule_remediate_hosts_path,
|
|
5
|
+
class: 'btn btn-default remediate-selected',
|
|
6
|
+
onclick: 'show_modal(this); return false;',
|
|
7
|
+
disabled: true %>
|
|
8
|
+
<% end %>
|
|
9
|
+
<% end %>
|
|
@@ -1,35 +1,53 @@
|
|
|
1
|
-
<% title _('Remediate
|
|
1
|
+
<% title _('Remediate') %>
|
|
2
2
|
<% javascript 'foreman_tasks/trigger_form' %>
|
|
3
3
|
<% stylesheet 'foreman_tasks/trigger_form' %>
|
|
4
4
|
|
|
5
|
+
<% if @statuses.any? %>
|
|
6
|
+
<% if @statuses.group_by(&:class).map { |i| i.first}.select { |i| i.try(:dangerous_remediate?) }.any? %>
|
|
7
|
+
<%= alert(:text => _('This will cause a service interruption.'), :class => 'alert-warning', :close => false) %>
|
|
8
|
+
<% end %>
|
|
5
9
|
|
|
6
|
-
|
|
7
|
-
<%= alert(:text => _('This will cause a service interruption.'), :class => 'alert-warning', :close => false) %>
|
|
8
|
-
<% end -%>
|
|
10
|
+
<%= n_('One host selected for remediation.', '%s hosts selected for remediation.', @statuses.count) % @statuses.count %>
|
|
9
11
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
12
|
+
<ul class='hosts-list' style='max-height: 100px; overflow-y: scroll;'>
|
|
13
|
+
<% @statuses.each do |status| %>
|
|
14
|
+
<li>
|
|
15
|
+
<%= status.host.name %>
|
|
16
|
+
<%= content_tag(:span, nil, class: 'glyphicon glyphicon-warning-sign text-warning', title: _('This will cause a service interruption.')) if status.class.try(:dangerous_remediate?) %>
|
|
17
|
+
</li>
|
|
18
|
+
<% end %>
|
|
19
|
+
</ul>
|
|
20
|
+
|
|
21
|
+
<%= form_for @triggering, html: { class: 'form-horizontal', id: 'schedule_remediate_form' },
|
|
22
|
+
url: submit_remediate_hosts_path(host_association: @statuses_params[:host_association],
|
|
23
|
+
owned_only: @statuses_params[:owned_only],
|
|
24
|
+
status_ids: @statuses_params[:status_ids]) do |f| %>
|
|
25
|
+
<%= javascript_tag do %>
|
|
26
|
+
$(function() { trigger_form_selector_binds('<%= f.options[:html][:id] %>','<%= f.object_name %>') });
|
|
27
|
+
<% end %>
|
|
28
|
+
<div class="form-group">
|
|
29
|
+
<label class="col-md-2 control-label"><%= _('Schedule') %></label>
|
|
30
|
+
<div class="col-md-8">
|
|
31
|
+
<%= fields_for :triggering, @triggering do |trigger_fields| %>
|
|
32
|
+
<%= radio_button_f trigger_fields, :mode, :class => 'trigger_mode_selector', :value => 'immediate', :text => _('Execute now') %>
|
|
33
|
+
<%= radio_button_f trigger_fields, :mode, :class => 'trigger_mode_selector', :value => 'future', :text => _('Schedule future execution') %>
|
|
34
|
+
</div>
|
|
20
35
|
</div>
|
|
21
|
-
</div>
|
|
22
36
|
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
37
|
+
<div class="trigger_fields">
|
|
38
|
+
<%= content_tag(:fieldset, nil, id: 'trigger_mode_future', class: "trigger_mode_form #{'hidden' unless @triggering.future?}") do
|
|
39
|
+
safe_join([
|
|
40
|
+
text_f(f, :start_at_raw, label: _('Start at'), placeholder: 'YYYY-mm-dd HH:MM'),
|
|
41
|
+
text_f(f, :start_before_raw, label: _('Start before'), placeholder: 'YYYY-mm-dd HH:MM',
|
|
42
|
+
label_help: _('Indicates that the action should be cancelled if it cannot be started before this time.'))
|
|
43
|
+
])
|
|
44
|
+
end %>
|
|
45
|
+
</div>
|
|
46
|
+
<% end %>
|
|
33
47
|
|
|
34
|
-
|
|
48
|
+
<%= submit_or_cancel f, false, :cancel_path => { controller: :'foreman_wreckingball/hosts', action: :status_dashboard } %>
|
|
49
|
+
<% end %>
|
|
50
|
+
<% else %>
|
|
51
|
+
<%= content_tag :h3, _('No hosts selected') %>
|
|
52
|
+
<%= content_tag :p, _('Please select some hosts and try again') %>
|
|
35
53
|
<% end %>
|
|
@@ -13,11 +13,11 @@
|
|
|
13
13
|
render 'status_dashboard_empty'
|
|
14
14
|
end
|
|
15
15
|
%>
|
|
16
|
-
<%= render :
|
|
17
|
-
:
|
|
18
|
-
:
|
|
19
|
-
:
|
|
20
|
-
button_tag(_('Cancel'), :
|
|
21
|
-
button_tag(_('Submit'), :
|
|
16
|
+
<%= render partial: 'common/modal', locals: {
|
|
17
|
+
id: 'confirmation-modal',
|
|
18
|
+
title: _('Please Confirm'),
|
|
19
|
+
buttons: [
|
|
20
|
+
button_tag(_('Cancel'), class: 'btn btn-default', data: { dismiss: 'modal' }, type: 'button'),
|
|
21
|
+
button_tag(_('Submit'), class: 'btn btn-primary', data: { action: 'submit' }, onclick: 'submit_modal_form()')
|
|
22
22
|
]
|
|
23
23
|
} %>
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
<% title _('VMware Managed Hosts Overview') %>
|
|
2
|
-
<% javascript 'foreman_wreckingball/
|
|
3
|
-
<%
|
|
4
|
-
<% stylesheet 'foreman_wreckingball/status_hosts_table' %>
|
|
2
|
+
<% javascript 'foreman_wreckingball/status_managed_hosts_dashboard' %>
|
|
3
|
+
<% stylesheet 'foreman_wreckingball/status_managed_hosts_dashboard' %>
|
|
5
4
|
|
|
6
5
|
<%= title_actions(
|
|
7
6
|
button_group(
|
|
@@ -13,37 +12,89 @@
|
|
|
13
12
|
)
|
|
14
13
|
) %>
|
|
15
14
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
15
|
+
<%= render partial: 'status_managed_hosts_dashboard_cards', locals: {
|
|
16
|
+
missing_hosts_count: @missing_hosts.count,
|
|
17
|
+
duplicate_vms_count: @duplicate_vms.map(&:second).flatten.count,
|
|
18
|
+
different_hosts_count: @different_hosts.count
|
|
19
|
+
} %>
|
|
20
|
+
|
|
21
|
+
<ul class='nav nav-tabs' data-tabs='tabs'>
|
|
22
|
+
<li class='active'>
|
|
23
|
+
<%= content_tag :a, _('List of Hosts not found in vSphere'), href: '#missing_vms_tab', 'data-toggle': 'tab' %>
|
|
24
|
+
</li>
|
|
25
|
+
<li>
|
|
26
|
+
<%= content_tag :a, _('List of VMs with same uuid'), href: '#duplicate_vms_tab', 'data-toggle': 'tab' %>
|
|
27
|
+
</li>
|
|
28
|
+
<li>
|
|
29
|
+
<%= content_tag :a, _('List of VMs associated with different Compute Resources'), href: '#different_vms_tab', 'data-toggle': 'tab' %>
|
|
30
|
+
</li>
|
|
31
|
+
</ul>
|
|
32
|
+
|
|
33
|
+
<div class='tab-content'>
|
|
34
|
+
<div class='tab-pane active' id='missing_vms_tab'>
|
|
35
|
+
<% if @missing_hosts.empty? %>
|
|
36
|
+
<%= content_tag :p, _('No hosts to show'), class: 'ca' %>
|
|
37
|
+
<% else %>
|
|
38
|
+
<%= content_tag :table, id: 'missing_vms', class: table_css_classes do %>
|
|
39
|
+
<thead>
|
|
40
|
+
<tr>
|
|
41
|
+
<%= content_tag :th, _('Name') %>
|
|
42
|
+
</tr>
|
|
43
|
+
</thead>
|
|
44
|
+
<tbody>
|
|
45
|
+
<% @missing_hosts.each do |host| %>
|
|
46
|
+
<tr>
|
|
47
|
+
<%= content_tag :td, link_to_if_authorized(host.name, hash_for_host_path(id: host)) %>
|
|
48
|
+
</tr>
|
|
49
|
+
<% end %>
|
|
50
|
+
</tbody>
|
|
51
|
+
<% end %>
|
|
22
52
|
<% end %>
|
|
23
|
-
</div>
|
|
24
53
|
</div>
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
54
|
+
<div class='tab-pane' id='duplicate_vms_tab'>
|
|
55
|
+
<% if @duplicate_vms.empty? %>
|
|
56
|
+
<%= content_tag :p, _('No hosts to show'), class: 'ca' %>
|
|
57
|
+
<% else %>
|
|
58
|
+
<%= content_tag :table, id: 'duplicate_vms', class: table_css_classes do %>
|
|
59
|
+
<thead>
|
|
60
|
+
<tr>
|
|
61
|
+
<%= content_tag :th, _('UUID') %>
|
|
62
|
+
<%= content_tag :th, _('Name') %>
|
|
63
|
+
</tr>
|
|
64
|
+
</thead>
|
|
65
|
+
<tbody>
|
|
66
|
+
<% @duplicate_vms.each do |_uuid, hosts| %>
|
|
67
|
+
<% hosts.each do |host| %>
|
|
68
|
+
<tr>
|
|
69
|
+
<%= content_tag :td, host.uuid %>
|
|
70
|
+
<%= content_tag :td, link_to_if_authorized(host.name, hash_for_host_path(id: host)) %>
|
|
71
|
+
</tr>
|
|
72
|
+
<% end %>
|
|
73
|
+
<% end %>
|
|
74
|
+
</tbody>
|
|
34
75
|
<% end %>
|
|
35
76
|
<% end %>
|
|
36
|
-
</div>
|
|
37
77
|
</div>
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
78
|
+
<div class='tab-pane' id='different_vms_tab'>
|
|
79
|
+
<% if @different_hosts.empty? %>
|
|
80
|
+
<%= content_tag :p, _('No hosts to show'), class: 'ca' %>
|
|
81
|
+
<% else %>
|
|
82
|
+
<%= content_tag :table, id: 'different_vms', class: table_css_classes do %>
|
|
83
|
+
<thead>
|
|
84
|
+
<tr>
|
|
85
|
+
<%= content_tag :th, _('UUID') %>
|
|
86
|
+
<%= content_tag :th, _('Name') %>
|
|
87
|
+
</tr>
|
|
88
|
+
</thead>
|
|
89
|
+
<tbody>
|
|
90
|
+
<% @different_hosts.each do |host| %>
|
|
91
|
+
<tr>
|
|
92
|
+
<%= content_tag :td, host.uuid %>
|
|
93
|
+
<%= content_tag :td, link_to_if_authorized(host.name, hash_for_host_path(id: host)) %>
|
|
94
|
+
</tr>
|
|
95
|
+
<% end %>
|
|
96
|
+
</tbody>
|
|
97
|
+
<% end %>
|
|
46
98
|
<% end %>
|
|
47
|
-
</div>
|
|
48
99
|
</div>
|
|
49
|
-
|
|
100
|
+
</div>
|
data/config/routes.rb
CHANGED
|
@@ -4,15 +4,13 @@ Rails.application.routes.draw do
|
|
|
4
4
|
scope '/wreckingball' do
|
|
5
5
|
constraints(:id => /[^\/]+/) do
|
|
6
6
|
resources :hosts, controller: 'foreman_wreckingball/hosts', only: [] do
|
|
7
|
-
member do
|
|
8
|
-
get :schedule_remediate
|
|
9
|
-
post :submit_remediate
|
|
10
|
-
end
|
|
11
7
|
collection do
|
|
12
8
|
get :status_dashboard
|
|
13
9
|
get :status_managed_hosts_dashboard
|
|
14
10
|
get 'status_dashboard/hosts(/:status)', as: :ajax_status_dashboard, action: :status_hosts, defaults: { format: :json }
|
|
15
11
|
put :refresh_status_dashboard
|
|
12
|
+
get :schedule_remediate
|
|
13
|
+
post :submit_remediate
|
|
16
14
|
end
|
|
17
15
|
end
|
|
18
16
|
end
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require 'test_plugin_helper'
|
|
4
|
+
|
|
5
|
+
module Actions
|
|
6
|
+
module ForemanWreckingball
|
|
7
|
+
describe Actions::ForemanWreckingball::BulkRemediate do
|
|
8
|
+
include Dynflow::Testing
|
|
9
|
+
|
|
10
|
+
let(:action_class) { ::Actions::ForemanWreckingball::BulkRemediate }
|
|
11
|
+
let(:bulk_action) { ::Actions::BulkAction }
|
|
12
|
+
let(:action) { create_action(action_class) }
|
|
13
|
+
|
|
14
|
+
setup do
|
|
15
|
+
Setting::Wreckingball.load_defaults
|
|
16
|
+
FactoryBot.create_list(:host, 2, :managed, :with_wreckingball_statuses)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
it 'plans remediate action' do
|
|
20
|
+
::ForemanWreckingball::Engine::WRECKINGBALL_STATUSES.map(&:constantize)
|
|
21
|
+
.select(&:supports_remediate?)
|
|
22
|
+
.each do |status|
|
|
23
|
+
statuses = HostStatus::Status.where(type: status.to_s)
|
|
24
|
+
plan_action(action, statuses)
|
|
25
|
+
|
|
26
|
+
assert_action_planed_with(action, bulk_action, status.remediate_action, statuses.map(&:host))
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
@@ -4,6 +4,8 @@ require 'test_plugin_helper'
|
|
|
4
4
|
|
|
5
5
|
module ForemanWreckingball
|
|
6
6
|
class HostsControllerTest < ActionController::TestCase
|
|
7
|
+
include ForemanWreckingball::StatusHelper
|
|
8
|
+
|
|
7
9
|
let(:fake_task) { OpenStruct.new(id: 123) }
|
|
8
10
|
|
|
9
11
|
setup do
|
|
@@ -222,56 +224,108 @@ module ForemanWreckingball
|
|
|
222
224
|
end
|
|
223
225
|
|
|
224
226
|
describe '#schedule_remediate' do
|
|
225
|
-
let(:host)
|
|
226
|
-
|
|
227
|
-
end
|
|
227
|
+
let(:host) { FactoryBot.create(:host, :with_wreckingball_statuses) }
|
|
228
|
+
let(:status_ids) { [host.vmware_tools_status_object.id] }
|
|
228
229
|
|
|
229
230
|
test 'shows a remediation schedule page' do
|
|
230
|
-
get :schedule_remediate, params: {
|
|
231
|
+
get :schedule_remediate, params: { status_ids: status_ids }, session: set_session_user
|
|
231
232
|
assert_response :success
|
|
232
233
|
end
|
|
233
234
|
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
235
|
+
context 'with status_id' do
|
|
236
|
+
let(:hosts) { FactoryBot.create_list(:host, 2, :with_wreckingball_statuses) }
|
|
237
|
+
let(:status_ids) { [hosts.first.vmware_operatingsystem_status_object.id] }
|
|
238
|
+
|
|
239
|
+
test 'remediate selected statuses' do
|
|
240
|
+
get :schedule_remediate, params: { status_ids: status_ids }, session: set_session_user
|
|
241
|
+
assert_statuses HostStatus::Status.find(status_ids)
|
|
242
|
+
end
|
|
237
243
|
end
|
|
238
244
|
|
|
239
|
-
|
|
240
|
-
FactoryBot.
|
|
241
|
-
|
|
242
|
-
|
|
245
|
+
context 'with host_association' do
|
|
246
|
+
let(:hosts) { FactoryBot.create_list(:host, 2, :with_wreckingball_statuses, owner: users(:admin)) }
|
|
247
|
+
let(:statuses) { hosts.map { |h| h.send(host_association) } }
|
|
248
|
+
let(:status_class) { ForemanWreckingball::OperatingsystemStatus }
|
|
249
|
+
let(:host_association) { status_class.host_association }
|
|
250
|
+
|
|
251
|
+
setup do
|
|
252
|
+
hosts.each { |h| h.send(host_association).update(status: status_class::MISMATCH) }
|
|
253
|
+
end
|
|
254
|
+
|
|
255
|
+
test 'remediate all statuses' do
|
|
256
|
+
get :schedule_remediate, params: { host_association: host_association }, session: set_session_user
|
|
257
|
+
assert_statuses statuses
|
|
258
|
+
end
|
|
259
|
+
|
|
260
|
+
context 'with owned_only' do
|
|
261
|
+
setup do
|
|
262
|
+
hosts_list = FactoryBot.create_list(:host, 2, :with_wreckingball_statuses, owner: users(:one))
|
|
263
|
+
hosts_list.each { |h| h.send(host_association).update(status: status_class::MISMATCH) }
|
|
264
|
+
end
|
|
265
|
+
|
|
266
|
+
test 'remediate only those statuses where the user is the owner of the host' do
|
|
267
|
+
get :schedule_remediate, params: { host_association: host_association, owned_only: true }, session: set_session_user
|
|
268
|
+
assert_statuses statuses
|
|
269
|
+
end
|
|
270
|
+
end
|
|
243
271
|
end
|
|
244
272
|
end
|
|
245
273
|
|
|
246
274
|
describe '#submit_remediate' do
|
|
247
|
-
let(:host)
|
|
248
|
-
|
|
275
|
+
let(:host) { FactoryBot.create(:host, :with_wreckingball_statuses) }
|
|
276
|
+
let(:status_ids) { [host.vmware_tools_status_object.id] }
|
|
277
|
+
|
|
278
|
+
setup do
|
|
279
|
+
ForemanTasks.stubs(:async_task).returns(fake_task)
|
|
249
280
|
end
|
|
250
281
|
|
|
251
282
|
test 'redirects to scheduled task' do
|
|
252
|
-
|
|
253
|
-
post :submit_remediate, params: { status_id: host.vmware_operatingsystem_status_object.id, id: host.id }, session: set_session_user
|
|
283
|
+
post :submit_remediate, params: { status_ids: status_ids }, session: set_session_user
|
|
254
284
|
assert_response :redirect
|
|
255
|
-
|
|
256
|
-
assert_redirected_to foreman_tasks_task_path(123)
|
|
285
|
+
assert_redirected_to foreman_tasks_task_path(fake_task.id)
|
|
257
286
|
end
|
|
258
287
|
|
|
259
|
-
test '
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
post :submit_remediate, params: { status_id: host.vmware_tools_status_object.id, id: host.id }, session: set_session_user
|
|
263
|
-
end
|
|
288
|
+
test 'returns not found when status_ids param is invalid' do
|
|
289
|
+
post :submit_remediate, params: { status_ids: 'invalid' }, session: set_session_user
|
|
290
|
+
assert_response :not_found
|
|
264
291
|
end
|
|
265
292
|
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
293
|
+
context 'with status_id' do
|
|
294
|
+
let(:hosts) { FactoryBot.create_list(:host, 2, :with_wreckingball_statuses) }
|
|
295
|
+
let(:status_ids) { [hosts.first.vmware_operatingsystem_status_object.id] }
|
|
296
|
+
|
|
297
|
+
test 'remediate selected statuses' do
|
|
298
|
+
post :submit_remediate, params: { status_ids: status_ids }, session: set_session_user
|
|
299
|
+
assert_statuses HostStatus::Status.find(status_ids)
|
|
300
|
+
end
|
|
269
301
|
end
|
|
270
302
|
|
|
271
|
-
|
|
272
|
-
FactoryBot.
|
|
273
|
-
|
|
274
|
-
|
|
303
|
+
context 'with host_association' do
|
|
304
|
+
let(:hosts) { FactoryBot.create_list(:host, 2, :with_wreckingball_statuses, owner: users(:admin)) }
|
|
305
|
+
let(:statuses) { hosts.map { |h| h.send(host_association) } }
|
|
306
|
+
let(:status_class) { ForemanWreckingball::OperatingsystemStatus }
|
|
307
|
+
let(:host_association) { status_class.host_association }
|
|
308
|
+
|
|
309
|
+
setup do
|
|
310
|
+
hosts.each { |h| h.send(host_association).update(status: status_class::MISMATCH) }
|
|
311
|
+
end
|
|
312
|
+
|
|
313
|
+
test 'remediate all statuses' do
|
|
314
|
+
post :submit_remediate, params: { host_association: host_association }, session: set_session_user
|
|
315
|
+
assert_statuses statuses
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
context 'with owned_only' do
|
|
319
|
+
setup do
|
|
320
|
+
hosts_list = FactoryBot.create_list(:host, 2, :with_wreckingball_statuses, owner: users(:one))
|
|
321
|
+
hosts_list.each { |h| h.send(host_association).update(status: status_class::MISMATCH) }
|
|
322
|
+
end
|
|
323
|
+
|
|
324
|
+
test 'remediate only those statuses where the user is the owner of the host' do
|
|
325
|
+
post :submit_remediate, params: { host_association: host_association, owned_only: true }, session: set_session_user
|
|
326
|
+
assert_statuses statuses
|
|
327
|
+
end
|
|
328
|
+
end
|
|
275
329
|
end
|
|
276
330
|
end
|
|
277
331
|
end
|
|
@@ -81,7 +81,7 @@ class HostsStatusManagedHostsTest < ActionDispatch::IntegrationTest
|
|
|
81
81
|
cr.stubs(:vms).returns([mock1_vm, mock2_vm])
|
|
82
82
|
other_cr.stubs(:vms).returns([mock3_vm])
|
|
83
83
|
|
|
84
|
-
|
|
84
|
+
Foreman::Model::Vmware.stubs(:all).returns([cr, other_cr])
|
|
85
85
|
|
|
86
86
|
visit status_managed_hosts_dashboard_hosts_path
|
|
87
87
|
|
data/test/test_plugin_helper.rb
CHANGED
|
@@ -5,6 +5,8 @@ require 'test_helper'
|
|
|
5
5
|
require 'database_cleaner'
|
|
6
6
|
require 'dynflow/testing'
|
|
7
7
|
|
|
8
|
+
Dir["#{__dir__}/helpers/foreman_wreckingball/**.rb"].each { |f| require f }
|
|
9
|
+
|
|
8
10
|
# Add plugin to FactoryBot's paths
|
|
9
11
|
FactoryBot.definition_file_paths << File.join(ForemanTasks::Engine.root, 'test', 'factories')
|
|
10
12
|
FactoryBot.definition_file_paths << File.join(File.dirname(__FILE__), 'factories')
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: foreman_wreckingball
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 3.
|
|
4
|
+
version: 3.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Timo Goebel
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date: 2019-01
|
|
11
|
+
date: 2019-03-01 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: foreman-tasks
|
|
@@ -64,12 +64,16 @@ files:
|
|
|
64
64
|
- Rakefile
|
|
65
65
|
- app/assets/javascripts/foreman_wreckingball/modal.js
|
|
66
66
|
- app/assets/javascripts/foreman_wreckingball/status_hosts_table.js
|
|
67
|
+
- app/assets/javascripts/foreman_wreckingball/status_managed_hosts_dashboard.js
|
|
67
68
|
- app/assets/javascripts/foreman_wreckingball/status_row.js
|
|
68
69
|
- app/assets/stylesheets/foreman_wreckingball/status_hosts_table.css.scss
|
|
70
|
+
- app/assets/stylesheets/foreman_wreckingball/status_managed_hosts_dashboard.css.scss
|
|
69
71
|
- app/controllers/foreman_wreckingball/hosts_controller.rb
|
|
70
72
|
- app/helpers/concerns/foreman_wreckingball/hosts_helper_extensions.rb
|
|
71
73
|
- app/helpers/foreman_wreckingball/hypervisors_helper.rb
|
|
74
|
+
- app/helpers/foreman_wreckingball/statuses_helper.rb
|
|
72
75
|
- app/jobs/update_hosts_vmware_facets.rb
|
|
76
|
+
- app/lib/actions/foreman_wreckingball/bulk_remediate.rb
|
|
73
77
|
- app/lib/actions/foreman_wreckingball/host/refresh_vmware_facet.rb
|
|
74
78
|
- app/lib/actions/foreman_wreckingball/host/remediate_hardware_version.rb
|
|
75
79
|
- app/lib/actions/foreman_wreckingball/host/remediate_spectre_v2.rb
|
|
@@ -106,8 +110,12 @@ files:
|
|
|
106
110
|
- app/views/foreman_wreckingball/hosts/_hosts.json.rabl
|
|
107
111
|
- app/views/foreman_wreckingball/hosts/_status_dashboard_content.erb
|
|
108
112
|
- app/views/foreman_wreckingball/hosts/_status_dashboard_empty.erb
|
|
113
|
+
- app/views/foreman_wreckingball/hosts/_status_managed_hosts_dashboard_cards.html.erb
|
|
114
|
+
- app/views/foreman_wreckingball/hosts/_status_managed_hosts_dashboard_cards_card.html.erb
|
|
109
115
|
- app/views/foreman_wreckingball/hosts/_status_row.html.erb
|
|
116
|
+
- app/views/foreman_wreckingball/hosts/_status_row_actions.html.erb
|
|
110
117
|
- app/views/foreman_wreckingball/hosts/_status_row_hosts_table.html.erb
|
|
118
|
+
- app/views/foreman_wreckingball/hosts/_status_row_hosts_table_actions.html.erb
|
|
111
119
|
- app/views/foreman_wreckingball/hosts/schedule_remediate.html.erb
|
|
112
120
|
- app/views/foreman_wreckingball/hosts/status_dashboard.html.erb
|
|
113
121
|
- app/views/foreman_wreckingball/hosts/status_hosts.json.rabl
|
|
@@ -129,6 +137,7 @@ files:
|
|
|
129
137
|
- locale/en/foreman_wreckingball.po
|
|
130
138
|
- locale/foreman_wreckingball.pot
|
|
131
139
|
- locale/gemspec.rb
|
|
140
|
+
- test/actions/foreman_wreckingball/bulk_remediate_test.rb
|
|
132
141
|
- test/actions/foreman_wreckingball/host/refresh_vmware_facet_test.rb
|
|
133
142
|
- test/actions/foreman_wreckingball/host/remediate_hardware_version_test.rb
|
|
134
143
|
- test/actions/foreman_wreckingball/host/remediate_spectre_v2_test.rb
|
|
@@ -141,6 +150,7 @@ files:
|
|
|
141
150
|
- test/factories/foreman_wreckingball_factories.rb
|
|
142
151
|
- test/factories/host.rb
|
|
143
152
|
- test/factories/task.rb
|
|
153
|
+
- test/helpers/foreman_wreckingball/status_helper.rb
|
|
144
154
|
- test/integration/hosts_status_dashboard_test.rb
|
|
145
155
|
- test/integration/hosts_status_managed_hosts_test.rb
|
|
146
156
|
- test/integration_test_plugin_helper.rb
|
|
@@ -210,9 +220,11 @@ test_files:
|
|
|
210
220
|
- test/actions/foreman_wreckingball/host/remediate_hardware_version_test.rb
|
|
211
221
|
- test/actions/foreman_wreckingball/host/refresh_vmware_facet_test.rb
|
|
212
222
|
- test/actions/foreman_wreckingball/host/remediate_spectre_v2_test.rb
|
|
223
|
+
- test/actions/foreman_wreckingball/bulk_remediate_test.rb
|
|
213
224
|
- test/actions/foreman_wreckingball/vmware/sync_compute_resource_test.rb
|
|
214
225
|
- test/actions/foreman_wreckingball/vmware/schedule_vmware_sync_test.rb
|
|
215
226
|
- test/test_plugin_helper.rb
|
|
216
227
|
- test/controllers/compute_resources_controller_test.rb
|
|
217
228
|
- test/controllers/foreman_wreckingball/hosts_controller_test.rb
|
|
229
|
+
- test/helpers/foreman_wreckingball/status_helper.rb
|
|
218
230
|
- test/integration_test_plugin_helper.rb
|