kaqing 2.0.14__py3-none-any.whl → 2.0.145__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/apps.py +2 -2
- adam/batch.py +13 -3
- adam/checks/check_utils.py +4 -4
- adam/checks/compactionstats.py +1 -1
- adam/checks/cpu.py +2 -2
- adam/checks/disk.py +1 -1
- adam/checks/gossip.py +1 -1
- adam/checks/memory.py +3 -3
- adam/checks/status.py +1 -1
- adam/commands/alter_tables.py +81 -0
- adam/commands/app.py +3 -3
- adam/commands/app_ping.py +2 -2
- adam/commands/audit/audit.py +86 -0
- adam/commands/audit/audit_repair_tables.py +77 -0
- adam/commands/audit/audit_run.py +58 -0
- adam/commands/audit/show_last10.py +51 -0
- adam/commands/audit/show_slow10.py +50 -0
- adam/commands/audit/show_top10.py +48 -0
- adam/commands/audit/utils_show_top10.py +59 -0
- adam/commands/bash/__init__.py +0 -0
- adam/commands/bash/bash.py +133 -0
- adam/commands/bash/bash_completer.py +93 -0
- adam/commands/cat.py +56 -0
- adam/commands/cd.py +12 -82
- adam/commands/check.py +6 -0
- adam/commands/cli_commands.py +3 -3
- adam/commands/code.py +60 -0
- adam/commands/command.py +48 -12
- adam/commands/commands_utils.py +4 -5
- adam/commands/cql/__init__.py +0 -0
- adam/commands/cql/cql_completions.py +28 -0
- adam/commands/cql/cql_utils.py +209 -0
- adam/commands/{cqlsh.py → cql/cqlsh.py} +15 -10
- adam/commands/deploy/code_utils.py +2 -2
- adam/commands/deploy/deploy.py +8 -21
- adam/commands/deploy/deploy_frontend.py +1 -1
- adam/commands/deploy/deploy_pg_agent.py +3 -3
- adam/commands/deploy/deploy_pod.py +28 -27
- adam/commands/deploy/deploy_utils.py +16 -26
- adam/commands/deploy/undeploy.py +8 -21
- adam/commands/deploy/undeploy_frontend.py +1 -1
- adam/commands/deploy/undeploy_pg_agent.py +5 -3
- adam/commands/deploy/undeploy_pod.py +12 -10
- adam/commands/devices/__init__.py +0 -0
- adam/commands/devices/device.py +27 -0
- adam/commands/devices/device_app.py +146 -0
- adam/commands/devices/device_auit_log.py +43 -0
- adam/commands/devices/device_cass.py +145 -0
- adam/commands/devices/device_export.py +86 -0
- adam/commands/devices/device_postgres.py +109 -0
- adam/commands/devices/devices.py +25 -0
- adam/commands/export/__init__.py +0 -0
- adam/commands/export/clean_up_export_session.py +53 -0
- adam/commands/export/clean_up_export_sessions.py +40 -0
- adam/commands/export/drop_export_database.py +58 -0
- adam/commands/export/drop_export_databases.py +46 -0
- adam/commands/export/export.py +83 -0
- adam/commands/export/export_databases.py +170 -0
- adam/commands/export/export_select.py +85 -0
- adam/commands/export/export_select_x.py +54 -0
- adam/commands/export/export_use.py +55 -0
- adam/commands/export/exporter.py +364 -0
- adam/commands/export/import_session.py +68 -0
- adam/commands/export/importer.py +67 -0
- adam/commands/export/importer_athena.py +80 -0
- adam/commands/export/importer_sqlite.py +47 -0
- adam/commands/export/show_column_counts.py +63 -0
- adam/commands/export/show_export_databases.py +39 -0
- adam/commands/export/show_export_session.py +51 -0
- adam/commands/export/show_export_sessions.py +47 -0
- adam/commands/export/utils_export.py +291 -0
- adam/commands/help.py +12 -7
- adam/commands/issues.py +6 -0
- adam/commands/kubectl.py +41 -0
- adam/commands/login.py +7 -4
- adam/commands/logs.py +2 -1
- adam/commands/ls.py +4 -107
- adam/commands/medusa/medusa.py +2 -26
- adam/commands/medusa/medusa_backup.py +2 -2
- adam/commands/medusa/medusa_restore.py +3 -4
- adam/commands/medusa/medusa_show_backupjobs.py +4 -3
- adam/commands/medusa/medusa_show_restorejobs.py +3 -3
- adam/commands/nodetool.py +9 -4
- adam/commands/param_set.py +1 -1
- adam/commands/postgres/postgres.py +42 -43
- adam/commands/postgres/{postgres_session.py → postgres_context.py} +43 -42
- adam/commands/postgres/postgres_utils.py +31 -0
- adam/commands/postgres/psql_completions.py +10 -0
- adam/commands/preview_table.py +18 -40
- adam/commands/pwd.py +2 -28
- adam/commands/reaper/reaper.py +4 -24
- adam/commands/reaper/reaper_restart.py +1 -1
- adam/commands/reaper/reaper_session.py +2 -2
- adam/commands/repair/repair.py +3 -27
- adam/commands/repair/repair_log.py +1 -1
- adam/commands/repair/repair_run.py +2 -2
- adam/commands/repair/repair_scan.py +1 -1
- adam/commands/repair/repair_stop.py +1 -1
- adam/commands/report.py +6 -0
- adam/commands/restart.py +2 -2
- adam/commands/rollout.py +1 -1
- adam/commands/show/show.py +11 -26
- adam/commands/show/show_app_actions.py +3 -0
- adam/commands/show/show_app_id.py +1 -1
- adam/commands/show/show_app_queues.py +3 -2
- adam/commands/show/show_cassandra_status.py +3 -3
- adam/commands/show/show_cassandra_version.py +3 -3
- adam/commands/show/show_host.py +33 -0
- adam/commands/show/show_login.py +3 -0
- adam/commands/show/show_processes.py +1 -1
- adam/commands/show/show_repairs.py +2 -2
- adam/commands/show/show_storage.py +1 -1
- adam/commands/watch.py +1 -1
- adam/config.py +16 -3
- adam/embedded_params.py +1 -1
- adam/pod_exec_result.py +10 -2
- adam/repl.py +127 -117
- adam/repl_commands.py +51 -16
- adam/repl_state.py +276 -55
- adam/sql/__init__.py +0 -0
- adam/sql/sql_completer.py +120 -0
- adam/sql/sql_state_machine.py +617 -0
- adam/sql/term_completer.py +76 -0
- adam/sso/authn_ad.py +1 -1
- adam/sso/cred_cache.py +1 -1
- adam/sso/idp.py +1 -1
- adam/utils.py +83 -2
- adam/utils_athena.py +145 -0
- adam/utils_audits.py +102 -0
- adam/utils_k8s/__init__.py +0 -0
- adam/utils_k8s/app_clusters.py +33 -0
- adam/utils_k8s/app_pods.py +31 -0
- adam/{k8s_utils → utils_k8s}/cassandra_clusters.py +6 -21
- adam/{k8s_utils → utils_k8s}/cassandra_nodes.py +12 -5
- adam/{k8s_utils → utils_k8s}/deployment.py +2 -2
- adam/{k8s_utils → utils_k8s}/kube_context.py +1 -1
- adam/{k8s_utils → utils_k8s}/pods.py +119 -26
- adam/{k8s_utils → utils_k8s}/secrets.py +4 -0
- adam/{k8s_utils → utils_k8s}/statefulsets.py +5 -4
- adam/utils_net.py +24 -0
- 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 +101 -0
- adam/version.py +1 -1
- {kaqing-2.0.14.dist-info → kaqing-2.0.145.dist-info}/METADATA +1 -1
- kaqing-2.0.145.dist-info/RECORD +227 -0
- adam/commands/bash.py +0 -87
- adam/commands/cql_utils.py +0 -53
- adam/commands/devices.py +0 -89
- kaqing-2.0.14.dist-info/RECORD +0 -167
- /adam/{k8s_utils → commands/audit}/__init__.py +0 -0
- /adam/{k8s_utils → utils_k8s}/config_maps.py +0 -0
- /adam/{k8s_utils → utils_k8s}/custom_resources.py +0 -0
- /adam/{k8s_utils → utils_k8s}/ingresses.py +0 -0
- /adam/{k8s_utils → utils_k8s}/jobs.py +0 -0
- /adam/{k8s_utils → utils_k8s}/service_accounts.py +0 -0
- /adam/{k8s_utils → utils_k8s}/services.py +0 -0
- /adam/{k8s_utils → utils_k8s}/volumes.py +0 -0
- {kaqing-2.0.14.dist-info → kaqing-2.0.145.dist-info}/WHEEL +0 -0
- {kaqing-2.0.14.dist-info → kaqing-2.0.145.dist-info}/entry_points.txt +0 -0
- {kaqing-2.0.14.dist-info → kaqing-2.0.145.dist-info}/top_level.txt +0 -0
adam/commands/ls.py
CHANGED
|
@@ -1,20 +1,8 @@
|
|
|
1
1
|
import copy
|
|
2
|
-
import re
|
|
3
2
|
|
|
4
3
|
from adam.commands.command import Command
|
|
5
|
-
from adam.commands.
|
|
6
|
-
from adam.commands.cqlsh import Cqlsh
|
|
7
|
-
from adam.commands.postgres.postgres_session import PostgresSession
|
|
8
|
-
from adam.config import Config
|
|
9
|
-
from adam.k8s_utils.custom_resources import CustomResources
|
|
10
|
-
from adam.k8s_utils.ingresses import Ingresses
|
|
11
|
-
from adam.k8s_utils.kube_context import KubeContext
|
|
12
|
-
from adam.k8s_utils.services import Services
|
|
13
|
-
from adam.k8s_utils.statefulsets import StatefulSets
|
|
14
|
-
from adam.pod_exec_result import PodExecResult
|
|
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
6
|
|
|
19
7
|
class Ls(Command):
|
|
20
8
|
COMMAND = 'ls'
|
|
@@ -43,103 +31,12 @@ class Ls(Command):
|
|
|
43
31
|
state = copy.copy(state)
|
|
44
32
|
state.device = arg.replace(':', '')
|
|
45
33
|
|
|
46
|
-
|
|
47
|
-
if state.pg_path:
|
|
48
|
-
pg = PostgresSession(state.namespace, state.pg_path)
|
|
49
|
-
if pg.db:
|
|
50
|
-
self.show_pg_tables(pg)
|
|
51
|
-
else:
|
|
52
|
-
self.show_pg_databases(pg)
|
|
53
|
-
else:
|
|
54
|
-
self.show_pg_hosts(state)
|
|
55
|
-
elif state.device == ReplState.A:
|
|
56
|
-
if state.app_env:
|
|
57
|
-
def line(n: str, ns: str):
|
|
58
|
-
host = Ingresses.get_host(Config().get('app.login.ingress', '{app_id}-k8singr-appleader-001').replace('{app_id}', f'{ns}-{n}'), ns)
|
|
59
|
-
if not host:
|
|
60
|
-
return None
|
|
61
|
-
|
|
62
|
-
endpoint = Config().get('app.login.url', 'https://{host}/{env}/{app}').replace('{host}', host).replace('{env}', state.app_env).replace('{app}', 'c3')
|
|
63
|
-
if not endpoint:
|
|
64
|
-
return None
|
|
65
|
-
|
|
66
|
-
return f"{n.split('-')[1]},{Ingresses.get_host(f'{ns}-{n}-k8singr-appleader-001', ns)},{endpoint}"
|
|
67
|
-
|
|
68
|
-
svcs = [l for l in [line(n, ns) for n, ns in Apps.apps(state.app_env)] if l]
|
|
69
|
-
|
|
70
|
-
log(lines_to_tabular(svcs, 'APP,HOST,ENDPOINT', separator=','))
|
|
71
|
-
else:
|
|
72
|
-
svcs = [n for n, ns in Apps.envs()]
|
|
73
|
-
|
|
74
|
-
log(lines_to_tabular(svcs, 'ENV', separator=','))
|
|
75
|
-
else:
|
|
76
|
-
if state.pod:
|
|
77
|
-
r: PodExecResult = Cqlsh().run(f'cql describe tables', state)
|
|
78
|
-
if r.stderr:
|
|
79
|
-
log(r.stderr)
|
|
80
|
-
log(r.stdout)
|
|
81
|
-
elif state.sts and state.namespace:
|
|
82
|
-
show_pods(StatefulSets.pods(state.sts, state.namespace), state.namespace, show_namespace=not KubeContext.in_cluster_namespace())
|
|
83
|
-
show_rollout(state.sts, state.namespace)
|
|
84
|
-
else:
|
|
85
|
-
self.show_statefulsets()
|
|
34
|
+
Devices.device(state).ls(cmd, state)
|
|
86
35
|
|
|
87
36
|
return state
|
|
88
37
|
|
|
89
|
-
def show_statefulsets(self):
|
|
90
|
-
ss = StatefulSets.list_sts_names(show_namespace=not KubeContext.in_cluster_namespace())
|
|
91
|
-
if len(ss) == 0:
|
|
92
|
-
log2('No cassandra statefulsets found.')
|
|
93
|
-
return
|
|
94
|
-
|
|
95
|
-
app_ids = CustomResources.get_app_ids()
|
|
96
|
-
list = []
|
|
97
|
-
for s in ss:
|
|
98
|
-
cr_name = CustomResources.get_cr_name(s)
|
|
99
|
-
app_id = 'Unknown'
|
|
100
|
-
if cr_name in app_ids:
|
|
101
|
-
app_id = app_ids[cr_name]
|
|
102
|
-
list.append(f"{s} {app_id}")
|
|
103
|
-
|
|
104
|
-
header = 'STATEFULSET_NAME@NAMESPACE APP_ID'
|
|
105
|
-
if KubeContext.in_cluster_namespace():
|
|
106
|
-
header = 'STATEFULSET_NAME APP_ID'
|
|
107
|
-
log(lines_to_tabular(list, header))
|
|
108
|
-
|
|
109
|
-
def show_pg_hosts(self, state: ReplState):
|
|
110
|
-
if state.namespace:
|
|
111
|
-
def line(pg: PostgresSession):
|
|
112
|
-
return f'{pg.directory()},{pg.endpoint()}:{pg.port()},{pg.username()},{pg.password()}'
|
|
113
|
-
|
|
114
|
-
lines = [line(PostgresSession(state.namespace, pg)) for pg in PostgresSession.hosts(state.namespace)]
|
|
115
|
-
|
|
116
|
-
log(lines_to_tabular(lines, 'NAME,ENDPOINT,USERNAME,PASSWORD', separator=','))
|
|
117
|
-
else:
|
|
118
|
-
def line(pg: PostgresSession):
|
|
119
|
-
return f'{pg.directory()},{pg.namespace},{pg.endpoint()}:{pg.port()},{pg.username()},{pg.password()}'
|
|
120
|
-
|
|
121
|
-
lines = [line(PostgresSession(state.namespace, pg)) for pg in PostgresSession.hosts(state.namespace)]
|
|
122
|
-
|
|
123
|
-
log(lines_to_tabular(lines, 'NAME,NAMESPACE,ENDPOINT,USERNAME,PASSWORD', separator=','))
|
|
124
|
-
|
|
125
|
-
def show_pg_databases(self, pg: PostgresSession):
|
|
126
|
-
lines = [db["name"] for db in pg.databases() if db["owner"] == PostgresSession.default_owner()]
|
|
127
|
-
|
|
128
|
-
log(lines_to_tabular(lines, 'DATABASE', separator=','))
|
|
129
|
-
|
|
130
|
-
def show_pg_tables(self, pg: PostgresSession):
|
|
131
|
-
lines = [db["name"] for db in pg.tables() if db["schema"] == PostgresSession.default_schema()]
|
|
132
|
-
|
|
133
|
-
log(lines_to_tabular(lines, 'NAME', separator=','))
|
|
134
|
-
|
|
135
38
|
def completion(self, state: ReplState):
|
|
136
|
-
|
|
137
|
-
return {}
|
|
138
|
-
|
|
139
|
-
if not state.sts:
|
|
140
|
-
return {Ls.COMMAND: {n: None for n in StatefulSets.list_sts_names()}}
|
|
141
|
-
|
|
142
|
-
return {Ls.COMMAND: None}
|
|
39
|
+
return Devices.device(state).ls_completion(Ls.COMMAND, state, default = super().completion(state))
|
|
143
40
|
|
|
144
41
|
def help(self, _: ReplState):
|
|
145
|
-
return f'{Ls.COMMAND} [device:]\t list apps, envs, clusters, nodes, pg hosts or
|
|
42
|
+
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,11 @@
|
|
|
1
1
|
import click
|
|
2
2
|
|
|
3
3
|
from adam.commands.command import Command
|
|
4
|
-
from adam.commands.command_helpers import ClusterCommandHelper
|
|
5
4
|
from .medusa_backup import MedusaBackup
|
|
6
5
|
from .medusa_restore import MedusaRestore
|
|
7
6
|
from .medusa_show_backupjobs import MedusaShowBackupJobs
|
|
8
7
|
from .medusa_show_restorejobs import MedusaShowRestoreJobs
|
|
9
8
|
from adam.repl_state import ReplState, RequiredState
|
|
10
|
-
from adam.utils import lines_to_tabular, log, log2
|
|
11
9
|
|
|
12
10
|
class Medusa(Command):
|
|
13
11
|
COMMAND = 'medusa'
|
|
@@ -31,20 +29,7 @@ class Medusa(Command):
|
|
|
31
29
|
if not(args := self.args(cmd)):
|
|
32
30
|
return super().run(cmd, state)
|
|
33
31
|
|
|
34
|
-
state, args
|
|
35
|
-
if not self.validate_state(state):
|
|
36
|
-
return state
|
|
37
|
-
|
|
38
|
-
if state.in_repl:
|
|
39
|
-
log(lines_to_tabular([c.help(ReplState()) for c in Medusa.cmd_list()], separator=':'))
|
|
40
|
-
|
|
41
|
-
return 'command-missing'
|
|
42
|
-
else:
|
|
43
|
-
# head with the Chain of Responsibility pattern
|
|
44
|
-
cmds = Command.chain(Medusa.cmd_list())
|
|
45
|
-
if not cmds.run(cmd, state):
|
|
46
|
-
log2('* Command is missing.')
|
|
47
|
-
Command.display_help()
|
|
32
|
+
return super().intermediate_run(cmd, state, args, Medusa.cmd_list())
|
|
48
33
|
|
|
49
34
|
def cmd_list():
|
|
50
35
|
return [MedusaBackup(), MedusaRestore(), MedusaShowBackupJobs(), MedusaShowRestoreJobs()]
|
|
@@ -55,15 +40,6 @@ class Medusa(Command):
|
|
|
55
40
|
|
|
56
41
|
return {}
|
|
57
42
|
|
|
58
|
-
def help(self, _: ReplState):
|
|
59
|
-
return None
|
|
60
|
-
|
|
61
43
|
class MedusaCommandHelper(click.Command):
|
|
62
44
|
def get_help(self, ctx: click.Context):
|
|
63
|
-
|
|
64
|
-
log()
|
|
65
|
-
log('Sub-Commands:')
|
|
66
|
-
|
|
67
|
-
log(lines_to_tabular([c.help(ReplState()).replace(f'{Medusa.COMMAND} ', ' ', 1) for c in Medusa.cmd_list()], separator=':'))
|
|
68
|
-
log()
|
|
69
|
-
ClusterCommandHelper.cluster_help()
|
|
45
|
+
Command.intermediate_help(super().get_help(ctx), Medusa.COMMAND, Medusa.cmd_list(), show_cluster_help=True)
|
|
@@ -2,9 +2,9 @@ from datetime import datetime
|
|
|
2
2
|
import re
|
|
3
3
|
|
|
4
4
|
from adam.commands.command import Command
|
|
5
|
-
from adam.
|
|
5
|
+
from adam.utils_k8s.statefulsets import StatefulSets
|
|
6
6
|
from adam.repl_state import ReplState, RequiredState
|
|
7
|
-
from adam.
|
|
7
|
+
from adam.utils_k8s.custom_resources import CustomResources
|
|
8
8
|
from adam.utils import log2
|
|
9
9
|
|
|
10
10
|
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
from datetime import datetime
|
|
2
2
|
|
|
3
3
|
from adam.commands.command import Command
|
|
4
|
-
from adam.
|
|
4
|
+
from adam.utils_k8s.statefulsets import StatefulSets
|
|
5
5
|
from adam.repl_state import ReplState, RequiredState
|
|
6
|
-
from adam.
|
|
6
|
+
from adam.utils_k8s.custom_resources import CustomResources
|
|
7
7
|
from adam.config import Config
|
|
8
8
|
from adam.utils import lines_to_tabular, log2
|
|
9
9
|
|
|
@@ -59,8 +59,7 @@ class MedusaRestore(Command):
|
|
|
59
59
|
now_dtformat = datetime.now().strftime("%Y-%m-%d.%H.%M.%S")
|
|
60
60
|
rtname = 'medusa-' + now_dtformat + '-restore-from-' + bkname
|
|
61
61
|
try:
|
|
62
|
-
|
|
63
|
-
# CustomResources.create_medusa_restorejob(rtname, bkname, dc, ns)
|
|
62
|
+
CustomResources.create_medusa_restorejob(rtname, bkname, dc, ns)
|
|
64
63
|
except Exception as e:
|
|
65
64
|
log2("Exception: MedusaRestore failed: %s\n" % e)
|
|
66
65
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from adam.commands.command import Command
|
|
2
|
-
from adam.
|
|
2
|
+
from adam.utils_k8s.statefulsets import StatefulSets
|
|
3
3
|
from adam.repl_state import ReplState, RequiredState
|
|
4
|
-
from adam.
|
|
4
|
+
from adam.utils_k8s.custom_resources import CustomResources
|
|
5
5
|
from adam.utils import lines_to_tabular, log2
|
|
6
6
|
|
|
7
7
|
|
|
@@ -29,6 +29,7 @@ class MedusaShowBackupJobs(Command):
|
|
|
29
29
|
state, args = self.apply_state(args, state)
|
|
30
30
|
if not self.validate_state(state):
|
|
31
31
|
return state
|
|
32
|
+
|
|
32
33
|
ns = state.namespace
|
|
33
34
|
dc = StatefulSets.get_datacenter(state.sts, ns)
|
|
34
35
|
if not dc:
|
|
@@ -49,4 +50,4 @@ class MedusaShowBackupJobs(Command):
|
|
|
49
50
|
return {}
|
|
50
51
|
|
|
51
52
|
def help(self, _: ReplState):
|
|
52
|
-
return f'{MedusaShowBackupJobs.COMMAND}\t show backups'
|
|
53
|
+
return f'{MedusaShowBackupJobs.COMMAND}\t show Medusa backups'
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
from adam.commands.command import Command
|
|
2
|
-
from adam.
|
|
2
|
+
from adam.utils_k8s.statefulsets import StatefulSets
|
|
3
3
|
from adam.repl_state import ReplState, RequiredState
|
|
4
|
-
from adam.
|
|
4
|
+
from adam.utils_k8s.custom_resources import CustomResources
|
|
5
5
|
from adam.utils import lines_to_tabular, log2
|
|
6
6
|
|
|
7
7
|
class MedusaShowRestoreJobs(Command):
|
|
@@ -49,4 +49,4 @@ class MedusaShowRestoreJobs(Command):
|
|
|
49
49
|
return {}
|
|
50
50
|
|
|
51
51
|
def help(self, _: ReplState):
|
|
52
|
-
return f'{MedusaShowRestoreJobs.COMMAND}\t show restores'
|
|
52
|
+
return f'{MedusaShowRestoreJobs.COMMAND}\t show Medusa restores'
|
adam/commands/nodetool.py
CHANGED
|
@@ -4,10 +4,11 @@ from adam.commands.command import Command
|
|
|
4
4
|
from adam.commands.command_helpers import ClusterOrPodCommandHelper
|
|
5
5
|
from adam.commands.nodetool_commands import NODETOOL_COMMANDS
|
|
6
6
|
from adam.config import Config
|
|
7
|
-
from adam.
|
|
8
|
-
from adam.
|
|
7
|
+
from adam.utils_k8s.cassandra_clusters import CassandraClusters
|
|
8
|
+
from adam.utils_k8s.cassandra_nodes import CassandraNodes
|
|
9
9
|
from adam.repl_state import ReplState, RequiredState
|
|
10
10
|
from adam.utils import log
|
|
11
|
+
from adam.utils_k8s.statefulsets import StatefulSets
|
|
11
12
|
|
|
12
13
|
class NodeTool(Command):
|
|
13
14
|
COMMAND = 'nodetool'
|
|
@@ -43,14 +44,18 @@ class NodeTool(Command):
|
|
|
43
44
|
elif state.sts:
|
|
44
45
|
return CassandraClusters.exec(state.sts, state.namespace, command, action='nodetool', show_out=True)
|
|
45
46
|
|
|
47
|
+
return state
|
|
48
|
+
|
|
46
49
|
def completion(self, state: ReplState):
|
|
47
50
|
if state.pod or state.sts:
|
|
48
|
-
|
|
51
|
+
d = {c: {'&': None} for c in NODETOOL_COMMANDS}
|
|
52
|
+
return {NodeTool.COMMAND: {'help': None} | d} | \
|
|
53
|
+
{f'@{p}': {NodeTool.COMMAND: d} for p in StatefulSets.pod_names(state.sts, state.namespace)}
|
|
49
54
|
|
|
50
55
|
return {}
|
|
51
56
|
|
|
52
57
|
def help(self, _: ReplState):
|
|
53
|
-
return f'{NodeTool.COMMAND} <sub-command
|
|
58
|
+
return f'{NodeTool.COMMAND} <sub-command> [&]\t run nodetool with arguments'
|
|
54
59
|
|
|
55
60
|
class NodeToolCommandHelper(click.Command):
|
|
56
61
|
def get_help(self, ctx: click.Context):
|
adam/commands/param_set.py
CHANGED
|
@@ -38,7 +38,7 @@ class SetParam(Command):
|
|
|
38
38
|
return value
|
|
39
39
|
|
|
40
40
|
def completion(self, _: ReplState):
|
|
41
|
-
return {SetParam.COMMAND: {key: None for key in Config().keys()}}
|
|
41
|
+
return {SetParam.COMMAND: {key: ({'true': None, 'false': None} if Config().get(key, None) in [True, False] else None) for key in Config().keys()}}
|
|
42
42
|
|
|
43
43
|
def help(self, _: ReplState):
|
|
44
44
|
return f"{SetParam.COMMAND} <key> <value>\t sets a Kaqing parameter to a different value"
|
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
import click
|
|
2
2
|
|
|
3
3
|
from adam.commands.command import Command
|
|
4
|
-
from adam.commands.
|
|
4
|
+
from adam.commands.postgres.psql_completions import psql_completions
|
|
5
|
+
from adam.commands.postgres.postgres_utils import pg_table_names
|
|
5
6
|
from .postgres_ls import PostgresLs
|
|
6
7
|
from .postgres_preview import PostgresPreview
|
|
7
|
-
from .
|
|
8
|
+
from .postgres_context import PostgresContext
|
|
8
9
|
from adam.repl_state import ReplState
|
|
9
|
-
from adam.utils import
|
|
10
|
+
from adam.utils import log, log2
|
|
10
11
|
|
|
11
12
|
class Postgres(Command):
|
|
12
13
|
COMMAND = 'pg'
|
|
@@ -30,35 +31,27 @@ class Postgres(Command):
|
|
|
30
31
|
|
|
31
32
|
state, args = self.apply_state(args, state)
|
|
32
33
|
|
|
33
|
-
if
|
|
34
|
-
if
|
|
34
|
+
if not args:
|
|
35
|
+
if state.in_repl:
|
|
35
36
|
log2('Please use SQL statement. e.g. pg \l')
|
|
36
|
-
|
|
37
|
-
return 'command-missing'
|
|
38
37
|
else:
|
|
39
|
-
self.run_sql(state, args)
|
|
40
|
-
else:
|
|
41
|
-
if not args:
|
|
42
38
|
log2('* Command or SQL statements is missing.')
|
|
43
39
|
Command.display_help()
|
|
44
40
|
|
|
45
|
-
|
|
46
|
-
else:
|
|
47
|
-
# head with the Chain of Responsibility pattern
|
|
48
|
-
cmds = Command.chain(Postgres.cmd_list())
|
|
49
|
-
if not cmds.run(cmd, state) :
|
|
50
|
-
if not args:
|
|
51
|
-
log2('* Command or SQL statements is missing.')
|
|
52
|
-
Command.display_help()
|
|
41
|
+
return 'command-missing'
|
|
53
42
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
43
|
+
if state.in_repl:
|
|
44
|
+
self.run_sql(state, args)
|
|
45
|
+
else:
|
|
46
|
+
# head with the Chain of Responsibility pattern
|
|
47
|
+
cmds = Command.chain(Postgres.cmd_list())
|
|
48
|
+
if not cmds.run(cmd, state) :
|
|
49
|
+
self.run_sql(state, args)
|
|
57
50
|
|
|
58
51
|
return state
|
|
59
52
|
|
|
60
53
|
def cmd_list():
|
|
61
|
-
return [PostgresLs(), PostgresPreview()]
|
|
54
|
+
return [PostgresLs(), PostgresPreview(), PostgresPg()]
|
|
62
55
|
|
|
63
56
|
def run_sql(self, state: ReplState, args: list[str]):
|
|
64
57
|
if not state.pg_path:
|
|
@@ -69,21 +62,23 @@ class Postgres(Command):
|
|
|
69
62
|
|
|
70
63
|
return state
|
|
71
64
|
|
|
72
|
-
|
|
65
|
+
background = False
|
|
66
|
+
if args and args[-1] == '&':
|
|
67
|
+
args = args[:-1]
|
|
68
|
+
background = True
|
|
69
|
+
|
|
70
|
+
PostgresContext.apply(state.namespace, state.pg_path).run_sql(' '.join(args), background=background)
|
|
73
71
|
|
|
74
72
|
def completion(self, state: ReplState):
|
|
73
|
+
if state.device != state.P:
|
|
74
|
+
# conflicts with cql completions
|
|
75
|
+
return {}
|
|
76
|
+
|
|
75
77
|
leaf = {}
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
'\dt': None,
|
|
81
|
-
'\du': None,
|
|
82
|
-
'delete': {'from': None},
|
|
83
|
-
'insert': {'into': None},
|
|
84
|
-
'select': None,
|
|
85
|
-
'update': None,
|
|
86
|
-
}
|
|
78
|
+
session = PostgresContext.apply(state.namespace, state.pg_path)
|
|
79
|
+
if session.db:
|
|
80
|
+
if pg_table_names(state.namespace, state.pg_path):
|
|
81
|
+
leaf = psql_completions(state.namespace, state.pg_path)
|
|
87
82
|
elif state.pg_path:
|
|
88
83
|
leaf = {
|
|
89
84
|
'\h': None,
|
|
@@ -96,18 +91,22 @@ class Postgres(Command):
|
|
|
96
91
|
return {}
|
|
97
92
|
|
|
98
93
|
def help(self, _: ReplState):
|
|
99
|
-
return f'
|
|
94
|
+
return f'<sql-statements> [&]\t run queries on Postgres databases'
|
|
100
95
|
|
|
101
96
|
class PostgresCommandHelper(click.Command):
|
|
102
97
|
def get_help(self, ctx: click.Context):
|
|
103
|
-
|
|
104
|
-
log()
|
|
105
|
-
log('Sub-Commands:')
|
|
106
|
-
|
|
107
|
-
log(lines_to_tabular([c.help(ReplState()).replace(f'{Postgres.COMMAND} ', ' ', 1) for c in Postgres.cmd_list()], separator='\t'))
|
|
108
|
-
log()
|
|
109
|
-
ClusterCommandHelper.cluster_help()
|
|
98
|
+
Command.intermediate_help(super().get_help(ctx), Postgres.COMMAND, Postgres.cmd_list(), show_cluster_help=True)
|
|
110
99
|
log('PG-Name: Kubernetes secret for Postgres credentials')
|
|
111
100
|
log(' e.g. stgawsscpsr-c3-c3-k8spg-cs-001')
|
|
112
101
|
log('Database: Postgres database name within a host')
|
|
113
|
-
log(' e.g. stgawsscpsr_c3_c3')
|
|
102
|
+
log(' e.g. stgawsscpsr_c3_c3')
|
|
103
|
+
|
|
104
|
+
# No action body, only for a help entry and auto-completion
|
|
105
|
+
class PostgresPg(Command):
|
|
106
|
+
COMMAND = 'pg'
|
|
107
|
+
|
|
108
|
+
def command(self):
|
|
109
|
+
return PostgresPg.COMMAND
|
|
110
|
+
|
|
111
|
+
def help(self, _: ReplState):
|
|
112
|
+
return f'pg <sql-statements>\t run queries on Postgres databases'
|
|
@@ -1,14 +1,37 @@
|
|
|
1
|
+
from datetime import datetime
|
|
1
2
|
import functools
|
|
2
3
|
import re
|
|
3
4
|
import subprocess
|
|
4
5
|
|
|
5
6
|
from adam.config import Config
|
|
6
|
-
from adam.
|
|
7
|
-
from adam.
|
|
8
|
-
from adam.
|
|
7
|
+
from adam.repl_session import ReplSession
|
|
8
|
+
from adam.utils_k8s.kube_context import KubeContext
|
|
9
|
+
from adam.utils_k8s.pods import Pods
|
|
10
|
+
from adam.utils_k8s.secrets import Secrets
|
|
9
11
|
from adam.utils import log2
|
|
10
12
|
|
|
11
|
-
class
|
|
13
|
+
class PostgresContext:
|
|
14
|
+
def apply(namespace: str, path: str, arg: str = None) -> 'PostgresContext':
|
|
15
|
+
context = PostgresContext(namespace, path)
|
|
16
|
+
|
|
17
|
+
if arg:
|
|
18
|
+
if arg == '..':
|
|
19
|
+
if context.db:
|
|
20
|
+
context.db = None
|
|
21
|
+
else:
|
|
22
|
+
context.host = None
|
|
23
|
+
else:
|
|
24
|
+
tks = arg.split('@')
|
|
25
|
+
if not context.host:
|
|
26
|
+
context.host = tks[0]
|
|
27
|
+
else:
|
|
28
|
+
context.db = tks[0]
|
|
29
|
+
|
|
30
|
+
if not namespace and tks[1]:
|
|
31
|
+
context.namespace = tks[1]
|
|
32
|
+
|
|
33
|
+
return context
|
|
34
|
+
|
|
12
35
|
def __init__(self, ns: str, path: str):
|
|
13
36
|
self.namespace = ns
|
|
14
37
|
self.conn_details = None
|
|
@@ -25,36 +48,7 @@ class PostgresSession:
|
|
|
25
48
|
if len(tks) > 1:
|
|
26
49
|
self.db = tks[1]
|
|
27
50
|
|
|
28
|
-
|
|
29
|
-
def __eq__(self, other: 'PostgresSession'):
|
|
30
|
-
return self.host == other.host
|
|
31
|
-
|
|
32
|
-
def __hash__(self):
|
|
33
|
-
return hash(self.host)
|
|
34
|
-
|
|
35
|
-
def find_namespace(self, arg: str):
|
|
36
|
-
if arg:
|
|
37
|
-
tks = arg.split('@')
|
|
38
|
-
if len(tks) > 1:
|
|
39
|
-
return tks[1]
|
|
40
|
-
|
|
41
|
-
return None
|
|
42
|
-
|
|
43
|
-
def directory(self, arg: str = None):
|
|
44
|
-
if arg:
|
|
45
|
-
if arg == '..':
|
|
46
|
-
if self.db:
|
|
47
|
-
self.db = None
|
|
48
|
-
else:
|
|
49
|
-
self.host = None
|
|
50
|
-
else:
|
|
51
|
-
tks = arg.split('@')
|
|
52
|
-
arg = tks[0]
|
|
53
|
-
if not self.host:
|
|
54
|
-
self.host = arg
|
|
55
|
-
else:
|
|
56
|
-
self.db = arg
|
|
57
|
-
|
|
51
|
+
def path(self):
|
|
58
52
|
if not self.host:
|
|
59
53
|
return None
|
|
60
54
|
|
|
@@ -65,7 +59,7 @@ class PostgresSession:
|
|
|
65
59
|
return f'{self.host}/{self.db}'
|
|
66
60
|
|
|
67
61
|
def hosts(ns: str):
|
|
68
|
-
return
|
|
62
|
+
return PostgresContext.hosts_for_namespace(ns)
|
|
69
63
|
|
|
70
64
|
@functools.lru_cache()
|
|
71
65
|
def hosts_for_namespace(ns: str):
|
|
@@ -82,7 +76,6 @@ class PostgresSession:
|
|
|
82
76
|
|
|
83
77
|
return [s for s in ss if not excludes(s)]
|
|
84
78
|
|
|
85
|
-
@functools.lru_cache()
|
|
86
79
|
def databases(self):
|
|
87
80
|
dbs = []
|
|
88
81
|
# List of databases
|
|
@@ -140,15 +133,16 @@ class PostgresSession:
|
|
|
140
133
|
|
|
141
134
|
return dbs
|
|
142
135
|
|
|
143
|
-
def run_sql(self, sql: str, show_out = True):
|
|
144
|
-
db = self.db if self.db else
|
|
136
|
+
def run_sql(self, sql: str, show_out = True, background = False):
|
|
137
|
+
db = self.db if self.db else PostgresContext.default_db()
|
|
145
138
|
|
|
146
139
|
if KubeContext.in_cluster():
|
|
147
140
|
cmd1 = f'env PGPASSWORD={self.password()} psql -h {self.endpoint()} -p {self.port()} -U {self.username()} {db} --pset pager=off -c'
|
|
148
141
|
log2(f'{cmd1} "{sql}"')
|
|
149
142
|
# remove double quotes from the sql argument
|
|
150
143
|
cmd = cmd1.split(' ') + [sql]
|
|
151
|
-
|
|
144
|
+
|
|
145
|
+
r = subprocess.run(cmd, capture_output=not background, text=True)
|
|
152
146
|
if show_out:
|
|
153
147
|
log2(r.stdout)
|
|
154
148
|
log2(r.stderr)
|
|
@@ -159,7 +153,7 @@ class PostgresSession:
|
|
|
159
153
|
pod_name = Config().get('pg.agent.name', 'ops-pg-agent')
|
|
160
154
|
|
|
161
155
|
if Config().get('pg.agent.just-in-time', False):
|
|
162
|
-
if not
|
|
156
|
+
if not PostgresContext.deploy_pg_agent(pod_name, ns):
|
|
163
157
|
return
|
|
164
158
|
|
|
165
159
|
real_pod_name = pod_name
|
|
@@ -169,14 +163,21 @@ class PostgresSession:
|
|
|
169
163
|
except:
|
|
170
164
|
try:
|
|
171
165
|
# try with the ops pod
|
|
166
|
+
pod_name = Config().get('pod.name', 'ops')
|
|
172
167
|
real_pod_name = Pods.get_with_selector(ns, label_selector = Config().get('pod.label-selector', 'run=ops')).metadata.name
|
|
173
168
|
except:
|
|
174
169
|
log2(f"Could not locate {pod_name} pod.")
|
|
175
170
|
return None
|
|
176
171
|
|
|
177
|
-
cmd = f'
|
|
172
|
+
cmd = f'psql -h {self.endpoint()} -p {self.port()} -U {self.username()} {db} --pset pager=off -c "{sql}"'
|
|
173
|
+
env_prefix = f'PGPASSWORD="{self.password()}"'
|
|
174
|
+
|
|
175
|
+
r = Pods.exec(real_pod_name, pod_name, ns, cmd, show_out=show_out, background=background, env_prefix=env_prefix)
|
|
176
|
+
if r and Config().get('repl.history.push-cat-remote-log-file', True):
|
|
177
|
+
if r.log_file and ReplSession().prompt_session:
|
|
178
|
+
ReplSession().prompt_session.history.append_string(f'@{r.pod} cat {r.log_file}')
|
|
178
179
|
|
|
179
|
-
return
|
|
180
|
+
return r
|
|
180
181
|
|
|
181
182
|
def deploy_pg_agent(pod_name: str, ns: str) -> str:
|
|
182
183
|
image = Config().get('pg.agent.image', 'seanahnsf/kaqing')
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
|
|
3
|
+
from adam.commands.postgres.postgres_context import PostgresContext
|
|
4
|
+
from adam.config import Config
|
|
5
|
+
|
|
6
|
+
TestPG = [False]
|
|
7
|
+
|
|
8
|
+
@functools.lru_cache()
|
|
9
|
+
def pg_database_names(ns: str, pg_path: str):
|
|
10
|
+
if TestPG[0]:
|
|
11
|
+
return ['azops88_c3ai_c3']
|
|
12
|
+
|
|
13
|
+
Config().wait_log('Inspecting Postgres Databases...')
|
|
14
|
+
|
|
15
|
+
pg = PostgresContext.apply(ns, pg_path)
|
|
16
|
+
return [db['name'] for db in pg.databases() if db['owner'] == PostgresContext.default_owner()]
|
|
17
|
+
|
|
18
|
+
@functools.lru_cache()
|
|
19
|
+
def pg_table_names(ns: str, pg_path: str):
|
|
20
|
+
if TestPG[0]:
|
|
21
|
+
return ['C3_2_XYZ1']
|
|
22
|
+
|
|
23
|
+
Config().wait_log('Inspecting Postgres Database...')
|
|
24
|
+
return [table['name'] for table in pg_tables(ns, pg_path) if table['schema'] == PostgresContext.default_schema()]
|
|
25
|
+
|
|
26
|
+
def pg_tables(ns: str, pg_path: str):
|
|
27
|
+
pg = PostgresContext.apply(ns, pg_path)
|
|
28
|
+
if pg.db:
|
|
29
|
+
return pg.tables()
|
|
30
|
+
|
|
31
|
+
return []
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
from adam.commands.postgres.postgres_utils import pg_table_names
|
|
2
|
+
from adam.sql.sql_completer import SqlCompleter
|
|
3
|
+
|
|
4
|
+
def psql_completions(ns: str, pg_path: str):
|
|
5
|
+
return {
|
|
6
|
+
'\h': None,
|
|
7
|
+
'\d': None,
|
|
8
|
+
'\dt': None,
|
|
9
|
+
'\du': None
|
|
10
|
+
} | SqlCompleter(lambda: pg_table_names(ns, pg_path)).completions_for_nesting()
|