github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/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/juju/juju/arch"
    13  	"github.com/juju/juju/network"
    14  )
    15  
    16  // An instance Id is a provider-specific identifier associated with an
    17  // instance (physical or virtual machine allocated in the provider).
    18  type Id string
    19  
    20  // UnknownId can be used to explicitly specify the instance ID does not matter.
    21  const UnknownId Id = ""
    22  
    23  // Instance represents the the realization of a machine in state.
    24  type Instance interface {
    25  	// Id returns a provider-generated identifier for the Instance.
    26  	Id() Id
    27  
    28  	// Status returns the provider-specific status for the instance.
    29  	Status() string
    30  
    31  	// Refresh refreshes local knowledge of the instance from the provider.
    32  	Refresh() error
    33  
    34  	// Addresses returns a list of hostnames or ip addresses
    35  	// associated with the instance.
    36  	Addresses() ([]network.Address, error)
    37  
    38  	// OpenPorts opens the given port ranges on the instance, which
    39  	// should have been started with the given machine id.
    40  	OpenPorts(machineId string, ports []network.PortRange) error
    41  
    42  	// ClosePorts closes the given port ranges on the instance, which
    43  	// should have been started with the given machine id.
    44  	ClosePorts(machineId string, ports []network.PortRange) error
    45  
    46  	// Ports returns the set of port ranges open on the instance,
    47  	// which should have been started with the given machine id. The
    48  	// port ranges are returned as sorted by network.SortPortRanges().
    49  	Ports(machineId string) ([]network.PortRange, error)
    50  }
    51  
    52  // HardwareCharacteristics represents the characteristics of the instance (if known).
    53  // Attributes that are nil are unknown or not supported.
    54  type HardwareCharacteristics struct {
    55  	Arch     *string   `json:",omitempty" yaml:"arch,omitempty"`
    56  	Mem      *uint64   `json:",omitempty" yaml:"mem,omitempty"`
    57  	RootDisk *uint64   `json:",omitempty" yaml:"rootdisk,omitempty"`
    58  	CpuCores *uint64   `json:",omitempty" yaml:"cpucores,omitempty"`
    59  	CpuPower *uint64   `json:",omitempty" yaml:"cpupower,omitempty"`
    60  	Tags     *[]string `json:",omitempty" yaml:"tags,omitempty"`
    61  
    62  	AvailabilityZone *string `json:",omitempty" yaml:"availabilityzone,omitempty"`
    63  }
    64  
    65  func uintStr(i uint64) string {
    66  	if i == 0 {
    67  		return ""
    68  	}
    69  	return fmt.Sprintf("%d", i)
    70  }
    71  
    72  // An error reporting that an error has occurred during instance creation
    73  // (e.g. due to a failed container from on of previous deploys) and
    74  // that it is safe to restart instance creation
    75  type RetryableCreationError struct {
    76  	message string
    77  }
    78  
    79  // Returns the error message
    80  func (e RetryableCreationError) Error() string { return e.message }
    81  
    82  func NewRetryableCreationError(errorMessage string) *RetryableCreationError {
    83  	return &RetryableCreationError{errorMessage}
    84  }
    85  
    86  // IsRetryableCreationError returns true if the given error is
    87  // RetryableCreationError
    88  func IsRetryableCreationError(err error) bool {
    89  	_, ok := err.(*RetryableCreationError)
    90  	return ok
    91  }
    92  
    93  func (hc HardwareCharacteristics) String() string {
    94  	var strs []string
    95  	if hc.Arch != nil {
    96  		strs = append(strs, fmt.Sprintf("arch=%s", *hc.Arch))
    97  	}
    98  	if hc.CpuCores != nil {
    99  		strs = append(strs, fmt.Sprintf("cpu-cores=%d", *hc.CpuCores))
   100  	}
   101  	if hc.CpuPower != nil {
   102  		strs = append(strs, fmt.Sprintf("cpu-power=%d", *hc.CpuPower))
   103  	}
   104  	if hc.Mem != nil {
   105  		strs = append(strs, fmt.Sprintf("mem=%dM", *hc.Mem))
   106  	}
   107  	if hc.RootDisk != nil {
   108  		strs = append(strs, fmt.Sprintf("root-disk=%dM", *hc.RootDisk))
   109  	}
   110  	if hc.Tags != nil && len(*hc.Tags) > 0 {
   111  		strs = append(strs, fmt.Sprintf("tags=%s", strings.Join(*hc.Tags, ",")))
   112  	}
   113  	if hc.AvailabilityZone != nil && *hc.AvailabilityZone != "" {
   114  		strs = append(strs, fmt.Sprintf("availability-zone=%s", *hc.AvailabilityZone))
   115  	}
   116  	return strings.Join(strs, " ")
   117  }
   118  
   119  // Implement gnuflag.Value
   120  func (hc *HardwareCharacteristics) Set(s string) error {
   121  	parsed, err := ParseHardware(s)
   122  	if err != nil {
   123  		return err
   124  	}
   125  	*hc = parsed
   126  	return nil
   127  }
   128  
   129  // MustParseHardware constructs a HardwareCharacteristics from the supplied arguments,
   130  // as Parse, but panics on failure.
   131  func MustParseHardware(args ...string) HardwareCharacteristics {
   132  	hc, err := ParseHardware(args...)
   133  	if err != nil {
   134  		panic(err)
   135  	}
   136  	return hc
   137  }
   138  
   139  // ParseHardware constructs a HardwareCharacteristics from the supplied arguments,
   140  // each of which must contain only spaces and name=value pairs. If any
   141  // name is specified more than once, an error is returned.
   142  func ParseHardware(args ...string) (HardwareCharacteristics, error) {
   143  	hc := HardwareCharacteristics{}
   144  	for _, arg := range args {
   145  		raws := strings.Split(strings.TrimSpace(arg), " ")
   146  		for _, raw := range raws {
   147  			if raw == "" {
   148  				continue
   149  			}
   150  			if err := hc.setRaw(raw); err != nil {
   151  				return HardwareCharacteristics{}, err
   152  			}
   153  		}
   154  	}
   155  	return hc, nil
   156  }
   157  
   158  // setRaw interprets a name=value string and sets the supplied value.
   159  func (hc *HardwareCharacteristics) setRaw(raw string) error {
   160  	eq := strings.Index(raw, "=")
   161  	if eq <= 0 {
   162  		return fmt.Errorf("malformed characteristic %q", raw)
   163  	}
   164  	name, str := raw[:eq], raw[eq+1:]
   165  	var err error
   166  	switch name {
   167  	case "arch":
   168  		err = hc.setArch(str)
   169  	case "cpu-cores":
   170  		err = hc.setCpuCores(str)
   171  	case "cpu-power":
   172  		err = hc.setCpuPower(str)
   173  	case "mem":
   174  		err = hc.setMem(str)
   175  	case "root-disk":
   176  		err = hc.setRootDisk(str)
   177  	case "tags":
   178  		err = hc.setTags(str)
   179  	case "availability-zone":
   180  		err = hc.setAvailabilityZone(str)
   181  	default:
   182  		return fmt.Errorf("unknown characteristic %q", name)
   183  	}
   184  	if err != nil {
   185  		return fmt.Errorf("bad %q characteristic: %v", name, err)
   186  	}
   187  	return nil
   188  }
   189  
   190  func (hc *HardwareCharacteristics) setArch(str string) error {
   191  	if hc.Arch != nil {
   192  		return fmt.Errorf("already set")
   193  	}
   194  	if str != "" && !arch.IsSupportedArch(str) {
   195  		return fmt.Errorf("%q not recognized", str)
   196  	}
   197  	hc.Arch = &str
   198  	return nil
   199  }
   200  
   201  func (hc *HardwareCharacteristics) setCpuCores(str string) (err error) {
   202  	if hc.CpuCores != nil {
   203  		return fmt.Errorf("already set")
   204  	}
   205  	hc.CpuCores, err = parseUint64(str)
   206  	return
   207  }
   208  
   209  func (hc *HardwareCharacteristics) setCpuPower(str string) (err error) {
   210  	if hc.CpuPower != nil {
   211  		return fmt.Errorf("already set")
   212  	}
   213  	hc.CpuPower, err = parseUint64(str)
   214  	return
   215  }
   216  
   217  func (hc *HardwareCharacteristics) setMem(str string) (err error) {
   218  	if hc.Mem != nil {
   219  		return fmt.Errorf("already set")
   220  	}
   221  	hc.Mem, err = parseSize(str)
   222  	return
   223  }
   224  
   225  func (hc *HardwareCharacteristics) setRootDisk(str string) (err error) {
   226  	if hc.RootDisk != nil {
   227  		return fmt.Errorf("already set")
   228  	}
   229  	hc.RootDisk, err = parseSize(str)
   230  	return
   231  }
   232  
   233  func (hc *HardwareCharacteristics) setTags(str string) (err error) {
   234  	if hc.Tags != nil {
   235  		return fmt.Errorf("already set")
   236  	}
   237  	hc.Tags = parseTags(str)
   238  	return
   239  }
   240  
   241  func (hc *HardwareCharacteristics) setAvailabilityZone(str string) error {
   242  	if hc.AvailabilityZone != nil {
   243  		return fmt.Errorf("already set")
   244  	}
   245  	if str != "" {
   246  		hc.AvailabilityZone = &str
   247  	}
   248  	return nil
   249  }
   250  
   251  // parseTags returns the tags in the value s
   252  func parseTags(s string) *[]string {
   253  	if s == "" {
   254  		return &[]string{}
   255  	}
   256  	tags := strings.Split(s, ",")
   257  	return &tags
   258  }
   259  
   260  func parseUint64(str string) (*uint64, error) {
   261  	var value uint64
   262  	if str != "" {
   263  		if val, err := strconv.ParseUint(str, 10, 64); err != nil {
   264  			return nil, fmt.Errorf("must be a non-negative integer")
   265  		} else {
   266  			value = uint64(val)
   267  		}
   268  	}
   269  	return &value, nil
   270  }
   271  
   272  func parseSize(str string) (*uint64, error) {
   273  	var value uint64
   274  	if str != "" {
   275  		mult := 1.0
   276  		if m, ok := mbSuffixes[str[len(str)-1:]]; ok {
   277  			str = str[:len(str)-1]
   278  			mult = m
   279  		}
   280  		val, err := strconv.ParseFloat(str, 64)
   281  		if err != nil || val < 0 {
   282  			return nil, fmt.Errorf("must be a non-negative float with optional M/G/T/P suffix")
   283  		}
   284  		val *= mult
   285  		value = uint64(math.Ceil(val))
   286  	}
   287  	return &value, nil
   288  }
   289  
   290  var mbSuffixes = map[string]float64{
   291  	"M": 1,
   292  	"G": 1024,
   293  	"T": 1024 * 1024,
   294  	"P": 1024 * 1024 * 1024,
   295  }