Project

General

Profile

Download (20.5 KB) Statistics
| Branch: | Tag: | Revision:
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'