foreman_wreckingball 3.2.0 → 3.3.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|