foreman/app/models/nic/base.rb @ bb3572ff
8838eb42 | Ohad Levy | # Represents a Host's network interface
|
|
# This class is the both parent
|
|||
module Nic
|
|||
class Base < ActiveRecord::Base
|
|||
include Foreman::STI
|
|||
feacea35 | Amos Benari | self.table_name = 'nics'
|
|
8838eb42 | Ohad Levy | ||
3034e8e2 | Ori Rabin | validates_lengths_from_database
|
|
8838eb42 | Ohad Levy | attr_accessible :host_id, :host,
|
|
cfd1c413 | Tomas Strachota | :mac, :name, :type,
|
|
d455f32c | Marek Hulan | :provider, :username, :password,
|
|
356b2e69 | Marek Hulan | :identifier, :virtual, :link, :tag, :attached_to,
|
|
43c4bd72 | Marek Hulan | :managed, :bond_options, :attached_devices, :mode,
|
|
:primary, :provision, :compute_attributes,
|
|||
8838eb42 | Ohad Levy | :_destroy # used for nested_attributes
|
|
before_validation :normalize_mac
|
|||
43c4bd72 | Marek Hulan | after_validation :set_validated
|
|
before_destroy :not_required_interface
|
|||
8838eb42 | Ohad Levy | ||
6a2fce1f | Marek Hulan | validates :mac, :uniqueness => {:scope => :virtual},
|
|
:if => Proc.new { |nic| nic.managed? && nic.host && nic.host.managed? && !nic.host.compute? && !nic.virtual? }, :allow_blank => true
|
|||
validates :mac, :presence => true,
|
|||
ba64f022 | Marek Hulan | :if => Proc.new { |nic| nic.managed? && nic.host_managed? && !nic.host.compute? && !nic.virtual? }
|
|
356b2e69 | Marek Hulan | validates :mac, :mac_address => true, :allow_blank => true
|
|
8838eb42 | Ohad Levy | ||
43c4bd72 | Marek Hulan | # TODO uniq on primary per host
|
|
# validate :uniq_with_hosts
|
|||
8838eb42 | Ohad Levy | ||
d455f32c | Marek Hulan | validates :host, :presence => true, :if => Proc.new { |nic| nic.require_host? }
|
|
8838eb42 | Ohad Levy | ||
43c4bd72 | Marek Hulan | validate :exclusive_primary_interface
|
|
validate :exclusive_provision_interface
|
|||
ba64f022 | Marek Hulan | validates :domain, :presence => true, :if => Proc.new { |nic| nic.host_managed? && nic.primary? }
|
|
validate :valid_domain, :if => Proc.new { |nic| nic.host_managed? && nic.primary? }
|
|||
validates :ip, :presence => true, :if => Proc.new { |nic| nic.host_managed? && nic.require_ip_validation? }
|
|||
43c4bd72 | Marek Hulan | ||
d6a19253 | Dominic Cleal | validate :validate_host_location, :if => Proc.new { |nic| SETTINGS[:locations_enabled] && nic.subnet.present? }
|
|
validate :validate_host_organization, :if => Proc.new { |nic| SETTINGS[:organizations_enabled] && nic.subnet.present? }
|
|||
8f695d94 | Shimon Shtein | ||
bb3572ff | Daniel Lobato | scope :bootable, -> { where(:type => "Nic::Bootable") }
|
|
scope :bmc, -> { where(:type => "Nic::BMC") }
|
|||
scope :bonds, -> { where(:type => "Nic::Bond") }
|
|||
scope :interfaces, -> { where(:type => "Nic::Interface") }
|
|||
scope :managed, -> { where(:type => "Nic::Managed") }
|
|||
scope :virtual, -> { where(:virtual => true) }
|
|||
scope :physical, -> { where(:virtual => false) }
|
|||
scope :is_managed, -> { where(:managed => true) }
|
|||
scope :primary, -> { { :conditions => { :primary => true } } }
|
|||
scope :provision, -> { { :conditions => { :provision => true } } }
|
|||
43c4bd72 | Marek Hulan | ||
belongs_to :subnet
|
|||
belongs_to :domain, :counter_cache => 'hosts_count'
|
|||
d455f32c | Marek Hulan | belongs_to_host :inverse_of => :interfaces, :class_name => "Host::Base"
|
|
43c4bd72 | Marek Hulan | # do counter cache only for primary interfaces
|
|
def belongs_to_counter_cache_after_create_for_domain
|
|||
super if self.primary
|
|||
end
|
|||
abd8f1d1 | Daniel Lobato | ||
43c4bd72 | Marek Hulan | def belongs_to_counter_cache_before_destroy_for_domain
|
|
super if self.primary
|
|||
end
|
|||
8838eb42 | Ohad Levy | # keep extra attributes needed for sub classes.
|
|
serialize :attrs, Hash
|
|||
43c4bd72 | Marek Hulan | # provider specific attributes
|
|
serialize :compute_attributes, Hash
|
|||
d455f32c | Marek Hulan | class Jail < ::Safemode::Jail
|
|
6d05514a | Tomas Strachota | allow :managed?, :subnet, :virtual?, :physical?, :mac, :ip, :identifier, :attached_to,
|
|
356b2e69 | Marek Hulan | :link, :tag, :domain, :vlanid, :bond_options, :attached_devices, :mode,
|
|
a45253c2 | Marek Hulan | :attached_devices_identifiers, :primary, :provision, :alias?, :inheriting_mac
|
|
d455f32c | Marek Hulan | end
|
|
6d05514a | Tomas Strachota | def physical?
|
|
!virtual?
|
|||
end
|
|||
5da15d1a | Tomas Strachota | def type_name
|
|
type.split("::").last
|
|||
end
|
|||
cad1b13c | Tomas Strachota | def self.humanized_name
|
|
# provide class name as a default value
|
|||
name.split("::").last
|
|||
end
|
|||
def self.type_by_name(name)
|
|||
a1b2ee53 | Marek Hulan | allowed_types.find { |nic_class| nic_class.humanized_name.downcase == name.to_s.downcase }
|
|
cad1b13c | Tomas Strachota | end
|
|
# NIC types have to be registered to to expose them to users
|
|||
def self.register_type(type)
|
|||
allowed_types << type
|
|||
end
|
|||
def self.allowed_types
|
|||
@allowed_types ||= []
|
|||
end
|
|||
43c4bd72 | Marek Hulan | # after every name change, we synchronize it to host object
|
|
def name=(*args)
|
|||
result = super
|
|||
sync_name
|
|||
result
|
|||
end
|
|||
def shortname
|
|||
domain.nil? ? name : name.to_s.chomp("." + domain.name)
|
|||
end
|
|||
def validated?
|
|||
!!@validated
|
|||
end
|
|||
# we should guarantee the fqdn is always fully qualified
|
|||
def fqdn
|
|||
return name if name.blank? || domain.blank?
|
|||
name.include?('.') ? name : "#{name}.#{domain}"
|
|||
end
|
|||
def clone
|
|||
# do not copy system specific attributes
|
|||
self.deep_clone(:except => [:name, :mac, :ip])
|
|||
end
|
|||
5ad3c4f3 | Marek Hulan | # if this interface does not have MAC and is attached to other interface,
|
|
# we can fetch mac from this other interface
|
|||
def inheriting_mac
|
|||
if self.mac.nil? || self.mac.empty?
|
|||
self.host.interfaces.detect { |i| i.identifier == self.attached_to }.try(:mac)
|
|||
else
|
|||
self.mac
|
|||
end
|
|||
end
|
|||
ba64f022 | Marek Hulan | # we don't consider host as managed if we are in non-unattended mode
|
|
# in which case host managed? flag can be true but we should consider
|
|||
# everything as unmanaged
|
|||
def host_managed?
|
|||
self.host && self.host.managed? && SETTINGS[:unattended]
|
|||
end
|
|||
8838eb42 | Ohad Levy | protected
|
|
def uniq_fields_with_hosts
|
|||
d455f32c | Marek Hulan | self.virtual? ? [] : [:mac]
|
|
8838eb42 | Ohad Levy | end
|
|
# make sure we don't have a conflicting interface with an host record
|
|||
def uniq_with_hosts
|
|||
failed = false
|
|||
uniq_fields_with_hosts.each do |attr|
|
|||
value = self.send(attr)
|
|||
unless value.blank?
|
|||
a95495bf | Scott Seago | if host && host.send(attr) == value
|
|
fb69591a | Lukas Zapletal | errors.add(attr, _("can't use the same value as the primary interface"))
|
|
8838eb42 | Ohad Levy | failed = true
|
|
elsif Host.where(attr => value).limit(1).pluck(attr).any?
|
|||
bfbf7ed8 | Lukas Zapletal | errors.add(attr, _("already in use"))
|
|
8838eb42 | Ohad Levy | failed = true
|
|
end
|
|||
end
|
|||
end
|
|||
!failed
|
|||
end
|
|||
def normalize_mac
|
|||
self.mac = Net::Validations.normalize_mac(mac)
|
|||
ee1f56de | Marek Hulan | rescue ArgumentError => e
|
|
self.errors.add(:mac, e.message)
|
|||
8838eb42 | Ohad Levy | end
|
|
d455f32c | Marek Hulan | ||
cbe1391f | Shlomi Zadok | def valid_domain
|
|
unless Domain.find_by_id(domain_id)
|
|||
self.errors.add(:domain_id, _("can't find domain with this id"))
|
|||
end
|
|||
end
|
|||
43c4bd72 | Marek Hulan | def set_validated
|
|
@validated = true
|
|||
end
|
|||
d455f32c | Marek Hulan | # do we require a host object associate to the interface? defaults to true
|
|
def require_host?
|
|||
true
|
|||
end
|
|||
356b2e69 | Marek Hulan | ||
43c4bd72 | Marek Hulan | def not_required_interface
|
|
if host && host.managed? && !host.being_destroyed?
|
|||
if self.primary?
|
|||
self.errors.add :primary, _("can't delete primary interface of managed host")
|
|||
end
|
|||
if self.provision?
|
|||
self.errors.add :provision, _("can't delete provision interface of managed host")
|
|||
end
|
|||
end
|
|||
!(self.errors[:primary].present? || self.errors[:provision].present?)
|
|||
end
|
|||
def exclusive_primary_interface
|
|||
if host && self.primary?
|
|||
primaries = host.interfaces.select { |i| i.primary? && i != self }
|
|||
errors.add :primary, _("host already has primary interface") unless primaries.empty?
|
|||
end
|
|||
end
|
|||
def exclusive_provision_interface
|
|||
if host && self.provision?
|
|||
provisions = host.interfaces.select { |i| i.provision? && i != self }
|
|||
errors.add :provision, _("host already has provision interface") unless provisions.empty?
|
|||
end
|
|||
end
|
|||
def require_ip_validation?
|
|||
# if it's not managed there's nowhere to specify an IP anyway
|
|||
return false if !self.host.managed? || !self.managed? || !self.provision?
|
|||
# if the CR will provide an IP, then don't validate yet
|
|||
return false if host.compute_provides?(:ip)
|
|||
ip_for_dns = (subnet.present? && subnet.dns_id.present?) || (domain.present? && domain.dns_id.present?)
|
|||
ip_for_dhcp = subnet.present? && subnet.dhcp_id.present?
|
|||
ip_for_token = Setting[:token_duration] == 0 && (host.pxe_build? || (host.image_build? && host.image.try(:user_data?)))
|
|||
# Any of these conditions will require an IP, so chain with OR
|
|||
ip_for_dns or ip_for_dhcp or ip_for_token
|
|||
end
|
|||
def sync_name
|
|||
synchronizer = NameSynchronizer.new(self)
|
|||
111b0459 | Daniel Lobato | synchronizer.sync_name if synchronizer.sync_required?
|
|
43c4bd72 | Marek Hulan | end
|
|
8f695d94 | Shimon Shtein | ||
d6a19253 | Dominic Cleal | def validate_host_location
|
|
return true if self.host.location.nil? && self.subnet.locations.empty?
|
|||
errors.add(:subnet, _("is not defined for host's location.")) unless include_or_empty?(self.subnet.locations, self.host.location)
|
|||
end
|
|||
8f695d94 | Shimon Shtein | ||
d6a19253 | Dominic Cleal | def validate_host_organization
|
|
return true if self.host.organization.nil? && self.subnet.organizations.empty?
|
|||
8f695d94 | Shimon Shtein | errors.add(:subnet, _("is not defined for host's organization.")) unless include_or_empty?(self.subnet.organizations, self.host.organization)
|
|
end
|
|||
private
|
|||
def include_or_empty?(list, item)
|
|||
(list.empty? && item.nil?) || list.include?(item)
|
|||
end
|
|||
cad1b13c | Tomas Strachota | end
|
|
5da15d1a | Tomas Strachota | end
|
|
cad1b13c | Tomas Strachota | ||
require_dependency 'nic/interface'
|