Initial Django project setup for Postgres cluster management - Created Django project structure with postgres_handholder main app - Added clusters, backups, and monitoring apps - Implemented comprehensive models for PostgresCluster, PostgresInstance, ClusterUser, ClusterDatabase - Added forms and views for cluster management - Created Bootstrap 5 templates with modern UI - Added Docker and Docker Compose configuration - Included Celery for background tasks - Added comprehensive requirements.txt with all dependencies - Updated README with installation and usage instructions - Added environment configuration example - Set up proper URL routing and app structure
This commit is contained in:
152
clusters/forms.py
Normal file
152
clusters/forms.py
Normal file
@ -0,0 +1,152 @@
|
||||
from django import forms
|
||||
from django.contrib.auth.models import User
|
||||
from .models import PostgresCluster, ClusterUser, ClusterDatabase
|
||||
|
||||
|
||||
class PostgresClusterForm(forms.ModelForm):
|
||||
"""Form for creating and editing Postgres clusters."""
|
||||
|
||||
class Meta:
|
||||
model = PostgresCluster
|
||||
fields = [
|
||||
'name', 'description', 'cluster_type', 'deployment_type',
|
||||
'postgres_version', 'port', 'data_directory',
|
||||
'cpu_limit', 'memory_limit', 'storage_size',
|
||||
'host', 'external_port', 'admin_user', 'admin_password',
|
||||
'extensions', 'libraries', 'tls_enabled', 'tls_cert_path',
|
||||
'tls_key_path', 'tls_ca_path', 'pgpool_enabled', 'pgpool_instances'
|
||||
]
|
||||
widgets = {
|
||||
'name': forms.TextInput(attrs={'class': 'form-control'}),
|
||||
'description': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
|
||||
'cluster_type': forms.Select(attrs={'class': 'form-select'}),
|
||||
'deployment_type': forms.Select(attrs={'class': 'form-select'}),
|
||||
'postgres_version': forms.Select(attrs={'class': 'form-select'}, choices=[
|
||||
('13', 'PostgreSQL 13'),
|
||||
('14', 'PostgreSQL 14'),
|
||||
('15', 'PostgreSQL 15'),
|
||||
('16', 'PostgreSQL 16'),
|
||||
('17', 'PostgreSQL 17'),
|
||||
]),
|
||||
'port': forms.NumberInput(attrs={'class': 'form-control'}),
|
||||
'data_directory': forms.TextInput(attrs={'class': 'form-control'}),
|
||||
'cpu_limit': forms.TextInput(attrs={'class': 'form-control'}),
|
||||
'memory_limit': forms.TextInput(attrs={'class': 'form-control'}),
|
||||
'storage_size': forms.TextInput(attrs={'class': 'form-control'}),
|
||||
'host': forms.TextInput(attrs={'class': 'form-control'}),
|
||||
'external_port': forms.NumberInput(attrs={'class': 'form-control'}),
|
||||
'admin_user': forms.TextInput(attrs={'class': 'form-control'}),
|
||||
'admin_password': forms.PasswordInput(attrs={'class': 'form-control'}),
|
||||
'tls_cert_path': forms.TextInput(attrs={'class': 'form-control'}),
|
||||
'tls_key_path': forms.TextInput(attrs={'class': 'form-control'}),
|
||||
'tls_ca_path': forms.TextInput(attrs={'class': 'form-control'}),
|
||||
'pgpool_instances': forms.NumberInput(attrs={'class': 'form-control'}),
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
# Set default values for extensions and libraries
|
||||
if not self.instance.pk:
|
||||
self.fields['extensions'].initial = [
|
||||
'pg_stat_statements',
|
||||
'pg_auth_mon',
|
||||
]
|
||||
self.fields['libraries'].initial = [
|
||||
'bg_mon',
|
||||
'pg_stat_statements',
|
||||
'pgextwlist',
|
||||
'pg_auth_mon',
|
||||
]
|
||||
|
||||
def clean_name(self):
|
||||
name = self.cleaned_data['name']
|
||||
if not name.replace('-', '').replace('_', '').isalnum():
|
||||
raise forms.ValidationError(
|
||||
'Cluster name can only contain letters, numbers, hyphens, and underscores.'
|
||||
)
|
||||
return name
|
||||
|
||||
def clean_admin_password(self):
|
||||
password = self.cleaned_data['admin_password']
|
||||
if len(password) < 8:
|
||||
raise forms.ValidationError(
|
||||
'Admin password must be at least 8 characters long.'
|
||||
)
|
||||
return password
|
||||
|
||||
|
||||
class ClusterUserForm(forms.ModelForm):
|
||||
"""Form for creating and editing cluster users."""
|
||||
|
||||
confirm_password = forms.CharField(
|
||||
widget=forms.PasswordInput(attrs={'class': 'form-control'}),
|
||||
help_text='Confirm the password'
|
||||
)
|
||||
|
||||
class Meta:
|
||||
model = ClusterUser
|
||||
fields = [
|
||||
'username', 'password', 'is_superuser', 'can_create_db',
|
||||
'can_login', 'permissions'
|
||||
]
|
||||
widgets = {
|
||||
'username': forms.TextInput(attrs={'class': 'form-control'}),
|
||||
'password': forms.PasswordInput(attrs={'class': 'form-control'}),
|
||||
'is_superuser': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
|
||||
'can_create_db': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
|
||||
'can_login': forms.CheckboxInput(attrs={'class': 'form-check-input'}),
|
||||
'permissions': forms.Textarea(attrs={'class': 'form-control', 'rows': 3}),
|
||||
}
|
||||
|
||||
def clean_username(self):
|
||||
username = self.cleaned_data['username']
|
||||
if not username.replace('_', '').isalnum():
|
||||
raise forms.ValidationError(
|
||||
'Username can only contain letters, numbers, and underscores.'
|
||||
)
|
||||
return username
|
||||
|
||||
def clean(self):
|
||||
cleaned_data = super().clean()
|
||||
password = cleaned_data.get('password')
|
||||
confirm_password = cleaned_data.get('confirm_password')
|
||||
|
||||
if password and confirm_password and password != confirm_password:
|
||||
raise forms.ValidationError('Passwords do not match.')
|
||||
|
||||
return cleaned_data
|
||||
|
||||
|
||||
class ClusterDatabaseForm(forms.ModelForm):
|
||||
"""Form for creating and editing cluster databases."""
|
||||
|
||||
class Meta:
|
||||
model = ClusterDatabase
|
||||
fields = ['name', 'owner', 'encoding', 'collation', 'ctype']
|
||||
widgets = {
|
||||
'name': forms.TextInput(attrs={'class': 'form-control'}),
|
||||
'owner': forms.Select(attrs={'class': 'form-select'}),
|
||||
'encoding': forms.Select(attrs={'class': 'form-select'}, choices=[
|
||||
('UTF8', 'UTF8'),
|
||||
('LATIN1', 'LATIN1'),
|
||||
('SQL_ASCII', 'SQL_ASCII'),
|
||||
]),
|
||||
'collation': forms.TextInput(attrs={'class': 'form-control'}),
|
||||
'ctype': forms.TextInput(attrs={'class': 'form-control'}),
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
cluster = kwargs.pop('cluster', None)
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
if cluster:
|
||||
self.fields['owner'].queryset = cluster.users.all()
|
||||
|
||||
def clean_name(self):
|
||||
name = self.cleaned_data['name']
|
||||
if not name.replace('_', '').isalnum():
|
||||
raise forms.ValidationError(
|
||||
'Database name can only contain letters, numbers, and underscores.'
|
||||
)
|
||||
return name
|
Reference in New Issue
Block a user