github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/core/instance/hardwarecharacteristics.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 15 // HardwareCharacteristics represents the characteristics of the instance (if known). 16 // Attributes that are nil are unknown or not supported. 17 type HardwareCharacteristics struct { 18 // Arch is the architecture of the processor. 19 Arch *string `json:"arch,omitempty" yaml:"arch,omitempty"` 20 21 // Mem is the size of RAM in megabytes. 22 Mem *uint64 `json:"mem,omitempty" yaml:"mem,omitempty"` 23 24 // RootDisk is the size of the disk in megabytes. 25 RootDisk *uint64 `json:"root-disk,omitempty" yaml:"rootdisk,omitempty"` 26 27 // CpuCores is the number of logical cores the processor has. 28 CpuCores *uint64 `json:"cpu-cores,omitempty" yaml:"cpucores,omitempty"` 29 30 // CpuPower is a relative representation of the speed of the processor. 31 CpuPower *uint64 `json:"cpu-power,omitempty" yaml:"cpupower,omitempty"` 32 33 // Tags is a list of strings that identify the machine. 34 Tags *[]string `json:"tags,omitempty" yaml:"tags,omitempty"` 35 36 // AvailabilityZone defines the zone in which the machine resides. 37 AvailabilityZone *string `json:"availability-zone,omitempty" yaml:"availabilityzone,omitempty"` 38 } 39 40 func (hc HardwareCharacteristics) String() string { 41 var strs []string 42 if hc.Arch != nil { 43 strs = append(strs, fmt.Sprintf("arch=%s", *hc.Arch)) 44 } 45 if hc.CpuCores != nil { 46 strs = append(strs, fmt.Sprintf("cores=%d", *hc.CpuCores)) 47 } 48 if hc.CpuPower != nil { 49 strs = append(strs, fmt.Sprintf("cpu-power=%d", *hc.CpuPower)) 50 } 51 if hc.Mem != nil { 52 strs = append(strs, fmt.Sprintf("mem=%dM", *hc.Mem)) 53 } 54 if hc.RootDisk != nil { 55 strs = append(strs, fmt.Sprintf("root-disk=%dM", *hc.RootDisk)) 56 } 57 if hc.Tags != nil && len(*hc.Tags) > 0 { 58 strs = append(strs, fmt.Sprintf("tags=%s", strings.Join(*hc.Tags, ","))) 59 } 60 if hc.AvailabilityZone != nil && *hc.AvailabilityZone != "" { 61 strs = append(strs, fmt.Sprintf("availability-zone=%s", *hc.AvailabilityZone)) 62 } 63 return strings.Join(strs, " ") 64 } 65 66 // MustParseHardware constructs a HardwareCharacteristics from the supplied arguments, 67 // as Parse, but panics on failure. 68 func MustParseHardware(args ...string) HardwareCharacteristics { 69 hc, err := ParseHardware(args...) 70 if err != nil { 71 panic(err) 72 } 73 return hc 74 } 75 76 // ParseHardware constructs a HardwareCharacteristics from the supplied arguments, 77 // each of which must contain only spaces and name=value pairs. If any 78 // name is specified more than once, an error is returned. 79 func ParseHardware(args ...string) (HardwareCharacteristics, error) { 80 hc := HardwareCharacteristics{} 81 for _, arg := range args { 82 raws := strings.Split(strings.TrimSpace(arg), " ") 83 for _, raw := range raws { 84 if raw == "" { 85 continue 86 } 87 if err := hc.setRaw(raw); err != nil { 88 return HardwareCharacteristics{}, err 89 } 90 } 91 } 92 return hc, nil 93 } 94 95 // setRaw interprets a name=value string and sets the supplied value. 96 func (hc *HardwareCharacteristics) setRaw(raw string) error { 97 eq := strings.Index(raw, "=") 98 if eq <= 0 { 99 return fmt.Errorf("malformed characteristic %q", raw) 100 } 101 name, str := raw[:eq], raw[eq+1:] 102 var err error 103 switch name { 104 case "arch": 105 err = hc.setArch(str) 106 case "cores": 107 err = hc.setCpuCores(str) 108 case "cpu-power": 109 err = hc.setCpuPower(str) 110 case "mem": 111 err = hc.setMem(str) 112 case "root-disk": 113 err = hc.setRootDisk(str) 114 case "tags": 115 err = hc.setTags(str) 116 case "availability-zone": 117 err = hc.setAvailabilityZone(str) 118 default: 119 return fmt.Errorf("unknown characteristic %q", name) 120 } 121 if err != nil { 122 return fmt.Errorf("bad %q characteristic: %v", name, err) 123 } 124 return nil 125 } 126 127 func (hc *HardwareCharacteristics) setArch(str string) error { 128 if hc.Arch != nil { 129 return fmt.Errorf("already set") 130 } 131 if str != "" && !arch.IsSupportedArch(str) { 132 return fmt.Errorf("%q not recognized", str) 133 } 134 hc.Arch = &str 135 return nil 136 } 137 138 func (hc *HardwareCharacteristics) setCpuCores(str string) (err error) { 139 if hc.CpuCores != nil { 140 return fmt.Errorf("already set") 141 } 142 hc.CpuCores, err = parseUint64(str) 143 return 144 } 145 146 func (hc *HardwareCharacteristics) setCpuPower(str string) (err error) { 147 if hc.CpuPower != nil { 148 return fmt.Errorf("already set") 149 } 150 hc.CpuPower, err = parseUint64(str) 151 return 152 } 153 154 func (hc *HardwareCharacteristics) setMem(str string) (err error) { 155 if hc.Mem != nil { 156 return fmt.Errorf("already set") 157 } 158 hc.Mem, err = parseSize(str) 159 return 160 } 161 162 func (hc *HardwareCharacteristics) setRootDisk(str string) (err error) { 163 if hc.RootDisk != nil { 164 return fmt.Errorf("already set") 165 } 166 hc.RootDisk, err = parseSize(str) 167 return 168 } 169 170 func (hc *HardwareCharacteristics) setTags(str string) (err error) { 171 if hc.Tags != nil { 172 return fmt.Errorf("already set") 173 } 174 hc.Tags = parseTags(str) 175 return 176 } 177 178 func (hc *HardwareCharacteristics) setAvailabilityZone(str string) error { 179 if hc.AvailabilityZone != nil { 180 return fmt.Errorf("already set") 181 } 182 if str != "" { 183 hc.AvailabilityZone = &str 184 } 185 return nil 186 } 187 188 // parseTags returns the tags in the value s 189 func parseTags(s string) *[]string { 190 if s == "" { 191 return &[]string{} 192 } 193 tags := strings.Split(s, ",") 194 return &tags 195 } 196 197 func parseUint64(str string) (*uint64, error) { 198 var value uint64 199 if str != "" { 200 val, err := strconv.ParseUint(str, 10, 64) 201 if err != nil { 202 return nil, fmt.Errorf("must be a non-negative integer") 203 } 204 value = val 205 } 206 return &value, nil 207 } 208 209 func parseSize(str string) (*uint64, error) { 210 var value uint64 211 if str != "" { 212 mult := 1.0 213 if m, ok := mbSuffixes[str[len(str)-1:]]; ok { 214 str = str[:len(str)-1] 215 mult = m 216 } 217 val, err := strconv.ParseFloat(str, 64) 218 if err != nil || val < 0 { 219 return nil, fmt.Errorf("must be a non-negative float with optional M/G/T/P suffix") 220 } 221 val *= mult 222 value = uint64(math.Ceil(val)) 223 } 224 return &value, nil 225 } 226 227 var mbSuffixes = map[string]float64{ 228 "M": 1, 229 "G": 1024, 230 "T": 1024 * 1024, 231 "P": 1024 * 1024 * 1024, 232 }