Skip to content
This repository was archived by the owner on Mar 17, 2023. It is now read-only.

Commit ca39396

Browse files
committed
devlink: initial commit
0 parents  commit ca39396

11 files changed

+647
-0
lines changed

.travis.yml

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
language: go
2+
go:
3+
- 1.x.x
4+
os:
5+
- linux
6+
sudo: required
7+
before_install:
8+
- go get github.com/golang/lint/golint
9+
- go get honnef.co/go/tools/cmd/staticcheck
10+
- go get -d ./...
11+
script:
12+
- go build -tags=gofuzz ./...
13+
- go vet ./...
14+
- staticcheck ./...
15+
- golint -set_exit_status ./...
16+
- go test -v -race ./...

LICENSE.md

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
MIT License
2+
===========
3+
4+
Copyright (C) 2018 Matt Layher
5+
6+
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
7+
8+
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
9+
10+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

README.md

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
devlink [![Build Status](https://travis-ci.org/mdlayher/devlink.svg?branch=master)](https://travis-ci.org/mdlayher/devlink) [![GoDoc](https://godoc.org/github.com/mdlayher/devlink?status.svg)](https://godoc.org/github.com/mdlayher/devlink) [![Go Report Card](https://goreportcard.com/badge/github.com/mdlayher/devlink)](https://goreportcard.com/report/github.com/mdlayher/devlink)
2+
=======
3+
4+
Package `devlink` provides access to Linux's devlink interface.
5+
6+
For more information on devlink, please see https://lwn.net/Articles/674867/.
7+
8+
MIT Licensed.

client.go

+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
// Package devlink provides access to Linux's devlink interface.
2+
package devlink
3+
4+
import (
5+
"io"
6+
)
7+
8+
// A Client provides access to Linux devlink information.
9+
type Client struct {
10+
c osClient
11+
}
12+
13+
// New creates a new Client.
14+
func New() (*Client, error) {
15+
c, err := newClient()
16+
if err != nil {
17+
return nil, err
18+
}
19+
20+
return &Client{
21+
c: c,
22+
}, nil
23+
}
24+
25+
// Close releases resources used by a Client.
26+
func (c *Client) Close() error {
27+
return c.c.Close()
28+
}
29+
30+
// Devices retrieves all devlink devices on this system.
31+
func (c *Client) Devices() ([]*Device, error) {
32+
return c.c.Devices()
33+
}
34+
35+
// Ports retrieves all devlink ports attached to devices on this system.
36+
func (c *Client) Ports() ([]*Port, error) {
37+
return c.c.Ports()
38+
}
39+
40+
// An osClient is the operating system-specific implementation of Client.
41+
type osClient interface {
42+
io.Closer
43+
Devices() ([]*Device, error)
44+
Ports() ([]*Port, error)
45+
}
46+
47+
// A Device is a devlink device.
48+
type Device struct {
49+
Bus string
50+
Device string
51+
}
52+
53+
//go:generate stringer -type=PortType -output=string.go
54+
55+
// A PortType is the operating mode of a devlink port.
56+
type PortType int
57+
58+
// Possible PortType values.
59+
const (
60+
Unknown PortType = iota
61+
Auto
62+
Ethernet
63+
InfiniBand
64+
)
65+
66+
// A Port is a devlink port which is attached to a devlink device.
67+
type Port struct {
68+
Bus string
69+
Device string
70+
Port int
71+
Type PortType
72+
Name string
73+
}

client_linux.go

+144
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
//+build linux
2+
3+
package devlink
4+
5+
import (
6+
"os"
7+
8+
"github.com/mdlayher/devlink/internal/dlh"
9+
"github.com/mdlayher/genetlink"
10+
"github.com/mdlayher/netlink"
11+
"github.com/mdlayher/netlink/nlenc"
12+
)
13+
14+
var _ osClient = &client{}
15+
16+
// A client is a Linux-specific devlink client.
17+
type client struct {
18+
c *genetlink.Conn
19+
family genetlink.Family
20+
}
21+
22+
// newClient opens a connection to the devlink family using generic netlink.
23+
func newClient() (*client, error) {
24+
c, err := genetlink.Dial(nil)
25+
if err != nil {
26+
return nil, err
27+
}
28+
29+
return initClient(c)
30+
}
31+
32+
// initClient is the internal client constructor used in some tests.
33+
func initClient(c *genetlink.Conn) (*client, error) {
34+
f, err := c.GetFamily(dlh.GenlName)
35+
if err != nil {
36+
_ = c.Close()
37+
return nil, err
38+
}
39+
40+
return &client{
41+
c: c,
42+
family: f,
43+
}, nil
44+
}
45+
46+
// Close implements osClient.
47+
func (c *client) Close() error {
48+
return c.c.Close()
49+
}
50+
51+
// Devices implements osClient.
52+
func (c *client) Devices() ([]*Device, error) {
53+
msg := genetlink.Message{
54+
Header: genetlink.Header{
55+
Command: dlh.CmdGet,
56+
Version: dlh.GenlVersion,
57+
},
58+
}
59+
60+
flags := netlink.HeaderFlagsRequest | netlink.HeaderFlagsDump
61+
62+
msgs, err := c.c.Execute(msg, c.family.ID, flags)
63+
if err != nil {
64+
return nil, err
65+
}
66+
67+
return parseDevices(msgs)
68+
}
69+
70+
// Ports implements osClient.
71+
func (c *client) Ports() ([]*Port, error) {
72+
msg := genetlink.Message{
73+
Header: genetlink.Header{
74+
Command: dlh.CmdPortGet,
75+
Version: dlh.GenlVersion,
76+
},
77+
}
78+
79+
flags := netlink.HeaderFlagsRequest | netlink.HeaderFlagsDump
80+
81+
msgs, err := c.c.Execute(msg, c.family.ID, flags)
82+
if err != nil {
83+
return nil, err
84+
}
85+
86+
return parsePorts(msgs)
87+
}
88+
89+
// parseDevices parses Devices from a slice of generic netlink messages.
90+
func parseDevices(msgs []genetlink.Message) ([]*Device, error) {
91+
// It appears that a Device is just a subset of the attributes found in
92+
// a Port, so we just call the port parsing function to avoid duplication.
93+
ports, err := parsePorts(msgs)
94+
if err != nil {
95+
return nil, err
96+
}
97+
98+
ds := make([]*Device, 0, len(msgs))
99+
for _, p := range ports {
100+
ds = append(ds, &Device{
101+
Bus: p.Bus,
102+
Device: p.Device,
103+
})
104+
}
105+
106+
return ds, nil
107+
}
108+
109+
// parsePorts parses Ports from a slice of generic netlink messages.
110+
func parsePorts(msgs []genetlink.Message) ([]*Port, error) {
111+
if len(msgs) == 0 {
112+
// No devlink response found.
113+
return nil, os.ErrNotExist
114+
}
115+
116+
ps := make([]*Port, 0, len(msgs))
117+
for _, m := range msgs {
118+
attrs, err := netlink.UnmarshalAttributes(m.Data)
119+
if err != nil {
120+
return nil, err
121+
}
122+
123+
var p Port
124+
for _, a := range attrs {
125+
switch a.Type {
126+
case dlh.AttrBusName:
127+
p.Bus = nlenc.String(a.Data)
128+
case dlh.AttrDevName:
129+
p.Device = nlenc.String(a.Data)
130+
case dlh.AttrPortIndex:
131+
p.Port = int(nlenc.Uint32(a.Data))
132+
case dlh.AttrPortType:
133+
p.Type = PortType(nlenc.Uint16(a.Data))
134+
// Allow netdev/ibdev name to share the same "Name" field.
135+
case dlh.AttrPortNetdevName, dlh.AttrPortIbdevName:
136+
p.Name = nlenc.String(a.Data)
137+
}
138+
}
139+
140+
ps = append(ps, &p)
141+
}
142+
143+
return ps, nil
144+
}

client_linux_integration_test.go

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
//+build linux
2+
3+
package devlink_test
4+
5+
import (
6+
"testing"
7+
8+
"github.com/mdlayher/devlink"
9+
)
10+
11+
func TestLinuxClientIntegration(t *testing.T) {
12+
c, err := devlink.New()
13+
if err != nil {
14+
t.Fatalf("failed to open client: %v", err)
15+
}
16+
defer c.Close()
17+
18+
// TODO(mdlayher): expand upon this.
19+
20+
t.Run("devices", func(t *testing.T) {
21+
if _, err := c.Devices(); err != nil {
22+
t.Fatalf("failed to get devices: %v", err)
23+
}
24+
})
25+
26+
t.Run("ports", func(t *testing.T) {
27+
if _, err := c.Ports(); err != nil {
28+
t.Fatalf("failed to get devices: %v", err)
29+
}
30+
})
31+
}

client_others.go

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//+build !linux
2+
3+
package devlink
4+
5+
import (
6+
"fmt"
7+
"runtime"
8+
)
9+
10+
var (
11+
// errUnimplemented is returned by all functions on platforms that
12+
// cannot make use of devlink.
13+
errUnimplemented = fmt.Errorf("devlink not implemented on %s/%s",
14+
runtime.GOOS, runtime.GOARCH)
15+
)
16+
17+
var _ osClient = &client{}
18+
19+
// A client is an unimplemented devlink client.
20+
type client struct{}
21+
22+
// newClient always returns an error.
23+
func newClient() (*client, error) {
24+
return nil, errUnimplemented
25+
}
26+
27+
// Close implements osClient.
28+
func (c *client) Close() error {
29+
return errUnimplemented
30+
}
31+
32+
// CGroupStats implements osClient.
33+
func (c *client) Devices() ([]*Device, error) {
34+
return nil, errUnimplemented
35+
}
36+
37+
// PID implements osClient.
38+
func (c *client) Ports() ([]*Port, error) {
39+
return nil, errUnimplemented
40+
}

0 commit comments

Comments
 (0)