generate_dbeaver_connections.py 8.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. #!/usr/bin/env python3
  2. """
  3. Generate DBeaver data-sources.json from sdm status --json output
  4. Organizes PostgreSQL connections by customer folders
  5. Excludes readonly connections, includes activate connections
  6. """
  7. import subprocess
  8. import json
  9. import re
  10. import uuid
  11. import os
  12. from collections import defaultdict
  13. def run_sdm_status():
  14. try:
  15. result = subprocess.run(['sdm', 'status', '--json'], capture_output=True, text=True)
  16. if result.returncode != 0:
  17. print(f"Error running sdm status: {result.stderr}")
  18. return None
  19. return json.loads(result.stdout)
  20. except FileNotFoundError:
  21. print("Error: sdm command not found")
  22. return None
  23. except json.JSONDecodeError as e:
  24. print(f"Error parsing JSON: {e}")
  25. return None
  26. def parse_postgres_connections(sdm_data):
  27. connections = []
  28. for item in sdm_data:
  29. if item.get('type') == 'postgres':
  30. name = item.get('name', '')
  31. address = item.get('address', '')
  32. if 'readonly' in name:
  33. continue
  34. if ':' in address:
  35. addr, port = address.split(':')
  36. port = int(port)
  37. else:
  38. continue
  39. env_info = parse_connection_name(name)
  40. if env_info:
  41. connections.append({
  42. 'name': name,
  43. 'addr': addr,
  44. 'port': port,
  45. 'environment': env_info['environment'],
  46. 'customer': env_info['customer'],
  47. 'stage': env_info['stage']
  48. })
  49. return connections
  50. def parse_connection_name(name):
  51. pattern = r'oc-([^-]+)-([^-]+)-.*'
  52. match = re.match(pattern, name)
  53. if not match:
  54. return None
  55. environment = match.group(1)
  56. customer = match.group(2)
  57. if environment in ['dev', 'nextrc', 'nextrc2']:
  58. stage = 'internal'
  59. elif environment in ['stage', 'stage2', 'uat']:
  60. stage = 'stage'
  61. elif environment in ['prod', 'prod2']:
  62. stage = 'production'
  63. else:
  64. stage = 'unknown'
  65. return {
  66. 'environment': environment,
  67. 'customer': customer,
  68. 'stage': stage
  69. }
  70. def create_dbeaver_connection(conn, connection_id):
  71. if conn['customer'] == 'internal' or conn['customer'].startswith('feature'):
  72. folder = 'internal'
  73. elif conn['stage'] == 'stage':
  74. folder = conn['customer']
  75. elif conn['stage'] == 'production':
  76. folder = conn['customer']
  77. else:
  78. folder = 'other'
  79. display_name = conn['name'].replace('oc-', '').replace('-rds', '').replace('-activate', '')
  80. if 'activate' in conn['name']:
  81. display_name = f"activate-{display_name}"
  82. if 'activate' in conn['name']:
  83. default_db = "postgres"
  84. db_url = f"jdbc:postgresql://{conn['addr']}:{conn['port']}/postgres"
  85. else:
  86. default_db = "member_dossier"
  87. db_url = f"jdbc:postgresql://{conn['addr']}:{conn['port']}/member_dossier"
  88. return {
  89. "provider": "postgresql",
  90. "driver": "postgres-jdbc",
  91. "name": display_name,
  92. "save-password": True,
  93. "folder": folder,
  94. "configuration": {
  95. "host": conn['addr'],
  96. "port": str(conn['port']),
  97. "database": default_db,
  98. "url": db_url,
  99. "configurationType": "MANUAL",
  100. "home": "/bin",
  101. "type": conn['environment'],
  102. "closeIdleConnection": False,
  103. "provider-properties": {
  104. "@dbeaver-show-non-default-db@": "true",
  105. "@dbeaver-show-template-db@": "true",
  106. "@dbeaver-show-unavailable-db@": "true",
  107. "show-database-statistics": "false",
  108. "@dbeaver-read-all-data-types-db@": "false",
  109. "@dbeaver-use-prepared-statements-db@": "false",
  110. "postgresql.dd.plain.string": "false",
  111. "postgresql.dd.tag.string": "false"
  112. },
  113. "auth-model": "native"
  114. }
  115. }
  116. def group_connections(connections):
  117. folders = defaultdict(list)
  118. for conn in connections:
  119. if conn['customer'] == 'internal' or conn['customer'].startswith('feature'):
  120. folder = 'internal'
  121. elif conn['stage'] == 'stage':
  122. folder = conn['customer']
  123. elif conn['stage'] == 'production':
  124. folder = conn['customer']
  125. else:
  126. folder = 'other'
  127. folders[folder].append(conn)
  128. return folders
  129. def is_sdm_connection(conn_name):
  130. """Check if connection name matches SDM pattern (oc-*-*-*)"""
  131. pattern = r'oc-([^-]+)-([^-]+)-.*'
  132. return re.match(pattern, conn_name) is not None
  133. def load_existing_config(output_file):
  134. """Load existing DBeaver config, return empty if file doesn't exist"""
  135. if not os.path.exists(output_file):
  136. return {"folders": {}, "connections": {}}
  137. try:
  138. with open(output_file, 'r') as f:
  139. return json.load(f)
  140. except json.JSONDecodeError:
  141. print("⚠️ Existing config file is invalid, starting fresh")
  142. return {"folders": {}, "connections": {}}
  143. def generate_dbeaver_config(connections, existing_config):
  144. folders = group_connections(connections)
  145. # Start with existing folders
  146. folders_config = existing_config.get("folders", {}).copy()
  147. # Add folders for new SDM connections
  148. for folder_name in folders.keys():
  149. if folder_name not in folders_config:
  150. folders_config[folder_name] = {}
  151. # Start with existing connections, filtering out SDM connections
  152. connections_config = {}
  153. existing_connections = existing_config.get("connections", {})
  154. for conn_id, conn in existing_connections.items():
  155. # Keep non-SDM connections (those that don't start with oc- in their display name)
  156. display_name = conn.get("name", "")
  157. # Also check configuration host/type for SDM patterns
  158. config = conn.get("configuration", {})
  159. conn_type = config.get("type", "")
  160. # Only keep connections that don't match SDM pattern
  161. if not is_sdm_connection(display_name) and not is_sdm_connection(conn_type):
  162. connections_config[conn_id] = conn
  163. # Add new SDM connections
  164. for conn in connections:
  165. connection_id = f"postgres-jdbc-{uuid.uuid4().hex[:8]}-{uuid.uuid4().hex[:8]}"
  166. conn_config = create_dbeaver_connection(conn, connection_id)
  167. connections_config[connection_id] = conn_config
  168. return {
  169. "folders": folders_config,
  170. "connections": connections_config
  171. }
  172. def main():
  173. print("Generating DBeaver data-sources.json from sdm status --json...")
  174. output_file = os.path.expanduser('~/.local/share/DBeaverData/workspace6/Stuzo/.dbeaver/data-sources.json')
  175. # Load existing config to preserve non-SDM connections
  176. existing_config = load_existing_config(output_file)
  177. existing_count = len(existing_config.get("connections", {}))
  178. print(f"📁 Found {existing_count} existing connections")
  179. sdm_data = run_sdm_status()
  180. if not sdm_data:
  181. return
  182. connections = parse_postgres_connections(sdm_data)
  183. print(f"🔍 Found {len(connections)} PostgreSQL connections from SDM")
  184. folders = group_connections(connections)
  185. for folder_name, conns in folders.items():
  186. print(f" {folder_name}: {len(conns)} connections")
  187. dbeaver_config = generate_dbeaver_config(connections, existing_config)
  188. final_count = len(dbeaver_config["connections"])
  189. preserved_count = final_count - len(connections)
  190. print(f"💾 Preserved {preserved_count} non-SDM connections")
  191. print(f"📊 Total connections: {final_count} ({preserved_count} preserved + {len(connections)} SDM)")
  192. try:
  193. with open(output_file, 'w') as f:
  194. json.dump(dbeaver_config, f, indent='\t')
  195. print(f"✅ Successfully generated {output_file}")
  196. print("📝 Note: You may need to restart DBeaver to see the new connections")
  197. except Exception as e:
  198. print(f"❌ Error writing file: {e}")
  199. if __name__ == '__main__':
  200. main()