foreman_wreckingball 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/LICENSE +619 -0
- data/README.md +37 -0
- data/Rakefile +47 -0
- data/app/controllers/foreman_wreckingball/hosts_controller.rb +60 -0
- data/app/helpers/concerns/foreman_wreckingball/hosts_helper_extensions.rb +26 -0
- data/app/jobs/update_hosts_vmware_facets.rb +8 -0
- data/app/lib/actions/foreman_wreckingball/host/refresh_vmware_facet.rb +32 -0
- data/app/lib/actions/foreman_wreckingball/host/remediate_vmware_operatingsystem.rb +82 -0
- data/app/lib/actions/foreman_wreckingball/vmware/schedule_vmware_sync.rb +24 -0
- data/app/lib/actions/foreman_wreckingball/vmware/sync_compute_resource.rb +45 -0
- data/app/lib/fog_extensions/foreman_wreckingball/vsphere/host.rb +38 -0
- data/app/lib/fog_extensions/foreman_wreckingball/vsphere/real.rb +85 -0
- data/app/lib/vsphere_os_identifiers/data.yaml +538 -0
- data/app/lib/vsphere_os_identifiers/os.rb +19 -0
- data/app/lib/vsphere_os_identifiers.rb +34 -0
- data/app/models/concerns/foreman_wreckingball/compute_resource_extensions.rb +15 -0
- data/app/models/concerns/foreman_wreckingball/host_extensions.rb +15 -0
- data/app/models/concerns/foreman_wreckingball/vmware_extensions.rb +9 -0
- data/app/models/concerns/foreman_wreckingball/vmware_facet_host_extensions.rb +26 -0
- data/app/models/concerns/foreman_wreckingball/vmware_hypervisor_facet_host_extensions.rb +13 -0
- data/app/models/foreman_wreckingball/cpu_hot_add_status.rb +58 -0
- data/app/models/foreman_wreckingball/operatingsystem_status.rb +65 -0
- data/app/models/foreman_wreckingball/tools_status.rb +46 -0
- data/app/models/foreman_wreckingball/vmware_cluster.rb +14 -0
- data/app/models/foreman_wreckingball/vmware_facet.rb +53 -0
- data/app/models/foreman_wreckingball/vmware_hypervisor_facet.rb +18 -0
- data/app/services/foreman_wreckingball/vmware_cluster_importer.rb +41 -0
- data/app/services/foreman_wreckingball/vmware_hypervisor_importer.rb +119 -0
- data/app/views/foreman_wreckingball/hosts/_status_dashboard_content.erb +43 -0
- data/app/views/foreman_wreckingball/hosts/_status_dashboard_empty.erb +13 -0
- data/app/views/foreman_wreckingball/hosts/_status_row.html.erb +107 -0
- data/app/views/foreman_wreckingball/hosts/status_dashboard.html.erb +9 -0
- data/config/routes.rb +15 -0
- data/db/migrate/20171106155000_create_vmware_facets.rb +35 -0
- data/lib/foreman_wreckingball/engine.rb +97 -0
- data/lib/foreman_wreckingball/scheduled_jobs.rb +10 -0
- data/lib/foreman_wreckingball/version.rb +3 -0
- data/lib/foreman_wreckingball.rb +4 -0
- data/lib/tasks/foreman_vmware_checks_tasks.rake +49 -0
- data/locale/Makefile +60 -0
- data/locale/en/foreman_wreckingball.po +19 -0
- data/locale/foreman_wreckingball.pot +19 -0
- data/locale/gemspec.rb +2 -0
- data/test/test_plugin_helper.rb +6 -0
- data/test/unit/foreman_wreckingball_test.rb +11 -0
- 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>
|
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
|