mirror of
https://github.com/scylladb/scylladb.git
synced 2026-05-23 16:22:15 +00:00
133 lines
3.8 KiB
Python
133 lines
3.8 KiB
Python
#
|
|
# Copyright 2024-present ScyllaDB
|
|
#
|
|
# SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.1
|
|
#
|
|
|
|
import pytest
|
|
from copy import deepcopy
|
|
from enum import Enum, auto
|
|
from textwrap import indent
|
|
from typing import NamedTuple
|
|
from test.nodetool.rest_api_mock import expected_request
|
|
|
|
|
|
# see gms/application_state.hh
|
|
# cassandra sends the index of application_state over wire, but nodetool
|
|
# prints their names, so we have to replicate the mapping here
|
|
class State(Enum):
|
|
STATUS = 0
|
|
LOAD = auto()
|
|
SCHEMA = auto()
|
|
DC = auto()
|
|
RACK = auto()
|
|
RELEASE_VERSION = auto()
|
|
REMOVAL_COORDINATOR = auto()
|
|
INTERNAL_IP = auto()
|
|
RPC_ADDRESS = auto()
|
|
X_11_PADDIN = auto()
|
|
SEVERITY = auto()
|
|
NET_VERSION = auto()
|
|
HOST_ID = auto()
|
|
TOKENS = auto()
|
|
SUPPORTED_FEATURES = auto()
|
|
CACHE_HITRATES = auto()
|
|
SCHEMA_TABLES_VERSION = auto()
|
|
RPC_READY = auto()
|
|
VIEW_BACKLOG = auto()
|
|
SHARD_COUNT = auto()
|
|
IGNORE_MSB_BITS = auto()
|
|
CDC_GENERATION_ID = auto()
|
|
SNITCH_NAME = auto()
|
|
|
|
|
|
class VersionedValue(NamedTuple):
|
|
version: int
|
|
value: str
|
|
|
|
|
|
def endpoint_to_jsonable(e):
|
|
application_state = e['application_state']
|
|
states = []
|
|
for state, versioned_value in application_state.items():
|
|
jsonable = {'application_state': state.value,
|
|
'version': versioned_value.version,
|
|
'value': versioned_value.value}
|
|
states.append(jsonable)
|
|
j = deepcopy(e)
|
|
j['application_state'] = states
|
|
return j
|
|
|
|
|
|
def print_application_state(application_state):
|
|
lines = ''
|
|
for state, versioned_value in application_state.items():
|
|
if state != State.TOKENS:
|
|
lines += f'{state.name}:{versioned_value.value}\n'
|
|
return lines
|
|
|
|
|
|
def normalize_endpoints(endpoints):
|
|
# Cassandra's nodetool uses HashMap under the hood for collecting the map
|
|
# from state to the value of that state, and print out them by iterating
|
|
# the keys, but the order is not guaranteed to be ordered or consistent, so
|
|
# let's extract the state values out and sort them before comparing.
|
|
normalized = {}
|
|
addrs = None
|
|
state = []
|
|
|
|
def maybe_add_endpoint():
|
|
if addrs is not None:
|
|
normalized[addrs] = sorted(state)
|
|
|
|
for line in endpoints.split('\n'):
|
|
if line.startswith('/'):
|
|
maybe_add_endpoint()
|
|
addrs = line
|
|
state = []
|
|
else:
|
|
state.append(line)
|
|
maybe_add_endpoint()
|
|
|
|
return sorted(normalized)
|
|
|
|
|
|
def create_endpoint_info(i):
|
|
return {
|
|
'addrs': f'127.0.0.{i}',
|
|
'is_alive': True,
|
|
'update_time': 4096,
|
|
'generation': 1707827511,
|
|
'version': 400,
|
|
'application_state': {
|
|
State.DC: VersionedValue(17, "datacenter1"),
|
|
State.HOST_ID: VersionedValue(2, 'c850baa6-1f8f-431d-b898-09b646288c13'),
|
|
State.TOKENS: VersionedValue(24, 'secret'),
|
|
}
|
|
}
|
|
|
|
|
|
def format_endpoint_info(endpoint):
|
|
states_fmt = print_application_state(endpoint['application_state'])
|
|
return """\
|
|
/{addrs}
|
|
generation:{generation}
|
|
heartbeat:{heartbeat}
|
|
{more_info}
|
|
""".format(addrs=endpoint['addrs'],
|
|
generation=endpoint['generation'],
|
|
heartbeat=endpoint['version'],
|
|
more_info=indent(states_fmt, ' '))
|
|
|
|
|
|
@pytest.mark.parametrize("num_endpoints", [1, 2])
|
|
def test_gossipinfo(nodetool, num_endpoints):
|
|
endpoints = [create_endpoint_info(i) for i in range(num_endpoints)]
|
|
res = nodetool('gossipinfo', expected_requests=[
|
|
expected_request('GET', '/failure_detector/endpoints',
|
|
response=[endpoint_to_jsonable(endpoint) for endpoint in endpoints])
|
|
])
|
|
actual_output = res.stdout
|
|
expected_output = ''.join(format_endpoint_info(endpoint) for endpoint in endpoints)
|
|
assert normalize_endpoints(actual_output) == normalize_endpoints(expected_output)
|