forked from canonical/lxd
/
backup_volume.go
142 lines (122 loc) · 4.09 KB
/
backup_volume.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
package backup
import (
"os"
"strings"
"time"
"github.com/lxc/lxd/lxd/project"
"github.com/lxc/lxd/lxd/revert"
"github.com/lxc/lxd/lxd/state"
"github.com/lxc/lxd/shared"
"github.com/lxc/lxd/shared/api"
)
// VolumeBackup represents a custom volume backup.
type VolumeBackup struct {
CommonBackup
projectName string
poolName string
volumeName string
volumeOnly bool
}
// NewVolumeBackup instantiates a new VolumeBackup struct.
func NewVolumeBackup(state *state.State, projectName, poolName, volumeName string, ID int, name string, creationDate, expiryDate time.Time, volumeOnly, optimizedStorage bool) *VolumeBackup {
return &VolumeBackup{
CommonBackup: CommonBackup{
state: state,
id: ID,
name: name,
creationDate: creationDate,
expiryDate: expiryDate,
optimizedStorage: optimizedStorage,
},
projectName: projectName,
poolName: poolName,
volumeName: volumeName,
volumeOnly: volumeOnly,
}
}
// VolumeOnly returns whether only the volume itself is to be backed up.
func (b *VolumeBackup) VolumeOnly() bool {
return b.volumeOnly
}
// OptimizedStorage returns whether the backup is to be performed using optimization format of the storage driver.
func (b *VolumeBackup) OptimizedStorage() bool {
return b.optimizedStorage
}
// Rename renames a volume backup.
func (b *VolumeBackup) Rename(newName string) error {
oldBackupPath := shared.VarPath("backups", "custom", b.poolName, project.StorageVolume(b.projectName, b.name))
newBackupPath := shared.VarPath("backups", "custom", b.poolName, project.StorageVolume(b.projectName, newName))
// Extract the old and new parent backup paths from the old and new backup names rather than use
// instance.Name() as this may be in flux if the instance itself is being renamed, whereas the relevant
// instance name is encoded into the backup names.
oldParentName, _, _ := shared.InstanceGetParentAndSnapshotName(b.name)
oldParentBackupsPath := shared.VarPath("backups", "custom", b.poolName, project.StorageVolume(b.projectName, oldParentName))
newParentName, _, _ := shared.InstanceGetParentAndSnapshotName(newName)
newParentBackupsPath := shared.VarPath("backups", "custom", b.poolName, project.StorageVolume(b.projectName, newParentName))
revert := revert.New()
defer revert.Fail()
// Create the new backup path if doesn't exist.
if !shared.PathExists(newParentBackupsPath) {
err := os.MkdirAll(newParentBackupsPath, 0700)
if err != nil {
return err
}
}
// Rename the backup directory.
err := os.Rename(oldBackupPath, newBackupPath)
if err != nil {
return err
}
revert.Add(func() { os.Rename(newBackupPath, oldBackupPath) })
// Check if we can remove the old parent directory.
empty, _ := shared.PathIsEmpty(oldParentBackupsPath)
if empty {
err := os.Remove(oldParentBackupsPath)
if err != nil {
return err
}
}
// Rename the database record.
err = b.state.Cluster.RenameVolumeBackup(b.name, newName)
if err != nil {
return err
}
revert.Success()
return nil
}
// Delete removes a volume backup.
func (b *VolumeBackup) Delete() error {
backupPath := shared.VarPath("backups", "custom", b.poolName, project.StorageVolume(b.projectName, b.name))
// Delete the on-disk data.
if shared.PathExists(backupPath) {
err := os.RemoveAll(backupPath)
if err != nil {
return err
}
}
// Check if we can remove the volume directory.
backupsPath := shared.VarPath("backups", "custom", b.poolName, project.StorageVolume(b.projectName, b.volumeName))
empty, _ := shared.PathIsEmpty(backupsPath)
if empty {
err := os.Remove(backupsPath)
if err != nil {
return err
}
}
// Remove the database record.
err := b.state.Cluster.DeleteStoragePoolVolumeBackup(b.name)
if err != nil {
return err
}
return nil
}
// Render returns a VolumeBackup struct of the backup.
func (b *VolumeBackup) Render() *api.StoragePoolVolumeBackup {
return &api.StoragePoolVolumeBackup{
Name: strings.SplitN(b.name, "/", 2)[1],
CreatedAt: b.creationDate,
ExpiresAt: b.expiryDate,
VolumeOnly: b.volumeOnly,
OptimizedStorage: b.optimizedStorage,
}
}