github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/worker/storageprovisioner/blockdevices.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package storageprovisioner 5 6 import ( 7 stdcontext "context" 8 9 "github.com/juju/collections/set" 10 "github.com/juju/errors" 11 "github.com/juju/names/v5" 12 13 "github.com/juju/juju/rpc/params" 14 "github.com/juju/juju/storage" 15 ) 16 17 // machineBlockDevicesChanged is called when the block devices of the scoped 18 // machine have been seen to have changed. This triggers a refresh of all 19 // block devices for attached volumes backing pending filesystems. 20 func machineBlockDevicesChanged(ctx *context) error { 21 volumeTags := make([]names.VolumeTag, 0, len(ctx.incompleteFilesystemParams)) 22 // We must query volumes for both incomplete filesystems 23 // and incomplete filesystem attachments, because even 24 // though a filesystem attachment cannot exist without a 25 // filesystem, the filesystem may be created and attached 26 // in different sessions, and there is no guarantee that 27 // the block device will remain attached to the machine 28 // in between. 29 for _, params := range ctx.incompleteFilesystemParams { 30 if params.Volume == (names.VolumeTag{}) { 31 // Filesystem is not volume-backed. 32 continue 33 } 34 if _, ok := ctx.volumeBlockDevices[params.Volume]; ok { 35 // Backing-volume's block device is already attached. 36 continue 37 } 38 volumeTags = append(volumeTags, params.Volume) 39 } 40 for _, params := range ctx.incompleteFilesystemAttachmentParams { 41 filesystem, ok := ctx.filesystems[params.Filesystem] 42 if !ok { 43 continue 44 } 45 if filesystem.Volume == (names.VolumeTag{}) { 46 // Filesystem is not volume-backed. 47 continue 48 } 49 if _, ok := ctx.volumeBlockDevices[filesystem.Volume]; ok { 50 // Backing-volume's block device is already attached. 51 continue 52 } 53 var found bool 54 for _, tag := range volumeTags { 55 if filesystem.Volume == tag { 56 found = true 57 break 58 } 59 } 60 if !found { 61 volumeTags = append(volumeTags, filesystem.Volume) 62 } 63 } 64 // Gather any already attached volume backed filesystem attachments 65 // so we can see if the UUID of the attachment has been newly set. 66 mountedAttachments := make([]storage.FilesystemAttachmentParams, 0, len(ctx.filesystemAttachments)) 67 for _, attach := range ctx.filesystemAttachments { 68 filesystem, ok := ctx.filesystems[attach.Filesystem] 69 if !ok { 70 continue 71 } 72 if filesystem.Volume == (names.VolumeTag{}) { 73 // Filesystem is not volume-backed. 74 continue 75 } 76 if _, ok := ctx.volumeBlockDevices[filesystem.Volume]; !ok { 77 continue 78 } 79 mountedAttachments = append(mountedAttachments, storage.FilesystemAttachmentParams{ 80 AttachmentParams: storage.AttachmentParams{ 81 ReadOnly: attach.ReadOnly, 82 }, 83 Filesystem: attach.Filesystem, 84 Path: attach.Path, 85 }) 86 var found bool 87 for _, tag := range volumeTags { 88 if filesystem.Volume == tag { 89 found = true 90 break 91 } 92 } 93 if !found { 94 volumeTags = append(volumeTags, filesystem.Volume) 95 } 96 } 97 if len(volumeTags) == 0 { 98 return nil 99 } 100 updatedVolumes, err := refreshVolumeBlockDevices(ctx, volumeTags) 101 if err != nil { 102 return errors.Trace(err) 103 } 104 105 // For filesystems backed by volumes (managed filesystems), we re-run the attachment logic 106 // to allow for the fact that the mount (and its UUID) may have become available after 107 // we noticed that the volume appeared. 108 volumes := set.NewStrings() 109 for _, v := range updatedVolumes { 110 volumes.Add(v.String()) 111 } 112 var toUpdate []storage.FilesystemAttachmentParams 113 for _, a := range mountedAttachments { 114 filesystem, ok := ctx.filesystems[a.Filesystem] 115 if !ok { 116 continue 117 } 118 if volumes.Contains(filesystem.Volume.String()) { 119 toUpdate = append(toUpdate, a) 120 } 121 } 122 if len(toUpdate) == 0 { 123 return nil 124 } 125 ctx.config.Logger.Debugf("refreshing mounted filesystems: %#v", toUpdate) 126 _, err = ctx.managedFilesystemSource.AttachFilesystems(ctx.config.CloudCallContextFunc(stdcontext.Background()), toUpdate) 127 return err 128 } 129 130 // processPendingVolumeBlockDevices is called before waiting for any events, 131 // to force a block-device query for any volumes for which we have not 132 // previously observed block devices. 133 func processPendingVolumeBlockDevices(ctx *context) error { 134 if len(ctx.pendingVolumeBlockDevices) == 0 { 135 ctx.config.Logger.Tracef("no pending volume block devices") 136 return nil 137 } 138 volumeTags := make([]names.VolumeTag, len(ctx.pendingVolumeBlockDevices)) 139 for i, tag := range ctx.pendingVolumeBlockDevices.SortedValues() { 140 volumeTags[i] = tag.(names.VolumeTag) 141 } 142 // Clear out the pending set, so we don't force-refresh again. 143 ctx.pendingVolumeBlockDevices = names.NewSet() 144 _, err := refreshVolumeBlockDevices(ctx, volumeTags) 145 return err 146 } 147 148 // refreshVolumeBlockDevices refreshes the block devices for the specified volumes. 149 // It returns any volumes which have had the UUID newly set. 150 func refreshVolumeBlockDevices(ctx *context, volumeTags []names.VolumeTag) ([]names.VolumeTag, error) { 151 machineTag, ok := ctx.config.Scope.(names.MachineTag) 152 if !ok { 153 // This function should only be called by machine-scoped 154 // storage provisioners. 155 ctx.config.Logger.Warningf("refresh block devices, expected machine tag, got %v", ctx.config.Scope) 156 return nil, nil 157 } 158 ids := make([]params.MachineStorageId, len(volumeTags)) 159 for i, volumeTag := range volumeTags { 160 ids[i] = params.MachineStorageId{ 161 MachineTag: machineTag.String(), 162 AttachmentTag: volumeTag.String(), 163 } 164 } 165 var volumesWithUpdatedUUID []names.VolumeTag 166 results, err := ctx.config.Volumes.VolumeBlockDevices(ids) 167 if err != nil { 168 return nil, errors.Annotate(err, "refreshing volume block devices") 169 } 170 for i, result := range results { 171 if result.Error == nil { 172 existing, ok := ctx.volumeBlockDevices[volumeTags[i]] 173 if ok && existing.UUID == "" && result.Result.UUID != "" { 174 volumesWithUpdatedUUID = append(volumesWithUpdatedUUID, volumeTags[i]) 175 } 176 ctx.volumeBlockDevices[volumeTags[i]] = result.Result 177 for _, params := range ctx.incompleteFilesystemParams { 178 if params.Volume == volumeTags[i] { 179 updatePendingFilesystem(ctx, params) 180 } 181 } 182 for id, params := range ctx.incompleteFilesystemAttachmentParams { 183 filesystem, ok := ctx.filesystems[params.Filesystem] 184 if !ok { 185 continue 186 } 187 if filesystem.Volume == volumeTags[i] { 188 updatePendingFilesystemAttachment(ctx, id, params) 189 } 190 } 191 } else if params.IsCodeNotProvisioned(result.Error) || params.IsCodeNotFound(result.Error) { 192 // Either the volume (attachment) isn't provisioned, 193 // or the corresponding block device is not yet known. 194 // 195 // Neither of these errors is fatal; we just wait for 196 // the block device watcher to notify us again. 197 } else { 198 return nil, errors.Annotatef( 199 err, "getting block device info for volume attachment %v", 200 ids[i], 201 ) 202 } 203 } 204 return volumesWithUpdatedUUID, nil 205 }