Revision 05848084
Added by Timo Goebel about 8 years ago
app/models/subnet.rb | ||
---|---|---|
require 'ipaddr'
|
||
|
||
class Subnet < ActiveRecord::Base
|
||
IP_FIELDS = [:network, :mask, :gateway, :dns_primary, :dns_secondary, :from, :to]
|
||
REQUIRED_IP_FIELDS = [:network, :mask]
|
||
SUBNET_TYPES = {:'Subnet::Ipv4' => N_('IPv4')}
|
||
BOOT_MODES = {:static => N_('Static'), :dhcp => N_('DHCP')}
|
||
IPAM_MODES = {:dhcp => N_('DHCP'), :db => N_('Internal DB'), :none => N_('None')}
|
||
|
||
include Authorizable
|
||
include Foreman::STI
|
||
extend FriendlyId
|
||
friendly_id :name
|
||
include Taxonomix
|
||
include Parameterizable::ByIdName
|
||
include EncOutput
|
||
attr_accessible :name, :network, :mask, :gateway, :dns_primary, :dns_secondary, :ipam, :from,
|
||
attr_accessible :name, :type, :network, :mask, :gateway, :dns_primary, :dns_secondary, :ipam, :from,
|
||
:to, :vlanid, :boot_mode, :dhcp_id, :dhcp, :tftp_id, :tftp, :dns_id, :dns, :domain_ids, :domain_names,
|
||
:subnet_parameters_attributes
|
||
:subnet_parameters_attributes, :cidr
|
||
|
||
# This casts Subnet to Subnet::Ipv4 if no type is set
|
||
def self.new(*attributes, &block)
|
||
return Subnet::Ipv4.new_without_cast(*attributes, &block) if self == Subnet
|
||
super
|
||
end
|
||
|
||
# This sets the rails model name of all child classes to the
|
||
# model name of the parent class, i.e. Subnet.
|
||
# This is necessary for all STI classes to share the same
|
||
# route_key, param_key, ...
|
||
def self.inherited(child)
|
||
child.instance_eval do
|
||
# rubocop:disable Rails/Delegate
|
||
def model_name
|
||
superclass.model_name
|
||
end
|
||
# rubocop:enable Rails/Delegate
|
||
end
|
||
super
|
||
end
|
||
|
||
audited :allow_mass_assignment => true
|
||
|
||
... | ... | |
belongs_to :dns, :class_name => "SmartProxy"
|
||
has_many :subnet_domains, :dependent => :destroy, :inverse_of => :subnet
|
||
has_many :domains, :through => :subnet_domains
|
||
has_many :interfaces, :class_name => 'Nic::Base'
|
||
has_many :primary_interfaces, -> { where(:primary => true) }, :class_name => 'Nic::Base'
|
||
has_many :hosts, :through => :interfaces
|
||
has_many :primary_hosts, :through => :primary_interfaces, :source => :host
|
||
has_many :subnet_parameters, :dependent => :destroy, :foreign_key => :reference_id, :inverse_of => :subnet
|
||
has_many :parameters, :dependent => :destroy, :foreign_key => :reference_id, :class_name => "SubnetParameter"
|
||
accepts_nested_attributes_for :subnet_parameters, :allow_destroy => true
|
||
validates :network, :mask, :name, :presence => true
|
||
validates :network, :mask, :name, :cidr, :presence => true
|
||
validates_associated :subnet_domains
|
||
validates :network, :format => {:with => Net::Validations::IP_REGEXP}
|
||
validates :gateway, :dns_primary, :dns_secondary,
|
||
:allow_blank => true,
|
||
:allow_nil => true,
|
||
:format => {:with => Net::Validations::IP_REGEXP},
|
||
:length => { :maximum => 15, :message => N_("is too long (maximum is 15 characters)") }
|
||
validates :mask, :format => {:with => Net::Validations::MASK_REGEXP}
|
||
validates :boot_mode, :inclusion => BOOT_MODES.values
|
||
validates :ipam, :inclusion => IPAM_MODES.values
|
||
validates :ipam, :inclusion => {:in => Proc.new { |subnet| subnet.class.supported_ipam_modes.map {|m| Subnet::IPAM_MODES[m]} }, :message => N_('not supported by this protocol')}
|
||
validates :type, :inclusion => {:in => Proc.new { Subnet::SUBNET_TYPES.keys.map(&:to_s) }, :message => N_("must be one of [ %s ]" % Subnet::SUBNET_TYPES.keys.map(&:to_s).join(', ')) }
|
||
validates :name, :length => {:maximum => 255}, :uniqueness => true
|
||
|
||
validates :dns, :proxy_features => { :feature => "DNS", :message => N_('does not have the DNS feature') }
|
||
validates :tftp, :proxy_features => { :feature => "TFTP", :message => N_('does not have the TFTP feature') }
|
||
validates :dhcp, :proxy_features => { :feature => "DHCP", :message => N_('does not have the DHCP feature') }
|
||
validates :network, :uniqueness => true
|
||
|
||
validate :ensure_ip_addr_new
|
||
before_validation :cleanup_addresses
|
||
before_validation :normalize_addresses
|
||
validate :ensure_ip_addrs_valid
|
||
|
||
validate :validate_ranges
|
||
validate :check_if_type_changed, :on => :update
|
||
|
||
default_scope lambda {
|
||
with_taxonomy_scope do
|
||
... | ... | |
}
|
||
|
||
scoped_search :on => [:name, :network, :mask, :gateway, :dns_primary, :dns_secondary,
|
||
:vlanid, :ipam, :boot_mode], :complete_value => true
|
||
:vlanid, :ipam, :boot_mode, :type], :complete_value => true
|
||
|
||
scoped_search :in => :domains, :on => :name, :rename => :domain, :complete_value => true
|
||
scoped_search :in => :subnet_parameters, :on => :value, :on_key=> :name, :complete_value => true, :only_explicit => true, :rename => :params
|
||
... | ... | |
end
|
||
|
||
# Given an IP returns the subnet that contains that IP
|
||
# [+ip+] : "doted quad" string
|
||
# [+ip+] : IPv4 address
|
||
# Returns : Subnet object or nil if not found
|
||
def self.subnet_for(ip)
|
||
Subnet.all.each {|s| return s if s.contains? IPAddr.new(ip)}
|
||
nil
|
||
ip = IPAddr.new(ip)
|
||
Subnet.all.detect {|s| s.family == ip.family && s.contains?(ip)}
|
||
end
|
||
|
||
# Indicates whether the IP is within this subnet
|
||
# [+ip+] String: Contains 4 dotted decimal values
|
||
# [+ip+] String: IPv4 address
|
||
# Returns Boolean: True if if ip is in this subnet
|
||
def contains?(ip)
|
||
IPAddr.new("#{network}/#{mask}", Socket::AF_INET).include? IPAddr.new(ip, Socket::AF_INET)
|
||
ipaddr.include? IPAddr.new(ip, family)
|
||
end
|
||
|
||
def ipaddr
|
||
IPAddr.new("#{network}/#{mask}", family)
|
||
end
|
||
|
||
def cidr
|
||
return if mask.nil?
|
||
IPAddr.new(mask).to_i.to_s(2).count("1")
|
||
rescue invalid_address_error
|
||
nil
|
||
end
|
||
|
||
def cidr=(cidr)
|
||
return if cidr.nil?
|
||
self[:mask] = IPAddr.new(in_mask, family).mask(cidr).to_s
|
||
rescue invalid_address_error
|
||
nil
|
||
end
|
||
|
||
def supports_ipam_mode?(mode)
|
||
self.class.supported_ipam_modes.include?(mode)
|
||
end
|
||
|
||
def dhcp?
|
||
... | ... | |
return
|
||
end
|
||
|
||
if self.ipam == IPAM_MODES[:dhcp] && dhcp?
|
||
if self.ipam == IPAM_MODES[:dhcp] && dhcp? && supports_ipam_mode?(:dhcp)
|
||
# we have DHCP proxy so asking it for free IP
|
||
logger.debug "Asking #{dhcp.url} for free IP"
|
||
ip = dhcp_proxy.unused_ip(self, mac)["ip"]
|
||
logger.debug("Found #{ip}")
|
||
return(ip)
|
||
elsif self.ipam == IPAM_MODES[:db]
|
||
elsif self.ipam == IPAM_MODES[:db] && supports_ipam_mode?(:db)
|
||
# we have no DHCP proxy configured so Foreman becomes `DHCP` and manages reservations internally
|
||
logger.debug "Trying to find free IP for subnet in internal DB"
|
||
subnet_range = IPAddr.new("#{network}/#{mask}", Socket::AF_INET).to_range.to_a
|
||
from = self.from.present? ? IPAddr.new(self.from) : subnet_range[1]
|
||
to = self.to.present? ? IPAddr.new(self.to) : subnet_range[-2]
|
||
subnet_range = IPAddr.new("#{network}/#{mask}", family).to_range
|
||
from = self.from.present? ? IPAddr.new(self.from) : subnet_range.first(2).last
|
||
to = self.to.present? ? IPAddr.new(self.to) : IPAddr.new(subnet_range.last.to_i - 2, family)
|
||
(from..to).each do |address|
|
||
ip = address.to_s
|
||
if !self.known_ips.include?(ip) && !excluded_ips.include?(ip)
|
||
... | ... | |
ips = self.interfaces.map(&:ip) + self.hosts.includes(:interfaces).map(&:ip)
|
||
ips += [self.gateway, self.dns_primary, self.dns_secondary].select(&:present?)
|
||
self.clear_association_cache
|
||
ips.uniq
|
||
ips.compact.uniq
|
||
end
|
||
|
||
# imports subnets from a dhcp smart proxy
|
||
... | ... | |
end
|
||
|
||
def as_json(options = {})
|
||
super({:methods => [:to_label]}.merge(options))
|
||
super({:methods => [:to_label, :type]}.merge(options))
|
||
end
|
||
|
||
private
|
||
... | ... | |
end
|
||
|
||
def validate_ranges
|
||
errors.add(:from, _("invalid IP address")) if from.present? and !from =~ Net::Validations::IP_REGEXP
|
||
errors.add(:to, _("invalid IP address")) if to.present? and !to =~ Net::Validations::IP_REGEXP
|
||
errors.add(:from, _("does not belong to subnet")) if from.present? and not self.contains?(f=IPAddr.new(from))
|
||
errors.add(:to, _("does not belong to subnet")) if to.present? and not self.contains?(t=IPAddr.new(to))
|
||
errors.add(:from, _("can't be bigger than to range")) if from.present? and t.present? and f > t
|
||
if from.present? or to.present?
|
||
errors.add(:from, _("must be specified if to is defined")) if from.blank?
|
||
errors.add(:to, _("must be specified if from is defined")) if to.blank?
|
||
end
|
||
return if errors.keys.include?(:from) || errors.keys.include?(:to)
|
||
errors.add(:from, _("does not belong to subnet")) if from.present? and not self.contains?(f=IPAddr.new(from))
|
||
errors.add(:to, _("does not belong to subnet")) if to.present? and not self.contains?(t=IPAddr.new(to))
|
||
errors.add(:from, _("can't be bigger than to range")) if from.present? and t.present? and f > t
|
||
end
|
||
|
||
def cleanup_addresses
|
||
self.network = cleanup_ip(network) if network.present?
|
||
self.mask = cleanup_ip(mask) if mask.present?
|
||
self.gateway = cleanup_ip(gateway) if gateway.present?
|
||
self.dns_primary = cleanup_ip(dns_primary) if dns_primary.present?
|
||
self.dns_secondary = cleanup_ip(dns_secondary) if dns_secondary.present?
|
||
self
|
||
def check_if_type_changed
|
||
if self.type_changed?
|
||
errors.add(:type, _("can't be updated after subnet is saved"))
|
||
end
|
||
end
|
||
|
||
def cleanup_ip(address)
|
||
address.gsub!(/\.\.+/, ".")
|
||
address.gsub!(/2555+/, "255")
|
||
address
|
||
def normalize_addresses
|
||
IP_FIELDS.each do |f|
|
||
val = send(f)
|
||
send("#{f}=", normalize_ip(val)) if val.present?
|
||
end
|
||
self
|
||
end
|
||
|
||
def ensure_ip_addr_new
|
||
errors.add(:network, _("is invalid")) if network.present? && (IPAddr.new(network) rescue nil).nil? && !errors.keys.include?(:network)
|
||
errors.add(:mask, _("is invalid")) if mask.present? && (IPAddr.new(mask) rescue nil).nil? && !errors.keys.include?(:mask)
|
||
errors.add(:gateway, _("is invalid")) if gateway.present? && (IPAddr.new(gateway) rescue nil).nil? && !errors.keys.include?(:gateway)
|
||
errors.add(:dns_primary, _("is invalid")) if dns_primary.present? && (IPAddr.new(dns_primary) rescue nil).nil? && !errors.keys.include?(:dns_primary)
|
||
errors.add(:dns_secondary, _("is invalid")) if dns_secondary.present? && (IPAddr.new(dns_secondary) rescue nil).nil? && !errors.keys.include?(:dns_secondary)
|
||
def ensure_ip_addrs_valid
|
||
IP_FIELDS.each do |f|
|
||
errors.add(f, _("is invalid")) if (send(f).present? || REQUIRED_IP_FIELDS.include?(f)) && !validate_ip(send(f)) && !errors.keys.include?(f)
|
||
end
|
||
end
|
||
|
||
private
|
||
|
||
def invalid_address_error
|
||
# IPAddr::InvalidAddressError is undefined for ruby 1.9
|
||
return IPAddr::InvalidAddressError if IPAddr.const_defined?('InvalidAddressError')
|
||
ArgumentError
|
||
end
|
||
|
||
def enc_attributes
|
||
@enc_attributes ||= %w(name network mask gateway dns_primary dns_secondary from to boot_mode ipam vlanid)
|
||
@enc_attributes ||= %w(name type network mask cidr gateway dns_primary dns_secondary from to boot_mode ipam vlanid)
|
||
end
|
||
end
|
app/models/subnet/ipv4.rb | ||
---|---|---|
require 'socket'
|
||
|
||
class Subnet::Ipv4 < Subnet
|
||
has_many :interfaces, :class_name => 'Nic::Base', :foreign_key => :subnet_id
|
||
has_many :primary_interfaces, -> { where(:primary => true) }, :class_name => 'Nic::Base', :foreign_key => :subnet_id
|
||
# The has_many :through associations below have to be defined after the
|
||
# corresponding has_many associations and thus can not be defined in the parent class
|
||
has_many :hosts, :through => :interfaces
|
||
has_many :primary_hosts, :through => :primary_interfaces, :source => :host
|
||
|
||
validates :mask, :format => {:with => Net::Validations::MASK_REGEXP}
|
||
|
||
before_validation :cleanup_addresses
|
||
|
||
def family
|
||
Socket::AF_INET
|
||
end
|
||
|
||
def in_mask
|
||
IPAddr::IN4MASK
|
||
end
|
||
|
||
def validate_ip(ip)
|
||
Net::Validations.validate_ip(ip)
|
||
end
|
||
|
||
def self.supported_ipam_modes
|
||
[:dhcp, :db, :none]
|
||
end
|
||
|
||
def cleanup_addresses
|
||
IP_FIELDS.each do |f|
|
||
send("#{f}=", cleanup_ip(send(f))) if send(f).present?
|
||
end
|
||
self
|
||
end
|
||
|
||
private
|
||
|
||
def cleanup_ip(address)
|
||
address.gsub!(/\.\.+/, ".")
|
||
address.gsub!(/2555+/, "255")
|
||
address
|
||
end
|
||
|
||
def normalize_ip(address)
|
||
Net::Validations.normalize_ip(address)
|
||
end
|
||
end
|
app/validators/mac_address_validator.rb | ||
---|---|---|
class MacAddressValidator < ActiveModel::EachValidator
|
||
def validate_each(record, attribute, value)
|
||
make_invalid(record, attribute) unless Net::Validations.valid_mac?(value)
|
||
make_invalid(record, attribute) unless Net::Validations.validate_mac(value)
|
||
rescue Net::Validations::Error
|
||
make_invalid(record, attribute)
|
||
end
|
db/migrate/20160414063050_add_sti_to_subnets.rb | ||
---|---|---|
class AddStiToSubnets < ActiveRecord::Migration
|
||
def self.up
|
||
add_column :subnets, :type, :string, :default => 'Subnet::Ipv4', :null => false
|
||
add_index :subnets, :type
|
||
end
|
||
|
||
def self.down
|
||
remove_column :subnets, :type
|
||
end
|
||
end
|
lib/net.rb | ||
---|---|---|
|
||
module Net
|
||
class Record
|
||
include Net::Validations
|
||
attr_accessor :hostname, :proxy, :logger
|
||
|
||
def initialize(opts = {})
|
lib/net/dhcp/record.rb | ||
---|---|---|
|
||
def initialize(opts = { })
|
||
super(opts)
|
||
self.mac = validate_mac self.mac
|
||
self.network = validate_network self.network
|
||
self.ip = validate_ip self.ip
|
||
self.mac = Net::Validations.validate_mac! self.mac
|
||
self.network = Net::Validations.validate_network! self.network
|
||
self.ip = Net::Validations.validate_ip! self.ip
|
||
end
|
||
|
||
def to_s
|
lib/net/dns.rb | ||
---|---|---|
|
||
def initialize(opts = { })
|
||
super(opts)
|
||
self.ip = validate_ip self.ip
|
||
self.ip = Validations.validate_ip! self.ip
|
||
self.resolver ||= Resolv::DNS.new
|
||
end
|
||
|
lib/net/validations.rb | ||
---|---|---|
require 'ipaddr'
|
||
require 'socket'
|
||
|
||
module Net
|
||
module Validations
|
||
IP_REGEXP ||= /\A((25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)(\.|$)){4}\z/
|
||
... | ... | |
class Error < RuntimeError
|
||
end
|
||
|
||
def valid_mac?(mac)
|
||
# validates an IPv4 address
|
||
def self.validate_ip(ip)
|
||
return false unless ip.present?
|
||
IPAddr.new(ip, Socket::AF_INET) rescue return false
|
||
true
|
||
end
|
||
|
||
# validates an IPv4 address and raises an error
|
||
def self.validate_ip!(ip)
|
||
raise Error, "Invalid IP Address #{ip}" unless validate_ip(ip)
|
||
ip
|
||
end
|
||
|
||
# validates a network mask
|
||
def self.validate_mask(mask)
|
||
mask =~ MASK_REGEXP
|
||
end
|
||
|
||
# validates a network mask and raises an error
|
||
def self.validate_mask!(mask)
|
||
raise Error, "Invalid Subnet Mask #{mask}" unless validate_mask(mask)
|
||
mask
|
||
end
|
||
|
||
# validates the mac
|
||
def self.validate_mac(mac)
|
||
return false if mac.nil?
|
||
|
||
case mac.size
|
||
... | ... | |
false
|
||
end
|
||
|
||
module_function :valid_mac?
|
||
|
||
# validates the ip address
|
||
def validate_ip(ip)
|
||
raise Error, "Invalid IP Address #{ip}" unless (ip =~ IP_REGEXP)
|
||
ip
|
||
# validates the mac and raises an error
|
||
def self.validate_mac!(mac)
|
||
raise Error, "Invalid MAC #{mac}" unless validate_mac(mac)
|
||
mac
|
||
end
|
||
|
||
def validate_mask(mask)
|
||
raise Error, "Invalid Subnet Mask #{mask}" unless (mask =~ MASK_REGEXP)
|
||
mask
|
||
# validates the hostname
|
||
def self.validate_hostname(hostname)
|
||
hostname =~ HOST_REGEXP
|
||
end
|
||
|
||
# validates the mac
|
||
def validate_mac(mac)
|
||
raise Error, "Invalid MAC #{mac}" unless valid_mac? mac
|
||
mac
|
||
# validates the hostname and raises an error
|
||
def self.validate_hostname!(hostname)
|
||
raise Error, "Invalid hostname #{hostname}" unless validate_hostname(hostname)
|
||
hostname
|
||
end
|
||
|
||
# validates the hostname
|
||
def validate_hostname(hostname)
|
||
raise Error, "Invalid hostname #{hostname}" unless (hostname =~ HOST_REGEXP)
|
||
hostname
|
||
def self.validate_network(network)
|
||
validate_ip(network)
|
||
end
|
||
|
||
def validate_network(network)
|
||
begin
|
||
validate_ip(network)
|
||
rescue Error
|
||
raise Error, "Invalid Network #{network}"
|
||
end
|
||
def self.validate_network!(network)
|
||
raise(Error, "Invalid Network #{network}") unless validate_network(network)
|
||
network
|
||
end
|
||
|
||
# ensures that the ip address does not contain any leading spaces or invalid strings
|
||
def self.normalize_ip(ip)
|
||
return unless ip.present?
|
||
return ip unless ip =~ IP_REGEXP
|
||
ip.split(".").map(&:to_i).join(".")
|
||
end
|
||
|
test/factories/host_related.rb | ||
---|---|---|
overrides = {}
|
||
overrides[:locations] = [host.location] unless host.location.nil?
|
||
overrides[:organizations] = [host.organization] unless host.organization.nil?
|
||
host.subnet = FactoryGirl.build(:subnet, overrides)
|
||
host.subnet = FactoryGirl.build(:subnet_ipv4, overrides)
|
||
end
|
||
end
|
||
|
||
... | ... | |
overrides[:locations] = [location] unless location.nil?
|
||
overrides[:organizations] = [organization] unless organization.nil?
|
||
FactoryGirl.create(
|
||
:subnet,
|
||
:subnet_ipv4,
|
||
overrides
|
||
)
|
||
end
|
||
... | ... | |
overrides[:locations] = [location] unless location.nil?
|
||
overrides[:organizations] = [organization] unless organization.nil?
|
||
|
||
FactoryGirl.create(:subnet, overrides)
|
||
FactoryGirl.create(:subnet_ipv4, overrides)
|
||
end
|
||
domain do
|
||
FactoryGirl.create(:domain,
|
||
... | ... | |
end
|
||
|
||
trait :with_tftp_subnet do
|
||
subnet { FactoryGirl.build(:subnet, :tftp, locations: [location], organizations: [organization]) }
|
||
subnet { FactoryGirl.build(:subnet_ipv4, :tftp, locations: [location], organizations: [organization]) }
|
||
end
|
||
|
||
trait :with_tftp_orchestration do
|
||
... | ... | |
end
|
||
|
||
trait :with_subnet do
|
||
subnet
|
||
association :subnet, :factory => :subnet_ipv4
|
||
end
|
||
|
||
trait :with_rootpass do
|
test/factories/subnet.rb | ||
---|---|---|
FactoryGirl.define do
|
||
factory :subnet do
|
||
sequence(:name) {|n| "subnet#{n}" }
|
||
sequence(:network) {|n| "10.0.#{n}.0" }
|
||
mask "255.255.255.0"
|
||
|
||
trait :tftp do
|
||
association :tftp, :factory => :template_smart_proxy
|
||
... | ... | |
association :dns, :factory => :dns_smart_proxy
|
||
end
|
||
|
||
trait :ipam_db do
|
||
ipam "Internal DB"
|
||
trait :with_domains do
|
||
transient do
|
||
domains_count 2
|
||
end
|
||
|
||
after(:create) do |subnet, evaluator|
|
||
FactoryGirl.create_list(:domain, evaluator.domains_count, :subnets => [subnet])
|
||
end
|
||
end
|
||
|
||
factory :subnet_ipv4, :class => Subnet::Ipv4 do
|
||
network { 3.times.map { rand(256) }.join('.') + '.0' }
|
||
mask { '255.255.255.0' }
|
||
|
||
factory :subnet_ipv4_with_domains, :traits => [:with_domains]
|
||
|
||
trait :ipam_db do
|
||
ipam "Internal DB"
|
||
end
|
||
end
|
||
end
|
||
end
|
test/fixtures/subnets.yml | ||
---|---|---|
|
||
one:
|
||
name: one
|
||
type: Subnet::Ipv4
|
||
network: 2.3.4.0
|
||
mask: 255.255.255.0
|
||
dhcp: one
|
||
... | ... | |
|
||
two:
|
||
name: two
|
||
type: Subnet::Ipv4
|
||
network: 3.3.4.0
|
||
mask: 255.255.255.0
|
||
dhcp: one
|
||
... | ... | |
|
||
three:
|
||
name: three
|
||
type: Subnet::Ipv4
|
||
network: 3.3.4.3
|
||
mask: 255.255.255.0
|
||
dhcp: one
|
||
... | ... | |
|
||
four:
|
||
name: four
|
||
type: Subnet::Ipv4
|
||
network: 3.3.5.0
|
||
mask: 255.255.255.0
|
||
tftp: two
|
||
... | ... | |
|
||
five:
|
||
name: five
|
||
type: Subnet::Ipv4
|
||
network: 10.0.0.0
|
||
mask: 255.255.255.0
|
||
dhcp: one
|
test/functional/hostgroups_controller_test.rb | ||
---|---|---|
|
||
test "domain_selected should return subnets" do
|
||
domain = FactoryGirl.create(:domain)
|
||
subnet = FactoryGirl.create(:subnet)
|
||
subnet = FactoryGirl.create(:subnet_ipv4)
|
||
domain.subnets << subnet
|
||
domain.save
|
||
xhr :post, :domain_selected, {:id => Hostgroup.first, :hostgroup => {}, :domain_id => domain.id, :format => :json}, set_session_user
|
test/functional/subnets_controller_test.rb | ||
---|---|---|
assert_template 'new'
|
||
end
|
||
|
||
def test_create_valid
|
||
Subnet.any_instance.stubs(:valid?).returns(true)
|
||
post :create, {:subnet => {:network => "192.168.0.1", :mask => "255.255.255.0"}}, set_session_user
|
||
def test_create_valid_without_type
|
||
post :create, {:subnet => {:network => "192.168.0.1", :mask => "255.255.255.0", :name => 'testsubnet'}}, set_session_user
|
||
assert_redirected_to subnets_url
|
||
end
|
||
|
||
def test_create_valid_with_type
|
||
post :create, {:subnet => {:network => "192.168.0.1", :mask => "255.255.255.0", :name => 'testsubnet', :type => 'Subnet::Ipv4'}}, set_session_user
|
||
assert_redirected_to subnets_url
|
||
end
|
||
|
test/lib/net/net_test.rb | ||
---|---|---|
assert_equal logger, record.logger
|
||
end
|
||
end
|
||
|
test/lib/net/validations_test.rb | ||
---|---|---|
require 'net'
|
||
|
||
class ValidationsTest < ActiveSupport::TestCase
|
||
include Net::Validations
|
||
|
||
describe "valid_mac?" do
|
||
describe "validate_mac" do
|
||
test "nil is not valid" do
|
||
Net::Validations.valid_mac?(nil).must_be_same_as false
|
||
Net::Validations.validate_mac(nil).must_be_same_as false
|
||
end
|
||
|
||
test "48-bit MAC address is valid" do
|
||
Net::Validations.valid_mac?("aa:bb:cc:dd:ee:ff").must_be_same_as true
|
||
Net::Validations.validate_mac("aa:bb:cc:dd:ee:ff").must_be_same_as true
|
||
end
|
||
|
||
test "64-bit MAC address is valid" do
|
||
Net::Validations.valid_mac?("aa:bb:cc:dd:ee:ff:00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd").must_be_same_as true
|
||
Net::Validations.validate_mac("aa:bb:cc:dd:ee:ff:00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd").must_be_same_as true
|
||
end
|
||
|
||
test "MAC address is not valid" do
|
||
Net::Validations.valid_mac?("aa:bb:cc:dd:ee").must_be_same_as false
|
||
Net::Validations.valid_mac?("aa:bb:cc:dd:ee:ff:gg:11").must_be_same_as false
|
||
Net::Validations.valid_mac?("aa:bb:cc:dd:ee:zz").must_be_same_as false
|
||
Net::Validations.validate_mac("aa:bb:cc:dd:ee").must_be_same_as false
|
||
Net::Validations.validate_mac("aa:bb:cc:dd:ee:ff:gg:11").must_be_same_as false
|
||
Net::Validations.validate_mac("aa:bb:cc:dd:ee:zz").must_be_same_as false
|
||
end
|
||
end
|
||
|
||
test "48-bit mac address should be valid" do
|
||
assert_nothing_raised Net::Validations::Error do
|
||
validate_mac "aa:bb:cc:dd:ee:ff"
|
||
Net::Validations.validate_mac! "aa:bb:cc:dd:ee:ff"
|
||
end
|
||
end
|
||
|
||
test "64-bit mac address should be valid" do
|
||
assert_nothing_raised Net::Validations::Error do
|
||
validate_mac "aa:bb:cc:dd:ee:ff:00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd"
|
||
Net::Validations.validate_mac! "aa:bb:cc:dd:ee:ff:00:11:22:33:44:55:66:77:88:99:aa:bb:cc:dd"
|
||
end
|
||
end
|
||
|
||
test "mac should be invalid" do
|
||
assert_raise Net::Validations::Error do
|
||
validate_mac "abc123asdas"
|
||
Net::Validations.validate_mac! "abc123asdas"
|
||
end
|
||
end
|
||
|
||
test "hostname should be valid" do
|
||
assert_nothing_raised Net::Validations::Error do
|
||
validate_hostname "this.is.an.example.com"
|
||
Net::Validations.validate_hostname! "this.is.an.example.com"
|
||
end
|
||
assert_nothing_raised Net::Validations::Error do
|
||
validate_hostname "this-is.an.example.com"
|
||
Net::Validations.validate_hostname! "this-is.an.example.com"
|
||
end
|
||
assert_nothing_raised Net::Validations::Error do
|
||
validate_hostname "localhost"
|
||
Net::Validations.validate_hostname! "localhost"
|
||
end
|
||
end
|
||
|
||
test "hostname should not be valid" do
|
||
assert_raise Net::Validations::Error do
|
||
validate_hostname "-this.is.a.bad.example.com"
|
||
Net::Validations.validate_hostname! "-this.is.a.bad.example.com"
|
||
end
|
||
assert_raise Net::Validations::Error do
|
||
validate_hostname "this_is_a_bad.example.com"
|
||
Net::Validations.validate_hostname! "this_is_a_bad.example.com"
|
||
end
|
||
end
|
||
|
||
describe "network validation" do
|
||
test "network should be valid" do
|
||
assert_nothing_raised Net::Validations::Error do
|
||
Net::Validations.validate_network! "123.1.123.1"
|
||
end
|
||
end
|
||
|
||
test "network should not be valid" do
|
||
assert_raise Net::Validations::Error do
|
||
Net::Validations.validate_network! "invalid"
|
||
end
|
||
assert_raise Net::Validations::Error do
|
||
Net::Validations.validate_network! "9999.99.12.1"
|
||
end
|
||
end
|
||
end
|
||
|
||
describe "mask validation" do
|
||
test "mask should be valid" do
|
||
assert_nothing_raised Net::Validations::Error do
|
||
Net::Validations.validate_mask! "255.255.255.0"
|
||
end
|
||
end
|
||
|
||
test "mask should not be valid" do
|
||
assert_raise Net::Validations::Error do
|
||
Net::Validations.validate_mask! "22.222.22.2"
|
||
end
|
||
assert_raise Net::Validations::Error do
|
||
Net::Validations.validate_mask! "invalid"
|
||
end
|
||
end
|
||
end
|
||
|
||
... | ... | |
end
|
||
end
|
||
end
|
||
|
||
test "IPv4 address should be valid" do
|
||
assert Net::Validations.validate_ip("127.0.0.1")
|
||
end
|
||
|
||
test "IPv4 address should be invalid" do
|
||
refute Net::Validations.validate_ip("127.0.0.300")
|
||
end
|
||
|
||
test "empty IPv4 address should be invalid" do
|
||
refute Net::Validations.validate_ip('')
|
||
end
|
||
|
||
test "nil should be invalid ip" do
|
||
refute Net::Validations.validate_ip(nil)
|
||
end
|
||
|
||
test "return IP when IPv4 address is valid" do
|
||
assert_nothing_raised Net::Validations::Error do
|
||
assert "127.0.0.1", Net::Validations.validate_ip!("127.0.0.1")
|
||
end
|
||
end
|
||
|
||
test "raise error when IPv4 address is invalid" do
|
||
assert_raise Net::Validations::Error do
|
||
Net::Validations.validate_ip! "127.0.0.1.2"
|
||
end
|
||
end
|
||
|
||
test "should normalize IPv4 address" do
|
||
assert_equal "127.0.0.1", Net::Validations.normalize_ip("127.000.0.1")
|
||
end
|
||
|
||
test "should ignore invalid data when normalizing IPv4 address" do
|
||
assert_equal "xyz.1.2.3", Net::Validations.normalize_ip("xyz.1.2.3")
|
||
end
|
||
end
|
test/unit/helpers/application_helper_test.rb | ||
---|---|---|
|
||
def test_generate_link_for
|
||
proxy = FactoryGirl.create(:dhcp_smart_proxy)
|
||
subnet = FactoryGirl.create(:subnet, :name => 'My subnet')
|
||
subnet = FactoryGirl.create(:subnet_ipv4, :name => 'My subnet')
|
||
proxy.subnets = [subnet]
|
||
links = generate_links_for(proxy.subnets)
|
||
assert_equal(link_to(subnet.to_label, subnets_path(:search => "name = \"#{subnet.name}\"")), links)
|
test/unit/host_test.rb | ||
---|---|---|
test "should have only one provision interface" do
|
||
organization = FactoryGirl.create(:organization)
|
||
location = FactoryGirl.create(:location)
|
||
subnet = FactoryGirl.create(:subnet, :organizations => [organization], :locations => [location])
|
||
subnet = FactoryGirl.create(:subnet_ipv4, :organizations => [organization], :locations => [location])
|
||
host = FactoryGirl.create(:host, :managed, :organization => organization,
|
||
:location => location, :subnet => subnet,
|
||
:ip => subnet.network.succ)
|
||
... | ... | |
|
||
test "hosts with a DNS-enabled Subnet do require an IP" do
|
||
Setting[:token_duration] = 30 #enable tokens so that we only test the subnet
|
||
h=FactoryGirl.build(:host, :managed, :subnet => FactoryGirl.build(:subnet, :dns))
|
||
h=FactoryGirl.build(:host, :managed, :subnet => FactoryGirl.build(:subnet_ipv4, :dns))
|
||
assert h.require_ip_validation?
|
||
end
|
||
|
||
test "hosts with a DHCP-enabled Subnet do require an IP" do
|
||
Setting[:token_duration] = 30 #enable tokens so that we only test the subnet
|
||
h=FactoryGirl.build(:host, :managed, :subnet => FactoryGirl.build(:subnet, :dhcp))
|
||
h=FactoryGirl.build(:host, :managed, :subnet => FactoryGirl.build(:subnet_ipv4, :dhcp))
|
||
assert h.require_ip_validation?
|
||
end
|
||
|
||
test "hosts without a DNS/DHCP-enabled Subnet don't require an IP" do
|
||
Setting[:token_duration] = 30 #enable tokens so that we only test the subnet
|
||
h=FactoryGirl.build(:host, :managed, :subnet => FactoryGirl.build(:subnet, :dhcp => nil, :dns => nil))
|
||
h=FactoryGirl.build(:host, :managed, :subnet => FactoryGirl.build(:subnet_ipv4, :dhcp => nil, :dns => nil))
|
||
refute h.require_ip_validation?
|
||
end
|
||
|
test/unit/location_test.rb | ||
---|---|---|
|
||
test 'it should return array of used ids by hosts' do
|
||
location = taxonomies(:location1)
|
||
subnet = FactoryGirl.create(:subnet, :locations => [location])
|
||
subnet = FactoryGirl.create(:subnet_ipv4, :locations => [location])
|
||
domain = FactoryGirl.create(:domain)
|
||
FactoryGirl.create(:host,
|
||
:compute_resource => compute_resources(:one),
|
||
... | ... | |
|
||
test "used_and_selected_or_inherited_ids for inherited location" do
|
||
parent = taxonomies(:location1)
|
||
subnet = FactoryGirl.create(:subnet, :organizations => [taxonomies(:organization1)])
|
||
subnet = FactoryGirl.create(:subnet_ipv4, :organizations => [taxonomies(:organization1)])
|
||
domain1 = FactoryGirl.create(:domain)
|
||
domain2 = FactoryGirl.create(:domain)
|
||
parent.update_attribute(:domains,[domain1,domain2])
|
test/unit/nic_test.rb | ||
---|---|---|
disable_taxonomies do
|
||
orgs = FactoryGirl.build_pair(:organization)
|
||
locs = FactoryGirl.build_pair(:location)
|
||
subn = FactoryGirl.build(:subnet, :locations => [locs.first], :organizations => [orgs.first])
|
||
subn = FactoryGirl.build(:subnet_ipv4, :locations => [locs.first], :organizations => [orgs.first])
|
||
host = FactoryGirl.build(:host, :location => locs.last, :organization => orgs.last)
|
||
nic = Nic::Base.new :mac => "cabbccddeeff", :host => host
|
||
nic.subnet = subn
|
||
... | ... | |
test "Alias subnet can only use static boot mode if it's managed" do
|
||
nic = FactoryGirl.build(:nic_managed, :virtual => true, :attached_to => 'eth0', :managed => true, :identifier => 'eth0:0')
|
||
nic.host = FactoryGirl.build(:host)
|
||
nic.subnet = FactoryGirl.build(:subnet, :boot_mode => Subnet::BOOT_MODES[:dhcp])
|
||
nic.subnet = FactoryGirl.build(:subnet_ipv4, :boot_mode => Subnet::BOOT_MODES[:dhcp])
|
||
refute nic.valid?
|
||
assert_includes nic.errors.keys, :subnet_id
|
||
|
||
... | ... | |
context 'BMC' do
|
||
setup do
|
||
disable_orchestration
|
||
@subnet = FactoryGirl.create(:subnet, :dhcp, :ipam => Subnet::IPAM_MODES[:db])
|
||
@subnet = FactoryGirl.create(:subnet_ipv4, :dhcp, :ipam => Subnet::IPAM_MODES[:db])
|
||
@domain = FactoryGirl.create(:domain)
|
||
@interface = FactoryGirl.create(:nic_bmc, :ip => @subnet.unused_ip,
|
||
:host => FactoryGirl.create(:host),
|
test/unit/orchestration/dhcp_test.rb | ||
---|---|---|
i = FactoryGirl.build(:nic_managed, :ip => '10.0.0.10', :name => 'eth0:0')
|
||
i.host = h
|
||
i.domain = domains(:mydomain)
|
||
i.subnet = FactoryGirl.build(:subnet, :dhcp, :boot_mode => 'Static', :ipam => 'Internal DB')
|
||
i.subnet = FactoryGirl.build(:subnet_ipv4, :dhcp, :boot_mode => 'Static', :ipam => 'Internal DB')
|
||
refute i.dhcp?
|
||
end
|
||
end
|
||
... | ... | |
|
||
test "provision interface DHCP records should contain filename/next-server attributes" do
|
||
ProxyAPI::TFTP.any_instance.expects(:bootServer).returns('192.168.1.1')
|
||
subnet = FactoryGirl.build(:subnet, :dhcp, :tftp)
|
||
subnet = FactoryGirl.build(:subnet_ipv4, :dhcp, :tftp)
|
||
h = FactoryGirl.create(:host, :with_dhcp_orchestration, :with_tftp_orchestration, :subnet => subnet)
|
||
assert_equal 'pxelinux.0', h.provision_interface.dhcp_record.filename
|
||
assert_equal '192.168.1.1', h.provision_interface.dhcp_record.nextServer
|
test/unit/organization_test.rb | ||
---|---|---|
|
||
test 'it should return array of used ids by hosts' do
|
||
organization = taxonomies(:organization1)
|
||
subnet = FactoryGirl.create(:subnet, :organizations => [organization])
|
||
subnet = FactoryGirl.create(:subnet_ipv4, :organizations => [organization])
|
||
domain = FactoryGirl.create(:domain)
|
||
FactoryGirl.create(:host,
|
||
:compute_resource => compute_resources(:one),
|
test/unit/subnet.rb | ||
---|---|---|
require 'test_helper'
|
||
|
||
class SubnetTest < ActiveSupport::TestCase
|
||
test 'should be cast to Subnet::Ipv4 if no type is set' do
|
||
subnet = Subnet.new
|
||
assert_equal Subnet::Ipv4, subnet.class
|
||
end
|
||
|
||
test 'should be cast to Subnet::Ipv4 if type is set' do
|
||
subnet = Subnet.new(:type => 'Subnet::Ipv4')
|
||
assert_equal Subnet::Ipv4, subnet.class
|
||
end
|
||
|
||
test 'child class should not be cast to default sti class even if no type is set' do
|
||
class Subnet::Test < Subnet; end
|
||
subnet = Subnet::Test.new
|
||
assert_equal Subnet::Test, subnet.class
|
||
end
|
||
end
|
test/unit/subnet/ipv4_test.rb | ||
---|---|---|
require 'test_helper'
|
||
|
||
class SubnetIpv4Test < ActiveSupport::TestCase
|
||
def setup
|
||
User.current = users :admin
|
||
@subnet = Subnet::Ipv4.new
|
||
@attrs = { :network= => "123.123.123.0",
|
||
:mask= => "255.255.255.0",
|
||
:domains= => [domains(:mydomain)],
|
||
:name= => "valid" }
|
||
end
|
||
|
||
test 'can be created with domains' do
|
||
subnet = FactoryGirl.build(:subnet_ipv4)
|
||
subnet.domain_ids = [ domains(:mydomain).id ]
|
||
assert subnet.save
|
||
end
|
||
|
||
test "should have a network" do
|
||
create_a_domain_with_the_subnet
|
||
@subnet.network = nil
|
||
assert !@subnet.save
|
||
|
||
set_attr(:network=)
|
||
assert @subnet.save
|
||
end
|
||
|
||
test "should have a mask" do
|
||
create_a_domain_with_the_subnet
|
||
@subnet.mask = nil
|
||
assert !@subnet.save
|
||
|
||
@subnet.mask = "255.255.255.0"
|
||
assert @subnet.save
|
||
end
|
||
|
||
test "network should have ip format" do
|
||
@subnet.network = "asf.fwe6.we6s.q1"
|
||
set_attr(:mask=)
|
||
assert !@subnet.save
|
||
end
|
||
|
||
test "mask should have ip format" do
|
||
@subnet.mask = "asf.fwe6.we6s.q1"
|
||
set_attr(:network=, :domains=, :name=)
|
||
assert !@subnet.save
|
||
end
|
||
|
||
test "mask should have valid address" do
|
||
@subnet.mask = "255.0.0.255"
|
||
set_attr(:network=, :domains=, :name=)
|
||
refute @subnet.save
|
||
end
|
||
|
||
test "cidr setter should set the mask" do
|
||
@subnet = FactoryGirl.build(:subnet_ipv4)
|
||
@subnet.cidr = 24
|
||
assert_equal '255.255.255.0', @subnet.mask
|
||
end
|
||
|
||
test "cidr setter should not raise exception for invalid value" do
|
||
@subnet = FactoryGirl.build(:subnet_ipv4)
|
||
@subnet.cidr = 'green'
|
||
end
|
||
|
||
test "network should be unique" do
|
||
set_attr(:network=, :mask=, :domains=, :name=)
|
||
@subnet.save
|
||
|
||
other_subnet = Subnet::Ipv4.create(:network => "123.123.123.0", :mask => "255.255.255.0")
|
||
assert !other_subnet.save
|
||
end
|
||
|
||
test "the name should be unique in the domain scope" do
|
||
create_a_domain_with_the_subnet
|
||
|
||
other_subnet = Subnet::Ipv4.new( :mask => "111.111.111.1",
|
||
:network => "255.255.252.0",
|
||
:name => "valid",
|
||
:domain_ids => [domains(:mydomain).id] )
|
||
assert !other_subnet.valid?
|
||
assert !other_subnet.save
|
||
end
|
||
|
||
test "when to_label is applied should show the domain, the mask and network" do
|
||
create_a_domain_with_the_subnet
|
||
|
||
assert_equal "valid (123.123.123.0/24)", @subnet.to_label
|
||
end
|
||
|
||
test "should find the subnet by ip" do
|
||
@subnet = Subnet::Ipv4.new(:network => "123.123.123.0", :mask => "255.255.255.0", :name => "valid")
|
||
assert @subnet.save
|
||
assert @subnet.domain_ids = [domains(:mydomain).id]
|
||
assert_equal @subnet, Subnet::Ipv4.subnet_for("123.123.123.1")
|
||
end
|
||
|
||
def set_attr(*attr)
|
||
attr.each do |param|
|
||
@subnet.send param, @attrs[param]
|
||
end
|
||
end
|
||
|
||
def create_a_domain_with_the_subnet
|
||
@domain = Domain.where(:name => "domain").first_or_create
|
||
@subnet = Subnet::Ipv4.new(:network => "123.123.123.0", :mask => "255.255.255.0", :name => "valid")
|
||
assert @subnet.save
|
||
assert @subnet.domain_ids = [domains(:mydomain).id]
|
||
@subnet.save!
|
||
end
|
||
|
||
test "from cant be bigger than to range" do
|
||
s = subnets(:one)
|
||
s.to = "2.3.4.15"
|
||
s.from = "2.3.4.17"
|
||
assert !s.save
|
||
end
|
||
|
||
test "should be able to save ranges" do
|
||
s=subnets(:one)
|
||
s.from = "2.3.4.15"
|
||
s.to = "2.3.4.17"
|
||
assert s.save
|
||
end
|
||
|
||
test "should not be able to save ranges if they dont belong to the subnet" do
|
||
s=subnets(:one)
|
||
s.from = "2.3.3.15"
|
||
s.to = "2.3.4.17"
|
||
assert !s.save
|
||
end
|
||
|
||
test "should not be able to save ranges if one of them is missing" do
|
||
s=subnets(:one)
|
||
s.from = "2.3.4.15"
|
||
assert !s.save
|
||
s.to = "2.3.4.17"
|
||
assert s.save
|
||
end
|
||
|
||
test "should not be able to save ranges if one of them is invalid" do
|
||
s=subnets(:one)
|
||
s.from = "2.3.4.abc"
|
||
s.to = "2.3.4.17"
|
||
refute s.valid?
|
||
end
|
||
|
||
test "should strip whitespace before save" do
|
||
s = subnets(:one)
|
||
s.network = " 10.0.0.22 "
|
||
s.mask = " 255.255.255.0 "
|
||
s.gateway = " 10.0.0.138 "
|
||
s.dns_primary = " 10.0.0.50 "
|
||
s.dns_secondary = " 10.0.0.60 "
|
||
assert s.save
|
||
assert_equal "10.0.0.22", s.network
|
||
assert_equal "255.255.255.0", s.mask
|
||
assert_equal "10.0.0.138", s.gateway
|
||
assert_equal "10.0.0.50", s.dns_primary
|
||
assert_equal "10.0.0.60", s.dns_secondary
|
||
end
|
||
|
||
test "should fix typo with extra dots to single dot" do
|
||
s = subnets(:one)
|
||
s.network = "10..0.0..22"
|
||
assert s.save
|
||
assert_equal "10.0.0.22", s.network
|
||
end
|
||
|
||
test "should fix typo with extra 5 after 255" do
|
||
s = subnets(:one)
|
||
s.mask = "2555.255.25555.0"
|
||
assert s.save
|
||
assert_equal "255.255.255.0", s.mask
|
||
end
|
||
|
||
test "should not allow an address great than 15 characters" do
|
||
s = subnets(:one)
|
||
s.mask = "255.255.255.1111"
|
||
refute s.save
|
||
assert_match /Mask is invalid/, s.errors.full_messages.join("\n")
|
||
end
|
||
|
||
test "should invalidate addresses are indeed invalid" do
|
||
s = subnets(:one)
|
||
# more than 3 characters
|
||
s.network = "1234.101.102.103"
|
||
# missing dot
|
||
s.network = "100101.102.103."
|
||
refute s.valid?
|
||
# greater than 255
|
||
s.network = "300.300.300.0"
|
||
refute s.valid?
|
||
# missing number
|
||
s.network = "100.101.102"
|
||
refute s.valid?
|
||
assert_equal "is invalid", s.errors[:network].first
|
||
end
|
||
|
||
test "should clean invalid addresses" do
|
||
s = subnets(:one)
|
||
# trailing dot
|
||
s.network = "100.101.102.103."
|
||
assert s.valid?
|
||
end
|
||
|
||
# test module StripWhitespace which strips leading and trailing whitespace on :name field
|
||
test "should strip whitespace on name" do
|
||
s = Subnet::Ipv4.new(:name => ' ABC Network ', :network => "10.10.20.1", :mask => "255.255.255.0")
|
||
assert s.save!
|
||
assert_equal "ABC Network", s.name
|
||
end
|
||
|
||
test "should not destroy if hostgroup uses it" do
|
||
hostgroup = FactoryGirl.create(:hostgroup, :with_subnet)
|
||
subnet = hostgroup.subnet
|
||
refute subnet.destroy
|
||
assert_match /is used by/, subnet.errors.full_messages.join("\n")
|
||
end
|
||
|
||
test "should not destroy if host uses it" do
|
||
host = FactoryGirl.create(:host, :with_subnet)
|
||
subnet = host.subnet
|
||
refute subnet.destroy
|
||
assert_match /is used by/, subnet.errors.full_messages.join("\n")
|
||
end
|
||
|
||
test "should find unused IP on proxy if proxy is set" do
|
||
subnet = FactoryGirl.create(:subnet_ipv4, :name => 'my_subnet', :network => '192.168.1.0')
|
||
subnet.stubs(:dhcp? => true)
|
||
subnet.stubs(:dhcp => mock('attribute', :url => 'proxy.example.com'))
|
||
fake_proxy = mock("dhcp_proxy")
|
||
fake_proxy.stubs(:unused_ip => {'ip' => '192.168.1.25'})
|
||
subnet.stubs(:dhcp_proxy => fake_proxy)
|
||
assert_equal '192.168.1.25', subnet.unused_ip
|
||
end
|
||
|
||
test "should find unused IP in internal DB if proxy is not set" do
|
||
host = FactoryGirl.create(:host)
|
||
subnet = FactoryGirl.create(:subnet_ipv4, :name => 'my_subnet', :network => '192.168.2.0',
|
||
:ipam => Subnet::IPAM_MODES[:db])
|
||
subnet.stubs(:dhcp? => false)
|
||
assert_equal '192.168.2.1', subnet.unused_ip
|
||
|
||
subnet.reload
|
||
FactoryGirl.create(:nic_managed, :ip => '192.168.2.1', :subnet_id => subnet.id, :host => host, :mac => '00:00:00:00:00:01')
|
||
FactoryGirl.create(:nic_managed, :ip => '192.168.2.2', :subnet_id => subnet.id, :host => host, :mac => '00:00:00:00:00:02')
|
||
assert_equal '192.168.2.3', subnet.unused_ip
|
||
end
|
||
|
||
test "should find unused IP excluding named values in internal DB if proxy is not set" do
|
||
host = FactoryGirl.create(:host)
|
||
subnet = FactoryGirl.create(:subnet_ipv4, :name => 'my_subnet', :network => '192.168.2.0',
|
||
:ipam => Subnet::IPAM_MODES[:db])
|
||
subnet.stubs(:dhcp? => false)
|
||
assert_equal '192.168.2.3', subnet.unused_ip(nil, ['192.168.2.1', '192.168.2.2'])
|
||
|
||
subnet.reload
|
||
FactoryGirl.create(:nic_managed, :ip => '192.168.2.1', :subnet_id => subnet.id, :host => host, :mac => '00:00:00:00:00:01')
|
||
FactoryGirl.create(:nic_managed, :ip => '192.168.2.2', :subnet_id => subnet.id, :host => host, :mac => '00:00:00:00:00:02')
|
||
assert_equal '192.168.2.4', subnet.unused_ip(nil, ['192.168.2.3'])
|
||
end
|
||
|
||
test "#unused should respect subnet from and to if it's set" do
|
||
host = FactoryGirl.create(:host)
|
||
subnet = FactoryGirl.create(:subnet_ipv4, :name => 'my_subnet', :network => '192.168.2.0', :from => '192.168.2.10', :to => '192.168.2.12',
|
||
:ipam => Subnet::IPAM_MODES[:db])
|
||
subnet.stubs(:dhcp? => false)
|
||
assert_equal '192.168.2.10', subnet.unused_ip
|
||
|
||
subnet.reload
|
||
FactoryGirl.create(:nic_managed, :ip => '192.168.2.10', :subnet_id => subnet.id, :host => host, :mac => '00:00:00:00:00:01')
|
||
FactoryGirl.create(:nic_managed, :ip => '192.168.2.11', :subnet_id => subnet.id, :host => host, :mac => '00:00:00:00:00:02')
|
||
assert_equal '192.168.2.12', subnet.unused_ip
|
||
|
||
subnet.reload
|
||
FactoryGirl.create(:nic_managed, :ip => '192.168.2.12', :subnet_id => subnet.id, :host => host, :mac => '00:00:00:00:00:03')
|
||
assert_nil subnet.unused_ip
|
||
end
|
||
|
||
test "#unused does not suggest IP if mode is set to none" do
|
||
subnet = FactoryGirl.create(:subnet_ipv4, :name => 'my_subnet', :network => '192.168.2.0', :from => '192.168.2.10', :to => '192.168.2.12')
|
||
subnet.stubs(:dhcp? => false, :ipam => Subnet::IPAM_MODES[:none])
|
||
assert_nil subnet.unused_ip
|
||
end
|
||
|
||
test "#known_ips includes all host and interfaces IPs assigned to this subnet" do
|
||
subnet = FactoryGirl.create(:subnet_ipv4, :name => 'my_subnet', :network => '192.168.2.0', :from => '192.168.2.10', :to => '192.168.2.12',
|
||
:dns_primary => '192.168.2.2', :gateway => '192.168.2.3', :ipam => Subnet::IPAM_MODES[:db])
|
||
host = FactoryGirl.create(:host, :subnet => subnet, :ip => '192.168.2.1')
|
||
Nic::Managed.create :mac => "00:00:01:10:00:00", :host => host, :subnet => subnet, :name => "", :ip => '192.168.2.4'
|
||
|
||
assert_includes subnet.known_ips, '192.168.2.1'
|
||
assert_includes subnet.known_ips, '192.168.2.2'
|
||
assert_includes subnet.known_ips, '192.168.2.3'
|
||
assert_includes subnet.known_ips, '192.168.2.4'
|
||
assert_equal 4, subnet.known_ips.size
|
||
end
|
||
|
||
context 'import subnets' do
|
||
setup do
|
||
@mock_proxy = mock('dhcp_proxy')
|
||
@mock_proxy.stubs(:has_feature? => true)
|
||
@mock_proxy.stubs(:url => 'http://fake')
|
||
end
|
||
|
||
test 'options are imported from the dhcp proxy' do
|
||
dhcp_options = { 'routers' => ['192.168.11.1'],
|
||
'domain_name_servers' => ['192.168.11.1', '8.8.8.8'],
|
||
'range' => ['192.168.11.0', '192.168.11.200'] }
|
||
ProxyAPI::DHCP.any_instance.
|
||
stubs(:subnets => [ { 'network' => '192.168.11.0',
|
||
'netmask' => '255.255.255.0',
|
||
'options' => dhcp_options } ])
|
||
Subnet.expects(:new).with(:network => "192.168.11.0",
|
||
:mask => "255.255.255.0",
|
||
:gateway => "192.168.11.1",
|
||
:dns_primary => "192.168.11.1",
|
||
:dns_secondary => "8.8.8.8",
|
||
:from => "192.168.11.0",
|
||
:to => "192.168.11.200",
|
||
:dhcp => @mock_proxy)
|
||
Subnet.import(@mock_proxy)
|
||
end
|
||
|
||
test 'imports subnets without options' do
|
||
ProxyAPI::DHCP.any_instance.
|
||
stubs(:subnets => [ { 'network' => '192.168.11.0',
|
||
'netmask' => '255.255.255.0' } ])
|
||
Subnet.expects(:new).with(:network => "192.168.11.0",
|
||
:mask => "255.255.255.0",
|
||
:dhcp => @mock_proxy)
|
||
Subnet.import(@mock_proxy)
|
||
end
|
||
end
|
||
|
||
test "should not assign proxies without adequate features" do
|
||
proxy = smart_proxies(:puppetmaster)
|
||
subnet = Subnet::Ipv4.new(:name => "test subnet",
|
||
:network => "192.168.100.0",
|
||
:mask => "255.255.255.0",
|
||
:dhcp_id => proxy.id,
|
||
:dns_id => proxy.id,
|
||
:tftp_id => proxy.id)
|
||
refute subnet.save
|
||
assert_equal "does not have the DNS feature", subnet.errors["dns_id"].first
|
||
assert_equal "does not have the DHCP feature", subnet.errors["dhcp_id"].first
|
||
assert_equal "does not have the TFTP feature", subnet.errors["tftp_id"].first
|
||
end
|
||
|
||
test "#type cannot be updated for existing subnet" do
|
||
subnet = subnets(:one)
|
||
subnet.type = 'Subnet::Ipv6'
|
||
refute subnet.save
|
||
assert subnet.errors[:type].include?("can't be updated after subnet is saved")
|
||
end
|
||
|
||
test "should inherit model name from parent class" do
|
||
assert_equal Subnet.model_name, Subnet::Ipv4.model_name
|
||
end
|
||
end
|
test/unit/subnet_test.rb | ||
---|---|---|
require 'test_helper'
|
||
|
||
class SubnetTest < ActiveSupport::TestCase
|
||
def setup
|
||
User.current = users :admin
|
||
@subnet = Subnet.new
|
||
@attrs = { :network= => "123.123.123.1",
|
||
:mask= => "255.255.255.0",
|
||
:domains= => [domains(:mydomain)],
|
||
:name= => "valid" }
|
||
end
|
||
|
||
test 'can be created with domains' do
|
||
subnet = FactoryGirl.build(:subnet)
|
||
subnet.domain_ids = [ domains(:mydomain).id ]
|
||
assert subnet.save
|
||
end
|
||
|
||
test "should have a network" do
|
||
create_a_domain_with_the_subnet
|
||
@subnet.network = nil
|
||
assert !@subnet.save
|
||
|
||
set_attr(:network=)
|
||
assert @subnet.save
|
||
end
|
||
|
||
test "should have a mask" do
|
||
create_a_domain_with_the_subnet
|
||
@subnet.mask = nil
|
||
assert !@subnet.save
|
||
|
||
@subnet.mask = "255.255.255.0"
|
||
assert @subnet.save
|
||
end
|
||
|
||
test "network should have ip format" do
|
||
@subnet.network = "asf.fwe6.we6s.q1"
|
||
set_attr(:mask=)
|
||
assert !@subnet.save
|
||
end
|
||
|
||
test "mask should have ip format" do
|
||
@subnet.mask = "asf.fwe6.we6s.q1"
|
||
set_attr(:network=, :domains=, :name=)
|
||
assert !@subnet.save
|
||
end
|
||
|
||
test "mask should have valid address" do
|
||
@subnet.mask = "255.0.0.255"
|
||
set_attr(:network=, :domains=, :name=)
|
||
refute @subnet.save
|
||
end
|
||
|
||
test "network should be unique" do
|
||
set_attr(:network=, :mask=, :domains=, :name=)
|
||
@subnet.save
|
||
|
||
other_subnet = Subnet.create(:network => "123.123.123.1", :mask => "255.255.255.0")
|
||
assert !other_subnet.save
|
||
end
|
||
|
||
test "the name should be unique in the domain scope" do
|
||
create_a_domain_with_the_subnet
|
||
|
||
other_subnet = Subnet.new( :mask => "111.111.111.1",
|
||
:network => "255.255.252.0",
|
||
:name => "valid",
|
||
:domain_ids => [domains(:mydomain).id] )
|
||
assert !other_subnet.valid?
|
||
assert !other_subnet.save
|
||
end
|
||
|
||
test "when to_label is applied should show the domain, the mask and network" do
|
||
create_a_domain_with_the_subnet
|
||
|
||
assert_equal "valid (123.123.123.1/24)", @subnet.to_label
|
||
end
|
||
|
||
test "should find the subnet by ip" do
|
||
@subnet = Subnet.new(:network => "123.123.123.1",:mask => "255.255.255.0",:name => "valid")
|
||
assert @subnet.save
|
||
assert @subnet.domain_ids = [domains(:mydomain).id]
|
||
assert_equal @subnet, Subnet.subnet_for("123.123.123.1")
|
||
end
|
||
|
||
def set_attr(*attr)
|
||
attr.each do |param|
|
||
@subnet.send param, @attrs[param]
|
||
end
|
||
end
|
||
|
||
def create_a_domain_with_the_subnet
|
||
@domain = Domain.where(:name => "domain").first_or_create
|
||
@subnet = Subnet.new(:network => "123.123.123.1",:mask => "255.255.255.0",:name => "valid")
|
||
assert @subnet.save
|
||
assert @subnet.domain_ids = [domains(:mydomain).id]
|
||
@subnet.save!
|
||
end
|
||
|
||
test "from cant be bigger than to range" do
|
||
s = subnets(:one)
|
||
s.to = "2.3.4.15"
|
||
s.from = "2.3.4.17"
|
||
assert !s.save
|
||
end
|
||
|
||
test "should be able to save ranges" do
|
||
s=subnets(:one)
|
||
s.from = "2.3.4.15"
|
||
s.to = "2.3.4.17"
|
||
assert s.save
|
||
end
|
||
|
||
test "should not be able to save ranges if they dont belong to the subnet" do
|
||
s=subnets(:one)
|
||
s.from = "2.3.3.15"
|
||
s.to = "2.3.4.17"
|
||
assert !s.save
|
||
end
|
||
|
||
test "should not be able to save ranges if one of them is missing" do
|
||
s=subnets(:one)
|
||
s.from = "2.3.4.15"
|
||
assert !s.save
|
||
s.to = "2.3.4.17"
|
||
assert s.save
|
||
end
|
||
|
||
test "should strip whitespace before save" do
|
||
s = subnets(:one)
|
||
s.network = " 10.0.0.22 "
|
||
s.mask = " 255.255.255.0 "
|
||
s.gateway = " 10.0.0.138 "
|
||
s.dns_primary = " 10.0.0.50 "
|
||
s.dns_secondary = " 10.0.0.60 "
|
Also available in: Unified diff
fixes #14638 - Refactor Subnet into STI to allow different subnet types