github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/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/mgo/v3" 11 "github.com/juju/mgo/v3/bson" 12 "github.com/juju/mgo/v3/txn" 13 "github.com/juju/names/v5" 14 jujutxn "github.com/juju/txn/v3" 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 WWN string `bson:"wwn,omitempty"` 42 BusAddress string `bson:"busaddress,omitempty"` 43 Size uint64 `bson:"size"` 44 FilesystemType string `bson:"fstype,omitempty"` 45 InUse bool `bson:"inuse"` 46 MountPoint string `bson:"mountpoint,omitempty"` 47 SerialId string `bson:"serialid,omitempty"` 48 } 49 50 // WatchBlockDevices returns a new NotifyWatcher watching for 51 // changes to block devices associated with the specified machine. 52 func (sb *storageBackend) WatchBlockDevices(machine names.MachineTag) NotifyWatcher { 53 return newBlockDevicesWatcher(sb.mb, machine.Id()) 54 } 55 56 // BlockDevices returns the BlockDeviceInfo for the specified machine. 57 func (sb *storageBackend) BlockDevices(machine names.MachineTag) ([]BlockDeviceInfo, error) { 58 return getBlockDevices(sb.mb.db(), machine.Id()) 59 } 60 61 func getBlockDevices(db Database, machineId string) ([]BlockDeviceInfo, error) { 62 coll, cleanup := db.GetCollection(blockDevicesC) 63 defer cleanup() 64 65 var d blockDevicesDoc 66 err := coll.FindId(machineId).One(&d) 67 if err == mgo.ErrNotFound { 68 return nil, errors.NotFoundf("block devices not found for machine %q", machineId) 69 } else if err != nil { 70 return nil, errors.Annotate(err, "cannot get block device details") 71 } 72 return d.BlockDevices, nil 73 } 74 75 // setMachineBlockDevices updates the blockdevices collection with the 76 // currently attached block devices. Previously recorded block devices 77 // not in the list will be removed. 78 func setMachineBlockDevices(st modelBackend, machineId string, newInfo []BlockDeviceInfo) error { 79 db := st.db() 80 buildTxn := func(attempt int) ([]txn.Op, error) { 81 oldInfo, err := getBlockDevices(db, machineId) 82 if err != nil { 83 return nil, errors.Trace(err) 84 } 85 if !blockDevicesChanged(oldInfo, newInfo) { 86 return nil, jujutxn.ErrNoOperations 87 } 88 ops := []txn.Op{{ 89 C: machinesC, 90 Id: machineId, 91 Assert: notDeadDoc, 92 }, { 93 C: blockDevicesC, 94 Id: machineId, 95 Assert: txn.DocExists, 96 Update: bson.D{{"$set", bson.D{{"blockdevices", newInfo}}}}, 97 }} 98 return ops, nil 99 } 100 return db.Run(buildTxn) 101 } 102 103 func createMachineBlockDevicesOp(machineId string) txn.Op { 104 return txn.Op{ 105 C: blockDevicesC, 106 Id: machineId, 107 Insert: &blockDevicesDoc{Machine: machineId}, 108 Assert: txn.DocMissing, 109 } 110 } 111 112 func removeMachineBlockDevicesOp(machineId string) txn.Op { 113 return txn.Op{ 114 C: blockDevicesC, 115 Id: machineId, 116 Remove: true, 117 } 118 } 119 120 func blockDevicesChanged(oldDevices, newDevices []BlockDeviceInfo) bool { 121 if len(oldDevices) != len(newDevices) { 122 return true 123 } 124 for _, o := range oldDevices { 125 var found bool 126 for _, n := range newDevices { 127 if reflect.DeepEqual(o, n) { 128 found = true 129 break 130 } 131 } 132 if !found { 133 return true 134 } 135 } 136 return false 137 }