Attempting to fix NFS issues
[apex.git] / apex / overcloud / node.py
1 ##############################################################################
2 # Copyright (c) 2018 Tim Rozet (trozet@redhat.com) and others.
3 #
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 ##############################################################################
9
10 import logging
11 import os
12 import shutil
13 import xml.etree.ElementTree as ET
14
15 import distro
16 import libvirt
17
18 from apex.common.exceptions import OvercloudNodeException
19
20
21 class OvercloudNode:
22     """
23     Overcloud server
24     """
25     def __init__(self, role, ip, ovs_ctrlrs, ovs_mgrs, name, node_xml,
26                  disk_img):
27         self.role = role
28         self.ip = ip
29         self.ovs_ctrlrs = ovs_ctrlrs
30         self.ovs_mgrs = ovs_mgrs
31         self.name = name
32         self.node_xml_file = node_xml
33         self.node_xml = None
34         self.vm = None
35         self.disk_img = None
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')
43         if not self.conn:
44             raise OvercloudNodeException('Unable to open libvirt connection')
45
46         self.create(src_disk=disk_img)
47
48     def _configure_disk(self, disk):
49         # find default storage pool path
50         pool = self.conn.storagePoolLookupByName('default')
51         if pool is None:
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)
56         try:
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(
61                 e))
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)
68
69     @staticmethod
70     def _update_xml(xml, disk_path=None):
71         """
72         Updates a libvirt XML file for the current architecture and OS of this
73         machine
74         :param xml: XML string of Libvirt domain definition
75         :param disk_path: Optional file path to update for the backing disk
76         image
77         :return: Updated XML
78         """
79         logging.debug('Parsing xml')
80         try:
81             etree = ET.fromstring(xml)
82         except ET.ParseError:
83             logging.error('Unable to parse node XML: {}'.format(xml))
84             raise OvercloudNodeException('Unable to parse node XML')
85
86         try:
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')
93
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'
98         else:
99             qemu_path = '/usr/libexec/qemu-kvm'
100
101         try:
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')
108
109         if disk_path:
110             try:
111                 disk_element = etree.find('devices').find('disk').find(
112                     'source')
113                 disk_element.set('file', disk_path)
114                 logging.debug('XML updated with file path: {}'.format(
115                     disk_path))
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))
120
121         return ET.tostring(etree).decode('utf-8')
122
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
131         # storage location
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)
135
136     def start(self):
137         """
138         Boot node in libvirt
139         :return:
140         """
141         try:
142             self.vm.create()
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: '
147                                          '{}'.format(e))