Add unit tests for _has_nvidia_gpu_hardware()

Also add .gitreview and .zuul.yaml files

Change-Id: I7f839a0ea7c3a3f72a4d6be5dfd47f9ddd0c0bef
This commit is contained in:
Aurelien Lourot 2021-12-14 10:39:50 +01:00
parent a2f6ce020b
commit 4f49eab3fd
19 changed files with 212 additions and 27 deletions

4
.gitreview Normal file
View File

@ -0,0 +1,4 @@
[gerrit]
host=review.opendev.org
port=29418
project=openstack/charm-nova-compute-nvidia-vgpu

4
.zuul.yaml Normal file
View File

@ -0,0 +1,4 @@
- project:
templates:
- openstack-python3-charm-jobs
- openstack-cover-jobs

View File

@ -1,6 +1,6 @@
# Overview # Overview
This subordinate charm provides the NVidia vGPU support to the This subordinate charm provides the Nvidia vGPU support to the
[OpenStack Nova Compute service][charm-nova-compute]. [OpenStack Nova Compute service][charm-nova-compute].
# Bugs # Bugs

View File

@ -1,3 +1,5 @@
# NOTE(lourot): versions newer than 1.0.0 fail as described in # NOTE(lourot): versions newer than 1.0.0 fail as described in
# https://github.com/canonical/charmcraft/pull/487#issuecomment-894529408 # https://github.com/canonical/charmcraft/pull/487#issuecomment-894529408
git+https://github.com/canonical/charmcraft.git@1.0.0#egg=charmcraft git+https://github.com/canonical/charmcraft.git@1.0.0#egg=charmcraft
cffi==1.14.6; python_version < '3.6' # cffi 1.15.0 drops support for py35.

View File

@ -1,10 +1,10 @@
name: nova-compute-nvidia-vgpu name: nova-compute-nvidia-vgpu
summary: NVidia vGPU support for OpenStack Nova Compute summary: Nvidia vGPU support for OpenStack Nova Compute
maintainer: OpenStack Charmers <openstack-charmers@lists.ubuntu.com> maintainer: OpenStack Charmers <openstack-charmers@lists.ubuntu.com>
description: | description: |
OpenStack Compute, codenamed Nova, is a cloud computing fabric controller. OpenStack Compute, codenamed Nova, is a cloud computing fabric controller.
. .
This charm provides NVidia vGPU support for Nova. This charm provides Nvidia vGPU support for Nova.
tags: tags:
- openstack - openstack
series: series:

View File

@ -1,16 +1,89 @@
- project: - project:
templates: templates:
- charm-yoga-unit-jobs - charm-yoga-unit-jobs
- charm-yoga-functional-jobs
- charm-xena-functional-jobs
- charm-wallaby-functional-jobs
- charm-victoria-functional-jobs
check: check:
jobs: jobs:
# NOTE(lourot): adding `focal-ussuri` manually to the list here instead # NOTE(lourot): adding `focal-ussuri` manually to the list here instead
# of using `charm-ussuri-functional-jobs` as we don't support Bionic. # of using `charm-ussuri-functional-jobs` as we don't support Bionic.
- focal-ussuri - jammy-yoga-nvidia-vgpu:
voting: false
- impish-xena-nvidia-vgpu
- hirsute-wallaby-nvidia-vgpu
- focal-yoga-nvidia-vgpu
- focal-xena-nvidia-vgpu
- focal-wallaby-nvidia-vgpu
- focal-victoria-nvidia-vgpu
- focal-ussuri-nvidia-vgpu
vars: vars:
needs_charm_build: true needs_charm_build: true
charm_build_name: nova-compute-nvidia-vgpu charm_build_name: nova-compute-nvidia-vgpu
build_type: charmcraft build_type: charmcraft
- job:
name: jammy-yoga-nvidia-vgpu
description: Run a functional test against jammy-yoga
parent: func-target
dependencies: &smoke-jobs
- focal-ussuri-nvidia-vgpu
vars:
tox_extra_args: jammy-yoga
- job:
name: impish-xena-nvidia-vgpu
description: Run a functional test against impish-xena
parent: func-target
dependencies: *smoke-jobs
vars:
tox_extra_args: impish-xena
- job:
name: hirsute-wallaby-nvidia-vgpu
description: Run a functional test against hirsute-wallaby
parent: func-target
dependencies: *smoke-jobs
vars:
tox_extra_args: hirsute-wallaby
- job:
name: focal-yoga-nvidia-vgpu
description: Run a functional test against focal-yoga
parent: func-target
dependencies: *smoke-jobs
vars:
tox_extra_args: focal-yoga
- job:
name: focal-xena-nvidia-vgpu
description: Run a functional test against focal-xena
parent: func-target
dependencies: *smoke-jobs
vars:
tox_extra_args: focal-xena
- job:
name: focal-wallaby-nvidia-vgpu
description: Run a functional test against focal-wallaby
parent: func-target
dependencies: *smoke-jobs
vars:
tox_extra_args: focal-wallaby
- job:
name: focal-victoria-nvidia-vgpu
description: Run a functional test against focal-victoria
parent: func-target
dependencies: *smoke-jobs
vars:
tox_extra_args: focal-victoria
- job:
name: focal-ussuri-nvidia-vgpu
description: Run a functional test against focal-ussuri
parent: func-target
dependencies:
# The soft dependencies mean that if they are not configured to run
# they will be ignored. See
# https://github.com/openstack-charmers/zosci-config
- charm-build
- osci-lint
- name: tox-py36
soft: true
- name: tox-py38
soft: true
- name: tox-py39
soft: true
vars:
tox_extra_args: focal-ussuri

View File

@ -173,20 +173,28 @@ class NovaComputeNvidiaVgpuCharm(ops_openstack.core.OSBaseCharm):
return [package['version'] for package in return [package['version'] for package in
apt_cache().dpkg_list(['nvidia-vgpu-ubuntu-*']).values()] apt_cache().dpkg_list(['nvidia-vgpu-ubuntu-*']).values()]
@staticmethod @classmethod
@cached @cached
def _has_nvidia_gpu_hardware(): def _has_nvidia_gpu_hardware(cls):
"""Search for NVIDIA GPU hardware. """Search for NVIDIA GPU hardware.
:returns: True if some NVIDIA GPU hardware is found on the current :returns: True if some NVIDIA GPU hardware is found on the current
unit. unit.
:rtype: bool :rtype: bool
""" """
return cls._has_nvidia_gpu_hardware_notcached()
@staticmethod
def _has_nvidia_gpu_hardware_notcached():
nvidia_gpu_hardware_found = False nvidia_gpu_hardware_found = False
for device in SimpleParser().run(): for device in SimpleParser().run():
device_class = device.cls.name device_class = device.cls.name
device_vendor = device.vendor.name device_vendor = device.vendor.name
device_subsystem_vendor = device.subsystem_vendor.name try:
device_subsystem_vendor = device.subsystem_vendor.name
except AttributeError:
device_subsystem_vendor = ''
if '3D' in device_class and ('NVIDIA' in device_vendor or if '3D' in device_class and ('NVIDIA' in device_vendor or
'NVIDIA' in device_subsystem_vendor): 'NVIDIA' in device_subsystem_vendor):
logging.debug('NVIDIA GPU found: {}'.format(device)) logging.debug('NVIDIA GPU found: {}'.format(device))

View File

@ -4,7 +4,7 @@
charm-tools>=2.4.4 charm-tools>=2.4.4
coverage>=3.6 coverage>=3.6
mock>=1.2 mock>=1.2
flake8>=4.0.1 flake8>=4.0.1; python_version >= '3.6'
stestr>=2.2.0 stestr>=2.2.0
requests>=2.18.4 requests>=2.18.4
psutil psutil
@ -14,3 +14,4 @@ git+https://github.com/openstack-charmers/zaza.git#egg=zaza
git+https://github.com/openstack-charmers/zaza-openstack-tests.git#egg=zaza.openstack git+https://github.com/openstack-charmers/zaza-openstack-tests.git#egg=zaza.openstack
pytz # workaround for 14.04 pip/tox pytz # workaround for 14.04 pip/tox
pyudev # for ceph-* charm unit tests (not mocked?) pyudev # for ceph-* charm unit tests (not mocked?)
cffi==1.14.6; python_version < '3.6' # cffi 1.15.0 drops support for py35.

View File

@ -1,3 +1,5 @@
local_overlay_enabled: False
variables: variables:
openstack-origin: &openstack-origin distro openstack-origin: &openstack-origin distro
@ -165,7 +167,7 @@ applications:
- '10' - '10'
nova-compute-nvidia-vgpu: nova-compute-nvidia-vgpu:
charm: ../../../nova-compute-nvidia-vgpu charm: ../../nova-compute-nvidia-vgpu.charm
relations: relations:
- - 'ceph-osd:mon' - - 'ceph-osd:mon'

View File

@ -1,3 +1,5 @@
local_overlay_enabled: False
variables: variables:
openstack-origin: &openstack-origin cloud:focal-victoria openstack-origin: &openstack-origin cloud:focal-victoria
@ -165,7 +167,7 @@ applications:
- '10' - '10'
nova-compute-nvidia-vgpu: nova-compute-nvidia-vgpu:
charm: ../../../nova-compute-nvidia-vgpu charm: ../../nova-compute-nvidia-vgpu.charm
relations: relations:
- - 'ceph-osd:mon' - - 'ceph-osd:mon'

View File

@ -1,3 +1,5 @@
local_overlay_enabled: False
variables: variables:
openstack-origin: &openstack-origin cloud:focal-wallaby openstack-origin: &openstack-origin cloud:focal-wallaby
@ -165,7 +167,7 @@ applications:
- '10' - '10'
nova-compute-nvidia-vgpu: nova-compute-nvidia-vgpu:
charm: ../../../nova-compute-nvidia-vgpu charm: ../../nova-compute-nvidia-vgpu.charm
relations: relations:
- - 'ceph-osd:mon' - - 'ceph-osd:mon'

View File

@ -1,3 +1,5 @@
local_overlay_enabled: False
variables: variables:
openstack-origin: &openstack-origin cloud:focal-xena openstack-origin: &openstack-origin cloud:focal-xena
@ -165,7 +167,7 @@ applications:
- '10' - '10'
nova-compute-nvidia-vgpu: nova-compute-nvidia-vgpu:
charm: ../../../nova-compute-nvidia-vgpu charm: ../../nova-compute-nvidia-vgpu.charm
relations: relations:
- - 'ceph-osd:mon' - - 'ceph-osd:mon'

View File

@ -1,3 +1,5 @@
local_overlay_enabled: False
variables: variables:
openstack-origin: &openstack-origin cloud:focal-yoga openstack-origin: &openstack-origin cloud:focal-yoga
@ -165,7 +167,7 @@ applications:
- '10' - '10'
nova-compute-nvidia-vgpu: nova-compute-nvidia-vgpu:
charm: ../../../nova-compute-nvidia-vgpu charm: ../../nova-compute-nvidia-vgpu.charm
relations: relations:
- - 'ceph-osd:mon' - - 'ceph-osd:mon'

View File

@ -1,3 +1,5 @@
local_overlay_enabled: False
variables: variables:
openstack-origin: &openstack-origin distro openstack-origin: &openstack-origin distro
@ -165,7 +167,7 @@ applications:
- '10' - '10'
nova-compute-nvidia-vgpu: nova-compute-nvidia-vgpu:
charm: ../../../nova-compute-nvidia-vgpu charm: ../../nova-compute-nvidia-vgpu.charm
relations: relations:
- - 'ceph-osd:mon' - - 'ceph-osd:mon'

View File

@ -1,3 +1,5 @@
local_overlay_enabled: False
variables: variables:
openstack-origin: &openstack-origin distro openstack-origin: &openstack-origin distro
@ -165,7 +167,7 @@ applications:
- '10' - '10'
nova-compute-nvidia-vgpu: nova-compute-nvidia-vgpu:
charm: ../../../nova-compute-nvidia-vgpu charm: ../../nova-compute-nvidia-vgpu.charm
relations: relations:
- - 'ceph-osd:mon' - - 'ceph-osd:mon'

View File

@ -1,3 +1,5 @@
local_overlay_enabled: False
variables: variables:
openstack-origin: &openstack-origin distro openstack-origin: &openstack-origin distro
@ -165,7 +167,7 @@ applications:
- '10' - '10'
nova-compute-nvidia-vgpu: nova-compute-nvidia-vgpu:
charm: ../../../nova-compute-nvidia-vgpu charm: ../../nova-compute-nvidia-vgpu.charm
relations: relations:
- - 'ceph-osd:mon' - - 'ceph-osd:mon'

View File

@ -7,12 +7,12 @@ gate_bundles:
- focal-ussuri - focal-ussuri
- focal-victoria - focal-victoria
- focal-wallaby - focal-wallaby
- focal-xena
- focal-yoga - focal-yoga
- hirsute-wallaby - hirsute-wallaby
- impish-xena
dev_bundles: dev_bundles:
- focal-xena
- impish-xena
- jammy-yoga - jammy-yoga
configure: configure:

View File

@ -54,6 +54,11 @@ basepython = python3.8
deps = -r{toxinidir}/requirements.txt deps = -r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt -r{toxinidir}/test-requirements.txt
[testenv:py39]
basepython = python3.9
deps = -r{toxinidir}/requirements.txt
-r{toxinidir}/test-requirements.txt
[testenv:py3] [testenv:py3]
basepython = python3 basepython = python3
deps = -r{toxinidir}/requirements.txt deps = -r{toxinidir}/requirements.txt

View File

@ -13,23 +13,95 @@
# limitations under the License. # limitations under the License.
import unittest import unittest
from src.charm import NovaComputeNvidiaVgpuCharm
from mock import MagicMock, patch
from ops.model import ActiveStatus from ops.model import ActiveStatus
from ops.testing import Harness from ops.testing import Harness
import src.charm
class TestNovaComputeNvidiaVgpuCharm(unittest.TestCase):
class CharmTestCase(unittest.TestCase):
def setUp(self, obj, patches):
super().setUp()
self.patches = patches
self.obj = obj
self.patch_all()
def patch(self, method):
_m = patch.object(self.obj, method)
mock = _m.start()
self.addCleanup(_m.stop)
return mock
def patch_all(self):
for method in self.patches:
setattr(self, method, self.patch(method))
class MockLspciProperty:
def __init__(self, name):
self.name = name
class MockLspciDevice:
def __init__(self, cls_name, vendor_name):
self.cls = MockLspciProperty(cls_name)
self.vendor = MockLspciProperty(vendor_name)
class TestNovaComputeNvidiaVgpuCharm(CharmTestCase):
_PATCHES = [
'SimpleParser',
]
_PCI_DEVICES_LIST_WITHOUT_GPU = [
# This is an NVIDIA device, but not a GPU card:
MockLspciDevice(cls_name='VGA compatible controller',
vendor_name='NVIDIA Corporation'),
]
_PCI_DEVICES_LIST_WITH_NVIDIA_GPU = [
# This is an NVIDIA device, but not a GPU card:
MockLspciDevice(cls_name='VGA compatible controller',
vendor_name='NVIDIA Corporation'),
# This is an NVIDIA GPU card:
MockLspciDevice(cls_name='3D controller',
vendor_name='NVIDIA Corporation'),
]
def setUp(self): def setUp(self):
self.harness = Harness(NovaComputeNvidiaVgpuCharm) super().setUp(src.charm, self._PATCHES)
self.harness = Harness(src.charm.NovaComputeNvidiaVgpuCharm)
self.addCleanup(self.harness.cleanup) self.addCleanup(self.harness.cleanup)
self.harness.begin() self.harness.begin()
def test_start(self): def test_has_nvidia_gpu_hardware_with_hw(self):
self.SimpleParser.return_value = MagicMock()
self.SimpleParser.return_value.run.return_value = (
self._PCI_DEVICES_LIST_WITH_NVIDIA_GPU)
self.assertTrue(
self.harness.charm._has_nvidia_gpu_hardware_notcached())
def test_has_nvidia_gpu_hardware_without_hw(self):
self.SimpleParser.return_value = MagicMock()
self.SimpleParser.return_value.run.return_value = (
self._PCI_DEVICES_LIST_WITHOUT_GPU)
self.assertFalse(
self.harness.charm._has_nvidia_gpu_hardware_notcached())
def test_init(self):
self.assertEqual( self.assertEqual(
self.harness.framework.model.app.name, self.harness.framework.model.app.name,
'nova-compute-nvidia-vgpu') 'nova-compute-nvidia-vgpu')
# Test that charm is active upon installation. self.assertFalse(self.harness.charm._stored.is_started)
self.assertIsNone(
self.harness.charm._stored.last_installed_resource_hash)
def test_start(self):
self.harness.charm.on.start.emit() self.harness.charm.on.start.emit()
self.assertTrue(isinstance( self.assertTrue(isinstance(
self.harness.model.unit.status, ActiveStatus)) self.harness.model.unit.status, ActiveStatus))