github.com/mwhudson/juju@v0.0.0-20160512215208-90ff01f3497f/instance/instance.go (about) 1 // Copyright 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package instance 5 6 import ( 7 "fmt" 8 "math" 9 "strconv" 10 "strings" 11 12 "github.com/juju/utils/arch" 13 14 "github.com/juju/juju/network" 15 "github.com/juju/juju/status" 16 ) 17 18 // An instance Id is a provider-specific identifier associated with an 19 // instance (physical or virtual machine allocated in the provider). 20 type Id string 21 22 // InstanceStatus represents the status for a provider instance. 23 type InstanceStatus struct { 24 Status status.Status 25 Message string 26 } 27 28 // UnknownId can be used to explicitly specify the instance ID does not matter. 29 const UnknownId Id = "" 30 31 // Instance represents the the realization of a machine in state. 32 type Instance interface { 33 // Id returns a provider-generated identifier for the Instance. 34 Id() Id 35 36 // Status returns the provider-specific status for the instance. 37 Status() InstanceStatus 38 39 // Addresses returns a list of hostnames or ip addresses 40 // associated with the instance. 41 Addresses() ([]network.Address, error) 42 43 // OpenPorts opens the given port ranges on the instance, which 44 // should have been started with the given machine id. 45 OpenPorts(machineId string, ports []network.PortRange) error 46 47 // ClosePorts closes the given port ranges on the instance, which 48 // should have been started with the given machine id. 49 ClosePorts(machineId string, ports []network.PortRange) error 50 51 // Ports returns the set of port ranges open on the instance, 52 // which should have been started with the given machine id. The 53 // port ranges are returned as sorted by network.SortPortRanges(). 54 Ports(machineId string) ([]network.PortRange, error) 55 } 56 57 // HardwareCharacteristics represents the characteristics of the instance (if known). 58 // Attributes that are nil are unknown or not supported. 59 type HardwareCharacteristics struct { 60 Arch *string `json:",omitempty" yaml:"arch,omitempty"` 61 Mem *uint64 `json:",omitempty" yaml:"mem,omitempty"` 62 RootDisk *uint64 `json:",omitempty" yaml:"rootdisk,omitempty"` 63 CpuCores *uint64 `json:",omitempty" yaml:"cpucores,omitempty"` 64 CpuPower *uint64 `json:",omitempty" yaml:"cpupower,omitempty"` 65 Tags *[]string `json:",omitempty" yaml:"tags,omitempty"` 66 67 AvailabilityZone *string `json:",omitempty" yaml:"availabilityzone,omitempty"` 68 } 69 70 // An error reporting that an error has occurred during instance creation 71 // (e.g. due to a failed container from on of previous deploys) and 72 // that it is safe to restart instance creation 73 type RetryableCreationError struct { 74 message string 75 } 76 77 // Returns the error message 78 func (e RetryableCreationError) Error() string { return e.message } 79 80 func NewRetryableCreationError(errorMessage string) *RetryableCreationError { 81 return &RetryableCreationError{errorMessage} 82 } 83 84 // IsRetryableCreationError returns true if the given error is 85 // RetryableCreationError 86 func IsRetryableCreationError(err error) bool { 87 _, ok := err.(*RetryableCreationError) 88 return ok 89 } 90 91 func (hc HardwareCharacteristics) String() string { 92 var strs []string 93 if hc.Arch != nil { 94 strs = append(strs, fmt.Sprintf("arch=%s", *hc.Arch)) 95 } 96 if hc.CpuCores != nil { 97 strs = append(strs, fmt.Sprintf("cpu-cores=%d", *hc.CpuCores)) 98 } 99 if hc.CpuPower != nil { 100 strs = append(strs, fmt.Sprintf("cpu-power=%d", *hc.CpuPower)) 101 } 102 if hc.Mem != nil { 103 strs = append(strs, fmt.Sprintf("mem=%dM", *hc.Mem)) 104 } 105 if hc.RootDisk != nil { 106 strs = append(strs, fmt.Sprintf("root-disk=%dM", *hc.RootDisk)) 107 } 108 if hc.Tags != nil && len(*hc.Tags) > 0 { 109 strs = append(strs, fmt.Sprintf("tags=%s", strings.Join(*hc.Tags, ","))) 110 } 111 if hc.AvailabilityZone != nil && *hc.AvailabilityZone != "" { 112 strs = append(strs, fmt.Sprintf("availability-zone=%s", *hc.AvailabilityZone)) 113 } 114 return strings.Join(strs, " ") 115 } 116 117 // Implement gnuflag.Value 118 func (hc *HardwareCharacteristics) Set(s string) error { 119 parsed, err := ParseHardware(s) 120 if err != nil { 121 return err 122 } 123 *hc = parsed 124 return nil 125 } 126 127 // MustParseHardware constructs a HardwareCharacteristics from the supplied arguments, 128 // as Parse, but panics on failure. 129 func MustParseHardware(args ...string) HardwareCharacteristics { 130 hc, err := ParseHardware(args...) 131 if err != nil { 132 panic(err) 133 } 134 return hc 135 } 136 137 // ParseHardware constructs a HardwareCharacteristics from the supplied arguments, 138 // each of which must contain only spaces and name=value pairs. If any 139 // name is specified more than once, an error is returned. 140 func ParseHardware(args ...string) (HardwareCharacteristics, error) { 141 hc := HardwareCharacteristics{} 142 for _, arg := range args { 143 raws := strings.Split(strings.TrimSpace(arg), " ") 144 for _, raw := range raws { 145 if raw == "" { 146 continue 147 } 148 if err := hc.setRaw(raw); err != nil { 149 return HardwareCharacteristics{}, err 150 } 151 } 152 } 153 return hc, nil 154 } 155 156 // setRaw interprets a name=value string and sets the supplied value. 157 func (hc *HardwareCharacteristics) setRaw(raw string) error { 158 eq := strings.Index(raw, "=") 159 if eq <= 0 { 160 return fmt.Errorf("malformed characteristic %q", raw) 161 } 162 name, str := raw[:eq], raw[eq+1:] 163 var err error 164 switch name { 165 case "arch": 166 err = hc.setArch(str) 167 case "cpu-cores": 168 err = hc.setCpuCores(str) 169 case "cpu-power": 170 err = hc.setCpuPower(str) 171 case "mem": 172 err = hc.setMem(str) 173 case "root-disk": 174 err = hc.setRootDisk(str) 175 case "tags": 176 err = hc.setTags(str) 177 case "availability-zone": 178 err = hc.setAvailabilityZone(str) 179 default: 180 return fmt.Errorf("unknown characteristic %q", name) 181 } 182 if err != nil { 183 return fmt.Errorf("bad %q characteristic: %v", name, err) 184 } 185 return nil 186 } 187 188 func (hc *HardwareCharacteristics) setArch(str string) error { 189 if hc.Arch != nil { 190 return fmt.Errorf("already set") 191 } 192 if str != "" && !arch.IsSupportedArch(str) { 193 return fmt.Errorf("%q not recognized", str) 194 } 195 hc.Arch = &str 196 return nil 197 } 198 199 func (hc *HardwareCharacteristics) setCpuCores(str string) (err error) { 200 if hc.CpuCores != nil { 201 return fmt.Errorf("already set") 202 } 203 hc.CpuCores, err = parseUint64(str) 204 return 205 } 206 207 func (hc *HardwareCharacteristics) setCpuPower(str string) (err error) { 208 if hc.CpuPower != nil { 209 return fmt.Errorf("already set") 210 } 211 hc.CpuPower, err = parseUint64(str) 212 return 213 } 214 215 func (hc *HardwareCharacteristics) setMem(str string) (err error) { 216 if hc.Mem != nil { 217 return fmt.Errorf("already set") 218 } 219 hc.Mem, err = parseSize(str) 220 return 221 } 222 223 func (hc *HardwareCharacteristics) setRootDisk(str string) (err error) { 224 if hc.RootDisk != nil { 225 return fmt.Errorf("already set") 226 } 227 hc.RootDisk, err = parseSize(str) 228 return 229 } 230 231 func (hc *HardwareCharacteristics) setTags(str string) (err error) { 232 if hc.Tags != nil { 233 return fmt.Errorf("already set") 234 } 235 hc.Tags = parseTags(str) 236 return 237 } 238 239 func (hc *HardwareCharacteristics) setAvailabilityZone(str string) error { 240 if hc.AvailabilityZone != nil { 241 return fmt.Errorf("already set") 242 } 243 if str != "" { 244 hc.AvailabilityZone = &str 245 } 246 return nil 247 } 248 249 // parseTags returns the tags in the value s 250 func parseTags(s string) *[]string { 251 if s == "" { 252 return &[]string{} 253 } 254 tags := strings.Split(s, ",") 255 return &tags 256 } 257 258 func parseUint64(str string) (*uint64, error) { 259 var value uint64 260 if str != "" { 261 if val, err := strconv.ParseUint(str, 10, 64); err != nil { 262 return nil, fmt.Errorf("must be a non-negative integer") 263 } else { 264 value = uint64(val) 265 } 266 } 267 return &value, nil 268 } 269 270 func parseSize(str string) (*uint64, error) { 271 var value uint64 272 if str != "" { 273 mult := 1.0 274 if m, ok := mbSuffixes[str[len(str)-1:]]; ok { 275 str = str[:len(str)-1] 276 mult = m 277 } 278 val, err := strconv.ParseFloat(str, 64) 279 if err != nil || val < 0 { 280 return nil, fmt.Errorf("must be a non-negative float with optional M/G/T/P suffix") 281 } 282 val *= mult 283 value = uint64(math.Ceil(val)) 284 } 285 return &value, nil 286 } 287 288 var mbSuffixes = map[string]float64{ 289 "M": 1, 290 "G": 1024, 291 "T": 1024 * 1024, 292 "P": 1024 * 1024 * 1024, 293 }