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 = {
'config': {'linux.kernel_modules':
'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',
'limits.kernel.memlock': 'unlimited'
},
@ -193,7 +193,8 @@ class LocalTestHost(TestHost):
'vhost-net': {'path': '/dev/vhost-net', 'type': 'unix-char'},
'vhost-scsi': {'path': '/dev/vhost-scsi', 'type': 'unix-char'},
'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'],

View File

@ -25,6 +25,8 @@ limitations under the License.
import json
import platform
import os
import stat
from time import sleep
from os import path
@ -463,7 +465,10 @@ class NovaHypervisor(Question):
def yes(self, answer):
log.info('Configuring nova compute hypervisor ...')
self._maybe_enable_emulation()
check('snap-openstack', 'setup')
enable('libvirtd')
enable('virtlogd')
enable('nova-compute')
@ -478,7 +483,7 @@ class NovaHypervisor(Question):
log.info('Checking virtualization extensions presence on the host')
# Use KVM if it is supported, alternatively fall back to software
# 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'
' for Nova instances')
shell.config_set(**{'config.nova.virt-type': 'kvm'})
@ -487,7 +492,32 @@ class NovaHypervisor(Question):
log.warning('Hardware virtualization is not supported - software'
' emulation will be used for Nova instances')
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
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()