foreman_discovery 15.0.2 → 16.2.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/controllers/api/v2/discovery_rules_controller.rb +1 -1
- data/app/controllers/concerns/foreman/controller/discovered_extensions.rb +4 -0
- data/app/controllers/discovered_hosts_controller.rb +6 -3
- data/app/controllers/discovery_rules_controller.rb +1 -1
- data/app/models/discovery_rule.rb +1 -1
- data/app/models/host/discovered.rb +49 -31
- data/app/models/host/managed_extensions.rb +2 -2
- data/app/models/setting/discovered.rb +28 -33
- data/app/services/foreman_discovery/fact_parser.rb +1 -1
- data/app/services/foreman_discovery/host_converter.rb +38 -2
- data/app/services/foreman_discovery/host_fact_importer.rb +10 -0
- data/app/services/foreman_discovery/import_hooks/subnet_and_taxonomy.rb +6 -14
- data/app/services/foreman_discovery/node_api/node_resource.rb +1 -0
- data/app/services/foreman_discovery/subnet_suggestion.rb +26 -0
- data/app/views/foreman_discovery/debian_kexec.erb +3 -2
- data/app/views/foreman_discovery/redhat_kexec.erb +2 -1
- data/config/routes.rb +2 -0
- data/db/migrate/20150512150432_remove_old_discovery_reader_permissions.rb +1 -1
- data/db/migrate/20151023144501_regenerate_red_hat_kexec.rb +1 -1
- data/db/migrate/20180412124505_add_priority_score_to_discovery_rules.rb +1 -1
- data/extra/discover-host +34 -14
- data/lib/foreman_discovery/engine.rb +4 -4
- data/lib/foreman_discovery/version.rb +1 -1
- data/locale/ca/LC_MESSAGES/foreman_discovery.mo +0 -0
- data/locale/ca/foreman_discovery.edit.po +122 -93
- data/locale/ca/foreman_discovery.po +31 -8
- data/locale/de/LC_MESSAGES/foreman_discovery.mo +0 -0
- data/locale/de/foreman_discovery.edit.po +125 -96
- data/locale/de/foreman_discovery.po +34 -11
- data/locale/en/LC_MESSAGES/foreman_discovery.mo +0 -0
- data/locale/en/foreman_discovery.edit.po +118 -88
- data/locale/en/foreman_discovery.po +27 -4
- data/locale/en_GB/LC_MESSAGES/foreman_discovery.mo +0 -0
- data/locale/en_GB/foreman_discovery.edit.po +125 -96
- data/locale/en_GB/foreman_discovery.po +34 -11
- data/locale/es/LC_MESSAGES/foreman_discovery.mo +0 -0
- data/locale/es/foreman_discovery.edit.po +124 -95
- data/locale/es/foreman_discovery.po +33 -10
- data/locale/foreman_discovery.pot +119 -89
- data/locale/fr/LC_MESSAGES/foreman_discovery.mo +0 -0
- data/locale/fr/foreman_discovery.edit.po +122 -93
- data/locale/fr/foreman_discovery.po +31 -8
- data/locale/gl/LC_MESSAGES/foreman_discovery.mo +0 -0
- data/locale/gl/foreman_discovery.edit.po +120 -91
- data/locale/gl/foreman_discovery.po +29 -6
- data/locale/it/LC_MESSAGES/foreman_discovery.mo +0 -0
- data/locale/it/foreman_discovery.edit.po +120 -91
- data/locale/it/foreman_discovery.po +29 -6
- data/locale/ja/LC_MESSAGES/foreman_discovery.mo +0 -0
- data/locale/ja/foreman_discovery.edit.po +123 -94
- data/locale/ja/foreman_discovery.po +32 -9
- data/locale/ko/LC_MESSAGES/foreman_discovery.mo +0 -0
- data/locale/ko/foreman_discovery.edit.po +122 -93
- data/locale/ko/foreman_discovery.po +31 -8
- data/locale/pt_BR/LC_MESSAGES/foreman_discovery.mo +0 -0
- data/locale/pt_BR/foreman_discovery.edit.po +123 -94
- data/locale/pt_BR/foreman_discovery.po +32 -9
- data/locale/ru/LC_MESSAGES/foreman_discovery.mo +0 -0
- data/locale/ru/foreman_discovery.edit.po +123 -94
- data/locale/ru/foreman_discovery.po +32 -9
- data/locale/sv_SE/LC_MESSAGES/foreman_discovery.mo +0 -0
- data/locale/sv_SE/foreman_discovery.edit.po +121 -92
- data/locale/sv_SE/foreman_discovery.po +30 -7
- data/locale/zh_CN/LC_MESSAGES/foreman_discovery.mo +0 -0
- data/locale/zh_CN/foreman_discovery.edit.po +122 -93
- data/locale/zh_CN/foreman_discovery.po +31 -8
- data/locale/zh_TW/LC_MESSAGES/foreman_discovery.mo +0 -0
- data/locale/zh_TW/foreman_discovery.edit.po +120 -91
- data/locale/zh_TW/foreman_discovery.po +29 -6
- data/test/facts/bond0-eth0-eth1-active-passive.json +128 -0
- data/test/facts/facts_with_lldp_bond_candidate.json +2 -9
- data/test/facts/only-ipv6.json +205 -0
- data/test/facts/skylake-ipv6.json +223 -0
- data/test/functional/api/v2/discovered_hosts_controller_test.rb +1 -0
- data/test/functional/api/v2/settings_controller_test.rb +2 -2
- data/test/functional/discovered_hosts_controller_test.rb +15 -6
- data/test/integration/discovered_hosts_test.rb +6 -11
- data/test/test_helper_discovery.rb +12 -0
- data/test/unit/discovered_extensions_test.rb +54 -0
- data/test/unit/discovery_attribute_set_test.rb +13 -10
- data/test/unit/discovery_rule_test.rb +1 -0
- data/test/unit/host_discovered_test.rb +32 -29
- data/test/unit/managed_extensions_test.rb +2 -0
- metadata +39 -28
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: accf12de8ae1253e5023ba98c65da7c3af14aa8da4691fe67dd7f18bc5717518
|
4
|
+
data.tar.gz: 0dd4c83a875917ccf3933679a31a747d8f559e5195415fab619cc0220bcd97c4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a8fd61670a325106898043ab9533f7512835394a6141139580436e9c8fdd0027ff0aee02c0a53f6d92b5b4535d5bcbcabfb1cd91ddc0fd85ce8a0a54d2cde27e
|
7
|
+
data.tar.gz: 90263e8535e3d338ee335a280ff89dafd188d7e92c8f7becb340bc6ede0884bbf4ee915ad158de546c84058240c7338d1d4a736f3124e8c6c57f935862073f83
|
@@ -55,7 +55,7 @@ module Api
|
|
55
55
|
param_group :discovery_rule, :as => :update
|
56
56
|
|
57
57
|
def update
|
58
|
-
process_response @discovery_rule.
|
58
|
+
process_response @discovery_rule.update(discovery_rule_params)
|
59
59
|
end
|
60
60
|
|
61
61
|
api :DELETE, "/discovery_rules/:id/", N_("Delete a rule")
|
@@ -41,6 +41,8 @@ module Foreman::Controller::DiscoveredExtensions
|
|
41
41
|
# trigger the provisioning
|
42
42
|
def perform_auto_provision original_host, rule
|
43
43
|
raise(::Foreman::Exception.new(N_("No hostgroup associated with rule '%s'"), rule)) if rule.hostgroup.nil?
|
44
|
+
|
45
|
+
logger.debug "Auto-provisioning via rule #{rule} hostgroup #{rule.hostgroup} subnet #{rule.hostgroup.subnet}"
|
44
46
|
host = ::ForemanDiscovery::HostConverter.to_managed(original_host)
|
45
47
|
host.hostgroup_id = rule.hostgroup_id
|
46
48
|
host.comment = "Auto-discovered and provisioned via rule '#{rule.name}'"
|
@@ -55,6 +57,8 @@ module Foreman::Controller::DiscoveredExtensions
|
|
55
57
|
# explicitly set all inheritable attributes from hostgroup
|
56
58
|
host.attributes = host.apply_inherited_attributes(hostgroup_id: rule.hostgroup_id)
|
57
59
|
host.set_hostgroup_defaults
|
60
|
+
# change subnet and fetch unused IPs
|
61
|
+
::ForemanDiscovery::HostConverter.unused_ip_for_host(host, rule.hostgroup.subnet, rule.hostgroup.subnet6)
|
58
62
|
# save! does not work here
|
59
63
|
if host.save
|
60
64
|
host
|
@@ -78,6 +78,7 @@ class DiscoveredHostsController < ::ApplicationController
|
|
78
78
|
def perform_update(host, success_message = nil)
|
79
79
|
Taxonomy.no_taxonomy_scope do
|
80
80
|
::ForemanDiscovery::HostConverter.set_build_clean_facts(host)
|
81
|
+
::ForemanDiscovery::HostConverter.unused_ip_for_host(host)
|
81
82
|
if host.save
|
82
83
|
success_options = { :success_redirect => host_path(host), :redirect_xhr => request.xhr? }
|
83
84
|
success_options[:success_msg] = success_message if success_message
|
@@ -198,15 +199,17 @@ class DiscoveredHostsController < ::ApplicationController
|
|
198
199
|
|
199
200
|
def setup_host_class_variables
|
200
201
|
if @host.hostgroup
|
202
|
+
subnet = @host.hostgroup.subnet || @host.subnet
|
203
|
+
subnet6 = @host.hostgroup.subnet6 || @host.subnet6
|
201
204
|
@architecture = @host.hostgroup.architecture
|
202
205
|
@operatingsystem = @host.hostgroup.operatingsystem
|
203
206
|
@environment = @host.hostgroup.environment
|
204
207
|
@domain = @host.hostgroup.domain
|
205
|
-
@subnet =
|
208
|
+
@subnet = subnet
|
209
|
+
@subnet6 = subnet6
|
206
210
|
@compute_profile = @host.hostgroup.compute_profile
|
207
211
|
@realm = @host.hostgroup.realm
|
208
|
-
@host.interfaces.first.assign_attributes(subnet:
|
209
|
-
domain: @domain)
|
212
|
+
@host.interfaces.first.assign_attributes(subnet: subnet, subnet6: subnet6, domain: @domain)
|
210
213
|
@host.environment = @environment
|
211
214
|
end
|
212
215
|
end
|
@@ -22,7 +22,7 @@ class DiscoveryRule < ApplicationRecord
|
|
22
22
|
before_validation :enforce_taxonomy
|
23
23
|
|
24
24
|
belongs_to :hostgroup
|
25
|
-
|
25
|
+
has_many_hosts :dependent => :nullify
|
26
26
|
|
27
27
|
scoped_search :on => :name, :complete_value => :true
|
28
28
|
scoped_search :on => :priority, :only_explicit => true
|
@@ -44,9 +44,13 @@ class Host::Discovered < ::Host::Base
|
|
44
44
|
|
45
45
|
# Discovery import workflow:
|
46
46
|
# discovered#import_host ->
|
47
|
-
#
|
48
|
-
#
|
49
|
-
#
|
47
|
+
# ForemanDiscovery::HostFactImporter#import_facts ->
|
48
|
+
# ::HostFactImporter#import_facts ->
|
49
|
+
# ::HostFactImporter#parse_facts ->
|
50
|
+
# discovered#populate_fields_from_facts ->
|
51
|
+
# base#populate_fields_from_facts ->
|
52
|
+
# base#set_interfaces ->
|
53
|
+
# discovered#populate_discovery_fields_from_facts
|
50
54
|
def self.import_host facts
|
51
55
|
raise(::Foreman::Exception.new(N_("Invalid facts, must be a Hash"))) unless facts.is_a?(Hash) || facts.is_a?(ActionController::Parameters)
|
52
56
|
|
@@ -92,19 +96,11 @@ class Host::Discovered < ::Host::Base
|
|
92
96
|
|
93
97
|
# and save (interfaces are created via puppet parser extension)
|
94
98
|
host.save(:validate => false) if host.new_record?
|
95
|
-
|
99
|
+
importer = ForemanDiscovery::HostFactImporter.new(host)
|
100
|
+
raise ::Foreman::Exception.new(N_("Facts could not be imported")) unless importer.import_facts(facts)
|
96
101
|
host
|
97
102
|
end
|
98
103
|
|
99
|
-
def import_facts(facts)
|
100
|
-
# Discovered Hosts won't report in via puppet, so we can use that field to
|
101
|
-
# record the last time it sent facts...
|
102
|
-
self.last_report = Time.now
|
103
|
-
# Set the correct facts type for new foreman facts importing code.
|
104
|
-
facts[:_type] = :foreman_discovery
|
105
|
-
super(facts)
|
106
|
-
end
|
107
|
-
|
108
104
|
def setup_clone
|
109
105
|
# Nic::Managed needs this method but Discovered hosts shouldn't
|
110
106
|
# be doing orchestration anyway...
|
@@ -137,33 +133,55 @@ class Host::Discovered < ::Host::Base
|
|
137
133
|
subnet.present? && subnet.discovery.present?
|
138
134
|
end
|
139
135
|
|
140
|
-
def proxy_url
|
141
|
-
proxied? ? subnet.discovery.url + "/discovery/#{
|
136
|
+
def proxy_url(node_ip)
|
137
|
+
proxied? ? subnet.discovery.url + "/discovery/#{node_ip}" : "https://#{node_ip}:8443"
|
142
138
|
end
|
143
139
|
|
144
140
|
def refresh_facts
|
145
|
-
facts = ::ForemanDiscovery::NodeAPI::Inventory.new(:url => proxy_url).facter
|
141
|
+
facts = ::ForemanDiscovery::NodeAPI::Inventory.new(:url => proxy_url(self.ip)).facter
|
146
142
|
self.class.import_host facts
|
147
|
-
import_facts facts
|
143
|
+
::ForemanDiscovery::HostFactImporter.new(self).import_facts facts
|
148
144
|
rescue => e
|
149
145
|
::Foreman::Logging.exception("Unable to get facts from proxy", e)
|
150
146
|
raise ::Foreman::WrappedException.new(e, N_("Could not get facts from proxy %{url}: %{error}"), :url => proxy_url, :error => e)
|
151
147
|
end
|
152
148
|
|
153
|
-
def reboot
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
149
|
+
def reboot(old_ip = nil, new_ip = nil)
|
150
|
+
# perform the action against the original lease as well as the new reservation
|
151
|
+
ips = [old_ip, new_ip, self.ip].compact.uniq
|
152
|
+
logger.debug "Performing reboot calls against #{ips.to_sentence}, facts left #{facts.count}"
|
153
|
+
ips.each do |next_ip|
|
154
|
+
begin
|
155
|
+
node_url = proxy_url(next_ip)
|
156
|
+
logger.debug "Performing reboot call against #{node_url}"
|
157
|
+
resource = ::ForemanDiscovery::NodeAPI::Power.service(:url => node_url)
|
158
|
+
return true if resource.reboot
|
159
|
+
rescue => e
|
160
|
+
msg = N_("Unable to perform reboot on %{name} (%{url}): %{msg}")
|
161
|
+
::Foreman::Logging.exception(msg % { :name => name, :url => node_url, :msg => e.to_s }, e)
|
162
|
+
end
|
163
|
+
end
|
164
|
+
msg = N_("Unable to perform %{action} on %{ips}")
|
165
|
+
raise ::Foreman::Exception.new(msg, action: "reboot", ips: ips.to_sentence)
|
166
|
+
end
|
167
|
+
|
168
|
+
def kexec(json, old_ip = nil, new_ip = nil)
|
169
|
+
# perform the action against the original lease as well as the new reservation
|
170
|
+
ips = [old_ip, new_ip, self.ip].compact.uniq
|
171
|
+
logger.debug "Performing kexec calls against #{ips.to_sentence}, #{facts.count} facts left"
|
172
|
+
ips.each do |next_ip|
|
173
|
+
begin
|
174
|
+
node_url = proxy_url(next_ip)
|
175
|
+
logger.debug "Performing kexec call against #{node_url}"
|
176
|
+
resource = ::ForemanDiscovery::NodeAPI::Power.service(:url => node_url)
|
177
|
+
return true if resource.kexec(json)
|
178
|
+
rescue => e
|
179
|
+
msg = N_("Unable to perform kexec on %{name} (%{url}): %{msg}")
|
180
|
+
::Foreman::Logging.exception(msg % { :name => name, :url => node_url, :msg => e.to_s }, e)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
msg = N_("Unable to perform %{action} on %{ips}")
|
184
|
+
raise ::Foreman::Exception.new(msg, action: "kexec", ips: ips.to_sentence)
|
167
185
|
end
|
168
186
|
|
169
187
|
def self.model_name
|
@@ -27,7 +27,7 @@ module Host::ManagedExtensions
|
|
27
27
|
end
|
28
28
|
|
29
29
|
def setReboot
|
30
|
-
old.becomes(Host::Discovered).reboot
|
30
|
+
old.becomes(Host::Discovered).reboot(old.ip, ip)
|
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)
|
58
|
+
old.becomes(Host::Discovered).kexec(render_kexec_template.to_json, old.ip, ip)
|
59
59
|
true
|
60
60
|
rescue ::Foreman::Exception => e
|
61
61
|
Foreman::Logging.exception("Unable to kexec", e)
|
@@ -8,41 +8,36 @@ class Setting::Discovered < ::Setting
|
|
8
8
|
BLANK_ATTRS << 'discovery_facts_hardware'
|
9
9
|
BLANK_ATTRS << 'discovery_facts_network'
|
10
10
|
BLANK_ATTRS << 'discovery_facts_ipmi'
|
11
|
-
BLANK_ATTRS << 'discovery_prefix'
|
12
11
|
|
13
|
-
|
14
|
-
|
15
|
-
return unless super
|
12
|
+
STRING_PRESENCE_ATTRS = ['discovery_hostname', 'discovery_prefix']
|
13
|
+
validates :value, :presence => true, :if => proc { |s| STRING_PRESENCE_ATTRS.include?(s.name) }
|
16
14
|
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
end
|
44
|
-
|
45
|
-
true
|
15
|
+
def self.default_settings
|
16
|
+
[
|
17
|
+
self.set('discovery_location', N_("The default location to place discovered hosts in"), "", N_("Discovery location"), nil, { :collection => Proc.new {Hash[Location.all.map{|loc| [loc[:title], loc[:title]]}]} }),
|
18
|
+
self.set('discovery_organization', N_("The default organization to place discovered hosts in"), "", N_("Discovery organization"), nil, { :collection => Proc.new {Hash[Organization.all.map{|org| [org[:title], org[:title]]}]} }),
|
19
|
+
self.set('discovery_fact', N_("Fact name to use for primary interface detection"), "discovery_bootif", N_("Interface fact")),
|
20
|
+
self.set('discovery_auto_bond', N_("Automatic bond interface (if another interface is detected on the same VLAN via LLDP)"), false, N_("Create bond interfaces")),
|
21
|
+
self.set('discovery_clean_facts', N_("Clean all reported facts during provisioning (except discovery facts)"), false, N_("Clean all facts")),
|
22
|
+
self.set('discovery_hostname', N_("List of facts to use for the hostname (separated by comma, first wins)"), "discovery_bootif", N_("Hostname facts")),
|
23
|
+
self.set('discovery_auto', N_("Automatically provision newly discovered hosts, according to the provisioning rules"), false, N_("Auto provisioning")),
|
24
|
+
self.set('discovery_reboot', N_("Automatically reboot or kexec discovered host during provisioning"), true, N_("Reboot")),
|
25
|
+
self.set('discovery_prefix', N_("The default prefix to use for the host name, must start with a letter"), "mac", N_("Hostname prefix")),
|
26
|
+
self.set('discovery_fact_column', N_("Extra facter columns to show in host lists (separate by comma)"), "", N_("Fact columns")),
|
27
|
+
self.set('discovery_facts_highlights', N_("Regex to organize facts for highlights section - e.g. ^(abc|cde)$"), "", N_("Highlighted facts")),
|
28
|
+
self.set('discovery_facts_storage', N_("Regex to organize facts for storage section"), "", N_("Storage facts")),
|
29
|
+
self.set('discovery_facts_software', N_("Regex to organize facts for software section"), "", N_("Software facts")),
|
30
|
+
self.set('discovery_facts_hardware', N_("Regex to organize facts for hardware section"), "", N_("Hardware facts")),
|
31
|
+
self.set('discovery_facts_network', N_("Regex to organize facts for network section"), "", N_("Network facts")),
|
32
|
+
self.set('discovery_facts_ipmi', N_("Regex to organize facts for ipmi section"), "", N_("IPMI facts")),
|
33
|
+
self.set('discovery_lock', N_("Automatically generate PXE configuration to pin a newly discovered host to discovery"), false, N_("Lock PXE")),
|
34
|
+
self.set('discovery_pxelinux_lock_template', N_("PXELinux template to be used when pinning a host to discovery"), 'pxelinux_discovery', N_("Locked PXELinux template name"), nil, { :collection => Proc.new {Hash[ProvisioningTemplate.where(:template_kind => TemplateKind.find_by_name(:snippet)).map{|template| [template[:name], template[:name]]}]} }),
|
35
|
+
self.set('discovery_pxegrub_lock_template', N_("PXEGrub template to be used when pinning a host to discovery"), 'pxegrub_discovery', N_("Locked PXEGrub template name"), nil, { :collection => Proc.new {Hash[ProvisioningTemplate.where(:template_kind => TemplateKind.find_by_name(:snippet)).map{|template| [template[:name], template[:name]]}]} }),
|
36
|
+
self.set('discovery_pxegrub2_lock_template', N_("PXEGrub2 template to be used when pinning a host to discovery"), 'pxegrub2_discovery', N_("Locked PXEGrub2 template name"), nil, { :collection => Proc.new {Hash[ProvisioningTemplate.where(:template_kind => TemplateKind.find_by_name(:snippet)).map{|template| [template[:name], template[:name]]}]} }),
|
37
|
+
self.set('discovery_always_rebuild_dns', N_("Force DNS entries creation when provisioning discovered host"), true, N_("Force DNS")),
|
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
|
+
self.set('discovery_naming', N_("Discovery hostname naming pattern"), 'Fact', N_("Type of name generator"), nil, {:collection => Proc.new {::Host::Discovered::NAMING_PATTERNS} }),
|
40
|
+
]
|
46
41
|
end
|
47
42
|
|
48
43
|
def self.discovery_fact_column_array
|
@@ -6,7 +6,7 @@ module ForemanDiscovery
|
|
6
6
|
raise(::Foreman::Exception.new(N_("Discovered host '%{host}' has all NICs filtered out, filter: %{filter}") %
|
7
7
|
{:host => host, :filter => Setting[:ignored_interface_identifiers]})) if interfaces.size == 0
|
8
8
|
bootif_mac = FacterUtils::bootif_mac(facts).try(:downcase)
|
9
|
-
detected = interfaces.detect { |_, values| values[:macaddress].try(:downcase) == bootif_mac && values[:ipaddress].present? }
|
9
|
+
detected = interfaces.detect { |_, values| values[:macaddress].try(:downcase) == bootif_mac && (values[:ipaddress].present? || values[:ipaddress6].present?) }
|
10
10
|
Rails.logger.debug "Discovery fact parser detected primary interface: #{detected}"
|
11
11
|
# return the detected interface as array [name, facts]
|
12
12
|
detected || raise(::Foreman::Exception.new(N_("Unable to find primary NIC with %{mac} specified via '%{fact}', NIC filter: %{filter}") %
|
@@ -1,7 +1,6 @@
|
|
1
1
|
class ForemanDiscovery::HostConverter
|
2
|
-
|
3
2
|
# Converts discovered host to managed host without uptading the database.
|
4
|
-
# Record must be saved explicitly (using save! or
|
3
|
+
# Record must be saved explicitly (using save! or update! or similar).
|
5
4
|
# Creates shallow copy.
|
6
5
|
def self.to_managed(original_host, set_managed = true, set_build = true, added_attributes = {})
|
7
6
|
host = original_host.becomes(::Host::Managed)
|
@@ -35,4 +34,41 @@ class ForemanDiscovery::HostConverter
|
|
35
34
|
host.build = true
|
36
35
|
end
|
37
36
|
|
37
|
+
def self.unused_ip_for_subnet(subnet, mac, existing_ip)
|
38
|
+
# prefer existing reservation to prevent conflicts
|
39
|
+
existing_rec = subnet&.dhcp_proxy&.record(subnet.network, mac)
|
40
|
+
|
41
|
+
if existing_rec && existing_rec.type == "reservation"
|
42
|
+
# reuse the reservation
|
43
|
+
existing_rec.ip
|
44
|
+
else
|
45
|
+
# no reservation - find new unused IP
|
46
|
+
ipam = subnet.unused_ip(mac)
|
47
|
+
raise(::Foreman::Exception.new(N_("IPAM must be configured for subnet '%s'"), subnet)) unless ipam.present?
|
48
|
+
|
49
|
+
# None IPAM returns nil - in that case keep the current address
|
50
|
+
suggested_ip = ipam.suggest_ip
|
51
|
+
suggested_ip.nil? ? existing_ip : suggested_ip
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
def self.unused_ip_for_host(host, new_subnet = nil, new_subnet6 = nil)
|
56
|
+
host.interfaces.each do |interface|
|
57
|
+
next unless interface.managed?
|
58
|
+
|
59
|
+
interface.subnet = new_subnet if new_subnet
|
60
|
+
interface.subnet6 = new_subnet6 if new_subnet6
|
61
|
+
interface.ip = unused_ip_for_subnet(interface.subnet, interface.mac, interface.ip) if interface.subnet
|
62
|
+
interface.ip6 = unused_ip_for_subnet(interface.subnet6, interface.mac, interface.ip6) if interface.subnet6
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def self.change_subnet_and_unused_ip(host, hostgroup)
|
67
|
+
if host.subnet != hostgroup.subnet || host.subnet6 != hostgroup.subnet6
|
68
|
+
Rails.logger.debug "Discovered host subnets #{[host.subnet, host.subnet6]} do not match hostgroup subnets #{[hostgroup.subnet, hostgroup.subnet6]}"
|
69
|
+
unused_ip_for_host(host, host.hostgroup.subnet, host.hostgroup.subnet6)
|
70
|
+
else
|
71
|
+
Rails.logger.debug "Discovered host subnets #{[host.subnet, host.subnet6]} match hostgroup subnets"
|
72
|
+
end
|
73
|
+
end
|
38
74
|
end
|
@@ -0,0 +1,10 @@
|
|
1
|
+
class ForemanDiscovery::HostFactImporter < ::HostFactImporter
|
2
|
+
def import_facts(facts)
|
3
|
+
# Discovered Hosts won't report in via puppet, so we can use that field to
|
4
|
+
# record the last time it sent facts...
|
5
|
+
host.last_report = Time.now
|
6
|
+
# Set the correct facts type for new foreman facts importing code.
|
7
|
+
facts[:_type] = :foreman_discovery
|
8
|
+
super(facts)
|
9
|
+
end
|
10
|
+
end
|
@@ -4,14 +4,15 @@ module ForemanDiscovery
|
|
4
4
|
class SubnetAndTaxonomy < ImportHook
|
5
5
|
def after_populate
|
6
6
|
primary_ip = host.primary_interface.ip
|
7
|
+
primary_ip6 = host.primary_interface.ip6
|
7
8
|
|
8
|
-
unless primary_ip
|
9
|
+
unless primary_ip || primary_ip6
|
9
10
|
logger.warn "Unable to assign subnet - reboot trigger may not be possible, primary interface is missing IP address"
|
10
11
|
return
|
11
12
|
end
|
12
13
|
|
13
14
|
# set subnet
|
14
|
-
|
15
|
+
set_subnets(primary_ip, primary_ip6)
|
15
16
|
# set location and organization
|
16
17
|
set_location
|
17
18
|
set_organization
|
@@ -19,18 +20,9 @@ module ForemanDiscovery
|
|
19
20
|
|
20
21
|
private
|
21
22
|
|
22
|
-
def
|
23
|
-
host.primary_interface.subnet =
|
24
|
-
|
25
|
-
|
26
|
-
def suggested_subnet(ip)
|
27
|
-
subnet = Subnet.subnet_for(ip)
|
28
|
-
if subnet
|
29
|
-
logger.info "Detected subnet: #{subnet} with taxonomy #{subnet.organizations.collect(&:name)}/#{subnet.locations.collect(&:name)}"
|
30
|
-
else
|
31
|
-
logger.warn "Subnet could not be detected for #{ip}"
|
32
|
-
end
|
33
|
-
subnet
|
23
|
+
def set_subnets(ip, ip6)
|
24
|
+
host.primary_interface.subnet = ForemanDiscovery::SubnetSuggestion.for(ip: ip, kind: 'IPv4') if ip
|
25
|
+
host.primary_interface.subnet6 = ForemanDiscovery::SubnetSuggestion.for(ip: ip6, kind: 'IPv6') if ip6
|
34
26
|
end
|
35
27
|
|
36
28
|
def set_location
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module ForemanDiscovery
|
2
|
+
class SubnetSuggestion
|
3
|
+
attr_accessor :ip, :kind
|
4
|
+
|
5
|
+
def self.for(ip:, kind:)
|
6
|
+
new(ip: ip, kind: kind).()
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(ip:, kind:)
|
10
|
+
self.ip = ip
|
11
|
+
self.kind = kind
|
12
|
+
end
|
13
|
+
|
14
|
+
def call
|
15
|
+
return unless ip
|
16
|
+
|
17
|
+
subnet = Subnet.unscoped.subnet_for(ip)
|
18
|
+
if subnet
|
19
|
+
Rails.logger.info "Detected #{kind} subnet: #{subnet} with taxonomy #{subnet.organizations.collect(&:name)}/#{subnet.locations.collect(&:name)}"
|
20
|
+
else
|
21
|
+
Rails.logger.info "#{kind} subnet could not be detected for #{ip}"
|
22
|
+
end
|
23
|
+
subnet
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -12,9 +12,10 @@ environments. The template must generate JSON format with the following items
|
|
12
12
|
"kernel", "initram", "append" and "extra". The kexec command is composed in
|
13
13
|
the following way:
|
14
14
|
|
15
|
-
kexec --force --
|
15
|
+
kexec --force --debug --append=$append --initrd=$initram $extra $kernel
|
16
16
|
|
17
17
|
Please read kexec(8) man page for more information about semantics.
|
18
|
+
Extra options like --reset-vga can be set via "extra" array.
|
18
19
|
-%>
|
19
20
|
<%
|
20
21
|
mac = @host.facts['discovery_bootif']
|
@@ -28,7 +29,7 @@ Please read kexec(8) man page for more information about semantics.
|
|
28
29
|
options << @host.facts['append']
|
29
30
|
options << "domain=#{@host.domain}"
|
30
31
|
options << 'console-setup/ask_detect=false console-setup/layout=USA console-setup/variant=USA keyboard-configuration/layoutcode=us localechooser/translation/warn-light=true localechooser/translation/warn-severe=true'
|
31
|
-
options << "locale=#{
|
32
|
+
options << "locale=#{host_param('lang') || 'en_US'}"
|
32
33
|
options << "inst.stage2=#{@host.operatingsystem.medium_uri(@host)}" if @host.operatingsystem.name.match(/Atomic/i)
|
33
34
|
-%>
|
34
35
|
{
|