kaqing 2.0.14__py3-none-any.whl → 2.0.145__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 (163) hide show
  1. adam/apps.py +2 -2
  2. adam/batch.py +13 -3
  3. adam/checks/check_utils.py +4 -4
  4. adam/checks/compactionstats.py +1 -1
  5. adam/checks/cpu.py +2 -2
  6. adam/checks/disk.py +1 -1
  7. adam/checks/gossip.py +1 -1
  8. adam/checks/memory.py +3 -3
  9. adam/checks/status.py +1 -1
  10. adam/commands/alter_tables.py +81 -0
  11. adam/commands/app.py +3 -3
  12. adam/commands/app_ping.py +2 -2
  13. adam/commands/audit/audit.py +86 -0
  14. adam/commands/audit/audit_repair_tables.py +77 -0
  15. adam/commands/audit/audit_run.py +58 -0
  16. adam/commands/audit/show_last10.py +51 -0
  17. adam/commands/audit/show_slow10.py +50 -0
  18. adam/commands/audit/show_top10.py +48 -0
  19. adam/commands/audit/utils_show_top10.py +59 -0
  20. adam/commands/bash/__init__.py +0 -0
  21. adam/commands/bash/bash.py +133 -0
  22. adam/commands/bash/bash_completer.py +93 -0
  23. adam/commands/cat.py +56 -0
  24. adam/commands/cd.py +12 -82
  25. adam/commands/check.py +6 -0
  26. adam/commands/cli_commands.py +3 -3
  27. adam/commands/code.py +60 -0
  28. adam/commands/command.py +48 -12
  29. adam/commands/commands_utils.py +4 -5
  30. adam/commands/cql/__init__.py +0 -0
  31. adam/commands/cql/cql_completions.py +28 -0
  32. adam/commands/cql/cql_utils.py +209 -0
  33. adam/commands/{cqlsh.py → cql/cqlsh.py} +15 -10
  34. adam/commands/deploy/code_utils.py +2 -2
  35. adam/commands/deploy/deploy.py +8 -21
  36. adam/commands/deploy/deploy_frontend.py +1 -1
  37. adam/commands/deploy/deploy_pg_agent.py +3 -3
  38. adam/commands/deploy/deploy_pod.py +28 -27
  39. adam/commands/deploy/deploy_utils.py +16 -26
  40. adam/commands/deploy/undeploy.py +8 -21
  41. adam/commands/deploy/undeploy_frontend.py +1 -1
  42. adam/commands/deploy/undeploy_pg_agent.py +5 -3
  43. adam/commands/deploy/undeploy_pod.py +12 -10
  44. adam/commands/devices/__init__.py +0 -0
  45. adam/commands/devices/device.py +27 -0
  46. adam/commands/devices/device_app.py +146 -0
  47. adam/commands/devices/device_auit_log.py +43 -0
  48. adam/commands/devices/device_cass.py +145 -0
  49. adam/commands/devices/device_export.py +86 -0
  50. adam/commands/devices/device_postgres.py +109 -0
  51. adam/commands/devices/devices.py +25 -0
  52. adam/commands/export/__init__.py +0 -0
  53. adam/commands/export/clean_up_export_session.py +53 -0
  54. adam/commands/export/clean_up_export_sessions.py +40 -0
  55. adam/commands/export/drop_export_database.py +58 -0
  56. adam/commands/export/drop_export_databases.py +46 -0
  57. adam/commands/export/export.py +83 -0
  58. adam/commands/export/export_databases.py +170 -0
  59. adam/commands/export/export_select.py +85 -0
  60. adam/commands/export/export_select_x.py +54 -0
  61. adam/commands/export/export_use.py +55 -0
  62. adam/commands/export/exporter.py +364 -0
  63. adam/commands/export/import_session.py +68 -0
  64. adam/commands/export/importer.py +67 -0
  65. adam/commands/export/importer_athena.py +80 -0
  66. adam/commands/export/importer_sqlite.py +47 -0
  67. adam/commands/export/show_column_counts.py +63 -0
  68. adam/commands/export/show_export_databases.py +39 -0
  69. adam/commands/export/show_export_session.py +51 -0
  70. adam/commands/export/show_export_sessions.py +47 -0
  71. adam/commands/export/utils_export.py +291 -0
  72. adam/commands/help.py +12 -7
  73. adam/commands/issues.py +6 -0
  74. adam/commands/kubectl.py +41 -0
  75. adam/commands/login.py +7 -4
  76. adam/commands/logs.py +2 -1
  77. adam/commands/ls.py +4 -107
  78. adam/commands/medusa/medusa.py +2 -26
  79. adam/commands/medusa/medusa_backup.py +2 -2
  80. adam/commands/medusa/medusa_restore.py +3 -4
  81. adam/commands/medusa/medusa_show_backupjobs.py +4 -3
  82. adam/commands/medusa/medusa_show_restorejobs.py +3 -3
  83. adam/commands/nodetool.py +9 -4
  84. adam/commands/param_set.py +1 -1
  85. adam/commands/postgres/postgres.py +42 -43
  86. adam/commands/postgres/{postgres_session.py → postgres_context.py} +43 -42
  87. adam/commands/postgres/postgres_utils.py +31 -0
  88. adam/commands/postgres/psql_completions.py +10 -0
  89. adam/commands/preview_table.py +18 -40
  90. adam/commands/pwd.py +2 -28
  91. adam/commands/reaper/reaper.py +4 -24
  92. adam/commands/reaper/reaper_restart.py +1 -1
  93. adam/commands/reaper/reaper_session.py +2 -2
  94. adam/commands/repair/repair.py +3 -27
  95. adam/commands/repair/repair_log.py +1 -1
  96. adam/commands/repair/repair_run.py +2 -2
  97. adam/commands/repair/repair_scan.py +1 -1
  98. adam/commands/repair/repair_stop.py +1 -1
  99. adam/commands/report.py +6 -0
  100. adam/commands/restart.py +2 -2
  101. adam/commands/rollout.py +1 -1
  102. adam/commands/show/show.py +11 -26
  103. adam/commands/show/show_app_actions.py +3 -0
  104. adam/commands/show/show_app_id.py +1 -1
  105. adam/commands/show/show_app_queues.py +3 -2
  106. adam/commands/show/show_cassandra_status.py +3 -3
  107. adam/commands/show/show_cassandra_version.py +3 -3
  108. adam/commands/show/show_host.py +33 -0
  109. adam/commands/show/show_login.py +3 -0
  110. adam/commands/show/show_processes.py +1 -1
  111. adam/commands/show/show_repairs.py +2 -2
  112. adam/commands/show/show_storage.py +1 -1
  113. adam/commands/watch.py +1 -1
  114. adam/config.py +16 -3
  115. adam/embedded_params.py +1 -1
  116. adam/pod_exec_result.py +10 -2
  117. adam/repl.py +127 -117
  118. adam/repl_commands.py +51 -16
  119. adam/repl_state.py +276 -55
  120. adam/sql/__init__.py +0 -0
  121. adam/sql/sql_completer.py +120 -0
  122. adam/sql/sql_state_machine.py +617 -0
  123. adam/sql/term_completer.py +76 -0
  124. adam/sso/authn_ad.py +1 -1
  125. adam/sso/cred_cache.py +1 -1
  126. adam/sso/idp.py +1 -1
  127. adam/utils.py +83 -2
  128. adam/utils_athena.py +145 -0
  129. adam/utils_audits.py +102 -0
  130. adam/utils_k8s/__init__.py +0 -0
  131. adam/utils_k8s/app_clusters.py +33 -0
  132. adam/utils_k8s/app_pods.py +31 -0
  133. adam/{k8s_utils → utils_k8s}/cassandra_clusters.py +6 -21
  134. adam/{k8s_utils → utils_k8s}/cassandra_nodes.py +12 -5
  135. adam/{k8s_utils → utils_k8s}/deployment.py +2 -2
  136. adam/{k8s_utils → utils_k8s}/kube_context.py +1 -1
  137. adam/{k8s_utils → utils_k8s}/pods.py +119 -26
  138. adam/{k8s_utils → utils_k8s}/secrets.py +4 -0
  139. adam/{k8s_utils → utils_k8s}/statefulsets.py +5 -4
  140. adam/utils_net.py +24 -0
  141. adam/utils_repl/__init__.py +0 -0
  142. adam/utils_repl/automata_completer.py +48 -0
  143. adam/utils_repl/repl_completer.py +46 -0
  144. adam/utils_repl/state_machine.py +173 -0
  145. adam/utils_sqlite.py +101 -0
  146. adam/version.py +1 -1
  147. {kaqing-2.0.14.dist-info → kaqing-2.0.145.dist-info}/METADATA +1 -1
  148. kaqing-2.0.145.dist-info/RECORD +227 -0
  149. adam/commands/bash.py +0 -87
  150. adam/commands/cql_utils.py +0 -53
  151. adam/commands/devices.py +0 -89
  152. kaqing-2.0.14.dist-info/RECORD +0 -167
  153. /adam/{k8s_utils → commands/audit}/__init__.py +0 -0
  154. /adam/{k8s_utils → utils_k8s}/config_maps.py +0 -0
  155. /adam/{k8s_utils → utils_k8s}/custom_resources.py +0 -0
  156. /adam/{k8s_utils → utils_k8s}/ingresses.py +0 -0
  157. /adam/{k8s_utils → utils_k8s}/jobs.py +0 -0
  158. /adam/{k8s_utils → utils_k8s}/service_accounts.py +0 -0
  159. /adam/{k8s_utils → utils_k8s}/services.py +0 -0
  160. /adam/{k8s_utils → utils_k8s}/volumes.py +0 -0
  161. {kaqing-2.0.14.dist-info → kaqing-2.0.145.dist-info}/WHEEL +0 -0
  162. {kaqing-2.0.14.dist-info → kaqing-2.0.145.dist-info}/entry_points.txt +0 -0
  163. {kaqing-2.0.14.dist-info → kaqing-2.0.145.dist-info}/top_level.txt +0 -0
adam/embedded_params.py CHANGED
@@ -1,2 +1,2 @@
1
1
  def config():
2
- return {'app': {'console-endpoint': 'https://{host}/{env}/{app}/static/console/index.html', 'cr': {'cluster-regex': '(.*?-.*?)-.*', 'group': 'ops.c3.ai', 'v': 'v2', 'plural': 'c3cassandras'}, 'label': 'c3__app_id-0', 'login': {'admin-group': '{host}/C3.ClusterAdmin', 'ingress': '{app_id}-k8singr-appleader-001', 'timeout': 5, 'session-check-url': 'https://{host}/{env}/{app}/api/8/C3/userSessionToken', 'cache-creds': True, 'cache-username': True, 'url': 'https://{host}/{env}/{app}', 'another': "You're logged in to {has}. However, for this app, you need to log in to {need}.", 'token-server-url': 'http://localhost:{port}', 'password-max-length': 128}, 'strip': '0'}, 'bash': {'workers': 32}, 'cassandra': {'service-name': 'all-pods-service'}, 'cql': {'workers': 32, 'samples': 3, 'secret': {'cluster-regex': '(.*?-.*?)-.*', 'name': '{cluster}-superuser', 'password-item': 'password'}}, 'checks': {'compactions-threshold': 250, 'cpu-busy-threshold': 98.0, 'cpu-threshold': 0.0, 'cassandra-data-path': '/c3/cassandra', 'root-disk-threshold': 50, 'cassandra-disk-threshold': 50, 'snapshot-size-cmd': "ls /c3/cassandra/data/data/*/*/snapshots | grep snapshots | sed 's/:$//g' | xargs -I {} du -sk {} | awk '{print $1}' | awk '{s+=$1} END {print s}'", 'snapshot-size-threshold': '40G', 'table-sizes-cmd': "ls -Al /c3/cassandra/data/data/ | awk '{print $9}' | sed 's/\\^r//g' | xargs -I {} du -sk /c3/cassandra/data/data/{}"}, 'get-host-id': {'workers': 32}, 'idps': {'ad': {'email-pattern': '.*@c3.ai', 'uri': 'https://login.microsoftonline.com/53ad779a-93e7-485c-ba20-ac8290d7252b/oauth2/v2.0/authorize?response_type=id_token&response_mode=form_post&client_id=00ff94a8-6b0a-4715-98e0-95490012d818&scope=openid+email+profile&redirect_uri=https%3A%2F%2Fplat.c3ci.cloud%2Fc3%2Fc3%2Foidc%2Flogin&nonce={nonce}&state=EMPTY', 'jwks-uri': 'https://login.microsoftonline.com/common/discovery/keys', 'contact': 'Please contact ted.tran@c3.ai.', 'whitelist-file': '/kaqing/members'}, 'okta': {'default': True, 'email-pattern': '.*@c3iot.com', 'uri': 'https://c3energy.okta.com/oauth2/v1/authorize?response_type=id_token&response_mode=form_post&client_id={client_id}&scope=openid+email+profile+groups&redirect_uri=https%3A%2F%2F{host}%2Fc3%2Fc3%2Foidc%2Flogin&nonce={nonce}&state=EMPTY', 'jwks-uri': 'https://c3energy.okta.com/oauth2/v1/keys'}}, 'issues': {'workers': 32}, 'logs': {'path': '/c3/cassandra/logs/system.log'}, 'medusa': {'restore-auto-complete': False}, 'nodetool': {'workers': 32, 'samples': 3, 'commands_in_line': 40}, 'pg': {'name-pattern': '^{namespace}.*-k8spg-.*', 'excludes': '.helm., -admin-secret', 'agent': {'name': 'ops-pg-agent', 'just-in-time': False, 'timeout': 86400, 'image': 'seanahnsf/kaqing'}, 'default-db': 'postgres', 'default-schema': 'postgres', 'secret': {'endpoint-key': 'postgres-db-endpoint', 'port-key': 'postgres-db-port', 'username-key': 'postgres-admin-username', 'password-key': 'postgres-admin-password'}}, 'pod': {'name': 'ops', 'image': 'seanahnsf/kaqing-cloud', 'sa': {'name': 'ops', 'proto': 'c3', 'additional-cluster-roles': 'c3aiops-k8ssandra-operator'}, 'label-selector': 'run=ops'}, 'preview': {'rows': 10}, 'processes': {'columns': 'pod,cpu,mem', 'header': 'POD_NAME,CPU,MEM/LIMIT'}, 'reaper': {'service-name': 'reaper-service', 'port-forward': {'timeout': 86400, 'local-port': 9001}, 'abort-runs-batch': 10, 'show-runs-batch': 100, 'pod': {'cluster-regex': '(.*?-.*?-.*?-.*?)-.*', 'label-selector': 'k8ssandra.io/reaper={cluster}-reaper'}, 'secret': {'cluster-regex': '(.*?-.*?)-.*', 'name': '{cluster}-reaper-ui', 'password-item': 'password'}}, 'repair': {'log-path': '/home/cassrepair/logs/', 'image': 'ci-registry.c3iot.io/cloudops/cassrepair:2.0.13', 'secret': 'ciregistryc3iotio', 'env': {'interval': 24, 'timeout': 60, 'pr': False, 'runs': 1}}, 'repl': {'start-drive': 'a', 'auto-enter-app': 'c3/c3', 'auto-enter-only-cluster': True}, 'status': {'columns': 'status,address,load,tokens,owns,host_id,gossip,compactions', 'header': '--,Address,Load,Tokens,Owns,Host ID,GOSSIP,COMPACTIONS'}, 'storage': {'columns': 'pod,volume_root,volume_cassandra,snapshots,data,compactions', 'header': 'POD_NAME,VOLUME /,VOLUME CASS,SNAPSHOTS,DATA,COMPACTIONS'}, 'watch': {'auto': 'rollout', 'timeout': 3600, 'interval': 10}, 'debug': {'timings': False, 'exit-on-error': False, 'show-parallelism': False, 'show-out': False}}
2
+ return {'app': {'console-endpoint': 'https://{host}/{env}/{app}/static/console/index.html', 'container-name': 'c3-server', 'cr': {'cluster-regex': '(.*?-.*?)-.*', 'group': 'ops.c3.ai', 'v': 'v2', 'plural': 'c3cassandras'}, 'label': 'c3__app_id-0', 'login': {'admin-group': '{host}/C3.ClusterAdmin', 'ingress': '{app_id}-k8singr-appleader-001', 'timeout': 5, 'session-check-url': 'https://{host}/{env}/{app}/api/8/C3/userSessionToken', 'cache-creds': True, 'cache-username': True, 'url': 'https://{host}/{env}/{app}', 'another': "You're logged in to {has}. However, for this app, you need to log in to {need}.", 'token-server-url': 'http://localhost:{port}', 'password-max-length': 128}, 'strip': '0'}, 'audit': {'endpoint': 'https://4psvtaxlcb.execute-api.us-west-2.amazonaws.com/prod/', 'workers': 3, 'timeout': 10, 'log-audit-queries': False, 'athena': {'auto-repair': {'elapsed_hours': 12}, 'region': 'us-west-2', 'catalog': 'AwsDataCatalog', 'database': 'audit', 'repair-partition-tables': 'audit', 'output': 's3://s3.ops--audit/ddl/results', 'repair-cluster-tables': 'cluster'}}, 'bash': {'workers': 32}, 'cassandra': {'service-name': 'all-pods-service'}, 'cql': {'workers': 32, 'samples': 3, 'secret': {'cluster-regex': '(.*?-.*?)-.*', 'name': '{cluster}-superuser', 'password-item': 'password'}, 'alter-tables': {'excludes': 'system_auth,system_traces,reaper_db,system_distributed,system_views,system,system_schema,system_virtual_schema', 'gc-grace-periods': '3600,86400,864000,7776000', 'batching': True}}, 'export': {'workers': 8, 'csv_dir': '/c3/cassandra/tmp', 'default-importer': 'sqlite', 'sqlite': {'workers': 8, 'columns': '<row-key>', 'local-db-dir': '/tmp/qing-db', 'column_counts_query': 'select id, count(id) as columns from {table} group by id order by columns desc limit 10'}, 'athena': {'workers': 8, 'columns': '<keys>', 'bucket': 'c3.ops--qing', 'column_counts_query': 'select id, count(id) as columns from {table} group by id order by columns desc limit 10'}, 'csv': {'workers': 8, 'columns': '<row-key>'}}, 'checks': {'compactions-threshold': 250, 'cpu-busy-threshold': 98.0, 'cpu-threshold': 0.0, 'cassandra-data-path': '/c3/cassandra', 'root-disk-threshold': 50, 'cassandra-disk-threshold': 50, 'snapshot-size-cmd': "ls /c3/cassandra/data/data/*/*/snapshots | grep snapshots | sed 's/:$//g' | xargs -I {} du -sk {} | awk '{print $1}' | awk '{s+=$1} END {print s}'", 'snapshot-size-threshold': '40G', 'table-sizes-cmd': "ls -Al /c3/cassandra/data/data/ | awk '{print $9}' | sed 's/\\^r//g' | xargs -I {} du -sk /c3/cassandra/data/data/{}"}, 'get-host-id': {'workers': 32}, 'idps': {'ad': {'email-pattern': '.*@c3.ai', 'uri': 'https://login.microsoftonline.com/53ad779a-93e7-485c-ba20-ac8290d7252b/oauth2/v2.0/authorize?response_type=id_token&response_mode=form_post&client_id=00ff94a8-6b0a-4715-98e0-95490012d818&scope=openid+email+profile&redirect_uri=https%3A%2F%2Fplat.c3ci.cloud%2Fc3%2Fc3%2Foidc%2Flogin&nonce={nonce}&state=EMPTY', 'jwks-uri': 'https://login.microsoftonline.com/common/discovery/keys', 'contact': 'Please contact ted.tran@c3.ai.', 'whitelist-file': '/kaqing/members'}, 'okta': {'default': True, 'email-pattern': '.*@c3iot.com', 'uri': 'https://c3energy.okta.com/oauth2/v1/authorize?response_type=id_token&response_mode=form_post&client_id={client_id}&scope=openid+email+profile+groups&redirect_uri=https%3A%2F%2F{host}%2Fc3%2Fc3%2Foidc%2Flogin&nonce={nonce}&state=EMPTY', 'jwks-uri': 'https://c3energy.okta.com/oauth2/v1/keys'}}, 'issues': {'workers': 32}, 'logs': {'path': '/c3/cassandra/logs/system.log'}, 'log-prefix': '/tmp/qing', 'medusa': {'restore-auto-complete': False}, 'nodetool': {'workers': 32, 'samples': 3, 'commands_in_line': 40}, 'pg': {'name-pattern': '^{namespace}.*-k8spg-.*', 'excludes': '.helm., -admin-secret', 'agent': {'name': 'ops-pg-agent', 'just-in-time': False, 'timeout': 86400, 'image': 'seanahnsf/kaqing'}, 'default-db': 'postgres', 'default-schema': 'postgres', 'secret': {'endpoint-key': 'postgres-db-endpoint', 'port-key': 'postgres-db-port', 'username-key': 'postgres-admin-username', 'password-key': 'postgres-admin-password'}}, 'pod': {'name': 'ops', 'image': 'seanahnsf/kaqing-cloud', 'sa': {'name': 'ops', 'proto': 'c3', 'additional-cluster-roles': 'c3aiops-k8ssandra-operator'}, 'label-selector': 'run=ops'}, 'preview': {'rows': 10}, 'processes': {'columns': 'pod,cpu,mem', 'header': 'POD_NAME,CPU,MEM/LIMIT'}, 'reaper': {'service-name': 'reaper-service', 'port-forward': {'timeout': 86400, 'local-port': 9001}, 'abort-runs-batch': 10, 'show-runs-batch': 100, 'pod': {'cluster-regex': '(.*?-.*?-.*?-.*?)-.*', 'label-selector': 'k8ssandra.io/reaper={cluster}-reaper'}, 'secret': {'cluster-regex': '(.*?-.*?)-.*', 'name': '{cluster}-reaper-ui', 'password-item': 'password'}}, 'repair': {'log-path': '/home/cassrepair/logs/', 'image': 'ci-registry.c3iot.io/cloudops/cassrepair:2.0.14', 'secret': 'ciregistryc3iotio', 'env': {'interval': 24, 'timeout': 60, 'pr': False, 'runs': 1}}, 'repl': {'start-drive': 'c', 'a': {'auto-enter': 'c3/c3/*'}, 'c': {'auto-enter': 'cluster'}, 'x': {'auto-enter': 'latest'}, 'history': {'push-cat-remote-log-file': True}, 'background-process': {'auto-nohup': True}}, 'status': {'columns': 'status,address,load,tokens,owns,host_id,gossip,compactions', 'header': '--,Address,Load,Tokens,Owns,Host ID,GOSSIP,COMPACTIONS'}, 'storage': {'columns': 'pod,volume_root,volume_cassandra,snapshots,data,compactions', 'header': 'POD_NAME,VOLUME /,VOLUME CASS,SNAPSHOTS,DATA,COMPACTIONS'}, 'watch': {'auto': 'rollout', 'timeout': 3600, 'interval': 10}, 'debug': False, 'debugs': {'timings': False, 'exit-on-error': False, 'show-parallelism': False}}
adam/pod_exec_result.py CHANGED
@@ -15,12 +15,14 @@ class PodExecResult:
15
15
  # ]
16
16
  # }
17
17
  # }
18
- def __init__(self, stdout: str, stderr: str, command: str = None, error_output: str = None):
18
+ def __init__(self, stdout: str, stderr: str, command: str = None, error_output: str = None, pod: str = None, log_file: str = None):
19
19
  self.stdout: str = stdout
20
20
  self.stderr: str = stderr
21
21
  self.command: str = command
22
22
  if error_output:
23
23
  self.error = yaml.safe_load(error_output)
24
+ self.pod = pod
25
+ self.log_file = log_file
24
26
 
25
27
  def exit_code(self) -> int:
26
28
  code = 0
@@ -30,4 +32,10 @@ class PodExecResult:
30
32
  except:
31
33
  pass
32
34
 
33
- return code
35
+ return code
36
+
37
+ def __str__(self):
38
+ return f'{"OK" if self.exit_code() == 0 else self.exit_code()} {self.command}'
39
+
40
+ def __audit_extra__(self):
41
+ return self.log_file if self.log_file else None
adam/repl.py CHANGED
@@ -1,25 +1,28 @@
1
1
  import os
2
- import re
3
2
  import time
4
3
  import traceback
4
+ from typing import cast
5
5
  import click
6
- from prompt_toolkit.completion import NestedCompleter
6
+ import concurrent
7
7
  from prompt_toolkit.key_binding import KeyBindings
8
8
 
9
9
  from adam.cli_group import cli
10
10
  from adam.commands.command import Command
11
11
  from adam.commands.command_helpers import ClusterCommandHelper
12
+ from adam.commands.devices.devices import Devices
12
13
  from adam.commands.help import Help
13
- from adam.commands.postgres.postgres_session import PostgresSession
14
14
  from adam.config import Config
15
- from adam.k8s_utils.kube_context import KubeContext
16
- from adam.k8s_utils.statefulsets import StatefulSets
15
+ from adam.utils_audits import Audits
16
+ from adam.utils_k8s.app_pods import AppPods
17
+ from adam.utils_k8s.kube_context import KubeContext
18
+ from adam.utils_k8s.statefulsets import StatefulSets
17
19
  from adam.log import Log
18
20
  from adam.repl_commands import ReplCommands
19
21
  from adam.repl_session import ReplSession
20
22
  from adam.repl_state import ReplState
21
- from adam.utils import deep_merge_dicts, lines_to_tabular, log2
23
+ from adam.utils import deep_merge_dicts, deep_sort_dict, lines_to_tabular, log2
22
24
  from adam.apps import Apps
25
+ from adam.utils_repl.repl_completer import ReplCompleter
23
26
  from . import __version__
24
27
 
25
28
  def enter_repl(state: ReplState):
@@ -33,54 +36,13 @@ def enter_repl(state: ReplState):
33
36
  session = ReplSession().prompt_session
34
37
 
35
38
  def prompt_msg():
36
- msg = ''
37
- if state.device == ReplState.P:
38
- msg = f'{ReplState.P}:'
39
- pg = PostgresSession(state.namespace, state.pg_path) if state.pg_path else None
40
- if pg and pg.db:
41
- msg += pg.db
42
- elif pg and pg.host:
43
- msg += pg.host
44
- elif state.device == ReplState.A:
45
- msg = f'{ReplState.A}:'
46
- if state.app_env:
47
- msg += state.app_env
48
- if state.app_app:
49
- msg += f'/{state.app_app}'
50
- else:
51
- msg = f'{ReplState.C}:'
52
- if state.pod:
53
- # cs-d0767a536f-cs-d0767a536f-default-sts-0
54
- group = re.match(r".*?-.*?-(.*)", state.pod)
55
- msg += group[1]
56
- elif state.sts:
57
- # cs-d0767a536f-cs-d0767a536f-default-sts
58
- group = re.match(r".*?-.*?-(.*)", state.sts)
59
- msg += group[1]
39
+ msg = state.__str__()
60
40
 
61
41
  return f"{msg}$ " if state.bash_session else f"{msg}> "
62
42
 
63
43
  Log.log2(f'kaqing {__version__}')
64
- ss = StatefulSets.list_sts_name_and_ns()
65
-
66
- if state.device == ReplState.C:
67
- if not ss:
68
- raise Exception("no Cassandra clusters found")
69
- elif len(ss) == 1 and Config().get('repl.auto-enter-only-cluster', True):
70
- cluster = ss[0]
71
- state.sts = cluster[0]
72
- state.namespace = cluster[1]
73
- state.wait_log(f'Moving to the only Cassandra cluster: {state.sts}@{state.namespace}...')
74
- elif state.device == ReplState.A:
75
- if app := Config().get('repl.auto-enter-app', 'c3/c3'):
76
- if app != 'no':
77
- ea = app.split('/')
78
- state.app_env = ea[0]
79
- if len(ea) > 1:
80
- state.app_app = ea[1]
81
- state.wait_log(f'Moving to {state.app_env}/{state.app_app}...')
82
- else:
83
- state.wait_log(f'Moving to {state.app_env}...')
44
+
45
+ Devices.device(state).enter(state)
84
46
 
85
47
  kb = KeyBindings()
86
48
 
@@ -88,72 +50,120 @@ def enter_repl(state: ReplState):
88
50
  def _(event):
89
51
  event.app.current_buffer.text = ''
90
52
 
91
- # use sorted command list only for auto-completion
92
- sorted_cmds = sorted(cmd_list, key=lambda cmd: cmd.command())
93
- while True:
94
- try:
95
- completer = NestedCompleter.from_nested_dict({})
96
- if not state.bash_session:
97
- completions = {}
98
- # app commands are available only on a: drive
99
- if state.device == ReplState.A and state.app_app:
100
- completions = Apps(path='apps.yaml').commands()
101
-
102
- for cmd in sorted_cmds:
103
- s1 = time.time()
104
- try:
105
- completions = deep_merge_dicts(completions, cmd.completion(state))
106
- finally:
107
- if Config().get('debug.timings', False):
108
- Config().debug('Timing completion calc', cmd.command(), f'{time.time() - s1:.2f}')
109
-
110
- completer = NestedCompleter.from_nested_dict(completions)
111
-
112
- cmd = session.prompt(prompt_msg(), completer=completer, key_bindings=kb)
113
- s0 = time.time()
114
-
115
- if state.bash_session:
116
- if cmd.strip(' ') == 'exit':
117
- state.exit_bash()
118
- continue
119
-
120
- cmd = f'bash {cmd}'
121
-
122
- if cmd and cmd.strip(' ') and not cmds.run(cmd, state):
123
- c_sql_tried = False
124
- if state.device == ReplState.P:
125
- pg = PostgresSession(state.namespace, state.pg_path)
126
- if pg.db:
127
- c_sql_tried = True
128
- cmd = f'pg {cmd}'
129
- cmds.run(cmd, state)
130
- elif state.device == ReplState.A:
131
- if state.app_app:
132
- c_sql_tried = True
133
- cmd = f'app {cmd}'
134
- cmds.run(cmd, state)
135
- elif state.sts:
136
- c_sql_tried = True
137
- cmd = f'cql {cmd}'
138
- cmds.run(cmd, state)
139
-
140
- if not c_sql_tried:
141
- log2(f'* Invalid command: {cmd}')
142
- log2()
143
- lines = [c.help(state) for c in cmd_list if c.help(state)]
144
- log2(lines_to_tabular(lines, separator='\t'))
145
- except EOFError: # Handle Ctrl+D (EOF) for graceful exit
146
- break
147
- except Exception as e:
148
- if Config().get('debug.exit-on-error', False):
149
- raise e
150
- else:
151
- log2(e)
152
- Config().debug(traceback.format_exc())
153
- finally:
154
- state.clear_wait_log_flag()
155
- if Config().get('debug.timings', False) and 'cmd' in locals() and 's0' in locals():
156
- print('Timing command', cmd, f'{time.time() - s0:.2f}')
53
+ with concurrent.futures.ThreadPoolExecutor(max_workers=Config().get('audit.workers', 3)) as executor:
54
+ # warm up AWS lambda - this log line may timeout and get lost, which is fine
55
+ executor.submit(Audits.log, 'entering kaqing repl', state.namespace, 'z', 0.0)
56
+
57
+ s0 = time.time()
58
+
59
+ # use sorted command list only for auto-completion
60
+ sorted_cmds = sorted(cmd_list, key=lambda cmd: cmd.command())
61
+ while True:
62
+ result = None
63
+ try:
64
+ completer = ReplCompleter.from_nested_dict({})
65
+ if not state.bash_session:
66
+ completions = {}
67
+ # app commands are available only on a: drive
68
+ if state.device == ReplState.A and state.app_app:
69
+ completions = Apps(path='apps.yaml').commands()
70
+
71
+ for cmd in sorted_cmds:
72
+ s1 = time.time()
73
+ try:
74
+ completions = deep_sort_dict(deep_merge_dicts(completions, cmd.completion(state)))
75
+ finally:
76
+ if Config().get('debugs.timings', False):
77
+ log2(f'Timing auto-completion-calc {cmd.command()}: {time.time() - s1:.2f}')
78
+
79
+ # print(json.dumps(completions, indent=4))
80
+ completer = ReplCompleter.from_nested_dict(completions)
81
+
82
+ cmd = session.prompt(prompt_msg(), completer=completer, key_bindings=kb)
83
+ s0 = time.time()
84
+
85
+ if state.bash_session:
86
+ if cmd.strip(' ') == 'exit':
87
+ state.exit_bash()
88
+ continue
89
+
90
+ cmd = f'bash {cmd}'
91
+
92
+ def targetted(state: ReplState, cmd: str):
93
+ if not (cmd.startswith('@') and len(arry := cmd.split(' ')) > 1):
94
+ return state, cmd
95
+
96
+ if state.device == ReplState.A and state.app_app or state.device == ReplState.P:
97
+ state.push()
98
+
99
+ state.app_pod = arry[0].strip('@')
100
+ cmd = ' '.join(arry[1:])
101
+ elif state.device == ReplState.P:
102
+ state.push()
103
+
104
+ state.app_pod = arry[0].strip('@')
105
+ cmd = ' '.join(arry[1:])
106
+ elif state.sts:
107
+ state.push()
108
+
109
+ state.pod = arry[0].strip('@')
110
+ cmd = ' '.join(arry[1:])
111
+
112
+ return (state, cmd)
113
+
114
+ target, cmd = targetted(state, cmd)
115
+ if cmd and cmd.strip(' ') and not (result := cmds.run(cmd, target)):
116
+ result = try_device_default_action(target, cmds, cmd_list, cmd)
117
+
118
+ if result and type(result) is ReplState and (s := cast(ReplState, result).export_session) != state.export_session:
119
+ state.export_session = s
120
+
121
+ except EOFError: # Handle Ctrl+D (EOF) for graceful exit
122
+ break
123
+ except Exception as e:
124
+ if Config().get('debugs.exit-on-error', False):
125
+ raise e
126
+ else:
127
+ log2(e)
128
+ Config().debug(traceback.format_exc())
129
+ finally:
130
+ if not state.bash_session:
131
+ state.pop()
132
+
133
+ Config().clear_wait_log_flag()
134
+ if Config().get('debugs.timings', False) and 'cmd' in locals() and 's0' in locals():
135
+ log2(f'Timing command {cmd}: {time.time() - s0:.2f}')
136
+
137
+ # offload audit logging
138
+ if cmd and (state.device != ReplState.L or Config().get('audit.log-audit-queries', False)):
139
+ executor.submit(Audits.log, cmd, state.namespace, state.device, time.time() - s0, get_audit_extra(result))
140
+
141
+ def try_device_default_action(state: ReplState, cmds: Command, cmd_list: list[Command], cmd: str):
142
+ default_action_tried, result = Devices.device(state).try_fallback_action(cmds, state, cmd)
143
+
144
+ if not default_action_tried:
145
+ log2(f'* Invalid command: {cmd}')
146
+ log2()
147
+ lines = [c.help(state) for c in cmd_list if c.help(state)]
148
+ log2(lines_to_tabular(lines, separator='\t'))
149
+
150
+ return result
151
+
152
+ def get_audit_extra(result: any):
153
+ if not result:
154
+ return None
155
+
156
+ if type(result) is list:
157
+ extras = set()
158
+
159
+ for r in result:
160
+ if hasattr(r, '__audit_extra__') and (x := r.__audit_extra__()):
161
+ extras.add(x)
162
+
163
+ return ','.join(list(extras))
164
+
165
+ if hasattr(result, '__audit_extra__') and (x := result.__audit_extra__()):
166
+ return x
157
167
 
158
168
  @cli.command(context_settings=dict(ignore_unknown_options=True, allow_extra_args=True), cls=ClusterCommandHelper, help="Enter interactive shell.")
159
169
  @click.option('--kubeconfig', '-k', required=False, metavar='path', help='path to kubeconfig file')
@@ -168,5 +178,5 @@ def repl(kubeconfig: str, config: str, param: list[str], cluster:str, namespace:
168
178
  return
169
179
 
170
180
  state = ReplState(device=Config().get('repl.start-drive', 'a'), ns_sts=cluster, namespace=namespace, in_repl=True)
171
- state, _ = state.apply_args(extra_args)
181
+ state, _ = state.apply_device_arg(extra_args)
172
182
  enter_repl(state)
adam/repl_commands.py CHANGED
@@ -1,5 +1,9 @@
1
+ from adam.commands.alter_tables import AlterTables
1
2
  from adam.commands.app import App
2
3
  from adam.commands.app_ping import AppPing
4
+ from adam.commands.audit.audit import Audit
5
+ from adam.commands.cat import Cat
6
+ from adam.commands.code import Code
3
7
  from adam.commands.deploy.code_start import CodeStart
4
8
  from adam.commands.deploy.code_stop import CodeStop
5
9
  from adam.commands.deploy.deploy import Deploy
@@ -10,22 +14,40 @@ from adam.commands.deploy.undeploy import Undeploy
10
14
  from adam.commands.deploy.undeploy_frontend import UndeployFrontend
11
15
  from adam.commands.deploy.undeploy_pg_agent import UndeployPgAgent
12
16
  from adam.commands.deploy.undeploy_pod import UndeployPod
17
+ from adam.commands.devices.device_app import DeviceApp
18
+ from adam.commands.devices.device_auit_log import DeviceAuditLog
19
+ from adam.commands.devices.device_cass import DeviceCass
20
+ from adam.commands.devices.device_export import DeviceExport
21
+ from adam.commands.devices.device_postgres import DevicePostgres
22
+ from adam.commands.export.drop_export_database import DropExportDatabase
23
+ from adam.commands.export.export import ExportTables
24
+ from adam.commands.export.import_session import ImportSession
25
+ from adam.commands.export.clean_up_export_session import CleanUpExportSession
26
+ from adam.commands.export.clean_up_export_sessions import CleanUpExportSessions
27
+ from adam.commands.export.drop_export_databases import DropExportDatabases
28
+ from adam.commands.export.export_select import ExportSelect
29
+ from adam.commands.export.export_use import ExportUse
30
+ from adam.commands.export.export_select_x import ExportSelectX
31
+ from adam.commands.export.show_column_counts import ShowColumnCounts
32
+ from adam.commands.export.show_export_databases import ShowExportDatabases
33
+ from adam.commands.export.show_export_session import ShowExportSession
34
+ from adam.commands.export.show_export_sessions import ShowExportSessions
35
+ from adam.commands.kubectl import Kubectl
13
36
  from adam.commands.shell import Shell
14
37
  from adam.commands.show.show_app_queues import ShowAppQueues
15
38
  from adam.commands.cp import ClipboardCopy
16
- from adam.commands.bash import Bash
39
+ from adam.commands.bash.bash import Bash
17
40
  from adam.commands.cd import Cd
18
41
  from adam.commands.check import Check
19
42
  from adam.commands.command import Command
20
- from adam.commands.cqlsh import Cqlsh
21
- from adam.commands.devices import DeviceApp, DeviceCass, DevicePostgres
43
+ from adam.commands.cql.cqlsh import Cqlsh
22
44
  from adam.commands.exit import Exit
23
45
  from adam.commands.medusa.medusa import Medusa
24
46
  from adam.commands.param_get import GetParam
25
47
  from adam.commands.issues import Issues
26
48
  from adam.commands.ls import Ls
27
49
  from adam.commands.nodetool import NodeTool
28
- from adam.commands.postgres.postgres import Postgres
50
+ from adam.commands.postgres.postgres import Postgres, PostgresPg
29
51
  from adam.commands.preview_table import PreviewTable
30
52
  from adam.commands.pwd import Pwd
31
53
  from adam.commands.reaper.reaper import Reaper
@@ -40,6 +62,7 @@ from adam.commands.show.show_app_id import ShowAppId
40
62
  from adam.commands.show.show_cassandra_status import ShowCassandraStatus
41
63
  from adam.commands.show.show_cassandra_version import ShowCassandraVersion
42
64
  from adam.commands.show.show_commands import ShowKubectlCommands
65
+ from adam.commands.show.show_host import ShowHost
43
66
  from adam.commands.show.show_login import ShowLogin
44
67
  from adam.commands.show.show_params import ShowParams
45
68
  from adam.commands.show.show_processes import ShowProcesses
@@ -50,10 +73,10 @@ from adam.commands.watch import Watch
50
73
 
51
74
  class ReplCommands:
52
75
  def repl_cmd_list() -> list[Command]:
53
- cmds: list[Command] = ReplCommands.navigation() + ReplCommands.cassandra_check() + ReplCommands.cassandra_ops() + \
54
- ReplCommands.tools() + ReplCommands.app() + ReplCommands.exit()
76
+ cmds: list[Command] = ReplCommands.navigation() + ReplCommands.cassandra_ops() + ReplCommands.postgres_ops() + \
77
+ ReplCommands.app_ops() + ReplCommands.audit_ops() + ReplCommands.export_ops() + ReplCommands.tools() + ReplCommands.exit()
55
78
 
56
- intermediate_cmds: list[Command] = [App(), Reaper(), Repair(), Deploy(), Show(), Undeploy()]
79
+ intermediate_cmds: list[Command] = [App(), Audit(), Reaper(), Repair(), Deploy(), Show(), Undeploy()]
57
80
  ic = [c.command() for c in intermediate_cmds]
58
81
  # 1. dedup commands
59
82
  deduped = []
@@ -70,20 +93,32 @@ class ReplCommands:
70
93
  return deduped
71
94
 
72
95
  def navigation() -> list[Command]:
73
- return [Ls(), PreviewTable(), DeviceApp(), DevicePostgres(), DeviceCass(), Cd(), Pwd(), ClipboardCopy(),
74
- GetParam(), SetParam(), ShowParams(), ShowKubectlCommands(), ShowLogin(), ShowAdam()]
75
-
76
- def cassandra_check() -> list[Command]:
77
- return [ShowCassandraStatus(), ShowCassandraVersion(), ShowRepairs(), ShowStorage(), ShowProcesses(), Check(), Issues(), NodeTool(), Report()]
96
+ return [Ls(), PreviewTable(), DeviceApp(), DevicePostgres(), DeviceCass(), DeviceAuditLog(), DeviceExport(), Cd(), Cat(), Pwd(), ClipboardCopy(),
97
+ GetParam(), SetParam(), ShowParams(), ShowKubectlCommands(), ShowLogin(), ShowAdam(), ShowHost()]
78
98
 
79
99
  def cassandra_ops() -> list[Command]:
80
- return Medusa.cmd_list() + [Restart(), RollOut(), Watch()] + Reaper.cmd_list() + Repair.cmd_list()
100
+ return [Cqlsh(), ShowCassandraStatus(), ShowCassandraVersion(), ShowRepairs(), ShowStorage(), ShowProcesses(),
101
+ Check(), Issues(), NodeTool(), Report(), AlterTables(), Bash(),
102
+ ExportTables(), ExportSelect(), ExportUse(), ShowExportDatabases(), ShowColumnCounts(),
103
+ DropExportDatabase(), DropExportDatabases(),
104
+ ShowExportSessions(), ShowExportSession(),
105
+ CleanUpExportSession(), CleanUpExportSessions(), ImportSession()] + \
106
+ Medusa.cmd_list() + [Restart(), RollOut(), Watch()] + Reaper.cmd_list() + Repair.cmd_list()
81
107
 
82
- def tools() -> list[Command]:
83
- return [Cqlsh(), Postgres(), Bash(), Shell(), CodeStart(), CodeStop(), DeployFrontend(), UndeployFrontend(), DeployPod(), UndeployPod(), DeployPgAgent(), UndeployPgAgent()]
108
+ def postgres_ops() -> list[Command]:
109
+ return [Postgres(), DeployPgAgent(), UndeployPgAgent(), PostgresPg()]
84
110
 
85
- def app() -> list[Command]:
111
+ def app_ops() -> list[Command]:
86
112
  return [ShowAppActions(), ShowAppId(), ShowAppQueues(), AppPing(), App()]
87
113
 
114
+ def audit_ops() -> list[Command]:
115
+ return [Audit()] + Audit.cmd_list()
116
+
117
+ def export_ops() -> list[Command]:
118
+ return [ExportSelectX(), DropExportDatabase(), DropExportDatabases(), ShowColumnCounts()]
119
+
120
+ def tools() -> list[Command]:
121
+ return [Shell(), CodeStart(), CodeStop(), DeployFrontend(), UndeployFrontend(), DeployPod(), UndeployPod(), Kubectl(), Code()]
122
+
88
123
  def exit() -> list[Command]:
89
124
  return [Exit()]