Skip to content

Commit

Permalink
Add implementation of Infiniband interface
Browse files Browse the repository at this point in the history
new package incapsulates the guid config file validation,
guid pool implementation and host manipulations to set IB VF GUIDs
  • Loading branch information
almaslennikov committed Jul 23, 2024
1 parent 7116dc0 commit 0022c10
Show file tree
Hide file tree
Showing 14 changed files with 871 additions and 16 deletions.
3 changes: 3 additions & 0 deletions pkg/consts/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,6 +138,9 @@ const (

// ManageSoftwareBridgesFeatureGate: enables management of software bridges by the operator
ManageSoftwareBridgesFeatureGate = "manageSoftwareBridges"

// The path to the file on the host filesystem that contains the IB GUID distribution for IB VFs
InfinibandGUIDConfigFilePath = SriovConfBasePath + "/infiniband/guids"
)

const (
Expand Down
62 changes: 62 additions & 0 deletions pkg/host/internal/infiniband/guid.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package infiniband

import (
"fmt"
"math/rand"
"net"
)

// GUID address is an uint64 encapsulation for network hardware address
type GUID uint64

const (
guidLength = 8
byteBitLen = 8
byteMask = 0xff
)

// ParseGUID parses string only as GUID 64 bit
func ParseGUID(s string) (GUID, error) {
ha, err := net.ParseMAC(s)
if err != nil {
return 0, err
}
if len(ha) != guidLength {
return 0, fmt.Errorf("invalid GUID address %s", s)
}
var guid uint64
for idx, octet := range ha {
guid |= uint64(octet) << uint(byteBitLen*(guidLength-1-idx))
}
return GUID(guid), nil
}

// String returns the string representation of GUID
func (g GUID) String() string {
return g.HardwareAddr().String()
}

// HardwareAddr returns GUID representation as net.HardwareAddr
func (g GUID) HardwareAddr() net.HardwareAddr {
value := uint64(g)
ha := make(net.HardwareAddr, guidLength)
for idx := guidLength - 1; idx >= 0; idx-- {
ha[idx] = byte(value & byteMask)
value >>= byteBitLen
}

return ha
}

func generateRandomGUID() net.HardwareAddr {
guid := make(net.HardwareAddr, 8)

// First field is 0x01 - xfe to avoid all zero and all F invalid guids
guid[0] = byte(1 + rand.Intn(0xfe))

for i := 1; i < len(guid); i++ {
guid[i] = byte(rand.Intn(0x100))
}

return guid
}
29 changes: 29 additions & 0 deletions pkg/host/internal/infiniband/guid_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package infiniband

import (
"net"

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

var _ = Describe("GUID", func() {
It("should parse and process GUIDs correctly", func() {
guidStr := "00:01:02:03:04:05:06:08"
nextGuidStr := "00:01:02:03:04:05:06:09"

guid, err := ParseGUID(guidStr)
Expect(err).NotTo(HaveOccurred())

Expect(guid.String()).To(Equal(guidStr))
Expect((guid + 1).String()).To(Equal(nextGuidStr))
})
It("should represent GUID as HW address", func() {
guidStr := "00:01:02:03:04:05:06:08"

guid, err := ParseGUID(guidStr)
Expect(err).NotTo(HaveOccurred())

Expect(guid.HardwareAddr()).To(Equal(net.HardwareAddr{0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x08}))
})
})
126 changes: 126 additions & 0 deletions pkg/host/internal/infiniband/ib_guid_config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
package infiniband

import (
"encoding/json"
"fmt"
"os"

"github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/internal/lib/netlink"
"github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/types"
"github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/utils"
)

type ibPfGUIDJSONConfig struct {
PciAddress string `json:"pciAddress,omitempty"`
PfGUID string `json:"pfGuid,omitempty"`
GUIDs []string `json:"guids,omitempty"`
GUIDsRange *GUIDRangeJSON `json:"guidsRange,omitempty"`
}

type GUIDRangeJSON struct {
Start string `json:"start,omitempty"`
End string `json:"end,omitempty"`
}

type ibPfGUIDConfig struct {
GUIDs []GUID
GUIDRange *GUIDRange
}

type GUIDRange struct {
Start GUID
End GUID
}

func getIbGUIDConfig(configPath string, netlinkLib netlink.NetlinkLib, networkHelper types.NetworkInterface) (map[string]ibPfGUIDConfig, error) {
links, err := netlinkLib.LinkList()
if err != nil {
return nil, err
}
rawConfigs, err := readJSONConfig(configPath)
if err != nil {
return nil, err
}
resultConfigs := map[string]ibPfGUIDConfig{}
// Parse JSON config into an internal struct
for _, rawConfig := range rawConfigs {
pciAddress, err := getPfPciAddressFromRawConfig(rawConfig, links, networkHelper)
if err != nil {
return nil, fmt.Errorf("failed to extract pci address from ib guid config: %w", err)
}
if len(rawConfig.GUIDs) == 0 && (rawConfig.GUIDsRange == nil || (rawConfig.GUIDsRange.Start == "" || rawConfig.GUIDsRange.End == "")) {
return nil, fmt.Errorf("either guid list or guid range should be provided, got none")
}
if len(rawConfig.GUIDs) != 0 && rawConfig.GUIDsRange != nil {
return nil, fmt.Errorf("either guid list or guid range should be provided, got both")
}
if rawConfig.GUIDsRange != nil && ((rawConfig.GUIDsRange.Start != "" && rawConfig.GUIDsRange.End == "") || (rawConfig.GUIDsRange.Start == "" && rawConfig.GUIDsRange.End != "")) {
return nil, fmt.Errorf("both guid rangeStart and rangeEnd should be provided, got one")
}
if len(rawConfig.GUIDs) != 0 {
var guids []GUID
for _, guidStr := range rawConfig.GUIDs {
guid, err := ParseGUID(guidStr)
if err != nil {
return nil, fmt.Errorf("failed to parse ib guid %s: %w", guidStr, err)
}
guids = append(guids, guid)
}
resultConfigs[pciAddress] = ibPfGUIDConfig{
GUIDs: guids,
}
continue
}

rangeStart, err := ParseGUID(rawConfig.GUIDsRange.Start)
if err != nil {
return nil, fmt.Errorf("failed to parse ib guid range start: %w", err)
}
rangeEnd, err := ParseGUID(rawConfig.GUIDsRange.End)
if err != nil {
return nil, fmt.Errorf("failed to parse ib guid range end: %w", err)
}
if rangeEnd < rangeStart {
return nil, fmt.Errorf("range end cannot be less then range start")
}
resultConfigs[pciAddress] = ibPfGUIDConfig{
GUIDRange: &GUIDRange{
Start: rangeStart,
End: rangeEnd,
},
}
}
return resultConfigs, nil
}

// readJSONConfig reads the file at the given path and unmarshals the contents into an array of ibPfGUIDJSONConfig structs
func readJSONConfig(configPath string) ([]ibPfGUIDJSONConfig, error) {
data, err := os.ReadFile(utils.GetHostExtensionPath(configPath))
if err != nil {
return nil, fmt.Errorf("failed to read ib guid config: %w", err)
}
var configs []ibPfGUIDJSONConfig
if err := json.Unmarshal(data, &configs); err != nil {
return nil, fmt.Errorf("failed to unmarshal content of ib guid config: %w", err)
}
return configs, nil
}

func getPfPciAddressFromRawConfig(pfRawConfig ibPfGUIDJSONConfig, links []netlink.Link, networkHelper types.NetworkInterface) (string, error) {
if pfRawConfig.PciAddress != "" && pfRawConfig.PfGUID != "" {
return "", fmt.Errorf("either PCI address or PF GUID required to describe an interface, both provided")
}
if pfRawConfig.PciAddress == "" && pfRawConfig.PfGUID == "" {
return "", fmt.Errorf("either PCI address or PF GUID required to describe an interface, none provided")
}
if pfRawConfig.PciAddress != "" {
return pfRawConfig.PciAddress, nil
}
// PfGUID is provided, need to resolve the pci address
for _, link := range links {
if link.Attrs().HardwareAddr.String() == pfRawConfig.PfGUID {
return networkHelper.GetPciAddressFromInterfaceName(link.Attrs().Name)
}
}
return "", fmt.Errorf("no matching link found for pf guid: %s", pfRawConfig.PfGUID)
}
Loading

0 comments on commit 0022c10

Please sign in to comment.