github.com/makyo/juju@v0.0.0-20160425123129-2608902037e9/state/blockdevices.go (about) 1 // Copyright 2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package state 5 6 import ( 7 "reflect" 8 9 "github.com/juju/errors" 10 "github.com/juju/names" 11 jujutxn "github.com/juju/txn" 12 "gopkg.in/mgo.v2" 13 "gopkg.in/mgo.v2/bson" 14 "gopkg.in/mgo.v2/txn" 15 ) 16 17 // BlockDevice represents the state of a block device in the model. 18 type BlockDevice interface { 19 // Machine returns the ID of the machine the block device is attached to. 20 Machine() string 21 22 // Info returns the block device's BlockDeviceInfo. 23 Info() BlockDeviceInfo 24 } 25 26 // blockDevicesDoc records information about a machine's block devices. 27 type blockDevicesDoc struct { 28 DocID string `bson:"_id"` 29 ModelUUID string `bson:"model-uuid"` 30 Machine string `bson:"machineid"` 31 BlockDevices []BlockDeviceInfo `bson:"blockdevices"` 32 } 33 34 // BlockDeviceInfo describes information about a block device. 35 type BlockDeviceInfo struct { 36 DeviceName string `bson:"devicename"` 37 DeviceLinks []string `bson:"devicelinks,omitempty"` 38 Label string `bson:"label,omitempty"` 39 UUID string `bson:"uuid,omitempty"` 40 HardwareId string `bson:"hardwareid,omitempty"` 41 BusAddress string `bson:"busaddress,omitempty"` 42 Size uint64 `bson:"size"` 43 FilesystemType string `bson:"fstype,omitempty"` 44 InUse bool `bson:"inuse"` 45 MountPoint string `bson:"mountpoint,omitempty"` 46 } 47 48 // WatchBlockDevices returns a new NotifyWatcher watching for 49 // changes to block devices associated with the specified machine. 50 func (st *State) WatchBlockDevices(machine names.MachineTag) NotifyWatcher { 51 return newBlockDevicesWatcher(st, machine.Id()) 52 } 53 54 // BlockDevices returns the BlockDeviceInfo for the specified machine. 55 func (st *State) BlockDevices(machine names.MachineTag) ([]BlockDeviceInfo, error) { 56 return st.blockDevices(machine.Id()) 57 } 58 59 func (st *State) blockDevices(machineId string) ([]BlockDeviceInfo, error) { 60 coll, cleanup := st.getCollection(blockDevicesC) 61 defer cleanup() 62 63 var d blockDevicesDoc 64 err := coll.FindId(machineId).One(&d) 65 if err == mgo.ErrNotFound { 66 return nil, errors.NotFoundf("block devices not found for machine %q", machineId) 67 } else if err != nil { 68 return nil, errors.Annotate(err, "cannot get block device details") 69 } 70 return d.BlockDevices, nil 71 } 72 73 // setMachineBlockDevices updates the blockdevices collection with the 74 // currently attached block devices. Previously recorded block devices 75 // not in the list will be removed. 76 func setMachineBlockDevices(st *State, machineId string, newInfo []BlockDeviceInfo) error { 77 buildTxn := func(attempt int) ([]txn.Op, error) { 78 oldInfo, err := st.blockDevices(machineId) 79 if err != nil { 80 return nil, errors.Trace(err) 81 } 82 if !blockDevicesChanged(oldInfo, newInfo) { 83 return nil, jujutxn.ErrNoOperations 84 } 85 ops := []txn.Op{{ 86 C: machinesC, 87 Id: machineId, 88 Assert: isAliveDoc, 89 }, { 90 C: blockDevicesC, 91 Id: machineId, 92 Assert: txn.DocExists, 93 Update: bson.D{{"$set", bson.D{{"blockdevices", newInfo}}}}, 94 }} 95 return ops, nil 96 } 97 return st.run(buildTxn) 98 } 99 100 func createMachineBlockDevicesOp(machineId string) txn.Op { 101 return txn.Op{ 102 C: blockDevicesC, 103 Id: machineId, 104 Insert: &blockDevicesDoc{Machine: machineId}, 105 Assert: txn.DocMissing, 106 } 107 } 108 109 func removeMachineBlockDevicesOp(machineId string) txn.Op { 110 return txn.Op{ 111 C: blockDevicesC, 112 Id: machineId, 113 Remove: true, 114 } 115 } 116 117 func blockDevicesChanged(oldDevices, newDevices []BlockDeviceInfo) bool { 118 if len(oldDevices) != len(newDevices) { 119 return true 120 } 121 for _, o := range oldDevices { 122 var found bool 123 for _, n := range newDevices { 124 if reflect.DeepEqual(o, n) { 125 found = true 126 break 127 } 128 } 129 if !found { 130 return true 131 } 132 } 133 return false 134 }