Add unit tests for _has_nvidia_gpu_hardware()
Also add .gitreview and .zuul.yaml files Change-Id: I7f839a0ea7c3a3f72a4d6be5dfd47f9ddd0c0bef
This commit is contained in:
parent
a2f6ce020b
commit
4f49eab3fd
4
.gitreview
Normal file
4
.gitreview
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
[gerrit]
|
||||||
|
host=review.opendev.org
|
||||||
|
port=29418
|
||||||
|
project=openstack/charm-nova-compute-nvidia-vgpu
|
4
.zuul.yaml
Normal file
4
.zuul.yaml
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
- project:
|
||||||
|
templates:
|
||||||
|
- openstack-python3-charm-jobs
|
||||||
|
- openstack-cover-jobs
|
@ -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
|
||||||
|
@ -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.
|
||||||
|
@ -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:
|
||||||
|
83
osci.yaml
83
osci.yaml
@ -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
|
||||||
|
14
src/charm.py
14
src/charm.py
@ -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))
|
||||||
|
@ -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.
|
||||||
|
@ -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'
|
||||||
|
@ -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'
|
||||||
|
@ -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'
|
||||||
|
@ -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'
|
||||||
|
@ -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'
|
||||||
|
@ -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'
|
||||||
|
@ -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'
|
||||||
|
@ -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'
|
||||||
|
@ -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:
|
||||||
|
5
tox.ini
5
tox.ini
@ -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
|
||||||
|
@ -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))
|
||||||
|
Loading…
Reference in New Issue
Block a user