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  }