Skip to content
This repository was archived by the owner on Feb 23, 2026. It is now read-only.

Commit cb1dde6

Browse files
authored
Add intelligent version detection for OVS 3.x+ (#10)
* Add intelligent version detection for OVS 3.x+ - Query ovs-appctl for OVS version when missing from DB - Get db_version from schema instead of defaulting to 'unknown' - Read system_type and system_version from /etc/os-release - Only use 'unknown' as final fallback when queries fail - Add comprehensive tests for new functions * Enhance getSystemID to query database before falling back to file * Update upload-artifact action to v4 in CI workflow * Update Go version in CI workflow to 1.24.x * Update linter to use golangci-lint and adjust dependency installation * Lint errors fixes * Refactor string manipulation and clean up code in various files for lint/tests checks * Refactor string trimming to use TrimPrefix for improved clarity in GetAppClusteringInfo and to make tests pass * Removing closing braces on a previous empty else branch * Fix missing closing braces in GetAppClusteringInfo function * Refactor string trimming to use TrimPrefix for improved clarity in GetAppClusteringInfo * Refactor database existence check in TestListDatabasesMethod for compatibility with newer vovs versions * Add version command support and enhance OvnChassis structure with NbCfg and NbCfgTimestamp fields
1 parent ba2fa6b commit cb1dde6

16 files changed

Lines changed: 557 additions & 85 deletions

.github/workflows/main.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ jobs:
1414
core:
1515
strategy:
1616
matrix:
17-
go-version: [1.20.x]
17+
go-version: [1.24.x]
1818
platform: [ubuntu-latest]
1919
name: Build
2020
runs-on: ${{ matrix.platform }}
@@ -74,7 +74,7 @@ jobs:
7474
- name: Generate coverage report
7575
run: make coverage
7676
- name: Upload coverage report
77-
uses: actions/upload-artifact@v3
77+
uses: actions/upload-artifact@v4
7878
with:
7979
name: Test Coverage Report
8080
path: .coverage/coverage.html

Makefile

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ rights:
2222
@sudo chmod o+rw /run/ovn/ovnsb_db.sock || true
2323

2424
linter:
25-
@golint
26-
@echo "PASS: golint"
25+
@golangci-lint run ./...
26+
@echo "PASS: golangci-lint"
2727

2828
test: covdir linter rights
2929
@go test $(VERBOSE) -coverprofile=.coverage/coverage.out
@@ -50,7 +50,7 @@ clean:
5050

5151
dep:
5252
@echo "Making dependencies check ..."
53-
@go install golang.org/x/lint/golint@latest
53+
@go install github.com/golangci/golangci-lint/cmd/golangci-lint@latest
5454
@go install github.com/kyoh86/richgo@latest
5555
@go install github.com/caddyserver/xcaddy/cmd/xcaddy@latest
5656
@go install github.com/greenpau/versioned/cmd/versioned@latest

app_cluster.go

Lines changed: 29 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,7 @@ func (cli *OvnClient) GetAppClusteringInfo(db string) (ClusterState, error) {
102102
}
103103
if strings.HasPrefix(line, "Name:") {
104104
parserOn = true
105-
s := strings.TrimLeft(line, "Name: ")
105+
s := strings.TrimPrefix(line, "Name: ")
106106
s = strings.Join(strings.Fields(s), " ")
107107
server.Database = s
108108
continue
@@ -111,7 +111,7 @@ func (cli *OvnClient) GetAppClusteringInfo(db string) (ClusterState, error) {
111111
continue
112112
}
113113
if strings.HasPrefix(line, "Cluster ID:") {
114-
s := strings.TrimLeft(line, "Cluster ID:")
114+
s := strings.TrimPrefix(line, "Cluster ID:")
115115
s = strings.Join(strings.Fields(strings.TrimSpace(s)), " ")
116116
arr := strings.Split(s, " ")
117117
if len(arr) != 2 {
@@ -124,7 +124,7 @@ func (cli *OvnClient) GetAppClusteringInfo(db string) (ClusterState, error) {
124124
}
125125
continue
126126
} else if strings.HasPrefix(line, "Server ID:") {
127-
s := strings.TrimLeft(line, "Server ID:")
127+
s := strings.TrimPrefix(line, "Server ID:")
128128
s = strings.Join(strings.Fields(strings.TrimSpace(s)), " ")
129129
arr := strings.Split(s, " ")
130130
if len(arr) != 2 {
@@ -137,12 +137,12 @@ func (cli *OvnClient) GetAppClusteringInfo(db string) (ClusterState, error) {
137137
}
138138
continue
139139
} else if strings.HasPrefix(line, "Address:") {
140-
s := strings.TrimLeft(line, "Address:")
140+
s := strings.TrimPrefix(line, "Address:")
141141
s = strings.Join(strings.Fields(s), " ")
142142
server.Address = s
143143
continue
144144
} else if strings.HasPrefix(line, "Status:") {
145-
s := strings.TrimLeft(line, "Status:")
145+
s := strings.TrimPrefix(line, "Status:")
146146
s = strings.Join(strings.Fields(s), " ")
147147
switch s {
148148
case "cluster member":
@@ -152,7 +152,7 @@ func (cli *OvnClient) GetAppClusteringInfo(db string) (ClusterState, error) {
152152
}
153153
continue
154154
} else if strings.HasPrefix(line, "Role:") {
155-
s := strings.TrimLeft(line, "Role:")
155+
s := strings.TrimPrefix(line, "Role:")
156156
s = strings.Join(strings.Fields(s), " ")
157157
switch s {
158158
case "leader":
@@ -166,29 +166,29 @@ func (cli *OvnClient) GetAppClusteringInfo(db string) (ClusterState, error) {
166166
}
167167
continue
168168
} else if strings.HasPrefix(line, "Term:") {
169-
s := strings.TrimLeft(line, "Term:")
169+
s := strings.TrimPrefix(line, "Term:")
170170
s = strings.Join(strings.Fields(s), " ")
171171
if term, err := strconv.ParseUint(s, 10, 64); err == nil {
172172
server.Term = term
173173
}
174174
} else if strings.HasPrefix(line, "Leader:") {
175-
s := strings.TrimLeft(line, "Leader:")
175+
s := strings.TrimPrefix(line, "Leader:")
176176
s = strings.Join(strings.Fields(s), " ")
177177
if s == "self" {
178178
server.IsLeaderSelf = 1
179179
} else {
180180
server.IsLeaderSelf = 0
181181
}
182182
} else if strings.HasPrefix(line, "Vote:") {
183-
s := strings.TrimLeft(line, "Vote:")
183+
s := strings.TrimPrefix(line, "Vote:")
184184
s = strings.Join(strings.Fields(s), " ")
185185
if s == "self" {
186186
server.IsVotedSelf = 1
187187
} else {
188188
server.IsVotedSelf = 0
189189
}
190190
} else if strings.HasPrefix(line, "Log:") {
191-
s := strings.TrimLeft(line, "Log:")
191+
s := strings.TrimPrefix(line, "Log:")
192192
s = strings.Join(strings.Fields(s), " ")
193193
s = strings.TrimLeft(s, "[")
194194
s = strings.TrimRight(s, "]")
@@ -205,19 +205,19 @@ func (cli *OvnClient) GetAppClusteringInfo(db string) (ClusterState, error) {
205205
server.Log.High = i
206206
}
207207
} else if strings.HasPrefix(line, "Entries not yet committed:") {
208-
s := strings.TrimLeft(line, "Entries not yet committed:")
208+
s := strings.TrimPrefix(line, "Entries not yet committed:")
209209
s = strings.Join(strings.Fields(s), " ")
210210
if i, err := strconv.ParseUint(s, 10, 64); err == nil {
211211
server.NotCommittedEntries = i
212212
}
213213
} else if strings.HasPrefix(line, "Entries not yet applied:") {
214-
s := strings.TrimLeft(line, "Entries not yet applied:")
214+
s := strings.TrimPrefix(line, "Entries not yet applied:")
215215
s = strings.Join(strings.Fields(s), " ")
216216
if i, err := strconv.ParseUint(s, 10, 64); err == nil {
217217
server.NotAppliedEntries = i
218218
}
219219
} else if strings.HasPrefix(line, "Connections:") {
220-
s := strings.TrimLeft(line, "Connections:")
220+
s := strings.TrimPrefix(line, "Connections:")
221221
s = strings.Join(strings.Fields(s), " ")
222222
conns := strings.Split(strings.Join(strings.Fields(s), " "), " ")
223223
for _, entry := range conns {
@@ -270,45 +270,41 @@ func (cli *OvnClient) GetAppClusteringInfo(db string) (ClusterState, error) {
270270
var peerMatchIndex uint64
271271
for _, e := range arr {
272272
if strings.HasPrefix(e, "tcp:") || strings.HasPrefix(e, "ssl:") {
273-
if isSelf != true {
273+
if !isSelf {
274274
peerAddress = strings.TrimRight(e, ")")
275275
}
276276
} else if strings.HasPrefix(e, "next_index=") {
277-
nextIndex := strings.TrimLeft(e, "next_index=")
277+
nextIndex := strings.TrimPrefix(e, "next_index=")
278278
if i, err := strconv.ParseUint(nextIndex, 10, 64); err == nil {
279-
if isSelf == true {
279+
if isSelf {
280280
server.NextIndex = i
281281
} else {
282282
peerNextIndex = i
283283
}
284284
}
285285
} else if strings.HasPrefix(e, "match_index=") {
286-
matchIndex := strings.TrimLeft(e, "match_index=")
286+
matchIndex := strings.TrimPrefix(e, "match_index=")
287287
if i, err := strconv.ParseUint(matchIndex, 10, 64); err == nil {
288-
if isSelf == true {
288+
if isSelf {
289289
server.MatchIndex = i
290290
} else {
291291
peerMatchIndex = i
292292
}
293293
}
294-
} else {
295-
// do nothing
296294
}
297-
}
298-
if isSelf != true {
299-
if _, exists := server.Peers[peerID]; !exists {
300-
peer := ClusterPeer{}
301-
peer.ID = peerID
302-
server.Peers[peerID] = &peer
295+
if !isSelf {
296+
if _, exists := server.Peers[peerID]; !exists {
297+
peer := ClusterPeer{}
298+
peer.ID = peerID
299+
server.Peers[peerID] = &peer
300+
}
301+
peer := server.Peers[peerID]
302+
peer.NextIndex = peerNextIndex
303+
peer.MatchIndex = peerMatchIndex
304+
peer.Address = peerAddress
303305
}
304-
peer := server.Peers[peerID]
305-
peer.NextIndex = peerNextIndex
306-
peer.MatchIndex = peerMatchIndex
307-
peer.Address = peerAddress
306+
continue
308307
}
309-
continue
310-
} else {
311-
// do nothing
312308
}
313309
}
314310
//spew.Dump(server)

client.go

Lines changed: 15 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ package ovsdb
1919
import (
2020
"encoding/json"
2121
"fmt"
22+
2223
//"github.com/davecgh/go-spew/spew"
2324
"io"
2425
//"math/rand"
@@ -58,7 +59,7 @@ func NewClient(s string, t int) (Client, error) {
5859
cli.errQueue = make(chan error, 1)
5960
go ovsdbMessenger(cli.Endpoint, cli.Timeout, cli.txQueue, cli.rxQueue, cli.errQueue)
6061
err := <-cli.errQueue
61-
return cli, err
62+
return cli, err //nolint:govet
6263
}
6364

6465
// Close TODO
@@ -82,7 +83,7 @@ func (cli *Client) query(method string, param interface{}) (*Response, error) {
8283
Params: param,
8384
}
8485
for {
85-
if cli.closed == false {
86+
if !cli.closed {
8687
cli.txQueue <- req
8788
select {
8889
case err := <-cli.errQueue:
@@ -114,7 +115,6 @@ func (cli *Client) query(method string, param interface{}) (*Response, error) {
114115
retryAttempts--
115116
}
116117
}
117-
return nil, fmt.Errorf("%s", errMsgs)
118118
}
119119

120120
func (cli *Client) getColumns(db, table string) (map[string]string, error) {
@@ -266,20 +266,18 @@ func ovsdbMessenger(s string, t int, rxQueue <-chan Request, txQueue chan<- Resp
266266
errQueue <- nil
267267
cli := newClientCodec(conn)
268268
for {
269-
select {
270-
case reqMsg := <-rxQueue:
271-
var req rpc.Request
272-
if reqMsg.Method == "shutdown" {
273-
cli.Close()
274-
errQueue <- nil
275-
return
276-
}
277-
req.ServiceMethod = reqMsg.Method
278-
req.Seq = counter
279-
if err := cli.WriteRequest(&req, reqMsg.Params); err != nil {
280-
errQueue <- err
281-
return
282-
}
269+
reqMsg := <-rxQueue
270+
var req rpc.Request
271+
if reqMsg.Method == "shutdown" {
272+
cli.Close()
273+
errQueue <- nil
274+
return
275+
}
276+
req.ServiceMethod = reqMsg.Method
277+
req.Seq = counter
278+
if err := cli.WriteRequest(&req, reqMsg.Params); err != nil {
279+
errQueue <- err
280+
return
283281
}
284282
if err := cli.ReadResponseHeader(&resp); err != nil {
285283
errQueue <- err
@@ -319,5 +317,4 @@ func ovsdbMessenger(s string, t int, rxQueue <-chan Request, txQueue chan<- Resp
319317
}
320318
txQueue <- respMsg
321319
}
322-
return
323320
}

database.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ type OvsDatabase struct {
4343
Schema struct {
4444
Version string
4545
}
46-
connected bool
46+
connected bool //nolint:unused
4747
}
4848

4949
// Databases - DOCS-TBD

database_test.go

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,15 @@ func TestListDatabasesMethod(t *testing.T) {
3232
t.Fatalf("FAIL: expected to find a single database, but found: %d", len(databases))
3333
}
3434
dbName := "Open_vSwitch"
35-
if databases[0] != dbName {
36-
t.Fatalf("FAIL: expected to find '%s' database, but found: %s", dbName, databases[0])
35+
found := false
36+
for _, db := range databases {
37+
if db == dbName {
38+
found = true
39+
break
40+
}
41+
}
42+
if !found {
43+
t.Fatalf("FAIL: expected to find '%s' database in %v", dbName, databases)
3744
}
3845
t.Logf("PASS: 'list_dbs' method completed successfully")
3946
}

echo.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ func (c *Client) Echo(s string) error {
2424
method := "echo"
2525
js, err := encodeString(s)
2626
if err != nil {
27-
fmt.Errorf("'%s' method failed: %v", method, err)
27+
_ = fmt.Errorf("'%s' method failed: %v", method, err)
2828
}
2929
response, err := c.query(method, js)
3030
if err != nil {

encoder.go

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ var methods = map[string]method{
3333
"get_schema": {Name: "get_schema"},
3434
"transact": {Name: "transact"},
3535
"list-commands": {Name: "list-commands"},
36+
"version": {Name: "version"},
3637
"coverage/show": {Name: "coverage/show"},
3738
"memory/show": {Name: "memory/show"},
3839
"cluster/status": {Name: "cluster/status"},
@@ -56,7 +57,7 @@ func newOvsdbEncoder(w io.Writer) *ovsdbEncoder {
5657
// An encodeState encodes JSON into a bytes.Buffer.
5758
type encodeState struct {
5859
bytes.Buffer // accumulated output
59-
scratch [64]byte
60+
scratch [64]byte //nolint:unused
6061
}
6162

6263
var encodeStatePool sync.Pool
@@ -111,6 +112,7 @@ func (enc *ovsdbEncoder) Encode(v interface{}) error {
111112
e.WriteString(s)
112113
// e.WriteString("\"Open_vSwitch\",{\"op\":\"select\",\"table\":\"Open_vSwitch\",\"where\":[]}")
113114
case "list-commands":
115+
case "version":
114116
case "coverage/show":
115117
case "memory/show":
116118
case "dpif/show":

0 commit comments

Comments
 (0)