@@ -22,6 +22,7 @@ import (
22
22
"sync"
23
23
24
24
"github.com/gophercloud/gophercloud"
25
+ "github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/extraroutes"
25
26
"github.com/gophercloud/gophercloud/openstack/networking/v2/extensions/layer3/routers"
26
27
"github.com/gophercloud/gophercloud/openstack/networking/v2/ports"
27
28
@@ -40,21 +41,25 @@ type Routes struct {
40
41
os * OpenStack
41
42
// router's private network IDs
42
43
networkIDs []string
43
- // OpenStack can modify only one route at once
44
+ // whether Neutron supports "extraroute-atomic" extension
45
+ atomicRoutes bool
46
+ // Neutron with no "extraroute-atomic" extension can modify only one route at
47
+ // once
44
48
sync.Mutex
45
49
}
46
50
47
51
var _ cloudprovider.Routes = & Routes {}
48
52
49
53
// NewRoutes creates a new instance of Routes
50
- func NewRoutes (os * OpenStack , network * gophercloud.ServiceClient ) (cloudprovider.Routes , error ) {
54
+ func NewRoutes (os * OpenStack , network * gophercloud.ServiceClient , atomicRoutes bool ) (cloudprovider.Routes , error ) {
51
55
if os .routeOpts .RouterID == "" {
52
56
return nil , errors .ErrNoRouterID
53
57
}
54
58
55
59
return & Routes {
56
- network : network ,
57
- os : os ,
60
+ network : network ,
61
+ os : os ,
62
+ atomicRoutes : atomicRoutes ,
58
63
}, nil
59
64
}
60
65
@@ -90,9 +95,7 @@ func (r *Routes) ListRoutes(ctx context.Context, clusterName string) ([]*cloudpr
90
95
}
91
96
92
97
// detect router's private network ID for further VM ports filtering
93
- r .Lock ()
94
98
r .networkIDs , err = getRouterNetworkIDs (r .network , r .os .routeOpts .RouterID )
95
- r .Unlock ()
96
99
if err != nil {
97
100
return nil , err
98
101
}
@@ -146,10 +149,16 @@ func getAddrByNodeName(name types.NodeName, needIPv6 bool, nodes []*v1.Node) str
146
149
if ip == nil {
147
150
continue
148
151
}
149
- if needIPv6 && ip .To4 () == nil {
152
+ isIPv6 := ip .To4 () == nil
153
+ if needIPv6 {
154
+ if isIPv6 {
155
+ return v .Address
156
+ }
157
+ continue
158
+ }
159
+ if ! isIPv6 {
150
160
return v .Address
151
161
}
152
- return v .Address
153
162
}
154
163
}
155
164
}
@@ -184,6 +193,52 @@ func updateRoutes(network *gophercloud.ServiceClient, router *routers.Router, ne
184
193
return unwinder , nil
185
194
}
186
195
196
+ func addRoute (network * gophercloud.ServiceClient , routerID string , newRoute []routers.Route ) (func (), error ) {
197
+ mc := metrics .NewMetricContext ("router" , "update" )
198
+ _ , err := extraroutes .Add (network , routerID , extraroutes.Opts {
199
+ Routes : & newRoute ,
200
+ }).Extract ()
201
+ if mc .ObserveRequest (err ) != nil {
202
+ return nil , err
203
+ }
204
+
205
+ unwinder := func () {
206
+ klog .V (4 ).Infof ("Reverting routes change to router %v" , routerID )
207
+ mc := metrics .NewMetricContext ("router" , "update" )
208
+ _ , err := extraroutes .Remove (network , routerID , extraroutes.Opts {
209
+ Routes : & newRoute ,
210
+ }).Extract ()
211
+ if mc .ObserveRequest (err ) != nil {
212
+ klog .Warningf ("Unable to reset routes during error unwind: %v" , err )
213
+ }
214
+ }
215
+
216
+ return unwinder , nil
217
+ }
218
+
219
+ func removeRoute (network * gophercloud.ServiceClient , routerID string , oldRoute []routers.Route ) (func (), error ) {
220
+ mc := metrics .NewMetricContext ("router" , "update" )
221
+ _ , err := extraroutes .Remove (network , routerID , extraroutes.Opts {
222
+ Routes : & oldRoute ,
223
+ }).Extract ()
224
+ if mc .ObserveRequest (err ) != nil {
225
+ return nil , err
226
+ }
227
+
228
+ unwinder := func () {
229
+ klog .V (4 ).Infof ("Reverting routes change to router %v" , routerID )
230
+ mc := metrics .NewMetricContext ("router" , "update" )
231
+ _ , err := extraroutes .Add (network , routerID , extraroutes.Opts {
232
+ Routes : & oldRoute ,
233
+ }).Extract ()
234
+ if mc .ObserveRequest (err ) != nil {
235
+ klog .Warningf ("Unable to reset routes during error unwind: %v" , err )
236
+ }
237
+ }
238
+
239
+ return unwinder , nil
240
+ }
241
+
187
242
func updateAllowedAddressPairs (network * gophercloud.ServiceClient , port * ports.Port , newPairs []ports.AddressPair ) (func (), error ) {
188
243
origPairs := port .AllowedAddressPairs // shallow copy
189
244
@@ -211,9 +266,6 @@ func updateAllowedAddressPairs(network *gophercloud.ServiceClient, port *ports.P
211
266
212
267
// CreateRoute creates the described managed route
213
268
func (r * Routes ) CreateRoute (ctx context.Context , clusterName string , nameHint string , route * cloudprovider.Route ) error {
214
- r .Lock ()
215
- defer r .Unlock ()
216
-
217
269
ip , _ , _ := net .ParseCIDR (route .DestinationCIDR )
218
270
isCIDRv6 := ip .To4 () == nil
219
271
@@ -232,31 +284,50 @@ func (r *Routes) CreateRoute(ctx context.Context, clusterName string, nameHint s
232
284
233
285
klog .V (4 ).Infof ("Using nexthop %v for node %v" , addr , route .TargetNode )
234
286
235
- mc := metrics .NewMetricContext ("router" , "get" )
236
- router , err := routers .Get (r .network , r .os .routeOpts .RouterID ).Extract ()
237
- if mc .ObserveRequest (err ) != nil {
238
- return err
239
- }
287
+ if ! r .atomicRoutes {
288
+ // classical logic
289
+ r .Lock ()
290
+ defer r .Unlock ()
240
291
241
- routes := router .Routes
292
+ mc := metrics .NewMetricContext ("router" , "get" )
293
+ router , err := routers .Get (r .network , r .os .routeOpts .RouterID ).Extract ()
294
+ if mc .ObserveRequest (err ) != nil {
295
+ return err
296
+ }
242
297
243
- for _ , item := range routes {
244
- if item .DestinationCIDR == route .DestinationCIDR && item .NextHop == addr {
245
- klog .V (4 ).Infof ("Skipping existing route: %v" , route )
246
- return nil
298
+ routes := router .Routes
299
+
300
+ for _ , item := range routes {
301
+ if item .DestinationCIDR == route .DestinationCIDR && item .NextHop == addr {
302
+ klog .V (4 ).Infof ("Skipping existing route: %v" , route )
303
+ return nil
304
+ }
247
305
}
248
- }
249
306
250
- routes = append (routes , routers.Route {
251
- DestinationCIDR : route .DestinationCIDR ,
252
- NextHop : addr ,
253
- })
307
+ routes = append (routes , routers.Route {
308
+ DestinationCIDR : route .DestinationCIDR ,
309
+ NextHop : addr ,
310
+ })
254
311
255
- unwind , err := updateRoutes (r .network , router , routes )
256
- if err != nil {
257
- return err
312
+ unwind , err := updateRoutes (r .network , router , routes )
313
+ if err != nil {
314
+ return err
315
+ }
316
+
317
+ defer onFailure .call (unwind )
318
+ } else {
319
+ // atomic route update
320
+ route := []routers.Route {{
321
+ DestinationCIDR : route .DestinationCIDR ,
322
+ NextHop : addr ,
323
+ }}
324
+ unwind , err := addRoute (r .network , r .os .routeOpts .RouterID , route )
325
+ if err != nil {
326
+ return err
327
+ }
328
+
329
+ defer onFailure .call (unwind )
258
330
}
259
- defer onFailure .call (unwind )
260
331
261
332
// get the port of addr on target node.
262
333
port , err := getPortByIP (r .network , addr , r .networkIDs )
@@ -291,9 +362,6 @@ func (r *Routes) CreateRoute(ctx context.Context, clusterName string, nameHint s
291
362
292
363
// DeleteRoute deletes the specified managed route
293
364
func (r * Routes ) DeleteRoute (ctx context.Context , clusterName string , route * cloudprovider.Route ) error {
294
- r .Lock ()
295
- defer r .Unlock ()
296
-
297
365
klog .V (4 ).Infof ("DeleteRoute(%v, %v)" , clusterName , route )
298
366
299
367
onFailure := newCaller ()
@@ -314,36 +382,57 @@ func (r *Routes) DeleteRoute(ctx context.Context, clusterName string, route *clo
314
382
}
315
383
}
316
384
317
- mc := metrics .NewMetricContext ("router" , "get" )
318
- router , err := routers .Get (r .network , r .os .routeOpts .RouterID ).Extract ()
319
- if mc .ObserveRequest (err ) != nil {
320
- return err
321
- }
385
+ if ! r .atomicRoutes {
386
+ // classical logic
387
+ r .Lock ()
388
+ defer r .Unlock ()
322
389
323
- routes := router .Routes
324
- index := - 1
325
- for i , item := range routes {
326
- if item .DestinationCIDR == route .DestinationCIDR && (item .NextHop == addr || route .Blackhole && item .NextHop == string (route .TargetNode )) {
327
- index = i
328
- break
390
+ mc := metrics .NewMetricContext ("router" , "get" )
391
+ router , err := routers .Get (r .network , r .os .routeOpts .RouterID ).Extract ()
392
+ if mc .ObserveRequest (err ) != nil {
393
+ return err
329
394
}
330
- }
331
395
332
- if index == - 1 {
333
- klog .V (4 ).Infof ("Skipping non-existent route: %v" , route )
334
- return nil
335
- }
396
+ routes := router .Routes
397
+ index := - 1
398
+ for i , item := range routes {
399
+ if item .DestinationCIDR == route .DestinationCIDR && (item .NextHop == addr || route .Blackhole && item .NextHop == string (route .TargetNode )) {
400
+ index = i
401
+ break
402
+ }
403
+ }
336
404
337
- // Delete element `index`
338
- routes [index ] = routes [len (routes )- 1 ]
339
- routes = routes [:len (routes )- 1 ]
405
+ if index == - 1 {
406
+ klog .V (4 ).Infof ("Skipping non-existent route: %v" , route )
407
+ return nil
408
+ }
340
409
341
- unwind , err := updateRoutes (r .network , router , routes )
342
- // If this was a blackhole route we are done, there are no ports to update
343
- if err != nil || route .Blackhole {
344
- return err
410
+ // Delete element `index`
411
+ routes [index ] = routes [len (routes )- 1 ]
412
+ routes = routes [:len (routes )- 1 ]
413
+
414
+ unwind , err := updateRoutes (r .network , router , routes )
415
+ // If this was a blackhole route we are done, there are no ports to update
416
+ if err != nil || route .Blackhole {
417
+ return err
418
+ }
419
+
420
+ defer onFailure .call (unwind )
421
+ } else {
422
+ // atomic route update
423
+ blackhole := route .Blackhole
424
+ route := []routers.Route {{
425
+ DestinationCIDR : route .DestinationCIDR ,
426
+ NextHop : addr ,
427
+ }}
428
+ unwind , err := removeRoute (r .network , r .os .routeOpts .RouterID , route )
429
+ // If this was a blackhole route we are done, there are no ports to update
430
+ if err != nil || blackhole {
431
+ return err
432
+ }
433
+
434
+ defer onFailure .call (unwind )
345
435
}
346
- defer onFailure .call (unwind )
347
436
348
437
// get the port of addr on target node.
349
438
port , err := getPortByIP (r .network , addr , r .networkIDs )
@@ -352,7 +441,7 @@ func (r *Routes) DeleteRoute(ctx context.Context, clusterName string, route *clo
352
441
}
353
442
354
443
addrPairs := port .AllowedAddressPairs
355
- index = - 1
444
+ index : = - 1
356
445
for i , item := range addrPairs {
357
446
if item .IPAddress == route .DestinationCIDR {
358
447
index = i
0 commit comments