Skip to content

Commit

Permalink
Add parser for PVE storage
Browse files Browse the repository at this point in the history
  • Loading branch information
iBug committed Apr 3, 2024
1 parent 1057670 commit bea7e54
Show file tree
Hide file tree
Showing 7 changed files with 255 additions and 7 deletions.
5 changes: 4 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
BIN := vct
VERSION := $(shell git describe --tags --always --dirty)

.PHONY: all $(BIN) clean
.PHONY: all $(BIN) test clean

all: $(BIN)

$(BIN):
go build -ldflags='-s -w -X main.version=$(VERSION)'

test:
go test -v ./...

clean:
rm -f $(BIN)
8 changes: 7 additions & 1 deletion cmd/iolimit/iolimit.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,11 @@ func MakeCmd() *cobra.Command {
}

func iolimitMain(ctid string, iops cgroup.IOPS) error {
return cgroup.SetIOPSForLXC(ctid, cgroup.IOPSLine{IOPS: iops})
// TODO: Find major:minor
iopsline := cgroup.IOPSLine{
Major: 0,
Minor: 0,
IOPS: iops,
}
return cgroup.SetIOPSForLXC(ctid, iopsline)
}
6 changes: 5 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@ go 1.20
require (
github.com/ryanuber/columnize v2.1.2+incompatible
github.com/spf13/cobra v1.8.0
golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3
github.com/stretchr/testify v1.9.0
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8
)

require (
github.com/davecgh/go-spew v1.1.1 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
12 changes: 10 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
@@ -1,14 +1,22 @@
github.com/cpuguy83/go-md2man/v2 v2.0.3/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/ryanuber/columnize v2.1.2+incompatible h1:C89EOx/XBWwIXl8wm8OPJBd7kPF25UfsK2X7Ph/zCAk=
github.com/ryanuber/columnize v2.1.2+incompatible/go.mod h1:sm1tb6uqfes/u+d4ooFouqFdy9/2g9QGwK3SQygK0Ts=
github.com/spf13/cobra v1.8.0 h1:7aJaZx1B85qltLMc546zn58BxxfZdR/W22ej9CFoEf0=
github.com/spf13/cobra v1.8.0/go.mod h1:WXLWApfZ71AjXPya3WOlMsY9yMs7YeiHhFVlvLyhcho=
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 h1:hNQpMuAJe5CtcUqCXaWga3FHu+kQvCqcsoVaQgSV60o=
golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3/go.mod h1:idGWGoKP1toJGkd5/ig9ZLuPcZBC3ewk7SzmH0uou08=
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8 h1:aAcj0Da7eBAtrTp03QXWvm88pSyOt+UgdZw2BFZ+lEw=
golang.org/x/exp v0.0.0-20240325151524-a685a6edb6d8/go.mod h1:CQ1k9gNrJ50XIzaKCRR2hssIjF07kZFEiieALBM/ARQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
53 changes: 51 additions & 2 deletions pkg/pve/pct.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
package pve

import "os/exec"
import (
"bufio"
"fmt"
"io"
"os"
"os/exec"
"strings"
)

const PctPath = "/usr/sbin/pct"
const (
EtcPve = "/etc/pve"
PctPath = "/usr/sbin/pct"
)

func PctCmd(args ...string) *exec.Cmd {
return exec.Command(PctPath, args...)
Expand All @@ -11,3 +21,42 @@ func PctCmd(args ...string) *exec.Cmd {
func StopCmd(vmid string) *exec.Cmd {
return PctCmd("stop", vmid)
}

func parseConfig(r io.Reader) map[string]string {
config := make(map[string]string)
scanner := bufio.NewScanner(r)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if comment, ok := strings.CutPrefix(line, "#"); ok {
config["#"] += comment + "\n"
continue
}
fields := strings.Fields(line)
if len(fields) != 2 {
// Invalid line?
continue
}
config[fields[0]] = fields[1]
}
return config
}

func GetConfig(typ, vmid string) (map[string]string, error) {
if typ == "qemu" {
typ = "qemu-server"
}
f, err := os.Open(fmt.Sprintf("%s/%s/%s.cfg", EtcPve, typ, vmid))
if err != nil {
return nil, err
}
defer f.Close()
return parseConfig(f), nil
}

func GetLXCConfig(vmid string) (map[string]string, error) {
return GetConfig("lxc", vmid)
}

func GetQemuConfig(vmid string) (map[string]string, error) {
return GetConfig("qemu", vmid)
}
121 changes: 121 additions & 0 deletions pkg/pve/storage.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package pve

import (
"bufio"
"fmt"
"io"
"io/fs"
"os"
"slices"
"strings"
"syscall"
)

const StorageConf = "/etc/pve/storage.cfg"

type PVEStorage struct {
Type string
Name string
Attr map[string]string
}

func newPVEStorage() PVEStorage {
return PVEStorage{
Attr: make(map[string]string),
}
}

func parseStorage(r io.Reader) []PVEStorage {
items := make([]PVEStorage, 0)
item := newPVEStorage()
scanner := bufio.NewScanner(r)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
if strings.HasPrefix(line, "#") {
continue
}
fields := strings.Fields(line)
if len(fields) != 2 {
// Invalid line?
continue
}
if strings.HasSuffix(fields[0], ":") {
if item.Type != "" {
items = append(items, item)
item = newPVEStorage()
}
item.Type = strings.TrimSuffix(fields[0], ":")
item.Name = fields[1]
} else {
item.Attr[fields[0]] = fields[1]
}
}
if item.Type != "" {
items = append(items, item)
}
return items
}

func GetStorage() ([]PVEStorage, error) {
f, err := os.Open(StorageConf)
if os.IsNotExist(err) {
return nil, nil
} else if err != nil {
return nil, err
}
defer f.Close()
return parseStorage(f), nil
}

func devMajorMinor(device uint64) (major, minor uint64) {
major = (device >> 8) & 0xfff
minor = (device & 0xff) | ((device >> 12) & 0xfff00)
return
}

func getBlockDevForDir(dir string) (uint64, uint64, error) {
fileInfo, err := os.Lstat(dir)
if err != nil {
return 0, 0, err
}
stat, ok := fileInfo.Sys().(*syscall.Stat_t)
if !ok {
return 0, 0, fmt.Errorf("failed to get backing device for %s: %w", dir, err)
}
major, minor := devMajorMinor(stat.Dev)
return major, minor, nil
}

func getBlockDevForLVM(vgname, lvname string) (uint64, uint64, error) {
fileInfo, err := os.Stat(fmt.Sprintf("/dev/%s/%s", vgname, lvname))
if err != nil {
return 0, 0, err
}
stat, ok := fileInfo.Sys().(*syscall.Stat_t)
if !ok {
return 0, 0, fmt.Errorf("failed to get device number for %s/%s: %w", vgname, lvname, err)
}
major, minor := devMajorMinor(stat.Rdev)
return major, minor, nil
}

// Finds the block device for the given storage and name.
// The "aux" parameter is used to help determine the type of the storage.
func GetBlockDevForStorage(storage, name string, aux []PVEStorage) (uint64, uint64, error) {
i := slices.IndexFunc(aux, func(s PVEStorage) bool {
return s.Name == name
})
if i == -1 {
return 0, 0, fs.ErrNotExist
}
info := aux[i]
switch info.Type {
case "dir":
return getBlockDevForDir(storage)
case "lvm", "lvmthin":
vgname := info.Attr["vgname"]
return getBlockDevForLVM(vgname, name)
default:
return 0, 0, fmt.Errorf("unsupported storage type %s", info.Type)
}
}
57 changes: 57 additions & 0 deletions pkg/pve/storage_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
package pve

import (
"strings"
"testing"

"github.com/stretchr/testify/assert"
)

const testStorageCfg = `dir: local
path /var/lib/vz
content vztmpl,iso,images
lvmthin: local-lvm
thinpool data
vgname pve
content rootdir,images
dir: nfs-template
path /mnt/vz
content vztmpl,iso,images
shared 1
`

var testStorage = []PVEStorage{
{
Type: "dir",
Name: "local",
Attr: map[string]string{
"path": "/var/lib/vz",
"content": "vztmpl,iso,images",
},
},
{
Type: "lvmthin",
Name: "local-lvm",
Attr: map[string]string{
"thinpool": "data",
"vgname": "pve",
"content": "rootdir,images",
},
},
{
Type: "dir",
Name: "nfs-template",
Attr: map[string]string{
"path": "/mnt/vz",
"content": "vztmpl,iso,images",
"shared": "1",
},
},
}

func TestParseStorage(t *testing.T) {
as := assert.New(t)
as.Equal(testStorage, parseStorage(strings.NewReader(testStorageCfg)))
}

0 comments on commit bea7e54

Please sign in to comment.