Add template rendering in NovaHypervisor

In order to apply dynamically generated virt-type config to the actual
templates they need to be rendered.

Also improves a KVM presence check since the CPU features may be visible
to a container but KVM API via a character file might not be available
there.

The cpu-mode is now also set to "host-model" instead of "host-passthrough"
when emulation is used as it is done in the default config.

Closes-Bug: #1942761
Change-Id: I689543232a94f4df16445c6e3057c5a329d3f6ae
This commit is contained in:
Dmitrii Shcherbakov
2021-09-06 13:20:49 +03:00
parent 42a50992ed
commit 648a45f3d9
3 changed files with 114 additions and 4 deletions

View File

@@ -183,7 +183,7 @@ class LocalTestHost(TestHost):
profile_conf = { profile_conf = {
'config': {'linux.kernel_modules': 'config': {'linux.kernel_modules':
'iptable_nat, ip6table_nat, ebtables, openvswitch,' 'iptable_nat, ip6table_nat, ebtables, openvswitch,'
'tap, vhost, vhost_net, vhost_scsi, vhost_vsock', 'tap, vhost, vhost_net, vhost_scsi, vhost_vsock, kvm',
'security.nesting': 'true', 'security.nesting': 'true',
'limits.kernel.memlock': 'unlimited' 'limits.kernel.memlock': 'unlimited'
}, },
@@ -193,7 +193,8 @@ class LocalTestHost(TestHost):
'vhost-net': {'path': '/dev/vhost-net', 'type': 'unix-char'}, 'vhost-net': {'path': '/dev/vhost-net', 'type': 'unix-char'},
'vhost-scsi': {'path': '/dev/vhost-scsi', 'type': 'unix-char'}, 'vhost-scsi': {'path': '/dev/vhost-scsi', 'type': 'unix-char'},
'vhost-vsock': {'path': '/dev/vhost-vsock', 'vhost-vsock': {'path': '/dev/vhost-vsock',
'type': 'unix-char'} 'type': 'unix-char'},
'kvm': {'path': '/dev/kvm', 'type': 'unix-char'},
} }
} }
self.run(['sudo', 'lxc', 'profile', 'edit', 'microstack'], self.run(['sudo', 'lxc', 'profile', 'edit', 'microstack'],

View File

@@ -25,6 +25,8 @@ limitations under the License.
import json import json
import platform import platform
import os
import stat
from time import sleep from time import sleep
from os import path from os import path
@@ -463,7 +465,10 @@ class NovaHypervisor(Question):
def yes(self, answer): def yes(self, answer):
log.info('Configuring nova compute hypervisor ...') log.info('Configuring nova compute hypervisor ...')
self._maybe_enable_emulation() self._maybe_enable_emulation()
check('snap-openstack', 'setup')
enable('libvirtd') enable('libvirtd')
enable('virtlogd') enable('virtlogd')
enable('nova-compute') enable('nova-compute')
@@ -478,7 +483,7 @@ class NovaHypervisor(Question):
log.info('Checking virtualization extensions presence on the host') log.info('Checking virtualization extensions presence on the host')
# Use KVM if it is supported, alternatively fall back to software # Use KVM if it is supported, alternatively fall back to software
# emulation. # emulation.
if self._is_hw_virt_supported(): if self._is_hw_virt_supported() and self._is_kvm_api_available():
log.info('Hardware virtualization is supported - KVM will be used' log.info('Hardware virtualization is supported - KVM will be used'
' for Nova instances') ' for Nova instances')
shell.config_set(**{'config.nova.virt-type': 'kvm'}) shell.config_set(**{'config.nova.virt-type': 'kvm'})
@@ -487,7 +492,32 @@ class NovaHypervisor(Question):
log.warning('Hardware virtualization is not supported - software' log.warning('Hardware virtualization is not supported - software'
' emulation will be used for Nova instances') ' emulation will be used for Nova instances')
shell.config_set(**{'config.nova.virt-type': 'qemu'}) shell.config_set(**{'config.nova.virt-type': 'qemu'})
shell.config_set(**{'config.nova.cpu-mode': 'host-passthrough'}) shell.config_set(**{'config.nova.cpu-mode': 'host-model'})
@staticmethod
def _is_kvm_api_available():
kvm_devpath = '/dev/kvm'
if not os.path.exists(kvm_devpath):
log.warning(f'{kvm_devpath} does not exist')
return False
elif not os.access(kvm_devpath, os.R_OK | os.W_OK):
log.warning(f'{kvm_devpath} is not RW-accessible')
return False
kvm_dev = os.stat(kvm_devpath)
if not stat.S_ISCHR(kvm_dev.st_mode):
log.warning(f'{kvm_devpath} is not a character device')
return False
major = os.major(kvm_dev.st_rdev)
minor = os.minor(kvm_dev.st_rdev)
if major != 10:
log.warning(
f'{kvm_devpath} has an unexpected major number: {major}')
return False
elif minor != 232:
log.warning(
f'{kvm_devpath} has an unexpected minor number: {minor}')
return False
return True
@staticmethod @staticmethod
def _is_hw_virt_supported(): def _is_hw_virt_supported():

View File

@@ -0,0 +1,79 @@
import unittest
import mock
import os
import sys
from init.questions import NovaHypervisor
sys.path.append(os.getcwd()) # noqa
class TestNovaHypervisor(unittest.TestCase):
@mock.patch('os.path.exists', return_value=False)
def test_is_kvm_api_available_no_kvm_char_file(self, mock_exists):
self.assertFalse(NovaHypervisor._is_kvm_api_available())
@mock.patch('os.access', side_effect=(
lambda p, m: False if p == '/dev/kvm' else True))
@mock.patch('os.path.exists', return_value=True)
def test_is_kvm_api_available_inaccessible_kvm_char_file(
self, mock_exists, mock_access):
self.assertFalse(NovaHypervisor._is_kvm_api_available())
@mock.patch('stat.S_ISCHR', side_effect=lambda m: False)
@mock.patch('os.stat')
@mock.patch('os.access', return_value=True)
@mock.patch('os.path.exists', return_value=True)
def test_is_kvm_api_available_kvm_file_is_not_char_file(
self, mock_exists, mock_access, mock_stat, mock_ischr):
self.assertFalse(NovaHypervisor._is_kvm_api_available())
@mock.patch('os.minor')
@mock.patch('os.major')
@mock.patch('stat.S_ISCHR', return_value=True)
@mock.patch('os.stat')
@mock.patch('os.access', return_value=True)
@mock.patch('os.path.exists', return_value=True)
def test_is_kvm_api_available_kvm_file_invalid_major_minor(
self, mock_exists, mock_access, mock_stat, mock_ischr,
invalid_major, invalid_minor):
self.assertFalse(NovaHypervisor._is_kvm_api_available())
@mock.patch('os.minor')
@mock.patch('os.major', return_value=42)
@mock.patch('stat.S_ISCHR', return_value=True)
@mock.patch('os.stat')
@mock.patch('os.access', return_value=True)
@mock.patch('os.path.exists', return_value=True)
def test_is_kvm_api_available_kvm_file_invalid_major(
self, mock_exists, mock_access, mock_stat, mock_ischr,
invalid_major, invalid_minor):
self.assertFalse(NovaHypervisor._is_kvm_api_available())
@mock.patch('os.minor', return_value=42)
@mock.patch('os.major', return_value=10)
@mock.patch('stat.S_ISCHR', return_value=True)
@mock.patch('os.stat')
@mock.patch('os.access', return_value=True)
@mock.patch('os.path.exists', return_value=True)
def test_is_kvm_api_available_kvm_file_invalid_minor(
self, mock_exists, mock_access, mock_stat, mock_ischr,
invalid_major, invalid_minor):
self.assertFalse(NovaHypervisor._is_kvm_api_available())
@mock.patch('os.minor', return_value=232)
@mock.patch('os.major', return_value=10)
@mock.patch('stat.S_ISCHR', return_value=True)
@mock.patch('os.stat')
@mock.patch('os.access', return_value=True)
@mock.patch('os.path.exists', return_value=True)
def test_is_kvm_api_available_ok(
self, mock_exists, mock_access, mock_stat, mock_ischr,
invalid_major, invalid_minor):
self.assertTrue(NovaHypervisor._is_kvm_api_available())
if __name__ == '__main__':
unittest.main()