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 }