charm-nova-compute-nvidia-vgpu/unit_tests/test_charm_utils.py
Myles Penner 79664a6261 Add charmcraft 3 support
Summary of changes:
- Update the charmcraft.yaml file to use base and platforms, only
  allowing noble support.
- Update config to default to caracal.
- Update osci.yaml to use the charmcraft 3.x/beta
- Drop non-noble tests
- Update constraints file from -2024.1.txt to -noble.txt
- Add charmbuild to .zuul.yaml

Change-Id: I98d3beee200783db9303772d6df81f46ce455c3e
2024-09-05 18:53:08 +00:00

253 lines
9.7 KiB
Python

# Copyright 2022 Canonical Ltd
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
import sys
import unittest
from mock import ANY, MagicMock, patch
sys.path.append('src') # noqa
from ops.model import (
ActiveStatus,
BlockedStatus,
)
import charm_utils
class TestCharmUtils(unittest.TestCase):
@patch('nvidia_utils.has_nvidia_gpu_hardware')
def test_is_nvidia_software_to_be_installed(self,
has_nvidia_gpu_hardware_mock):
has_nvidia_gpu_hardware_mock.return_value = True, 1
self.assertTrue(
charm_utils.is_nvidia_software_to_be_installed_notcached({
'force-install-nvidia-vgpu': False}))
has_nvidia_gpu_hardware_mock.return_value = False, 0
self.assertTrue(
charm_utils.is_nvidia_software_to_be_installed_notcached({
'force-install-nvidia-vgpu': True}))
self.assertFalse(
charm_utils.is_nvidia_software_to_be_installed_notcached({
'force-install-nvidia-vgpu': False}))
@patch('nvidia_utils.update_initramfs')
@patch('nvidia_utils.render')
@patch('charm_utils.apt_install')
@patch('charm_utils._path_and_hash_nvidia_resource')
@patch('charm_utils.is_nvidia_software_to_be_installed')
def test_install_nvidia_software_if_needed(
self, is_software_to_be_installed_mock, path_and_hash_mock,
apt_install_mock, render_template_mock, update_initramfs_mock):
is_software_to_be_installed_mock.return_value = True
unit_stored_state = MagicMock()
unit_stored_state.last_installed_resource_hash = 'hash-1'
# If a software package with the exact same hash has already been
# installed, no new installation should be performed:
path_and_hash_mock.return_value = (
'path-to-software',
'hash-1',
)
charm_utils.install_nvidia_software_if_needed(unit_stored_state, None,
None)
self.assertFalse(apt_install_mock.called)
# If there is now a new software package with a different hash,
# installation should be performed:
path_and_hash_mock.return_value = (
'path-to-software',
'hash-2',
)
charm_utils.install_nvidia_software_if_needed(unit_stored_state, None,
None)
apt_install_mock.assert_called_once_with(['path-to-software'],
fatal=True)
render_template_mock.assert_called_once_with(
'disable-nouveau.conf', ANY, ANY)
update_initramfs_mock.assert_called_once_with()
@patch('charm_utils.ows_check_services_running')
@patch('charm_utils.is_nvidia_software_to_be_installed')
@patch('nvidia_utils.installed_nvidia_software_versions')
@patch('nvidia_utils.has_nvidia_gpu_hardware')
def test_check_status(
self, has_hw_mock, installed_sw_mock, is_sw_to_be_installed_mock,
check_services_running_mock):
has_hw_mock.return_value = True, 1
installed_sw_mock.return_value = ['42', '43']
is_sw_to_be_installed_mock.return_value = True
check_services_running_mock.return_value = (None, None)
self.assertEqual(
charm_utils.check_status(None, None),
ActiveStatus(
'Unit is ready (1 GPU)'
)
)
has_hw_mock.return_value = False, 0
installed_sw_mock.return_value = ['42', '43']
is_sw_to_be_installed_mock.return_value = True
check_services_running_mock.return_value = (None, None)
self.assertEqual(
charm_utils.check_status(None, None),
ActiveStatus(
'Unit is ready (0 GPU)'
)
)
has_hw_mock.return_value = True, 1
installed_sw_mock.return_value = []
is_sw_to_be_installed_mock.return_value = True
check_services_running_mock.return_value = (None, None)
self.assertEqual(
charm_utils.check_status(None, None),
BlockedStatus(
'NVIDIA GPU detected, drivers not installed'
)
)
has_hw_mock.return_value = True, 2
installed_sw_mock.return_value = []
is_sw_to_be_installed_mock.return_value = False
check_services_running_mock.return_value = (None, None)
self.assertEqual(
charm_utils.check_status(None, None),
ActiveStatus(
'Unit is ready (2 GPU)'
)
)
has_hw_mock.return_value = True, 1
installed_sw_mock.return_value = ['42', '43']
is_sw_to_be_installed_mock.return_value = True
check_services_running_mock.return_value = (
None, 'Services not running that should be: nvidia-vgpu-mgr')
self.assertEqual(
charm_utils.check_status(None, None),
BlockedStatus('manual reboot required')
)
@patch('nvidia_utils._installed_nvidia_software_packages')
@patch('charm_utils.get_os_codename_package')
def test_set_principal_unit_relation_data(self, release_codename_mock,
installed_packages_mock):
release_codename_mock.return_value = 'xena'
installed_packages_mock.return_value = [{
'name': 'nvidia-vgpu-ubuntu-470',
'version': '470.68',
'architecture': 'amd64',
'description': 'NVIDIA vGPU driver - version 470.68'
}]
relation_data_to_be_set = {}
charm_config = {
'vgpu-device-mappings': "{'nvidia-35': ['0000:84:00.0']}"
}
charm_services = ['nvidia-vgpu-mgr']
charm_utils.set_principal_unit_relation_data(
relation_data_to_be_set, charm_config, charm_services)
self.assertIn(
'0000:84:00.0',
relation_data_to_be_set['subordinate_configuration'])
self.assertIn(
'nvidia-vgpu-mgr',
relation_data_to_be_set['services'])
self.assertIn(
'nvidia-vgpu-ubuntu-470',
relation_data_to_be_set['releases-packages-map'])
relation_data_to_be_set = {}
charm_config = {
'vgpu-device-mappings': ''
}
charm_utils.set_principal_unit_relation_data(
relation_data_to_be_set, charm_config, charm_services)
self.assertEqual(
'{"nova": {"/etc/nova/nova.conf": {"sections": {"devices": '
'[["enabled_mdev_types", ""]]}}}}',
relation_data_to_be_set['subordinate_configuration'])
@patch('charm_utils.file_hash')
def test_path_and_hash_nvidia_resource(self, file_hash_mock):
file_hash_mock.return_value = 'nvidia-software-hash'
resources = MagicMock()
resources.fetch.return_value = 'nvidia-software-path'
self.assertEqual(charm_utils._path_and_hash_nvidia_resource(resources),
('nvidia-software-path', 'nvidia-software-hash'))
@patch('charm_utils.get_os_codename_package')
def test_nova_conf_sections(self, release_codename_mock):
vgpu_device_mappings = {
'nvidia-35': ['0000:84:00.0', '0000:85:00.0'],
'nvidia-36': ['0000:86:00.0'],
}
expected_queens_nova_conf_sections = {
'devices': [
('enabled_vgpu_types', 'nvidia-35, nvidia-36'),
],
}
expected_ussuri_nova_conf_sections = {
'devices': [
('enabled_vgpu_types', 'nvidia-35, nvidia-36'),
],
'vgpu_nvidia-35': [
('device_addresses', '0000:84:00.0,0000:85:00.0'),
],
'vgpu_nvidia-36': [
('device_addresses', '0000:86:00.0'),
],
}
expected_xena_nova_conf_sections = {
'devices': [
('enabled_mdev_types', 'nvidia-35, nvidia-36'),
],
'mdev_nvidia-35': [
('device_addresses', '0000:84:00.0,0000:85:00.0'),
],
'mdev_nvidia-36': [
('device_addresses', '0000:86:00.0'),
],
}
release_codename_mock.return_value = 'xena'
self.assertEqual(charm_utils._nova_conf_sections(vgpu_device_mappings),
expected_xena_nova_conf_sections)
release_codename_mock.return_value = None
self.assertEqual(charm_utils._nova_conf_sections(vgpu_device_mappings),
expected_queens_nova_conf_sections)
release_codename_mock.return_value = 'ussuri'
self.assertEqual(charm_utils._nova_conf_sections(vgpu_device_mappings),
expected_ussuri_nova_conf_sections)
release_codename_mock.return_value = 'wallaby'
self.assertEqual(charm_utils._nova_conf_sections(vgpu_device_mappings),
expected_ussuri_nova_conf_sections)
release_codename_mock.return_value = 'queens'
self.assertEqual(charm_utils._nova_conf_sections(vgpu_device_mappings),
expected_queens_nova_conf_sections)
with self.assertRaises(charm_utils.UnsupportedOpenStackRelease):
release_codename_mock.return_value = 'pike'
charm_utils._nova_conf_sections(vgpu_device_mappings)