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 }