kaqing 2.0.115__py3-none-any.whl → 2.0.172__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/__init__.py +0 -2
- adam/app_session.py +8 -11
- adam/batch.py +3 -3
- adam/checks/check_utils.py +14 -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 +18 -0
- adam/commands/alter_tables.py +43 -47
- adam/commands/audit/audit.py +24 -25
- adam/commands/audit/audit_repair_tables.py +14 -17
- adam/commands/audit/audit_run.py +15 -23
- adam/commands/audit/show_last10.py +10 -13
- adam/commands/audit/show_slow10.py +10 -13
- adam/commands/audit/show_top10.py +10 -14
- adam/commands/audit/utils_show_top10.py +2 -3
- adam/commands/bash/__init__.py +5 -0
- adam/commands/bash/bash.py +8 -96
- adam/commands/bash/utils_bash.py +16 -0
- adam/commands/cat.py +14 -19
- adam/commands/cd.py +12 -100
- adam/commands/check.py +20 -21
- adam/commands/cli_commands.py +2 -3
- adam/commands/code.py +20 -23
- adam/commands/command.py +123 -39
- adam/commands/commands_utils.py +8 -17
- adam/commands/cp.py +33 -39
- adam/commands/cql/cql_completions.py +28 -10
- adam/commands/cql/cqlsh.py +10 -30
- adam/commands/cql/utils_cql.py +343 -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 -27
- adam/commands/deploy/deploy_frontend.py +14 -17
- adam/commands/deploy/deploy_pg_agent.py +2 -5
- adam/commands/deploy/deploy_pod.py +65 -73
- adam/commands/deploy/deploy_utils.py +14 -24
- adam/commands/deploy/undeploy.py +4 -27
- adam/commands/deploy/undeploy_frontend.py +4 -7
- adam/commands/deploy/undeploy_pg_agent.py +5 -7
- adam/commands/deploy/undeploy_pod.py +11 -12
- adam/commands/devices/__init__.py +0 -0
- adam/commands/devices/device.py +118 -0
- adam/commands/devices/device_app.py +173 -0
- adam/commands/devices/device_auit_log.py +49 -0
- adam/commands/devices/device_cass.py +185 -0
- adam/commands/devices/device_export.py +86 -0
- adam/commands/devices/device_postgres.py +144 -0
- adam/commands/devices/devices.py +25 -0
- adam/commands/exit.py +1 -4
- adam/commands/export/clean_up_all_export_sessions.py +37 -0
- adam/commands/export/clean_up_export_sessions.py +51 -0
- adam/commands/export/drop_export_database.py +55 -0
- adam/commands/export/drop_export_databases.py +43 -0
- adam/commands/export/export.py +19 -26
- adam/commands/export/export_databases.py +174 -0
- adam/commands/export/export_handlers.py +71 -0
- adam/commands/export/export_select.py +48 -22
- adam/commands/export/export_select_x.py +54 -0
- adam/commands/export/export_use.py +19 -23
- adam/commands/export/exporter.py +353 -0
- adam/commands/export/import_session.py +40 -0
- adam/commands/export/importer.py +67 -0
- adam/commands/export/importer_athena.py +77 -0
- adam/commands/export/importer_sqlite.py +39 -0
- adam/commands/export/show_column_counts.py +54 -0
- adam/commands/export/show_export_databases.py +36 -0
- adam/commands/export/show_export_session.py +48 -0
- adam/commands/export/show_export_sessions.py +44 -0
- adam/commands/export/utils_export.py +223 -162
- adam/commands/help.py +1 -1
- adam/commands/intermediate_command.py +49 -0
- adam/commands/issues.py +11 -43
- adam/commands/kubectl.py +3 -6
- adam/commands/login.py +22 -24
- adam/commands/logs.py +3 -6
- adam/commands/ls.py +11 -128
- adam/commands/medusa/medusa.py +4 -22
- adam/commands/medusa/medusa_backup.py +20 -24
- adam/commands/medusa/medusa_restore.py +29 -33
- adam/commands/medusa/medusa_show_backupjobs.py +14 -18
- adam/commands/medusa/medusa_show_restorejobs.py +11 -18
- adam/commands/nodetool.py +6 -15
- adam/commands/param_get.py +11 -12
- adam/commands/param_set.py +9 -10
- adam/commands/postgres/postgres.py +41 -34
- adam/commands/postgres/postgres_context.py +57 -24
- adam/commands/postgres/postgres_ls.py +4 -8
- adam/commands/postgres/postgres_preview.py +5 -9
- adam/commands/postgres/psql_completions.py +1 -1
- adam/commands/postgres/utils_postgres.py +66 -0
- adam/commands/preview_table.py +5 -44
- adam/commands/pwd.py +14 -47
- adam/commands/reaper/reaper.py +4 -27
- adam/commands/reaper/reaper_forward.py +48 -55
- 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 +11 -30
- adam/commands/reaper/reaper_runs.py +42 -57
- adam/commands/reaper/reaper_runs_abort.py +29 -49
- adam/commands/reaper/reaper_schedule_activate.py +11 -30
- adam/commands/reaper/reaper_schedule_start.py +10 -29
- adam/commands/reaper/reaper_schedule_stop.py +10 -29
- adam/commands/reaper/reaper_schedules.py +4 -14
- adam/commands/reaper/reaper_status.py +8 -16
- adam/commands/reaper/utils_reaper.py +196 -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 -38
- adam/commands/repair/repair_stop.py +5 -11
- adam/commands/report.py +27 -29
- adam/commands/restart.py +25 -26
- adam/commands/rollout.py +19 -24
- adam/commands/shell.py +10 -4
- adam/commands/show/show.py +10 -25
- adam/commands/show/show_cassandra_repairs.py +35 -0
- adam/commands/show/show_cassandra_status.py +32 -43
- adam/commands/show/show_cassandra_version.py +5 -18
- adam/commands/show/show_commands.py +19 -24
- adam/commands/show/show_host.py +1 -1
- adam/commands/show/show_login.py +20 -27
- adam/commands/show/show_processes.py +15 -19
- adam/commands/show/show_storage.py +10 -20
- adam/commands/watch.py +26 -29
- adam/config.py +5 -14
- adam/embedded_params.py +1 -1
- adam/log.py +4 -4
- adam/pod_exec_result.py +3 -3
- adam/repl.py +40 -103
- adam/repl_commands.py +32 -16
- adam/repl_state.py +57 -28
- adam/sql/sql_completer.py +44 -28
- adam/sql/sql_state_machine.py +89 -28
- 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 +435 -6
- adam/utils_athena.py +57 -37
- adam/utils_audits.py +12 -14
- adam/utils_issues.py +32 -0
- adam/utils_k8s/app_clusters.py +13 -18
- adam/utils_k8s/app_pods.py +2 -0
- adam/utils_k8s/cassandra_clusters.py +22 -19
- adam/utils_k8s/cassandra_nodes.py +2 -2
- 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 +87 -0
- adam/utils_k8s/pods.py +40 -77
- 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 +1 -12
- adam/utils_net.py +4 -4
- adam/utils_repl/__init__.py +0 -0
- adam/utils_repl/automata_completer.py +48 -0
- adam/utils_repl/repl_completer.py +46 -0
- adam/utils_repl/state_machine.py +173 -0
- adam/utils_sqlite.py +137 -0
- adam/version.py +1 -1
- {kaqing-2.0.115.dist-info → kaqing-2.0.172.dist-info}/METADATA +1 -1
- kaqing-2.0.172.dist-info/RECORD +230 -0
- adam/commands/app.py +0 -67
- adam/commands/app_ping.py +0 -44
- adam/commands/cql/cql_utils.py +0 -204
- adam/commands/devices.py +0 -147
- adam/commands/export/export_on_x.py +0 -76
- adam/commands/export/export_rmdbs.py +0 -65
- adam/commands/postgres/postgres_utils.py +0 -31
- adam/commands/reaper/reaper_session.py +0 -159
- adam/commands/show/show_app_actions.py +0 -56
- adam/commands/show/show_app_id.py +0 -47
- adam/commands/show/show_app_queues.py +0 -45
- adam/commands/show/show_repairs.py +0 -47
- adam/utils_export.py +0 -42
- kaqing-2.0.115.dist-info/RECORD +0 -203
- {kaqing-2.0.115.dist-info → kaqing-2.0.172.dist-info}/WHEEL +0 -0
- {kaqing-2.0.115.dist-info → kaqing-2.0.172.dist-info}/entry_points.txt +0 -0
- {kaqing-2.0.115.dist-info → kaqing-2.0.172.dist-info}/top_level.txt +0 -0
adam/commands/kubectl.py
CHANGED
|
@@ -25,13 +25,10 @@ class Kubectl(Command):
|
|
|
25
25
|
if not(args := self.args(cmd)):
|
|
26
26
|
return super().run(cmd, state)
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
return state
|
|
31
|
-
|
|
32
|
-
subprocess.run(["kubectl"] + args)
|
|
28
|
+
with self.validate(args, state) as (args, state):
|
|
29
|
+
subprocess.run(["kubectl"] + args)
|
|
33
30
|
|
|
34
|
-
|
|
31
|
+
return state
|
|
35
32
|
|
|
36
33
|
def completion(self, state: ReplState):
|
|
37
34
|
return super().completion(state)
|
adam/commands/login.py
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import os
|
|
2
2
|
import signal
|
|
3
|
-
import traceback
|
|
4
3
|
|
|
5
4
|
from adam.app_session import AppSession
|
|
6
5
|
from adam.apps import Apps
|
|
6
|
+
from adam.commands import extract_options
|
|
7
7
|
from adam.config import Config
|
|
8
8
|
from adam.sso.idp import Idp
|
|
9
9
|
from adam.sso.idp_login import IdpLogin
|
|
10
10
|
from adam.commands.command import Command
|
|
11
|
-
from adam.repl_state import ReplState
|
|
12
|
-
from adam.utils import log, log2
|
|
11
|
+
from adam.repl_state import ReplState
|
|
12
|
+
from adam.utils import log, log2, log_exc
|
|
13
13
|
|
|
14
14
|
class Login(Command):
|
|
15
15
|
COMMAND = 'login'
|
|
@@ -30,37 +30,35 @@ class Login(Command):
|
|
|
30
30
|
return ReplState.NON_L
|
|
31
31
|
|
|
32
32
|
def run(self, cmd: str, state: ReplState):
|
|
33
|
+
if not(args := self.args(cmd)):
|
|
34
|
+
return super().run(cmd, state)
|
|
35
|
+
|
|
33
36
|
def custom_handler(signum, frame):
|
|
34
37
|
AppSession.ctrl_c_entered = True
|
|
35
38
|
|
|
36
39
|
signal.signal(signal.SIGINT, custom_handler)
|
|
37
40
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
args, debug = Command.extract_options(args, ['d'])
|
|
43
|
-
if debug:
|
|
44
|
-
Config().set('debug', True)
|
|
41
|
+
with self.validate(args, state) as (args, state):
|
|
42
|
+
with extract_options(args, 'd') as (args, debug):
|
|
43
|
+
if debug:
|
|
44
|
+
Config().set('debug', True)
|
|
45
45
|
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
username: str = os.getenv('USERNAME')
|
|
47
|
+
if len(args) > 0:
|
|
48
|
+
username = args[0]
|
|
49
49
|
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
50
|
+
login: IdpLogin = None
|
|
51
|
+
with log_exc(True):
|
|
52
|
+
if not(host := Apps.app_host('c3', 'c3', state.namespace)):
|
|
53
|
+
log2('Cannot locate ingress for app.')
|
|
54
|
+
return state
|
|
55
55
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
except:
|
|
59
|
-
log2(traceback.format_exc())
|
|
56
|
+
if not (login := Idp.login(host, username=username, use_token_from_env=False)):
|
|
57
|
+
log2('Invalid username/password. Please try again.')
|
|
60
58
|
|
|
61
|
-
|
|
59
|
+
log(f'IDP_TOKEN={login.ser() if login else ""}')
|
|
62
60
|
|
|
63
|
-
|
|
61
|
+
return state
|
|
64
62
|
|
|
65
63
|
def completion(self, state: ReplState):
|
|
66
64
|
return super().completion(state)
|
adam/commands/logs.py
CHANGED
|
@@ -25,12 +25,9 @@ class Logs(Command):
|
|
|
25
25
|
if not(args := self.args(cmd)):
|
|
26
26
|
return super().run(cmd, state)
|
|
27
27
|
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
return state
|
|
31
|
-
|
|
32
|
-
path = Config().get('logs.path', '/c3/cassandra/logs/system.log')
|
|
33
|
-
return CassandraNodes.exec(state.pod, state.namespace, f'cat {path}')
|
|
28
|
+
with self.validate(args, state) as (args, state):
|
|
29
|
+
path = Config().get('logs.path', '/c3/cassandra/logs/system.log')
|
|
30
|
+
return CassandraNodes.exec(state.pod, state.namespace, f'cat {path}')
|
|
34
31
|
|
|
35
32
|
def completion(self, _: ReplState):
|
|
36
33
|
# available only on cli
|
adam/commands/ls.py
CHANGED
|
@@ -1,21 +1,8 @@
|
|
|
1
1
|
import copy
|
|
2
2
|
|
|
3
|
-
from adam.commands.bash.bash import Bash
|
|
4
3
|
from adam.commands.command import Command
|
|
5
|
-
from adam.commands.
|
|
6
|
-
from adam.commands.postgres.postgres_utils import pg_database_names, pg_table_names
|
|
7
|
-
from adam.commands.postgres.postgres_context import PostgresContext
|
|
8
|
-
from adam.config import Config
|
|
9
|
-
from adam.utils_athena import Athena
|
|
10
|
-
from adam.utils_k8s.app_pods import AppPods
|
|
11
|
-
from adam.utils_k8s.custom_resources import CustomResources
|
|
12
|
-
from adam.utils_k8s.ingresses import Ingresses
|
|
13
|
-
from adam.utils_k8s.kube_context import KubeContext
|
|
14
|
-
from adam.utils_k8s.statefulsets import StatefulSets
|
|
4
|
+
from adam.commands.devices.devices import Devices
|
|
15
5
|
from adam.repl_state import ReplState
|
|
16
|
-
from adam.utils import lines_to_tabular, log, log2
|
|
17
|
-
from adam.apps import Apps
|
|
18
|
-
from adam.utils_audits import Audits
|
|
19
6
|
|
|
20
7
|
class Ls(Command):
|
|
21
8
|
COMMAND = 'ls'
|
|
@@ -36,123 +23,19 @@ class Ls(Command):
|
|
|
36
23
|
if not(args := self.args(cmd)):
|
|
37
24
|
return super().run(cmd, state)
|
|
38
25
|
|
|
39
|
-
|
|
26
|
+
with self.validate(args, state) as (args, state):
|
|
27
|
+
if len(args) > 0:
|
|
28
|
+
arg = args[0]
|
|
29
|
+
if arg in ['p:', 'c:'] and arg != f'{state.device}:':
|
|
30
|
+
state = copy.copy(state)
|
|
31
|
+
state.device = arg.replace(':', '')
|
|
40
32
|
|
|
41
|
-
|
|
42
|
-
arg = args[0]
|
|
43
|
-
if arg in ['p:', 'c:'] and arg != f'{state.device}:':
|
|
44
|
-
state = copy.copy(state)
|
|
45
|
-
state.device = arg.replace(':', '')
|
|
33
|
+
Devices.device(state).ls(cmd, state)
|
|
46
34
|
|
|
47
|
-
|
|
48
|
-
if state.pg_path:
|
|
49
|
-
pg: PostgresContext = PostgresContext.apply(state.namespace, state.pg_path)
|
|
50
|
-
if pg.db:
|
|
51
|
-
self.show_pg_tables(pg)
|
|
52
|
-
else:
|
|
53
|
-
self.show_pg_databases(pg)
|
|
54
|
-
else:
|
|
55
|
-
self.show_pg_hosts(state)
|
|
56
|
-
elif state.device == ReplState.A:
|
|
57
|
-
if state.app_pod:
|
|
58
|
-
return Bash().run('bash ' + cmd, state)
|
|
59
|
-
elif state.app_app:
|
|
60
|
-
pods = AppPods.pod_names(state.namespace, state.app_env, state.app_app)
|
|
61
|
-
|
|
62
|
-
log(lines_to_tabular(pods, 'POD_NAME'))
|
|
63
|
-
elif state.app_env:
|
|
64
|
-
def line(n: str, ns: str):
|
|
65
|
-
host = Ingresses.get_host(Config().get('app.login.ingress', '{app_id}-k8singr-appleader-001').replace('{app_id}', f'{ns}-{n}'), ns)
|
|
66
|
-
if not host:
|
|
67
|
-
return None
|
|
68
|
-
|
|
69
|
-
endpoint = Config().get('app.login.url', 'https://{host}/{env}/{app}').replace('{host}', host).replace('{env}', state.app_env).replace('{app}', 'c3')
|
|
70
|
-
if not endpoint:
|
|
71
|
-
return None
|
|
72
|
-
|
|
73
|
-
return f"{n.split('-')[1]},{Ingresses.get_host(f'{ns}-{n}-k8singr-appleader-001', ns)},{endpoint}"
|
|
74
|
-
|
|
75
|
-
svcs = [l for l in [line(n, ns) for n, ns in Apps.apps(state.app_env)] if l]
|
|
76
|
-
|
|
77
|
-
log(lines_to_tabular(svcs, 'APP,HOST,ENDPOINT', separator=','))
|
|
78
|
-
else:
|
|
79
|
-
svcs = [n for n, ns in Apps.envs()]
|
|
80
|
-
|
|
81
|
-
log(lines_to_tabular(svcs, 'ENV', separator=','))
|
|
82
|
-
elif state.device == ReplState.L:
|
|
83
|
-
self.show_audit_log_tables()
|
|
84
|
-
else:
|
|
85
|
-
if state.pod:
|
|
86
|
-
return Bash().run('bash ' + cmd, state)
|
|
87
|
-
elif state.sts and state.namespace:
|
|
88
|
-
show_pods(StatefulSets.pods(state.sts, state.namespace), state.namespace, show_namespace=not KubeContext.in_cluster_namespace())
|
|
89
|
-
show_rollout(state.sts, state.namespace)
|
|
90
|
-
else:
|
|
91
|
-
self.show_statefulsets()
|
|
92
|
-
|
|
93
|
-
return state
|
|
94
|
-
|
|
95
|
-
def show_statefulsets(self):
|
|
96
|
-
ss = StatefulSets.list_sts_names()
|
|
97
|
-
if len(ss) == 0:
|
|
98
|
-
log2('No Cassandra clusters found.')
|
|
99
|
-
return
|
|
100
|
-
|
|
101
|
-
app_ids = CustomResources.get_app_ids()
|
|
102
|
-
list = []
|
|
103
|
-
for s in ss:
|
|
104
|
-
cr_name = CustomResources.get_cr_name(s)
|
|
105
|
-
app_id = 'Unknown'
|
|
106
|
-
if cr_name in app_ids:
|
|
107
|
-
app_id = app_ids[cr_name]
|
|
108
|
-
list.append(f"{s} {app_id}")
|
|
109
|
-
|
|
110
|
-
header = 'STATEFULSET_NAME@NAMESPACE APP_ID'
|
|
111
|
-
if KubeContext.in_cluster_namespace():
|
|
112
|
-
header = 'STATEFULSET_NAME APP_ID'
|
|
113
|
-
log(lines_to_tabular(list, header))
|
|
114
|
-
|
|
115
|
-
def show_pg_hosts(self, state: ReplState):
|
|
116
|
-
if state.namespace:
|
|
117
|
-
def line(pg: PostgresContext):
|
|
118
|
-
return f'{pg.path()},{pg.endpoint()}:{pg.port()},{pg.username()},{pg.password()}'
|
|
119
|
-
|
|
120
|
-
lines = [line(PostgresContext.apply(state.namespace, pg)) for pg in PostgresContext.hosts(state.namespace)]
|
|
121
|
-
|
|
122
|
-
log(lines_to_tabular(lines, 'NAME,ENDPOINT,USERNAME,PASSWORD', separator=','))
|
|
123
|
-
else:
|
|
124
|
-
def line(pg: PostgresContext):
|
|
125
|
-
return f'{pg.path()},{pg.namespace},{pg.endpoint()}:{pg.port()},{pg.username()},{pg.password()}'
|
|
126
|
-
|
|
127
|
-
lines = [line(PostgresContext.apply(state.namespace, pg)) for pg in PostgresContext.hosts(state.namespace)]
|
|
128
|
-
|
|
129
|
-
log(lines_to_tabular(lines, 'NAME,NAMESPACE,ENDPOINT,USERNAME,PASSWORD', separator=','))
|
|
130
|
-
|
|
131
|
-
def show_pg_databases(self, pg: PostgresContext):
|
|
132
|
-
log(lines_to_tabular(pg_database_names(pg.namespace, pg.path()), 'DATABASE', separator=','))
|
|
133
|
-
|
|
134
|
-
def show_pg_tables(self, pg: PostgresContext):
|
|
135
|
-
log(lines_to_tabular(pg_table_names(pg.namespace, pg.path()), 'NAME', separator=','))
|
|
136
|
-
|
|
137
|
-
def show_audit_log_tables(self):
|
|
138
|
-
log(lines_to_tabular(Athena.table_names(), 'NAME', separator=','))
|
|
35
|
+
return state
|
|
139
36
|
|
|
140
37
|
def completion(self, state: ReplState):
|
|
141
|
-
|
|
142
|
-
def pod_names():
|
|
143
|
-
return [p for p in StatefulSets.pod_names(state.sts, state.namespace)]
|
|
144
|
-
|
|
145
|
-
if state.sts:
|
|
146
|
-
return super().completion(state) | {f'@{p}': {'ls': None} for p in pod_names()}
|
|
147
|
-
else:
|
|
148
|
-
return {Ls.COMMAND: {n: None for n in StatefulSets.list_sts_names()}}
|
|
149
|
-
elif state.device == ReplState.A and state.app_app:
|
|
150
|
-
def pod_names():
|
|
151
|
-
return [p for p in AppPods.pod_names(state.namespace, state.app_env, state.app_app)]
|
|
152
|
-
|
|
153
|
-
return super().completion(state) | {f'@{p}': {'ls': None} for p in pod_names()}
|
|
154
|
-
|
|
155
|
-
return super().completion(state)
|
|
38
|
+
return Devices.device(state).ls_completion(Ls.COMMAND, state, default = super().completion(state))
|
|
156
39
|
|
|
157
40
|
def help(self, _: ReplState):
|
|
158
|
-
return f'{Ls.COMMAND} [device:]\t list apps, envs, clusters, nodes, pg hosts or
|
|
41
|
+
return f'{Ls.COMMAND} [device:]\t list apps, envs, clusters, nodes, pg hosts/databases or export databases'
|
adam/commands/medusa/medusa.py
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import click
|
|
2
2
|
|
|
3
3
|
from adam.commands.command import Command
|
|
4
|
+
from adam.commands.intermediate_command import IntermediateCommand
|
|
4
5
|
from .medusa_backup import MedusaBackup
|
|
5
6
|
from .medusa_restore import MedusaRestore
|
|
6
7
|
from .medusa_show_backupjobs import MedusaShowBackupJobs
|
|
7
8
|
from .medusa_show_restorejobs import MedusaShowRestoreJobs
|
|
8
|
-
from adam.repl_state import ReplState, RequiredState
|
|
9
9
|
|
|
10
|
-
class Medusa(
|
|
10
|
+
class Medusa(IntermediateCommand):
|
|
11
11
|
COMMAND = 'medusa'
|
|
12
12
|
|
|
13
13
|
# the singleton pattern
|
|
@@ -16,30 +16,12 @@ class Medusa(Command):
|
|
|
16
16
|
|
|
17
17
|
return cls.instance
|
|
18
18
|
|
|
19
|
-
def __init__(self, successor: Command=None):
|
|
20
|
-
super().__init__(successor)
|
|
21
|
-
|
|
22
19
|
def command(self):
|
|
23
20
|
return Medusa.COMMAND
|
|
24
21
|
|
|
25
|
-
def
|
|
26
|
-
return RequiredState.CLUSTER
|
|
27
|
-
|
|
28
|
-
def run(self, cmd: str, state: ReplState):
|
|
29
|
-
if not(args := self.args(cmd)):
|
|
30
|
-
return super().run(cmd, state)
|
|
31
|
-
|
|
32
|
-
return super().intermediate_run(cmd, state, args, Medusa.cmd_list())
|
|
33
|
-
|
|
34
|
-
def cmd_list():
|
|
22
|
+
def cmd_list(self):
|
|
35
23
|
return [MedusaBackup(), MedusaRestore(), MedusaShowBackupJobs(), MedusaShowRestoreJobs()]
|
|
36
24
|
|
|
37
|
-
def completion(self, state: ReplState):
|
|
38
|
-
if state.sts:
|
|
39
|
-
return super().completion(state)
|
|
40
|
-
|
|
41
|
-
return {}
|
|
42
|
-
|
|
43
25
|
class MedusaCommandHelper(click.Command):
|
|
44
26
|
def get_help(self, ctx: click.Context):
|
|
45
|
-
|
|
27
|
+
IntermediateCommand.intermediate_help(super().get_help(ctx), Medusa.COMMAND, Medusa().cmd_list(), show_cluster_help=True)
|
|
@@ -7,7 +7,6 @@ from adam.repl_state import ReplState, RequiredState
|
|
|
7
7
|
from adam.utils_k8s.custom_resources import CustomResources
|
|
8
8
|
from adam.utils import log2
|
|
9
9
|
|
|
10
|
-
|
|
11
10
|
class MedusaBackup(Command):
|
|
12
11
|
COMMAND = 'backup'
|
|
13
12
|
|
|
@@ -29,33 +28,30 @@ class MedusaBackup(Command):
|
|
|
29
28
|
def run(self, cmd: str, state: ReplState):
|
|
30
29
|
if not(args := self.args(cmd)):
|
|
31
30
|
return super().run(cmd, state)
|
|
32
|
-
state, args = self.apply_state(args, state)
|
|
33
|
-
if not self.validate_state(state):
|
|
34
|
-
return state
|
|
35
|
-
|
|
36
|
-
ns = state.namespace
|
|
37
|
-
sts = state.sts
|
|
38
|
-
now_dtformat = datetime.now().strftime("%Y-%m-%d.%H.%M.%S")
|
|
39
|
-
bkname = 'medusa-' + now_dtformat + 'full-backup-' + sts
|
|
40
|
-
if len(args) == 1:
|
|
41
|
-
bkname = str(args[0])
|
|
42
|
-
groups = re.match(r'^(.*?-.*?-).*', sts)
|
|
43
|
-
dc = StatefulSets.get_datacenter(state.sts, ns)
|
|
44
|
-
if not dc:
|
|
45
|
-
return state
|
|
46
31
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
32
|
+
with self.validate(args, state) as (args, state):
|
|
33
|
+
ns = state.namespace
|
|
34
|
+
sts = state.sts
|
|
35
|
+
now_dtformat = datetime.now().strftime("%Y-%m-%d.%H.%M.%S")
|
|
36
|
+
bkname = 'medusa-' + now_dtformat + 'full-backup-' + sts
|
|
37
|
+
if len(args) == 1:
|
|
38
|
+
bkname = str(args[0])
|
|
39
|
+
groups = re.match(r'^(.*?-.*?-).*', sts)
|
|
40
|
+
dc = StatefulSets.get_datacenter(state.sts, ns)
|
|
41
|
+
if not dc:
|
|
42
|
+
return state
|
|
43
|
+
|
|
44
|
+
try:
|
|
45
|
+
CustomResources.create_medusa_backupjob(bkname, dc, ns)
|
|
46
|
+
except Exception as e:
|
|
47
|
+
log2("Exception: MedusaBackup failed: %s\n" % e)
|
|
48
|
+
finally:
|
|
49
|
+
CustomResources.clear_caches()
|
|
51
50
|
|
|
52
|
-
|
|
51
|
+
return state
|
|
53
52
|
|
|
54
53
|
def completion(self, state: ReplState):
|
|
55
|
-
|
|
56
|
-
return super().completion(state)
|
|
57
|
-
|
|
58
|
-
return {}
|
|
54
|
+
return super().completion(state)
|
|
59
55
|
|
|
60
56
|
def help(self, _: ReplState):
|
|
61
57
|
return f'{MedusaBackup.COMMAND}\t start a backup job'
|
|
@@ -5,7 +5,7 @@ from adam.utils_k8s.statefulsets import StatefulSets
|
|
|
5
5
|
from adam.repl_state import ReplState, RequiredState
|
|
6
6
|
from adam.utils_k8s.custom_resources import CustomResources
|
|
7
7
|
from adam.config import Config
|
|
8
|
-
from adam.utils import lines_to_tabular, log2
|
|
8
|
+
from adam.utils import lines_to_tabular, log2, log_exc
|
|
9
9
|
|
|
10
10
|
class MedusaRestore(Command):
|
|
11
11
|
COMMAND = 'restore'
|
|
@@ -28,47 +28,43 @@ class MedusaRestore(Command):
|
|
|
28
28
|
def run(self, cmd: str, state: ReplState):
|
|
29
29
|
if not(args := self.args(cmd)):
|
|
30
30
|
return super().run(cmd, state)
|
|
31
|
-
state, args = self.apply_state(args, state)
|
|
32
|
-
if not self.validate_state(state):
|
|
33
|
-
return state
|
|
34
31
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
32
|
+
with self.validate(args, state) as (args, state):
|
|
33
|
+
ns = state.namespace
|
|
34
|
+
dc: str = StatefulSets.get_datacenter(state.sts, ns)
|
|
35
|
+
if not dc:
|
|
36
|
+
return state
|
|
39
37
|
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
38
|
+
if len(args) == 1:
|
|
39
|
+
bkname = args[0]
|
|
40
|
+
job = CustomResources.medusa_get_backupjob(dc, ns, bkname)
|
|
41
|
+
if not job:
|
|
42
|
+
log2('\n* Backup job name is not valid.')
|
|
43
|
+
bklist = [f"{x['metadata']['name']}\t{x['metadata']['creationTimestamp']}\t{x['status'].get('finishTime', '')}" for x in CustomResources.medusa_show_backupjobs(dc, ns)]
|
|
44
|
+
log2(lines_to_tabular(bklist, 'NAME\tCREATED\tFINISHED', separator='\t'))
|
|
47
45
|
|
|
48
|
-
|
|
46
|
+
return state
|
|
49
47
|
|
|
50
|
-
|
|
48
|
+
if not input(f"Restoring from {bkname} created at {job['metadata']['creationTimestamp']}. Please enter Yes to continue: ").lower() in ['y', 'yes']:
|
|
49
|
+
return state
|
|
50
|
+
else:
|
|
51
|
+
bklist = [f"{x['metadata']['name']}\t{x['metadata']['creationTimestamp']}\t{x['status'].get('finishTime', '')}" for x in CustomResources.medusa_show_backupjobs(dc, ns)]
|
|
52
|
+
log2('\n* Missing Backup Name')
|
|
53
|
+
log2('Usage: qing medusa restore <backup> <sts@name_space>\n')
|
|
54
|
+
log2(lines_to_tabular(bklist, 'NAME\tCREATED\tFINISHED', separator='\t'))
|
|
51
55
|
return state
|
|
52
|
-
else:
|
|
53
|
-
bklist = [f"{x['metadata']['name']}\t{x['metadata']['creationTimestamp']}\t{x['status'].get('finishTime', '')}" for x in CustomResources.medusa_show_backupjobs(dc, ns)]
|
|
54
|
-
log2('\n* Missing Backup Name')
|
|
55
|
-
log2('Usage: qing medusa restore <backup> <sts@name_space>\n')
|
|
56
|
-
log2(lines_to_tabular(bklist, 'NAME\tCREATED\tFINISHED', separator='\t'))
|
|
57
|
-
return state
|
|
58
56
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
except Exception as e:
|
|
64
|
-
log2("Exception: MedusaRestore failed: %s\n" % e)
|
|
57
|
+
now_dtformat = datetime.now().strftime("%Y-%m-%d.%H.%M.%S")
|
|
58
|
+
rtname = 'medusa-' + now_dtformat + '-restore-from-' + bkname
|
|
59
|
+
with log_exc(lambda e: "Exception: MedusaRestore failed: %s\n" % e):
|
|
60
|
+
CustomResources.create_medusa_restorejob(rtname, bkname, dc, ns)
|
|
65
61
|
|
|
66
|
-
|
|
62
|
+
return state
|
|
67
63
|
|
|
68
64
|
def completion(self, state: ReplState):
|
|
69
|
-
if state
|
|
65
|
+
if sc := super().completion(state):
|
|
70
66
|
ns = state.namespace
|
|
71
|
-
dc = StatefulSets.get_datacenter(state.sts, ns)
|
|
67
|
+
dc: str = StatefulSets.get_datacenter(state.sts, ns)
|
|
72
68
|
if not dc:
|
|
73
69
|
return {}
|
|
74
70
|
|
|
@@ -77,7 +73,7 @@ class MedusaRestore(Command):
|
|
|
77
73
|
|
|
78
74
|
return super().completion(state, leaf)
|
|
79
75
|
else:
|
|
80
|
-
return
|
|
76
|
+
return sc
|
|
81
77
|
|
|
82
78
|
return {}
|
|
83
79
|
|
|
@@ -2,7 +2,7 @@ from adam.commands.command import Command
|
|
|
2
2
|
from adam.utils_k8s.statefulsets import StatefulSets
|
|
3
3
|
from adam.repl_state import ReplState, RequiredState
|
|
4
4
|
from adam.utils_k8s.custom_resources import CustomResources
|
|
5
|
-
from adam.utils import lines_to_tabular, log2
|
|
5
|
+
from adam.utils import lines_to_tabular, log2, log_exc
|
|
6
6
|
|
|
7
7
|
|
|
8
8
|
class MedusaShowBackupJobs(Command):
|
|
@@ -26,28 +26,24 @@ class MedusaShowBackupJobs(Command):
|
|
|
26
26
|
def run(self, cmd: str, state: ReplState):
|
|
27
27
|
if not(args := self.args(cmd)):
|
|
28
28
|
return super().run(cmd, state)
|
|
29
|
-
state, args = self.apply_state(args, state)
|
|
30
|
-
if not self.validate_state(state):
|
|
31
|
-
return state
|
|
32
29
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
30
|
+
with self.validate(args, state) as (args, state):
|
|
31
|
+
ns = state.namespace
|
|
32
|
+
dc = StatefulSets.get_datacenter(state.sts, ns)
|
|
33
|
+
if not dc:
|
|
34
|
+
return state
|
|
37
35
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
except Exception as e:
|
|
42
|
-
log2("Exception: MedusaShowBackupJobs failed: %s\n" % e)
|
|
36
|
+
# try:
|
|
37
|
+
with log_exc(lambda e: "Exception: MedusaShowBackupJobs failed: %s\n" % e):
|
|
38
|
+
CustomResources.clear_caches()
|
|
43
39
|
|
|
44
|
-
|
|
40
|
+
bklist = [f"{x['metadata']['name']}\t{x['metadata']['creationTimestamp']}\t{x['status'].get('finishTime', '') if 'status' in x else 'unknown'}" for x in CustomResources.medusa_show_backupjobs(dc, ns)]
|
|
41
|
+
log2(lines_to_tabular(bklist, 'NAME\tCREATED\tFINISHED', separator='\t'))
|
|
45
42
|
|
|
46
|
-
|
|
47
|
-
if state.sts:
|
|
48
|
-
return super().completion(state)
|
|
43
|
+
return state
|
|
49
44
|
|
|
50
|
-
|
|
45
|
+
def completion(self, state: ReplState):
|
|
46
|
+
return super().completion(state)
|
|
51
47
|
|
|
52
48
|
def help(self, _: ReplState):
|
|
53
49
|
return f'{MedusaShowBackupJobs.COMMAND}\t show Medusa backups'
|
|
@@ -2,7 +2,7 @@ from adam.commands.command import Command
|
|
|
2
2
|
from adam.utils_k8s.statefulsets import StatefulSets
|
|
3
3
|
from adam.repl_state import ReplState, RequiredState
|
|
4
4
|
from adam.utils_k8s.custom_resources import CustomResources
|
|
5
|
-
from adam.utils import lines_to_tabular, log2
|
|
5
|
+
from adam.utils import lines_to_tabular, log2, log_exc
|
|
6
6
|
|
|
7
7
|
class MedusaShowRestoreJobs(Command):
|
|
8
8
|
COMMAND = 'show restores'
|
|
@@ -25,28 +25,21 @@ class MedusaShowRestoreJobs(Command):
|
|
|
25
25
|
def run(self, cmd: str, state: ReplState):
|
|
26
26
|
if not(args := self.args(cmd)):
|
|
27
27
|
return super().run(cmd, state)
|
|
28
|
-
state, args = self.apply_state(args, state)
|
|
29
|
-
if not self.validate_state(state):
|
|
30
|
-
return state
|
|
31
28
|
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
29
|
+
with self.validate(args, state) as (args, state):
|
|
30
|
+
ns = state.namespace
|
|
31
|
+
dc = StatefulSets.get_datacenter(state.sts, ns)
|
|
32
|
+
if not dc:
|
|
33
|
+
return state
|
|
36
34
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
except Exception as e:
|
|
41
|
-
log2("Exception: MedusaShowRestoreJobs failed: %s\n" % e)
|
|
35
|
+
with log_exc(lambda e: "Exception: MedusaShowRestoreJobs failed: %s\n" % e):
|
|
36
|
+
rtlist = CustomResources.medusa_show_restorejobs(dc, ns)
|
|
37
|
+
log2(lines_to_tabular(rtlist, 'NAME\tCREATED\tFINISHED', separator='\t'))
|
|
42
38
|
|
|
43
|
-
|
|
39
|
+
return state
|
|
44
40
|
|
|
45
41
|
def completion(self, state: ReplState):
|
|
46
|
-
|
|
47
|
-
return super().completion(state)
|
|
48
|
-
|
|
49
|
-
return {}
|
|
42
|
+
return super().completion(state)
|
|
50
43
|
|
|
51
44
|
def help(self, _: ReplState):
|
|
52
45
|
return f'{MedusaShowRestoreJobs.COMMAND}\t show Medusa restores'
|
adam/commands/nodetool.py
CHANGED
|
@@ -2,10 +2,9 @@ import click
|
|
|
2
2
|
|
|
3
3
|
from adam.commands.command import Command
|
|
4
4
|
from adam.commands.command_helpers import ClusterOrPodCommandHelper
|
|
5
|
+
from adam.commands.cql.utils_cql import cassandra
|
|
5
6
|
from adam.commands.nodetool_commands import NODETOOL_COMMANDS
|
|
6
7
|
from adam.config import Config
|
|
7
|
-
from adam.utils_k8s.cassandra_clusters import CassandraClusters
|
|
8
|
-
from adam.utils_k8s.cassandra_nodes import CassandraNodes
|
|
9
8
|
from adam.repl_state import ReplState, RequiredState
|
|
10
9
|
from adam.utils import log
|
|
11
10
|
from adam.utils_k8s.statefulsets import StatefulSets
|
|
@@ -32,22 +31,14 @@ class NodeTool(Command):
|
|
|
32
31
|
if not(args := self.args(cmd)):
|
|
33
32
|
return super().run(cmd, state)
|
|
34
33
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
user, pw = state.user_pass()
|
|
40
|
-
command = f"nodetool -u {user} -pw {pw} {' '.join(args)}"
|
|
34
|
+
with self.validate(args, state) as (args, state):
|
|
35
|
+
with cassandra(state) as pods:
|
|
36
|
+
pods.nodetool(' '.join(args))
|
|
41
37
|
|
|
42
|
-
|
|
43
|
-
return CassandraNodes.exec(state.pod, state.namespace, command, show_out=True)
|
|
44
|
-
elif state.sts:
|
|
45
|
-
return CassandraClusters.exec(state.sts, state.namespace, command, action='nodetool', show_out=True)
|
|
46
|
-
|
|
47
|
-
return state
|
|
38
|
+
return state
|
|
48
39
|
|
|
49
40
|
def completion(self, state: ReplState):
|
|
50
|
-
if
|
|
41
|
+
if super().completion(state):
|
|
51
42
|
d = {c: {'&': None} for c in NODETOOL_COMMANDS}
|
|
52
43
|
return {NodeTool.COMMAND: {'help': None} | d} | \
|
|
53
44
|
{f'@{p}': {NodeTool.COMMAND: d} for p in StatefulSets.pod_names(state.sts, state.namespace)}
|
adam/commands/param_get.py
CHANGED
|
@@ -22,21 +22,20 @@ class GetParam(Command):
|
|
|
22
22
|
if not(args := self.args(cmd)):
|
|
23
23
|
return super().run(cmd, state)
|
|
24
24
|
|
|
25
|
-
|
|
25
|
+
with self.validate(args, state) as (args, state):
|
|
26
|
+
if len(args) < 1:
|
|
27
|
+
lines = [f'{key}\t{Config().get(key, None)}' for key in Config().keys()]
|
|
28
|
+
log(lines_to_tabular(lines, separator='\t'))
|
|
26
29
|
|
|
27
|
-
|
|
28
|
-
lines = [f'{key}\t{Config().get(key, None)}' for key in Config().keys()]
|
|
29
|
-
log(lines_to_tabular(lines, separator='\t'))
|
|
30
|
+
return state
|
|
30
31
|
|
|
31
|
-
|
|
32
|
+
key = args[0]
|
|
33
|
+
if v := Config().get(key, None):
|
|
34
|
+
log(v)
|
|
35
|
+
else:
|
|
36
|
+
log2(f'{key} is not set.')
|
|
32
37
|
|
|
33
|
-
|
|
34
|
-
if v := Config().get(key, None):
|
|
35
|
-
log(v)
|
|
36
|
-
else:
|
|
37
|
-
log2(f'{key} is not set.')
|
|
38
|
-
|
|
39
|
-
return v if v else state
|
|
38
|
+
return v if v else state
|
|
40
39
|
|
|
41
40
|
def completion(self, _: ReplState):
|
|
42
41
|
return {GetParam.COMMAND: {key: None for key in Config().keys()}}
|