|
3 | 3 | package deviceplugin
|
4 | 4 |
|
5 | 5 | import (
|
| 6 | + "errors" |
6 | 7 | "os"
|
7 |
| - "os/exec" |
8 |
| - "regexp" |
| 8 | + "path" |
9 | 9 | "strings"
|
10 | 10 |
|
11 | 11 | "github.com/golang/glog"
|
12 | 12 | )
|
13 | 13 |
|
14 |
| -// Takes the output from lshw and returns the device name for each solarflare |
15 |
| -// device. |
16 |
| -func parseOutput(output string) []string { |
17 |
| - // "lshw -short -class network" sample output: |
18 |
| - // H/W path Device Class Description |
19 |
| - // ========================================================= |
20 |
| - // /0/100/1b/0 enp2s0f0 network XtremeScale SFC9250 10/25/40/50/100G Ethernet Controller |
21 |
| - // /0/100/1b/0.1 enp2s0f1 network XtremeScale SFC9250 10/25/40/50/100G Ethernet Controller |
22 |
| - // /0/100/1c.1/0 eno1 network NetXtreme BCM5720 Gigabit Ethernet PCIe |
23 |
| - // /0/100/1c.1/0.1 eno2 network NetXtreme BCM5720 Gigabit Ethernet PCIe |
24 |
| - |
25 |
| - lines := strings.Split(output, "\n") |
| 14 | +const ( |
| 15 | + sysClassNetPath = "/sys/class/net/" |
| 16 | + solarflareVendor = "0x1924" |
| 17 | +) |
26 | 18 |
|
27 |
| - var interfaces []string |
| 19 | +func isSFCNic(devicePath string) bool { |
| 20 | + deviceDir, err := os.Stat(path.Join(devicePath, "device")) |
| 21 | + if errors.Is(err, os.ErrNotExist) { |
| 22 | + // Not a physical device, so won't have the "vendor" file |
| 23 | + return false |
| 24 | + } else if err != nil { |
| 25 | + glog.Errorf("Failed to stat %s (%v)", devicePath, err) |
| 26 | + return false |
| 27 | + } |
| 28 | + if !deviceDir.IsDir() { |
| 29 | + return false |
| 30 | + } |
28 | 31 |
|
29 |
| - // Assume that we are running as root, if not then we would have to skip |
30 |
| - // an additional line at the start of the output |
31 |
| - skip_lines := 2 |
32 |
| - end_lines := 1 |
33 |
| - if os.Geteuid() != 0 { |
34 |
| - skip_lines = 3 |
35 |
| - end_lines = 2 |
| 32 | + data, err := os.ReadFile(path.Join(devicePath, "device", "vendor")) |
| 33 | + if errors.Is(err, os.ErrNotExist) { |
| 34 | + // File doesn't exist but that is fine |
| 35 | + return false |
| 36 | + } else if err != nil { |
| 37 | + glog.Errorf("Error reading %s (%v)", |
| 38 | + path.Join(devicePath, "device", "vendor"), err) |
| 39 | + return false |
36 | 40 | }
|
37 | 41 |
|
38 |
| - for _, line := range lines[skip_lines : len(lines)-end_lines] { |
39 |
| - // This regex makes the assumption that all interface names only |
40 |
| - // contain either lowercase letters or numbers. If that is not true, |
41 |
| - // then this should be updated to reflect that. |
42 |
| - r := regexp.MustCompile("([a-z0-9]+) *network *.*SFC") |
43 |
| - out := r.FindStringSubmatch(line) |
44 |
| - if out != nil { |
45 |
| - // It is safe to access out[1] here since the return value of |
46 |
| - // FindStringSubmatch is an array where the first value is the |
47 |
| - // whole string and any subsequent values are the submatches. |
48 |
| - // In this case since there is a submatch that should match the |
49 |
| - // device name if FindStringSubmatch returns non-nil then there |
50 |
| - // will be at least 2 elements in the return array. |
51 |
| - interfaces = append(interfaces, out[1]) |
| 42 | + vendor := strings.TrimSuffix(string(data), "\n") |
| 43 | + return vendor == solarflareVendor |
| 44 | +} |
| 45 | + |
| 46 | +func readSysFiles() ([]string, error) { |
| 47 | + infos, err := os.ReadDir(sysClassNetPath) |
| 48 | + if err != nil { |
| 49 | + glog.Errorf("Error reading %s (%v)", sysClassNetPath, err) |
| 50 | + return []string{}, err |
| 51 | + } |
| 52 | + interfaces := []string{} |
| 53 | + for _, info := range infos { |
| 54 | + if isSFCNic(path.Join(sysClassNetPath, info.Name())) { |
| 55 | + interfaces = append(interfaces, info.Name()) |
52 | 56 | }
|
53 | 57 | }
|
54 |
| - return interfaces |
| 58 | + return interfaces, nil |
55 | 59 | }
|
56 | 60 |
|
57 | 61 | // Returns a list of the Solarflare interfaces present on the node
|
58 | 62 | func queryNics() ([]string, error) {
|
59 |
| - // Depending on what information we are looking for in the output I think it |
60 |
| - // is quite tempting to retrieve the information in a json format, then |
61 |
| - // parse this using golang's built-in features. |
62 |
| - bytes, err := exec.Command("lshw", "-short", "-class", "network").CombinedOutput() |
63 |
| - output := string(bytes) |
| 63 | + interfaces, err := readSysFiles() |
64 | 64 | if err != nil {
|
65 |
| - glog.Error(output) |
66 |
| - glog.Errorf("error while listing sfc devices : %v", err) |
67 |
| - return nil, err |
| 65 | + glog.Errorf("Failed to list interfaces (%v)", err) |
| 66 | + return []string{}, err |
68 | 67 | }
|
69 |
| - interfaces := parseOutput(output) |
70 | 68 | return interfaces, nil
|
71 | 69 | }
|
0 commit comments