• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

k8snetworkplumbingwg / sriov-network-operator / 9206688050

23 May 2024 10:50AM UTC coverage: 39.655% (+0.06%) from 39.599%
9206688050

Pull #587

github

web-flow
Merge 5f3c4e903 into 87e4dadb1
Pull Request #587: Add more checks on generic plugin to discover discrepancies from the desired state

54 of 127 new or added lines in 7 files covered. (42.52%)

6 existing lines in 2 files now uncovered.

5175 of 13050 relevant lines covered (39.66%)

0.44 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

45.71
/pkg/host/internal/network/network.go
1
package network
2

3
import (
4
        "errors"
5
        "fmt"
6
        "io/fs"
7
        "os"
8
        "path/filepath"
9
        "strconv"
10
        "strings"
11
        "time"
12

13
        "github.com/cenkalti/backoff"
14
        "github.com/vishvananda/netlink/nl"
15
        "sigs.k8s.io/controller-runtime/pkg/log"
16

17
        "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/consts"
18
        dputilsPkg "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/internal/lib/dputils"
19
        ethtoolPkg "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/internal/lib/ethtool"
20
        netlinkPkg "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/internal/lib/netlink"
21
        "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/host/types"
22
        "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/utils"
23
        "github.com/k8snetworkplumbingwg/sriov-network-operator/pkg/vars"
24
)
25

26
type network struct {
27
        utilsHelper utils.CmdInterface
28
        dputilsLib  dputilsPkg.DPUtilsLib
29
        netlinkLib  netlinkPkg.NetlinkLib
30
        ethtoolLib  ethtoolPkg.EthtoolLib
31
}
32

33
func New(utilsHelper utils.CmdInterface, dputilsLib dputilsPkg.DPUtilsLib, netlinkLib netlinkPkg.NetlinkLib, ethtoolLib ethtoolPkg.EthtoolLib) types.NetworkInterface {
1✔
34
        return &network{
1✔
35
                utilsHelper: utilsHelper,
1✔
36
                dputilsLib:  dputilsLib,
1✔
37
                netlinkLib:  netlinkLib,
1✔
38
                ethtoolLib:  ethtoolLib,
1✔
39
        }
1✔
40
}
1✔
41

42
// TryToGetVirtualInterfaceName get the interface name of a virtio interface
43
func (n *network) TryToGetVirtualInterfaceName(pciAddr string) string {
×
44
        log.Log.Info("TryToGetVirtualInterfaceName() get interface name for device", "device", pciAddr)
×
45

×
46
        // To support different driver that is not virtio-pci like mlx
×
47
        name := n.TryGetInterfaceName(pciAddr)
×
48
        if name != "" {
×
49
                return name
×
50
        }
×
51

52
        netDir, err := filepath.Glob(filepath.Join(vars.FilesystemRoot, consts.SysBusPciDevices, pciAddr, "virtio*", "net"))
×
53
        if err != nil || len(netDir) < 1 {
×
54
                return ""
×
55
        }
×
56

57
        fInfos, err := os.ReadDir(netDir[0])
×
58
        if err != nil {
×
59
                log.Log.Error(err, "TryToGetVirtualInterfaceName(): failed to read net directory", "dir", netDir[0])
×
60
                return ""
×
61
        }
×
62

63
        names := make([]string, 0)
×
64
        for _, f := range fInfos {
×
65
                names = append(names, f.Name())
×
66
        }
×
67

68
        if len(names) < 1 {
×
69
                return ""
×
70
        }
×
71

72
        return names[0]
×
73
}
74

75
func (n *network) TryGetInterfaceName(pciAddr string) string {
×
76
        names, err := n.dputilsLib.GetNetNames(pciAddr)
×
77
        if err != nil || len(names) < 1 {
×
78
                return ""
×
79
        }
×
80
        netDevName := names[0]
×
81

×
82
        // Switchdev PF and their VFs representors are existing under the same PCI address since kernel 5.8
×
83
        // if device is switchdev then return PF name
×
84
        for _, name := range names {
×
85
                if !n.IsSwitchdev(name) {
×
86
                        continue
×
87
                }
88
                // Try to get the phys port name, if not exists then fallback to check without it
89
                // phys_port_name should be in formant p<port-num> e.g p0,p1,p2 ...etc.
90
                if physPortName, err := n.GetPhysPortName(name); err == nil {
×
91
                        if !vars.PfPhysPortNameRe.MatchString(physPortName) {
×
92
                                continue
×
93
                        }
94
                }
95
                return name
×
96
        }
97

98
        log.Log.V(2).Info("tryGetInterfaceName()", "name", netDevName)
×
99
        return netDevName
×
100
}
101

102
func (n *network) GetPhysSwitchID(name string) (string, error) {
×
103
        swIDFile := filepath.Join(vars.FilesystemRoot, consts.SysClassNet, name, "phys_switch_id")
×
104
        physSwitchID, err := os.ReadFile(swIDFile)
×
105
        if err != nil {
×
106
                return "", err
×
107
        }
×
108
        if physSwitchID != nil {
×
109
                return strings.TrimSpace(string(physSwitchID)), nil
×
110
        }
×
111
        return "", nil
×
112
}
113

114
func (n *network) GetPhysPortName(name string) (string, error) {
×
115
        devicePortNameFile := filepath.Join(vars.FilesystemRoot, consts.SysClassNet, name, "phys_port_name")
×
116
        physPortName, err := os.ReadFile(devicePortNameFile)
×
117
        if err != nil {
×
118
                return "", err
×
119
        }
×
120
        if physPortName != nil {
×
121
                return strings.TrimSpace(string(physPortName)), nil
×
122
        }
×
123
        return "", nil
×
124
}
125

126
func (n *network) IsSwitchdev(name string) bool {
×
127
        switchID, err := n.GetPhysSwitchID(name)
×
128
        if err != nil || switchID == "" {
×
129
                return false
×
130
        }
×
131

132
        return true
×
133
}
134

135
func (n *network) GetNetdevMTU(pciAddr string) int {
×
136
        log.Log.V(2).Info("GetNetdevMTU(): get MTU", "device", pciAddr)
×
137
        ifaceName := n.TryGetInterfaceName(pciAddr)
×
138
        if ifaceName == "" {
×
139
                return 0
×
140
        }
×
141

142
        link, err := n.netlinkLib.LinkByName(ifaceName)
×
143
        if err != nil {
×
144
                log.Log.Error(err, "GetNetdevMTU(): fail to get Link ", "device", ifaceName)
×
145
                return 0
×
146
        }
×
147

148
        return link.Attrs().MTU
×
149
}
150

151
func (n *network) SetNetdevMTU(pciAddr string, mtu int) error {
×
152
        log.Log.V(2).Info("SetNetdevMTU(): set MTU", "device", pciAddr, "mtu", mtu)
×
153
        if mtu <= 0 {
×
154
                log.Log.V(2).Info("SetNetdevMTU(): refusing to set MTU", "mtu", mtu)
×
155
                return nil
×
156
        }
×
157
        b := backoff.NewConstantBackOff(1 * time.Second)
×
158
        err := backoff.Retry(func() error {
×
159
                ifaceName := n.TryGetInterfaceName(pciAddr)
×
160
                if ifaceName == "" {
×
161
                        log.Log.Error(nil, "SetNetdevMTU(): fail to get interface name", "device", pciAddr)
×
162
                        return fmt.Errorf("failed to get netdevice for device %s", pciAddr)
×
163
                }
×
164

165
                link, err := n.netlinkLib.LinkByName(ifaceName)
×
166
                if err != nil {
×
167
                        log.Log.Error(err, "SetNetdevMTU(): fail to get Link ", "device", ifaceName)
×
168
                        return err
×
169
                }
×
170
                return n.netlinkLib.LinkSetMTU(link, mtu)
×
171
        }, backoff.WithMaxRetries(b, 10))
172

173
        if err != nil {
×
174
                log.Log.Error(err, "SetNetdevMTU(): fail to set mtu after retrying")
×
175
                return err
×
176
        }
×
177
        return nil
×
178
}
179

180
// GetNetDevMac returns network device MAC address or empty string if address cannot be
181
// retrieved.
182
func (n *network) GetNetDevMac(ifaceName string) string {
×
183
        log.Log.V(2).Info("GetNetDevMac(): get Mac", "device", ifaceName)
×
184
        link, err := n.netlinkLib.LinkByName(ifaceName)
×
185
        if err != nil {
×
186
                log.Log.Error(err, "GetNetDevMac(): failed to get Link", "device", ifaceName)
×
187
                return ""
×
188
        }
×
189
        return link.Attrs().HardwareAddr.String()
×
190
}
191

192
// GetNetDevNodeGUID returns the network interface node GUID if device is RDMA capable otherwise returns empty string
193
func (n *network) GetNetDevNodeGUID(pciAddr string) string {
1✔
194
        if len(pciAddr) == 0 {
2✔
195
                return ""
1✔
196
        }
1✔
197

198
        rdmaDevicesPath := filepath.Join(vars.FilesystemRoot, consts.SysBusPciDevices, pciAddr, "infiniband")
1✔
199
        rdmaDevices, err := os.ReadDir(rdmaDevicesPath)
1✔
200
        if err != nil {
2✔
201
                if !errors.Is(err, fs.ErrNotExist) {
1✔
NEW
202
                        log.Log.Error(err, "GetNetDevNodeGUID(): failed to read RDMA related directory", "pciAddr", pciAddr)
×
NEW
203
                }
×
204
                return ""
1✔
205
        }
206

207
        if len(rdmaDevices) != 1 {
2✔
208
                log.Log.Error(err, "GetNetDevNodeGUID(): expected just one RDMA device", "pciAddr", pciAddr, "numOfDevices", len(rdmaDevices))
1✔
209
                return ""
1✔
210
        }
1✔
211

212
        rdmaLink, err := n.netlinkLib.RdmaLinkByName(rdmaDevices[0].Name())
1✔
213
        if err != nil {
2✔
214
                log.Log.Error(err, "GetNetDevNodeGUID(): failed to get RDMA link", "pciAddr", pciAddr)
1✔
215
                return ""
1✔
216
        }
1✔
217

218
        return rdmaLink.Attrs.NodeGuid
1✔
219
}
220

221
func (n *network) GetNetDevLinkSpeed(ifaceName string) string {
×
222
        log.Log.V(2).Info("GetNetDevLinkSpeed(): get LinkSpeed", "device", ifaceName)
×
223
        speedFilePath := filepath.Join(vars.FilesystemRoot, consts.SysClassNet, ifaceName, "speed")
×
224
        data, err := os.ReadFile(speedFilePath)
×
225
        if err != nil {
×
226
                log.Log.Error(err, "GetNetDevLinkSpeed(): fail to read Link Speed file", "path", speedFilePath)
×
227
                return ""
×
228
        }
×
229

230
        return fmt.Sprintf("%s Mb/s", strings.TrimSpace(string(data)))
×
231
}
232

233
// GetDevlinkDeviceParam returns devlink parameter for the device as a string, if the parameter has multiple values
234
// then the function will return only first one from the list.
235
func (n *network) GetDevlinkDeviceParam(pciAddr, paramName string) (string, error) {
1✔
236
        funcLog := log.Log.WithValues("device", pciAddr, "param", paramName)
1✔
237
        funcLog.V(2).Info("GetDevlinkDeviceParam(): get device parameter")
1✔
238
        param, err := n.netlinkLib.DevlinkGetDeviceParamByName(consts.BusPci, pciAddr, paramName)
1✔
239
        if err != nil {
2✔
240
                funcLog.Error(err, "GetDevlinkDeviceParam(): fail to get devlink device param")
1✔
241
                return "", err
1✔
242
        }
1✔
243
        if len(param.Values) == 0 {
1✔
244
                err = fmt.Errorf("param %s has no value", paramName)
×
245
                funcLog.Error(err, "GetDevlinkDeviceParam(): error")
×
246
                return "", err
×
247
        }
×
248
        var value string
1✔
249
        switch param.Type {
1✔
250
        case nl.DEVLINK_PARAM_TYPE_U8, nl.DEVLINK_PARAM_TYPE_U16, nl.DEVLINK_PARAM_TYPE_U32:
1✔
251
                var valData uint64
1✔
252
                switch v := param.Values[0].Data.(type) {
1✔
253
                case uint8:
1✔
254
                        valData = uint64(v)
1✔
255
                case uint16:
1✔
256
                        valData = uint64(v)
1✔
257
                case uint32:
1✔
258
                        valData = uint64(v)
1✔
259
                default:
×
260
                        return "", fmt.Errorf("unexpected uint type type")
×
261
                }
262
                value = strconv.FormatUint(valData, 10)
1✔
263

264
        case nl.DEVLINK_PARAM_TYPE_STRING:
1✔
265
                value = param.Values[0].Data.(string)
1✔
266
        case nl.DEVLINK_PARAM_TYPE_BOOL:
1✔
267
                value = strconv.FormatBool(param.Values[0].Data.(bool))
1✔
268
        default:
×
269
                return "", fmt.Errorf("unknown value type: %d", param.Type)
×
270
        }
271
        funcLog.V(2).Info("GetDevlinkDeviceParam(): result", "value", value)
1✔
272
        return value, nil
1✔
273
}
274

275
// SetDevlinkDeviceParam set devlink parameter for the device, accepts paramName and value
276
// as a string. Automatically set CMODE for the parameter and converts the value to the right
277
// type before submitting it.
278
func (n *network) SetDevlinkDeviceParam(pciAddr, paramName, value string) error {
1✔
279
        funcLog := log.Log.WithValues("device", pciAddr, "param", paramName, "value", value)
1✔
280
        funcLog.V(2).Info("SetDevlinkDeviceParam(): set device parameter")
1✔
281
        param, err := n.netlinkLib.DevlinkGetDeviceParamByName(consts.BusPci, pciAddr, paramName)
1✔
282
        if err != nil {
2✔
283
                funcLog.Error(err, "SetDevlinkDeviceParam(): can't get existing param data")
1✔
284
                return err
1✔
285
        }
1✔
286
        if len(param.Values) == 0 {
1✔
287
                err = fmt.Errorf("param %s has no value", paramName)
×
288
                funcLog.Error(err, "SetDevlinkDeviceParam(): error")
×
289
                return err
×
290
        }
×
291
        targetCMOD := param.Values[0].CMODE
1✔
292
        var typedValue interface{}
1✔
293
        var v uint64
1✔
294
        switch param.Type {
1✔
295
        case nl.DEVLINK_PARAM_TYPE_U8:
1✔
296
                v, err = strconv.ParseUint(value, 10, 8)
1✔
297
                typedValue = uint8(v)
1✔
298
        case nl.DEVLINK_PARAM_TYPE_U16:
1✔
299
                v, err = strconv.ParseUint(value, 10, 16)
1✔
300
                typedValue = uint16(v)
1✔
301
        case nl.DEVLINK_PARAM_TYPE_U32:
1✔
302
                v, err = strconv.ParseUint(value, 10, 32)
1✔
303
                typedValue = uint32(v)
1✔
304
        case nl.DEVLINK_PARAM_TYPE_STRING:
1✔
305
                err = nil
1✔
306
                typedValue = value
1✔
307
        case nl.DEVLINK_PARAM_TYPE_BOOL:
1✔
308
                typedValue, err = strconv.ParseBool(value)
1✔
309
        default:
×
310
                return fmt.Errorf("parameter has unknown value type: %d", param.Type)
×
311
        }
312
        if err != nil {
2✔
313
                err = fmt.Errorf("failed to convert value %s to the required type: %T, devlink paramType is: %d", value, typedValue, param.Type)
1✔
314
                funcLog.Error(err, "SetDevlinkDeviceParam(): error")
1✔
315
                return err
1✔
316
        }
1✔
317
        if err := n.netlinkLib.DevlinkSetDeviceParam(consts.BusPci, pciAddr, paramName, targetCMOD, typedValue); err != nil {
2✔
318
                funcLog.Error(err, "SetDevlinkDeviceParam(): failed to set parameter")
1✔
319
                return err
1✔
320
        }
1✔
321
        return nil
1✔
322
}
323

324
// EnableHwTcOffload makes sure that hw-tc-offload feature is enabled if device supports it
325
func (n *network) EnableHwTcOffload(ifaceName string) error {
1✔
326
        log.Log.V(2).Info("EnableHwTcOffload(): enable offloading", "device", ifaceName)
1✔
327
        hwTcOffloadFeatureName := "hw-tc-offload"
1✔
328

1✔
329
        knownFeatures, err := n.ethtoolLib.FeatureNames(ifaceName)
1✔
330
        if err != nil {
2✔
331
                log.Log.Error(err, "EnableHwTcOffload(): can't list supported features", "device", ifaceName)
1✔
332
                return err
1✔
333
        }
1✔
334
        if _, isKnown := knownFeatures[hwTcOffloadFeatureName]; !isKnown {
2✔
335
                log.Log.V(0).Info("EnableHwTcOffload(): can't enable feature, feature is not supported", "device", ifaceName)
1✔
336
                return nil
1✔
337
        }
1✔
338
        currentFeaturesState, err := n.ethtoolLib.Features(ifaceName)
1✔
339
        if err != nil {
2✔
340
                log.Log.Error(err, "EnableHwTcOffload(): can't read features state for device", "device", ifaceName)
1✔
341
                return err
1✔
342
        }
1✔
343
        if currentFeaturesState[hwTcOffloadFeatureName] {
2✔
344
                log.Log.V(2).Info("EnableHwTcOffload(): already enabled", "device", ifaceName)
1✔
345
                return nil
1✔
346
        }
1✔
347
        if err := n.ethtoolLib.Change(ifaceName, map[string]bool{hwTcOffloadFeatureName: true}); err != nil {
2✔
348
                log.Log.Error(err, "EnableHwTcOffload(): can't set feature for device", "device", ifaceName)
1✔
349
                return err
1✔
350
        }
1✔
351
        updatedFeaturesState, err := n.ethtoolLib.Features(ifaceName)
1✔
352
        if err != nil {
2✔
353
                log.Log.Error(err, "EnableHwTcOffload(): can't read features state for device", "device", ifaceName)
1✔
354
                return err
1✔
355
        }
1✔
356
        if updatedFeaturesState[hwTcOffloadFeatureName] {
2✔
357
                log.Log.V(2).Info("EnableHwTcOffload(): feature enabled", "device", ifaceName)
1✔
358
                return nil
1✔
359
        }
1✔
360
        log.Log.V(0).Info("EnableHwTcOffload(): feature is still disabled, not supported by device", "device", ifaceName)
1✔
361
        return nil
1✔
362
}
363

364
// GetNetDevLinkAdminState returns the admin state of the interface.
NEW
365
func (n *network) GetNetDevLinkAdminState(ifaceName string) string {
×
NEW
366
        log.Log.V(2).Info("GetNetDevLinkAdminState(): get LinkAdminState", "device", ifaceName)
×
NEW
367
        if len(ifaceName) == 0 {
×
NEW
368
                return ""
×
NEW
369
        }
×
370

NEW
371
        link, err := n.netlinkLib.LinkByName(ifaceName)
×
NEW
372
        if err != nil {
×
NEW
373
                log.Log.Error(err, "GetNetDevLinkAdminState(): failed to get link", "device", ifaceName)
×
NEW
374
                return ""
×
NEW
375
        }
×
376

NEW
377
        if n.netlinkLib.IsLinkAdminStateUp(link) {
×
NEW
378
                return consts.LinkAdminStateUp
×
NEW
379
        }
×
380

NEW
381
        return consts.LinkAdminStateDown
×
382
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc