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
data/Rakefile
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
#!/usr/bin/env rake
|
2
|
+
begin
|
3
|
+
require 'bundler/setup'
|
4
|
+
rescue LoadError
|
5
|
+
puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
|
6
|
+
end
|
7
|
+
begin
|
8
|
+
require 'rdoc/task'
|
9
|
+
rescue LoadError
|
10
|
+
require 'rdoc/rdoc'
|
11
|
+
require 'rake/rdoctask'
|
12
|
+
RDoc::Task = Rake::RDocTask
|
13
|
+
end
|
14
|
+
|
15
|
+
RDoc::Task.new(:rdoc) do |rdoc|
|
16
|
+
rdoc.rdoc_dir = 'rdoc'
|
17
|
+
rdoc.title = 'ForemanWreckingball'
|
18
|
+
rdoc.options << '--line-numbers'
|
19
|
+
rdoc.rdoc_files.include('README.rdoc')
|
20
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
21
|
+
end
|
22
|
+
|
23
|
+
APP_RAKEFILE = File.expand_path('../test/dummy/Rakefile', __FILE__)
|
24
|
+
|
25
|
+
Bundler::GemHelper.install_tasks
|
26
|
+
|
27
|
+
require 'rake/testtask'
|
28
|
+
|
29
|
+
Rake::TestTask.new(:test) do |t|
|
30
|
+
t.libs << 'lib'
|
31
|
+
t.libs << 'test'
|
32
|
+
t.pattern = 'test/**/*_test.rb'
|
33
|
+
t.verbose = false
|
34
|
+
end
|
35
|
+
|
36
|
+
task default: :test
|
37
|
+
|
38
|
+
begin
|
39
|
+
require 'rubocop/rake_task'
|
40
|
+
RuboCop::RakeTask.new
|
41
|
+
rescue => _
|
42
|
+
puts 'Rubocop not loaded.'
|
43
|
+
end
|
44
|
+
|
45
|
+
task :default do
|
46
|
+
Rake::Task['rubocop'].execute
|
47
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module ForemanWreckingball
|
2
|
+
class HostsController < ::HostsController
|
3
|
+
before_action :find_resource, :only => [:remediate]
|
4
|
+
before_action :find_status, :only => [:remediate]
|
5
|
+
|
6
|
+
def status_dashboard
|
7
|
+
statuses = [
|
8
|
+
ToolsStatus,
|
9
|
+
OperatingsystemStatus,
|
10
|
+
CpuHotAddStatus
|
11
|
+
]
|
12
|
+
|
13
|
+
@newest_data = Host.authorized(:view_hosts, Host).joins(:vmware_facet).maximum('vmware_facets.updated_at')
|
14
|
+
|
15
|
+
@data = statuses.map do |status|
|
16
|
+
host_association = status.host_association
|
17
|
+
hosts = Host.authorized(:view_hosts, Host).joins(host_association).includes(host_association).joins(:vmware_facet).includes(host_association => :host).includes(:environment).preload(:owner).order(:name)
|
18
|
+
{
|
19
|
+
name: status.status_name,
|
20
|
+
hosts: hosts,
|
21
|
+
description: status.description,
|
22
|
+
host_association: host_association,
|
23
|
+
supports_remediate: status.supports_remediate?
|
24
|
+
}
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def refresh_status_dashboard
|
29
|
+
flash[:success] = _('Refresh Compute Resource data task was successfully scheduled.')
|
30
|
+
task = ::ForemanTasks.async_task(::Actions::ForemanWreckingball::Vmware::ScheduleVmwareSync)
|
31
|
+
redirect_to(foreman_tasks_task_path(task.id))
|
32
|
+
end
|
33
|
+
|
34
|
+
def remediate
|
35
|
+
raise Foreman::Exception.new('VMware Status can not be remediated.') unless @status.class.respond_to?(:supports_remediate?) && @status.class.supports_remediate?
|
36
|
+
flash[:success] = _('Remediate VM task for %s was successfully scheduled.') % @host
|
37
|
+
task = ::ForemanTasks.async_task(::Actions::ForemanWreckingball::Host::RemediateVmwareOperatingsystem, @host)
|
38
|
+
redirect_to(foreman_tasks_task_path(task.id))
|
39
|
+
end
|
40
|
+
|
41
|
+
private
|
42
|
+
|
43
|
+
def find_status
|
44
|
+
@status = HostStatus::Status.find_by!(:id => params[:status_id], :host_id => @host.id)
|
45
|
+
end
|
46
|
+
|
47
|
+
def action_permission
|
48
|
+
case params[:action]
|
49
|
+
when 'status_dashboard'
|
50
|
+
'view'
|
51
|
+
when 'refresh_status_dashboard'
|
52
|
+
'refresh_vmware_status'
|
53
|
+
when 'remediate'
|
54
|
+
'remediate_vmware_status'
|
55
|
+
else
|
56
|
+
super
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module ForemanWreckingball
|
2
|
+
module HostsHelperExtensions
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
def classes_for_vmware_status_row(counter)
|
6
|
+
return 'pficon-error-circle-o list-view-pf-icon-danger' if (counter[:critical] || 0) > 0
|
7
|
+
return 'pficon-warning-triangle-o list-view-pf-icon-warning' if (counter[:warning] || 0) > 0
|
8
|
+
'pficon-ok list-view-pf-icon-success'
|
9
|
+
end
|
10
|
+
|
11
|
+
def vmware_status_counter(hosts, status_object)
|
12
|
+
count = hosts.map do |host|
|
13
|
+
host.public_send(status_object).to_global
|
14
|
+
end.group_by do |status|
|
15
|
+
status
|
16
|
+
end.each_with_object({}) do |(status, items), hash|
|
17
|
+
hash[status]=items.size
|
18
|
+
end
|
19
|
+
{
|
20
|
+
:ok => count[HostStatus::Global::OK] || 0,
|
21
|
+
:warning => count[HostStatus::Global::WARN] || 0,
|
22
|
+
:critical => count[HostStatus::Global::ERROR] || 0
|
23
|
+
}
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module Actions
|
2
|
+
module ForemanWreckingball
|
3
|
+
module Host
|
4
|
+
class RefreshVmwareFacet < Actions::EntryAction
|
5
|
+
def plan(host)
|
6
|
+
action_subject(host)
|
7
|
+
plan_self
|
8
|
+
end
|
9
|
+
|
10
|
+
def run
|
11
|
+
User.as_anonymous_admin do
|
12
|
+
host = ::Host.find(input[:host][:id])
|
13
|
+
state = host.refresh_vmware_facet!
|
14
|
+
output[:state] = state
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def rescue_strategy
|
19
|
+
Dynflow::Action::Rescue::Skip
|
20
|
+
end
|
21
|
+
|
22
|
+
def humanized_name
|
23
|
+
_('Refresh VMware data')
|
24
|
+
end
|
25
|
+
|
26
|
+
def humanized_input
|
27
|
+
input[:host] && input[:host][:name]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module Actions
|
2
|
+
module ForemanWreckingball
|
3
|
+
module Host
|
4
|
+
class RemediateVmwareOperatingsystem < Actions::EntryAction
|
5
|
+
def plan(host)
|
6
|
+
action_subject(host)
|
7
|
+
plan_self
|
8
|
+
end
|
9
|
+
|
10
|
+
def run
|
11
|
+
User.as_anonymous_admin do
|
12
|
+
host = ::Host.find(input[:host][:id])
|
13
|
+
|
14
|
+
fail _('Foreman does not know the Architecture of this host.') unless host.architecture
|
15
|
+
fail _('Foreman does not know the Operatingsystem of this host.') unless host.operatingsystem
|
16
|
+
|
17
|
+
initially_powered_on = host.power.ready?
|
18
|
+
output[:initially_powered_on] = initially_powered_on
|
19
|
+
|
20
|
+
vm = host.compute_object
|
21
|
+
|
22
|
+
if initially_powered_on
|
23
|
+
vm.stop
|
24
|
+
vm.wait_for { power_state == 'poweredOff' }
|
25
|
+
end
|
26
|
+
|
27
|
+
fail _('Could not shut down VM.') if vm.ready?
|
28
|
+
|
29
|
+
current_os_id = host.vmware_facet.guest_id
|
30
|
+
current_os = VsphereOsIdentifiers.lookup(current_os_id)
|
31
|
+
|
32
|
+
selectors = {
|
33
|
+
:architecture => host.architecture.name,
|
34
|
+
:osfamily => host.operatingsystem.family,
|
35
|
+
:name => host.operatingsystem.name,
|
36
|
+
:major => host.operatingsystem.major.to_i
|
37
|
+
}
|
38
|
+
|
39
|
+
desired_os = VsphereOsIdentifiers.find_by(selectors)
|
40
|
+
desired_os ||= VsphereOsIdentifiers.find_by(selectors.except(:major))
|
41
|
+
desired_os ||= VsphereOsIdentifiers.find_by(selectors.except(:major, :name))
|
42
|
+
|
43
|
+
fail _('Could not auto detect desired operatingsystem.') unless desired_os
|
44
|
+
|
45
|
+
fail _('VMs current and desired OS (%s) already match. No update necessary.') % current_os.description if current_os == desired_os
|
46
|
+
|
47
|
+
output[:old_operatingsystem] = current_os.description
|
48
|
+
output[:old_operatingsystem_id] = current_os.id
|
49
|
+
output[:new_operatingsytem] = desired_os.description
|
50
|
+
output[:new_operatingsytem_id] = desired_os.id
|
51
|
+
|
52
|
+
vm.vm_reconfig_hardware({'guestId' => desired_os.id})
|
53
|
+
|
54
|
+
if initially_powered_on
|
55
|
+
vm.start
|
56
|
+
end
|
57
|
+
|
58
|
+
state = host.refresh_vmware_facet!
|
59
|
+
output[:state] = state
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
def humanized_name
|
64
|
+
_('Correct VM Operatingsystem')
|
65
|
+
end
|
66
|
+
|
67
|
+
def humanized_input
|
68
|
+
input[:host] && input[:host][:name]
|
69
|
+
end
|
70
|
+
|
71
|
+
def append_error(message)
|
72
|
+
output[:errors] ||= []
|
73
|
+
output[:errors] << message
|
74
|
+
end
|
75
|
+
|
76
|
+
def rescue_strategy
|
77
|
+
Dynflow::Action::Rescue::Skip
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Actions
|
2
|
+
module ForemanWreckingball
|
3
|
+
module Vmware
|
4
|
+
class ScheduleVmwareSync < Actions::EntryAction
|
5
|
+
def plan
|
6
|
+
compute_resources = ComputeResource.where(:type => 'Foreman::Model::Vmware')
|
7
|
+
sequence do
|
8
|
+
compute_resources.each do |compute_resource|
|
9
|
+
plan_action(::Actions::ForemanWreckingball::Vmware::SyncComputeResource, compute_resource)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def humanized_name
|
15
|
+
_('VMware Data Synchronization')
|
16
|
+
end
|
17
|
+
|
18
|
+
def rescue_strategy
|
19
|
+
Dynflow::Action::Rescue::Skip
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module Actions
|
2
|
+
module ForemanWreckingball
|
3
|
+
module Vmware
|
4
|
+
class SyncComputeResource < Actions::EntryAction
|
5
|
+
def plan(compute_resource)
|
6
|
+
action_subject(compute_resource)
|
7
|
+
plan_self
|
8
|
+
end
|
9
|
+
|
10
|
+
def run
|
11
|
+
User.as_anonymous_admin do
|
12
|
+
compute_resource = ComputeResource.find(input[:compute_resource][:id])
|
13
|
+
::ForemanWreckingball::VmwareClusterImporter.new(
|
14
|
+
:compute_resource => compute_resource
|
15
|
+
).import!
|
16
|
+
|
17
|
+
::ForemanWreckingball::VmwareHypervisorImporter.new(
|
18
|
+
:compute_resource => compute_resource.reload
|
19
|
+
).import!
|
20
|
+
|
21
|
+
compute_resource.hosts.each do |host|
|
22
|
+
host.refresh_vmware_facet!
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def humanized_name
|
28
|
+
_('Refresh Compute Resource')
|
29
|
+
end
|
30
|
+
|
31
|
+
def resource_locks
|
32
|
+
:update
|
33
|
+
end
|
34
|
+
|
35
|
+
def rescue_strategy
|
36
|
+
Dynflow::Action::Rescue::Skip
|
37
|
+
end
|
38
|
+
|
39
|
+
def self.cleanup_after
|
40
|
+
'1d'
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module FogExtensions
|
2
|
+
module ForemanWreckingball
|
3
|
+
module Vsphere
|
4
|
+
module Host
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
module Overrides
|
8
|
+
def vm_ids
|
9
|
+
attributes[:vm_ids] = attributes[:vm_ids].call if attributes[:vm_ids].is_a?(Proc)
|
10
|
+
attributes[:vm_ids]
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
included do
|
15
|
+
prepend Overrides
|
16
|
+
|
17
|
+
attribute :cpu_cores
|
18
|
+
attribute :cpu_sockets
|
19
|
+
attribute :cpu_threads
|
20
|
+
attribute :memory
|
21
|
+
attribute :uuid
|
22
|
+
attribute :model
|
23
|
+
attribute :vendor
|
24
|
+
attribute :ipaddress
|
25
|
+
attribute :ipaddress6
|
26
|
+
attribute :product_name
|
27
|
+
attribute :product_version
|
28
|
+
attribute :hostname
|
29
|
+
attribute :domainname
|
30
|
+
end
|
31
|
+
|
32
|
+
def memory_mb
|
33
|
+
memory / 1024 / 1024
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module FogExtensions
|
2
|
+
module ForemanWreckingball
|
3
|
+
module Vsphere
|
4
|
+
module Real
|
5
|
+
extend ActiveSupport::Concern
|
6
|
+
|
7
|
+
module Overrides
|
8
|
+
def list_hosts(filters = {})
|
9
|
+
cluster = get_raw_cluster(filters[:cluster], filters[:datacenter])
|
10
|
+
|
11
|
+
results = property_collector_results(host_system_filter_spec(cluster))
|
12
|
+
|
13
|
+
results.map do |host|
|
14
|
+
hsh = map_attrs_to_hash(host, host_system_attribute_mapping)
|
15
|
+
hsh.merge(
|
16
|
+
datacenter: filters[:datacenter],
|
17
|
+
cluster: filters[:cluster],
|
18
|
+
ipaddress: (host['config.network.vnic'].first.spec.ip.ipAddress rescue nil),
|
19
|
+
ipaddress6: (host['config.network.vnic'].first.spec.ip.ipV6Config.ipV6Address.first.ipAddress rescue nil),
|
20
|
+
vm_ids: Proc.new { host['vm'].map {|vm| vm.config.instanceUuid rescue nil} }
|
21
|
+
)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
included do
|
27
|
+
prepend Overrides
|
28
|
+
end
|
29
|
+
|
30
|
+
protected
|
31
|
+
|
32
|
+
def map_attrs_to_hash(obj, attribute_mapping)
|
33
|
+
attribute_mapping.each_with_object({}) do |(k, v), hsh|
|
34
|
+
hsh[k] = obj[v]
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def property_collector_results(filter_spec)
|
39
|
+
property_collector = connection.serviceContent.propertyCollector
|
40
|
+
property_collector.RetrieveProperties(:specSet => [filter_spec])
|
41
|
+
end
|
42
|
+
|
43
|
+
def compute_resource_host_traversal_spec
|
44
|
+
RbVmomi::VIM.TraversalSpec(
|
45
|
+
:name => 'computeResourceHostTraversalSpec',
|
46
|
+
:type => 'ComputeResource',
|
47
|
+
:path => 'host',
|
48
|
+
:skip => false
|
49
|
+
)
|
50
|
+
end
|
51
|
+
|
52
|
+
def host_system_filter_spec(obj)
|
53
|
+
RbVmomi::VIM.PropertyFilterSpec(
|
54
|
+
:objectSet => [
|
55
|
+
:obj => obj,
|
56
|
+
:selectSet => [
|
57
|
+
compute_resource_host_traversal_spec
|
58
|
+
]
|
59
|
+
],
|
60
|
+
:propSet => [
|
61
|
+
{ :type => 'HostSystem', :pathSet => host_system_attribute_mapping.values + ['config.network.vnic', 'vm'] }
|
62
|
+
]
|
63
|
+
)
|
64
|
+
end
|
65
|
+
|
66
|
+
def host_system_attribute_mapping
|
67
|
+
{
|
68
|
+
name: 'name',
|
69
|
+
cpu_cores: 'hardware.cpuInfo.numCpuCores',
|
70
|
+
cpu_sockets: 'hardware.cpuInfo.numCpuPackages',
|
71
|
+
cpu_threads: 'hardware.cpuInfo.numCpuThreads',
|
72
|
+
memory: 'hardware.memorySize',
|
73
|
+
uuid: 'hardware.systemInfo.uuid',
|
74
|
+
model: 'hardware.systemInfo.model',
|
75
|
+
vendor: 'hardware.systemInfo.vendor',
|
76
|
+
product_name: 'summary.config.product.name',
|
77
|
+
product_version: 'summary.config.product.version',
|
78
|
+
hostname: 'config.network.dnsConfig.hostName',
|
79
|
+
domainname: 'config.network.dnsConfig.domainName'
|
80
|
+
}
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|