Files
postgres-handholder/clusters/models.py

200 lines
7.1 KiB
Python

from django.db import models
from django.contrib.auth.models import User
from django.core.validators import MinValueValidator, MaxValueValidator
import json
class PostgresCluster(models.Model):
"""Model for managing Postgres cluster configurations."""
CLUSTER_TYPES = [
('single', 'Single Instance'),
('replica', 'Primary with Replicas'),
('standby', 'Standby Cluster'),
]
DEPLOYMENT_TYPES = [
('docker', 'Docker'),
('kubernetes', 'Kubernetes'),
('local', 'Local Linux'),
]
name = models.CharField(max_length=100, unique=True)
description = models.TextField(blank=True)
cluster_type = models.CharField(max_length=20, choices=CLUSTER_TYPES, default='single')
deployment_type = models.CharField(max_length=20, choices=DEPLOYMENT_TYPES, default='docker')
# PostgreSQL Configuration
postgres_version = models.CharField(max_length=10, default='15')
port = models.IntegerField(default=5432, validators=[MinValueValidator(1024), MaxValueValidator(65535)])
data_directory = models.CharField(max_length=255, default='/var/lib/postgresql/data')
# Resource Configuration
cpu_limit = models.CharField(max_length=20, default='2')
memory_limit = models.CharField(max_length=20, default='4Gi')
storage_size = models.CharField(max_length=20, default='10Gi')
# Network Configuration
host = models.CharField(max_length=255, default='localhost')
external_port = models.IntegerField(default=5432, validators=[MinValueValidator(1024), MaxValueValidator(65535)])
# Authentication
admin_user = models.CharField(max_length=50, default='postgres')
admin_password = models.CharField(max_length=255)
# Extensions and Libraries
extensions = models.JSONField(default=list, blank=True)
libraries = models.JSONField(default=list, blank=True)
# TLS Configuration
tls_enabled = models.BooleanField(default=False)
tls_cert_path = models.CharField(max_length=255, blank=True)
tls_key_path = models.CharField(max_length=255, blank=True)
tls_ca_path = models.CharField(max_length=255, blank=True)
# Connection Pooling
pgpool_enabled = models.BooleanField(default=False)
pgpool_instances = models.IntegerField(default=1, validators=[MinValueValidator(1), MaxValueValidator(10)])
# Status
status = models.CharField(max_length=20, default='stopped', choices=[
('running', 'Running'),
('stopped', 'Stopped'),
('starting', 'Starting'),
('stopping', 'Stopping'),
('error', 'Error'),
('updating', 'Updating'),
])
# Metadata
created_by = models.ForeignKey(User, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
ordering = ['-created_at']
def __str__(self):
return self.name
def get_connection_string(self):
"""Get the connection string for this cluster."""
return f"postgresql://{self.admin_user}:{self.admin_password}@{self.host}:{self.external_port}/postgres"
def get_extensions_list(self):
"""Get the list of enabled extensions."""
return self.extensions if isinstance(self.extensions, list) else []
def get_libraries_list(self):
"""Get the list of enabled libraries."""
return self.libraries if isinstance(self.libraries, list) else []
class PostgresInstance(models.Model):
"""Model for managing individual Postgres instances within a cluster."""
INSTANCE_TYPES = [
('primary', 'Primary'),
('replica', 'Replica'),
('standby', 'Standby'),
]
cluster = models.ForeignKey(PostgresCluster, on_delete=models.CASCADE, related_name='instances')
name = models.CharField(max_length=100)
instance_type = models.CharField(max_length=20, choices=INSTANCE_TYPES, default='primary')
# Instance Configuration
host = models.CharField(max_length=255)
port = models.IntegerField(validators=[MinValueValidator(1024), MaxValueValidator(65535)])
data_directory = models.CharField(max_length=255)
# Status
status = models.CharField(max_length=20, default='stopped', choices=[
('running', 'Running'),
('stopped', 'Stopped'),
('starting', 'Starting'),
('stopping', 'Stopping'),
('error', 'Error'),
('syncing', 'Syncing'),
])
# Replication Configuration (for replicas)
replication_slot = models.CharField(max_length=100, blank=True)
lag_seconds = models.IntegerField(default=0)
# Resource Usage
cpu_usage = models.FloatField(default=0.0)
memory_usage = models.FloatField(default=0.0)
disk_usage = models.FloatField(default=0.0)
# Metadata
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
unique_together = ['cluster', 'name']
ordering = ['instance_type', 'name']
def __str__(self):
return f"{self.cluster.name} - {self.name}"
class ClusterUser(models.Model):
"""Model for managing database users within clusters."""
cluster = models.ForeignKey(PostgresCluster, on_delete=models.CASCADE, related_name='users')
username = models.CharField(max_length=50)
password = models.CharField(max_length=255)
is_superuser = models.BooleanField(default=False)
can_create_db = models.BooleanField(default=False)
can_login = models.BooleanField(default=True)
# Permissions
permissions = models.JSONField(default=dict, blank=True)
# Metadata
created_by = models.ForeignKey(User, on_delete=models.CASCADE)
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
unique_together = ['cluster', 'username']
ordering = ['username']
def __str__(self):
return f"{self.cluster.name} - {self.username}"
class ClusterDatabase(models.Model):
"""Model for managing databases within clusters."""
cluster = models.ForeignKey(PostgresCluster, on_delete=models.CASCADE, related_name='databases')
name = models.CharField(max_length=50)
owner = models.ForeignKey(ClusterUser, on_delete=models.CASCADE)
# Configuration
encoding = models.CharField(max_length=20, default='UTF8')
collation = models.CharField(max_length=50, default='en_US.utf8')
ctype = models.CharField(max_length=50, default='en_US.utf8')
# Size tracking
size_bytes = models.BigIntegerField(default=0)
# Metadata
created_at = models.DateTimeField(auto_now_add=True)
updated_at = models.DateTimeField(auto_now=True)
class Meta:
unique_together = ['cluster', 'name']
ordering = ['name']
def __str__(self):
return f"{self.cluster.name} - {self.name}"
def get_size_mb(self):
"""Get database size in MB."""
return round(self.size_bytes / (1024 * 1024), 2)
def get_size_gb(self):
"""Get database size in GB."""
return round(self.size_bytes / (1024 * 1024 * 1024), 2)