~ubuntu-virt/vmbuilder/trunk

« back to all changes in this revision

Viewing changes to VMBuilder/disk.py

  • Committer: Soren Hansen
  • Date: 2008-06-27 12:47:26 UTC
  • Revision ID: soren.hansen@canonical.com-20080627124726-kj690sepircudc3h
Import python rewrite.. It's not quite at a useful point yet (only cli+kvm+hardy is in a usable state), but it's getting there..

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
from VMBuilder.util import run_cmd 
 
2
import VMBuilder
 
3
import logging
 
4
import string
 
5
 
 
6
class Disk(object):
 
7
    index = 1
 
8
 
 
9
    def __init__(self, size='5G', preallocated=False, filename=None, dir=None):
 
10
        self.size = self.parse_size(size)
 
11
        self.preallocated = preallocated
 
12
        if filename:
 
13
            self.filename = filename
 
14
        else:
 
15
            self.filename = 'disk%d.img' % Disk.index
 
16
        # This will only work for up to 26 disks. If you *actually* 
 
17
        # need more than that, just shout, but right now, I can't 
 
18
        # be bothered to fix it. - Soren
 
19
        self.devname = '/dev/sd%s' % string.ascii_lowercase[Disk.index]
 
20
        if dir:
 
21
            self.filename = '%s/%s' % (dir, self.filename)
 
22
        Disk.index += 1    
 
23
        self.partitions = []
 
24
 
 
25
    def parse_size(self, size_str):
 
26
        """Takes a size like qemu-img would accept it and returns the size in MB"""
 
27
        try:
 
28
            return int(size_str)
 
29
        except ValueError, e:
 
30
            pass
 
31
 
 
32
        try:
 
33
            num = int(size_str[:-1])
 
34
        except ValueError, e:
 
35
            raise ValueError("Invalid size: %s" % size_str)
 
36
 
 
37
        if size_str[-1:] == 'g' or size_str[-1:] == 'G':
 
38
            return num * 1024
 
39
        if size_str[-1:] == 'm' or size_str[-1:] == 'M':
 
40
            return num
 
41
        if size_str[-1:] == 'k' or size_str[-1:] == 'K':
 
42
            return num / 1024
 
43
 
 
44
    def create(self):
 
45
        if not self.preallocated:
 
46
            logging.info('Creating disk image: %s' % self.filename)
 
47
            run_cmd('qemu-img', 'create', '-f', 'raw', self.filename, '%dM' % self.size)
 
48
 
 
49
        logging.info('Adding partition table to disk image: %s' % self.filename)
 
50
        run_cmd('parted', '--script', self.filename, 'mklabel', 'msdos')
 
51
 
 
52
        for part in self.partitions:
 
53
            part.create(self)
 
54
 
 
55
        logging.info('Creating loop devices corresponding to the created partitions')
 
56
        kpartx_output = run_cmd('kpartx', '-av', self.filename)
 
57
        VMBuilder.add_clean_cb(lambda : self.unmap(ignore_fail=True))
 
58
        parts = kpartx_output.split('\n')[2:-1]
 
59
        mapdevs = []
 
60
        for line in parts:
 
61
            mapdevs.append(line.split(' ')[2])
 
62
        for (part, mapdev) in zip(self.partitions, mapdevs):
 
63
            part.mapdev = '/dev/mapper/%s' % mapdev
 
64
 
 
65
        logging.info("Creating file systems")
 
66
        for part in self.partitions:
 
67
            part.mkfs()
 
68
 
 
69
    def unmap(self, ignore_fail=False):
 
70
        run_cmd('kpartx', '-d', self.filename, ignore_fail=ignore_fail)
 
71
 
 
72
    def add_part(self, begin, length, type, mntpnt):
 
73
        end = begin+length-1
 
74
        for part in self.partitions:
 
75
            if (begin >= part.begin and begin <= part.end) or \
 
76
                (end >= part.begin and end <= part.end):
 
77
                raise Exception('Partitions are overlapping')
 
78
            if begin > end:
 
79
                raise Exception('Partition\'s last block is before its first')
 
80
            if begin < 0 or end > self.size:
 
81
                raise Exception('Partition is out of bounds. start=%d, end=%d, disksize=%d' % (begin,end,self.size))
 
82
        part = self.Partition(begin=begin, end=end, type=self.Partition.str_to_type(type), mntpnt=mntpnt)
 
83
        self.partitions.append(part)
 
84
        self.partitions.sort(cmp=lambda x,y: x.begin - y.begin)
 
85
 
 
86
    def convert(self, destination, format):
 
87
        logging.info('Converting %s to %s, format %s' % (self.filename, format, destination))
 
88
        run_cmd('qemu-img', 'convert', '-O', format, self.filename, destination)
 
89
 
 
90
    class Partition(object):
 
91
        TYPE_EXT2 = 0
 
92
        TYPE_EXT3 = 1
 
93
        TYPE_XFS = 2
 
94
        TYPE_SWAP = 3
 
95
 
 
96
        def __init__(self, begin, end, type, mntpnt):
 
97
            self.begin = begin
 
98
            self.end = end
 
99
            self.type = type
 
100
            self.mntpnt = mntpnt
 
101
            self.mapdev = None
 
102
 
 
103
        def parted_fstype(self):
 
104
            return { self.TYPE_EXT2: 'ext2', self.TYPE_EXT3: 'ext2', self.TYPE_XFS: 'ext2', self.TYPE_SWAP: 'linux-swap' }[self.type]
 
105
 
 
106
        def mkfs_fstype(self):
 
107
            return { self.TYPE_EXT2: 'mkfs.ext2', self.TYPE_EXT3: 'mkfs.ext3', self.TYPE_XFS: 'mkfs.xfs', self.TYPE_SWAP: 'mkswap' }[self.type]
 
108
 
 
109
        def fstab_fstype(self):
 
110
            return { self.TYPE_EXT2: 'ext2', self.TYPE_EXT3: 'ext3', self.TYPE_XFS: 'xfs', self.TYPE_SWAP: 'swap' }[self.type]
 
111
 
 
112
        def fstab_options(self):
 
113
            return 'defaults'
 
114
 
 
115
        def create(self, disk):
 
116
            logging.info('Adding type %d partition to disk image: %s' % (self.type, disk.filename))
 
117
            run_cmd('parted', '--script', '--', disk.filename, 'mkpart', 'primary', self.parted_fstype(), self.begin, self.end)
 
118
 
 
119
        def mkfs(self):
 
120
            if not self.mapdev:
 
121
                raise Exception('We can\'t mkfs before we have a mapper device')
 
122
            run_cmd(self.mkfs_fstype(), self.mapdev)
 
123
            self.uuid = run_cmd('vol_id', '--uuid', self.mapdev).rstrip()
 
124
 
 
125
        @classmethod
 
126
        def str_to_type(cls, type):
 
127
            try:
 
128
                return { 'ext2': cls.TYPE_EXT2,
 
129
                         'ext3': cls.TYPE_EXT3,
 
130
                         'xfs': cls.TYPE_XFS,
 
131
                         'swap': cls.TYPE_SWAP,
 
132
                         'linux-swap': cls.TYPE_SWAP }[type]
 
133
            except KeyError, e:
 
134
                raise Exception('Unknown partition type')
 
135
 
 
136