Skip to content

Commit a3991ee

Browse files
committed
CP-54481: support DMV RPU plugin
add unit test for DMV code Signed-off-by: Chunjie Zhu <chunjie.zhu@cloud.com>
1 parent 2bb35de commit a3991ee

File tree

2 files changed

+217
-49
lines changed

2 files changed

+217
-49
lines changed

tests/test_dmv.py

Lines changed: 184 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,184 @@
1+
"""tests/test_dmv.py: Unit test for xcp/dmv.py"""
2+
import unittest
3+
from unittest import mock
4+
import types
5+
import json
6+
import errno
7+
from xcp import dmv
8+
9+
10+
class TestDMV(unittest.TestCase):
11+
@mock.patch("os.listdir")
12+
def test_get_all_kabi_dirs(self, m_listdir):
13+
m_listdir.return_value = ["4.19.0", "5.10.0"]
14+
dirs = dmv.get_all_kabi_dirs()
15+
self.assertIn(("4.19.0", "/lib/modules/4.19.0/updates", "/lib/modules/4.19.0/dmv"), dirs)
16+
self.assertIn(("5.10.0", "/lib/modules/5.10.0/updates", "/lib/modules/5.10.0/dmv"), dirs)
17+
18+
def test_note_offset(self):
19+
self.assertEqual(dmv.note_offset(1), 3)
20+
self.assertEqual(dmv.note_offset(4), 0)
21+
self.assertEqual(dmv.note_offset(5), 3)
22+
23+
def test_id_matches(self):
24+
self.assertTrue(dmv.id_matches("*", "1234"))
25+
self.assertTrue(dmv.id_matches("1234", "*"))
26+
self.assertTrue(dmv.id_matches("1234", "1234"))
27+
self.assertFalse(dmv.id_matches("1234", "5678"))
28+
29+
def test_pci_matches_true(self):
30+
present = {"vendor": "14e4", "device": "163c", "subvendor": "*", "subdevice": "*"}
31+
driver_pci_ids = {
32+
"abc.ko": [
33+
{"vendor_id": "14e4", "device_id": "163c", "subvendor_id": "*", "subdevice_id": "*"}
34+
]
35+
}
36+
self.assertTrue(dmv.pci_matches(present, driver_pci_ids))
37+
38+
def test_pci_matches_false(self):
39+
present = {"vendor": "abcd", "device": "9999", "subvendor": "*", "subdevice": "*"}
40+
driver_pci_ids = {
41+
"abc.ko": [
42+
{"vendor_id": "14e4", "device_id": "163c", "subvendor_id": "*", "subdevice_id": "*"}
43+
]
44+
}
45+
self.assertFalse(dmv.pci_matches(present, driver_pci_ids))
46+
47+
@mock.patch("re.compile")
48+
def test_hardware_present_true(self, m_compile):
49+
m = mock.Mock()
50+
m.finditer.return_value = [
51+
mock.Mock(groupdict=lambda: {"vendor": "14e4", "device": "163c", "subvendor": "*", "subdevice": "*"})
52+
]
53+
m_compile.return_value = m
54+
pci_ids = {
55+
"abc.ko": [
56+
{"vendor_id": "14e4", "device_id": "163c", "subvendor_id": "*", "subdevice_id": "*"}
57+
]
58+
}
59+
self.assertTrue(dmv.hardware_present("dummy", pci_ids))
60+
61+
@mock.patch("re.compile")
62+
def test_hardware_present_false(self, m_compile):
63+
m = mock.Mock()
64+
m.finditer.return_value = [
65+
mock.Mock(groupdict=lambda: {"vendor": "abcd", "device": "9999", "subvendor": "*", "subdevice": "*"})
66+
]
67+
m_compile.return_value = m
68+
pci_ids = {
69+
"abc.ko": [
70+
{"vendor_id": "14e4", "device_id": "163c", "subvendor_id": "*", "subdevice_id": "*"}
71+
]
72+
}
73+
self.assertFalse(dmv.hardware_present("dummy", pci_ids))
74+
75+
@mock.patch("os.path.isfile")
76+
@mock.patch("builtins.open", new_callable=mock.mock_open)
77+
@mock.patch("struct.calcsize")
78+
@mock.patch("struct.unpack")
79+
def test_get_active_variant(self, m_unpack, m_calcsize, m_open, m_isfile):
80+
m_isfile.return_value = True
81+
m_calcsize.return_value = 12
82+
m_unpack.return_value = (9, 3, 1)
83+
fake_file = mock.Mock()
84+
fake_file.read.side_effect = [
85+
b"x"*12, # header
86+
b"", # offset
87+
b"XenServer", b"", b"v1\x00", b"", # vendor, offset, content, offset
88+
]
89+
m_open.return_value.__enter__.return_value = fake_file
90+
result = dmv.get_active_variant(["foo.ko"])
91+
self.assertEqual(result, "v1")
92+
93+
@mock.patch("os.path.isfile")
94+
def test_get_loaded_modules(self, m_isfile):
95+
m_isfile.side_effect = lambda path: "foo" in path
96+
result = dmv.get_loaded_modules(["foo.ko", "bar.ko"])
97+
self.assertEqual(result, ["foo.ko"])
98+
99+
@mock.patch("os.path.islink")
100+
@mock.patch("os.path.realpath")
101+
@mock.patch("os.path.dirname")
102+
@mock.patch("builtins.open", new_callable=mock.mock_open, read_data='{"variant": "v1"}')
103+
@mock.patch("json.load")
104+
def test_variant_selected(self, m_json_load, m_open, m_dirname, m_realpath, m_islink):
105+
m_islink.return_value = True
106+
m_realpath.return_value = "/some/dir"
107+
m_dirname.return_value = "/some/dir"
108+
m_json_load.return_value = {"variant": "v1"}
109+
d = dmv.DriverMultiVersion("/updates", None)
110+
result = d.variant_selected(["foo.ko"])
111+
self.assertEqual(result, "v1")
112+
113+
@mock.patch("xcp.dmv.open_with_codec_handling")
114+
@mock.patch("xcp.dmv.hardware_present")
115+
def test_parse_dmv_info(self, m_hw_present, m_open_codec):
116+
m_hw_present.return_value = True
117+
info_json = {
118+
"category": "net",
119+
"name": "foo",
120+
"description": "desc",
121+
"variant": "v1",
122+
"version": "1.0",
123+
"priority": 1,
124+
"status": "ok",
125+
"pci_ids": {
126+
"foo.ko": [
127+
{"vendor_id": "14e4", "device_id": "163c", "subvendor_id": "*", "subdevice_id": "*"}
128+
]
129+
}
130+
}
131+
m_open_codec.return_value.__enter__.return_value = mock.Mock(
132+
spec=["read"], read=lambda: json.dumps(info_json)
133+
)
134+
with mock.patch("json.load", return_value=info_json):
135+
lspci_out = types.SimpleNamespace(stdout="dummy")
136+
d = dmv.DriverMultiVersion("", lspci_out)
137+
json_data, json_formatted = d.parse_dmv_info("dummy")
138+
self.assertEqual(json_data["name"], "foo")
139+
self.assertEqual(json_formatted["type"], "net")
140+
self.assertTrue(json_formatted["variants"]["v1"]["hardware_present"])
141+
142+
def test_merge_jsondata(self):
143+
mgr = dmv.DriverMultiVersionManager(runtime=True)
144+
oldone = {
145+
"type": "net",
146+
"friendly_name": "foo",
147+
"description": "desc",
148+
"info": "foo",
149+
"variants": {"v1": {"version": "1.0"}},
150+
"selected": "v1",
151+
"active": "v1",
152+
"loaded modules": ["foo.ko"]
153+
}
154+
newone = {
155+
"type": "net",
156+
"friendly_name": "foo",
157+
"description": "desc",
158+
"info": "foo",
159+
"variants": {"v2": {"version": "2.0"}},
160+
"selected": None,
161+
"active": None,
162+
"loaded modules": ["bar.ko"]
163+
}
164+
mgr.merge_jsondata(oldone, newone)
165+
merged = mgr.dmv_list["drivers"]["foo"]
166+
self.assertIn("v1", merged["variants"])
167+
self.assertIn("v2", merged["variants"])
168+
self.assertEqual(merged["selected"], "v1")
169+
self.assertEqual(merged["active"], "v1")
170+
self.assertEqual(merged["loaded modules"], ["foo.ko", "bar.ko"])
171+
172+
def test_process_dmv_data(self):
173+
mgr = dmv.DriverMultiVersionManager()
174+
json_data = {"name": "foo"}
175+
json_formatted = {"type": "net"}
176+
mgr.process_dmv_data(json_data, json_formatted)
177+
self.assertEqual(mgr.dmv_list["drivers"]["foo"], json_formatted)
178+
179+
def test_get_set_error(self):
180+
mgr = dmv.DriverMultiVersionManager()
181+
mgr.set_dmv_error(errno.ENOENT)
182+
err = mgr.get_dmv_error()
183+
self.assertEqual(err["exit_code"], errno.ENOENT)
184+
self.assertIn("No such file", err["message"])

xcp/dmv.py

Lines changed: 33 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -91,37 +91,36 @@ def id_matches(id1, id2):
9191
return True
9292
return id1 == id2
9393

94-
'''
95-
driver_pci_ids example:
96-
{
97-
"abc.ko": [
98-
{
99-
"vendor_id": "14e4",
100-
"device_id": "163c",
101-
"subvendor_id": "*",
102-
"subdevice_id": "*"
103-
},
104-
{
105-
"vendor_id": "14e4",
106-
"device_id": "163b",
107-
"subvendor_id": "*",
108-
"subdevice_id": "*"
109-
}],
110-
"de.ko": [
111-
{
112-
"vendor_id": "eees",
113-
"device_id": "163c",
114-
"subvendor_id": "*",
115-
"subdevice_id": "*"
116-
},
117-
{
118-
"vendor_id": "14f4",
119-
"device_id": "16db",
120-
"subvendor_id": "2123",
121-
"subdevice_id": "1123"
122-
}]
123-
}
124-
'''
94+
95+
# driver_pci_ids example:
96+
# {
97+
# "abc.ko": [
98+
# {
99+
# "vendor_id": "14e4",
100+
# "device_id": "163c",
101+
# "subvendor_id": "*",
102+
# "subdevice_id": "*"
103+
# },
104+
# {
105+
# "vendor_id": "14e4",
106+
# "device_id": "163b",
107+
# "subvendor_id": "*",
108+
# "subdevice_id": "*"
109+
# }],
110+
# "de.ko": [
111+
# {
112+
# "vendor_id": "eees",
113+
# "device_id": "163c",
114+
# "subvendor_id": "*",
115+
# "subdevice_id": "*"
116+
# },
117+
# {
118+
# "vendor_id": "14f4",
119+
# "device_id": "16db",
120+
# "subvendor_id": "2123",
121+
# "subdevice_id": "1123"
122+
# }]
123+
# }
125124
def pci_matches(present_pci_id, driver_pci_ids):
126125
"""Check if present PCI ID matches any of the driver PCI IDs."""
127126
merged_driver_pci_id_list = []
@@ -170,22 +169,6 @@ def hardware_present(lspci_out, pci_ids):
170169
return True
171170
return False
172171

173-
def variant_selected(modules, updates_dir):
174-
"""Check and return which driver is selected"""
175-
# Check if any module in the modules is selected
176-
for module in modules:
177-
slink_file = os.path.join(updates_dir, module)
178-
if os.path.islink(slink_file):
179-
module_path = os.path.realpath(slink_file)
180-
module_dir = os.path.dirname(module_path)
181-
info_file = os.path.join(module_dir, "info.json")
182-
with open(info_file, "r", encoding="ascii") as json_file:
183-
json_data = json.load(json_file)
184-
variant = json_data["variant"]
185-
186-
return variant
187-
return None
188-
189172
class DriverMultiVersion(object):
190173
def __init__(self, updates_dir, lspci_out, runtime=False):
191174
self.updates_dir = updates_dir
@@ -279,7 +262,8 @@ def merge_jsondata(self, oldone, newone):
279262
loaded = oldone["loaded modules"] + newone["loaded modules"]
280263
json_formatted["loaded modules"] = loaded
281264

282-
self.dmv_list["drivers"][oldone["info"]] = json_formatted
265+
drvname = oldone["info"]
266+
self.dmv_list["drivers"][drvname] = json_formatted
283267

284268
def process_dmv_data(self, json_data, json_formatted):
285269
if not json_data["name"] in self.dmv_list["drivers"]:
@@ -296,7 +280,7 @@ def parse_dmv_list(self):
296280
for _, updates_dir, dmv_dir in get_all_kabi_dirs():
297281
if not os.path.isdir(dmv_dir):
298282
continue
299-
283+
300284
for path, _, files in os.walk(dmv_dir):
301285
if "info.json" not in files:
302286
continue

0 commit comments

Comments
 (0)