kaqing 2.0.14__py3-none-any.whl → 2.0.189__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of kaqing might be problematic. Click here for more details.

Files changed (228) 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 +16 -6
  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 +66 -0
  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 +106 -0
  25. adam/commands/audit/__init__.py +0 -0
  26. adam/commands/audit/audit.py +67 -0
  27. adam/commands/audit/audit_repair_tables.py +72 -0
  28. adam/commands/audit/audit_run.py +50 -0
  29. adam/commands/audit/completions_l.py +15 -0
  30. adam/commands/audit/show_last10.py +36 -0
  31. adam/commands/audit/show_slow10.py +36 -0
  32. adam/commands/audit/show_top10.py +36 -0
  33. adam/commands/audit/utils_show_top10.py +71 -0
  34. adam/commands/bash/__init__.py +5 -0
  35. adam/commands/bash/bash.py +36 -0
  36. adam/commands/bash/bash_completer.py +93 -0
  37. adam/commands/bash/utils_bash.py +16 -0
  38. adam/commands/cat.py +36 -0
  39. adam/commands/cd.py +14 -88
  40. adam/commands/check.py +18 -21
  41. adam/commands/cli_commands.py +11 -7
  42. adam/commands/clipboard_copy.py +87 -0
  43. adam/commands/code.py +57 -0
  44. adam/commands/command.py +220 -19
  45. adam/commands/commands_utils.py +28 -31
  46. adam/commands/cql/__init__.py +0 -0
  47. adam/commands/cql/completions_c.py +28 -0
  48. adam/commands/{cqlsh.py → cql/cqlsh.py} +13 -32
  49. adam/commands/cql/utils_cql.py +305 -0
  50. adam/commands/deploy/code_start.py +7 -10
  51. adam/commands/deploy/code_stop.py +4 -21
  52. adam/commands/deploy/code_utils.py +5 -5
  53. adam/commands/deploy/deploy.py +4 -40
  54. adam/commands/deploy/deploy_frontend.py +15 -18
  55. adam/commands/deploy/deploy_pg_agent.py +4 -7
  56. adam/commands/deploy/deploy_pod.py +74 -77
  57. adam/commands/deploy/deploy_utils.py +16 -26
  58. adam/commands/deploy/undeploy.py +4 -40
  59. adam/commands/deploy/undeploy_frontend.py +5 -8
  60. adam/commands/deploy/undeploy_pg_agent.py +7 -8
  61. adam/commands/deploy/undeploy_pod.py +16 -17
  62. adam/commands/devices/__init__.py +0 -0
  63. adam/commands/devices/device.py +149 -0
  64. adam/commands/devices/device_app.py +163 -0
  65. adam/commands/devices/device_auit_log.py +49 -0
  66. adam/commands/devices/device_cass.py +179 -0
  67. adam/commands/devices/device_export.py +87 -0
  68. adam/commands/devices/device_postgres.py +160 -0
  69. adam/commands/devices/devices.py +25 -0
  70. adam/commands/download_file.py +47 -0
  71. adam/commands/exit.py +1 -4
  72. adam/commands/export/__init__.py +0 -0
  73. adam/commands/export/clean_up_all_export_sessions.py +37 -0
  74. adam/commands/export/clean_up_export_sessions.py +39 -0
  75. adam/commands/export/completions_x.py +11 -0
  76. adam/commands/export/download_export_session.py +40 -0
  77. adam/commands/export/drop_export_database.py +39 -0
  78. adam/commands/export/drop_export_databases.py +37 -0
  79. adam/commands/export/export.py +37 -0
  80. adam/commands/export/export_databases.py +246 -0
  81. adam/commands/export/export_select.py +34 -0
  82. adam/commands/export/export_sessions.py +209 -0
  83. adam/commands/export/export_use.py +49 -0
  84. adam/commands/export/export_x_select.py +48 -0
  85. adam/commands/export/exporter.py +332 -0
  86. adam/commands/export/import_files.py +44 -0
  87. adam/commands/export/import_session.py +44 -0
  88. adam/commands/export/importer.py +81 -0
  89. adam/commands/export/importer_athena.py +148 -0
  90. adam/commands/export/importer_sqlite.py +67 -0
  91. adam/commands/export/show_column_counts.py +45 -0
  92. adam/commands/export/show_export_databases.py +39 -0
  93. adam/commands/export/show_export_session.py +39 -0
  94. adam/commands/export/show_export_sessions.py +37 -0
  95. adam/commands/export/utils_export.py +344 -0
  96. adam/commands/find_files.py +51 -0
  97. adam/commands/find_processes.py +76 -0
  98. adam/commands/head.py +36 -0
  99. adam/commands/help.py +14 -9
  100. adam/commands/intermediate_command.py +52 -0
  101. adam/commands/issues.py +14 -40
  102. adam/commands/kubectl.py +38 -0
  103. adam/commands/login.py +26 -25
  104. adam/commands/logs.py +5 -7
  105. adam/commands/ls.py +11 -115
  106. adam/commands/medusa/medusa.py +4 -46
  107. adam/commands/medusa/medusa_backup.py +22 -29
  108. adam/commands/medusa/medusa_restore.py +51 -49
  109. adam/commands/medusa/medusa_show_backupjobs.py +20 -21
  110. adam/commands/medusa/medusa_show_restorejobs.py +16 -21
  111. adam/commands/medusa/utils_medusa.py +15 -0
  112. adam/commands/nodetool.py +8 -17
  113. adam/commands/param_get.py +11 -14
  114. adam/commands/param_set.py +9 -13
  115. adam/commands/postgres/completions_p.py +22 -0
  116. adam/commands/postgres/postgres.py +49 -73
  117. adam/commands/postgres/postgres_databases.py +270 -0
  118. adam/commands/postgres/postgres_ls.py +4 -8
  119. adam/commands/postgres/postgres_preview.py +5 -9
  120. adam/commands/postgres/utils_postgres.py +79 -0
  121. adam/commands/preview_table.py +10 -69
  122. adam/commands/pwd.py +14 -43
  123. adam/commands/reaper/reaper.py +6 -49
  124. adam/commands/reaper/reaper_forward.py +49 -56
  125. adam/commands/reaper/reaper_forward_session.py +6 -0
  126. adam/commands/reaper/reaper_forward_stop.py +10 -16
  127. adam/commands/reaper/reaper_restart.py +8 -15
  128. adam/commands/reaper/reaper_run_abort.py +8 -33
  129. adam/commands/reaper/reaper_runs.py +43 -58
  130. adam/commands/reaper/reaper_runs_abort.py +29 -49
  131. adam/commands/reaper/reaper_schedule_activate.py +14 -33
  132. adam/commands/reaper/reaper_schedule_start.py +9 -33
  133. adam/commands/reaper/reaper_schedule_stop.py +9 -33
  134. adam/commands/reaper/reaper_schedules.py +4 -14
  135. adam/commands/reaper/reaper_status.py +8 -16
  136. adam/commands/reaper/utils_reaper.py +203 -0
  137. adam/commands/repair/repair.py +4 -46
  138. adam/commands/repair/repair_log.py +6 -12
  139. adam/commands/repair/repair_run.py +29 -36
  140. adam/commands/repair/repair_scan.py +33 -41
  141. adam/commands/repair/repair_stop.py +6 -13
  142. adam/commands/report.py +25 -21
  143. adam/commands/restart.py +27 -28
  144. adam/commands/rollout.py +20 -25
  145. adam/commands/shell.py +12 -4
  146. adam/commands/show/show.py +15 -46
  147. adam/commands/show/show_adam.py +3 -3
  148. adam/commands/show/show_cassandra_repairs.py +37 -0
  149. adam/commands/show/show_cassandra_status.py +48 -52
  150. adam/commands/show/show_cassandra_version.py +5 -18
  151. adam/commands/show/show_cli_commands.py +56 -0
  152. adam/commands/show/show_host.py +33 -0
  153. adam/commands/show/show_login.py +23 -27
  154. adam/commands/show/show_params.py +2 -5
  155. adam/commands/show/show_processes.py +18 -21
  156. adam/commands/show/show_storage.py +11 -20
  157. adam/commands/watch.py +27 -30
  158. adam/config.py +8 -6
  159. adam/embedded_params.py +1 -1
  160. adam/log.py +4 -4
  161. adam/pod_exec_result.py +13 -5
  162. adam/repl.py +136 -120
  163. adam/repl_commands.py +66 -24
  164. adam/repl_session.py +8 -1
  165. adam/repl_state.py +343 -73
  166. adam/sql/__init__.py +0 -0
  167. adam/sql/lark_completer.py +284 -0
  168. adam/sql/lark_parser.py +604 -0
  169. adam/sql/sql_completer.py +118 -0
  170. adam/sql/sql_state_machine.py +630 -0
  171. adam/sql/term_completer.py +76 -0
  172. adam/sso/authn_ad.py +7 -9
  173. adam/sso/authn_okta.py +4 -6
  174. adam/sso/cred_cache.py +4 -6
  175. adam/sso/idp.py +10 -13
  176. adam/utils.py +539 -11
  177. adam/utils_athena.py +145 -0
  178. adam/utils_audits.py +102 -0
  179. adam/utils_issues.py +32 -0
  180. adam/utils_k8s/__init__.py +0 -0
  181. adam/utils_k8s/app_clusters.py +28 -0
  182. adam/utils_k8s/app_pods.py +36 -0
  183. adam/utils_k8s/cassandra_clusters.py +44 -0
  184. adam/{k8s_utils → utils_k8s}/cassandra_nodes.py +12 -5
  185. adam/{k8s_utils → utils_k8s}/custom_resources.py +16 -17
  186. adam/{k8s_utils → utils_k8s}/deployment.py +2 -2
  187. adam/{k8s_utils → utils_k8s}/ingresses.py +2 -2
  188. adam/{k8s_utils → utils_k8s}/jobs.py +7 -11
  189. adam/utils_k8s/k8s.py +96 -0
  190. adam/{k8s_utils → utils_k8s}/kube_context.py +3 -3
  191. adam/{k8s_utils → utils_k8s}/pods.py +132 -83
  192. adam/{k8s_utils → utils_k8s}/secrets.py +7 -3
  193. adam/{k8s_utils → utils_k8s}/service_accounts.py +5 -4
  194. adam/{k8s_utils → utils_k8s}/services.py +2 -2
  195. adam/{k8s_utils → utils_k8s}/statefulsets.py +9 -16
  196. adam/utils_local.py +4 -0
  197. adam/utils_net.py +24 -0
  198. adam/utils_repl/__init__.py +0 -0
  199. adam/utils_repl/appendable_completer.py +6 -0
  200. adam/utils_repl/automata_completer.py +48 -0
  201. adam/utils_repl/repl_completer.py +172 -0
  202. adam/utils_repl/state_machine.py +173 -0
  203. adam/utils_sqlite.py +137 -0
  204. adam/version.py +1 -1
  205. {kaqing-2.0.14.dist-info → kaqing-2.0.189.dist-info}/METADATA +1 -1
  206. kaqing-2.0.189.dist-info/RECORD +253 -0
  207. kaqing-2.0.189.dist-info/top_level.txt +2 -0
  208. teddy/__init__.py +0 -0
  209. teddy/lark_parser.py +436 -0
  210. teddy/lark_parser2.py +618 -0
  211. adam/commands/app.py +0 -67
  212. adam/commands/bash.py +0 -87
  213. adam/commands/cp.py +0 -95
  214. adam/commands/cql_utils.py +0 -53
  215. adam/commands/devices.py +0 -89
  216. adam/commands/postgres/postgres_session.py +0 -247
  217. adam/commands/reaper/reaper_session.py +0 -159
  218. adam/commands/show/show_app_actions.py +0 -53
  219. adam/commands/show/show_commands.py +0 -61
  220. adam/commands/show/show_repairs.py +0 -47
  221. adam/k8s_utils/cassandra_clusters.py +0 -48
  222. kaqing-2.0.14.dist-info/RECORD +0 -167
  223. kaqing-2.0.14.dist-info/top_level.txt +0 -1
  224. /adam/{k8s_utils → commands/app}/__init__.py +0 -0
  225. /adam/{k8s_utils → utils_k8s}/config_maps.py +0 -0
  226. /adam/{k8s_utils → utils_k8s}/volumes.py +0 -0
  227. {kaqing-2.0.14.dist-info → kaqing-2.0.189.dist-info}/WHEEL +0 -0
  228. {kaqing-2.0.14.dist-info → kaqing-2.0.189.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,203 @@
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)
195
+
196
+ def schedules_auto_completion(ids: callable):
197
+ auto = Config().get('reaper.schedules-auto-complete', 'off')
198
+
199
+ leaf = None
200
+ if auto == 'on':
201
+ leaf = {id: None for id in ids()}
202
+
203
+ return (leaf, auto == 'lazy')
@@ -1,15 +1,12 @@
1
1
  import click
2
2
 
3
- from adam.commands.command import Command
4
- from adam.commands.command_helpers import ClusterCommandHelper
3
+ from adam.commands.intermediate_command import IntermediateCommand
5
4
  from .repair_run import RepairRun
6
5
  from .repair_scan import RepairScan
7
6
  from .repair_stop import RepairStop
8
7
  from .repair_log import RepairLog
9
- from adam.repl_state import ReplState, RequiredState
10
- from adam.utils import lines_to_tabular, log, log2
11
8
 
12
- class Repair(Command):
9
+ class Repair(IntermediateCommand):
13
10
  COMMAND = 'repair'
14
11
 
15
12
  # the singleton pattern
@@ -18,51 +15,12 @@ class Repair(Command):
18
15
 
19
16
  return cls.instance
20
17
 
21
- def __init__(self, successor: Command=None):
22
- super().__init__(successor)
23
-
24
18
  def command(self):
25
19
  return Repair.COMMAND
26
20
 
27
- def required(self):
28
- return RequiredState.CLUSTER
29
-
30
- def run(self, cmd: str, state: ReplState):
31
- if not(args := self.args(cmd)):
32
- return super().run(cmd, state)
33
-
34
- state, args = self.apply_state(args, state)
35
- if not self.validate_state(state):
36
- return state
37
-
38
- if state.in_repl:
39
- log(lines_to_tabular([c.help(ReplState()) for c in Repair.cmd_list()], separator='\t'))
40
-
41
- return 'command-missing'
42
- else:
43
- # head with the Chain of Responsibility pattern
44
- cmds = Command.chain(Repair.cmd_list())
45
- if not cmds.run(cmd, state):
46
- log2('* Command is missing.')
47
- Command.display_help()
48
-
49
- def cmd_list():
21
+ def cmd_list(self):
50
22
  return [RepairRun(), RepairScan(), RepairStop(), RepairLog()]
51
23
 
52
- def completion(self, state: ReplState):
53
- if state.sts:
54
- return super().completion(state)
55
- return {}
56
-
57
- def help(self, _: ReplState):
58
- return None
59
-
60
24
  class RepairCommandHelper(click.Command):
61
25
  def get_help(self, ctx: click.Context):
62
- log(super().get_help(ctx))
63
- log()
64
- log('Sub-Commands:')
65
-
66
- log(lines_to_tabular([c.help(ReplState()).replace(f'{Repair.COMMAND} ', ' ', 1) for c in Repair.cmd_list()], separator='\t'))
67
- log()
68
- ClusterCommandHelper.cluster_help()
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'
@@ -1,12 +1,12 @@
1
1
  from adam.commands.command import Command
2
- from adam.k8s_utils.jobs import Jobs
3
- from adam.k8s_utils.volumes import Volumes
2
+ from adam.commands.reaper.utils_reaper import Reapers
3
+ from adam.utils_k8s.jobs import Jobs
4
+ from adam.utils_k8s.volumes import Volumes
4
5
  from adam.repl_state import ReplState, RequiredState
5
6
  from adam.config import Config
6
- from adam.commands.reaper.reaper_session import ReaperSession
7
7
  from adam.commands.reaper.reaper_runs_abort import ReaperRunsAbort
8
8
  from adam.commands.reaper.reaper_schedule_stop import ReaperScheduleStop
9
- from adam.utils import log2
9
+ from adam.utils import log2, log_exc
10
10
 
11
11
  class RepairRun(Command):
12
12
  COMMAND = 'repair run'
@@ -30,43 +30,36 @@ class RepairRun(Command):
30
30
  if not(args := self.args(cmd)):
31
31
  return super().run(cmd, state)
32
32
 
33
- state, args = self.apply_state(args, state)
34
- if not self.validate_state(state):
35
- return state
36
-
37
- replace = False
38
- if len(args) == 1:
39
- replace = args[0] == 'replace'
33
+ with self.validate(args, state) as (args, state):
34
+ replace = False
35
+ if len(args) == 1:
36
+ replace = args[0] == 'replace'
40
37
 
41
- log2("Stopping all reaper schedules...")
42
- reaper = ReaperSession.create(state)
43
- schedules = reaper.schedule_ids(state)
44
- for schedule_id in schedules:
45
- ReaperScheduleStop().run(f'reaper stop schedule {schedule_id}', state)
46
- log2("Aborting all reaper runs...")
47
- state = ReaperRunsAbort().run('reaper abort runs', state)
38
+ with log_exc():
39
+ log2("Stopping all reaper schedules...")
40
+ for schedule_id in Reapers.cached_schedule_ids(state):
41
+ ReaperScheduleStop().run(f'reaper stop schedule {schedule_id}', state)
42
+ log2("Aborting all reaper runs...")
43
+ state = ReaperRunsAbort().run('reaper abort runs', state)
48
44
 
49
- image = Config().get('repair.image', 'ci-registry.c3iot.io/cloudops/cassrepair:2.0.11')
50
- secret = Config().get('repair.secret', 'ciregistryc3iotio')
51
- log_path = Config().get('repair.log-path', '/home/cassrepair/logs/')
52
- user, _ = state.user_pass()
53
- ns = state.namespace
54
- env = Config().get('repair.env', {})
55
- env["cluster"] = ns
56
- env_from = {"username": user, "password": user}
57
- pvc_name ='cassrepair-log-' + state.sts
58
- Volumes.create_pvc(pvc_name, 30, ns)
59
- if replace:
60
- Jobs.delete('cassrepair-'+state.sts, ns)
61
- Jobs.create('cassrepair-'+state.sts, ns, image, secret, env, env_from, 'cassrepair', pvc_name, log_path)
45
+ image = Config().get('repair.image', 'ci-registry.c3iot.io/cloudops/cassrepair:2.0.11')
46
+ secret = Config().get('repair.secret', 'ciregistryc3iotio')
47
+ log_path = Config().get('repair.log-path', '/home/cassrepair/logs/')
48
+ user, _ = state.user_pass()
49
+ ns = state.namespace
50
+ env = Config().get('repair.env', {})
51
+ env["cluster"] = ns
52
+ env_from = {"username": user, "password": user}
53
+ pvc_name ='cassrepair-log-' + state.sts
54
+ Volumes.create_pvc(pvc_name, 30, ns)
55
+ if replace:
56
+ Jobs.delete('cassrepair-'+state.sts, ns)
57
+ Jobs.create('cassrepair-'+state.sts, ns, image, secret, env, env_from, 'cassrepair', pvc_name, log_path)
62
58
 
63
- return state
59
+ return state
64
60
 
65
61
  def completion(self, state: ReplState):
66
- if state.sts:
67
- return super().completion(state)
68
-
69
- return {}
62
+ return super().completion(state)
70
63
 
71
64
  def help(self, _: ReplState):
72
65
  return f'{RepairRun.COMMAND} [replace]\t start a repair job, default not replacing'
@@ -1,7 +1,5 @@
1
- import time
2
-
3
1
  from adam.commands.command import Command
4
- from adam.k8s_utils.pods import Pods
2
+ from adam.utils_k8s.pods import Pods
5
3
  from adam.repl_state import ReplState, RequiredState
6
4
  from adam.utils import log2
7
5
  from adam.config import Config
@@ -28,47 +26,41 @@ class RepairScan(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
- n = "7"
36
- if len(args) == 1:
37
- n = str(args[0])
38
- image = Config().get('repair.image', 'ci-registry.c3iot.io/cloudops/cassrepair:2.0.11')
39
- secret = Config().get('repair.secret', 'ciregistryc3iotio')
40
- log_path = secret = Config().get('repair.log-path', '/home/cassrepair/logs/')
41
- ns = state.namespace
42
- pvc_name ='cassrepair-log-' + state.sts
43
- pod_name = 'repair-scan'
44
-
45
- try:
46
- Pods.create(ns, pod_name, image, ["sh", "-c", "tail -f /dev/null"],
47
- secret=secret,
48
- env={},
49
- volume_name='cassrepair-log',
50
- pvc_name=pvc_name,
51
- mount_path='/home/cassrepair/logs/')
52
- except Exception as e:
53
- if e.status == 409:
54
- log2(f"Pod {pod_name} already exists")
55
- else:
56
- log2("Exception when calling BatchV1Apii->create_namespaced_job: %s\n" % e)
29
+ with self.validate(args, state) as (args, state):
30
+ n = "7"
31
+ if len(args) == 1:
32
+ n = str(args[0])
33
+ image = Config().get('repair.image', 'ci-registry.c3iot.io/cloudops/cassrepair:2.0.11')
34
+ secret = Config().get('repair.secret', 'ciregistryc3iotio')
35
+ log_path = secret = Config().get('repair.log-path', '/home/cassrepair/logs/')
36
+ ns = state.namespace
37
+ pvc_name ='cassrepair-log-' + state.sts
38
+ pod_name = 'repair-scan'
39
+
40
+ try:
41
+ Pods.create(ns, pod_name, image, ["sh", "-c", "tail -f /dev/null"],
42
+ secret=secret,
43
+ env={},
44
+ volume_name='cassrepair-log',
45
+ pvc_name=pvc_name,
46
+ mount_path='/home/cassrepair/logs/')
47
+ except Exception as e:
48
+ if e.status == 409:
49
+ log2(f"Pod {pod_name} already exists")
50
+ else:
51
+ log2("Exception when calling BatchV1Apii->create_namespaced_job: %s\n" % e)
52
+
53
+ Pods.wait_for_running(ns, pod_name, 'Waiting for the scanner pod to start up...')
54
+
55
+ try:
56
+ Pods.exec(pod_name, pod_name, ns, f"find {log_path} -type f -mtime -{n} -print0 | xargs -0 grep failed")
57
+ finally:
58
+ Pods.delete(pod_name, ns)
57
59
 
58
- Pods.wait_for_running(ns, pod_name, 'Waiting for the scanner pod to start up...')
59
-
60
- try:
61
- Pods.exec(pod_name, pod_name, ns, f"find {log_path} -type f -mtime -{n} -print0 | xargs -0 grep failed")
62
- finally:
63
- Pods.delete(pod_name, ns)
64
-
65
- return state
60
+ return state
66
61
 
67
62
  def completion(self, state: ReplState):
68
- if state.sts:
69
- return super().completion(state)
70
-
71
- return {}
63
+ return super().completion(state)
72
64
 
73
65
  def help(self, _: ReplState):
74
66
  return f'{RepairScan.COMMAND} [n]\t scan last n days repair log, default 7 days'
@@ -1,7 +1,6 @@
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
- from adam.config import Config
5
4
 
6
5
  class RepairStop(Command):
7
6
  COMMAND = 'repair stop'
@@ -25,20 +24,14 @@ class RepairStop(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
31
-
32
- ns = state.namespace
33
- Jobs.delete('cassrepair-'+state.sts, ns)
27
+ with self.validate(args, state) as (args, state):
28
+ ns = state.namespace
29
+ Jobs.delete('cassrepair-'+state.sts, ns)
34
30
 
35
- return state
31
+ return state
36
32
 
37
33
  def completion(self, state: ReplState):
38
- if state.sts:
39
- return super().completion(state)
40
-
41
- return {}
34
+ return super().completion(state)
42
35
 
43
36
  def help(self, _: ReplState):
44
37
  return f'{RepairStop.COMMAND}\t delete a repair job'
adam/commands/report.py CHANGED
@@ -22,36 +22,40 @@ class Report(Command):
22
22
  def command(self):
23
23
  return Report.COMMAND
24
24
 
25
+ def required(self):
26
+ return ReplState.NON_L
27
+
25
28
  def run(self, cmd: str, state: ReplState):
26
29
  if not(args := self.args(cmd)):
27
30
  return super().run(cmd, state)
28
31
 
29
- output: dict[str, any] = {}
30
- state, args = self.apply_state(args, state)
31
- if state.in_repl:
32
- args, show = Command.extract_options(args, ['-s', '--show'])
32
+ with self.validate(args, state) as (args, state):
33
+ output: dict[str, any] = {}
34
+
35
+ if state.in_repl:
36
+ args, show = Command.extract_options(args, ['-s', '--show'])
33
37
 
34
- args, redirect = Command.extract_options(args, ['>'])
35
- if not redirect or not args:
36
- log2('Please specify file name: e.g. report > /tmp/report.log')
37
- return 'no-report-destination'
38
+ args, redirect = Command.extract_options(args, ['>'])
39
+ if not redirect or not args:
40
+ log2('Please specify file name: e.g. report > /tmp/report.log')
41
+ return 'no-report-destination'
38
42
 
39
- results = run_checks(state.sts, state.namespace, state.pod, show_output=show)
40
- output = CheckResult.report(results)
41
- with open(args[0], "w") as json_file:
42
- json.dump(output, json_file, indent=2)
43
- log2(f'Report stored in {args[0]}.')
44
- else:
45
- args, show = Command.extract_options(args, ['-s', '--show'])
43
+ results = run_checks(state.sts, state.namespace, state.pod, show_out=show)
44
+ output = CheckResult.report(results)
45
+ with open(args[0], "w") as json_file:
46
+ json.dump(output, json_file, indent=2)
47
+ log2(f'Report stored in {args[0]}.')
48
+ else:
49
+ args, show = Command.extract_options(args, ['-s', '--show'])
46
50
 
47
- results = run_checks(state.sts, state.namespace, state.pod, show_output=show)
48
- output = CheckResult.report(results)
49
- click.echo(json.dumps(output, indent=2))
51
+ results = run_checks(state.sts, state.namespace, state.pod, show_out=show)
52
+ output = CheckResult.report(results)
53
+ click.echo(json.dumps(output, indent=2))
50
54
 
51
- return output
55
+ return output
52
56
 
53
- def completion(self, _: ReplState):
54
- return {Report.COMMAND: {">": None}}
57
+ def completion(self, state: ReplState):
58
+ return super().completion(state, {">": None})
55
59
 
56
60
  def help(self, _: ReplState):
57
61
  return f"{Report.COMMAND} > <file-name>\t generate report"
adam/commands/restart.py CHANGED
@@ -1,6 +1,7 @@
1
+ from adam.commands import extract_options
1
2
  from adam.commands.command import Command
2
- from adam.k8s_utils.pods import Pods
3
- from adam.k8s_utils.statefulsets import StatefulSets
3
+ from adam.utils_k8s.pods import Pods
4
+ from adam.utils_k8s.statefulsets import StatefulSets
4
5
  from adam.repl_state import ReplState, RequiredState
5
6
  from adam.utils import log2
6
7
 
@@ -26,34 +27,32 @@ class Restart(Command):
26
27
  if not(args := self.args(cmd)):
27
28
  return super().run(cmd, state)
28
29
 
29
- state, args = self.apply_state(args, state)
30
- if not self.validate_state(state):
31
- return state
32
-
33
- args, forced = Command.extract_options(args, '--force')
34
- if not args:
35
- if state.pod:
36
- log2(f'Restarting {state.pod}...')
37
- Pods.delete(state.pod, state.namespace)
38
- else:
39
- if not forced:
40
- log2('Please add --force for restarting all nodes in a cluster.')
41
- return 'force-needed'
42
-
43
- log2(f'Restarting all pods from {state.sts}...')
44
- for pod_name in StatefulSets.pod_names(state.sts, state.namespace):
45
- Pods.delete(pod_name, state.namespace)
46
- else:
47
- for arg in args:
48
- Pods.delete(arg, state.namespace)
49
-
50
- return state
30
+ with self.validate(args, state) as (args, state):
31
+ with extract_options(args, '--force') as (args, forced):
32
+ if not args:
33
+ if state.pod:
34
+ log2(f'Restarting {state.pod}...')
35
+ Pods.delete(state.pod, state.namespace)
36
+ else:
37
+ if not forced:
38
+ log2('Please add --force for restarting all nodes in a cluster.')
39
+ return 'force-needed'
40
+
41
+ log2(f'Restarting all pods from {state.sts}...')
42
+ for pod_name in StatefulSets.pod_names(state.sts, state.namespace):
43
+ Pods.delete(pod_name, state.namespace)
44
+ else:
45
+ for arg in args:
46
+ Pods.delete(arg, state.namespace)
47
+
48
+ return state
51
49
 
52
50
  def completion(self, state: ReplState):
53
- if state.pod:
54
- return {Restart.COMMAND: None}
55
- elif state.sts:
56
- return {Restart.COMMAND: {p: None for p in StatefulSets.pod_names(state.sts, state.namespace)}}
51
+ if super().completion(state):
52
+ if state.pod:
53
+ return {Restart.COMMAND: None}
54
+ elif state.sts:
55
+ return {Restart.COMMAND: {p: None for p in StatefulSets.pod_names(state.sts, state.namespace)}}
57
56
 
58
57
  return {}
59
58