import logging from django import forms from django.contrib import admin from django.core.exceptions import ValidationError from django.db.models import Q from lib.decorators import readonly from manager.models import CloneContainer, DevContainer from mikrotik.models import DNSStatic from proxmox.models import Lxc @readonly def resync_all(*args, **kwargs): from proxmox.models import Lxc from lib.proxmox import Proxmox from mikrotik.models import DNSStatic, IPDHCPLease from manager.models import DevContainer from mikrotik.admin import sync_ipaddress_from_mikrotik, sync_ipdhcplease_from_mikrotik, sync_dns_static_from_mikrotik from proxmox.admin import sync_all_lxc_templates_from_proxmox, sync_all_lxc_from_proxmox fulls = [] pm = Proxmox() lxcs = {} leases = {} dnse = {} sync_all_lxc_templates_from_proxmox() sync_all_lxc_from_proxmox() sync_ipaddress_from_mikrotik() sync_ipdhcplease_from_mikrotik() sync_dns_static_from_mikrotik() for lxc in Lxc.objects.all(): lxcs[lxc.hwaddr.upper()] = lxc for lease in IPDHCPLease.objects.all(): leases[lease.mac_address.upper()] = lease for dns in DNSStatic.objects.all(): dnse[dns.address.upper()] = dns container = {c.hwaddr.upper():c for c in DevContainer.objects.all()} for hwaddr, lxc in lxcs.items(): if hwaddr not in leases: logging.warning(f'LXC {lxc} has no DHCP lease') continue lease = leases[hwaddr] if lease.address not in dnse: logging.warning(f'DHCP lease {lease} for {lxc} has no DNS entry') continue dns = dnse[lease.address] if hwaddr in container: container[hwaddr].dns = dns container[hwaddr].lease = lease container[hwaddr].save() elif lxc: DevContainer.objects.create( dns=dns, lease=lease, lxc=lxc, ) # Now remove the non lxc devcontainers DevContainer.objects.filter(lxc__isnull=True).delete() def shell_baseimport(): from proxmox.models import Lxc from lib.proxmox import Proxmox from mikrotik.models import DNSStatic, IPDHCPLease, IPAddress from manager.models import DevContainer from lib.mikrotik import MikrotikModelMixin fulls = [] for empt in (DevContainer, Lxc, IPAddress, IPDHCPLease, DNSStatic): if empt.objects.count() != 0: fulls.append(empt) if fulls: msg = [] queries = [] for f in fulls: logging.error(f'{f.__name__} is not empty.') queries.append(f'DELETE FROM {f._meta.db_table};') msg.append( f"\n\nPlease delete all objects and try again.\n" f"\nThis can only be done with a raw query\n" ) msg.append('\n'.join(queries)) logging.error('\n'.join(msg) + '\n\n') raise ValidationError("Some tables not empty - see above output") pm = Proxmox() lxcs = {} for lxc in pm.get_all_lxc(as_dict=False): lxc.save() lxcs[lxc.hwaddr.upper()] = lxc addresses = IPAddress.get_all_as_object() for a in addresses: a.save() leases = {} for lease in IPDHCPLease.get_all_as_object(): if isinstance(lease, MikrotikModelMixin): lease.save() if lease.mac_address in lxcs: leases[lease.address.upper()] = lease else: logging.warning(f'IPDHCPLease {lease} is not a MikrotikModelMixin') dnse = {} for dns in DNSStatic.get_all_as_object(): if isinstance(dns, MikrotikModelMixin): dns.save() if dns.address in leases: dnse[dns.address.upper()] = dns else: logging.warning(f'DNSStatic {dns} is not a MikrotikModelMixin') for _a, dns in dnse.items(): lease = leases[_a] lxc = lxcs[lease.mac_address.upper()] DevContainer.objects.get_or_create( lxc=lxc, dns=dns, lease=lease, ) class DevContainerAdminForm(forms.ModelForm): disksize = forms.IntegerField( required=False, min_value=1, max_value=100, help_text="Disk Size of rootfs - can not be shrinked" ) cores = forms.IntegerField( required=False, min_value=1, help_text="Number of Cores" ) memory = forms.CharField( required=False, help_text="Memory in MB" ) class Meta: model = DevContainer fields = '__all__' def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) instance = kwargs.get('instance') if instance and hasattr(instance, 'lxc') and instance.lxc: self.fields['disksize'].initial = instance.lxc.disksize self.fields['disksize'].min_value = instance.lxc.disksize self.fields['cores'].initial = instance.lxc.cores self.fields['memory'].initial = instance.lxc.memory def save(self, commit=True): instance = super().save(commit=False) if hasattr(instance, 'lxc') and instance.lxc: lxc = instance.lxc if 'disksize' in self.cleaned_data and self.cleaned_data['disksize']: lxc.disksize = self.cleaned_data['disksize'] if 'cores' in self.cleaned_data and self.cleaned_data['cores']: lxc.cores = self.cleaned_data['cores'] if 'memory' in self.cleaned_data and self.cleaned_data['memory']: lxc.memory = self.cleaned_data['memory'] lxc.save() if commit: instance.save() return instance def clone_selected_containers(modeladmin, request, queryset): for container in queryset: container.execute() class CloneContainerAdminForm(forms.ModelForm): class Meta: model = CloneContainer fields = '__all__' def clean_name(self): name = self.cleaned_data.get('name') if not name: return name dns_domain = Q(name=name) | Q(name__endswith=name) | Q(name__endswith=name.replace('.', r'\.')) if DNSStatic.objects.filter(dns_domain).exists(): raise ValidationError(f"Ein DNS-Eintrag mit dem Namen '{name}' existiert bereits.") if Lxc.objects.filter(hostname=name).exists(): raise ValidationError(f"A LXC with name or regex '{name}' exists.") # Prüfe, ob der Name bereits als CloneContainer-Name existiert # Ausschluss der aktuellen Instanz bei Updates existing_clone_query = CloneContainer.objects.filter(name=name) if self.instance.pk: existing_clone_query = existing_clone_query.exclude(pk=self.instance.pk) if existing_clone_query.exists(): raise ValidationError(f"A CloneContainer with name '{name}' exists.") return name def clean(self): cleaned_data = super().clean() if not cleaned_data.get('template') and not cleaned_data.get('vm'): raise ValidationError("Please select a template or a VM.") return cleaned_data @admin.register(CloneContainer) class CloneContainerAdmin(admin.ModelAdmin): autocomplete_fields = ['vm', 'network'] list_display = ('hostname', 'cloned_from', 'network', 'memory', 'cores', 'disksize', 'status',) search_fields = ('hostname', 'vm__name', 'vm__vmid',) actions = [clone_selected_containers] form = CloneContainerAdminForm def cloned_from(self, obj): return obj.vm cloned_from.short_description = 'Cloned from' cloned_from.admin_order_field = 'vm__name' @admin.register(DevContainer) class DevContainerAdmin(admin.ModelAdmin): form = DevContainerAdminForm autocomplete_fields = ['dns', 'lxc', 'lease'] # list_display = ('name', 'address', 'hostname_or_regexp', 'lxc__disksize', 'hwaddr', 'status',) list_filter = ( 'lxc__status', 'lease__status', ('lxc', admin.EmptyFieldListFilter), ('lease', admin.EmptyFieldListFilter), ('dns', admin.EmptyFieldListFilter), ) search_fields = ('dns__name', 'dns__address', 'lease__address', 'dns__regexp') actions = [resync_all] def hostname_or_regexp(self, obj): return obj.hostname hostname_or_regexp.short_description = 'Hostname or Regexp' hostname_or_regexp.admin_order_field = 'dns__name'