Skip to content
Permalink
Browse files

Improve libvirt size handling and formatting

  • Loading branch information
subuk committed Nov 24, 2019
1 parent 88697a0 commit 722729f238b21971799cb6dd51ad65342606ec99
@@ -59,7 +59,7 @@ type VirtualMachine struct {
VCpus int
Arch Arch
State VirtualMachineState
Memory uint // KiB
Memory uint64 // Bytes
Interfaces []*VirtualMachineAttachedInterface
Volumes []*VirtualMachineAttachedVolume
Config *VirtualMachineConfig
@@ -89,8 +89,8 @@ func (vm *VirtualMachine) IsRunning() bool {
return vm.State == StateRunning
}

func (vm *VirtualMachine) MemoryMiB() uint {
return vm.Memory / 1024
func (vm *VirtualMachine) MemoryMiB() uint64 {
return vm.Memory / 1024 / 1024
}

func (vm *VirtualMachine) Disks() []*VirtualMachineAttachedVolume {
@@ -14,7 +14,7 @@ type VolumeMetadata struct {
type Volume struct {
Type VolumeType
Path string
Size uint64 // MiB
Size uint64 // Bytes
Pool string
Format VolumeFormat
AttachedTo string
@@ -26,14 +26,6 @@ func (volume *Volume) Base() string {
return filepath.Base(volume.Path)
}

func (volume *Volume) SizeMb() uint64 {
return volume.Size
}

func (volume *Volume) SizeGb() uint64 {
return volume.Size / 1024
}

type VolumeRepository interface {
Get(path string) (*Volume, error)
GetByName(pool, name string) (*Volume, error)
@@ -6,19 +6,11 @@ type VolumePoolRepository interface {

type VolumePool struct {
Name string
Size uint64 // MiB
Used uint64 // MiB
Free uint64 // MiB
Size uint64 // Bytes
Used uint64 // Bytes
Free uint64 // Bytes
}

func (pool *VolumePool) UsagePercent() int {
return int(100 * pool.Used / pool.Size)
}

func (pool *VolumePool) FreeGB() uint64 {
return pool.Free / 1024
}

func (pool *VolumePool) SizeGb() uint64 {
return pool.Size / 1024
}
1 go.mod
@@ -4,6 +4,7 @@ go 1.12

require (
github.com/akamensky/argparse v0.0.0-20190829110830-5293d9863374
github.com/dustin/go-humanize v1.0.0
github.com/go-bindata/go-bindata v3.1.2+incompatible
github.com/google/uuid v1.1.1
github.com/gorilla/csrf v1.6.0
2 go.sum
@@ -3,6 +3,8 @@ github.com/akamensky/argparse v0.0.0-20190829110830-5293d9863374/go.mod h1:pdh+2
github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
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/dustin/go-humanize v1.0.0 h1:VSnTsYCnlFHaM2/igO1h6X3HA71jcobQuxemgkq4zYo=
github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk=
github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385 h1:clC1lXBpe2kTj2VHdaIu9ajZQe4kcEY9j0NsnDDBZ3o=
github.com/eknkc/amber v0.0.0-20171010120322-cdade1c07385/go.mod h1:0vRUJqYpeSZifjYj7uP3BG/gKcuzL9xWVV/Y+cK33KM=
github.com/go-bindata/go-bindata v3.1.2+incompatible h1:5vjJMVhowQdPzjE1LdxyFF7YFTXg5IgGVW4gBr5IbvE=
@@ -1,16 +1,21 @@
package libvirt

import (
"fmt"
"strconv"
"strings"
)

func ParseLibvirtSizeToMegabytes(unit string, value uint64) uint64 {
func ParseLibvirtSizeToBytes(unit string, value uint64) uint64 {
switch unit {
default:
panic("unknown storage pool capacity unit")
panic(fmt.Sprintf("unknown libvirt size unit '%s'", unit))
case "bytes":
return value / 1024 / 1024
return value
case "KiB":
return value * 1024
case "MiB":
return value * 1024 * 1024
}
}

@@ -1,11 +1,10 @@
package libvirt

import (
"fmt"
"subuk/vmango/compute"

"github.com/libvirt/libvirt-go"
"github.com/libvirt/libvirt-go-xml"
libvirtxml "github.com/libvirt/libvirt-go-xml"
)

func VirtualMachineAttachedVolumeFromDomainDiskConfig(diskConfig libvirtxml.DomainDisk) *compute.VirtualMachineAttachedVolume {
@@ -63,6 +62,7 @@ func VirtualMachineFromDomainConfig(domainConfig *libvirtxml.Domain, domainInfo
vm := &compute.VirtualMachine{}
vm.Id = domainConfig.Name
vm.VCpus = domainConfig.VCPU.Value
vm.Memory = ParseLibvirtSizeToBytes(domainConfig.Memory.Unit, uint64(domainConfig.Memory.Value))

switch domainConfig.OS.Type.Arch {
default:
@@ -92,16 +92,6 @@ func VirtualMachineFromDomainConfig(domainConfig *libvirtxml.Domain, domainInfo
vm.State = compute.StateStopped
}

switch domainConfig.Memory.Unit {
case "KiB":
vm.Memory = domainConfig.Memory.Value
case "MiB":
vm.Memory = domainConfig.Memory.Value * 1024
case "GiB":
vm.Memory = domainConfig.Memory.Value * 1024
default:
return nil, fmt.Errorf("unknown memory unit '%s' for domain %s", domainConfig.Memory.Unit, domainConfig.Name)
}
if domainConfig.CPUTune != nil {
vm.Cpupin = &compute.VirtualMachineCpuPin{
Vcpus: map[uint][]uint{},
@@ -4,7 +4,7 @@ import (
"subuk/vmango/compute"
"subuk/vmango/util"

"github.com/libvirt/libvirt-go-xml"
libvirtxml "github.com/libvirt/libvirt-go-xml"
)

type VolumePoolRepository struct {
@@ -48,13 +48,13 @@ func (repo *VolumePoolRepository) List() ([]*compute.VolumePool, error) {
}
// Size: virPoolConfig.Capacity
if virPoolConfig.Capacity != nil {
volumePool.Size = ParseLibvirtSizeToMegabytes(virPoolConfig.Capacity.Unit, virPoolConfig.Capacity.Value)
volumePool.Size = ParseLibvirtSizeToBytes(virPoolConfig.Capacity.Unit, virPoolConfig.Capacity.Value)
}
if virPoolConfig.Allocation != nil {
volumePool.Used = ParseLibvirtSizeToMegabytes(virPoolConfig.Allocation.Unit, virPoolConfig.Allocation.Value)
volumePool.Used = ParseLibvirtSizeToBytes(virPoolConfig.Allocation.Unit, virPoolConfig.Allocation.Value)
}
if virPoolConfig.Available != nil {
volumePool.Free = ParseLibvirtSizeToMegabytes(virPoolConfig.Available.Unit, virPoolConfig.Available.Value)
volumePool.Free = ParseLibvirtSizeToBytes(virPoolConfig.Available.Unit, virPoolConfig.Available.Value)
}
volumePools = append(volumePools, volumePool)
}
@@ -43,6 +43,7 @@ func (repo *VolumeRepository) virVolumeToVolume(pool *libvirt.StoragePool, virVo
volume.Pool = poolConfig.Name
volume.Format = compute.FormatUnknown
volume.Metadata = repo.metadata[virVolumeConfig.Target.Path]
volume.Size = ParseLibvirtSizeToBytes(virVolumeConfig.Capacity.Unit, virVolumeConfig.Capacity.Value)

switch poolConfig.Type {
case "logical":
@@ -59,13 +60,6 @@ func (repo *VolumeRepository) virVolumeToVolume(pool *libvirt.StoragePool, virVo
volume.Format = compute.FormatRaw
}
}

switch virVolumeConfig.Capacity.Unit {
default:
return nil, fmt.Errorf("unknown volume capacity unit '%s'", virVolumeConfig.Capacity.Unit)
case "bytes":
volume.Size = virVolumeConfig.Capacity.Value / 1024 / 1024
}
return volume, nil
}

@@ -60,8 +60,8 @@ <h6>Volume Pools</h6>
{{ range .VolumePools }}
<tr>
<td>{{ .Name }}</td>
<td>{{ .FreeGB }}G</td>
<td>{{ .SizeGb }}G</td>
<td>{{ .Free | HumanizeBytes }}</td>
<td>{{ .Size | HumanizeBytes }}</td>
<td>{{ .UsagePercent }}%</td>
</tr>
{{ end }}
@@ -49,7 +49,7 @@ <h4>Create Virtual Machine</h4>
<label>Volume Pool</label>
<select required="required" class="form-control" name="RootVolumePool">
{{ range .Pools }}
<option value="{{ .Name }}">{{ .Name }} ({{ .FreeGB }}G free {{ .UsagePercent }}% used)</option>
<option value="{{ .Name }}">{{ .Name }} ({{ .Free | HumanizeBytes }} free {{ .UsagePercent }}% used)</option>
{{ end }}
</select>
</div>
@@ -20,7 +20,7 @@ <h1>{{ .Vm.Id }}</h1>
{{ if .Vm.Config }}Configured<br>{{ end }}
Autostart {{ if .Vm.Autostart }}enabled{{ else }}disabled{{ end }}<br>
{{ if .Vm.GuestAgent }}Guest agent integration enabled<br>{{ end }}
{{ .Vm.MemoryMiB }}MiB RAM, {{ .Vm.VCpus }} CPU<br>
{{ .Vm.Memory | HumanizeBytes }} RAM, {{ .Vm.VCpus }} CPU<br>
{{ .Vm.Arch }}<br>
{{ if .Vm.Cpupin }}
Emulator: {{ .Vm.Cpupin.Emulator | JoinUint "," }}<br>
@@ -72,7 +72,7 @@ <h5 class="card-title">Volume {{ .Base }}</h5>
<p class="card-text">
Pool: {{ .Pool }}<br>
Path: {{ .Path }}<br>
Size: {{ if lt .Size 1024 }}{{ .SizeMb }}M{{ else }}{{ .SizeGb }}G{{ end }}<br>
Size: {{ .Size | HumanizeBytes }}<br>
</p>
<form method="post" action="{{ Url "virtual-machine-detach-volume" "id" $.Vm.Id }}">{{ CSRFField $.Request }}
<input type="hidden" name="Path" value="{{ .Path }}">
@@ -31,7 +31,7 @@ <h4 class="card-title">Machines</h4>
<td><a href="{{ Url "virtual-machine-detail" "id" .Id }}">{{ .Id }}</a></td>
<td>{{ .State }}</td>
<td>{{ .VCpus }}</td>
<td>{{ .MemoryMiB }}MiB</td>
<td>{{ .Memory | HumanizeBytes }}</td>
<td>{{ .IpAddressList | Join " " }}</td>
</tr>
{{ end }}
@@ -35,7 +35,7 @@ <h4 class="card-title">Volumes</h4>
<div class="col-md-3">
<select required="required" class="form-control" name="Pool">
{{ range .Pools }}
<option value="{{ .Name }}">{{ .Name }} ({{ .FreeGB }}G free {{ .UsagePercent }}% used)</option>
<option value="{{ .Name }}">{{ .Name }} ({{ .Free | HumanizeBytes }} free {{ .UsagePercent }}% used)</option>
{{ end }}
</select>
<small id="nameHelp" class="form-text text-muted">Pool</small>
@@ -66,7 +66,7 @@ <h4 class="card-title">Volumes</h4>
<td>{{ .Path }}</td>
<td>{{ .Type }}</td>
<td>{{ .Format }}</td>
<td>{{ .Size }}MiB</td>
<td>{{ .Size | HumanizeBytes }}</td>
<td>{{ .Pool }}</td>
<td>
{{ if .AttachedTo }}
@@ -12,6 +12,7 @@ import (
"subuk/vmango/util"
"time"

"github.com/dustin/go-humanize"
"github.com/gorilla/csrf"
"github.com/gorilla/mux"
"github.com/gorilla/sessions"
@@ -43,20 +44,8 @@ func TemplateFuncs(env *Environ) []template.FuncMap {
"Version": func() string {
return AppVersion
},
"HumanizeBytes": func(max int, number uint64) string {
var suffixes = []string{"b", "K", "M", "G", "T"}
i := 0
for {
if number < 10240 {
break
}
number = number / 1024
i++
if i >= max || i >= len(suffixes) {
break
}
}
return fmt.Sprintf("%d%s", number, suffixes[i])
"HumanizeBytes": func(number uint64) string {
return humanize.IBytes(number)
},
"LimitString": func(limit int, s string) string {
slen := len(s)

0 comments on commit 722729f

Please sign in to comment.
You can’t perform that action at this time.