Skip to content

Commit b24cf94

Browse files
committed
introduce new prefix column in ip addresses table
1 parent 1e123e3 commit b24cf94

20 files changed

+375
-148
lines changed

src/bosh-director/lib/bosh/director/deployment_plan/dynamic_network.rb

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,7 +94,7 @@ def initialize(name, subnets, logger)
9494
def network_settings(reservation, default_properties = Network::REQUIRED_DEFAULTS, availability_zone = nil)
9595
unless reservation.dynamic?
9696
raise NetworkReservationWrongType,
97-
"IP '#{format_ip(reservation.ip)}' on network '#{reservation.network.name}' does not belong to dynamic pool"
97+
"IP '#{reservation.ip}' on network '#{reservation.network.name}' does not belong to dynamic pool"
9898
end
9999

100100
if availability_zone.nil?

src/bosh-director/lib/bosh/director/deployment_plan/instance_group_networks_parser.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ def parse_networks(instance_group_spec, instance_group_name, manifest_networks)
3535
default_for = safe_property(network_spec, 'default', class: Array, default: [])
3636
static_ips = parse_static_ips(network_spec['static_ips'], instance_group_name)
3737

38+
puts "static_ips"
39+
puts static_ips
40+
3841
deployment_network = look_up_deployment_network(manifest_networks, instance_group_name, network_name)
3942
deployment_network.validate_reference_from_job!(network_spec, instance_group_name)
4043

src/bosh-director/lib/bosh/director/deployment_plan/ip_provider/ip_provider.rb

Lines changed: 6 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -65,15 +65,8 @@ def reserve_manual(reservation)
6565
@logger.debug("Allocating dynamic ip for manual network '#{reservation.network.name}'")
6666

6767
filter_subnet_by_instance_az(reservation).each do |subnet|
68-
ip = if reservation.is_prefix_reservation
69-
@ip_repo.allocate_dynamic_prefix(reservation, subnet)
70-
else
71-
@ip_repo.allocate_dynamic_ip(reservation, subnet)
72-
end
73-
74-
@logger.debug("Reserving dynamic IP/prefix '#{ip}' for manual network '#{reservation.network.name}'")
75-
76-
if ip
68+
if (ip = @ip_repo.allocate_dynamic_ip(reservation, subnet))
69+
@logger.debug("Reserving dynamic IP '#{ip}' for manual network '#{reservation.network.name}'")
7770
reservation.resolve_ip(ip)
7871
reservation.resolve_type(:dynamic)
7972
break
@@ -82,13 +75,13 @@ def reserve_manual(reservation)
8275

8376
if reservation.ip.nil?
8477
raise NetworkReservationNotEnoughCapacity,
85-
"Failed to reserve IP/Prefix for '#{reservation.instance_model}' for manual network '#{reservation.network.name}': no more available"
78+
"Failed to reserve IP for '#{reservation.instance_model}' for manual network '#{reservation.network.name}': no more available"
8679
end
8780
else
8881
@logger.debug("Reserving #{reservation.desc} for manual network '#{reservation.network.name}'")
8982

9083
if (subnet = reservation.network.find_subnet_containing(reservation.ip))
91-
if subnet.restricted_ips.include?(reservation.ip)
84+
if subnet.restricted_ips.include?(reservation.ip.to_i)
9285
message = "Failed to reserve IP '#{format_ip(reservation.ip)}' for network '#{subnet.network_name}': IP belongs to reserved range"
9386
@logger.error(message)
9487
raise Bosh::Director::NetworkReservationIpReserved, message
@@ -106,7 +99,8 @@ def reserve_manual_with_subnet(reservation, subnet)
10699
@ip_repo.add(reservation)
107100

108101
subnet_az_names = subnet.availability_zone_names.to_a.join(', ')
109-
if subnet.static_ips.include?(reservation.ip)
102+
103+
if subnet.static_ips.include?(reservation.ip.to_i)
110104
reservation.resolve_type(:static)
111105
@logger.debug("Found subnet with azs '#{subnet_az_names}' for #{format_ip(reservation.ip)}. Reserved as static network reservation.")
112106
else

src/bosh-director/lib/bosh/director/deployment_plan/ip_provider/ip_repo.rb

Lines changed: 89 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ class IpRepo
33
include Bosh::Director::IpUtil
44
class IpFoundInDatabaseAndCanBeRetried < StandardError; end
55
class NoMoreIPsAvailableAndStopRetrying < StandardError; end
6+
class PrefixOutOfRange < StandardError; end
67

78
def initialize(logger)
89
@logger = Bosh::Director::TaggedLogger.new(logger, 'network-configuration')
@@ -11,7 +12,7 @@ def initialize(logger)
1112
def delete(ip)
1213
ip_or_cidr = Bosh::Director::IpAddrOrCidr.new(ip)
1314

14-
ip_address = Bosh::Director::Models::IpAddress.first(address_str: ip_or_cidr.to_i.to_s)
15+
ip_address = Bosh::Director::Models::IpAddress.first(address_str: ip_or_cidr.to_cidr_s)
1516

1617
if ip_address
1718
@logger.debug("Releasing ip '#{ip_or_cidr}'")
@@ -22,18 +23,19 @@ def delete(ip)
2223
end
2324

2425
def add(reservation)
25-
ip_or_cidr = Bosh::Director::IpAddrOrCidr.new(reservation.ip)
26+
ip_or_cidr = Bosh::Director::IpAddrOrCidr.new(reservation.ip.to_cidr_s)
2627

2728
reservation_type = reservation.network.ip_type(ip_or_cidr)
2829

2930
reserve_with_instance_validation(
3031
reservation.instance_model,
31-
ip_or_cidr,
32+
ip_or_cidr.to_cidr_s,
3233
reservation,
3334
reservation_type.eql?(:static),
3435
)
3536

3637
reservation.resolve_type(reservation_type)
38+
3739
@logger.debug("Reserved ip '#{ip_or_cidr}' for #{reservation.network.name} as #{reservation_type}")
3840
end
3941

@@ -51,24 +53,7 @@ def allocate_dynamic_ip(reservation, subnet)
5153
end
5254

5355
@logger.debug("Allocated dynamic IP '#{ip_address}' for #{reservation.network.name}")
54-
ip_address.to_i
55-
end
56-
57-
def allocate_dynamic_prefix(reservation, subnet)
58-
begin
59-
cidr = try_to_allocate_dynamic_prefix(reservation, subnet)
60-
rescue NoMoreIPsAvailableAndStopRetrying
61-
@logger.debug('Failed to allocate dynamic prefix: no more available')
62-
return nil
63-
rescue IpFoundInDatabaseAndCanBeRetried
64-
@logger.debug('Retrying to allocate dynamic prefix: probably a race condition with another deployment')
65-
# IP can be taken by other deployment that runs in parallel
66-
# retry until succeeds or out of range
67-
retry
68-
end
69-
70-
@logger.debug("Allocated dynamic cidr '#{cidr}' for #{reservation.network.name}")
71-
cidr.to_i
56+
ip_address
7257
end
7358

7459
def allocate_vip_ip(reservation, subnet)
@@ -91,90 +76,120 @@ def allocate_vip_ip(reservation, subnet)
9176

9277
private
9378

79+
def all_ip_addresses
80+
Bosh::Director::Models::IpAddress.select(:address_str).all.map { |a| a.address }
81+
end
82+
83+
def get_prefix(ip)
84+
if Bosh::Director::IpAddrOrCidr.new(ip).ipv6?
85+
128
86+
else
87+
32
88+
end
89+
end
90+
9491
def try_to_allocate_dynamic_ip(reservation, subnet)
9592
addresses_in_use = Set.new(all_ip_addresses)
9693

97-
first_range_address = subnet.range.to_range.first.to_i - 1
94+
95+
first_range_address = Bosh::Director::IpAddrOrCidr.new(subnet.range.to_range.first.to_i - 1)
96+
9897
addresses_we_cant_allocate = addresses_in_use
99-
addresses_we_cant_allocate << first_range_address
10098

101-
addresses_we_cant_allocate.merge(subnet.restricted_ips.to_a) unless subnet.restricted_ips.empty?
102-
addresses_we_cant_allocate.merge(subnet.static_ips.to_a) unless subnet.static_ips.empty?
103-
addr = find_first_available_address(addresses_we_cant_allocate, first_range_address)
104-
ip_address = Bosh::Director::IpAddrOrCidr.new(addr)
99+
addresses_we_cant_allocate.merge(subnet.restricted_ips.map { |int_ip| Bosh::Director::IpAddrOrCidr.new(int_ip)}) unless subnet.restricted_ips.empty?
100+
addresses_we_cant_allocate.merge(subnet.static_ips.map { |int_ip| Bosh::Director::IpAddrOrCidr.new(int_ip)}) unless subnet.static_ips.empty?
101+
102+
if subnet.range.ipv6?
103+
addresses_we_cant_allocate.delete_if { |ipaddr| ipaddr.ipv4? }
104+
if subnet.prefix.nil?
105+
prefix = 128
106+
else
107+
prefix = subnet.prefix
108+
end
109+
else
110+
addresses_we_cant_allocate.delete_if { |ipaddr| ipaddr.ipv6? }
111+
if subnet.prefix.nil?
112+
prefix = 32
113+
else
114+
prefix = subnet.prefix
115+
end
116+
end
117+
118+
ip_address = find_next_available_ip(addresses_we_cant_allocate, first_range_address, prefix)
105119

106120
unless subnet.range == ip_address || subnet.range.include?(ip_address)
107121
raise NoMoreIPsAvailableAndStopRetrying
108122
end
109123

110-
save_ip(ip_address, reservation, false)
124+
unless prefix
125+
if ip_address.ipv6?
126+
host_bits = 128 - prefix
127+
else
128+
host_bits = 32 - prefix
129+
end
130+
no_of_addresses = 2**host_bits
131+
if subnet.range.last < (ip_address.to_i + no_of_addresses)
132+
raise NoMoreIPsAvailableAndStopRetrying
133+
end
134+
end
135+
136+
save_ip(ip_address.to_cidr_s, reservation, false)
111137

112138
ip_address
113139
end
114140

115-
def try_to_allocate_dynamic_prefix(reservation, subnet)
116-
@logger.debug("Allocating dynamic prefix for #{reservation.network.name}")
117-
addresses_in_use = Set.new(all_ip_addresses)
118-
119-
first_range_address = subnet.range.to_range.first.to_i - 1
120-
addresses_we_cant_allocate = addresses_in_use
121-
addresses_we_cant_allocate << first_range_address
141+
def find_next_available_ip(ip_entries, first_range_address, prefix)
142+
filtered_ips = ip_entries.sort_by { |ip| ip.to_i }.reject { |ip| ip.to_i < first_range_address.to_i } #remove ips that are below subnet range
122143

123-
addresses_we_cant_allocate.merge(subnet.restricted_ips.to_a) unless subnet.restricted_ips.empty?
124-
addresses_we_cant_allocate.merge(subnet.static_ips.to_a) unless subnet.static_ips.empty?
144+
if prefix == 32 || prefix == 128
145+
available_ip = filtered_ips.find { |ip| !filtered_ips.include?(Bosh::Director::IpAddrOrCidr.new(ip.to_i + 1)) }.to_i + 1
125146

126-
addr = Bosh::Director::IpAddrOrCidr.new(find_first_available_address(addresses_we_cant_allocate, first_range_address)).to_s
127-
cidr_string = get_first_available_prefix(addr, subnet.prefix)
128-
129-
@logger.debug("FIRST AVAILABLE PREFIX '#{cidr_string}' for #{reservation.network.name}")
130-
131-
cidr = Bosh::Director::IpAddrOrCidr.new(cidr_string)
132-
133-
unless subnet.range == cidr || subnet.range.include?(cidr)
134-
raise NoMoreIPsAvailableAndStopRetrying
147+
found_cidr = Bosh::Director::IpAddrOrCidr.new("#{Bosh::Director::IpAddrOrCidr.new(available_ip)}/#{prefix}")
148+
else
149+
current_ip = Bosh::Director::IpAddrOrCidr.new(first_range_address.to_i + 1)
150+
found = false
151+
152+
while found == false
153+
current_prefix = Bosh::Director::IpAddrOrCidr.new("#{current_ip}/#{prefix}")
154+
155+
if filtered_ips.any? { |ip| current_prefix.include?(ip) }
156+
current_ip = Bosh::Director::IpAddrOrCidr.new(current_ip.to_i + current_prefix.count)
157+
filtered_ips.reject{ |ip| ip.to_i < current_ip.to_i }
158+
else
159+
found_cidr = current_prefix
160+
found = true
161+
end
162+
end
135163
end
136164

137-
save_ip(cidr, reservation, false)
138-
139-
cidr
140-
end
141-
142-
def find_first_available_address(addresses_we_cant_allocate, first_address)
143-
last_address_we_cant_use = addresses_we_cant_allocate
144-
.to_a
145-
.reject { |a| a < first_address }
146-
.sort
147-
.find { |a| !addresses_we_cant_allocate.include?(a + 1) }
148-
last_address_we_cant_use + 1
149-
end
150-
151-
def get_first_available_prefix(first_available_addr, prefix)
152-
"#{first_available_addr}/#{prefix}"
165+
found_cidr
153166
end
154167

155168
def try_to_allocate_vip_ip(reservation, subnet)
156-
addresses_in_use = Set.new(all_ip_addresses)
169+
addresses_in_use = Set.new(all_ip_addresses.map { |ip| ip.to_i })
170+
171+
if Bosh::Director::IpAddrOrCidr.new(subnet.static_ips.first.to_i).ipv6?
172+
prefix = 128
173+
else
174+
prefix = 32
175+
end
157176

158177
available_ips = subnet.static_ips - addresses_in_use
159178

160179
raise NoMoreIPsAvailableAndStopRetrying if available_ips.empty?
161180

162-
ip_address = Bosh::Director::IpAddrOrCidr.new(available_ips.first)
181+
ip_address = Bosh::Director::IpAddrOrCidr.new("#{Bosh::Director::IpAddrOrCidr.new(available_ips.first)}/#{prefix}")
163182

164-
save_ip(ip_address, reservation, false)
183+
save_ip(ip_address.to_cidr_s, reservation, false)
165184

166185
ip_address
167186
end
168187

169-
def all_ip_addresses
170-
Bosh::Director::Models::IpAddress.select(:address_str).all.map { |a| a.address_str.to_i }
171-
end
172-
173188
def reserve_with_instance_validation(instance_model, ip, reservation, is_static)
174189
# try to save IP first before validating its instance to prevent race conditions
175190
save_ip(ip, reservation, is_static)
176191
rescue IpFoundInDatabaseAndCanBeRetried
177-
ip_address = Bosh::Director::Models::IpAddress.first(address_str: ip.to_i.to_s)
192+
ip_address = Bosh::Director::Models::IpAddress.first(address_str: ip.to_s)
178193

179194
retry unless ip_address
180195

@@ -204,15 +219,17 @@ def validate_instance_and_update_reservation_type(instance_model, ip, ip_address
204219
end
205220

206221
def save_ip(ip, reservation, is_static)
222+
@logger.debug("Adding IP Address: #{ip} from reservation: #{reservation}")
207223
ip_address = Bosh::Director::Models::IpAddress.new(
208-
address_str: ip.to_i.to_s,
224+
address_str: ip.to_s,
209225
network_name: reservation.network.name,
210226
task_id: Bosh::Director::Config.current_job.task_id,
211227
static: is_static,
212-
)
228+
)
213229
reservation.instance_model.add_ip_address(ip_address)
214230
rescue Sequel::ValidationFailed, Sequel::DatabaseError => e
215231
error_message = e.message.downcase
232+
@logger.debug("ERROR!!! #{error_message}")
216233
if error_message.include?('unique') || error_message.include?('duplicate')
217234
raise IpFoundInDatabaseAndCanBeRetried, e.inspect
218235
else

src/bosh-director/lib/bosh/director/deployment_plan/job_network.rb

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,25 @@
11
module Bosh::Director
22
module DeploymentPlan
33
class JobNetwork
4-
attr_reader :name, :static_ips, :deployment_network
4+
attr_reader :name, :static_ips, :deployment_network, :prefix
55

66
def initialize(name, static_ips, default_for, deployment_network)
77
@name = name
88
@static_ips = static_ips
99
@default_for = default_for
1010
@deployment_network = deployment_network
11+
subnet = deployment_network.find_subnet_containing(static_ips.first)
12+
13+
if subnet.nil?
14+
if Bosh::Director::IpAddrOrCidr.new(static_ips.first).ipv6?
15+
@prefix = 128
16+
else
17+
@prefix = 32
18+
end
19+
else
20+
@prefix = subnet.prefix
21+
end
22+
1123
end
1224

1325
def availability_zones

src/bosh-director/lib/bosh/director/deployment_plan/manual_network.rb

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,9 +52,14 @@ def network_settings(reservation, default_properties = REQUIRED_DEFAULTS, availa
5252
raise NetworkReservationInvalidIp, "Provided IP '#{ip_or_cidr}' does not belong to any subnet"
5353
end
5454

55+
unless subnet.prefix == ip_or_cidr.prefix
56+
raise NetworkReservationInvalidPRefix, "Subnet Prefix #{subnet.prefix} and ip reservation prefix #{ip_or_cidr.prefix} do not match"
57+
end
58+
5559
config = {
5660
"type" => "manual",
5761
"ip" => ip_or_cidr.to_s,
62+
"prefix" => subnet.prefix.to_s,
5863
"netmask" => subnet.netmask,
5964
"cloud_properties" => subnet.cloud_properties
6065
}

src/bosh-director/lib/bosh/director/deployment_plan/manual_network_subnet.rb

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,8 +77,14 @@ def self.parse(network_name, subnet_spec, availability_zones, managed = false)
7777
static_ips.add(ip)
7878
end
7979

80-
unless prefix.nil?
81-
if range.prefix > prefix
80+
if prefix.nil?
81+
if range.ipv6?
82+
prefix = 128
83+
else
84+
prefix = 32
85+
end
86+
else
87+
if range.prefix > prefix.to_i
8288
raise NetworkPrefixSizeTooBig, "Prefix size '#{prefix}' is larger than range prefix '#{range.prefix}'"
8389
end
8490
end
@@ -106,7 +112,7 @@ def self.parse(network_name, subnet_spec, availability_zones, managed = false)
106112
)
107113
end
108114

109-
def initialize(network_name, range, gateway, name_servers, cloud_properties, netmask, availability_zone_names, restricted_ips, static_ips, subnet_name = nil, netmask_bits = nil, prefix)
115+
def initialize(network_name, range, gateway, name_servers, cloud_properties, netmask, availability_zone_names, restricted_ips, static_ips, subnet_name = nil, netmask_bits = nil, prefix = nil)
110116
@network_name = network_name
111117
@name = subnet_name
112118
@netmask_bits = netmask_bits

0 commit comments

Comments
 (0)