-
Notifications
You must be signed in to change notification settings - Fork 906
/
driver_dir_utils.go
137 lines (110 loc) · 3.43 KB
/
driver_dir_utils.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
package drivers
import (
"fmt"
"github.com/canonical/lxd/lxd/storage/quota"
"github.com/canonical/lxd/shared/logger"
"github.com/canonical/lxd/shared/revert"
"github.com/canonical/lxd/shared/units"
)
// withoutGetVolID returns a copy of this struct but with a volIDFunc which will cause quotas to be skipped.
func (d *dir) withoutGetVolID() Driver {
newDriver := &dir{}
getVolID := func(volType VolumeType, volName string) (int64, error) { return volIDQuotaSkip, nil }
newDriver.init(d.state, d.name, d.config, d.logger, getVolID, d.commonRules)
_ = newDriver.load()
return newDriver
}
// setupInitialQuota enables quota on a new volume and sets with an initial quota from config.
// Returns a revert fail function that can be used to undo this function if a subsequent step fails.
func (d *dir) setupInitialQuota(vol Volume) (revert.Hook, error) {
if vol.IsVMBlock() {
return nil, nil
}
volPath := vol.MountPath()
// Get the volume ID for the new volume, which is used to set project quota.
volID, err := d.getVolID(vol.volType, vol.name)
if err != nil {
return nil, err
}
revert := revert.New()
defer revert.Fail()
// Define a function to revert the quota being setup.
revertFunc := func() { _ = d.deleteQuota(volPath, volID) }
revert.Add(revertFunc)
// Initialise the volume's project using the volume ID and set the quota.
sizeBytes, err := units.ParseByteSizeString(vol.ConfigSize())
if err != nil {
return nil, err
}
err = d.setQuota(volPath, volID, sizeBytes)
if err != nil {
return nil, err
}
revert.Success()
return revertFunc, nil
}
// deleteQuota removes the project quota for a volID from a path.
func (d *dir) deleteQuota(path string, volID int64) error {
if volID == volIDQuotaSkip {
// Disabled on purpose, just ignore
return nil
}
if volID == 0 {
return fmt.Errorf("Missing volume ID")
}
ok, err := quota.Supported(path)
if err != nil || !ok {
// Skipping quota as underlying filesystem doesn't support project quotas.
return nil
}
err = quota.DeleteProject(path, d.quotaProjectID(volID))
if err != nil {
return err
}
return nil
}
// quotaProjectID generates a project quota ID from a volume ID.
func (d *dir) quotaProjectID(volID int64) uint32 {
if volID == volIDQuotaSkip {
// Disabled on purpose, just ignore
return 0
}
return uint32(volID + 10000)
}
// setQuota sets the project quota on the path. The volID generates a quota project ID.
func (d *dir) setQuota(path string, volID int64, sizeBytes int64) error {
if volID == volIDQuotaSkip {
// Disabled on purpose, just ignore.
return nil
}
if volID == 0 {
return fmt.Errorf("Missing volume ID")
}
ok, err := quota.Supported(path)
if err != nil || !ok {
if sizeBytes > 0 {
// Skipping quota as underlying filesystem doesn't support project quotas.
d.logger.Warn("The backing filesystem doesn't support quotas, skipping set quota", logger.Ctx{"path": path, "size": sizeBytes, "volID": volID})
}
return nil
}
projectID := d.quotaProjectID(volID)
currentProjectID, err := quota.GetProject(path)
if err != nil {
return err
}
// Remove current project if desired project ID is different.
if currentProjectID != d.quotaProjectID(volID) {
err = quota.DeleteProject(path, currentProjectID)
if err != nil {
return err
}
}
// Initialise the project.
err = quota.SetProject(path, projectID)
if err != nil {
return err
}
// Set the project quota size.
return quota.SetProjectQuota(path, projectID, sizeBytes)
}