~nskaggs/+junk/xenial-test

« back to all changes in this revision

Viewing changes to src/github.com/juju/juju/apiserver/common/storagecommon/storage.go

  • Committer: Nicholas Skaggs
  • Date: 2016-10-24 20:56:05 UTC
  • Revision ID: nicholas.skaggs@canonical.com-20161024205605-z8lta0uvuhtxwzwl
Initi with beta15

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
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.NewModelTag(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
}