smart_proxy_dhcp_infoblox 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,24 +1,16 @@
1
- require 'smart_proxy_dhcp_infoblox/dhcp_infoblox_version'
2
-
3
1
  module Proxy::DHCP::Infoblox
4
2
  class Plugin < ::Proxy::Provider
5
- plugin :dhcp_infoblox, ::Proxy::DHCP::Infoblox::VERSION, :provider_class => "::Proxy::DHCP::Infoblox::Provider"
3
+ plugin :dhcp_infoblox, ::Proxy::DHCP::Infoblox::VERSION
4
+
5
+ default_settings :record_type => 'host', :range => false
6
+ validate_presence :username, :password
6
7
 
7
- # Settings listed under default_settings are required.
8
- # An exception will be raised if they are initialized with nil values.
9
- # Settings not listed under default_settings are considered optional and by default have nil value.
10
- default_settings :infoblox_user => 'infoblox',
11
- :infoblox_pw => 'infoblox',
12
- :infoblox_host => 'infoblox.my.domain',
13
- :record_type => 'host',
14
- :wapi_version => '2.0',
15
- :range => false
8
+ requires :dhcp, '>= 1.13'
16
9
 
17
- requires :dhcp, '>= 1.11'
10
+ load_classes ::Proxy::DHCP::Infoblox::PluginConfiguration
11
+ load_validators :record_type_validator => ::Proxy::DHCP::Infoblox::RecordTypeValidator
12
+ load_dependency_injection_wirings ::Proxy::DHCP::Infoblox::PluginConfiguration
18
13
 
19
- after_activation do
20
- require 'smart_proxy_dhcp_infoblox/dhcp_infoblox_main'
21
- require 'smart_proxy_dhcp_infoblox/dhcp_infoblox_dependencies'
22
- end
14
+ validate :record_type, :record_type_validator => true
23
15
  end
24
16
  end
@@ -1,7 +1,7 @@
1
1
  module Proxy
2
2
  module DHCP
3
3
  module Infoblox
4
- VERSION = '0.0.4'
4
+ VERSION = '0.0.5'
5
5
  end
6
6
  end
7
7
  end
@@ -0,0 +1,53 @@
1
+ require 'smart_proxy_dhcp_infoblox/common_crud'
2
+
3
+ module ::Proxy::DHCP::Infoblox
4
+ class FixedAddressCRUD < CommonCRUD
5
+ def initialize(connection)
6
+ @memoized_host = nil
7
+ @memoized_condition = nil
8
+ super
9
+ end
10
+
11
+ def all_hosts(subnet_address)
12
+ network = ::Infoblox::Fixedaddress.find(@connection, 'network' => subnet_address, '_max_results' => 2147483646) #2**(32-cidr_to_i(subnet_address)))
13
+ network.map {|h| build_reservation(h.name, h, subnet_address)}.compact
14
+ end
15
+
16
+ def find_record_by_ip(subnet_address, ip_address)
17
+ found = find_host('ipv4addr' => ip_address)
18
+ return nil if found.nil?
19
+ build_reservation(found.name, found, subnet_address)
20
+ end
21
+
22
+ def find_record_by_mac(subnet_address, mac_address)
23
+ found = find_host('mac' => mac_address)
24
+ return nil if found.nil?
25
+ build_reservation(found.name, found, subnet_address)
26
+ end
27
+
28
+ def find_host(condition)
29
+ return @memoized_host if (!@memoized_host.nil? && @memoized_condition == condition)
30
+ @memoized_condition = condition
31
+ @memoized_host = ::Infoblox::Fixedaddress.find(@connection, condition.merge('_max_results' => 1)).first
32
+ end
33
+
34
+ def find_host_and_name_by_ip(ip_address)
35
+ h = find_host('ipv4addr' => ip_address)
36
+ [h.name, h]
37
+ end
38
+
39
+ def build_host(options)
40
+ host = ::Infoblox::Fixedaddress.new(:connection => @connection)
41
+ host.name = options[:hostname]
42
+ host.ipv4addr = options[:ip]
43
+ host.mac = options[:mac]
44
+ # TODO: nextserver, use_nextserver, bootfile, and use_bootfile attrs exist but are not available in the model
45
+ # Might be useful to extend the model to include these
46
+ #host.nextserver = options[:nextServer]
47
+ #host.use_nextserver = true
48
+ #host.bootfile = options[:filename]
49
+ #host.use_bootfile = true
50
+ host
51
+ end
52
+ end
53
+ end
@@ -0,0 +1,31 @@
1
+ module ::Proxy::DHCP::Infoblox
2
+ class GridRestart
3
+ MAX_ATTEMPTS = 3
4
+
5
+ include ::Proxy::Log
6
+ attr_reader :connection
7
+
8
+ def initialize(connection)
9
+ @connection = connection
10
+ end
11
+
12
+ def try_restart
13
+ logger.debug 'Restarting grid.'
14
+
15
+ MAX_ATTEMPTS.times do |tries|
16
+ sleep tries
17
+ return if restart
18
+ end
19
+
20
+ logger.info 'Restarting Grid failed.'
21
+ false
22
+ end
23
+
24
+ def restart
25
+ (@grid ||= ::Infoblox::Grid.get(@connection).first).restartservices
26
+ true
27
+ rescue Exception => e
28
+ false
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,60 @@
1
+ require 'smart_proxy_dhcp_infoblox/common_crud'
2
+ require 'smart_proxy_dhcp_infoblox/network_address_range_regex_generator'
3
+
4
+ module ::Proxy::DHCP::Infoblox
5
+ class HostIpv4AddressCRUD < CommonCRUD
6
+ def initialize(connection)
7
+ @memoized_host = nil
8
+ @memoized_condition = nil
9
+ super
10
+ end
11
+
12
+ def all_hosts(subnet_address)
13
+ address_range_regex = NetworkAddressesRegularExpressionGenerator.new.generate_regex(subnet_address)
14
+
15
+ hosts = ::Infoblox::Host.find(
16
+ @connection,
17
+ 'ipv4addr~' => address_range_regex,
18
+ '_max_results' => 2147483646)
19
+
20
+ ip_addr_matcher = Regexp.new(address_range_regex) # pre-compile the regex
21
+ hosts.map {|host| build_reservation(host.name, host.ipv4addrs.find {|ip| ip_addr_matcher =~ ip.ipv4addr}, subnet_address)}.compact
22
+ end
23
+
24
+ def find_record_by_ip(subnet_address, ip_address)
25
+ found = find_host('ipv4addr' => ip_address)
26
+ return nil if found.nil?
27
+ build_reservation(found.name, found.ipv4addrs.find {|ip| ip.ipv4addr == ip_address}, subnet_address)
28
+ end
29
+
30
+ def find_record_by_mac(subnet_address, mac_address)
31
+ found = find_host('mac' => mac_address)
32
+ return nil if found.nil?
33
+ build_reservation(found.name, found.ipv4addrs.find {|ip| ip.mac == mac_address}, subnet_address)
34
+ end
35
+
36
+ def find_host_and_name_by_ip(ip_address)
37
+ h = find_host('ipv4addr' => ip_address)
38
+ [h.name, h.ipv4addrs.find {|ip| ip.ipv4addr == ip_address}]
39
+ end
40
+
41
+ def find_host(condition)
42
+ return @memoized_host if (!@memoized_host.nil? && @memoized_condition == condition)
43
+ @memoized_condition = condition
44
+ @memoized_host = ::Infoblox::Host.find(@connection, condition.merge('_max_results' => 1)).first
45
+ end
46
+
47
+ def build_host(options)
48
+ host = ::Infoblox::Host.new(:connection => @connection)
49
+ host.name = options[:hostname]
50
+ host_addr = host.add_ipv4addr(options[:ip]).last
51
+ host_addr.mac = options[:mac]
52
+ host_addr.configure_for_dhcp = true
53
+ host_addr.nextserver = options[:nextServer]
54
+ host_addr.use_nextserver = true
55
+ host_addr.bootfile = options[:filename]
56
+ host_addr.use_bootfile = true
57
+ host
58
+ end
59
+ end
60
+ end
@@ -0,0 +1,34 @@
1
+ module ::Proxy::DHCP::Infoblox
2
+ module IpAddressArithmetic
3
+ def cidr_to_ip_mask(prefix_length)
4
+ bitmask = 0xFFFFFFFF ^ (2 ** (32-prefix_length) - 1)
5
+ (0..3).map {|i| (bitmask >> i*8) & 0xFF}.reverse.join('.')
6
+ end
7
+
8
+ def ipv4_to_i(an_address)
9
+ an_address.split('.').inject(0) {|a, c| (a << 8) + c.to_i}
10
+ end
11
+
12
+ def i_to_ipv4(i)
13
+ (0..3).inject([]) {|a, c| a.push((i >> (c * 8)) & 0xFF)}.reverse.join('.')
14
+ end
15
+
16
+ def cidr_to_bitmask(prefix_length)
17
+ 0xFFFFFFFF ^ (2 ** (32-prefix_length) - 1)
18
+ end
19
+
20
+ def cidr_to_i(an_address_with_cidr)
21
+ an_address_with_cidr.split("/").last.to_i
22
+ end
23
+
24
+ def network_cidr_to_range(network_and_cidr)
25
+ network_addr, cidr = network_and_cidr.split('/')
26
+ mask = cidr_to_bitmask(cidr.to_i)
27
+
28
+ range_start = ipv4_to_i(network_addr) & mask
29
+ range_end = range_start | (0xFFFFFFFF ^ mask)
30
+
31
+ [i_to_ipv4(range_start), i_to_ipv4(range_end)]
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,131 @@
1
+ require 'smart_proxy_dhcp_infoblox/ip_address_arithmetic'
2
+
3
+ module ::Proxy::DHCP::Infoblox
4
+ class RangeRegularExpressionGenerator
5
+ class Node
6
+ attr_accessor :value, :children
7
+
8
+ def initialize(value, children = [])
9
+ @value = value
10
+ @children = children
11
+ end
12
+
13
+ def add_children(values)
14
+ return if values.empty?
15
+ node = (found = children.find {|n| n.value == values.first}).nil? ? add_child(Node.new(values.first)) : found
16
+ node.add_children(values[1..-1])
17
+ end
18
+
19
+ def <=>(other)
20
+ return -1 if value.to_s == '0?'
21
+ return 1 if other.value.to_s == '0?'
22
+ return 0 if value == other.value
23
+ return -1 if value < other.value
24
+ 1
25
+ end
26
+
27
+ def add_child(a_node)
28
+ children.push(a_node)
29
+ children.sort!
30
+ a_node
31
+ end
32
+
33
+ def group_children
34
+ children.each {|n| n.group_children}
35
+ return if children.size < 2
36
+ @children = children[1..-1].inject([MergedNode.new(children.first)]) do |grouped, to_group|
37
+ current = MergedNode.new(to_group)
38
+ found = grouped.find {|g| ((g.value != ['0?'] && current.value != ['0?']) || (current.value == ['0?'] && g.value == ['0?'])) && (g.children == current.children)}
39
+ found.nil? ? grouped.push(current) : found.merge(current)
40
+ grouped
41
+ end
42
+ end
43
+
44
+ def as_regex
45
+ children.empty? ? [value.to_s] : children.map {|c| c.as_regex.map {|r| value.to_s + r}}.flatten
46
+ end
47
+ end
48
+
49
+ class MergedNode
50
+ attr_accessor :value, :children
51
+ def initialize(a_node)
52
+ @value = [a_node.value].flatten
53
+ @children = a_node.children
54
+ end
55
+
56
+ def merge(other)
57
+ value.push(other.value).flatten!
58
+ self
59
+ end
60
+
61
+ def as_regex
62
+ children.empty? ? [value_as_regex] : children.map {|c| c.as_regex.map {|r| value_as_regex + r}}.flatten
63
+ end
64
+
65
+ def value_as_regex
66
+ value.size < 2 ? value.first.to_s : "[#{value.join('')}]"
67
+ end
68
+
69
+ def ==(other)
70
+ return false if self.class != other.class
71
+ value == other.value
72
+ end
73
+ end
74
+
75
+ class Root < Node
76
+ def add_number(a_number)
77
+ add_children((['0?', '0?'] + digits(a_number))[-3, 3])
78
+ end
79
+
80
+ def as_regex
81
+ group_children
82
+ "(%s)" % children.map {|c| c.as_regex}.join('|')
83
+ end
84
+
85
+ def digits(a_number)
86
+ to_return = []
87
+ begin
88
+ to_return.push(a_number % 10)
89
+ a_number = a_number / 10
90
+ end while a_number != 0
91
+ to_return.reverse
92
+ end
93
+ end
94
+
95
+ def range_regex(range_start, range_end)
96
+ root = Root.new(nil)
97
+ (range_start..range_end).to_a.each {|i| root.add_number(i)}
98
+ root.as_regex
99
+ end
100
+ end
101
+
102
+ class NetworkAddressesRegularExpressionGenerator
103
+ include IpAddressArithmetic
104
+
105
+ def generate_regex(network_and_cidr)
106
+ range_to_regex(network_cidr_range_octets(network_and_cidr))
107
+ end
108
+
109
+ def network_cidr_range_octets(network_and_cidr)
110
+ range = network_cidr_to_range(network_and_cidr)
111
+ range_start_octets = range.first.split('.').map(&:to_i)
112
+ range_end_octets = range.last.split('.').map(&:to_i)
113
+
114
+ (0..3).map {|i| [range_start_octets[i], range_end_octets[i]]}
115
+ end
116
+
117
+ def range_to_regex(range)
118
+ range.inject([]) do |a, c|
119
+ start_range, end_range = c
120
+ regex = if start_range == end_range
121
+ start_range.to_s
122
+ elsif start_range == 0 && end_range == 255
123
+ '.+'
124
+ else
125
+ RangeRegularExpressionGenerator.new.range_regex(start_range + 1, end_range - 1)
126
+ end
127
+ a.push(regex)
128
+ end.join('\.')
129
+ end
130
+ end
131
+ end
@@ -0,0 +1,37 @@
1
+ module Proxy::DHCP::Infoblox
2
+ class PluginConfiguration
3
+ def load_classes
4
+ require 'infoblox'
5
+ require 'dhcp_common/dhcp_common'
6
+ require 'smart_proxy_dhcp_infoblox/host_ipv4_address_crud'
7
+ require 'smart_proxy_dhcp_infoblox/fixed_address_crud'
8
+ require 'smart_proxy_dhcp_infoblox/grid_restart'
9
+ require 'smart_proxy_dhcp_infoblox/unused_ips'
10
+ require 'smart_proxy_dhcp_infoblox/dhcp_infoblox_main'
11
+ end
12
+
13
+ def load_dependency_injection_wirings(c, settings)
14
+
15
+
16
+ c.dependency :connection, (lambda do
17
+ ::Infoblox.wapi_version = '2.0'
18
+ ::Infoblox::Connection.new(:username => settings[:username] ,:password => settings[:password],
19
+ :host => settings[:server], :ssl_opts => {:verify => false})
20
+ end)
21
+
22
+
23
+ c.dependency :unused_ips, lambda { ::Proxy::DHCP::Infoblox::UnusedIps.new(c.get_dependency(:connection), settings[:use_ranges])}
24
+ c.dependency :host_ipv4_crud, lambda { ::Proxy::DHCP::Infoblox::HostIpv4AddressCRUD.new(c.get_dependency(:connection))}
25
+ c.dependency :fixed_address_crud, lambda { ::Proxy::DHCP::Infoblox::FixedAddressCRUD.new(c.get_dependency(:connection))}
26
+ c.dependency :grid_restart, lambda { ::Proxy::DHCP::Infoblox::GridRestart.new(c.get_dependency(:connection))}
27
+ c.dependency :dhcp_provider, (lambda do
28
+ ::Proxy::DHCP::Infoblox::Provider.new(
29
+ c.get_dependency(:connection),
30
+ settings[:record_type] == 'host' ? c.get_dependency(:host_ipv4_crud) : c.get_dependency(:fixed_address_crud),
31
+ c.get_dependency(:grid_restart),
32
+ c.get_dependency(:unused_ips),
33
+ settings[:subnets])
34
+ end)
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,8 @@
1
+ module ::Proxy::DHCP::Infoblox
2
+ class RecordTypeValidator < ::Proxy::PluginValidators::Base
3
+ def validate!(settings)
4
+ return true if ['host', 'fixedaddress'].include?(settings[:record_type])
5
+ raise ::Proxy::Error::ConfigurationError, "Setting 'record_type' can be set to either 'host' or 'fixedaddress'"
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,47 @@
1
+ require 'ipaddr'
2
+ require 'smart_proxy_dhcp_infoblox/ip_address_arithmetic'
3
+
4
+ module Proxy::DHCP::Infoblox
5
+ class UnusedIps
6
+ include IpAddressArithmetic
7
+ attr_reader :connection, :use_ranges
8
+
9
+ def initialize(connection, use_ranges)
10
+ @connection = connection
11
+ @use_ranges = use_ranges
12
+ end
13
+
14
+ def unused_ip(network_address, from_ip_address, to_ip_address)
15
+ @use_ranges ? unused_range_ip(network_address, from_ip_address, to_ip_address) : unused_network_ip(network_address, from_ip_address, to_ip_address)
16
+ end
17
+
18
+ def unused_network_ip(network_address, from_ip_address, to_ip_address)
19
+ find_network(network_address).next_available_ip(1, excluded_ips(find_network(network_address).network, from_ip_address, to_ip_address)).first
20
+ end
21
+
22
+ def unused_range_ip(network_address, from_ip_address, to_ip_address)
23
+ find_range(network_address, from_ip_address, to_ip_address).next_available_ip(1).first
24
+ end
25
+
26
+ def excluded_ips(subnet_address, from, to)
27
+ return [] if from.nil? || to.nil?
28
+ (IPAddr.new(network_cidr_to_range(subnet_address).first)..IPAddr.new(network_cidr_to_range(subnet_address).last)).to_a.map(&:to_s) -
29
+ (IPAddr.new(from)..IPAddr.new(to)).to_a.map(&:to_s)
30
+ end
31
+
32
+ def find_range(network_address, from, to)
33
+ ranges = ::Infoblox::Range.find(@connection, 'network~' => network_address)
34
+ range = (from.nil? || to.nil?) ? ranges.first : ranges.find {|r| r.start_addr == from && r.end_addr == to}
35
+ raise "No Ranges found for #{network_address} network" if range.nil?
36
+ range
37
+ end
38
+
39
+ def find_network(network_address)
40
+ return @memoized_network if !@memoized_network.nil? && @memoized_address == network_address
41
+ @memoized_address = network_address
42
+ @memoized_network = ::Infoblox::Network.find(@connection, 'network~' => network_address, '_max_results' => 1).first
43
+ raise "Subnet #{network_address} not found" if @memoized_network.nil?
44
+ @memoized_network
45
+ end
46
+ end
47
+ end