@@ -22,9 +22,10 @@ const (
22
22
23
23
// Constraints describes a set of storage constraints.
24
24
type Constraints struct {
25
- // Source is the name of the storage source (ebs, ceph, ...) that
26
- // must provide the storage, or "" if any source may be used.
27
- Source string
25
+ // Pool is the name of the storage pool (ebs, ceph, custompool, ...)
26
+ // that must provide the storage, or "" if the default pool should be
27
+ // used.
28
+ Pool string
28
29
29
30
// Minimum is the minimum acceptable values for each constraint variable.
30
31
Minimum ConstraintValues
@@ -41,108 +42,104 @@ type ConstraintValues struct {
41
42
42
43
// Count is the number of instances of the storage to create.
43
44
Count uint64
44
-
45
- // Persistent indicates that the storage should be persistent
46
- // beyond the lifetime of the machine that it is initially
47
- // attached to.
48
- Persistent bool
49
-
50
- // IOPS is the number of IOPS (I/O Operations Per Second) that the
51
- // storage should be capable of.
52
- IOPS uint64
53
45
}
54
46
55
- const (
56
- countSnippet = "(?:(-?[0-9]+)x)"
57
- sizeSuffixSnippet = "(?:[MGTPEZY](?:i?B)?)"
58
- sizeSnippet = "( -?[0-9]+(?:\\ .[0-9]+)?" + sizeSuffixSnippet + "?)"
47
+ var (
48
+ poolRE = regexp . MustCompile ( "^[a-zA-Z]+[-?a-zA-Z0-9]*$" )
49
+ countRE = regexp . MustCompile ( "^-?[0-9]+$" )
50
+ sizeRE = regexp . MustCompile ( "^ -?[0-9]+(?:\\ .[0-9]+)?[MGTPEZY](?:i?B)?$" )
59
51
)
60
52
61
- var countSizeRE = regexp .MustCompile ("^" + countSnippet + "?" + sizeSnippet + "$" )
62
-
63
53
// ParseConstraints parses the specified string and creates a
64
54
// Constraints structure.
65
55
//
66
- // The acceptable format for storage constraints is:
67
- // [SOURCE:][[COUNTx]SIZE][,persistent][,iops:IOPS]
68
- // where
69
- // SOURCE identifies the storage source. SOURCE can be a
70
- // string starting with a letter of the alphabet, followed
71
- // by zero or more alpha-numeric characters optionally
72
- // separated by hyphens.
56
+ // The acceptable format for storage constraints is a comma separated
57
+ // sequence of: POOL, COUNT, and SIZE, where
58
+ //
59
+ // POOL identifies the storage pool. POOL can be a string
60
+ // starting with a letter, followed by zero or more digits
61
+ // or letters optionally separated by hyphens.
73
62
//
74
63
// COUNT is a positive integer indicating how many instances
75
64
// of the storage to create. If unspecified, and SIZE is
76
65
// specified, COUNT defaults to 1.
77
66
//
78
67
// SIZE describes the minimum size of the storage instances to
79
- // create. SIZE is a floating point number and optional multiplier
80
- // from the set (M, G, T, P, E, Z, Y), which are all treated as
68
+ // create. SIZE is a floating point number and multiplier from
69
+ // the set (M, G, T, P, E, Z, Y), which are all treated as
81
70
// powers of 1024.
82
- //
83
- // IOPS is a positive integer describing the minimum number of
84
- // IOPS the storage should be capable of. If unspecified, then
85
- // there is no constraint.
86
71
func ParseConstraints (s string ) (Constraints , error ) {
87
72
var cons Constraints
88
- if i := strings .IndexRune (s , ':' ); i >= 0 {
89
- cons .Source , s = s [:i ], s [i + 1 :]
90
- }
91
-
92
- var countSizeMatch []string
93
- if i := strings .IndexRune (s , ',' ); i >= 0 {
94
- countSizeMatch = countSizeRE .FindStringSubmatch (s [:i ])
95
- if countSizeMatch != nil {
96
- s = s [i + 1 :]
73
+ fields := strings .Split (s , "," )
74
+ for _ , field := range fields {
75
+ if field == "" {
76
+ continue
97
77
}
98
- } else {
99
- countSizeMatch = countSizeRE .FindStringSubmatch (s )
100
- if countSizeMatch != nil {
101
- s = ""
102
- }
103
- }
104
- var err error
105
- if countSizeMatch != nil {
106
- if countSizeMatch [1 ] != "" {
107
- if countSizeMatch [1 ][0 ] != '-' {
108
- cons .Preferred .Count , err = strconv .ParseUint (countSizeMatch [1 ], 10 , 64 )
109
- if err != nil {
110
- return cons , errors .Annotatef (err , "cannot parse count %q" , countSizeMatch [1 ])
111
- }
112
- }
113
- if cons .Preferred .Count == 0 {
114
- return cons , errors .Errorf ("count must be greater than zero, got %q" , countSizeMatch [1 ])
78
+ if isValidPoolName (field ) {
79
+ if cons .Pool != "" {
80
+ logger .Warningf ("pool name is already set to %q, ignoring %q" , cons .Pool , field )
81
+ } else {
82
+ cons .Pool = field
115
83
}
116
- } else {
117
- // Size is specified, but count is not; default count to 1.
118
- cons .Preferred .Count = 1
84
+ continue
119
85
}
120
- cons .Preferred .Size , err = utils .ParseSize (countSizeMatch [2 ])
121
- if err != nil {
122
- return cons , errors .Annotate (err , "cannot parse size" )
86
+ if count , ok , err := parseCount (field ); ok {
87
+ if err != nil {
88
+ return cons , errors .Annotate (err , "cannot parse count" )
89
+ }
90
+ cons .Preferred .Count = count
91
+ continue
123
92
}
124
- }
125
-
126
- // Remaining constraints may be in any order.
127
- for _ , field := range strings .Split (s , "," ) {
128
- field = strings .TrimSpace (field )
129
- switch {
130
- case field == "" :
131
- case field == persistentConstraint :
132
- cons .Preferred .Persistent = true
133
- case strings .HasPrefix (strings .ToLower (field ), iopsConstraintPrefix ):
134
- value := field [len (iopsConstraintPrefix ):]
135
- cons .Preferred .IOPS , err = strconv .ParseUint (value , 10 , 64 )
93
+ if size , ok , err := parseSize (field ); ok {
136
94
if err != nil {
137
- return cons , errors .Annotatef (err , "cannot parse IOPS %q" , value )
95
+ return cons , errors .Annotate (err , "cannot parse size" )
138
96
}
139
- default :
140
- logger . Warningf ( "ignoring unknown storage constraint %q" , field )
97
+ cons . Preferred . Size = size
98
+ continue
141
99
}
100
+ logger .Warningf ("ignoring unknown storage constraint %q" , field )
101
+ }
102
+ if cons .Preferred .Count == 0 && cons .Preferred .Size > 0 {
103
+ cons .Preferred .Count = 1
142
104
}
143
105
144
106
// Explicitly specified constraints are always required;
145
107
// the minimum is the same as the preferred.
146
108
cons .Minimum = cons .Preferred
147
109
return cons , nil
148
110
}
111
+
112
+ func isValidPoolName (s string ) bool {
113
+ return poolRE .MatchString (s )
114
+ }
115
+
116
+ func parseCount (s string ) (uint64 , bool , error ) {
117
+ if ! countRE .MatchString (s ) {
118
+ return 0 , false , nil
119
+ }
120
+ var n uint64
121
+ var err error
122
+ if s [0 ] == '-' {
123
+ goto bad
124
+ }
125
+ n , err = strconv .ParseUint (s , 10 , 64 )
126
+ if err != nil {
127
+ return 0 , false , nil
128
+ }
129
+ if n > 0 {
130
+ return n , true , nil
131
+ }
132
+ bad:
133
+ return 0 , true , errors .Errorf ("count must be greater than zero, got %q" , s )
134
+ }
135
+
136
+ func parseSize (s string ) (uint64 , bool , error ) {
137
+ if ! sizeRE .MatchString (s ) {
138
+ return 0 , false , nil
139
+ }
140
+ size , err := utils .ParseSize (s )
141
+ if err != nil {
142
+ return 0 , true , err
143
+ }
144
+ return size , true , nil
145
+ }
0 commit comments