~ubuntu-branches/ubuntu/utopic/xen/utopic

« back to all changes in this revision

Viewing changes to tools/python/xen/remus/image.py

  • Committer: Bazaar Package Importer
  • Author(s): Bastian Blank
  • Date: 2010-05-06 15:47:38 UTC
  • mto: (1.3.1) (15.1.1 sid) (4.1.1 experimental)
  • mto: This revision was merged to the branch mainline in revision 3.
  • Revision ID: james.westby@ubuntu.com-20100506154738-agoz0rlafrh1fnq7
Tags: upstream-4.0.0
ImportĀ upstreamĀ versionĀ 4.0.0

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
# VM image file manipulation
 
2
 
 
3
import logging, struct
 
4
 
 
5
import vm
 
6
 
 
7
SIGNATURE = 'LinuxGuestRecord'
 
8
LONGLEN = struct.calcsize('L')
 
9
INTLEN = struct.calcsize('i')
 
10
PAGE_SIZE = 4096
 
11
# ~0L
 
12
P2M_EXT_SIG = 4294967295L
 
13
# frames per page
 
14
FPP = 1024
 
15
LTAB_MASK = 0xf << 28
 
16
BATCH_SIZE = 1024
 
17
IDXLEN = INTLEN + BATCH_SIZE * LONGLEN
 
18
 
 
19
logging.basicConfig(level=logging.DEBUG)
 
20
log = logging.getLogger()
 
21
 
 
22
class VMParseException(Exception): pass
 
23
 
 
24
class VMImage(object):
 
25
    def __init__(self, img=None):
 
26
        """img may be a path or a file object.
 
27
        If compact is True, apply checkpoints to base image instead
 
28
        of simply concatenating them.
 
29
        """
 
30
        self.img = img
 
31
 
 
32
        self.dom = None
 
33
        self.fd = None
 
34
        self.header = None
 
35
        self.nr_pfns = 0
 
36
        # p2m extension header (unparsed)
 
37
        self.p2mext = None
 
38
 
 
39
        if self.img:
 
40
            self.open(self.img)
 
41
 
 
42
    def open(self, img):
 
43
        if isinstance(img, str):
 
44
            self.fd = file(img, 'rb')
 
45
        else:
 
46
            self.fd = img
 
47
 
 
48
        self.readheader()
 
49
 
 
50
    def readheader(self):
 
51
        sig = self.fd.read(len(SIGNATURE))
 
52
        if sig != SIGNATURE:
 
53
            raise VMParseException("Bad signature in image")
 
54
 
 
55
        hlen = self.fd.read(INTLEN)
 
56
        hlen, = struct.unpack('!i', hlen)
 
57
 
 
58
        self.header = self.fd.read(hlen)
 
59
        self.dom = parseheader(self.header)
 
60
 
 
61
    def readp2mfl(self):
 
62
        "read the P2M frame list"
 
63
        pfnlen = self.fd.read(LONGLEN)
 
64
        self.nr_pfns, = struct.unpack('L', pfnlen)
 
65
        p2m0 = self.fd.read(LONGLEN)
 
66
 
 
67
        p2mhdr = p2m0
 
68
        p2m0, = struct.unpack('L', p2m0)
 
69
        if p2m0 == P2M_EXT_SIG:
 
70
            elen = self.fd.read(INTLEN)
 
71
            elen, = struct.unpack('I', elen)
 
72
 
 
73
            self.p2mext = self.fd.read(elen)
 
74
 
 
75
            p2m0 = self.fd.read(LONGLEN)
 
76
            p2m0, = struct.unpack('L', p2m0)
 
77
        p2mfl = [p2m0]
 
78
 
 
79
        p2mfle = (self.nr_pfns + FPP - 1)/FPP - 1
 
80
        p2ms = self.fd.read(LONGLEN * p2mfle)
 
81
        p2mfl.extend(struct.unpack('%dL' % p2mfle, p2ms))
 
82
 
 
83
        self.p2mfl = p2mfl
 
84
 
 
85
    def flush(self):
 
86
        self.ofd.write(self.tail)
 
87
 
 
88
class Writer(object):
 
89
    """compress a stream of checkpoints into a single image of the
 
90
    last checkpoint"""
 
91
    def __init__(self, fd, compact=False):
 
92
        self.fd = fd
 
93
        self.compact = compact
 
94
 
 
95
        self.vm = None
 
96
        self.tail = None
 
97
        # offset to first batch of pages
 
98
        self.imgstart = 0
 
99
        # PFN mappings
 
100
        self.pfns = []
 
101
 
 
102
    def __del__(self):
 
103
        self.close()
 
104
 
 
105
    def writeheader(self):
 
106
        hlen = struct.pack('!i', len(self.vm.header))
 
107
        header = ''.join([SIGNATURE, hlen, self.vm.header])
 
108
        self.fd.write(header)
 
109
 
 
110
    def writep2mfl(self):
 
111
        p2m = [struct.pack('L', self.vm.nr_pfns)]
 
112
        if self.vm.p2mext:
 
113
            p2m.extend([struct.pack('L', P2M_EXT_SIG), self.vm.p2mext])
 
114
        p2m.append(struct.pack('%dL' % len(self.vm.p2mfl), *self.vm.p2mfl))
 
115
        self.fd.write(''.join(p2m))
 
116
 
 
117
    def writebatch(self, batch):
 
118
        def offset(pfn):
 
119
            isz = (pfn / BATCH_SIZE + 1) * IDXLEN
 
120
            return self.imgstart + isz + pfn * PAGE_SIZE
 
121
 
 
122
        if not self.compact:
 
123
            return self.fd.write(batch)
 
124
 
 
125
        batch = parsebatch(batch)
 
126
        # sort pages for better disk seek behaviour
 
127
        batch.sort(lambda x, y: cmp(x[0] & ~LTAB_MASK, y[0] & ~LTAB_MASK))
 
128
 
 
129
        for pfndesc, page in batch:
 
130
            pfn = pfndesc & ~LTAB_MASK
 
131
            if pfn > self.vm.nr_pfns:
 
132
                log.error('INVALID PFN: %d' % pfn)
 
133
            if len(self.pfns) <= pfn:
 
134
                self.pfns.extend([0] * (pfn - len(self.pfns) + 1))
 
135
            self.pfns[pfn] = pfndesc
 
136
            self.fd.seek(offset(pfn))
 
137
            self.fd.write(page)
 
138
 
 
139
        #print "max offset: %d, %d" % (len(self.pfns), offset(self.pfns[-1]))
 
140
 
 
141
    def writeindex(self):
 
142
        "Write batch header in front of each page"
 
143
        hdrlen = INTLEN + BATCH_SIZE * LONGLEN
 
144
        batches = (len(self.pfns) + BATCH_SIZE - 1) / BATCH_SIZE
 
145
 
 
146
        for i in xrange(batches):
 
147
            offset = self.imgstart + i * (hdrlen + (PAGE_SIZE * BATCH_SIZE))
 
148
            pfnoff = i * BATCH_SIZE
 
149
            # python auto-clamps overreads
 
150
            pfns = self.pfns[pfnoff:pfnoff + BATCH_SIZE]
 
151
 
 
152
            self.fd.seek(offset)
 
153
            self.fd.write(struct.pack('i', len(pfns)))
 
154
            self.fd.write(struct.pack('%dL' % len(pfns), *pfns))
 
155
 
 
156
    def slurp(self, ifd):
 
157
        """Apply an incremental checkpoint to a loaded image.
 
158
        accepts a path or a file object."""
 
159
        if isinstance(ifd, str):
 
160
            ifd = file(ifd, 'rb')
 
161
 
 
162
        if not self.vm:
 
163
            self.vm = VMImage(ifd)
 
164
            self.writeheader()
 
165
 
 
166
            self.vm.readp2mfl()
 
167
            self.writep2mfl()
 
168
            self.imgstart = self.fd.tell()
 
169
 
 
170
        while True:
 
171
            l, batch = readbatch(ifd)
 
172
            if l <= 0:
 
173
                break
 
174
            self.writebatch(batch)
 
175
        self.tail = batch + ifd.read()
 
176
 
 
177
    def flush(self):
 
178
        if self.tail:
 
179
            self.fd.seek(0, 2)
 
180
            self.fd.write(self.tail)
 
181
            if self.compact:
 
182
                self.writeindex()
 
183
        self.tail = None
 
184
 
 
185
    def close(self):
 
186
        self.flush()
 
187
 
 
188
def parseheader(header):
 
189
    "parses a header sexpression"
 
190
    return vm.parsedominfo(vm.strtosxpr(header))
 
191
 
 
192
def makeheader(dominfo):
 
193
    "create an image header from a VM dominfo sxpr"
 
194
    items = [SIGNATURE]
 
195
    sxpr = vm.sxprtostr(dominfo)
 
196
    items.append(struct.pack('!i', len(sxpr)))
 
197
    items.append(sxpr)
 
198
    return ''.join(items)
 
199
 
 
200
def readbatch(fd):
 
201
    batch = []
 
202
    batchlen = fd.read(INTLEN)
 
203
    batch.append(batchlen)
 
204
    batchlen, = struct.unpack('i', batchlen)
 
205
    log.info("batch length: %d" % batchlen)
 
206
    if batchlen <= 0:
 
207
        return (batchlen, batch[0])
 
208
 
 
209
    batchfns = fd.read(LONGLEN * batchlen)
 
210
    batch.append(batchfns)
 
211
    pages = fd.read(PAGE_SIZE * batchlen)
 
212
    if len(pages) != PAGE_SIZE * batchlen:
 
213
        log.error('SHORT READ: %d' % len(pages))
 
214
    batch.append(pages)
 
215
 
 
216
    return (batchlen, ''.join(batch))
 
217
 
 
218
def parsebatch(batch):
 
219
    "parse a batch string into pages"
 
220
    batchlen, batch = batch[:INTLEN], batch[INTLEN:]
 
221
    batchlen, = struct.unpack('i', batchlen)
 
222
    #print 'batch length: %d' % batchlen
 
223
    pfnlen = batchlen * LONGLEN
 
224
    pfns = struct.unpack('%dL' % batchlen, batch[:pfnlen])
 
225
    pagebuf = batch[pfnlen:]
 
226
    pages = [pagebuf[i*PAGE_SIZE:(i+1)*PAGE_SIZE] for i in xrange(batchlen)]
 
227
    return zip(pfns, pages)