Skip to content

Commit 067fa6b

Browse files
authored
Merge pull request #1134 from rackerlabs/split-nvme-driver-library
feat(cinder-understack): split up library from driver interface and add test framework
2 parents 343e6d6 + 0f70489 commit 067fa6b

File tree

8 files changed

+2894
-147
lines changed

8 files changed

+2894
-147
lines changed

.github/workflows/code-test.yaml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,13 @@ jobs:
5353
python-version-file: python/${{ matrix.project }}/pyproject.toml
5454
- run: uv sync
5555
- run: uv build --wheel
56-
- run: "uv run pytest --cov --cov-report xml:coverage.xml"
56+
- run: |
57+
set -e
58+
if [ -e .stestr.conf ]; then
59+
uv run stestr run
60+
else
61+
uv run pytest --cov --cov-report xml:coverage.xml
62+
fi
5763
- uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4
5864
with:
5965
name: coverage-${{ matrix.project }}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
.stestr/
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
[DEFAULT]
2+
test_path=${OS_TEST_PATH:-./cinder_understack/tests}
3+
top_dir=./

python/cinder-understack/cinder_understack/dynamic_netapp_driver.py

Lines changed: 84 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
from cinder import context
44
from cinder import exception
5+
from cinder import interface
56
from cinder.objects import volume_type as vol_type_obj
67
from cinder.volume import driver as volume_driver
78
from cinder.volume.drivers.netapp import options
@@ -31,8 +32,8 @@
3132
]
3233

3334

34-
class NetappCinderDynamicDriver(NetAppNVMeStorageLibrary):
35-
"""NetApp NVMe driver with dynamic SVM selection from volume types.
35+
class NetappDynamicLibrary(NetAppNVMeStorageLibrary):
36+
"""NetApp NVMe storage library with dynamic SVM selection from volume types.
3637
3738
Key difference from standard NetApp drivers:
3839
- Standard: One SVM per backend, all config in cinder.conf
@@ -964,6 +965,87 @@ def create_volume(self, volume):
964965
) from e
965966

966967

968+
@interface.volumedriver
969+
class NetappCinderDynamicDriver(volume_driver.BaseVD):
970+
"""NetApp NVMe driver with dynamic multi-SVM support.
971+
972+
This driver follows the standard Cinder pattern by inheriting from BaseVD
973+
and delegating storage operations to the NetappDynamicLibrary.
974+
"""
975+
976+
VERSION = "1.0.0"
977+
DRIVER_NAME = "NetApp_Dynamic_NVMe"
978+
979+
def __init__(self, *args, **kwargs):
980+
"""Initialize the driver and create library instance."""
981+
super().__init__(*args, **kwargs)
982+
self.library = NetappDynamicLibrary(self.DRIVER_NAME, "NVMe", **kwargs)
983+
984+
def do_setup(self, context):
985+
"""Setup the driver."""
986+
self.library.do_setup(context)
987+
988+
def check_for_setup_error(self):
989+
"""Check for setup errors."""
990+
self.library.check_for_setup_error()
991+
992+
def create_volume(self, volume):
993+
"""Create a volume."""
994+
return self.library.create_volume(volume)
995+
996+
def delete_volume(self, volume):
997+
"""Delete a volume."""
998+
return self.library.delete_volume(volume)
999+
1000+
def create_snapshot(self, snapshot):
1001+
"""Create a snapshot."""
1002+
return self.library.create_snapshot(snapshot)
1003+
1004+
def delete_snapshot(self, snapshot):
1005+
"""Delete a snapshot."""
1006+
return self.library.delete_snapshot(snapshot)
1007+
1008+
def create_volume_from_snapshot(self, volume, snapshot):
1009+
"""Create a volume from a snapshot."""
1010+
return self.library.create_volume_from_snapshot(volume, snapshot)
1011+
1012+
def create_cloned_volume(self, volume, src_vref):
1013+
"""Create a cloned volume."""
1014+
return self.library.create_cloned_volume(volume, src_vref)
1015+
1016+
def extend_volume(self, volume, new_size):
1017+
"""Extend a volume."""
1018+
return self.library.extend_volume(volume, new_size)
1019+
1020+
def initialize_connection(self, volume, connector):
1021+
"""Initialize connection to volume."""
1022+
return self.library.initialize_connection(volume, connector)
1023+
1024+
def terminate_connection(self, volume, connector, **kwargs):
1025+
"""Terminate connection to volume."""
1026+
return self.library.terminate_connection(volume, connector, **kwargs)
1027+
1028+
def get_volume_stats(self, refresh=False):
1029+
"""Get volume stats."""
1030+
return self.library.get_volume_stats(refresh)
1031+
1032+
def update_provider_info(self, volumes, snapshots):
1033+
"""Update provider info."""
1034+
return self.library.update_provider_info(volumes, snapshots)
1035+
1036+
def create_export(self, context, volume, connector):
1037+
"""Create export for volume."""
1038+
return self.library.create_export(context, volume, connector)
1039+
1040+
def ensure_export(self, context, volume):
1041+
"""Ensure export for volume."""
1042+
return self.library.ensure_export(context, volume)
1043+
1044+
def remove_export(self, context, volume):
1045+
"""Remove export for volume."""
1046+
return self.library.remove_export(context, volume)
1047+
1048+
9671049
# NOTES
9681050
# Namespace: Manually created because we skip standard do_setup()
9691051
# Pool: Custom svm#flexvol format to support multi-SVM
Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
"""Test NetApp dynamic driver implementation."""
2+
3+
from unittest import mock
4+
5+
from cinder.tests.unit import test
6+
from cinder.tests.unit.volume.drivers.netapp import fakes as na_fakes
7+
8+
from cinder_understack import dynamic_netapp_driver
9+
10+
11+
class NetappDynamicDriverTestCase(test.TestCase):
12+
"""Test case for NetappCinderDynamicDriver."""
13+
14+
def setUp(self):
15+
"""Set up test case."""
16+
super().setUp()
17+
18+
kwargs = {
19+
"configuration": self.get_config_base(),
20+
"host": "openstack@netapp_dynamic",
21+
}
22+
self.driver = dynamic_netapp_driver.NetappCinderDynamicDriver(**kwargs)
23+
self.library = self.driver.library
24+
25+
def get_config_base(self):
26+
"""Get base configuration for testing."""
27+
return na_fakes.create_configuration()
28+
29+
def test_driver_has_correct_attributes(self):
30+
"""Test that driver has expected attributes."""
31+
self.assertEqual("1.0.0", self.driver.VERSION)
32+
self.assertEqual("NetApp_Dynamic_NVMe", self.driver.DRIVER_NAME)
33+
34+
def test_driver_has_library_instance(self):
35+
"""Test that driver has library instance."""
36+
self.assertIsInstance(self.library, dynamic_netapp_driver.NetappDynamicLibrary)
37+
38+
def test_library_inherits_from_netapp_library(self):
39+
"""Test that library inherits from NetApp NVMe library."""
40+
from cinder.volume.drivers.netapp.dataontap.nvme_library import (
41+
NetAppNVMeStorageLibrary,
42+
)
43+
44+
self.assertIsInstance(self.library, NetAppNVMeStorageLibrary)
45+
46+
@mock.patch.object(dynamic_netapp_driver.NetappDynamicLibrary, "do_setup")
47+
def test_do_setup_calls_library(self, mock_do_setup):
48+
"""Test that do_setup delegates to library."""
49+
context = mock.Mock()
50+
self.driver.do_setup(context)
51+
mock_do_setup.assert_called_once_with(context)
52+
53+
@mock.patch.object(dynamic_netapp_driver.NetappDynamicLibrary, "create_volume")
54+
def test_create_volume_calls_library(self, mock_create_volume):
55+
"""Test that create_volume delegates to library."""
56+
volume = mock.Mock()
57+
self.driver.create_volume(volume)
58+
mock_create_volume.assert_called_once_with(volume)
59+
60+
@mock.patch.object(dynamic_netapp_driver.NetappDynamicLibrary, "get_volume_stats")
61+
def test_get_volume_stats_calls_library(self, mock_get_volume_stats):
62+
"""Test that get_volume_stats delegates to library."""
63+
self.driver.get_volume_stats(refresh=True)
64+
mock_get_volume_stats.assert_called_once_with(True)

python/cinder-understack/cinder_understack/tests/test_noop.py

Lines changed: 0 additions & 2 deletions
This file was deleted.

python/cinder-understack/pyproject.toml

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -24,17 +24,19 @@ classifiers = [
2424
"Programming Language :: Python :: 3.10",
2525
]
2626
dependencies = [
27+
"cinder>=25.0.0,<26",
28+
"oslo.utils>=6.0.0,<8"
2729
]
2830

2931
[project.urls]
3032
Source = "https://github.com/rackerlabs/understack"
3133

3234
[dependency-groups]
3335
test = [
34-
"pytest>=8.3.2,<9",
35-
"pytest-cov>=6.2.1,<7",
36-
"pytest-mock>=3.14.0,<4",
37-
]
36+
"ddt>=1.4.4",
37+
"fixtures>=3.0.0",
38+
"stestr>=3.2.1",
39+
]
3840

3941
[tool.uv]
4042
default-groups = ["test"]

0 commit comments

Comments
 (0)