The default 'cassandra' superuser was removed from ScyllaDB, which
broke PGO training. exec_cql.py relied on username/password auth
('cassandra'/'cassandra') to execute setup CQL scripts like auth.cql
and counters.cql.
Switch exec_cql.py to connect via the Unix domain maintenance socket
instead. The maintenance socket bypasses authentication, no credentials
are needed. Additionally, create the 'cassandra' superuser via the
maintenance socket during the populate phase, so that cassandra-stress
keeps working. cassandra-stress hardcodes user=cassandra password=cassandra.
Changes:
- exec_cql.py: replace host/port/username/password arguments with a
single --socket argument; add connect_maintenance_socket() with
wait ready logic
- pgo.py: add maintenance_socket_path() helper; update
populate_auth_conns() and populate_counters() to pass the socket
path to exec_cql.py
Fixes SCYLLADB-1070
Closes scylladb/scylladb#29081
83 lines
3.0 KiB
Python
83 lines
3.0 KiB
Python
#!/usr/bin/env python3
|
|
# -*- coding: utf-8 -*-
|
|
#
|
|
# Copyright (C) 2025-present ScyllaDB
|
|
#
|
|
# SPDX-License-Identifier: LicenseRef-ScyllaDB-Source-Available-1.0
|
|
#
|
|
|
|
"""exec_cql.py
|
|
Execute CQL statements from a file where each non-empty, non-comment line is exactly one CQL statement.
|
|
Connects via a Unix domain socket (maintenance socket), bypassing authentication.
|
|
Requires python cassandra-driver. Stops at first failure.
|
|
Usage:
|
|
./exec_cql.py --file ./conf/auth.cql --socket /path/to/cql.m
|
|
"""
|
|
import argparse, os, sys
|
|
from typing import Sequence
|
|
|
|
def read_statements(path: str) -> list[tuple[int, str]]:
|
|
stms: list[tuple[int, str]] = []
|
|
with open(path, 'r', encoding='utf-8') as f:
|
|
for lineno, raw in enumerate(f, start=1):
|
|
line = raw.strip()
|
|
if not line or line.startswith('--'):
|
|
continue
|
|
if line:
|
|
stms.append((lineno, line))
|
|
return stms
|
|
|
|
def exec_statements(statements: list[tuple[int, str]], socket_path: str, timeout: float) -> int:
|
|
"""Execute CQL statements via a Unix domain socket (maintenance socket).
|
|
|
|
The maintenance socket only starts listening after the auth subsystem is
|
|
fully initialised, so a successful connect means the node is ready.
|
|
"""
|
|
from cassandra.cluster import Cluster
|
|
from cassandra.connection import UnixSocketEndPoint # type: ignore
|
|
from cassandra.policies import WhiteListRoundRobinPolicy # type: ignore
|
|
|
|
ep = UnixSocketEndPoint(socket_path)
|
|
try:
|
|
cluster = Cluster(
|
|
contact_points=[ep],
|
|
load_balancing_policy=WhiteListRoundRobinPolicy([ep]),
|
|
)
|
|
session = cluster.connect()
|
|
except Exception as e:
|
|
print(f'ERROR: failed to connect to maintenance socket {socket_path}: {e}', file=sys.stderr)
|
|
return 2
|
|
|
|
try:
|
|
for _, (lineno, s) in enumerate(statements, 1):
|
|
try:
|
|
session.execute(s, timeout=timeout)
|
|
except Exception as e:
|
|
print(f"ERROR executing statement from file line {lineno}: {s}\n{e}", file=sys.stderr)
|
|
return 1
|
|
finally:
|
|
cluster.shutdown()
|
|
return 0
|
|
|
|
def main(argv: Sequence[str]) -> int:
|
|
ap = argparse.ArgumentParser(description='Execute one-line CQL statements from file via maintenance socket')
|
|
ap.add_argument('--file', required=True)
|
|
ap.add_argument('--socket', required=True,
|
|
help='Path to the Unix domain maintenance socket (<workdir>/cql.m)')
|
|
ap.add_argument('--timeout', type=float, default=30.0)
|
|
args = ap.parse_args(argv)
|
|
if not os.path.isfile(args.file):
|
|
print(f"File not found: {args.file}", file=sys.stderr)
|
|
return 2
|
|
stmts = read_statements(args.file)
|
|
if not stmts:
|
|
print('No statements found', file=sys.stderr)
|
|
return 1
|
|
rc = exec_statements(stmts, args.socket, args.timeout)
|
|
if rc == 0:
|
|
print('All statements executed successfully')
|
|
return rc
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(main(sys.argv[1:]))
|