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.
- adam/__init__.py +0 -2
- adam/app_session.py +9 -12
- adam/apps.py +18 -4
- adam/batch.py +4 -4
- adam/checks/check_utils.py +16 -46
- adam/checks/cpu.py +7 -1
- adam/checks/cpu_metrics.py +52 -0
- adam/checks/disk.py +2 -3
- adam/columns/columns.py +3 -1
- adam/columns/cpu.py +3 -1
- adam/columns/cpu_metrics.py +22 -0
- adam/columns/memory.py +3 -4
- adam/commands/__init__.py +24 -0
- adam/commands/alter_tables.py +33 -48
- adam/commands/app/__init__.py +0 -0
- adam/commands/app/app.py +38 -0
- adam/commands/{app_ping.py → app/app_ping.py} +7 -13
- adam/commands/app/show_app_actions.py +49 -0
- adam/commands/{show → app}/show_app_id.py +8 -11
- adam/commands/{show → app}/show_app_queues.py +7 -14
- adam/commands/app/utils_app.py +106 -0
- adam/commands/audit/audit.py +21 -40
- adam/commands/audit/audit_repair_tables.py +14 -19
- adam/commands/audit/audit_run.py +14 -22
- adam/commands/audit/completions_l.py +15 -0
- adam/commands/audit/show_last10.py +4 -19
- adam/commands/audit/show_slow10.py +4 -18
- adam/commands/audit/show_top10.py +4 -16
- adam/commands/audit/utils_show_top10.py +15 -3
- adam/commands/bash/__init__.py +5 -0
- adam/commands/bash/bash.py +7 -104
- adam/commands/bash/utils_bash.py +16 -0
- adam/commands/cat.py +7 -27
- adam/commands/cd.py +7 -11
- adam/commands/check.py +15 -24
- adam/commands/cli_commands.py +8 -4
- adam/commands/clipboard_copy.py +87 -0
- adam/commands/code.py +21 -24
- adam/commands/command.py +207 -42
- adam/commands/commands_utils.py +25 -27
- adam/commands/cql/completions_c.py +28 -0
- adam/commands/cql/cqlsh.py +9 -33
- adam/commands/cql/{cql_utils.py → utils_cql.py} +111 -15
- adam/commands/deploy/code_start.py +7 -10
- adam/commands/deploy/code_stop.py +4 -21
- adam/commands/deploy/code_utils.py +3 -3
- adam/commands/deploy/deploy.py +4 -27
- adam/commands/deploy/deploy_frontend.py +14 -17
- adam/commands/deploy/deploy_pg_agent.py +3 -6
- adam/commands/deploy/deploy_pod.py +64 -68
- adam/commands/deploy/undeploy.py +4 -27
- adam/commands/deploy/undeploy_frontend.py +4 -7
- adam/commands/deploy/undeploy_pg_agent.py +5 -8
- adam/commands/deploy/undeploy_pod.py +9 -12
- adam/commands/devices/device.py +124 -2
- adam/commands/devices/device_app.py +41 -24
- adam/commands/devices/device_auit_log.py +10 -4
- adam/commands/devices/device_cass.py +48 -14
- adam/commands/devices/device_export.py +13 -12
- adam/commands/devices/device_postgres.py +105 -54
- adam/commands/download_file.py +47 -0
- adam/commands/exit.py +1 -4
- adam/commands/export/clean_up_all_export_sessions.py +37 -0
- adam/commands/export/clean_up_export_sessions.py +9 -10
- adam/commands/export/completions_x.py +11 -0
- adam/commands/export/download_export_session.py +40 -0
- adam/commands/export/drop_export_database.py +7 -26
- adam/commands/export/drop_export_databases.py +5 -14
- adam/commands/export/export.py +6 -52
- adam/commands/export/export_databases.py +108 -32
- adam/commands/export/export_select.py +8 -59
- adam/commands/export/export_sessions.py +209 -0
- adam/commands/export/export_use.py +14 -20
- adam/commands/export/export_x_select.py +48 -0
- adam/commands/export/exporter.py +135 -167
- adam/commands/export/import_files.py +44 -0
- adam/commands/export/import_session.py +11 -35
- adam/commands/export/importer.py +19 -5
- adam/commands/export/importer_athena.py +112 -44
- adam/commands/export/importer_sqlite.py +42 -22
- adam/commands/export/show_column_counts.py +13 -31
- adam/commands/export/show_export_databases.py +7 -7
- adam/commands/export/show_export_session.py +8 -20
- adam/commands/export/show_export_sessions.py +6 -16
- adam/commands/export/utils_export.py +64 -11
- adam/commands/find_files.py +51 -0
- adam/commands/find_processes.py +76 -0
- adam/commands/head.py +36 -0
- adam/commands/help.py +2 -2
- adam/commands/intermediate_command.py +52 -0
- adam/commands/issues.py +11 -43
- adam/commands/kubectl.py +3 -6
- adam/commands/login.py +22 -24
- adam/commands/logs.py +3 -6
- adam/commands/ls.py +9 -10
- adam/commands/medusa/medusa.py +4 -22
- adam/commands/medusa/medusa_backup.py +20 -27
- adam/commands/medusa/medusa_restore.py +49 -46
- adam/commands/medusa/medusa_show_backupjobs.py +16 -18
- adam/commands/medusa/medusa_show_restorejobs.py +13 -18
- adam/commands/medusa/utils_medusa.py +15 -0
- adam/commands/nodetool.py +7 -21
- adam/commands/param_get.py +11 -14
- adam/commands/param_set.py +8 -12
- adam/commands/postgres/completions_p.py +22 -0
- adam/commands/postgres/postgres.py +34 -57
- adam/commands/postgres/postgres_databases.py +270 -0
- adam/commands/postgres/postgres_ls.py +4 -8
- adam/commands/postgres/postgres_preview.py +5 -9
- adam/commands/postgres/utils_postgres.py +79 -0
- adam/commands/preview_table.py +8 -45
- adam/commands/pwd.py +13 -16
- adam/commands/reaper/reaper.py +4 -27
- adam/commands/reaper/reaper_forward.py +49 -56
- adam/commands/reaper/reaper_forward_session.py +6 -0
- adam/commands/reaper/reaper_forward_stop.py +10 -16
- adam/commands/reaper/reaper_restart.py +7 -14
- adam/commands/reaper/reaper_run_abort.py +8 -33
- adam/commands/reaper/reaper_runs.py +43 -58
- adam/commands/reaper/reaper_runs_abort.py +29 -49
- adam/commands/reaper/reaper_schedule_activate.py +14 -33
- adam/commands/reaper/reaper_schedule_start.py +9 -33
- adam/commands/reaper/reaper_schedule_stop.py +9 -33
- adam/commands/reaper/reaper_schedules.py +4 -14
- adam/commands/reaper/reaper_status.py +8 -16
- adam/commands/reaper/utils_reaper.py +203 -0
- adam/commands/repair/repair.py +4 -22
- adam/commands/repair/repair_log.py +5 -11
- adam/commands/repair/repair_run.py +27 -34
- adam/commands/repair/repair_scan.py +32 -40
- adam/commands/repair/repair_stop.py +5 -12
- adam/commands/report.py +27 -29
- adam/commands/restart.py +25 -26
- adam/commands/rollout.py +19 -24
- adam/commands/shell.py +12 -4
- adam/commands/show/show.py +11 -27
- adam/commands/show/show_adam.py +3 -3
- adam/commands/show/show_cassandra_repairs.py +37 -0
- adam/commands/show/show_cassandra_status.py +47 -51
- adam/commands/show/show_cassandra_version.py +5 -18
- adam/commands/show/show_cli_commands.py +56 -0
- adam/commands/show/show_host.py +1 -1
- adam/commands/show/show_login.py +20 -27
- adam/commands/show/show_params.py +2 -5
- adam/commands/show/show_processes.py +18 -21
- adam/commands/show/show_storage.py +11 -20
- adam/commands/watch.py +26 -29
- adam/config.py +5 -16
- adam/embedded_params.py +1 -1
- adam/log.py +4 -4
- adam/pod_exec_result.py +3 -3
- adam/repl.py +45 -39
- adam/repl_commands.py +26 -19
- adam/repl_session.py +8 -1
- adam/repl_state.py +85 -36
- adam/sql/lark_completer.py +284 -0
- adam/sql/lark_parser.py +604 -0
- adam/sql/sql_completer.py +4 -6
- adam/sql/sql_state_machine.py +29 -16
- adam/sso/authn_ad.py +6 -8
- adam/sso/authn_okta.py +4 -6
- adam/sso/cred_cache.py +3 -5
- adam/sso/idp.py +9 -12
- adam/utils.py +484 -37
- adam/utils_athena.py +19 -19
- adam/utils_audits.py +12 -12
- adam/utils_issues.py +32 -0
- adam/utils_k8s/app_clusters.py +14 -19
- adam/utils_k8s/app_pods.py +7 -2
- adam/utils_k8s/cassandra_clusters.py +30 -19
- adam/utils_k8s/cassandra_nodes.py +2 -2
- adam/utils_k8s/custom_resources.py +16 -17
- adam/utils_k8s/ingresses.py +2 -2
- adam/utils_k8s/jobs.py +7 -11
- adam/utils_k8s/k8s.py +96 -0
- adam/utils_k8s/kube_context.py +2 -2
- adam/utils_k8s/pods.py +37 -81
- adam/utils_k8s/secrets.py +4 -4
- adam/utils_k8s/service_accounts.py +5 -4
- adam/utils_k8s/services.py +2 -2
- adam/utils_k8s/statefulsets.py +6 -14
- adam/utils_local.py +4 -0
- adam/utils_repl/appendable_completer.py +6 -0
- adam/utils_repl/repl_completer.py +128 -2
- adam/utils_repl/state_machine.py +3 -3
- adam/utils_sqlite.py +78 -42
- adam/version.py +1 -1
- {kaqing-2.0.145.dist-info → kaqing-2.0.189.dist-info}/METADATA +1 -1
- kaqing-2.0.189.dist-info/RECORD +253 -0
- kaqing-2.0.189.dist-info/top_level.txt +2 -0
- teddy/__init__.py +0 -0
- teddy/lark_parser.py +436 -0
- teddy/lark_parser2.py +618 -0
- adam/commands/app.py +0 -67
- adam/commands/cp.py +0 -95
- adam/commands/cql/cql_completions.py +0 -28
- adam/commands/export/clean_up_export_session.py +0 -53
- adam/commands/export/export_select_x.py +0 -54
- adam/commands/postgres/postgres_context.py +0 -248
- adam/commands/postgres/postgres_utils.py +0 -31
- adam/commands/postgres/psql_completions.py +0 -10
- adam/commands/reaper/reaper_session.py +0 -159
- adam/commands/show/show_app_actions.py +0 -56
- adam/commands/show/show_commands.py +0 -61
- adam/commands/show/show_repairs.py +0 -47
- kaqing-2.0.145.dist-info/RECORD +0 -227
- kaqing-2.0.145.dist-info/top_level.txt +0 -1
- {kaqing-2.0.145.dist-info → kaqing-2.0.189.dist-info}/WHEEL +0 -0
- {kaqing-2.0.145.dist-info → kaqing-2.0.189.dist-info}/entry_points.txt +0 -0
adam/commands/export/exporter.py
CHANGED
|
@@ -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.
|
|
9
|
-
from adam.commands.
|
|
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
|
|
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
|
-
|
|
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(
|
|
71
|
+
def import_session(spec_str: str, state: ReplState, max_workers = 0) -> tuple[list[str], ExportSpec]:
|
|
70
72
|
import_spec: ImportSpec = None
|
|
71
|
-
|
|
72
|
-
import_spec = Exporter.import_spec(
|
|
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
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
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
|
-
|
|
114
|
-
|
|
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
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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
|
-
|
|
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(),
|
|
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
|
|
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
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
with
|
|
363
|
-
|
|
364
|
-
Exporter.
|
|
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.
|
|
3
|
-
from adam.commands.export.exporter import
|
|
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
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
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
|
-
|
|
63
|
-
|
|
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
|
|
adam/commands/export/importer.py
CHANGED
|
@@ -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,
|
|
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
|
-
|
|
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,
|
|
34
|
-
|
|
35
|
-
|
|
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}'
|