@@ -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 ,
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
0 commit comments