pingable_ipam_plugin 0.0.2 → 0.0.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 5167c6a2b4558580a184c8f21d89054329acb2d87ab6b803b50f2838cd6d0c3f
4
- data.tar.gz: 23f4fb9c0b37e4e5e2879092a5b00197b05655c4afb1e2574288a86e1edaaaec
3
+ metadata.gz: afb4f76ebb1e0b6ba231a0a52e696e78728abd2eb38ef75946397fc3531e4114
4
+ data.tar.gz: 402d327725e8605041658ba455b869f38ab09edc327d359e81b89155850c4194
5
5
  SHA512:
6
- metadata.gz: 35052c2ee42d1f1e6d729eb6c13a03b014852aa75e457794c349ec122493ff85e6f1443f2be593185a4c1547e7d4161052b5a0109528e86a76c18485afa04c84
7
- data.tar.gz: 27fb31ea168284836131069b6385a481d1709d21792f5e52cf674561d25cc9cd7f661418c087ddd725574ef34961619785d8b0991f032deaa6cbd79a8c3ef869
6
+ metadata.gz: 5755029b6b0c67cd28260d1e05715ac34fd04317cff271d9db4067e4f5094159c515da6a9bda1664586b95ba4deca91809542a080551ad49d3d521ec5fde4170
7
+ data.tar.gz: 2a8ccfb8448d928be6bd31a20a3db1a531c3fbe32e8baeabacc266570fe026f98035fbbdba9984e5e000d736b09f923d8faff018edc02b56ddbd96baeb604c32
data/README.md CHANGED
@@ -3,6 +3,9 @@
3
3
  This IPAM mode extends the defaul Random DB adding one extra step
4
4
  before suggesting IP: ping that address and if it is pingable it will not be suggested.
5
5
 
6
+ To prevent human error when the host is created without assigned subnet the plugin adds
7
+ a new validation rule: the subnet must be selected.
8
+
6
9
  ## Installation
7
10
 
8
11
  See [How_to_Install_a_Plugin](http://projects.theforeman.org/projects/foreman/wiki/How_to_Install_a_Plugin)
@@ -0,0 +1,9 @@
1
+ module PingableIpamPlugin
2
+ module NicExtensions
3
+ extend ActiveSupport::Concern
4
+ included do
5
+ validates :subnet_id, :presence => true, :if => Proc.new { |nic| nic.host_managed? && nic.primary? }
6
+ end
7
+ end
8
+ end
9
+
@@ -1,5 +1,6 @@
1
1
  require 'timeout'
2
2
  require 'socket'
3
+ require 'resolv'
3
4
 
4
5
  module IPAM
5
6
  class PingRandomDb < Base
@@ -7,8 +8,8 @@ module IPAM
7
8
  @generator ||= Random.new(mac ? mac.delete(':').to_i(16) : Random.new_seed)
8
9
  end
9
10
 
10
- def random_ip
11
- IPAddr.new(generator.rand(subnet_range.first.to_i..subnet_range.last.to_i), subnet.family)
11
+ def random_ip(range)
12
+ IPAddr.new(generator.rand(range.first.to_i..range.last.to_i), subnet.family)
12
13
  end
13
14
 
14
15
  def tcp_pingable?(ip)
@@ -32,34 +33,20 @@ module IPAM
32
33
  # Whether or not Errno::ECONNREFUSED is considered a successful ping
33
34
  @service_check = true
34
35
  @timeout = 1
35
- @exception = nil
36
36
  bool = false
37
- tcp = nil
38
37
 
39
38
  begin
40
- Timeout.timeout(@timeout) do
41
- begin
42
- tcp = TCPSocket.new(ip, port)
43
- rescue Errno::ECONNREFUSED => err
44
- if @service_check
45
- bool = true
46
- else
47
- @exception = err
48
- end
49
- rescue Exception => err
50
- @exception = err
51
- else
52
- bool = true
53
- end
39
+ bool = Socket.tcp(ip, port, connect_timeout: @timeout) { true }
40
+ rescue Errno::ECONNREFUSED
41
+ if @service_check
42
+ bool = true
54
43
  end
55
- rescue Timeout::Error => err
56
- @exception = err
57
- ensure
58
- tcp.close if tcp
44
+ rescue Exception
45
+ bool = false
59
46
  end
60
47
 
61
48
  if bool
62
- logger.info "Succesful telnet ping #{ip}, port #{port}"
49
+ logger.info "[IPAM] Succesful telnet ping #{ip}, port #{port}"
63
50
  end
64
51
 
65
52
  bool
@@ -77,34 +64,63 @@ module IPAM
77
64
  system("ping -c 2 -W 1 #{ip} > /dev/null")
78
65
  end
79
66
  rescue => err
80
- logger.debug "Unable to icmp ping #{ip} because #{err.inspect}."
67
+ logger.warn "[IPAM] Unable to icmp ping #{ip} because #{err.inspect}."
81
68
  true
82
69
  end
83
70
 
71
+ def ns_resolvable? ip
72
+ begin
73
+ name = Resolv.getname ip
74
+ logger.warn "[IPAM] Found a DNS resolvable IP #{ip}, resolved name: #{name}."
75
+ true
76
+ rescue StandardError
77
+ false
78
+ end
79
+ end
80
+
84
81
  # Safety check not to spend much CPU time when there are no many free IPs left. This gives up
85
82
  # in about a second on Ryzen 1700 running with Ruby 2.4.
86
83
  MAX_ITERATIONS = 100_000
87
84
 
88
85
  def suggest_ip
89
86
  iterations = 0
87
+ # Remove IPs already excluded or known.
88
+ range = subnet_range.to_a - excluded_ips.to_a
89
+ range -= subnet.known_ips.to_a
90
+
91
+ unless range.empty?
92
+ logger.info "[IPAM] IP range from #{range.first.to_s} to #{range.last.to_s}"
93
+ end
94
+
90
95
  loop do
96
+ if range.empty?
97
+ errors.add(:subnet, _('no free IP could be found in our DB, enlarge subnet range'))
98
+ return nil
99
+ end
100
+
91
101
  # next random IP from the sequence generated by MAC seed
92
- candidate = random_ip
102
+ candidate = random_ip(range)
93
103
  iterations += 1
94
104
  break if iterations >= MAX_ITERATIONS
95
105
  # try to match it
96
106
  ip = candidate.to_s
107
+ # Check again if something has been changed.
97
108
  if !excluded_ips.include?(ip) && !subnet.known_ips.include?(ip)
98
- logger.debug "Searching for free IP - pinging #{ip}."
109
+ logger.info "[IPAM] Searching for free IP - resolving #{ip}"
99
110
  if tcp_pingable?(ip) || icmp_pingable?(ip)
100
- logger.warn("Found a pingable IP (#{ip}) address which not marked as known. Skipping it...")
111
+ logger.warn("[IPAM] Found a pingable IP (#{ip}) address which not marked as known. Skipping it...")
101
112
  else
102
- logger.debug("Found #{ip} in #{iterations} iterations")
103
- return ip
113
+ unless ns_resolvable?(ip)
114
+ logger.info("[IPAM] Found #{ip} in #{iterations} iterations")
115
+ return ip
116
+ end
104
117
  end
105
118
  end
119
+
120
+ range -= [ip]
106
121
  end
107
- logger.debug("Not suggesting IP Address for #{subnet} as no free IP found in reasonable time (#{iterations} iterations)")
122
+
123
+ logger.warn("[IPAM] Not suggesting IP Address for #{subnet} as no free IP found in reasonable time (#{iterations} iterations)")
108
124
  errors.add(:subnet, _('no random free IP could be found in our DB, enlarge subnet range'))
109
125
  nil
110
126
  end
@@ -16,6 +16,7 @@ module PingableIpamPlugin
16
16
  begin
17
17
  ::IPAM.send(:include, PingableIpamPlugin::IpamExtensions)
18
18
  ::Subnet::Ipv4.singleton_class.send(:prepend, PingableIpamPlugin::Ipv4Extensions)
19
+ ::Nic::Base.send(:include, PingableIpamPlugin::NicExtensions)
19
20
  rescue => e
20
21
  Rails.logger.warn "PingableIpamPlugin: skipping engine hook (#{e})"
21
22
  end
@@ -1,3 +1,3 @@
1
1
  module PingableIpamPlugin
2
- VERSION = '0.0.2'.freeze
2
+ VERSION = '0.0.7'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pingable_ipam_plugin
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.2
4
+ version: 0.0.7
5
5
  platform: ruby
6
6
  authors:
7
7
  - Pavel Parshin
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2020-01-20 00:00:00.000000000 Z
11
+ date: 2021-03-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rubocop
@@ -51,6 +51,7 @@ files:
51
51
  - Rakefile
52
52
  - app/models/concerns/pingable_ipam_plugin/ipam_extensions.rb
53
53
  - app/models/concerns/pingable_ipam_plugin/ipv4_extensions.rb
54
+ - app/models/concerns/pingable_ipam_plugin/nic_extensions.rb
54
55
  - app/services/ipam/ping_random_db.rb
55
56
  - lib/pingable_ipam_plugin.rb
56
57
  - lib/pingable_ipam_plugin/engine.rb