fuel-devops/devops/driver/libvirt/libvirt_xml_builder.py

292 lines
11 KiB
Python

# Copyright 2013 - 2016 Mirantis, Inc.
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
# not use this file except in compliance with the License. You may obtain
# a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
# License for the specific language governing permissions and limitations
# under the License.
import json
import os
import uuid
from ipaddr import IPAddress
from ipaddr import IPNetwork
from xmlbuilder import XMLBuilder
class LibvirtXMLBuilder(object):
def __init__(self, driver):
super(LibvirtXMLBuilder, self).__init__()
self.driver = driver
NAME_SIZE = 80
def _get_name(self, *args):
name = '_'.join(filter(None, list(args)))
if len(name) > self.NAME_SIZE:
hash_str = str(hash(name))
name = hash_str + name[len(name) - self.NAME_SIZE + len(hash_str):]
return name
def build_network_xml(self, network):
"""Generate network XML
:type network: Network
:rtype : String
"""
network_xml = XMLBuilder('network')
network_xml.name(self._get_name(
network.environment and network.environment.name or '',
network.name))
stp_val = 'off'
if self.driver.stp:
stp_val = 'on'
network_xml.bridge(
name="fuelbr{0}".format(network.id),
stp=stp_val, delay="0")
if network.forward is not None:
network_xml.forward(mode=network.forward)
if network.ip_network is not None:
ip_network = IPNetwork(network.ip_network)
with network_xml.ip(
address=str(ip_network[1]),
prefix=str(ip_network.prefixlen)):
if network.has_pxe_server:
network_xml.tftp(root=network.tftp_root_dir)
if network.has_dhcp_server:
with network_xml.dhcp:
network_xml.range(start=str(network.ip_pool_start),
end=str(network.ip_pool_end))
for interface in network.interfaces:
for address in interface.addresses:
if IPAddress(address.ip_address) in ip_network:
network_xml.host(
mac=str(interface.mac_address),
ip=str(address.ip_address),
name=interface.node.name
)
if network.has_pxe_server:
network_xml.bootp(file="pxelinux.0")
return str(network_xml)
def build_volume_xml(self, volume):
"""Generate volume XML
:type volume: Volume
:rtype : String
"""
volume_xml = XMLBuilder('volume')
volume_xml.name(
self._get_name(
volume.environment and volume.environment.name or '',
volume.name))
volume_xml.capacity(str(volume.capacity))
with volume_xml.target:
volume_xml.format(type=volume.format)
if volume.backing_store is not None:
with volume_xml.backingStore:
volume_xml.path(self.driver.volume_path(volume.backing_store))
volume_xml.format(type=volume.backing_store.format)
return str(volume_xml)
def build_snapshot_xml(self, name=None, description=None, node=None,
disk_only=False, external=False, external_dir=None):
"""Generate snapshot XML
:rtype : String
:type name: String
:type description: String
"""
xml_builder = XMLBuilder('domainsnapshot')
if name is not None:
xml_builder.name(name)
if description is not None:
xml_builder.description(description)
if external:
domain = self.driver.conn.lookupByUUIDString(node.uuid)
# Add memory file for active machines
if domain.isActive() and not disk_only:
memory_file = '{0}/snapshot-memory-{1}_{2}.{3}'.format(
external_dir,
node.environment.name,
node.name,
name)
file_count = 0
tmp_memory_file = memory_file
while os.path.exists(tmp_memory_file):
tmp_memory_file = memory_file + '-' + str(file_count)
file_count += 1
xml_builder.memory(
file=tmp_memory_file,
snapshot='external')
else:
xml_builder.memory(snapshot='no')
for disk in node.disk_devices:
if disk.device == 'disk':
with xml_builder.disks:
xml_builder.disk(name=disk.target_dev,
file=disk.volume.get_path(),
snapshot='external')
return str(xml_builder)
def _build_disk_device(self, device_xml, disk_device):
"""Build xml for disk
:param device_xml: XMLBuilder
:param disk_device: DiskDevice
"""
with device_xml.disk(type=disk_device.type, device=disk_device.device):
# https://bugs.launchpad.net/ubuntu/+source/qemu-kvm/+bug/741887
device_xml.driver(type=disk_device.volume.format, cache="unsafe")
device_xml.source(file=self.driver.volume_path(disk_device.volume))
if disk_device.bus == 'usb':
device_xml.target(
dev=disk_device.target_dev,
bus=disk_device.bus,
removable='on')
device_xml.readonly()
else:
device_xml.target(
dev=disk_device.target_dev,
bus=disk_device.bus)
device_xml.serial(''.join(uuid.uuid4().hex))
def _build_interface_device(self, device_xml, interface):
"""Build xml for interface
:param device_xml: XMLBuilder
:param interface: Network
"""
if interface.type != 'network':
raise NotImplementedError(
message='Interface types different from network are not '
'implemented yet')
with device_xml.interface(type=interface.type):
device_xml.mac(address=interface.mac_address)
device_xml.source(
network=self.driver.network_name(interface.network))
device_xml.target(dev="fuelnet{0}".format(interface.id))
if interface.type is not None:
device_xml.model(type=interface.model)
device_xml.filterref(filter='{}_{}_{}'.format(
interface.network.environment.name,
interface.network.name,
interface.mac_address))
def build_network_filter(self, network):
"""Generate nwfilter XML for network
:type network: Network
:type state: String accept | drop
:rtype : String
"""
filter_xml = XMLBuilder(
'filter',
name="{}_{}".format(network.environment.name, network.name))
return str(filter_xml)
def build_interface_filter(self, interface):
"""Generate nwfilter XML for interface
:type network: Interface
:type state: String accept | drop
:rtype : String
"""
filter_xml = XMLBuilder(
'filter',
name="{}_{}_{}".format(
interface.network.environment.name,
interface.network.name,
interface.mac_address))
filter_xml.filterref(filter="{}_{}".format(
interface.network.environment.name,
interface.network.name))
return str(filter_xml)
def build_node_xml(self, node, emulator):
"""Generate node XML
:type node: Node
:type emulator: String
:rtype : String
"""
node_xml = XMLBuilder("domain", type=node.hypervisor)
node_xml.name(
self._get_name(node.environment and node.environment.name or '',
node.name))
if self.driver.use_host_cpu:
node_xml.cpu(mode='host-passthrough')
node_xml.vcpu(str(node.vcpu))
node_xml.memory(str(node.memory * 1024), unit='KiB')
if self.driver.use_hugepages:
with node_xml.memoryBacking:
node_xml.hugepages
node_xml.clock(offset='utc')
with node_xml.clock.timer(name='rtc',
tickpolicy='catchup', track='wall'):
node_xml.catchup(
threshold='123',
slew='120',
limit='10000')
node_xml.clock.timer(
name='pit',
tickpolicy='delay')
node_xml.clock.timer(
name='hpet',
present='yes' if self.driver.hpet else 'no')
with node_xml.os:
node_xml.type(node.os_type, arch=node.architecture)
for boot_dev in json.loads(node.boot):
node_xml.boot(dev=boot_dev)
if self.driver.reboot_timeout:
node_xml.bios(rebootTimeout='{0}'.format(
self.driver.reboot_timeout))
if node.should_enable_boot_menu:
node_xml.bootmenu(enable='yes', timeout='3000')
with node_xml.devices:
node_xml.controller(type='usb', model='nec-xhci')
node_xml.emulator(emulator)
if node.has_vnc:
if node.vnc_password:
node_xml.graphics(
type='vnc',
listen='0.0.0.0',
autoport='yes',
passwd=node.vnc_password)
else:
node_xml.graphics(
type='vnc',
listen='0.0.0.0',
autoport='yes')
for disk_device in node.disk_devices:
self._build_disk_device(node_xml, disk_device)
for interface in node.interfaces:
self._build_interface_device(node_xml, interface)
with node_xml.video:
node_xml.model(type='vga', vram='9216', heads='1')
with node_xml.serial(type='pty'):
node_xml.target(port='0')
with node_xml.console(type='pty'):
node_xml.target(type='serial', port='0')
return str(node_xml)