The following pull request was submitted through Github.
It can be accessed and reviewed at: https://github.com/lxc/lxd/pull/7163

This e-mail was sent by the LXC bot, direct replies will not reach the author
unless they happen to be subscribed to this list.

=== Description (from pull-request) ===
Also adds support for newer versions of the ip tool that use "address" rather than "MAC" field for VF hwaddr.

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
From e7d98a1f36899fc43badf89f80c0fe8befab01c0 Mon Sep 17 00:00:00 2001
From: Thomas Parrott <thomas.parr...@canonical.com>
Date: Thu, 9 Apr 2020 13:18:11 +0100
Subject: [PATCH] lxd/device/nic/sriov: Updates networkGetVirtFuncInfo to use
 json output from ip tool

Also adds support for newer versions of the ip tool that use "address" rather 
than "MAC" field for VF hwaddr.

Signed-off-by: Thomas Parrott <thomas.parr...@canonical.com>
---
 lxd/device/nic_sriov.go | 172 ++++++++++++++++++++++++++++++----------
 1 file changed, 129 insertions(+), 43 deletions(-)

diff --git a/lxd/device/nic_sriov.go b/lxd/device/nic_sriov.go
index d725db3ca1..8cba0fd09e 100644
--- a/lxd/device/nic_sriov.go
+++ b/lxd/device/nic_sriov.go
@@ -3,7 +3,9 @@ package device
 import (
        "bufio"
        "bytes"
+       "encoding/json"
        "fmt"
+       "io"
        "io/ioutil"
        "os/exec"
        "regexp"
@@ -352,10 +354,10 @@ func (d *nicSRIOV) setupSriovParent(vfDevice string, vfID 
int, volatile map[stri
        defer revert.Fail()
 
        // Record properties of VF settings on the parent device.
-       volatile["last_state.vf.hwaddr"] = vfInfo.mac
+       volatile["last_state.vf.hwaddr"] = vfInfo.Address
        volatile["last_state.vf.id"] = fmt.Sprintf("%d", vfID)
-       volatile["last_state.vf.vlan"] = fmt.Sprintf("%d", vfInfo.vlan)
-       volatile["last_state.vf.spoofcheck"] = fmt.Sprintf("%t", 
vfInfo.spoofcheck)
+       volatile["last_state.vf.vlan"] = fmt.Sprintf("%d", 
vfInfo.VLANs[0]["vlan"])
+       volatile["last_state.vf.spoofcheck"] = fmt.Sprintf("%t", 
vfInfo.SpoofCheck)
 
        // Record the host interface we represents the VF device which we will 
move into instance.
        volatile["host_name"] = vfDevice
@@ -480,65 +482,149 @@ func (d *nicSRIOV) setupSriovParent(vfDevice string, 
vfID int, volatile map[stri
        return vfPCIDev, nil
 }
 
-// virtFuncInfo holds information about SR-IOV virtual functions.
-type virtFuncInfo struct {
-       mac        string
-       vlan       int
-       spoofcheck bool
+// VirtFuncInfo holds information about SR-IOV virtual functions.
+type VirtFuncInfo struct {
+       VF         int              `json:"vf"`
+       Address    string           `json:"address"`
+       MAC        string           `json:"mac"` // Deprecated
+       VLANs      []map[string]int `json:"vlan_list"`
+       SpoofCheck bool             `json:"spoofchk"`
 }
 
 // networkGetVirtFuncInfo returns info about an SR-IOV virtual function from 
the ip tool.
-func (d *nicSRIOV) networkGetVirtFuncInfo(devName string, vfID int) (vf 
virtFuncInfo, err error) {
-       cmd := exec.Command("ip", "link", "show", devName)
-       stdout, err := cmd.StdoutPipe()
-       if err != nil {
-               return
-       }
+func (d *nicSRIOV) networkGetVirtFuncInfo(devName string, vfID int) 
(VirtFuncInfo, error) {
+       vf := VirtFuncInfo{}
+       vfNotFoundErr := fmt.Errorf("no matching virtual function found")
 
-       err = cmd.Start()
+       ipPath, err := exec.LookPath("ip")
        if err != nil {
-               return
+               return vf, fmt.Errorf("ip command not found")
        }
-       defer stdout.Close()
 
-       // Try and match: "vf 1 MAC 00:00:00:00:00:00, vlan 4095, spoof 
checking off"
-       reVlan := regexp.MustCompile(fmt.Sprintf(`vf %d MAC 
((?:[[:xdigit:]]{2}:){5}[[:xdigit:]]{2}).*, vlan (\d+), spoof checking (\w+)`, 
vfID))
-
-       // IP link command doesn't show the vlan property if its set to 0, so 
we need to detect that.
-       // Try and match: "vf 1 MAC 00:00:00:00:00:00, spoof checking off"
-       reNoVlan := regexp.MustCompile(fmt.Sprintf(`vf %d MAC 
((?:[[:xdigit:]]{2}:){5}[[:xdigit:]]{2}).*, spoof checking (\w+)`, vfID))
-       scanner := bufio.NewScanner(stdout)
-       for scanner.Scan() {
-               // First try and find VF and reads its properties with VLAN 
activated.
-               res := reVlan.FindStringSubmatch(scanner.Text())
-               if len(res) == 4 {
-                       vlan, err := strconv.Atoi(res[2])
-                       if err != nil {
+       // Function to get VF info using regex matching, for older versions of 
ip tool. Less reliable.
+       vfFindByRegex := func(devName string, vfID int) (VirtFuncInfo, error) {
+               cmd := exec.Command(ipPath, "link", "show", devName)
+               stdout, err := cmd.StdoutPipe()
+               if err != nil {
+                       return vf, err
+               }
+               defer stdout.Close()
+
+               err = cmd.Start()
+               if err != nil {
+                       return vf, err
+               }
+               defer cmd.Wait()
+
+               // Try and match: "vf 1 MAC 00:00:00:00:00:00, vlan 4095, spoof 
checking off"
+               reVlan := regexp.MustCompile(fmt.Sprintf(`vf %d MAC 
((?:[[:xdigit:]]{2}:){5}[[:xdigit:]]{2}).*, vlan (\d+), spoof checking (\w+)`, 
vfID))
+
+               // IP link command doesn't show the vlan property if its set to 
0, so we need to detect that.
+               // Try and match: "vf 1 MAC 00:00:00:00:00:00, spoof checking 
off"
+               reNoVlan := regexp.MustCompile(fmt.Sprintf(`vf %d MAC 
((?:[[:xdigit:]]{2}:){5}[[:xdigit:]]{2}).*, spoof checking (\w+)`, vfID))
+               scanner := bufio.NewScanner(stdout)
+               for scanner.Scan() {
+                       // First try and find VF and read its properties with 
VLAN activated.
+                       res := reVlan.FindStringSubmatch(scanner.Text())
+                       if len(res) == 4 {
+                               vlan, err := strconv.Atoi(res[2])
+                               if err != nil {
+                                       return vf, err
+                               }
+
+                               vf.Address = res[1]
+                               vf.VLANs = append(vf.VLANs, 
map[string]int{"vlan": vlan})
+                               vf.SpoofCheck = shared.IsTrue(res[3])
+
                                return vf, err
                        }
 
-                       vf.mac = res[1]
-                       vf.vlan = vlan
-                       vf.spoofcheck = shared.IsTrue(res[3])
-                       return vf, err
+                       // Next try and find VF and read its properties with 
VLAN missing.
+                       res = reNoVlan.FindStringSubmatch(scanner.Text())
+                       if len(res) == 3 {
+                               vf.Address = res[1]
+                               // Missing VLAN ID means 0 when resetting later.
+                               vf.VLANs = append(vf.VLANs, 
map[string]int{"vlan": 0})
+                               vf.SpoofCheck = shared.IsTrue(res[2])
+
+                               return vf, err
+                       }
                }
 
-               // Next try and find VF and reads its properties with VLAN 
missing.
-               res = reNoVlan.FindStringSubmatch(scanner.Text())
-               if len(res) == 3 {
-                       vf.mac = res[1]
-                       vf.vlan = 0 // Missing VLAN ID means 0 when resetting 
later.
-                       vf.spoofcheck = shared.IsTrue(res[2])
+               err = scanner.Err()
+               if err != nil {
                        return vf, err
                }
+
+               return vf, vfNotFoundErr
        }
 
-       err = scanner.Err()
+       // First try using the JSON output format as is more reliable to parse.
+       cmd := exec.Command(ipPath, "-j", "link", "show", devName)
+       stdout, err := cmd.StdoutPipe()
        if err != nil {
-               return
+               return vf, err
+       }
+       defer stdout.Close()
+
+       err = cmd.Start()
+       if err != nil {
+               return vf, err
+       }
+       defer cmd.Wait()
+
+       // Temporary struct to decode ip output into.
+       var ifInfo []struct {
+               VFList []VirtFuncInfo `json:"vfinfo_list"`
+       }
+
+       // Decode JSON output.
+       dec := json.NewDecoder(stdout)
+       err = dec.Decode(&ifInfo)
+       if err != nil && err != io.EOF {
+               return vf, err
+       }
+
+       err = cmd.Wait()
+       if err != nil {
+               // If JSON command fails, fallback to using regex match mode 
for older versions of ip tool.
+               // This does not support the newer VF "link/ether" output 
prefix.
+               return vfFindByRegex(devName, vfID)
+       }
+
+       if len(ifInfo) == 0 {
+               return vf, vfNotFoundErr
+       }
+
+       // Search VFs returned for match.
+       found := false
+       for _, vfInfo := range ifInfo[0].VFList {
+               if vfInfo.VF == vfID {
+                       vf = vfInfo // Found a match.
+                       found = true
+               }
+       }
+
+       if !found {
+               return vf, vfNotFoundErr
+       }
+
+       // Always populate VLANs slice if not already populated. Missing VLAN 
ID means 0 when resetting later.
+       if len(vf.VLANs) == 0 {
+               vf.VLANs = append(vf.VLANs, map[string]int{"vlan": 0})
+       }
+
+       // Ensure empty VLAN entry is consistently populated.
+       if _, found = vf.VLANs[0]["vlan"]; !found {
+               vf.VLANs[0]["vlan"] = 0
+       }
+
+       // If ip tool has provided old mac field, copy into newer address field.
+       if vf.MAC != "" && vf.Address == "" {
+               vf.Address = vf.MAC
        }
 
-       return vf, fmt.Errorf("no matching virtual function found")
+       return vf, nil
 }
 
 // networkGetVFDevicePCISlot returns the PCI slot name for a network virtual 
function device.
_______________________________________________
lxc-devel mailing list
lxc-devel@lists.linuxcontainers.org
http://lists.linuxcontainers.org/listinfo/lxc-devel

Reply via email to