292 lines
11 KiB
Python
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)
|