import logging from functools import cached_property from django.db import models from django.db.models.signals import post_save, pre_delete, pre_save from django.dispatch import receiver from lib.db import TaskAwareModelMixin from lib.decorators import skip_signal from lib.mikrotik import MikrotikModelMixin, is_local_ip class DNSStatic(MikrotikModelMixin, TaskAwareModelMixin): id = models.CharField(max_length=150, null=True, blank=True, default='') name = models.CharField(max_length=150, null=True, blank=True, unique=True, ) address = models.CharField(max_length=150, null=True, blank=True, default='') ttl = models.CharField(max_length=150, null=True, blank=True, default='') dynamic = models.CharField(max_length=150, null=True, blank=True, default='', editable=False) regexp = models.CharField(max_length=150, null=True, blank=True, unique=True, ) # lease = models.ForeignKey('IPDHCPLease', on_delete=models.SET_NULL, null=True, blank=True, default=None, related_name='dns_statics', name) @property def unique_on_router(self): return ['address', ('name', 'regexp')] @property def get_self_params(self): return {f:getattr(self, f) for f in ['address', 'name', 'regexp', 'comment'] if getattr(self, f, None)} def __str__(self): return f'{self.address} - {self.name or self.regexp}' @property def no_mikrotik_props(self): return super().no_mikrotik_props + ['ttl'] @property def router_base(self): return '/ip/dns/static' @cached_property def router_list(self): ret = [] for r in self.router_get_all: if is_local_ip(r['address']): ret.append(r) self.response = ret return self.response class IPAddress(MikrotikModelMixin, TaskAwareModelMixin): id = models.CharField(max_length=150, null=True, blank=True, default='') address = models.CharField(max_length=150, null=True, blank=True, default='') network = models.CharField(max_length=150, null=True, blank=True, default='') interface = models.CharField(max_length=150, null=True, blank=True, default='') actual_interface = models.CharField(max_length=150, null=True, blank=True, default='') invalid = models.CharField(max_length=150, null=True, blank=True, default='') dynamic = models.CharField(max_length=150, null=True, blank=True, default='', editable=False) @property def unique_on_router(self): return ['address', 'network', 'interface'] def __str__(self): return f'{self.address} - {self.comment}' @property def router_base(self): return '/ip/address' @property def no_mikrotik_props(self): return super().no_mikrotik_props + [ 'actual_interface', 'invalid', ] @property def get_next_ip(self): # sync_from_mikrotik(IPDHCPLease) net = '.'.join(self.network.split('.')[:-1]) + '.' used = sorted(IPDHCPLease.objects.filter(address__startswith=net).values_list('address', flat=True), reverse=True) next32 = 1 if used: next32 += int(used[0].split('.')[-1]) return f'{net}{next32}' @cached_property def router_list(self): """ { "id": "*XXX", "address": "X.X.X.X/XX", "network": "X.X.X.X", "interface": "<>", "actual-interface": "", "invalid": "false", "dynamic": "true", "disabled": "false" } """ ret = [] for r in self.router_get_all: if all([ r['invalid'] == 'false', r['disabled'] == 'false', 'container' in r.get('comment', '').lower(), is_local_ip(r['address']), r['address'].endswith('.1/24'), ]): ret.append(r) return ret class IPDHCPLease(MikrotikModelMixin, TaskAwareModelMixin): id = models.CharField(max_length=150, null=True, blank=True, default='') address = models.CharField(max_length=150, null=True, blank=True, default='') mac_address = models.CharField(max_length=150, null=True, blank=True, default='') client_id = models.CharField(max_length=150, null=True, blank=True, default='') hostname = models.CharField(max_length=150, null=True, blank=True, default='') valid_until = models.CharField(max_length=150, null=True, blank=True, default='') dynamic = models.CharField(max_length=150, null=True, blank=True, default='', editable=False) blocked = models.CharField(max_length=150, null=True, blank=True, default='') active_client_id = models.CharField(max_length=150, null=True, blank=True, default='') active_mac_address = models.CharField(max_length=150, null=True, blank=True, default='') expires_after = models.CharField(max_length=150, null=True, blank=True, default='') age = models.CharField(max_length=150, null=True, blank=True, default='') active_server = models.CharField(max_length=150, null=True, blank=True, default='') active_address = models.CharField(max_length=150, null=True, blank=True, default='') host_name = models.CharField(max_length=150, null=True, blank=True, default='') radius = models.CharField(max_length=150, null=True, blank=True, default='') last_seen = models.CharField(max_length=150, null=True, blank=True, default='') dhcp_option = models.CharField(max_length=150, null=True, blank=True, default='') status = models.CharField(max_length=150, null=True, blank=True, default='') server = models.CharField(max_length=150, null=True, blank=True, default='') address_lists = models.CharField(max_length=150, null=True, blank=True, default='') always_broadcast = models.CharField(max_length=150, null=True, blank=True, default='') lease_time = models.CharField(max_length=150, null=True, blank=True, default='') @property def unique_on_router(self): return ['address', 'nac_address'] @property def mikrotik_send_params(self): return { 'address': self.address, 'mac-address': self.mac_address, 'comment': self.comment, } def save(self, *args, **kwargs): self.mac_address = self.mac_address.upper() if self.mac_address else '' super().save(*args, **kwargs) @property def network_24(self): return '.'.join(self.address.split('.')[:-1]) + '.' def __str__(self): return f'{self.address} - {self.mac_address} - {self.status}' @property def router_base(self): return '/ip/dhcp-server/lease' @cached_property def router_list(self): return self.router_get_all @property def no_mikrotik_props(self): return super().no_mikrotik_props + [ 'active_client_id', 'active_mac_address', 'expires_after', 'age', 'active_server', 'active_address', 'hostname', 'host_name', 'radius', 'last_seen', 'status', 'lease_time', 'server', ] for cl in [IPAddress, IPDHCPLease, DNSStatic]: @receiver(pre_save, sender=cl) def send_before_save(sender, instance: MikrotikModelMixin, **kwargs): if instance._state.adding: logging.info(f'Created {instance} via pre_save event - do nothing') return instance try: response = instance.sync_to_router() logging.debug(f'Update {instance} to router with {response}') except Exception as e: logging.error(f'Error while updating {instance} to router: {e}') return instance @receiver(post_save, sender=cl) @skip_signal(signaltype='post_save') def send_after_save(sender, instance: MikrotikModelMixin, created, **kwargs): if created: logging.info(f'Created {instance} via post_save event - sync once') instance.sync_to_router() return instance # @skip_signal(signaltype='pre_delete') @receiver(pre_delete, sender=cl) def send_before_delete(sender, instance, **kwargs): try: response = instance.delete_from_router() logging.debug(f'Deleted {instance} from router with {response}') except Exception as e: logging.error(f'Error while deleting {instance} from router: {e}') return instance