12
"github.com/gorilla/websocket"
14
"github.com/lxc/lxd/shared"
15
"github.com/lxc/lxd/shared/logging"
17
log "gopkg.in/inconshreveable/log15.v2"
20
/* Some interesting filesystems */
22
filesystemSuperMagicTmpfs = 0x01021994
23
filesystemSuperMagicExt4 = 0xEF53
24
filesystemSuperMagicXfs = 0x58465342
25
filesystemSuperMagicNfs = 0x6969
26
filesystemSuperMagicZfs = 0x2fc12fc1
30
* filesystemDetect returns the filesystem on which
31
* the passed-in path sits
33
func filesystemDetect(path string) (string, error) {
34
fs := syscall.Statfs_t{}
36
err := syscall.Statfs(path, &fs)
42
case filesystemSuperMagicBtrfs:
44
case filesystemSuperMagicZfs:
46
case filesystemSuperMagicTmpfs:
48
case filesystemSuperMagicExt4:
50
case filesystemSuperMagicXfs:
52
case filesystemSuperMagicNfs:
55
shared.Debugf("Unknown backing filesystem type: 0x%x", fs.Type)
56
return string(fs.Type), nil
60
// storageRsyncCopy copies a directory using rsync (with the --devices option).
61
func storageRsyncCopy(source string, dest string) (string, error) {
62
if err := os.MkdirAll(dest, 0755); err != nil {
66
rsyncVerbosity := "-q"
68
rsyncVerbosity = "-vi"
71
output, err := exec.Command(
80
shared.AddSlash(source),
81
dest).CombinedOutput()
83
return string(output), err
86
// storageType defines the type of a storage
90
storageTypeBtrfs storageType = iota
97
func storageTypeToString(sType storageType) string {
99
case storageTypeBtrfs:
105
case storageTypeMock:
112
type MigrationStorageSourceDriver interface {
113
/* snapshots for this container, if any */
114
Snapshots() []container
116
/* send any bits of the container/snapshots that are possible while the
117
* container is still running.
119
SendWhileRunning(conn *websocket.Conn) error
121
/* send the final bits (e.g. a final delta snapshot for zfs, btrfs, or
122
* do a final rsync) of the fs after the container has been
123
* checkpointed. This will only be called when a container is actually
124
* being live migrated.
126
SendAfterCheckpoint(conn *websocket.Conn) error
128
/* Called after either success or failure of a migration, can be used
129
* to clean up any temporary snapshots, etc.
134
type storage interface {
135
Init(config map[string]interface{}) (storage, error)
137
GetStorageType() storageType
138
GetStorageTypeName() string
139
GetStorageTypeVersion() string
141
// ContainerCreate creates an empty container (no rootfs/metadata.yaml)
142
ContainerCreate(container container) error
144
// ContainerCreateFromImage creates a container from a image.
145
ContainerCreateFromImage(container container, imageFingerprint string) error
147
ContainerCanRestore(container container, sourceContainer container) error
148
ContainerDelete(container container) error
149
ContainerCopy(container container, sourceContainer container) error
150
ContainerStart(container container) error
151
ContainerStop(container container) error
152
ContainerRename(container container, newName string) error
153
ContainerRestore(container container, sourceContainer container) error
154
ContainerSetQuota(container container, size int64) error
155
ContainerGetUsage(container container) (int64, error)
157
ContainerSnapshotCreate(
158
snapshotContainer container, sourceContainer container) error
159
ContainerSnapshotDelete(snapshotContainer container) error
160
ContainerSnapshotRename(snapshotContainer container, newName string) error
161
ContainerSnapshotStart(container container) error
162
ContainerSnapshotStop(container container) error
164
/* for use in migrating snapshots */
165
ContainerSnapshotCreateEmpty(snapshotContainer container) error
167
ImageCreate(fingerprint string) error
168
ImageDelete(fingerprint string) error
170
MigrationType() MigrationFSType
172
// Get the pieces required to migrate the source. This contains a list
173
// of the "object" (i.e. container or snapshot, depending on whether or
174
// not it is a snapshot name) to be migrated in order, and a channel
175
// for arguments of the specific migration command. We use a channel
176
// here so we don't have to invoke `zfs send` or `rsync` or whatever
177
// and keep its stdin/stdout open for each snapshot during the course
178
// of migration, we can do it lazily.
180
// N.B. that the order here important: e.g. in btrfs/zfs, snapshots
181
// which are parents of other snapshots should be sent first, to save
182
// as much transfer as possible. However, the base container is always
183
// sent as the first object, since that is the grandparent of every
186
// We leave sending containers which are snapshots of other containers
187
// already present on the target instance as an exercise for the
188
// enterprising developer.
189
MigrationSource(container container) (MigrationStorageSourceDriver, error)
190
MigrationSink(live bool, container container, objects []container, conn *websocket.Conn) error
193
func newStorage(d *Daemon, sType storageType) (storage, error) {
194
var nilmap map[string]interface{}
195
return newStorageWithConfig(d, sType, nilmap)
198
func newStorageWithConfig(d *Daemon, sType storageType, config map[string]interface{}) (storage, error) {
200
return d.Storage, nil
206
case storageTypeBtrfs:
207
if d.Storage != nil && d.Storage.GetStorageType() == storageTypeBtrfs {
208
return d.Storage, nil
211
s = &storageLogWrapper{w: &storageBtrfs{d: d}}
213
if d.Storage != nil && d.Storage.GetStorageType() == storageTypeZfs {
214
return d.Storage, nil
217
s = &storageLogWrapper{w: &storageZfs{d: d}}
219
if d.Storage != nil && d.Storage.GetStorageType() == storageTypeLvm {
220
return d.Storage, nil
223
s = &storageLogWrapper{w: &storageLvm{d: d}}
225
if d.Storage != nil && d.Storage.GetStorageType() == storageTypeDir {
226
return d.Storage, nil
229
s = &storageLogWrapper{w: &storageDir{d: d}}
232
return s.Init(config)
235
func storageForFilename(d *Daemon, filename string) (storage, error) {
236
config := make(map[string]interface{})
237
storageType := storageTypeDir
240
return newStorageWithConfig(d, storageTypeMock, config)
243
filesystem, err := filesystemDetect(filename)
245
return nil, fmt.Errorf("couldn't detect filesystem for '%s': %v", filename, err)
248
if shared.PathExists(filename + ".lv") {
249
storageType = storageTypeLvm
250
lvPath, err := os.Readlink(filename + ".lv")
252
return nil, fmt.Errorf("couldn't read link dest for '%s': %v", filename+".lv", err)
254
vgname := filepath.Base(filepath.Dir(lvPath))
255
config["vgName"] = vgname
256
} else if shared.PathExists(filename + ".zfs") {
257
storageType = storageTypeZfs
258
} else if shared.PathExists(filename+".btrfs") || filesystem == "btrfs" {
259
storageType = storageTypeBtrfs
262
return newStorageWithConfig(d, storageType, config)
265
func storageForImage(d *Daemon, imgInfo *shared.ImageInfo) (storage, error) {
266
imageFilename := shared.VarPath("images", imgInfo.Fingerprint)
267
return storageForFilename(d, imageFilename)
270
type storageShared struct {
278
func (ss *storageShared) initShared() error {
279
ss.log = logging.AddContext(
281
log.Ctx{"driver": fmt.Sprintf("storage/%s", ss.sTypeName)},
286
func (ss *storageShared) GetStorageType() storageType {
290
func (ss *storageShared) GetStorageTypeName() string {
294
func (ss *storageShared) GetStorageTypeVersion() string {
295
return ss.sTypeVersion
298
func (ss *storageShared) shiftRootfs(c container) error {
300
rpath := c.RootfsPath()
302
shared.Log.Debug("Shifting root filesystem",
303
log.Ctx{"container": c.Name(), "rootfs": rpath})
305
idmapset := c.IdmapSet()
308
return fmt.Errorf("IdmapSet of container '%s' is nil", c.Name())
311
err := idmapset.ShiftRootfs(rpath)
313
shared.Debugf("Shift of rootfs %s failed: %s", rpath, err)
317
/* Set an acl so the container root can descend the container dir */
318
// TODO: i changed this so it calls ss.setUnprivUserAcl, which does
319
// the acl change only if the container is not privileged, think thats right.
320
return ss.setUnprivUserAcl(c, dpath)
323
func (ss *storageShared) setUnprivUserAcl(c container, destPath string) error {
324
idmapset := c.IdmapSet()
326
// Skip for privileged containers
331
// Make sure the map is valid. Skip if container uid 0 == host uid 0
332
uid, _ := idmapset.ShiftIntoNs(0, 0)
335
return fmt.Errorf("Container doesn't have a uid 0 in its map")
340
// Attempt to set a POSIX ACL first. Fallback to chmod if the fs doesn't support it.
341
acl := fmt.Sprintf("%d:rx", uid)
342
_, err := exec.Command("setfacl", "-m", acl, destPath).CombinedOutput()
344
_, err := exec.Command("chmod", "+x", destPath).CombinedOutput()
346
return fmt.Errorf("Failed to chmod the container path.")
353
type storageLogWrapper struct {
358
func (lw *storageLogWrapper) Init(config map[string]interface{}) (storage, error) {
359
_, err := lw.w.Init(config)
360
lw.log = logging.AddContext(
362
log.Ctx{"driver": fmt.Sprintf("storage/%s", lw.w.GetStorageTypeName())},
369
func (lw *storageLogWrapper) GetStorageType() storageType {
370
return lw.w.GetStorageType()
373
func (lw *storageLogWrapper) GetStorageTypeName() string {
374
return lw.w.GetStorageTypeName()
377
func (lw *storageLogWrapper) GetStorageTypeVersion() string {
378
return lw.w.GetStorageTypeVersion()
381
func (lw *storageLogWrapper) ContainerCreate(container container) error {
385
"name": container.Name(),
386
"isPrivileged": container.IsPrivileged()})
387
return lw.w.ContainerCreate(container)
390
func (lw *storageLogWrapper) ContainerCreateFromImage(
391
container container, imageFingerprint string) error {
394
"ContainerCreateFromImage",
396
"imageFingerprint": imageFingerprint,
397
"name": container.Name(),
398
"isPrivileged": container.IsPrivileged()})
399
return lw.w.ContainerCreateFromImage(container, imageFingerprint)
402
func (lw *storageLogWrapper) ContainerCanRestore(container container, sourceContainer container) error {
403
lw.log.Debug("ContainerCanRestore", log.Ctx{"container": container.Name()})
404
return lw.w.ContainerCanRestore(container, sourceContainer)
407
func (lw *storageLogWrapper) ContainerDelete(container container) error {
408
lw.log.Debug("ContainerDelete", log.Ctx{"container": container.Name()})
409
return lw.w.ContainerDelete(container)
412
func (lw *storageLogWrapper) ContainerCopy(
413
container container, sourceContainer container) error {
418
"container": container.Name(),
419
"source": sourceContainer.Name()})
420
return lw.w.ContainerCopy(container, sourceContainer)
423
func (lw *storageLogWrapper) ContainerStart(container container) error {
424
lw.log.Debug("ContainerStart", log.Ctx{"container": container.Name()})
425
return lw.w.ContainerStart(container)
428
func (lw *storageLogWrapper) ContainerStop(container container) error {
429
lw.log.Debug("ContainerStop", log.Ctx{"container": container.Name()})
430
return lw.w.ContainerStop(container)
433
func (lw *storageLogWrapper) ContainerRename(
434
container container, newName string) error {
439
"container": container.Name(),
441
return lw.w.ContainerRename(container, newName)
444
func (lw *storageLogWrapper) ContainerRestore(
445
container container, sourceContainer container) error {
450
"container": container.Name(),
451
"source": sourceContainer.Name()})
452
return lw.w.ContainerRestore(container, sourceContainer)
455
func (lw *storageLogWrapper) ContainerSetQuota(
456
container container, size int64) error {
461
"container": container.Name(),
463
return lw.w.ContainerSetQuota(container, size)
466
func (lw *storageLogWrapper) ContainerGetUsage(
467
container container) (int64, error) {
472
"container": container.Name()})
473
return lw.w.ContainerGetUsage(container)
476
func (lw *storageLogWrapper) ContainerSnapshotCreate(
477
snapshotContainer container, sourceContainer container) error {
479
lw.log.Debug("ContainerSnapshotCreate",
481
"snapshotContainer": snapshotContainer.Name(),
482
"sourceContainer": sourceContainer.Name()})
484
return lw.w.ContainerSnapshotCreate(snapshotContainer, sourceContainer)
487
func (lw *storageLogWrapper) ContainerSnapshotCreateEmpty(snapshotContainer container) error {
488
lw.log.Debug("ContainerSnapshotCreateEmpty",
490
"snapshotContainer": snapshotContainer.Name()})
492
return lw.w.ContainerSnapshotCreateEmpty(snapshotContainer)
495
func (lw *storageLogWrapper) ContainerSnapshotDelete(
496
snapshotContainer container) error {
498
lw.log.Debug("ContainerSnapshotDelete",
499
log.Ctx{"snapshotContainer": snapshotContainer.Name()})
500
return lw.w.ContainerSnapshotDelete(snapshotContainer)
503
func (lw *storageLogWrapper) ContainerSnapshotRename(
504
snapshotContainer container, newName string) error {
506
lw.log.Debug("ContainerSnapshotRename",
508
"snapshotContainer": snapshotContainer.Name(),
510
return lw.w.ContainerSnapshotRename(snapshotContainer, newName)
513
func (lw *storageLogWrapper) ContainerSnapshotStart(container container) error {
514
lw.log.Debug("ContainerStart", log.Ctx{"container": container.Name()})
515
return lw.w.ContainerSnapshotStart(container)
518
func (lw *storageLogWrapper) ContainerSnapshotStop(container container) error {
519
lw.log.Debug("ContainerStop", log.Ctx{"container": container.Name()})
520
return lw.w.ContainerSnapshotStop(container)
523
func (lw *storageLogWrapper) ImageCreate(fingerprint string) error {
526
log.Ctx{"fingerprint": fingerprint})
527
return lw.w.ImageCreate(fingerprint)
530
func (lw *storageLogWrapper) ImageDelete(fingerprint string) error {
531
lw.log.Debug("ImageDelete", log.Ctx{"fingerprint": fingerprint})
532
return lw.w.ImageDelete(fingerprint)
536
func (lw *storageLogWrapper) MigrationType() MigrationFSType {
537
return lw.w.MigrationType()
540
func (lw *storageLogWrapper) MigrationSource(container container) (MigrationStorageSourceDriver, error) {
541
lw.log.Debug("MigrationSource", log.Ctx{"container": container.Name()})
542
return lw.w.MigrationSource(container)
545
func (lw *storageLogWrapper) MigrationSink(live bool, container container, objects []container, conn *websocket.Conn) error {
546
objNames := []string{}
547
for _, obj := range objects {
548
objNames = append(objNames, obj.Name())
551
lw.log.Debug("MigrationSink", log.Ctx{
553
"container": container.Name(),
557
return lw.w.MigrationSink(live, container, objects, conn)
560
func ShiftIfNecessary(container container, srcIdmap *shared.IdmapSet) error {
561
dstIdmap := container.IdmapSet()
563
dstIdmap = new(shared.IdmapSet)
566
if !reflect.DeepEqual(srcIdmap, dstIdmap) {
569
idmapBytes, err := json.Marshal(srcIdmap.Idmap)
573
jsonIdmap = string(idmapBytes)
578
err := container.ConfigKeySet("volatile.last_state.idmap", jsonIdmap)
587
type rsyncStorageSourceDriver struct {
589
snapshots []container
592
func (s rsyncStorageSourceDriver) Snapshots() []container {
596
func (s rsyncStorageSourceDriver) SendWhileRunning(conn *websocket.Conn) error {
597
toSend := append([]container{s.container}, s.snapshots...)
599
for _, send := range toSend {
601
if err := RsyncSend(shared.AddSlash(path), conn); err != nil {
609
func (s rsyncStorageSourceDriver) SendAfterCheckpoint(conn *websocket.Conn) error {
610
/* resync anything that changed between our first send and the checkpoint */
611
return RsyncSend(shared.AddSlash(s.container.Path()), conn)
614
func (s rsyncStorageSourceDriver) Cleanup() {
618
func rsyncMigrationSource(container container) (MigrationStorageSourceDriver, error) {
619
snapshots, err := container.Snapshots()
624
return rsyncStorageSourceDriver{container, snapshots}, nil
627
func rsyncMigrationSink(live bool, container container, snapshots []container, conn *websocket.Conn) error {
628
/* the first object is the actual container */
629
if err := RsyncRecv(shared.AddSlash(container.Path()), conn); err != nil {
633
if len(snapshots) > 0 {
634
err := os.MkdirAll(shared.VarPath(fmt.Sprintf("snapshots/%s", container.Name())), 0700)
640
for _, snap := range snapshots {
641
if err := RsyncRecv(shared.AddSlash(snap.Path()), conn); err != nil {
647
/* now receive the final sync */
648
if err := RsyncRecv(shared.AddSlash(container.Path()), conn); err != nil {