github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/provider/maas/instance.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package maas
     5  
     6  import (
     7  	"fmt"
     8  	"strings"
     9  	"sync"
    10  
    11  	"github.com/juju/errors"
    12  	"launchpad.net/gomaasapi"
    13  
    14  	"github.com/juju/juju/instance"
    15  	"github.com/juju/juju/network"
    16  )
    17  
    18  type maasInstance struct {
    19  	environ *maasEnviron
    20  
    21  	mu         sync.Mutex
    22  	maasObject *gomaasapi.MAASObject
    23  }
    24  
    25  var _ instance.Instance = (*maasInstance)(nil)
    26  
    27  func (mi *maasInstance) String() string {
    28  	hostname, err := mi.hostname()
    29  	if err != nil {
    30  		// This is meant to be impossible, but be paranoid.
    31  		hostname = fmt.Sprintf("<DNSName failed: %q>", err)
    32  	}
    33  	return fmt.Sprintf("%s:%s", hostname, mi.Id())
    34  }
    35  
    36  func (mi *maasInstance) Id() instance.Id {
    37  	return maasObjectId(mi.getMaasObject())
    38  }
    39  
    40  func maasObjectId(maasObject *gomaasapi.MAASObject) instance.Id {
    41  	// Use the node's 'resource_uri' value.
    42  	return instance.Id(maasObject.URI().String())
    43  }
    44  
    45  func (mi *maasInstance) Status() string {
    46  	// MAAS does not track node status once they're allocated.
    47  	// Since any instance that juju knows about will be an
    48  	// allocated one, it doesn't make sense to report any
    49  	// state unless we obtain it through some means other than
    50  	// through the MAAS API.
    51  	return ""
    52  }
    53  
    54  // Refresh refreshes the instance with the most up-to-date information
    55  // from the MAAS server.
    56  func (mi *maasInstance) Refresh() error {
    57  	mi.mu.Lock()
    58  	defer mi.mu.Unlock()
    59  	insts, err := mi.environ.Instances([]instance.Id{maasObjectId(mi.maasObject)})
    60  	if err != nil {
    61  		return err
    62  	}
    63  	mi.maasObject = insts[0].(*maasInstance).maasObject
    64  	return nil
    65  }
    66  
    67  func (mi *maasInstance) getMaasObject() *gomaasapi.MAASObject {
    68  	mi.mu.Lock()
    69  	defer mi.mu.Unlock()
    70  	return mi.maasObject
    71  }
    72  
    73  func (mi *maasInstance) Addresses() ([]network.Address, error) {
    74  	name, err := mi.hostname()
    75  	if err != nil {
    76  		return nil, err
    77  	}
    78  	// MAAS prefers to use the dns name for intra-node communication.
    79  	// When Juju looks up the address to use for communicating between
    80  	// nodes, it looks up the address by scope. So we add a cloud
    81  	// local address for that purpose.
    82  	addrs := network.NewAddresses(name, name)
    83  	addrs[0].Scope = network.ScopePublic
    84  	addrs[1].Scope = network.ScopeCloudLocal
    85  
    86  	// Append any remaining IP addresses after the preferred ones.
    87  	ips, err := mi.ipAddresses()
    88  	if err != nil {
    89  		return nil, err
    90  	}
    91  	addrs = append(addrs, network.NewAddresses(ips...)...)
    92  
    93  	return addrs, nil
    94  }
    95  
    96  func (mi *maasInstance) ipAddresses() ([]string, error) {
    97  	// we have to do this the hard way, since maasObject doesn't have this built-in yet
    98  	addressArray := mi.getMaasObject().GetMap()["ip_addresses"]
    99  	if addressArray.IsNil() {
   100  		// Older MAAS versions do not return ip_addresses.
   101  		return nil, nil
   102  	}
   103  	objs, err := addressArray.GetArray()
   104  	if err != nil {
   105  		return nil, err
   106  	}
   107  	ips := make([]string, len(objs))
   108  	for i, obj := range objs {
   109  		s, err := obj.GetString()
   110  		if err != nil {
   111  			return nil, err
   112  		}
   113  		ips[i] = s
   114  	}
   115  	return ips, nil
   116  }
   117  
   118  func (mi *maasInstance) architecture() (arch, subarch string, err error) {
   119  	// MAAS may return an architecture of the form, for example,
   120  	// "amd64/generic"; we only care about the major part.
   121  	arch, err = mi.getMaasObject().GetField("architecture")
   122  	if err != nil {
   123  		return "", "", err
   124  	}
   125  	parts := strings.SplitN(arch, "/", 2)
   126  	arch = parts[0]
   127  	if len(parts) == 2 {
   128  		subarch = parts[1]
   129  	}
   130  	return arch, subarch, nil
   131  }
   132  
   133  func (mi *maasInstance) zone() string {
   134  	zone, _ := mi.getMaasObject().GetField("zone")
   135  	return zone
   136  }
   137  
   138  func (mi *maasInstance) cpuCount() (uint64, error) {
   139  	count, err := mi.getMaasObject().GetMap()["cpu_count"].GetFloat64()
   140  	if err != nil {
   141  		return 0, err
   142  	}
   143  	return uint64(count), nil
   144  }
   145  
   146  func (mi *maasInstance) memory() (uint64, error) {
   147  	mem, err := mi.getMaasObject().GetMap()["memory"].GetFloat64()
   148  	if err != nil {
   149  		return 0, err
   150  	}
   151  	return uint64(mem), nil
   152  }
   153  
   154  func (mi *maasInstance) tagNames() ([]string, error) {
   155  	obj := mi.getMaasObject().GetMap()["tag_names"]
   156  	if obj.IsNil() {
   157  		return nil, errors.NotFoundf("tag_names")
   158  	}
   159  	array, err := obj.GetArray()
   160  	if err != nil {
   161  		return nil, err
   162  	}
   163  	tags := make([]string, len(array))
   164  	for i, obj := range array {
   165  		tag, err := obj.GetString()
   166  		if err != nil {
   167  			return nil, err
   168  		}
   169  		tags[i] = tag
   170  	}
   171  	return tags, nil
   172  }
   173  
   174  func (mi *maasInstance) hardwareCharacteristics() (*instance.HardwareCharacteristics, error) {
   175  	nodeArch, _, err := mi.architecture()
   176  	if err != nil {
   177  		return nil, errors.Annotate(err, "error determining architecture")
   178  	}
   179  	nodeCpuCount, err := mi.cpuCount()
   180  	if err != nil {
   181  		return nil, errors.Annotate(err, "error determining cpu count")
   182  	}
   183  	nodeMemoryMB, err := mi.memory()
   184  	if err != nil {
   185  		return nil, errors.Annotate(err, "error determining available memory")
   186  	}
   187  	zone := mi.zone()
   188  	hc := &instance.HardwareCharacteristics{
   189  		Arch:             &nodeArch,
   190  		CpuCores:         &nodeCpuCount,
   191  		Mem:              &nodeMemoryMB,
   192  		AvailabilityZone: &zone,
   193  	}
   194  	nodeTags, err := mi.tagNames()
   195  	if err != nil && !errors.IsNotFound(err) {
   196  		return nil, errors.Annotate(err, "error determining tag names")
   197  	}
   198  	if len(nodeTags) > 0 {
   199  		hc.Tags = &nodeTags
   200  	}
   201  	return hc, nil
   202  }
   203  
   204  func (mi *maasInstance) hostname() (string, error) {
   205  	// A MAAS instance has its DNS name immediately.
   206  	return mi.getMaasObject().GetField("hostname")
   207  }
   208  
   209  // MAAS does not do firewalling so these port methods do nothing.
   210  func (mi *maasInstance) OpenPorts(machineId string, ports []network.PortRange) error {
   211  	logger.Debugf("unimplemented OpenPorts() called")
   212  	return nil
   213  }
   214  
   215  func (mi *maasInstance) ClosePorts(machineId string, ports []network.PortRange) error {
   216  	logger.Debugf("unimplemented ClosePorts() called")
   217  	return nil
   218  }
   219  
   220  func (mi *maasInstance) Ports(machineId string) ([]network.PortRange, error) {
   221  	logger.Debugf("unimplemented Ports() called")
   222  	return nil, nil
   223  }