Skip to content

Commit 8df4e48

Browse files
mregrockalexvru
andauthored
Tests for distconf (#14953)
Co-authored-by: Alexander Rutkovsky <alexvru@ydb.tech>
1 parent f1d45a2 commit 8df4e48

File tree

7 files changed

+441
-41
lines changed

7 files changed

+441
-41
lines changed

ydb/tests/functional/config/test_config_with_metadata.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ def setup_class(cls):
4141
nodes=nodes_count,
4242
use_in_memory_pdisks=False,
4343
metadata_section=cls.metadata_section,
44+
simple_config=True,
4445
)
4546
cls.cluster = KiKiMR(configurator=configurator)
4647
cls.cluster.start()
Lines changed: 196 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,196 @@
1+
# -*- coding: utf-8 -*-
2+
import logging
3+
import yaml
4+
import tempfile
5+
from hamcrest import assert_that
6+
import time
7+
8+
from ydb.tests.library.common.types import Erasure
9+
import ydb.tests.library.common.cms as cms
10+
from ydb.tests.library.clients.kikimr_http_client import SwaggerClient
11+
from ydb.tests.library.harness.kikimr_runner import KiKiMR
12+
from ydb.tests.library.clients.kikimr_config_client import ConfigClient
13+
from ydb.tests.library.harness.kikimr_config import KikimrConfigGenerator
14+
from ydb.tests.library.kv.helpers import create_kv_tablets_and_wait_for_start
15+
from ydb.public.api.protos.ydb_status_codes_pb2 import StatusIds
16+
from ydb.tests.library.harness.util import LogLevels
17+
18+
import ydb.public.api.protos.ydb_config_pb2 as config
19+
20+
logger = logging.getLogger(__name__)
21+
22+
23+
def value_for(key, tablet_id):
24+
return "Value: <key = {key}, tablet_id = {tablet_id}>".format(
25+
key=key, tablet_id=tablet_id)
26+
27+
28+
def fetch_config(config_client):
29+
fetch_config_response = config_client.fetch_all_configs()
30+
assert_that(fetch_config_response.operation.status == StatusIds.SUCCESS)
31+
32+
result = config.FetchConfigResult()
33+
fetch_config_response.operation.result.Unpack(result)
34+
return result.config[0].config
35+
36+
37+
def get_config_version(yaml_config):
38+
config = yaml.safe_load(yaml_config)
39+
return config.get('metadata', {}).get('version', 0)
40+
41+
42+
class DistConfKiKiMRTest(object):
43+
erasure = Erasure.BLOCK_4_2
44+
use_config_store = True
45+
separate_node_configs = True
46+
metadata_section = {
47+
"kind": "MainConfig",
48+
"version": 0,
49+
"cluster": "",
50+
}
51+
52+
@classmethod
53+
def setup_class(cls):
54+
nodes_count = 8 if cls.erasure == Erasure.BLOCK_4_2 else 9
55+
log_configs = {
56+
'BS_NODE': LogLevels.DEBUG,
57+
'GRPC_SERVER': LogLevels.DEBUG,
58+
'GRPC_PROXY': LogLevels.DEBUG,
59+
'TX_PROXY': LogLevels.DEBUG,
60+
'TICKET_PARSER': LogLevels.DEBUG,
61+
}
62+
cls.configurator = KikimrConfigGenerator(
63+
cls.erasure,
64+
nodes=nodes_count,
65+
use_in_memory_pdisks=False,
66+
use_config_store=cls.use_config_store,
67+
metadata_section=cls.metadata_section,
68+
separate_node_configs=cls.separate_node_configs,
69+
simple_config=True,
70+
use_self_management=True,
71+
extra_grpc_services=['config'],
72+
additional_log_configs=log_configs)
73+
74+
cls.cluster = KiKiMR(configurator=cls.configurator)
75+
cls.cluster.start()
76+
77+
cms.request_increase_ratio_limit(cls.cluster.client)
78+
host = cls.cluster.nodes[1].host
79+
grpc_port = cls.cluster.nodes[1].port
80+
cls.swagger_client = SwaggerClient(host, cls.cluster.nodes[1].mon_port)
81+
cls.config_client = ConfigClient(host, grpc_port)
82+
83+
@classmethod
84+
def teardown_class(cls):
85+
cls.cluster.stop()
86+
87+
def check_kikimr_is_operational(self, table_path, tablet_ids):
88+
for partition_id, tablet_id in enumerate(tablet_ids):
89+
write_resp = self.cluster.kv_client.kv_write(
90+
table_path, partition_id, "key", value_for("key", tablet_id)
91+
)
92+
assert_that(write_resp.operation.status == StatusIds.SUCCESS)
93+
94+
read_resp = self.cluster.kv_client.kv_read(
95+
table_path, partition_id, "key"
96+
)
97+
assert_that(read_resp.operation.status == StatusIds.SUCCESS)
98+
99+
100+
class TestKiKiMRDistConfBasic(DistConfKiKiMRTest):
101+
102+
def test_cluster_is_operational_with_distconf(self):
103+
table_path = '/Root/mydb/mytable_with_metadata'
104+
number_of_tablets = 5
105+
tablet_ids = create_kv_tablets_and_wait_for_start(
106+
self.cluster.client,
107+
self.cluster.kv_client,
108+
self.swagger_client,
109+
number_of_tablets,
110+
table_path,
111+
timeout_seconds=3
112+
)
113+
self.check_kikimr_is_operational(table_path, tablet_ids)
114+
115+
def test_cluster_expand_with_distconf(self):
116+
table_path = '/Root/mydb/mytable_with_expand'
117+
number_of_tablets = 5
118+
119+
tablet_ids = create_kv_tablets_and_wait_for_start(
120+
self.cluster.client,
121+
self.cluster.kv_client,
122+
self.swagger_client,
123+
number_of_tablets,
124+
table_path,
125+
timeout_seconds=3
126+
)
127+
128+
current_node_ids = list(self.cluster.nodes.keys())
129+
expected_new_node_id = max(current_node_ids) + 1
130+
131+
node_port_allocator = self.configurator.port_allocator.get_node_port_allocator(expected_new_node_id)
132+
133+
fetched_config = fetch_config(self.config_client)
134+
dumped_fetched_config = yaml.safe_load(fetched_config)
135+
config_section = dumped_fetched_config["config"]
136+
137+
# create new pdisk
138+
tmp_file = tempfile.NamedTemporaryFile(prefix="pdisk{}".format(1), suffix=".data",
139+
dir=None)
140+
pdisk_path = tmp_file.name
141+
142+
# add new host config
143+
host_config_id = len(config_section["host_configs"]) + 1
144+
config_section["host_configs"].append({
145+
"drive": [
146+
{
147+
"path": pdisk_path,
148+
"type": "ROT"
149+
}
150+
],
151+
"host_config_id": host_config_id,
152+
})
153+
154+
# add new node in hosts
155+
config_section["hosts"].append({
156+
"host_config_id": host_config_id,
157+
"host": "localhost",
158+
"port": node_port_allocator.ic_port,
159+
})
160+
self.configurator.full_config = dumped_fetched_config
161+
162+
# prepare new node
163+
new_node = self.cluster.prepare_node(self.configurator)
164+
new_node.format_pdisk(pdisk_path, self.configurator.static_pdisk_size)
165+
dumped_fetched_config["metadata"]["version"] = 1
166+
167+
# replace config
168+
replace_config_response = self.config_client.replace_config(yaml.dump(dumped_fetched_config))
169+
assert_that(replace_config_response.operation.status == StatusIds.SUCCESS)
170+
# start new node
171+
new_node.start()
172+
173+
self.check_kikimr_is_operational(table_path, tablet_ids)
174+
175+
time.sleep(5)
176+
177+
try:
178+
pdisk_info = self.swagger_client.pdisk_info(new_node.node_id)
179+
180+
pdisks_list = pdisk_info['PDiskStateInfo']
181+
182+
found_pdisk_in_viewer = False
183+
for pdisk_entry in pdisks_list:
184+
node_id_in_entry = pdisk_entry.get('NodeId')
185+
path_in_entry = pdisk_entry.get('Path')
186+
state_in_entry = pdisk_entry.get('State')
187+
188+
if node_id_in_entry == new_node.node_id and path_in_entry == pdisk_path:
189+
logger.info(f"Found matching PDisk in viewer: NodeId={node_id_in_entry}, Path={path_in_entry}, State={state_in_entry}")
190+
found_pdisk_in_viewer = True
191+
break
192+
except Exception as e:
193+
logger.error(f"Viewer API check failed: {e}", exc_info=True)
194+
if 'pdisk_info' in locals():
195+
logger.error(f"Viewer API response content: {pdisk_info}")
196+
raise

ydb/tests/functional/config/test_generate_dynamic_config.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ def setup_class(cls):
3030
configurator = KikimrConfigGenerator(cls.erasure,
3131
nodes=nodes_count,
3232
use_in_memory_pdisks=False,
33+
simple_config=True,
3334
)
3435
cls.cluster = KiKiMR(configurator=configurator)
3536
cls.cluster.start()
@@ -77,6 +78,7 @@ def setup_class(cls):
7778
use_in_memory_pdisks=False,
7879
use_config_store=True,
7980
separate_node_configs=True,
81+
simple_config=True,
8082
)
8183

8284
cls.cluster = KiKiMR(configurator=configurator)

ydb/tests/functional/config/ya.make

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ PY3TEST()
33
TEST_SRCS(
44
test_config_with_metadata.py
55
test_generate_dynamic_config.py
6+
test_distconf.py
67
)
78

89
SPLIT_FACTOR(10)
@@ -22,8 +23,11 @@ ENDIF()
2223

2324

2425
ENV(YDB_DRIVER_BINARY="ydb/apps/ydbd/ydbd")
26+
ENV(YDB_CLI_BINARY="ydb/apps/ydb/ydb")
27+
ENV(IAM_TOKEN="")
2528
DEPENDS(
2629
ydb/apps/ydbd
30+
ydb/apps/ydb
2731
)
2832

2933
PEERDIR(

0 commit comments

Comments
 (0)