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