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
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,8 @@
1
+ class UpdateHostsVmwareFacets < ActiveJob::Base
2
+ after_perform do
3
+ self.class.set(:wait => 12.hours).perform_later
4
+ end
5
+
6
+ def perform
7
+ end
8
+ 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