263 lines
No EOL
8.4 KiB
Python
Executable file
263 lines
No EOL
8.4 KiB
Python
Executable file
#!/usr/bin/env python3
|
|
"""
|
|
Generate TinyRDM connections.yaml from sdm status --json output
|
|
Organizes Redis connections into Internal, Stage, and Production groups
|
|
Excludes activate and readonly connections
|
|
"""
|
|
|
|
import subprocess
|
|
import json
|
|
import re
|
|
import os
|
|
import yaml
|
|
from collections import defaultdict
|
|
|
|
def run_sdm_status():
|
|
try:
|
|
result = subprocess.run(['sdm', 'status', '--json'], capture_output=True, text=True)
|
|
if result.returncode != 0:
|
|
print(f"Error running sdm status: {result.stderr}")
|
|
return None
|
|
return json.loads(result.stdout)
|
|
except FileNotFoundError:
|
|
print("Error: sdm command not found")
|
|
return None
|
|
except json.JSONDecodeError as e:
|
|
print(f"Error parsing JSON: {e}")
|
|
return None
|
|
|
|
def parse_redis_connections(sdm_data):
|
|
connections = []
|
|
excluded = ['activate-cache', 'activate-readonly', 'readonly-redis']
|
|
|
|
for item in sdm_data:
|
|
if item.get('type') != 'redis':
|
|
continue
|
|
|
|
name = item.get('name', '')
|
|
if any(x in name for x in excluded):
|
|
continue
|
|
|
|
address = item.get('address', '')
|
|
if ':' not in address:
|
|
continue
|
|
|
|
addr, port = address.split(':')
|
|
env_info = parse_connection_name(name)
|
|
|
|
if env_info:
|
|
connections.append({
|
|
'name': name,
|
|
'addr': addr,
|
|
'port': int(port),
|
|
**env_info
|
|
})
|
|
|
|
return connections
|
|
|
|
def parse_connection_name(name):
|
|
pattern = r'oc-([^-]+)-([^-]+)-.*'
|
|
match = re.match(pattern, name)
|
|
|
|
if not match:
|
|
return None
|
|
|
|
environment = match.group(1)
|
|
customer = match.group(2)
|
|
|
|
if environment in ['dev', 'nextrc', 'nextrc2']:
|
|
stage = 'internal'
|
|
elif environment in ['stage', 'stage2', 'uat']:
|
|
stage = 'stage'
|
|
elif environment in ['prod', 'prod2']:
|
|
stage = 'production'
|
|
else:
|
|
stage = 'unknown'
|
|
|
|
return {
|
|
'environment': environment,
|
|
'customer': customer,
|
|
'stage': stage
|
|
}
|
|
|
|
def is_sdm_connection(conn_name):
|
|
"""Check if connection name matches SDM pattern (oc-*-*-*)"""
|
|
pattern = r'oc-([^-]+)-([^-]+)-.*'
|
|
return re.match(pattern, conn_name) is not None
|
|
|
|
def load_existing_connections(output_file):
|
|
"""Load existing connections, filtering out SDM connections"""
|
|
if not os.path.exists(output_file):
|
|
return {}
|
|
|
|
try:
|
|
with open(output_file, 'r') as f:
|
|
data = yaml.safe_load(f) or []
|
|
|
|
preserved_groups = {}
|
|
|
|
for item in data:
|
|
if not isinstance(item, dict):
|
|
continue
|
|
|
|
# Handle grouped connections
|
|
if item.get("type") == "group":
|
|
group_name = item.get("name", "")
|
|
connections = item.get("connections", [])
|
|
|
|
# Filter out SDM connections
|
|
non_sdm_conns = []
|
|
for conn in connections:
|
|
conn_name = conn.get("name", "")
|
|
if conn_name and not is_sdm_connection(conn_name):
|
|
non_sdm_conns.append(conn)
|
|
|
|
if non_sdm_conns:
|
|
preserved_groups[group_name] = non_sdm_conns
|
|
|
|
# Handle ungrouped connections (top-level connections)
|
|
elif item.get("type") != "group" and "addr" in item and "port" in item:
|
|
# This is a standalone connection (not in a group)
|
|
conn_name = item.get("name", "")
|
|
if conn_name and not is_sdm_connection(conn_name):
|
|
# Put ungrouped connections into a special group
|
|
if 'ungrouped' not in preserved_groups:
|
|
preserved_groups['ungrouped'] = []
|
|
preserved_groups['ungrouped'].append(item)
|
|
|
|
return preserved_groups
|
|
|
|
except Exception as e:
|
|
print(f"⚠️ Error reading existing config: {e}")
|
|
return {}
|
|
|
|
def create_connection_yaml(name, addr, port, stage):
|
|
colors = {
|
|
'internal': '#4ECF60',
|
|
'stage': '#FFA500',
|
|
'production': '#FF0000'
|
|
}
|
|
|
|
color = colors.get(stage, '#808080')
|
|
|
|
return f""" - name: {name}
|
|
last_db: 0
|
|
network: tcp
|
|
addr: {addr}
|
|
port: {port}
|
|
default_filter: '*'
|
|
key_separator: ':'
|
|
conn_timeout: 60
|
|
exec_timeout: 60
|
|
db_filter_type: none
|
|
load_size: 10000
|
|
mark_color: '{color}'
|
|
refresh_interval: 5"""
|
|
|
|
def group_connections(connections):
|
|
groups = defaultdict(list)
|
|
|
|
for conn in connections:
|
|
# Group by customer (internal/features -> 'internal', otherwise by customer)
|
|
if conn['customer'] == 'internal' or conn['customer'].startswith('feature'):
|
|
group_name = 'internal'
|
|
elif conn['stage'] in ['stage', 'production']:
|
|
group_name = conn['customer']
|
|
else:
|
|
group_name = 'other'
|
|
|
|
# Use stage directly for color coding
|
|
conn_yaml = create_connection_yaml(
|
|
conn['name'],
|
|
conn['addr'],
|
|
conn['port'],
|
|
conn['stage']
|
|
)
|
|
|
|
groups[group_name].append(conn_yaml)
|
|
|
|
return groups
|
|
|
|
def format_connection_for_yaml(conn):
|
|
"""Format a connection dict as YAML string with proper indentation"""
|
|
yaml_str = yaml.dump(conn, default_flow_style=False, sort_keys=False)
|
|
# Add list marker and proper indentation
|
|
lines = yaml_str.split('\n')
|
|
formatted_lines = []
|
|
for i, line in enumerate(lines):
|
|
if line.strip():
|
|
# First line should start with " - "
|
|
if i == 0:
|
|
formatted_lines.append(f" - {line.strip()}")
|
|
else:
|
|
formatted_lines.append(f" {line}")
|
|
return '\n'.join(formatted_lines)
|
|
|
|
def generate_yaml_content(groups, preserved_connections):
|
|
"""Generate YAML content from SDM groups and preserved connections"""
|
|
yaml_lines = []
|
|
|
|
# Prefer 'internal' first, then alphabetical by customer
|
|
ordered_groups = []
|
|
if 'internal' in groups:
|
|
ordered_groups.append('internal')
|
|
ordered_groups.extend(sorted([g for g in groups.keys() if g != 'internal']))
|
|
|
|
# Add preserved groups
|
|
for group_name in preserved_connections.keys():
|
|
if group_name not in ordered_groups:
|
|
ordered_groups.append(group_name)
|
|
|
|
for group_name in ordered_groups:
|
|
yaml_lines.append(f"- name: {group_name}")
|
|
yaml_lines.append(" last_db: 0")
|
|
yaml_lines.append(" type: group")
|
|
yaml_lines.append(" connections:")
|
|
|
|
# Add connections (preserved first, then SDM)
|
|
if group_name in preserved_connections:
|
|
for conn_dict in preserved_connections[group_name]:
|
|
yaml_lines.append(format_connection_for_yaml(conn_dict))
|
|
if group_name in groups:
|
|
for conn_yaml in groups[group_name]:
|
|
yaml_lines.append(conn_yaml)
|
|
|
|
yaml_lines.append("")
|
|
|
|
return '\n'.join(yaml_lines)
|
|
|
|
def main():
|
|
print("Generating TinyRDM connections.yaml from sdm status --json...")
|
|
|
|
output_file = os.path.expanduser('~/.config/TinyRDM/connections.yaml')
|
|
|
|
# Load existing connections to preserve non-SDM connections
|
|
preserved_connections = load_existing_connections(output_file)
|
|
preserved_count = sum(len(conns) for conns in preserved_connections.values())
|
|
if preserved_count > 0:
|
|
print(f"📁 Found {preserved_count} existing non-SDM connections")
|
|
|
|
sdm_data = run_sdm_status()
|
|
if not sdm_data:
|
|
return
|
|
|
|
connections = parse_redis_connections(sdm_data)
|
|
print(f"🔍 Found {len(connections)} Redis connections from SDM")
|
|
|
|
groups = group_connections(connections)
|
|
for group_name, conns in groups.items():
|
|
print(f" {group_name}: {len(conns)} connections")
|
|
|
|
yaml_content = generate_yaml_content(groups, preserved_connections)
|
|
|
|
try:
|
|
with open(output_file, 'w') as f:
|
|
f.write(yaml_content)
|
|
print(f"✅ Successfully generated {output_file}")
|
|
if preserved_count > 0:
|
|
print(f"💾 Preserved {preserved_count} non-SDM connections")
|
|
except Exception as e:
|
|
print(f"❌ Error writing file: {e}")
|
|
|
|
if __name__ == '__main__':
|
|
main() |