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,240 +0,0 @@
1
- import functools
2
- import re
3
- import subprocess
4
-
5
- from adam.config import Config
6
- from adam.k8s_utils.kube_context import KubeContext
7
- from adam.k8s_utils.pods import Pods
8
- from adam.k8s_utils.secrets import Secrets
9
- from adam.utils import log2
10
-
11
- class PostgresSession:
12
- def __init__(self, ns: str, path: str):
13
- self.namespace = ns
14
- self.conn_details = None
15
- self.host = None
16
- self.db = None
17
-
18
- if path:
19
- tks = path.split('/')
20
- hn = tks[0].split('@')
21
- self.host = hn[0]
22
- if len(hn) > 1 and not ns:
23
- self.namespace = hn[1]
24
-
25
- if len(tks) > 1:
26
- self.db = tks[1]
27
-
28
- def find_namespace(self, arg: str):
29
- if arg:
30
- tks = arg.split('@')
31
- if len(tks) > 1:
32
- return tks[1]
33
-
34
- return None
35
-
36
- def directory(self, arg: str = None):
37
- if arg:
38
- if arg == '..':
39
- if self.db:
40
- self.db = None
41
- else:
42
- self.host = None
43
- else:
44
- tks = arg.split('@')
45
- arg = tks[0]
46
- if not self.host:
47
- self.host = arg
48
- else:
49
- self.db = arg
50
-
51
- if not self.host:
52
- return None
53
-
54
- d = self.host
55
- if not self.db:
56
- return d
57
-
58
- return f'{self.host}/{self.db}'
59
-
60
- def hosts(ns: str):
61
- return PostgresSession.hosts_for_namespace(ns)
62
-
63
- @functools.lru_cache()
64
- def hosts_for_namespace(ns: str):
65
- ss = Secrets.list_secrets(ns, name_pattern=Config().get('pg.name-pattern', '^{namespace}.*k8spg.*'))
66
-
67
- def excludes(name: str):
68
- exs = Config().get('pg.excludes', '.helm., -admin-secret')
69
- if exs:
70
- for ex in exs.split(','):
71
- if ex.strip(' ') in name:
72
- return True
73
-
74
- return False
75
-
76
- return [s for s in ss if not excludes(s)]
77
-
78
- def databases(self):
79
- dbs = []
80
- # List of databases
81
- # Name | Owner | Encoding | Collate | Ctype | ICU Locale | Locale Provider | Access privileges
82
- # ---------------------------------------+----------+----------+-------------+-------------+------------+-----------------+-----------------------
83
- # postgres | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | | libc |
84
- # stgawsscpsr_c3_c3 | postgres | UTF8 | C | C | | libc |
85
- # template1 | postgres | UTF8 | en_US.UTF-8 | en_US.UTF-8 | | libc | =c/postgres +
86
- # | | | | | | | postgres=CTc/postgres
87
- # (48 rows)
88
- if r := self.run_sql('\l', show_out=False):
89
- s = 0
90
- for line in r.stdout.split('\n'):
91
- line: str = line.strip(' \r')
92
- if s == 0:
93
- if 'List of databases' in line:
94
- s = 1
95
- elif s == 1:
96
- if 'Name' in line and 'Owner' in line and 'Encoding' in line:
97
- s = 2
98
- elif s == 2:
99
- if line.startswith('---------'):
100
- s = 3
101
- elif s == 3:
102
- groups = re.match(r'^\s*(\S*)\s*\|\s*(\S*)\s*\|.*', line)
103
- if groups and groups[1] != '|':
104
- dbs.append({'name': groups[1], 'owner': groups[2]})
105
-
106
- return dbs
107
-
108
- def tables(self):
109
- dbs = []
110
- # List of relations
111
- # Schema | Name | Type | Owner
112
- # ----------+------------------------------------------------------------+-------+---------------
113
- # postgres | c3_2_admin_aclpriv | table | postgres
114
- # postgres | c3_2_admin_aclpriv_a | table | postgres
115
- if r := self.run_sql('\dt', show_out=False):
116
- s = 0
117
- for line in r.stdout.split('\n'):
118
- line: str = line.strip(' \r')
119
- if s == 0:
120
- if 'List of relations' in line:
121
- s = 1
122
- elif s == 1:
123
- if 'Schema' in line and 'Name' in line and 'Type' in line:
124
- s = 2
125
- elif s == 2:
126
- if line.startswith('---------'):
127
- s = 3
128
- elif s == 3:
129
- groups = re.match(r'^\s*(\S*)\s*\|\s*(\S*)\s*\|.*', line)
130
- if groups and groups[1] != '|':
131
- dbs.append({'schema': groups[1], 'name': groups[2]})
132
-
133
- return dbs
134
-
135
- def run_sql(self, sql: str, show_out = True):
136
- db = self.db if self.db else PostgresSession.default_db()
137
-
138
- if KubeContext.in_cluster():
139
- cmd1 = f'env PGPASSWORD={self.password()} psql -h {self.endpoint()} -p {self.port()} -U {self.username()} {db} --pset pager=off -c'
140
- log2(f'{cmd1} "{sql}"')
141
- # remove double quotes from the sql argument
142
- cmd = cmd1.split(' ') + [sql]
143
- r = subprocess.run(cmd, capture_output=True, text=True)
144
- if show_out:
145
- log2(r.stdout)
146
- log2(r.stderr)
147
-
148
- return r
149
- else:
150
- ns = self.namespace
151
- pod_name = Config().get('pg.agent.name', 'ops-pg-agent')
152
-
153
- if Config().get('pg.agent.just-in-time', False):
154
- if not PostgresSession.deploy_pg_agent(pod_name, ns):
155
- return
156
-
157
- real_pod_name = pod_name
158
- try:
159
- # try with dedicated pg agent pod name configured
160
- Pods.get(ns, pod_name)
161
- except:
162
- try:
163
- # try with the ops pod
164
- pod_name = Config().get('pod.name', 'ops')
165
- real_pod_name = Pods.get_with_selector(ns, label_selector = Config().get('pod.label-selector', 'run=ops')).metadata.name
166
- except:
167
- log2(f"Could not locate {pod_name} pod.")
168
- return None
169
-
170
- cmd = f'PGPASSWORD="{self.password()}" psql -h {self.endpoint()} -p {self.port()} -U {self.username()} {db} --pset pager=off -c "{sql}"'
171
-
172
- return Pods.exec(real_pod_name, pod_name, ns, cmd, show_out=show_out)
173
-
174
- def deploy_pg_agent(pod_name: str, ns: str) -> str:
175
- image = Config().get('pg.agent.image', 'seanahnsf/kaqing')
176
- timeout = Config().get('pg.agent.timeout', 3600)
177
- try:
178
- Pods.create(ns, pod_name, image, ['sleep', f'{timeout}'], env={'NAMESPACE': ns}, sa_name='c3')
179
- except Exception as e:
180
- if e.status == 409:
181
- if Pods.completed(ns, pod_name):
182
- try:
183
- Pods.delete(pod_name, ns)
184
- Pods.create(ns, pod_name, image, ['sleep', f'{timeout}'], env={'NAMESPACE': ns}, sa_name='c3')
185
- except Exception as e2:
186
- log2("Exception when calling BatchV1Api->create_pod: %s\n" % e2)
187
-
188
- return
189
- else:
190
- log2("Exception when calling BatchV1Api->create_pod: %s\n" % e)
191
-
192
- return
193
-
194
- Pods.wait_for_running(ns, pod_name)
195
-
196
- return pod_name
197
-
198
- def undeploy_pg_agent(pod_name: str, ns: str):
199
- Pods.delete(pod_name, ns, grace_period_seconds=0)
200
-
201
- def endpoint(self):
202
- if not self.conn_details:
203
- self.conn_details = Secrets.get_data(self.namespace, self.host)
204
-
205
- endpoint_key = Config().get('pg.secret.endpoint-key', 'postgres-db-endpoint')
206
-
207
- return self.conn_details[endpoint_key] if endpoint_key in self.conn_details else ''
208
-
209
- def port(self):
210
- if not self.conn_details:
211
- self.conn_details = Secrets.get_data(self.namespace, self.host)
212
-
213
- port_key = Config().get('pg.secret.port-key', 'postgres-db-port')
214
-
215
- return self.conn_details[port_key] if port_key in self.conn_details else ''
216
-
217
- def username(self):
218
- if not self.conn_details:
219
- self.conn_details = Secrets.get_data(self.namespace, self.host)
220
-
221
- username_key = Config().get('pg.secret.username-key', 'postgres-admin-username')
222
-
223
- return self.conn_details[username_key] if username_key in self.conn_details else ''
224
-
225
- def password(self):
226
- if not self.conn_details:
227
- self.conn_details = Secrets.get_data(self.namespace, self.host)
228
-
229
- password_key = Config().get('pg.secret.password-key', 'postgres-admin-password')
230
-
231
- return self.conn_details[password_key] if password_key in self.conn_details else ''
232
-
233
- def default_db():
234
- return Config().get('pg.default-db', 'postgres')
235
-
236
- def default_owner():
237
- return Config().get('pg.default-owner', 'postgres')
238
-
239
- def default_schema():
240
- return Config().get('pg.default-schema', 'postgres')
@@ -1,31 +0,0 @@
1
- import functools
2
-
3
- from adam.commands.postgres.postgres_session import PostgresSession
4
- from adam.config import Config
5
-
6
- TestPG = [False]
7
-
8
- @functools.lru_cache()
9
- def pg_database_names(ns: str, pg_path: str):
10
- if TestPG[0]:
11
- return ['azops88_c3ai_c3']
12
-
13
- Config().wait_log('Inspecting Postgres Databases...')
14
-
15
- pg = PostgresSession(ns, pg_path)
16
- return [db['name'] for db in pg.databases() if db['owner'] == PostgresSession.default_owner()]
17
-
18
- @functools.lru_cache()
19
- def pg_table_names(ns: str, pg_path: str):
20
- if TestPG[0]:
21
- return ['C3_2_XYZ1']
22
-
23
- Config().wait_log('Inspecting Postgres Database...')
24
- return [table['name'] for table in pg_tables(ns, pg_path) if table['schema'] == PostgresSession.default_schema()]
25
-
26
- def pg_tables(ns: str, pg_path: str):
27
- pg = PostgresSession(ns, pg_path)
28
- if pg.db:
29
- return pg.tables()
30
-
31
- return []
@@ -1,11 +0,0 @@
1
- from adam.commands.postgres.postgres_utils import pg_table_names
2
- from adam.sql.term_completer import TermCompleter
3
-
4
- class PsqlTableNameCompleter(TermCompleter):
5
- def __init__(self, namespace: str, pg_path: str, ignore_case: bool = True):
6
- super().__init__(pg_table_names(namespace, pg_path), ignore_case=ignore_case)
7
- self.namespace = namespace
8
- self.pg_path = pg_path
9
-
10
- def __repr__(self) -> str:
11
- return "PsqlTableCompleter(%r, pg_path=%r)" % (self.namespace, self.pg_path)
@@ -1,159 +0,0 @@
1
- from collections.abc import Callable
2
- import threading
3
- from kubernetes import client
4
- import portforward
5
- import re
6
- import requests
7
- from typing import List, cast
8
-
9
- from adam.config import Config
10
- from adam.k8s_utils.kube_context import KubeContext
11
- from adam.repl_state import ReplState
12
- from adam.utils import lines_to_tabular, log2
13
-
14
- class ReaperSession:
15
- is_forwarding = False
16
- stopping = threading.Event()
17
- schedules_ids_by_cluster: dict[str, list[str]] = {}
18
-
19
- def __init__(self, pod: str, headers: dict[str, str] = None):
20
- self.pod = pod
21
- self.headers = headers
22
-
23
- def login(self, state: ReplState, local_addr: str, remote_addr: str, show_output = True) -> str :
24
- user, pw = state.user_pass(secret_path='reaper.secret')
25
-
26
- response = requests.post(f'http://{local_addr}/login', headers={
27
- 'Accept': '*'
28
- },data={
29
- 'username':user,
30
- 'password':pw})
31
- if show_output:
32
- log2(f'POST {remote_addr}/login')
33
- log2(f' username={user}&password={pw}')
34
-
35
- if int(response.status_code / 100) != 2:
36
- if show_output:
37
- log2("login failed")
38
- return None
39
-
40
- return response.headers['Set-Cookie']
41
-
42
- def port_forwarded(self, state: ReplState, path: str, body: Callable[[str, dict[str, str]], requests.Response], method: str = None, show_output = True):
43
- local_port = Config().get('reaper.port-forward.local-port', 9001)
44
- target_port = 8080
45
-
46
- def f(local_addr: str, remote_addr: str):
47
- if not self.headers:
48
- self.headers = self.cookie_header(state, local_addr, remote_addr, show_output=show_output)
49
-
50
- if show_output and method:
51
- log2(f'{method} {remote_addr}/{path}')
52
- response = body(f'http://{local_addr}/{path}', self.headers)
53
-
54
- if response:
55
- if int(response.status_code / 100) != 2:
56
- if show_output:
57
- log2(response.status_code)
58
- return response
59
-
60
- if show_output:
61
- log2()
62
-
63
- return response if response else 'no-response'
64
-
65
- if KubeContext.in_cluster():
66
- # cs-a526330d23-cs-a526330d23-default-sts-0 ->
67
- # curl http://cs-a526330d23-cs-a526330d23-reaper-service.stgawsscpsr.svc.cluster.local:8080
68
- groups = re.match(r'^(.*?-.*?-.*?-.*?-).*', state.sts)
69
- if groups:
70
- svc_name = Config().get('reaper.service-name', 'reaper-service')
71
- svc = f'{groups[1]}{svc_name}.{state.namespace}.svc.cluster.local:{target_port}'
72
- return f(local_addr=svc, remote_addr=svc)
73
- else:
74
- return None
75
- else:
76
- with portforward.forward(state.namespace, self.pod, local_port, target_port):
77
- return f(local_addr=f'localhost:{local_port}', remote_addr=f'{self.pod}:{target_port}')
78
-
79
- def cookie_header(self, state: ReplState, local_addr, remote_addr, show_output = True):
80
- return {'Cookie': self.login(state, local_addr, remote_addr, show_output=show_output)}
81
-
82
- def create(state: ReplState) -> 'ReaperSession':
83
- pods = ReaperSession.list_reaper_pods(state.sts if state.sts else state.pod, state.namespace)
84
- if pods:
85
- return ReaperSession(pods[0].metadata.name)
86
- else:
87
- log2('No reaper found.')
88
-
89
- return None
90
-
91
- def list_reaper_pods(sts_name: str, namespace: str) -> List[client.V1Pod]:
92
- v1 = client.CoreV1Api()
93
-
94
- # k8ssandra.io/reaper: cs-d0767a536f-cs-d0767a536f-reaper
95
- groups = re.match(Config().get('reaper.pod.cluster-regex', r'(.*?-.*?-.*?-.*?)-.*'), sts_name)
96
- label_selector = Config().get('reaper.pod.label-selector', 'k8ssandra.io/reaper={cluster}-reaper').replace('{cluster}', groups[1])
97
-
98
- return cast(List[client.V1Pod], v1.list_namespaced_pod(namespace, label_selector=label_selector).items)
99
-
100
- def show_schedules(self, state: ReplState, filter: Callable[[list[dict]], dict] = None):
101
- schedules = self.list_schedules(state, filter=filter)
102
- # forced refresh of schedule list
103
- if not filter:
104
- self.schedules_ids_by_cluster[state.sts] = [schedule['id'] for schedule in schedules]
105
- self.show_schedules_tabular(schedules)
106
-
107
- def schedule_ids(self, state: ReplState, show_output = True, filter: Callable[[list[dict]], dict] = None):
108
- schedules = self.list_schedules(state, show_output=show_output, filter=filter)
109
- return [schedule['id'] for schedule in schedules]
110
-
111
- def list_schedules(self, state: ReplState, show_output = True, filter: Callable[[list[dict]], dict] = None) -> list[dict]:
112
- def body(uri: str, headers: dict[str, str]):
113
- return requests.get(uri, headers=headers)
114
-
115
- response = self.port_forwarded(state, 'repair_schedule', body, method='GET', show_output=show_output)
116
- if not response:
117
- return
118
-
119
- res = response.json()
120
- if filter:
121
- res = filter(res)
122
-
123
- return res
124
-
125
- def show_schedules_tabular(self, schedules: list[dict]):
126
- log2(lines_to_tabular([f"{schedule['id']} {schedule['state']} {schedule['cluster_name']} {schedule['keyspace_name']}" for schedule in schedules], 'ID STATE CLUSTER KEYSPACE'))
127
-
128
- def show_schedule(self, state: ReplState, schedule_id: str):
129
- def filter(schedules: list[dict]):
130
- return [schedule for schedule in schedules if schedule['id'] == schedule_id]
131
-
132
- self.show_schedules(state, filter)
133
-
134
- def reaper_spec(self, state: ReplState) -> dict[str, any]:
135
- user, pw = state.user_pass(secret_path='reaper.secret')
136
- local_port = Config().get('reaper.port-forward.local-port', 9001)
137
-
138
- return {
139
- 'pod': self.pod,
140
- 'exec': f'kubectl exec -it {self.pod} -n {state.namespace} -- bash',
141
- 'forward': f'kubectl port-forward pods/{self.pod} -n {state.namespace} {local_port}:8080',
142
- 'web-uri': f'http://localhost:{local_port}/webui',
143
- 'username': user,
144
- 'password': pw
145
- }
146
-
147
- def cached_schedule_ids(state: ReplState) -> list[str]:
148
- if state.sts in ReaperSession.schedules_ids_by_cluster:
149
- return ReaperSession.schedules_ids_by_cluster[state.sts]
150
-
151
- if reaper := ReaperSession.create(state):
152
- Config().wait_log('Inspecting Cassandra Reaper...')
153
-
154
- schedules = reaper.schedule_ids(state, show_output = False)
155
- ReaperSession.schedules_ids_by_cluster[state.sts] = schedules
156
-
157
- return schedules
158
-
159
- return []
@@ -1,53 +0,0 @@
1
- from adam.app_session import AppSession
2
- from adam.apps import AppAction, Apps
3
- from adam.commands.command import Command
4
- from adam.config import Config
5
- from adam.repl_state import ReplState
6
- from adam.utils import lines_to_tabular, log
7
-
8
- class ShowAppActions(Command):
9
- COMMAND = 'show app actions'
10
-
11
- # the singleton pattern
12
- def __new__(cls, *args, **kwargs):
13
- if not hasattr(cls, 'instance'): cls.instance = super(ShowAppActions, cls).__new__(cls)
14
-
15
- return cls.instance
16
-
17
- def __init__(self, successor: Command=None):
18
- super().__init__(successor)
19
-
20
- def command(self):
21
- return ShowAppActions.COMMAND
22
-
23
- def run(self, cmd: str, state: ReplState):
24
- if not self.args(cmd):
25
- return super().run(cmd, state)
26
-
27
- lines = []
28
- for typ in Apps().app_types():
29
- for action in typ.actions:
30
- a: AppAction = action
31
- args = ','.join(a.arguments())
32
- if args:
33
- line = f'{typ.name}.{a.name},{args}'
34
- else:
35
- line = f'{typ.name}.{a.name},'
36
- if a.help:
37
- line = f'{line},{a.help}'
38
- lines.append(line)
39
- log(lines_to_tabular(lines, 'ACTION,ARGS,DESCRIPTION', separator=','))
40
- log()
41
-
42
- app_session: AppSession = AppSession.create(state.app_env or 'c3', state.app_app or 'c3')
43
- endpoint = Config().get('app.console-endpoint', 'https://{host}/{env}/{app}/static/console/index.html')
44
- endpoint = endpoint.replace('{host}', app_session.host).replace('{env}', app_session.env).replace('{app}', state.app_app or 'c3')
45
- log(lines_to_tabular([f'CONSOLE:,{endpoint}'], separator=','))
46
-
47
- return lines
48
-
49
- def completion(self, state: ReplState):
50
- return super().completion(state)
51
-
52
- def help(self, _: ReplState):
53
- return f"{ShowAppActions.COMMAND}\t show app actions"
@@ -1,47 +0,0 @@
1
- from adam.commands.command import Command
2
- from adam.k8s_utils.cassandra_clusters import CassandraClusters
3
- from adam.k8s_utils.cassandra_nodes import CassandraNodes
4
- from adam.repl_state import ReplState, RequiredState
5
-
6
- class ShowRepairs(Command):
7
- COMMAND = 'show cassandra repairs'
8
-
9
- # the singleton pattern
10
- def __new__(cls, *args, **kwargs):
11
- if not hasattr(cls, 'instance'): cls.instance = super(ShowRepairs, cls).__new__(cls)
12
-
13
- return cls.instance
14
-
15
- def __init__(self, successor: Command=None):
16
- super().__init__(successor)
17
-
18
- def command(self):
19
- return ShowRepairs.COMMAND
20
-
21
- def required(self):
22
- return RequiredState.CLUSTER_OR_POD
23
-
24
- def run(self, cmd: str, state: ReplState):
25
- if not(args := self.args(cmd)):
26
- return super().run(cmd, state)
27
-
28
- state, _ = state.apply_args(args)
29
- if not self.validate_state(state):
30
- return state
31
-
32
- user, pw = state.user_pass()
33
- command = f"nodetool -u {user} -pw {pw} repair_admin list"
34
-
35
- if state.pod:
36
- return CassandraNodes.exec(state.pod, state.namespace, command)
37
- else:
38
- return CassandraClusters.exec(state.sts, state.namespace, command, action='nodetool')
39
-
40
- def completion(self, state: ReplState):
41
- if state.sts:
42
- return super().completion(state)
43
-
44
- return {}
45
-
46
- def help(self, _: ReplState):
47
- return f'{ShowRepairs.COMMAND}\t show Cassandra repairs'
@@ -1,35 +0,0 @@
1
- from collections.abc import Callable
2
- from concurrent.futures import ThreadPoolExecutor
3
- import sys
4
- from typing import TypeVar
5
-
6
- from adam.config import Config
7
- from adam.k8s_utils.cassandra_nodes import CassandraNodes
8
- from adam.pod_exec_result import PodExecResult
9
- from adam.utils import log2
10
- from .statefulsets import StatefulSets
11
- from .pods import Pods
12
- from .kube_context import KubeContext
13
-
14
- T = TypeVar('T')
15
-
16
- # utility collection on cassandra clusters; methods are all static
17
- class CassandraClusters:
18
- def exec(statefulset: str, namespace: str, command: str, action: str = 'action', max_workers=0, show_out=True, on_any = False) -> list[PodExecResult]:
19
- def body(executor: ThreadPoolExecutor, pod: str, namespace: str, show_out: bool):
20
- if executor:
21
- return executor.submit(CassandraNodes.exec, pod, namespace, command, False, False,)
22
-
23
- return CassandraNodes.exec(pod, namespace, command, show_out=show_out)
24
-
25
- def post(result, show_out: bool):
26
- if KubeContext.show_out(show_out):
27
- print(result.command)
28
- if result.stdout:
29
- print(result.stdout)
30
- if result.stderr:
31
- log2(result.stderr, file=sys.stderr)
32
-
33
- return result
34
-
35
- return StatefulSets.on_cluster(statefulset, namespace, body, post=post, action=action, max_workers=max_workers, show_out=show_out, on_any=on_any)
adam/sql/sql_utils.py DELETED
@@ -1,5 +0,0 @@
1
- import re
2
-
3
- def safe_terms(text: str):
4
- tokens = re.findall(r'"[^"]+"|\b\w+\b|\S', text)
5
- return tokens, len(tokens) > 1 or text.startswith(' ') or text.endswith(' ')