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