kaqing 2.0.186__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/app_session.py +1 -1
- 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 +15 -9
- 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.186.dist-info → kaqing-2.0.200.dist-info}/METADATA +1 -1
- {kaqing-2.0.186.dist-info → kaqing-2.0.200.dist-info}/RECORD +73 -46
- kaqing-2.0.200.dist-info/top_level.txt +2 -0
- teddy/__init__.py +0 -0
- teddy/lark_parser.py +436 -0
- teddy/lark_parser2.py +618 -0
- adam/commands/logs.py +0 -37
- adam/commands/report.py +0 -61
- adam/commands/restart.py +0 -60
- kaqing-2.0.186.dist-info/top_level.txt +0 -1
- {kaqing-2.0.186.dist-info → kaqing-2.0.200.dist-info}/WHEEL +0 -0
- {kaqing-2.0.186.dist-info → kaqing-2.0.200.dist-info}/entry_points.txt +0 -0
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
from adam.commands import extract_options, validate_args
|
|
2
|
+
from adam.commands.command import Command
|
|
3
|
+
from adam.commands.cql.utils_cql import cassandra, cassandra_tables as get_tables
|
|
4
|
+
from adam.config import Config
|
|
5
|
+
from adam.repl_state import ReplState, RequiredState
|
|
6
|
+
from adam.utils import log2, log_exc
|
|
7
|
+
|
|
8
|
+
class AlterTables(Command):
|
|
9
|
+
COMMAND = 'alter tables with'
|
|
10
|
+
|
|
11
|
+
# the singleton pattern
|
|
12
|
+
def __new__(cls, *args, **kwargs):
|
|
13
|
+
if not hasattr(cls, 'instance'): cls.instance = super(AlterTables, cls).__new__(cls)
|
|
14
|
+
|
|
15
|
+
return cls.instance
|
|
16
|
+
|
|
17
|
+
def __init__(self, successor: Command=None):
|
|
18
|
+
super().__init__(successor)
|
|
19
|
+
|
|
20
|
+
def required(self):
|
|
21
|
+
return RequiredState.CLUSTER
|
|
22
|
+
|
|
23
|
+
def command(self):
|
|
24
|
+
return AlterTables.COMMAND
|
|
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, '--include-reaper') as (args, include_reaper):
|
|
32
|
+
with validate_args(args, state, name='gc grace in seconds') as arg_str:
|
|
33
|
+
excludes = [e.strip(' \r\n') for e in Config().get(
|
|
34
|
+
'cql.alter-tables.excludes',
|
|
35
|
+
'system_auth,system_traces,reaper_db,system_distributed,system_views,system,system_schema,system_virtual_schema').split(',')]
|
|
36
|
+
batching = Config().get('cql.alter-tables.batching', True)
|
|
37
|
+
tables = get_tables(state, on_any=True)
|
|
38
|
+
for k, v in tables.items():
|
|
39
|
+
if k not in excludes or k == 'reaper_db' and include_reaper:
|
|
40
|
+
if batching:
|
|
41
|
+
# alter table <table_name> with GC_GRACE_SECONDS = <timeout>;
|
|
42
|
+
cql = ';\n'.join([f'alter table {k}.{t} with {arg_str}' for t in v])
|
|
43
|
+
with log_exc(True):
|
|
44
|
+
with cassandra(state) as pods:
|
|
45
|
+
pods.cql(cql, show_out=Config().is_debug(), show_query=not Config().is_debug(), on_any=True)
|
|
46
|
+
continue
|
|
47
|
+
else:
|
|
48
|
+
for t in v:
|
|
49
|
+
with log_exc(True):
|
|
50
|
+
# alter table <table_name> with GC_GRACE_SECONDS = <timeout>;
|
|
51
|
+
cql = f'alter table {k}.{t} with {arg_str}'
|
|
52
|
+
with cassandra(state) as pods:
|
|
53
|
+
pods.cql(show_out=Config().is_debug(), show_query=not Config().is_debug(), on_any=True)
|
|
54
|
+
continue
|
|
55
|
+
|
|
56
|
+
log2(f'{len(v)} tables altered in {k}.')
|
|
57
|
+
|
|
58
|
+
# do not continue to cql route
|
|
59
|
+
return state
|
|
60
|
+
|
|
61
|
+
def completion(self, _: ReplState) -> dict[str, any]:
|
|
62
|
+
# auto completion is taken care of by lark completer
|
|
63
|
+
return {}
|
|
64
|
+
|
|
65
|
+
def help(self, _: ReplState) -> str:
|
|
66
|
+
return f'{AlterTables.COMMAND} <param = value> [--include-reaper] \t alter schema on all tables'
|
|
@@ -18,6 +18,7 @@ def completions_c(state: ReplState) -> dict[str, any]:
|
|
|
18
18
|
'table-props': lambda x: {
|
|
19
19
|
'GC_GRACE_SECONDS': ps
|
|
20
20
|
},
|
|
21
|
+
'table-props-value': lambda x: {'GC_GRACE_SECONDS': ps}[x],
|
|
21
22
|
'export-database-types': lambda x: ['athena', 'sqlite', 'csv'],
|
|
22
23
|
'export-databases': lambda x: ExportDatabases.database_names(),
|
|
23
24
|
'export-sessions': lambda x: ExportSessions.export_session_names(state.sts, state.pod, state.namespace),
|
adam/commands/cql/utils_cql.py
CHANGED
|
@@ -55,7 +55,7 @@ def table_spec(state: ReplState, table: str, on_any=False) -> 'TableSpec':
|
|
|
55
55
|
|
|
56
56
|
return parse_cql_desc_table(r.stdout if state.pod else r[0].stdout)
|
|
57
57
|
|
|
58
|
-
def run_cql(state: ReplState, cql: str, opts: list = [], show_out = False, show_query = False, use_single_quotes = False, on_any = False, backgrounded=False, log_file=None) -> list[PodExecResult]:
|
|
58
|
+
def run_cql(state: ReplState, cql: str, opts: list = [], show_out = False, show_query = False, use_single_quotes = False, on_any = False, backgrounded=False, log_file=None, via_sh=True) -> list[PodExecResult]:
|
|
59
59
|
if show_query:
|
|
60
60
|
log2(cql)
|
|
61
61
|
|
|
@@ -69,7 +69,7 @@ def run_cql(state: ReplState, cql: str, opts: list = [], show_out = False, show_
|
|
|
69
69
|
|
|
70
70
|
with log_timing(cql):
|
|
71
71
|
with cassandra(state) as pods:
|
|
72
|
-
return pods.exec(command, action='cql', show_out=show_out, on_any=on_any, backgrounded=backgrounded, log_file=log_file)
|
|
72
|
+
return pods.exec(command, action='cql', show_out=show_out, on_any=on_any, backgrounded=backgrounded, log_file=log_file, via_sh=via_sh)
|
|
73
73
|
|
|
74
74
|
def parse_cql_desc_tables(out: str):
|
|
75
75
|
# Keyspace data_endpoint_auth
|
|
@@ -227,16 +227,16 @@ class CassandraPodService:
|
|
|
227
227
|
def __init__(self, handler: 'CassandraExecHandler'):
|
|
228
228
|
self.handler = handler
|
|
229
229
|
|
|
230
|
-
def exec(self, command: str, action='bash', show_out = True, on_any = False, throw_err = False, shell = '/bin/sh', backgrounded = False, log_file = None) -> Union[PodExecResult, list[PodExecResult]]:
|
|
230
|
+
def exec(self, command: str, action='bash', show_out = True, on_any = False, throw_err = False, shell = '/bin/sh', backgrounded = False, log_file = None, via_sh = True) -> Union[PodExecResult, list[PodExecResult]]:
|
|
231
231
|
state = self.handler.state
|
|
232
232
|
pod = self.handler.pod
|
|
233
233
|
|
|
234
234
|
if pod:
|
|
235
235
|
return CassandraNodes.exec(pod, state.namespace, command,
|
|
236
|
-
show_out=show_out, throw_err=throw_err, shell=shell, backgrounded=backgrounded, log_file=log_file)
|
|
236
|
+
show_out=show_out, throw_err=throw_err, shell=shell, backgrounded=backgrounded, log_file=log_file, via_sh=via_sh)
|
|
237
237
|
elif state.sts:
|
|
238
238
|
return CassandraClusters.exec(state.sts, state.namespace, command, action=action,
|
|
239
|
-
show_out=show_out, on_any=on_any, shell=shell, backgrounded=backgrounded, log_file=log_file)
|
|
239
|
+
show_out=show_out, on_any=on_any, shell=shell, backgrounded=backgrounded, log_file=log_file, via_sh=via_sh)
|
|
240
240
|
|
|
241
241
|
return []
|
|
242
242
|
|
|
File without changes
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from adam.commands.command import Command
|
|
2
|
+
from adam.commands.debug.debug_completes import DebugCompletes
|
|
3
|
+
from adam.commands.debug.debug_timings import DebugTimings
|
|
4
|
+
from adam.commands.intermediate_command import IntermediateCommand
|
|
5
|
+
|
|
6
|
+
class Debug(IntermediateCommand):
|
|
7
|
+
COMMAND = 'debug'
|
|
8
|
+
|
|
9
|
+
# the singleton pattern
|
|
10
|
+
def __new__(cls, *args, **kwargs):
|
|
11
|
+
if not hasattr(cls, 'instance'): cls.instance = super(Debug, 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 Debug.COMMAND
|
|
20
|
+
|
|
21
|
+
def cmd_list(self):
|
|
22
|
+
return [DebugTimings(), DebugCompletes()]
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from adam.commands import validate_args
|
|
2
|
+
from adam.commands.command import Command
|
|
3
|
+
from adam.config import Config
|
|
4
|
+
from adam.repl_state import ReplState
|
|
5
|
+
|
|
6
|
+
class DebugCompletes(Command):
|
|
7
|
+
COMMAND = 'debug completes'
|
|
8
|
+
|
|
9
|
+
# the singleton pattern
|
|
10
|
+
def __new__(cls, *args, **kwargs):
|
|
11
|
+
if not hasattr(cls, 'instance'): cls.instance = super(DebugCompletes, 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 DebugCompletes.COMMAND
|
|
20
|
+
|
|
21
|
+
def run(self, cmd: str, state: ReplState):
|
|
22
|
+
if not(args := self.args(cmd)):
|
|
23
|
+
return super().run(cmd, state)
|
|
24
|
+
|
|
25
|
+
with self.validate(args, state) as (args, state):
|
|
26
|
+
with validate_args(args, state, name='on, off or file') as args:
|
|
27
|
+
Config().set('debugs.complete', args)
|
|
28
|
+
|
|
29
|
+
return state
|
|
30
|
+
|
|
31
|
+
def completion(self, state: ReplState):
|
|
32
|
+
return super().completion(state, {f: None for f in ['on', 'off', 'file']})
|
|
33
|
+
|
|
34
|
+
def help(self, _: ReplState):
|
|
35
|
+
return f'{DebugCompletes.COMMAND} on|off|file\t turn auto complete debug on or off'
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from adam.commands import validate_args
|
|
2
|
+
from adam.commands.command import Command
|
|
3
|
+
from adam.config import Config
|
|
4
|
+
from adam.repl_state import ReplState
|
|
5
|
+
|
|
6
|
+
class DebugTimings(Command):
|
|
7
|
+
COMMAND = 'debug timings'
|
|
8
|
+
|
|
9
|
+
# the singleton pattern
|
|
10
|
+
def __new__(cls, *args, **kwargs):
|
|
11
|
+
if not hasattr(cls, 'instance'): cls.instance = super(DebugTimings, 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 DebugTimings.COMMAND
|
|
20
|
+
|
|
21
|
+
def run(self, cmd: str, state: ReplState):
|
|
22
|
+
if not(args := self.args(cmd)):
|
|
23
|
+
return super().run(cmd, state)
|
|
24
|
+
|
|
25
|
+
with self.validate(args, state) as (args, state):
|
|
26
|
+
with validate_args(args, state, name='on, off or file') as args:
|
|
27
|
+
Config().set('debugs.timings', args)
|
|
28
|
+
|
|
29
|
+
return state
|
|
30
|
+
|
|
31
|
+
def completion(self, state: ReplState):
|
|
32
|
+
return super().completion(state, {f: None for f in ['on', 'off', 'file']})
|
|
33
|
+
|
|
34
|
+
def help(self, _: ReplState):
|
|
35
|
+
return f'{DebugTimings.COMMAND} on|off|file\t turn timing debug on or off'
|
adam/commands/devices/devices.py
CHANGED
|
@@ -7,7 +7,7 @@ from adam.commands.devices.device_postgres import DevicePostgres
|
|
|
7
7
|
from adam.repl_state import ReplState
|
|
8
8
|
|
|
9
9
|
class Devices:
|
|
10
|
-
def
|
|
10
|
+
def of(state: ReplState) -> Device:
|
|
11
11
|
if state.device == ReplState.A:
|
|
12
12
|
return DeviceApp()
|
|
13
13
|
elif state.device == ReplState.C:
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
from adam.commands.command import Command
|
|
2
|
+
from adam.commands.devices.devices import Devices
|
|
3
|
+
from adam.config import Config
|
|
4
|
+
from adam.pod_exec_result import PodExecResult
|
|
5
|
+
from adam.utils import log2
|
|
6
|
+
from adam.utils_k8s.cassandra_nodes import CassandraNodes
|
|
7
|
+
from adam.repl_state import ReplState, RequiredState
|
|
8
|
+
from adam.utils_k8s.pods import Pods
|
|
9
|
+
|
|
10
|
+
class DownloadCassandraLog(Command):
|
|
11
|
+
COMMAND = 'download cassandra log'
|
|
12
|
+
|
|
13
|
+
# the singleton pattern
|
|
14
|
+
def __new__(cls, *args, **kwargs):
|
|
15
|
+
if not hasattr(cls, 'instance'): cls.instance = super(DownloadCassandraLog, cls).__new__(cls)
|
|
16
|
+
|
|
17
|
+
return cls.instance
|
|
18
|
+
|
|
19
|
+
def __init__(self, successor: Command=None):
|
|
20
|
+
super().__init__(successor)
|
|
21
|
+
|
|
22
|
+
def command(self):
|
|
23
|
+
return DownloadCassandraLog.COMMAND
|
|
24
|
+
|
|
25
|
+
def required(self):
|
|
26
|
+
return RequiredState.POD
|
|
27
|
+
|
|
28
|
+
def run(self, cmd: str, state: ReplState):
|
|
29
|
+
if not(args := self.args(cmd)):
|
|
30
|
+
return super().run(cmd, state)
|
|
31
|
+
|
|
32
|
+
with self.validate(args, state) as (args, state):
|
|
33
|
+
path = Config().get('logs.path', '/c3/cassandra/logs/system.log')
|
|
34
|
+
r: PodExecResult = CassandraNodes.exec(state.pod, state.namespace, f'cat {path}', backgrounded=True, no_history=True)
|
|
35
|
+
|
|
36
|
+
to_file = Pods.download_file(state.pod, 'cassandra', state.namespace, path)
|
|
37
|
+
log2(f'Downloaded to {to_file}.')
|
|
38
|
+
|
|
39
|
+
return r
|
|
40
|
+
|
|
41
|
+
def completion(self, state: ReplState):
|
|
42
|
+
return super().completion(state, pods=Devices.of(state).pods(state, '-'), auto='jit')
|
|
43
|
+
|
|
44
|
+
def help(self, _: ReplState):
|
|
45
|
+
return f'{DownloadCassandraLog.COMMAND}\t download cassandra system log'
|
adam/commands/download_file.py
CHANGED
|
@@ -31,8 +31,8 @@ class DownloadFile(Command):
|
|
|
31
31
|
|
|
32
32
|
with self.validate(args, state) as (args, state):
|
|
33
33
|
with validate_args(args, state, name='file'):
|
|
34
|
-
to_file = Pods.download_file(Devices.
|
|
35
|
-
Devices.
|
|
34
|
+
to_file = Pods.download_file(Devices.of(state).pod(state),
|
|
35
|
+
Devices.of(state).default_container(state),
|
|
36
36
|
state.namespace,
|
|
37
37
|
args[0],
|
|
38
38
|
args[1] if len(args) > 1 else None)
|
|
@@ -41,7 +41,7 @@ class DownloadFile(Command):
|
|
|
41
41
|
return state
|
|
42
42
|
|
|
43
43
|
def completion(self, state: ReplState):
|
|
44
|
-
return super().completion(state, lambda: {f: None for f in Devices.
|
|
44
|
+
return super().completion(state, lambda: {f: None for f in Devices.of(state).files(state)}, pods=Devices.of(state).pods(state, '-'), auto='jit')
|
|
45
45
|
|
|
46
46
|
def help(self, _: ReplState):
|
|
47
47
|
return f'{DownloadFile.COMMAND} from-file [to-file]\t download file from pod'
|
|
@@ -42,7 +42,7 @@ class ExportSessions:
|
|
|
42
42
|
return {}
|
|
43
43
|
|
|
44
44
|
for log_file in log_files[:limit]:
|
|
45
|
-
m = re.match(f'{log_prefix()}-(.*?)_.*\.log?(.*)', log_file)
|
|
45
|
+
m = re.match(f'{log_prefix()}-([ces].*?)_.*\.log?(.*)', log_file)
|
|
46
46
|
if m:
|
|
47
47
|
s = m.group(1)
|
|
48
48
|
state = m.group(2) # '', '.pending_import', '.done'
|
adam/commands/export/exporter.py
CHANGED
|
@@ -221,7 +221,7 @@ class Exporter:
|
|
|
221
221
|
queries.append(f"COPY {spec.keyspace}.{table}({columns}) TO '{csv_file}' WITH HEADER = TRUE")
|
|
222
222
|
r: PodExecResult = ing(
|
|
223
223
|
f'[{session}] Dumping table {spec.keyspace}.{table}{f" with consistency {consistency}" if consistency else ""}',
|
|
224
|
-
lambda: run_cql(state, ';'.join(queries), show_out=Config().is_debug(), backgrounded=True, log_file=log_file),
|
|
224
|
+
lambda: run_cql(state, ';'.join(queries), show_out=Config().is_debug(), backgrounded=True, log_file=log_file, via_sh=False),
|
|
225
225
|
suppress_log=suppress_ing_log)
|
|
226
226
|
|
|
227
227
|
return log_file
|
adam/commands/find_processes.py
CHANGED
|
@@ -32,7 +32,7 @@ class FindProcesses(Command):
|
|
|
32
32
|
with validate_args(args, state, name='words to look for'):
|
|
33
33
|
arg = ' | '.join([f'grep {a}' for a in args])
|
|
34
34
|
awk = "awk '{ print $1, $2, $8, $NF }'"
|
|
35
|
-
rs = Devices.
|
|
35
|
+
rs = Devices.of(state).bash(state, state, f"ps -ef | grep -v grep | {arg} | {awk}".split(' '))
|
|
36
36
|
|
|
37
37
|
lines: list[list[str]] = []
|
|
38
38
|
for r in rs:
|
|
@@ -65,7 +65,7 @@ class FindProcesses(Command):
|
|
|
65
65
|
log2(f'@{pod} bash kill -9 {pid}')
|
|
66
66
|
|
|
67
67
|
with state_with_pod(state, pod) as state1:
|
|
68
|
-
Devices.
|
|
68
|
+
Devices.of(state).bash(state, state1, ['kill', '-9', pid])
|
|
69
69
|
|
|
70
70
|
return rs
|
|
71
71
|
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import click
|
|
2
|
+
import json
|
|
3
|
+
|
|
4
|
+
from adam.checks.check_result import CheckResult
|
|
5
|
+
from adam.checks.check_utils import run_checks
|
|
6
|
+
from adam.commands import extract_options
|
|
7
|
+
from adam.commands.command import Command
|
|
8
|
+
from adam.commands.commands_utils import kaqing_log_file
|
|
9
|
+
from adam.repl_state import ReplState
|
|
10
|
+
from adam.utils import log2
|
|
11
|
+
|
|
12
|
+
class GenerateReport(Command):
|
|
13
|
+
COMMAND = 'generate report'
|
|
14
|
+
|
|
15
|
+
# the singleton pattern
|
|
16
|
+
def __new__(cls, *args, **kwargs):
|
|
17
|
+
if not hasattr(cls, 'instance'): cls.instance = super(GenerateReport, cls).__new__(cls)
|
|
18
|
+
|
|
19
|
+
return cls.instance
|
|
20
|
+
|
|
21
|
+
def __init__(self, successor: Command=None):
|
|
22
|
+
super().__init__(successor)
|
|
23
|
+
|
|
24
|
+
def command(self):
|
|
25
|
+
return GenerateReport.COMMAND
|
|
26
|
+
|
|
27
|
+
def required(self):
|
|
28
|
+
return ReplState.NON_L
|
|
29
|
+
|
|
30
|
+
def run(self, cmd: str, state: ReplState):
|
|
31
|
+
if not(args := self.args(cmd)):
|
|
32
|
+
return super().run(cmd, state)
|
|
33
|
+
|
|
34
|
+
with self.validate(args, state) as (args, state):
|
|
35
|
+
with extract_options(args, ['-s', '--show']) as (args, show_out):
|
|
36
|
+
results = run_checks(state.sts, state.namespace, state.pod, show_out=show_out)
|
|
37
|
+
output = CheckResult.report(results)
|
|
38
|
+
|
|
39
|
+
if state.in_repl:
|
|
40
|
+
with kaqing_log_file() as json_file:
|
|
41
|
+
json.dump(output, json_file, indent=2)
|
|
42
|
+
log2(f'Report stored in {json_file.name}.')
|
|
43
|
+
else:
|
|
44
|
+
click.echo(json.dumps(output, indent=2))
|
|
45
|
+
|
|
46
|
+
return output
|
|
47
|
+
|
|
48
|
+
def completion(self, state: ReplState):
|
|
49
|
+
return super().completion(state, {'-s': None})
|
|
50
|
+
|
|
51
|
+
def help(self, _: ReplState):
|
|
52
|
+
return f"{GenerateReport.COMMAND} [-s]\t generate report"
|
adam/commands/head.py
CHANGED
|
@@ -27,10 +27,10 @@ class Head(Command):
|
|
|
27
27
|
|
|
28
28
|
with self.validate(args, state) as (args, state):
|
|
29
29
|
with validate_args(args, state, name='file'):
|
|
30
|
-
return Devices.
|
|
30
|
+
return Devices.of(state).bash(state, state, cmd.split(' '))
|
|
31
31
|
|
|
32
32
|
def completion(self, state: ReplState):
|
|
33
|
-
return super().completion(state, lambda: {f: None for f in Devices.
|
|
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
34
|
|
|
35
35
|
def help(self, _: ReplState):
|
|
36
36
|
return f'{Head.COMMAND} file [&]\t run head command on the pod'
|
adam/commands/ls.py
CHANGED
|
@@ -30,12 +30,12 @@ class Ls(Command):
|
|
|
30
30
|
state = copy.copy(state)
|
|
31
31
|
state.device = arg.replace(':', '')
|
|
32
32
|
|
|
33
|
-
Devices.
|
|
33
|
+
Devices.of(state).ls(cmd, state)
|
|
34
34
|
|
|
35
35
|
return state
|
|
36
36
|
|
|
37
37
|
def completion(self, state: ReplState):
|
|
38
|
-
return super().completion(state, {'&': None}, pods=Devices.
|
|
38
|
+
return super().completion(state, {'&': None}, pods=Devices.of(state).pods(state, '-'))
|
|
39
39
|
|
|
40
40
|
def help(self, _: ReplState):
|
|
41
41
|
return f'{Ls.COMMAND} [device:]\t list apps, envs, clusters, nodes, pg hosts/databases or export databases'
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
from collections.abc import Callable
|
|
2
1
|
from datetime import datetime
|
|
3
2
|
from functools import partial
|
|
4
3
|
|
|
@@ -8,7 +7,6 @@ from adam.commands.medusa.utils_medusa import medusa_backup_names
|
|
|
8
7
|
from adam.utils_k8s.statefulsets import StatefulSets
|
|
9
8
|
from adam.repl_state import ReplState, RequiredState
|
|
10
9
|
from adam.utils_k8s.custom_resources import CustomResources
|
|
11
|
-
from adam.config import Config
|
|
12
10
|
from adam.utils import tabulize, log2, log_exc
|
|
13
11
|
|
|
14
12
|
class MedusaRestore(Command):
|
|
@@ -69,20 +67,6 @@ class MedusaRestore(Command):
|
|
|
69
67
|
|
|
70
68
|
def completion(self, state: ReplState):
|
|
71
69
|
return super().completion(state, lambda: {id: None for id in medusa_backup_names(state)}, auto_key='medusa.backups')
|
|
72
|
-
# if sc := super().completion(state):
|
|
73
|
-
# ns = state.namespace
|
|
74
|
-
# dc: str = StatefulSets.get_datacenter(state.sts, ns)
|
|
75
|
-
# if not dc:
|
|
76
|
-
# return {}
|
|
77
|
-
|
|
78
|
-
# if Config().get('medusa.restore-auto-complete', False):
|
|
79
|
-
# leaf = {id: None for id in [f"{x['metadata']['name']}" for x in CustomResources.medusa_show_backupjobs(dc, ns)]}
|
|
80
|
-
|
|
81
|
-
# return super().completion(state, leaf)
|
|
82
|
-
# else:
|
|
83
|
-
# return sc
|
|
84
|
-
|
|
85
|
-
return {}
|
|
86
70
|
|
|
87
71
|
def help(self, _: ReplState):
|
|
88
72
|
return f'{MedusaRestore.COMMAND}\t start a restore job'
|
adam/commands/nodetool.py
CHANGED
|
@@ -38,7 +38,7 @@ class NodeTool(Command):
|
|
|
38
38
|
return state
|
|
39
39
|
|
|
40
40
|
def completion(self, state: ReplState):
|
|
41
|
-
return super().completion(state, {c: {'&': None} for c in NODETOOL_COMMANDS}, pods=Devices.
|
|
41
|
+
return super().completion(state, {c: {'&': None} for c in NODETOOL_COMMANDS}, pods=Devices.of(state).pods(state, '-'))
|
|
42
42
|
|
|
43
43
|
def help(self, _: ReplState):
|
|
44
44
|
return f'{NodeTool.COMMAND} <sub-command> [&]\t run nodetool with arguments'
|
|
File without changes
|
adam/commands/os/cat.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 Cat(Command):
|
|
7
|
+
COMMAND = 'cat'
|
|
8
|
+
|
|
9
|
+
# the singleton pattern
|
|
10
|
+
def __new__(cls, *args, **kwargs):
|
|
11
|
+
if not hasattr(cls, 'instance'): cls.instance = super(Cat, 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 Cat.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'{Cat.COMMAND} file [&]\t run cat command on the pod'
|
|
@@ -0,0 +1,47 @@
|
|
|
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.config import Config
|
|
5
|
+
from adam.pod_exec_result import PodExecResult
|
|
6
|
+
from adam.repl_state import ReplState, RequiredState
|
|
7
|
+
from adam.utils import log2
|
|
8
|
+
from adam.utils_k8s.pods import Pods
|
|
9
|
+
|
|
10
|
+
class DownloadFile(Command):
|
|
11
|
+
COMMAND = 'download file'
|
|
12
|
+
|
|
13
|
+
# the singleton pattern
|
|
14
|
+
def __new__(cls, *args, **kwargs):
|
|
15
|
+
if not hasattr(cls, 'instance'): cls.instance = super(DownloadFile, cls).__new__(cls)
|
|
16
|
+
|
|
17
|
+
return cls.instance
|
|
18
|
+
|
|
19
|
+
def __init__(self, successor: Command=None):
|
|
20
|
+
super().__init__(successor)
|
|
21
|
+
|
|
22
|
+
def command(self):
|
|
23
|
+
return DownloadFile.COMMAND
|
|
24
|
+
|
|
25
|
+
def required(self):
|
|
26
|
+
return [RequiredState.CLUSTER_OR_POD, RequiredState.APP_APP, ReplState.P]
|
|
27
|
+
|
|
28
|
+
def run(self, cmd: str, state: ReplState):
|
|
29
|
+
if not(args := self.args(cmd)):
|
|
30
|
+
return super().run(cmd, state)
|
|
31
|
+
|
|
32
|
+
with self.validate(args, state) as (args, state):
|
|
33
|
+
with validate_args(args, state, name='file'):
|
|
34
|
+
to_file = Pods.download_file(Devices.of(state).pod(state),
|
|
35
|
+
Devices.of(state).default_container(state),
|
|
36
|
+
state.namespace,
|
|
37
|
+
args[0],
|
|
38
|
+
args[1] if len(args) > 1 else None)
|
|
39
|
+
log2(f'Downloaded to {to_file}.')
|
|
40
|
+
|
|
41
|
+
return state
|
|
42
|
+
|
|
43
|
+
def completion(self, state: ReplState):
|
|
44
|
+
return super().completion(state, lambda: {f: None for f in Devices.of(state).files(state)}, pods=Devices.of(state).pods(state, '-'), auto='jit')
|
|
45
|
+
|
|
46
|
+
def help(self, _: ReplState):
|
|
47
|
+
return f'{DownloadFile.COMMAND} from-file [to-file]\t download file from pod'
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import os
|
|
2
|
+
|
|
3
|
+
from adam.commands.command import Command
|
|
4
|
+
from adam.repl_state import ReplState
|
|
5
|
+
from adam.utils import log2
|
|
6
|
+
from adam.utils_local import local_tmp_dir
|
|
7
|
+
|
|
8
|
+
class FindLocalFiles(Command):
|
|
9
|
+
COMMAND = 'find local'
|
|
10
|
+
|
|
11
|
+
# the singleton pattern
|
|
12
|
+
def __new__(cls, *args, **kwargs):
|
|
13
|
+
if not hasattr(cls, 'instance'): cls.instance = super(FindLocalFiles, 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 FindLocalFiles.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, state):
|
|
28
|
+
cmd = 'find'
|
|
29
|
+
|
|
30
|
+
if not args:
|
|
31
|
+
cmd = f'find {local_tmp_dir()}'
|
|
32
|
+
elif len(args) == 1:
|
|
33
|
+
cmd = f"find {local_tmp_dir()} -name '{args[0]}'"
|
|
34
|
+
else:
|
|
35
|
+
new_args = [f"'{arg}'" if '*' in arg else arg for arg in args]
|
|
36
|
+
cmd = 'find ' + ' '.join(new_args)
|
|
37
|
+
|
|
38
|
+
log2(cmd)
|
|
39
|
+
os.system(cmd)
|
|
40
|
+
|
|
41
|
+
return state
|
|
42
|
+
|
|
43
|
+
def completion(self, state: ReplState):
|
|
44
|
+
return super().completion(state, {
|
|
45
|
+
'*.csv': None,
|
|
46
|
+
'*.db': None,
|
|
47
|
+
'*': None
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
def help(self, _: ReplState):
|
|
51
|
+
return f'{FindLocalFiles.COMMAND} [linux-find-arguments]\t find files from local machine'
|