vm.py 11.3 KB
Newer Older
tarokkk committed
1 2
import lxml.etree as ET

3
from vmcelery import native_ovs
Bach Dániel committed
4 5


tarokkk committed
6 7 8 9 10
# VM Instance class


class VMInstance:
    name = None
tarokkk committed
11 12 13
    arch = None
    vm_type = None
    os_boot = None
tarokkk committed
14 15 16 17 18
    vcpu = None
    cpu_share = None
    memory_max = None
    network_list = list()
    disk_list = list()
19
    graphics = dict
Guba Sándor committed
20
    raw_data = None
tarokkk committed
21 22 23 24

    def __init__(self,
                 name,
                 vcpu,
Őry Máté committed
25
                 memory_max,
26
                 memory=None,
Guba Sándor committed
27
                 emulator='/usr/bin/kvm',
28
                 cpu_share=100,
tarokkk committed
29
                 arch="x86_64",
30
                 boot_menu=False,
31
                 vm_type="test",
tarokkk committed
32 33
                 network_list=None,
                 disk_list=None,
34
                 graphics=None,
Guba Sándor committed
35
                 acpi=True,
36
                 raw_data="",
Guba Sándor committed
37
                 boot_token="",
user committed
38 39
                 seclabel_type="dynamic",
                 seclabel_mode="apparmor"):
tarokkk committed
40
        '''Default Virtual Machine constructor
41 42 43
        name    - unique name for the instance
        vcpu    - nubmer of processors
        memory_max  - maximum virtual memory (actual memory maybe add late)
44
        memory
45 46 47 48 49 50 51 52
        cpu_share   - KVM process priority (0-100)
        arch        - libvirt arch parameter default x86_64
        os_boot     - boot device default hd
        vm_type     - hypervisor type default kvm
        network_list    - VMNetwork list
        disk_list   - VMDIsk list
        graphics    - Dict that keys are: type, listen, port, passwd
        acpi        - True/False to enable acpi
user committed
53 54
        seclabel_type - libvirt security label type
        seclabel_mode - libvirt security mode (selinux, apparmor)
tarokkk committed
55 56
        '''
        self.name = name
57
        self.emulator = emulator
tarokkk committed
58 59 60
        self.vcpu = vcpu
        self.cpu_share = cpu_share
        self.memory_max = memory_max
61 62 63 64
        if memory is None:
            self.memory = memory_max
        else:
            self.memory = memory
tarokkk committed
65
        self.arch = arch
66
        self.boot_menu = boot_menu
tarokkk committed
67
        self.vm_type = vm_type
tarokkk committed
68 69
        self.network_list = network_list
        self.disk_list = disk_list
70 71
        self.graphics = graphics
        self.acpi = acpi
Guba Sándor committed
72
        self.raw_data = raw_data
user committed
73 74
        self.seclabel_type = seclabel_type
        self.seclabel_mode = seclabel_mode
Guba Sándor committed
75
        self.boot_token = boot_token
tarokkk committed
76

77 78 79
    @classmethod
    def deserialize(cls, desc):
        desc['disk_list'] = [VMDisk.deserialize(d) for d in desc['disk_list']]
80 81
        desc['network_list'] = [VMNetwork.deserialize(
            n) for n in desc['network_list']]
82 83
        return cls(**desc)

tarokkk committed
84 85 86 87 88 89 90 91 92 93
    def build_xml(self):
        '''Return the root Element Tree object
        '''
        ET.register_namespace(
            'qemu', 'http://libvirt.org/schemas/domain/qemu/1.0')
        xml_top = ET.Element(
            'domain',
            attrib={
                'type': self.vm_type
            })
94 95 96
        # Building raw data into xml
        if self.raw_data:
            xml_top.append(ET.fromstring(self.raw_data))
tarokkk committed
97 98
        # Basic virtual machine paramaters
        ET.SubElement(xml_top, 'name').text = self.name
99
        ET.SubElement(xml_top, 'vcpu').text = str(self.vcpu)
100 101 102 103 104 105
        cpu = ET.SubElement(xml_top, 'cpu')
        ET.SubElement(cpu, 'topology',
                      attrib={
                          'sockets': str(1),
                          'cores': str(self.vcpu),
                          'threads': str(1)})
106 107
        ET.SubElement(xml_top, 'memory').text = str(self.memory_max)
        ET.SubElement(xml_top, 'currentMemory').text = str(self.memory)
tarokkk committed
108 109
        # Cpu tune
        cputune = ET.SubElement(xml_top, 'cputune')
110
        ET.SubElement(cputune, 'shares').text = str(self.cpu_share)
tarokkk committed
111 112 113
        # Os specific options
        os = ET.SubElement(xml_top, 'os')
        ET.SubElement(os, 'type', attrib={'arch': self.arch}).text = "hvm"
114 115
        ET.SubElement(os, 'bootmenu', attrib={
                      'enable': "yes" if self.boot_menu else "no"})
tarokkk committed
116 117
        # Devices
        devices = ET.SubElement(xml_top, 'devices')
118
        ET.SubElement(devices, 'emulator').text = self.emulator
tarokkk committed
119 120 121 122
        for disk in self.disk_list:
            devices.append(disk.build_xml())
        for network in self.network_list:
            devices.append(network.build_xml())
Guba Sándor committed
123 124 125 126 127 128 129 130 131 132
        # Serial console
        serial = ET.SubElement(devices,
                               'console',
                               attrib={'type': 'unix'})
        ET.SubElement(serial,
                      'target',
                      attrib={'port': '0'})
        ET.SubElement(serial,
                      'source',
                      attrib={'mode': 'bind',
133 134
                              'path': '/var/lib/libvirt/serial/%s'
                              % self.name})
135 136 137 138 139 140 141 142 143 144 145 146
        # Virtio console
        virtio = ET.SubElement(devices,
                               'channel',
                               attrib={'type': 'unix'})
        ET.SubElement(virtio,
                      'target',
                      attrib={'type': 'virtio', 'name': 'agent'})
        ET.SubElement(virtio,
                      'source',
                      attrib={'mode': 'bind',
                              'path': '/var/lib/libvirt/serial/vio-%s'
                              % self.name})
147 148 149 150 151 152 153
        # Console/graphics section
        if self.graphics is not None:
            ET.SubElement(devices,
                          'graphics',
                          attrib={
                              'type': self.graphics['type'],
                              'listen': self.graphics['listen'],
154
                              'port': str(self.graphics['port']),
Őry Máté committed
155 156
                              # 'passwd': self.graphics['passwd'],
                              # TODO: Add this as option
157
                          })
Guba Sándor committed
158 159 160 161
            ET.SubElement(devices,
                          'input',
                          attrib={
                              'type': 'tablet',
Őry Máté committed
162
                              'bus': 'usb', })
Guba Sándor committed
163
        # Features (TODO: features as list)
164 165 166
        features = ET.SubElement(xml_top, 'features')
        if self.acpi:
            ET.SubElement(features, 'acpi')
user committed
167 168 169 170 171
        # Security label
        ET.SubElement(xml_top, 'seclabel', attrib={
            'type': self.seclabel_type,
            'mode': self.seclabel_mode
        })
tarokkk committed
172 173 174 175 176 177 178 179
        return xml_top

    def dump_xml(self):
        return ET.tostring(self.build_xml(),
                           encoding='utf8',
                           method='xml',
                           pretty_print=True)

tarokkk committed
180 181

class VMDisk:
tarokkk committed
182

tarokkk committed
183 184 185 186 187 188 189 190 191
    '''Virtual MAchine disk representing class
    '''
    name = None
    source = None
    disk_type = None
    disk_device = None
    driver_name = None
    driver_type = None
    driver_cache = None
tarokkk committed
192
    target_device = None
tarokkk committed
193 194 195 196 197 198 199

    def __init__(self,
                 source,
                 disk_type="file",
                 disk_device="disk",
                 driver_name="qemu",
                 driver_type="qcow2",
user committed
200
                 driver_cache="none",
Guba Sándor committed
201 202
                 target_device="vda",
                 target_bus="virtio"):
tarokkk committed
203 204 205 206 207 208
        self.source = source
        self.disk_type = disk_type
        self.disk_device = disk_device
        self.driver_name = driver_name
        self.driver_type = driver_type
        self.driver_cache = driver_cache
tarokkk committed
209
        self.target_device = target_device
Guba Sándor committed
210
        self.target_bus = target_bus
tarokkk committed
211

212 213 214 215
    @classmethod
    def deserialize(cls, desc):
        return cls(**desc)

216
    def build_xml(self):
tarokkk committed
217 218 219 220 221
        xml_top = ET.Element('disk',
                             attrib={'type': self.disk_type,
                                     'device': self.disk_device})
        ET.SubElement(xml_top, 'source',
                      attrib={self.disk_type: self.source})
tarokkk committed
222
        ET.SubElement(xml_top, 'target',
Guba Sándor committed
223 224
                      attrib={'dev': self.target_device,
                              'bus': self.target_bus})
tarokkk committed
225 226 227 228 229 230
        ET.SubElement(xml_top, 'driver',
                      attrib={
                          'name': self.driver_name,
                          'type': self.driver_type,
                          'cache': self.driver_cache})
        return xml_top
tarokkk committed
231

232 233 234 235 236 237
    def dump_xml(self):
        return ET.tostring(self.build_xml(),
                           encoding='utf8',
                           method='xml',
                           pretty_print=True)

tarokkk committed
238 239

class VMNetwork:
240

tarokkk committed
241 242
    ''' Virtual Machine network representing class
    name            -- network device name
tarokkk committed
243
    bridge          -- bridg for the port
tarokkk committed
244
    mac             -- the MAC address of the quest interface
245 246
    ipv4            -- the IPv4 address of virtual machine (Flow control)
    ipv6            -- the IPv6 address of virtual machine (Flow controlo)
tarokkk committed
247
    vlan            -- Port VLAN configuration
tarokkk committed
248 249 250 251
    network_type    -- need to be "ethernet" by default
    model           -- available models in libvirt
    QoS             -- CIRCLE QoS class?
    comment         -- Any comment
Guba Sándor committed
252
    managed         -- Apply managed flow rules for spoofing prevent
tarokkk committed
253 254 255 256
    script          -- Executable network script /bin/true by default
    '''
    # Class attributes
    name = None
tarokkk committed
257
    bridge = None
tarokkk committed
258 259 260 261 262 263
    network_type = None
    mac = None
    model = None
    QoS = None
    script_exec = '/bin/true'
    comment = None
tarokkk committed
264
    vlan = 0
265 266
    ipv4 = None
    ipv6 = None
Guba Sándor committed
267
    managed = False
tarokkk committed
268 269 270 271

    def __init__(self,
                 name,
                 mac,
272
                 bridge="cloud",
273 274
                 ipv4=None,
                 ipv6=None,
Bach Dániel committed
275
                 network_type=None,
276
                 virtual_port=None,
tarokkk committed
277
                 model='virtio',
tarokkk committed
278
                 QoS=None,
Guba Sándor committed
279 280
                 vlan=0,
                 managed=False):
tarokkk committed
281
        self.name = name
tarokkk committed
282
        self.bridge = bridge
tarokkk committed
283
        self.mac = mac
284 285
        self.ipv4 = ipv4
        self.ipv6 = ipv6
tarokkk committed
286
        self.model = model
Bach Dániel committed
287
        if not network_type:
288
            if native_ovs:
Bach Dániel committed
289 290
                self.network_type = 'bridge'
                self.virtual_port = 'openvswitch'
Bach Dániel committed
291 292
            else:
                self.network_type = 'ethernet'
Bach Dániel committed
293
                self.virtual_port = virtual_port
Bach Dániel committed
294 295 296
        else:
            self.network_type = network_type
            self.virtual_port = virtual_port
tarokkk committed
297
        self.QoS = QoS
tarokkk committed
298
        self.vlan = vlan
Guba Sándor committed
299
        self.managed = managed
tarokkk committed
300

301 302 303 304
    @classmethod
    def deserialize(cls, desc):
        return cls(**desc)

tarokkk committed
305
    # XML dump
306
    def build_xml(self):
tarokkk committed
307
        xml_top = ET.Element('interface', attrib={'type': self.network_type})
308 309
        if self.vlan > 0 and self.network_type == "bridge":
            xml_vlan = ET.SubElement(xml_top, 'vlan')
Bach Dániel committed
310
            ET.SubElement(xml_vlan, 'tag', attrib={'id': str(self.vlan)})
311 312 313 314 315 316 317
        if self.network_type == "bridge":
            ET.SubElement(xml_top, 'source', attrib={'bridge': self.bridge})
        if self.network_type == "ethernet":
            ET.SubElement(xml_top, 'script', attrib={'path': self.script_exec})
        if self.virtual_port is not None:
            ET.SubElement(xml_top, 'virtualport',
                          attrib={'type': self.virtual_port})
tarokkk committed
318 319 320
        ET.SubElement(xml_top, 'target', attrib={'dev': self.name})
        ET.SubElement(xml_top, 'mac', attrib={'address': self.mac})
        ET.SubElement(xml_top, 'model', attrib={'type': self.model})
321 322
        # ET.SubElement(xml_top, 'rom', attrib={'bar': 'off'}) Bugged (hot-plug
        # failure)
tarokkk committed
323
        return xml_top
324 325 326

    def dump_xml(self):
        return ET.tostring(self.build_xml(), encoding='utf8',
327
                           method='xml',
Bach Dániel committed
328
                           pretty_print=True)