Skip to content

Commit 97f4e41

Browse files
Merge pull request #518 from TrekkieCoder/main
PR - BFD implementation for faster failover detection
2 parents c9f3d1d + 90b05d6 commit 97f4e41

File tree

7 files changed

+481
-111
lines changed

7 files changed

+481
-111
lines changed
+53
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
name: Docker-Multi-Arch
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
tagName:
7+
description: 'Tag Name'
8+
required: true
9+
default: 'latest'
10+
11+
jobs:
12+
build:
13+
runs-on: ubuntu-latest
14+
name: build for amd64/arm64
15+
steps:
16+
- uses: actions/checkout@v2
17+
with:
18+
submodules: recursive
19+
20+
- name: Login to GitHub Container Registry
21+
uses: docker/login-action@v1
22+
with:
23+
registry: ghcr.io
24+
username: ${{ github.actor }}
25+
password: ${{ secrets.GITHUB_TOKEN }}
26+
27+
# Setup hardware emulator using QEMU
28+
- name: Set up QEMU
29+
uses: docker/setup-qemu-action@v2
30+
31+
# Setup Docker Buildx for multi-arch images
32+
- name: Set up Docker Buildx
33+
uses: docker/setup-buildx-action@v2
34+
35+
- name: Build Check
36+
if: |
37+
github.repository != 'loxilb-io/loxilb'
38+
uses: docker/build-push-action@v4
39+
with:
40+
context: .
41+
platforms: linux/amd64, linux/arm64
42+
push: false
43+
tags: ghcr.io/loxilb-io/loxilb:${{ github.event.inputs.tagName }}
44+
45+
- name: Build and push
46+
if: |
47+
github.repository == 'loxilb-io/loxilb'
48+
uses: docker/build-push-action@v4
49+
with:
50+
context: .
51+
platforms: linux/amd64, linux/arm64
52+
push: true
53+
tags: ghcr.io/loxilb-io/loxilb:${{ github.event.inputs.tagName }}

loxinet/cluster.go

+30-96
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,13 @@
1717
package loxinet
1818

1919
import (
20+
"errors"
21+
"fmt"
2022
cmn "github.com/loxilb-io/loxilb/common"
2123
opts "github.com/loxilb-io/loxilb/options"
24+
bfd "github.com/loxilb-io/loxilb/proto"
2225
tk "github.com/loxilb-io/loxilib"
23-
24-
"bufio"
25-
"errors"
26-
"fmt"
2726
"net"
28-
"os"
29-
"os/exec"
3027
"time"
3128
)
3229

@@ -60,13 +57,25 @@ type ClusterNode struct {
6057
// CIStateH - Cluster context handler
6158
type CIStateH struct {
6259
SpawnKa bool
63-
kaMode bool
60+
RemoteIP net.IP
6461
ClusterMap map[string]*ClusterInstance
6562
StateMap map[string]int
6663
NodeMap map[string]*ClusterNode
6764
}
6865

69-
func kaSpawn() {
66+
func (ci *CIStateH) BFDSessionNotify(instance string, remote string, ciState string) {
67+
var sm cmn.HASMod
68+
69+
sm.Instance = instance
70+
sm.State = ciState
71+
sm.Vip = net.ParseIP("0.0.0.0")
72+
tk.LogIt(tk.LogInfo, "ci-change instance %s - state %s vip %v\n", instance, ciState, sm.Vip)
73+
mh.mtx.Lock()
74+
defer mh.mtx.Unlock()
75+
ci.CIStateUpdate(sm)
76+
}
77+
78+
func (ci *CIStateH) startBFDProto() {
7079
url := fmt.Sprintf("http://127.0.0.1:%d/config/params", opts.Opts.Port)
7180
for {
7281
if IsLoxiAPIActive(url) {
@@ -76,107 +85,31 @@ func kaSpawn() {
7685
time.Sleep(1 * time.Second)
7786
}
7887

79-
RunCommand("rm -f /etc/shared/keepalive.state", false)
80-
RunCommand("pkill keepalived", false)
8188
mh.dp.WaitXsyncReady("ka")
8289
// We need some cool-off period for loxilb to self sync-up in the cluster
8390
time.Sleep(KAInitTiVal * time.Second)
8491

85-
for {
86-
if exists := FileExists(KAConfigFile); !exists {
87-
time.Sleep(2000 * time.Millisecond)
88-
continue
89-
}
90-
91-
pid := ReadPIDFile(KAPidFile1)
92-
if pid != 0 {
93-
time.Sleep(5000 * time.Millisecond)
94-
continue
95-
}
96-
97-
tk.LogIt(tk.LogInfo, "KA spawning\n")
98-
cmd := exec.Command("/usr/sbin/keepalived", "-f", KAConfigFile, "-n")
99-
err := cmd.Run()
100-
if err != nil {
101-
tk.LogIt(tk.LogError, "Error in running KA:%s\n", err)
102-
} else {
103-
tk.LogIt(tk.LogInfo, "KA found dead. Reaping\n")
104-
}
105-
106-
rmf := fmt.Sprintf("rm -f %s", KAPidFile1)
107-
RunCommand(rmf, false)
108-
rmf = fmt.Sprintf("rm -f %s", KAPidFile2)
109-
RunCommand(rmf, false)
110-
111-
time.Sleep(2000 * time.Millisecond)
112-
}
113-
}
114-
115-
func (h *CIStateH) CISync() {
116-
var sm cmn.HASMod
117-
var ciState int
118-
var ok bool
119-
clusterStateFile := "/etc/shared/keepalive.state"
120-
rf, err := os.Open(clusterStateFile)
121-
if err == nil {
122-
123-
fsc := bufio.NewScanner(rf)
124-
fsc.Split(bufio.ScanLines)
125-
126-
for fsc.Scan() {
127-
var inst string
128-
var state string
129-
var vip string
130-
// Format style -
131-
// INSTANCE default is in BACKUP state
132-
_, err = fmt.Sscanf(fsc.Text(), "INSTANCE %s is in %s state vip %s", &inst, &state, &vip)
133-
if err != nil {
134-
continue
135-
}
136-
137-
if ciState, ok = h.StateMap[state]; !ok {
138-
continue
139-
}
140-
141-
notify := false
142-
143-
if eci, ok := h.ClusterMap[inst]; !ok {
144-
notify = true
145-
} else {
146-
if eci.State != ciState {
147-
notify = true
148-
}
149-
}
150-
151-
if notify {
152-
sm.Instance = inst
153-
sm.State = state
154-
sm.Vip = net.ParseIP(vip)
155-
tk.LogIt(tk.LogInfo, "ci-change instance %s - state %s vip %v\n", inst, state, sm.Vip)
156-
h.CIStateUpdate(sm)
157-
}
158-
}
159-
160-
rf.Close()
92+
bs := bfd.StructNew(3784)
93+
err := bs.BFDAddRemote(ci.RemoteIP.String(), 3784, bfd.BFDMinSysTXIntervalUs, 3, "Default", ci)
94+
if err != nil {
95+
tk.LogIt(tk.LogCritical, "KA - Cant add BFD remote\n")
16196
}
16297
}
16398

16499
// CITicker - Periodic ticker for Cluster module
165100
func (h *CIStateH) CITicker() {
166-
mh.mtx.Lock()
167-
h.CISync()
168-
mh.mtx.Unlock()
101+
// Nothing to do currently
169102
}
170103

171104
// CISpawn - Spawn CI application
172-
func (h *CIStateH) CISpawn() {
173-
if h.SpawnKa {
174-
go kaSpawn()
105+
func (ci *CIStateH) CISpawn() {
106+
if ci.SpawnKa {
107+
go ci.startBFDProto()
175108
}
176109
}
177110

178111
// CIInit - routine to initialize Cluster context
179-
func CIInit(spawnKa bool, kaMode bool) *CIStateH {
112+
func CIInit(spawnKa bool, remoteIP net.IP) *CIStateH {
180113
var nCIh = new(CIStateH)
181114
nCIh.StateMap = make(map[string]int)
182115
nCIh.StateMap["MASTER"] = cmn.CIStateMaster
@@ -185,7 +118,7 @@ func CIInit(spawnKa bool, kaMode bool) *CIStateH {
185118
nCIh.StateMap["STOP"] = cmn.CIStateNotDefined
186119
nCIh.StateMap["NOT_DEFINED"] = cmn.CIStateNotDefined
187120
nCIh.SpawnKa = spawnKa
188-
nCIh.kaMode = kaMode
121+
nCIh.RemoteIP = remoteIP
189122
nCIh.ClusterMap = make(map[string]*ClusterInstance)
190123

191124
if _, ok := nCIh.ClusterMap[cmn.CIDefault]; !ok {
@@ -237,9 +170,9 @@ func (h *CIStateH) CIVipGet(inst string) (net.IP, error) {
237170
return net.IPv4zero, errors.New("not found")
238171
}
239172

240-
// IsCIKAMode - routine to get HA state
173+
// IsCIKAMode - routine to get KA mode
241174
func (h *CIStateH) IsCIKAMode() bool {
242-
return h.kaMode
175+
return false
243176
}
244177

245178
// CIStateUpdate - routine to update cluster state
@@ -274,6 +207,7 @@ func (h *CIStateH) CIStateUpdate(cm cmn.HASMod) (int, error) {
274207
if mh.bgp != nil {
275208
mh.bgp.UpdateCIState(cm.Instance, ci.State, ci.Vip)
276209
}
210+
mh.zr.Rules.RuleVIPSyncToClusterState()
277211
return ci.State, nil
278212
}
279213

loxinet/rules.go

+9
Original file line numberDiff line numberDiff line change
@@ -2582,3 +2582,12 @@ func (R *RuleH) AdvRuleVIPIfL2(IP net.IP) error {
25822582

25832583
return nil
25842584
}
2585+
2586+
func (R *RuleH) RuleVIPSyncToClusterState() {
2587+
for vip := range R.vipMap {
2588+
ip := net.ParseIP(vip)
2589+
if ip != nil {
2590+
R.AdvRuleVIPIfL2(ip)
2591+
}
2592+
}
2593+
}

loxinet/utils.go

+12-13
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424
"encoding/binary"
2525
"errors"
2626
"fmt"
27+
tk "github.com/loxilb-io/loxilib"
2728
"io/ioutil"
2829
"net"
2930
"net/http"
@@ -32,9 +33,6 @@ import (
3233
"strconv"
3334
"syscall"
3435
"time"
35-
36-
opts "github.com/loxilb-io/loxilb/options"
37-
tk "github.com/loxilb-io/loxilib"
3836
)
3937

4038
// IterIntf - interface implementation to iterate various loxinet
@@ -140,18 +138,19 @@ func LogString2Level(logStr string) tk.LogLevelT {
140138
}
141139

142140
// KAString2Mode - Convert ka mode in string opts to spawn/KAMode
143-
func KAString2Mode(kaStr string) (bool, bool) {
141+
func KAString2Mode(kaStr string) (bool, net.IP) {
144142
spawnKa := false
145-
kaMode := false
146-
switch opts.Opts.Ka {
147-
case "in":
148-
spawnKa = true
149-
kaMode = true
150-
case "out":
151-
spawnKa = false
152-
kaMode = true
143+
144+
if kaStr == "none" {
145+
return spawnKa, nil
146+
}
147+
148+
remote := net.ParseIP(kaStr)
149+
if remote == nil {
150+
return spawnKa, remote
153151
}
154-
return spawnKa, kaMode
152+
spawnKa = true
153+
return spawnKa, remote
155154
}
156155

157156
// HTTPSProber - Do a https probe for given url

main.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import (
2525
"time"
2626
)
2727

28-
var version string = "0.9.1"
28+
var version string = "0.9.2-beta"
2929
var buildInfo string = ""
3030

3131
func main() {

options/options.go

+1-1
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import (
66

77
var Opts struct {
88
Bgp bool `short:"b" long:"bgp" description:"Connect and Sync with GoBGP server"`
9-
Ka string `short:"k" long:"ka" description:"One of in,out"`
9+
Ka string `short:"k" long:"ka" description:"KeepAlive/BFD RemoteIP" default:"none"`
1010
Version bool `short:"v" long:"version" description:"Show loxilb version"`
1111
NoAPI bool `short:"a" long:"api" description:"Run Rest API server"`
1212
NoNlp bool `short:"n" long:"nonlp" description:"Do not register with nlp"`

0 commit comments

Comments
 (0)