kaqing 2.0.188__py3-none-any.whl → 2.0.200__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 +6 -6
- adam/commands/bash/bash.py +1 -1
- adam/commands/bash/utils_bash.py +1 -1
- adam/commands/cassandra/__init__.py +0 -0
- adam/commands/cassandra/download_cassandra_log.py +45 -0
- adam/commands/cassandra/nodetool.py +64 -0
- adam/commands/cassandra/nodetool_commands.py +120 -0
- adam/commands/cassandra/restart_cluster.py +47 -0
- adam/commands/cassandra/restart_node.py +51 -0
- adam/commands/cassandra/restart_nodes.py +47 -0
- adam/commands/cassandra/rollout.py +88 -0
- adam/commands/cat.py +2 -2
- adam/commands/cd.py +2 -2
- adam/commands/command.py +1 -1
- adam/commands/commands_utils.py +8 -13
- adam/commands/cql/alter_tables.py +66 -0
- adam/commands/cql/completions_c.py +1 -0
- adam/commands/cql/utils_cql.py +5 -5
- 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/devices.py +1 -1
- adam/commands/download_cassandra_log.py +45 -0
- adam/commands/download_file.py +3 -3
- adam/commands/export/export_sessions.py +1 -1
- adam/commands/export/exporter.py +1 -1
- adam/commands/find_processes.py +2 -2
- adam/commands/generate_report.py +52 -0
- adam/commands/head.py +2 -2
- adam/commands/ls.py +2 -2
- adam/commands/medusa/medusa_restore.py +0 -16
- adam/commands/nodetool.py +1 -1
- adam/commands/os/__init__.py +0 -0
- adam/commands/os/cat.py +36 -0
- adam/commands/os/download_file.py +47 -0
- adam/commands/os/find_files.py +51 -0
- adam/commands/os/find_processes.py +76 -0
- adam/commands/os/head.py +36 -0
- adam/commands/os/shell.py +41 -0
- adam/commands/postgres/postgres_databases.py +2 -3
- 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_cli_commands.py +1 -1
- adam/config.py +4 -6
- adam/embedded_params.py +1 -1
- adam/repl.py +5 -3
- adam/repl_commands.py +11 -6
- 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/utils.py +95 -23
- adam/utils_k8s/app_clusters.py +1 -1
- adam/utils_k8s/app_pods.py +2 -3
- adam/utils_k8s/cassandra_clusters.py +7 -3
- adam/utils_k8s/cassandra_nodes.py +8 -5
- adam/utils_k8s/kube_context.py +1 -4
- adam/utils_k8s/pods.py +55 -1
- adam/utils_repl/repl_completer.py +4 -87
- adam/version.py +1 -1
- {kaqing-2.0.188.dist-info → kaqing-2.0.200.dist-info}/METADATA +1 -1
- {kaqing-2.0.188.dist-info → kaqing-2.0.200.dist-info}/RECORD +69 -45
- adam/commands/logs.py +0 -37
- adam/commands/report.py +0 -61
- adam/commands/restart.py +0 -60
- {kaqing-2.0.188.dist-info → kaqing-2.0.200.dist-info}/WHEEL +0 -0
- {kaqing-2.0.188.dist-info → kaqing-2.0.200.dist-info}/entry_points.txt +0 -0
- {kaqing-2.0.188.dist-info → kaqing-2.0.200.dist-info}/top_level.txt +0 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
from adam.commands import extract_options, validate_args
|
|
2
|
+
from adam.commands.command import Command
|
|
3
|
+
from adam.commands.devices.devices import Devices
|
|
4
|
+
from adam.commands.export.utils_export import state_with_pod
|
|
5
|
+
from adam.repl_state import ReplState, RequiredState
|
|
6
|
+
from adam.utils import log2, tabulize
|
|
7
|
+
|
|
8
|
+
class FindProcesses(Command):
|
|
9
|
+
COMMAND = 'find processes'
|
|
10
|
+
|
|
11
|
+
# the singleton pattern
|
|
12
|
+
def __new__(cls, *args, **kwargs):
|
|
13
|
+
if not hasattr(cls, 'instance'): cls.instance = super(FindProcesses, 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 FindProcesses.COMMAND
|
|
22
|
+
|
|
23
|
+
def required(self):
|
|
24
|
+
return [RequiredState.CLUSTER_OR_POD, RequiredState.APP_APP, ReplState.P]
|
|
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, '-kill') as (args, kill):
|
|
32
|
+
with validate_args(args, state, name='words to look for'):
|
|
33
|
+
arg = ' | '.join([f'grep {a}' for a in args])
|
|
34
|
+
awk = "awk '{ print $1, $2, $8, $NF }'"
|
|
35
|
+
rs = Devices.of(state).bash(state, state, f"ps -ef | grep -v grep | {arg} | {awk}".split(' '))
|
|
36
|
+
|
|
37
|
+
lines: list[list[str]] = []
|
|
38
|
+
for r in rs:
|
|
39
|
+
for l in r.stdout.split('\n'):
|
|
40
|
+
l = l.strip(' \t\r\n')
|
|
41
|
+
if not l:
|
|
42
|
+
continue
|
|
43
|
+
|
|
44
|
+
tokens = [r.pod] + l.split(' ')
|
|
45
|
+
lines.append(tokens)
|
|
46
|
+
|
|
47
|
+
pids = []
|
|
48
|
+
for l in lines:
|
|
49
|
+
pids.append(f'{l[2]}@{l[0]}')
|
|
50
|
+
|
|
51
|
+
tabulize(lines, lambda l: '\t'.join(l), header = 'POD\tUSER\tPID\tCMD\tLAST_ARG', separator='\t')
|
|
52
|
+
log2()
|
|
53
|
+
log2(f'PIDS with {",".join(args)}: {",".join(pids)}')
|
|
54
|
+
|
|
55
|
+
if kill:
|
|
56
|
+
log2()
|
|
57
|
+
for pidp in pids:
|
|
58
|
+
pid_n_pod = pidp.split('@')
|
|
59
|
+
pid = pid_n_pod[0]
|
|
60
|
+
if len(pid_n_pod) < 2:
|
|
61
|
+
continue
|
|
62
|
+
|
|
63
|
+
pod = pid_n_pod[1]
|
|
64
|
+
|
|
65
|
+
log2(f'@{pod} bash kill -9 {pid}')
|
|
66
|
+
|
|
67
|
+
with state_with_pod(state, pod) as state1:
|
|
68
|
+
Devices.of(state).bash(state, state1, ['kill', '-9', pid])
|
|
69
|
+
|
|
70
|
+
return rs
|
|
71
|
+
|
|
72
|
+
def completion(self, state: ReplState):
|
|
73
|
+
return super().completion(state)
|
|
74
|
+
|
|
75
|
+
def help(self, _: ReplState):
|
|
76
|
+
return f'{FindProcesses.COMMAND} word... [-kill]\t find processes with words'
|
adam/commands/os/head.py
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
from adam.commands import validate_args
|
|
2
|
+
from adam.commands.command import Command
|
|
3
|
+
from adam.commands.devices.devices import Devices
|
|
4
|
+
from adam.repl_state import ReplState, RequiredState
|
|
5
|
+
|
|
6
|
+
class Head(Command):
|
|
7
|
+
COMMAND = 'head'
|
|
8
|
+
|
|
9
|
+
# the singleton pattern
|
|
10
|
+
def __new__(cls, *args, **kwargs):
|
|
11
|
+
if not hasattr(cls, 'instance'): cls.instance = super(Head, cls).__new__(cls)
|
|
12
|
+
|
|
13
|
+
return cls.instance
|
|
14
|
+
|
|
15
|
+
def __init__(self, successor: Command=None):
|
|
16
|
+
super().__init__(successor)
|
|
17
|
+
|
|
18
|
+
def command(self):
|
|
19
|
+
return Head.COMMAND
|
|
20
|
+
|
|
21
|
+
def required(self):
|
|
22
|
+
return [RequiredState.CLUSTER_OR_POD, RequiredState.APP_APP, ReplState.P]
|
|
23
|
+
|
|
24
|
+
def run(self, cmd: str, state: ReplState):
|
|
25
|
+
if not(args := self.args(cmd)):
|
|
26
|
+
return super().run(cmd, state)
|
|
27
|
+
|
|
28
|
+
with self.validate(args, state) as (args, state):
|
|
29
|
+
with validate_args(args, state, name='file'):
|
|
30
|
+
return Devices.of(state).bash(state, state, cmd.split(' '))
|
|
31
|
+
|
|
32
|
+
def completion(self, state: ReplState):
|
|
33
|
+
return super().completion(state, lambda: {f: None for f in Devices.of(state).files(state)}, pods=Devices.of(state).pods(state, '-'), auto='jit')
|
|
34
|
+
|
|
35
|
+
def help(self, _: ReplState):
|
|
36
|
+
return f'{Head.COMMAND} file [&]\t run head command on the pod'
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from adam.commands import validate_args
|
|
4
|
+
from adam.commands.command import Command
|
|
5
|
+
from adam.repl_state import ReplState
|
|
6
|
+
from adam.utils import log2
|
|
7
|
+
|
|
8
|
+
class Shell(Command):
|
|
9
|
+
COMMAND = ':sh'
|
|
10
|
+
|
|
11
|
+
# the singleton pattern
|
|
12
|
+
def __new__(cls, *args, **kwargs):
|
|
13
|
+
if not hasattr(cls, 'instance'): cls.instance = super(Shell, 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 Shell.COMMAND
|
|
22
|
+
|
|
23
|
+
def run(self, cmd: str, state: ReplState):
|
|
24
|
+
if not(args := self.args(cmd)):
|
|
25
|
+
return super().run(cmd, state)
|
|
26
|
+
|
|
27
|
+
with self.validate(args, state) as (args, _):
|
|
28
|
+
with validate_args(args, state, at_least=0) as args_str:
|
|
29
|
+
if args_str:
|
|
30
|
+
os.system(args_str)
|
|
31
|
+
log2()
|
|
32
|
+
else:
|
|
33
|
+
os.system('QING_DROPPED=true bash')
|
|
34
|
+
|
|
35
|
+
return state
|
|
36
|
+
|
|
37
|
+
def completion(self, state: ReplState):
|
|
38
|
+
return super().completion(state)
|
|
39
|
+
|
|
40
|
+
def help(self, _: ReplState):
|
|
41
|
+
return f'{Shell.COMMAND}\t drop down to shell'
|
|
@@ -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'@{r.pod} cat {r.log_file}')
|
|
150
149
|
|
|
151
150
|
return r
|
|
152
151
|
|
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"
|
|
@@ -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):
|
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-prefix': '/tmp/qing'}, '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-tmp-dir': '/tmp/qing-db', 'logs': {'path': '/c3/cassandra/logs/system.log'}, 'log-prefix': '/tmp/qing', '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': '
|
|
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-prefix': '/tmp/qing'}, '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-tmp-dir': '/tmp/qing-db', 'logs': {'path': '/c3/cassandra/logs/system.log'}, 'log-prefix': '/tmp/qing', '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, 'via-sh': 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
|
@@ -7,6 +7,9 @@ from adam.commands.app.show_app_queues import ShowAppQueues
|
|
|
7
7
|
from adam.commands.audit.audit import Audit
|
|
8
8
|
from adam.commands.cat import Cat
|
|
9
9
|
from adam.commands.code import Code
|
|
10
|
+
from adam.commands.debug.debug import Debug
|
|
11
|
+
from adam.commands.debug.debug_timings import DebugTimings
|
|
12
|
+
from adam.commands.download_cassandra_log import DownloadCassandraLog
|
|
10
13
|
from adam.commands.download_file import DownloadFile
|
|
11
14
|
from adam.commands.deploy.code_start import CodeStart
|
|
12
15
|
from adam.commands.deploy.code_stop import CodeStop
|
|
@@ -42,6 +45,8 @@ from adam.commands.find_files import FindLocalFiles
|
|
|
42
45
|
from adam.commands.find_processes import FindProcesses
|
|
43
46
|
from adam.commands.head import Head
|
|
44
47
|
from adam.commands.kubectl import Kubectl
|
|
48
|
+
from adam.commands.restart_cluster import RestartCluster
|
|
49
|
+
from adam.commands.restart_node import RestartNode
|
|
45
50
|
from adam.commands.shell import Shell
|
|
46
51
|
from adam.commands.clipboard_copy import ClipboardCopy
|
|
47
52
|
from adam.commands.bash.bash import Bash
|
|
@@ -60,8 +65,8 @@ from adam.commands.preview_table import PreviewTable
|
|
|
60
65
|
from adam.commands.pwd import Pwd
|
|
61
66
|
from adam.commands.reaper.reaper import Reaper
|
|
62
67
|
from adam.commands.repair.repair import Repair
|
|
63
|
-
from adam.commands.
|
|
64
|
-
from adam.commands.
|
|
68
|
+
from adam.commands.generate_report import GenerateReport
|
|
69
|
+
from adam.commands.restart_nodes import RestartNodes
|
|
65
70
|
from adam.commands.rollout import RollOut
|
|
66
71
|
from adam.commands.param_set import SetParam
|
|
67
72
|
from adam.commands.show.show import Show
|
|
@@ -82,7 +87,7 @@ class ReplCommands:
|
|
|
82
87
|
cmds: list[Command] = ReplCommands.navigation() + ReplCommands.cassandra_ops() + ReplCommands.postgres_ops() + \
|
|
83
88
|
ReplCommands.app_ops() + ReplCommands.audit_ops() + ReplCommands.export_ops() + ReplCommands.tools() + ReplCommands.exit()
|
|
84
89
|
|
|
85
|
-
intermediate_cmds: list[Command] = [App(), Audit(), Reaper(), Repair(), Deploy(), Show(), Undeploy()]
|
|
90
|
+
intermediate_cmds: list[Command] = [App(), Audit(), Reaper(), Repair(), Debug(), Deploy(), Show(), Undeploy()]
|
|
86
91
|
ic = [c.command() for c in intermediate_cmds]
|
|
87
92
|
# 1. dedup commands
|
|
88
93
|
deduped = []
|
|
@@ -104,13 +109,13 @@ class ReplCommands:
|
|
|
104
109
|
GetParam(), SetParam(), ShowParams(), ShowKubectlCommands(), ShowLogin(), ShowAdam(), ShowHost()]
|
|
105
110
|
|
|
106
111
|
def cassandra_ops() -> list[Command]:
|
|
107
|
-
return [Cqlsh(), ShowCassandraStatus(), ShowCassandraVersion(), ShowCassandraRepairs(), ShowStorage(), ShowProcesses(),
|
|
108
|
-
Check(), Issues(), NodeTool(),
|
|
112
|
+
return [Cqlsh(), DownloadCassandraLog(), ShowCassandraStatus(), ShowCassandraVersion(), ShowCassandraRepairs(), ShowStorage(), ShowProcesses(),
|
|
113
|
+
Check(), Issues(), NodeTool(), GenerateReport(), AlterTables(), Bash(),
|
|
109
114
|
ExportTables(), ExportXSelect(), ExportUse(), ShowExportDatabases(), ShowColumnCounts(),
|
|
110
115
|
DropExportDatabase(), DropExportDatabases(),
|
|
111
116
|
ShowExportSessions(), ShowExportSession(), DownloadExportSession(),
|
|
112
117
|
CleanUpExportSessions(), CleanUpAllExportSessions(), ImportSession(), ImportCSVFiles()] + \
|
|
113
|
-
Medusa().cmd_list() + [
|
|
118
|
+
Medusa().cmd_list() + [RestartNodes(), RestartNode(), RestartCluster(), RollOut(), Watch()] + Reaper().cmd_list() + Repair().cmd_list() + Debug().cmd_list()
|
|
114
119
|
|
|
115
120
|
def postgres_ops() -> list[Command]:
|
|
116
121
|
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',
|