forked from quadrifoglio/go-qemu
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathimage.go
246 lines (196 loc) · 5.48 KB
/
image.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
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
package qemu
import (
"encoding/json"
"fmt"
"os"
"os/exec"
"strconv"
"time"
)
const (
ImageFormatRAW = "raw"
ImageFormatCLOOP = "cloop"
ImageFormatCOW = "cow"
ImageFormatQCOW = "qcow"
ImageFormatQCOW2 = "qcow2"
ImageFormatVDMK = "vdmk"
ImageFormatVDI = "vdi"
ImageFormatVHDX = "vhdx"
ImageFormatVPC = "vpc"
)
// Image represents a QEMU disk image
type Image struct {
Path string // Image location (file)
Format string // Image format
Size uint64 // Image size in bytes
ClusterSize uint64
ActualSize uint64
BackingFormat string
BackingFull string
BackingFile string
snapshots []Snapshot
}
// Snapshot represents a QEMU image snapshot
// Snapshots are snapshots of the complete virtual machine including CPU state
// RAM, device state and the content of all the writable disks
type Snapshot struct {
ID int
Name string
Date time.Time
VMClock time.Time
}
// NewImage constructs a new Image data structure based
// on the specified parameters
func NewImage(path, format string, size uint64) Image {
var img Image
img.Path = path
img.Format = format
img.Size = size
return img
}
// OpenImage retreives the information of the specified image
// file into an Image data structure
func OpenImage(path string) (Image, error) {
var img Image
if _, err := os.Stat(path); os.IsNotExist(err) {
return img, err
}
img.Path = path
err := img.retreiveInfos()
if err != nil {
return img, err
}
return img, nil
}
func (i *Image) retreiveInfos() error {
type snapshotInfo struct {
ID string `json:"id"`
Name string `json:"name"`
DateSec int64 `json:"date-sec"`
DateNsec int64 `json:"date-nsec"`
ClockSec int64 `json:"vm-clock-sec"`
ClockNsec int64 `json:"vm-clock-nsec"`
}
type imgInfo struct {
Snapshots []snapshotInfo `json:"snapshots"`
Format string `json:"format"`
Size uint64 `json:"virtual-size"`
ClusterSize uint64 `json:"cluster-size"`
ActualSize uint64 `json:"actual-size"`
BackingFormat string `json:"backing-filename-format"`
Backing string `json:"backing-filename"`
BackingFull string `json:"full-backing-filename"`
}
var info imgInfo
cmd := exec.Command("qemu-img", "info", "--output=json", i.Path)
out, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("'qemu-img info' output: %s", oneLine(out))
}
err = json.Unmarshal(out, &info)
if err != nil {
return fmt.Errorf("'qemu-img info' invalid json output")
}
i.Format = info.Format
i.Size = info.Size
i.ClusterSize = info.ClusterSize
i.ActualSize = info.ActualSize
i.BackingFormat = info.BackingFormat
i.BackingFile = info.Backing
i.BackingFull = info.BackingFull
i.snapshots = make([]Snapshot, 0)
for _, snap := range info.Snapshots {
var s Snapshot
id, err := strconv.Atoi(snap.ID)
if err != nil {
continue
}
s.ID = id
s.Name = snap.Name
s.Date = time.Unix(snap.DateSec, snap.DateNsec)
s.VMClock = time.Unix(snap.ClockSec, snap.ClockNsec)
i.snapshots = append(i.snapshots, s)
}
return nil
}
// Snapshots returns the snapshots contained
// within the image
func (i Image) Snapshots() ([]Snapshot, error) {
err := i.retreiveInfos()
if err != nil {
return nil, err
}
if len(i.snapshots) == 0 {
return make([]Snapshot, 0), nil
}
return i.snapshots, nil
}
// CreateSnapshot creates a snapshot of the image
// with the specified name
func (i *Image) CreateSnapshot(name string) error {
cmd := exec.Command("qemu-img", "snapshot", "-c", name, i.Path)
out, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("'qemu-img snapshot' output: %s", oneLine(out))
}
return nil
}
// RestoreSnapshot restores the the image to the
// specified snapshot name
func (i Image) RestoreSnapshot(name string) error {
cmd := exec.Command("qemu-img", "snapshot", "-a", name, i.Path)
out, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("'qemu-img snapshot' output: %s", oneLine(out))
}
return nil
}
// DeleteSnapshot deletes the the corresponding
// snapshot from the image
func (i Image) DeleteSnapshot(name string) error {
cmd := exec.Command("qemu-img", "snapshot", "-d", name, i.Path)
out, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("'qemu-img snapshot' output: %s", oneLine(out))
}
return nil
}
// SetBackingFile sets a backing file for the image
// If it is specified, the image will only record the
// differences from the backing file
func (i *Image) SetBackingFile(backingFile string) error {
if _, err := os.Stat(backingFile); os.IsNotExist(err) {
return err
}
i.BackingFile = backingFile
return nil
}
// Create actually creates the image based on the Image structure
// using the 'qemu-img create' command
func (i Image) Create() error {
args := []string{"create", "-f", i.Format}
if len(i.BackingFile) > 0 {
args = append(args, "-F", i.Format)
args = append(args, "-b")
args = append(args, i.BackingFile)
}
args = append(args, i.Path)
args = append(args, strconv.FormatUint(i.Size, 10))
cmd := exec.Command("qemu-img", args...)
out, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("'qemu-img create' output: %s", oneLine(out))
}
return nil
}
// Rebase changes the backing file of the image
// to the specified file path
func (i *Image) Rebase(backingFile string) error {
i.BackingFile = backingFile
cmd := exec.Command("qemu-img", "rebase", "-b", backingFile, i.Path)
out, err := cmd.CombinedOutput()
if err != nil {
return fmt.Errorf("'qemu-img rebase' output: %s", oneLine(out))
}
return nil
}