diff options
Diffstat (limited to 'apex/overcloud')
-rw-r--r-- | apex/overcloud/node.py | 147 |
1 files changed, 147 insertions, 0 deletions
diff --git a/apex/overcloud/node.py b/apex/overcloud/node.py new file mode 100644 index 00000000..622d1fd1 --- /dev/null +++ b/apex/overcloud/node.py @@ -0,0 +1,147 @@ +############################################################################## +# Copyright (c) 2018 Tim Rozet (trozet@redhat.com) and others. +# +# All rights reserved. This program and the accompanying materials +# are made available under the terms of the Apache License, Version 2.0 +# which accompanies this distribution, and is available at +# http://www.apache.org/licenses/LICENSE-2.0 +############################################################################## + +import logging +import os +import shutil +import xml.etree.ElementTree as ET + +import distro +import libvirt + +from apex.common.exceptions import OvercloudNodeException + + +class OvercloudNode: + """ + Overcloud server + """ + def __init__(self, role, ip, ovs_ctrlrs, ovs_mgrs, name, node_xml, + disk_img): + self.role = role + self.ip = ip + self.ovs_ctrlrs = ovs_ctrlrs + self.ovs_mgrs = ovs_mgrs + self.name = name + self.node_xml_file = node_xml + self.node_xml = None + self.vm = None + self.disk_img = None + if not os.path.isfile(self.node_xml_file): + raise OvercloudNodeException('XML definition file not found: ' + '{}'.format(self.node_xml_file)) + if not os.path.isfile(disk_img): + raise OvercloudNodeException('Disk image file not found: ' + '{}'.format(disk_img)) + self.conn = libvirt.open('qemu:///system') + if not self.conn: + raise OvercloudNodeException('Unable to open libvirt connection') + + self.create(src_disk=disk_img) + + def _configure_disk(self, disk): + # find default storage pool path + pool = self.conn.storagePoolLookupByName('default') + if pool is None: + raise OvercloudNodeException('Cannot find default storage pool') + pool_xml = pool.XMLDesc() + logging.debug('Default storage pool xml: {}'.format(pool_xml)) + etree = ET.fromstring(pool_xml) + try: + path = etree.find('target').find('path').text + logging.info('System libvirt default pool path: {}'.format(path)) + except AttributeError as e: + logging.error('Failure to find libvirt storage path: {}'.format( + e)) + raise OvercloudNodeException('Cannot find default storage path') + # copy disk to system path + self.disk_img = os.path.join(path, os.path.basename(disk)) + logging.info('Copying disk image to: {}. This may take some ' + 'time...'.format(self.disk_img)) + shutil.copyfile(disk, self.disk_img) + + @staticmethod + def _update_xml(xml, disk_path=None): + """ + Updates a libvirt XML file for the current architecture and OS of this + machine + :param xml: XML string of Libvirt domain definition + :param disk_path: Optional file path to update for the backing disk + image + :return: Updated XML + """ + logging.debug('Parsing xml') + try: + etree = ET.fromstring(xml) + except ET.ParseError: + logging.error('Unable to parse node XML: {}'.format(xml)) + raise OvercloudNodeException('Unable to parse node XML') + + try: + type_element = etree.find('os').find('type') + if 'machine' in type_element.keys(): + type_element.set('machine', 'pc') + logging.debug('XML updated with machine "pc"') + except AttributeError: + logging.warning('Failure to set XML machine type') + + # qemu-kvm path may differ per system, need to detect it and update xml + linux_ver = distro.linux_distribution()[0] + if linux_ver == 'Fedora': + qemu_path = '/usr/bin/qemu-kvm' + else: + qemu_path = '/usr/libexec/qemu-kvm' + + try: + etree.find('devices').find('emulator').text = qemu_path + logging.debug('XML updated with emulator location: ' + '{}'.format(qemu_path)) + xml = ET.tostring(etree).decode('utf-8') + except AttributeError: + logging.warning('Failure to update XML qemu path') + + if disk_path: + try: + disk_element = etree.find('devices').find('disk').find( + 'source') + disk_element.set('file', disk_path) + logging.debug('XML updated with file path: {}'.format( + disk_path)) + except AttributeError: + logging.error('Failure to parse XML and set disk type') + raise OvercloudNodeException( + 'Unable to set new disk path in xml {}'.format(xml)) + + return ET.tostring(etree).decode('utf-8') + + def create(self, src_disk): + # copy disk to pool and get new disk location + logging.debug('Preparing disk image') + self._configure_disk(src_disk) + logging.debug('Parsing node XML from {}'.format(self.node_xml_file)) + with open(self.node_xml_file, 'r') as fh: + self.node_xml = fh.read() + # if machine is not pc we need to set, also need to update qemu-kvm and + # storage location + self.node_xml = self._update_xml(self.node_xml, self.disk_img) + logging.info('Creating node {} in libvirt'.format(self.name)) + self.vm = self.conn.defineXML(self.node_xml) + + def start(self): + """ + Boot node in libvirt + :return: + """ + try: + self.vm.create() + logging.info('Node {} started'.format(self.name)) + except libvirt.libvirtError as e: + logging.error('Failed to start domain: {}'.format(self.name)) + raise OvercloudNodeException('Failed to start VM. Reason: ' + '{}'.format(e)) |