github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/state/linklayerdevices_ipaddresses.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package state 5 6 import ( 7 "fmt" 8 9 "github.com/juju/errors" 10 "gopkg.in/mgo.v2/bson" 11 "gopkg.in/mgo.v2/txn" 12 13 "github.com/juju/juju/network" 14 ) 15 16 // ipAddressDoc describes the persistent state of an IP address assigned to a 17 // link-layer network device (a.k.a network interface card - NIC). 18 type ipAddressDoc struct { 19 // DocID is the IP address global key, prefixed by ModelUUID. 20 DocID string `bson:"_id"` 21 22 // ModelUUID is the UUID of the model this IP address belongs to. 23 ModelUUID string `bson:"model-uuid"` 24 25 // ProviderID is a provider-specific ID of the IP address, prefixed by 26 // ModelUUID. Empty when not supported by the provider. 27 ProviderID string `bson:"providerid,omitempty"` 28 29 // DeviceName is the name of the link-layer device this IP address is 30 // assigned to. 31 DeviceName string `bson:"device-name"` 32 33 // MachineID is the ID of the machine this IP address's device belongs to. 34 MachineID string `bson:"machine-id"` 35 36 // SubnetCIDR is the CIDR of the subnet this IP address belongs to. The CIDR 37 // will either match a known provider subnet or a machine-local subnet (like 38 // 10.0.3.0/24 or 127.0.0.0/8). 39 SubnetCIDR string `bson:"subnet-cidr"` 40 41 // ConfigMethod is the method used to configure this IP address. 42 ConfigMethod AddressConfigMethod `bson:"config-method"` 43 44 // Value is the value of the configured IP address, e.g. 192.168.1.2 or 45 // 2001:db8::/64. 46 Value string `bson:"value"` 47 48 // DNSServers contains a list of DNS nameservers that apply to this IP 49 // address's device. Can be empty. 50 DNSServers []string `bson:"dns-servers,omitempty"` 51 52 // DNSSearchDomains contains a list of DNS domain names used to qualify 53 // hostnames, and can be empty. 54 DNSSearchDomains []string `bson:"dns-search-domains,omitempty"` 55 56 // GatewayAddress is the IP address of the gateway this IP address's device 57 // uses. Can be empty. 58 GatewayAddress string `bson:"gateway-address,omitempty"` 59 } 60 61 // AddressConfigMethod is the method used to configure a link-layer device's IP 62 // address. 63 type AddressConfigMethod string 64 65 const ( 66 // LoopbackAddress is used for IP addresses of LoopbackDevice types. 67 LoopbackAddress AddressConfigMethod = "loopback" 68 69 // StaticAddress is used for statically configured addresses. 70 StaticAddress AddressConfigMethod = "static" 71 72 // DynamicAddress is used for addresses dynamically configured via DHCP. 73 DynamicAddress AddressConfigMethod = "dynamic" 74 75 // ManualAddress is used for manually configured addresses. 76 ManualAddress AddressConfigMethod = "manual" 77 ) 78 79 // IsValidAddressConfigMethod returns whether the given value is a valid method 80 // to configure a link-layer network device's IP address. 81 func IsValidAddressConfigMethod(value string) bool { 82 switch AddressConfigMethod(value) { 83 case LoopbackAddress, StaticAddress, DynamicAddress, ManualAddress: 84 return true 85 } 86 return false 87 } 88 89 // Address represents the state of an IP address assigned to a link-layer 90 // network device on a machine. 91 // 92 // TODO(dimitern): Rename to IPAddress once the IPAddress type is gone 93 // along with the addressable containers handling code? 94 type Address struct { 95 st *State 96 doc ipAddressDoc 97 } 98 99 func newIPAddress(st *State, doc ipAddressDoc) *Address { 100 return &Address{st: st, doc: doc} 101 } 102 103 // DocID returns the globally unique ID of the IP address, including the model 104 // UUID as prefix. 105 func (addr *Address) DocID() string { 106 return addr.st.docID(addr.doc.DocID) 107 } 108 109 // ProviderID returns the provider-specific IP address ID, if set. 110 func (addr *Address) ProviderID() network.Id { 111 return network.Id(addr.localProviderID()) 112 } 113 114 func (addr *Address) localProviderID() string { 115 return addr.st.localID(addr.doc.ProviderID) 116 } 117 118 // MachineID returns the ID of the machine this IP address belongs to. 119 func (addr *Address) MachineID() string { 120 return addr.doc.MachineID 121 } 122 123 // Machine returns the Machine this IP address belongs to. 124 func (addr *Address) Machine() (*Machine, error) { 125 return addr.st.Machine(addr.doc.MachineID) 126 } 127 128 // machineProxy is a convenience wrapper for calling Machine methods from an 129 // *Address. 130 func (addr *Address) machineProxy() *Machine { 131 return &Machine{st: addr.st, doc: machineDoc{Id: addr.doc.MachineID}} 132 } 133 134 // DeviceName returns the name of the link-layer device this IP address is 135 // assigned to. 136 func (addr *Address) DeviceName() string { 137 return addr.doc.DeviceName 138 } 139 140 // Device returns the LinkLayeyDevice this IP address is assigned to. 141 func (addr *Address) Device() (*LinkLayerDevice, error) { 142 return addr.machineProxy().LinkLayerDevice(addr.doc.DeviceName) 143 } 144 145 // SubnetCIDR returns the CIDR of the subnet this IP address comes from. 146 func (addr *Address) SubnetCIDR() string { 147 return addr.doc.SubnetCIDR 148 } 149 150 // Subnet returns the Subnet this IP address comes from. Returns nil and 151 // errors.NotFoundError if the address comes from an unknown subnet (i.e. 152 // machine-local one). 153 func (addr *Address) Subnet() (*Subnet, error) { 154 return addr.st.Subnet(addr.doc.SubnetCIDR) 155 } 156 157 // ConfigMethod returns the AddressConfigMethod used for this IP address. 158 func (addr *Address) ConfigMethod() AddressConfigMethod { 159 return addr.doc.ConfigMethod 160 } 161 162 // Value returns the value of this IP address. 163 func (addr *Address) Value() string { 164 return addr.doc.Value 165 } 166 167 // DNSServers returns the list of DNS nameservers to use, which can be empty. 168 func (addr *Address) DNSServers() []string { 169 return addr.doc.DNSServers 170 } 171 172 // DNSSearchDomains returns the list of DNS domains to use for qualifying 173 // hostnames. Can be empty. 174 func (addr *Address) DNSSearchDomains() []string { 175 return addr.doc.DNSSearchDomains 176 } 177 178 // GatewayAddress returns the gateway address to use, which can be empty. 179 func (addr *Address) GatewayAddress() string { 180 return addr.doc.GatewayAddress 181 } 182 183 // String returns a human-readable representation of the IP address. 184 func (addr *Address) String() string { 185 return fmt.Sprintf( 186 "%s address %q of device %q on machine %q", 187 addr.doc.ConfigMethod, addr.doc.Value, 188 addr.doc.DeviceName, addr.doc.MachineID, 189 ) 190 } 191 192 func (addr *Address) globalKey() string { 193 return ipAddressGlobalKey(addr.doc.MachineID, addr.doc.DeviceName, addr.doc.Value) 194 } 195 196 func ipAddressGlobalKey(machineID, deviceName, address string) string { 197 deviceGlobalKey := linkLayerDeviceGlobalKey(machineID, deviceName) 198 if deviceGlobalKey == "" || address == "" { 199 return "" 200 } 201 return deviceGlobalKey + "#ip#" + address 202 } 203 204 // Remove removes the IP address, if it exists. No error is returned when the 205 // address was already removed. 206 func (addr *Address) Remove() (err error) { 207 defer errors.DeferredAnnotatef(&err, "cannot remove %s", addr) 208 209 removeOp := removeIPAddressDocOp(addr.doc.DocID) 210 return addr.st.runTransaction([]txn.Op{removeOp}) 211 } 212 213 // removeIPAddressDocOpOp returns an operation to remove the ipAddressDoc 214 // matching the given ipAddressDocID, without asserting it still exists. 215 func removeIPAddressDocOp(ipAddressDocID string) txn.Op { 216 return txn.Op{ 217 C: ipAddressesC, 218 Id: ipAddressDocID, 219 Remove: true, 220 } 221 } 222 223 // insertIPAddressDocOp returns an operation inserting the given newDoc, 224 // asserting it does not exist yet. 225 func insertIPAddressDocOp(newDoc *ipAddressDoc) txn.Op { 226 return txn.Op{ 227 C: ipAddressesC, 228 Id: newDoc.DocID, 229 Assert: txn.DocMissing, 230 Insert: *newDoc, 231 } 232 } 233 234 // updateIPAddressDocOp returns an operation updating the fields of existingDoc 235 // with the respective values of those fields in newDoc. DocID, ModelUUID, 236 // Value, MachineID, and DeviceName cannot be changed. ProviderID cannot be 237 // changed once set. DNSServers and DNSSearchDomains are deleted when nil. In 238 // all other cases newDoc values overwrites existingDoc values. 239 func updateIPAddressDocOp(existingDoc, newDoc *ipAddressDoc) txn.Op { 240 changes := make(bson.M) 241 deletes := make(bson.M) 242 if existingDoc.ProviderID == "" && newDoc.ProviderID != "" { 243 // Only allow changing the ProviderID if it was empty. 244 changes["providerid"] = newDoc.ProviderID 245 } 246 if existingDoc.ConfigMethod != newDoc.ConfigMethod { 247 changes["config-method"] = newDoc.ConfigMethod 248 } 249 250 if existingDoc.SubnetCIDR != newDoc.SubnetCIDR { 251 changes["subnet-cidr"] = newDoc.SubnetCIDR 252 } 253 254 if newDoc.DNSServers == nil { 255 deletes["dns-servers"] = 1 256 } else { 257 changes["dns-servers"] = newDoc.DNSServers 258 } 259 260 if newDoc.DNSSearchDomains == nil { 261 deletes["dns-search-domains"] = 1 262 } else { 263 changes["dns-search-domains"] = newDoc.DNSSearchDomains 264 } 265 266 if existingDoc.GatewayAddress != newDoc.GatewayAddress { 267 changes["gateway-address"] = newDoc.GatewayAddress 268 } 269 270 var updates bson.D 271 if len(changes) > 0 { 272 updates = append(updates, bson.DocElem{Name: "$set", Value: changes}) 273 } 274 if len(deletes) > 0 { 275 updates = append(updates, bson.DocElem{Name: "$unset", Value: deletes}) 276 } 277 278 return txn.Op{ 279 C: ipAddressesC, 280 Id: existingDoc.DocID, 281 Assert: txn.DocExists, 282 Update: updates, 283 } 284 } 285 286 func findAddressesQuery(machineID, deviceName string) bson.D { 287 var query bson.D 288 if machineID != "" { 289 query = append(query, bson.DocElem{Name: "machine-id", Value: machineID}) 290 } 291 if deviceName != "" { 292 query = append(query, bson.DocElem{Name: "device-name", Value: deviceName}) 293 } 294 return query 295 } 296 297 func (st *State) removeMatchingIPAddressesDocOps(findQuery bson.D) ([]txn.Op, error) { 298 var ops []txn.Op 299 callbackFunc := func(resultDoc *ipAddressDoc) { 300 ops = append(ops, removeIPAddressDocOp(resultDoc.DocID)) 301 } 302 303 selectDocIDOnly := bson.D{{"_id", 1}} 304 err := st.forEachIPAddressDoc(findQuery, selectDocIDOnly, callbackFunc) 305 if err != nil { 306 return nil, errors.Trace(err) 307 } 308 309 return ops, nil 310 } 311 312 func (st *State) forEachIPAddressDoc(findQuery, docFieldsToSelect bson.D, callbackFunc func(resultDoc *ipAddressDoc)) error { 313 addresses, closer := st.getCollection(ipAddressesC) 314 defer closer() 315 316 query := addresses.Find(findQuery) 317 if docFieldsToSelect != nil { 318 query = query.Select(docFieldsToSelect) 319 } 320 iter := query.Iter() 321 322 var resultDoc ipAddressDoc 323 for iter.Next(&resultDoc) { 324 callbackFunc(&resultDoc) 325 } 326 327 return errors.Trace(iter.Close()) 328 }