1
// Copyright 2013 Canonical Ltd.
2
// Licensed under the AGPLv3, see LICENCE file for details.
12
"launchpad.net/juju-core/instance"
15
// Value describes a user's requirements of the hardware on which units
16
// of a service will run. Constraints are used to choose an existing machine
17
// onto which a unit will be deployed, or to provision a new machine if no
18
// existing one satisfies the requirements.
21
// Arch, if not nil or empty, indicates that a machine must run the named
23
Arch *string `json:"arch,omitempty" yaml:"arch,omitempty"`
25
// Container, if not nil, indicates that a machine must be the specified container type.
26
Container *instance.ContainerType `json:"container,omitempty" yaml:"container,omitempty"`
28
// CpuCores, if not nil, indicates that a machine must have at least that
29
// number of effective cores available.
30
CpuCores *uint64 `json:"cpu-cores,omitempty" yaml:"cpu-cores,omitempty"`
32
// CpuPower, if not nil, indicates that a machine must have at least that
33
// amount of CPU power available, where 100 CpuPower is considered to be
34
// equivalent to 1 Amazon ECU (or, roughly, a single 2007-era Xeon).
35
CpuPower *uint64 `json:"cpu-power,omitempty" yaml:"cpu-power,omitempty"`
37
// Mem, if not nil, indicates that a machine must have at least that many
39
Mem *uint64 `json:"mem,omitempty" yaml:"mem,omitempty"`
42
// String expresses a constraints.Value in the language in which it was specified.
43
func (v Value) String() string {
46
strs = append(strs, "arch="+*v.Arch)
48
if v.Container != nil {
49
strs = append(strs, "container="+string(*v.Container))
51
if v.CpuCores != nil {
52
strs = append(strs, "cpu-cores="+uintStr(*v.CpuCores))
54
if v.CpuPower != nil {
55
strs = append(strs, "cpu-power="+uintStr(*v.CpuPower))
62
strs = append(strs, "mem="+s)
64
return strings.Join(strs, " ")
67
// WithFallbacks returns a copy of v with nil values taken from v0.
68
func (v Value) WithFallbacks(v0 Value) Value {
73
if v.Container != nil {
74
v1.Container = v.Container
76
if v.CpuCores != nil {
77
v1.CpuCores = v.CpuCores
79
if v.CpuPower != nil {
80
v1.CpuPower = v.CpuPower
88
func uintStr(i uint64) string {
92
return fmt.Sprintf("%d", i)
95
// Parse constructs a constraints.Value from the supplied arguments,
96
// each of which must contain only spaces and name=value pairs. If any
97
// name is specified more than once, an error is returned.
98
func Parse(args ...string) (Value, error) {
100
for _, arg := range args {
101
raws := strings.Split(strings.TrimSpace(arg), " ")
102
for _, raw := range raws {
106
if err := cons.setRaw(raw); err != nil {
114
// MustParse constructs a constraints.Value from the supplied arguments,
115
// as Parse, but panics on failure.
116
func MustParse(args ...string) Value {
117
v, err := Parse(args...)
124
// Constraints implements gnuflag.Value for a Constraints.
125
type ConstraintsValue struct {
129
func (v ConstraintsValue) Set(s string) error {
130
cons, err := Parse(s)
138
func (v ConstraintsValue) String() string {
139
return v.Target.String()
142
// setRaw interprets a name=value string and sets the supplied value.
143
func (v *Value) setRaw(raw string) error {
144
eq := strings.Index(raw, "=")
146
return fmt.Errorf("malformed constraint %q", raw)
148
name, str := raw[:eq], raw[eq+1:]
154
err = v.setContainer(str)
156
err = v.setCpuCores(str)
158
err = v.setCpuPower(str)
162
return fmt.Errorf("unknown constraint %q", name)
165
return fmt.Errorf("bad %q constraint: %v", name, err)
170
// SetYAML is required to unmarshall a constraints.Value object
171
// to ensure the container attribute is correctly handled when it is empty.
172
// Because ContainerType is an alias for string, Go's reflect logic used in the
173
// YAML decode determines that *string and *ContainerType are not assignable so
174
// the container value of "" in the YAML is ignored.
175
func (v *Value) SetYAML(tag string, value interface{}) bool {
176
values := value.(map[interface{}]interface{})
177
for k, val := range values {
178
vstr := fmt.Sprintf("%v", val)
184
ctype := instance.ContainerType(vstr)
187
v.CpuCores, err = parseUint64(vstr)
189
v.CpuPower, err = parseUint64(vstr)
191
v.Mem, err = parseUint64(vstr)
202
func (v *Value) setContainer(str string) error {
203
if v.Container != nil {
204
return fmt.Errorf("already set")
207
ctype := instance.ContainerType("")
210
ctype, err := instance.ParseSupportedContainerTypeOrNone(str)
219
// HasContainer returns true if the constraints.Value specifies a container.
220
func (v *Value) HasContainer() bool {
221
return v.Container != nil && *v.Container != "" && *v.Container != instance.NONE
224
func (v *Value) setArch(str string) error {
226
return fmt.Errorf("already set")
230
case "amd64", "i386", "arm":
232
return fmt.Errorf("%q not recognized", str)
238
func (v *Value) setCpuCores(str string) (err error) {
239
if v.CpuCores != nil {
240
return fmt.Errorf("already set")
242
v.CpuCores, err = parseUint64(str)
246
func (v *Value) setCpuPower(str string) (err error) {
247
if v.CpuPower != nil {
248
return fmt.Errorf("already set")
250
v.CpuPower, err = parseUint64(str)
254
func (v *Value) setMem(str string) error {
256
return fmt.Errorf("already set")
261
if m, ok := mbSuffixes[str[len(str)-1:]]; ok {
262
str = str[:len(str)-1]
265
val, err := strconv.ParseFloat(str, 64)
266
if err != nil || val < 0 {
267
return fmt.Errorf("must be a non-negative float with optional M/G/T/P suffix")
270
value = uint64(math.Ceil(val))
276
func parseUint64(str string) (*uint64, error) {
279
if val, err := strconv.ParseUint(str, 10, 64); err != nil {
280
return nil, fmt.Errorf("must be a non-negative integer")
288
var mbSuffixes = map[string]float64{
292
"P": 1024 * 1024 * 1024,