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

Import LXD changes #253

Merged
merged 26 commits into from
Nov 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
32b653d
[lxd-import] lxd/storage/drivers: Add new cephfs create keys
masnax Nov 20, 2023
d5a6f47
[lxd-import] lxd/storage/drivers: Update cephfs entity helpers
masnax Nov 20, 2023
98a2bfc
[lxd-import] lxd/storage/drivers: Add DefaultVMBlockFilesystemSize to…
roosterfish Oct 16, 2023
83dfbab
[lxd-import] lxd/storage/drivers/btrfs: Set drivers DefaultVMBlockFil…
roosterfish Oct 16, 2023
2724271
[lxd-import] lxd/storage/drivers/ceph: Set drivers DefaultVMBlockFile…
roosterfish Oct 16, 2023
98ae1c7
[lxd-import] lxd/storage/drivers/cephfs: Set drivers DefaultVMBlockFi…
roosterfish Oct 16, 2023
d617169
[lxd-import] lxd/storage/drivers/dir: Set drivers DefaultVMBlockFiles…
roosterfish Oct 16, 2023
15f24a7
[lxd-import] lxd/storage/drivers/lvm: Set drivers DefaultVMBlockFiles…
roosterfish Oct 16, 2023
0399c6c
[lxd-import] lxd/storage/drivers/mock: Set drivers DefaultVMBlockFile…
roosterfish Oct 16, 2023
b974d35
[lxd-import] lxd/storage/drivers/zfs: Set drivers DefaultVMBlockFiles…
roosterfish Oct 16, 2023
b9f7cd9
[lxd-import] lxd/storage/backend: Use drivers default VM block volume…
roosterfish Oct 2, 2023
2a9f737
[lxd-import] lxd/storage/drivers/volume: Use drivers default VM block…
roosterfish Oct 13, 2023
cdbaa56
[lxd-import] lxd/project: Fix typo in comment
roosterfish Nov 24, 2023
4bf263a
[lxd-import] lxd/instance/drivers: Use the pools default VM block fil…
roosterfish Nov 24, 2023
e812f56
[lxd-import] lxd/storage: Use the pools default VM block filesystem size
roosterfish Nov 24, 2023
46ccb64
[lxd-import] lxd/project: Add TODO for instance limits accounting
roosterfish Nov 28, 2023
077eb1c
[lxd-import] lxd/instance: Use stable random generator for temporary …
roosterfish Oct 9, 2023
70d92cf
[lxd-import] lxd/instance: Improve error message
roosterfish Nov 23, 2023
a05a497
[lxd-import] lxd/instance/drivers/qemu: Run specific remote config on…
roosterfish Sep 20, 2023
ff81ace
[lxd-import] lxd/storage/drivers: Create cephfs entities if keys spec…
masnax Nov 20, 2023
b516a1a
[lxd-import] lxd/storage/drivers: Revert osd/fs creation
masnax Nov 28, 2023
20a5687
[lxd-import] doc/reference: Add doc reference for new config keys
masnax Nov 20, 2023
221826e
[lxd-import] shared/version: Add storage_cephfs_create_missing extension
masnax Nov 20, 2023
3b0352c
[lxd-import] lxd/storage/drivers: Collect subvolumes via filepath tra…
markylaing Nov 28, 2023
6d346ca
[lxd-import] doc/howto: Make pool name consistent in iso tutorial.
markylaing Nov 29, 2023
d416550
[lxd-import] test/suites: Add cephfs create_missing test
masnax Nov 28, 2023
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
3 changes: 3 additions & 0 deletions doc/api-extensions.md
Original file line number Diff line number Diff line change
Expand Up @@ -2284,3 +2284,6 @@ Calling `POST /1.0/storage-pools/<pool>/custom/<volume>?target=<target>` will mo

## `disk_io_bus`
This introduces a new `io.bus` property to disk devices which can be used to override the bus the disk is attached to.

## `storage_cephfs_create_missing`
This introduces the configuration keys `cephfs.create_missing`, `cephfs.osd_pg_num`, `cephfs.meta_pool` and `cephfs.osd_pool` to be used when adding a `cephfs` storage pool to instruct Incus to create the necessary entities for the storage pool, if they do not exist.
2 changes: 1 addition & 1 deletion doc/howto/instances_create.md
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ The second step is to import an ISO image that can later be attached to the VM a

Lastly, you need to attach the custom ISO volume to the VM using the following command:

incus config device add iso-vm iso-volume disk pool=default source=iso-volume boot.priority=10
incus config device add iso-vm iso-volume disk pool=<pool> source=iso-volume boot.priority=10

The `boot.priority` configuration key ensures that the VM will boot from the ISO first.
Start the VM and connect to the console as there might be a menu you need to interact with:
Expand Down
4 changes: 4 additions & 0 deletions doc/reference/storage_cephfs.md
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ Key | Type | Default
`cephfs.fscache` | bool | `false` | Enable use of kernel `fscache` and `cachefilesd`
`cephfs.path` | string | `/` | The base path for the CephFS mount
`cephfs.user.name` | string | `admin` | The Ceph user to use
`cephfs.create_missing` | bool | `false` | Create the file-system and missing data and metadata OSD pools
`cephfs.osd_pg_num` | string | - | OSD pool `pg_num` to use when creating missing OSD pools
`cephfs.meta_pool` | string | - | Metadata OSD pool name to create for the file-system
`cephfs.data_pool` | string | - | Data OSD pool name to create for the file-system
`source` | string | - | Existing CephFS file system or file system path to use
`volatile.pool.pristine` | string | `true` | Whether the CephFS file system was empty on creation time

Expand Down
24 changes: 13 additions & 11 deletions internal/server/instance/drivers/driver_qemu.go
Original file line number Diff line number Diff line change
Expand Up @@ -1044,7 +1044,7 @@ func (d *qemu) validateStartup(stateful bool, statusCode api.StatusCode) error {
return err
}

stateDiskSizeStr := deviceConfig.DefaultVMBlockFilesystemSize
stateDiskSizeStr := d.storagePool.Driver().Info().DefaultVMBlockFilesystemSize
if rootDiskDevice["size.state"] != "" {
stateDiskSizeStr = rootDiskDevice["size.state"]
}
Expand Down Expand Up @@ -3565,19 +3565,21 @@ func (d *qemu) addRootDriveConfig(qemuDev map[string]string, mountInfo *storageP
if d.storagePool.Driver().Info().Remote {
vol := d.storagePool.GetVolume(storageDrivers.VolumeTypeVM, storageDrivers.ContentTypeBlock, project.Instance(d.project.Name, d.name), nil)

config := d.storagePool.ToAPI().Config
if util.ValueInSlice(d.storagePool.Driver().Info().Name, []string{"ceph", "cephfs"}) {
config := d.storagePool.ToAPI().Config

userName := config["ceph.user.name"]
if userName == "" {
userName = storageDrivers.CephDefaultUser
}
userName := config["ceph.user.name"]
if userName == "" {
userName = storageDrivers.CephDefaultUser
}

clusterName := config["ceph.cluster_name"]
if clusterName == "" {
clusterName = storageDrivers.CephDefaultUser
}
clusterName := config["ceph.cluster_name"]
if clusterName == "" {
clusterName = storageDrivers.CephDefaultUser
}

driveConf.DevPath = device.DiskGetRBDFormat(clusterName, userName, config["ceph.osd.pool_name"], vol.Name())
driveConf.DevPath = device.DiskGetRBDFormat(clusterName, userName, config["ceph.osd.pool_name"], vol.Name())
}
}

return d.addDriveConfig(qemuDev, bootIndexes, driveConf)
Expand Down
34 changes: 27 additions & 7 deletions internal/server/instance/instance_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import (
"github.com/lxc/incus/internal/server/seccomp"
"github.com/lxc/incus/internal/server/state"
"github.com/lxc/incus/internal/server/sys"
localUtil "github.com/lxc/incus/internal/server/util"
internalUtil "github.com/lxc/incus/internal/util"
"github.com/lxc/incus/internal/version"
"github.com/lxc/incus/shared/api"
Expand Down Expand Up @@ -1031,9 +1032,18 @@ func NextSnapshotName(s *state.State, inst Instance, defaultPattern string) (str
return pattern, nil
}

// temporaryName concatenates the move prefix and instUUID for a temporary instance.
func temporaryName(instUUID string) string {
return fmt.Sprintf("move-of-%s", instUUID)
// temporaryName returns the temporary instance name using a stable random generator.
// The returned string is a valid DNS name.
func temporaryName(instUUID string) (string, error) {
r, err := localUtil.GetStableRandomGenerator(instUUID)
if err != nil {
return "", err
}

// The longest temporary name is move-of-18446744073709551615 which has a length
// of 30 characters since 18446744073709551615 is the biggest value for an uint64.
// The prefix is attached to have a valid DNS name that doesn't start with numbers.
return fmt.Sprintf("move-of-%d", r.Uint64()), nil
}

// MoveTemporaryName returns a name derived from the instance's volatile.uuid, to use when moving an instance
Expand All @@ -1046,11 +1056,11 @@ func MoveTemporaryName(inst Instance) (string, error) {
instUUID = uuid.New().String()
err := inst.VolatileSet(map[string]string{"volatile.uuid": instUUID})
if err != nil {
return "", fmt.Errorf("Failed generating instance UUID: %w", err)
return "", fmt.Errorf("Failed setting volatile.uuid to %s: %w", instUUID, err)
}
}

return temporaryName(instUUID), nil
return temporaryName(instUUID)
}

// IsSameLogicalInstance returns true if the supplied Instance and db.Instance have the same project and name or
Expand All @@ -1065,12 +1075,22 @@ func IsSameLogicalInstance(inst Instance, dbInst *db.InstanceArgs) bool {
if dbInst.Config["volatile.uuid"] == inst.LocalConfig()["volatile.uuid"] {
// Accommodate moving instances between storage pools.
// Check temporary copy against source.
if dbInst.Name == temporaryName(inst.LocalConfig()["volatile.uuid"]) {
tempName, err := temporaryName(inst.LocalConfig()["volatile.uuid"])
if err != nil {
return false
}

if dbInst.Name == tempName {
return true
}

// Check source against temporary copy.
if inst.Name() == temporaryName(dbInst.Config["volatile.uuid"]) {
tempName, err = temporaryName(dbInst.Config["volatile.uuid"])
if err != nil {
return false
}

if inst.Name() == tempName {
return true
}

Expand Down
4 changes: 3 additions & 1 deletion internal/server/project/permissions.go
Original file line number Diff line number Diff line change
Expand Up @@ -1251,7 +1251,7 @@ func expandInstancesConfigAndDevices(instances []api.Instance, profiles []api.Pr
}

// Sum of the effective values for the given limits across all project
// enties (instances and custom volumes).
// entities (instances and custom volumes).
func getTotalsAcrossProjectEntities(info *projectInfo, keys []string, skipUnset bool) (map[string]int64, error) {
totals := map[string]int64{}

Expand Down Expand Up @@ -1329,6 +1329,8 @@ func getInstanceLimits(inst api.Instance, keys []string, skipUnset bool) (map[st
if inst.Type == instancetype.VM.String() {
sizeStateValue, ok := device["size.state"]
if !ok {
// TODO: In case the VMs storage drivers config drive size isn't the default,
// the limits accounting will be incorrect.
sizeStateValue = deviceconfig.DefaultVMBlockFilesystemSize
}

Expand Down
5 changes: 2 additions & 3 deletions internal/server/storage/backend.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,6 @@ import (
"github.com/lxc/incus/internal/server/cluster/request"
"github.com/lxc/incus/internal/server/db"
"github.com/lxc/incus/internal/server/db/cluster"
deviceConfig "github.com/lxc/incus/internal/server/device/config"
"github.com/lxc/incus/internal/server/instance"
"github.com/lxc/incus/internal/server/instance/instancetype"
"github.com/lxc/incus/internal/server/lifecycle"
Expand Down Expand Up @@ -918,7 +917,7 @@ func (b *backend) CreateInstanceFromBackup(srcBackup backup.Info, srcData io.Rea
// filesystem volume as well, allowing a former quota to be removed from both
// volumes.
if vmStateSize == "" && size != "" {
vmStateSize = deviceConfig.DefaultVMBlockFilesystemSize
vmStateSize = b.driver.Info().DefaultVMBlockFilesystemSize
}

l.Debug("Applying filesystem volume quota from root disk config", logger.Ctx{"size.state": vmStateSize})
Expand Down Expand Up @@ -2636,7 +2635,7 @@ func (b *backend) SetInstanceQuota(inst instance.Instance, size string, vmStateS
// this will also pass empty quota for the config filesystem volume as well, allowing a former
// quota to be removed from both volumes.
if vmStateSize == "" && size != "" {
vmStateSize = deviceConfig.DefaultVMBlockFilesystemSize
vmStateSize = b.driver.Info().DefaultVMBlockFilesystemSize
}

fsVol := vol.NewVMBlockFilesystemVolume()
Expand Down
30 changes: 16 additions & 14 deletions internal/server/storage/drivers/driver_btrfs.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (
"github.com/lxc/incus/internal/linux"
"github.com/lxc/incus/internal/migration"
"github.com/lxc/incus/internal/revert"
deviceConfig "github.com/lxc/incus/internal/server/device/config"
localMigration "github.com/lxc/incus/internal/server/migration"
"github.com/lxc/incus/internal/server/operations"
internalUtil "github.com/lxc/incus/internal/util"
Expand Down Expand Up @@ -91,20 +92,21 @@ func (d *btrfs) load() error {
// Info returns info about the driver and its environment.
func (d *btrfs) Info() Info {
return Info{
Name: "btrfs",
Version: btrfsVersion,
OptimizedImages: true,
OptimizedBackups: true,
OptimizedBackupHeader: true,
PreservesInodes: !d.state.OS.RunningInUserNS,
Remote: d.isRemote(),
VolumeTypes: []VolumeType{VolumeTypeBucket, VolumeTypeCustom, VolumeTypeImage, VolumeTypeContainer, VolumeTypeVM},
BlockBacking: false,
RunningCopyFreeze: false,
DirectIO: true,
IOUring: true,
MountedRoot: true,
Buckets: true,
Name: "btrfs",
Version: btrfsVersion,
DefaultVMBlockFilesystemSize: deviceConfig.DefaultVMBlockFilesystemSize,
OptimizedImages: true,
OptimizedBackups: true,
OptimizedBackupHeader: true,
PreservesInodes: !d.state.OS.RunningInUserNS,
Remote: d.isRemote(),
VolumeTypes: []VolumeType{VolumeTypeBucket, VolumeTypeCustom, VolumeTypeImage, VolumeTypeContainer, VolumeTypeVM},
BlockBacking: false,
RunningCopyFreeze: false,
DirectIO: true,
IOUring: true,
MountedRoot: true,
Buckets: true,
}
}

Expand Down
72 changes: 52 additions & 20 deletions internal/server/storage/drivers/driver_btrfs_utils.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import (
"context"
"fmt"
"io"
"io/fs"
"os"
"os/exec"
"path/filepath"
Expand Down Expand Up @@ -107,39 +108,70 @@ func (d *btrfs) hasSubvolumes(path string) (bool, error) {
}

func (d *btrfs) getSubvolumes(path string) ([]string, error) {
// Make sure the path has a trailing slash.
if !strings.HasSuffix(path, "/") {
path = path + "/"
}

poolMountPath := GetPoolMountPath(d.name)
if !strings.HasPrefix(path, poolMountPath+"/") {
return nil, fmt.Errorf("%q is outside pool mount path %q", path, poolMountPath)
}

path = strings.TrimPrefix(path, poolMountPath+"/")
var result []string

// Make sure the path has a trailing slash.
if !strings.HasSuffix(path, "/") {
path = path + "/"
}
if d.state.OS.RunningInUserNS {
// If using BTRFS in a nested container we cannot use "btrfs subvolume list" due to a permission error.
// So instead walk the directory tree testing each directory to see if it is subvolume.
err := filepath.Walk(path, func(fpath string, entry fs.FileInfo, err error) error {
if err != nil {
return err
}

var stdout bytes.Buffer
err := subprocess.RunCommandWithFds(d.state.ShutdownCtx, nil, &stdout, "btrfs", "subvolume", "list", poolMountPath)
if err != nil {
return nil, err
}
// Ignore the base path.
if strings.TrimRight(fpath, "/") == strings.TrimRight(path, "/") {
return nil
}

result := []string{}
// Subvolumes can only be directories.
if !entry.IsDir() {
return nil
}

scanner := bufio.NewScanner(&stdout)
for scanner.Scan() {
fields := strings.Fields(scanner.Text())
// Check if directory is a subvolume.
if d.isSubvolume(fpath) {
result = append(result, strings.TrimPrefix(fpath, path))
}

if len(fields) != 9 {
continue
return nil
})
if err != nil {
return nil, err
}

if !strings.HasPrefix(fields[8], path) {
continue
} else {
// If not running inside a nested container we can use "btrfs subvolume list" to get subvolumes which is more
// performant than walking the directory tree.
var stdout bytes.Buffer
err := subprocess.RunCommandWithFds(d.state.ShutdownCtx, nil, &stdout, "btrfs", "subvolume", "list", poolMountPath)
if err != nil {
return nil, err
}

result = append(result, strings.TrimPrefix(fields[8], path))
path = strings.TrimPrefix(path, poolMountPath+"/")
scanner := bufio.NewScanner(&stdout)
for scanner.Scan() {
fields := strings.Fields(scanner.Text())

if len(fields) != 9 {
continue
}

if !strings.HasPrefix(fields[8], path) {
continue
}

result = append(result, strings.TrimPrefix(fields[8], path))
}
}

return result, nil
Expand Down
24 changes: 13 additions & 11 deletions internal/server/storage/drivers/driver_ceph.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

"github.com/lxc/incus/internal/migration"
"github.com/lxc/incus/internal/revert"
deviceConfig "github.com/lxc/incus/internal/server/device/config"
localMigration "github.com/lxc/incus/internal/server/migration"
"github.com/lxc/incus/internal/server/operations"
"github.com/lxc/incus/shared/api"
Expand Down Expand Up @@ -80,17 +81,18 @@ func (d *ceph) isRemote() bool {
// Info returns info about the driver and its environment.
func (d *ceph) Info() Info {
return Info{
Name: "ceph",
Version: cephVersion,
OptimizedImages: true,
PreservesInodes: false,
Remote: d.isRemote(),
VolumeTypes: []VolumeType{VolumeTypeCustom, VolumeTypeImage, VolumeTypeContainer, VolumeTypeVM},
BlockBacking: true,
RunningCopyFreeze: true,
DirectIO: true,
IOUring: true,
MountedRoot: false,
Name: "ceph",
Version: cephVersion,
DefaultVMBlockFilesystemSize: deviceConfig.DefaultVMBlockFilesystemSize,
OptimizedImages: true,
PreservesInodes: false,
Remote: d.isRemote(),
VolumeTypes: []VolumeType{VolumeTypeCustom, VolumeTypeImage, VolumeTypeContainer, VolumeTypeVM},
BlockBacking: true,
RunningCopyFreeze: true,
DirectIO: true,
IOUring: true,
MountedRoot: false,
}
}

Expand Down
Loading
Loading