github.com/mhilton/juju-juju@v0.0.0-20150901100907-a94dd2c73455/apiserver/common/storage.go (about) 1 // Copyright 2015 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package common 5 6 import ( 7 "path" 8 9 "github.com/juju/errors" 10 "github.com/juju/names" 11 12 "github.com/juju/juju/environs/config" 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 machien 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 machien and volume. 51 WatchVolumeAttachment(names.MachineTag, names.VolumeTag) state.NotifyWatcher 52 } 53 54 // StorageAttachmentInfo returns the StorageAttachmentInfo for the specified 55 // StorageAttachment by gathering information from related entities (volumes, 56 // filesystems). 57 func StorageAttachmentInfo( 58 st StorageInterface, 59 att state.StorageAttachment, 60 machineTag names.MachineTag, 61 ) (*storage.StorageAttachmentInfo, error) { 62 storageInstance, err := st.StorageInstance(att.StorageInstance()) 63 if err != nil { 64 return nil, errors.Annotate(err, "getting storage instance") 65 } 66 switch storageInstance.Kind() { 67 case state.StorageKindBlock: 68 return volumeStorageAttachmentInfo(st, storageInstance, machineTag) 69 case state.StorageKindFilesystem: 70 return filesystemStorageAttachmentInfo(st, storageInstance, machineTag) 71 } 72 return nil, errors.Errorf("invalid storage kind %v", storageInstance.Kind()) 73 } 74 75 func volumeStorageAttachmentInfo( 76 st StorageInterface, 77 storageInstance state.StorageInstance, 78 machineTag names.MachineTag, 79 ) (*storage.StorageAttachmentInfo, error) { 80 storageTag := storageInstance.StorageTag() 81 volume, err := st.StorageInstanceVolume(storageTag) 82 if err != nil { 83 return nil, errors.Annotate(err, "getting volume") 84 } 85 volumeInfo, err := volume.Info() 86 if err != nil { 87 return nil, errors.Annotate(err, "getting volume info") 88 } 89 volumeAttachment, err := st.VolumeAttachment(machineTag, volume.VolumeTag()) 90 if err != nil { 91 return nil, errors.Annotate(err, "getting volume attachment") 92 } 93 volumeAttachmentInfo, err := volumeAttachment.Info() 94 if err != nil { 95 return nil, errors.Annotate(err, "getting volume attachment info") 96 } 97 devicePath, err := volumeAttachmentDevicePath( 98 volumeInfo, 99 volumeAttachmentInfo, 100 ) 101 if err != nil { 102 return nil, errors.Trace(err) 103 } 104 return &storage.StorageAttachmentInfo{ 105 storage.StorageKindBlock, 106 devicePath, 107 }, nil 108 } 109 110 func filesystemStorageAttachmentInfo( 111 st StorageInterface, 112 storageInstance state.StorageInstance, 113 machineTag names.MachineTag, 114 ) (*storage.StorageAttachmentInfo, error) { 115 storageTag := storageInstance.StorageTag() 116 filesystem, err := st.StorageInstanceFilesystem(storageTag) 117 if err != nil { 118 return nil, errors.Annotate(err, "getting filesystem") 119 } 120 filesystemAttachment, err := st.FilesystemAttachment(machineTag, filesystem.FilesystemTag()) 121 if err != nil { 122 return nil, errors.Annotate(err, "getting filesystem attachment") 123 } 124 filesystemAttachmentInfo, err := filesystemAttachment.Info() 125 if err != nil { 126 return nil, errors.Annotate(err, "getting filesystem attachment info") 127 } 128 return &storage.StorageAttachmentInfo{ 129 storage.StorageKindFilesystem, 130 filesystemAttachmentInfo.MountPoint, 131 }, nil 132 } 133 134 // WatchStorageAttachment returns a state.NotifyWatcher that reacts to changes 135 // to the VolumeAttachmentInfo or FilesystemAttachmentInfo corresponding to the tags 136 // specified. 137 func WatchStorageAttachment( 138 st StorageInterface, 139 storageTag names.StorageTag, 140 machineTag names.MachineTag, 141 unitTag names.UnitTag, 142 ) (state.NotifyWatcher, error) { 143 storageInstance, err := st.StorageInstance(storageTag) 144 if err != nil { 145 return nil, errors.Annotate(err, "getting storage instance") 146 } 147 var w state.NotifyWatcher 148 switch storageInstance.Kind() { 149 case state.StorageKindBlock: 150 volume, err := st.StorageInstanceVolume(storageTag) 151 if err != nil { 152 return nil, errors.Annotate(err, "getting storage volume") 153 } 154 w = st.WatchVolumeAttachment(machineTag, volume.VolumeTag()) 155 case state.StorageKindFilesystem: 156 filesystem, err := st.StorageInstanceFilesystem(storageTag) 157 if err != nil { 158 return nil, errors.Annotate(err, "getting storage filesystem") 159 } 160 w = st.WatchFilesystemAttachment(machineTag, filesystem.FilesystemTag()) 161 default: 162 return nil, errors.Errorf("invalid storage kind %v", storageInstance.Kind()) 163 } 164 w2 := st.WatchStorageAttachment(storageTag, unitTag) 165 return newMultiNotifyWatcher(w, w2), nil 166 } 167 168 var errNoDevicePath = errors.New("cannot determine device path: no serial or persistent device name") 169 170 // volumeAttachmentDevicePath returns the absolute device path for 171 // a volume attachment. The value is only meaningful in the context 172 // of the machine that the volume is attached to. 173 func volumeAttachmentDevicePath( 174 volumeInfo state.VolumeInfo, 175 volumeAttachmentInfo state.VolumeAttachmentInfo, 176 ) (string, error) { 177 if volumeInfo.HardwareId != "" { 178 return path.Join("/dev/disk/by-id", volumeInfo.HardwareId), nil 179 } else if volumeAttachmentInfo.DeviceName != "" { 180 return path.Join("/dev", volumeAttachmentInfo.DeviceName), nil 181 } 182 return "", errNoDevicePath 183 } 184 185 // MaybeAssignedStorageInstance calls the provided function to get a 186 // StorageTag, and returns the corresponding state.StorageInstance if 187 // it didn't return an errors.IsNotAssigned error, or nil if it did. 188 func MaybeAssignedStorageInstance( 189 getTag func() (names.StorageTag, error), 190 getStorageInstance func(names.StorageTag) (state.StorageInstance, error), 191 ) (state.StorageInstance, error) { 192 tag, err := getTag() 193 if err == nil { 194 return getStorageInstance(tag) 195 } else if errors.IsNotAssigned(err) { 196 return nil, nil 197 } 198 return nil, errors.Trace(err) 199 } 200 201 // storageTags returns the tags that should be set on a volume or filesystem, 202 // if the provider supports them. 203 func storageTags( 204 storageInstance state.StorageInstance, 205 cfg *config.Config, 206 ) (map[string]string, error) { 207 uuid, _ := cfg.UUID() 208 storageTags := tags.ResourceTags(names.NewEnvironTag(uuid), cfg) 209 if storageInstance != nil { 210 storageTags[tags.JujuStorageInstance] = storageInstance.Tag().Id() 211 storageTags[tags.JujuStorageOwner] = storageInstance.Owner().Id() 212 } 213 return storageTags, nil 214 }