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  }