github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/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  	host := network.Address{name, network.HostName, "", network.ScopePublic}
    79  	addrs := []network.Address{host}
    80  	// MAAS prefers to use the dns name for intra-node communication.
    81  	// When Juju looks up the address to use for communicating between nodes, it looks
    82  	// up the address bu scope. So we add a cloud local address for that purpose.
    83  	cloudHost := network.Address{name, network.HostName, "", network.ScopeCloudLocal}
    84  	addrs = append(addrs, cloudHost)
    85  
    86  	ips, err := mi.ipAddresses()
    87  	if err != nil {
    88  		return nil, err
    89  	}
    90  
    91  	for _, ip := range ips {
    92  		a := network.NewAddress(ip, network.ScopeUnknown)
    93  		addrs = append(addrs, a)
    94  	}
    95  
    96  	return addrs, nil
    97  }
    98  
    99  func (mi *maasInstance) ipAddresses() ([]string, error) {
   100  	// we have to do this the hard way, since maasObject doesn't have this built-in yet
   101  	addressArray := mi.getMaasObject().GetMap()["ip_addresses"]
   102  	if addressArray.IsNil() {
   103  		// Older MAAS versions do not return ip_addresses.
   104  		return nil, nil
   105  	}
   106  	objs, err := addressArray.GetArray()
   107  	if err != nil {
   108  		return nil, err
   109  	}
   110  	ips := make([]string, len(objs))
   111  	for i, obj := range objs {
   112  		s, err := obj.GetString()
   113  		if err != nil {
   114  			return nil, err
   115  		}
   116  		ips[i] = s
   117  	}
   118  	return ips, nil
   119  }
   120  
   121  func (mi *maasInstance) architecture() (arch, subarch string, err error) {
   122  	// MAAS may return an architecture of the form, for example,
   123  	// "amd64/generic"; we only care about the major part.
   124  	arch, err = mi.getMaasObject().GetField("architecture")
   125  	if err != nil {
   126  		return "", "", err
   127  	}
   128  	parts := strings.SplitN(arch, "/", 2)
   129  	arch = parts[0]
   130  	if len(parts) == 2 {
   131  		subarch = parts[1]
   132  	}
   133  	return arch, subarch, nil
   134  }
   135  
   136  func (mi *maasInstance) zone() string {
   137  	zone, _ := mi.getMaasObject().GetField("zone")
   138  	return zone
   139  }
   140  
   141  func (mi *maasInstance) cpuCount() (uint64, error) {
   142  	count, err := mi.getMaasObject().GetMap()["cpu_count"].GetFloat64()
   143  	if err != nil {
   144  		return 0, err
   145  	}
   146  	return uint64(count), nil
   147  }
   148  
   149  func (mi *maasInstance) memory() (uint64, error) {
   150  	mem, err := mi.getMaasObject().GetMap()["memory"].GetFloat64()
   151  	if err != nil {
   152  		return 0, err
   153  	}
   154  	return uint64(mem), nil
   155  }
   156  
   157  func (mi *maasInstance) tagNames() ([]string, error) {
   158  	obj := mi.getMaasObject().GetMap()["tag_names"]
   159  	if obj.IsNil() {
   160  		return nil, errors.NotFoundf("tag_names")
   161  	}
   162  	array, err := obj.GetArray()
   163  	if err != nil {
   164  		return nil, err
   165  	}
   166  	tags := make([]string, len(array))
   167  	for i, obj := range array {
   168  		tag, err := obj.GetString()
   169  		if err != nil {
   170  			return nil, err
   171  		}
   172  		tags[i] = tag
   173  	}
   174  	return tags, nil
   175  }
   176  
   177  func (mi *maasInstance) hardwareCharacteristics() (*instance.HardwareCharacteristics, error) {
   178  	nodeArch, _, err := mi.architecture()
   179  	if err != nil {
   180  		return nil, errors.Annotate(err, "error determining architecture")
   181  	}
   182  	nodeCpuCount, err := mi.cpuCount()
   183  	if err != nil {
   184  		return nil, errors.Annotate(err, "error determining cpu count")
   185  	}
   186  	nodeMemoryMB, err := mi.memory()
   187  	if err != nil {
   188  		return nil, errors.Annotate(err, "error determining available memory")
   189  	}
   190  	zone := mi.zone()
   191  	hc := &instance.HardwareCharacteristics{
   192  		Arch:             &nodeArch,
   193  		CpuCores:         &nodeCpuCount,
   194  		Mem:              &nodeMemoryMB,
   195  		AvailabilityZone: &zone,
   196  	}
   197  	nodeTags, err := mi.tagNames()
   198  	if err != nil && !errors.IsNotFound(err) {
   199  		return nil, errors.Annotate(err, "error determining tag names")
   200  	}
   201  	if len(nodeTags) > 0 {
   202  		hc.Tags = &nodeTags
   203  	}
   204  	return hc, nil
   205  }
   206  
   207  func (mi *maasInstance) hostname() (string, error) {
   208  	// A MAAS instance has its DNS name immediately.
   209  	return mi.getMaasObject().GetField("hostname")
   210  }
   211  
   212  // MAAS does not do firewalling so these port methods do nothing.
   213  func (mi *maasInstance) OpenPorts(machineId string, ports []network.PortRange) error {
   214  	logger.Debugf("unimplemented OpenPorts() called")
   215  	return nil
   216  }
   217  
   218  func (mi *maasInstance) ClosePorts(machineId string, ports []network.PortRange) error {
   219  	logger.Debugf("unimplemented ClosePorts() called")
   220  	return nil
   221  }
   222  
   223  func (mi *maasInstance) Ports(machineId string) ([]network.PortRange, error) {
   224  	logger.Debugf("unimplemented Ports() called")
   225  	return nil, nil
   226  }