github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/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 "github.com/juju/mgo/v3/bson" 11 "github.com/juju/mgo/v3/txn" 12 13 "github.com/juju/juju/core/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 // ProviderNetworkID is a provider-specific ID for this address's network. 30 // Empty when not supported by the provider. 31 ProviderNetworkID string `bson:"provider-network-id,omitempty"` 32 33 // ProviderSubnetID is a provider-specific ID for this address's subnet. 34 // Empty when not supported by the provider. 35 ProviderSubnetID string `bson:"provider-subnet-id,omitempty"` 36 37 // DeviceName is the name of the link-layer device this IP address is 38 // assigned to. 39 DeviceName string `bson:"device-name"` 40 41 // MachineID is the ID of the machine this IP address's device belongs to. 42 MachineID string `bson:"machine-id"` 43 44 // SubnetCIDR is the CIDR of the subnet this IP address belongs to. 45 // The CIDR will either match a known provider subnet or a machine-local 46 // subnet (like 10.0.3.0/24 or 127.0.0.0/8). 47 SubnetCIDR string `bson:"subnet-cidr"` 48 49 // ConfigMethod is the method used to configure this IP address. 50 ConfigMethod network.AddressConfigType `bson:"config-method"` 51 52 // Value is the value of the configured IP address, e.g. 192.168.1.2 or 53 // 2001:db8::/64. 54 Value string `bson:"value"` 55 56 // DNSServers contains a list of DNS nameservers that apply to this IP 57 // address's device. Can be empty. 58 DNSServers []string `bson:"dns-servers,omitempty"` 59 60 // DNSSearchDomains contains a list of DNS domain names used to qualify 61 // hostnames, and can be empty. 62 DNSSearchDomains []string `bson:"dns-search-domains,omitempty"` 63 64 // GatewayAddress is the IP address of the gateway this IP address's device 65 // uses. Can be empty. 66 GatewayAddress string `bson:"gateway-address,omitempty"` 67 68 // IsDefaultGateway is set to true if that device/subnet is the default 69 // gateway for the machine. 70 IsDefaultGateway bool `bson:"is-default-gateway,omitempty"` 71 72 // Origin represents the authoritative source of the ipAddress. 73 // It is expected that either the provider gave us this address or the 74 // machine gave us this address. 75 // Giving us this information allows us to reason about when a ipAddress is 76 // in use. 77 // This should always be required, hence the lack of omitempty (upgrade 78 // steps should correctly assign this for all addresses) 79 Origin network.Origin `bson:"origin"` 80 81 // IsShadow indicates whether this address is virtual/floating/shadow 82 // address assigned to a NIC by a provider rather than being associated 83 // directly with a device on-machine. 84 IsShadow bool `bson:"is-shadow,omitempty"` 85 86 // IsSecondary if true, indicates that this address is not the primary 87 // address associated with the NIC. 88 // Such addresses can be added by clustering solutions like Pacemaker. 89 // We need to prevent these addresses being supplied with higher 90 // priority than primary addresses in returns to network-get calls. 91 IsSecondary bool `bson:"is-secondary,omitempty"` 92 } 93 94 // Address represents the state of an IP address assigned to a link-layer 95 // network device on a machine. 96 type Address struct { 97 st *State 98 doc ipAddressDoc 99 } 100 101 func newIPAddress(st *State, doc ipAddressDoc) *Address { 102 return &Address{st: st, doc: doc} 103 } 104 105 // DocID returns the globally unique ID of the IP address, including the model 106 // UUID as prefix. 107 func (addr *Address) DocID() string { 108 return addr.st.docID(addr.doc.DocID) 109 } 110 111 // ProviderID returns the provider-specific IP address ID, if set. 112 func (addr *Address) ProviderID() network.Id { 113 return network.Id(addr.doc.ProviderID) 114 } 115 116 // ProviderSubnetID returns the provider-specific subnet ID, if set. 117 func (addr *Address) ProviderSubnetID() network.Id { 118 return network.Id(addr.doc.ProviderSubnetID) 119 } 120 121 // ProviderNetworkID returns the provider-specific network ID, if set. 122 func (addr *Address) ProviderNetworkID() network.Id { 123 return network.Id(addr.doc.ProviderNetworkID) 124 } 125 126 // MachineID returns the ID of the machine this IP address belongs to. 127 func (addr *Address) MachineID() string { 128 return addr.doc.MachineID 129 } 130 131 // Machine returns the Machine this IP address belongs to. 132 func (addr *Address) Machine() (*Machine, error) { 133 return addr.st.Machine(addr.doc.MachineID) 134 } 135 136 // DeviceName returns the name of the link-layer device this IP address is 137 // assigned to. 138 func (addr *Address) DeviceName() string { 139 return addr.doc.DeviceName 140 } 141 142 // Device returns the LinkLayerDevice this IP address is assigned to. 143 func (addr *Address) Device() (*LinkLayerDevice, error) { 144 devID := linkLayerDeviceDocIDFromName(addr.st, addr.doc.MachineID, addr.doc.DeviceName) 145 dev, err := addr.st.LinkLayerDevice(devID) 146 return dev, errors.Trace(err) 147 } 148 149 // SubnetCIDR returns the CIDR of the subnet this IP address comes from. 150 func (addr *Address) SubnetCIDR() string { 151 return addr.doc.SubnetCIDR 152 } 153 154 // Subnet returns the Subnet this IP address comes from. Returns nil and 155 // errors.NotFoundError if the address comes from an unknown subnet (i.e. 156 // machine-local one). 157 func (addr *Address) Subnet() (*Subnet, error) { 158 return addr.st.SubnetByCIDR(addr.doc.SubnetCIDR) 159 } 160 161 // ConfigMethod returns the AddressConfigMethod used for this IP address. 162 func (addr *Address) ConfigMethod() network.AddressConfigType { 163 return addr.doc.ConfigMethod 164 } 165 166 // LoopbackConfigMethod returns whether AddressConfigMethod used for this IP 167 // address was loopback. 168 func (addr *Address) LoopbackConfigMethod() bool { 169 return addr.doc.ConfigMethod == network.ConfigLoopback 170 } 171 172 // Value returns the value of this IP address. 173 func (addr *Address) Value() string { 174 return addr.doc.Value 175 } 176 177 // DNSServers returns the list of DNS nameservers to use, which can be empty. 178 func (addr *Address) DNSServers() []string { 179 return addr.doc.DNSServers 180 } 181 182 // DNSSearchDomains returns the list of DNS domains to use for qualifying 183 // hostnames. Can be empty. 184 func (addr *Address) DNSSearchDomains() []string { 185 return addr.doc.DNSSearchDomains 186 } 187 188 // GatewayAddress returns the gateway address to use, which can be empty. 189 func (addr *Address) GatewayAddress() string { 190 return addr.doc.GatewayAddress 191 } 192 193 // IsDefaultGateway returns true if this address is used for the default 194 // gateway on the machine. 195 func (addr *Address) IsDefaultGateway() bool { 196 return addr.doc.IsDefaultGateway 197 } 198 199 // Origin represents the authoritative source of the ipAddress. 200 // it is set using precedence, with "provider" overriding "machine". 201 // It is used to determine whether the address is no longer recognised 202 // and is safe to remove. 203 func (addr *Address) Origin() network.Origin { 204 return addr.doc.Origin 205 } 206 207 // IsShadow indicates whether this address is virtual/floating/shadow 208 // address. In cross-model relations, we may want to return this address 209 // for a device if its non-shadow address is bound to a cloud-local 210 // subnet. 211 func (addr *Address) IsShadow() bool { 212 return addr.doc.IsShadow 213 } 214 215 // IsSecondary if true, indicates that this address is not the primary 216 // address associated with the NIC. 217 func (addr *Address) IsSecondary() bool { 218 return addr.doc.IsSecondary 219 } 220 221 // String returns a human-readable representation of the IP address. 222 func (addr *Address) String() string { 223 return fmt.Sprintf( 224 "%s address %q of device %q on machine %q", 225 addr.doc.ConfigMethod, addr.doc.Value, 226 addr.doc.DeviceName, addr.doc.MachineID, 227 ) 228 } 229 230 func (addr *Address) globalKey() string { 231 return ipAddressGlobalKey(addr.doc.MachineID, addr.doc.DeviceName, addr.doc.Value) 232 } 233 234 func ipAddressGlobalKey(machineID, deviceName, address string) string { 235 deviceGlobalKey := linkLayerDeviceGlobalKey(machineID, deviceName) 236 if deviceGlobalKey == "" || address == "" { 237 return "" 238 } 239 return deviceGlobalKey + "#ip#" + address 240 } 241 242 // SetOriginOps returns the transaction operations required to set the input 243 // origin for the the address. 244 // If the address has a provider ID and origin is changing from provider to 245 // machine, remove the ID from the address document and the global collection. 246 func (addr *Address) SetOriginOps(origin network.Origin) []txn.Op { 247 if addr.Origin() == origin { 248 return nil 249 } 250 251 updates := bson.D{bson.DocElem{Name: "$set", Value: bson.M{"origin": origin}}} 252 253 removeProviderID := origin == network.OriginMachine && 254 addr.Origin() == network.OriginProvider && 255 addr.ProviderID() != "" 256 257 if removeProviderID { 258 updates = append(updates, bson.DocElem{Name: "$unset", Value: bson.M{"providerid": 1}}) 259 } 260 261 ops := []txn.Op{{ 262 C: ipAddressesC, 263 Id: addr.DocID(), 264 Assert: txn.DocExists, 265 Update: updates, 266 }} 267 268 if removeProviderID { 269 return append(ops, addr.st.networkEntityGlobalKeyRemoveOp("address", addr.ProviderID())) 270 } 271 return ops 272 } 273 274 // SetProviderIDOps returns the transaction operations required to update the 275 // address with the input provider ID. 276 // Setting the provider ID updates the address origin to provider. 277 func (addr *Address) SetProviderIDOps(id network.Id) ([]txn.Op, error) { 278 // We only set the provider ID if it was previously empty. 279 if addr.doc.ProviderID != "" || id == "" || addr.doc.ProviderID == id.String() { 280 return nil, nil 281 } 282 283 // Since we assume that we are now setting the ID for the first time, 284 // ensure that it has not already been used to identify another device. 285 exists, err := addr.st.networkEntityGlobalKeyExists("address", id) 286 if err != nil { 287 return nil, errors.Trace(err) 288 } 289 if exists { 290 return nil, newProviderIDNotUniqueError(id) 291 } 292 293 return []txn.Op{ 294 addr.st.networkEntityGlobalKeyOp("address", id), 295 { 296 C: ipAddressesC, 297 Id: addr.doc.DocID, 298 Assert: txn.DocExists, 299 Update: bson.M{"$set": bson.M{ 300 "providerid": id, 301 "origin": network.OriginProvider, 302 }}, 303 }, 304 }, nil 305 } 306 307 // SetProviderNetIDsOps returns the transaction operations required to ensure 308 // that the input provider IDs are set against the address. 309 // This is distinct from SetProviderIDOps above, because we assume that the 310 // uniqueness of the IDs has already been established and that they are 311 // recorded in the global collection. 312 func (addr *Address) SetProviderNetIDsOps(networkID, subnetID network.Id) []txn.Op { 313 updates := bson.M{} 314 315 if addr.doc.ProviderNetworkID != networkID.String() { 316 updates["provider-network-id"] = networkID 317 } 318 if addr.doc.ProviderSubnetID != subnetID.String() { 319 updates["provider-subnet-id"] = subnetID 320 } 321 322 if len(updates) == 0 { 323 return nil 324 } 325 326 return []txn.Op{{ 327 C: ipAddressesC, 328 Id: addr.doc.DocID, 329 Assert: txn.DocExists, 330 Update: bson.M{"$set": updates}, 331 }} 332 } 333 334 func (addr *Address) UpdateOps(args LinkLayerDeviceAddress) ([]txn.Op, error) { 335 address, subnet, err := args.addressAndSubnet() 336 if err != nil { 337 return nil, errors.Trace(err) 338 } 339 340 newDoc := ipAddressDoc{ 341 DocID: addr.doc.DocID, 342 ModelUUID: addr.doc.ModelUUID, 343 ProviderID: args.ProviderID.String(), 344 ProviderNetworkID: args.ProviderNetworkID.String(), 345 ProviderSubnetID: args.ProviderSubnetID.String(), 346 DeviceName: args.DeviceName, 347 MachineID: addr.doc.MachineID, 348 SubnetCIDR: subnet, 349 ConfigMethod: args.ConfigMethod, 350 Value: address, 351 DNSServers: args.DNSServers, 352 DNSSearchDomains: args.DNSSearchDomains, 353 GatewayAddress: args.GatewayAddress, 354 IsDefaultGateway: args.IsDefaultGateway, 355 IsSecondary: args.IsSecondary, 356 Origin: args.Origin, 357 } 358 359 if op, updating := updateIPAddressDocOp(&addr.doc, &newDoc); updating { 360 return []txn.Op{op}, nil 361 } 362 return nil, nil 363 } 364 365 // Remove removes the IP address if it exists. 366 // No error is returned if the address was already removed. 367 func (addr *Address) Remove() error { 368 return errors.Annotatef(addr.st.db().RunTransaction(addr.RemoveOps()), "removing address %s", addr) 369 } 370 371 // RemoveOps returns transaction operations that will ensure that the 372 // address is not present in the collection and that if set, 373 // its provider ID is removed from the global register. 374 func (addr *Address) RemoveOps() []txn.Op { 375 ops := []txn.Op{{ 376 C: ipAddressesC, 377 Id: addr.doc.DocID, 378 Remove: true, 379 }} 380 381 if addr.ProviderID() != "" { 382 ops = append(ops, addr.st.networkEntityGlobalKeyRemoveOp("address", addr.ProviderID())) 383 } 384 385 return ops 386 } 387 388 // insertIPAddressDocOp returns an operation inserting the given newDoc, 389 // asserting it does not exist yet. 390 func insertIPAddressDocOp(newDoc *ipAddressDoc) txn.Op { 391 return txn.Op{ 392 C: ipAddressesC, 393 Id: newDoc.DocID, 394 Assert: txn.DocMissing, 395 Insert: *newDoc, 396 } 397 } 398 399 func strsDiffer(a, b []string) bool { 400 if len(a) != len(b) { 401 return true 402 } 403 for i := range a { 404 if a[i] != b[i] { 405 return true 406 } 407 } 408 return false 409 } 410 411 // updateIPAddressDocOp returns an operation updating the fields of existingDoc 412 // with the respective values of those fields in newDoc. DocID, ModelUUID, 413 // Value, MachineID, and DeviceName cannot be changed. ProviderID cannot be 414 // changed once set. DNSServers and DNSSearchDomains are deleted when nil. In 415 // all other cases newDoc values overwrites existingDoc values. 416 func updateIPAddressDocOp(existingDoc, newDoc *ipAddressDoc) (txn.Op, bool) { 417 changes := make(bson.M) 418 deletes := make(bson.M) 419 if existingDoc.ProviderID == "" && newDoc.ProviderID != "" { 420 // Only allow changing the ProviderID if it was empty. 421 changes["providerid"] = newDoc.ProviderID 422 } 423 if existingDoc.ProviderSubnetID == "" && newDoc.ProviderSubnetID != "" { 424 // Only allow changing the ProviderSubnetID if it was empty. 425 changes["provider-subnet-id"] = newDoc.ProviderSubnetID 426 } 427 if existingDoc.ConfigMethod != newDoc.ConfigMethod { 428 changes["config-method"] = newDoc.ConfigMethod 429 } 430 431 if existingDoc.SubnetCIDR != newDoc.SubnetCIDR { 432 changes["subnet-cidr"] = newDoc.SubnetCIDR 433 } 434 435 if strsDiffer(newDoc.DNSServers, existingDoc.DNSServers) { 436 if len(newDoc.DNSServers) == 0 { 437 deletes["dns-servers"] = 1 438 } else { 439 changes["dns-servers"] = newDoc.DNSServers 440 } 441 } 442 if strsDiffer(newDoc.DNSSearchDomains, existingDoc.DNSSearchDomains) { 443 if len(newDoc.DNSSearchDomains) == 0 { 444 deletes["dns-search-domains"] = 1 445 } else { 446 changes["dns-search-domains"] = newDoc.DNSSearchDomains 447 } 448 } 449 450 if existingDoc.GatewayAddress != newDoc.GatewayAddress { 451 changes["gateway-address"] = newDoc.GatewayAddress 452 } 453 454 if existingDoc.IsSecondary != newDoc.IsSecondary { 455 changes["is-secondary"] = newDoc.IsSecondary 456 } 457 458 var updates bson.D 459 if len(changes) > 0 { 460 updates = append(updates, bson.DocElem{Name: "$set", Value: changes}) 461 } 462 if len(deletes) > 0 { 463 updates = append(updates, bson.DocElem{Name: "$unset", Value: deletes}) 464 } 465 466 return txn.Op{ 467 C: ipAddressesC, 468 Id: existingDoc.DocID, 469 Assert: txn.DocExists, 470 Update: updates, 471 }, len(updates) > 0 472 } 473 474 func findAddressesQuery(machineID, deviceName string) bson.D { 475 var query bson.D 476 if machineID != "" { 477 query = append(query, bson.DocElem{Name: "machine-id", Value: machineID}) 478 } 479 if deviceName != "" { 480 query = append(query, bson.DocElem{Name: "device-name", Value: deviceName}) 481 } 482 return query 483 } 484 485 func (st *State) removeMatchingIPAddressesDocOps(findQuery bson.D) ([]txn.Op, error) { 486 var ops []txn.Op 487 callbackFunc := func(resultDoc *ipAddressDoc) { 488 addr := &Address{st: st, doc: *resultDoc} 489 ops = append(ops, addr.RemoveOps()...) 490 } 491 492 err := st.forEachIPAddressDoc(findQuery, callbackFunc) 493 if err != nil { 494 return nil, errors.Trace(err) 495 } 496 497 return ops, nil 498 } 499 500 func (st *State) forEachIPAddressDoc(findQuery bson.D, callbackFunc func(resultDoc *ipAddressDoc)) error { 501 addresses, closer := st.db().GetCollection(ipAddressesC) 502 defer closer() 503 504 query := addresses.Find(findQuery) 505 iter := query.Iter() 506 507 var resultDoc ipAddressDoc 508 for iter.Next(&resultDoc) { 509 callbackFunc(&resultDoc) 510 } 511 512 return errors.Trace(iter.Close()) 513 } 514 515 // AllIPAddresses returns all ip addresses in the model. 516 func (st *State) AllIPAddresses() (addresses []*Address, err error) { 517 addressesCollection, closer := st.db().GetCollection(ipAddressesC) 518 defer closer() 519 520 var docs []ipAddressDoc 521 err = addressesCollection.Find(bson.D{}).All(&docs) 522 if err != nil { 523 return nil, errors.Errorf("cannot get all ip addresses") 524 } 525 for _, a := range docs { 526 addresses = append(addresses, newIPAddress(st, a)) 527 } 528 return addresses, nil 529 }