Skip to content

Commit e12b809

Browse files
committed
Add compatibility test for InferPDiskSlotCount feature (#27297)
1 parent 414d0a4 commit e12b809

File tree

3 files changed

+256
-4
lines changed

3 files changed

+256
-4
lines changed
Lines changed: 251 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,251 @@
1+
# -*- coding: utf-8 -*-
2+
import pytest
3+
import time
4+
import logging
5+
6+
from ydb.tests.library.compatibility.fixtures import RestartToAnotherVersionFixture
7+
from ydb.tests.library.compatibility.fixtures import init_stable_binary_path, init_stable_name
8+
from ydb.tests.library.compatibility.fixtures import inter_stable_binary_path, inter_stable_name
9+
from ydb.tests.library.compatibility.fixtures import current_binary_path, current_name
10+
from ydb.tests.library.common.types import Erasure
11+
import ydb.core.protos.msgbus_pb2 as msgbus
12+
import ydb.core.protos.blobstorage_config_pb2 as blobstorage_config_pb2
13+
14+
logger = logging.getLogger(__name__)
15+
16+
CONST_PDISK_PATH = "SectorMap:TestInferPDiskSettings:480"
17+
CONST_EXPECTED_SLOT_COUNT = 14
18+
CONST_480_GB = 480 * 1024**3
19+
CONST_10_GB = 10 * 1024**3
20+
21+
all_binary_combinations_restart = [
22+
[init_stable_binary_path, inter_stable_binary_path],
23+
[inter_stable_binary_path, current_binary_path],
24+
[init_stable_binary_path, current_binary_path],
25+
]
26+
all_binary_combinations_ids_restart = [
27+
"restart_{}_to_{}".format(init_stable_name, inter_stable_name),
28+
"restart_{}_to_{}".format(inter_stable_name, current_name),
29+
"restart_{}_to_{}".format(init_stable_name, current_name),
30+
]
31+
32+
33+
@pytest.mark.parametrize("base_setup",
34+
argvalues=all_binary_combinations_restart,
35+
ids=all_binary_combinations_ids_restart,
36+
indirect=True)
37+
class TestUpgradeThenRollback(RestartToAnotherVersionFixture):
38+
@pytest.fixture(autouse=True, scope="function")
39+
def setup(self):
40+
cluster_generator = self.setup_cluster(
41+
erasure=Erasure.NONE,
42+
nodes=2,
43+
use_in_memory_pdisks=False)
44+
next(cluster_generator)
45+
46+
host_configs = self.read_host_configs()
47+
for host_config in host_configs:
48+
drive = host_config.Drive.add()
49+
drive.Path = CONST_PDISK_PATH
50+
drive.PDiskConfig.ExpectedSlotCount = CONST_EXPECTED_SLOT_COUNT
51+
self.define_host_configs(host_configs)
52+
53+
yield
54+
55+
def read_host_configs(self):
56+
request = msgbus.TBlobStorageConfigRequest()
57+
request.Domain = 1
58+
request.Request.Command.add().ReadHostConfig.SetInParent()
59+
60+
response = self.cluster.client.send(request, 'BlobStorageConfig').BlobStorageConfigResponse
61+
logger.info(f"read_host_config response: {response}")
62+
if not response.Success:
63+
raise RuntimeError('read_host_config request failed: %s' % response.ErrorDescription)
64+
status = response.Status[0]
65+
if not status.Success:
66+
raise RuntimeError('read_host_config has failed status: %s' % status.ErrorDescription)
67+
68+
return status.HostConfig
69+
70+
def define_host_configs(self, host_configs):
71+
"""Define host configuration with specified host config"""
72+
request = msgbus.TBlobStorageConfigRequest()
73+
request.Domain = 1
74+
for host_config in host_configs:
75+
request.Request.Command.add().DefineHostConfig.MergeFrom(host_config)
76+
77+
logger.info(f"define_host_config request: {request}")
78+
response = self.cluster.client.send(request, 'BlobStorageConfig').BlobStorageConfigResponse
79+
logger.info(f"define_host_config responce: {response}")
80+
if not response.Success:
81+
raise RuntimeError('define_host_config request failed: %s' % response.ErrorDescription)
82+
for i, status in enumerate(response.Status):
83+
if not status.Success:
84+
raise RuntimeError('define_host_config has failed status[%d]: %s' % (i, status))
85+
86+
def pdisk_set_all_active(self):
87+
"""Update all drive statuses to ACTIVE. Equivalent to
88+
`dstool pdisk set --status=ACTIVE --pdisk-ids <pdisks>`
89+
"""
90+
base_config = self.query_base_config()
91+
92+
request = msgbus.TBlobStorageConfigRequest()
93+
request.Domain = 1
94+
95+
for pdisk in base_config.BaseConfig.PDisk:
96+
if pdisk.Path != CONST_PDISK_PATH:
97+
continue
98+
cmd = request.Request.Command.add().UpdateDriveStatus
99+
cmd.HostKey.NodeId = pdisk.NodeId
100+
cmd.PDiskId = pdisk.PDiskId
101+
cmd.Status = blobstorage_config_pb2.EDriveStatus.ACTIVE
102+
103+
logger.info(f"update_all_drive_status_active request: {request}")
104+
response = self.cluster.client.send(request, 'BlobStorageConfig').BlobStorageConfigResponse
105+
logger.info(f"update_all_drive_status_active response: {response}")
106+
107+
if not response.Success:
108+
raise RuntimeError('update_all_drive_status_active request failed: %s' % response.ErrorDescription)
109+
for i, status in enumerate(response.Status):
110+
if not status.Success:
111+
raise RuntimeError('update_all_drive_status_active has failed status[%d]: %s' % (i, status))
112+
113+
def query_base_config(self):
114+
request = msgbus.TBlobStorageConfigRequest()
115+
request.Domain = 1
116+
117+
# Add QueryBaseConfig command
118+
command = request.Request.Command.add()
119+
command.QueryBaseConfig.RetrieveDevices = True
120+
command.QueryBaseConfig.VirtualGroupsOnly = False
121+
122+
# Send the request
123+
response = self.cluster.client.send(request, 'BlobStorageConfig').BlobStorageConfigResponse
124+
if not response.Success:
125+
raise RuntimeError('query_base_config failed: %s' % response.ErrorDescription)
126+
127+
status = response.Status[0]
128+
if not status.Success:
129+
raise RuntimeError('query_base_config failed: %s' % status.ErrorDescription)
130+
131+
return status
132+
133+
def pdisk_list(self):
134+
"""Equivalent to `dstool pdisk list`"""
135+
base_config = self.query_base_config()
136+
137+
# Collect PDisk information
138+
pdisks_info = []
139+
for pdisk in base_config.BaseConfig.PDisk:
140+
if pdisk.Path != CONST_PDISK_PATH:
141+
continue
142+
pdisks_info.append(pdisk)
143+
return pdisks_info
144+
145+
def wait_and_check_pdisk_list(self, check_pdisks_fn, deadline, delay=1):
146+
while True:
147+
pdisks = self.pdisk_list()
148+
try:
149+
check_pdisks_fn(pdisks)
150+
logger.info(f"pdisk_list good: {pdisks}")
151+
return
152+
except AssertionError as e:
153+
if time.time() > deadline:
154+
logger.warning(f"pdisk_list incorrect: {pdisks}")
155+
raise e from e
156+
else:
157+
time.sleep(delay)
158+
159+
def test_infer_pdisk_expected_slot_count(self):
160+
assert self.current_binary_paths_index == 0
161+
logger.info(f"Test started on {self.versions[0]} {time.time()=}")
162+
#################################################################
163+
164+
t1 = time.time()
165+
timeout = 20
166+
167+
def check_pdisks(pdisks):
168+
for pdisk in pdisks:
169+
assert pdisk.Path == CONST_PDISK_PATH
170+
assert pdisk.PDiskConfig.ExpectedSlotCount == CONST_EXPECTED_SLOT_COUNT
171+
assert pdisk.DriveStatus == blobstorage_config_pb2.EDriveStatus.ACTIVE
172+
assert pdisk.PDiskMetrics.TotalSize == CONST_480_GB
173+
if self.versions[0] < (25, 3):
174+
assert not pdisk.PDiskMetrics.HasField('SlotCount')
175+
assert not pdisk.PDiskMetrics.HasField('SlotSizeInUnits')
176+
else:
177+
assert pdisk.PDiskMetrics.SlotCount == CONST_EXPECTED_SLOT_COUNT
178+
assert pdisk.PDiskMetrics.HasField('SlotSizeInUnits') and \
179+
pdisk.PDiskMetrics.SlotSizeInUnits == 0
180+
assert pdisk.PDiskMetrics.UpdateTimestamp * 1e-6 > t1
181+
assert pdisk.PDiskMetrics.UpdateTimestamp * 1e-6 < t1 + timeout
182+
self.wait_and_check_pdisk_list(check_pdisks, deadline=t1+timeout)
183+
184+
self.change_cluster_version()
185+
assert self.current_binary_paths_index == 1
186+
logger.info(f"Restarted on version {self.versions[1]} {time.time()=}")
187+
######################################################################
188+
189+
t2 = time.time()
190+
host_configs = self.read_host_configs()
191+
for host_config in host_configs:
192+
drive = host_config.Drive[1]
193+
assert drive.Path == CONST_PDISK_PATH
194+
drive.ClearField('PDiskConfig')
195+
drive.PDiskConfig.SetInParent()
196+
drive.InferPDiskSlotCountFromUnitSize = CONST_10_GB
197+
drive.InferPDiskSlotCountMax = 32
198+
self.define_host_configs(host_configs)
199+
logger.info(f"Inferred PDisk setting applied {time.time()=}")
200+
201+
self.pdisk_set_all_active()
202+
logger.info(f"Drives activated {time.time()=}")
203+
204+
deadline = time.time() + timeout
205+
206+
def check_pdisks(pdisks):
207+
for pdisk in pdisks:
208+
assert pdisk.Path == CONST_PDISK_PATH
209+
assert pdisk.DriveStatus == blobstorage_config_pb2.EDriveStatus.ACTIVE
210+
assert not pdisk.HasField('PDiskConfig')
211+
assert pdisk.ExpectedSlotCount == 16 # hardcoded default
212+
assert pdisk.PDiskMetrics.TotalSize == CONST_480_GB
213+
assert pdisk.PDiskMetrics.SlotCount == 24
214+
assert pdisk.PDiskMetrics.SlotSizeInUnits == 2
215+
assert pdisk.InferPDiskSlotCountFromUnitSize == CONST_10_GB
216+
assert pdisk.InferPDiskSlotCountMax == 32
217+
assert pdisk.PDiskMetrics.UpdateTimestamp * 1e-6 > t2
218+
assert pdisk.PDiskMetrics.UpdateTimestamp * 1e-6 < deadline
219+
self.wait_and_check_pdisk_list(check_pdisks, deadline)
220+
221+
t3 = time.time()
222+
self.change_cluster_version()
223+
assert self.current_binary_paths_index == 0
224+
logger.info(f"Restarted back on version {self.versions[0]} {time.time()=}")
225+
###########################################################################
226+
227+
self.pdisk_set_all_active()
228+
logger.info(f"Drives activated {time.time()=}")
229+
230+
deadline = time.time() + timeout
231+
232+
def check_pdisks(pdisks):
233+
for pdisk in pdisks:
234+
assert pdisk.Path == CONST_PDISK_PATH
235+
assert pdisk.DriveStatus == blobstorage_config_pb2.EDriveStatus.ACTIVE
236+
assert not pdisk.HasField('PDiskConfig')
237+
assert pdisk.ExpectedSlotCount == 16 # hardcoded default
238+
assert pdisk.PDiskMetrics.TotalSize == CONST_480_GB
239+
if self.versions[0] < (25, 3):
240+
assert not pdisk.PDiskMetrics.HasField('SlotCount')
241+
assert not pdisk.PDiskMetrics.HasField('SlotSizeInUnits')
242+
assert pdisk.InferPDiskSlotCountFromUnitSize == 0
243+
assert pdisk.InferPDiskSlotCountMax == 0
244+
else:
245+
assert pdisk.PDiskMetrics.HasField('SlotCount') and pdisk.PDiskMetrics.SlotCount == 24
246+
assert pdisk.PDiskMetrics.HasField('SlotSizeInUnits') and pdisk.PDiskMetrics.SlotSizeInUnits == 2
247+
assert pdisk.InferPDiskSlotCountFromUnitSize == CONST_10_GB
248+
assert pdisk.InferPDiskSlotCountMax == 32
249+
assert pdisk.PDiskMetrics.UpdateTimestamp * 1e-6 > t3
250+
assert pdisk.PDiskMetrics.UpdateTimestamp * 1e-6 < deadline
251+
self.wait_and_check_pdisk_list(check_pdisks, deadline)

ydb/tests/compatibility/ya.make

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ TEST_SRCS(
2626
test_node_broker_delta_protocol.py
2727
test_table_schema_compatibility.py
2828
test_workload_manager.py
29+
test_infer_pdisk_expected_slot_count.py
2930
udf/test_datetime2.py
3031
udf/test_digest.py
3132
udf/test_digest_regression.py

ydb/tests/library/compatibility/fixtures.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,9 @@ def setup_cluster(self, tenant_db=None, **kwargs):
109109
extra_feature_flags = copy.copy(extra_feature_flags)
110110
extra_feature_flags["suppress_compatibility_check"] = True
111111
self.config = KikimrConfigGenerator(
112-
erasure=Erasure.MIRROR_3_DC,
112+
erasure=kwargs.pop("erasure", Erasure.MIRROR_3_DC),
113113
binary_paths=[self.all_binary_paths[self.current_binary_paths_index]],
114-
use_in_memory_pdisks=False,
114+
use_in_memory_pdisks=kwargs.pop("use_in_memory_pdisks", False),
115115
extra_feature_flags=extra_feature_flags,
116116
**kwargs,
117117
)
@@ -262,9 +262,9 @@ def setup_cluster(self, tenant_db=None, **kwargs):
262262
# By default draining is not enabled to faster tests
263263
extra_feature_flags["enable_drain_on_shutdown"] = True
264264
self.config = KikimrConfigGenerator(
265-
erasure=Erasure.MIRROR_3_DC,
265+
erasure=kwargs.pop("erasure", Erasure.MIRROR_3_DC),
266266
binary_paths=[self.all_binary_paths[0]],
267-
use_in_memory_pdisks=False,
267+
use_in_memory_pdisks=kwargs.pop("use_in_memory_pdisks", False),
268268
extra_feature_flags=extra_feature_flags,
269269
**kwargs,
270270
)

0 commit comments

Comments
 (0)