@@ -147,6 +147,27 @@ func (d *disk) sourceIsLocalPath(source string) bool {
147147 return true
148148}
149149
150+ func (d * disk ) sourceVolumeFields () (volumeName string , volumeType storageDrivers.VolumeType , dbVolumeType int , volumeTypeName string , err error ) {
151+ volumeName = d .config ["source" ]
152+
153+ volumeTypeName = cluster .StoragePoolVolumeTypeNameCustom
154+ if d .config ["source-type" ] != "" {
155+ volumeTypeName = d .config ["source-type" ]
156+ }
157+
158+ dbVolumeType , err = storagePools .VolumeTypeNameToDBType (volumeTypeName )
159+ if err != nil {
160+ return volumeName , volumeType , dbVolumeType , volumeTypeName , err
161+ }
162+
163+ volumeType , err = storagePools .VolumeDBTypeToType (dbVolumeType )
164+ if err != nil {
165+ return volumeName , volumeType , dbVolumeType , volumeTypeName , err
166+ }
167+
168+ return volumeName , volumeType , dbVolumeType , volumeTypeName , nil
169+ }
170+
150171// Check that unshared custom storage block volumes are not added to profiles or
151172// multiple instances unless they will not be accessed concurrently.
152173func (d * disk ) checkBlockVolSharing (instanceType instancetype.Type , projectName string , volume * api.StorageVolume ) error {
@@ -250,6 +271,15 @@ func (d *disk) validateConfig(instConf instance.ConfigReader) error {
250271 // required: yes
251272 // shortdesc: Source of a file system or block device
252273 "source" : validate .IsAny ,
274+ // lxdmeta:generate(entities=device-disk; group=device-conf; key=source-type)
275+ // Possible values are `custom` (the default) or `virtual-machine`. This
276+ // key is only valid when `source` is the name of a storage volume.
277+ // ---
278+ // type: string
279+ // defaultdesc: `custom`
280+ // required: no
281+ // shortdesc: Type of the backing storage volume
282+ "source-type" : validate .Optional (validate .IsOneOf (cluster .StoragePoolVolumeTypeNameCustom , cluster .StoragePoolVolumeTypeNameVM )),
253283 // lxdmeta:generate(entities=device-disk; group=device-conf; key=limits.read)
254284 // You can specify a value in byte/s (various suffixes supported, see {ref}`instances-limit-units`) or in IOPS (must be suffixed with `iops`).
255285 // See also {ref}`storage-configure-io`.
@@ -389,6 +419,10 @@ func (d *disk) validateConfig(instConf instance.ConfigReader) error {
389419 return fmt .Errorf (`Cannot use both "required" and deprecated "optional" properties at the same time` )
390420 }
391421
422+ if d .config ["source-type" ] != "" && d .config ["pool" ] == "" {
423+ return fmt .Errorf (`"source-type" can only be used on storage volume disk devices` )
424+ }
425+
392426 if d .config ["source" ] == "" && d .config ["path" ] != "/" {
393427 return fmt .Errorf (`Non root disk devices require the "source" property` )
394428 }
@@ -475,7 +509,7 @@ func (d *disk) validateConfig(instConf instance.ConfigReader) error {
475509
476510 // Non-root volume validation.
477511 if ! instancetype .IsRootDiskDevice (d .config ) {
478- volumeType , dbVolumeType , _ , volumeName , err := storagePools . DiskVolumeSourceParse ( d . config [ "source" ] )
512+ volumeName , volumeType , dbVolumeType , _ , err := d . sourceVolumeFields ( )
479513 if err != nil {
480514 return err
481515 }
@@ -492,10 +526,8 @@ func (d *disk) validateConfig(instConf instance.ConfigReader) error {
492526 }
493527
494528 // Derive the effective storage project name from the instance config's project.
495- storageProjectName , err = project .StorageVolumeProject (d .state .DB .Cluster , instConf .Project ().Name , dbVolumeType )
496- if err != nil {
497- return err
498- }
529+ instProj := instConf .Project ()
530+ storageProjectName = project .StorageVolumeProjectFromRecord (& instProj , dbVolumeType )
499531
500532 // GetStoragePoolVolume returns a volume with an empty Location field for remote drivers.
501533 err = d .state .DB .Cluster .Transaction (context .TODO (), func (ctx context.Context , tx * db.ClusterTx ) error {
@@ -706,15 +738,13 @@ func (d *disk) Register() error {
706738 return err
707739 }
708740 } else if d .config ["path" ] != "/" && d .config ["source" ] != "" && d .config ["pool" ] != "" {
709- volumeType , dbVolumeType , volumeTypeName , volumeName , err := storagePools . DiskVolumeSourceParse ( d . config [ "source" ] )
741+ volumeName , volumeType , dbVolumeType , volumeTypeName , err := d . sourceVolumeFields ( )
710742 if err != nil {
711743 return err
712744 }
713745
714- storageProjectName , err := project .StorageVolumeProject (d .state .DB .Cluster , d .inst .Project ().Name , dbVolumeType )
715- if err != nil {
716- return err
717- }
746+ instProj := d .inst .Project ()
747+ storageProjectName := project .StorageVolumeProjectFromRecord (& instProj , dbVolumeType )
718748
719749 // Try to mount the volume that should already be mounted to reinitialise the ref counter.
720750 _ , err = d .pool .MountVolume (storageProjectName , volumeName , volumeType , nil )
@@ -836,16 +866,14 @@ func (d *disk) startContainer() (*deviceConfig.RunConfig, error) {
836866 // If ownerShift is none and pool is specified then check whether the pool itself
837867 // has owner shifting enabled, and if so enable shifting on this device too.
838868 if ownerShift == deviceConfig .MountOwnerShiftNone && d .config ["pool" ] != "" {
839- _ , dbVolumeType , _ , volumeName , err := storagePools . DiskVolumeSourceParse ( d . config [ "source" ] )
869+ volumeName , _ , dbVolumeType , _ , err := d . sourceVolumeFields ( )
840870 if err != nil {
841871 return nil , err
842872 }
843873
844874 // Only custom volumes can be attached currently.
845- storageProjectName , err := project .StorageVolumeProject (d .state .DB .Cluster , d .inst .Project ().Name , dbVolumeType )
846- if err != nil {
847- return nil , err
848- }
875+ instProj := d .inst .Project ()
876+ storageProjectName := project .StorageVolumeProjectFromRecord (& instProj , dbVolumeType )
849877
850878 var dbVolume * db.StorageVolume
851879 err = d .state .DB .Cluster .Transaction (context .TODO (), func (ctx context.Context , tx * db.ClusterTx ) error {
@@ -1088,16 +1116,14 @@ func (d *disk) startVM() (*deviceConfig.RunConfig, error) {
10881116 if d .config ["pool" ] != "" {
10891117 var revertFunc func ()
10901118
1091- volumeType , dbVolumeType , _ , volumeName , err := storagePools . DiskVolumeSourceParse ( d . config [ "source" ] )
1119+ volumeName , volumeType , dbVolumeType , _ , err := d . sourceVolumeFields ( )
10921120 if err != nil {
10931121 return nil , err
10941122 }
10951123
10961124 // Derive the effective storage project name from the instance config's project.
1097- storageProjectName , err := project .StorageVolumeProject (d .state .DB .Cluster , d .inst .Project ().Name , dbVolumeType )
1098- if err != nil {
1099- return nil , err
1100- }
1125+ instProj := d .inst .Project ()
1126+ storageProjectName := project .StorageVolumeProjectFromRecord (& instProj , dbVolumeType )
11011127
11021128 // GetStoragePoolVolume returns a volume with an empty Location field for remote drivers.
11031129 var dbVolume * db.StorageVolume
@@ -1601,16 +1627,14 @@ func (d *disk) mountPoolVolume() (func(), string, *storagePools.MountInfo, error
16011627 return nil , "" , nil , fmt .Errorf (`When the "pool" property is set "source" must specify the name of a volume, not a path` )
16021628 }
16031629
1604- volumeType , dbVolumeType , volumeTypeName , volumeName , err := storagePools . DiskVolumeSourceParse ( d . config [ "source" ] )
1630+ volumeName , volumeType , dbVolumeType , volumeTypeName , err := d . sourceVolumeFields ( )
16051631 if err != nil {
16061632 return nil , "" , nil , err
16071633 }
16081634
16091635 // Only custom volumes can be attached currently.
1610- storageProjectName , err := project .StorageVolumeProject (d .state .DB .Cluster , d .inst .Project ().Name , dbVolumeType )
1611- if err != nil {
1612- return nil , "" , nil , err
1613- }
1636+ instProj := d .inst .Project ()
1637+ storageProjectName := project .StorageVolumeProjectFromRecord (& instProj , dbVolumeType )
16141638
16151639 mountInfo , err = d .pool .MountVolume (storageProjectName , volumeName , volumeType , nil )
16161640 if err != nil {
@@ -1646,7 +1670,7 @@ func (d *disk) mountPoolVolume() (func(), string, *storagePools.MountInfo, error
16461670
16471671 err = d .storagePoolVolumeAttachShift (storageProjectName , d .pool .Name (), volumeName , dbVolumeType , srcPath )
16481672 if err != nil {
1649- return nil , "" , nil , fmt .Errorf (" Failed shifting storage volume %q of type %q on storage pool %q: %w" , volumeName , volumeTypeName , d .pool .Name (), err )
1673+ return nil , "" , nil , fmt .Errorf (` Failed shifting storage volume "%s/%s" on storage pool %q: %w` , volumeTypeName , volumeName , d .pool .Name (), err )
16501674 }
16511675 }
16521676
@@ -2085,16 +2109,14 @@ func (d *disk) postStop() error {
20852109
20862110 // Check if pool-specific action should be taken to unmount custom volume disks.
20872111 if d .config ["pool" ] != "" && d .config ["path" ] != "/" {
2088- volumeType , dbVolumeType , _ , volumeName , err := storagePools . DiskVolumeSourceParse ( d . config [ "source" ] )
2112+ volumeName , volumeType , dbVolumeType , _ , err := d . sourceVolumeFields ( )
20892113 if err != nil {
20902114 return err
20912115 }
20922116
20932117 // Only custom volumes can be attached currently.
2094- storageProjectName , err := project .StorageVolumeProject (d .state .DB .Cluster , d .inst .Project ().Name , dbVolumeType )
2095- if err != nil {
2096- return err
2097- }
2118+ instProj := d .inst .Project ()
2119+ storageProjectName := project .StorageVolumeProjectFromRecord (& instProj , dbVolumeType )
20982120
20992121 _ , err = d .pool .UnmountVolume (storageProjectName , volumeName , volumeType , nil )
21002122 if err != nil && ! errors .Is (err , storageDrivers .ErrInUse ) {
0 commit comments