launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/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 "errors" 8 "fmt" 9 "math" 10 "sort" 11 "strconv" 12 "strings" 13 ) 14 15 var ErrNoDNSName = errors.New("DNS name not allocated") 16 17 // An instance Id is a provider-specific identifier associated with an 18 // instance (physical or virtual machine allocated in the provider). 19 type Id string 20 21 // Port identifies a network port number for a particular protocol. 22 type Port struct { 23 Protocol string 24 Number int 25 } 26 27 func (p Port) String() string { 28 return fmt.Sprintf("%d/%s", p.Number, p.Protocol) 29 } 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() string 38 39 // Refresh refreshes local knowledge of the instance from the provider. 40 Refresh() error 41 42 // Addresses returns a list of hostnames or ip addresses 43 // associated with the instance. This will supercede DNSName 44 // which can be implemented by selecting a preferred address. 45 Addresses() ([]Address, error) 46 47 // DNSName returns the DNS name for the instance. 48 // If the name is not yet allocated, it will return 49 // an ErrNoDNSName error. 50 DNSName() (string, error) 51 52 // WaitDNSName returns the DNS name for the instance, 53 // waiting until it is allocated if necessary. 54 // TODO: We may not need this in the interface any more. All 55 // implementations now delegate to environs.WaitDNSName. 56 WaitDNSName() (string, error) 57 58 // OpenPorts opens the given ports on the instance, which 59 // should have been started with the given machine id. 60 OpenPorts(machineId string, ports []Port) error 61 62 // ClosePorts closes the given ports on the instance, which 63 // should have been started with the given machine id. 64 ClosePorts(machineId string, ports []Port) error 65 66 // Ports returns the set of ports open on the instance, which 67 // should have been started with the given machine id. 68 // The ports are returned as sorted by SortPorts. 69 Ports(machineId string) ([]Port, error) 70 } 71 72 // HardwareCharacteristics represents the characteristics of the instance (if known). 73 // Attributes that are nil are unknown or not supported. 74 type HardwareCharacteristics struct { 75 Arch *string `yaml:"arch,omitempty"` 76 Mem *uint64 `yaml:"mem,omitempty"` 77 RootDisk *uint64 `yaml:"rootdisk,omitempty"` 78 CpuCores *uint64 `yaml:"cpucores,omitempty"` 79 CpuPower *uint64 `yaml:"cpupower,omitempty"` 80 Tags *[]string `yaml:"tags,omitempty"` 81 } 82 83 func uintStr(i uint64) string { 84 if i == 0 { 85 return "" 86 } 87 return fmt.Sprintf("%d", i) 88 } 89 90 func (hc HardwareCharacteristics) String() string { 91 var strs []string 92 if hc.Arch != nil { 93 strs = append(strs, fmt.Sprintf("arch=%s", *hc.Arch)) 94 } 95 if hc.CpuCores != nil { 96 strs = append(strs, fmt.Sprintf("cpu-cores=%d", *hc.CpuCores)) 97 } 98 if hc.CpuPower != nil { 99 strs = append(strs, fmt.Sprintf("cpu-power=%d", *hc.CpuPower)) 100 } 101 if hc.Mem != nil { 102 strs = append(strs, fmt.Sprintf("mem=%dM", *hc.Mem)) 103 } 104 if hc.RootDisk != nil { 105 strs = append(strs, fmt.Sprintf("root-disk=%dM", *hc.RootDisk)) 106 } 107 if hc.Tags != nil && len(*hc.Tags) > 0 { 108 strs = append(strs, fmt.Sprintf("tags=%s", strings.Join(*hc.Tags, ","))) 109 } 110 return strings.Join(strs, " ") 111 } 112 113 // MustParseHardware constructs a HardwareCharacteristics from the supplied arguments, 114 // as Parse, but panics on failure. 115 func MustParseHardware(args ...string) HardwareCharacteristics { 116 hc, err := ParseHardware(args...) 117 if err != nil { 118 panic(err) 119 } 120 return hc 121 } 122 123 // ParseHardware constructs a HardwareCharacteristics from the supplied arguments, 124 // each of which must contain only spaces and name=value pairs. If any 125 // name is specified more than once, an error is returned. 126 func ParseHardware(args ...string) (HardwareCharacteristics, error) { 127 hc := HardwareCharacteristics{} 128 for _, arg := range args { 129 raws := strings.Split(strings.TrimSpace(arg), " ") 130 for _, raw := range raws { 131 if raw == "" { 132 continue 133 } 134 if err := hc.setRaw(raw); err != nil { 135 return HardwareCharacteristics{}, err 136 } 137 } 138 } 139 return hc, nil 140 } 141 142 // setRaw interprets a name=value string and sets the supplied value. 143 func (hc *HardwareCharacteristics) setRaw(raw string) error { 144 eq := strings.Index(raw, "=") 145 if eq <= 0 { 146 return fmt.Errorf("malformed characteristic %q", raw) 147 } 148 name, str := raw[:eq], raw[eq+1:] 149 var err error 150 switch name { 151 case "arch": 152 err = hc.setArch(str) 153 case "cpu-cores": 154 err = hc.setCpuCores(str) 155 case "cpu-power": 156 err = hc.setCpuPower(str) 157 case "mem": 158 err = hc.setMem(str) 159 case "root-disk": 160 err = hc.setRootDisk(str) 161 case "tags": 162 err = hc.setTags(str) 163 default: 164 return fmt.Errorf("unknown characteristic %q", name) 165 } 166 if err != nil { 167 return fmt.Errorf("bad %q characteristic: %v", name, err) 168 } 169 return nil 170 } 171 172 func (hc *HardwareCharacteristics) setArch(str string) error { 173 if hc.Arch != nil { 174 return fmt.Errorf("already set") 175 } 176 switch str { 177 case "": 178 case "amd64", "i386", "arm": 179 default: 180 return fmt.Errorf("%q not recognized", str) 181 } 182 hc.Arch = &str 183 return nil 184 } 185 186 func (hc *HardwareCharacteristics) setCpuCores(str string) (err error) { 187 if hc.CpuCores != nil { 188 return fmt.Errorf("already set") 189 } 190 hc.CpuCores, err = parseUint64(str) 191 return 192 } 193 194 func (hc *HardwareCharacteristics) setCpuPower(str string) (err error) { 195 if hc.CpuPower != nil { 196 return fmt.Errorf("already set") 197 } 198 hc.CpuPower, err = parseUint64(str) 199 return 200 } 201 202 func (hc *HardwareCharacteristics) setMem(str string) (err error) { 203 if hc.Mem != nil { 204 return fmt.Errorf("already set") 205 } 206 hc.Mem, err = parseSize(str) 207 return 208 } 209 210 func (hc *HardwareCharacteristics) setRootDisk(str string) (err error) { 211 if hc.RootDisk != nil { 212 return fmt.Errorf("already set") 213 } 214 hc.RootDisk, err = parseSize(str) 215 return 216 } 217 218 func (hc *HardwareCharacteristics) setTags(str string) (err error) { 219 if hc.Tags != nil { 220 return fmt.Errorf("already set") 221 } 222 hc.Tags = parseTags(str) 223 return 224 } 225 226 // parseTags returns the tags in the value s 227 func parseTags(s string) *[]string { 228 if s == "" { 229 return &[]string{} 230 } 231 tags := strings.Split(s, ",") 232 return &tags 233 } 234 235 func parseUint64(str string) (*uint64, error) { 236 var value uint64 237 if str != "" { 238 if val, err := strconv.ParseUint(str, 10, 64); err != nil { 239 return nil, fmt.Errorf("must be a non-negative integer") 240 } else { 241 value = uint64(val) 242 } 243 } 244 return &value, nil 245 } 246 247 func parseSize(str string) (*uint64, error) { 248 var value uint64 249 if str != "" { 250 mult := 1.0 251 if m, ok := mbSuffixes[str[len(str)-1:]]; ok { 252 str = str[:len(str)-1] 253 mult = m 254 } 255 val, err := strconv.ParseFloat(str, 64) 256 if err != nil || val < 0 { 257 return nil, fmt.Errorf("must be a non-negative float with optional M/G/T/P suffix") 258 } 259 val *= mult 260 value = uint64(math.Ceil(val)) 261 } 262 return &value, nil 263 } 264 265 var mbSuffixes = map[string]float64{ 266 "M": 1, 267 "G": 1024, 268 "T": 1024 * 1024, 269 "P": 1024 * 1024 * 1024, 270 } 271 272 type portSlice []Port 273 274 func (p portSlice) Len() int { return len(p) } 275 func (p portSlice) Swap(i, j int) { p[i], p[j] = p[j], p[i] } 276 func (p portSlice) Less(i, j int) bool { 277 p1 := p[i] 278 p2 := p[j] 279 if p1.Protocol != p2.Protocol { 280 return p1.Protocol < p2.Protocol 281 } 282 return p1.Number < p2.Number 283 } 284 285 // SortPorts sorts the given ports, first by protocol, 286 // then by number. 287 func SortPorts(ports []Port) { 288 sort.Sort(portSlice(ports)) 289 }