@@ -3,6 +3,7 @@ class IpRepo
3
3
include Bosh ::Director ::IpUtil
4
4
class IpFoundInDatabaseAndCanBeRetried < StandardError ; end
5
5
class NoMoreIPsAvailableAndStopRetrying < StandardError ; end
6
+ class PrefixOutOfRange < StandardError ; end
6
7
7
8
def initialize ( logger )
8
9
@logger = Bosh ::Director ::TaggedLogger . new ( logger , 'network-configuration' )
@@ -11,7 +12,7 @@ def initialize(logger)
11
12
def delete ( ip )
12
13
ip_or_cidr = Bosh ::Director ::IpAddrOrCidr . new ( ip )
13
14
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 )
15
16
16
17
if ip_address
17
18
@logger . debug ( "Releasing ip '#{ ip_or_cidr } '" )
@@ -22,18 +23,19 @@ def delete(ip)
22
23
end
23
24
24
25
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 )
26
27
27
28
reservation_type = reservation . network . ip_type ( ip_or_cidr )
28
29
29
30
reserve_with_instance_validation (
30
31
reservation . instance_model ,
31
- ip_or_cidr ,
32
+ ip_or_cidr . to_cidr_s ,
32
33
reservation ,
33
34
reservation_type . eql? ( :static ) ,
34
35
)
35
36
36
37
reservation . resolve_type ( reservation_type )
38
+
37
39
@logger . debug ( "Reserved ip '#{ ip_or_cidr } ' for #{ reservation . network . name } as #{ reservation_type } " )
38
40
end
39
41
@@ -51,24 +53,7 @@ def allocate_dynamic_ip(reservation, subnet)
51
53
end
52
54
53
55
@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
72
57
end
73
58
74
59
def allocate_vip_ip ( reservation , subnet )
@@ -91,90 +76,120 @@ def allocate_vip_ip(reservation, subnet)
91
76
92
77
private
93
78
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
+
94
91
def try_to_allocate_dynamic_ip ( reservation , subnet )
95
92
addresses_in_use = Set . new ( all_ip_addresses )
96
93
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
+
98
97
addresses_we_cant_allocate = addresses_in_use
99
- addresses_we_cant_allocate << first_range_address
100
98
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 )
105
119
106
120
unless subnet . range == ip_address || subnet . range . include? ( ip_address )
107
121
raise NoMoreIPsAvailableAndStopRetrying
108
122
end
109
123
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 )
111
137
112
138
ip_address
113
139
end
114
140
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
122
143
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
125
146
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
135
163
end
136
164
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
153
166
end
154
167
155
168
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
157
176
158
177
available_ips = subnet . static_ips - addresses_in_use
159
178
160
179
raise NoMoreIPsAvailableAndStopRetrying if available_ips . empty?
161
180
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 } " )
163
182
164
- save_ip ( ip_address , reservation , false )
183
+ save_ip ( ip_address . to_cidr_s , reservation , false )
165
184
166
185
ip_address
167
186
end
168
187
169
- def all_ip_addresses
170
- Bosh ::Director ::Models ::IpAddress . select ( :address_str ) . all . map { |a | a . address_str . to_i }
171
- end
172
-
173
188
def reserve_with_instance_validation ( instance_model , ip , reservation , is_static )
174
189
# try to save IP first before validating its instance to prevent race conditions
175
190
save_ip ( ip , reservation , is_static )
176
191
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 )
178
193
179
194
retry unless ip_address
180
195
@@ -204,15 +219,17 @@ def validate_instance_and_update_reservation_type(instance_model, ip, ip_address
204
219
end
205
220
206
221
def save_ip ( ip , reservation , is_static )
222
+ @logger . debug ( "Adding IP Address: #{ ip } from reservation: #{ reservation } " )
207
223
ip_address = Bosh ::Director ::Models ::IpAddress . new (
208
- address_str : ip . to_i . to_s ,
224
+ address_str : ip . to_s ,
209
225
network_name : reservation . network . name ,
210
226
task_id : Bosh ::Director ::Config . current_job . task_id ,
211
227
static : is_static ,
212
- )
228
+ )
213
229
reservation . instance_model . add_ip_address ( ip_address )
214
230
rescue Sequel ::ValidationFailed , Sequel ::DatabaseError => e
215
231
error_message = e . message . downcase
232
+ @logger . debug ( "ERROR!!! #{ error_message } " )
216
233
if error_message . include? ( 'unique' ) || error_message . include? ( 'duplicate' )
217
234
raise IpFoundInDatabaseAndCanBeRetried , e . inspect
218
235
else
0 commit comments