mirror of
https://github.com/scylladb/scylladb.git
synced 2026-04-28 20:27:03 +00:00
the "keyspace" argument of the "ring" command is optional. but before this change, we considered it a mandatory option. it was wrong. so, in this change, we make it optional, and print out the warning message if the keyspace is not specified. Signed-off-by: Kefu Chai <kefu.chai@scylladb.com> Closes scylladb/scylladb#17472
193 lines
6.5 KiB
Python
193 lines
6.5 KiB
Python
#
|
|
# Copyright 2023-present ScyllaDB
|
|
#
|
|
# SPDX-License-Identifier: AGPL-3.0-or-later
|
|
#
|
|
|
|
from typing import NamedTuple
|
|
import pytest
|
|
import subprocess
|
|
from rest_api_mock import expected_request
|
|
|
|
|
|
class Host(NamedTuple):
|
|
dc: str
|
|
rack: str
|
|
endpoint: str
|
|
status: str
|
|
state: str
|
|
load: float
|
|
ownership: float
|
|
|
|
|
|
def map_to_json(mapper, mapped_type=None):
|
|
json = []
|
|
for key, value in mapper.items():
|
|
if mapped_type is None:
|
|
json.append({'key': key, 'value': value})
|
|
else:
|
|
json.append({'key': key, 'value': mapped_type(value)})
|
|
return json
|
|
|
|
|
|
def format_stat(width, address, rack, status, state, load, owns, token):
|
|
return f"{address:<{width}} {rack:<12}{status:<7}{state:<8}{load:<16}{owns:<20}{token:<44}\n"
|
|
|
|
|
|
def format_size(v):
|
|
units = {
|
|
1 << 40: 'TB',
|
|
1 << 30: 'GB',
|
|
1 << 20: 'MB',
|
|
1 << 10: 'KB',
|
|
}
|
|
for n, unit in units.items():
|
|
if v > n:
|
|
d = v / n
|
|
return f'{d:.2f} {unit}'
|
|
return f'{v} bytes'
|
|
|
|
|
|
@pytest.mark.parametrize("keyspace,host_status,host_state",
|
|
[
|
|
('ks', 'live', 'joining'),
|
|
('ks', 'live', 'normal'),
|
|
('ks', 'live', 'leaving'),
|
|
('ks', 'live', 'moving'),
|
|
('ks', 'down', 'n/a'),
|
|
('', 'live', 'normal'),
|
|
])
|
|
def test_ring(request, nodetool, keyspace, host_status, host_state):
|
|
host = Host('dc0', 'rack0', '127.0.0.1', host_status, host_state,
|
|
6414780.0, 1.0)
|
|
token_to_endpoint = {
|
|
"-9217327499541836964": host.endpoint,
|
|
"9066719992055809912": host.endpoint,
|
|
"50927788561116407": host.endpoint,
|
|
}
|
|
endpoint_to_ownership = {
|
|
host.endpoint: host.ownership,
|
|
}
|
|
|
|
all_hosts = [host]
|
|
|
|
def hosts_in_status(status):
|
|
return list(h.endpoint for h in all_hosts if h.status == status)
|
|
|
|
def hosts_in_state(state):
|
|
return list(h.endpoint for h in all_hosts if h.state == state)
|
|
|
|
status_to_endpoints = dict(
|
|
(status, hosts_in_status(status)) for status in ['live', 'down'])
|
|
state_to_endpoints = dict(
|
|
(state, hosts_in_state(state)) for state in ['joining', 'leaving', 'moving'])
|
|
load_map = dict((h.endpoint, h.load) for h in all_hosts)
|
|
|
|
expected_requests = [
|
|
expected_request('GET', '/storage_service/tokens_endpoint',
|
|
response=map_to_json(token_to_endpoint))
|
|
]
|
|
|
|
is_scylla = request.config.getoption("nodetool") == "scylla"
|
|
print_all_keyspaces = keyspace == ''
|
|
|
|
if is_scylla and print_all_keyspaces:
|
|
# scylla nodetool does not bother getting ownership if keyspace is not
|
|
# specified
|
|
pass
|
|
else:
|
|
expected_requests.append(
|
|
expected_request('GET', f'/storage_service/ownership/{keyspace}',
|
|
response=map_to_json(endpoint_to_ownership, str)))
|
|
expected_requests += [
|
|
expected_request('GET', '/snitch/datacenter',
|
|
params={'host': host.endpoint},
|
|
multiple=expected_request.ANY,
|
|
response=host.dc),
|
|
expected_request('GET', '/gossiper/endpoint/live',
|
|
response=status_to_endpoints['live']),
|
|
expected_request('GET', '/gossiper/endpoint/down',
|
|
response=status_to_endpoints['down']),
|
|
expected_request('GET', '/storage_service/nodes/joining',
|
|
response=state_to_endpoints['joining']),
|
|
expected_request('GET', '/storage_service/nodes/leaving',
|
|
response=state_to_endpoints['leaving']),
|
|
expected_request('GET', '/storage_service/nodes/moving',
|
|
response=state_to_endpoints['moving']),
|
|
expected_request('GET', '/storage_service/load_map',
|
|
response=map_to_json(load_map)),
|
|
expected_request('GET', '/snitch/rack',
|
|
params={'host': host.endpoint},
|
|
multiple=expected_request.ANY,
|
|
response=host.rack),
|
|
]
|
|
args = []
|
|
if keyspace:
|
|
args = [keyspace]
|
|
actual_output = nodetool('ring', *args, expected_requests=expected_requests)
|
|
|
|
expected_output = f'''
|
|
Datacenter: {host.dc}
|
|
==========
|
|
'''
|
|
max_width = max(len(endpoint) for endpoint in token_to_endpoint.values())
|
|
last_token = list(token_to_endpoint)[-1]
|
|
expected_output += format_stat(max_width, 'Address', 'Rack',
|
|
'Status', 'State', 'Load', 'Owns', 'Token')
|
|
expected_output += format_stat(max_width, '', '',
|
|
'', '', '', '', last_token)
|
|
have_vnode = False
|
|
all_endpoints = set()
|
|
for token, endpoint in token_to_endpoint.items():
|
|
assert host.endpoint == endpoint
|
|
|
|
if endpoint in all_endpoints:
|
|
have_vnode = True
|
|
all_endpoints.add(endpoint)
|
|
|
|
if endpoint in status_to_endpoints['live']:
|
|
status = 'Up'
|
|
elif endpoint in status_to_endpoints['down']:
|
|
status = 'Down'
|
|
else:
|
|
status = '?'
|
|
|
|
if endpoint in state_to_endpoints['joining']:
|
|
state = 'Joining'
|
|
elif endpoint in state_to_endpoints['leaving']:
|
|
state = 'Leaving'
|
|
elif endpoint in state_to_endpoints['moving']:
|
|
state = 'Moving'
|
|
else:
|
|
state = 'Normal'
|
|
|
|
load = format_size(host.load)
|
|
|
|
if print_all_keyspaces:
|
|
ownership = '?'
|
|
else:
|
|
# scylla nodetool always prints out the ownership percentage,
|
|
# since it prints out the warning, so user is aware if the
|
|
# ownership is meaningless or not
|
|
ownership_percent = host.ownership * 100
|
|
ownership = f'{ownership_percent:.2f}%'
|
|
|
|
expected_output += format_stat(max_width, endpoint, host.rack,
|
|
status, state, load, ownership, token)
|
|
|
|
expected_output += '\n'
|
|
|
|
if have_vnode:
|
|
expected_output += '''\
|
|
Warning: "nodetool ring" is used to output all the tokens of a node.
|
|
To view status related info of a node use "nodetool status" instead.
|
|
|
|
|
|
'''
|
|
warning = ''
|
|
if print_all_keyspaces:
|
|
warning = '''Note: Non-system keyspaces don't have the same replication settings, effective ownership information is meaningless\n'''
|
|
expected_output += f'''\
|
|
{warning}'''
|
|
assert actual_output == expected_output
|