Skip to content

Commit

Permalink
This patch includes vlan trunking for Intel and Mellanox:
Browse files Browse the repository at this point in the history
(1) k8snetworkplumbingwg#149, plus
(2) extension for Mellanox

Signed-off-by: Jing Zhang <[email protected]>
  • Loading branch information
jingczhang committed Oct 4, 2024
1 parent fca6591 commit c465ccb
Show file tree
Hide file tree
Showing 16 changed files with 865 additions and 2 deletions.
3 changes: 2 additions & 1 deletion docs/configuration-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ An SR-IOV CNI config with each field filled out looks like:
"trust": "on",
"link_state": "enable",
"logLevel": "debug",
"logFile": "/tmp/sriov.log"
"logFile": "/tmp/sriov.log",
"vlan_trunk": "10,12,20-30,1100"
}
```

Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ module github.com/k8snetworkplumbingwg/sriov-cni
go 1.22.4

require (
github.com/Masterminds/semver v1.5.0
github.com/containernetworking/cni v1.2.0-rc0.0.20240317203738-a448e71e9867
github.com/containernetworking/plugins v1.4.2-0.20240312120516-c860b78de419
github.com/k8snetworkplumbingwg/cni-log v0.0.0-20230801160229-b6e062c9e0f2
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
github.com/BurntSushi/toml v1.1.0 h1:ksErzDEI1khOiGPgpwuI7x2ebx/uXQNw7xJpn9Eq1+I=
github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
github.com/containernetworking/cni v1.2.0-rc0.0.20240317203738-a448e71e9867 h1:DQ9iOvlXFOn+sJfbdvXyGISf/4xHNFGxJltq4mixK00=
github.com/containernetworking/cni v1.2.0-rc0.0.20240317203738-a448e71e9867/go.mod h1:Lt0TQcZQVDju64fYxUhDziTgXCDe3Olzi9I4zZJLWHg=
github.com/containernetworking/plugins v1.4.2-0.20240312120516-c860b78de419 h1:mvCb6RL9/tZwgXnkYNQQk6JDtLgHdtFde8uVm7VKg04=
Expand Down
6 changes: 6 additions & 0 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -137,6 +137,12 @@ func LoadConf(bytes []byte) (*sriovtypes.NetConf, error) {
return nil, fmt.Errorf("LoadConf(): invalid link_state value: %s", n.LinkState)
}

if n.VlanTrunk != "" {
if err := utils.ValidateVlanTrunkValue(n.VlanTrunk); err != nil {
return nil, fmt.Errorf("LoadConf(): invalid vlan_trunk value: %s", n.VlanTrunk)
}
}

return n, nil
}

Expand Down
34 changes: 34 additions & 0 deletions pkg/factory/factory.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package factory

import (
"fmt"

"github.com/k8snetworkplumbingwg/sriov-cni/pkg/providers"
"github.com/k8snetworkplumbingwg/sriov-cni/pkg/types"
"github.com/k8snetworkplumbingwg/sriov-cni/pkg/utils"
)

const (
//IntelProviderID Intel vendor ID
IntelProviderID = "0x8086"
//MellanoxProviderID Mellanox vendor ID
MellanoxProviderID = "0x15b3"
)

// GetProviderConfig get Config for specific NIC
func GetProviderConfig(deviceID string) (types.VlanTrunkProviderConfig, error) {
vendor, err := utils.GetVendorID(deviceID)
if err != nil {
return nil, fmt.Errorf("GetVendorID Error: %q", err)
}

switch vendor {
case IntelProviderID:
return providers.NewIntelTrunkProviderConfig(), nil
case MellanoxProviderID:
return providers.NewMellanoxTrunkProviderConfig(), nil
default:
return nil, fmt.Errorf("Not supported vendor: %q", vendor)
}

}
32 changes: 32 additions & 0 deletions pkg/factory/factory_suite_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
package factory

import (
"testing"

"github.com/k8snetworkplumbingwg/sriov-cni/pkg/utils"

. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

func check(e error) {
if e != nil {
panic(e)
}
}
func TestFactory(t *testing.T) {
RegisterFailHandler(Fail)
RunSpecs(t, "Factory Suite")
}

var _ = BeforeSuite(func() {
// create test sys tree
err := utils.CreateTmpSysFs()
check(err)
})

var _ = AfterSuite(func() {
var err error
err = utils.RemoveTmpSysFs()
check(err)
})
26 changes: 26 additions & 0 deletions pkg/factory/factory_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package factory

import (
. "github.com/onsi/ginkgo/v2"
. "github.com/onsi/gomega"
)

var _ = Describe("Factory", func() {

Context("Checking GetProviderConfig function", func() {
It("Assuming existing vf", func() {
_, err := GetProviderConfig("0000:af:06.1")
// Expect(result).To(Equal(&providers.IntelTrunkProviderConfig{ProviderName: "Intel"}))
Expect(err).NotTo(HaveOccurred(), "Existing vf should not return an error")
})
It("Assuming existing vf", func() {
_, err := GetProviderConfig("0000:cf:06.0")
// Expect(result).To(Equal(&providers.MellanoxTrunkProviderConfig{ProviderName: "Mellanox"}))
Expect(err).NotTo(HaveOccurred(), "Existing vf should not return an error, unless the vendor is not supported")
})
It("Assuming not existing vf", func() {
_, err := GetProviderConfig("0000:af:07.0")
Expect(err).To(HaveOccurred(), "Not existing vf should return an error")
})
})
})
173 changes: 173 additions & 0 deletions pkg/providers/intel.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
package providers

import (
"bytes"
"fmt"
"io/ioutil"
"os/exec"
"strconv"
"strings"

"github.com/Masterminds/semver"

sriovtypes "github.com/k8snetworkplumbingwg/sriov-cni/pkg/types"
"github.com/k8snetworkplumbingwg/sriov-cni/pkg/utils"
)

var execCommand = exec.Command

//IntelTrunkProviderConfig stores name of the provider
type IntelTrunkProviderConfig struct {
ProviderName string
VlanData string
}

//NewIntelTrunkProviderConfig creates new Intel provider configuraton
func NewIntelTrunkProviderConfig() sriovtypes.VlanTrunkProviderConfig {
return &IntelTrunkProviderConfig{
ProviderName: "Intel",
}
}

//InitConfig initializes provider configuration for given trunking ranges
func (p *IntelTrunkProviderConfig) InitConfig(vlanRanges *sriovtypes.VlanTrunkRangeData) {
p.GetVlanData(vlanRanges)
return
}

//ApplyConfig applies provider configuration
func (p *IntelTrunkProviderConfig) ApplyConfig(conf *sriovtypes.NetConf) error {
if trunkingSupported := CheckTrunkSupport(); trunkingSupported == false {
return fmt.Errorf("Vlan trunking supported only by i40e version 2.7.11 and higher, please upgrade your driver")
}

if err := AddVlanFiltering(p.VlanData, conf.Master, conf.VFID); err != nil {

Check failure on line 44 in pkg/providers/intel.go

View workflow job for this annotation

GitHub Actions / Lint

if-return: redundant if ...; err != nil check, just return error instead. (revive)

Check failure on line 44 in pkg/providers/intel.go

View workflow job for this annotation

GitHub Actions / Lint

if-return: redundant if ...; err != nil check, just return error instead. (revive)
return err
}

return nil
}

//RemoveConfig removes configuration
func (p *IntelTrunkProviderConfig) RemoveConfig(conf *sriovtypes.NetConf) error {
if err := RemoveVlanFiltering(p.VlanData, conf.Master, conf.VFID); err != nil {

Check failure on line 53 in pkg/providers/intel.go

View workflow job for this annotation

GitHub Actions / Lint

if-return: redundant if ...; err != nil check, just return error instead. (revive)

Check failure on line 53 in pkg/providers/intel.go

View workflow job for this annotation

GitHub Actions / Lint

if-return: redundant if ...; err != nil check, just return error instead. (revive)
return err
}

return nil
}

//GetVlanData converts vlanRanges.VlanTrunkRanges into string
func (p *IntelTrunkProviderConfig) GetVlanData(vlanRanges *sriovtypes.VlanTrunkRangeData) {
vlanData := ""
var start, end string

for i, vlanRange := range vlanRanges.VlanTrunkRanges {

start = strconv.Itoa(int(vlanRange.Start))
end = strconv.Itoa(int(vlanRange.End))
vlanData = vlanData + start

if start != end {
vlanData = vlanData + "-" + end
}
if i < len(vlanRanges.VlanTrunkRanges)-1 {
vlanData = vlanData + ","
}

}
p.VlanData = vlanData
return
}

//AddVlanFiltering writes "add [trunking ranges]" to trunk file
func AddVlanFiltering(vlanData, pfName string, vfid int) error {
addTrunk := "add " + vlanData
trunkFile := fmt.Sprintf(utils.TrunkFileDirectory, pfName, vfid)

errwrite := ioutil.WriteFile(trunkFile, []byte(addTrunk), 0644)

Check failure on line 88 in pkg/providers/intel.go

View workflow job for this annotation

GitHub Actions / Lint

G306: Expect WriteFile permissions to be 0600 or less (gosec)

Check failure on line 88 in pkg/providers/intel.go

View workflow job for this annotation

GitHub Actions / Lint

G306: Expect WriteFile permissions to be 0600 or less (gosec)
if errwrite != nil {
return fmt.Errorf("f.Write: %q", errwrite)
}

infraInterfaces, infraVlans, err := utils.GetInfraVlanData()
if err == nil {
for _, pf := range infraInterfaces {
if pf == pfName {
vlanData := ""
for _, vlan := range infraVlans {
if vlanData == "" {
vlanData = fmt.Sprintf("%d", vlan)
} else {
vlanData = fmt.Sprintf("%s,%d", vlanData, vlan)
}
}
if vlanData != "" {
removeTrunk := "rem " + vlanData
errwrite := ioutil.WriteFile(trunkFile, []byte(removeTrunk), 0644)

Check failure on line 107 in pkg/providers/intel.go

View workflow job for this annotation

GitHub Actions / Lint

G306: Expect WriteFile permissions to be 0600 or less (gosec)

Check failure on line 107 in pkg/providers/intel.go

View workflow job for this annotation

GitHub Actions / Lint

G306: Expect WriteFile permissions to be 0600 or less (gosec)
if errwrite != nil {
return fmt.Errorf("f.Write: %q", errwrite)
}
}
}
}
}

return nil
}

//RemoveVlanFiltering writes "rem [trunking ranges]" to trunk file
func RemoveVlanFiltering(vlanData, pfName string, vfid int) error {
removeTrunk := "rem " + vlanData
trunkFile := fmt.Sprintf(utils.TrunkFileDirectory, pfName, vfid)

errwrite := ioutil.WriteFile(trunkFile, []byte(removeTrunk), 0644)

Check failure on line 124 in pkg/providers/intel.go

View workflow job for this annotation

GitHub Actions / Lint

G306: Expect WriteFile permissions to be 0600 or less (gosec)

Check failure on line 124 in pkg/providers/intel.go

View workflow job for this annotation

GitHub Actions / Lint

G306: Expect WriteFile permissions to be 0600 or less (gosec)
if errwrite != nil {
return fmt.Errorf("f.Write: %q", errwrite)
}

return nil
}

// CheckTrunkSupport checks installed driver version; trunking is supported for version 2.7.11 and higher
func CheckTrunkSupport() bool {
var stdout bytes.Buffer
modinfoCmd := "modinfo -F version i40e"
cmd := execCommand("sh", "-c", modinfoCmd)
cmd.Stdout = &stdout

if err := cmd.Run(); err != nil {
fmt.Printf("modinfo returned error: %v %s", err, stdout.String())
return false
}

stdoutSplit := strings.Split(stdout.String(), "\n")
if len(stdoutSplit) == 0 {
fmt.Printf("unexpected output after parsing driver version: %s", stdout.String())
return false
}
driverVersion := stdoutSplit[0]
numDots := strings.Count(driverVersion, ".")
if numDots < 2 {
fmt.Printf("unexpected driver version: %s", driverVersion)
return false
}
//truncate driver version to only major.minor.patch version format length to ensure semver compatibility
if numDots > 2 {
truncVersion := strings.Split(driverVersion, ".")[:3]
driverVersion = strings.Join(truncVersion, ".")
}

v1, _ := semver.NewVersion("2.7.11")
v2, err := semver.NewVersion(driverVersion)
if err != nil {
fmt.Printf("invalid version error: %v %s", err, driverVersion)
return false
}

if v2.Compare(v1) >= 0 {
return true
}

return false
}
Loading

0 comments on commit c465ccb

Please sign in to comment.