github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/apiserver/common/storagecommon/storage.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 // Package storagecommon provides common storage-related services 5 // for API server facades. 6 package storagecommon 7 8 import ( 9 "github.com/juju/errors" 10 "gopkg.in/juju/names.v2" 11 12 "github.com/juju/juju/apiserver/common" 13 "github.com/juju/juju/environs/tags" 14 "github.com/juju/juju/state" 15 "github.com/juju/juju/storage" 16 ) 17 18 // StorageInterface is an interface for obtaining information about storage 19 // instances and related entities. 20 type StorageInterface interface { 21 // StorageInstance returns the state.StorageInstance corresponding 22 // to the specified storage tag. 23 StorageInstance(names.StorageTag) (state.StorageInstance, error) 24 25 // StorageInstanceFilesystem returns the state.Filesystem assigned 26 // to the storage instance with the specified storage tag. 27 StorageInstanceFilesystem(names.StorageTag) (state.Filesystem, error) 28 29 // StorageInstanceVolume returns the state.Volume assigned to the 30 // storage instance with the specified storage tag. 31 StorageInstanceVolume(names.StorageTag) (state.Volume, error) 32 33 // FilesystemAttachment returns the state.FilesystemAttachment 34 // corresponding to the identified machine and filesystem. 35 FilesystemAttachment(names.MachineTag, names.FilesystemTag) (state.FilesystemAttachment, error) 36 37 // VolumeAttachment returns the state.VolumeAttachment corresponding 38 // to the identified machine and volume. 39 VolumeAttachment(names.MachineTag, names.VolumeTag) (state.VolumeAttachment, error) 40 41 // WatchStorageAttachment watches for changes to the storage attachment 42 // corresponding to the identfified unit and storage instance. 43 WatchStorageAttachment(names.StorageTag, names.UnitTag) state.NotifyWatcher 44 45 // WatchFilesystemAttachment watches for changes to the filesystem 46 // attachment corresponding to the identfified machine and filesystem. 47 WatchFilesystemAttachment(names.MachineTag, names.FilesystemTag) state.NotifyWatcher 48 49 // WatchVolumeAttachment watches for changes to the volume attachment 50 // corresponding to the identfified machine and volume. 51 WatchVolumeAttachment(names.MachineTag, names.VolumeTag) state.NotifyWatcher 52 53 // WatchBlockDevices watches for changes to block devices associated 54 // with the specified machine. 55 WatchBlockDevices(names.MachineTag) state.NotifyWatcher 56 57 // BlockDevices returns information about block devices published 58 // for the specified machine. 59 BlockDevices(names.MachineTag) ([]state.BlockDeviceInfo, error) 60 } 61 62 // StorageAttachmentInfo returns the StorageAttachmentInfo for the specified 63 // StorageAttachment by gathering information from related entities (volumes, 64 // filesystems). 65 // 66 // StorageAttachmentInfo returns an error satisfying errors.IsNotProvisioned 67 // if the storage attachment is not yet fully provisioned and ready for use 68 // by a charm. 69 func StorageAttachmentInfo( 70 st StorageInterface, 71 att state.StorageAttachment, 72 machineTag names.MachineTag, 73 ) (*storage.StorageAttachmentInfo, error) { 74 storageInstance, err := st.StorageInstance(att.StorageInstance()) 75 if err != nil { 76 return nil, errors.Annotate(err, "getting storage instance") 77 } 78 switch storageInstance.Kind() { 79 case state.StorageKindBlock: 80 return volumeStorageAttachmentInfo(st, storageInstance, machineTag) 81 case state.StorageKindFilesystem: 82 return filesystemStorageAttachmentInfo(st, storageInstance, machineTag) 83 } 84 return nil, errors.Errorf("invalid storage kind %v", storageInstance.Kind()) 85 } 86 87 func volumeStorageAttachmentInfo( 88 st StorageInterface, 89 storageInstance state.StorageInstance, 90 machineTag names.MachineTag, 91 ) (*storage.StorageAttachmentInfo, error) { 92 storageTag := storageInstance.StorageTag() 93 volume, err := st.StorageInstanceVolume(storageTag) 94 if err != nil { 95 return nil, errors.Annotate(err, "getting volume") 96 } 97 volumeInfo, err := volume.Info() 98 if err != nil { 99 return nil, errors.Annotate(err, "getting volume info") 100 } 101 volumeAttachment, err := st.VolumeAttachment(machineTag, volume.VolumeTag()) 102 if err != nil { 103 return nil, errors.Annotate(err, "getting volume attachment") 104 } 105 volumeAttachmentInfo, err := volumeAttachment.Info() 106 if err != nil { 107 return nil, errors.Annotate(err, "getting volume attachment info") 108 } 109 blockDevices, err := st.BlockDevices(machineTag) 110 if err != nil { 111 return nil, errors.Annotate(err, "getting block devices") 112 } 113 blockDevice, ok := MatchingBlockDevice( 114 blockDevices, 115 volumeInfo, 116 volumeAttachmentInfo, 117 ) 118 if !ok { 119 // We must not say that a block-kind storage attachment is 120 // provisioned until its block device has shown up on the 121 // machine, otherwise the charm may attempt to use it and 122 // fail. 123 return nil, errors.NotProvisionedf("%v", names.ReadableString(storageTag)) 124 } 125 devicePath, err := volumeAttachmentDevicePath( 126 volumeInfo, 127 volumeAttachmentInfo, 128 *blockDevice, 129 ) 130 if err != nil { 131 return nil, errors.Trace(err) 132 } 133 return &storage.StorageAttachmentInfo{ 134 storage.StorageKindBlock, 135 devicePath, 136 }, nil 137 } 138 139 func filesystemStorageAttachmentInfo( 140 st StorageInterface, 141 storageInstance state.StorageInstance, 142 machineTag names.MachineTag, 143 ) (*storage.StorageAttachmentInfo, error) { 144 storageTag := storageInstance.StorageTag() 145 filesystem, err := st.StorageInstanceFilesystem(storageTag) 146 if err != nil { 147 return nil, errors.Annotate(err, "getting filesystem") 148 } 149 filesystemAttachment, err := st.FilesystemAttachment(machineTag, filesystem.FilesystemTag()) 150 if err != nil { 151 return nil, errors.Annotate(err, "getting filesystem attachment") 152 } 153 filesystemAttachmentInfo, err := filesystemAttachment.Info() 154 if err != nil { 155 return nil, errors.Annotate(err, "getting filesystem attachment info") 156 } 157 return &storage.StorageAttachmentInfo{ 158 storage.StorageKindFilesystem, 159 filesystemAttachmentInfo.MountPoint, 160 }, nil 161 } 162 163 // WatchStorageAttachment returns a state.NotifyWatcher that reacts to changes 164 // to the VolumeAttachmentInfo or FilesystemAttachmentInfo corresponding to the 165 // tags specified. 166 func WatchStorageAttachment( 167 st StorageInterface, 168 storageTag names.StorageTag, 169 machineTag names.MachineTag, 170 unitTag names.UnitTag, 171 ) (state.NotifyWatcher, error) { 172 storageInstance, err := st.StorageInstance(storageTag) 173 if err != nil { 174 return nil, errors.Annotate(err, "getting storage instance") 175 } 176 var watchers []state.NotifyWatcher 177 switch storageInstance.Kind() { 178 case state.StorageKindBlock: 179 volume, err := st.StorageInstanceVolume(storageTag) 180 if err != nil { 181 return nil, errors.Annotate(err, "getting storage volume") 182 } 183 // We need to watch both the volume attachment, and the 184 // machine's block devices. A volume attachment's block 185 // device could change (most likely, become present). 186 watchers = []state.NotifyWatcher{ 187 st.WatchVolumeAttachment(machineTag, volume.VolumeTag()), 188 // TODO(axw) 2015-09-30 #1501203 189 // We should filter the events to only those relevant 190 // to the volume attachment. This means we would need 191 // to either start th block device watcher after we 192 // have provisioned the volume attachment (cleaner?), 193 // or have the filter ignore changes until the volume 194 // attachment is provisioned. 195 st.WatchBlockDevices(machineTag), 196 } 197 case state.StorageKindFilesystem: 198 filesystem, err := st.StorageInstanceFilesystem(storageTag) 199 if err != nil { 200 return nil, errors.Annotate(err, "getting storage filesystem") 201 } 202 watchers = []state.NotifyWatcher{ 203 st.WatchFilesystemAttachment(machineTag, filesystem.FilesystemTag()), 204 } 205 default: 206 return nil, errors.Errorf("invalid storage kind %v", storageInstance.Kind()) 207 } 208 watchers = append(watchers, st.WatchStorageAttachment(storageTag, unitTag)) 209 return common.NewMultiNotifyWatcher(watchers...), nil 210 } 211 212 // volumeAttachmentDevicePath returns the absolute device path for 213 // a volume attachment. The value is only meaningful in the context 214 // of the machine that the volume is attached to. 215 func volumeAttachmentDevicePath( 216 volumeInfo state.VolumeInfo, 217 volumeAttachmentInfo state.VolumeAttachmentInfo, 218 blockDevice state.BlockDeviceInfo, 219 ) (string, error) { 220 if volumeInfo.HardwareId != "" || volumeAttachmentInfo.DeviceName != "" || volumeAttachmentInfo.DeviceLink != "" { 221 // Prefer the volume attachment's information over what is 222 // in the published block device information. 223 var deviceLinks []string 224 if volumeAttachmentInfo.DeviceLink != "" { 225 deviceLinks = []string{volumeAttachmentInfo.DeviceLink} 226 } 227 return storage.BlockDevicePath(storage.BlockDevice{ 228 HardwareId: volumeInfo.HardwareId, 229 DeviceName: volumeAttachmentInfo.DeviceName, 230 DeviceLinks: deviceLinks, 231 }) 232 } 233 return storage.BlockDevicePath(BlockDeviceFromState(blockDevice)) 234 } 235 236 // MaybeAssignedStorageInstance calls the provided function to get a 237 // StorageTag, and returns the corresponding state.StorageInstance if 238 // it didn't return an errors.IsNotAssigned error, or nil if it did. 239 func MaybeAssignedStorageInstance( 240 getTag func() (names.StorageTag, error), 241 getStorageInstance func(names.StorageTag) (state.StorageInstance, error), 242 ) (state.StorageInstance, error) { 243 tag, err := getTag() 244 if err == nil { 245 return getStorageInstance(tag) 246 } else if errors.IsNotAssigned(err) { 247 return nil, nil 248 } 249 return nil, errors.Trace(err) 250 } 251 252 // storageTags returns the tags that should be set on a volume or filesystem, 253 // if the provider supports them. 254 func storageTags( 255 storageInstance state.StorageInstance, 256 modelUUID, controllerUUID string, 257 tagger tags.ResourceTagger, 258 ) (map[string]string, error) { 259 storageTags := tags.ResourceTags( 260 names.NewModelTag(modelUUID), 261 names.NewControllerTag(controllerUUID), 262 tagger, 263 ) 264 if storageInstance != nil { 265 storageTags[tags.JujuStorageInstance] = storageInstance.Tag().Id() 266 storageTags[tags.JujuStorageOwner] = storageInstance.Owner().Id() 267 } 268 return storageTags, nil 269 }