kaqing 2.0.52__py3-none-any.whl → 2.0.184__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 (223) hide show
  1. adam/__init__.py +0 -2
  2. adam/app_session.py +9 -12
  3. adam/apps.py +20 -6
  4. adam/batch.py +15 -19
  5. adam/checks/check_utils.py +19 -49
  6. adam/checks/compactionstats.py +1 -1
  7. adam/checks/cpu.py +9 -3
  8. adam/checks/cpu_metrics.py +52 -0
  9. adam/checks/disk.py +3 -4
  10. adam/checks/gossip.py +1 -1
  11. adam/checks/memory.py +3 -3
  12. adam/checks/status.py +1 -1
  13. adam/columns/columns.py +3 -1
  14. adam/columns/cpu.py +3 -1
  15. adam/columns/cpu_metrics.py +22 -0
  16. adam/columns/memory.py +3 -4
  17. adam/commands/__init__.py +24 -0
  18. adam/commands/alter_tables.py +37 -63
  19. adam/commands/app/app.py +38 -0
  20. adam/commands/{app_ping.py → app/app_ping.py} +8 -14
  21. adam/commands/app/show_app_actions.py +49 -0
  22. adam/commands/{show → app}/show_app_id.py +9 -12
  23. adam/commands/{show → app}/show_app_queues.py +8 -14
  24. adam/commands/app/utils_app.py +98 -0
  25. adam/commands/audit/audit.py +81 -0
  26. adam/commands/audit/audit_repair_tables.py +72 -0
  27. adam/commands/audit/audit_run.py +50 -0
  28. adam/commands/audit/show_last10.py +37 -0
  29. adam/commands/audit/show_slow10.py +36 -0
  30. adam/commands/audit/show_top10.py +36 -0
  31. adam/commands/audit/utils_show_top10.py +71 -0
  32. adam/commands/bash/__init__.py +5 -0
  33. adam/commands/bash/bash.py +36 -0
  34. adam/commands/bash/bash_completer.py +93 -0
  35. adam/commands/bash/utils_bash.py +16 -0
  36. adam/commands/cat.py +36 -0
  37. adam/commands/cd.py +14 -89
  38. adam/commands/check.py +18 -21
  39. adam/commands/cli_commands.py +5 -6
  40. adam/commands/clipboard_copy.py +86 -0
  41. adam/commands/code.py +57 -0
  42. adam/commands/command.py +197 -35
  43. adam/commands/commands_utils.py +15 -31
  44. adam/commands/cql/cql_completions.py +29 -8
  45. adam/commands/cql/cqlsh.py +12 -27
  46. adam/commands/cql/utils_cql.py +297 -0
  47. adam/commands/deploy/code_start.py +7 -10
  48. adam/commands/deploy/code_stop.py +4 -21
  49. adam/commands/deploy/code_utils.py +5 -5
  50. adam/commands/deploy/deploy.py +4 -21
  51. adam/commands/deploy/deploy_frontend.py +14 -17
  52. adam/commands/deploy/deploy_pg_agent.py +3 -6
  53. adam/commands/deploy/deploy_pod.py +71 -79
  54. adam/commands/deploy/deploy_utils.py +16 -26
  55. adam/commands/deploy/undeploy.py +4 -21
  56. adam/commands/deploy/undeploy_frontend.py +4 -7
  57. adam/commands/deploy/undeploy_pg_agent.py +6 -8
  58. adam/commands/deploy/undeploy_pod.py +15 -16
  59. adam/commands/devices/__init__.py +0 -0
  60. adam/commands/devices/device.py +123 -0
  61. adam/commands/devices/device_app.py +163 -0
  62. adam/commands/devices/device_auit_log.py +49 -0
  63. adam/commands/devices/device_cass.py +179 -0
  64. adam/commands/devices/device_export.py +84 -0
  65. adam/commands/devices/device_postgres.py +150 -0
  66. adam/commands/devices/devices.py +25 -0
  67. adam/commands/download_file.py +47 -0
  68. adam/commands/exit.py +1 -4
  69. adam/commands/export/__init__.py +0 -0
  70. adam/commands/export/clean_up_all_export_sessions.py +37 -0
  71. adam/commands/export/clean_up_export_sessions.py +39 -0
  72. adam/commands/export/download_export_session.py +39 -0
  73. adam/commands/export/drop_export_database.py +39 -0
  74. adam/commands/export/drop_export_databases.py +37 -0
  75. adam/commands/export/export.py +53 -0
  76. adam/commands/export/export_databases.py +245 -0
  77. adam/commands/export/export_select.py +59 -0
  78. adam/commands/export/export_select_x.py +54 -0
  79. adam/commands/export/export_sessions.py +209 -0
  80. adam/commands/export/export_use.py +49 -0
  81. adam/commands/export/exporter.py +332 -0
  82. adam/commands/export/import_files.py +44 -0
  83. adam/commands/export/import_session.py +44 -0
  84. adam/commands/export/importer.py +81 -0
  85. adam/commands/export/importer_athena.py +177 -0
  86. adam/commands/export/importer_sqlite.py +67 -0
  87. adam/commands/export/show_column_counts.py +45 -0
  88. adam/commands/export/show_export_databases.py +38 -0
  89. adam/commands/export/show_export_session.py +39 -0
  90. adam/commands/export/show_export_sessions.py +37 -0
  91. adam/commands/export/utils_export.py +343 -0
  92. adam/commands/find_files.py +51 -0
  93. adam/commands/find_processes.py +76 -0
  94. adam/commands/head.py +36 -0
  95. adam/commands/help.py +14 -9
  96. adam/commands/intermediate_command.py +49 -0
  97. adam/commands/issues.py +14 -40
  98. adam/commands/kubectl.py +38 -0
  99. adam/commands/login.py +26 -25
  100. adam/commands/logs.py +5 -7
  101. adam/commands/ls.py +11 -110
  102. adam/commands/medusa/medusa.py +4 -22
  103. adam/commands/medusa/medusa_backup.py +22 -29
  104. adam/commands/medusa/medusa_restore.py +40 -39
  105. adam/commands/medusa/medusa_show_backupjobs.py +19 -20
  106. adam/commands/medusa/medusa_show_restorejobs.py +15 -20
  107. adam/commands/nodetool.py +11 -15
  108. adam/commands/param_get.py +11 -14
  109. adam/commands/param_set.py +8 -12
  110. adam/commands/postgres/postgres.py +45 -46
  111. adam/commands/postgres/postgres_databases.py +269 -0
  112. adam/commands/postgres/postgres_ls.py +4 -8
  113. adam/commands/postgres/postgres_preview.py +5 -9
  114. adam/commands/postgres/psql_completions.py +4 -3
  115. adam/commands/postgres/utils_postgres.py +70 -0
  116. adam/commands/preview_table.py +10 -61
  117. adam/commands/pwd.py +14 -43
  118. adam/commands/reaper/reaper.py +4 -24
  119. adam/commands/reaper/reaper_forward.py +49 -56
  120. adam/commands/reaper/reaper_forward_session.py +6 -0
  121. adam/commands/reaper/reaper_forward_stop.py +10 -16
  122. adam/commands/reaper/reaper_restart.py +8 -15
  123. adam/commands/reaper/reaper_run_abort.py +8 -33
  124. adam/commands/reaper/reaper_runs.py +43 -58
  125. adam/commands/reaper/reaper_runs_abort.py +29 -49
  126. adam/commands/reaper/reaper_schedule_activate.py +9 -32
  127. adam/commands/reaper/reaper_schedule_start.py +9 -32
  128. adam/commands/reaper/reaper_schedule_stop.py +9 -32
  129. adam/commands/reaper/reaper_schedules.py +4 -14
  130. adam/commands/reaper/reaper_status.py +8 -16
  131. adam/commands/reaper/utils_reaper.py +194 -0
  132. adam/commands/repair/repair.py +4 -22
  133. adam/commands/repair/repair_log.py +6 -12
  134. adam/commands/repair/repair_run.py +29 -36
  135. adam/commands/repair/repair_scan.py +33 -39
  136. adam/commands/repair/repair_stop.py +6 -12
  137. adam/commands/report.py +25 -21
  138. adam/commands/restart.py +27 -28
  139. adam/commands/rollout.py +20 -25
  140. adam/commands/shell.py +12 -4
  141. adam/commands/show/show.py +11 -23
  142. adam/commands/show/show_adam.py +3 -3
  143. adam/commands/show/show_cassandra_repairs.py +35 -0
  144. adam/commands/show/show_cassandra_status.py +34 -52
  145. adam/commands/show/show_cassandra_version.py +5 -18
  146. adam/commands/show/show_commands.py +20 -25
  147. adam/commands/show/show_host.py +33 -0
  148. adam/commands/show/show_login.py +23 -27
  149. adam/commands/show/show_params.py +2 -5
  150. adam/commands/show/show_processes.py +16 -20
  151. adam/commands/show/show_storage.py +10 -20
  152. adam/commands/watch.py +27 -30
  153. adam/config.py +7 -15
  154. adam/embedded_params.py +1 -1
  155. adam/log.py +4 -4
  156. adam/pod_exec_result.py +13 -5
  157. adam/repl.py +126 -119
  158. adam/repl_commands.py +63 -29
  159. adam/repl_state.py +320 -71
  160. adam/sql/sql_completer.py +98 -383
  161. adam/sql/sql_state_machine.py +630 -0
  162. adam/sql/term_completer.py +14 -4
  163. adam/sso/authn_ad.py +6 -8
  164. adam/sso/authn_okta.py +4 -6
  165. adam/sso/cred_cache.py +4 -6
  166. adam/sso/idp.py +10 -13
  167. adam/utils.py +511 -10
  168. adam/utils_athena.py +145 -0
  169. adam/utils_audits.py +102 -0
  170. adam/utils_issues.py +32 -0
  171. adam/utils_k8s/__init__.py +0 -0
  172. adam/utils_k8s/app_clusters.py +28 -0
  173. adam/utils_k8s/app_pods.py +36 -0
  174. adam/utils_k8s/cassandra_clusters.py +44 -0
  175. adam/{k8s_utils → utils_k8s}/cassandra_nodes.py +11 -4
  176. adam/{k8s_utils → utils_k8s}/custom_resources.py +16 -17
  177. adam/{k8s_utils → utils_k8s}/deployment.py +2 -2
  178. adam/{k8s_utils → utils_k8s}/ingresses.py +2 -2
  179. adam/{k8s_utils → utils_k8s}/jobs.py +7 -11
  180. adam/utils_k8s/k8s.py +87 -0
  181. adam/{k8s_utils → utils_k8s}/kube_context.py +2 -2
  182. adam/{k8s_utils → utils_k8s}/pods.py +109 -74
  183. adam/{k8s_utils → utils_k8s}/secrets.py +7 -3
  184. adam/{k8s_utils → utils_k8s}/service_accounts.py +5 -4
  185. adam/{k8s_utils → utils_k8s}/services.py +2 -2
  186. adam/{k8s_utils → utils_k8s}/statefulsets.py +3 -14
  187. adam/utils_local.py +4 -0
  188. adam/utils_net.py +24 -0
  189. adam/utils_repl/__init__.py +0 -0
  190. adam/utils_repl/automata_completer.py +48 -0
  191. adam/utils_repl/repl_completer.py +46 -0
  192. adam/utils_repl/state_machine.py +173 -0
  193. adam/utils_sqlite.py +137 -0
  194. adam/version.py +1 -1
  195. {kaqing-2.0.52.dist-info → kaqing-2.0.184.dist-info}/METADATA +1 -1
  196. kaqing-2.0.184.dist-info/RECORD +244 -0
  197. adam/commands/app.py +0 -67
  198. adam/commands/bash.py +0 -87
  199. adam/commands/cp.py +0 -95
  200. adam/commands/cql/cql_table_completer.py +0 -8
  201. adam/commands/cql/cql_utils.py +0 -109
  202. adam/commands/describe/describe.py +0 -46
  203. adam/commands/describe/describe_keyspace.py +0 -60
  204. adam/commands/describe/describe_keyspaces.py +0 -50
  205. adam/commands/describe/describe_table.py +0 -60
  206. adam/commands/describe/describe_tables.py +0 -50
  207. adam/commands/devices.py +0 -89
  208. adam/commands/postgres/postgres_session.py +0 -240
  209. adam/commands/postgres/postgres_utils.py +0 -31
  210. adam/commands/postgres/psql_table_completer.py +0 -11
  211. adam/commands/reaper/reaper_session.py +0 -159
  212. adam/commands/show/show_app_actions.py +0 -53
  213. adam/commands/show/show_repairs.py +0 -47
  214. adam/k8s_utils/cassandra_clusters.py +0 -35
  215. adam/sql/sql_utils.py +0 -5
  216. kaqing-2.0.52.dist-info/RECORD +0 -184
  217. /adam/commands/{describe → app}/__init__.py +0 -0
  218. /adam/{k8s_utils → commands/audit}/__init__.py +0 -0
  219. /adam/{k8s_utils → utils_k8s}/config_maps.py +0 -0
  220. /adam/{k8s_utils → utils_k8s}/volumes.py +0 -0
  221. {kaqing-2.0.52.dist-info → kaqing-2.0.184.dist-info}/WHEEL +0 -0
  222. {kaqing-2.0.52.dist-info → kaqing-2.0.184.dist-info}/entry_points.txt +0 -0
  223. {kaqing-2.0.52.dist-info → kaqing-2.0.184.dist-info}/top_level.txt +0 -0
@@ -1,13 +1,7 @@
1
- import functools
2
-
1
+ from adam.commands import validate_args
3
2
  from adam.commands.command import Command
4
- from adam.commands.cql.cql_table_completer import CqlTableNameCompleter
5
- from adam.commands.cql.cql_utils import run_cql, table_names, tables
6
- from adam.commands.postgres.postgres_session import PostgresSession
7
- from adam.commands.postgres.psql_table_completer import PsqlTableNameCompleter
8
- from adam.config import Config
3
+ from adam.commands.devices.devices import Devices
9
4
  from adam.repl_state import ReplState, RequiredState
10
- from adam.utils import lines_to_tabular, log, log2
11
5
 
12
6
  class PreviewTable(Command):
13
7
  COMMAND = 'preview'
@@ -25,66 +19,21 @@ class PreviewTable(Command):
25
19
  return PreviewTable.COMMAND
26
20
 
27
21
  def required(self):
28
- return RequiredState.CLUSTER_OR_POD
22
+ return [RequiredState.CLUSTER_OR_POD, RequiredState.PG_DATABASE, ReplState.L, RequiredState.EXPORT_DB]
29
23
 
30
24
  def run(self, cmd: str, state: ReplState):
31
25
  if not(args := self.args(cmd)):
32
26
  return super().run(cmd, state)
33
27
 
34
- state, args = self.apply_state(args, state)
35
- if state.device == ReplState.P:
36
- if not self.validate_state(state, RequiredState.PG_DATABASE):
37
- return state
38
- else:
39
- if not self.validate_state(state):
40
- return state
41
-
42
- if not args:
43
- def show_tables():
44
- if state.device == ReplState.P:
45
- pg = PostgresSession(state.namespace, state.pg_path)
46
- lines = [db["name"] for db in pg.tables() if db["schema"] == PostgresSession.default_schema()]
47
- log(lines_to_tabular(lines, separator=','))
48
- else:
49
- run_cql(state, f'describe tables', show_out=True)
50
-
51
- if state.in_repl:
52
- log2('Table is required.')
53
- log2()
54
- log2('Tables:')
55
- show_tables()
56
- else:
57
- log2('* Table is missing.')
58
- show_tables()
59
-
60
- Command.display_help()
61
-
62
- return 'command-missing'
28
+ with self.validate(args, state) as (args, state):
29
+ with validate_args(args, state, at_least=1) as table:
30
+ Devices.device(state).preview(table, state)
63
31
 
64
- table = args[0]
65
-
66
- rows = Config().get('preview.rows', 10)
67
- if state.device == ReplState.P:
68
- PostgresSession(state.namespace, state.pg_path).run_sql(f'select * from {table} limit {rows}')
69
- else:
70
- run_cql(state, f'select * from {table} limit {rows}', show_out=True, use_single_quotes=True)
71
-
72
- return state
73
-
74
- def completion(self, state: ReplState):
75
- if state.device == ReplState.P:
76
- return {PreviewTable.COMMAND: PsqlTableNameCompleter(state.namespace, state.pg_path)}
77
- elif state.sts:
78
- return {PreviewTable.COMMAND: CqlTableNameCompleter(table_names(state))}
32
+ return state
79
33
 
34
+ def completion(self, _: ReplState):
35
+ # taken care of by the sql completer
80
36
  return {}
81
37
 
82
38
  def help(self, _: ReplState):
83
- return f'{PreviewTable.COMMAND} TABLE\t preview table'
84
-
85
- @functools.lru_cache()
86
- def cql_tables(state: ReplState):
87
- if state.pod:
88
- return tables(state)
89
-
90
- return tables(state, on_any=True)
39
+ return f'{PreviewTable.COMMAND} TABLE\t preview table'
adam/commands/pwd.py CHANGED
@@ -1,8 +1,8 @@
1
1
  from adam.app_session import AppSession
2
2
  from adam.commands.command import Command
3
- from adam.commands.postgres.postgres_session import PostgresSession
3
+ from adam.commands.devices.devices import Devices
4
4
  from adam.repl_state import ReplState
5
- from adam.utils import lines_to_tabular, log
5
+ from adam.utils import tabulize, log, log_exc
6
6
 
7
7
  class Pwd(Command):
8
8
  COMMAND = 'pwd'
@@ -23,49 +23,20 @@ class Pwd(Command):
23
23
  if not(args := self.args(cmd)):
24
24
  return super().run(cmd, state)
25
25
 
26
- state, _ = self.apply_state(args, state)
26
+ with self.validate(args, state) as (_, state):
27
+ host = "unknown"
28
+ with log_exc():
29
+ app_session: AppSession = AppSession.create('c3', 'c3')
30
+ host = app_session.host
27
31
 
28
- def device_line(state: ReplState, device: str):
29
- words = []
32
+ tabulize([device.pwd(state) for device in Devices.all()] + [
33
+ f'',
34
+ f'HOST\t{host}',
35
+ f'NAMESPACE\t{state.namespace if state.namespace else "/"}',
36
+ ], header='DEVICE\tLOCATION', separator='\t')
37
+ log()
30
38
 
31
- if device == ReplState.P:
32
- pg = PostgresSession(state.namespace, state.pg_path)
33
-
34
- if pg.host:
35
- words.append(f'host/{pg.host}')
36
- if pg.db:
37
- words.append(f'database/{pg.db}')
38
- elif device == ReplState.A:
39
- if state.app_env:
40
- words.append(f'env/{state.app_env}')
41
- if state.app_app:
42
- words.append(f'app/{state.app_app}')
43
- else:
44
- if state.sts:
45
- words.append(f'sts/{state.sts}')
46
- if state.pod:
47
- words.append(f'pod/{state.pod}')
48
-
49
- return '\t'.join([f'{device}:>'] + (words if words else ['/']))
50
-
51
- host = "unknown"
52
- try:
53
- app_session: AppSession = AppSession.create('c3', 'c3')
54
- host = app_session.host
55
- except:
56
- pass
57
-
58
- log(lines_to_tabular([
59
- device_line(state, ReplState.A),
60
- device_line(state, ReplState.C),
61
- device_line(state, ReplState.P),
62
- f'',
63
- f'HOST\t{host}',
64
- f'NAMESPACE\t{state.namespace if state.namespace else "/"}',
65
- ], 'DEVICE\tLOCATION', separator='\t'))
66
- log()
67
-
68
- return state
39
+ return state
69
40
 
70
41
  def completion(self, state: ReplState):
71
42
  return super().completion(state)
@@ -1,6 +1,6 @@
1
1
  import click
2
2
 
3
- from adam.commands.command import Command
3
+ from adam.commands.intermediate_command import IntermediateCommand
4
4
  from .reaper_forward import ReaperForward
5
5
  from .reaper_forward_stop import ReaperForwardStop
6
6
  from .reaper_restart import ReaperRestart
@@ -12,11 +12,9 @@ from .reaper_schedule_start import ReaperScheduleStart
12
12
  from .reaper_schedule_stop import ReaperScheduleStop
13
13
  from .reaper_schedules import ReaperSchedules
14
14
  from .reaper_status import ReaperStatus
15
- from adam.repl_state import ReplState, RequiredState
16
15
 
17
- class Reaper(Command):
16
+ class Reaper(IntermediateCommand):
18
17
  COMMAND = 'reaper'
19
- reaper_login = None
20
18
 
21
19
  # the singleton pattern
22
20
  def __new__(cls, *args, **kwargs):
@@ -24,32 +22,14 @@ class Reaper(Command):
24
22
 
25
23
  return cls.instance
26
24
 
27
- def __init__(self, successor: Command=None):
28
- super().__init__(successor)
29
-
30
25
  def command(self):
31
26
  return Reaper.COMMAND
32
27
 
33
- def required(self):
34
- return RequiredState.CLUSTER
35
-
36
- def run(self, cmd: str, state: ReplState):
37
- if not(args := self.args(cmd)):
38
- return super().run(cmd, state)
39
-
40
- return super().intermediate_run(cmd, state, args, Reaper.cmd_list())
41
-
42
- def cmd_list():
28
+ def cmd_list(self):
43
29
  return [ReaperSchedules(), ReaperScheduleStop(), ReaperScheduleActivate(), ReaperScheduleStart(),
44
30
  ReaperForwardStop(), ReaperForward(), ReaperRunAbort(), ReaperRunsAbort(), ReaperRestart(),
45
31
  ReaperRuns(), ReaperStatus()]
46
32
 
47
- def completion(self, state: ReplState):
48
- if state.sts:
49
- return super().completion(state)
50
-
51
- return {}
52
-
53
33
  class ReaperCommandHelper(click.Command):
54
34
  def get_help(self, ctx: click.Context):
55
- Command.intermediate_help(super().get_help(ctx), Reaper.COMMAND, Reaper.cmd_list(), show_cluster_help=True)
35
+ IntermediateCommand.intermediate_help(super().get_help(ctx), Reaper.COMMAND, Reaper().cmd_list(), show_cluster_help=True)
@@ -1,16 +1,17 @@
1
+ from functools import partial
1
2
  import threading
2
3
  import time
3
4
 
4
5
  from adam.commands.command import Command
5
- from .reaper_session import ReaperSession
6
+ from adam.commands.reaper.reaper_forward_session import ReaperForwardSession
7
+ from adam.commands.reaper.utils_reaper import Reapers, port_forwarding
6
8
  from adam.config import Config
7
9
  from adam.repl_session import ReplSession
8
10
  from adam.repl_state import ReplState, RequiredState
9
- from adam.utils import lines_to_tabular, log2
11
+ from adam.utils import tabulize, log2
10
12
 
11
13
  class ReaperForward(Command):
12
14
  COMMAND = 'reaper forward'
13
- reaper_login = None
14
15
 
15
16
  # the singleton pattern
16
17
  def __new__(cls, *args, **kwargs):
@@ -31,70 +32,62 @@ class ReaperForward(Command):
31
32
  if not(args := self.args(cmd)):
32
33
  return super().run(cmd, state)
33
34
 
34
- state, args = self.apply_state(args, state)
35
- if not self.validate_state(state):
36
- return state
35
+ with self.validate(args, state) as (args, state):
36
+ if not Reapers.pod_name(state):
37
+ return state
37
38
 
38
- if not(reaper := ReaperSession.create(state)):
39
- return state
39
+ spec = Reapers.reaper_spec(state)
40
+ if state.in_repl:
41
+ if ReaperForwardSession.is_forwarding:
42
+ log2("Another port-forward is already running.")
40
43
 
41
- spec = reaper.reaper_spec(state)
42
- if state.in_repl:
43
- if ReaperSession.is_forwarding:
44
- log2("Another port-forward is already running.")
45
-
46
- return "already-running"
47
-
48
- # make it a daemon to exit with a Ctrl-D
49
- thread = threading.Thread(target=self.loop, args=(state, reaper), daemon=True)
50
- thread.start()
51
-
52
- while not ReaperSession.is_forwarding:
53
- time.sleep(1)
54
-
55
- d = {
56
- 'reaper-ui': spec["web-uri"],
57
- 'reaper-username': spec["username"],
58
- 'reaper-password': spec["password"]
59
- }
60
- log2()
61
- log2(lines_to_tabular([f'{k},{v}' for k, v in d.items()], separator=','))
62
-
63
- for k, v in d.items():
64
- ReplSession().prompt_session.history.append_string(f'cp {k}')
65
- log2()
66
- log2(f'Use <Up> arrow key to copy the values to clipboard.')
67
- else:
68
- try:
69
- log2(f'Click: {spec["web-uri"]}')
70
- log2(f'username: {spec["username"]}')
71
- log2(f'password: {spec["password"]}')
44
+ return "already-running"
45
+
46
+ # make it a daemon to exit with a Ctrl-D
47
+ thread = threading.Thread(target=self.loop, args=(state,), daemon=True)
48
+ thread.start()
49
+
50
+ while not ReaperForwardSession.is_forwarding:
51
+ time.sleep(1)
52
+
53
+ d = {
54
+ 'reaper-ui': spec["web-uri"],
55
+ 'reaper-username': spec["username"],
56
+ 'reaper-password': spec["password"]
57
+ }
72
58
  log2()
73
- log2(f"Press Ctrl+C to break.")
59
+ tabulize(d.items(), lambda a: f'{a[0]},{a[1]}', separator=',')
74
60
 
75
- time.sleep(Config().get('reaper.port-forward.timeout', 3600 * 24))
76
- except KeyboardInterrupt:
77
- pass
61
+ for k, v in d.items():
62
+ ReplSession().prompt_session.history.append_string(f'cp {k}')
63
+ log2()
64
+ log2(f'Use <Up> arrow key to copy the values to clipboard.')
65
+ else:
66
+ try:
67
+ log2(f'Click: {spec["web-uri"]}')
68
+ log2(f'username: {spec["username"]}')
69
+ log2(f'password: {spec["password"]}')
70
+ log2()
71
+ log2(f"Press Ctrl+C to break.")
72
+
73
+ time.sleep(Config().get('reaper.port-forward.timeout', 3600 * 24))
74
+ except KeyboardInterrupt:
75
+ pass
78
76
 
79
- return state
77
+ return state
80
78
 
81
- def loop(self, state: ReplState, reaper: ReaperSession):
82
- def body(uri: str, _: dict[str, str]):
83
- ReaperSession.is_forwarding = True
79
+ def loop(self, state: ReplState):
80
+ with port_forwarding(state, Reapers.local_port(), partial(Reapers.svc_or_pod, state), Reapers.target_port()):
81
+ ReaperForwardSession.is_forwarding = True
84
82
  try:
85
- while not ReaperSession.stopping.is_set():
83
+ while not ReaperForwardSession.stopping.is_set():
86
84
  time.sleep(1)
87
85
  finally:
88
- ReaperSession.stopping.clear()
89
- ReaperSession.is_forwarding = False
90
-
91
- return reaper.port_forwarded(state, 'webui', body)
86
+ ReaperForwardSession.stopping.clear()
87
+ ReaperForwardSession.is_forwarding = False
92
88
 
93
89
  def completion(self, state: ReplState):
94
- if state.sts:
95
- return super().completion(state)
96
-
97
- return {}
90
+ return super().completion(state)
98
91
 
99
92
  def help(self, _: ReplState):
100
93
  return f'{ReaperForward.COMMAND}\t port-forward to reaper'
@@ -0,0 +1,6 @@
1
+ import threading
2
+
3
+ class ReaperForwardSession:
4
+ is_forwarding = False
5
+ stopping = threading.Event()
6
+ schedules_ids_by_cluster: dict[str, list[str]] = {}
@@ -1,11 +1,11 @@
1
1
  from adam.commands.command import Command
2
- from .reaper_session import ReaperSession
2
+ from adam.commands.reaper.reaper_forward_session import ReaperForwardSession
3
+ from adam.commands.reaper.utils_reaper import Reapers
3
4
  from adam.repl_state import ReplState, RequiredState
4
5
  from adam.utils import log2
5
6
 
6
7
  class ReaperForwardStop(Command):
7
8
  COMMAND = 'reaper forward stop'
8
- reaper_login = None
9
9
 
10
10
  # the singleton pattern
11
11
  def __new__(cls, *args, **kwargs):
@@ -26,24 +26,18 @@ class ReaperForwardStop(Command):
26
26
  if not(args := self.args(cmd)):
27
27
  return super().run(cmd, state)
28
28
 
29
- state, args = self.apply_state(args, state)
30
- if not self.validate_state(state):
31
- return state
32
-
33
- if not ReaperSession.create(state):
34
- return state
29
+ with self.validate(args, state) as (args, state):
30
+ if not Reapers.pod_name(state):
31
+ return state
35
32
 
36
- ReaperSession.is_forwarding = False
37
- ReaperSession.stopping.set()
38
- log2("Stopped reaper forward session.")
33
+ ReaperForwardSession.is_forwarding = False
34
+ ReaperForwardSession.stopping.set()
35
+ log2("Stopped reaper forward session.")
39
36
 
40
- return state
37
+ return state
41
38
 
42
39
  def completion(self, state: ReplState):
43
- if state.sts:
44
- return super().completion(state)
45
-
46
- return {}
40
+ return super().completion(state)
47
41
 
48
42
  def help(self, _: ReplState):
49
43
  return f'{ReaperForwardStop.COMMAND}\t stop port-forward to reaper'
@@ -1,11 +1,10 @@
1
1
  from adam.commands.command import Command
2
- from adam.k8s_utils.pods import Pods
3
- from .reaper_session import ReaperSession
2
+ from adam.commands.reaper.utils_reaper import Reapers
3
+ from adam.utils_k8s.pods import Pods
4
4
  from adam.repl_state import ReplState, RequiredState
5
5
 
6
6
  class ReaperRestart(Command):
7
7
  COMMAND = 'reaper restart'
8
- reaper_login = None
9
8
 
10
9
  # the singleton pattern
11
10
  def __new__(cls, *args, **kwargs):
@@ -26,22 +25,16 @@ class ReaperRestart(Command):
26
25
  if not(args := self.args(cmd)):
27
26
  return super().run(cmd, state)
28
27
 
29
- state, args = self.apply_state(args, state)
30
- if not self.validate_state(state):
31
- return state
32
-
33
- if not(reaper := ReaperSession.create(state)):
34
- return state
28
+ with self.validate(args, state) as (args, state):
29
+ if not (pod := Reapers.pod_name(state)):
30
+ return state
35
31
 
36
- Pods.delete(reaper.pod, state.namespace)
32
+ Pods.delete(pod, state.namespace)
37
33
 
38
- return state
34
+ return state
39
35
 
40
36
  def completion(self, state: ReplState):
41
- if state.sts:
42
- return super().completion(state)
43
-
44
- return {}
37
+ return super().completion(state)
45
38
 
46
39
  def help(self, _: ReplState):
47
40
  return f'{ReaperRestart.COMMAND}\t restart reaper'
@@ -1,13 +1,11 @@
1
- import requests
2
-
1
+ from adam.commands import validate_args
3
2
  from adam.commands.command import Command
4
- from .reaper_session import ReaperSession
3
+ from adam.commands.reaper.utils_reaper import reaper
5
4
  from adam.repl_state import ReplState, RequiredState
6
5
  from adam.utils import log2
7
6
 
8
7
  class ReaperRunAbort(Command):
9
8
  COMMAND = 'reaper abort run'
10
- reaper_login = None
11
9
 
12
10
  # the singleton pattern
13
11
  def __new__(cls, *args, **kwargs):
@@ -28,38 +26,15 @@ class ReaperRunAbort(Command):
28
26
  if not(args := self.args(cmd)):
29
27
  return super().run(cmd, state)
30
28
 
31
- state, args = self.apply_state(args, state)
32
- if not self.validate_state(state):
33
- return state
34
-
35
- if not args:
36
- if state.in_repl:
37
- log2('Specify run id to abort.')
38
- else:
39
- Command.display_help()
40
-
41
- return state
42
-
43
- if not(reaper := ReaperSession.create(state)):
44
- return state
29
+ with self.validate(args, state) as (args, state):
30
+ with validate_args(args, state, name='run id') as run_id:
31
+ with reaper(state) as http:
32
+ http.put(f'repair_run/{run_id}/state/ABORTED')
45
33
 
46
- self.stop_run(state, reaper, args[0])
47
-
48
- return state
49
-
50
- def stop_run(self, state: ReplState, reaper: ReaperSession, run_id: str):
51
- def body(uri: str, headers: dict[str, str]):
52
- return requests.put(uri, headers=headers)
53
-
54
- # PAUSED, RUNNING, ABORTED
55
- # PUT /repair_run/{id}/state/{state}
56
- reaper.port_forwarded(state, f'repair_run/{run_id}/state/ABORTED', body, method='PUT')
34
+ return state
57
35
 
58
36
  def completion(self, state: ReplState):
59
- if state.sts:
60
- return super().completion(state)
61
-
62
- return {}
37
+ return super().completion(state)
63
38
 
64
39
  def help(self, _: ReplState):
65
40
  return f'{ReaperRunAbort.COMMAND} <run-id>\t abort reaper run'
@@ -1,14 +1,11 @@
1
- import requests
2
-
3
1
  from adam.commands.command import Command
4
- from .reaper_session import ReaperSession
2
+ from adam.commands.reaper.utils_reaper import reaper
5
3
  from adam.config import Config
6
4
  from adam.repl_state import ReplState, RequiredState
7
- from adam.utils import convert_seconds, epoch, lines_to_tabular, log, log2
5
+ from adam.utils import convert_seconds, epoch, tabulize, log, log2
8
6
 
9
7
  class ReaperRuns(Command):
10
8
  COMMAND = 'reaper show runs'
11
- reaper_login = None
12
9
 
13
10
  # the singleton pattern
14
11
  def __new__(cls, *args, **kwargs):
@@ -29,63 +26,51 @@ class ReaperRuns(Command):
29
26
  if not(args := self.args(cmd)):
30
27
  return super().run(cmd, state)
31
28
 
32
- state, args = self.apply_state(args, state)
33
- if not self.validate_state(state):
34
- return state
35
-
36
- if not(reaper := ReaperSession.create(state)):
37
- return state
38
-
39
- self.show_runs(state, reaper)
40
-
41
- return state
42
-
43
- def show_runs(self, state: ReplState, reaper: ReaperSession):
44
- def body(uri: str, headers: dict[str, str]):
45
- return requests.get(uri, headers=headers, params={
46
- 'cluster_name': 'all',
47
- 'limit': Config().get('reaper.show-runs-batch', 10)
48
- })
49
-
50
- def line(run):
51
- state = run['state']
52
- start_time = run['start_time']
53
- end_time = run['end_time']
54
- duration = '-'
55
- if state == 'DONE' and end_time:
56
- hours, minutes, seconds = convert_seconds(epoch(end_time) - epoch(start_time))
57
- if hours:
58
- duration = f"{hours:2d}h {minutes:2d}m {seconds:2d}s"
59
- elif minutes:
60
- duration = f"{minutes:2d}m {seconds:2d}s"
29
+ with self.validate(args, state) as (args, state):
30
+ header = 'ID,START,DURATION,STATE,CLUSTER,KEYSPACE,TABLES,REPAIRED'
31
+
32
+ def line(run):
33
+ id = run['id']
34
+ state = run['state']
35
+ start_time = run['start_time']
36
+ end_time = run['end_time']
37
+ duration = '-'
38
+ if state == 'DONE' and end_time:
39
+ hours, minutes, seconds = convert_seconds(epoch(end_time) - epoch(start_time))
40
+ if hours:
41
+ duration = f"{hours:2d}h {minutes:2d}m {seconds:2d}s"
42
+ elif minutes:
43
+ duration = f"{minutes:2d}m {seconds:2d}s"
44
+ else:
45
+ duration = f"{seconds:2d}s"
46
+
47
+ return f"{id},{start_time},{duration},{state},{run['cluster_name']},{run['keyspace_name']},{len(run['column_families'])},{run['segments_repaired']}/{run['total_segments']}"
48
+
49
+ with reaper(state) as http:
50
+ response = http.get('repair_run?state=RUNNING', params={
51
+ 'cluster_name': 'all',
52
+ 'limit': Config().get('reaper.show-runs-batch', 10)
53
+ })
54
+
55
+ runs = response.json()
56
+ if runs:
57
+ tabulize(sorted([line(run) for run in runs], reverse=True), header=header, separator=",")
61
58
  else:
62
- duration = f"{seconds:2d}s"
59
+ log2('No running runs found.')
60
+ log2()
63
61
 
64
- return f"{start_time},{duration},{state},{run['cluster_name']},{run['keyspace_name']},{len(run['column_families'])},{run['segments_repaired']}/{run['total_segments']}"
62
+ response = http.get('repair_run?state=PAUSED,ABORTED,DONE', params={
63
+ 'cluster_name': 'all',
64
+ 'limit': Config().get('reaper.show-runs-batch', 10)
65
+ })
65
66
 
66
- # PAUSED, RUNNING, ABORTED
67
- response = reaper.port_forwarded(state, 'repair_run?state=RUNNING', body, method='GET')
68
- if not response:
69
- return
70
-
71
- header = 'Start,Duration,State,Cluster,Keyspace,Tables,Repaired'
72
-
73
- runs = response.json()
74
- if runs:
75
- log(lines_to_tabular(sorted([line(run) for run in runs], reverse=True), header, separator=","))
76
- else:
77
- log2('No running runs found.')
78
- log2()
79
-
80
- response = reaper.port_forwarded(state, 'repair_run?state=PAUSED,ABORTED,DONE', body, method='GET')
81
- if not response:
82
- return
67
+ runs = response.json()
68
+ if runs:
69
+ tabulize(sorted([line(run) for run in runs], reverse=True), header=header, separator=",")
70
+ else:
71
+ log2('No runs found.')
83
72
 
84
- runs = response.json()
85
- if runs:
86
- log(lines_to_tabular(sorted([line(run) for run in runs], reverse=True), header, separator=","))
87
- else:
88
- log2('No runs found.')
73
+ return state
89
74
 
90
75
  def completion(self, state: ReplState):
91
76
  if state.sts: