Project

General

Profile

Download (6.38 KB) Statistics
| Branch: | Tag: | Revision:
06823dc7 Ohad Levy
require 'ipaddr'
96b38b3c Ohad Levy
class Subnet < ActiveRecord::Base
acfbc458 Marek Hulan
include Authorizable
611f5bff Amos Benari
include Taxonomix
229d7436 Joseph Magen
audited :allow_mass_assignment => true
d7611b24 Greg Sutcliffe
ff8cc704 Joseph Mitchell Magen
before_destroy EnsureNotUsedBy.new(:hosts, :interfaces )
d7611b24 Greg Sutcliffe
has_many_hosts
7d993b41 Joseph Mitchell Magen
has_many :hostgroups
06823dc7 Ohad Levy
belongs_to :dhcp, :class_name => "SmartProxy"
belongs_to :tftp, :class_name => "SmartProxy"
dd42df0a Ohad Levy
belongs_to :dns, :class_name => "SmartProxy"
fa62ea80 Ohad Levy
has_many :subnet_domains, :dependent => :destroy
has_many :domains, :through => :subnet_domains
8838eb42 Ohad Levy
has_many :interfaces, :class_name => 'Nic::Base'
f2c78d4a Joseph Magen
validates :network, :mask, :name, :presence => true
fa62ea80 Ohad Levy
validates_associated :subnet_domains
f2c78d4a Joseph Magen
validates :network, :uniqueness => true,
:format => {:with => Net::Validations::IP_REGEXP},
cba97ace Joseph Magen
:length => {:maximum => 15, :message => N_("must be at most 15 characters")}
f2c78d4a Joseph Magen
validates :gateway, :dns_primary, :dns_secondary,
:allow_blank => true,
:allow_nil => true,
:format => {:with => Net::Validations::IP_REGEXP},
cba97ace Joseph Magen
:length => { :maximum => 15, :message => N_("must be at most 15 characters") }
f2c78d4a Joseph Magen
validates :mask, :format => {:with => Net::Validations::IP_REGEXP},
cba97ace Joseph Magen
:length => {:maximum => 15, :message => N_("must be at most 15 characters")}
f2c78d4a Joseph Magen
31aa5db5 Joseph Mitchell Magen
validate :ensure_ip_addr_new
before_validation :cleanup_addresses
fa62ea80 Ohad Levy
validate :name_should_be_uniq_across_domains
611f5bff Amos Benari
7a900b06 Ohad Levy
validate :validate_ranges
96b38b3c Ohad Levy
611f5bff Amos Benari
default_scope lambda {
with_taxonomy_scope do
order('vlanid')
end
}

fe3d8f82 Ohad Levy
scoped_search :on => [:name, :network, :mask, :gateway, :dns_primary, :dns_secondary, :vlanid], :complete_value => true
fa62ea80 Ohad Levy
scoped_search :in => :domains, :on => :name, :rename => :domain, :complete_value => true
8104eced Ohad Levy
8bd4e480 Ohad Levy
class Jail < ::Safemode::Jail
allow :name, :network, :mask, :cidr, :title, :to_label, :gateway, :dns_primary, :dns_secondary, :vlanid
end

06823dc7 Ohad Levy
# Subnets are displayed in the form of their network network/network mask
1fa008a4 Joseph Magen
def network_address
06823dc7 Ohad Levy
"#{network}/#{cidr}"
96b38b3c Ohad Levy
end

1fa008a4 Joseph Magen
def to_label
"#{name} (#{network_address})"
be770e85 Ohad Levy
end

06823dc7 Ohad Levy
# Subnets are sorted on their priority value
# [+other+] : Subnet object with which to compare ourself
# +returns+ : Subnet object with higher precedence
def <=> (other)
9f9effa8 Joseph Mitchell Magen
if self.vlanid.present? && other.vlanid.present?
self.vlanid <=> other.vlanid
else
return -1
end
96b38b3c Ohad Levy
end
06823dc7 Ohad Levy
96b38b3c Ohad Levy
# Given an IP returns the subnet that contains that IP
# [+ip+] : "doted quad" string
06823dc7 Ohad Levy
# 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)}
96b38b3c Ohad Levy
nil
end

06823dc7 Ohad Levy
# Indicates whether the IP is within this subnet
# [+ip+] String: Contains 4 dotted decimal values
# 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)
end

def cidr
IPAddr.new(mask).to_i.to_s(2).count("1")
end

b1116c90 Ohad Levy
def dhcp?
6285a614 Ohad Levy
!!(dhcp and dhcp.url and !dhcp.url.blank?)
b1116c90 Ohad Levy
end

6285a614 Ohad Levy
def dhcp_proxy attrs = {}
@dhcp_proxy ||= ProxyAPI::DHCP.new({:url => dhcp.url}.merge(attrs)) if dhcp?
end

def tftp?
!!(tftp and tftp.url and !tftp.url.blank?)
end

def tftp_proxy attrs = {}
@tftp_proxy ||= ProxyAPI::TFTP.new({:url => tftp.url}.merge(attrs)) if tftp?
06823dc7 Ohad Levy
end

dd42df0a Ohad Levy
# do we support DNS PTR records for this subnet
def dns?
!!(dns and dns.url and !dns.url.blank?)
end

def dns_proxy attrs = {}
@dns_proxy ||= ProxyAPI::DNS.new({:url => dns.url}.merge(attrs)) if dns?
end

e89efb28 Ohad Levy
def unused_ip mac = nil
b1116c90 Ohad Levy
return unless dhcp?
e89efb28 Ohad Levy
dhcp_proxy.unused_ip(self, mac)["ip"]
06823dc7 Ohad Levy
rescue => e
455f5d2b Paul Kelly
logger.warn "Failed to fetch a free IP from our proxy: #{e}"
06823dc7 Ohad Levy
nil
96b38b3c Ohad Levy
end
10139fde José Luis Escalante
4c091cd8 Ohad Levy
# imports subnets from a dhcp smart proxy
def self.import proxy
return unless proxy.features.include?(Feature.find_by_name("DHCP"))
ProxyAPI::DHCP.new(:url => proxy.url).subnets.map do |s|
# do not import existing networks.
attrs = { :network => s["network"], :mask => s["netmask"] }
next if first(:conditions => attrs)
new(attrs.update(:dhcp => proxy))
end.compact
end

7a900b06 Ohad Levy
private

def validate_ranges
bfbf7ed8 Lukas Zapletal
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
67799065 Ohad Levy
if from.present? or to.present?
bfbf7ed8 Lukas Zapletal
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?
7a900b06 Ohad Levy
end
end
fa62ea80 Ohad Levy
def name_should_be_uniq_across_domains
return if domains.empty?
domains.each do |d|
conds = new_record? ? ['name = ?', name] : ['subnets.name = ? AND subnets.id != ?', name, id]
bfbf7ed8 Lukas Zapletal
errors.add(:name, _("domain %s already has a subnet with this name") % d) if d.subnets.where(conds).first
fa62ea80 Ohad Levy
end
end
31aa5db5 Joseph Mitchell Magen
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
end

def cleanup_ip(address)
address.gsub!(/\.\.+/, ".")
address.gsub!(/2555+/, "255")
address
end

def ensure_ip_addr_new
16874851 Joseph Mitchell Magen
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)
31aa5db5 Joseph Mitchell Magen
end

06823dc7 Ohad Levy
end