Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Various bugfixes #984

Merged
merged 13 commits into from
Jul 11, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 17 additions & 6 deletions cmd/incus/top.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,8 @@ func (c *cmdTop) Run(cmd *cobra.Command, args []string) error {
}

// These variables can be changed by the UI
refreshInterval := 5 * time.Second // default 5 seconds, could change this to a flag
sortingMethod := alphabetical // default is alphabetical, could change this to a flag
refreshInterval := 10 * time.Second // default 10 seconds, could change this to a flag
sortingMethod := alphabetical // default is alphabetical, could change this to a flag

// Start the ticker for periodic updates
ticker := time.NewTicker(refreshInterval)
Expand Down Expand Up @@ -228,13 +228,24 @@ type displayData struct {
}

func (dd *displayData) toStringArray(project bool) []string {
var memUsage string
var diskUsage string

if dd.memoryUsage > 0 {
memUsage = units.GetByteSizeStringIEC(int64(dd.memoryUsage), 2)
}

if dd.diskUsage > 0 {
diskUsage = units.GetByteSizeStringIEC(int64(dd.diskUsage), 2)
}

if project {
dataStringified := [5]string{
dd.project,
dd.instanceName,
fmt.Sprintf("%.2f", dd.cpuUsage),
units.GetByteSizeStringIEC(int64(dd.memoryUsage), 2),
units.GetByteSizeStringIEC(int64(dd.diskUsage), 2),
memUsage,
diskUsage,
}

return dataStringified[:]
Expand All @@ -243,8 +254,8 @@ func (dd *displayData) toStringArray(project bool) []string {
dataStringified := [4]string{
dd.instanceName,
fmt.Sprintf("%.2f", dd.cpuUsage),
units.GetByteSizeStringIEC(int64(dd.memoryUsage), 2),
units.GetByteSizeStringIEC(int64(dd.diskUsage), 2),
memUsage,
diskUsage,
}

return dataStringified[:]
Expand Down
11 changes: 8 additions & 3 deletions internal/server/device/disk.go
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,12 @@ func (d *disk) CanMigrate() bool {
}

// Remote disks are migratable.
if d.pool.Driver().Info().Remote {
if d.pool != nil && d.pool.Driver().Info().Remote {
return true
}

// Virtual disks are migratable.
if !slices.Contains([]string{diskSourceCloudInit, diskSourceAgent}, d.config["source"]) {
return true
}

Expand Down Expand Up @@ -547,7 +552,7 @@ func (d *disk) validateConfig(instConf instance.ConfigReader) error {
return fmt.Errorf("Failed checking if custom volume is exclusively attached to another instance: %w", err)
}

if remoteInstance != nil && remoteInstance.ID != instConf.ID() {
if dbVolume.ContentType != db.StoragePoolVolumeContentTypeNameISO && remoteInstance != nil && remoteInstance.ID != instConf.ID() {
return fmt.Errorf("Custom volume is already attached to an instance on a different node")
}

Expand Down Expand Up @@ -629,7 +634,7 @@ func (d *disk) validateConfig(instConf instance.ConfigReader) error {
return fmt.Errorf("Shared filesystem are incompatible with migration.stateful=true")
}

if d.config["pool"] == "" {
if d.config["pool"] == "" && !slices.Contains([]string{diskSourceCloudInit, diskSourceAgent}, d.config["source"]) {
return fmt.Errorf("Only Incus-managed disks are allowed with migration.stateful=true")
}

Expand Down
13 changes: 12 additions & 1 deletion internal/server/instance/drivers/driver_lxc.go
Original file line number Diff line number Diff line change
Expand Up @@ -8695,9 +8695,20 @@ func (d *lxc) getFSStats() (*metrics.MetricSet, error) {
return nil, fmt.Errorf("Failed to stat %s: %w", mountpoint, err)
}

isMounted := false
// Grab the pool information to compare.
poolStatfs, err := linux.StatVFS(internalUtil.VarPath("storage-pools", dev["pool"]))
if err != nil {
return nil, fmt.Errorf("Failed to stat %s: %w", mountpoint, err)
}

// Check if we have actual mount-specific information.
if statfs.Type == poolStatfs.Type && statfs.Blocks == poolStatfs.Blocks && statfs.Bfree == poolStatfs.Bfree && statfs.Bavail == poolStatfs.Bavail {
continue
}

// Check if mountPath is in mountMap
isMounted := false

for mountDev, mountInfo := range mountMap {
if mountInfo.Mountpoint != mountpoint {
continue
Expand Down
4 changes: 2 additions & 2 deletions internal/server/instance/drivers/driver_qemu.go
Original file line number Diff line number Diff line change
Expand Up @@ -6793,7 +6793,7 @@ func (d *qemu) migrateSendLive(pool storagePools.Pool, clusterMoveSourceName str

// Notify the shared disks that they're going to be accessed from another system.
for _, dev := range d.expandedDevices.Sorted() {
if dev.Config["type"] != "disk" || dev.Config["path"] == "/" {
if dev.Config["type"] != "disk" || dev.Config["path"] == "/" || dev.Config["pool"] == "" {
continue
}

Expand Down Expand Up @@ -7305,7 +7305,7 @@ func (d *qemu) MigrateReceive(args instance.MigrateReceiveArgs) error {

// Notify the shared disks that they're going to be accessed from another system.
for _, dev := range d.expandedDevices.Sorted() {
if dev.Config["type"] != "disk" || dev.Config["path"] == "/" {
if dev.Config["type"] != "disk" || dev.Config["path"] == "/" || dev.Config["pool"] == "" {
continue
}

Expand Down
2 changes: 1 addition & 1 deletion internal/server/storage/drivers/driver_lvm.go
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,7 @@ func (d *lvm) Info() Info {
DirectIO: true,
IOUring: true,
MountedRoot: false,
Buckets: true,
Buckets: !d.isRemote(),
}
}

Expand Down
13 changes: 10 additions & 3 deletions internal/server/storage/drivers/driver_lvm_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -778,9 +778,16 @@ func (d *lvm) activateVolume(vol Volume) (bool, error) {
}

if !util.PathExists(volDevPath) {
_, err := subprocess.RunCommand("lvchange", "--activate", "y", "--ignoreactivationskip", volDevPath)
if err != nil {
return false, fmt.Errorf("Failed to activate LVM logical volume %q: %w", volDevPath, err)
if d.clustered {
_, err := subprocess.RunCommand("lvchange", "--activate", "sy", "--ignoreactivationskip", volDevPath)
if err != nil {
return false, fmt.Errorf("Failed to activate LVM logical volume %q: %w", volDevPath, err)
}
} else {
_, err := subprocess.RunCommand("lvchange", "--activate", "y", "--ignoreactivationskip", volDevPath)
if err != nil {
return false, fmt.Errorf("Failed to activate LVM logical volume %q: %w", volDevPath, err)
}
}

d.logger.Debug("Activated logical volume", logger.Ctx{"volName": vol.Name(), "dev": volDevPath})
Expand Down
32 changes: 4 additions & 28 deletions internal/server/storage/drivers/driver_lvm_volumes.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@ import (
"os"
"os/exec"
"strings"
"time"

"golang.org/x/sys/unix"

Expand Down Expand Up @@ -167,35 +166,12 @@ func (d *lvm) CreateVolumeFromMigration(vol Volume, conn io.ReadWriteCloser, vol
return err
}

// Mark the volume for shared locking during live migration.
if vol.volType == VolumeTypeVM || vol.IsCustomBlock() {
volDevPath := d.lvmDevPath(d.config["lvm.vg_name"], vol.volType, vol.contentType, vol.Name())
_, err := subprocess.RunCommand("lvchange", "--activate", "sy", "--ignoreactivationskip", volDevPath)
if vol.IsVMBlock() {
fsVol := vol.NewVMBlockFilesystemVolume()
err := d.CreateVolumeFromMigration(fsVol, conn, volTargetArgs, preFiller, op)
if err != nil {
return err
}

go func(volDevPath string) {
// Attempt to re-lock as host-exclusive as soon as possible.
// This will only happen once the migration on the source system is fully over.
// Re-try every 10s until success as we can't predict how long a migration may take (from a few seconds to hours).
for {
_, err := subprocess.RunCommand("lvchange", "--activate", "ey", "--ignoreactivationskip", volDevPath)
if err == nil {
break
}

time.Sleep(10 * time.Second)
}
}(volDevPath)

if vol.IsVMBlock() {
fsVol := vol.NewVMBlockFilesystemVolume()
err := d.CreateVolumeFromMigration(fsVol, conn, volTargetArgs, preFiller, op)
if err != nil {
return err
}
}
}

return nil
Expand Down Expand Up @@ -869,7 +845,7 @@ func (d *lvm) RenameVolume(vol Volume, newVolName string, op *operations.Operati
// MigrateVolume sends a volume for migration.
func (d *lvm) MigrateVolume(vol Volume, conn io.ReadWriteCloser, volSrcArgs *migration.VolumeSourceArgs, op *operations.Operation) error {
if d.clustered && volSrcArgs.ClusterMove {
// Mark the volume for shared locking during live migration.
// Ensure the volume allows shared access.
if vol.volType == VolumeTypeVM || vol.IsCustomBlock() {
// Block volume.
volDevPath := d.lvmDevPath(d.config["lvm.vg_name"], vol.volType, vol.contentType, vol.Name())
Expand Down
2 changes: 1 addition & 1 deletion shared/subprocess/proc_windows.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import (
)

// Process struct. Has ability to set runtime arguments.
type Process struct {}
type Process struct{}

// GetPid returns the pid for the given process object.
func (p *Process) GetPid() (int64, error) {
Expand Down
4 changes: 2 additions & 2 deletions test/suites/metrics.sh
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,8 @@ test_metrics() {
incus config set core.metrics_authentication=false
curl -k -s -X GET "https://${metrics_addr}/1.0/metrics" | grep "name=\"c1\""

# Filesystem metrics should contain instance type
curl -k -s -X GET "https://${metrics_addr}/1.0/metrics" | grep "incus_filesystem_avail_bytes" | grep "type=\"container\""
# Check that metrics contain instance type
curl -k -s -X GET "https://${metrics_addr}/1.0/metrics" | grep "incus_cpu_effective_total" | grep "type=\"container\""

incus delete -f c1 c2
}
Loading