kaqing 2.0.98__py3-none-any.whl → 2.0.203__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.
- adam/__init__.py +0 -2
- adam/app_session.py +9 -12
- adam/apps.py +18 -4
- adam/batch.py +11 -25
- adam/checks/check_utils.py +16 -46
- adam/checks/cpu.py +7 -1
- adam/checks/cpu_metrics.py +52 -0
- adam/checks/disk.py +2 -3
- adam/columns/columns.py +3 -1
- adam/columns/cpu.py +3 -1
- adam/columns/cpu_metrics.py +22 -0
- adam/columns/memory.py +3 -4
- adam/commands/__init__.py +24 -0
- adam/commands/alter_tables.py +37 -63
- adam/commands/app/app.py +38 -0
- adam/commands/{app_ping.py → app/app_ping.py} +8 -14
- adam/commands/app/show_app_actions.py +49 -0
- adam/commands/{show → app}/show_app_id.py +8 -11
- adam/commands/{show → app}/show_app_queues.py +8 -14
- adam/commands/app/utils_app.py +106 -0
- adam/commands/audit/audit.py +31 -35
- adam/commands/audit/audit_repair_tables.py +26 -48
- adam/commands/audit/audit_run.py +50 -0
- adam/commands/audit/completions_l.py +15 -0
- adam/commands/audit/show_last10.py +36 -0
- adam/commands/audit/show_slow10.py +36 -0
- adam/commands/audit/show_top10.py +36 -0
- adam/commands/audit/utils_show_top10.py +71 -0
- adam/commands/bash/__init__.py +5 -0
- adam/commands/bash/bash.py +36 -0
- adam/commands/bash/bash_completer.py +93 -0
- adam/commands/bash/utils_bash.py +16 -0
- 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/{restart.py → cassandra/restart_cluster.py} +12 -26
- 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 +36 -0
- adam/commands/cd.py +14 -92
- adam/commands/check.py +18 -21
- adam/commands/cli_commands.py +8 -4
- adam/commands/clipboard_copy.py +87 -0
- adam/commands/code.py +57 -0
- adam/commands/command.py +212 -39
- adam/commands/commands_utils.py +20 -28
- adam/commands/cql/alter_tables.py +66 -0
- adam/commands/cql/completions_c.py +29 -0
- adam/commands/cql/cqlsh.py +10 -29
- adam/commands/cql/utils_cql.py +305 -0
- 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/deploy/code_start.py +7 -10
- adam/commands/deploy/code_stop.py +4 -21
- adam/commands/deploy/code_utils.py +3 -3
- adam/commands/deploy/deploy.py +4 -21
- adam/commands/deploy/deploy_frontend.py +14 -17
- adam/commands/deploy/deploy_pg_agent.py +3 -6
- adam/commands/deploy/deploy_pod.py +65 -73
- adam/commands/deploy/deploy_utils.py +14 -24
- adam/commands/deploy/undeploy.py +4 -21
- adam/commands/deploy/undeploy_frontend.py +4 -7
- adam/commands/deploy/undeploy_pg_agent.py +6 -8
- adam/commands/deploy/undeploy_pod.py +11 -12
- adam/commands/devices/__init__.py +0 -0
- adam/commands/devices/device.py +149 -0
- adam/commands/devices/device_app.py +163 -0
- adam/commands/devices/device_auit_log.py +49 -0
- adam/commands/devices/device_cass.py +179 -0
- adam/commands/devices/device_export.py +87 -0
- adam/commands/devices/device_postgres.py +160 -0
- adam/commands/devices/devices.py +25 -0
- adam/commands/download_cassandra_log.py +45 -0
- adam/commands/download_file.py +47 -0
- adam/commands/exit.py +1 -4
- adam/commands/export/__init__.py +0 -0
- adam/commands/export/clean_up_all_export_sessions.py +37 -0
- adam/commands/export/clean_up_export_sessions.py +39 -0
- adam/commands/export/completions_x.py +11 -0
- adam/commands/export/download_export_session.py +40 -0
- adam/commands/export/drop_export_database.py +39 -0
- adam/commands/export/drop_export_databases.py +37 -0
- adam/commands/export/export.py +37 -0
- adam/commands/export/export_databases.py +247 -0
- adam/commands/export/export_select.py +34 -0
- adam/commands/export/export_sessions.py +211 -0
- adam/commands/export/export_use.py +49 -0
- adam/commands/export/export_x_select.py +48 -0
- adam/commands/export/exporter.py +361 -0
- adam/commands/export/import_files.py +44 -0
- adam/commands/export/import_session.py +44 -0
- adam/commands/export/importer.py +82 -0
- adam/commands/export/importer_athena.py +150 -0
- adam/commands/export/importer_sqlite.py +69 -0
- adam/commands/export/show_column_counts.py +45 -0
- adam/commands/export/show_export_databases.py +39 -0
- adam/commands/export/show_export_session.py +39 -0
- adam/commands/export/show_export_sessions.py +37 -0
- adam/commands/export/utils_export.py +366 -0
- adam/commands/find_files.py +51 -0
- adam/commands/find_processes.py +76 -0
- adam/commands/generate_report.py +52 -0
- adam/commands/head.py +36 -0
- adam/commands/help.py +12 -8
- adam/commands/intermediate_command.py +52 -0
- adam/commands/issues.py +14 -40
- adam/commands/kubectl.py +38 -0
- adam/commands/login.py +26 -25
- adam/commands/ls.py +11 -116
- adam/commands/medusa/medusa.py +4 -22
- adam/commands/medusa/medusa_backup.py +20 -27
- adam/commands/medusa/medusa_restore.py +35 -48
- adam/commands/medusa/medusa_show_backupjobs.py +17 -18
- adam/commands/medusa/medusa_show_restorejobs.py +13 -18
- adam/commands/medusa/utils_medusa.py +15 -0
- adam/commands/nodetool.py +8 -19
- 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/param_get.py +11 -14
- adam/commands/param_set.py +8 -12
- adam/commands/postgres/completions_p.py +22 -0
- adam/commands/postgres/postgres.py +47 -55
- adam/commands/postgres/postgres_databases.py +269 -0
- adam/commands/postgres/postgres_ls.py +4 -8
- adam/commands/postgres/postgres_preview.py +5 -9
- adam/commands/postgres/utils_postgres.py +79 -0
- adam/commands/preview_table.py +10 -61
- adam/commands/pwd.py +14 -46
- adam/commands/reaper/reaper.py +4 -24
- adam/commands/reaper/reaper_forward.py +49 -56
- adam/commands/reaper/reaper_forward_session.py +6 -0
- adam/commands/reaper/reaper_forward_stop.py +10 -16
- adam/commands/reaper/reaper_restart.py +7 -14
- adam/commands/reaper/reaper_run_abort.py +8 -33
- adam/commands/reaper/reaper_runs.py +43 -58
- adam/commands/reaper/reaper_runs_abort.py +29 -49
- adam/commands/reaper/reaper_schedule_activate.py +14 -33
- adam/commands/reaper/reaper_schedule_start.py +9 -33
- adam/commands/reaper/reaper_schedule_stop.py +9 -33
- adam/commands/reaper/reaper_schedules.py +4 -14
- adam/commands/reaper/reaper_status.py +8 -16
- adam/commands/reaper/utils_reaper.py +203 -0
- adam/commands/repair/repair.py +4 -22
- adam/commands/repair/repair_log.py +5 -11
- adam/commands/repair/repair_run.py +27 -34
- adam/commands/repair/repair_scan.py +32 -40
- adam/commands/repair/repair_stop.py +5 -12
- adam/commands/restart_cluster.py +47 -0
- adam/commands/restart_node.py +51 -0
- adam/commands/restart_nodes.py +47 -0
- adam/commands/rollout.py +19 -24
- adam/commands/shell.py +12 -4
- adam/commands/show/show.py +10 -23
- adam/commands/show/show_adam.py +3 -3
- adam/commands/show/show_cassandra_repairs.py +37 -0
- adam/commands/show/show_cassandra_status.py +47 -51
- adam/commands/show/show_cassandra_version.py +5 -18
- adam/commands/show/show_cli_commands.py +56 -0
- adam/commands/show/show_host.py +1 -1
- adam/commands/show/show_login.py +23 -27
- adam/commands/show/show_params.py +2 -5
- adam/commands/show/show_processes.py +18 -21
- adam/commands/show/show_storage.py +11 -20
- adam/commands/watch.py +26 -29
- adam/config.py +5 -15
- adam/embedded_params.py +1 -1
- adam/log.py +4 -4
- adam/repl.py +105 -133
- adam/repl_commands.py +68 -28
- adam/repl_session.py +9 -1
- adam/repl_state.py +300 -62
- adam/sql/async_executor.py +44 -0
- adam/sql/lark_completer.py +286 -0
- adam/sql/lark_parser.py +604 -0
- adam/sql/qingl.lark +1076 -0
- adam/sql/sql_completer.py +104 -64
- adam/sql/sql_state_machine.py +630 -0
- adam/sql/term_completer.py +3 -0
- adam/sso/authn_ad.py +6 -8
- adam/sso/authn_okta.py +4 -6
- adam/sso/cred_cache.py +3 -5
- adam/sso/idp.py +9 -12
- adam/utils.py +640 -10
- adam/utils_athena.py +140 -87
- adam/utils_audits.py +102 -0
- adam/utils_issues.py +32 -0
- adam/utils_k8s/app_clusters.py +28 -0
- adam/utils_k8s/app_pods.py +35 -0
- adam/utils_k8s/cassandra_clusters.py +34 -21
- adam/utils_k8s/cassandra_nodes.py +9 -6
- adam/utils_k8s/custom_resources.py +16 -17
- adam/utils_k8s/ingresses.py +2 -2
- adam/utils_k8s/jobs.py +7 -11
- adam/utils_k8s/k8s.py +96 -0
- adam/utils_k8s/kube_context.py +3 -6
- adam/{pod_exec_result.py → utils_k8s/pod_exec_result.py} +11 -5
- adam/utils_k8s/pods.py +146 -75
- adam/utils_k8s/secrets.py +4 -4
- adam/utils_k8s/service_accounts.py +5 -4
- adam/utils_k8s/services.py +2 -2
- adam/utils_k8s/statefulsets.py +6 -14
- adam/utils_local.py +42 -0
- adam/utils_net.py +4 -4
- adam/utils_repl/__init__.py +0 -0
- adam/utils_repl/appendable_completer.py +6 -0
- adam/utils_repl/automata_completer.py +48 -0
- adam/utils_repl/repl_completer.py +89 -0
- adam/utils_repl/state_machine.py +173 -0
- adam/utils_sqlite.py +137 -0
- adam/version.py +1 -1
- {kaqing-2.0.98.dist-info → kaqing-2.0.203.dist-info}/METADATA +1 -1
- kaqing-2.0.203.dist-info/RECORD +277 -0
- kaqing-2.0.203.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/app.py +0 -67
- adam/commands/bash.py +0 -92
- adam/commands/cp.py +0 -95
- adam/commands/cql/cql_completions.py +0 -11
- adam/commands/cql/cql_table_completer.py +0 -8
- adam/commands/cql/cql_utils.py +0 -115
- adam/commands/describe/describe.py +0 -47
- adam/commands/describe/describe_keyspace.py +0 -60
- adam/commands/describe/describe_keyspaces.py +0 -49
- adam/commands/describe/describe_schema.py +0 -49
- adam/commands/describe/describe_table.py +0 -60
- adam/commands/describe/describe_tables.py +0 -49
- adam/commands/devices.py +0 -118
- adam/commands/logs.py +0 -39
- adam/commands/postgres/postgres_session.py +0 -240
- adam/commands/postgres/postgres_utils.py +0 -31
- adam/commands/postgres/psql_completions.py +0 -10
- adam/commands/postgres/psql_table_completer.py +0 -11
- adam/commands/reaper/reaper_session.py +0 -159
- adam/commands/report.py +0 -57
- adam/commands/show/show_app_actions.py +0 -53
- adam/commands/show/show_commands.py +0 -61
- adam/commands/show/show_repairs.py +0 -47
- adam/sql/state_machine.py +0 -460
- kaqing-2.0.98.dist-info/RECORD +0 -191
- kaqing-2.0.98.dist-info/top_level.txt +0 -1
- /adam/commands/{describe → app}/__init__.py +0 -0
- {kaqing-2.0.98.dist-info → kaqing-2.0.203.dist-info}/WHEEL +0 -0
- {kaqing-2.0.98.dist-info → kaqing-2.0.203.dist-info}/entry_points.txt +0 -0
adam/repl_state.py
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
|
-
import copy
|
|
1
|
+
from copy import copy
|
|
2
2
|
from enum import Enum
|
|
3
3
|
import re
|
|
4
|
+
from typing import Callable
|
|
4
5
|
|
|
5
|
-
from adam.
|
|
6
|
+
from adam.utils_k8s.app_clusters import AppClusters
|
|
7
|
+
from adam.utils_k8s.app_pods import AppPods
|
|
6
8
|
from adam.utils_k8s.cassandra_clusters import CassandraClusters
|
|
7
9
|
from adam.utils_k8s.cassandra_nodes import CassandraNodes
|
|
8
10
|
from adam.utils_k8s.kube_context import KubeContext
|
|
@@ -17,25 +19,8 @@ class BashSession:
|
|
|
17
19
|
def pwd(self, state: 'ReplState'):
|
|
18
20
|
command = f'cat /tmp/.qing-{self.session_id}'
|
|
19
21
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
elif state.sts:
|
|
23
|
-
rs = CassandraClusters.exec(state.sts, state.namespace, command, action='bash', show_out=False)
|
|
24
|
-
|
|
25
|
-
dir = None
|
|
26
|
-
for r in rs:
|
|
27
|
-
if r.exit_code(): # if fails to read the session file, ignore
|
|
28
|
-
continue
|
|
29
|
-
|
|
30
|
-
dir0 = r.stdout.strip(' \r\n')
|
|
31
|
-
if dir:
|
|
32
|
-
if dir != dir0:
|
|
33
|
-
log2('Inconsitent working dir found across multiple pods.')
|
|
34
|
-
return None
|
|
35
|
-
else:
|
|
36
|
-
dir = dir0
|
|
37
|
-
|
|
38
|
-
return dir
|
|
22
|
+
with device(state) as pods:
|
|
23
|
+
return pods.exec(command, action='bash', show_out=False)
|
|
39
24
|
|
|
40
25
|
class RequiredState(Enum):
|
|
41
26
|
CLUSTER = 'cluster'
|
|
@@ -44,17 +29,22 @@ class RequiredState(Enum):
|
|
|
44
29
|
NAMESPACE = 'namespace'
|
|
45
30
|
PG_DATABASE = 'pg_database'
|
|
46
31
|
APP_APP = 'app_app'
|
|
32
|
+
EXPORT_DB = 'export_db'
|
|
47
33
|
|
|
48
34
|
class ReplState:
|
|
49
35
|
A = 'a'
|
|
50
36
|
C = 'c'
|
|
51
37
|
L = 'l'
|
|
52
38
|
P = 'p'
|
|
39
|
+
X = 'x'
|
|
40
|
+
|
|
41
|
+
ANY = [A, C, L, P, X]
|
|
42
|
+
NON_L = [A, C, P, X]
|
|
53
43
|
|
|
54
44
|
def __init__(self, device: str = None,
|
|
55
45
|
sts: str = None, pod: str = None, namespace: str = None, ns_sts: str = None,
|
|
56
46
|
pg_path: str = None,
|
|
57
|
-
app_env: str = None, app_app: str = None,
|
|
47
|
+
app_env: str = None, app_app: str = None, app_pod: str = None,
|
|
58
48
|
in_repl = False, bash_session: BashSession = None, remote_dir = None):
|
|
59
49
|
self.namespace = KubeContext.in_cluster_namespace()
|
|
60
50
|
|
|
@@ -64,12 +54,16 @@ class ReplState:
|
|
|
64
54
|
self.pg_path = pg_path
|
|
65
55
|
self.app_env = app_env
|
|
66
56
|
self.app_app = app_app
|
|
57
|
+
self.app_pod = app_pod
|
|
67
58
|
if namespace:
|
|
68
59
|
self.namespace = namespace
|
|
69
60
|
self.in_repl = in_repl
|
|
70
61
|
self.bash_session = bash_session
|
|
71
62
|
self.remote_dir = remote_dir
|
|
72
|
-
|
|
63
|
+
self.original_state: ReplState = None
|
|
64
|
+
self.pod_targetted = False
|
|
65
|
+
|
|
66
|
+
self.export_session: str = None
|
|
73
67
|
|
|
74
68
|
if ns_sts:
|
|
75
69
|
nn = ns_sts.split('@')
|
|
@@ -84,13 +78,51 @@ class ReplState:
|
|
|
84
78
|
def __hash__(self):
|
|
85
79
|
return hash((self.sts, self.pod))
|
|
86
80
|
|
|
81
|
+
def __str__(self):
|
|
82
|
+
msg = ''
|
|
83
|
+
if self.device == ReplState.P:
|
|
84
|
+
msg = f'{ReplState.P}:'
|
|
85
|
+
host, database = self.pg_host_n_database()
|
|
86
|
+
if database:
|
|
87
|
+
msg += database
|
|
88
|
+
elif host:
|
|
89
|
+
msg += host
|
|
90
|
+
elif self.device == ReplState.A:
|
|
91
|
+
msg = f'{ReplState.A}:'
|
|
92
|
+
if self.app_env:
|
|
93
|
+
msg += self.app_env
|
|
94
|
+
if self.app_app:
|
|
95
|
+
msg += f'/{self.app_app}'
|
|
96
|
+
if self.app_pod:
|
|
97
|
+
# azops88-c3-c3-k8sdeploy-appleader-001-79957cf5b6-9k4bw
|
|
98
|
+
group = re.match(r".*?-.*?-.*?-.*?-(.*?-.*?)-.*", self.app_pod)
|
|
99
|
+
msg += '/' + group[1]
|
|
100
|
+
elif self.device == ReplState.L:
|
|
101
|
+
msg = f'{ReplState.L}:'
|
|
102
|
+
elif self.device == ReplState.X:
|
|
103
|
+
msg = f'{ReplState.X}:'
|
|
104
|
+
if self.export_session:
|
|
105
|
+
msg += self.export_session
|
|
106
|
+
else:
|
|
107
|
+
msg = f'{ReplState.C}:'
|
|
108
|
+
if self.pod:
|
|
109
|
+
# cs-d0767a536f-cs-d0767a536f-default-sts-0
|
|
110
|
+
group = re.match(r".*?-.*?-(.*)", self.pod)
|
|
111
|
+
msg += group[1]
|
|
112
|
+
elif self.sts:
|
|
113
|
+
# cs-d0767a536f-cs-d0767a536f-default-sts
|
|
114
|
+
group = re.match(r".*?-.*?-(.*)", self.sts)
|
|
115
|
+
msg += group[1]
|
|
116
|
+
|
|
117
|
+
return msg
|
|
118
|
+
|
|
87
119
|
def apply_args(self, args: list[str], cmd: list[str] = None, resolve_pg = True, args_to_check = 6) -> tuple['ReplState', list[str]]:
|
|
88
120
|
state = self
|
|
89
121
|
|
|
90
122
|
new_args = []
|
|
91
123
|
for index, arg in enumerate(args):
|
|
92
124
|
if index < args_to_check:
|
|
93
|
-
state = copy
|
|
125
|
+
state = copy(state)
|
|
94
126
|
|
|
95
127
|
s, n = KubeContext.is_sts_name(arg)
|
|
96
128
|
if s:
|
|
@@ -128,9 +160,9 @@ class ReplState:
|
|
|
128
160
|
new_args = []
|
|
129
161
|
for index, arg in enumerate(args):
|
|
130
162
|
if index < 6:
|
|
131
|
-
state = copy
|
|
163
|
+
state = copy(state)
|
|
132
164
|
|
|
133
|
-
groups = re.match(r'^([a|c|l|p]):(.*)$', arg)
|
|
165
|
+
groups = re.match(r'^([a|c|l|p|x]):(.*)$', arg)
|
|
134
166
|
if groups:
|
|
135
167
|
if groups[1] == 'p':
|
|
136
168
|
state.device = 'p'
|
|
@@ -149,6 +181,8 @@ class ReplState:
|
|
|
149
181
|
state.namespace = ns
|
|
150
182
|
elif groups[1] == 'l':
|
|
151
183
|
state.device = 'l'
|
|
184
|
+
elif groups[1] == 'x':
|
|
185
|
+
state.device = 'x'
|
|
152
186
|
else:
|
|
153
187
|
state.device = 'a'
|
|
154
188
|
if path := groups[2]:
|
|
@@ -166,84 +200,288 @@ class ReplState:
|
|
|
166
200
|
|
|
167
201
|
return (state, new_args)
|
|
168
202
|
|
|
169
|
-
def validate(self, required: RequiredState =
|
|
170
|
-
if not
|
|
171
|
-
|
|
172
|
-
|
|
203
|
+
def validate(self, required: list[RequiredState] = [], show_err = True):
|
|
204
|
+
if not required:
|
|
205
|
+
return True
|
|
206
|
+
|
|
207
|
+
def default_err():
|
|
208
|
+
if self.in_repl:
|
|
209
|
+
log2(f'Not a valid command on {self.device}: drive.')
|
|
210
|
+
else:
|
|
211
|
+
log2('* on a wrong device.')
|
|
212
|
+
log2()
|
|
213
|
+
display_help()
|
|
214
|
+
|
|
215
|
+
if type(required) is not list:
|
|
216
|
+
valid, err = self._validate(required)
|
|
217
|
+
if valid:
|
|
218
|
+
return True
|
|
219
|
+
|
|
220
|
+
if show_err:
|
|
221
|
+
if err:
|
|
222
|
+
err()
|
|
223
|
+
else:
|
|
224
|
+
default_err()
|
|
225
|
+
|
|
226
|
+
return False
|
|
227
|
+
|
|
228
|
+
devices = [r for r in required if r in [ReplState.L, ReplState.A, ReplState.C, ReplState.P, ReplState.X]]
|
|
229
|
+
non_devices = [r for r in required if r not in [ReplState.L, ReplState.A, ReplState.C, ReplState.P, ReplState.X]]
|
|
230
|
+
|
|
231
|
+
first_error: Callable = None
|
|
232
|
+
for r in non_devices:
|
|
233
|
+
valid, err = self._validate(r)
|
|
234
|
+
if valid:
|
|
235
|
+
return True
|
|
236
|
+
|
|
237
|
+
if not first_error:
|
|
238
|
+
first_error = err
|
|
239
|
+
|
|
240
|
+
if devices:
|
|
241
|
+
valid, err = self._validate_device(devices)
|
|
242
|
+
if valid:
|
|
243
|
+
return True
|
|
244
|
+
|
|
245
|
+
if not first_error:
|
|
246
|
+
first_error = err
|
|
247
|
+
|
|
248
|
+
if show_err and first_error:
|
|
249
|
+
if first_error:
|
|
250
|
+
first_error()
|
|
251
|
+
else:
|
|
252
|
+
default_err()
|
|
253
|
+
|
|
254
|
+
return False
|
|
255
|
+
|
|
256
|
+
def _validate(self, required: RequiredState):
|
|
257
|
+
if required == RequiredState.CLUSTER:
|
|
258
|
+
if self.device != ReplState.C:
|
|
259
|
+
return (False, None)
|
|
260
|
+
|
|
261
|
+
if not self.namespace or not self.sts:
|
|
262
|
+
def error():
|
|
173
263
|
if self.in_repl:
|
|
174
264
|
log2('cd to a Cassandra cluster first.')
|
|
175
265
|
else:
|
|
176
266
|
log2('* Cassandra cluster is missing.')
|
|
177
267
|
log2()
|
|
178
268
|
display_help()
|
|
269
|
+
return (False, error)
|
|
179
270
|
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
271
|
+
elif required == RequiredState.POD:
|
|
272
|
+
if self.device != ReplState.C:
|
|
273
|
+
return (False, None)
|
|
274
|
+
|
|
275
|
+
if not self.namespace or not self.pod:
|
|
276
|
+
def error():
|
|
183
277
|
if self.in_repl:
|
|
184
278
|
log2('cd to a pod first.')
|
|
185
279
|
else:
|
|
186
280
|
log2('* Pod is missing.')
|
|
187
281
|
log2()
|
|
188
282
|
display_help()
|
|
283
|
+
return (False, error)
|
|
284
|
+
|
|
285
|
+
elif required == RequiredState.CLUSTER_OR_POD:
|
|
286
|
+
if self.device != ReplState.C:
|
|
287
|
+
return (False, None)
|
|
189
288
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
if not self.namespace or not self.sts and not self.pod:
|
|
289
|
+
if not self.namespace or not self.sts and not self.pod:
|
|
290
|
+
def error():
|
|
193
291
|
if self.in_repl:
|
|
194
292
|
log2('cd to a Cassandra cluster first.')
|
|
195
293
|
else:
|
|
196
294
|
log2('* Cassandra cluster or pod is missing.')
|
|
197
295
|
log2()
|
|
198
296
|
display_help()
|
|
297
|
+
return (False, error)
|
|
199
298
|
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
299
|
+
elif required == RequiredState.NAMESPACE:
|
|
300
|
+
if self.device != ReplState.C:
|
|
301
|
+
return (False, None)
|
|
302
|
+
|
|
303
|
+
if not self.namespace:
|
|
304
|
+
def error():
|
|
203
305
|
if self.in_repl:
|
|
204
306
|
log2('Namespace is required.')
|
|
205
307
|
else:
|
|
206
308
|
log2('* namespace is missing.')
|
|
207
309
|
log2()
|
|
208
310
|
display_help()
|
|
311
|
+
return (False, error)
|
|
312
|
+
|
|
313
|
+
elif required == RequiredState.PG_DATABASE:
|
|
314
|
+
if self.device != ReplState.P:
|
|
315
|
+
return (False, None)
|
|
316
|
+
|
|
317
|
+
_, database = self.pg_host_n_database()
|
|
318
|
+
if not database:
|
|
319
|
+
def error():
|
|
320
|
+
if self.in_repl:
|
|
321
|
+
log2('cd to a database first.')
|
|
322
|
+
else:
|
|
323
|
+
log2('* database is missing.')
|
|
324
|
+
log2()
|
|
325
|
+
display_help()
|
|
326
|
+
return (False, error)
|
|
327
|
+
|
|
328
|
+
elif required == RequiredState.APP_APP:
|
|
329
|
+
if self.device != ReplState.A:
|
|
330
|
+
return (False, None)
|
|
331
|
+
|
|
332
|
+
if not self.app_app:
|
|
333
|
+
def error():
|
|
334
|
+
if self.in_repl:
|
|
335
|
+
log2('cd to an app first.')
|
|
336
|
+
else:
|
|
337
|
+
log2('* app is missing.')
|
|
338
|
+
log2()
|
|
339
|
+
display_help()
|
|
340
|
+
return (False, error)
|
|
209
341
|
|
|
210
|
-
|
|
342
|
+
elif required == RequiredState.EXPORT_DB:
|
|
343
|
+
if self.device not in [ReplState.C, ReplState.X]:
|
|
344
|
+
return (False, None)
|
|
211
345
|
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
346
|
+
if not self.export_session:
|
|
347
|
+
def error():
|
|
348
|
+
if self.in_repl:
|
|
349
|
+
if self.device == ReplState.C:
|
|
350
|
+
log2("Select an export database first with 'use' command.")
|
|
351
|
+
else:
|
|
352
|
+
log2('cd to an export database first.')
|
|
353
|
+
else:
|
|
354
|
+
log2('* export database is missing.')
|
|
355
|
+
log2()
|
|
356
|
+
display_help()
|
|
357
|
+
return (False, error)
|
|
358
|
+
|
|
359
|
+
elif required in [ReplState.L, ReplState.A, ReplState.C, ReplState.P, ReplState.X] and self.device != required:
|
|
360
|
+
def error():
|
|
215
361
|
if self.in_repl:
|
|
216
|
-
log2('
|
|
362
|
+
log2(f'Switch to {required}: first.')
|
|
217
363
|
else:
|
|
218
|
-
log2('*
|
|
364
|
+
log2('* on a wrong device.')
|
|
219
365
|
log2()
|
|
220
366
|
display_help()
|
|
367
|
+
return (False, error)
|
|
221
368
|
|
|
222
|
-
|
|
369
|
+
return (True, None)
|
|
223
370
|
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
371
|
+
def _validate_device(self, devices: list[RequiredState]):
|
|
372
|
+
if self.device not in devices:
|
|
373
|
+
def error():
|
|
374
|
+
if self.in_repl:
|
|
375
|
+
log2(f'Not a valid command on {self.device}: drive.')
|
|
376
|
+
else:
|
|
377
|
+
log2('* on a wrong device.')
|
|
378
|
+
log2()
|
|
379
|
+
display_help()
|
|
380
|
+
return (False, error)
|
|
233
381
|
|
|
234
|
-
return True
|
|
382
|
+
return (True, None)
|
|
235
383
|
|
|
236
384
|
def user_pass(self, secret_path = 'cql.secret'):
|
|
237
385
|
return Secrets.get_user_pass(self.pod if self.pod else self.sts, self.namespace, secret_path=secret_path)
|
|
238
386
|
|
|
239
387
|
def enter_bash(self, bash_session: BashSession):
|
|
388
|
+
self.push()
|
|
389
|
+
|
|
240
390
|
self.bash_session = bash_session
|
|
241
|
-
if self.device != ReplState.C:
|
|
242
|
-
self.device = ReplState.C
|
|
243
|
-
log2(f'Moved to {ReplState.C}: automatically. Will move back to {ReplState.P}: when you exit the bash session.')
|
|
244
391
|
|
|
245
392
|
def exit_bash(self):
|
|
246
|
-
|
|
247
|
-
|
|
393
|
+
self.pop()
|
|
394
|
+
self.bash_session = None
|
|
395
|
+
|
|
396
|
+
def push(self, pod_targetted=False):
|
|
397
|
+
if not self.original_state:
|
|
398
|
+
self.original_state = copy(self)
|
|
399
|
+
self.pod_targetted = pod_targetted
|
|
400
|
+
|
|
401
|
+
def pop(self):
|
|
402
|
+
if o := self.original_state:
|
|
403
|
+
self.device = o.device
|
|
404
|
+
self.sts = o.sts
|
|
405
|
+
self.pod = o.pod
|
|
406
|
+
self.pg_path = o.pg_path
|
|
407
|
+
self.app_env = o.app_env
|
|
408
|
+
self.app_app = o.app_app
|
|
409
|
+
self.app_pod = o.app_pod
|
|
410
|
+
# self.export_session = o.export_session
|
|
411
|
+
self.namespace = o.namespace
|
|
412
|
+
|
|
413
|
+
self.original_state = None
|
|
414
|
+
self.pod_targetted = False
|
|
415
|
+
|
|
416
|
+
def pg_host_n_database(self):
|
|
417
|
+
host = None
|
|
418
|
+
database = None
|
|
419
|
+
|
|
420
|
+
if self.pg_path:
|
|
421
|
+
host_n_db = self.pg_path.split('/')
|
|
422
|
+
host = host_n_db[0]
|
|
423
|
+
if len(host_n_db) > 1:
|
|
424
|
+
database = host_n_db[1]
|
|
425
|
+
|
|
426
|
+
return host, database
|
|
427
|
+
|
|
428
|
+
def with_no_pod(self):
|
|
429
|
+
state1 = copy(self)
|
|
430
|
+
state1.pod = None
|
|
431
|
+
|
|
432
|
+
return state1
|
|
433
|
+
|
|
434
|
+
def with_pod(self, pod: str):
|
|
435
|
+
state1 = copy(self)
|
|
436
|
+
state1.pod = pod
|
|
437
|
+
|
|
438
|
+
return state1
|
|
439
|
+
|
|
440
|
+
class DevicePodService:
|
|
441
|
+
def __init__(self, handler: 'DeviceExecHandler'):
|
|
442
|
+
self.handler = handler
|
|
443
|
+
|
|
444
|
+
def exec(self, command: str, action='bash', show_out = True):
|
|
445
|
+
state = self.handler.state
|
|
446
|
+
|
|
447
|
+
rs = None
|
|
448
|
+
if state.device == ReplState.A and state.app_app:
|
|
449
|
+
if state.app_pod:
|
|
450
|
+
rs = [AppPods.exec(state.app_pod, state.namespace, command, show_out=show_out)]
|
|
451
|
+
else:
|
|
452
|
+
pods = AppPods.pod_names(state.namespace, state.app_env, state.app_app)
|
|
453
|
+
rs = AppClusters.exec(pods, state.namespace, command, show_out=show_out)
|
|
454
|
+
elif state.pod:
|
|
455
|
+
rs = [CassandraNodes.exec(state.pod, state.namespace, command, show_out=show_out)]
|
|
456
|
+
elif state.sts:
|
|
457
|
+
rs = CassandraClusters.exec(state.sts, state.namespace, command, action=action, show_out=show_out)
|
|
458
|
+
# assume that pg-agent or ops pod is single pod
|
|
459
|
+
|
|
460
|
+
dir = None
|
|
461
|
+
if rs:
|
|
462
|
+
for r in rs:
|
|
463
|
+
if r.exit_code(): # if fails to read the session file, ignore
|
|
464
|
+
continue
|
|
465
|
+
|
|
466
|
+
dir0 = r.stdout.strip(' \r\n')
|
|
467
|
+
if dir:
|
|
468
|
+
if dir != dir0:
|
|
469
|
+
log2('Inconsitent working dir found across multiple pods.')
|
|
470
|
+
return None
|
|
471
|
+
else:
|
|
472
|
+
dir = dir0
|
|
473
|
+
|
|
474
|
+
return dir
|
|
475
|
+
|
|
476
|
+
class DeviceExecHandler:
|
|
477
|
+
def __init__(self, state: ReplState):
|
|
478
|
+
self.state = state
|
|
479
|
+
|
|
480
|
+
def __enter__(self):
|
|
481
|
+
return DevicePodService(self)
|
|
482
|
+
|
|
483
|
+
def __exit__(self, exc_type, exc_val, exc_tb):
|
|
484
|
+
return False
|
|
248
485
|
|
|
249
|
-
|
|
486
|
+
def device(state: ReplState):
|
|
487
|
+
return DeviceExecHandler(state)
|
|
@@ -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()))
|