1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
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))
|