Skip to content

Commit d2f8d10

Browse files
committed
qa/cephfs: update tests for test_volumes & unit-test for earmarking
Signed-off-by: Avan Thakkar <[email protected]>
1 parent a08ddab commit d2f8d10

File tree

2 files changed

+212
-0
lines changed

2 files changed

+212
-0
lines changed

qa/tasks/cephfs/test_volumes.py

Lines changed: 126 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2367,6 +2367,124 @@ def test_subvolume_create_and_ls_providing_group_as_nogroup(self):
23672367

23682368
# verify trash dir is clean.
23692369
self._wait_for_trash_empty()
2370+
2371+
def test_subvolume_create_with_earmark(self):
2372+
# create subvolume with earmark
2373+
subvolume = self._gen_subvol_name()
2374+
earmark = "nfs.test"
2375+
self._fs_cmd("subvolume", "create", self.volname, subvolume, "--earmark", earmark)
2376+
2377+
# make sure it exists
2378+
subvolpath = self._get_subvolume_path(self.volname, subvolume)
2379+
self.assertNotEqual(subvolpath, None)
2380+
2381+
# verify the earmark
2382+
get_earmark = self._fs_cmd("subvolume", "earmark", "get", self.volname, subvolume)
2383+
self.assertEqual(get_earmark.rstrip('\n'), earmark)
2384+
2385+
def test_subvolume_set_and_get_earmark(self):
2386+
# create subvolume
2387+
subvolume = self._gen_subvol_name()
2388+
self._fs_cmd("subvolume", "create", self.volname, subvolume)
2389+
2390+
# set earmark
2391+
earmark = "smb.test"
2392+
self._fs_cmd("subvolume", "earmark", "set", self.volname, subvolume, "--earmark", earmark)
2393+
2394+
# get earmark
2395+
get_earmark = self._fs_cmd("subvolume", "earmark", "get", self.volname, subvolume)
2396+
self.assertEqual(get_earmark.rstrip('\n'), earmark)
2397+
2398+
def test_subvolume_clear_earmark(self):
2399+
# create subvolume
2400+
subvolume = self._gen_subvol_name()
2401+
self._fs_cmd("subvolume", "create", self.volname, subvolume)
2402+
2403+
# set earmark
2404+
earmark = "smb.test"
2405+
self._fs_cmd("subvolume", "earmark", "set", self.volname, subvolume, "--earmark", earmark)
2406+
2407+
# remove earmark
2408+
self._fs_cmd("subvolume", "earmark", "rm", self.volname, subvolume)
2409+
2410+
# get earmark
2411+
get_earmark = self._fs_cmd("subvolume", "earmark", "get", self.volname, subvolume)
2412+
self.assertEqual(get_earmark, "")
2413+
2414+
def test_earmark_on_non_existing_subvolume(self):
2415+
subvolume = "non_existing_subvol"
2416+
earmark = "nfs.test"
2417+
commands = [
2418+
("set", earmark),
2419+
("get", None),
2420+
("rm", None),
2421+
]
2422+
2423+
for action, arg in commands:
2424+
try:
2425+
# Build the command arguments
2426+
cmd_args = ["subvolume", "earmark", action, self.volname, subvolume]
2427+
if arg is not None:
2428+
cmd_args.extend(["--earmark", arg])
2429+
2430+
# Execute the command with built arguments
2431+
self._fs_cmd(*cmd_args)
2432+
except CommandFailedError as ce:
2433+
self.assertEqual(ce.exitstatus, errno.ENOENT)
2434+
2435+
def test_get_remove_earmark_when_not_set(self):
2436+
# Create a subvolume without setting an earmark
2437+
subvolume = self._gen_subvol_name()
2438+
self._fs_cmd("subvolume", "create", self.volname, subvolume)
2439+
2440+
# Attempt to get an earmark when it's not set
2441+
get_earmark = self._fs_cmd("subvolume", "earmark", "get", self.volname, subvolume)
2442+
self.assertEqual(get_earmark, "")
2443+
2444+
# Attempt to remove an earmark when it's not set
2445+
self._fs_cmd("subvolume", "earmark", "rm", self.volname, subvolume)
2446+
2447+
def test_set_invalid_earmark(self):
2448+
# Create a subvolume
2449+
subvolume = self._gen_subvol_name()
2450+
self._fs_cmd("subvolume", "create", self.volname, subvolume)
2451+
2452+
# Attempt to set an invalid earmark
2453+
invalid_earmark = "invalid_format"
2454+
expected_message = (
2455+
f"Invalid earmark specified: '{invalid_earmark}'. A valid earmark should "
2456+
"either be empty or start with 'nfs' or 'smb', followed by dot-separated "
2457+
"non-empty components."
2458+
)
2459+
try:
2460+
self._fs_cmd("subvolume", "earmark", "set", self.volname, subvolume, "--earmark", invalid_earmark)
2461+
except CommandFailedError as ce:
2462+
self.assertEqual(ce.exitstatus, errno.EINVAL, expected_message)
2463+
2464+
def test_earmark_on_deleted_subvolume_with_retained_snapshot(self):
2465+
subvolume = self._gen_subvol_name()
2466+
snapshot = self._gen_subvol_snap_name()
2467+
2468+
# Create subvolume and snapshot
2469+
self._fs_cmd("subvolume", "create", self.volname, subvolume)
2470+
self._fs_cmd("subvolume", "snapshot", "create", self.volname, subvolume, snapshot)
2471+
2472+
# Delete subvolume while retaining the snapshot
2473+
self._fs_cmd("subvolume", "rm", self.volname, subvolume, "--retain-snapshots")
2474+
2475+
# Define the expected error message
2476+
error_message = f'subvolume "{subvolume}" is removed and has only snapshots retained'
2477+
2478+
# Test cases for setting, getting, and removing earmarks
2479+
for operation in ["get", "rm", "set"]:
2480+
try:
2481+
extra_arg = "smb" if operation == "set" else None
2482+
if operation == "set":
2483+
self._fs_cmd("subvolume", "earmark", operation, self.volname, subvolume, "--earmark", extra_arg)
2484+
else:
2485+
self._fs_cmd("subvolume", "earmark", operation, self.volname, subvolume)
2486+
except CommandFailedError as ce:
2487+
self.assertEqual(ce.exitstatus, errno.ENOENT, error_message)
23702488

23712489
def test_subvolume_expand(self):
23722490
"""
@@ -2440,6 +2558,14 @@ def test_subvolume_info(self):
24402558
for feature in ['snapshot-clone', 'snapshot-autoprotect', 'snapshot-retention']:
24412559
self.assertIn(feature, subvol_info["features"], msg="expected feature '{0}' in subvolume".format(feature))
24422560

2561+
# set earmark
2562+
earmark = "smb.test"
2563+
self._fs_cmd("subvolume", "earmark", "set", self.volname, subvolume, "--earmark", earmark)
2564+
2565+
subvol_info = json.loads(self._get_subvolume_info(self.volname, subvolume))
2566+
2567+
self.assertEqual(subvol_info["earmark"], earmark)
2568+
24432569
# remove subvolumes
24442570
self._fs_cmd("subvolume", "rm", self.volname, subvolume)
24452571

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import pytest
2+
import errno
3+
from unittest import mock
4+
5+
from ceph.fs.earmarking import CephFSVolumeEarmarking, EarmarkException, EarmarkTopScope
6+
# Mock constants
7+
XATTR_SUBVOLUME_EARMARK_NAME = 'user.ceph.subvolume.earmark'
8+
9+
10+
class TestCephFSVolumeEarmarking:
11+
12+
@pytest.fixture
13+
def mock_fs(self):
14+
return mock.Mock()
15+
16+
@pytest.fixture
17+
def earmarking(self, mock_fs):
18+
return CephFSVolumeEarmarking(mock_fs, "/test/path")
19+
20+
def test_get_earmark_success(self, earmarking, mock_fs):
21+
mock_fs.getxattr.return_value = b"nfs"
22+
result = earmarking.get_earmark()
23+
assert result == "nfs"
24+
mock_fs.getxattr.assert_called_once_with("/test/path", XATTR_SUBVOLUME_EARMARK_NAME)
25+
26+
def test_get_earmark_no_earmark_set(self, earmarking, mock_fs):
27+
mock_fs.getxattr.return_value = b""
28+
result = earmarking.get_earmark()
29+
30+
assert result == ""
31+
mock_fs.getxattr.assert_called_once_with("/test/path", XATTR_SUBVOLUME_EARMARK_NAME)
32+
33+
def test_get_earmark_error(self, earmarking, mock_fs):
34+
mock_fs.getxattr.side_effect = OSError(errno.EIO, "I/O error")
35+
36+
with pytest.raises(EarmarkException) as excinfo:
37+
earmarking.get_earmark()
38+
39+
assert excinfo.value.errno == -errno.EIO
40+
assert "I/O error" in str(excinfo.value)
41+
42+
# Ensure that the getxattr method was called exactly once
43+
mock_fs.getxattr.assert_called_once_with("/test/path", XATTR_SUBVOLUME_EARMARK_NAME)
44+
45+
def test_set_earmark_success(self, earmarking, mock_fs):
46+
earmarking.set_earmark(EarmarkTopScope.NFS.value)
47+
mock_fs.setxattr.assert_called_once_with(
48+
"/test/path", XATTR_SUBVOLUME_EARMARK_NAME, b"nfs", 0
49+
)
50+
51+
def test_set_earmark_invalid(self, earmarking):
52+
with pytest.raises(EarmarkException) as excinfo:
53+
earmarking.set_earmark("invalid_scope")
54+
55+
assert excinfo.value.errno == errno.EINVAL
56+
assert "Invalid earmark specified" in str(excinfo.value)
57+
58+
def test_set_earmark_error(self, earmarking, mock_fs):
59+
mock_fs.setxattr.side_effect = OSError(errno.EIO, "I/O error")
60+
61+
with pytest.raises(EarmarkException) as excinfo:
62+
earmarking.set_earmark(EarmarkTopScope.NFS.value)
63+
64+
assert excinfo.value.errno == -errno.EIO
65+
assert "I/O error" in str(excinfo.value)
66+
mock_fs.setxattr.assert_called_once_with(
67+
"/test/path", XATTR_SUBVOLUME_EARMARK_NAME, b"nfs", 0
68+
)
69+
70+
def test_clear_earmark_success(self, earmarking, mock_fs):
71+
earmarking.clear_earmark()
72+
mock_fs.setxattr.assert_called_once_with(
73+
"/test/path", XATTR_SUBVOLUME_EARMARK_NAME, b"", 0
74+
)
75+
76+
def test_clear_earmark_error(self, earmarking, mock_fs):
77+
mock_fs.setxattr.side_effect = OSError(errno.EIO, "I/O error")
78+
79+
with pytest.raises(EarmarkException) as excinfo:
80+
earmarking.clear_earmark()
81+
82+
assert excinfo.value.errno == -errno.EIO
83+
assert "I/O error" in str(excinfo.value)
84+
mock_fs.setxattr.assert_called_once_with(
85+
"/test/path", XATTR_SUBVOLUME_EARMARK_NAME, b"", 0
86+
)

0 commit comments

Comments
 (0)