1 ##############################################################################
2 # Copyright (c) 2018 Tim Rozet (trozet@redhat.com) and others.
4 # All rights reserved. This program and the accompanying materials
5 # are made available under the terms of the Apache License, Version 2.0
6 # which accompanies this distribution, and is available at
7 # http://www.apache.org/licenses/LICENSE-2.0
8 ##############################################################################
13 import xml.etree.ElementTree as ET
18 from apex.common.exceptions import OvercloudNodeException
25 def __init__(self, role, ip, ovs_ctrlrs, ovs_mgrs, name, node_xml,
29 self.ovs_ctrlrs = ovs_ctrlrs
30 self.ovs_mgrs = ovs_mgrs
32 self.node_xml_file = node_xml
36 if not os.path.isfile(self.node_xml_file):
37 raise OvercloudNodeException('XML definition file not found: '
38 '{}'.format(self.node_xml_file))
39 if not os.path.isfile(disk_img):
40 raise OvercloudNodeException('Disk image file not found: '
41 '{}'.format(disk_img))
42 self.conn = libvirt.open('qemu:///system')
44 raise OvercloudNodeException('Unable to open libvirt connection')
46 self.create(src_disk=disk_img)
48 def _configure_disk(self, disk):
49 # find default storage pool path
50 pool = self.conn.storagePoolLookupByName('default')
52 raise OvercloudNodeException('Cannot find default storage pool')
53 pool_xml = pool.XMLDesc()
54 logging.debug('Default storage pool xml: {}'.format(pool_xml))
55 etree = ET.fromstring(pool_xml)
57 path = etree.find('target').find('path').text
58 logging.info('System libvirt default pool path: {}'.format(path))
59 except AttributeError as e:
60 logging.error('Failure to find libvirt storage path: {}'.format(
62 raise OvercloudNodeException('Cannot find default storage path')
63 # copy disk to system path
64 self.disk_img = os.path.join(path, os.path.basename(disk))
65 logging.info('Copying disk image to: {}. This may take some '
66 'time...'.format(self.disk_img))
67 shutil.copyfile(disk, self.disk_img)
70 def _update_xml(xml, disk_path=None):
72 Updates a libvirt XML file for the current architecture and OS of this
74 :param xml: XML string of Libvirt domain definition
75 :param disk_path: Optional file path to update for the backing disk
79 logging.debug('Parsing xml')
81 etree = ET.fromstring(xml)
83 logging.error('Unable to parse node XML: {}'.format(xml))
84 raise OvercloudNodeException('Unable to parse node XML')
87 type_element = etree.find('os').find('type')
88 if 'machine' in type_element.keys():
89 type_element.set('machine', 'pc')
90 logging.debug('XML updated with machine "pc"')
91 except AttributeError:
92 logging.warning('Failure to set XML machine type')
94 # qemu-kvm path may differ per system, need to detect it and update xml
95 linux_ver = distro.linux_distribution()[0]
96 if linux_ver == 'Fedora':
97 qemu_path = '/usr/bin/qemu-kvm'
99 qemu_path = '/usr/libexec/qemu-kvm'
102 etree.find('devices').find('emulator').text = qemu_path
103 logging.debug('XML updated with emulator location: '
104 '{}'.format(qemu_path))
105 xml = ET.tostring(etree).decode('utf-8')
106 except AttributeError:
107 logging.warning('Failure to update XML qemu path')
111 disk_element = etree.find('devices').find('disk').find(
113 disk_element.set('file', disk_path)
114 logging.debug('XML updated with file path: {}'.format(
116 except AttributeError:
117 logging.error('Failure to parse XML and set disk type')
118 raise OvercloudNodeException(
119 'Unable to set new disk path in xml {}'.format(xml))
121 return ET.tostring(etree).decode('utf-8')
123 def create(self, src_disk):
124 # copy disk to pool and get new disk location
125 logging.debug('Preparing disk image')
126 self._configure_disk(src_disk)
127 logging.debug('Parsing node XML from {}'.format(self.node_xml_file))
128 with open(self.node_xml_file, 'r') as fh:
129 self.node_xml = fh.read()
130 # if machine is not pc we need to set, also need to update qemu-kvm and
132 self.node_xml = self._update_xml(self.node_xml, self.disk_img)
133 logging.info('Creating node {} in libvirt'.format(self.name))
134 self.vm = self.conn.defineXML(self.node_xml)
143 logging.info('Node {} started'.format(self.name))
144 except libvirt.libvirtError as e:
145 logging.error('Failed to start domain: {}'.format(self.name))
146 raise OvercloudNodeException('Failed to start VM. Reason: '