kaqing 1.77.0__py3-none-any.whl → 2.0.171__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.
Files changed (308) hide show
  1. adam/__init__.py +1 -0
  2. adam/app_session.py +182 -0
  3. {walker → adam}/apps.py +8 -24
  4. {walker → adam}/batch.py +54 -97
  5. {walker → adam}/checks/check.py +3 -3
  6. {walker → adam}/checks/check_result.py +1 -1
  7. adam/checks/check_utils.py +65 -0
  8. {walker → adam}/checks/compactionstats.py +6 -6
  9. {walker → adam}/checks/cpu.py +14 -8
  10. adam/checks/cpu_metrics.py +52 -0
  11. {walker → adam}/checks/disk.py +6 -6
  12. {walker → adam}/checks/gossip.py +5 -5
  13. {walker → adam}/checks/memory.py +7 -7
  14. {walker → adam}/checks/status.py +5 -5
  15. {walker → adam}/cli.py +3 -3
  16. {walker → adam}/columns/column.py +1 -1
  17. adam/columns/columns.py +45 -0
  18. {walker → adam}/columns/compactions.py +5 -5
  19. {walker → adam}/columns/cpu.py +6 -4
  20. adam/columns/cpu_metrics.py +22 -0
  21. {walker → adam}/columns/dir_data.py +3 -3
  22. {walker → adam}/columns/dir_snapshots.py +3 -3
  23. {walker → adam}/columns/gossip.py +5 -5
  24. {walker → adam}/columns/host_id.py +3 -3
  25. {walker → adam}/columns/memory.py +3 -3
  26. {walker → adam}/columns/node_address.py +3 -3
  27. {walker → adam}/columns/node_load.py +3 -3
  28. {walker → adam}/columns/node_owns.py +3 -3
  29. {walker → adam}/columns/node_status.py +3 -3
  30. {walker → adam}/columns/node_tokens.py +3 -3
  31. {walker → adam}/columns/node_utils.py +2 -2
  32. {walker → adam}/columns/pod_name.py +2 -2
  33. {walker → adam}/columns/volume_cassandra.py +4 -4
  34. {walker → adam}/columns/volume_root.py +3 -3
  35. adam/commands/__init__.py +15 -0
  36. adam/commands/alter_tables.py +81 -0
  37. adam/commands/app_cmd.py +38 -0
  38. {walker → adam}/commands/app_ping.py +10 -16
  39. adam/commands/audit/audit.py +84 -0
  40. adam/commands/audit/audit_repair_tables.py +74 -0
  41. adam/commands/audit/audit_run.py +50 -0
  42. adam/commands/audit/show_last10.py +48 -0
  43. adam/commands/audit/show_slow10.py +47 -0
  44. adam/commands/audit/show_top10.py +45 -0
  45. adam/commands/audit/utils_show_top10.py +59 -0
  46. adam/commands/bash/__init__.py +5 -0
  47. adam/commands/bash/bash.py +36 -0
  48. adam/commands/bash/bash_completer.py +93 -0
  49. adam/commands/bash/utils_bash.py +16 -0
  50. adam/commands/cat.py +50 -0
  51. adam/commands/cd.py +43 -0
  52. adam/commands/check.py +73 -0
  53. {walker → adam}/commands/cli_commands.py +7 -8
  54. adam/commands/code.py +57 -0
  55. adam/commands/command.py +190 -0
  56. {walker → adam}/commands/command_helpers.py +1 -1
  57. {walker → adam}/commands/commands_utils.py +15 -25
  58. adam/commands/cp.py +89 -0
  59. adam/commands/cql/cql_completions.py +33 -0
  60. {walker/commands → adam/commands/cql}/cqlsh.py +20 -35
  61. adam/commands/cql/utils_cql.py +343 -0
  62. {walker/commands/frontend → adam/commands/deploy}/code_start.py +11 -14
  63. adam/commands/deploy/code_stop.py +40 -0
  64. {walker/commands/frontend → adam/commands/deploy}/code_utils.py +7 -9
  65. adam/commands/deploy/deploy.py +25 -0
  66. adam/commands/deploy/deploy_frontend.py +49 -0
  67. adam/commands/deploy/deploy_pg_agent.py +35 -0
  68. adam/commands/deploy/deploy_pod.py +108 -0
  69. adam/commands/deploy/deploy_utils.py +29 -0
  70. adam/commands/deploy/undeploy.py +25 -0
  71. adam/commands/deploy/undeploy_frontend.py +38 -0
  72. adam/commands/deploy/undeploy_pg_agent.py +39 -0
  73. adam/commands/deploy/undeploy_pod.py +48 -0
  74. adam/commands/devices/device.py +118 -0
  75. adam/commands/devices/device_app.py +173 -0
  76. adam/commands/devices/device_auit_log.py +49 -0
  77. adam/commands/devices/device_cass.py +185 -0
  78. adam/commands/devices/device_export.py +86 -0
  79. adam/commands/devices/device_postgres.py +144 -0
  80. adam/commands/devices/devices.py +25 -0
  81. {walker → adam}/commands/exit.py +3 -6
  82. adam/commands/export/clean_up_all_export_sessions.py +37 -0
  83. adam/commands/export/clean_up_export_sessions.py +51 -0
  84. adam/commands/export/drop_export_database.py +55 -0
  85. adam/commands/export/drop_export_databases.py +43 -0
  86. adam/commands/export/export.py +53 -0
  87. adam/commands/export/export_databases.py +170 -0
  88. adam/commands/export/export_handlers.py +71 -0
  89. adam/commands/export/export_select.py +81 -0
  90. adam/commands/export/export_select_x.py +54 -0
  91. adam/commands/export/export_use.py +52 -0
  92. adam/commands/export/exporter.py +352 -0
  93. adam/commands/export/import_session.py +40 -0
  94. adam/commands/export/importer.py +67 -0
  95. adam/commands/export/importer_athena.py +80 -0
  96. adam/commands/export/importer_sqlite.py +47 -0
  97. adam/commands/export/show_column_counts.py +54 -0
  98. adam/commands/export/show_export_databases.py +36 -0
  99. adam/commands/export/show_export_session.py +48 -0
  100. adam/commands/export/show_export_sessions.py +44 -0
  101. adam/commands/export/utils_export.py +314 -0
  102. {walker → adam}/commands/help.py +17 -12
  103. adam/commands/intermediate_command.py +49 -0
  104. adam/commands/issues.py +43 -0
  105. adam/commands/kubectl.py +38 -0
  106. adam/commands/login.py +70 -0
  107. {walker → adam}/commands/logs.py +8 -10
  108. adam/commands/ls.py +41 -0
  109. adam/commands/medusa/medusa.py +27 -0
  110. adam/commands/medusa/medusa_backup.py +57 -0
  111. adam/commands/medusa/medusa_restore.py +83 -0
  112. adam/commands/medusa/medusa_show_backupjobs.py +51 -0
  113. adam/commands/medusa/medusa_show_restorejobs.py +47 -0
  114. {walker → adam}/commands/nodetool.py +17 -21
  115. {walker → adam}/commands/param_get.py +15 -16
  116. adam/commands/param_set.py +43 -0
  117. adam/commands/postgres/postgres.py +104 -0
  118. adam/commands/postgres/postgres_context.py +274 -0
  119. {walker → adam}/commands/postgres/postgres_ls.py +7 -11
  120. {walker → adam}/commands/postgres/postgres_preview.py +8 -13
  121. adam/commands/postgres/psql_completions.py +10 -0
  122. adam/commands/postgres/utils_postgres.py +66 -0
  123. adam/commands/preview_table.py +37 -0
  124. adam/commands/pwd.py +47 -0
  125. adam/commands/reaper/reaper.py +35 -0
  126. adam/commands/reaper/reaper_forward.py +93 -0
  127. adam/commands/reaper/reaper_forward_session.py +6 -0
  128. {walker → adam}/commands/reaper/reaper_forward_stop.py +13 -19
  129. {walker → adam}/commands/reaper/reaper_restart.py +10 -17
  130. adam/commands/reaper/reaper_run_abort.py +46 -0
  131. adam/commands/reaper/reaper_runs.py +82 -0
  132. adam/commands/reaper/reaper_runs_abort.py +63 -0
  133. adam/commands/reaper/reaper_schedule_activate.py +45 -0
  134. adam/commands/reaper/reaper_schedule_start.py +45 -0
  135. adam/commands/reaper/reaper_schedule_stop.py +45 -0
  136. {walker → adam}/commands/reaper/reaper_schedules.py +6 -16
  137. {walker → adam}/commands/reaper/reaper_status.py +11 -19
  138. adam/commands/reaper/utils_reaper.py +196 -0
  139. adam/commands/repair/repair.py +26 -0
  140. {walker → adam}/commands/repair/repair_log.py +7 -10
  141. adam/commands/repair/repair_run.py +70 -0
  142. adam/commands/repair/repair_scan.py +71 -0
  143. {walker → adam}/commands/repair/repair_stop.py +8 -11
  144. adam/commands/report.py +61 -0
  145. adam/commands/restart.py +60 -0
  146. {walker → adam}/commands/rollout.py +25 -30
  147. adam/commands/shell.py +34 -0
  148. adam/commands/show/show.py +39 -0
  149. walker/commands/show/show_version.py → adam/commands/show/show_adam.py +14 -10
  150. adam/commands/show/show_app_actions.py +57 -0
  151. {walker → adam}/commands/show/show_app_id.py +12 -15
  152. {walker → adam}/commands/show/show_app_queues.py +9 -12
  153. adam/commands/show/show_cassandra_repairs.py +38 -0
  154. adam/commands/show/show_cassandra_status.py +124 -0
  155. {walker → adam}/commands/show/show_cassandra_version.py +6 -16
  156. adam/commands/show/show_commands.py +59 -0
  157. walker/commands/show/show_storage.py → adam/commands/show/show_host.py +11 -13
  158. adam/commands/show/show_login.py +62 -0
  159. {walker → adam}/commands/show/show_params.py +4 -4
  160. adam/commands/show/show_processes.py +51 -0
  161. adam/commands/show/show_storage.py +42 -0
  162. adam/commands/watch.py +82 -0
  163. {walker → adam}/config.py +10 -22
  164. {walker → adam}/embedded_apps.py +1 -1
  165. adam/embedded_params.py +2 -0
  166. adam/log.py +47 -0
  167. {walker → adam}/pod_exec_result.py +10 -2
  168. adam/repl.py +182 -0
  169. adam/repl_commands.py +124 -0
  170. adam/repl_state.py +458 -0
  171. adam/sql/__init__.py +0 -0
  172. adam/sql/sql_completer.py +120 -0
  173. adam/sql/sql_state_machine.py +618 -0
  174. adam/sql/term_completer.py +76 -0
  175. adam/sso/__init__.py +0 -0
  176. {walker → adam}/sso/authenticator.py +5 -1
  177. adam/sso/authn_ad.py +170 -0
  178. {walker → adam}/sso/authn_okta.py +39 -22
  179. adam/sso/cred_cache.py +60 -0
  180. adam/sso/id_token.py +23 -0
  181. adam/sso/idp.py +143 -0
  182. adam/sso/idp_login.py +50 -0
  183. adam/sso/idp_session.py +55 -0
  184. adam/sso/sso_config.py +63 -0
  185. adam/utils.py +679 -0
  186. adam/utils_app.py +98 -0
  187. adam/utils_athena.py +145 -0
  188. adam/utils_audits.py +106 -0
  189. adam/utils_issues.py +32 -0
  190. adam/utils_k8s/__init__.py +0 -0
  191. adam/utils_k8s/app_clusters.py +28 -0
  192. adam/utils_k8s/app_pods.py +33 -0
  193. adam/utils_k8s/cassandra_clusters.py +36 -0
  194. adam/utils_k8s/cassandra_nodes.py +33 -0
  195. adam/utils_k8s/config_maps.py +34 -0
  196. {walker/k8s_utils → adam/utils_k8s}/custom_resources.py +7 -2
  197. adam/utils_k8s/deployment.py +56 -0
  198. {walker/k8s_utils → adam/utils_k8s}/ingresses.py +3 -4
  199. {walker/k8s_utils → adam/utils_k8s}/jobs.py +3 -3
  200. adam/utils_k8s/k8s.py +87 -0
  201. {walker/k8s_utils → adam/utils_k8s}/kube_context.py +4 -4
  202. adam/utils_k8s/pods.py +290 -0
  203. {walker/k8s_utils → adam/utils_k8s}/secrets.py +8 -4
  204. adam/utils_k8s/service_accounts.py +170 -0
  205. {walker/k8s_utils → adam/utils_k8s}/services.py +3 -4
  206. {walker/k8s_utils → adam/utils_k8s}/statefulsets.py +6 -16
  207. {walker/k8s_utils → adam/utils_k8s}/volumes.py +10 -1
  208. adam/utils_net.py +24 -0
  209. adam/utils_repl/__init__.py +0 -0
  210. adam/utils_repl/automata_completer.py +48 -0
  211. adam/utils_repl/repl_completer.py +46 -0
  212. adam/utils_repl/state_machine.py +173 -0
  213. adam/utils_sqlite.py +109 -0
  214. adam/version.py +5 -0
  215. {kaqing-1.77.0.dist-info → kaqing-2.0.171.dist-info}/METADATA +1 -1
  216. kaqing-2.0.171.dist-info/RECORD +236 -0
  217. kaqing-2.0.171.dist-info/entry_points.txt +3 -0
  218. kaqing-2.0.171.dist-info/top_level.txt +1 -0
  219. kaqing-1.77.0.dist-info/RECORD +0 -159
  220. kaqing-1.77.0.dist-info/entry_points.txt +0 -3
  221. kaqing-1.77.0.dist-info/top_level.txt +0 -1
  222. walker/__init__.py +0 -3
  223. walker/app_session.py +0 -168
  224. walker/checks/check_utils.py +0 -97
  225. walker/columns/columns.py +0 -43
  226. walker/commands/add_user.py +0 -68
  227. walker/commands/app.py +0 -67
  228. walker/commands/bash.py +0 -87
  229. walker/commands/cd.py +0 -115
  230. walker/commands/check.py +0 -68
  231. walker/commands/command.py +0 -104
  232. walker/commands/cp.py +0 -95
  233. walker/commands/cql_utils.py +0 -53
  234. walker/commands/devices.py +0 -89
  235. walker/commands/frontend/code_stop.py +0 -57
  236. walker/commands/frontend/setup.py +0 -60
  237. walker/commands/frontend/setup_frontend.py +0 -58
  238. walker/commands/frontend/teardown.py +0 -61
  239. walker/commands/frontend/teardown_frontend.py +0 -42
  240. walker/commands/issues.py +0 -69
  241. walker/commands/login.py +0 -72
  242. walker/commands/ls.py +0 -145
  243. walker/commands/medusa/medusa.py +0 -69
  244. walker/commands/medusa/medusa_backup.py +0 -61
  245. walker/commands/medusa/medusa_restore.py +0 -86
  246. walker/commands/medusa/medusa_show_backupjobs.py +0 -52
  247. walker/commands/medusa/medusa_show_restorejobs.py +0 -52
  248. walker/commands/param_set.py +0 -44
  249. walker/commands/postgres/postgres.py +0 -113
  250. walker/commands/postgres/postgres_session.py +0 -225
  251. walker/commands/preview_table.py +0 -98
  252. walker/commands/processes.py +0 -53
  253. walker/commands/pwd.py +0 -64
  254. walker/commands/reaper/reaper.py +0 -78
  255. walker/commands/reaper/reaper_forward.py +0 -100
  256. walker/commands/reaper/reaper_run_abort.py +0 -65
  257. walker/commands/reaper/reaper_runs.py +0 -97
  258. walker/commands/reaper/reaper_runs_abort.py +0 -83
  259. walker/commands/reaper/reaper_schedule_activate.py +0 -64
  260. walker/commands/reaper/reaper_schedule_start.py +0 -64
  261. walker/commands/reaper/reaper_schedule_stop.py +0 -64
  262. walker/commands/reaper/reaper_session.py +0 -159
  263. walker/commands/repair/repair.py +0 -68
  264. walker/commands/repair/repair_run.py +0 -72
  265. walker/commands/repair/repair_scan.py +0 -79
  266. walker/commands/report.py +0 -57
  267. walker/commands/restart.py +0 -61
  268. walker/commands/show/show.py +0 -72
  269. walker/commands/show/show_app_actions.py +0 -53
  270. walker/commands/show/show_cassandra_status.py +0 -35
  271. walker/commands/show/show_commands.py +0 -58
  272. walker/commands/show/show_processes.py +0 -35
  273. walker/commands/show/show_repairs.py +0 -47
  274. walker/commands/status.py +0 -128
  275. walker/commands/storage.py +0 -52
  276. walker/commands/user_entry.py +0 -69
  277. walker/commands/watch.py +0 -85
  278. walker/embedded_params.py +0 -2
  279. walker/k8s_utils/cassandra_clusters.py +0 -48
  280. walker/k8s_utils/cassandra_nodes.py +0 -26
  281. walker/k8s_utils/pods.py +0 -211
  282. walker/repl.py +0 -165
  283. walker/repl_commands.py +0 -58
  284. walker/repl_state.py +0 -211
  285. walker/sso/authn_ad.py +0 -94
  286. walker/sso/idp.py +0 -150
  287. walker/sso/idp_login.py +0 -29
  288. walker/sso/sso_config.py +0 -45
  289. walker/utils.py +0 -194
  290. walker/version.py +0 -5
  291. {walker → adam}/checks/__init__.py +0 -0
  292. {walker → adam}/checks/check_context.py +0 -0
  293. {walker → adam}/checks/issue.py +0 -0
  294. {walker → adam}/cli_group.py +0 -0
  295. {walker → adam}/columns/__init__.py +0 -0
  296. {walker/commands → adam/commands/audit}/__init__.py +0 -0
  297. {walker/commands/frontend → adam/commands/cql}/__init__.py +0 -0
  298. {walker/commands/medusa → adam/commands/deploy}/__init__.py +0 -0
  299. {walker/commands/postgres → adam/commands/devices}/__init__.py +0 -0
  300. {walker/commands/reaper → adam/commands/export}/__init__.py +0 -0
  301. {walker/commands/repair → adam/commands/medusa}/__init__.py +0 -0
  302. {walker → adam}/commands/nodetool_commands.py +0 -0
  303. {walker/commands/show → adam/commands/postgres}/__init__.py +0 -0
  304. {walker/k8s_utils → adam/commands/reaper}/__init__.py +0 -0
  305. {walker/sso → adam/commands/repair}/__init__.py +0 -0
  306. /walker/medusa_show_restorejobs.py → /adam/commands/show/__init__.py +0 -0
  307. {walker → adam}/repl_session.py +0 -0
  308. {kaqing-1.77.0.dist-info → kaqing-2.0.171.dist-info}/WHEEL +0 -0
@@ -0,0 +1,48 @@
1
+ from adam.commands.command import Command
2
+ from adam.commands.export.export_databases import ExportDatabases
3
+ from adam.commands.export.exporter import Exporter
4
+ from adam.repl_state import ReplState, RequiredState
5
+ from adam.utils import log2
6
+
7
+ class ShowExportSession(Command):
8
+ COMMAND = 'show export session'
9
+
10
+ # the singleton pattern
11
+ def __new__(cls, *args, **kwargs):
12
+ if not hasattr(cls, 'instance'): cls.instance = super(ShowExportSession, cls).__new__(cls)
13
+
14
+ return cls.instance
15
+
16
+ def __init__(self, successor: Command=None):
17
+ super().__init__(successor)
18
+
19
+ def command(self):
20
+ return ShowExportSession.COMMAND
21
+
22
+ def required(self):
23
+ return RequiredState.CLUSTER_OR_POD
24
+
25
+ def run(self, cmd: str, state: ReplState):
26
+ if not(args := self.args(cmd)):
27
+ return super().run(cmd, state)
28
+
29
+ with self.validate(args, state) as (args, state):
30
+ if not args:
31
+ if state.in_repl:
32
+ log2('Specify export database name.')
33
+ else:
34
+ log2('* Database name is missing.')
35
+
36
+ Command.display_help()
37
+
38
+ return 'command-missing'
39
+
40
+ ExportDatabases.disply_export_session(state.sts, state.pod, state.namespace, args[0])
41
+
42
+ return state
43
+
44
+ def completion(self, state: ReplState):
45
+ return super().completion(state, {session: None for session in Exporter.export_session_names(state.sts, state.pod, state.namespace)})
46
+
47
+ def help(self, _: ReplState):
48
+ return f'{ShowExportSession.COMMAND} <export-session-name>\t show export session'
@@ -0,0 +1,44 @@
1
+ from adam.commands.command import Command
2
+ from adam.commands.export.exporter import Exporter
3
+ from adam.repl_state import ReplState, RequiredState
4
+ from adam.utils import lines_to_tabular, log
5
+ from adam.utils_k8s.statefulsets import StatefulSets
6
+
7
+ class ShowExportSessions(Command):
8
+ COMMAND = 'show export sessions'
9
+
10
+ # the singleton pattern
11
+ def __new__(cls, *args, **kwargs):
12
+ if not hasattr(cls, 'instance'): cls.instance = super(ShowExportSessions, cls).__new__(cls)
13
+
14
+ return cls.instance
15
+
16
+ def __init__(self, successor: Command=None):
17
+ super().__init__(successor)
18
+
19
+ def command(self):
20
+ return ShowExportSessions.COMMAND
21
+
22
+ def required(self):
23
+ return RequiredState.CLUSTER_OR_POD
24
+
25
+ def run(self, cmd: str, state: ReplState):
26
+ if not(args := self.args(cmd)):
27
+ return super().run(cmd, state)
28
+
29
+ with self.validate(args, state) as (args, state):
30
+ pod = state.pod
31
+ if not pod:
32
+ pod = StatefulSets.pod_names(state.sts, state.namespace)[0]
33
+
34
+ sessions: dict[str, str] = Exporter.find_export_sessions(pod, state.namespace)
35
+ log(lines_to_tabular([f'{session}\t{export_state}' for session, export_state in sorted(sessions.items(), reverse=True)],
36
+ header='EXPORT_SESSION\tSTATUS', separator='\t'))
37
+
38
+ return state
39
+
40
+ def completion(self, state: ReplState):
41
+ return super().completion(state)
42
+
43
+ def help(self, _: ReplState):
44
+ return f'{ShowExportSessions.COMMAND}\t list export sessions'
@@ -0,0 +1,314 @@
1
+ import io
2
+ import re
3
+
4
+ from adam.config import Config
5
+ from adam.pod_exec_result import PodExecResult
6
+ from adam.repl_state import ReplState
7
+ from adam.utils_k8s.cassandra_nodes import CassandraNodes
8
+ from adam.utils_k8s.pods import log_prefix
9
+ from adam.utils_k8s.statefulsets import StatefulSets
10
+
11
+ class ImportSpec:
12
+ def __init__(self, session: str, importer: str):
13
+ self.session = session
14
+ self.importer = importer
15
+
16
+ def parse_specs(specs_str: str):
17
+ session: str = None
18
+ importer: str = None
19
+
20
+ if specs_str:
21
+ importer, session = ImportSpec._extract_importer(specs_str.strip(' '))
22
+
23
+ return ImportSpec(session, importer)
24
+
25
+ def _extract_importer(spec_str: str) -> tuple[str, str]:
26
+ importer = None
27
+ rest = spec_str
28
+
29
+ p = re.compile(r"(.*?)to\s+(.*)", re.IGNORECASE)
30
+ match = p.match(spec_str)
31
+ if match:
32
+ rest = match.group(1).strip(' ')
33
+ importer = match.group(2).strip(' ')
34
+
35
+ return importer, rest
36
+
37
+ class ExportSpec(ImportSpec):
38
+ def __init__(self, keyspace: str, consistency: str, importer: str, tables: list['ExportTableSpec'], session: str = None):
39
+ super().__init__(None, importer)
40
+
41
+ self.keyspace = keyspace
42
+ self.consistency = consistency
43
+ self.tables = tables
44
+ self.session = session
45
+
46
+ def __str__(self):
47
+ return f'keyspace: {self.keyspace}, consistency: {self.consistency}, importer: {self.importer}, tables: {",".join([t.table for t in self.tables])}, session: {self.session}'
48
+
49
+ def __eq__(self, other):
50
+ if not isinstance(other, ExportSpec):
51
+ return NotImplemented
52
+
53
+ return self.keyspace == other.keyspace and self.tables == other.tables and self.consistency == other.consistency and self.importer == other.importer and self.session == other.session
54
+
55
+ def parse_specs(specs_str: str):
56
+ keyspace: str = None
57
+ consistency: str = None
58
+ importer: str = None
59
+ specs: list[ExportTableSpec] = None
60
+
61
+ if specs_str:
62
+ importer, specs_str = ExportSpec._extract_importer(specs_str.strip(' '))
63
+ keyspace, specs_str = ExportSpec._extract_keyspace(specs_str)
64
+ consistency, specs = ExportSpec._extract_consisteny(specs_str)
65
+
66
+ return ExportSpec(keyspace, consistency, importer, specs)
67
+
68
+ def _extract_keyspace(spec_str: str) -> tuple[str, str]:
69
+ keyspace = None
70
+ rest = spec_str
71
+
72
+ p = re.compile(r"\s*\*\s+in\s+(\S+)(.*)", re.IGNORECASE)
73
+ match = p.match(spec_str)
74
+ if match:
75
+ keyspace = match.group(1).strip(' ')
76
+ rest = match.group(2).strip(' ')
77
+ elif spec_str.startswith('*'):
78
+ keyspace = '*'
79
+ rest = spec_str[1:].strip(' ')
80
+
81
+ return keyspace, rest
82
+
83
+ def _extract_consisteny(spec_str: str) -> tuple[str, list['ExportTableSpec']]:
84
+ consistency = None
85
+
86
+ p = re.compile(r"(.*?)with\s+consistency\s+(.*)", re.IGNORECASE)
87
+ match = p.match(spec_str)
88
+ if match:
89
+ spec_str = match.group(1).strip(' ')
90
+ consistency = match.group(2)
91
+
92
+ if spec_str:
93
+ p = r",\s*(?![^()]*\))"
94
+ specs = re.split(p, spec_str)
95
+
96
+ return consistency, [ExportTableSpec.parse(spec) for spec in specs]
97
+
98
+ return consistency, None
99
+
100
+ class ExportTableSpec:
101
+ def __init__(self, keyspace: str, table: str, columns: str = None, target_table: str = None):
102
+ self.keyspace = keyspace
103
+ self.table = table
104
+ self.columns = columns
105
+ self.target_table = target_table
106
+
107
+ def __str__(self):
108
+ return f'{self.keyspace}.{self.table}({self.columns}) AS {self.target_table}'
109
+
110
+ def __eq__(self, other):
111
+ if not isinstance(other, ExportTableSpec):
112
+ return NotImplemented
113
+
114
+ return self.keyspace == other.keyspace and self.table == other.table and self.columns == other.columns and self.target_table == other.target_table
115
+
116
+ def from_status(status: 'ExportTableStatus'):
117
+ return ExportTableSpec(status.keyspace, status.table, target_table=status.target_table)
118
+
119
+ def parse(spec_str: str) -> 'ExportTableSpec':
120
+ target = None
121
+
122
+ p = re.compile(r"(.*?)\s+as\s+(.*)", re.IGNORECASE)
123
+ match = p.match(spec_str)
124
+ if match:
125
+ spec_str = match.group(1)
126
+ target = match.group(2)
127
+
128
+ keyspace = None
129
+ table = spec_str
130
+ columns = None
131
+
132
+ p = re.compile('(.*?)\.(.*?)\((.*)\)')
133
+ match = p.match(spec_str)
134
+ if match:
135
+ keyspace = match.group(1)
136
+ table = match.group(2)
137
+ columns = match.group(3)
138
+ else:
139
+ p = re.compile('(.*?)\.(.*)')
140
+ match = p.match(spec_str)
141
+ if match:
142
+ keyspace = match.group(1)
143
+ table = match.group(2)
144
+
145
+ return ExportTableSpec(keyspace, table, columns, target)
146
+
147
+ def __eq__(self, other):
148
+ if isinstance(other, ExportTableSpec):
149
+ return self.keyspace == other.keyspace and self.table == other.table and self.columns == other.columns and self.target_table == other.target_table
150
+
151
+ return False
152
+
153
+ def __str__(self):
154
+ return f'{self.keyspace}.{self.table}({self.columns}) as {self.target_table}'
155
+
156
+ class ExportTableStatus:
157
+ def __init__(self, keyspace: str, target_table: str, status: str, table: str = None):
158
+ self.keyspace = keyspace
159
+ self.target_table = target_table
160
+ self.status = status
161
+ self.table = table
162
+
163
+ def __str__(self):
164
+ return f'{self.keyspace}.{self.table} as {self.target_table} = {self.status}'
165
+
166
+ def __eq__(self, other):
167
+ if isinstance(other, ExportTableStatus):
168
+ return self.keyspace == other.keyspace and self.table == other.table and self.status == other.status and self.target_table == other.target_table
169
+
170
+ return False
171
+
172
+ def from_session(sts: str, pod: str, namespace: str, export_session: str):
173
+ statuses: list[ExportTableStatus] = []
174
+
175
+ status_in_whole = 'done'
176
+ log_files: list[str] = find_files(pod, namespace, f'{log_prefix()}-{export_session}_*.log*')
177
+
178
+ for log_file in log_files:
179
+ status: ExportTableStatus = ExportTableStatus.from_log_file(pod, namespace, export_session, log_file)
180
+ statuses.append(status)
181
+
182
+ if status.status != 'done':
183
+ status_in_whole = status.status
184
+
185
+ return statuses, status_in_whole
186
+
187
+ def from_log_file(pod: str, namespace: str, copy_session: str, log_file: str):
188
+ def get_csv_files_n_table(target_table: str):
189
+ db = f'{copy_session}_{target_table}'
190
+ csv_file = f'{csv_dir()}/{db}/*.csv'
191
+ csv_files: list[str] = find_files(pod, namespace, csv_file)
192
+ if csv_files:
193
+ table = target_table
194
+ m = re.match(f'{csv_dir()}/{db}/(.*).csv', csv_files[0])
195
+ if m:
196
+ table = m.group(1)
197
+ return csv_files, table
198
+
199
+ return csv_files, target_table
200
+
201
+ m = re.match(f'{log_prefix()}-{copy_session}_(.*?)\.(.*?)\.log(.*)', log_file)
202
+ if m:
203
+ keyspace = m.group(1)
204
+ target_table = m.group(2)
205
+ state = m.group(3)
206
+ if state == '.pending_import':
207
+ _, table = get_csv_files_n_table(target_table)
208
+ return ExportTableStatus(keyspace, target_table, 'pending_import', table)
209
+ elif state == '.done':
210
+ return ExportTableStatus(keyspace, target_table, 'done', target_table)
211
+
212
+ # 4 rows exported to 1 files in 0 day, 0 hour, 0 minute, and 1.335 seconds.
213
+ pattern = 'rows exported to'
214
+ r: PodExecResult = CassandraNodes.exec(pod, namespace, f"grep '{pattern}' {log_file}", show_out=Config().is_debug(), shell='bash')
215
+ if r.exit_code() == 0:
216
+ csv_files, table = get_csv_files_n_table(target_table)
217
+ if csv_files:
218
+ return ExportTableStatus(keyspace, target_table, 'exported', table)
219
+ else:
220
+ return ExportTableStatus(keyspace, target_table, 'imported', target_table)
221
+ else:
222
+ return ExportTableStatus(keyspace, target_table, 'export_in_pregress')
223
+
224
+ return ExportTableStatus(None, None, 'unknown')
225
+
226
+ def csv_dir():
227
+ return Config().get('export.csv_dir', '/c3/cassandra/tmp')
228
+
229
+ def find_files(pod: str, namespace: str, pattern: str, mmin: int = 0):
230
+ if mmin:
231
+ r = CassandraNodes.exec(pod, namespace, f'find {pattern} -mmin -{mmin}', show_out=Config().is_debug(), shell='bash')
232
+ else:
233
+ r = CassandraNodes.exec(pod, namespace, f'find {pattern}', show_out=Config().is_debug(), shell='bash')
234
+
235
+ log_files = []
236
+ for line in r.stdout.split('\n'):
237
+ line = line.strip(' \r')
238
+ if line:
239
+ log_files.append(line)
240
+
241
+ return log_files
242
+
243
+ class GeneratorStream(io.RawIOBase):
244
+ def __init__(self, generator):
245
+ self._generator = generator
246
+ self._buffer = b'' # Buffer to store leftover bytes from generator yields
247
+
248
+ def readable(self):
249
+ return True
250
+
251
+ def _read_from_generator(self):
252
+ try:
253
+ chunk = next(self._generator)
254
+ if isinstance(chunk, str):
255
+ chunk = chunk.encode('utf-8') # Encode if generator yields strings
256
+ self._buffer += chunk
257
+ except StopIteration:
258
+ pass # Generator exhausted
259
+
260
+ def readinto(self, b):
261
+ # Fill the buffer if necessary
262
+ while len(self._buffer) < len(b):
263
+ old_buffer_len = len(self._buffer)
264
+ self._read_from_generator()
265
+ if len(self._buffer) == old_buffer_len: # Generator exhausted and buffer empty
266
+ break
267
+
268
+ bytes_to_read = min(len(b), len(self._buffer))
269
+ b[:bytes_to_read] = self._buffer[:bytes_to_read]
270
+ self._buffer = self._buffer[bytes_to_read:]
271
+ return bytes_to_read
272
+
273
+ def read(self, size=-1):
274
+ if size == -1: # Read all remaining data
275
+ while True:
276
+ old_buffer_len = len(self._buffer)
277
+ self._read_from_generator()
278
+ if len(self._buffer) == old_buffer_len:
279
+ break
280
+ data = self._buffer
281
+ self._buffer = b''
282
+ return data
283
+ else:
284
+ # Ensure enough data in buffer
285
+ while len(self._buffer) < size:
286
+ old_buffer_len = len(self._buffer)
287
+ self._read_from_generator()
288
+ if len(self._buffer) == old_buffer_len:
289
+ break
290
+
291
+ data = self._buffer[:size]
292
+ self._buffer = self._buffer[size:]
293
+ return data
294
+
295
+ class PodHandler:
296
+ def __init__(self, state: ReplState):
297
+ self.state = state
298
+
299
+ def __enter__(self):
300
+ state = self.state
301
+
302
+ if not state.pod:
303
+ state.push()
304
+ state.pod = StatefulSets.pod_names(state.sts, state.namespace)[0]
305
+
306
+ return state
307
+
308
+ def __exit__(self, exc_type, exc_val, exc_tb):
309
+ self.state.pop()
310
+
311
+ return False
312
+
313
+ def state_with_pod(state: ReplState):
314
+ return PodHandler(state)
@@ -1,7 +1,7 @@
1
- from walker.commands.command import Command
2
- from walker.repl_commands import ReplCommands
3
- from walker.repl_state import ReplState
4
- from walker.utils import lines_to_tabular, log
1
+ from adam.commands.command import Command
2
+ from adam.repl_commands import ReplCommands
3
+ from adam.repl_state import ReplState
4
+ from adam.utils import lines_to_tabular, log
5
5
 
6
6
  class Help(Command):
7
7
  COMMAND = 'help'
@@ -23,20 +23,25 @@ class Help(Command):
23
23
  return super().run(cmd, state)
24
24
 
25
25
  def section(cmds : list[ReplCommands]):
26
- return [f' {c.help(state)}' for c in cmds if c.help(state)]
26
+ sorted_cmds = sorted(cmds, key=lambda cmd: cmd.command())
27
+ return [f' {c.help(state)}' for c in sorted_cmds if c.help(state)]
27
28
 
28
29
  lines = []
29
- lines.append('NAVIGATION & HELP')
30
- lines.append(' a: | c: | p:\t switch to another operational device: App, Cassandra or Postgres')
30
+ lines.append('NAVIGATION')
31
+ lines.append(' a: | c: | l: | p: | x:\t switch to another operational device: App, Cassandra, Audit, Postgres or Export')
31
32
  lines.extend(section(ReplCommands.navigation()))
32
- lines.append('CHECK CASSANDRA')
33
- lines.extend(section(ReplCommands.cassandra_check()))
34
- lines.append('CASSANDRA OPERATIONS')
33
+ lines.append('CASSANDRA')
35
34
  lines.extend(section(ReplCommands.cassandra_ops()))
35
+ lines.append('POSTGRES')
36
+ lines.extend(section(ReplCommands.postgres_ops()))
37
+ lines.append('APP')
38
+ lines.extend(section(ReplCommands.app_ops()))
39
+ lines.append('EXPORT DB')
40
+ lines.extend(section(ReplCommands.export_ops()))
41
+ lines.append('AUDIT')
42
+ lines.extend(section(ReplCommands.audit_ops()))
36
43
  lines.append('TOOLS')
37
44
  lines.extend(section(ReplCommands.tools()))
38
- lines.append('APP')
39
- lines.extend(section(ReplCommands.app()))
40
45
  lines.append('')
41
46
  lines.extend(section(ReplCommands.exit()))
42
47
 
@@ -0,0 +1,49 @@
1
+ from abc import abstractmethod
2
+
3
+ from adam.commands.command import Command
4
+ from adam.commands.command_helpers import ClusterCommandHelper
5
+ from adam.repl_state import ReplState
6
+ from adam.utils import lines_to_tabular, log, log2
7
+
8
+ class IntermediateCommand(Command):
9
+ def run(self, cmd: str, state: ReplState):
10
+ if not(args := self.args(cmd)):
11
+ return super().run(cmd, state)
12
+
13
+ return self.intermediate_run(cmd, state, args, self.cmd_list())
14
+
15
+ @abstractmethod
16
+ def cmd_list(self):
17
+ pass
18
+
19
+ def intermediate_run(self, cmd: str, state: ReplState, args: list[str], cmds: list['Command'], separator='\t', display_help=True):
20
+ state, _ = self.apply_state(args, state)
21
+
22
+ if state.in_repl:
23
+ if display_help:
24
+ log(lines_to_tabular([c.help(state) for c in cmds], separator=separator))
25
+
26
+ return 'command-missing'
27
+ else:
28
+ # head with the Chain of Responsibility pattern
29
+ if not self.run_subcommand(cmd, state):
30
+ if display_help:
31
+ log2('* Command is missing.')
32
+ Command.display_help()
33
+ return 'command-missing'
34
+
35
+ return state
36
+
37
+ def run_subcommand(self, cmd: str, state: ReplState):
38
+ cmds = Command.chain(self.cmd_list())
39
+ return cmds.run(cmd, state)
40
+
41
+ def intermediate_help(super_help: str, cmd: str, cmd_list: list['Command'], separator='\t', show_cluster_help=False):
42
+ log(super_help)
43
+ log()
44
+ log('Sub-Commands:')
45
+
46
+ log(lines_to_tabular([c.help(ReplState()).replace(f'{cmd} ', ' ', 1) for c in cmd_list], separator=separator))
47
+ if show_cluster_help:
48
+ log()
49
+ ClusterCommandHelper.cluster_help()
@@ -0,0 +1,43 @@
1
+ from adam.checks.check_result import CheckResult
2
+ from adam.checks.check_utils import run_checks
3
+ from adam.commands import extract_options
4
+ from adam.commands.command import Command
5
+ from adam.repl_state import ReplState
6
+ from adam.utils_issues import IssuesUtils
7
+
8
+ class Issues(Command):
9
+ COMMAND = 'issues'
10
+
11
+ # the singleton pattern
12
+ def __new__(cls, *args, **kwargs):
13
+ if not hasattr(cls, 'instance'): cls.instance = super(Issues, cls).__new__(cls)
14
+
15
+ return cls.instance
16
+
17
+ def __init__(self, successor: Command=None):
18
+ super().__init__(successor)
19
+
20
+ def command(self):
21
+ return Issues.COMMAND
22
+
23
+ def required(self):
24
+ return ReplState.NON_L
25
+
26
+ def run(self, cmd: str, state: ReplState):
27
+ if not(args := self.args(cmd)):
28
+ return super().run(cmd, state)
29
+
30
+ with self.validate(args, state) as (args, state):
31
+ with extract_options(args, ['-s', '--show']) as (args, show_out):
32
+ results = run_checks(state.sts, state.namespace, state.pod, show_out=show_out)
33
+
34
+ issues = CheckResult.collect_issues(results)
35
+ IssuesUtils.show_issues(issues, in_repl=state.in_repl)
36
+
37
+ return issues if issues else 'issues'
38
+
39
+ def completion(self, state: ReplState):
40
+ return super().completion(state, {'-s': None})
41
+
42
+ def help(self, _: ReplState):
43
+ return f'{Issues.COMMAND} [-s]\t find all issues'
@@ -0,0 +1,38 @@
1
+ import subprocess
2
+
3
+ from adam.commands.command import Command
4
+ from adam.repl_state import ReplState, RequiredState
5
+
6
+ class Kubectl(Command):
7
+ COMMAND = 'k'
8
+
9
+ # the singleton pattern
10
+ def __new__(cls, *args, **kwargs):
11
+ if not hasattr(cls, 'instance'): cls.instance = super(Kubectl, cls).__new__(cls)
12
+
13
+ return cls.instance
14
+
15
+ def __init__(self, successor: Command=None):
16
+ super().__init__(successor)
17
+
18
+ def command(self):
19
+ return Kubectl.COMMAND
20
+
21
+ def required(self):
22
+ return RequiredState.NAMESPACE
23
+
24
+ def run(self, cmd: str, state: ReplState):
25
+ if not(args := self.args(cmd)):
26
+ return super().run(cmd, state)
27
+
28
+ with self.validate(args, state) as (args, state):
29
+ subprocess.run(["kubectl"] + args)
30
+
31
+ return state
32
+
33
+ def completion(self, state: ReplState):
34
+ return super().completion(state)
35
+
36
+
37
+ def help(self, _: ReplState):
38
+ return f'{Kubectl.COMMAND} \t run a kubectl command'
adam/commands/login.py ADDED
@@ -0,0 +1,70 @@
1
+ import os
2
+ import signal
3
+ import traceback
4
+
5
+ from adam.app_session import AppSession
6
+ from adam.apps import Apps
7
+ from adam.commands import extract_options
8
+ from adam.config import Config
9
+ from adam.sso.idp import Idp
10
+ from adam.sso.idp_login import IdpLogin
11
+ from adam.commands.command import Command
12
+ from adam.repl_state import ReplState, RequiredState
13
+ from adam.utils import log, log2
14
+
15
+ class Login(Command):
16
+ COMMAND = 'login'
17
+
18
+ # the singleton pattern
19
+ def __new__(cls, *args, **kwargs):
20
+ if not hasattr(cls, 'instance'): cls.instance = super(Login, cls).__new__(cls)
21
+
22
+ return cls.instance
23
+
24
+ def __init__(self, successor: Command=None):
25
+ super().__init__(successor)
26
+
27
+ def command(self):
28
+ return Login.COMMAND
29
+
30
+ def required(self):
31
+ return ReplState.NON_L
32
+
33
+ def run(self, cmd: str, state: ReplState):
34
+ if not(args := self.args(cmd)):
35
+ return super().run(cmd, state)
36
+
37
+ def custom_handler(signum, frame):
38
+ AppSession.ctrl_c_entered = True
39
+
40
+ signal.signal(signal.SIGINT, custom_handler)
41
+
42
+ with self.validate(args, state) as (args, state):
43
+ with extract_options(args, 'd') as (args, debug):
44
+ if debug:
45
+ Config().set('debug', True)
46
+
47
+ username: str = os.getenv('USERNAME')
48
+ if len(args) > 0:
49
+ username = args[0]
50
+
51
+ login: IdpLogin = None
52
+ try:
53
+ if not(host := Apps.app_host('c3', 'c3', state.namespace)):
54
+ log2('Cannot locate ingress for app.')
55
+ return state
56
+
57
+ if not (login := Idp.login(host, username=username, use_token_from_env=False)):
58
+ log2('Invalid username/password. Please try again.')
59
+ except:
60
+ log2(traceback.format_exc())
61
+
62
+ log(f'IDP_TOKEN={login.ser() if login else ""}')
63
+
64
+ return state
65
+
66
+ def completion(self, state: ReplState):
67
+ return super().completion(state)
68
+
69
+ def help(self, _: ReplState):
70
+ return f'{Login.COMMAND}\t SSO login'
@@ -1,7 +1,7 @@
1
- from walker.commands.command import Command
2
- from walker.config import Config
3
- from walker.k8s_utils.cassandra_nodes import CassandraNodes
4
- from walker.repl_state import ReplState, RequiredState
1
+ from adam.commands.command import Command
2
+ from adam.config import Config
3
+ from adam.utils_k8s.cassandra_nodes import CassandraNodes
4
+ from adam.repl_state import ReplState, RequiredState
5
5
 
6
6
  class Logs(Command):
7
7
  COMMAND = 'logs'
@@ -25,14 +25,12 @@ class Logs(Command):
25
25
  if not(args := self.args(cmd)):
26
26
  return super().run(cmd, state)
27
27
 
28
- state, args = self.apply_state(args, state)
29
- if not self.validate_state(state):
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):
33
+ # available only on cli
36
34
  return {}
37
35
 
38
36
  def help(self, _: ReplState):