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,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
5
  from adam.utils import log2
8
6
 
9
7
  class ReaperRunsAbort(Command):
10
8
  COMMAND = 'reaper abort runs'
11
- reaper_login = None
12
9
 
13
10
  # the singleton pattern
14
11
  def __new__(cls, *args, **kwargs):
@@ -29,55 +26,38 @@ class ReaperRunsAbort(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
29
+ with self.validate(args, state) as (args, state):
30
+ with reaper(state) as http:
31
+ # PAUSED, RUNNING, ABORTED
32
+ aborted = 0
33
+
34
+ while True == True:
35
+ response = http.get('repair_run?state=RUNNING', params={
36
+ 'cluster_name': 'all',
37
+ 'limit': Config().get('reaper.abort-runs-batch', 10)
38
+ })
39
+ if not response:
40
+ break
41
+
42
+ runs = response.json()
43
+ if not runs:
44
+ break
45
+
46
+ for run in runs:
47
+ run_id = run['id']
48
+ # PUT /repair_run/{id}/state/{state}
49
+ http.put(f'repair_run/{run_id}/state/ABORTED')
50
+ aborted += 1
51
+
52
+ if aborted:
53
+ log2(f'Aborted {aborted} runs in total.')
54
+ else:
55
+ log2('No running repair runs found.')
35
56
 
36
- if not(reaper := ReaperSession.create(state)):
37
57
  return state
38
58
 
39
- self.stop_runs(state, reaper)
40
-
41
- return state
42
-
43
- def stop_runs(self, state: ReplState, reaper: ReaperSession):
44
- def body_list(uri: str, headers: dict[str, str]):
45
- return requests.get(uri, headers=headers, params={
46
- 'cluster_name': 'all',
47
- 'limit': Config().get('reaper.abort-runs-batch', 10)
48
- })
49
-
50
- def body_abort(uri: str, headers: dict[str, str]):
51
- return requests.put(uri, headers=headers)
52
-
53
- # PAUSED, RUNNING, ABORTED
54
- aborted = 0
55
- while True == True:
56
- response = reaper.port_forwarded(state, 'repair_run?state=RUNNING', body_list, method='GET')
57
- if not response:
58
- break
59
-
60
- runs = response.json()
61
- if not runs:
62
- break
63
-
64
- for run in runs:
65
- run_id = run['id']
66
- # PUT /repair_run/{id}/state/{state}
67
- reaper.port_forwarded(state, f'repair_run/{run_id}/state/ABORTED', body_abort, method='PUT')
68
- log2(f'Aborted {len(runs)} runs.')
69
- aborted += 1
70
-
71
- if aborted:
72
- log2(f'Aborted {aborted} runs in total.')
73
- else:
74
- log2('No running repair runs found.')
75
-
76
59
  def completion(self, state: ReplState):
77
- if state.sts:
78
- return super().completion(state)
79
-
80
- return {}
60
+ return super().completion(state)
81
61
 
82
62
  def help(self, _: ReplState):
83
63
  return f'{ReaperRunsAbort.COMMAND}\t abort all running reaper runs'
@@ -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 Reapers, reaper
5
4
  from adam.repl_state import ReplState, RequiredState
6
5
  from adam.utils import log2
7
6
 
8
7
  class ReaperScheduleActivate(Command):
9
8
  COMMAND = 'reaper activate schedule'
10
- reaper_login = None
11
9
 
12
10
  # the singleton pattern
13
11
  def __new__(cls, *args, **kwargs):
@@ -28,37 +26,16 @@ class ReaperScheduleActivate(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
- log2('Specify schedule to activate.')
37
-
38
- return state
39
-
40
- schedule_id = args[0]
41
- if not(reaper := ReaperSession.create(state)):
42
- return state
43
-
44
- self.activate_schedule(state, reaper, schedule_id)
29
+ with self.validate(args, state) as (args, state):
30
+ with validate_args(args, state, name='schedule') as schedule_id:
31
+ with reaper(state) as http:
32
+ http.put(f'repair_schedule/{schedule_id}?state=ACTIVE')
33
+ Reapers.show_schedule(state, schedule_id)
45
34
 
46
- return schedule_id
47
-
48
- def activate_schedule(self, state: ReplState, reaper: ReaperSession, schedule_id: str):
49
- def body(uri: str, headers: dict[str, str]):
50
- return requests.put(uri, headers=headers)
51
-
52
- reaper.port_forwarded(state, f'repair_schedule/{schedule_id}?state=ACTIVE', body, method='PUT')
53
- reaper.show_schedule(state, schedule_id)
35
+ return schedule_id
54
36
 
55
37
  def completion(self, state: ReplState):
56
- if state.sts:
57
- leaf = {id: None for id in ReaperSession.cached_schedule_ids(state)}
58
-
59
- return super().completion(state, leaf)
60
-
61
- return {}
38
+ return super().completion(state, lambda: {id: None for id in Reapers.cached_schedule_ids(state)})
62
39
 
63
40
  def help(self, _: ReplState):
64
41
  return f'{ReaperScheduleActivate.COMMAND} <schedule-id>\t resume reaper schedule'
@@ -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 Reapers, reaper
5
4
  from adam.repl_state import ReplState, RequiredState
6
5
  from adam.utils import log2
7
6
 
8
7
  class ReaperScheduleStart(Command):
9
8
  COMMAND = 'reaper start schedule'
10
- reaper_login = None
11
9
 
12
10
  # the singleton pattern
13
11
  def __new__(cls, *args, **kwargs):
@@ -28,37 +26,16 @@ class ReaperScheduleStart(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
- log2('Specify schedule to activate.')
37
-
38
- return state
39
-
40
- schedule_id = args[0]
41
- if not(reaper := ReaperSession.create(state)):
42
- return schedule_id
43
-
44
- self.start_schedule(state, reaper, schedule_id)
29
+ with self.validate(args, state) as (args, state):
30
+ with validate_args(args, state, name='schedule') as schedule_id:
31
+ with reaper(state) as http:
32
+ http.post(f'repair_schedule/start/{schedule_id}')
33
+ Reapers.show_schedule(state, schedule_id)
45
34
 
46
- return schedule_id
47
-
48
- def start_schedule(self, state: ReplState, reaper: ReaperSession, schedule_id: str):
49
- def body(uri: str, headers: dict[str, str]):
50
- return requests.post(uri, headers=headers)
51
-
52
- reaper.port_forwarded(state, f'repair_schedule/start/{schedule_id}', body, method='POST')
53
- reaper.show_schedule(state, schedule_id)
35
+ return schedule_id
54
36
 
55
37
  def completion(self, state: ReplState):
56
- if state.sts:
57
- leaf = {id: None for id in ReaperSession.cached_schedule_ids(state)}
58
-
59
- return super().completion(state, leaf)
60
-
61
- return {}
38
+ return super().completion(state, lambda: {id: None for id in Reapers.cached_schedule_ids(state)})
62
39
 
63
40
  def help(self, _: ReplState):
64
41
  return f'{ReaperScheduleStart.COMMAND} <schedule-id>\t start reaper runs for schedule'
@@ -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 Reapers, reaper
5
4
  from adam.repl_state import ReplState, RequiredState
6
5
  from adam.utils import log2
7
6
 
8
7
  class ReaperScheduleStop(Command):
9
8
  COMMAND = 'reaper stop schedule'
10
- reaper_login = None
11
9
 
12
10
  # the singleton pattern
13
11
  def __new__(cls, *args, **kwargs):
@@ -28,37 +26,16 @@ class ReaperScheduleStop(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
- log2('Specify run schedule to stop.')
37
-
38
- return state
39
-
40
- schedule_id = args[0]
41
- if not(reaper := ReaperSession.create(state)):
42
- return schedule_id
43
-
44
- self.stop_schedule(state, reaper, schedule_id)
29
+ with self.validate(args, state) as (args, state):
30
+ with validate_args(args, state, name='schedule') as schedule_id:
31
+ with reaper(state) as http:
32
+ http.put(f'repair_schedule/{schedule_id}?state=PAUSED')
33
+ Reapers.show_schedule(state, schedule_id)
45
34
 
46
- return schedule_id
47
-
48
- def stop_schedule(self, state: ReplState, reaper: ReaperSession, schedule_id: str):
49
- def body(uri: str, headers: dict[str, str]):
50
- return requests.put(uri, headers=headers)
51
-
52
- reaper.port_forwarded(state, f'repair_schedule/{schedule_id}?state=PAUSED', body, method='PUT')
53
- reaper.show_schedule(state, schedule_id)
35
+ return schedule_id
54
36
 
55
37
  def completion(self, state: ReplState):
56
- if state.sts:
57
- leaf = {id: None for id in ReaperSession.cached_schedule_ids(state)}
58
-
59
- return super().completion(state, leaf)
60
-
61
- return {}
38
+ return super().completion(state, lambda: {id: None for id in Reapers.cached_schedule_ids(state)})
62
39
 
63
40
  def help(self, _: ReplState):
64
41
  return f'{ReaperScheduleStop.COMMAND} <schedule-id>\t pause reaper schedule'
@@ -1,10 +1,9 @@
1
1
  from adam.commands.command import Command
2
- from .reaper_session import ReaperSession
2
+ from adam.commands.reaper.utils_reaper import Reapers
3
3
  from adam.repl_state import ReplState, RequiredState
4
4
 
5
5
  class ReaperSchedules(Command):
6
6
  COMMAND = 'reaper show schedules'
7
- reaper_login = None
8
7
 
9
8
  # the singleton pattern
10
9
  def __new__(cls, *args, **kwargs):
@@ -25,22 +24,13 @@ class ReaperSchedules(Command):
25
24
  if not(args := self.args(cmd)):
26
25
  return super().run(cmd, state)
27
26
 
28
- state, args = self.apply_state(args, state)
29
- if not self.validate_state(state):
30
- return state
27
+ with self.validate(args, state) as (args, state):
28
+ Reapers.show_schedules(state)
31
29
 
32
- if not(reaper := ReaperSession.create(state)):
33
30
  return state
34
31
 
35
- reaper.show_schedules(state)
36
-
37
- return state
38
-
39
32
  def completion(self, state: ReplState):
40
- if state.sts:
41
- return super().completion(state)
42
-
43
- return {}
33
+ return super().completion(state)
44
34
 
45
35
  def help(self, _: ReplState):
46
36
  return f'{ReaperSchedules.COMMAND}\t show reaper schedules'
@@ -4,13 +4,11 @@ from kubernetes import client
4
4
 
5
5
  from adam.commands.command import Command
6
6
  from adam.commands.commands_utils import show_pods
7
- from .reaper_session import ReaperSession
7
+ from adam.commands.reaper.utils_reaper import Reapers
8
8
  from adam.repl_state import ReplState, RequiredState
9
- from adam.utils import lines_to_tabular, log, log2
10
9
 
11
10
  class ReaperStatus(Command):
12
11
  COMMAND = 'reaper status'
13
- reaper_login = None
14
12
 
15
13
  # the singleton pattern
16
14
  def __new__(cls, *args, **kwargs):
@@ -31,18 +29,15 @@ class ReaperStatus(Command):
31
29
  if not(args := self.args(cmd)):
32
30
  return super().run(cmd, state)
33
31
 
34
- state, args = self.apply_state(args, state)
35
- if not self.validate_state(state):
36
- return state
37
-
38
- if not(reaper := ReaperSession.create(state)):
39
- return state
32
+ with self.validate(args, state) as (args, state):
33
+ if not Reapers.pod_name(state):
34
+ return state
40
35
 
41
- pods = self.list_pods(state.sts, state.namespace)
36
+ pods = self.list_pods(state.sts, state.namespace)
42
37
 
43
- show_pods(pods, state.namespace, show_host_id=False)
38
+ show_pods(pods, state.namespace, show_host_id=False)
44
39
 
45
- return state
40
+ return state
46
41
 
47
42
  def list_pods(self, sts_name: str, namespace: str) -> List[client.V1Pod]:
48
43
  v1 = client.CoreV1Api()
@@ -55,10 +50,7 @@ class ReaperStatus(Command):
55
50
  return cast(List[client.V1Pod], v1.list_namespaced_pod(namespace, label_selector=label_selector).items)
56
51
 
57
52
  def completion(self, state: ReplState):
58
- if state.sts:
59
- return super().completion(state)
60
-
61
- return {}
53
+ return super().completion(state)
62
54
 
63
55
  def help(self, _: ReplState):
64
56
  return f'{ReaperStatus.COMMAND}\t show reaper status'
@@ -0,0 +1,194 @@
1
+ from collections.abc import Callable
2
+ from functools import partial
3
+ from typing import List, cast
4
+ from kubernetes import client
5
+ import re
6
+
7
+ import requests
8
+ from adam.config import Config
9
+ from adam.repl_state import ReplState
10
+ from adam.utils import tabulize, log2, wait_log
11
+ from adam.utils_k8s.k8s import port_forwarding
12
+
13
+ class ReaperService:
14
+ def __init__(self, state: ReplState, local_addr: str, remote_addr: str, show_out = True):
15
+ self.state = state
16
+ self.local_addr = local_addr
17
+ self.remote_addr = remote_addr
18
+ self.show_out = show_out
19
+ self.headers = None
20
+
21
+ def get(self, path: str, params: dict[str, any] = {}):
22
+ with logging(self, 'GET', path) as (url, headers):
23
+ return requests.get(url, headers=headers, params=params)
24
+
25
+ def put(self, path: str, params: dict[str, any] = {}):
26
+ with logging(self, 'PUT', path) as (url, headers):
27
+ return requests.put(url, headers=headers, params=params)
28
+
29
+ def post(self, path: str, params: dict[str, any] = {}):
30
+ with logging(self, 'POST', path) as (url, headers):
31
+ return requests.post(url, headers=headers, params=params)
32
+
33
+ class ReaperLogginHandler:
34
+ def __init__(self, svc: ReaperService, method: str, path: str):
35
+ self.svc = svc
36
+ self.method = method
37
+ self.path = path
38
+
39
+ def __enter__(self) -> tuple[str, dict[str, any]]:
40
+ if not self.svc.headers:
41
+ self.svc.headers = Reapers.cookie_header(self.svc.state, self.svc.local_addr, self.svc.remote_addr, show_output=self.svc.show_out)
42
+
43
+ if self.svc.show_out and self.method:
44
+ log2(f'{self.method} {self.svc.remote_addr}/{self.path}')
45
+
46
+ return (f'http://{self.svc.local_addr}/{self.path}', self.svc.headers)
47
+
48
+ def __exit__(self, exc_type, exc_val, exc_tb):
49
+ if exc_val and isinstance(exc_val, requests.Response):
50
+ if int(exc_val.status_code / 100) != 2:
51
+ if self.svc.show_out:
52
+ log2(exc_val.status_code)
53
+
54
+ return False
55
+
56
+ def logging(svc: ReaperService, method: str, path: str):
57
+ return ReaperLogginHandler(svc, method, path)
58
+
59
+ class ReaperHandler:
60
+ def __init__(self, state: ReplState, show_out = True):
61
+ self.state = state
62
+ self.show_out = show_out
63
+ self.headers = None
64
+ self.forwarding = None
65
+
66
+ def __enter__(self):
67
+ self.forwarding = port_forwarding(self.state, Reapers.local_port(), partial(Reapers.svc_or_pod, self.state), Reapers.target_port())
68
+ (local_addr, remote_addr) = self.forwarding.__enter__()
69
+
70
+ return ReaperService(self.state, local_addr, remote_addr, show_out=self.show_out)
71
+
72
+ def __exit__(self, exc_type, exc_val, exc_tb):
73
+ if self.forwarding:
74
+ return self.forwarding.__exit__(exc_type, exc_val, exc_tb)
75
+
76
+ return False
77
+
78
+ def reaper(state: ReplState, show_out = True):
79
+ return ReaperHandler(state, show_out=show_out)
80
+
81
+ class Reapers:
82
+ schedules_ids_by_cluster: dict[str, list[str]] = {}
83
+
84
+ def pod_name(state: ReplState):
85
+ pods = Reapers.list_reaper_pods(state.sts if state.sts else state.pod, state.namespace)
86
+ if pods:
87
+ return pods[0].metadata.name
88
+
89
+ return None
90
+
91
+ def show_schedule(state: ReplState, schedule_id: str):
92
+ def filter(schedules: list[dict]):
93
+ return [schedule for schedule in schedules if schedule['id'] == schedule_id]
94
+
95
+ Reapers.show_schedules(state, filter)
96
+
97
+ def show_schedules(state: ReplState, filter: Callable[[list[dict]], dict] = None):
98
+ schedules = Reapers.list_schedules(state, filter=filter)
99
+
100
+ # forced refresh of schedule list
101
+ if not filter:
102
+ Reapers.schedules_ids_by_cluster[state.sts] = [schedule['id'] for schedule in schedules]
103
+
104
+ tabulize(schedules, lambda s: f"{s['id']} {s['state']} {s['cluster_name']} {s['keyspace_name']}", header='ID STATE CLUSTER KEYSPACE', to=2)
105
+
106
+ def schedule_ids(state: ReplState, show_output = True, filter: Callable[[list[dict]], dict] = None):
107
+ schedules = Reapers.list_schedules(state, show_output=show_output, filter=filter)
108
+ return [schedule['id'] for schedule in schedules]
109
+
110
+ def list_schedules(state: ReplState, show_output = True, filter: Callable[[list[dict]], dict] = None) -> list[dict]:
111
+ with reaper(state, show_out=show_output) as requests:
112
+ if not (response := requests.get('repair_schedule')):
113
+ return
114
+
115
+ res = response.json()
116
+ if filter:
117
+ res = filter(res)
118
+
119
+ return res
120
+
121
+ def list_reaper_pods(sts_name: str, namespace: str) -> List[client.V1Pod]:
122
+ v1 = client.CoreV1Api()
123
+
124
+ # k8ssandra.io/reaper: cs-d0767a536f-cs-d0767a536f-reaper
125
+ groups = re.match(Config().get('reaper.pod.cluster-regex', r'(.*?-.*?-.*?-.*?)-.*'), sts_name)
126
+ label_selector = Config().get('reaper.pod.label-selector', 'k8ssandra.io/reaper={cluster}-reaper').replace('{cluster}', groups[1])
127
+
128
+ return cast(List[client.V1Pod], v1.list_namespaced_pod(namespace, label_selector=label_selector).items)
129
+
130
+ def cookie_header(state: ReplState, local_addr, remote_addr, show_output = True):
131
+ return {'Cookie': Reapers.login(state, local_addr, remote_addr, show_output=show_output)}
132
+
133
+ def login(state: ReplState, local_addr: str, remote_addr: str, show_output = True) -> str :
134
+ user, pw = state.user_pass(secret_path='reaper.secret')
135
+
136
+ response = requests.post(f'http://{local_addr}/login', headers={
137
+ 'Accept': '*'
138
+ },data={
139
+ 'username':user,
140
+ 'password':pw})
141
+ if show_output:
142
+ log2(f'POST {remote_addr}/login')
143
+ log2(f' username={user}&password={pw}')
144
+
145
+ if int(response.status_code / 100) != 2:
146
+ if show_output:
147
+ log2("login failed")
148
+ return None
149
+
150
+ return response.headers['Set-Cookie']
151
+
152
+ def reaper_spec(state: ReplState) -> dict[str, any]:
153
+ if not (pod := Reapers.pod_name(state)):
154
+ return {}
155
+
156
+ user, pw = state.user_pass(secret_path='reaper.secret')
157
+
158
+ return {
159
+ 'pod': pod,
160
+ 'exec': f'kubectl exec -it {pod} -n {state.namespace} -- bash',
161
+ 'forward': f'kubectl port-forward pods/{pod} -n {state.namespace} {Reapers.local_port()}:{Reapers.target_port()}',
162
+ 'web-uri': f'http://localhost:{Reapers.local_port()}/webui',
163
+ 'username': user,
164
+ 'password': pw
165
+ }
166
+
167
+ def cached_schedule_ids(state: ReplState) -> list[str]:
168
+ if state.sts in Reapers.schedules_ids_by_cluster:
169
+ return Reapers.schedules_ids_by_cluster[state.sts]
170
+
171
+ if pod := Reapers.pod_name(state):
172
+ wait_log('Inspecting Cassandra Reaper...')
173
+
174
+ schedules = Reapers.schedule_ids(state, show_output = False)
175
+ Reapers.schedules_ids_by_cluster[state.sts] = schedules
176
+
177
+ return schedules
178
+
179
+ return []
180
+
181
+ def svc_name():
182
+ return Config().get('reaper.service-name', 'reaper-service')
183
+
184
+ def local_port():
185
+ return Config().get('reaper.port-forward.local-port', 9001)
186
+
187
+ def target_port():
188
+ return 8080
189
+
190
+ def svc_or_pod(state: ReplState, is_service: bool):
191
+ if is_service:
192
+ return Reapers.svc_name()
193
+ else:
194
+ return Reapers.pod_name(state)
@@ -1,13 +1,12 @@
1
1
  import click
2
2
 
3
- from adam.commands.command import Command
3
+ from adam.commands.intermediate_command import IntermediateCommand
4
4
  from .repair_run import RepairRun
5
5
  from .repair_scan import RepairScan
6
6
  from .repair_stop import RepairStop
7
7
  from .repair_log import RepairLog
8
- from adam.repl_state import ReplState, RequiredState
9
8
 
10
- class Repair(Command):
9
+ class Repair(IntermediateCommand):
11
10
  COMMAND = 'repair'
12
11
 
13
12
  # the singleton pattern
@@ -16,29 +15,12 @@ class Repair(Command):
16
15
 
17
16
  return cls.instance
18
17
 
19
- def __init__(self, successor: Command=None):
20
- super().__init__(successor)
21
-
22
18
  def command(self):
23
19
  return Repair.COMMAND
24
20
 
25
- def required(self):
26
- return RequiredState.CLUSTER
27
-
28
- def run(self, cmd: str, state: ReplState):
29
- if not(args := self.args(cmd)):
30
- return super().run(cmd, state)
31
-
32
- return super().intermediate_run(cmd, state, args, Repair.cmd_list())
33
-
34
- def cmd_list():
21
+ def cmd_list(self):
35
22
  return [RepairRun(), RepairScan(), RepairStop(), RepairLog()]
36
23
 
37
- def completion(self, state: ReplState):
38
- if state.sts:
39
- return super().completion(state)
40
- return {}
41
-
42
24
  class RepairCommandHelper(click.Command):
43
25
  def get_help(self, ctx: click.Context):
44
- Command.intermediate_help(super().get_help(ctx), Repair.COMMAND, Repair.cmd_list(), show_cluster_help=True)
26
+ IntermediateCommand.intermediate_help(super().get_help(ctx), Repair.COMMAND, Repair().cmd_list(), show_cluster_help=True)
@@ -1,5 +1,5 @@
1
1
  from adam.commands.command import Command
2
- from adam.k8s_utils.jobs import Jobs
2
+ from adam.utils_k8s.jobs import Jobs
3
3
  from adam.repl_state import ReplState, RequiredState
4
4
 
5
5
  class RepairLog(Command):
@@ -24,20 +24,14 @@ class RepairLog(Command):
24
24
  if not(args := self.args(cmd)):
25
25
  return super().run(cmd, state)
26
26
 
27
- state, args = self.apply_state(args, state)
28
- if not self.validate_state(state):
29
- return state
30
-
31
- ns = state.namespace
32
- Jobs.get_logs('cassrepair-'+state.sts, ns)
27
+ with self.validate(args, state) as (args, state):
28
+ ns = state.namespace
29
+ Jobs.get_logs('cassrepair-'+state.sts, ns)
33
30
 
34
- return state
31
+ return state
35
32
 
36
33
  def completion(self, state: ReplState):
37
- if state.sts:
38
- return super().completion(state)
39
-
40
- return {}
34
+ return super().completion(state)
41
35
 
42
36
  def help(self, _: ReplState):
43
37
  return f'{RepairLog.COMMAND}\t get repair job logs'