github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/state/linklayerdevices.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  	"runtime"
     9  	"strings"
    10  
    11  	"github.com/juju/errors"
    12  	jujutxn "github.com/juju/txn"
    13  	"gopkg.in/mgo.v2/bson"
    14  	"gopkg.in/mgo.v2/txn"
    15  
    16  	"github.com/juju/juju/network"
    17  )
    18  
    19  // linkLayerDeviceDoc describes the persistent state of a link-layer network
    20  // device for a machine.
    21  type linkLayerDeviceDoc struct {
    22  	// DocID is the link-layer device global key, prefixed by ModelUUID.
    23  	DocID string `bson:"_id"`
    24  
    25  	// Name is the name of the network device as it appears on the machine.
    26  	Name string `bson:"name"`
    27  
    28  	// ModelUUID is the UUID of the model this device belongs to.
    29  	ModelUUID string `bson:"model-uuid"`
    30  
    31  	// MTU is the maximum transmission unit the device can handle.
    32  	MTU uint `bson:"mtu"`
    33  
    34  	// ProviderID is a provider-specific ID of the device, prefixed by
    35  	// ModelUUID. Empty when not supported by the provider.
    36  	ProviderID string `bson:"providerid,omitempty"`
    37  
    38  	// MachineID is the ID of the machine this device belongs to.
    39  	MachineID string `bson:"machine-id"`
    40  
    41  	// Type is the undelying type of the device.
    42  	Type LinkLayerDeviceType `bson:"type"`
    43  
    44  	// MACAddress is the media access control (MAC) address of the device.
    45  	MACAddress string `bson:"mac-address"`
    46  
    47  	// IsAutoStart is true if the device should be activated on boot.
    48  	IsAutoStart bool `bson:"is-auto-start"`
    49  
    50  	// IsUp is true when the device is up (enabled).
    51  	IsUp bool `bson:"is-up"`
    52  
    53  	// ParentName is the name of the parent device, which may be empty. When set
    54  	// the parent device must be on the same machine, unless the current device
    55  	// is inside a container, in which case ParentName can be a global key of a
    56  	// BridgeDevice on the host machine of the container.
    57  	ParentName string `bson:"parent-name"`
    58  }
    59  
    60  // LinkLayerDeviceType defines the type of a link-layer network device.
    61  type LinkLayerDeviceType string
    62  
    63  const (
    64  	// LoopbackDevice is used for loopback devices.
    65  	LoopbackDevice LinkLayerDeviceType = "loopback"
    66  
    67  	// EthernetDevice is used for Ethernet (IEEE 802.3) devices.
    68  	EthernetDevice LinkLayerDeviceType = "ethernet"
    69  
    70  	// VLAN_8021QDevice is used for IEEE 802.1Q VLAN devices.
    71  	VLAN_8021QDevice LinkLayerDeviceType = "802.1q"
    72  
    73  	// BondDevice is used for bonding devices.
    74  	BondDevice LinkLayerDeviceType = "bond"
    75  
    76  	// BridgeDevice is used for OSI layer-2 bridge devices.
    77  	BridgeDevice LinkLayerDeviceType = "bridge"
    78  )
    79  
    80  // IsValidLinkLayerDeviceType returns whether the given value is a valid
    81  // link-layer network device type.
    82  func IsValidLinkLayerDeviceType(value string) bool {
    83  	switch LinkLayerDeviceType(value) {
    84  	case LoopbackDevice, EthernetDevice,
    85  		VLAN_8021QDevice,
    86  		BondDevice, BridgeDevice:
    87  		return true
    88  	}
    89  	return false
    90  }
    91  
    92  // LinkLayerDevice represents the state of a link-layer network device for a
    93  // machine.
    94  type LinkLayerDevice struct {
    95  	st  *State
    96  	doc linkLayerDeviceDoc
    97  }
    98  
    99  func newLinkLayerDevice(st *State, doc linkLayerDeviceDoc) *LinkLayerDevice {
   100  	return &LinkLayerDevice{st: st, doc: doc}
   101  }
   102  
   103  // AllLinkLayerDevices returns all link layer devices in the model.
   104  func (st *State) AllLinkLayerDevices() (devices []*LinkLayerDevice, err error) {
   105  	devicesCollection, closer := st.getCollection(linkLayerDevicesC)
   106  	defer closer()
   107  
   108  	sdocs := []linkLayerDeviceDoc{}
   109  	err = devicesCollection.Find(nil).All(&sdocs)
   110  	if err != nil {
   111  		return nil, errors.Errorf("cannot get all link layer devices")
   112  	}
   113  	for _, d := range sdocs {
   114  		devices = append(devices, newLinkLayerDevice(st, d))
   115  	}
   116  	return devices, nil
   117  }
   118  
   119  // DocID returns the globally unique ID of the link-layer device, including the
   120  // model UUID as prefix.
   121  func (dev *LinkLayerDevice) DocID() string {
   122  	return dev.st.docID(dev.doc.DocID)
   123  }
   124  
   125  // Name returns the name of the device, as it appears on the machine.
   126  func (dev *LinkLayerDevice) Name() string {
   127  	return dev.doc.Name
   128  }
   129  
   130  // MTU returns the maximum transmission unit the device can handle.
   131  func (dev *LinkLayerDevice) MTU() uint {
   132  	return dev.doc.MTU
   133  }
   134  
   135  // ProviderID returns the provider-specific device ID, if set.
   136  func (dev *LinkLayerDevice) ProviderID() network.Id {
   137  	return network.Id(dev.doc.ProviderID)
   138  }
   139  
   140  // MachineID returns the ID of the machine this device belongs to.
   141  func (dev *LinkLayerDevice) MachineID() string {
   142  	return dev.doc.MachineID
   143  }
   144  
   145  // Machine returns the Machine this device belongs to.
   146  func (dev *LinkLayerDevice) Machine() (*Machine, error) {
   147  	return dev.st.Machine(dev.doc.MachineID)
   148  }
   149  
   150  // Type returns this device's underlying type.
   151  func (dev *LinkLayerDevice) Type() LinkLayerDeviceType {
   152  	return dev.doc.Type
   153  }
   154  
   155  // MACAddress returns the media access control (MAC) address of the device.
   156  func (dev *LinkLayerDevice) MACAddress() string {
   157  	return dev.doc.MACAddress
   158  }
   159  
   160  // IsAutoStart returns whether the device is set to automatically start on boot.
   161  func (dev *LinkLayerDevice) IsAutoStart() bool {
   162  	return dev.doc.IsAutoStart
   163  }
   164  
   165  // IsUp returns whether the device is currently up.
   166  func (dev *LinkLayerDevice) IsUp() bool {
   167  	return dev.doc.IsUp
   168  }
   169  
   170  // ParentName returns the name of this device's parent device, if set. The
   171  // parent device is almost always on the same machine as the child device, but
   172  // as a special case a child device on a container machine can have a parent
   173  // BridgeDevice on the container's host machine. In the last case ParentName()
   174  // returns the global key of the parent device, not just its name.
   175  func (dev *LinkLayerDevice) ParentName() string {
   176  	return dev.doc.ParentName
   177  }
   178  
   179  func (dev *LinkLayerDevice) parentDeviceNameAndMachineID() (string, string) {
   180  	if dev.doc.ParentName == "" {
   181  		// No parent set, so no ID and name to return.
   182  		return "", ""
   183  	}
   184  	// In case ParentName is a global key, try getting the host machine ID from
   185  	// there first.
   186  	hostMachineID, parentDeviceName, err := parseLinkLayerDeviceParentNameAsGlobalKey(dev.doc.ParentName)
   187  	if err != nil {
   188  		// We validate the ParentName before setting it, so this case cannot
   189  		// happen and we're only logging the error.
   190  		logger.Errorf("%s has invalid parent: %v", dev, err)
   191  		return "", ""
   192  	}
   193  	if hostMachineID == "" {
   194  		// Parent device is on the same machine and ParentName is not a global
   195  		// key.
   196  		return dev.doc.ParentName, dev.doc.MachineID
   197  	}
   198  	return parentDeviceName, hostMachineID
   199  }
   200  
   201  // ParentDevice returns the LinkLayerDevice corresponding to the parent device
   202  // of this device, if set. When no parent device name is set, it returns nil and
   203  // no error.
   204  func (dev *LinkLayerDevice) ParentDevice() (*LinkLayerDevice, error) {
   205  	if dev.doc.ParentName == "" {
   206  		return nil, nil
   207  	}
   208  
   209  	parentDeviceName, parentMachineID := dev.parentDeviceNameAndMachineID()
   210  	return dev.machineProxy(parentMachineID).LinkLayerDevice(parentDeviceName)
   211  }
   212  
   213  func (dev *LinkLayerDevice) parentDocID() string {
   214  	parentDeviceName, parentMachineID := dev.parentDeviceNameAndMachineID()
   215  	parentGlobalKey := linkLayerDeviceGlobalKey(parentMachineID, parentDeviceName)
   216  	if parentGlobalKey == "" {
   217  		return ""
   218  	}
   219  	return dev.st.docID(parentGlobalKey)
   220  }
   221  
   222  // machineProxy is a convenience wrapper for calling Machine.LinkLayerDevice()
   223  // or Machine.forEachLinkLayerDeviceDoc() from a *LinkLayerDevice and machineID.
   224  func (dev *LinkLayerDevice) machineProxy(machineID string) *Machine {
   225  	return &Machine{st: dev.st, doc: machineDoc{Id: machineID}}
   226  }
   227  
   228  // Remove removes the device, if it exists. No error is returned when the device
   229  // was already removed. ErrParentDeviceHasChildren is returned if this device is
   230  // a parent to one or more existing devices and therefore cannot be removed.
   231  func (dev *LinkLayerDevice) Remove() (err error) {
   232  	defer errors.DeferredAnnotatef(&err, "cannot remove %s", dev)
   233  
   234  	buildTxn := func(attempt int) ([]txn.Op, error) {
   235  		if attempt > 0 {
   236  			if err = dev.errNoOperationsIfMissing(); err != nil {
   237  				return nil, err
   238  			}
   239  		}
   240  		ops, err := removeLinkLayerDeviceOps(dev.st, dev.DocID(), dev.parentDocID())
   241  		if err != nil {
   242  			return nil, err
   243  		}
   244  		if dev.ProviderID() != "" {
   245  			op := dev.st.networkEntityGlobalKeyRemoveOp("linklayerdevice", dev.ProviderID())
   246  			ops = append(ops, op)
   247  		}
   248  		return ops, nil
   249  	}
   250  	return dev.st.run(buildTxn)
   251  }
   252  
   253  func (dev *LinkLayerDevice) errNoOperationsIfMissing() error {
   254  	_, err := dev.machineProxy(dev.doc.MachineID).LinkLayerDevice(dev.doc.Name)
   255  	if errors.IsNotFound(err) {
   256  		return jujutxn.ErrNoOperations
   257  	} else if err != nil {
   258  		return errors.Trace(err)
   259  	}
   260  	return nil
   261  }
   262  
   263  // removeLinkLayerDeviceOps returns the list of operations needed to remove the
   264  // device with the given linkLayerDeviceDocID, asserting it still exists and has
   265  // no children referring to it. If the device is a child, parentDeviceDocID will
   266  // be non-empty and the operations includes decrementing the parent's
   267  // NumChildren.
   268  func removeLinkLayerDeviceOps(st *State, linkLayerDeviceDocID, parentDeviceDocID string) ([]txn.Op, error) {
   269  	var numChildren int
   270  	if parentDeviceDocID == "" {
   271  		// If not a child, verify it has no children.
   272  		var err error
   273  		numChildren, err = getParentDeviceNumChildrenRefs(st, linkLayerDeviceDocID)
   274  		if err != nil {
   275  			return nil, errors.Trace(err)
   276  		}
   277  	}
   278  
   279  	// We know the DocID has a valid format for a global key, hence the last
   280  	// return below is ignored.
   281  	machineID, deviceName, canBeGlobalKey := parseLinkLayerDeviceGlobalKey(linkLayerDeviceDocID)
   282  	if !canBeGlobalKey {
   283  		return nil, errors.Errorf(
   284  			"link-layer device %q on machine %q has unexpected key format",
   285  			machineID, deviceName,
   286  		)
   287  	}
   288  	if numChildren > 0 {
   289  		return nil, newParentDeviceHasChildrenError(deviceName, numChildren)
   290  	}
   291  
   292  	var ops []txn.Op
   293  	if parentDeviceDocID != "" {
   294  		ops = append(ops, decrementDeviceNumChildrenOp(parentDeviceDocID))
   295  	}
   296  
   297  	addressesQuery := findAddressesQuery(machineID, deviceName)
   298  	if addressesOps, err := st.removeMatchingIPAddressesDocOps(addressesQuery); err == nil {
   299  		ops = append(ops, addressesOps...)
   300  	} else {
   301  		return nil, errors.Trace(err)
   302  	}
   303  
   304  	return append(ops,
   305  		removeLinkLayerDeviceDocOp(linkLayerDeviceDocID),
   306  		removeLinkLayerDevicesRefsOp(linkLayerDeviceDocID),
   307  	), nil
   308  }
   309  
   310  // removeLinkLayerDeviceDocOp returns an operation to remove the
   311  // linkLayerDeviceDoc matching the given linkLayerDeviceDocID, asserting it
   312  // still exists.
   313  func removeLinkLayerDeviceDocOp(linkLayerDeviceDocID string) txn.Op {
   314  	return txn.Op{
   315  		C:      linkLayerDevicesC,
   316  		Id:     linkLayerDeviceDocID,
   317  		Assert: txn.DocExists,
   318  		Remove: true,
   319  	}
   320  }
   321  
   322  // removeLinkLayerDeviceUnconditionallyOps returns the list of operations to
   323  // unconditionally remove the device matching the given linkLayerDeviceDocID,
   324  // along with its linkLayerDevicesRefsDoc. No asserts are included for the
   325  // existence of both documents.
   326  func removeLinkLayerDeviceUnconditionallyOps(linkLayerDeviceDocID string) []txn.Op {
   327  	// Reuse the regular remove ops, but drop their asserts.
   328  	removeDeviceDocOp := removeLinkLayerDeviceDocOp(linkLayerDeviceDocID)
   329  	removeDeviceDocOp.Assert = nil
   330  	removeRefsOp := removeLinkLayerDevicesRefsOp(linkLayerDeviceDocID)
   331  	removeRefsOp.Assert = nil
   332  
   333  	return []txn.Op{
   334  		removeDeviceDocOp,
   335  		removeRefsOp,
   336  	}
   337  }
   338  
   339  // insertLinkLayerDeviceDocOp returns an operation inserting the given newDoc,
   340  // asserting it does not exist yet.
   341  func insertLinkLayerDeviceDocOp(newDoc *linkLayerDeviceDoc) txn.Op {
   342  	return txn.Op{
   343  		C:      linkLayerDevicesC,
   344  		Id:     newDoc.DocID,
   345  		Assert: txn.DocMissing,
   346  		Insert: *newDoc,
   347  	}
   348  }
   349  
   350  // updateLinkLayerDeviceDocOp returns an operation updating the fields of
   351  // existingDoc with the respective values of those fields in newDoc. DocID,
   352  // ModelUUID, MachineID, and Name cannot be changed. ProviderID cannot be
   353  // changed once set. In all other cases newDoc values overwrites existingDoc
   354  // values.
   355  func updateLinkLayerDeviceDocOp(existingDoc, newDoc *linkLayerDeviceDoc) txn.Op {
   356  	changes := make(bson.M)
   357  	if existingDoc.ProviderID == "" && newDoc.ProviderID != "" {
   358  		// Only allow changing the ProviderID if it was empty.
   359  		changes["providerid"] = newDoc.ProviderID
   360  	}
   361  	if existingDoc.Type != newDoc.Type {
   362  		changes["type"] = newDoc.Type
   363  	}
   364  	if existingDoc.MTU != newDoc.MTU {
   365  		changes["mtu"] = newDoc.MTU
   366  	}
   367  	if existingDoc.MACAddress != newDoc.MACAddress {
   368  		changes["mac-address"] = newDoc.MACAddress
   369  	}
   370  	if existingDoc.IsAutoStart != newDoc.IsAutoStart {
   371  		changes["is-auto-start"] = newDoc.IsAutoStart
   372  	}
   373  	if existingDoc.IsUp != newDoc.IsUp {
   374  		changes["is-up"] = newDoc.IsUp
   375  	}
   376  	if existingDoc.ParentName != newDoc.ParentName {
   377  		changes["parent-name"] = newDoc.ParentName
   378  	}
   379  
   380  	var updates bson.D
   381  	if len(changes) > 0 {
   382  		updates = append(updates, bson.DocElem{Name: "$set", Value: changes})
   383  	}
   384  
   385  	return txn.Op{
   386  		C:      linkLayerDevicesC,
   387  		Id:     existingDoc.DocID,
   388  		Assert: txn.DocExists,
   389  		Update: updates,
   390  	}
   391  }
   392  
   393  // assertLinkLayerDeviceExistsOp returns an operation asserting the document
   394  // matching linkLayerDeviceDocID exists.
   395  func assertLinkLayerDeviceExistsOp(linkLayerDeviceDocID string) txn.Op {
   396  	return txn.Op{
   397  		C:      linkLayerDevicesC,
   398  		Id:     linkLayerDeviceDocID,
   399  		Assert: txn.DocExists,
   400  	}
   401  }
   402  
   403  // String returns a human-readable representation of the device.
   404  func (dev *LinkLayerDevice) String() string {
   405  	return fmt.Sprintf("%s device %q on machine %q", dev.doc.Type, dev.doc.Name, dev.doc.MachineID)
   406  }
   407  
   408  func (dev *LinkLayerDevice) globalKey() string {
   409  	return linkLayerDeviceGlobalKey(dev.doc.MachineID, dev.doc.Name)
   410  }
   411  
   412  func linkLayerDeviceGlobalKey(machineID, deviceName string) string {
   413  	if machineID == "" || deviceName == "" {
   414  		return ""
   415  	}
   416  	return "m#" + machineID + "#d#" + deviceName
   417  }
   418  
   419  func parseLinkLayerDeviceGlobalKey(globalKey string) (machineID, deviceName string, canBeGlobalKey bool) {
   420  	if !strings.Contains(globalKey, "#") {
   421  		// Can't be a global key.
   422  		return "", "", false
   423  	}
   424  	keyParts := strings.Split(globalKey, "#")
   425  	if len(keyParts) != 4 || (keyParts[0] != "m" && keyParts[2] != "d") {
   426  		// Invalid global key format.
   427  		return "", "", true
   428  	}
   429  	machineID, deviceName = keyParts[1], keyParts[3]
   430  	return machineID, deviceName, true
   431  }
   432  
   433  // IsValidLinkLayerDeviceName returns whether the given name is a valid network
   434  // link-layer device name, depending on the runtime.GOOS value.
   435  func IsValidLinkLayerDeviceName(name string) bool {
   436  	if runtimeGOOS == "linux" {
   437  		return isValidLinuxDeviceName(name)
   438  	}
   439  	hasHash := strings.Contains(name, "#")
   440  	return !hasHash && stringLengthBetween(name, 1, 255)
   441  }
   442  
   443  // runtimeGOOS is defined to allow patching in tests.
   444  var runtimeGOOS = runtime.GOOS
   445  
   446  // isValidLinuxDeviceName returns whether the given deviceName is valid,
   447  // using the same criteria as dev_valid_name(9) in the Linux kernel:
   448  // - no whitespace allowed
   449  // - length from 1 to 15 ASCII characters
   450  // - literal "." and ".." as names are not allowed.
   451  // Additionally, we don't allow "#" in the name.
   452  func isValidLinuxDeviceName(name string) bool {
   453  	hasWhitespace := whitespaceReplacer.Replace(name) != name
   454  	isDot, isDoubleDot := name == ".", name == ".."
   455  	hasValidLength := stringLengthBetween(name, 1, 15)
   456  	hasHash := strings.Contains(name, "#")
   457  
   458  	return hasValidLength && !(hasHash || hasWhitespace || isDot || isDoubleDot)
   459  }
   460  
   461  // whitespaceReplacer strips whitespace characters from the input string.
   462  var whitespaceReplacer = strings.NewReplacer(
   463  	" ", "",
   464  	"\t", "",
   465  	"\v", "",
   466  	"\n", "",
   467  	"\r", "",
   468  )
   469  
   470  func stringLengthBetween(value string, minLength, maxLength uint) bool {
   471  	length := uint(len(value))
   472  	return length >= minLength && length <= maxLength
   473  }
   474  
   475  // Addresses returns all IP addresses assigned to the device.
   476  func (dev *LinkLayerDevice) Addresses() ([]*Address, error) {
   477  	var allAddresses []*Address
   478  	callbackFunc := func(resultDoc *ipAddressDoc) {
   479  		allAddresses = append(allAddresses, newIPAddress(dev.st, *resultDoc))
   480  	}
   481  
   482  	findQuery := findAddressesQuery(dev.doc.MachineID, dev.doc.Name)
   483  	if err := dev.st.forEachIPAddressDoc(findQuery, callbackFunc); err != nil {
   484  		return nil, errors.Trace(err)
   485  	}
   486  	return allAddresses, nil
   487  }
   488  
   489  // RemoveAddresses removes all IP addresses assigned to the device.
   490  func (dev *LinkLayerDevice) RemoveAddresses() error {
   491  	findQuery := findAddressesQuery(dev.doc.MachineID, dev.doc.Name)
   492  	ops, err := dev.st.removeMatchingIPAddressesDocOps(findQuery)
   493  	if err != nil {
   494  		return errors.Trace(err)
   495  	}
   496  
   497  	return dev.st.runTransaction(ops)
   498  }