| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263 |
- #!/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()
|