@@ -37,11 +37,12 @@ import (
3737)
3838
3939const (
40- OvnNorthdServiceName = "ovn-northd"
41- OvnNorthdPid = "/var/run/ovn/ovn-northd.pid"
42- DefaultProbeInterval = 5
43- MaxFailCount = 3
44- northdDialTimeout = 3 * time .Second
40+ OvnNorthdServiceName = "ovn-northd"
41+ OvnNorthdPid = "/var/run/ovn/ovn-northd.pid"
42+ DefaultProbeInterval = 5
43+ MaxFailCount = 3
44+ maxDuplicateLeaderObservations = 3
45+ northdDialTimeout = 3 * time .Second
4546)
4647
4748var failCount int
@@ -50,14 +51,29 @@ var labelSelector = labels.Set{discoveryv1.LabelServiceName: OvnNorthdServiceNam
5051
5152// Configuration is the controller conf
5253type Configuration struct {
53- KubeConfigFile string
54- KubeClient kubernetes.Interface
55- ProbeInterval int
56- EnableCompact bool
57- IsICDBServer bool
58- localAddress string
59- remoteAddresses []string
60- singleReplica bool
54+ KubeConfigFile string
55+ KubeClient kubernetes.Interface
56+ ProbeInterval int
57+ EnableCompact bool
58+ IsICDBServer bool
59+ localAddress string
60+ remoteAddresses []string
61+ singleReplica bool
62+ duplicateLeaderObservations map [string ]int
63+ }
64+
65+ func (c * Configuration ) observeDuplicateLeader (database string , duplicate bool ) (int , bool ) {
66+ if ! duplicate {
67+ delete (c .duplicateLeaderObservations , database )
68+ return 0 , false
69+ }
70+ if c .duplicateLeaderObservations == nil {
71+ c .duplicateLeaderObservations = make (map [string ]int )
72+ }
73+
74+ c .duplicateLeaderObservations [database ]++
75+ count := c .duplicateLeaderObservations [database ]
76+ return count , count >= maxDuplicateLeaderObservations
6177}
6278
6379// ParseFlags parses cmd args then init kubeclient and conf
@@ -198,8 +214,8 @@ func checkOvnIsAlive() bool {
198214 return true
199215}
200216
201- // isDBLeader checks whether the ovn db at address is leader for the given database
202- func isDBLeader (address , database string ) bool {
217+ // isDBLeader checks whether the ovn db at address is leader for the given database.
218+ func isDBLeader (address , database string ) ( bool , error ) {
203219 var dbAddr string
204220 switch database {
205221 case ovnnb .DatabaseName :
@@ -211,8 +227,7 @@ func isDBLeader(address, database string) bool {
211227 case util .DatabaseICSB :
212228 dbAddr = ovs .OvsdbServerAddress (address , intstr .FromInt32 (util .ICSBDatabasePort ))
213229 default :
214- klog .Errorf ("isDBLeader: unsupported database %s" , database )
215- return false
230+ return false , fmt .Errorf ("unsupported database %s" , database )
216231 }
217232
218233 result , err := ovs .Query (dbAddr , serverdb .DatabaseName , 1 , ovsdb.Operation {
@@ -226,29 +241,24 @@ func isDBLeader(address, database string) bool {
226241 Columns : []string {"leader" },
227242 })
228243 if err != nil {
229- klog .Errorf ("failed to query leader info from ovsdb-server %s for database %s: %v" , address , database , err )
230- return false
244+ return false , fmt .Errorf ("failed to query leader info from ovsdb-server %s for database %s: %w" , address , database , err )
231245 }
232246 if len (result ) != 1 {
233- klog .Errorf ("unexpected number of results when querying leader info from ovsdb-server %s for database %s: %d" , address , database , len (result ))
234- return false
247+ return false , fmt .Errorf ("unexpected number of results when querying leader info from ovsdb-server %s for database %s: %d" , address , database , len (result ))
235248 }
236249 if len (result [0 ].Rows ) == 0 {
237- klog .Errorf ("no rows returned when querying leader info from ovsdb-server %s for database %s" , address , database )
238- return false
250+ return false , fmt .Errorf ("no rows returned when querying leader info from ovsdb-server %s for database %s" , address , database )
239251 }
240252 if len (result [0 ].Rows ) != 1 {
241- klog .Errorf ("unexpected number of rows when querying leader info from ovsdb-server %s for database %s: %d" , address , database , len (result [0 ].Rows ))
242- return false
253+ return false , fmt .Errorf ("unexpected number of rows when querying leader info from ovsdb-server %s for database %s: %d" , address , database , len (result [0 ].Rows ))
243254 }
244255
245256 leader , ok := result [0 ].Rows [0 ]["leader" ].(bool )
246257 if ! ok {
247- klog .Errorf ("unexpected data format for leader info from ovsdb-server %s for database %s: %v" , address , database , result [0 ].Rows [0 ]["leader" ])
248- return false
258+ return false , fmt .Errorf ("unexpected data format for leader info from ovsdb-server %s for database %s: %v" , address , database , result [0 ].Rows [0 ]["leader" ])
249259 }
250260
251- return leader
261+ return leader , nil
252262}
253263
254264func checkNorthdActive () bool {
@@ -466,6 +476,103 @@ func validateRaftHeader(data map[string]any, dbCID string) error {
466476 return nil
467477}
468478
479+ type dbLeaderQueryFunc func (address , database string ) (bool , error )
480+
481+ func checkDuplicateDBLeader (
482+ cfg * Configuration ,
483+ localLeader bool ,
484+ localQueryErr error ,
485+ component , database string ,
486+ queryLeader dbLeaderQueryFunc ,
487+ ) {
488+ if localQueryErr != nil {
489+ klog .Error (localQueryErr )
490+ return
491+ }
492+ if ! localLeader {
493+ cfg .observeDuplicateLeader (database , false )
494+ return
495+ }
496+
497+ var remoteLeader string
498+ remoteQueryFailed := false
499+ for addr := range slices .Values (cfg .remoteAddresses ) {
500+ leader , err := queryLeader (addr , database )
501+ if err != nil {
502+ klog .Error (err )
503+ remoteQueryFailed = true
504+ continue
505+ }
506+ if leader {
507+ remoteLeader = addr
508+ break
509+ }
510+ }
511+
512+ if remoteLeader == "" {
513+ if ! remoteQueryFailed {
514+ cfg .observeDuplicateLeader (database , false )
515+ }
516+ return
517+ }
518+
519+ count , confirmed := cfg .observeDuplicateLeader (database , true )
520+ if ! confirmed {
521+ klog .Warningf ("found another %s leader at %s (%d/%d consecutive observations), waiting for raft convergence" ,
522+ component , remoteLeader , count , maxDuplicateLeaderObservations )
523+ return
524+ }
525+
526+ klog .Fatalf ("found another %s leader at %s for %d consecutive observations, exiting process to restart" ,
527+ component , remoteLeader , count )
528+ }
529+
530+ func doICDBLeaderCheck (cfg * Configuration , podName , podNamespace string ) {
531+ icNbLeader , icNbLeaderErr := isDBLeader (cfg .localAddress , util .DatabaseICNB )
532+ icSbLeader , icSbLeaderErr := isDBLeader (cfg .localAddress , util .DatabaseICSB )
533+ if icNbLeaderErr != nil {
534+ klog .Error (icNbLeaderErr )
535+ }
536+ if icSbLeaderErr != nil {
537+ klog .Error (icSbLeaderErr )
538+ }
539+
540+ patch := util.KVPatch {
541+ "ovn-ic-nb-leader" : strconv .FormatBool (icNbLeader ),
542+ "ovn-ic-sb-leader" : strconv .FormatBool (icSbLeader ),
543+ }
544+ if err := util .PatchLabels (cfg .KubeClient .CoreV1 ().Pods (podNamespace ), podName , patch ); err != nil {
545+ klog .Errorf ("failed to patch labels for pod %s/%s: %v" , podNamespace , podName , err )
546+ return
547+ }
548+
549+ if icNbLeader {
550+ if err := updateTS (); err != nil {
551+ klog .Errorf ("update ts num failed err: %v" , err )
552+ return
553+ }
554+ }
555+
556+ for addr := range slices .Values (cfg .remoteAddresses ) {
557+ if icNbLeader {
558+ remoteLeader , err := isDBLeader (addr , util .DatabaseICNB )
559+ if err != nil {
560+ klog .Error (err )
561+ } else if remoteLeader {
562+ klog .Fatalf ("found another ovn-ic-nb leader at %s, exiting process to restart" , addr )
563+ }
564+ }
565+ if icSbLeader {
566+ remoteLeader , err := isDBLeader (addr , util .DatabaseICSB )
567+ if err != nil {
568+ klog .Error (err )
569+ } else if remoteLeader {
570+ klog .Fatalf ("found another ovn-ic-sb leader at %s, exiting process to restart" , addr )
571+ }
572+ }
573+ }
574+ }
575+
469576func doOvnLeaderCheck (cfg * Configuration , podName , podNamespace string ) {
470577 if podName == "" || podNamespace == "" {
471578 util .LogFatalAndExit (nil , "env variables POD_NAME and POD_NAMESPACE must be set" )
@@ -510,8 +617,11 @@ func doOvnLeaderCheck(cfg *Configuration, podName, podNamespace string) {
510617 return
511618 }
512619
513- nbLeader := isDBLeader (cfg .localAddress , ovnnb .DatabaseName )
514- sbLeader := isDBLeader (cfg .localAddress , ovnsb .DatabaseName )
620+ nbLeader , nbLeaderErr := isDBLeader (cfg .localAddress , ovnnb .DatabaseName )
621+ sbLeader , sbLeaderErr := isDBLeader (cfg .localAddress , ovnsb .DatabaseName )
622+ checkDuplicateDBLeader (cfg , nbLeader , nbLeaderErr , "ovn-nb" , ovnnb .DatabaseName , isDBLeader )
623+ checkDuplicateDBLeader (cfg , sbLeader , sbLeaderErr , "ovn-sb" , ovnsb .DatabaseName , isDBLeader )
624+
515625 northdActive := checkNorthdActive ()
516626 patch := util.KVPatch {
517627 "ovn-nb-leader" : strconv .FormatBool (nbLeader ),
@@ -529,15 +639,6 @@ func doOvnLeaderCheck(cfg *Configuration, podName, podNamespace string) {
529639 }
530640 }
531641
532- for addr := range slices .Values (cfg .remoteAddresses ) {
533- if nbLeader && isDBLeader (addr , ovnnb .DatabaseName ) {
534- klog .Fatalf ("found another ovn-nb leader at %s, exiting process to restart" , addr )
535- }
536- if sbLeader && isDBLeader (addr , ovnsb .DatabaseName ) {
537- klog .Fatalf ("found another ovn-sb leader at %s, exiting process to restart" , addr )
538- }
539- }
540-
541642 if cfg .EnableCompact {
542643 compactOvnDatabase ("nb" )
543644 compactOvnDatabase ("sb" )
@@ -546,32 +647,7 @@ func doOvnLeaderCheck(cfg *Configuration, podName, podNamespace string) {
546647 backupRaftHeader ("nb" )
547648 backupRaftHeader ("sb" )
548649 } else {
549- icNbLeader := isDBLeader (cfg .localAddress , util .DatabaseICNB )
550- icSbLeader := isDBLeader (cfg .localAddress , util .DatabaseICSB )
551- patch := util.KVPatch {
552- "ovn-ic-nb-leader" : strconv .FormatBool (icNbLeader ),
553- "ovn-ic-sb-leader" : strconv .FormatBool (icSbLeader ),
554- }
555- if err := util .PatchLabels (cfg .KubeClient .CoreV1 ().Pods (podNamespace ), podName , patch ); err != nil {
556- klog .Errorf ("failed to patch labels for pod %s/%s: %v" , podNamespace , podName , err )
557- return
558- }
559-
560- if icNbLeader {
561- if err := updateTS (); err != nil {
562- klog .Errorf ("update ts num failed err: %v" , err )
563- return
564- }
565- }
566-
567- for addr := range slices .Values (cfg .remoteAddresses ) {
568- if icNbLeader && isDBLeader (addr , util .DatabaseICNB ) {
569- klog .Fatalf ("found another ovn-ic-nb leader at %s, exiting process to restart" , addr )
570- }
571- if icSbLeader && isDBLeader (addr , util .DatabaseICSB ) {
572- klog .Fatalf ("found another ovn-ic-sb leader at %s, exiting process to restart" , addr )
573- }
574- }
650+ doICDBLeaderCheck (cfg , podName , podNamespace )
575651 }
576652}
577653
0 commit comments