summaryrefslogtreecommitdiffstats
path: root/anate/vm.py
blob: 71fcabc585ff55afa754cb22238672264e669e1a (plain)
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
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger('anate.vm')

import os
import devices


class VirtualDisk(devices.DMDevice):

    _LOOP_DEV_MAJOR = 7
    _AVAILABLE_MINORS = [0, 1, 2, 3, 4, 5, 6, 7]

    def getAvailableMinor(self):
        return self._AVAILABLE_MINORS[0]

    def useMinor(self, minor):
        self._AVAILABLE_MINORS.remove(minor)

    def freeMinor(self, minor):
        self._AVAILABLE_MINORS.append(minor)
        self._AVAILABLE_MINORS.sort()

    def __init__(self, name, size, usable_size=None):
        if not usable_size:
            usable_size = size
        elif usable_size > size:
            logger.error('usable_size cannot be greater than size')
            return

        self._remove_node = False
        self._loop_node = self._createLoopNode(name)
        if not self._loop_node:
            raise OSError, 'node error'

        self._loop_dev = self._createLoopDev(name, self._loop_node, usable_size)

        devices.DMDevice.__init__(self, name, self._loop_dev, size)

    def _createLoopNode(self, name):
        loop_node = devices.BlockDeviceNode(os.path.join('/dev/', name), major=None, minor=None)
        if loop_node.available:
            logger.info('using existing node %s', loop_node.path)
            loop_node.major, loop_node.minor = devices.getDeviceNumbers(loop_node.path)
            if loop_node.major != self._LOOP_DEV_MAJOR:
                logger.error('not a loop device node')
                return
            if loop_node.minor not in self._AVAILABLE_MINORS:
                logger.error('node not available')
                return
        else:
            logger.info('creating new node %s', loop_node.path)
            loop_node.major = self._LOOP_DEV_MAJOR
            loop_node.minor = self.getAvailableMinor()
            loop_node.create()
            self._remove_node = True

        self.useMinor(loop_node.minor)
        logger.info('using minor %d', loop_node.minor)
        logger.info('available minors: %s', self._AVAILABLE_MINORS)

        if not loop_node.available:
            raise OSError, 'node error'

        return loop_node

    def _createLoopDev(self, name, loop_node, size):
        loop_dev = devices.LoopDevice(name, loop_node, os.path.join('/tmp/', name), size)
        loop_dev.initialize()
        if not loop_dev.active:
            raise OSError, 'loop device error'

        return loop_dev

    def create(self):
        self.initialize()
        if not self.active:
            raise OSError, 'dm device error'

    def remove(self):
        self.destroy()
        if self.active:
            raise OSError, 'dm device error'

        self._loop_dev.destroy()
        if self._loop_dev.active:
            raise OSError, 'loop device error'

        if self._remove_node:
            logger.info('removing node %s', self._loop_node.path)
            self._loop_node.unlink()
            if self._loop_node.available:
                raise OSError, 'node error'

        self.freeMinor(self._loop_node.minor)
        logger.info('freeing minor %d', self._loop_node.minor)
        logger.info('available minors: %s', self._AVAILABLE_MINORS)


class VM(object):
    def __init__(self):
        self._disks = {}

    def addDisk(self, name, size, usable_size=None):
        if name in self.disks:
            logger.error('disk %s already exists', name)
            return

        disk = VirtualDisk(name, size=size, usable_size=usable_size)
        disk.create()
        self._disks[name] = disk

    def removeDisk(self, name):
        if name not in self.disks:
            logger.error('disk %s does not exist', name)
            return

        disk = self.getDisk(name)
        if disk.active:
            disk.remove()
        del self._disks[name]

    def halt(self):
        for disk in self.disks.values():
            if disk.active:
                disk.remove()

        self._disks = {}

    @property
    def disks(self):
        return self._disks

    def getDisk(self, name):
        return self.disks.get(name, None)