@@ -56,6 +56,7 @@ type Client interface {
56
56
MonitorAll (context.Context ) (MonitorCookie , error )
57
57
MonitorCancel (ctx context.Context , cookie MonitorCookie ) error
58
58
NewTableMonitor (m model.Model , fields ... interface {}) TableMonitor
59
+ CurrentEndpoint () string
59
60
API
60
61
}
61
62
@@ -68,9 +69,10 @@ type MonitorCookie struct {
68
69
69
70
// ovsdbClient is an OVSDB client
70
71
type ovsdbClient struct {
71
- options * options
72
- rpcClient * rpc2.Client
73
- rpcMutex sync.RWMutex
72
+ options * options
73
+ rpcClient * rpc2.Client
74
+ rpcMutex sync.RWMutex
75
+ activeEndpoint string
74
76
75
77
// The name of the "primary" database - that is to say, the DB
76
78
// that the user expects to interact with.
@@ -143,7 +145,16 @@ func newOVSDBClient(databaseModel *model.DBModel, opts ...Option) (*ovsdbClient,
143
145
// The connection can be configured using one or more Option(s), like WithTLSConfig
144
146
// If no WithEndpoint option is supplied, the default of unix:/var/run/openvswitch/ovsdb.sock is used
145
147
func (o * ovsdbClient ) Connect (ctx context.Context ) error {
146
- return o .connect (ctx , false )
148
+ if err := o .connect (ctx , false ); err != nil {
149
+ return err
150
+ }
151
+
152
+ if o .options .leaderOnly {
153
+ if err := o .watchForLeaderChange (); err != nil {
154
+ return err
155
+ }
156
+ }
157
+ return nil
147
158
}
148
159
149
160
func (o * ovsdbClient ) connect (ctx context.Context , reconnect bool ) error {
@@ -165,6 +176,8 @@ func (o *ovsdbClient) connect(ctx context.Context, reconnect bool) error {
165
176
fmt .Errorf ("failed to connect to %s: %w" , endpoint , err ))
166
177
continue
167
178
} else {
179
+ log .Printf ("libovsdb: connected to %s" , endpoint )
180
+ o .activeEndpoint = endpoint
168
181
connected = true
169
182
break
170
183
}
@@ -184,6 +197,7 @@ func (o *ovsdbClient) connect(ctx context.Context, reconnect bool) error {
184
197
185
198
// if we're reconnecting, re-start all the monitors
186
199
if reconnect {
200
+ log .Printf ("libovsdb: reconnected - restarting monitors" )
187
201
for dbName , db := range o .databases {
188
202
db .monitorsMutex .Lock ()
189
203
defer db .monitorsMutex .Unlock ()
@@ -200,10 +214,12 @@ func (o *ovsdbClient) connect(ctx context.Context, reconnect bool) error {
200
214
for _ , db := range o .databases {
201
215
go db .cache .Run (o .stopCh )
202
216
}
217
+
203
218
return nil
204
219
}
205
220
206
221
func (o * ovsdbClient ) tryEndpoint (ctx context.Context , u * url.URL ) error {
222
+ log .Printf ("libovsdb: trying to connect to DB %s" , u )
207
223
var dialer net.Dialer
208
224
var err error
209
225
var c net.Conn
@@ -421,6 +437,15 @@ func (o *ovsdbClient) Connected() bool {
421
437
return o .rpcClient != nil
422
438
}
423
439
440
+ func (o * ovsdbClient ) CurrentEndpoint () string {
441
+ o .rpcMutex .RLock ()
442
+ defer o .rpcMutex .RUnlock ()
443
+ if o .rpcClient == nil {
444
+ return ""
445
+ }
446
+ return o .activeEndpoint
447
+ }
448
+
424
449
// DisconnectNotify returns a channel which will notify the caller when the
425
450
// server has disconnected
426
451
func (o * ovsdbClient ) DisconnectNotify () chan struct {} {
@@ -680,6 +705,48 @@ func (o *ovsdbClient) Echo(ctx context.Context) error {
680
705
return nil
681
706
}
682
707
708
+ // watchForLeaderChange will trigger a reconnect if the connected endpoint
709
+ // ever loses leadership
710
+ func (o * ovsdbClient ) watchForLeaderChange () error {
711
+ updates := make (chan model.Model )
712
+ o .databases [serverDB ].cache .AddEventHandler (& cache.EventHandlerFuncs {
713
+ UpdateFunc : func (table string , _ , new model.Model ) {
714
+ if table == "Database" {
715
+ updates <- new
716
+ }
717
+ },
718
+ })
719
+
720
+ err := o .monitor (context .Background (), newMonitorCookie (serverDB ), false ,
721
+ TableMonitor {
722
+ Table : "Database" ,
723
+ },
724
+ )
725
+ if err != nil {
726
+ return err
727
+ }
728
+
729
+ go func () {
730
+ for m := range updates {
731
+ dbInfo , ok := m .(* serverdb.Database )
732
+ if ! ok {
733
+ continue
734
+ }
735
+
736
+ // Ignore the dbInfo for _Server
737
+ if dbInfo .Name != o .primaryDBName {
738
+ continue
739
+ }
740
+
741
+ if dbInfo .Model == serverdb .DatabaseModelClustered && ! dbInfo .Leader {
742
+ log .Printf ("libovsdb: endpoint %s lost leader, reconnecting" , o .activeEndpoint )
743
+ o .Disconnect ()
744
+ }
745
+ }
746
+ }()
747
+ return nil
748
+ }
749
+
683
750
func (o * ovsdbClient ) handleDisconnectNotification () {
684
751
<- o .rpcClient .DisconnectNotify ()
685
752
// close the stopCh, which will stop the cache event processor
@@ -691,8 +758,13 @@ func (o *ovsdbClient) handleDisconnectNotification() {
691
758
connect := func () error {
692
759
ctx , cancel := context .WithTimeout (context .Background (), o .options .timeout )
693
760
defer cancel ()
694
- return o .connect (ctx , true )
761
+ err := o .connect (ctx , true )
762
+ if err != nil {
763
+ log .Printf ("libovsdb: failed to reconnect: %s" , err )
764
+ }
765
+ return err
695
766
}
767
+ log .Printf ("libovsdb: connection to %s lost, reconnecting..." , o .activeEndpoint )
696
768
err := backoff .Retry (connect , o .options .backoff )
697
769
if err != nil {
698
770
// TODO: We should look at passing this back to the
0 commit comments