disk.py 4.94 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
import os
import subprocess
import re

re_qemu_img = re.compile(r'(file format: (?P<format>(qcow2|raw))|'
                         r'virtual size: \w+ \((?P<size>[0-9]+) bytes\)|'
                         r'backing file: \S+ \(actual path: (?P<base>\S+)\))$')


class Disk(object):
Guba Sándor committed
11 12 13 14 15 16
    ''' Storage driver DISK object.
        Handle qcow2, raw and iso images.
        TYPES, CREATE_TYPES, SNAPSHOT_TYPES are hand managed restrictions.
    '''
    TYPES = [('qcow2-norm', 'qcow2 normal'), ('qcow2-snap', 'qcow2 snapshot'),
             ('iso', 'iso'), ('raw-ro', 'raw read-only'), ('raw-rw', 'raw')]
17

Guba Sándor committed
18
    CREATE_TYPES = ['qcow2', 'raw']
Guba Sándor committed
19 20

    def __init__(self, dir, name, format, size, base_name):
21 22 23
        # TODO: tests
        self.name = name
        self.dir = os.path.realpath(dir)
Guba Sándor committed
24
        if format not in [k[0] for k in self.TYPES]:
25 26 27 28 29
            raise Exception('Invalid format: %s' % format)
        self.format = format
        self.size = int(size)
        self.base_name = base_name

Dudás Ádám committed
30 31 32 33 34 35 36 37 38 39 40 41 42
    @classmethod
    def deserialize(cls, desc):
        return cls(**desc)

    def get_desc(self):
        return {
            'name': self.name,
            'dir': self.dir,
            'format': self.format,
            'size': self.size,
            'base_name': self.base_name,
        }

43 44 45 46 47 48 49 50 51 52 53 54
    def get_path(self):
        return os.path.realpath(self.dir + '/' + self.name)

    def get_base(self):
        return os.path.realpath(self.dir + '/' + self.base_name)

    def __unicode__(self):
        return u'%s %s %s %s' % (self.get_path(), self.format,
                                 self.size, self.get_base())

    @classmethod
    def get(cls, dir, name):
55 56
        ''' Create disk from path
        '''
57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76
        path = os.path.realpath(dir + '/' + name)
        output = subprocess.check_output(['qemu-img', 'info', path])

        type = 'normal'
        base = None
        for line in output.split('\n'):
            m = re_qemu_img.search(line)
            if m:
                res = m.groupdict()
                if res.get('format', None) is not None:
                    format = res['format']
                if res.get('size', None) is not None:
                    size = res['size']
                if res.get('base', None) is not None:
                    base = os.path.basename(res['base'])
                    type = 'snapshot'

        return Disk(dir, name, format, size, base, type)

    def create(self):
77 78 79 80
        ''' Creating new image format specified at self.format.
            self.format van be "qcow2-normal"
        '''
        # Check if type is avaliable to create
Guba Sándor committed
81
        if self.format not in self.CREATE_TYPES:
82 83 84 85 86 87 88
            raise Exception('Invalid format: %s' % self.format)
        # Check for file if already exist
        if os.path.isfile(self.get_path()):
            raise Exception('File already exists: %s' % self.get_path())
        # Build list of Strings as command parameters
        cmdline = ['qemu-img',
                   'create',
Guba Sándor committed
89
                   '-f', self.format,
90 91 92 93 94 95 96 97
                   str(self.size)]
        # Call subprocess
        subprocess.check_output(cmdline)

    def snapshot(self):
        ''' Creating qcow2 snapshot with base image.
        '''
        # Check if snapshot type match
Guba Sándor committed
98
        if self.format != 'qcow2':
99 100
            raise Exception('Invalid format: %s' % self.format)
        # Check if file already exists
101 102
        if os.path.isfile(self.get_path()):
            raise Exception('File already exists: %s' % self.get_path())
103 104 105 106
        # Check if base file exist
        if not os.path.isfile(self.get_base()):
            raise Exception('Image Base does not exists: %s' % self.get_base())
        # Build list of Strings as command parameters
107 108
        cmdline = ['qemu-img',
                   'create',
Guba Sándor committed
109
                   '-f', self.format,
110 111 112
                   '-b', self.get_base(),
                   str(self.size)]
        # Call subprocess
113 114
        subprocess.check_output(cmdline)

Guba Sándor committed
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135
    def merge(self, new_disk):
        ''' Merging a new_disk from the actual disk and its base.
        '''
        # Check if snapshot type match
        if self.format != 'qcow2':
            raise Exception('Invalid format: %s' % self.format)
        # Check if file already exists
        if os.path.isfile(new_disk.get_path()):
            raise Exception('File already exists: %s' % self.get_path())
        # Check if base file exist
        if not os.path.isfile(self.get_path()):
            raise Exception('Original image does not exists: %s'
                            % self.get_base())
        cmdline = ['qemu-img',
                   'convert',
                   self.get_path(),
                   '-O', new_disk.format,
                   new_disk.get_path()]
        # Call subprocess
        subprocess.check_output(cmdline)

136
    def delete(self):
137 138
        ''' Delete file
        '''
139 140 141 142 143 144
        if os.path.isfile(self.get_path()):
            os.unlink(self.get_path())

    @classmethod
    def list(cls, dir):
        return [cls.get(dir, file) for file in os.listdir(dir)]