kaqing 2.0.145__py3-none-any.whl → 2.0.189__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.

Files changed (209) hide show
  1. adam/__init__.py +0 -2
  2. adam/app_session.py +9 -12
  3. adam/apps.py +18 -4
  4. adam/batch.py +4 -4
  5. adam/checks/check_utils.py +16 -46
  6. adam/checks/cpu.py +7 -1
  7. adam/checks/cpu_metrics.py +52 -0
  8. adam/checks/disk.py +2 -3
  9. adam/columns/columns.py +3 -1
  10. adam/columns/cpu.py +3 -1
  11. adam/columns/cpu_metrics.py +22 -0
  12. adam/columns/memory.py +3 -4
  13. adam/commands/__init__.py +24 -0
  14. adam/commands/alter_tables.py +33 -48
  15. adam/commands/app/__init__.py +0 -0
  16. adam/commands/app/app.py +38 -0
  17. adam/commands/{app_ping.py → app/app_ping.py} +7 -13
  18. adam/commands/app/show_app_actions.py +49 -0
  19. adam/commands/{show → app}/show_app_id.py +8 -11
  20. adam/commands/{show → app}/show_app_queues.py +7 -14
  21. adam/commands/app/utils_app.py +106 -0
  22. adam/commands/audit/audit.py +21 -40
  23. adam/commands/audit/audit_repair_tables.py +14 -19
  24. adam/commands/audit/audit_run.py +14 -22
  25. adam/commands/audit/completions_l.py +15 -0
  26. adam/commands/audit/show_last10.py +4 -19
  27. adam/commands/audit/show_slow10.py +4 -18
  28. adam/commands/audit/show_top10.py +4 -16
  29. adam/commands/audit/utils_show_top10.py +15 -3
  30. adam/commands/bash/__init__.py +5 -0
  31. adam/commands/bash/bash.py +7 -104
  32. adam/commands/bash/utils_bash.py +16 -0
  33. adam/commands/cat.py +7 -27
  34. adam/commands/cd.py +7 -11
  35. adam/commands/check.py +15 -24
  36. adam/commands/cli_commands.py +8 -4
  37. adam/commands/clipboard_copy.py +87 -0
  38. adam/commands/code.py +21 -24
  39. adam/commands/command.py +207 -42
  40. adam/commands/commands_utils.py +25 -27
  41. adam/commands/cql/completions_c.py +28 -0
  42. adam/commands/cql/cqlsh.py +9 -33
  43. adam/commands/cql/{cql_utils.py → utils_cql.py} +111 -15
  44. adam/commands/deploy/code_start.py +7 -10
  45. adam/commands/deploy/code_stop.py +4 -21
  46. adam/commands/deploy/code_utils.py +3 -3
  47. adam/commands/deploy/deploy.py +4 -27
  48. adam/commands/deploy/deploy_frontend.py +14 -17
  49. adam/commands/deploy/deploy_pg_agent.py +3 -6
  50. adam/commands/deploy/deploy_pod.py +64 -68
  51. adam/commands/deploy/undeploy.py +4 -27
  52. adam/commands/deploy/undeploy_frontend.py +4 -7
  53. adam/commands/deploy/undeploy_pg_agent.py +5 -8
  54. adam/commands/deploy/undeploy_pod.py +9 -12
  55. adam/commands/devices/device.py +124 -2
  56. adam/commands/devices/device_app.py +41 -24
  57. adam/commands/devices/device_auit_log.py +10 -4
  58. adam/commands/devices/device_cass.py +48 -14
  59. adam/commands/devices/device_export.py +13 -12
  60. adam/commands/devices/device_postgres.py +105 -54
  61. adam/commands/download_file.py +47 -0
  62. adam/commands/exit.py +1 -4
  63. adam/commands/export/clean_up_all_export_sessions.py +37 -0
  64. adam/commands/export/clean_up_export_sessions.py +9 -10
  65. adam/commands/export/completions_x.py +11 -0
  66. adam/commands/export/download_export_session.py +40 -0
  67. adam/commands/export/drop_export_database.py +7 -26
  68. adam/commands/export/drop_export_databases.py +5 -14
  69. adam/commands/export/export.py +6 -52
  70. adam/commands/export/export_databases.py +108 -32
  71. adam/commands/export/export_select.py +8 -59
  72. adam/commands/export/export_sessions.py +209 -0
  73. adam/commands/export/export_use.py +14 -20
  74. adam/commands/export/export_x_select.py +48 -0
  75. adam/commands/export/exporter.py +135 -167
  76. adam/commands/export/import_files.py +44 -0
  77. adam/commands/export/import_session.py +11 -35
  78. adam/commands/export/importer.py +19 -5
  79. adam/commands/export/importer_athena.py +112 -44
  80. adam/commands/export/importer_sqlite.py +42 -22
  81. adam/commands/export/show_column_counts.py +13 -31
  82. adam/commands/export/show_export_databases.py +7 -7
  83. adam/commands/export/show_export_session.py +8 -20
  84. adam/commands/export/show_export_sessions.py +6 -16
  85. adam/commands/export/utils_export.py +64 -11
  86. adam/commands/find_files.py +51 -0
  87. adam/commands/find_processes.py +76 -0
  88. adam/commands/head.py +36 -0
  89. adam/commands/help.py +2 -2
  90. adam/commands/intermediate_command.py +52 -0
  91. adam/commands/issues.py +11 -43
  92. adam/commands/kubectl.py +3 -6
  93. adam/commands/login.py +22 -24
  94. adam/commands/logs.py +3 -6
  95. adam/commands/ls.py +9 -10
  96. adam/commands/medusa/medusa.py +4 -22
  97. adam/commands/medusa/medusa_backup.py +20 -27
  98. adam/commands/medusa/medusa_restore.py +49 -46
  99. adam/commands/medusa/medusa_show_backupjobs.py +16 -18
  100. adam/commands/medusa/medusa_show_restorejobs.py +13 -18
  101. adam/commands/medusa/utils_medusa.py +15 -0
  102. adam/commands/nodetool.py +7 -21
  103. adam/commands/param_get.py +11 -14
  104. adam/commands/param_set.py +8 -12
  105. adam/commands/postgres/completions_p.py +22 -0
  106. adam/commands/postgres/postgres.py +34 -57
  107. adam/commands/postgres/postgres_databases.py +270 -0
  108. adam/commands/postgres/postgres_ls.py +4 -8
  109. adam/commands/postgres/postgres_preview.py +5 -9
  110. adam/commands/postgres/utils_postgres.py +79 -0
  111. adam/commands/preview_table.py +8 -45
  112. adam/commands/pwd.py +13 -16
  113. adam/commands/reaper/reaper.py +4 -27
  114. adam/commands/reaper/reaper_forward.py +49 -56
  115. adam/commands/reaper/reaper_forward_session.py +6 -0
  116. adam/commands/reaper/reaper_forward_stop.py +10 -16
  117. adam/commands/reaper/reaper_restart.py +7 -14
  118. adam/commands/reaper/reaper_run_abort.py +8 -33
  119. adam/commands/reaper/reaper_runs.py +43 -58
  120. adam/commands/reaper/reaper_runs_abort.py +29 -49
  121. adam/commands/reaper/reaper_schedule_activate.py +14 -33
  122. adam/commands/reaper/reaper_schedule_start.py +9 -33
  123. adam/commands/reaper/reaper_schedule_stop.py +9 -33
  124. adam/commands/reaper/reaper_schedules.py +4 -14
  125. adam/commands/reaper/reaper_status.py +8 -16
  126. adam/commands/reaper/utils_reaper.py +203 -0
  127. adam/commands/repair/repair.py +4 -22
  128. adam/commands/repair/repair_log.py +5 -11
  129. adam/commands/repair/repair_run.py +27 -34
  130. adam/commands/repair/repair_scan.py +32 -40
  131. adam/commands/repair/repair_stop.py +5 -12
  132. adam/commands/report.py +27 -29
  133. adam/commands/restart.py +25 -26
  134. adam/commands/rollout.py +19 -24
  135. adam/commands/shell.py +12 -4
  136. adam/commands/show/show.py +11 -27
  137. adam/commands/show/show_adam.py +3 -3
  138. adam/commands/show/show_cassandra_repairs.py +37 -0
  139. adam/commands/show/show_cassandra_status.py +47 -51
  140. adam/commands/show/show_cassandra_version.py +5 -18
  141. adam/commands/show/show_cli_commands.py +56 -0
  142. adam/commands/show/show_host.py +1 -1
  143. adam/commands/show/show_login.py +20 -27
  144. adam/commands/show/show_params.py +2 -5
  145. adam/commands/show/show_processes.py +18 -21
  146. adam/commands/show/show_storage.py +11 -20
  147. adam/commands/watch.py +26 -29
  148. adam/config.py +5 -16
  149. adam/embedded_params.py +1 -1
  150. adam/log.py +4 -4
  151. adam/pod_exec_result.py +3 -3
  152. adam/repl.py +45 -39
  153. adam/repl_commands.py +26 -19
  154. adam/repl_session.py +8 -1
  155. adam/repl_state.py +85 -36
  156. adam/sql/lark_completer.py +284 -0
  157. adam/sql/lark_parser.py +604 -0
  158. adam/sql/sql_completer.py +4 -6
  159. adam/sql/sql_state_machine.py +29 -16
  160. adam/sso/authn_ad.py +6 -8
  161. adam/sso/authn_okta.py +4 -6
  162. adam/sso/cred_cache.py +3 -5
  163. adam/sso/idp.py +9 -12
  164. adam/utils.py +484 -37
  165. adam/utils_athena.py +19 -19
  166. adam/utils_audits.py +12 -12
  167. adam/utils_issues.py +32 -0
  168. adam/utils_k8s/app_clusters.py +14 -19
  169. adam/utils_k8s/app_pods.py +7 -2
  170. adam/utils_k8s/cassandra_clusters.py +30 -19
  171. adam/utils_k8s/cassandra_nodes.py +2 -2
  172. adam/utils_k8s/custom_resources.py +16 -17
  173. adam/utils_k8s/ingresses.py +2 -2
  174. adam/utils_k8s/jobs.py +7 -11
  175. adam/utils_k8s/k8s.py +96 -0
  176. adam/utils_k8s/kube_context.py +2 -2
  177. adam/utils_k8s/pods.py +37 -81
  178. adam/utils_k8s/secrets.py +4 -4
  179. adam/utils_k8s/service_accounts.py +5 -4
  180. adam/utils_k8s/services.py +2 -2
  181. adam/utils_k8s/statefulsets.py +6 -14
  182. adam/utils_local.py +4 -0
  183. adam/utils_repl/appendable_completer.py +6 -0
  184. adam/utils_repl/repl_completer.py +128 -2
  185. adam/utils_repl/state_machine.py +3 -3
  186. adam/utils_sqlite.py +78 -42
  187. adam/version.py +1 -1
  188. {kaqing-2.0.145.dist-info → kaqing-2.0.189.dist-info}/METADATA +1 -1
  189. kaqing-2.0.189.dist-info/RECORD +253 -0
  190. kaqing-2.0.189.dist-info/top_level.txt +2 -0
  191. teddy/__init__.py +0 -0
  192. teddy/lark_parser.py +436 -0
  193. teddy/lark_parser2.py +618 -0
  194. adam/commands/app.py +0 -67
  195. adam/commands/cp.py +0 -95
  196. adam/commands/cql/cql_completions.py +0 -28
  197. adam/commands/export/clean_up_export_session.py +0 -53
  198. adam/commands/export/export_select_x.py +0 -54
  199. adam/commands/postgres/postgres_context.py +0 -248
  200. adam/commands/postgres/postgres_utils.py +0 -31
  201. adam/commands/postgres/psql_completions.py +0 -10
  202. adam/commands/reaper/reaper_session.py +0 -159
  203. adam/commands/show/show_app_actions.py +0 -56
  204. adam/commands/show/show_commands.py +0 -61
  205. adam/commands/show/show_repairs.py +0 -47
  206. kaqing-2.0.145.dist-info/RECORD +0 -227
  207. kaqing-2.0.145.dist-info/top_level.txt +0 -1
  208. {kaqing-2.0.145.dist-info → kaqing-2.0.189.dist-info}/WHEEL +0 -0
  209. {kaqing-2.0.145.dist-info → kaqing-2.0.189.dist-info}/entry_points.txt +0 -0
@@ -1,23 +1,20 @@
1
- from concurrent.futures import ThreadPoolExecutor, as_completed
2
1
  from datetime import datetime
3
- import functools
4
- import re
5
2
  import time
6
- import traceback
7
3
 
8
- from adam.commands.cql.cql_utils import cassandra_table_names, run_cql, table_spec
9
- from adam.commands.export.export_databases import ExportDatabases
4
+ from adam.commands.command import InvalidArgumentsException
5
+ from adam.commands.cql.utils_cql import cassandra_table_names, run_cql, table_spec
6
+ from adam.commands.export.export_databases import export_db
7
+ from adam.commands.export.export_sessions import ExportSessions
10
8
  from adam.commands.export.importer import Importer
11
9
  from adam.commands.export.importer_athena import AthenaImporter
12
10
  from adam.commands.export.importer_sqlite import SqliteImporter
13
- from adam.commands.export.utils_export import ExportSpec, ExportTableStatus, ExportTableSpec, ImportSpec, csv_dir, find_files
11
+ from adam.commands.export.utils_export import ExportSpec, ExportTableStatus, ExportTableSpec, ImportSpec, csv_dir, find_files, state_with_pod
14
12
  from adam.config import Config
15
13
  from adam.pod_exec_result import PodExecResult
16
14
  from adam.repl_state import ReplState
17
- from adam.utils import elapsed_time, log2, ing
15
+ from adam.utils import debug, log, parallelize, log2, ing, log_exc
18
16
  from adam.utils_k8s.cassandra_nodes import CassandraNodes
19
17
  from adam.utils_k8s.pods import log_prefix
20
- from adam.utils_k8s.statefulsets import StatefulSets
21
18
 
22
19
  class Exporter:
23
20
  def export_tables(args: list[str], state: ReplState, export_only: bool = False, max_workers = 0) -> tuple[list[str], ExportSpec]:
@@ -25,7 +22,7 @@ class Exporter:
25
22
  log2('export-only for testing')
26
23
 
27
24
  spec: ExportSpec = None
28
- try:
25
+ with log_exc(True):
29
26
  spec = Exporter.export_spec(' '.join(args), state)
30
27
 
31
28
  statuses, spec = Exporter._export_tables(spec, state, max_workers=max_workers, export_state='init')
@@ -33,8 +30,6 @@ class Exporter:
33
30
  return statuses, spec
34
31
 
35
32
  return Exporter._export_tables(spec, state, export_only, max_workers, 'pending_export')
36
- except Exception as e:
37
- log2(e)
38
33
 
39
34
  return [], None
40
35
 
@@ -53,12 +48,19 @@ class Exporter:
53
48
  raise Exception(f"You're currently using {importer_from_session} export database. You cannot export tables with {spec.importer} type database.")
54
49
  else:
55
50
  spec.importer = Importer.importer_from_session(session)
51
+
52
+ if spec.importer == 'athena' and not AthenaImporter.ping():
53
+ raise Exception('Credentials for Athena is not present.')
56
54
  else:
57
55
  if not spec.importer:
58
56
  spec.importer = Config().get('export.default-importer', 'sqlite')
59
57
 
60
58
  prefix = Importer.prefix_from_importer(spec.importer)
61
59
  session = f'{prefix}{datetime.now().strftime("%Y%m%d%H%M%S")[3:]}'
60
+
61
+ if spec.importer == 'athena' and not AthenaImporter.ping():
62
+ raise Exception('Credentials for Athena is not present.')
63
+
62
64
  if spec.importer != 'csv':
63
65
  state.export_session = session
64
66
 
@@ -66,10 +68,10 @@ class Exporter:
66
68
 
67
69
  return spec
68
70
 
69
- def import_session(args: list[str], state: ReplState, max_workers = 0) -> tuple[list[str], ExportSpec]:
71
+ def import_session(spec_str: str, state: ReplState, max_workers = 0) -> tuple[list[str], ExportSpec]:
70
72
  import_spec: ImportSpec = None
71
- try:
72
- import_spec = Exporter.import_spec(' '.join(args), state)
73
+ with log_exc(True):
74
+ import_spec = Exporter.import_spec(spec_str, state)
73
75
  tables, status_in_whole = ExportTableStatus.from_session(state.sts, state.pod, state.namespace, import_spec.session)
74
76
  if status_in_whole == 'done':
75
77
  log2(f'The session has been completely done - no more csv files are found.')
@@ -77,17 +79,34 @@ class Exporter:
77
79
 
78
80
  spec = ExportSpec(None, None, importer=import_spec.importer, tables=[ExportTableSpec.from_status(table) for table in tables], session=import_spec.session)
79
81
 
80
- return Exporter._export_tables(spec, state, max_workers=max_workers)
81
- except Exception as e:
82
- if Config().is_debug():
83
- traceback.print_exception(e)
84
- else:
85
- log2(e)
82
+ return Exporter._export_tables(spec, state, max_workers=max_workers, export_state = 'import')
83
+
84
+ return [], None
85
+
86
+ def import_local_csv_files(spec_str: str, state: ReplState, max_workers = 0) -> tuple[list[str], ExportSpec]:
87
+ spec: ImportSpec = None
88
+ with log_exc(True):
89
+ spec = Exporter.import_spec(spec_str, state, files=True)
90
+ if not spec.table_name:
91
+ log2(f"Use 'as <database-name>.<table-name>'.")
92
+ raise InvalidArgumentsException()
93
+
94
+ d_t = spec.table_name.split('.')
95
+ if len(d_t) != 2:
96
+ log2(f'Need <database-name>.<table-name> format for target table.')
97
+ raise InvalidArgumentsException()
98
+
99
+ database = d_t[0]
100
+ table = d_t[1]
101
+ im = AthenaImporter() if spec.importer == 'athena' else SqliteImporter()
102
+
103
+ with parallelize(spec.files, max_workers, msg='Importing|Imported {size} csv files') as exec:
104
+ return exec.map(lambda f: im.import_from_local_csv(state, database, table, f, len(spec.files) > 1, True)), spec
86
105
 
87
106
  return [], None
88
107
 
89
- def import_spec(spec_str: str, state: ReplState):
90
- spec: ImportSpec = ImportSpec.parse_specs(spec_str)
108
+ def import_spec(spec_str: str, state: ReplState, files = False):
109
+ spec: ImportSpec = ImportSpec.parse_specs(spec_str, files=files)
91
110
 
92
111
  session = state.export_session
93
112
  if session:
@@ -99,19 +118,26 @@ class Exporter:
99
118
  spec.importer = Importer.importer_from_session(state.export_session)
100
119
  if not spec.importer:
101
120
  spec.importer = Config().get('export.default-importer', 'sqlite')
121
+
122
+ if spec.importer == 'athena' and not AthenaImporter.ping():
123
+ raise Exception('Credentials for Athena is not present.')
102
124
  else:
103
- if spec.importer:
104
- if not AthenaImporter.ping():
105
- raise Exception('Credentials for Athena are not present.')
106
- else:
125
+ if not spec.importer:
107
126
  spec.importer = Importer.importer_from_session(spec.session)
108
127
 
109
- if spec.importer == 'csv':
128
+ if not spec.importer or spec.importer == 'csv':
110
129
  spec.importer = Config().get('export.default-importer', 'sqlite')
111
130
 
131
+ if spec.importer == 'athena' and not AthenaImporter.ping():
132
+ raise Exception('Credentials for Athena is not present.')
133
+
112
134
  prefix = Importer.prefix_from_importer(spec.importer)
113
- session = f'{prefix}{spec.session[1:]}'
114
- state.export_session = session
135
+ if spec.session:
136
+ spec.session = f'{prefix}{spec.session[1:]}'
137
+ else:
138
+ spec.session = f'{prefix}{datetime.now().strftime("%Y%m%d%H%M%S")[3:]}'
139
+
140
+ state.export_session = spec.session
115
141
 
116
142
  return spec
117
143
 
@@ -128,20 +154,14 @@ class Exporter:
128
154
  if export_state == 'init':
129
155
  CassandraNodes.exec(state.pod, state.namespace, f'rm -rf {csv_dir()}/{spec.session}_*', show_out=Config().is_debug(), shell='bash')
130
156
 
131
- if max_workers > 1 and len(spec.tables) > 1:
132
- log2(f'Executing on {len(spec.tables)} Cassandra tables in parallel...')
133
- start_time = time.time()
134
- try:
135
- with ThreadPoolExecutor(max_workers=max_workers) as executor:
136
- futures = [executor.submit(Exporter.export_table, table, state, spec.session, spec.importer, export_only, True, consistency=spec.consistency, export_state=export_state) for table in spec.tables]
137
- if len(futures) == 0:
138
- return [], spec
139
-
140
- return [future.result() for future in as_completed(futures)], spec
141
- finally:
142
- log2(f"{len(spec.tables)} parallel table export elapsed time: {elapsed_time(start_time)} with {max_workers} workers")
143
- else:
144
- return [Exporter.export_table(table, state, spec.session, spec.importer, export_only, multi_tables=len(spec.tables) > 1, consistency=spec.consistency, export_state=export_state) for table in spec.tables], spec
157
+ action = f'[{spec.session}] Exporting|Exported'
158
+ if export_state == 'init':
159
+ action = f'[{spec.session}] Preparing|Prepared'
160
+ elif export_state == 'import':
161
+ action = f'[{spec.session}] Importing|Imported'
162
+
163
+ with parallelize(spec.tables, max_workers, msg=action + ' {size} Cassandra tables') as exec:
164
+ return exec.map(lambda table: Exporter.export_table(table, state, spec.session, spec.importer, export_only, len(spec.tables) > 1, consistency=spec.consistency, export_state=export_state)), spec
145
165
 
146
166
  def export_table(spec: ExportTableSpec, state: ReplState, session: str, importer: str, export_only = False, multi_tables = True, consistency: str = None, export_state=None):
147
167
  s: str = None
@@ -167,8 +187,7 @@ class Exporter:
167
187
  status: ExportTableStatus = ExportTableStatus.from_log_file(state.pod, state.namespace, session, log_file)
168
188
  while status.status != 'done':
169
189
  if status.status == 'export_in_pregress':
170
- if Config().is_debug():
171
- log2('Exporting to CSV is still in progess, sleeping for 1 sec...')
190
+ debug('Exporting to CSV is still in progess, sleeping for 1 sec...')
172
191
  time.sleep(1)
173
192
  elif status.status == 'exported':
174
193
  log_file = Exporter.rename_to_pending_import(spec, state, session, target_table)
@@ -202,7 +221,7 @@ class Exporter:
202
221
  queries.append(f"COPY {spec.keyspace}.{table}({columns}) TO '{csv_file}' WITH HEADER = TRUE")
203
222
  r: PodExecResult = ing(
204
223
  f'[{session}] Dumping table {spec.keyspace}.{table}{f" with consistency {consistency}" if consistency else ""}',
205
- lambda: run_cql(state, ';'.join(queries), show_out=Config().is_debug(), background=True, log_file=log_file),
224
+ lambda: run_cql(state, ';'.join(queries), show_out=Config().is_debug(), backgrounded=True, log_file=log_file),
206
225
  suppress_log=suppress_ing_log)
207
226
 
208
227
  return log_file
@@ -217,114 +236,7 @@ class Exporter:
217
236
 
218
237
  def import_from_csv(spec: ExportTableSpec, state: ReplState, session: str, importer: str, table: str, target_table: str, columns: str, multi_tables = True, create_db = False):
219
238
  im = AthenaImporter() if importer == 'athena' else SqliteImporter()
220
- return im.import_from_csv(state.pod, state.namespace, state.export_session, session if session else state.export_session, spec.keyspace, table, target_table, columns, multi_tables, create_db)
221
-
222
- def clear_export_session_cache():
223
- Exporter.find_export_sessions.cache_clear()
224
- Exporter.export_session_names.cache_clear()
225
-
226
- @functools.lru_cache()
227
- def export_session_names(sts: str, pod: str, namespace: str, importer: str = None, export_state = None):
228
- if not sts or not namespace:
229
- return []
230
-
231
- if not pod:
232
- pod = StatefulSets.pod_names(sts, namespace)[0]
233
-
234
- if not pod:
235
- return []
236
-
237
- return [session for session, state in Exporter.find_export_sessions(pod, namespace, importer).items() if not export_state or state == export_state]
238
-
239
- @functools.lru_cache()
240
- def find_export_sessions(pod: str, namespace: str, importer: str = None, limit = 100):
241
- sessions: dict[str, str] = {}
242
-
243
- prefix = Importer.prefix_from_importer(importer)
244
-
245
- log_files: list[str] = find_files(pod, namespace, f'{log_prefix()}-{prefix}*_*.log*')
246
-
247
- if not log_files:
248
- return {}
249
-
250
- for log_file in log_files[:limit]:
251
- m = re.match(f'{log_prefix()}-(.*?)_.*\.log?(.*)', log_file)
252
- if m:
253
- s = m.group(1)
254
- state = m.group(2) # '', '.pending_import', '.done'
255
- if state:
256
- state = state.strip('.')
257
- else:
258
- state = 'in_export'
259
-
260
- if s not in sessions:
261
- sessions[s] = state
262
- elif sessions[s] == 'done' and state != 'done':
263
- sessions[s] = state
264
-
265
- return sessions
266
-
267
- def clean_up_all_sessions(sts: str, pod: str, namespace: str):
268
- if not sts or not namespace:
269
- return False
270
-
271
- if not pod:
272
- pod = StatefulSets.pod_names(sts, namespace)[0]
273
-
274
- CassandraNodes.exec(pod, namespace, f'rm -rf {csv_dir()}/*', show_out=Config().is_debug(), shell='bash')
275
- CassandraNodes.exec(pod, namespace, f'rm -rf {log_prefix()}-*.log*', show_out=Config().is_debug(), shell='bash')
276
-
277
- return True
278
-
279
- def clean_up_sessions(sts: str, pod: str, namespace: str, sessions: list[str], max_workers = 0):
280
- if not sessions:
281
- return []
282
-
283
- if not max_workers:
284
- max_workers = Config().action_workers('export', 8)
285
-
286
- if max_workers > 1 and len(sessions) > 1:
287
- log2(f'Executing on {len(sessions)} export session clean ups in parallel...')
288
- start_time = time.time()
289
- try:
290
- with ThreadPoolExecutor(max_workers=max_workers) as executor:
291
- futures = [executor.submit(Exporter.clean_up_session, sts, pod, namespace, session, True) for session in sessions]
292
- if len(futures) == 0:
293
- return []
294
-
295
- return [future.result() for future in as_completed(futures)]
296
- finally:
297
- log2(f"{len(sessions)} parallel session clean ups elapsed time: {elapsed_time(start_time)} with {max_workers} workers")
298
- else:
299
- return [Exporter.clean_up_session(sts, pod, namespace, session) for session in sessions]
300
-
301
- def clean_up_session(sts: str, pod: str, namespace: str, session: str, multi_tables = True):
302
- if not sts or not namespace:
303
- return 0, 0
304
-
305
- if not pod:
306
- pod = StatefulSets.pod_names(sts, namespace)[0]
307
-
308
- if not pod:
309
- return 0, 0
310
-
311
- csv_cnt = 0
312
- log_cnt = 0
313
-
314
- log_files: list[str] = find_files(pod, namespace, f'{log_prefix()}-{session}_*.log*')
315
-
316
- for log_file in log_files:
317
- m = re.match(f'{log_prefix()}-{session}_(.*?)\.(.*?)\.log.*', log_file)
318
- if m:
319
- table = m.group(2)
320
-
321
- CassandraNodes.exec(pod, namespace, f'rm -rf {csv_dir()}/{session}_{table}', show_out=not multi_tables, shell='bash')
322
- csv_cnt += 1
323
-
324
- CassandraNodes.exec(pod, namespace, f'rm -rf {log_file}', show_out=not multi_tables, shell='bash')
325
- log_cnt += 1
326
-
327
- return csv_cnt, log_cnt
239
+ return im.import_from_csv(state, session if session else state.export_session, spec.keyspace, table, target_table, columns, multi_tables, create_db)
328
240
 
329
241
  def resove_table_n_columns(spec: ExportTableSpec, state: ReplState, include_ks_in_target = False, importer = 'sqlite'):
330
242
  table = spec.table
@@ -350,15 +262,71 @@ class Exporter:
350
262
 
351
263
  return table, target_table, columns
352
264
 
353
- def drop_databases(sts: str, pod: str, namespace: str, db: str = None):
354
- importer = None
355
- if db:
356
- importer = Importer.importer_from_session(db)
357
-
358
- sessions_done = Exporter.export_session_names(sts, pod, namespace, importer=importer, export_state='done')
359
- sessions = ExportDatabases.sessions_from_dbs(ExportDatabases.drop_export_dbs(db))
360
- if sessions_done and sessions:
361
- intersects = list(set(sessions_done) & set(sessions))
362
- with ing(f'Cleaning up {len(intersects)} completed sessions'):
363
- Exporter.clean_up_sessions(sts, pod, namespace, list(intersects))
364
- Exporter.clear_export_session_cache()
265
+ class ExportService:
266
+ def __init__(self, handler: 'ExporterHandler'):
267
+ self.handler = handler
268
+
269
+ def export(self, args: list[str], export_only=False):
270
+ state = self.handler.state
271
+ export_session = state.export_session
272
+ spec: ExportSpec = None
273
+ try:
274
+ with state_with_pod(state) as state:
275
+ # --export-only for testing only
276
+ statuses, spec = Exporter.export_tables(args, state, export_only=export_only)
277
+ if not statuses:
278
+ return state
279
+
280
+ ExportSessions.clear_export_session_cache()
281
+
282
+ if spec.importer == 'csv' or export_only:
283
+ ExportSessions.show_session(state.sts, state.pod, state.namespace, spec.session)
284
+ else:
285
+ log()
286
+ with export_db(state) as dbs:
287
+ dbs.show_database()
288
+ finally:
289
+ # if exporting to csv, do not bind the new session id to repl state
290
+ if spec and spec.importer == 'csv':
291
+ state.export_session = export_session
292
+
293
+ return state
294
+
295
+ def import_session(self, spec_str: str):
296
+ state = self.handler.state
297
+
298
+ tables, _ = Exporter.import_session(spec_str, state)
299
+ if tables:
300
+ ExportSessions.clear_export_session_cache()
301
+
302
+ log()
303
+ with export_db(state) as dbs:
304
+ dbs.show_database()
305
+
306
+ return state
307
+
308
+ def import_files(self, spec_str: str):
309
+ state = self.handler.state
310
+
311
+ tables, _ = Exporter.import_local_csv_files(spec_str, state)
312
+ if tables:
313
+ ExportSessions.clear_export_session_cache()
314
+
315
+ log()
316
+ with export_db(state) as dbs:
317
+ dbs.show_database()
318
+
319
+ return state
320
+
321
+ class ExporterHandler:
322
+ def __init__(self, state: ReplState):
323
+ self.state = state
324
+
325
+ def __enter__(self):
326
+ return ExportService(self)
327
+
328
+ def __exit__(self, exc_type, exc_val, exc_tb):
329
+ return False
330
+
331
+ def export(state: ReplState):
332
+ return ExporterHandler(state)
@@ -0,0 +1,44 @@
1
+ from adam.commands import validate_args
2
+ from adam.commands.command import Command
3
+ from adam.commands.export.export_sessions import ExportSessions
4
+ from adam.commands.export.exporter import export
5
+ from adam.commands.export.utils_export import state_with_pod
6
+ from adam.repl_state import ReplState, RequiredState
7
+
8
+ class ImportCSVFiles(Command):
9
+ COMMAND = 'import files'
10
+
11
+ # the singleton pattern
12
+ def __new__(cls, *args, **kwargs):
13
+ if not hasattr(cls, 'instance'): cls.instance = super(ImportCSVFiles, 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 ImportCSVFiles.COMMAND
22
+
23
+ def required(self):
24
+ return RequiredState.CLUSTER_OR_POD
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 validate_args(args, state, name='file') as spec:
32
+ with state_with_pod(state) as state:
33
+ with export(state) as exporter:
34
+ return exporter.import_files(spec)
35
+
36
+ def completion(self, state: ReplState):
37
+ # warm up cache
38
+ # ExportSessions.export_session_names(state.sts, state.pod, state.namespace)
39
+ # ExportSessions.export_session_names(state.sts, state.pod, state.namespace, export_state='pending_import')
40
+
41
+ return {}
42
+
43
+ def help(self, _: ReplState):
44
+ return f'{ImportCSVFiles.COMMAND} <file-names,...>\t import files in session to Athena or SQLite'
@@ -1,9 +1,9 @@
1
+ from adam.commands import validate_args
1
2
  from adam.commands.command import Command
2
- from adam.commands.export.export_databases import ExportDatabases
3
- from adam.commands.export.exporter import Exporter
3
+ from adam.commands.export.export_sessions import ExportSessions
4
+ from adam.commands.export.exporter import export
5
+ from adam.commands.export.utils_export import state_with_pod
4
6
  from adam.repl_state import ReplState, RequiredState
5
- from adam.utils import log, log2
6
- from adam.utils_k8s.statefulsets import StatefulSets
7
7
 
8
8
  class ImportSession(Command):
9
9
  COMMAND = 'import session'
@@ -27,40 +27,16 @@ class ImportSession(Command):
27
27
  if not(args := self.args(cmd)):
28
28
  return super().run(cmd, state)
29
29
 
30
- state, args = self.apply_state(args, state)
31
- if not self.validate_state(state):
32
- return state
33
-
34
- if not args:
35
- if state.in_repl:
36
- log2('Specify export session name.')
37
- else:
38
- log2('* Export session name is missing.')
39
-
40
- Command.display_help()
41
-
42
- return 'command-missing'
43
-
44
- if not state.pod:
45
- state.push()
46
- state.pod = StatefulSets.pod_names(state.sts, state.namespace)[0]
47
-
48
- try:
49
- tables, _ = Exporter.import_session(args, state)
50
- if tables:
51
- Exporter.clear_export_session_cache()
52
-
53
- log()
54
- ExportDatabases.display_export_db(state.export_session)
55
- finally:
56
- state.pop()
57
-
58
- return state
30
+ with self.validate(args, state) as (args, state):
31
+ with validate_args(args, state, name='export session') as spec:
32
+ with state_with_pod(state) as state:
33
+ with export(state) as exporter:
34
+ return exporter.import_session(spec)
59
35
 
60
36
  def completion(self, state: ReplState):
61
37
  # warm up cache
62
- Exporter.export_session_names(state.sts, state.pod, state.namespace)
63
- Exporter.export_session_names(state.sts, state.pod, state.namespace, export_state='pending_import')
38
+ # ExportSessions.export_session_names(state.sts, state.pod, state.namespace)
39
+ # ExportSessions.export_session_names(state.sts, state.pod, state.namespace, export_state='pending_import')
64
40
 
65
41
  return {}
66
42
 
@@ -2,6 +2,7 @@ from abc import abstractmethod
2
2
 
3
3
  from adam.commands.export.utils_export import csv_dir
4
4
  from adam.config import Config
5
+ from adam.repl_state import ReplState
5
6
  from adam.utils import ing
6
7
  from adam.utils_k8s.cassandra_nodes import CassandraNodes
7
8
  from adam.utils_k8s.pods import log_prefix
@@ -12,10 +13,20 @@ class Importer:
12
13
  pass
13
14
 
14
15
  @abstractmethod
15
- def import_from_csv(self, pod: str, namespace: str, to_session: str, from_session: str, keyspace: str, table: str, target_table: str, columns: str, multi_tables = True, create_db = False):
16
+ def import_from_csv(self, state: ReplState, from_session: str, keyspace: str, table: str, target_table: str, columns: str, multi_tables = True, create_db = False):
16
17
  pass
17
18
 
18
- def move_to_done(self, pod: str, namespace: str, to_session: str, from_session: str, keyspace: str, target_table: str):
19
+ @abstractmethod
20
+ def import_from_local_csv(self, state: ReplState,
21
+ keyspace: str, table: str, target_table: str, columns: str,
22
+ csv_file: str,
23
+ multi_tables = True, create_db = False):
24
+ pass
25
+
26
+ def move_to_done(self, state: ReplState, from_session: str, keyspace: str, target_table: str):
27
+ pod = state.pod
28
+ namespace = state.namespace
29
+ to_session = state.export_session
19
30
  log_file = f'{log_prefix()}-{from_session}_{keyspace}.{target_table}.log.pending_import'
20
31
 
21
32
  to = f'{log_prefix()}-{to_session}_{keyspace}.{target_table}.log.done'
@@ -30,9 +41,12 @@ class Importer:
30
41
 
31
42
  return session
32
43
 
33
- def remove_csv(self, pod: str, namespace: str, session: str, table: str, target_table: str, multi_tables = True):
34
- with ing(f'[{session}] Cleaning up temporary files', suppress_log=multi_tables):
35
- CassandraNodes.exec(pod, namespace, f'rm -rf {self.csv_file(session, table, target_table)}', show_out=Config().is_debug(), shell='bash')
44
+ def remove_csv(self, state: ReplState, from_session: str, table: str, target_table: str, multi_tables = True):
45
+ pod = state.pod
46
+ namespace = state.namespace
47
+
48
+ with ing(f'[{from_session}] Cleaning up temporary files', suppress_log=multi_tables):
49
+ CassandraNodes.exec(pod, namespace, f'rm -rf {self.csv_file(from_session, table, target_table)}', show_out=Config().is_debug(), shell='bash')
36
50
 
37
51
  def db(self, session: str, keyspace: str):
38
52
  return f'{session}_{keyspace}'