foreman_wreckingball 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +619 -0
  3. data/README.md +37 -0
  4. data/Rakefile +47 -0
  5. data/app/controllers/foreman_wreckingball/hosts_controller.rb +60 -0
  6. data/app/helpers/concerns/foreman_wreckingball/hosts_helper_extensions.rb +26 -0
  7. data/app/jobs/update_hosts_vmware_facets.rb +8 -0
  8. data/app/lib/actions/foreman_wreckingball/host/refresh_vmware_facet.rb +32 -0
  9. data/app/lib/actions/foreman_wreckingball/host/remediate_vmware_operatingsystem.rb +82 -0
  10. data/app/lib/actions/foreman_wreckingball/vmware/schedule_vmware_sync.rb +24 -0
  11. data/app/lib/actions/foreman_wreckingball/vmware/sync_compute_resource.rb +45 -0
  12. data/app/lib/fog_extensions/foreman_wreckingball/vsphere/host.rb +38 -0
  13. data/app/lib/fog_extensions/foreman_wreckingball/vsphere/real.rb +85 -0
  14. data/app/lib/vsphere_os_identifiers/data.yaml +538 -0
  15. data/app/lib/vsphere_os_identifiers/os.rb +19 -0
  16. data/app/lib/vsphere_os_identifiers.rb +34 -0
  17. data/app/models/concerns/foreman_wreckingball/compute_resource_extensions.rb +15 -0
  18. data/app/models/concerns/foreman_wreckingball/host_extensions.rb +15 -0
  19. data/app/models/concerns/foreman_wreckingball/vmware_extensions.rb +9 -0
  20. data/app/models/concerns/foreman_wreckingball/vmware_facet_host_extensions.rb +26 -0
  21. data/app/models/concerns/foreman_wreckingball/vmware_hypervisor_facet_host_extensions.rb +13 -0
  22. data/app/models/foreman_wreckingball/cpu_hot_add_status.rb +58 -0
  23. data/app/models/foreman_wreckingball/operatingsystem_status.rb +65 -0
  24. data/app/models/foreman_wreckingball/tools_status.rb +46 -0
  25. data/app/models/foreman_wreckingball/vmware_cluster.rb +14 -0
  26. data/app/models/foreman_wreckingball/vmware_facet.rb +53 -0
  27. data/app/models/foreman_wreckingball/vmware_hypervisor_facet.rb +18 -0
  28. data/app/services/foreman_wreckingball/vmware_cluster_importer.rb +41 -0
  29. data/app/services/foreman_wreckingball/vmware_hypervisor_importer.rb +119 -0
  30. data/app/views/foreman_wreckingball/hosts/_status_dashboard_content.erb +43 -0
  31. data/app/views/foreman_wreckingball/hosts/_status_dashboard_empty.erb +13 -0
  32. data/app/views/foreman_wreckingball/hosts/_status_row.html.erb +107 -0
  33. data/app/views/foreman_wreckingball/hosts/status_dashboard.html.erb +9 -0
  34. data/config/routes.rb +15 -0
  35. data/db/migrate/20171106155000_create_vmware_facets.rb +35 -0
  36. data/lib/foreman_wreckingball/engine.rb +97 -0
  37. data/lib/foreman_wreckingball/scheduled_jobs.rb +10 -0
  38. data/lib/foreman_wreckingball/version.rb +3 -0
  39. data/lib/foreman_wreckingball.rb +4 -0
  40. data/lib/tasks/foreman_vmware_checks_tasks.rake +49 -0
  41. data/locale/Makefile +60 -0
  42. data/locale/en/foreman_wreckingball.po +19 -0
  43. data/locale/foreman_wreckingball.pot +19 -0
  44. data/locale/gemspec.rb +2 -0
  45. data/test/test_plugin_helper.rb +6 -0
  46. data/test/unit/foreman_wreckingball_test.rb +11 -0
  47. metadata +133 -0
@@ -0,0 +1,58 @@
1
+ module ForemanWreckingball
2
+ class CpuHotAddStatus < ::HostStatus::Status
3
+ OK = 0
4
+ PERFORMANCE_DEGRATION = 1
5
+
6
+ def self.status_name
7
+ N_('CPU Hot Plug')
8
+ end
9
+
10
+ def self.host_association
11
+ :vmware_cpu_hot_add_status_object
12
+ end
13
+
14
+ def self.description
15
+ N_('Enabling CPU hot-add disables vNUMA, the virtual machine will instead use UMA. This might cause a performance degration.')
16
+ end
17
+
18
+ def self.supports_remediate?
19
+ false
20
+ end
21
+
22
+ def to_status(_options = {})
23
+ performance_degration? ? PERFORMANCE_DEGRATION : OK
24
+ end
25
+
26
+ def to_global(_options = {})
27
+ case status
28
+ when PERFORMANCE_DEGRATION
29
+ HostStatus::Global::ERROR
30
+ else
31
+ HostStatus::Global::OK
32
+ end
33
+ end
34
+
35
+ def to_label(_options = {})
36
+ case status
37
+ when PERFORMANCE_DEGRATION
38
+ N_('Possible performance degration')
39
+ else
40
+ N_('No Impact')
41
+ end
42
+ end
43
+
44
+ def relevant?(_options = {})
45
+ host && !!host.vmware_facet && host.vmware_facet.try(:cpu_hot_add?)
46
+ end
47
+
48
+ def performance_degration?
49
+ min_cores = hypervisor_min_cores
50
+ return false unless min_cores
51
+ host.vmware_facet.cpu_hot_add? && host.vmware_facet.cpus > min_cores
52
+ end
53
+
54
+ def hypervisor_min_cores
55
+ host.vmware_facet.vmware_hypervisor_facets.minimum(:cpu_cores)
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,65 @@
1
+ module ForemanWreckingball
2
+ class OperatingsystemStatus < ::HostStatus::Status
3
+ OK = 0
4
+ MISMATCH = 1
5
+
6
+ def self.status_name
7
+ N_('VM Operatingsystem')
8
+ end
9
+
10
+ def self.host_association
11
+ :vmware_operatingsystem_status_object
12
+ end
13
+
14
+ def self.description
15
+ N_('The VM operatingsystem should match the operatingsystem installed.')
16
+ end
17
+
18
+ def self.supports_remediate?
19
+ true
20
+ end
21
+
22
+ def self.remediate_action
23
+ ::Actions::ForemanWreckingball::Host::RemediateVmwareOperatingsystem
24
+ end
25
+
26
+ def to_status(_options = {})
27
+ os_matches_identifier? ? OK : MISMATCH
28
+ end
29
+
30
+ def to_global(_options = {})
31
+ case status
32
+ when MISMATCH
33
+ HostStatus::Global::WARN
34
+ else
35
+ HostStatus::Global::OK
36
+ end
37
+ end
38
+
39
+ def to_label(_options = {})
40
+ case status
41
+ when MISMATCH
42
+ N_('VM OS is incorrect')
43
+ else
44
+ N_('OK')
45
+ end
46
+ end
47
+
48
+ def relevant?(_options = {})
49
+ host && !!host.vmware_facet
50
+ end
51
+
52
+ def os_matches_identifier?
53
+ guest_id = host.vmware_facet.guest_id
54
+ vsphere_os = VsphereOsIdentifiers.lookup(guest_id)
55
+ return true unless vsphere_os
56
+ os = host.operatingsystem
57
+ return true unless host.operatingsystem && host.architecture
58
+ return false if vsphere_os.architecture && vsphere_os.architecture != host.architecture.name
59
+ return false if vsphere_os.osfamily && vsphere_os.osfamily != host.operatingsystem.family
60
+ return false if vsphere_os.name && vsphere_os.name != host.operatingsystem.name
61
+ return false if vsphere_os.major && vsphere_os.major != host.operatingsystem.major.to_i
62
+ true
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,46 @@
1
+ module ForemanWreckingball
2
+ class ToolsStatus < ::HostStatus::Status
3
+ POWERDOWN = 10
4
+
5
+ def self.status_name
6
+ N_('VMware Tools')
7
+ end
8
+
9
+ def self.host_association
10
+ :vmware_tools_status_object
11
+ end
12
+
13
+ def self.description
14
+ N_('VMWare Tools should be running and up-to-date.')
15
+ end
16
+
17
+ def self.supports_remediate?
18
+ false
19
+ end
20
+
21
+ def to_status(_options = {})
22
+ return POWERDOWN unless host.supports_power_and_running?
23
+ VmwareFacet.tools_states[host.vmware_facet.tools_state]
24
+ end
25
+
26
+ def to_global(_options = {})
27
+ case status
28
+ when VmwareFacet.tools_states[:toolsOk], POWERDOWN
29
+ HostStatus::Global::OK
30
+ when VmwareFacet.tools_states[:toolsOld]
31
+ HostStatus::Global::WARN
32
+ else
33
+ HostStatus::Global::ERROR
34
+ end
35
+ end
36
+
37
+ def to_label(_options = {})
38
+ return N_('Powered down') if status == POWERDOWN
39
+ host.vmware_facet.tools_state_label
40
+ end
41
+
42
+ def relevant?(_options = {})
43
+ host && !host.build? && !!host.vmware_facet
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,14 @@
1
+ module ForemanWreckingball
2
+ class VmwareCluster < ActiveRecord::Base
3
+ has_many :vmware_hypervisor_facets, :class_name => '::ForemanWreckingball::VmwareHypervisorFacet'
4
+ has_many :hosts, :class_name => '::Host::Managed', :through => :vmware_hypervisor_facets,
5
+ :inverse_of => :vmware_cluster
6
+ belongs_to :compute_resource, :inverse_of => :vmware_clusters
7
+ has_many :vmware_facets, :class_name => '::ForemanWreckingball::VmwareFacet', :inverse_of => :vmware_clusters
8
+
9
+ validates_lengths_from_database
10
+
11
+ validates :compute_resource, :presence => true, :allow_blank => false
12
+ validates :name, :presence => true, :allow_blank => false, uniqueness: { scope: :compute_resource}
13
+ end
14
+ end
@@ -0,0 +1,53 @@
1
+ module ForemanWreckingball
2
+ class VmwareFacet < ActiveRecord::Base
3
+ include Facets::Base
4
+
5
+ VALID_GUEST_STATUSES = [:toolsNotInstalled, :toolsNotRunning, :toolsOk, :toolsOld].freeze
6
+
7
+ enum :tools_state => VALID_GUEST_STATUSES
8
+
9
+ belongs_to :vmware_cluster, :class_name => '::ForemanWreckingball::VmwareCluster',
10
+ :inverse_of => :vmware_facets
11
+
12
+ has_many :vmware_hypervisor_facets, :class_name => '::ForemanWreckingball::VmwareHypervisorFacet', :through => :vmware_cluster,
13
+ :inverse_of => :vmware_facets
14
+
15
+ validates_lengths_from_database
16
+
17
+ validates :host, :presence => true, :allow_blank => false
18
+
19
+ def tools_state_label
20
+ case tools_state.to_sym
21
+ when :toolsNotInstalled
22
+ N_('Not installed')
23
+ when :toolsNotRunning
24
+ N_('Not running')
25
+ when :toolsOk
26
+ N_('OK')
27
+ when :toolsOld
28
+ N_('Out of date')
29
+ end
30
+ end
31
+
32
+ def refresh!
33
+ vm = host.compute_object
34
+ return unless vm
35
+ self.update(
36
+ :vmware_cluster => ::ForemanWreckingball::VmwareCluster.find_by(:name => vm.cluster, :compute_resource => host.compute_resource),
37
+ :cpus => vm.cpus,
38
+ :corespersocket => vm.corespersocket,
39
+ :memory_mb => vm.memory_mb,
40
+ :tools_state => vm.tools_state,
41
+ :guest_id => vm.guest_id,
42
+ :cpu_hot_add => vm.cpuHotAddEnabled
43
+ )
44
+ end
45
+
46
+ def refresh_statuses
47
+ host.get_status(::ForemanWreckingball::ToolsStatus).refresh!
48
+ host.get_status(::ForemanWreckingball::CpuHotAddStatus).refresh!
49
+ host.get_status(::ForemanWreckingball::OperatingsystemStatus).refresh!
50
+ host.refresh_global_status!
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,18 @@
1
+ module ForemanWreckingball
2
+ class VmwareHypervisorFacet < ActiveRecord::Base
3
+ include Facets::Base
4
+
5
+ validates_lengths_from_database
6
+
7
+ validates :host, :presence => true, :allow_blank => false
8
+
9
+ belongs_to :vmware_cluster, :inverse_of => :vmware_hypervisor_facets, :class_name => 'ForemanWreckingball::VmwareCluster'
10
+
11
+ has_many :vmware_facets, :class_name => '::ForemanWreckingball::VmwareFacet', :through => :vmware_clusters,
12
+ :inverse_of => :vmware_hypervisor_facets
13
+
14
+ def self.sanitize_name(name)
15
+ name.gsub('_', '-').chomp('.').downcase
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,41 @@
1
+ module ForemanWreckingball
2
+ class VmwareClusterImporter
3
+ delegate :logger, :to => :Rails
4
+ attr_accessor :compute_resource, :counters
5
+
6
+ def initialize(options = {})
7
+ @compute_resource = options.fetch(:compute_resource)
8
+ @counters = {}
9
+ end
10
+
11
+ def import!
12
+ ActiveRecord::Base.transaction do
13
+ delete_removed_clusters
14
+ create_new_clusters
15
+ end
16
+ logger.info("Import clusters for '#{compute_resource}' completed. Added: #{counters[:added] || 0}, Updated: #{counters[:updated] || 0}, Deleted: #{counters[:deleted] || 0} clusters")
17
+ end
18
+
19
+ def delete_removed_clusters
20
+ delete_query = ::ForemanWreckingball::VmwareCluster.where(:compute_resource => compute_resource).where.not(:name => cluster_names)
21
+ if ActiveRecord::Base.connection.adapter_name.downcase.starts_with? 'mysql'
22
+ counters[:deleted] = ::ForemanWreckingball::VmwareCluster.where(:id => delete_query.pluck(:id)).delete_all
23
+ else
24
+ counters[:deleted] = delete_query.delete_all
25
+ end
26
+ end
27
+
28
+ def create_new_clusters
29
+ existing_clusters = ::ForemanWreckingball::VmwareCluster.where(:compute_resource => compute_resource).pluck(:name)
30
+ clusters_to_create = cluster_names - existing_clusters
31
+ clusters_to_create.each do |cluster_name|
32
+ ::ForemanWreckingball::VmwareCluster.create(:name => cluster_name, :compute_resource => compute_resource)
33
+ end
34
+ counters[:added] = clusters_to_create.size
35
+ end
36
+
37
+ def cluster_names
38
+ @cluster_names ||= compute_resource.clusters
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,119 @@
1
+ module ForemanWreckingball
2
+ class VmwareHypervisorImporter
3
+ delegate :logger, :to => :Rails
4
+ attr_accessor :compute_resource, :counters
5
+
6
+ def initialize(options = {})
7
+ @compute_resource = options.fetch(:compute_resource)
8
+ @hypervisors = {}
9
+ @counters = {}
10
+ end
11
+
12
+ def import!
13
+ logger.info "Can not determine organization for compute resource #{compute_resource}." if SETTINGS[:organizations_enabled]
14
+ compute_resource.refresh_cache
15
+ compute_resource.vmware_clusters.each do |cluster|
16
+ import_hypervisors(cluster)
17
+ end
18
+ logger.info("Import hypervisors for '#{compute_resource}' completed. Added: #{counters[:added] || 0}, Updated: #{counters[:updated] || 0}, Deleted: #{counters[:deleted] || 0} hypervisors")
19
+ end
20
+
21
+ def import_hypervisors(cluster)
22
+ hypervisors(cluster).each do |hypervisor|
23
+ import_hypervisor(cluster, hypervisor)
24
+ end
25
+ end
26
+
27
+ def import_hypervisor(cluster, hypervisor)
28
+ host = find_host_for_hypervisor(hypervisor)
29
+
30
+ counter = host.vmware_hypervisor_facet ? :updated : :added
31
+
32
+ ipaddress6 = hypervisor.ipaddress6
33
+ ipaddress6 = nil if (IPAddr.new('fe80::/10').include?(ipaddress6) rescue false)
34
+
35
+ hostname = hypervisor.hostname
36
+ domainname = hypervisor.domainname
37
+
38
+ unless hostname.present? && domainname.present?
39
+ logger.info "Trying to guess host- and domainname for #{hypervisor.name}."
40
+ hostname, domainname = hypervisor.name.split('.', 2)
41
+ end
42
+
43
+ unless hostname.present? && domainname.present?
44
+ logger.error "Could not guess host- and domainname for #{hypervisor.name}. Skipping."
45
+ return false
46
+ end
47
+
48
+ result = host.update(
49
+ :name => hostname,
50
+ :domain => ::Domain.where(:name => domainname).first_or_create,
51
+ :model => ::Model.where(:name => hypervisor.model.strip).first_or_create,
52
+ :ip => hypervisor.ipaddress,
53
+ :ip6 => ipaddress6,
54
+ :vmware_hypervisor_facet_attributes => {
55
+ :vmware_cluster => cluster,
56
+ :cpu_cores => hypervisor.cpu_cores,
57
+ :cpu_sockets => hypervisor.cpu_sockets,
58
+ :cpu_threads => hypervisor.cpu_threads,
59
+ :memory => hypervisor.memory,
60
+ :uuid => hypervisor.uuid
61
+ }
62
+ )
63
+ if result
64
+ increment_counter(counter)
65
+ else
66
+ logger.error "Failed to save host #{host}. Reason: #{host.errors.full_messages.to_sentence}"
67
+ end
68
+ end
69
+
70
+ def hypervisors(cluster)
71
+ @hypervisors[cluster.name.to_sym] ||= compute_resource.hypervisors(:cluster_id => cluster.name)
72
+ end
73
+
74
+ def find_host_for_hypervisor(hypervisor)
75
+ name = ::ForemanWreckingball::VmwareHypervisorFacet.sanitize_name(hypervisor.name)
76
+ hostname = ::ForemanWreckingball::VmwareHypervisorFacet.sanitize_name([hypervisor.hostname, hypervisor.domainname].join('.'))
77
+ hostname = nil unless hostname.present?
78
+ katello_name = katello_hypervisor_hostname(hostname || name)
79
+ katello_uuid = katello_hypervisor_hostname(hypervisor.uuid)
80
+
81
+ host = ::ForemanWreckingball::VmwareHypervisorFacet.find_by(:uuid => hypervisor.uuid).try(:host)
82
+ host ||= ::Host.find_by(:name => hostname) if hostname
83
+ host ||= ::Host.find_by(:name => name)
84
+ host ||= ::Host.find_by(:name => katello_name) if katello_name
85
+ host ||= ::Host.find_by(:name => katello_uuid) if katello_uuid
86
+ host ||= create_host_for_hypervisor(hostname || name)
87
+ host
88
+ end
89
+
90
+ def create_host_for_hypervisor(name)
91
+ host = ::Host::Managed.new(:name => name, :organization => organization,
92
+ :location => location, :managed => false, :enabled => false)
93
+ host.save!
94
+ host
95
+ end
96
+
97
+ def organization
98
+ return unless SETTINGS[:organizations_enabled]
99
+ compute_resource.organizations.first
100
+ end
101
+
102
+ def location
103
+ return unless SETTINGS[:locations_enabled]
104
+ compute_resource.locations.first
105
+ end
106
+
107
+ def katello_hypervisor_hostname(hostname)
108
+ return unless organization
109
+ "virt-who-#{hostname}-#{organization.id}"
110
+ end
111
+
112
+ private
113
+
114
+ def increment_counter(id)
115
+ counters[id] ||= 0
116
+ counters[id] += 1
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,43 @@
1
+ <%=
2
+ alert(:header => _('Notice'),
3
+ :class => 'alert-info',
4
+ :text => _('The status data is not updated automatically and might be out of data. The newest data was recorded on %s.').html_safe % (respond_to?(:date_time_absolute) ? date_time_absolute(@newest_data, :long) : l(@newest_data, :format => :long))
5
+ )
6
+ %>
7
+
8
+ <div class="list-group list-view-pf list-view-pf-equalized-column" style="max-height: initial;">
9
+ <% @data.each_with_index do |status, idx| %>
10
+ <%=
11
+ render :partial => 'status_row', locals: {
12
+ name: status[:name],
13
+ description: status[:description],
14
+ counter: vmware_status_counter(status[:hosts], status[:host_association]),
15
+ status: status[:host_association],
16
+ hosts: status[:hosts],
17
+ supports_remediate: status[:supports_remediate],
18
+ id: idx
19
+ }
20
+ %>
21
+ <% end %>
22
+ </div>
23
+ <script>
24
+ $(document).ready(function () {
25
+ // click the list-view heading then expand a row
26
+ $(".list-group-item-header").click(function(event){
27
+ if(!$(event.target).is("button, a, input, .fa-ellipsis-v")){
28
+ $(this).find(".fa-angle-right").toggleClass("fa-angle-down")
29
+ .end().parent().toggleClass("list-view-pf-expand-active")
30
+ .find(".list-group-item-container").toggleClass("hidden");
31
+ } else {
32
+ }
33
+ })
34
+
35
+ // click the close button, hide the expand row and remove the active status
36
+ $(".list-group-item-container .close").on("click", function (){
37
+ $(this).parent().addClass("hidden")
38
+ .parent().removeClass("list-view-pf-expand-active")
39
+ .find(".fa-angle-right").removeClass("fa-angle-down");
40
+ })
41
+
42
+ });
43
+ </script>
@@ -0,0 +1,13 @@
1
+ <div class="blank-slate-pf">
2
+ <div class="blank-slate-pf-icon">
3
+ <%= icon_text('tachometer', '', :kind => 'fa') %>
4
+ </div>
5
+ <h1><%= _('VMware Status Overview') %></h1>
6
+ <p>
7
+ <%= _('You don\'t seem to have imported any VM data into Foreman.') %></br>
8
+ <%= _('You need to run an initial import to gather all required data.') %>
9
+ </p>
10
+ <div class="blank-slate-pf-main-action">
11
+ <%= display_link_if_authorized(_('Start Data Import'), hash_for_refresh_status_dashboard_hosts_path, :title => _('Import data into Foreman'), :method => :put, :class => 'btn btn-primary btn-lg') %>
12
+ </div>
13
+ </div>
@@ -0,0 +1,107 @@
1
+ <div class="list-group-item list-view-pf-stacked list-view-pf-top-align">
2
+ <div class="list-group-item-header">
3
+ <div class="list-view-pf-expand">
4
+ <span class="fa fa-angle-right"></span>
5
+ </div>
6
+ <div class="list-view-pf-actions">
7
+ <% if User.current.allowed_to?(hash_for_refresh_status_dashboard_hosts_path) %>
8
+ <div class="dropdown pull-right dropdown-kebab-pf">
9
+ <button class="btn btn-link dropdown-toggle" type="button" id="dropdownKebabRight<%= id %>" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
10
+ <span class="fa fa-ellipsis-v"></span>
11
+ </button>
12
+ <ul class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownKebabRight<%= id %>">
13
+ <li><%= display_link_if_authorized(_('Refresh'), hash_for_refresh_status_dashboard_hosts_path, :title => _('Refresh data'), :method => :put) %></li>
14
+ </ul>
15
+ </div>
16
+ <% end %>
17
+ </div>
18
+ <div class="list-view-pf-main-info">
19
+ <div class="list-view-pf-left">
20
+ <span class="pficon list-view-pf-icon-md <%= classes_for_vmware_status_row(counter) %>"></span>
21
+ </div>
22
+ <div class="list-view-pf-body">
23
+ <div class="list-view-pf-description">
24
+ <div class="list-group-item-heading">
25
+ <%= _(name) %>
26
+ </div>
27
+ <div class="list-group-item-text">
28
+ <%= _(description) %>
29
+ </div>
30
+ </div>
31
+ <div class="list-view-pf-additional-info">
32
+ <div class="list-view-pf-additional-info-item">
33
+ <%= icon_text('ok', '', :kind => 'pficon') %>
34
+ <strong><%= counter[:ok] %></strong>
35
+ <%= _('OK') %>
36
+ </div>
37
+ <div class="list-view-pf-additional-info-item">
38
+ <%= icon_text('warning-triangle-o', '', :kind => 'pficon') %>
39
+ <strong><%= counter[:warning] %></strong>
40
+ <%= _('Warning') %>
41
+ </div>
42
+ <div class="list-view-pf-additional-info-item">
43
+ <%= icon_text('error-circle-o', '', :kind => 'pficon') %>
44
+ <strong><%= counter[:critical] %></strong>
45
+ <%= _('Critical') %>
46
+ </div>
47
+ </div>
48
+ </div>
49
+ </div>
50
+ </div>
51
+ <div class="list-group-item-container container-fluid hidden">
52
+ <div class="close">
53
+ <span class="pficon pficon-close"></span>
54
+ </div>
55
+ <div class="row">
56
+ <div class="col-md-12">
57
+ <% if counter[:warning] > 0 || counter[:critical] > 0 %>
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_remediate_host_path(:id => host, :status_id => host.public_send(status).id).merge(:auth_object => host, :permission => 'remediate_vmware_status_hosts'),
89
+ :data => { :confirm => _('Are you sure? This might cause a service interruption.') },
90
+ :method => :put,
91
+ :target => :blank
92
+ )
93
+ )
94
+ %>
95
+ </td>
96
+ <% end %>
97
+ </tr>
98
+ <% end %>
99
+ </tbody>
100
+ </table>
101
+ <% else %>
102
+ <p><%= _('No manual actions required.') %></p>
103
+ <% end %>
104
+ </div>
105
+ </div>
106
+ </div>
107
+ </div>
@@ -0,0 +1,9 @@
1
+ <% title _('VMware Status Overview') %>
2
+
3
+ <%=
4
+ if @newest_data
5
+ render 'status_dashboard_content'
6
+ else
7
+ render 'status_dashboard_empty'
8
+ end
9
+ %>
data/config/routes.rb ADDED
@@ -0,0 +1,15 @@
1
+ Rails.application.routes.draw do
2
+ scope '/wreckingball' do
3
+ constraints(:id => /[^\/]+/) do
4
+ resources :hosts, controller: 'foreman_wreckingball/hosts', :only => [] do
5
+ member do
6
+ put :remediate
7
+ end
8
+ collection do
9
+ get :status_dashboard
10
+ put :refresh_status_dashboard
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,35 @@
1
+ class CreateVmwareFacets < ActiveRecord::Migration
2
+ def change
3
+ create_table :vmware_clusters do |t|
4
+ t.string :name, limit: 255, index: true
5
+ t.references :compute_resource, foreign_key: true
6
+
7
+ t.timestamps null: false
8
+ end
9
+
10
+ create_table :vmware_hypervisor_facets do |t|
11
+ t.references :host, null: false, foreign_key: true, index: true, unique: true
12
+ t.references :vmware_cluster, foreign_key: true, index: true
13
+ t.integer :cpu_cores
14
+ t.integer :cpu_sockets
15
+ t.integer :cpu_threads
16
+ t.integer :memory, limit: 8
17
+ t.string :uuid, limit: 255
18
+
19
+ t.timestamps null: false
20
+ end
21
+
22
+ create_table :vmware_facets do |t|
23
+ t.references :host, null: false, foreign_key: true, index: true, unique: true
24
+ t.references :vmware_cluster, foreign_key: true, index: true
25
+ t.integer :cpus
26
+ t.integer :corespersocket
27
+ t.integer :memory_mb
28
+ t.string :guest_id, limit: 255, index: true
29
+ t.integer :tools_state, default: 1, index: true
30
+ t.boolean :cpu_hot_add, null: false, default: false
31
+
32
+ t.timestamps null: false
33
+ end
34
+ end
35
+ end