diff --git a/Makefile b/Makefile index b4e455e..7060ff6 100644 --- a/Makefile +++ b/Makefile @@ -59,6 +59,7 @@ $(DISTDIR)/$(PACKAGE)_$(VERSION)-$(RELEASE)_$(ARCH).tgz: $(OBJECTS) ln -sf /usr/bin/xenstore $(STAGEDIR)/usr/bin/xenstore-write ; \ ln -sf /usr/bin/xenstore $(STAGEDIR)/usr/bin/xenstore-exists ; \ ln -sf /usr/bin/xenstore $(STAGEDIR)/usr/bin/xenstore-rm ; \ + ln -sf /usr/bin/xenstore $(STAGEDIR)/usr/bin/xenstore-list ; \ install -d $(STAGEDIR)/etc/udev/rules.d/ ; \ install -m 644 $(SOURCEDIR)/xen-vcpu-hotplug.rules $(STAGEDIR)/etc/udev/rules.d/z10_xen-vcpu-hotplug.rules ; \ cd $(STAGEDIR) ; \ diff --git a/guestmetric/guestmetric_linux.go b/guestmetric/guestmetric_linux.go index dd75c09..e61ccca 100644 --- a/guestmetric/guestmetric_linux.go +++ b/guestmetric/guestmetric_linux.go @@ -119,9 +119,9 @@ func enumNetworkAddresses(iface string) (GuestMetric, error) { return d, nil } -func getVifId(iface string) (string, error) { - filePath := fmt.Sprintf("/sys/class/net/%s/device/nodename", iface) - strLine, err := readSysfs(filePath) +func getPlainVifId(path string) (string, error) { + nodenamePath := fmt.Sprintf("%s/device/nodename", path) + strLine, err := readSysfs(nodenamePath) if err != nil { return "", err } @@ -131,12 +131,53 @@ func getVifId(iface string) (string, error) { vifId = matched[1] } if vifId == "" { - return "", fmt.Errorf("Not found string like \"device/vif/[id]\" in file %s", filePath) + return "", fmt.Errorf("Not found string like \"device/vif/[id]\" in file %s", nodenamePath) } else { return vifId, nil } } +func (c *Collector) getSriovVifId(path string) (string, error) { + sriovDevicePath := "xenserver/device/net-sriov-vf" + macAddress, err := readSysfs(path + "/address") + if err != nil { + return "", err + } + subPaths, err := c.Client.List(sriovDevicePath) + if err != nil { + return "", err + } + for _, subPath := range subPaths { + iterMac, err := c.Client.Read(fmt.Sprintf("%s/%s/mac", sriovDevicePath, subPath)) + if err != nil { + continue + } + if iterMac == macAddress { + return subPath, nil + } + } + return "", fmt.Errorf("Cannot find a MAC address to map with %s", path) +} + +// return vif_xenstore_prefix * vif_id * error where +// `vif_xenstore_prefix` could be either `attr/vif` for plain VIF or +// `xenserver/attr/net-sriov-vf` for SR-IOV VIF +func (c *Collector) getTargetXenstorePath(path string) (string, string, error) { + plainVifPrefix := "attr/vif" + sriovVifPrefix := "xenserver/attr/net-sriov-vf" + // try to get `vif_id` from nodename interface, only a plain VIF have the nodename interface. + vifId, err1 := getPlainVifId(path) + if vifId != "" { + return plainVifPrefix, vifId, nil + } + // not a plain VIF, it could possible be an SR-IOV VIF, try to get vif_id from MAC address mapping + vifId, err2 := c.getSriovVifId(path) + if vifId != "" { + return sriovVifPrefix, vifId, nil + } + return "", "", fmt.Errorf("Failed to get VIF ID, errors: %s | %s", err1.Error(), err2.Error()) +} + func (c *Collector) CollectNetworkAddr() (GuestMetric, error) { current := make(GuestMetric, 0) @@ -149,20 +190,20 @@ func (c *Collector) CollectNetworkAddr() (GuestMetric, error) { } paths = append(paths, prefixPaths...) } - for _, path := range paths { - iface := filepath.Base(path) - vifId, err := getVifId(iface) + // a path is going to be like "/sys/class/net/eth0" + prefix, vifId, err := c.getTargetXenstorePath(path) if err != nil { continue } + iface := filepath.Base(path) if addrs, err := enumNetworkAddresses(iface); err == nil { for tag, addr := range addrs { - current[fmt.Sprintf("vif/%s/%s", vifId, tag)] = addr + current[fmt.Sprintf("%s/%s/%s", prefix, vifId, tag)] = addr } } } - return prefixKeys("attr/", current), nil + return current, nil } func readSysfs(filename string) (string, error) { diff --git a/xenstore/xenstore.go b/xenstore/xenstore.go index c6fe1a5..912de25 100644 --- a/xenstore/xenstore.go +++ b/xenstore/xenstore.go @@ -46,6 +46,24 @@ func xs_read(script_name string, args []string) { } } +func xs_list(script_name string, args []string) { + if len(args) == 0 || args[0] == "-h" { + die("Usage: %s key [ key ... ]", script_name) + } + + xs := new_xs() + for _, key := range args[:] { + result, err := xs.List(key) + if err != nil { + die("%s error: %v", script_name, err) + } + + for _, subPath := range result { + fmt.Println(subPath) + } + } +} + func xs_write(script_name string, args []string) { if len(args) == 0 || args[0] == "-h" || len(args)%2 != 0 { die("Usage: %s key value [ key value ... ]", script_name) @@ -111,6 +129,8 @@ func main() { switch operation { case "read": xs_read(script_name, args) + case "list": + xs_list(script_name, args) case "write": xs_write(script_name, args) case "rm": diff --git a/xenstoreclient/xenstore.go b/xenstoreclient/xenstore.go index 5112c40..571481a 100644 --- a/xenstoreclient/xenstore.go +++ b/xenstoreclient/xenstore.go @@ -63,6 +63,7 @@ type XenStoreClient interface { Close() error DO(packet *Packet) (*Packet, error) Read(path string) (string, error) + List(path string) ([]string, error) Mkdir(path string) error Rm(path string) error Write(path string, value string) error @@ -281,6 +282,25 @@ func (xs *XenStore) Read(path string) (string, error) { return string(resp.Value), nil } +func (xs *XenStore) List(path string) ([]string, error) { + v := []byte(path + "\x00") + req := &Packet{ + OpCode: XS_DIRECTORY, + Req: 0, + TxID: xs.tx, + Length: uint32(len(v)), + Value: v, + } + resp, err := xs.DO(req) + if err != nil { + return []string{}, err + } + subItems := strings.Split( + string(bytes.Trim(resp.Value, "\x00")), "\x00") + + return subItems, nil +} + func (xs *XenStore) Mkdir(path string) error { v := []byte(path + "\x00") req := &Packet{ @@ -507,6 +527,10 @@ func (xs *CachedXenStore) Read(path string) (string, error) { return xs.xs.Read(path) } +func (xs *CachedXenStore) List(path string) ([]string, error) { + return xs.xs.List(path) +} + func (xs *CachedXenStore) Mkdir(path string) error { return xs.xs.Mkdir(path) }