github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/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 is the architecture of the processor.
    61  	Arch *string `json:"arch,omitempty" yaml:"arch,omitempty"`
    62  
    63  	// Mem is the size of RAM in megabytes.
    64  	Mem *uint64 `json:"mem,omitempty" yaml:"mem,omitempty"`
    65  
    66  	// RootDisk is the size of the disk in megabytes.
    67  	RootDisk *uint64 `json:"root-disk,omitempty" yaml:"rootdisk,omitempty"`
    68  
    69  	// CpuCores is the number of logical cores the processor has.
    70  	CpuCores *uint64 `json:"cpu-cores,omitempty" yaml:"cpucores,omitempty"`
    71  
    72  	// CpuPower is a relative representation of the speed of the processor.
    73  	CpuPower *uint64 `json:"cpu-power,omitempty" yaml:"cpupower,omitempty"`
    74  
    75  	// Tags is a list of strings that identify the machine.
    76  	Tags *[]string `json:"tags,omitempty" yaml:"tags,omitempty"`
    77  
    78  	// AvailabilityZone defines the zone in which the machine resides.
    79  	AvailabilityZone *string `json:"availability-zone,omitempty" yaml:"availabilityzone,omitempty"`
    80  }
    81  
    82  func (hc HardwareCharacteristics) String() string {
    83  	var strs []string
    84  	if hc.Arch != nil {
    85  		strs = append(strs, fmt.Sprintf("arch=%s", *hc.Arch))
    86  	}
    87  	if hc.CpuCores != nil {
    88  		strs = append(strs, fmt.Sprintf("cores=%d", *hc.CpuCores))
    89  	}
    90  	if hc.CpuPower != nil {
    91  		strs = append(strs, fmt.Sprintf("cpu-power=%d", *hc.CpuPower))
    92  	}
    93  	if hc.Mem != nil {
    94  		strs = append(strs, fmt.Sprintf("mem=%dM", *hc.Mem))
    95  	}
    96  	if hc.RootDisk != nil {
    97  		strs = append(strs, fmt.Sprintf("root-disk=%dM", *hc.RootDisk))
    98  	}
    99  	if hc.Tags != nil && len(*hc.Tags) > 0 {
   100  		strs = append(strs, fmt.Sprintf("tags=%s", strings.Join(*hc.Tags, ",")))
   101  	}
   102  	if hc.AvailabilityZone != nil && *hc.AvailabilityZone != "" {
   103  		strs = append(strs, fmt.Sprintf("availability-zone=%s", *hc.AvailabilityZone))
   104  	}
   105  	return strings.Join(strs, " ")
   106  }
   107  
   108  // MustParseHardware constructs a HardwareCharacteristics from the supplied arguments,
   109  // as Parse, but panics on failure.
   110  func MustParseHardware(args ...string) HardwareCharacteristics {
   111  	hc, err := ParseHardware(args...)
   112  	if err != nil {
   113  		panic(err)
   114  	}
   115  	return hc
   116  }
   117  
   118  // ParseHardware constructs a HardwareCharacteristics from the supplied arguments,
   119  // each of which must contain only spaces and name=value pairs. If any
   120  // name is specified more than once, an error is returned.
   121  func ParseHardware(args ...string) (HardwareCharacteristics, error) {
   122  	hc := HardwareCharacteristics{}
   123  	for _, arg := range args {
   124  		raws := strings.Split(strings.TrimSpace(arg), " ")
   125  		for _, raw := range raws {
   126  			if raw == "" {
   127  				continue
   128  			}
   129  			if err := hc.setRaw(raw); err != nil {
   130  				return HardwareCharacteristics{}, err
   131  			}
   132  		}
   133  	}
   134  	return hc, nil
   135  }
   136  
   137  // setRaw interprets a name=value string and sets the supplied value.
   138  func (hc *HardwareCharacteristics) setRaw(raw string) error {
   139  	eq := strings.Index(raw, "=")
   140  	if eq <= 0 {
   141  		return fmt.Errorf("malformed characteristic %q", raw)
   142  	}
   143  	name, str := raw[:eq], raw[eq+1:]
   144  	var err error
   145  	switch name {
   146  	case "arch":
   147  		err = hc.setArch(str)
   148  	case "cores":
   149  		err = hc.setCpuCores(str)
   150  	case "cpu-power":
   151  		err = hc.setCpuPower(str)
   152  	case "mem":
   153  		err = hc.setMem(str)
   154  	case "root-disk":
   155  		err = hc.setRootDisk(str)
   156  	case "tags":
   157  		err = hc.setTags(str)
   158  	case "availability-zone":
   159  		err = hc.setAvailabilityZone(str)
   160  	default:
   161  		return fmt.Errorf("unknown characteristic %q", name)
   162  	}
   163  	if err != nil {
   164  		return fmt.Errorf("bad %q characteristic: %v", name, err)
   165  	}
   166  	return nil
   167  }
   168  
   169  func (hc *HardwareCharacteristics) setArch(str string) error {
   170  	if hc.Arch != nil {
   171  		return fmt.Errorf("already set")
   172  	}
   173  	if str != "" && !arch.IsSupportedArch(str) {
   174  		return fmt.Errorf("%q not recognized", str)
   175  	}
   176  	hc.Arch = &str
   177  	return nil
   178  }
   179  
   180  func (hc *HardwareCharacteristics) setCpuCores(str string) (err error) {
   181  	if hc.CpuCores != nil {
   182  		return fmt.Errorf("already set")
   183  	}
   184  	hc.CpuCores, err = parseUint64(str)
   185  	return
   186  }
   187  
   188  func (hc *HardwareCharacteristics) setCpuPower(str string) (err error) {
   189  	if hc.CpuPower != nil {
   190  		return fmt.Errorf("already set")
   191  	}
   192  	hc.CpuPower, err = parseUint64(str)
   193  	return
   194  }
   195  
   196  func (hc *HardwareCharacteristics) setMem(str string) (err error) {
   197  	if hc.Mem != nil {
   198  		return fmt.Errorf("already set")
   199  	}
   200  	hc.Mem, err = parseSize(str)
   201  	return
   202  }
   203  
   204  func (hc *HardwareCharacteristics) setRootDisk(str string) (err error) {
   205  	if hc.RootDisk != nil {
   206  		return fmt.Errorf("already set")
   207  	}
   208  	hc.RootDisk, err = parseSize(str)
   209  	return
   210  }
   211  
   212  func (hc *HardwareCharacteristics) setTags(str string) (err error) {
   213  	if hc.Tags != nil {
   214  		return fmt.Errorf("already set")
   215  	}
   216  	hc.Tags = parseTags(str)
   217  	return
   218  }
   219  
   220  func (hc *HardwareCharacteristics) setAvailabilityZone(str string) error {
   221  	if hc.AvailabilityZone != nil {
   222  		return fmt.Errorf("already set")
   223  	}
   224  	if str != "" {
   225  		hc.AvailabilityZone = &str
   226  	}
   227  	return nil
   228  }
   229  
   230  // parseTags returns the tags in the value s
   231  func parseTags(s string) *[]string {
   232  	if s == "" {
   233  		return &[]string{}
   234  	}
   235  	tags := strings.Split(s, ",")
   236  	return &tags
   237  }
   238  
   239  func parseUint64(str string) (*uint64, error) {
   240  	var value uint64
   241  	if str != "" {
   242  		if val, err := strconv.ParseUint(str, 10, 64); err != nil {
   243  			return nil, fmt.Errorf("must be a non-negative integer")
   244  		} else {
   245  			value = uint64(val)
   246  		}
   247  	}
   248  	return &value, nil
   249  }
   250  
   251  func parseSize(str string) (*uint64, error) {
   252  	var value uint64
   253  	if str != "" {
   254  		mult := 1.0
   255  		if m, ok := mbSuffixes[str[len(str)-1:]]; ok {
   256  			str = str[:len(str)-1]
   257  			mult = m
   258  		}
   259  		val, err := strconv.ParseFloat(str, 64)
   260  		if err != nil || val < 0 {
   261  			return nil, fmt.Errorf("must be a non-negative float with optional M/G/T/P suffix")
   262  		}
   263  		val *= mult
   264  		value = uint64(math.Ceil(val))
   265  	}
   266  	return &value, nil
   267  }
   268  
   269  var mbSuffixes = map[string]float64{
   270  	"M": 1,
   271  	"G": 1024,
   272  	"T": 1024 * 1024,
   273  	"P": 1024 * 1024 * 1024,
   274  }