Class: IPAM::PingRandomDb
- Inherits:
-
Base
- Object
- Base
- IPAM::PingRandomDb
- Defined in:
- app/services/ipam/ping_random_db.rb
Constant Summary collapse
- MAX_ITERATIONS =
Safety check not to spend much CPU time when there are no many free IPs left. This gives up in about a second on Ryzen 1700 running with Ruby 2.4.
100_000
Instance Method Summary collapse
- #generator ⇒ Object
- #icmp_pingable?(ip) ⇒ Boolean
- #random_ip(range) ⇒ Object
- #suggest_ip ⇒ Object
- #tcp_pingable?(ip) ⇒ Boolean
- #tcp_pingable_by_port?(ip, port) ⇒ Boolean
Instance Method Details
#generator ⇒ Object
6 7 8 |
# File 'app/services/ipam/ping_random_db.rb', line 6 def generator @generator ||= Random.new(mac ? mac.delete(':').to_i(16) : Random.new_seed) end |
#icmp_pingable?(ip) ⇒ Boolean
56 57 58 59 60 61 62 63 64 65 66 67 68 |
# File 'app/services/ipam/ping_random_db.rb', line 56 def icmp_pingable? ip # Always shell to ping, instead of using net-ping if RUBY_PLATFORM =~ /mingw/ # Windows uses different options for ping and does not have /dev/null system("ping -n 2 -w 1000 #{ip} > NUL") else # Default to Linux ping options and send to /dev/null system("ping -c 2 -W 1 #{ip} > /dev/null") end rescue => err logger.debug "Unable to icmp ping #{ip} because #{err.inspect}." true end |
#random_ip(range) ⇒ Object
10 11 12 |
# File 'app/services/ipam/ping_random_db.rb', line 10 def random_ip(range) IPAddr.new(generator.rand(range.first.to_i..range.last.to_i), subnet.family) end |
#suggest_ip ⇒ Object
74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'app/services/ipam/ping_random_db.rb', line 74 def suggest_ip iterations = 0 # Remove IPs already excluded or known. range = subnet_range.to_a - excluded_ips.to_a range -= subnet.known_ips.to_a loop do # next random IP from the sequence generated by MAC seed candidate = random_ip(range) iterations += 1 break if iterations >= MAX_ITERATIONS # try to match it ip = candidate.to_s # Check again if something has been changed. if !excluded_ips.include?(ip) && !subnet.known_ips.include?(ip) logger.debug "Searching for free IP - pinging #{ip}." if tcp_pingable?(ip) || icmp_pingable?(ip) logger.warn("Found a pingable IP (#{ip}) address which not marked as known. Skipping it...") else logger.debug("Found #{ip} in #{iterations} iterations") return ip end end end logger.debug("Not suggesting IP Address for #{subnet} as no free IP found in reasonable time (#{iterations} iterations)") errors.add(:subnet, _('no random free IP could be found in our DB, enlarge subnet range')) nil end |
#tcp_pingable?(ip) ⇒ Boolean
14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
# File 'app/services/ipam/ping_random_db.rb', line 14 def tcp_pingable?(ip) ports = [7, 22, 80, 443] begin ports.each do |port| if tcp_pingable_by_port?(ip, port) return true end end false end end |
#tcp_pingable_by_port?(ip, port) ⇒ Boolean
29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 |
# File 'app/services/ipam/ping_random_db.rb', line 29 def tcp_pingable_by_port?(ip, port) # This code is from net-ping, and stripped down for use here # Whether or not Errno::ECONNREFUSED is considered a successful ping @service_check = true @timeout = 1 bool = false begin bool = Socket.tcp(ip, port, connect_timeout: @timeout) { true } rescue Errno::ECONNREFUSED if @service_check bool = true end rescue Exception bool = false end if bool logger.info "Succesful telnet ping #{ip}, port #{port}" end bool rescue true end |