Permalink
Switch branches/tags
Find file
Fetching contributors…
Cannot retrieve contributors at this time
278 lines (242 sloc) 7.5 KB
// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.
package instance
import (
"fmt"
"math"
"strconv"
"strings"
"github.com/juju/utils/arch"
"github.com/juju/juju/network"
"github.com/juju/juju/status"
)
// An instance Id is a provider-specific identifier associated with an
// instance (physical or virtual machine allocated in the provider).
type Id string
// InstanceStatus represents the status for a provider instance.
type InstanceStatus struct {
Status status.Status
Message string
}
// UnknownId can be used to explicitly specify the instance ID does not matter.
const UnknownId Id = ""
// Instance represents the the realization of a machine in state.
type Instance interface {
// Id returns a provider-generated identifier for the Instance.
Id() Id
// Status returns the provider-specific status for the instance.
Status() InstanceStatus
// Addresses returns a list of hostnames or ip addresses
// associated with the instance.
Addresses() ([]network.Address, error)
// OpenPorts opens the given port ranges on the instance, which
// should have been started with the given machine id.
OpenPorts(machineId string, rules []network.IngressRule) error
// ClosePorts closes the given port ranges on the instance, which
// should have been started with the given machine id.
ClosePorts(machineId string, rules []network.IngressRule) error
// IngressRules returns the set of ingress rules for the instance,
// which should have been applied to the given machine id. The
// rules are returned as sorted by network.SortIngressRules().
// It is expected that there be only one ingress rule result for a given
// port range - the rule's SourceCIDRs will contain all applicable source
// address rules for that port range.
IngressRules(machineId string) ([]network.IngressRule, error)
}
// HardwareCharacteristics represents the characteristics of the instance (if known).
// Attributes that are nil are unknown or not supported.
type HardwareCharacteristics struct {
// Arch is the architecture of the processor.
Arch *string `json:"arch,omitempty" yaml:"arch,omitempty"`
// Mem is the size of RAM in megabytes.
Mem *uint64 `json:"mem,omitempty" yaml:"mem,omitempty"`
// RootDisk is the size of the disk in megabytes.
RootDisk *uint64 `json:"root-disk,omitempty" yaml:"rootdisk,omitempty"`
// CpuCores is the number of logical cores the processor has.
CpuCores *uint64 `json:"cpu-cores,omitempty" yaml:"cpucores,omitempty"`
// CpuPower is a relative representation of the speed of the processor.
CpuPower *uint64 `json:"cpu-power,omitempty" yaml:"cpupower,omitempty"`
// Tags is a list of strings that identify the machine.
Tags *[]string `json:"tags,omitempty" yaml:"tags,omitempty"`
// AvailabilityZone defines the zone in which the machine resides.
AvailabilityZone *string `json:"availability-zone,omitempty" yaml:"availabilityzone,omitempty"`
}
func (hc HardwareCharacteristics) String() string {
var strs []string
if hc.Arch != nil {
strs = append(strs, fmt.Sprintf("arch=%s", *hc.Arch))
}
if hc.CpuCores != nil {
strs = append(strs, fmt.Sprintf("cores=%d", *hc.CpuCores))
}
if hc.CpuPower != nil {
strs = append(strs, fmt.Sprintf("cpu-power=%d", *hc.CpuPower))
}
if hc.Mem != nil {
strs = append(strs, fmt.Sprintf("mem=%dM", *hc.Mem))
}
if hc.RootDisk != nil {
strs = append(strs, fmt.Sprintf("root-disk=%dM", *hc.RootDisk))
}
if hc.Tags != nil && len(*hc.Tags) > 0 {
strs = append(strs, fmt.Sprintf("tags=%s", strings.Join(*hc.Tags, ",")))
}
if hc.AvailabilityZone != nil && *hc.AvailabilityZone != "" {
strs = append(strs, fmt.Sprintf("availability-zone=%s", *hc.AvailabilityZone))
}
return strings.Join(strs, " ")
}
// MustParseHardware constructs a HardwareCharacteristics from the supplied arguments,
// as Parse, but panics on failure.
func MustParseHardware(args ...string) HardwareCharacteristics {
hc, err := ParseHardware(args...)
if err != nil {
panic(err)
}
return hc
}
// ParseHardware constructs a HardwareCharacteristics from the supplied arguments,
// each of which must contain only spaces and name=value pairs. If any
// name is specified more than once, an error is returned.
func ParseHardware(args ...string) (HardwareCharacteristics, error) {
hc := HardwareCharacteristics{}
for _, arg := range args {
raws := strings.Split(strings.TrimSpace(arg), " ")
for _, raw := range raws {
if raw == "" {
continue
}
if err := hc.setRaw(raw); err != nil {
return HardwareCharacteristics{}, err
}
}
}
return hc, nil
}
// setRaw interprets a name=value string and sets the supplied value.
func (hc *HardwareCharacteristics) setRaw(raw string) error {
eq := strings.Index(raw, "=")
if eq <= 0 {
return fmt.Errorf("malformed characteristic %q", raw)
}
name, str := raw[:eq], raw[eq+1:]
var err error
switch name {
case "arch":
err = hc.setArch(str)
case "cores":
err = hc.setCpuCores(str)
case "cpu-power":
err = hc.setCpuPower(str)
case "mem":
err = hc.setMem(str)
case "root-disk":
err = hc.setRootDisk(str)
case "tags":
err = hc.setTags(str)
case "availability-zone":
err = hc.setAvailabilityZone(str)
default:
return fmt.Errorf("unknown characteristic %q", name)
}
if err != nil {
return fmt.Errorf("bad %q characteristic: %v", name, err)
}
return nil
}
func (hc *HardwareCharacteristics) setArch(str string) error {
if hc.Arch != nil {
return fmt.Errorf("already set")
}
if str != "" && !arch.IsSupportedArch(str) {
return fmt.Errorf("%q not recognized", str)
}
hc.Arch = &str
return nil
}
func (hc *HardwareCharacteristics) setCpuCores(str string) (err error) {
if hc.CpuCores != nil {
return fmt.Errorf("already set")
}
hc.CpuCores, err = parseUint64(str)
return
}
func (hc *HardwareCharacteristics) setCpuPower(str string) (err error) {
if hc.CpuPower != nil {
return fmt.Errorf("already set")
}
hc.CpuPower, err = parseUint64(str)
return
}
func (hc *HardwareCharacteristics) setMem(str string) (err error) {
if hc.Mem != nil {
return fmt.Errorf("already set")
}
hc.Mem, err = parseSize(str)
return
}
func (hc *HardwareCharacteristics) setRootDisk(str string) (err error) {
if hc.RootDisk != nil {
return fmt.Errorf("already set")
}
hc.RootDisk, err = parseSize(str)
return
}
func (hc *HardwareCharacteristics) setTags(str string) (err error) {
if hc.Tags != nil {
return fmt.Errorf("already set")
}
hc.Tags = parseTags(str)
return
}
func (hc *HardwareCharacteristics) setAvailabilityZone(str string) error {
if hc.AvailabilityZone != nil {
return fmt.Errorf("already set")
}
if str != "" {
hc.AvailabilityZone = &str
}
return nil
}
// parseTags returns the tags in the value s
func parseTags(s string) *[]string {
if s == "" {
return &[]string{}
}
tags := strings.Split(s, ",")
return &tags
}
func parseUint64(str string) (*uint64, error) {
var value uint64
if str != "" {
if val, err := strconv.ParseUint(str, 10, 64); err != nil {
return nil, fmt.Errorf("must be a non-negative integer")
} else {
value = uint64(val)
}
}
return &value, nil
}
func parseSize(str string) (*uint64, error) {
var value uint64
if str != "" {
mult := 1.0
if m, ok := mbSuffixes[str[len(str)-1:]]; ok {
str = str[:len(str)-1]
mult = m
}
val, err := strconv.ParseFloat(str, 64)
if err != nil || val < 0 {
return nil, fmt.Errorf("must be a non-negative float with optional M/G/T/P suffix")
}
val *= mult
value = uint64(math.Ceil(val))
}
return &value, nil
}
var mbSuffixes = map[string]float64{
"M": 1,
"G": 1024,
"T": 1024 * 1024,
"P": 1024 * 1024 * 1024,
}