foreman_discovery 16.3.5 → 18.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (112) hide show
  1. checksums.yaml +4 -4
  2. data/app/controllers/api/v2/discovered_hosts_controller.rb +1 -0
  3. data/app/controllers/discovered_hosts_controller.rb +34 -38
  4. data/app/controllers/discovery_rules_controller.rb +12 -1
  5. data/app/helpers/discovered_hosts_helper.rb +1 -1
  6. data/app/helpers/discovery_rules_helper.rb +1 -0
  7. data/app/models/discovery_fact_name.rb +9 -0
  8. data/app/models/discovery_rule.rb +10 -5
  9. data/app/models/host/discovered.rb +26 -9
  10. data/app/models/host/managed_extensions.rb +2 -2
  11. data/app/models/setting/discovered.rb +1 -0
  12. data/app/services/discovery_fact_importer.rb +5 -0
  13. data/app/services/foreman_discovery/fact_to_category_resolver.rb +106 -0
  14. data/app/services/foreman_discovery/host_converter.rb +3 -3
  15. data/app/services/foreman_discovery/ui_notifications/failed_discovery.rb +34 -0
  16. data/app/services/foreman_discovery/ui_notifications/new_host.rb +2 -1
  17. data/app/views/discovered_hosts/_discovered_hosts_list.html.erb +44 -40
  18. data/app/views/discovered_hosts/welcome.html.erb +0 -1
  19. data/app/views/discovery_rules/clone.erb +3 -0
  20. data/app/views/discovery_rules/index.html.erb +4 -0
  21. data/app/views/discovery_rules/welcome.html.erb +14 -0
  22. data/app/views/foreman_discovery/debian_kexec.erb +1 -1
  23. data/app/views/foreman_discovery/redhat_kexec.erb +1 -1
  24. data/config/routes.rb +2 -0
  25. data/db/seeds.d/80_discovery_ui_notification.rb +11 -5
  26. data/lib/foreman_discovery/engine.rb +5 -7
  27. data/lib/foreman_discovery/version.rb +1 -1
  28. data/locale/ca/LC_MESSAGES/foreman_discovery.mo +0 -0
  29. data/locale/ca/foreman_discovery.edit.po +103 -70
  30. data/locale/ca/foreman_discovery.po +45 -12
  31. data/locale/de/LC_MESSAGES/foreman_discovery.mo +0 -0
  32. data/locale/de/foreman_discovery.edit.po +109 -76
  33. data/locale/de/foreman_discovery.po +51 -18
  34. data/locale/en/LC_MESSAGES/foreman_discovery.mo +0 -0
  35. data/locale/en/foreman_discovery.edit.po +98 -66
  36. data/locale/en/foreman_discovery.po +37 -4
  37. data/locale/en_GB/LC_MESSAGES/foreman_discovery.mo +0 -0
  38. data/locale/en_GB/foreman_discovery.edit.po +107 -74
  39. data/locale/en_GB/foreman_discovery.po +42 -9
  40. data/locale/es/LC_MESSAGES/foreman_discovery.mo +0 -0
  41. data/locale/es/foreman_discovery.edit.po +109 -76
  42. data/locale/es/foreman_discovery.po +76 -41
  43. data/locale/foreman_discovery.pot +137 -84
  44. data/locale/fr/LC_MESSAGES/foreman_discovery.mo +0 -0
  45. data/locale/fr/foreman_discovery.edit.po +151 -118
  46. data/locale/fr/foreman_discovery.po +102 -69
  47. data/locale/gl/LC_MESSAGES/foreman_discovery.mo +0 -0
  48. data/locale/gl/foreman_discovery.edit.po +101 -68
  49. data/locale/gl/foreman_discovery.po +39 -6
  50. data/locale/it/LC_MESSAGES/foreman_discovery.mo +0 -0
  51. data/locale/it/foreman_discovery.edit.po +107 -74
  52. data/locale/it/foreman_discovery.po +51 -18
  53. data/locale/ja/LC_MESSAGES/foreman_discovery.mo +0 -0
  54. data/locale/ja/foreman_discovery.edit.po +136 -103
  55. data/locale/ja/foreman_discovery.po +101 -70
  56. data/locale/ko/LC_MESSAGES/foreman_discovery.mo +0 -0
  57. data/locale/ko/foreman_discovery.edit.po +107 -74
  58. data/locale/ko/foreman_discovery.po +50 -17
  59. data/locale/pt_BR/LC_MESSAGES/foreman_discovery.mo +0 -0
  60. data/locale/pt_BR/foreman_discovery.edit.po +109 -76
  61. data/locale/pt_BR/foreman_discovery.po +75 -39
  62. data/locale/ru/LC_MESSAGES/foreman_discovery.mo +0 -0
  63. data/locale/ru/foreman_discovery.edit.po +112 -79
  64. data/locale/ru/foreman_discovery.po +52 -19
  65. data/locale/sv_SE/LC_MESSAGES/foreman_discovery.mo +0 -0
  66. data/locale/sv_SE/foreman_discovery.edit.po +101 -68
  67. data/locale/sv_SE/foreman_discovery.po +41 -8
  68. data/locale/zh_CN/LC_MESSAGES/foreman_discovery.mo +0 -0
  69. data/locale/zh_CN/foreman_discovery.edit.po +153 -120
  70. data/locale/zh_CN/foreman_discovery.po +112 -82
  71. data/locale/zh_TW/LC_MESSAGES/foreman_discovery.mo +0 -0
  72. data/locale/zh_TW/foreman_discovery.edit.po +107 -74
  73. data/locale/zh_TW/foreman_discovery.po +50 -17
  74. data/package.json +6 -6
  75. data/test/functional/api/v2/discovered_hosts_controller_test.rb +9 -0
  76. data/test/functional/api/v2/fact_value_extensions_test.rb +1 -4
  77. data/test/functional/discovered_hosts_controller_test.rb +6 -5
  78. data/test/functional/discovery_rules_controller_test.rb +6 -1
  79. data/test/integration/discovered_hosts_test.rb +63 -9
  80. data/test/test_helper_discovery.rb +40 -27
  81. data/test/test_plugin_helper.rb +7 -0
  82. data/test/unit/discovered_extensions_test.rb +36 -16
  83. data/test/unit/discovery_attribute_set_test.rb +2 -8
  84. data/test/unit/discovery_rule_test.rb +24 -2
  85. data/test/unit/fact_parser_test.rb +1 -4
  86. data/test/unit/fact_to_category_resolver_test.rb +41 -0
  87. data/test/unit/managed_extensions_test.rb +1 -0
  88. data/test/unit/ui_notifications/destroy_host_test.rb +2 -9
  89. data/test/unit/ui_notifications/new_host_test.rb +3 -3
  90. data/webpack/__mocks__/foremanReact/common/I18n.js +3 -0
  91. data/webpack/__mocks__/foremanReact/common/helpers.js +1 -0
  92. data/webpack/__mocks__/foremanReact/common/index.js +5 -0
  93. data/webpack/__mocks__/foremanReact/components/common/EmptyState/DefaultEmptyState.js +69 -0
  94. data/webpack/__mocks__/foremanReact/components/common/EmptyState/EmptyStatePattern.js +77 -0
  95. data/webpack/__mocks__/foremanReact/components/common/EmptyState/EmptyStatePropTypes.js +29 -0
  96. data/webpack/__mocks__/foremanReact/components/common/EmptyState/index.js +5 -0
  97. data/webpack/index.js +9 -8
  98. data/webpack/src/ForemanDiscovery/DiscoveredHosts/Components/EmptyState/EmptyState.js +7 -7
  99. data/webpack/src/ForemanDiscovery/DiscoveredHosts/Components/EmptyState/__test__/EmptyState.test.js +12 -0
  100. data/webpack/src/ForemanDiscovery/DiscoveredHosts/Components/EmptyState/__test__/__snapshots__/EmptyState.test.js.snap +16 -0
  101. data/webpack/src/ForemanDiscovery/DiscoveredHosts/Components/EmptyState/index.js +1 -1
  102. data/webpack/src/ForemanDiscovery/DiscoveredHosts/index.js +3 -3
  103. data/webpack/src/ForemanDiscovery/DiscoveryRules/Components/EmptyState/EmptyState.js +34 -0
  104. data/webpack/src/ForemanDiscovery/DiscoveryRules/Components/EmptyState/index.js +1 -0
  105. data/webpack/src/ForemanDiscovery/DiscoveryRules/Components/__test__/EmptyState.test.js +19 -0
  106. data/webpack/src/ForemanDiscovery/DiscoveryRules/Components/__test__/__snapshots__/EmptyState.test.js.snap +22 -0
  107. data/webpack/src/ForemanDiscovery/DiscoveryRules/index.js +6 -0
  108. data/webpack/src/reducers.js +0 -2
  109. metadata +35 -16
  110. data/test/functional/api/v2/settings_controller_test.rb +0 -22
  111. data/test/models/setting_test.rb +0 -11
  112. data/test/unit/setting_discovered_test.rb +0 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3069a28357316df978cd2a85a1ed6e919b0208d5051a1064222ccc52efde30dd
4
- data.tar.gz: 8709517978ccc3a1b9d37913145d519a579315004c7ea6d3cd8ac7fd612a7bea
3
+ metadata.gz: 5e5ed6f9be99d08e38902acdbfff3e02aa7b086415cc6c8208a446ad8195fe18
4
+ data.tar.gz: cebfddeec98ce1ce01778dbe3c94fa1581132462a01e5ee04c2f78a6812a8c02
5
5
  SHA512:
6
- metadata.gz: 1f325332129ba0a4c79cff63f4e1b8f1adc1741c8a11fe0c689992896d8fe8fe08b98e60081bd17d575edcb88e6707be12ae635cc38ed1d5085d5b865ba2e097
7
- data.tar.gz: 49e1f0a7becea2ffbe9fcdb7b120988e8dc3d20c4ad4a195de4dea8ca878c5261860951d3d9a7875a0b81933649f3730791d186e577c16aeeabf8235c8a711cc
6
+ metadata.gz: 42a2c9cfe529fbf8e22c11ce4b7260ae6bd61d0005688b5b50199c6c7e2a432cbe8a99b1a4fd1f94757246892fa60426181382dd6de874bfe0d495d1e98b140c
7
+ data.tar.gz: 5a151a76f41f68258a90154209c81e2fa3a16dc6049191269d8b261dca51d9c5ed536ad7c18180079d9186c58a80ed145640541bbd2534f6c666a78aea407cc8
@@ -122,6 +122,7 @@ module Api
122
122
  end
123
123
  process_response state
124
124
  rescue Exception => e
125
+ ForemanDiscovery::UINotifications::FailedDiscovery.new(e).deliver!
125
126
  Foreman::Logging.exception("Host discovery failed, facts: #{facts}", e)
126
127
  render :json => {'message'=>e.to_s}, :status => :unprocessable_entity
127
128
  end
@@ -9,10 +9,14 @@ class DiscoveredHostsController < ::ApplicationController
9
9
  before_action :find_by_name_incl_subnet, :only => [:show]
10
10
  before_action :find_multiple, :only => [:multiple_destroy, :submit_multiple_destroy, :multiple_reboot, :submit_multiple_reboot, :multiple_auto_provision, :submit_multiple_auto_provision]
11
11
  before_action :taxonomy_scope, :only => [:edit]
12
+ before_action :check_for_subnet, :only => [:reboot, :auto_provision, :submit_multiple_reboot, :submit_multiple_auto_provision]
12
13
 
13
14
  around_action :skip_bullet, :only => [:edit]
14
15
 
15
16
  helper :hosts
17
+ if defined?(ForemanPuppet)
18
+ helper ForemanPuppet::HostsAndHostgroupsHelper
19
+ end
16
20
 
17
21
  layout 'layouts/application'
18
22
 
@@ -38,14 +42,10 @@ class DiscoveredHostsController < ::ApplicationController
38
42
  @range = nil
39
43
  # summary report text
40
44
  @report_summary = nil
41
- init_regex_and_categories
42
- @interfaces = []
43
- get_interfaces
44
- @host.facts_hash.each do |key, value|
45
- value = number_to_human_size(value) if /size$/.match(key)
46
- assign_fact_to_category(key, value)
47
- end
48
- add_custom_facts
45
+ resolver = ForemanDiscovery::FactToCategoryResolver.new(@host)
46
+ @categories_names = ForemanDiscovery::FactToCategoryResolver::CATEGORIES_NAMES
47
+ @categories = resolver.categories
48
+ @interfaces = resolver.interfaces
49
49
  end
50
50
 
51
51
  def destroy
@@ -203,41 +203,16 @@ class DiscoveredHostsController < ::ApplicationController
203
203
  subnet6 = @host.hostgroup.subnet6 || @host.subnet6
204
204
  @architecture = @host.hostgroup.architecture
205
205
  @operatingsystem = @host.hostgroup.operatingsystem
206
- @environment = @host.hostgroup.environment
206
+ if defined?(ForemanPuppet)
207
+ @environment = @host.hostgroup.environment
208
+ @host.environment = @environment
209
+ end
207
210
  @domain = @host.hostgroup.domain
208
211
  @subnet = subnet
209
212
  @subnet6 = subnet6
210
213
  @compute_profile = @host.hostgroup.compute_profile
211
214
  @realm = @host.hostgroup.realm
212
215
  @host.interfaces.first.assign_attributes(subnet: subnet, subnet6: subnet6, domain: @domain)
213
- @host.environment = @environment
214
- end
215
- end
216
-
217
- def init_regex_and_categories
218
- hightlights = Setting[:discovery_facts_highlights].empty? ? /^(productname|memorysize|manufacturer|architecture|macaddress$|processorcount|physicalprocessorcount|discovery_subnet|discovery_boot|ipaddress$)/ : Regexp.new(Setting[:discovery_facts_highlights])
219
- storage = Setting[:discovery_facts_storage].empty? ? /^blockdevice/ : Regexp.new(Setting[:discovery_facts_storage])
220
- hardware = Setting[:discovery_facts_hardware].empty? ? /^(hardw|manufacturer|memo|process)/ : Regexp.new(Setting[:discovery_facts__hardware])
221
- network = Setting[:discovery_facts_network].empty? ? /^(interfaces|dhcp|fqdn|hostname)/ : Regexp.new(Setting[:discovery_facts_network])
222
- software = Setting[:discovery_facts_software].empty? ? /^(bios|os|discovery)/ : Regexp.new(Setting[:discovery_facts_software])
223
- ipmi = Setting[:discovery_facts_ipmi].empty? ? /^ipmi/ : Regexp.new(Setting[:discovery_facts_ipmi])
224
- @regex_array = [hightlights, storage, hardware, network, software, ipmi, false]
225
- @categories = Array.new(7) { Hash.new }
226
- @categories_names = [N_("Highlights"), N_("Storage"), N_("Hardware"), N_("Network"), N_("Software"), N_("IPMI"), N_("Miscellaneous")]
227
- end
228
-
229
- def assign_fact_to_category(key, value)
230
- if @interfaces.any? {|interface| key.include? interface[:identifier]}
231
- @categories[3][key] = value
232
- return
233
- end
234
- @regex_array.each_with_index do |regex, index|
235
- if !regex
236
- @categories[index][key] = value
237
- elsif regex.match key
238
- @categories[index][key] = value
239
- break
240
- end
241
216
  end
242
217
  end
243
218
 
@@ -247,7 +222,9 @@ class DiscoveredHostsController < ::ApplicationController
247
222
 
248
223
  def load_vars_for_ajax
249
224
  return unless @host
250
- @environment = @host.environment
225
+ if defined?(ForemanPuppet)
226
+ @environment = @host.environment
227
+ end
251
228
  @architecture = @host.architecture
252
229
  @domain = @host.domain
253
230
  @operatingsystem = @host.operatingsystem
@@ -292,6 +269,21 @@ class DiscoveredHostsController < ::ApplicationController
292
269
  @host
293
270
  end
294
271
 
272
+ def check_for_subnet
273
+ hosts_without_subnet = []
274
+ case params[:action]
275
+ when 'reboot', 'auto_provision'
276
+ if @host.subnet.nil?
277
+ process_warning :warning_msg => _("Discovered host reported from unknown subnet, communication will not be proxied.")
278
+ end
279
+ when 'submit_multiple_reboot', 'submit_multiple_auto_provision'
280
+ hosts_without_subnet = @hosts.limit(3).select { |host| host.subnet.nil? }.pluck(:name)
281
+ if hosts_without_subnet.present?
282
+ process_warning :warning_msg => _("Discovered hosts reported from unknown subnet are %s, communication will not be proxied.") % hosts_without_subnet.join(', ')
283
+ end
284
+ end
285
+ end
286
+
295
287
  def find_by_name_incl_subnet
296
288
  find_by_name({:interfaces => :subnet})
297
289
  end
@@ -347,4 +339,8 @@ class DiscoveredHostsController < ::ApplicationController
347
339
  assign_fact_to_category("discovery_subnet", discovery_subnet)
348
340
  end
349
341
  end
342
+
343
+ def process_warning(hash = {})
344
+ warning hash[:warning_msg]
345
+ end
350
346
  end
@@ -4,7 +4,11 @@ class DiscoveryRulesController < ApplicationController
4
4
 
5
5
  include Foreman::Controller::Parameters::DiscoveryRule
6
6
 
7
- before_action :find_resource, :only => [:edit, :update, :destroy, :enable, :disable, :auto_provision]
7
+ before_action :find_resource, :only => [:edit, :update, :destroy, :enable, :disable, :auto_provision, :clone]
8
+
9
+ def model_of_controller
10
+ DiscoveryRule
11
+ end
8
12
 
9
13
  def index
10
14
  base = resource_base.search_for(params[:search], :order => (params[:order]))
@@ -15,6 +19,11 @@ class DiscoveryRulesController < ApplicationController
15
19
  @discovery_rule = DiscoveryRule.new(:priority => DiscoveryRule.suggest_priority)
16
20
  end
17
21
 
22
+ def clone
23
+ @discovery_rule = @discovery_rule.deep_clone except: [:name, :priority]
24
+ @discovery_rule.priority = DiscoveryRule.suggest_priority
25
+ end
26
+
18
27
  def create
19
28
  @discovery_rule = DiscoveryRule.new(discovery_rule_params)
20
29
  if @discovery_rule.save
@@ -55,6 +64,8 @@ class DiscoveryRulesController < ApplicationController
55
64
 
56
65
  def action_permission
57
66
  case params[:action]
67
+ when 'clone'
68
+ :create
58
69
  when 'enable', 'disable'
59
70
  :edit
60
71
  else
@@ -7,7 +7,7 @@ module DiscoveredHostsHelper
7
7
  end
8
8
 
9
9
  def disc_report_column(record)
10
- record.last_report? ? (_("%s ago") % time_ago_in_words(record.last_report.getlocal)) : ""
10
+ record.last_report? ? date_time_relative(record.last_report.getlocal) : ""
11
11
  end
12
12
 
13
13
  def discovered_hosts_title_actions(host)
@@ -30,6 +30,7 @@ module DiscoveryRulesHelper
30
30
  else
31
31
  actions << display_link_if_authorized(_('Enable'), hash_for_enable_discovery_rule_path(:id => rule).merge(:auth_object => rule, :authorizer => authorizer), :data => { :confirm => _("Enable rule '%s'?") % rule })
32
32
  end
33
+ actions << display_link_if_authorized(_("Clone"), hash_for_clone_discovery_rule_path(id: rule).merge(auth_object: rule, authorizer: authorizer))
33
34
  actions << display_delete_if_authorized(hash_for_discovery_rule_path(:id => rule).merge(:auth_object => rule, :authorizer => authorizer), :data => { :confirm => _("Delete rule '%s'?") % rule })
34
35
  end
35
36
  end
@@ -0,0 +1,9 @@
1
+ class DiscoveryFactName < FactName
2
+ def origin
3
+ 'Discovery'
4
+ end
5
+
6
+ def icon_path
7
+ "icons16x16/stub/darkred-d"
8
+ end
9
+ end
@@ -17,7 +17,6 @@ class DiscoveryRule < ApplicationRecord
17
17
  validates :max_count, :numericality => { :only_integer => true, :greater_than_or_equal_to => 0, :less_than => 2**31 }
18
18
  validates :priority, :presence => true, :numericality => { :only_integer => true, :greater_than_or_equal_to => 0, :less_than => 2**31 }
19
19
  validates_lengths_from_database
20
- validates_uniqueness_of :priority
21
20
  before_validation :default_int_attributes
22
21
  before_validation :enforce_taxonomy
23
22
 
@@ -41,19 +40,25 @@ class DiscoveryRule < ApplicationRecord
41
40
  self.priority ||= 0
42
41
  end
43
42
 
44
- def self.suggest_priority
45
- self.unscoped.maximum(:priority).to_i + STEP
43
+ def self.suggest_priority(organization = Organization.current)
44
+ discovery_rules = DiscoveryRule.unscoped
45
+ return (discovery_rules.maximum(:priority).to_i + STEP) if organization.nil?
46
+ discovery_rule_ids = TaxableTaxonomy.where(
47
+ taxable_type: 'DiscoveryRule',
48
+ taxonomy_id: organization.id).pluck(:taxable_id)
49
+ discovery_rules = discovery_rules.where(id: discovery_rule_ids)
50
+ discovery_rules.maximum(:priority).to_i + STEP
46
51
  end
47
52
 
48
53
  def enforce_taxonomy
49
54
  return if hostgroup.nil?
50
55
  unless (ms = hostgroup.organizations - organizations).empty?
51
56
  names = ms.collect(&:name).to_sentence
52
- errors.add(:organizations, n_("Host group organization %s must also be associated to the discovery rule", "Host group organizations %s must also be associated to the discovery rule", ms.size) % names)
57
+ errors.add(:base, n_("Host group organization %s must also be associated to the discovery rule", "Host group organizations %s must also be associated to the discovery rule", ms.size) % names)
53
58
  end
54
59
  unless (ms = hostgroup.locations - locations).empty?
55
60
  names = ms.collect(&:name).to_sentence
56
- errors.add(:locations, n_("Host group location %s must also be associated to the discovery rule", "Host group locations %s must also be associated to the discovery rule", ms.size) % names)
61
+ errors.add(:base, n_("Host group location %s must also be associated to the discovery rule", "Host group locations %s must also be associated to the discovery rule", ms.size) % names)
57
62
  end
58
63
  end
59
64
  end
@@ -133,26 +133,39 @@ class Host::Discovered < ::Host::Base
133
133
  subnet.present? && subnet.discovery.present?
134
134
  end
135
135
 
136
+ def ip4or6
137
+ if Setting[:discovery_prefer_ipv6]
138
+ IPAddr.new(self.ip6 || self.ip)
139
+ else
140
+ IPAddr.new(self.ip || self.ip6)
141
+ end
142
+ end
143
+
136
144
  def proxy_url(node_ip)
137
- proxied? ? subnet.discovery.url + "/discovery/#{node_ip}" : "https://#{node_ip}:8443"
145
+ wrapped_ip = node_ip.ipv6? ? "[#{node_ip}]" : node_ip
146
+ proxied? ? subnet.discovery.url + "/discovery/#{node_ip}" : "https://#{wrapped_ip}:8443"
138
147
  end
139
148
 
140
149
  def refresh_facts
141
- facts = ::ForemanDiscovery::NodeAPI::Inventory.new(:url => proxy_url(self.ip)).facter
150
+ facts = ::ForemanDiscovery::NodeAPI::Inventory.new(:url => proxy_url(ip4or6)).facter
142
151
  self.class.import_host facts
143
152
  ::ForemanDiscovery::HostFactImporter.new(self).import_facts facts
144
153
  rescue => e
145
154
  ::Foreman::Logging.exception("Unable to get facts from proxy", e)
146
- raise ::Foreman::WrappedException.new(e, N_("Could not get facts from proxy %{url}: %{error}"), :url => proxy_url(self.ip), :error => e)
155
+ raise ::Foreman::WrappedException.new(e, N_("Could not get facts from proxy %{url}: %{error}"), :url => proxy_url(ip4or6), :error => e)
147
156
  end
148
157
 
149
- def reboot(old_ip = nil, new_ip = nil)
158
+ def reboot(old_ip = nil, new_ip = nil, old_ip6 = nil, new_ip6 = nil)
150
159
  # perform the action against the original lease as well as the new reservation
151
- ips = [old_ip, new_ip, self.ip].compact.uniq
160
+ if Setting[:discovery_prefer_ipv6]
161
+ ips = [old_ip6, new_ip6, self.ip6, old_ip, new_ip, self.ip].compact.uniq
162
+ else
163
+ ips = [old_ip, new_ip, self.ip, old_ip6, new_ip6, self.ip6].compact.uniq
164
+ end
152
165
  logger.debug "Performing reboot calls against #{ips.to_sentence}, facts left #{facts.count}"
153
166
  ips.each do |next_ip|
154
167
  begin
155
- node_url = proxy_url(next_ip)
168
+ node_url = proxy_url(IPAddr.new(next_ip))
156
169
  logger.debug "Performing reboot call against #{node_url}"
157
170
  resource = ::ForemanDiscovery::NodeAPI::Power.service(:url => node_url)
158
171
  return true if resource.reboot
@@ -165,13 +178,17 @@ class Host::Discovered < ::Host::Base
165
178
  raise ::Foreman::Exception.new(msg, action: "reboot", ips: ips.to_sentence)
166
179
  end
167
180
 
168
- def kexec(json, old_ip = nil, new_ip = nil)
181
+ def kexec(json, old_ip = nil, new_ip = nil, old_ip6 = nil, new_ip6 = nil)
169
182
  # perform the action against the original lease as well as the new reservation
170
- ips = [old_ip, new_ip, self.ip].compact.uniq
183
+ if Setting[:discovery_prefer_ipv6]
184
+ ips = [old_ip6, new_ip6, self.ip6, old_ip, new_ip, self.ip].compact.uniq
185
+ else
186
+ ips = [old_ip, new_ip, self.ip, old_ip6, new_ip6, self.ip6].compact.uniq
187
+ end
171
188
  logger.debug "Performing kexec calls against #{ips.to_sentence}, #{facts.count} facts left"
172
189
  ips.each do |next_ip|
173
190
  begin
174
- node_url = proxy_url(next_ip)
191
+ node_url = proxy_url(IPAddr.new(next_ip))
175
192
  logger.debug "Performing kexec call against #{node_url}"
176
193
  resource = ::ForemanDiscovery::NodeAPI::Power.service(:url => node_url)
177
194
  return true if resource.kexec(json)
@@ -27,7 +27,7 @@ module Host::ManagedExtensions
27
27
  end
28
28
 
29
29
  def setReboot
30
- old.becomes(Host::Discovered).reboot(old.ip, ip)
30
+ old.becomes(Host::Discovered).reboot(old.ip, ip, old.ip6, ip6)
31
31
  # It is too late to report error in the post_queue, we catch them and
32
32
  # continue. If flash is implemented for new hosts (http://projects.theforeman.org/issues/10559)
33
33
  # we can report the error to the user perhaps.
@@ -55,7 +55,7 @@ module Host::ManagedExtensions
55
55
  end
56
56
 
57
57
  def setKexec
58
- old.becomes(Host::Discovered).kexec(render_kexec_template.to_json, old.ip, ip)
58
+ old.becomes(Host::Discovered).kexec(render_kexec_template.to_json, old.ip, ip, old.ip6, ip6)
59
59
  true
60
60
  rescue ::Foreman::Exception => e
61
61
  Foreman::Logging.exception("Unable to kexec", e)
@@ -37,6 +37,7 @@ class Setting::Discovered < ::Setting
37
37
  self.set('discovery_always_rebuild_dns', N_("Force DNS entries creation when provisioning discovered host"), true, N_("Force DNS")),
38
38
  self.set('discovery_error_on_existing', N_("Do not allow to discover existing managed host matching MAC of a provisioning NIC (errors out early)"), false, N_("Error on existing NIC")),
39
39
  self.set('discovery_naming', N_("Discovery hostname naming pattern"), 'Fact', N_("Type of name generator"), nil, {:collection => Proc.new {::Host::Discovered::NAMING_PATTERNS} }),
40
+ self.set('discovery_prefer_ipv6', N_("Prefer IPv6 to IPv4 when calling discovered nodes"), false, N_("Prefer IPv6")),
40
41
  ]
41
42
  end
42
43
 
@@ -0,0 +1,5 @@
1
+ class DiscoveryFactImporter < StructuredFactImporter
2
+ def fact_name_class
3
+ DiscoveryFactName
4
+ end
5
+ end
@@ -0,0 +1,106 @@
1
+ module ForemanDiscovery
2
+ class FactToCategoryResolver
3
+ include ActionView::Helpers::NumberHelper
4
+
5
+ attr_reader :categories, :interfaces
6
+
7
+ CATEGORIES_NAMES = [N_("Highlights"),
8
+ N_("Storage"),
9
+ N_("Hardware"),
10
+ N_("Network"),
11
+ N_("Software"),
12
+ N_("IPMI"),
13
+ N_("Miscellaneous")].freeze
14
+
15
+ def initialize(host)
16
+ categories = %i[highlights storage hardware network software ipmi]
17
+
18
+ @regex_array = categories.map do |category|
19
+ settings_category = settings_discovery_fact_prefix(category)
20
+ if settings_category.empty?
21
+ send(category)
22
+ else
23
+ Regexp.new(settings_category)
24
+ end
25
+ end
26
+
27
+ @regex_array << false
28
+
29
+ @categories = []
30
+
31
+ @interfaces = host.interfaces.map do |interface|
32
+ {
33
+ identifier: interface["identifier"],
34
+ type: interface["type"],
35
+ mac: interface["mac"],
36
+ ip: interface["ip"] || "N/A",
37
+ primary: interface["primary"],
38
+ provision: interface["provision"],
39
+ }
40
+ end
41
+
42
+ assign_facts(host)
43
+ end
44
+
45
+ private
46
+
47
+ def assign_facts(host)
48
+ host.facts_hash.each do |key, value|
49
+ value = number_to_human_size(value) if /size$/.match(key)
50
+ assign_fact_to_category(key, value)
51
+ end
52
+
53
+ return if host.primary_interface.subnet.nil?
54
+
55
+ discovery_subnet = "#{host.primary_interface.subnet.name} (#{host.primary_interface.subnet.network})"
56
+ assign_fact_to_category("discovery_subnet", discovery_subnet)
57
+ end
58
+
59
+ def assign_fact_to_category(key, value)
60
+ if @interfaces.any? { |interface| key.include? interface[:identifier] }
61
+ @categories[3] = {} if @categories[3].nil?
62
+ @categories[3][key] = value
63
+ return
64
+ end
65
+
66
+ @regex_array.each_with_index do |regex, index|
67
+ @categories[index] = {} if @categories[index].nil?
68
+ if !regex
69
+ @categories[index][key] = value
70
+ elsif regex.match key
71
+ @categories[index][key] = value
72
+ break
73
+ end
74
+ end
75
+ end
76
+
77
+ def highlights
78
+ /^(productname|memorysize|manufacturer|architecture|macaddress$|processorcount|physicalprocessorcount|discovery_subnet|discovery_boot|ipaddress$)/
79
+ end
80
+
81
+ def storage
82
+ /^blockdevice/
83
+ end
84
+
85
+ def hardware
86
+ /^(hardw|manufacturer|memo|process)/
87
+ end
88
+
89
+ def network
90
+ /^(interfaces|dhcp|fqdn|hostname)/
91
+ end
92
+
93
+ def software
94
+ /^(bios|os|discovery)/
95
+ end
96
+
97
+ def ipmi
98
+ /^ipmi/
99
+ end
100
+
101
+ def settings_discovery_fact_prefix(name)
102
+ Setting["discovery_facts_#{name}".to_sym]
103
+ end
104
+ end
105
+ end
106
+
@@ -21,10 +21,10 @@ class ForemanDiscovery::HostConverter
21
21
  def self.set_build_clean_facts(host)
22
22
  # fact cleaning
23
23
  if Setting['discovery_clean_facts']
24
- # clean all facts except those starting with "discovery_"
24
+ # clean all facts except those from Discovery
25
25
  host.define_singleton_method(:clear_facts) do
26
- keep_ids = FactValue.where("host_id = #{host.id}").joins(:fact_name).where("fact_names.name like 'discovery_%'").pluck("fact_values.id")
27
- FactValue.where("host_id = #{host.id} and id not in (?)", keep_ids).delete_all
26
+ keep_ids = FactValue.where(host_id: host.id, fact_names: { type: 'DiscoveryFactName' }).where("fact_names.name like 'discovery_%'").joins(:fact_name).pluck("fact_values.id")
27
+ FactValue.where.not(id: keep_ids).delete_all
28
28
  end
29
29
  else
30
30
  # clean no facts (default behavior)
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+ module ForemanDiscovery
3
+ module UINotifications
4
+ # Adds notification upon failed discovery
5
+ class FailedDiscovery < ::UINotifications::Base
6
+ def initialize(message)
7
+ @message = message
8
+ end
9
+
10
+ private
11
+
12
+ def create
13
+ add_notification if update_notifications.zero?
14
+ end
15
+
16
+ def update_notifications
17
+ blueprint.mass_update_expiry
18
+ end
19
+
20
+ def add_notification
21
+ Notification.create!(
22
+ initiator: initiator,
23
+ audience: ::Notification::AUDIENCE_ADMIN,
24
+ message: _("One or more hosts with failed discovery due to error: #{@message}"),
25
+ notification_blueprint: blueprint,
26
+ )
27
+ end
28
+
29
+ def blueprint
30
+ @blueprint ||= NotificationBlueprint.find_by(name: 'failed_discovery')
31
+ end
32
+ end
33
+ end
34
+ end
@@ -8,7 +8,7 @@ module ForemanDiscovery
8
8
  private
9
9
 
10
10
  def create
11
- add_notification if update_notifications.zero?
11
+ add_notification
12
12
  end
13
13
 
14
14
  def update_notifications
@@ -19,6 +19,7 @@ module ForemanDiscovery
19
19
  Notification.create!(
20
20
  initiator: initiator,
21
21
  audience: ::Notification::AUDIENCE_SUBJECT,
22
+ message: _("Host %s has been dicovered") % subject.name,
22
23
  subject: subject,
23
24
  notification_blueprint: blueprint
24
25
  )
@@ -1,47 +1,51 @@
1
1
  <% title _('Discovered Hosts') %>
2
2
  <table class="<%= table_css_classes('table-condensed') %>" >
3
- <tr>
4
- <th class="ca"><%= check_box_tag "check_all", "", false, { :onclick => "tfm.hosts.table.toggleCheck()", :'check-title' => _("Select all items in this page"), :'uncheck-title'=> _("items selected. Uncheck to Clear") } %></th>
5
- <th class=''><%= sort :name, :as => _('Name') %></th>
6
- <th class="hidden-tablet hidden-xs"><%= sort :model, :as => _('Model') %></th>
7
- <th class="hidden-tablet hidden-xs"><%= sort :ip, :as => _('IP Address') %></th>
8
- <th class="hidden-tablet hidden-xs"><%= sort :cpu_count, :as => _('CPUs') %></th>
9
- <th class="hidden-tablet hidden-xs"><%= sort :memory, :as => _('Memory') %></th>
10
- <th class="hidden-tablet hidden-xs"><%= sort :disk_count, :as => _('Disk Count') %></th>
11
- <th class="hidden-tablet hidden-xs"><%= sort :disks_size, :as => _('Disks Size') %></th>
12
- <% Setting::Discovered.discovery_fact_column_array.each do |fact_column| %>
13
- <th class="hidden-tablet hidden-xs"><%= fact_column.capitalize %></th>
14
- <% end %>
15
- <th class="hidden-tablet hidden-xs"><%= sort :location, :as => _('Location') %></th>
16
- <th class="hidden-tablet hidden-xs"><%= sort :organization, :as => _('Organization') %></th>
17
- <th class="hidden-tablet hidden-xs"><%= sort :subnet, :as => _("Subnet") %></th>
18
- <th class="hidden-tablet hidden-xs"><%= sort :last_report, :as => _("Last Facts Upload") %></th>
19
- <th class="hidden-tablet hidden-xs"><%= _("Actions") %></th>
20
- </tr>
21
- <% @hosts.each do |host| -%>
3
+ <thead>
22
4
  <tr>
23
- <td class="ca">
24
- <%= check_box_tag "host_ids[]", nil, false, :id => "host_ids_#{host.id}", :disabled => !authorized_for_edit_destroy?, :class => 'host_select_boxes', :onclick => 'tfm.hosts.table.hostChecked(this)' -%>
25
- </td>
26
- <%= render :partial => "discovered_host", :locals => {:host => host} %>
27
- <td class="hidden-tablet hidden-xs"><%= host.location.try(:title) %></td>
28
- <td class="hidden-tablet hidden-xs"><%= host.organization.try(:title) %></td>
29
- <td class="hidden-tablet hidden-xs"><%= host.primary_interface.try(:subnet).try(:to_label) %></td>
30
- <td class="hidden-tablet hidden-xs"><%= disc_report_column(host) %></td>
31
- <td>
32
- <!-- Modal -->
33
- <%= render :partial => "discovered_host_modal", :locals => {:host => host} %>
34
- </div>
35
- <%= action_buttons(
36
- provision_button(host, hash_for_edit_discovered_host_path(:id => host)),
37
- display_link_if_authorized(_("Auto Provision"), hash_for_auto_provision_discovered_host_path(:id => host), :method => :post),
38
- display_link_if_authorized(_("Refresh facts"), hash_for_refresh_facts_discovered_host_path(:id => host)),
39
- display_link_if_authorized(_("Reboot"), hash_for_reboot_discovered_host_path(:id => host), :method => :put),
40
- display_delete_if_authorized(hash_for_discovered_host_path(:id => host), :data => { :confirm => _("Delete %s?") % host.name }, :action => :destroy)) %>
5
+ <th class="ca"><%= check_box_tag "check_all", "", false, { :onclick => "tfm.hosts.table.toggleCheck()", :'check-title' => _("Select all items in this page"), :'uncheck-title'=> _("items selected. Uncheck to Clear") } %></th>
6
+ <th class=''><%= sort :name, :as => _('Name') %></th>
7
+ <th class="hidden-tablet hidden-xs"><%= sort :model, :as => _('Model') %></th>
8
+ <th class="hidden-tablet hidden-xs"><%= sort :ip, :as => _('IP Address') %></th>
9
+ <th class="hidden-tablet hidden-xs"><%= sort :cpu_count, :as => _('CPUs') %></th>
10
+ <th class="hidden-tablet hidden-xs"><%= sort :memory, :as => _('Memory') %></th>
11
+ <th class="hidden-tablet hidden-xs"><%= sort :disk_count, :as => _('Disk Count') %></th>
12
+ <th class="hidden-tablet hidden-xs"><%= sort :disks_size, :as => _('Disks Size') %></th>
13
+ <% Setting::Discovered.discovery_fact_column_array.each do |fact_column| %>
14
+ <th class="hidden-tablet hidden-xs"><%= fact_column.capitalize %></th>
15
+ <% end %>
16
+ <th class="hidden-tablet hidden-xs"><%= sort :location, :as => _('Location') %></th>
17
+ <th class="hidden-tablet hidden-xs"><%= sort :organization, :as => _('Organization') %></th>
18
+ <th class="hidden-tablet hidden-xs"><%= sort :subnet, :as => _("Subnet") %></th>
19
+ <th class="hidden-tablet hidden-xs"><%= sort :last_report, :as => _("Last Facts Upload") %></th>
20
+ <th class="hidden-tablet hidden-xs"><%= _("Actions") %></th>
21
+ </tr>
22
+ </thead>
23
+ <tbody>
24
+ <% @hosts.each do |host| -%>
25
+ <tr>
26
+ <td class="ca">
27
+ <%= check_box_tag "host_ids[]", nil, false, :id => "host_ids_#{host.id}", :disabled => !authorized_for_edit_destroy?, :class => 'host_select_boxes', :onclick => 'tfm.hosts.table.hostChecked(this)' -%>
41
28
  </td>
42
- </tr>
43
- <% end -%>
44
- </table>
29
+ <%= render :partial => "discovered_host", :locals => {:host => host} %>
30
+ <td class="hidden-tablet hidden-xs"><%= host.location.try(:title) %></td>
31
+ <td class="hidden-tablet hidden-xs"><%= host.organization.try(:title) %></td>
32
+ <td class="hidden-tablet hidden-xs"><%= host.primary_interface.try(:subnet).try(:to_label) %></td>
33
+ <td class="hidden-tablet hidden-xs"><%= disc_report_column(host) %></td>
34
+ <td>
35
+ <!-- Modal -->
36
+ <%= render :partial => "discovered_host_modal", :locals => {:host => host} %>
37
+ </div>
38
+ <%= action_buttons(
39
+ provision_button(host, hash_for_edit_discovered_host_path(:id => host)),
40
+ display_link_if_authorized(_("Auto Provision"), hash_for_auto_provision_discovered_host_path(:id => host), :method => :post),
41
+ display_link_if_authorized(_("Refresh facts"), hash_for_refresh_facts_discovered_host_path(:id => host)),
42
+ display_link_if_authorized(_("Reboot"), hash_for_reboot_discovered_host_path(:id => host), :method => :put),
43
+ display_delete_if_authorized(hash_for_discovered_host_path(:id => host), :data => { :confirm => _("Delete %s?") % host.name }, :action => :destroy)) %>
44
+ </td>
45
+ </tr>
46
+ <% end -%>
47
+ </tbody>
48
+ </table>
45
49
 
46
50
  <div id="confirmation-modal" class="modal fade">
47
51
  <div class="modal-dialog">
@@ -8,7 +8,6 @@
8
8
  <% content_for(:title, _("Discovered Hosts")) %>
9
9
 
10
10
  <% content_for(:content) do %>
11
- <%= notifications %>
12
11
  <div id="organization-id" data-id="<%= Organization.current.id if Organization.current %>" ></div>
13
12
  <div id="user-id" data-id="<%= User.current.id if User.current %>" ></div>
14
13
  <%= react_component('DiscoveredHosts', docUrl: discovery_doc_url ) %>
@@ -0,0 +1,3 @@
1
+ <% title _("Clone %s") % @discovered_rules.to_s %>
2
+
3
+ <%= render :partial => 'form' %>
@@ -8,6 +8,8 @@
8
8
  <th><%= sort :search, :as => s_("DiscoveryRule|Query") %></th>
9
9
  <th><%= _("Host Group") %></th>
10
10
  <th><%= _("Hosts/Limit") %></th>
11
+ <th><%= sort :location, :as => _('Location') %></th>
12
+ <th><%= sort :organization, :as => _('Organization') %></th>
11
13
  <th><%= sort :enabled, :as => s_("DiscoveryRule|Enabled") %></th>
12
14
  <th><%= _("Actions") %></th>
13
15
  </tr>
@@ -18,6 +20,8 @@
18
20
  <td><%= trunc_with_tooltip(rule.search) %></td>
19
21
  <td><%= label_with_link(rule.hostgroup, 26, authorizer) %></td>
20
22
  <td><%= rule.hosts.count %> / <%= rule.max_count %></td>
23
+ <td><%= rule.locations.find_by(title: Location.current.try(:title)).try(:title) || rule.locations.pluck(:title).join(",") %></td>
24
+ <td><%= rule.organizations.find_by(title: Organization.current.try(:title)).try(:title) || rule.organizations.pluck(:title).join(",") %></td>
21
25
  <td><%= checked_icon rule.enabled %></td>
22
26
  <td><%= action_buttons(*permitted_discovery_actions(rule)) %></td>
23
27
  </tr>