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 }