foreman_wreckingball 3.0.1 → 3.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. checksums.yaml +4 -4
  2. data/app/assets/javascripts/foreman_wreckingball/status_hosts_table.js +49 -0
  3. data/app/assets/stylesheets/foreman_wreckingball/status_hosts_table.css.scss +13 -0
  4. data/app/controllers/foreman_wreckingball/hosts_controller.rb +53 -13
  5. data/app/helpers/concerns/foreman_wreckingball/hosts_helper_extensions.rb +0 -15
  6. data/app/lib/actions/foreman_wreckingball/host/remediate_hardware_version.rb +63 -0
  7. data/app/lib/fog_extensions/foreman_wreckingball/vsphere/mock.rb +13 -0
  8. data/app/lib/fog_extensions/foreman_wreckingball/vsphere/real.rb +7 -0
  9. data/app/lib/fog_extensions/foreman_wreckingball/vsphere/server.rb +14 -0
  10. data/app/models/concerns/foreman_wreckingball/host_extensions.rb +5 -0
  11. data/app/models/foreman_wreckingball/hardware_version_status.rb +62 -0
  12. data/app/models/foreman_wreckingball/spectre_v2_status.rb +2 -2
  13. data/app/models/foreman_wreckingball/tools_status.rb +1 -1
  14. data/app/models/foreman_wreckingball/vmware_facet.rb +9 -0
  15. data/app/models/setting/wreckingball.rb +22 -0
  16. data/app/services/foreman_wreckingball/vmware_cluster_importer.rb +6 -1
  17. data/app/services/foreman_wreckingball/vmware_hypervisor_importer.rb +16 -1
  18. data/app/views/foreman_wreckingball/hosts/_hosts.json.rabl +41 -0
  19. data/app/views/foreman_wreckingball/hosts/_status_dashboard_content.erb +1 -2
  20. data/app/views/foreman_wreckingball/hosts/_status_row.html.erb +8 -43
  21. data/app/views/foreman_wreckingball/hosts/_status_row_hosts_table.html.erb +17 -0
  22. data/app/views/foreman_wreckingball/hosts/status_dashboard.html.erb +2 -0
  23. data/app/views/foreman_wreckingball/hosts/status_hosts.json.rabl +13 -0
  24. data/config/routes.rb +1 -0
  25. data/db/migrate/20181020174609_add_power_state_to_vmware_facets.rb +7 -0
  26. data/db/migrate/20181021111543_add_indexes_to_vmware_hypervisor_facets.rb +8 -0
  27. data/lib/foreman_wreckingball/engine.rb +17 -1
  28. data/lib/foreman_wreckingball/version.rb +1 -1
  29. data/test/actions/foreman_wreckingball/host/refresh_vmware_facet_test.rb +2 -1
  30. data/test/actions/foreman_wreckingball/host/remediate_hardware_version_test.rb +63 -0
  31. data/test/actions/foreman_wreckingball/host/remediate_vmware_operatingsystem_test.rb +3 -1
  32. data/test/controllers/foreman_wreckingball/hosts_controller_test.rb +19 -0
  33. data/test/factories/compute_resource.rb +5 -0
  34. data/test/factories/foreman_wreckingball_factories.rb +15 -0
  35. data/test/factories/host.rb +8 -0
  36. data/test/models/foreman_wreckingball/hardware_version_status_test.rb +88 -0
  37. data/test/models/foreman_wreckingball/tools_status_test.rb +5 -2
  38. data/test/models/foreman_wreckingball/vmware_facet_test.rb +23 -2
  39. data/test/models/host_test.rb +1 -0
  40. data/test/unit/foreman_wreckingball/vmware_hypervisor_importer_test.rb +11 -0
  41. metadata +18 -3
  42. data/app/services/foreman_wreckingball/debris_collector.rb +0 -249
@@ -2,7 +2,6 @@
2
2
 
3
3
  module ForemanWreckingball
4
4
  class VmwareClusterImporter
5
- delegate :logger, :to => :Rails
6
5
  attr_accessor :compute_resource, :counters
7
6
 
8
7
  def initialize(options = {})
@@ -39,5 +38,11 @@ module ForemanWreckingball
39
38
  def cluster_names
40
39
  @cluster_names ||= compute_resource.clusters
41
40
  end
41
+
42
+ private
43
+
44
+ def logger
45
+ ::Foreman::Logging.logger('foreman_wreckingball/import')
46
+ end
42
47
  end
43
48
  end
@@ -2,7 +2,6 @@
2
2
 
3
3
  module ForemanWreckingball
4
4
  class VmwareHypervisorImporter
5
- delegate :logger, :to => :Rails
6
5
  attr_accessor :compute_resource, :counters
7
6
 
8
7
  def initialize(options = {})
@@ -16,6 +15,7 @@ module ForemanWreckingball
16
15
  compute_resource.refresh_cache
17
16
  compute_resource.vmware_clusters.each do |cluster|
18
17
  import_hypervisors(cluster)
18
+ delete_removed_hypervisors(cluster)
19
19
  end
20
20
  logger.info("Import hypervisors for '#{compute_resource}' completed. Added: #{counters[:added] || 0}, Updated: #{counters[:updated] || 0}, Deleted: #{counters[:deleted] || 0} hypervisors") # rubocop:disable Metrics/LineLength
21
21
  end
@@ -101,6 +101,17 @@ module ForemanWreckingball
101
101
  host
102
102
  end
103
103
 
104
+ def delete_removed_hypervisors(cluster)
105
+ hypervisor_names = hypervisors(cluster).map(&:name)
106
+ hypervisor_uuids = hypervisors(cluster).map(&:uuid)
107
+ delete_query = ::ForemanWreckingball::VmwareHypervisorFacet.joins(:host).where(vmware_cluster: cluster).where.not('hosts.name': hypervisor_names).where.not(uuid: hypervisor_uuids)
108
+ counters[:deleted] = if ActiveRecord::Base.connection.adapter_name.downcase.starts_with?('mysql')
109
+ ::ForemanWreckingball::VmwareHypervisorFacet.where(:id => delete_query.pluck(:id)).delete_all
110
+ else
111
+ delete_query.delete_all
112
+ end
113
+ end
114
+
104
115
  def organization
105
116
  return unless SETTINGS[:organizations_enabled]
106
117
  compute_resource.organizations.first
@@ -122,5 +133,9 @@ module ForemanWreckingball
122
133
  counters[id] ||= 0
123
134
  counters[id] += 1
124
135
  end
136
+
137
+ def logger
138
+ ::Foreman::Logging.logger('foreman_wreckingball/import')
139
+ end
125
140
  end
126
141
  end
@@ -0,0 +1,41 @@
1
+ # frozen_string_literal: true
2
+
3
+ collection @hosts
4
+
5
+ attributes :name
6
+
7
+ child owner: :owner do
8
+ attribute :name
9
+ end
10
+
11
+ child :environment do
12
+ attribute :name
13
+ end
14
+
15
+ node(:path) { |host| host_path(host) }
16
+
17
+ node(:status) do |host|
18
+ status = host.public_send(locals[:host_association])
19
+ {
20
+ label: status.to_label,
21
+ icon_class: host_global_status_icon_class(status.to_global),
22
+ status_class: host_global_status_class(status.to_global)
23
+ }
24
+ end
25
+
26
+ node(:remediate, if: lambda do |host|
27
+ locals[:supports_remediate] && begin
28
+ options = hash_for_schedule_remediate_host_path(id: host,
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
36
+ {
37
+ label: _('Remediate'),
38
+ title: _('Remediate Host OS'),
39
+ path: schedule_remediate_host_path(host, status_id: status_id)
40
+ }
41
+ end
@@ -11,9 +11,8 @@
11
11
  render :partial => 'status_row', locals: {
12
12
  name: status[:name],
13
13
  description: status[:description],
14
- counter: vmware_status_counter(status[:hosts], status[:host_association]),
14
+ counter: status[:counter],
15
15
  status: status[:host_association],
16
- hosts: status[:hosts],
17
16
  supports_remediate: status[:supports_remediate],
18
17
  id: idx
19
18
  }
@@ -1,4 +1,4 @@
1
- <div class="list-group-item list-view-pf-stacked list-view-pf-top-align">
1
+ <div class="status-row list-group-item list-view-pf-stacked list-view-pf-top-align">
2
2
  <div class="list-group-item-header">
3
3
  <div class="list-view-pf-expand">
4
4
  <span class="fa fa-angle-right"></span>
@@ -53,50 +53,15 @@
53
53
  <span class="pficon pficon-close"></span>
54
54
  </div>
55
55
  <div class="row">
56
- <div class="col-md-12">
56
+ <div class="col-md-12 status-hosts-container">
57
57
  <% if counter[:warning] > 0 || counter[:critical] > 0 %>
58
58
  <p><%= _('The following host require manual attention:') %></p>
59
- <table class="table table-striped table-fixed">
60
- <thead>
61
- <tr>
62
- <th><%= _('Hostname') %></th>
63
- <th><%= _('Status') %></th>
64
- <th><%= _('Owner') %></th>
65
- <th><%= _('Puppet Environment') %></th>
66
- <% if supports_remediate %>
67
- <th><%= _('Actions') %></th>
68
- <% end %>
69
- </tr>
70
- </thead>
71
- <tbody>
72
- <% hosts.each do |host| %>
73
- <% next if host.public_send(status).to_global == HostStatus::Global::OK %>
74
- <tr>
75
- <td class="ellipsis"><%= link_to_if_authorized(host.name, hash_for_host_path(:id => host)) %></td>
76
- <td>
77
- <span class="<%= host_global_status_icon_class(host.public_send(status).to_global) %>"></span>
78
- <span class="<%= host_global_status_class(host.public_send(status).to_global) %>"><%= _(host.public_send(status).to_label) %></span>
79
- </td>
80
- <td class="ellipsis"><%= host.owner %></td>
81
- <td class="ellipsis"><%= host.environment %></td>
82
- <% if supports_remediate %>
83
- <td>
84
- <%=
85
- action_buttons(
86
- display_link_if_authorized(
87
- _('Remediate'),
88
- hash_for_schedule_remediate_host_path(:id => host, :status_id => host.public_send(status).id).merge(:auth_object => host, :permission => 'remediate_vmware_status_hosts'),
89
- :data => { :title => _('Remediate Host OS'), :submit_class => 'btn-danger' },
90
- :onclick => 'show_modal(this); return false;'
91
- )
92
- )
93
- %>
94
- </td>
95
- <% end %>
96
- </tr>
97
- <% end %>
98
- </tbody>
99
- </table>
59
+ <%=
60
+ render :partial => 'status_row_hosts_table', locals: {
61
+ status: status,
62
+ supports_remediate: supports_remediate
63
+ }
64
+ %>
100
65
  <% else %>
101
66
  <p><%= _('No manual actions required.') %></p>
102
67
  <% end %>
@@ -0,0 +1,17 @@
1
+ <%= content_tag :table, id: status,
2
+ class: 'table table-striped table-fixed status-hosts',
3
+ 'data-hosts-url': ajax_status_dashboard_hosts_path(status) do %>
4
+ <%= content_tag :thead do %>
5
+ <%= content_tag :tr do %>
6
+ <%= content_tag :th, _('Hostname') %>
7
+ <%= content_tag :th, _('Status') %>
8
+ <%= content_tag :th, _('Owner') %>
9
+ <%= content_tag :th, _('Puppet Environment') %>
10
+ <%= content_tag :th, _('Actions') %>
11
+ <% end %>
12
+ <% end %>
13
+ <%= content_tag(:tbody) {} %>
14
+ <% end %>
15
+
16
+ <%= alert header: _("Oops, we're sorry but something went wrong"), text: '',
17
+ class: 'alert-danger', close: false %>
@@ -1,5 +1,7 @@
1
1
  <% title _('VMware Status Overview') %>
2
2
  <% javascript 'foreman_wreckingball/modal' %>
3
+ <% javascript 'foreman_wreckingball/status_hosts_table' %>
4
+ <% stylesheet 'foreman_wreckingball/status_hosts_table' %>
3
5
  <% javascript 'foreman_tasks/trigger_form' %>
4
6
  <% stylesheet 'foreman_tasks/trigger_form' %>
5
7
 
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ object false
4
+
5
+ node(:recordsTotal) { @count }
6
+ node(:recordsFiltered) { @count }
7
+ node(:data) do
8
+ partial 'foreman_wreckingball/hosts/_hosts', object: @hosts,
9
+ locals: {
10
+ host_association: @status.host_association,
11
+ supports_remediate: @status.supports_remediate?
12
+ }
13
+ end
data/config/routes.rb CHANGED
@@ -10,6 +10,7 @@ Rails.application.routes.draw do
10
10
  end
11
11
  collection do
12
12
  get :status_dashboard
13
+ get 'status_dashboard/hosts(/:status)', as: :ajax_status_dashboard, action: :status_hosts, defaults: { format: :json }
13
14
  put :refresh_status_dashboard
14
15
  end
15
16
  end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddPowerStateToVmwareFacets < ActiveRecord::Migration[5.1]
4
+ def change
5
+ add_column :vmware_facets, :power_state, :integer, default: 1, index: true
6
+ end
7
+ end
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ class AddIndexesToVmwareHypervisorFacets < ActiveRecord::Migration[5.1]
4
+ def change
5
+ add_index :vmware_hypervisor_facets, :uuid
6
+ add_index :vmware_hypervisor_facets, [:vmware_cluster_id, :uuid]
7
+ end
8
+ end
@@ -23,6 +23,15 @@ module ForemanWreckingball
23
23
  end
24
24
  end
25
25
 
26
+ initializer 'foreman_wreckingball.load_default_settings', before: :load_config_initializers do
27
+ table_exists = begin
28
+ Setting.table_exists?
29
+ rescue StandardError
30
+ false
31
+ end
32
+ require_dependency File.expand_path('../../app/models/setting/wreckingball.rb', __dir__) if table_exists
33
+ end
34
+
26
35
  initializer 'foreman_wreckingball.register_plugin', :before => :finisher_hook do |_app|
27
36
  Foreman::Plugin.register :foreman_wreckingball do
28
37
  requires_foreman '>= 1.18'
@@ -49,6 +58,8 @@ module ForemanWreckingball
49
58
  register_custom_status(ForemanWreckingball::ToolsStatus)
50
59
  register_custom_status(ForemanWreckingball::OperatingsystemStatus)
51
60
  register_custom_status(ForemanWreckingball::CpuHotAddStatus)
61
+ register_custom_status(ForemanWreckingball::SpectreV2Status)
62
+ register_custom_status(ForemanWreckingball::HardwareVersionStatus)
52
63
 
53
64
  register_facet(ForemanWreckingball::VmwareFacet, :vmware_facet)
54
65
 
@@ -63,6 +74,9 @@ module ForemanWreckingball
63
74
  :partial => 'compute_resources/hypervisors_tab',
64
75
  :onlyif => proc { |cr| cr.provider_friendly_name == 'VMware' && cr.vmware_hypervisor_facets.any? }
65
76
  end
77
+
78
+ # add custom logger
79
+ logger :import, enabled: true
66
80
  end
67
81
  end
68
82
 
@@ -82,7 +96,9 @@ module ForemanWreckingball
82
96
 
83
97
  if ForemanWreckingball.fog_patches_required?
84
98
  Fog::Compute::Vsphere::Host.send(:include, FogExtensions::ForemanWreckingball::Vsphere::Host)
99
+ Fog::Compute::Vsphere::Server.send(:include, FogExtensions::ForemanWreckingball::Vsphere::Server)
85
100
  Fog::Compute::Vsphere::Real.send(:include, FogExtensions::ForemanWreckingball::Vsphere::Real)
101
+ Fog::Compute::Vsphere::Mock.send(:include, FogExtensions::ForemanWreckingball::Vsphere::Mock)
86
102
  end
87
103
  rescue StandardError => e
88
104
  Rails.logger.warn "ForemanWreckingball: skipping engine hook (#{e})\n#{e.backtrace.join("\n")}"
@@ -103,7 +119,7 @@ module ForemanWreckingball
103
119
  require 'fog/vsphere'
104
120
  require 'fog/vsphere/compute'
105
121
  require 'fog/vsphere/models/compute/host'
106
- !::Fog::Compute::Vsphere::Host.instance_methods.include?(:feature_capabilities)
122
+ true
107
123
  rescue LoadError
108
124
  false
109
125
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ForemanWreckingball
4
- VERSION = '3.0.1'
4
+ VERSION = '3.1.0'
5
5
  end
@@ -18,11 +18,12 @@ module Actions
18
18
  ::ForemanWreckingball::SpectreV2Status.any_instance.stubs(:recent_hw_version?).returns(true)
19
19
  # this is not stubbed correctly in fog-vsphere
20
20
  Fog::Compute::Vsphere::Server.any_instance.stubs(:cpuHotAddEnabled).returns(false)
21
+ Setting::Wreckingball.load_defaults
21
22
  end
22
23
  teardown { ::Fog.unmock! }
23
24
 
24
25
  let(:compute_resource) do
25
- cr = FactoryBot.create(:compute_resource, :vmware, :uuid => 'Solutions')
26
+ cr = FactoryBot.create(:compute_resource, :vmware, :with_taxonomy, :uuid => 'Solutions')
26
27
  ComputeResource.find(cr.id)
27
28
  end
28
29
 
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'test_plugin_helper'
4
+
5
+ module Actions
6
+ module ForemanWreckingball
7
+ module Vmware
8
+ class RemediateHardwareVersionTest < ActiveSupport::TestCase
9
+ include ::Dynflow::Testing
10
+ setup do
11
+ ::Fog.mock!
12
+ # this is not stubbed correctly in fog-vsphere
13
+ Fog::Compute::Vsphere::Server.any_instance.stubs(:cpuHotAddEnabled).returns(false)
14
+ Fog::Compute::Vsphere::Server.any_instance.stubs(:hardware_version).returns('vmx-13')
15
+ ::ForemanWreckingball::SpectreV2Status.any_instance.stubs(:recent_hw_version?).returns(true)
16
+ ::PowerManager::Virt.any_instance.stubs(:ready?).returns(true)
17
+ Setting::Wreckingball.load_defaults
18
+ end
19
+ teardown { ::Fog.unmock! }
20
+
21
+ let(:compute_resource) do
22
+ cr = FactoryBot.create(:compute_resource, :vmware, :with_taxonomy, :uuid => 'Solutions')
23
+ ComputeResource.find(cr.id)
24
+ end
25
+ let(:uuid) { '5032c8a5-9c5e-ba7a-3804-832a03e16381' }
26
+ let(:vm) { compute_resource.find_vm_by_uuid(uuid) }
27
+
28
+ let(:host) do
29
+ FactoryBot.create(
30
+ :host,
31
+ :managed,
32
+ :with_vmware_facet,
33
+ compute_resource: compute_resource,
34
+ uuid: uuid
35
+ )
36
+ end
37
+
38
+ let(:action_class) { ::Actions::ForemanWreckingball::Host::RemediateHardwareVersion }
39
+ let(:action) do
40
+ create_action(action_class).tap do |action|
41
+ action.stubs(:action_subject).returns(host)
42
+ action.input.update(
43
+ host: {
44
+ id: host.id
45
+ }
46
+ )
47
+ end
48
+ end
49
+ let(:planned_action) do
50
+ plan_action(action, host)
51
+ end
52
+ let(:runned_action) { run_action(planned_action) }
53
+
54
+ test "it remediates the host's hardware version" do
55
+ assert_equal :success, runned_action.state
56
+ assert_equal true, runned_action.output.fetch('state')
57
+ assert_equal true, runned_action.output.fetch('initially_powered_on')
58
+ assert_equal 'vmx-13', host.reload.vmware_facet.hardware_version
59
+ end
60
+ end
61
+ end
62
+ end
63
+ end
@@ -12,11 +12,13 @@ module Actions
12
12
  # this is not stubbed correctly in fog-vsphere
13
13
  Fog::Compute::Vsphere::Server.any_instance.stubs(:cpuHotAddEnabled).returns(false)
14
14
  ::ForemanWreckingball::SpectreV2Status.any_instance.stubs(:recent_hw_version?).returns(true)
15
+ ::PowerManager::Virt.any_instance.stubs(:ready?).returns(true)
16
+ Setting::Wreckingball.load_defaults
15
17
  end
16
18
  teardown { ::Fog.unmock! }
17
19
 
18
20
  let(:compute_resource) do
19
- cr = FactoryBot.create(:compute_resource, :vmware, :uuid => 'Solutions')
21
+ cr = FactoryBot.create(:compute_resource, :vmware, :with_taxonomy, :uuid => 'Solutions')
20
22
  ComputeResource.find(cr.id)
21
23
  end
22
24
  let(:uuid) { '5032c8a5-9c5e-ba7a-3804-832a03e16381' }
@@ -11,6 +11,10 @@ module ForemanWreckingball
11
11
  )
12
12
  end
13
13
 
14
+ setup do
15
+ Setting::Wreckingball.load_defaults
16
+ end
17
+
14
18
  describe '#status_dashboard' do
15
19
  test 'shows an empty status page' do
16
20
  get :status_dashboard, session: set_session_user
@@ -24,6 +28,21 @@ module ForemanWreckingball
24
28
  end
25
29
  end
26
30
 
31
+ describe '#status_hosts' do
32
+ test 'returns hosts for status' do
33
+ ok_status = FactoryBot.create(:vmware_hardware_version_status, :with_ok_status)
34
+ out_of_date_status = FactoryBot.create(:vmware_hardware_version_status, :with_out_of_date_status)
35
+
36
+ get :status_hosts, params: { status: ::ForemanWreckingball::HardwareVersionStatus.host_association },
37
+ session: set_session_user, xhr: true
38
+
39
+ assert_response :ok
40
+ hosts_names = JSON.parse(response.body)['data'].map { |host| host['name'] }
41
+ assert_includes hosts_names, out_of_date_status.host.name
42
+ refute_includes hosts_names, ok_status.host.name
43
+ end
44
+ end
45
+
27
46
  describe '#refresh_status_dashboard' do
28
47
  test 'redirects to scheduled task' do
29
48
  ForemanTasks.expects(:async_task).returns(fake_task)
@@ -11,5 +11,10 @@ FactoryBot.modify do
11
11
  create_list(:vmware_cluster, evaluator.vmware_clusters_count, compute_resource: compute_resource)
12
12
  end
13
13
  end
14
+
15
+ trait :with_taxonomy do
16
+ organizations { [Organization.find_by(name: 'Organization 1')] }
17
+ locations { [Location.find_by(name: 'Location 1')] }
18
+ end
14
19
  end
15
20
  end