kaqing 2.0.188__py3-none-any.whl → 2.0.211__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of kaqing might be problematic. Click here for more details.
- adam/batch.py +7 -7
- adam/commands/app/utils_app.py +1 -1
- adam/commands/bash/bash.py +1 -1
- adam/commands/bash/utils_bash.py +1 -1
- adam/commands/cassandra/__init__.py +0 -0
- adam/commands/command.py +1 -1
- adam/commands/commands_utils.py +8 -13
- adam/commands/{alter_tables.py → cql/alter_tables.py} +1 -1
- adam/commands/cql/completions_c.py +1 -0
- adam/commands/cql/utils_cql.py +14 -13
- adam/commands/debug/__init__.py +0 -0
- adam/commands/debug/debug.py +22 -0
- adam/commands/debug/debug_completes.py +35 -0
- adam/commands/debug/debug_timings.py +35 -0
- adam/commands/devices/device.py +1 -1
- adam/commands/devices/devices.py +1 -1
- adam/commands/download_cassandra_log.py +45 -0
- adam/commands/export/export_databases.py +13 -8
- adam/commands/export/export_sessions.py +12 -11
- adam/commands/export/exporter.py +140 -53
- adam/commands/export/import_session.py +0 -4
- adam/commands/export/importer.py +11 -11
- adam/commands/export/importer_athena.py +15 -6
- adam/commands/export/importer_sqlite.py +19 -8
- adam/commands/export/utils_export.py +37 -15
- adam/commands/generate_report.py +52 -0
- adam/commands/medusa/medusa_restore.py +0 -16
- adam/commands/nodetool.py +1 -1
- adam/commands/os/__init__.py +0 -0
- adam/commands/postgres/postgres_databases.py +2 -3
- adam/commands/postgres/postgres_ls.py +1 -1
- adam/commands/postgres/utils_postgres.py +2 -1
- adam/commands/preview_table.py +1 -1
- adam/commands/restart_cluster.py +47 -0
- adam/commands/restart_node.py +51 -0
- adam/commands/restart_nodes.py +47 -0
- adam/commands/show/show_cassandra_status.py +3 -10
- adam/commands/show/show_cli_commands.py +1 -1
- adam/commands/show/show_processes.py +1 -1
- adam/commands/show/show_storage.py +2 -1
- adam/config.py +4 -6
- adam/embedded_params.py +1 -1
- adam/repl.py +5 -3
- adam/repl_commands.py +23 -17
- adam/repl_session.py +4 -3
- adam/repl_state.py +6 -0
- adam/sql/async_executor.py +44 -0
- adam/sql/lark_completer.py +6 -4
- adam/sql/qingl.lark +1076 -0
- adam/sso/cred_cache.py +2 -5
- adam/utils.py +206 -83
- adam/utils_k8s/app_clusters.py +11 -4
- adam/utils_k8s/app_pods.py +10 -5
- adam/utils_k8s/cassandra_clusters.py +8 -4
- adam/utils_k8s/cassandra_nodes.py +14 -5
- adam/utils_k8s/kube_context.py +1 -4
- adam/{pod_exec_result.py → utils_k8s/pod_exec_result.py} +8 -2
- adam/utils_k8s/pods.py +83 -24
- adam/utils_local.py +78 -2
- adam/utils_repl/repl_completer.py +10 -89
- adam/utils_sqlite.py +3 -8
- adam/version.py +1 -1
- {kaqing-2.0.188.dist-info → kaqing-2.0.211.dist-info}/METADATA +1 -1
- {kaqing-2.0.188.dist-info → kaqing-2.0.211.dist-info}/RECORD +67 -65
- adam/commands/cat.py +0 -36
- adam/commands/cd.py +0 -41
- adam/commands/download_file.py +0 -47
- adam/commands/find_files.py +0 -51
- adam/commands/find_processes.py +0 -76
- adam/commands/head.py +0 -36
- adam/commands/logs.py +0 -37
- adam/commands/ls.py +0 -41
- adam/commands/report.py +0 -61
- adam/commands/restart.py +0 -60
- adam/commands/shell.py +0 -41
- {kaqing-2.0.188.dist-info → kaqing-2.0.211.dist-info}/WHEEL +0 -0
- {kaqing-2.0.188.dist-info → kaqing-2.0.211.dist-info}/entry_points.txt +0 -0
- {kaqing-2.0.188.dist-info → kaqing-2.0.211.dist-info}/top_level.txt +0 -0
|
@@ -144,9 +144,8 @@ class PostgresDatabases:
|
|
|
144
144
|
env_prefix = f'PGPASSWORD="{password}"'
|
|
145
145
|
|
|
146
146
|
r = Pods.exec(pod_name, container_name, state.namespace, cmd, show_out=show_out, backgrounded=backgrounded, env_prefix=env_prefix)
|
|
147
|
-
if r and
|
|
148
|
-
|
|
149
|
-
ReplSession().prompt_session.history.append_string(f'@{r.pod} cat {r.log_file}')
|
|
147
|
+
if r and r.log_file:
|
|
148
|
+
ReplSession().append_history(f':cat {r.log_file}')
|
|
150
149
|
|
|
151
150
|
return r
|
|
152
151
|
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import functools
|
|
2
2
|
|
|
3
3
|
from adam.commands.postgres.postgres_databases import PostgresDatabases, pg_path
|
|
4
|
+
from adam.config import Config
|
|
4
5
|
from adam.repl_state import ReplState
|
|
5
6
|
from adam.utils import log2, wait_log
|
|
6
7
|
from adam.utils_k8s.pods import Pods
|
|
@@ -62,7 +63,7 @@ class PostgresPodService:
|
|
|
62
63
|
if isinstance(args, list):
|
|
63
64
|
query = ' '.join(args)
|
|
64
65
|
|
|
65
|
-
PostgresDatabases.run_sql(state, query, backgrounded=backgrounded)
|
|
66
|
+
PostgresDatabases.run_sql(state, query, show_out=Config().is_debug(), backgrounded=backgrounded)
|
|
66
67
|
|
|
67
68
|
class PostgresExecHandler:
|
|
68
69
|
def __init__(self, state: ReplState, backgrounded=False):
|
adam/commands/preview_table.py
CHANGED
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
from adam.commands import extract_options
|
|
2
|
+
from adam.commands.command import Command
|
|
3
|
+
from adam.utils_k8s.pods import Pods
|
|
4
|
+
from adam.utils_k8s.statefulsets import StatefulSets
|
|
5
|
+
from adam.repl_state import ReplState, RequiredState
|
|
6
|
+
from adam.utils import log2
|
|
7
|
+
|
|
8
|
+
class RestartCluster(Command):
|
|
9
|
+
COMMAND = 'restart cluster'
|
|
10
|
+
|
|
11
|
+
# the singleton pattern
|
|
12
|
+
def __new__(cls, *args, **kwargs):
|
|
13
|
+
if not hasattr(cls, 'instance'): cls.instance = super(RestartCluster, cls).__new__(cls)
|
|
14
|
+
|
|
15
|
+
return cls.instance
|
|
16
|
+
|
|
17
|
+
def __init__(self, successor: Command=None):
|
|
18
|
+
super().__init__(successor)
|
|
19
|
+
|
|
20
|
+
def command(self):
|
|
21
|
+
return RestartCluster.COMMAND
|
|
22
|
+
|
|
23
|
+
def required(self):
|
|
24
|
+
return RequiredState.CLUSTER
|
|
25
|
+
|
|
26
|
+
def run(self, cmd: str, state: ReplState):
|
|
27
|
+
if not(args := self.args(cmd)):
|
|
28
|
+
return super().run(cmd, state)
|
|
29
|
+
|
|
30
|
+
with self.validate(args, state) as (args, state):
|
|
31
|
+
with extract_options(args, '--force') as (args, forced):
|
|
32
|
+
if not forced:
|
|
33
|
+
log2('Please add --force for restarting all nodes in a cluster.')
|
|
34
|
+
|
|
35
|
+
return 'force-needed'
|
|
36
|
+
|
|
37
|
+
log2(f'Restarting all pods from {state.sts}...')
|
|
38
|
+
for pod_name in StatefulSets.pod_names(state.sts, state.namespace):
|
|
39
|
+
Pods.delete(pod_name, state.namespace)
|
|
40
|
+
|
|
41
|
+
return state
|
|
42
|
+
|
|
43
|
+
def completion(self, state: ReplState):
|
|
44
|
+
return super().completion(state, {'--force': None})
|
|
45
|
+
|
|
46
|
+
def help(self, _: ReplState):
|
|
47
|
+
return f"{RestartCluster.COMMAND} --force\t restart all the nodes in the cluster"
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from adam.commands import extract_options
|
|
2
|
+
from adam.commands.command import Command
|
|
3
|
+
from adam.commands.devices.devices import Devices
|
|
4
|
+
from adam.utils_k8s.pods import Pods
|
|
5
|
+
from adam.repl_state import ReplState, RequiredState
|
|
6
|
+
from adam.utils import log2
|
|
7
|
+
|
|
8
|
+
class RestartNode(Command):
|
|
9
|
+
COMMAND = 'restart node'
|
|
10
|
+
|
|
11
|
+
# the singleton pattern
|
|
12
|
+
def __new__(cls, *args, **kwargs):
|
|
13
|
+
if not hasattr(cls, 'instance'): cls.instance = super(RestartNode, cls).__new__(cls)
|
|
14
|
+
|
|
15
|
+
return cls.instance
|
|
16
|
+
|
|
17
|
+
def __init__(self, successor: Command=None):
|
|
18
|
+
super().__init__(successor)
|
|
19
|
+
|
|
20
|
+
def command(self):
|
|
21
|
+
return RestartNode.COMMAND
|
|
22
|
+
|
|
23
|
+
def required(self):
|
|
24
|
+
return RequiredState.POD
|
|
25
|
+
|
|
26
|
+
def run(self, cmd: str, state: ReplState):
|
|
27
|
+
if not(args := self.args(cmd)):
|
|
28
|
+
return super().run(cmd, state)
|
|
29
|
+
|
|
30
|
+
with self.validate(args, state) as (args, state):
|
|
31
|
+
if not state.pod:
|
|
32
|
+
log2("'pod' is required")
|
|
33
|
+
|
|
34
|
+
return 'pod-needed'
|
|
35
|
+
|
|
36
|
+
with extract_options(args, '--force') as (args, forced):
|
|
37
|
+
if not forced:
|
|
38
|
+
log2('Please add --force for restarting pod.')
|
|
39
|
+
|
|
40
|
+
return 'force-needed'
|
|
41
|
+
|
|
42
|
+
log2(f'Restarting {state.pod}...')
|
|
43
|
+
Pods.delete(state.pod, state.namespace)
|
|
44
|
+
|
|
45
|
+
return state
|
|
46
|
+
|
|
47
|
+
def completion(self, state: ReplState):
|
|
48
|
+
return super().completion(state, {'--force': None}, pods=Devices.of(state).pods(state, '-'))
|
|
49
|
+
|
|
50
|
+
def help(self, _: ReplState):
|
|
51
|
+
return f"{RestartNode.COMMAND} --force\t restart the node"
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
from adam.commands import extract_options, validate_args
|
|
2
|
+
from adam.commands.command import Command
|
|
3
|
+
from adam.utils_k8s.pods import Pods
|
|
4
|
+
from adam.utils_k8s.statefulsets import StatefulSets
|
|
5
|
+
from adam.repl_state import ReplState, RequiredState
|
|
6
|
+
from adam.utils import log2
|
|
7
|
+
|
|
8
|
+
class RestartNodes(Command):
|
|
9
|
+
COMMAND = 'restart nodes'
|
|
10
|
+
|
|
11
|
+
# the singleton pattern
|
|
12
|
+
def __new__(cls, *args, **kwargs):
|
|
13
|
+
if not hasattr(cls, 'instance'): cls.instance = super(RestartNodes, cls).__new__(cls)
|
|
14
|
+
|
|
15
|
+
return cls.instance
|
|
16
|
+
|
|
17
|
+
def __init__(self, successor: Command=None):
|
|
18
|
+
super().__init__(successor)
|
|
19
|
+
|
|
20
|
+
def command(self):
|
|
21
|
+
return RestartNodes.COMMAND
|
|
22
|
+
|
|
23
|
+
def required(self):
|
|
24
|
+
return RequiredState.CLUSTER
|
|
25
|
+
|
|
26
|
+
def run(self, cmd: str, state: ReplState):
|
|
27
|
+
if not(args := self.args(cmd)):
|
|
28
|
+
return super().run(cmd, state)
|
|
29
|
+
|
|
30
|
+
with self.validate(args, state, apply=False) as (args, state):
|
|
31
|
+
with extract_options(args, '--force') as (args, forced):
|
|
32
|
+
with validate_args(args, state, name='pod name'):
|
|
33
|
+
if not forced:
|
|
34
|
+
log2('Please add --force for restarting nodes.')
|
|
35
|
+
|
|
36
|
+
return 'force-needed'
|
|
37
|
+
|
|
38
|
+
for arg in args:
|
|
39
|
+
Pods.delete(arg, state.namespace)
|
|
40
|
+
|
|
41
|
+
return state
|
|
42
|
+
|
|
43
|
+
def completion(self, state: ReplState):
|
|
44
|
+
return super().completion(state, lambda: {p: {'--force': None} for p in StatefulSets.pod_names(state.sts, state.namespace)})
|
|
45
|
+
|
|
46
|
+
def help(self, _: ReplState):
|
|
47
|
+
return f"{RestartNodes.COMMAND} <pod-name>... --force\t restart Cassandra nodes"
|
|
@@ -8,13 +8,14 @@ from adam.checks.gossip import Gossip
|
|
|
8
8
|
from adam.columns.columns import Columns
|
|
9
9
|
from adam.commands import extract_options, extract_trailing_options
|
|
10
10
|
from adam.commands.command import Command
|
|
11
|
+
from adam.commands.commands_utils import write_to_kaqing_log_file
|
|
11
12
|
from adam.commands.cql.utils_cql import cassandra
|
|
12
13
|
from adam.config import Config
|
|
13
14
|
from adam.repl_session import ReplSession
|
|
14
15
|
from adam.utils_issues import IssuesUtils
|
|
15
16
|
from adam.utils_k8s.statefulsets import StatefulSets
|
|
16
17
|
from adam.repl_state import ReplState, RequiredState
|
|
17
|
-
from adam.utils import SORT, tabulize, log2, log_exc
|
|
18
|
+
from adam.utils import SORT, log_dir, tabulize, log2, log_exc
|
|
18
19
|
from adam.checks.status import parse_nodetool_status
|
|
19
20
|
|
|
20
21
|
class ShowCassandraStatus(Command):
|
|
@@ -105,15 +106,7 @@ class ShowCassandraStatus(Command):
|
|
|
105
106
|
r = tabulize(status, lambda s: ','.join([c.host_value(check_results, s) for c in columns]), header=header, separator=',', sorted=SORT, to = 0 if backgrounded else 1)
|
|
106
107
|
|
|
107
108
|
if backgrounded:
|
|
108
|
-
|
|
109
|
-
log_file = f'{log_prefix}-{datetime.now().strftime("%d%H%M%S")}.log'
|
|
110
|
-
|
|
111
|
-
with open(log_file, 'w') as f:
|
|
112
|
-
f.write(r)
|
|
113
|
-
|
|
114
|
-
ReplSession().append_history(f':sh cat {log_file}')
|
|
115
|
-
|
|
116
|
-
r = log_file
|
|
109
|
+
r = write_to_kaqing_log_file(r)
|
|
117
110
|
|
|
118
111
|
IssuesUtils.show(check_results)
|
|
119
112
|
|
|
@@ -22,7 +22,7 @@ class ShowKubectlCommands(Command):
|
|
|
22
22
|
return RequiredState.CLUSTER_OR_POD
|
|
23
23
|
|
|
24
24
|
def run(self, cmd: str, state: ReplState):
|
|
25
|
-
if not self.args(cmd):
|
|
25
|
+
if not (args := self.args(cmd)):
|
|
26
26
|
return super().run(cmd, state)
|
|
27
27
|
|
|
28
28
|
with self.validate(args, state) as (args, state):
|
|
@@ -37,7 +37,7 @@ class ShowProcesses(Command):
|
|
|
37
37
|
header = Config().get('processes-qing.header', 'POD_NAME,Q_CPU/TOTAL,MEM/LIMIT')
|
|
38
38
|
|
|
39
39
|
with cassandra(state) as pods:
|
|
40
|
-
pods.display_table(cols, header, show_out=show_out, backgrounded=backgrounded)
|
|
40
|
+
pods.display_table(cols, header, show_out=show_out, backgrounded=backgrounded, msg='Checking processes')
|
|
41
41
|
|
|
42
42
|
return state
|
|
43
43
|
|
|
@@ -3,6 +3,7 @@ from adam.commands.command import Command
|
|
|
3
3
|
from adam.commands.cql.utils_cql import cassandra
|
|
4
4
|
from adam.config import Config
|
|
5
5
|
from adam.repl_state import ReplState, RequiredState
|
|
6
|
+
from adam.utils import ing
|
|
6
7
|
|
|
7
8
|
class ShowStorage(Command):
|
|
8
9
|
COMMAND = 'show storage'
|
|
@@ -32,7 +33,7 @@ class ShowStorage(Command):
|
|
|
32
33
|
cols = Config().get('storage.columns', 'pod,volume_root,volume_cassandra,snapshots,data,compactions')
|
|
33
34
|
header = Config().get('storage.header', 'POD_NAME,VOLUME /,VOLUME CASS,SNAPSHOTS,DATA,COMPACTIONS')
|
|
34
35
|
with cassandra(state) as pods:
|
|
35
|
-
pods.display_table(cols, header, show_out=show_out, backgrounded=backgrounded)
|
|
36
|
+
pods.display_table(cols, header, show_out=show_out, backgrounded=backgrounded, msg='Checking storage')
|
|
36
37
|
|
|
37
38
|
return state
|
|
38
39
|
|
adam/config.py
CHANGED
|
@@ -3,17 +3,13 @@ from typing import TypeVar, cast
|
|
|
3
3
|
import yaml
|
|
4
4
|
|
|
5
5
|
from . import __version__
|
|
6
|
-
from adam.utils import
|
|
6
|
+
from adam.utils import ConfigHolder, ConfigReadable, copy_config_file, get_deep_keys, log2
|
|
7
7
|
|
|
8
8
|
T = TypeVar('T')
|
|
9
9
|
|
|
10
|
-
class Config:
|
|
10
|
+
class Config(ConfigReadable):
|
|
11
11
|
EMBEDDED_PARAMS = {}
|
|
12
12
|
|
|
13
|
-
LogConfig.is_debug = lambda: Config().is_debug()
|
|
14
|
-
LogConfig.is_debug_complete = lambda: Config().get('debugs.complete', False)
|
|
15
|
-
LogConfig.is_debug_timing = lambda: Config().get('debugs.timings', False)
|
|
16
|
-
|
|
17
13
|
# the singleton pattern
|
|
18
14
|
def __new__(cls, *args, **kwargs):
|
|
19
15
|
if not hasattr(cls, 'instance'): cls.instance = super(Config, cls).__new__(cls)
|
|
@@ -29,6 +25,8 @@ class Config:
|
|
|
29
25
|
except:
|
|
30
26
|
with open(copy_config_file(f'params.yaml.{__version__}', 'adam.embedded_params', show_out=not is_user_entry)) as f:
|
|
31
27
|
self.params = cast(dict[str, any], yaml.safe_load(f))
|
|
28
|
+
|
|
29
|
+
ConfigHolder().config = self
|
|
32
30
|
elif not hasattr(self, 'params'):
|
|
33
31
|
with open(copy_config_file(f'params.yaml.{__version__}', 'adam.embedded_params', show_out=not is_user_entry)) as f:
|
|
34
32
|
self.params = cast(dict[str, any], yaml.safe_load(f))
|
adam/embedded_params.py
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
def config():
|
|
2
|
-
return {'app': {'console-endpoint': 'https://{host}/{env}/{app}/static/console/index.html', 'container-name': 'c3-server', 'cr': {'cluster-regex': '(.*?-.*?)-.*', 'group': 'ops.c3.ai', 'v': 'v2', 'plural': 'c3cassandras'}, 'label': 'c3__app_id-0', 'login': {'admin-group': '{host}/C3.ClusterAdmin', 'ingress': '{app_id}-k8singr-appleader-001', 'timeout': 5, 'session-check-url': 'https://{host}/{env}/{app}/api/8/C3/userSessionToken', 'cache-creds': True, 'cache-username': True, 'url': 'https://{host}/{env}/{app}', 'another': "You're logged in to {has}. However, for this app, you need to log in to {need}.", 'token-server-url': 'http://localhost:{port}', 'password-max-length': 128}, 'strip': '0'}, 'audit': {'endpoint': 'https://4psvtaxlcb.execute-api.us-west-2.amazonaws.com/prod/', 'workers': 3, 'timeout': 10, 'log-audit-queries': False, 'athena': {'auto-repair': {'elapsed_hours': 12}, 'region': 'us-west-2', 'catalog': 'AwsDataCatalog', 'database': 'audit', 'repair-partition-tables': 'audit', 'output': 's3://s3.ops--audit/ddl/results', 'repair-cluster-tables': 'cluster'}, 'queries': {'last10': "SELECT * FROM audit\nWHERE drive <> 'z' and ({date_condition})\nORDER BY ts DESC LIMIT {limit}", 'slow10': "SELECT * FROM audit\nWHERE drive <> 'z' and ({date_condition})\nORDER BY CAST(duration AS REAL) DESC LIMIT {limit}", 'top10': "SELECT min(c) AS cluster, line, COUNT(*) AS cnt, avg(CAST(duration AS REAL)) AS duration\nFROM audit WHERE drive <> 'z' and ({date_condition})\nGROUP BY line ORDER BY cnt DESC LIMIT {limit}"}}, 'bash': {'workers': 32}, 'cassandra': {'service-name': 'all-pods-service'}, 'cql': {'workers': 32, 'samples': 3, 'secret': {'cluster-regex': '(.*?-.*?)-.*', 'name': '{cluster}-superuser', 'password-item': 'password'}, 'alter-tables': {'excludes': 'system_auth,system_traces,reaper_db,system_distributed,system_views,system,system_schema,system_virtual_schema', 'gc-grace-periods': '3600,86400,864000,7776000', 'batching': True}}, 'checks': {'compactions-threshold': 250, 'cpu-busy-threshold': 98.0, 'cpu-threshold': 0.0, 'cassandra-data-path': '/c3/cassandra', 'root-disk-threshold': 50, 'cassandra-disk-threshold': 50, 'snapshot-size-cmd': "ls /c3/cassandra/data/data/*/*/snapshots | grep snapshots | sed 's/:$//g' | xargs -I {} du -sk {} | awk '{print $1}' | awk '{s+=$1} END {print s}'", 'snapshot-size-threshold': '40G', 'table-sizes-cmd': "ls -Al /c3/cassandra/data/data/ | awk '{print $9}' | sed 's/\\^r//g' | xargs -I {} du -sk /c3/cassandra/data/data/{}"}, 'download': {'workers': 8}, 'export': {'workers': 8, 'csv_dir': '/c3/cassandra/tmp', 'column_counts_query': 'select id, count(id) as columns from {table} group by id order by columns desc limit 10', 'default-importer': 'sqlite', 'sqlite': {'workers': 8, 'columns': '<row-key>', 'local-db-dir': '/tmp/qing-db'}, 'athena': {'workers': 8, 'columns': '<keys>', 'bucket': 'c3.ops--qing'}, 'csv': {'workers': 8, 'columns': '<row-key>'}, 'log-
|
|
2
|
+
return {'app': {'console-endpoint': 'https://{host}/{env}/{app}/static/console/index.html', 'container-name': 'c3-server', 'cr': {'cluster-regex': '(.*?-.*?)-.*', 'group': 'ops.c3.ai', 'v': 'v2', 'plural': 'c3cassandras'}, 'label': 'c3__app_id-0', 'login': {'admin-group': '{host}/C3.ClusterAdmin', 'ingress': '{app_id}-k8singr-appleader-001', 'timeout': 5, 'session-check-url': 'https://{host}/{env}/{app}/api/8/C3/userSessionToken', 'cache-creds': True, 'cache-username': True, 'url': 'https://{host}/{env}/{app}', 'another': "You're logged in to {has}. However, for this app, you need to log in to {need}.", 'token-server-url': 'http://localhost:{port}', 'password-max-length': 128}, 'strip': '0'}, 'audit': {'endpoint': 'https://4psvtaxlcb.execute-api.us-west-2.amazonaws.com/prod/', 'workers': 3, 'timeout': 10, 'log-audit-queries': False, 'athena': {'auto-repair': {'elapsed_hours': 12}, 'region': 'us-west-2', 'catalog': 'AwsDataCatalog', 'database': 'audit', 'repair-partition-tables': 'audit', 'output': 's3://s3.ops--audit/ddl/results', 'repair-cluster-tables': 'cluster'}, 'queries': {'last10': "SELECT * FROM audit\nWHERE drive <> 'z' and ({date_condition})\nORDER BY ts DESC LIMIT {limit}", 'slow10': "SELECT * FROM audit\nWHERE drive <> 'z' and ({date_condition})\nORDER BY CAST(duration AS REAL) DESC LIMIT {limit}", 'top10': "SELECT min(c) AS cluster, line, COUNT(*) AS cnt, avg(CAST(duration AS REAL)) AS duration\nFROM audit WHERE drive <> 'z' and ({date_condition})\nGROUP BY line ORDER BY cnt DESC LIMIT {limit}"}}, 'bash': {'workers': 32}, 'cassandra': {'service-name': 'all-pods-service'}, 'cql': {'workers': 32, 'samples': 3, 'secret': {'cluster-regex': '(.*?-.*?)-.*', 'name': '{cluster}-superuser', 'password-item': 'password'}, 'alter-tables': {'excludes': 'system_auth,system_traces,reaper_db,system_distributed,system_views,system,system_schema,system_virtual_schema', 'gc-grace-periods': '3600,86400,864000,7776000', 'batching': True}}, 'checks': {'compactions-threshold': 250, 'cpu-busy-threshold': 98.0, 'cpu-threshold': 0.0, 'cassandra-data-path': '/c3/cassandra', 'root-disk-threshold': 50, 'cassandra-disk-threshold': 50, 'snapshot-size-cmd': "ls /c3/cassandra/data/data/*/*/snapshots | grep snapshots | sed 's/:$//g' | xargs -I {} du -sk {} | awk '{print $1}' | awk '{s+=$1} END {print s}'", 'snapshot-size-threshold': '40G', 'table-sizes-cmd': "ls -Al /c3/cassandra/data/data/ | awk '{print $9}' | sed 's/\\^r//g' | xargs -I {} du -sk /c3/cassandra/data/data/{}"}, 'download': {'workers': 8}, 'export': {'workers': 8, 'csv_dir': '/c3/cassandra/tmp', 'column_counts_query': 'select id, count(id) as columns from {table} group by id order by columns desc limit 10', 'default-importer': 'sqlite', 'sqlite': {'workers': 8, 'columns': '<row-key>', 'local-db-dir': '/tmp/qing-db/q/export/db'}, 'athena': {'workers': 8, 'columns': '<keys>', 'bucket': 'c3.ops--qing'}, 'csv': {'workers': 8, 'columns': '<row-key>'}, 'log-dir': '/tmp/qing-db/q/export/logs'}, 'get-host-id': {'workers': 32}, 'idps': {'ad': {'email-pattern': '.*@c3.ai', 'uri': 'https://login.microsoftonline.com/53ad779a-93e7-485c-ba20-ac8290d7252b/oauth2/v2.0/authorize?response_type=id_token&response_mode=form_post&client_id=00ff94a8-6b0a-4715-98e0-95490012d818&scope=openid+email+profile&redirect_uri=https%3A%2F%2Fplat.c3ci.cloud%2Fc3%2Fc3%2Foidc%2Flogin&nonce={nonce}&state=EMPTY', 'jwks-uri': 'https://login.microsoftonline.com/common/discovery/keys', 'contact': 'Please contact ted.tran@c3.ai.', 'whitelist-file': '/kaqing/members'}, 'okta': {'default': True, 'email-pattern': '.*@c3iot.com', 'uri': 'https://c3energy.okta.com/oauth2/v1/authorize?response_type=id_token&response_mode=form_post&client_id={client_id}&scope=openid+email+profile+groups&redirect_uri=https%3A%2F%2F{host}%2Fc3%2Fc3%2Foidc%2Flogin&nonce={nonce}&state=EMPTY', 'jwks-uri': 'https://c3energy.okta.com/oauth2/v1/keys'}}, 'issues': {'workers': 32}, 'local-qing-dir': '/tmp/qing-db/q', 'local-downloads-dir': '/tmp/qing-db/q/downloads', 'logs': {'path': '/c3/cassandra/logs/system.log'}, 'log-dir': '/tmp/qing-db/q/logs', 'nodetool': {'workers': 96, 'commands_in_line': 40, 'status': {'workers': 32, 'samples': 3, 'commands_in_line': 40}}, 'pg': {'name-pattern': '^{namespace}.*-k8spg-.*', 'excludes': '.helm., -admin-secret', 'agent': {'name': 'ops-pg-agent', 'just-in-time': False, 'timeout': 86400, 'image': 'seanahnsf/kaqing'}, 'default-db': 'postgres', 'default-schema': 'postgres', 'secret': {'endpoint-key': 'postgres-db-endpoint', 'port-key': 'postgres-db-port', 'username-key': 'postgres-admin-username', 'password-key': 'postgres-admin-password'}}, 'pod': {'name': 'ops', 'image': 'seanahnsf/kaqing-cloud', 'sa': {'name': 'ops', 'proto': 'c3', 'additional-cluster-roles': 'c3aiops-k8ssandra-operator'}, 'label-selector': 'run=ops'}, 'preview': {'rows': 10}, 'processes': {'columns': 'pod,cpu-metrics,mem', 'header': 'POD_NAME,M_CPU(USAGE/LIMIT),MEM/LIMIT'}, 'processes-qing': {'columns': 'pod,cpu,mem', 'header': 'POD_NAME,Q_CPU/TOTAL,MEM/LIMIT'}, 'reaper': {'service-name': 'reaper-service', 'port-forward': {'timeout': 86400, 'local-port': 9001}, 'abort-runs-batch': 10, 'show-runs-batch': 100, 'pod': {'cluster-regex': '(.*?-.*?-.*?-.*?)-.*', 'label-selector': 'k8ssandra.io/reaper={cluster}-reaper'}, 'secret': {'cluster-regex': '(.*?-.*?)-.*', 'name': '{cluster}-reaper-ui', 'password-item': 'password'}}, 'repair': {'log-path': '/home/cassrepair/logs/', 'image': 'ci-registry.c3iot.io/cloudops/cassrepair:2.0.14', 'secret': 'ciregistryc3iotio', 'env': {'interval': 24, 'timeout': 60, 'pr': False, 'runs': 1}}, 'repl': {'start-drive': 'c', 'a': {'auto-enter': 'c3/c3'}, 'c': {'auto-enter': 'cluster'}, 'x': {'auto-enter': 'latest'}, 'history': {'push-cat-log-file': True, 'push-cat-remote-log-file': True}, 'background-process': {'auto-nohup': True}}, 'status': {'columns': 'status,address,load,tokens,owns,host_id,gossip,compactions', 'header': '--,Address,Load,Tokens,Owns,Host ID,GOSSIP,COMPACTIONS'}, 'storage': {'columns': 'pod,volume_root,volume_cassandra,snapshots,data,compactions', 'header': 'POD_NAME,VOLUME /,VOLUME CASS,SNAPSHOTS,DATA,COMPACTIONS'}, 'watch': {'auto': 'rollout', 'timeout': 3600, 'interval': 10}, 'auto-complete': {'c': {'tables': 'lazy'}, 'x': {'tables': 'lazy'}, 'cli': {'cp': 'jit'}, 'export': {'databases': 'jit'}, 'medusa': {'backups': 'jit'}, 'reaper': {'schedules': 'jit'}}, 'debug': False, 'debugs': {'complete': False, 'timings': False, 'exit-on-error': False}}
|
adam/repl.py
CHANGED
|
@@ -16,7 +16,7 @@ from adam.log import Log
|
|
|
16
16
|
from adam.repl_commands import ReplCommands
|
|
17
17
|
from adam.repl_session import ReplSession
|
|
18
18
|
from adam.repl_state import ReplState
|
|
19
|
-
from adam.utils import clear_wait_log_flag, debug_trace, deep_sort_dict, tabulize, log2, log_exc, log_timing
|
|
19
|
+
from adam.utils import CommandLog, clear_wait_log_flag, debug_trace, deep_sort_dict, tabulize, log2, log_exc, log_timing
|
|
20
20
|
from adam.apps import Apps
|
|
21
21
|
from adam.utils_repl.repl_completer import ReplCompleter, merge_completions
|
|
22
22
|
from . import __version__
|
|
@@ -43,7 +43,7 @@ def enter_repl(state: ReplState):
|
|
|
43
43
|
|
|
44
44
|
Log.log2(f'kaqing {__version__}')
|
|
45
45
|
|
|
46
|
-
Devices.
|
|
46
|
+
Devices.of(state).enter(state)
|
|
47
47
|
|
|
48
48
|
kb = KeyBindings()
|
|
49
49
|
|
|
@@ -142,8 +142,10 @@ def enter_repl(state: ReplState):
|
|
|
142
142
|
if cmd and (state.device != ReplState.L or Config().get('audit.log-audit-queries', False)):
|
|
143
143
|
exec.submit(Audits.log, cmd, state.namespace, state.device, time.time() - s0, get_audit_extra(result))
|
|
144
144
|
|
|
145
|
+
CommandLog.close_log_file()
|
|
146
|
+
|
|
145
147
|
def try_device_default_action(state: ReplState, cmds: Command, cmd_list: list[Command], cmd: str):
|
|
146
|
-
action_taken, result = Devices.
|
|
148
|
+
action_taken, result = Devices.of(state).try_fallback_action(cmds, state, cmd)
|
|
147
149
|
|
|
148
150
|
if not action_taken:
|
|
149
151
|
log2(f'* Invalid command: {cmd}')
|
adam/repl_commands.py
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
|
-
from adam.commands.alter_tables import AlterTables
|
|
2
1
|
from adam.commands.app.app import App
|
|
3
2
|
from adam.commands.app.app_ping import AppPing
|
|
4
3
|
from adam.commands.app.show_app_actions import ShowAppActions
|
|
5
4
|
from adam.commands.app.show_app_id import ShowAppId
|
|
6
5
|
from adam.commands.app.show_app_queues import ShowAppQueues
|
|
7
6
|
from adam.commands.audit.audit import Audit
|
|
8
|
-
from adam.commands.cat import Cat
|
|
7
|
+
from adam.commands.fs.cat import Cat
|
|
9
8
|
from adam.commands.code import Code
|
|
10
|
-
from adam.commands.
|
|
9
|
+
from adam.commands.cql.alter_tables import AlterTables
|
|
10
|
+
from adam.commands.debug.debug import Debug
|
|
11
|
+
from adam.commands.download_cassandra_log import DownloadCassandraLog
|
|
12
|
+
from adam.commands.fs.cat_local import CatLocal
|
|
13
|
+
from adam.commands.fs.download_file import DownloadFile
|
|
11
14
|
from adam.commands.deploy.code_start import CodeStart
|
|
12
15
|
from adam.commands.deploy.code_stop import CodeStop
|
|
13
16
|
from adam.commands.deploy.deploy import Deploy
|
|
@@ -38,14 +41,17 @@ from adam.commands.export.show_column_counts import ShowColumnCounts
|
|
|
38
41
|
from adam.commands.export.show_export_databases import ShowExportDatabases
|
|
39
42
|
from adam.commands.export.show_export_session import ShowExportSession
|
|
40
43
|
from adam.commands.export.show_export_sessions import ShowExportSessions
|
|
41
|
-
from adam.commands.find_files import FindLocalFiles
|
|
42
|
-
from adam.commands.find_processes import FindProcesses
|
|
43
|
-
from adam.commands.head import Head
|
|
44
|
+
from adam.commands.fs.find_files import FindLocalFiles
|
|
45
|
+
from adam.commands.fs.find_processes import FindProcesses
|
|
46
|
+
from adam.commands.fs.head import Head
|
|
47
|
+
from adam.commands.fs.ls_local import LsLocal
|
|
44
48
|
from adam.commands.kubectl import Kubectl
|
|
45
|
-
from adam.commands.
|
|
49
|
+
from adam.commands.restart_cluster import RestartCluster
|
|
50
|
+
from adam.commands.restart_node import RestartNode
|
|
51
|
+
from adam.commands.fs.shell import Shell
|
|
46
52
|
from adam.commands.clipboard_copy import ClipboardCopy
|
|
47
53
|
from adam.commands.bash.bash import Bash
|
|
48
|
-
from adam.commands.cd import Cd
|
|
54
|
+
from adam.commands.fs.cd import Cd
|
|
49
55
|
from adam.commands.check import Check
|
|
50
56
|
from adam.commands.command import Command
|
|
51
57
|
from adam.commands.cql.cqlsh import Cqlsh
|
|
@@ -53,15 +59,15 @@ from adam.commands.exit import Exit
|
|
|
53
59
|
from adam.commands.medusa.medusa import Medusa
|
|
54
60
|
from adam.commands.param_get import GetParam
|
|
55
61
|
from adam.commands.issues import Issues
|
|
56
|
-
from adam.commands.ls import Ls
|
|
62
|
+
from adam.commands.fs.ls import Ls
|
|
57
63
|
from adam.commands.nodetool import NodeTool
|
|
58
64
|
from adam.commands.postgres.postgres import Postgres, PostgresPg
|
|
59
65
|
from adam.commands.preview_table import PreviewTable
|
|
60
66
|
from adam.commands.pwd import Pwd
|
|
61
67
|
from adam.commands.reaper.reaper import Reaper
|
|
62
68
|
from adam.commands.repair.repair import Repair
|
|
63
|
-
from adam.commands.
|
|
64
|
-
from adam.commands.
|
|
69
|
+
from adam.commands.generate_report import GenerateReport
|
|
70
|
+
from adam.commands.restart_nodes import RestartNodes
|
|
65
71
|
from adam.commands.rollout import RollOut
|
|
66
72
|
from adam.commands.param_set import SetParam
|
|
67
73
|
from adam.commands.show.show import Show
|
|
@@ -82,7 +88,7 @@ class ReplCommands:
|
|
|
82
88
|
cmds: list[Command] = ReplCommands.navigation() + ReplCommands.cassandra_ops() + ReplCommands.postgres_ops() + \
|
|
83
89
|
ReplCommands.app_ops() + ReplCommands.audit_ops() + ReplCommands.export_ops() + ReplCommands.tools() + ReplCommands.exit()
|
|
84
90
|
|
|
85
|
-
intermediate_cmds: list[Command] = [App(), Audit(), Reaper(), Repair(), Deploy(), Show(), Undeploy()]
|
|
91
|
+
intermediate_cmds: list[Command] = [App(), Audit(), Reaper(), Repair(), Debug(), Deploy(), Show(), Undeploy()]
|
|
86
92
|
ic = [c.command() for c in intermediate_cmds]
|
|
87
93
|
# 1. dedup commands
|
|
88
94
|
deduped = []
|
|
@@ -99,18 +105,18 @@ class ReplCommands:
|
|
|
99
105
|
return deduped
|
|
100
106
|
|
|
101
107
|
def navigation() -> list[Command]:
|
|
102
|
-
return [Ls(), PreviewTable(), DeviceApp(), DevicePostgres(), DeviceCass(), DeviceAuditLog(), DeviceExport(),
|
|
103
|
-
Cd(), Cat(), Head(), DownloadFile(), FindLocalFiles(), FindProcesses(), Pwd(), ClipboardCopy(),
|
|
108
|
+
return [Ls(), LsLocal(), PreviewTable(), DeviceApp(), DevicePostgres(), DeviceCass(), DeviceAuditLog(), DeviceExport(),
|
|
109
|
+
Cd(), Cat(), CatLocal(), Head(), DownloadFile(), FindLocalFiles(), FindProcesses(), Pwd(), ClipboardCopy(),
|
|
104
110
|
GetParam(), SetParam(), ShowParams(), ShowKubectlCommands(), ShowLogin(), ShowAdam(), ShowHost()]
|
|
105
111
|
|
|
106
112
|
def cassandra_ops() -> list[Command]:
|
|
107
|
-
return [Cqlsh(), ShowCassandraStatus(), ShowCassandraVersion(), ShowCassandraRepairs(), ShowStorage(), ShowProcesses(),
|
|
108
|
-
Check(), Issues(), NodeTool(),
|
|
113
|
+
return [Cqlsh(), DownloadCassandraLog(), ShowCassandraStatus(), ShowCassandraVersion(), ShowCassandraRepairs(), ShowStorage(), ShowProcesses(),
|
|
114
|
+
Check(), Issues(), NodeTool(), GenerateReport(), AlterTables(), Bash(),
|
|
109
115
|
ExportTables(), ExportXSelect(), ExportUse(), ShowExportDatabases(), ShowColumnCounts(),
|
|
110
116
|
DropExportDatabase(), DropExportDatabases(),
|
|
111
117
|
ShowExportSessions(), ShowExportSession(), DownloadExportSession(),
|
|
112
118
|
CleanUpExportSessions(), CleanUpAllExportSessions(), ImportSession(), ImportCSVFiles()] + \
|
|
113
|
-
Medusa().cmd_list() + [
|
|
119
|
+
Medusa().cmd_list() + [RestartNodes(), RestartNode(), RestartCluster(), RollOut(), Watch()] + Reaper().cmd_list() + Repair().cmd_list() + Debug().cmd_list()
|
|
114
120
|
|
|
115
121
|
def postgres_ops() -> list[Command]:
|
|
116
122
|
return [Postgres(), DeployPgAgent(), UndeployPgAgent(), PostgresPg()]
|
adam/repl_session.py
CHANGED
|
@@ -2,6 +2,7 @@ from prompt_toolkit import PromptSession
|
|
|
2
2
|
from prompt_toolkit.auto_suggest import AutoSuggestFromHistory
|
|
3
3
|
|
|
4
4
|
from adam.config import Config
|
|
5
|
+
from adam.utils import ConfigHolder
|
|
5
6
|
|
|
6
7
|
class ReplSession:
|
|
7
8
|
# the singleton pattern
|
|
@@ -13,8 +14,8 @@ class ReplSession:
|
|
|
13
14
|
def __init__(self):
|
|
14
15
|
if not hasattr(self, 'prompt_session'):
|
|
15
16
|
self.prompt_session = PromptSession(auto_suggest=AutoSuggestFromHistory())
|
|
17
|
+
ConfigHolder().append_command_history = self.append_history
|
|
16
18
|
|
|
17
19
|
def append_history(self, entry: str):
|
|
18
|
-
if entry and Config().get('repl.history.push-cat-remote-log-file', True):
|
|
19
|
-
|
|
20
|
-
self.prompt_session.history.append_string(entry)
|
|
20
|
+
if entry and self.prompt_session and Config().get('repl.history.push-cat-remote-log-file', True):
|
|
21
|
+
self.prompt_session.history.append_string(entry)
|
adam/repl_state.py
CHANGED
|
@@ -431,6 +431,12 @@ class ReplState:
|
|
|
431
431
|
|
|
432
432
|
return state1
|
|
433
433
|
|
|
434
|
+
def with_pod(self, pod: str):
|
|
435
|
+
state1 = copy(self)
|
|
436
|
+
state1.pod = pod
|
|
437
|
+
|
|
438
|
+
return state1
|
|
439
|
+
|
|
434
440
|
class DevicePodService:
|
|
435
441
|
def __init__(self, handler: 'DeviceExecHandler'):
|
|
436
442
|
self.handler = handler
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
from concurrent.futures import ThreadPoolExecutor
|
|
3
|
+
import inspect
|
|
4
|
+
import threading
|
|
5
|
+
import traceback
|
|
6
|
+
|
|
7
|
+
from adam.utils import log2, log_timing
|
|
8
|
+
|
|
9
|
+
class AsyncExecutor:
|
|
10
|
+
# some lib does not handle asyncio loop properly, as sync exec submit does not work, use another async loop
|
|
11
|
+
|
|
12
|
+
lock = threading.Lock()
|
|
13
|
+
in_queue = set()
|
|
14
|
+
|
|
15
|
+
loop: asyncio.AbstractEventLoop = None
|
|
16
|
+
async_exec: ThreadPoolExecutor = None
|
|
17
|
+
|
|
18
|
+
def preload(action: callable, log_key: str = None):
|
|
19
|
+
with AsyncExecutor.lock:
|
|
20
|
+
if not AsyncExecutor.loop:
|
|
21
|
+
AsyncExecutor.loop = asyncio.new_event_loop()
|
|
22
|
+
AsyncExecutor.async_exec = ThreadPoolExecutor(max_workers=6, thread_name_prefix='async')
|
|
23
|
+
AsyncExecutor.loop.set_default_executor(AsyncExecutor.async_exec)
|
|
24
|
+
|
|
25
|
+
async def a():
|
|
26
|
+
try:
|
|
27
|
+
arg_needed = len(action.__code__.co_varnames)
|
|
28
|
+
|
|
29
|
+
if log_key:
|
|
30
|
+
with log_timing(log_key):
|
|
31
|
+
r = action(None) if arg_needed else action()
|
|
32
|
+
else:
|
|
33
|
+
r = action(None) if arg_needed else action()
|
|
34
|
+
if inspect.isawaitable(r):
|
|
35
|
+
await r
|
|
36
|
+
|
|
37
|
+
AsyncExecutor.in_queue.remove(log_key)
|
|
38
|
+
except Exception as e:
|
|
39
|
+
log2('preloading error', e, inspect.getsourcelines(action)[0][0])
|
|
40
|
+
traceback.print_exc()
|
|
41
|
+
|
|
42
|
+
if log_key not in AsyncExecutor.in_queue:
|
|
43
|
+
AsyncExecutor.in_queue.add(log_key)
|
|
44
|
+
AsyncExecutor.async_exec.submit(lambda: AsyncExecutor.loop.run_until_complete(a()))
|
adam/sql/lark_completer.py
CHANGED
|
@@ -5,10 +5,11 @@ from prompt_toolkit.completion import CompleteEvent, Completer, Completion, Nest
|
|
|
5
5
|
from prompt_toolkit.document import Document
|
|
6
6
|
|
|
7
7
|
from adam.config import Config
|
|
8
|
+
from adam.sql.async_executor import AsyncExecutor
|
|
8
9
|
from adam.sql.lark_parser import LarkParser
|
|
9
10
|
from adam.utils import debug, log_timing, offload
|
|
10
11
|
from adam.utils_repl.appendable_completer import AppendableCompleter
|
|
11
|
-
from adam.utils_repl.repl_completer import merge_completions
|
|
12
|
+
from adam.utils_repl.repl_completer import merge_completions
|
|
12
13
|
|
|
13
14
|
__all__ = [
|
|
14
15
|
"LarkCompleter",
|
|
@@ -33,7 +34,7 @@ class LarkCompleter(Completer, AppendableCompleter):
|
|
|
33
34
|
self.nested: NestedCompleter = None
|
|
34
35
|
self.options_lambda = options_lambda
|
|
35
36
|
if options_lambda and auto == 'lazy':
|
|
36
|
-
preload(options_lambda, log_key=name)
|
|
37
|
+
AsyncExecutor.preload(options_lambda, log_key=name)
|
|
37
38
|
|
|
38
39
|
self.variant = variant
|
|
39
40
|
self.parser = None
|
|
@@ -60,7 +61,7 @@ class LarkCompleter(Completer, AppendableCompleter):
|
|
|
60
61
|
for key, value in self.expandables.items():
|
|
61
62
|
if callable(value):
|
|
62
63
|
if self.auto_complete(key) == 'lazy':
|
|
63
|
-
preload(value, log_key=key)
|
|
64
|
+
AsyncExecutor.preload(value, log_key=key)
|
|
64
65
|
|
|
65
66
|
def from_lambda(name: str, options_lambda: callable, auto: str = 'lazy'):
|
|
66
67
|
return LarkCompleter(name=name, options_lambda=options_lambda, auto=auto, variant=None)
|
|
@@ -131,8 +132,9 @@ class LarkCompleter(Completer, AppendableCompleter):
|
|
|
131
132
|
'show_column_counts_command.path.identifier_ref': 'tables',
|
|
132
133
|
'export_statement.export_tables.keyspace_name': 'keyspaces',
|
|
133
134
|
|
|
134
|
-
'alter_cql_table_statement.properties.property.property_name': 'table-props',
|
|
135
135
|
'alter_tables_statement.properties.property.property_name': 'table-props',
|
|
136
|
+
'alter_cql_table_statement.properties.property.property_name': 'table-props',
|
|
137
|
+
'alter_tables_statement.properties.property.property_value.literal': 'table-props-value',
|
|
136
138
|
'alter_cql_table_statement.properties.property.property_value.literal': 'table-props-value',
|
|
137
139
|
|
|
138
140
|
'select_clause.projection.result_expr.expr.path.identifier_ref': 'columns',
|