Skip to content

Commit

Permalink
Merge pull request #1415 from steiler/kernelversioncheck
Browse files Browse the repository at this point in the history
Host kernel version check for SRL
  • Loading branch information
hellt committed Jun 1, 2023
2 parents 06505dc + 19ed38a commit 36472e8
Show file tree
Hide file tree
Showing 4 changed files with 156 additions and 2 deletions.
31 changes: 31 additions & 0 deletions nodes/srl/srl.go
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,12 @@ var (
srlCfgTpl, _ = template.New("srl-tls-profile").
Funcs(gomplate.CreateFuncs(context.Background(), new(data.Data))).
Parse(srlConfigCmdsTpl)

requiredKernelVersion = &utils.KernelVersion{
Major: 4,
Minor: 10,
Revision: 0,
}
)

// Register registers the node in the NodeRegistry.
Expand Down Expand Up @@ -366,6 +372,31 @@ func (s *srl) Ready(ctx context.Context) error {
}
}

// checkKernelVersion emits a warning if the present kernel version is lower than the required one.
func (s *srl) checkKernelVersion() error {
// retrieve running kernel version
kv, err := utils.GetKernelVersion()
if err != nil {
return err
}

// do the comparison
if !kv.GreaterOrEqual(requiredKernelVersion) {
log.Infof("Nokia SR Linux v23.3.1+ requires a kernel version greater than %s. Detected kernel version: %s", requiredKernelVersion, kv)
}
return nil
}

func (s *srl) CheckDeploymentConditions(ctx context.Context) error {
// perform the srl specific kernel version check
err := s.checkKernelVersion()
if err != nil {
return err
}

return s.DefaultNode.CheckDeploymentConditions(ctx)
}

func (s *srl) createSRLFiles() error {
log.Debugf("Creating directory structure for SRL container: %s", s.Cfg.ShortName)
var src string
Expand Down
3 changes: 2 additions & 1 deletion utils/file.go
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,8 @@ func ExpandHome(p string) string {

userId, isSet := os.LookupEnv("SUDO_UID")
if !isSet {
return curUserHomeDir
p = strings.Replace(p, "~", curUserHomeDir, 1)
return p
}

// lookup user to figure out Home Directory
Expand Down
69 changes: 69 additions & 0 deletions utils/kernel_module.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,13 @@ package utils

import (
"bufio"
"fmt"
"os"
"regexp"
"strconv"
"strings"

log "github.com/sirupsen/logrus"
)

// IsKernelModuleLoaded checks if a kernel module is loaded by parsing /proc/modules file.
Expand All @@ -21,3 +26,67 @@ func IsKernelModuleLoaded(name string) (bool, error) {
}
return false, f.Close()
}

const kernelOSReleasePath = "/proc/sys/kernel/osrelease"

// GetKernelVersion returns the parsed OS kernel version.
func GetKernelVersion() (*KernelVersion, error) {
ver, err := os.ReadFile(kernelOSReleasePath)
if err != nil {
return nil, err
}

log.Debugf("kernel version: %s", string(ver))

return parseKernelVersion(ver)
}

// KernelVersion holds the parsed OS kernel version.
type KernelVersion struct {
Major int
Minor int
Revision int
Remainder string // the rest of the version string, e.g. "-amd64"
}

func parseKernelVersion(v []byte) (*KernelVersion, error) {
// https: //regex101.com/r/cWqad0/1
re := regexp.MustCompile(`(?P<Major>\d+)\.(?P<Minor>\d+)\.(?P<Revision>\d+)(?P<Remainder>.*)`)

matches := re.FindSubmatch(v)

if len(matches) > 0 {
kv := &KernelVersion{}

kv.Major, _ = strconv.Atoi(string(matches[re.SubexpIndex("Major")]))
kv.Minor, _ = strconv.Atoi(string(matches[re.SubexpIndex("Minor")]))
kv.Revision, _ = strconv.Atoi(string(matches[re.SubexpIndex("Revision")]))
kv.Remainder = string(matches[re.SubexpIndex("Remainder")])

return kv, nil
}

return nil, fmt.Errorf("failed to parse kernel version")
}

// String returns the Kernel version as string.
func (kv *KernelVersion) String() string {
return fmt.Sprintf("%d.%d.%d", kv.Major, kv.Minor, kv.Revision)
}

// GreaterOrEqual returns true if the Kernel version is greater or equal to the compared Kernel version.
func (kv *KernelVersion) GreaterOrEqual(cmpKv *KernelVersion) bool {
if kv.Major < cmpKv.Major {
return false
}
if kv.Minor < cmpKv.Minor {
return false
}
// this must be >= because we're implementing GreaterEqual
// and this is the last position
if kv.Revision < cmpKv.Revision {
return false
}

return true
}
55 changes: 54 additions & 1 deletion utils/kernel_module_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
package utils

import "testing"
import (
"testing"

"github.com/google/go-cmp/cmp"
)

func TestIsKernelModuleLoaded(t *testing.T) {
type args struct {
Expand Down Expand Up @@ -42,3 +46,52 @@ func TestIsKernelModuleLoaded(t *testing.T) {
})
}
}

func TestParseKernelVersion(t *testing.T) {
tests := []struct {
input []byte
expectErr bool
expected *KernelVersion
}{
{[]byte("123.45.6789 example"), false, &KernelVersion{Major: 123, Minor: 45, Revision: 6789, Remainder: " example"}},
{[]byte("1.2.3-abc"), false, &KernelVersion{Major: 1, Minor: 2, Revision: 3, Remainder: "-abc"}},
{[]byte("invalid"), true, nil},
{[]byte(""), true, nil},
}

for _, test := range tests {
result, err := parseKernelVersion(test.input)

if (err != nil) != test.expectErr {
t.Errorf("Error expectation mismatch. Input: %s, Expected Error: %v, Actual Error: %v", test.input, test.expectErr, err)
}

if d := cmp.Diff(result, test.expected); d != "" {
t.Errorf("parseKernelVersion got = %+v, want %+v; Diff: %s", result, test.expected, d)
}
}
}

func TestKernelVersionGreaterOrEqual(t *testing.T) {
tests := []struct {
version *KernelVersion
compare *KernelVersion
expectResult bool
}{
{&KernelVersion{Major: 1, Minor: 2, Revision: 3}, &KernelVersion{Major: 1, Minor: 2, Revision: 3}, true},
{&KernelVersion{Major: 1, Minor: 2, Revision: 3}, &KernelVersion{Major: 1, Minor: 2, Revision: 2}, true},
{&KernelVersion{Major: 1, Minor: 2, Revision: 3}, &KernelVersion{Major: 1, Minor: 3, Revision: 3}, false},
{&KernelVersion{Major: 2, Minor: 3, Revision: 4}, &KernelVersion{Major: 1, Minor: 2, Revision: 3}, true},
{&KernelVersion{Major: 2, Minor: 3, Revision: 4}, &KernelVersion{Major: 3, Minor: 4, Revision: 5}, false},
{&KernelVersion{Major: 2, Minor: 3, Revision: 4}, &KernelVersion{Major: 2, Minor: 3, Revision: 5}, false},
}

for _, test := range tests {
result := test.version.GreaterOrEqual(test.compare)

if result != test.expectResult {
t.Errorf("Result mismatch. Version: %+v, Compare: %+v, Expected: %v, Actual: %v",
test.version, test.compare, test.expectResult, result)
}
}
}

0 comments on commit 36472e8

Please sign in to comment.