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  }