Skip to content

Commit 91abd2f

Browse files
committed
cqlsh-expansion python 3
1 parent d166dfd commit 91abd2f

25 files changed

+247
-149
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,4 +3,5 @@
33
/cqlsh-expansion/cqlsh_expansion.egg-info
44
/cqlsh-expansion/build
55
/cqlsh-expansion/dist
6+
/cqlsh-expansion/pylib/cqlshlib/__pycache__
67
.idea

Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#Amazon Keyspaces toolkit
22

3-
ARG CLI_VERSION=latest
3+
ARG CLI_VERSION=2.1.27
44
FROM amazon/aws-cli:$CLI_VERSION
55

66
ENV AWS_KEYSPACES_WORKING_DIR=/root

cqlsh-expansion/bin/cqlsh-expansion.py

Lines changed: 82 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
from glob import glob
3737
from io import StringIO
3838
from uuid import UUID
39+
from ssl import SSLContext, PROTOCOL_TLSv1_2, CERT_REQUIRED
3940

4041
if sys.version_info < (3, 6):
4142
sys.exit("\ncqlsh requires Python 3.6+\n")
@@ -131,7 +132,7 @@ def find_zip(libprefix):
131132
'Error: %s\n' % (sys.executable, sys.path, e))
132133

133134
from cassandra.auth import PlainTextAuthProvider
134-
from cassandra.cluster import Cluster
135+
from cassandra.cluster import Cluster, ExecutionProfile, EXEC_PROFILE_DEFAULT
135136
from cassandra.cqltypes import cql_typename
136137
from cassandra.marshal import int64_unpack
137138
from cassandra.metadata import (ColumnMetadata, KeyspaceMetadata, TableMetadata)
@@ -145,7 +146,7 @@ def find_zip(libprefix):
145146
if os.path.isdir(cqlshlibdir):
146147
sys.path.insert(0, cqlshlibdir)
147148

148-
from cqlshlib import cql3handling, pylexotron, sslhandling, cqlshhandling, authproviderhandling
149+
from cqlshlib import cql3handling, pylexotron, sslhandling, cqlshhandling, authproviderhandling, legacydesc3x
149150
from cqlshlib.copyutil import ExportTask, ImportTask
150151
from cqlshlib.displaying import (ANSI_RESET, BLUE, COLUMN_NAME_COLORS, CYAN,
151152
RED, WHITE, FormattedValue, colorme)
@@ -155,7 +156,7 @@ def find_zip(libprefix):
155156
from cqlshlib.tracing import print_trace, print_trace_session
156157
from cqlshlib.util import get_file_encoding_bomsize, trim_if_present
157158
from cqlshlib.util import is_file_secure
158-
159+
from cqlshlib.legacydesc3x import *
159160

160161
DEFAULT_HOST = '127.0.0.1'
161162
DEFAULT_PORT = 9042
@@ -233,18 +234,17 @@ def find_zip(libprefix):
233234
opt_values = optparse.Values()
234235
(options, arguments) = parser.parse_args(sys.argv[1:], values=opt_values)
235236

236-
# WARN message for deprecated options
237+
# Raise Exception for deprecated options
237238
if hasattr(options, 'sigv4'):
238-
print('\nWarning: Specified sigv4 flag was deprecated, refer to cqlshrc file for sigv4 authentication'
239+
raise Exception('\nWarning: Specified sigv4 flag was deprecated, refer to cqlshrc file for sigv4 authentication'
239240
'https://cassandra.apache.org/_/blog/Apache-Cassandra-4.1-Features-Authentication-Plugin-Support-for-CQLSH.html')
240241

241242
if hasattr(options, 'auth_provider_name'):
242-
print('\nWarning: auth-provider flag was deprecated, refer to cqlshrc file for authentication'
243+
raise Exception('\nWarning: auth-provider flag was deprecated, refer to cqlshrc file for authentication'
243244
'https://cassandra.apache.org/_/blog/Apache-Cassandra-4.1-Features-Authentication-Plugin-Support-for-CQLSH.html')
244245

245246
# BEGIN history/config definition
246247

247-
248248
def mkdirp(path):
249249
"""Creates all parent directories up to path parameter or fails when path exists, but it is not a directory."""
250250

@@ -262,7 +262,6 @@ def resolve_cql_history_file():
262262
else:
263263
return default_cql_history
264264

265-
266265
HISTORY = resolve_cql_history_file()
267266
HISTORY_DIR = os.path.dirname(HISTORY)
268267

@@ -477,6 +476,12 @@ def __init__(self, hostname, port, color=False,
477476
self.tracing_enabled = tracing_enabled
478477
self.page_size = self.default_page_size
479478
self.expand_enabled = expand_enabled
479+
Keypsaces_profile = ExecutionProfile(
480+
consistency_level=cassandra.ConsistencyLevel.LOCAL_QUORUM,
481+
request_timeout=request_timeout,
482+
row_factory = ordered_dict_factory,
483+
load_balancing_policy=WhiteListRoundRobinPolicy([self.hostname]))
484+
480485
if use_conn:
481486
self.conn = use_conn
482487
else:
@@ -486,12 +491,13 @@ def __init__(self, hostname, port, color=False,
486491
self.conn = Cluster(contact_points=(self.hostname,), port=self.port, cql_version=cqlver,
487492
auth_provider=self.auth_provider,
488493
ssl_options=sslhandling.ssl_settings(hostname, CONFIG_FILE) if ssl else None,
489-
load_balancing_policy=WhiteListRoundRobinPolicy([self.hostname]),
490494
control_connection_timeout=connect_timeout,
491-
connect_timeout=connect_timeout,
495+
connect_timeout=connect_timeout,
496+
execution_profiles={EXEC_PROFILE_DEFAULT: Keypsaces_profile},
492497
**kwargs)
493498
self.owns_connection = not use_conn
494499

500+
495501
if keyspace:
496502
self.session = self.conn.connect(keyspace)
497503
else:
@@ -511,9 +517,11 @@ def __init__(self, hostname, port, color=False,
511517

512518
self.display_timezone = display_timezone
513519

514-
self.session.default_timeout = request_timeout
515-
self.session.row_factory = ordered_dict_factory
516-
self.session.default_consistency_level = cassandra.ConsistencyLevel.ONE
520+
##### These configurations are moved to EXEC_PROFILE_DEFAULT
521+
# self.session.default_timeout = request_timeout
522+
# self.session.row_factory = ordered_dict_factory
523+
# self.session.default_consistency_level = cassandra.ConsistencyLevel.ONE
524+
517525
self.get_connection_versions()
518526
self.set_expanded_cql_version(self.connection_versions['cql'])
519527

@@ -629,15 +637,6 @@ def get_connection_versions(self):
629637
}
630638
self.connection_versions = vers
631639

632-
def get_keyspace_names(self):
633-
return list(self.conn.metadata.keyspaces.keys())
634-
635-
def get_columnfamily_names(self, ksname=None):
636-
if ksname is None:
637-
ksname = self.current_keyspace
638-
639-
return list(self.get_keyspace_meta(ksname).tables.keys())
640-
641640
def get_materialized_view_names(self, ksname=None):
642641
if ksname is None:
643642
ksname = self.current_keyspace
@@ -1370,117 +1369,86 @@ def do_describe(self, parsed):
13701369
will not work in cqlsh connected to Cassandra < 4.0.In cqlsh-expansion we have modified code to support describe statements for
13711370
Cassandra 3.11
13721371
"""
1373-
try:
1372+
stmt = SimpleStatement(parsed.extract_orig(), consistency_level=cassandra.ConsistencyLevel.LOCAL_ONE, fetch_size=self.page_size if self.use_paging else None)
1373+
future = self.session.execute_async(stmt)
13741374

1375+
if self.connection_versions['build'][0] < '4':
1376+
try:
13751377
what = parsed.matched[1][1].lower()
13761378
if what == 'keyspaces':
1377-
self.describe_keyspaces()
1379+
describe_keyspaces_3x(self)
13781380
elif what == 'keyspace':
13791381
ksname = self.cql_unprotect_name(parsed.get_binding('ksname', ''))
13801382
if not ksname:
13811383
ksname = self.current_keyspace
13821384
if ksname is None:
13831385
self.printerr('Not in any keyspace.')
13841386
return
1385-
self.describe_keyspace(ksname)
1387+
describe_keyspace_3x(self, ksname)
13861388
elif what in ('columnfamily', 'table'):
13871389
ks = self.cql_unprotect_name(parsed.get_binding('ksname', None))
13881390
cf = self.cql_unprotect_name(parsed.get_binding('cfname'))
1389-
self.describe_columnfamily(ks, cf)
1391+
describe_columnfamily_3x(self, ks, cf)
13901392
elif what in ('columnfamilies', 'tables'):
1391-
self.describe_columnfamilies(self.current_keyspace)
1393+
describe_columnfamilies_3x(self, self.current_keyspace)
13921394
elif what == 'desc ':
1393-
self.describe_schema(False)
1395+
describe_schema_3x(self, False)
13941396
elif what == 'full' and parsed.matched[2][1].lower() == 'schema':
1395-
self.describe_schema(True)
1397+
describe_schema_3x(self, True)
13961398
elif what == 'cluster':
1397-
self.describe_cluster()
1399+
describe_cluster_3x(self)
13981400
elif what:
13991401
ks = self.cql_unprotect_name(parsed.get_binding('ksname', None))
14001402
name = self.cql_unprotect_name(parsed.get_binding('cfname'))
14011403
if not name:
14021404
name = self.cql_unprotect_name(parsed.get_binding('idxname', None))
14031405
if not name:
14041406
name = self.cql_unprotect_name(parsed.get_binding('mvname', None))
1405-
self.describe_object(ks, name)
1406-
1407-
except CQL_ERRORS as err:
1407+
describe_object_3x(self, ks, name)
1408+
except CQL_ERRORS as err:
14081409
err_msg = err.message if hasattr(err, 'message') else str(err)
14091410
self.printerr(err_msg.partition("message=")[2].strip('"'))
1410-
except Exception:
1411+
except Exception:
14111412
import traceback
14121413
self.printerr(traceback.format_exc())
14131414

1414-
do_desc = do_describe
1415+
else:
1416+
try:
1417+
result = future.result()
1418+
what = parsed.matched[1][1].lower()
1419+
1420+
if what in ('columnfamilies', 'tables', 'types', 'functions', 'aggregates'):
1421+
self.describe_list(result)
1422+
elif what == 'keyspaces':
1423+
self.describe_keyspaces(result)
1424+
elif what == 'cluster':
1425+
self.describe_cluster(result)
1426+
elif what:
1427+
self.describe_element(result)
1428+
1429+
except CQL_ERRORS as err:
1430+
err_msg = err.message if hasattr(err, 'message') else str(err)
1431+
self.printerr(err_msg.partition("message=")[2].strip('"'))
1432+
except Exception:
1433+
import traceback
1434+
self.printerr(traceback.format_exc())
14151435

1416-
def print_recreate_keyspace(self, ksdef, out):
1417-
out.write(ksdef.export_as_string())
1418-
out.write("\n")
1419-
1420-
def print_recreate_columnfamily(self, ksname, cfname, out):
1421-
"""
1422-
Output CQL commands which should be pasteable back into a CQL session
1423-
to recreate the given table.
1424-
Writes output to the given out stream.
1425-
"""
1426-
out.write(self.get_table_meta(ksname, cfname).export_as_string())
1427-
out.write("\n")
1428-
1429-
def print_recreate_object(self, ks, name, out):
1430-
"""
1431-
Output CQL commands which should be pasteable back into a CQL session
1432-
to recreate the given object (ks, table or index).
1433-
Writes output to the given out stream.
1434-
"""
1435-
out.write(self.get_object_meta(ks, name).export_as_string())
1436-
out.write("\n")
1436+
if future:
1437+
if future.warnings:
1438+
self.print_warnings(future.warnings)
1439+
1440+
1441+
do_desc = do_describe
14371442

1438-
def describe_keyspaces(self):
1443+
def describe_keyspaces(self, rows):
14391444
"""
14401445
Print the output for a DESCRIBE KEYSPACES query
14411446
"""
1442-
print('')
1443-
cmd.Cmd.columnize(self, self.get_keyspace_names())
1444-
print('')
1447+
names = [r['name'] for r in rows]
14451448

1446-
def describe_keyspace(self, ksname):
1447-
print
1448-
self.print_recreate_keyspace(self.get_keyspace_meta(ksname), sys.stdout)
1449-
print
1450-
1451-
def describe_columnfamily(self, ksname, cfname):
1452-
if ksname is None:
1453-
ksname = self.current_keyspace
1454-
if ksname is None:
1455-
raise NoKeyspaceError("No keyspace specified and no current keyspace")
1456-
print
1457-
self.print_recreate_columnfamily(ksname, cfname, sys.stdout)
1458-
print
1459-
1460-
def describe_columnfamilies(self, ksname):
1461-
print
1462-
if ksname is None:
1463-
for k in self.get_keyspaces():
1464-
name = k.name
1465-
print('Keyspace %s' % name)
1466-
print('---------%s' % ('-' * len(name)))
1467-
cmd.Cmd.columnize(self, self.get_columnfamily_names(k.name))
1468-
print
1469-
else:
1470-
cmd.Cmd.columnize(self, self.get_columnfamily_names(ksname))
1471-
print
1472-
1473-
def describe_schema(self, include_system=False):
1474-
print
1475-
for k in self.get_keyspaces():
1476-
if include_system or k.name not in cql3handling.SYSTEM_KEYSPACES:
1477-
self.print_recreate_keyspace(k, sys.stdout)
1478-
print
1479-
1480-
def describe_object(self, ks, name):
1481-
print
1482-
self.print_recreate_object(ks, name, sys.stdout)
1483-
print
1449+
print('')
1450+
cmd.Cmd.columnize(self, names)
1451+
print('')
14841452

14851453
def describe_list(self, rows):
14861454
"""
@@ -1518,19 +1486,22 @@ def describe_element(self, rows):
15181486
self.query_out.write(row['create_statement'])
15191487
print('')
15201488

1521-
def describe_cluster(self):
1522-
print('\nCluster: %s' % self.get_cluster_name())
1523-
p = trim_if_present(self.get_partitioner(), 'org.apache.cassandra.dht.')
1524-
print('Partitioner: %s\n' % p)
1525-
# TODO: snitch?
1526-
# snitch = trim_if_present(self.get_snitch(), 'org.apache.cassandra.locator.')
1527-
# print 'Snitch: %s\n' % snitch
1528-
if self.current_keyspace is not None and self.current_keyspace != 'system':
1529-
print("Range ownership:")
1530-
ring = self.get_ring(self.current_keyspace)
1531-
for entry in ring.items():
1532-
print(' %39s [%s]' % (str(entry[0].value), ', '.join([host.address for host in entry[1]])))
1533-
print
1489+
def describe_cluster(self, rows):
1490+
"""
1491+
Print the output for a DESCRIBE CLUSTER query.
1492+
1493+
If a specified keyspace was in use the returned ResultSet will contains a 'range_ownership' column,
1494+
otherwise not.
1495+
"""
1496+
for row in rows:
1497+
print('\nCluster: %s' % row['cluster'])
1498+
print('Partitioner: %s' % row['partitioner'])
1499+
print('Snitch: %s\n' % row['snitch'])
1500+
if 'range_ownership' in row:
1501+
print("Range ownership:")
1502+
for entry in list(row['range_ownership'].items()):
1503+
print(' %39s [%s]' % (entry[0], ', '.join([host for host in entry[1]])))
1504+
print('')
15341505

15351506
def do_copy(self, parsed):
15361507
r"""
@@ -1613,7 +1584,6 @@ def do_copy(self, parsed):
16131584
When entering CSV data on STDIN, you can use the sequence "\."
16141585
on a line by itself to end the data input.
16151586
"""
1616-
16171587
ks = self.cql_unprotect_name(parsed.get_binding('ksname', None))
16181588
if ks is None:
16191589
ks = self.current_keyspace

cqlsh-expansion/config/cqlshrc_template

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,11 @@
2828
;; module and class will be used to dynamically load the class
2929
;; all other properties found here and in the credentials file under the class name
3030
;; will be passed to the constructor
31-
#module = cassandra_sigv4.auth
32-
#classname = SigV4AuthProvider
33-
#region_name = us-east-1
34-
module = cassandra.auth
35-
classname = PlainTextAuthProvider
31+
module = cassandra_sigv4.auth
32+
classname = SigV4AuthProvider
33+
region_name = us-east-1
34+
; module = cassandra.auth
35+
; classname = PlainTextAuthProvider
3636

3737
[ui]
3838
;; Whether or not to display query results with colors
@@ -123,7 +123,7 @@ certfile = ~/.cassandra/sf-class2-root.crt
123123

124124
; this is effectively ignored from 4.1 included as TLS protocol is auto-negotiated and will
125125
; be removed in the next major version of Cassandra, possible values were TLSv1, TLSv1_1 or TLSv1_2
126-
;version =
126+
;version = TLSv1_2
127127

128128
;; Optional section, overrides default certfile in [ssl] section, if present
129129
; [certfiles]
-201 Bytes
Binary file not shown.
Binary file not shown.
-98.4 KB
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.

0 commit comments

Comments
 (0)