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  }