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  }