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 }