Skip to content

Commit e8e07bf

Browse files
authored
Merge pull request #419 from threefoldtech/storaged_qgroups
deletes qgroups when deleting sub volumes
2 parents 83fe24d + aa4f8d9 commit e8e07bf

File tree

5 files changed

+190
-62
lines changed

5 files changed

+190
-62
lines changed

pkg/storage/filesystem/btrfs.go

Lines changed: 89 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import (
88
"strings"
99
"syscall"
1010

11+
"github.com/pkg/errors"
1112
"github.com/rs/zerolog/log"
1213

1314
"github.com/threefoldtech/zos/pkg"
@@ -174,6 +175,10 @@ func (p *btrfsPool) mounted(fs *Btrfs) (string, bool) {
174175
return "", false
175176
}
176177

178+
func (p *btrfsPool) ID() int {
179+
return 0
180+
}
181+
177182
func (p *btrfsPool) Name() string {
178183
return p.name
179184
}
@@ -182,6 +187,16 @@ func (p *btrfsPool) Path() string {
182187
return filepath.Join("/mnt", p.name)
183188
}
184189

190+
// Limit on a pool is not supported yet
191+
func (p *btrfsPool) Limit(size uint64) error {
192+
return fmt.Errorf("not implemented")
193+
}
194+
195+
// FsType of the filesystem of this volume
196+
func (p *btrfsPool) FsType() string {
197+
return "btrfs"
198+
}
199+
185200
// Mount mounts the pool in it's default mount location under /mnt/name
186201
func (p *btrfsPool) Mount() (string, error) {
187202
ctx := context.Background()
@@ -287,6 +302,7 @@ func (p *btrfsPool) Volumes() ([]Volume, error) {
287302

288303
for _, sub := range subs {
289304
volumes = append(volumes, newBtrfsVolume(
305+
sub.ID,
290306
filepath.Join(mnt, sub.Path),
291307
p.utils,
292308
))
@@ -296,11 +312,17 @@ func (p *btrfsPool) Volumes() ([]Volume, error) {
296312
}
297313

298314
func (p *btrfsPool) addVolume(root string) (*btrfsVolume, error) {
299-
if err := p.utils.SubvolumeAdd(context.Background(), root); err != nil {
315+
ctx := context.Background()
316+
if err := p.utils.SubvolumeAdd(ctx, root); err != nil {
317+
return nil, err
318+
}
319+
320+
volume, err := p.utils.SubvolumeInfo(ctx, root)
321+
if err != nil {
300322
return nil, err
301323
}
302324

303-
return newBtrfsVolume(root, p.utils), nil
325+
return newBtrfsVolume(volume.ID, root, p.utils), nil
304326
}
305327

306328
func (p *btrfsPool) AddVolume(name string) (Volume, error) {
@@ -314,7 +336,23 @@ func (p *btrfsPool) AddVolume(name string) (Volume, error) {
314336
}
315337

316338
func (p *btrfsPool) removeVolume(root string) error {
317-
return p.utils.SubvolumeRemove(context.Background(), root)
339+
ctx := context.Background()
340+
341+
info, err := p.utils.SubvolumeInfo(ctx, root)
342+
if err != nil {
343+
return err
344+
}
345+
346+
if err := p.utils.SubvolumeRemove(ctx, root); err != nil {
347+
return err
348+
}
349+
350+
qgroupID := fmt.Sprintf("0/%d", info.ID)
351+
if err := p.utils.QGroupDestroy(ctx, qgroupID, p.Path()); err != nil {
352+
return errors.Wrapf(err, "failed to delete qgroup %s", qgroupID)
353+
}
354+
355+
return nil
318356
}
319357

320358
func (p *btrfsPool) RemoveVolume(name string) error {
@@ -357,16 +395,6 @@ func (p *btrfsPool) Usage() (usage Usage, err error) {
357395
return Usage{Size: totalSize / raidSizeDivisor[du.Data.Profile], Used: uint64(fsi[0].Used)}, nil
358396
}
359397

360-
// Limit on a pool is not supported yet
361-
func (p *btrfsPool) Limit(size uint64) error {
362-
return fmt.Errorf("not implemented")
363-
}
364-
365-
// FsType of the filesystem of this volume
366-
func (p *btrfsPool) FsType() string {
367-
return "btrfs"
368-
}
369-
370398
// Type of the physical storage used for this pool
371399
func (p *btrfsPool) Type() pkg.DeviceType {
372400
// We only create heterogenous pools for now
@@ -393,49 +421,71 @@ func (p *btrfsPool) Reserved() (uint64, error) {
393421
return total, nil
394422
}
395423

424+
func (p *btrfsPool) Maintenance() error {
425+
// this method cleans up all the unused
426+
// qgroups that could exists on a filesystem
427+
428+
volumes, err := p.Volumes()
429+
if err != nil {
430+
return err
431+
}
432+
subVolsIDs := map[string]struct{}{}
433+
for _, volume := range volumes {
434+
// use the 0/X notation to match the qgroup IDs format
435+
subVolsIDs[fmt.Sprintf("0/%d", volume.ID())] = struct{}{}
436+
}
437+
438+
ctx := context.Background()
439+
qgroups, err := p.utils.QGroupList(ctx, p.Path())
440+
if err != nil {
441+
return err
442+
}
443+
444+
for qgroupID := range qgroups {
445+
// for all qgroup that doesn't have an linked
446+
// volume, delete the qgroup
447+
_, ok := subVolsIDs[qgroupID]
448+
if !ok {
449+
log.Debug().Str("id", qgroupID).Msg("destroy qgroup")
450+
if err := p.utils.QGroupDestroy(ctx, qgroupID, p.Path()); err != nil {
451+
return err
452+
}
453+
}
454+
}
455+
456+
return nil
457+
}
458+
396459
type btrfsVolume struct {
460+
id int
397461
path string
398462
utils *BtrfsUtil
399463
}
400464

401-
func newBtrfsVolume(path string, utils *BtrfsUtil) *btrfsVolume {
465+
func newBtrfsVolume(ID int, path string, utils *BtrfsUtil) *btrfsVolume {
402466
return &btrfsVolume{
467+
id: ID,
403468
path: path,
404469
utils: utils,
405470
}
406471
}
407472

408-
func (v *btrfsVolume) Path() string {
409-
return v.path
473+
func (v *btrfsVolume) ID() int {
474+
return v.id
410475
}
411476

412-
func (v *btrfsVolume) Volumes() ([]Volume, error) {
413-
var volumes []Volume
414-
415-
subs, err := v.utils.SubvolumeList(context.Background(), v.Path())
416-
if err != nil {
417-
return nil, err
418-
}
419-
420-
for _, sub := range subs {
421-
volumes = append(volumes, newBtrfsVolume(filepath.Join(v.Path(), sub.Path), v.utils))
422-
}
423-
424-
return volumes, nil
477+
func (v *btrfsVolume) Path() string {
478+
return v.path
425479
}
426480

427-
func (v *btrfsVolume) AddVolume(name string) (Volume, error) {
428-
mnt := filepath.Join(v.Path(), name)
429-
if err := v.utils.SubvolumeAdd(context.Background(), mnt); err != nil {
430-
return nil, err
431-
}
432-
433-
return newBtrfsVolume(mnt, v.utils), nil
481+
// Name of the filesystem
482+
func (v *btrfsVolume) Name() string {
483+
return filepath.Base(v.Path())
434484
}
435485

436-
func (v *btrfsVolume) RemoveVolume(name string) error {
437-
mnt := filepath.Join(v.Path(), name)
438-
return v.utils.SubvolumeRemove(context.Background(), mnt)
486+
// FsType of the filesystem
487+
func (v *btrfsVolume) FsType() string {
488+
return "btrfs"
439489
}
440490

441491
// Usage return the volume usage
@@ -470,13 +520,3 @@ func (v *btrfsVolume) Limit(size uint64) error {
470520

471521
return v.utils.QGroupLimit(ctx, size, v.Path())
472522
}
473-
474-
// Name of the filesystem
475-
func (v *btrfsVolume) Name() string {
476-
return filepath.Base(v.Path())
477-
}
478-
479-
// FsType of the filesystem
480-
func (v *btrfsVolume) FsType() string {
481-
return "btrfs"
482-
}

pkg/storage/filesystem/btrfs_ci_test.go

Lines changed: 57 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -162,13 +162,6 @@ func basePoolTest(t *testing.T, pool Pool) {
162162
assert.Equal(t, uint64(1024*1024*1024), usage.Size)
163163
})
164164

165-
t.Run("test subvolume list no subvolumes", func(t *testing.T) {
166-
volumes, err := volume.Volumes()
167-
require.NoError(t, err)
168-
169-
assert.Empty(t, volumes)
170-
})
171-
172165
t.Run("test limit subvolume", func(t *testing.T) {
173166
usage, err := volume.Usage()
174167
require.NoError(t, err)
@@ -301,3 +294,60 @@ func TestBtrfsListCI(t *testing.T) {
301294
ok := assert.Len(t, names, 0)
302295
assert.True(t, ok, "not all pools were listed")
303296
}
297+
298+
func TestCLeanUpQgroupsCI(t *testing.T) {
299+
if SkipCITests {
300+
t.Skip("test requires ability to create loop devices")
301+
}
302+
303+
devices, err := SetupDevices(1)
304+
require.NoError(t, err, "failed to initialize devices")
305+
defer devices.Destroy()
306+
307+
loops := devices.Loops()
308+
fs := NewBtrfs(&TestDeviceManager{loops})
309+
310+
names := make(map[string]struct{})
311+
for idx := range loops {
312+
loop := &loops[idx]
313+
name := fmt.Sprintf("test-list-%d", idx)
314+
names[name] = struct{}{}
315+
_, err := fs.Create(context.Background(), name, pkg.Single, loop)
316+
require.NoError(t, err)
317+
}
318+
pools, err := fs.List(context.Background(), func(p Pool) bool {
319+
return strings.HasPrefix(p.Name(), "test-")
320+
})
321+
pool := pools[0]
322+
323+
_, err = pool.Mount()
324+
require.NoError(t, err)
325+
defer pool.UnMount()
326+
327+
volume, err := pool.AddVolume("vol1")
328+
require.NoError(t, err)
329+
t.Logf("volume ID: %v\n", volume.ID())
330+
331+
err = volume.Limit(256 * 1024 * 1024)
332+
require.NoError(t, err)
333+
334+
btrfsVol, ok := volume.(*btrfsVolume)
335+
require.True(t, ok, "volume should be a btrfsVolume")
336+
337+
qgroups, err := btrfsVol.utils.QGroupList(context.TODO(), pool.Path())
338+
require.NoError(t, err)
339+
assert.Equal(t, 2, len(qgroups))
340+
t.Logf("qgroups before delete: %v", qgroups)
341+
342+
_, ok = qgroups[fmt.Sprintf("0/%d", btrfsVol.id)]
343+
assert.True(t, ok, "qgroups should contains a qgroup linked to the subvolume")
344+
345+
err = pool.RemoveVolume("vol1")
346+
require.NoError(t, err)
347+
348+
qgroups, err = btrfsVol.utils.QGroupList(context.TODO(), pool.Path())
349+
require.NoError(t, err)
350+
351+
t.Logf("remaining qgroups: %+v", qgroups)
352+
assert.Equal(t, 1, len(qgroups), "qgroups should have been deleted with the subvolume")
353+
}

pkg/storage/filesystem/btrfs_utils.go

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -174,6 +174,13 @@ func (u *BtrfsUtil) QGroupLimit(ctx context.Context, size uint64, path string) e
174174
return err
175175
}
176176

177+
// QGroupDestroy deletes a qgroup on a subvol
178+
func (u *BtrfsUtil) QGroupDestroy(ctx context.Context, id, path string) error {
179+
_, err := u.run(ctx, "btrfs", "qgroup", "destroy", id, path)
180+
181+
return err
182+
}
183+
177184
// GetDiskUsage get btrfs usage
178185
func (u *BtrfsUtil) GetDiskUsage(ctx context.Context, path string) (usage BtrfsDiskUsage, err error) {
179186
output, err := u.run(ctx, "btrfs", "filesystem", "df", "--raw", path)

pkg/storage/filesystem/filesystem.go

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,10 @@ type Usage struct {
1414

1515
// Volume represents a logical volume in the pool. Volumes can be nested
1616
type Volume interface {
17+
// Volume ID
18+
ID() int
1719
// Path of the volume
1820
Path() string
19-
// Volumes are all subvolumes of this volume
20-
Volumes() ([]Volume, error)
21-
// AddVolume adds a new subvolume with the given name
22-
AddVolume(name string) (Volume, error)
23-
// RemoveVolume removes a subvolume with the given name
24-
RemoveVolume(name string) error
2521
// Usage reports the current usage of the volume
2622
Usage() (Usage, error)
2723
// Limit the maximum size of the volume
@@ -50,8 +46,19 @@ type Pool interface {
5046
Type() pkg.DeviceType
5147
// Reserved is reserved size of the devices in bytes
5248
Reserved() (uint64, error)
49+
// Maintenance is a routine that is called at boot
50+
// and that all implementer can use to do some clean up and
51+
// other maintenance on the pool
52+
Maintenance() error
5353

5454
// Health() ?
55+
56+
// Volumes are all subvolumes of this volume
57+
Volumes() ([]Volume, error)
58+
// AddVolume adds a new subvolume with the given name
59+
AddVolume(name string) (Volume, error)
60+
// RemoveVolume removes a subvolume with the given name
61+
RemoveVolume(name string) error
5562
}
5663

5764
// Filter closure for Filesystem list

pkg/storage/storage.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,10 @@ func New() (pkg.StorageModule, error) {
6060
log.Info().Msgf("Finished initializing storage module")
6161
}
6262

63+
if err := s.Maintenance(); err != nil {
64+
log.Error().Err(err).Msg("storage devices maintenance failed")
65+
}
66+
6367
return s, err
6468
}
6569

@@ -211,6 +215,26 @@ func (s *storageModule) initialize(policy pkg.StoragePolicy) error {
211215
return s.ensureCache()
212216
}
213217

218+
func (s *storageModule) Maintenance() error {
219+
220+
for _, pool := range s.volumes {
221+
log.Info().
222+
Str("pool", pool.Name()).
223+
Msg("start storage pool maintained")
224+
if err := pool.Maintenance(); err != nil {
225+
log.Error().
226+
Err(err).
227+
Str("pool", pool.Name()).
228+
Msg("error during maintainace")
229+
return err
230+
}
231+
log.Info().
232+
Str("pool", pool.Name()).
233+
Msg("finished storage pool maintained")
234+
}
235+
return nil
236+
}
237+
214238
// CreateFilesystem with the given size in a storage pool.
215239
func (s *storageModule) CreateFilesystem(name string, size uint64, poolType pkg.DeviceType) (string, error) {
216240
log.Info().Msgf("Creating new volume with size %d", size)

0 commit comments

Comments
 (0)