foreman/app/models/host/base.rb @ fcc2b55d
d7611b24 | Greg Sutcliffe | module Host
|
|
4deab2f3 | Lukas Zapletal | class Base < ApplicationRecord
|
|
38964973 | Dominic Cleal | prepend Foreman::STI
|
|
acfbc458 | Marek Hulan | include Authorizable
|
|
e768c976 | Tomas Strachota | include Parameterizable::ByName
|
|
43c4bd72 | Marek Hulan | include DestroyFlag
|
|
8cf057c8 | Daniel Lobato | include InterfaceCloning
|
|
535d2329 | Amir Fefer | include Hostext::Ownership
|
|
f2ac9055 | Lukas Zapletal | include Foreman::TelemetryHelper
|
|
ae4998bc | Tomer Brisker | ||
feacea35 | Amos Benari | self.table_name = :hosts
|
|
8b737c9c | Joseph Magen | extend FriendlyId
|
|
friendly_id :name
|
|||
796352ed | Greg Sutcliffe | ||
3034e8e2 | Ori Rabin | validates_lengths_from_database
|
|
c773c0ef | Tomer Brisker | belongs_to :model, :name_accessor => 'hardware_model_name'
|
|
698e916c | Shimon Shtein | belongs_to :owner, :polymorphic => true
|
|
796352ed | Greg Sutcliffe | has_many :fact_values, :dependent => :destroy, :foreign_key => :host_id
|
|
has_many :fact_names, :through => :fact_values
|
|||
fb9f45e8 | Daniel Lobato | has_many :interfaces, -> { order(:identifier) }, :dependent => :destroy, :inverse_of => :host, :class_name => 'Nic::Base',
|
|
:foreign_key => :host_id
|
|||
has_one :primary_interface, -> { where(:primary => true) }, :class_name => 'Nic::Base', :foreign_key => 'host_id'
|
|||
has_one :provision_interface, -> { where(:provision => true) }, :class_name => 'Nic::Base', :foreign_key => 'host_id'
|
|||
43c4bd72 | Marek Hulan | has_one :domain, :through => :primary_interface
|
|
has_one :subnet, :through => :primary_interface
|
|||
9d56081f | Timo Goebel | has_one :subnet6, :through => :primary_interface
|
|
43c4bd72 | Marek Hulan | accepts_nested_attributes_for :interfaces, :allow_destroy => true
|
|
796352ed | Greg Sutcliffe | ||
bf75590c | Marek Hulan | belongs_to :location
|
|
belongs_to :organization
|
|||
9ba40b5e | Tomer Brisker | belongs_to :hostgroup
|
|
bf75590c | Marek Hulan | ||
96144a47 | Daniel Lobato | alias_attribute :hostname, :name
|
|
a4d7a037 | Marek Hulan | ||
630061d2 | Michael Moll | validates :name, :presence => true, :uniqueness => true, :format => {:with => Net::Validations::HOST_REGEXP, :message => _(Net::Validations::HOST_REGEXP_ERR_MSG)}
|
|
43c4bd72 | Marek Hulan | validate :host_has_required_interfaces
|
|
f2d5f955 | Marek Hulan | validate :uniq_interfaces_identifiers
|
|
dac16774 | Adam Ruzicka | validate :build_managed_only
|
|
a4d7a037 | Marek Hulan | ||
4cbf879e | Lukas Zapletal | include PxeLoaderSuggestion
|
|
43c4bd72 | Marek Hulan | ||
bb3572ff | Daniel Lobato | default_scope -> { where(taxonomy_conditions) }
|
|
9926db42 | Dominic Cleal | ||
def self.taxonomy_conditions
|
|||
ea71c8a7 | Dominic Cleal | org = Organization.expand(Organization.current) if SETTINGS[:organizations_enabled]
|
|
loc = Location.expand(Location.current) if SETTINGS[:locations_enabled]
|
|||
bf75590c | Marek Hulan | conditions = {}
|
|
5f606e11 | Daniel Lobato Garcia | conditions[:organization_id] = Array(org).map { |o| o.subtree_ids }.flatten.uniq unless org.nil?
|
|
conditions[:location_id] = Array(loc).map { |l| l.subtree_ids }.flatten.uniq unless loc.nil?
|
|||
9926db42 | Dominic Cleal | conditions
|
|
end
|
|||
bf75590c | Marek Hulan | ||
b597a8a5 | Shimon Shtein | scope :no_location, -> { rewhere(:location_id => nil) }
|
|
scope :no_organization, -> { rewhere(:organization_id => nil) }
|
|||
bf75590c | Marek Hulan | ||
28b7dd9f | Timo Goebel | delegate :ssh_authorized_keys, :to => :owner, :allow_nil => true
|
|
2d2390b5 | Timo Goebel | delegate :notification_recipients_ids, :to => :owner, :allow_nil => true
|
|
28b7dd9f | Timo Goebel | ||
03510341 | Lukas Zapletal | PRIMARY_INTERFACE_ATTRIBUTES = [:name, :ip, :ip6, :mac,
|
|
:subnet, :subnet_id, :subnet_name,
|
|||
:subnet6, :subnet6_id, :subnet6_name,
|
|||
:domain, :domain_id, :domain_name,
|
|||
:lookup_values_attributes].freeze
|
|||
43c4bd72 | Marek Hulan | # primary interface is mandatory because of delegated methods so we build it if it's missing
|
|
# similar for provision interface
|
|||
# we can't set name attribute until we have primary interface so we don't pass it to super
|
|||
# initializer and we set name when we are sure that we have primary interface
|
|||
# we can't create primary interface before calling super because args may contain nested
|
|||
# interface attributes
|
|||
def initialize(*args)
|
|||
values_for_primary_interface = {}
|
|||
03510341 | Lukas Zapletal | build_values_for_primary_interface!(values_for_primary_interface, args)
|
|
43c4bd72 | Marek Hulan | ||
super(*args)
|
|||
ad22248d | Marek Hulan | build_required_interfaces
|
|
03510341 | Lukas Zapletal | update_primary_interface_attributes(values_for_primary_interface)
|
|
43c4bd72 | Marek Hulan | end
|
|
295536d1 | Dominic Cleal | def dup
|
|
super.tap do |host|
|
|||
host.interfaces << self.primary_interface.dup if self.primary_interface.present?
|
|||
end
|
|||
end
|
|||
9d56081f | Timo Goebel | delegate :ip, :ip6, :mac,
|
|
43c4bd72 | Marek Hulan | :subnet, :subnet_id, :subnet_name,
|
|
9d56081f | Timo Goebel | :subnet6, :subnet6_id, :subnet6_name,
|
|
43c4bd72 | Marek Hulan | :domain, :domain_id, :domain_name,
|
|
3de2d5aa | Michael Moll | :hostname, :fqdn, :shortname,
|
|
43c4bd72 | Marek Hulan | :to => :primary_interface, :allow_nil => true
|
|
9d56081f | Timo Goebel | delegate :name=, :ip=, :ip6=, :mac=,
|
|
:subnet=, :subnet_id=, :subnet_name=,
|
|||
:subnet6=, :subnet6_id=, :subnet6_name=,
|
|||
43c4bd72 | Marek Hulan | :domain=, :domain_id=, :domain_name=, :to => :primary_interface
|
|
796352ed | Greg Sutcliffe | ||
d455f32c | Marek Hulan | attr_writer :updated_virtuals
|
|
def updated_virtuals
|
|||
@updated_virtuals ||= []
|
|||
end
|
|||
feacea35 | Amos Benari | def self.attributes_protected_by_default
|
|
super - [ inheritance_column ]
|
|||
end
|
|||
796352ed | Greg Sutcliffe | ||
715d097c | Shimon Shtein | def self.import_host(hostname, certname = nil, deprecated_proxy = nil)
|
|
raise(::Foreman::Exception.new("Invalid Hostname, must be a String")) unless hostname.is_a?(String)
|
|||
Foreman::Deprecation.deprecation_warning("1.19", "proxy parameter is deprecated, please use import_facts to set it") if deprecated_proxy
|
|||
# downcase everything
|
|||
hostname.try(:downcase!)
|
|||
certname.try(:downcase!)
|
|||
host = Host.find_by_certname(certname) if certname.present?
|
|||
host ||= Host.find_by_name(hostname)
|
|||
host ||= self.new(:name => hostname) # if no host was found, build a new one
|
|||
# if we were given a certname but found the Host by hostname we should update the certname
|
|||
# this also sets certname for newly created hosts
|
|||
host.certname = certname if certname.present?
|
|||
host
|
|||
end
|
|||
f0e9c776 | Lukas Zapletal | def create_new_host_when_facts_are_uploaded?
|
|
Setting[:create_new_host_when_facts_are_uploaded]
|
|||
end
|
|||
01055e77 | Greg Sutcliffe | # expect a facts hash
|
|
715d097c | Shimon Shtein | def import_facts(facts, source_proxy = nil)
|
|
74002ce3 | Justin Sherrill | return false if !create_new_host_when_facts_are_uploaded? && new_record?
|
|
d4ec6441 | Marek Hulan | ||
796352ed | Greg Sutcliffe | # we are not importing facts for hosts in build state (e.g. waiting for a re-installation)
|
|
fb68fd22 | Ohad Levy | raise ::Foreman::Exception.new('Host is pending for Build') if build?
|
|
d4ec6441 | Marek Hulan | facts = facts.with_indifferent_access
|
|
976bf6e0 | Dominic Cleal | facts[:domain] = facts[:domain].downcase if facts[:domain].present?
|
|
fb68fd22 | Ohad Levy | ||
715d097c | Shimon Shtein | type = facts.delete(:_type)
|
|
importer = FactImporter.importer_for(type).new(self, facts)
|
|||
f2ac9055 | Lukas Zapletal | telemetry_observe_histogram(:importer_facts_import_duration, facts.size, type: type)
|
|
telemetry_duration_histogram(:importer_facts_import_duration, 1000, type: type) do
|
|||
importer.import!
|
|||
end
|
|||
715d097c | Shimon Shtein | ||
save(:validate => false)
|
|||
parse_facts facts, type, source_proxy
|
|||
end
|
|||
def parse_facts(facts, type, source_proxy)
|
|||
796352ed | Greg Sutcliffe | time = facts[:_timestamp]
|
|
time = time.to_time if time.is_a?(String)
|
|||
abdced07 | Stephen Benjamin | self.last_compile = time if time
|
|
796352ed | Greg Sutcliffe | ||
715d097c | Shimon Shtein | unless build?
|
|
parser = FactParser.parser_for(type).new(facts)
|
|||
f2ac9055 | Lukas Zapletal | telemetry_duration_histogram(:importer_facts_import_duration, 1000, type: type) do
|
|
populate_fields_from_facts(parser, type, source_proxy)
|
|||
end
|
|||
715d097c | Shimon Shtein | end
|
|
fb68fd22 | Ohad Levy | ||
e88536b2 | Daniel Lobato | set_taxonomies(facts)
|
|
796352ed | Greg Sutcliffe | ||
# we are saving here with no validations, as we want this process to be as fast
|
|||
# as possible, assuming we already have all the right settings in Foreman.
|
|||
# If we don't (e.g. we never install the server via Foreman, we populate the fields from facts
|
|||
# TODO: if it was installed by Foreman and there is a mismatch,
|
|||
# we should probably send out an alert.
|
|||
96144a47 | Daniel Lobato | save(:validate => false)
|
|
796352ed | Greg Sutcliffe | end
|
|
def attributes_to_import_from_facts
|
|||
d455f32c | Marek Hulan | [ :model ]
|
|
796352ed | Greg Sutcliffe | end
|
|
715d097c | Shimon Shtein | def populate_fields_from_facts(parser, type, source_proxy)
|
|
ad22248d | Marek Hulan | # we must create interface if it's missing so we can store domain
|
|
build_required_interfaces(:managed => false)
|
|||
d455f32c | Marek Hulan | set_non_empty_values(parser, attributes_to_import_from_facts)
|
|
aa48b69c | Marek Hulan | set_interfaces(parser) if parser.parse_interfaces?
|
|
796352ed | Greg Sutcliffe | end
|
|
d455f32c | Marek Hulan | def set_non_empty_values(parser, methods)
|
|
796352ed | Greg Sutcliffe | methods.each do |attr|
|
|
d455f32c | Marek Hulan | value = parser.send(attr)
|
|
85021506 | Michael Moll | self.send("#{attr}=", value) if value.present?
|
|
796352ed | Greg Sutcliffe | end
|
|
end
|
|||
d455f32c | Marek Hulan | def set_interfaces(parser)
|
|
43c4bd72 | Marek Hulan | # if host has no information in primary interface we try to match it and update it
|
|
# instead of creating new interface, suggested primary interface mac and identifier
|
|||
# is saved to primary interface so we match it in updating code below
|
|||
if !self.managed? && self.primary_interface.mac.blank? && self.primary_interface.identifier.blank?
|
|||
identifier, values = parser.suggested_primary_interface(self)
|
|||
b064a42c | Marek Hulan | self.primary_interface.mac = Net::Validations.normalize_mac(values[:macaddress]) if values.present?
|
|
fedf3791 | Brandon Weeks | self.primary_interface.update_attribute(:identifier, identifier)
|
|
43c4bd72 | Marek Hulan | self.primary_interface.save!
|
|
end
|
|||
f2ac9055 | Lukas Zapletal | changed_count = 0
|
|
d455f32c | Marek Hulan | parser.interfaces.each do |name, attributes|
|
|
dc212679 | Dominic Cleal | iface = get_interface_scope(name, attributes).try(:first) || interface_class(name).new(:managed => false)
|
|
d455f32c | Marek Hulan | # create or update existing interface
|
|
f2ac9055 | Lukas Zapletal | changed_count += 1 if set_interface(attributes, name, iface)
|
|
d455f32c | Marek Hulan | end
|
|
ipmi = parser.ipmi_interface
|
|||
if ipmi.present?
|
|||
8ac0ffd9 | Michael Moll | existing = self.interfaces.find_by(:mac => ipmi[:macaddress], :type => Nic::BMC.name)
|
|
d455f32c | Marek Hulan | iface = existing || Nic::BMC.new(:managed => false)
|
|
iface.provider ||= 'IPMI'
|
|||
f2ac9055 | Lukas Zapletal | changed_count += 1 if set_interface(ipmi, 'ipmi', iface)
|
|
d455f32c | Marek Hulan | end
|
|
f2ac9055 | Lukas Zapletal | telemetry_increment_counter(:importer_facts_count_interfaces, changed_count, type: parser.class_name_humanized)
|
|
43c4bd72 | Marek Hulan | ||
self.interfaces.reload
|
|||
d455f32c | Marek Hulan | end
|
|
796352ed | Greg Sutcliffe | def facts_hash
|
|
hash = {}
|
|||
f2c78d4a | Joseph Magen | fact_values.includes(:fact_name).collect do |fact|
|
|
796352ed | Greg Sutcliffe | hash[fact.fact_name.name] = fact.value
|
|
end
|
|||
hash
|
|||
end
|
|||
13d9564d | Lukas Zapletal | alias_method :facts, :facts_hash
|
|
796352ed | Greg Sutcliffe | ||
9d402d07 | Greg Sutcliffe | def ==(comparison_object)
|
|
super ||
|
|||
comparison_object.is_a?(Host::Base) &&
|
|||
id.present? &&
|
|||
comparison_object.id == id
|
|||
end
|
|||
e88536b2 | Daniel Lobato | def set_taxonomies(facts)
|
|
['location', 'organization'].each do |taxonomy|
|
|||
next unless SETTINGS["#{taxonomy.pluralize}_enabled".to_sym]
|
|||
taxonomy_class = taxonomy.classify.constantize
|
|||
ea450c06 | Stephen Benjamin | taxonomy_fact = Setting["#{taxonomy}_fact"]
|
|
e88536b2 | Daniel Lobato | ||
e0910b7e | Michael Moll | if taxonomy_fact.present? && facts.key?(taxonomy_fact)
|
|
27752930 | kgaikwad | taxonomy_from_fact = taxonomy_class.find_by_title(facts[taxonomy_fact].to_s)
|
|
e88536b2 | Daniel Lobato | else
|
|
ea450c06 | Stephen Benjamin | default_taxonomy = taxonomy_class.find_by_title(Setting["default_#{taxonomy}"])
|
|
e88536b2 | Daniel Lobato | end
|
|
f4459c11 | David Davis | if self.send(taxonomy.to_s).present?
|
|
e88536b2 | Daniel Lobato | # Change taxonomy to fact taxonomy if set, otherwise leave it as is
|
|
self.send("#{taxonomy}=", taxonomy_from_fact) unless taxonomy_from_fact.nil?
|
|||
else
|
|||
# No taxonomy was set, set to fact taxonomy or default taxonomy
|
|||
self.send "#{taxonomy}=", (taxonomy_from_fact || default_taxonomy)
|
|||
end
|
|||
end
|
|||
end
|
|||
d455f32c | Marek Hulan | ||
def overwrite?
|
|||
@overwrite ||= false
|
|||
end
|
|||
# We have to coerce the value back to boolean. It is not done for us by the framework.
|
|||
def overwrite=(value)
|
|||
@overwrite = value.to_s == "true"
|
|||
end
|
|||
43c4bd72 | Marek Hulan | def primary_interface
|
|
get_interface_by_flag(:primary)
|
|||
end
|
|||
def provision_interface
|
|||
get_interface_by_flag(:provision)
|
|||
d455f32c | Marek Hulan | end
|
|
356b2e69 | Marek Hulan | def managed_interfaces
|
|
self.interfaces.managed.is_managed.all
|
|||
end
|
|||
def bond_interfaces
|
|||
self.interfaces.bonds.is_managed.all
|
|||
end
|
|||
cee12a22 | Julien Pivotto | def bridge_interfaces
|
|
self.interfaces.bridges.is_managed.all
|
|||
end
|
|||
356b2e69 | Marek Hulan | def interfaces_with_identifier(identifiers)
|
|
self.interfaces.is_managed.where(:identifier => identifiers).all
|
|||
end
|
|||
43c4bd72 | Marek Hulan | def reload(*args)
|
|
drop_primary_interface_cache
|
|||
drop_provision_interface_cache
|
|||
super
|
|||
end
|
|||
def becomes(*args)
|
|||
became = super
|
|||
became.drop_primary_interface_cache
|
|||
became.drop_provision_interface_cache
|
|||
became.interfaces = self.interfaces
|
|||
became
|
|||
end
|
|||
def drop_primary_interface_cache
|
|||
@primary_interface = nil
|
|||
end
|
|||
def drop_provision_interface_cache
|
|||
@provision_interface = nil
|
|||
end
|
|||
bf75590c | Marek Hulan | def matching?
|
|
missing_ids.empty?
|
|||
end
|
|||
def missing_ids
|
|||
Array.wrap(tax_location.try(:missing_ids)) + Array.wrap(tax_organization.try(:missing_ids))
|
|||
end
|
|||
def import_missing_ids
|
|||
tax_location.import_missing_ids if location
|
|||
tax_organization.import_missing_ids if organization
|
|||
end
|
|||
7c671609 | Dominic Cleal | # Provide _id aliases for consistency with the _name methods
|
|
alias_attribute :hardware_model_id, :model_id
|
|||
8cf057c8 | Daniel Lobato | def lookup_value_match
|
|
"fqdn=#{fqdn || name}"
|
|||
end
|
|||
# we must also clone interfaces objects so we can detect their attribute changes
|
|||
# method is public because it's used when we run orchestration from interface side
|
|||
def setup_clone
|
|||
return if new_record?
|
|||
@old = super { |clone| clone.interfaces = self.interfaces.map {|i| setup_object_clone(i) } }
|
|||
end
|
|||
a390cfb7 | Timo Goebel | def skip_orchestration?
|
|
false
|
|||
end
|
|||
700b555c | Lukáš Zapletal | def orchestrated?
|
|
self.class.included_modules.include?(Orchestration)
|
|||
end
|
|||
d455f32c | Marek Hulan | private
|
|
03510341 | Lukas Zapletal | def build_values_for_primary_interface!(values_for_primary_interface, args)
|
|
new_attrs = args.shift
|
|||
unless new_attrs.nil?
|
|||
new_attrs = new_attrs.with_indifferent_access
|
|||
values_for_primary_interface[:name] = NameGenerator.new.next_random_name unless new_attrs.has_key?(:name)
|
|||
PRIMARY_INTERFACE_ATTRIBUTES.each do |attr|
|
|||
values_for_primary_interface[attr] = new_attrs.delete(attr) if new_attrs.has_key?(attr)
|
|||
end
|
|||
model_name = new_attrs.delete(:model_name)
|
|||
new_attrs[:hardware_model_name] = model_name if model_name.present?
|
|||
args.unshift(new_attrs)
|
|||
end
|
|||
end
|
|||
def update_primary_interface_attributes(attrs)
|
|||
attrs.each do |name, value|
|
|||
self.send "#{name}=", value
|
|||
end
|
|||
end
|
|||
bf75590c | Marek Hulan | def tax_location
|
|
return nil unless location_id
|
|||
@tax_location ||= TaxHost.new(location, self)
|
|||
end
|
|||
def tax_organization
|
|||
return nil unless organization_id
|
|||
@tax_organization ||= TaxHost.new(organization, self)
|
|||
end
|
|||
ad22248d | Marek Hulan | def build_required_interfaces(attrs = {})
|
|
308f611d | Marek Hulan | if self.primary_interface.nil?
|
|
if self.interfaces.empty?
|
|||
self.interfaces.build(attrs.merge(:primary => true, :type => 'Nic::Managed'))
|
|||
else
|
|||
interface = self.interfaces.first
|
|||
interface.attributes = attrs
|
|||
interface.primary = true
|
|||
end
|
|||
end
|
|||
377bb86d | Dominic Cleal | self.primary_interface.provision = true if self.provision_interface.nil?
|
|
ad22248d | Marek Hulan | end
|
|
7be20b24 | Marek Hulan | def get_interface_scope(name, attributes, base = self.interfaces)
|
|
case interface_class(name).to_s
|
|||
# we search bonds based on identifiers, e.g. ubuntu sets random MAC after each reboot se we can't
|
|||
# rely on mac
|
|||
cee12a22 | Julien Pivotto | when 'Nic::Bond', 'Nic::Bridge'
|
|
7be20b24 | Marek Hulan | base.virtual.where(:identifier => name)
|
|
# for other interfaces we distinguish between virtual and physical interfaces
|
|||
# for virtual devices we don't check only mac address since it's not unique,
|
|||
# if we want to update the device it must have same identifier
|
|||
else
|
|||
begin
|
|||
macaddress = Net::Validations.normalize_mac(attributes[:macaddress])
|
|||
990dee69 | Timo Goebel | rescue Net::Validations::Error
|
|
7be20b24 | Marek Hulan | logger.debug "invalid mac during parsing: #{attributes[:macaddress]}"
|
|
end
|
|||
7b75a6a0 | Justin Sherrill | ||
mac_based = base.where(:mac => macaddress)
|
|||
7be20b24 | Marek Hulan | if attributes[:virtual]
|
|
7b75a6a0 | Justin Sherrill | mac_based.virtual.where(:identifier => name)
|
|
elsif mac_based.physical.any?
|
|||
mac_based.physical
|
|||
elsif !self.managed
|
|||
68388bc2 | Michael Moll | # Unmanaged host's interfaces are just used for reporting, so overwrite based on identifier first
|
|
7b75a6a0 | Justin Sherrill | base.where(:identifier => name)
|
|
7be20b24 | Marek Hulan | end
|
|
end
|
|||
end
|
|||
89380cc2 | Ondřej Pražák | def update_bonds(iface, name, attributes)
|
|
bond_interfaces.each do |bond|
|
|||
next unless bond.children_mac_addresses.include?(attributes['macaddress'])
|
|||
next if bond.attached_devices_identifiers.include? name
|
|||
update_bond bond, iface, name
|
|||
end
|
|||
end
|
|||
def update_bond(bond, iface, name)
|
|||
b03dcd1b | Michael Moll | if iface&.identifier
|
|
89380cc2 | Ondřej Pražák | bond.remove_device(iface.identifier)
|
|
bond.add_device(name)
|
|||
logger.debug "Updating bond #{bond.identifier}, id #{bond.id}: removing #{iface.identifier}, adding #{name} to attached interfaces"
|
|||
save_updated_bond bond
|
|||
end
|
|||
end
|
|||
def save_updated_bond(bond)
|
|||
bond.save!
|
|||
rescue StandardError => e
|
|||
logger.warn "Saving #{bond.identifier} NIC for host #{self.name} failed, skipping because #{e.message}:"
|
|||
bond.errors.full_messages.each { |e| logger.warn " #{e}" }
|
|||
end
|
|||
d455f32c | Marek Hulan | def set_interface(attributes, name, iface)
|
|
89380cc2 | Ondřej Pražák | # update bond.attached_interfaces when interface is in the list and identifier has changed
|
|
update_bonds(iface, name, attributes) if iface.identifier != name && !iface.virtual? && iface.persisted?
|
|||
d455f32c | Marek Hulan | attributes = attributes.clone
|
|
iface.mac = attributes.delete(:macaddress)
|
|||
iface.ip = attributes.delete(:ipaddress)
|
|||
9d56081f | Timo Goebel | iface.ip6 = attributes.delete(:ipaddress6)
|
|
34c2e24e | Timo Goebel | iface.ip6 = nil if (IPAddr.new('fe80::/10').include?(iface.ip6) rescue false)
|
|
fcc2b55d | Lukáš Zapletal | keep_subnet = attributes.delete(:keep_subnet)
|
|
911c6e9e | Timo Goebel | ||
fcc2b55d | Lukáš Zapletal | if Setting[:update_subnets_from_facts] && !keep_subnet
|
|
911c6e9e | Timo Goebel | iface.subnet = Subnet.subnet_for(iface.ip) if iface.ip_changed? && !iface.matches_subnet?(:ip, :subnet)
|
|
iface.subnet6 = Subnet.subnet_for(iface.ip6) if iface.ip6_changed? && !iface.matches_subnet?(:ip6, :subnet6)
|
|||
end
|
|||
d455f32c | Marek Hulan | iface.virtual = attributes.delete(:virtual) || false
|
|
iface.tag = attributes.delete(:tag) || ''
|
|||
a05b0df0 | Brandon Weeks | iface.attached_to = attributes.delete(:attached_to) if attributes[:attached_to].present?
|
|
d455f32c | Marek Hulan | iface.link = attributes.delete(:link) if attributes.has_key?(:link)
|
|
iface.identifier = name
|
|||
iface.host = self
|
|||
db6d6b8b | Marek Hulan | update_virtuals(iface.identifier_was, name) if iface.identifier_changed? && !iface.virtual? && iface.persisted? && iface.identifier_was.present?
|
|
d455f32c | Marek Hulan | iface.attrs = attributes
|
|
7106fab4 | Marek Hulan | ||
4d0870ad | Dominic Cleal | if iface.new_record? || iface.changed?
|
|
logger.debug "Saving #{name} NIC for host #{self.name}"
|
|||
result = iface.save
|
|||
e263719a | David Davis | ||
4d0870ad | Dominic Cleal | unless result
|
|
logger.warn "Saving #{name} NIC for host #{self.name} failed, skipping because:"
|
|||
iface.errors.full_messages.each { |e| logger.warn " #{e}" }
|
|||
end
|
|||
e263719a | David Davis | ||
4d0870ad | Dominic Cleal | result
|
|
end
|
|||
d455f32c | Marek Hulan | end
|
|
def update_virtuals(old, new)
|
|||
self.updated_virtuals ||= []
|
|||
356b2e69 | Marek Hulan | self.interfaces.where(:attached_to => old).virtual.each do |virtual_interface|
|
|
d455f32c | Marek Hulan | next if self.updated_virtuals.include?(virtual_interface.id) # may have been already renamed by another physical
|
|
356b2e69 | Marek Hulan | virtual_interface.attached_to = new
|
|
d455f32c | Marek Hulan | virtual_interface.identifier = virtual_interface.identifier.sub(old, new)
|
|
virtual_interface.save!
|
|||
self.updated_virtuals.push(virtual_interface.id)
|
|||
end
|
|||
end
|
|||
356b2e69 | Marek Hulan | ||
def interface_class(name)
|
|||
case name
|
|||
when FactParser::BONDS
|
|||
Nic::Bond
|
|||
cee12a22 | Julien Pivotto | when FactParser::BRIDGES
|
|
Nic::Bridge
|
|||
356b2e69 | Marek Hulan | else
|
|
Nic::Managed
|
|||
end
|
|||
end
|
|||
43c4bd72 | Marek Hulan | ||
# we can't use SQL query for new records, because interfaces may not exist yet
|
|||
def get_interface_by_flag(flag)
|
|||
if self.new_record?
|
|||
self.interfaces.detect(&flag)
|
|||
else
|
|||
cache = "@#{flag}_interface"
|
|||
if (result = instance_variable_get(cache))
|
|||
result
|
|||
else
|
|||
# we can't use SQL, we need to get even unsaved objects
|
|||
interface = self.interfaces.detect(&flag)
|
|||
db559817 | Dominic Cleal | interface.host = self if interface && !interface.destroyed? # inverse_of does not help (STI), but ignore this on deletion
|
|
43c4bd72 | Marek Hulan | instance_variable_set(cache, interface)
|
|
end
|
|||
end
|
|||
end
|
|||
# we require primary interface so have know the name of host
|
|||
# provision is required only for managed host and defaults to primary
|
|||
def host_has_required_interfaces
|
|||
check_primary_interface
|
|||
111b0459 | Daniel Lobato | check_provision_interface if self.managed?
|
|
43c4bd72 | Marek Hulan | end
|
|
def check_primary_interface
|
|||
if self.primary_interface.nil?
|
|||
errors.add :interfaces, _("host must have one primary interface")
|
|||
end
|
|||
end
|
|||
def check_provision_interface
|
|||
if self.provision_interface.nil?
|
|||
errors.add :interfaces, _("managed host must have one provision interface")
|
|||
end
|
|||
end
|
|||
f2d5f955 | Marek Hulan | ||
# we can't use standard unique validation on interface since we can't properly handle :scope => :host_id
|
|||
# for new hosts host_id does not exist at that moment, validation would work only for persisted records
|
|||
def uniq_interfaces_identifiers
|
|||
success = true
|
|||
identifiers = []
|
|||
1e7822a5 | Marek Hulan | relevant_interfaces = self.interfaces.select { |i| !i.marked_for_destruction? }
|
|
relevant_interfaces.each do |interface|
|
|||
f2d5f955 | Marek Hulan | next if interface.identifier.blank?
|
|
if identifiers.include?(interface.identifier)
|
|||
interface.errors.add :identifier, :taken
|
|||
success = false
|
|||
end
|
|||
identifiers.push(interface.identifier)
|
|||
end
|
|||
errors.add(:interfaces, _('some interfaces are invalid')) unless success
|
|||
success
|
|||
end
|
|||
75dc676f | Tom Caspy | ||
dac16774 | Adam Ruzicka | def build_managed_only
|
|
if !managed? && build?
|
|||
errors.add(:build, _('cannot be enabled for an unmanaged host'))
|
|||
end
|
|||
end
|
|||
75dc676f | Tom Caspy | def password_base64_encrypted?
|
|
if root_pass_changed?
|
|||
root_pass == hostgroup.try(:read_attribute, :root_pass)
|
|||
else
|
|||
true
|
|||
end
|
|||
end
|
|||
d7611b24 | Greg Sutcliffe | end
|
|
end
|
|||
7fff2188 | Marek Hulan | ||
require_dependency 'host/managed'
|