From bc6029e2891d6be6e0c228875fe86681e1f7e44d Mon Sep 17 00:00:00 2001 From: Nikolai R Kristiansen Date: Sat, 22 Aug 2015 23:52:16 +0200 Subject: [PATCH] Admin optimizations and formatting fixes - Removes model OSArchitecture - Denormalizes OS.architecture into OS.arch --- mdb/admin.py | 30 ++++-- mdb/migrations/0003_auto_20150822_2156.py | 27 ++++++ mdb/migrations/0004_auto_20150822_2157.py | 22 +++++ mdb/migrations/0005_auto_20150822_2204.py | 21 ++++ mdb/models.py | 113 +++++++++------------- requirements.txt | 2 +- 6 files changed, 135 insertions(+), 80 deletions(-) create mode 100644 mdb/migrations/0003_auto_20150822_2156.py create mode 100644 mdb/migrations/0004_auto_20150822_2157.py create mode 100644 mdb/migrations/0005_auto_20150822_2204.py diff --git a/mdb/admin.py b/mdb/admin.py index 025b606..6ababb0 100644 --- a/mdb/admin.py +++ b/mdb/admin.py @@ -12,6 +12,9 @@ class InterfaceInline(admin.TabularInline): model = Interface extra = 0 + def get_queryset(self, request): + return super(InterfaceInline, self).get_queryset(request).select_related() + class HostAdmin(admin.ModelAdmin): ordering = ('hostname',) @@ -27,21 +30,25 @@ class HostAdmin(admin.ModelAdmin): 'fields': ('owner', 'location', 'description' ) }), ('Hardware and Software Information', { - 'fields': (('brand', 'model'), 'serial_number', ('hostname', 'host_type'), ('operating_system', 'virtual')) - }), - ('Domain and Kerberos Information', { - 'description': 'If this host is a member of the LDAP domain, you need to tick the request kerberos principal checkbox. A principal will then be created for the host.', - 'classes': ['collapse'], - 'fields': ('request_kerberos_principal', 'kerberos_principal_created', - ('kerberos_principal_name', 'kerberos_principal_created_date')) + 'fields': (('brand', 'model', 'serial_number'), ('hostname', 'host_type'), ('operating_system', 'virtual')) }), + # FIXME: Not in use + # ('Domain and Kerberos Information', { + # 'description': 'If this host is a member of the LDAP domain, you need to tick the request kerberos principal checkbox. A principal will then be created for the host.', + # 'classes': ['collapse'], + # 'fields': ('request_kerberos_principal', 'kerberos_principal_created', + # ('kerberos_principal_name', 'kerberos_principal_created_date')) + # }), ) + def get_queryset(self, request): + return super(HostAdmin, self).get_queryset(request).select_related() + class Ip4AddressInline(admin.TabularInline): model = Ip4Address extra = 0 - readonly_fields = ['address'] + readonly_fields = ['address', 'assigned_to_host'] class DhcpOptionInline(admin.TabularInline): @@ -58,6 +65,10 @@ class SubnetAdmin(admin.ModelAdmin): list_display = ['name', 'network', 'netmask', 'num_addresses', 'broadcast_address', 'first_address', 'last_address'] inlines = [DhcpOptionInline, DhcpCustomFieldInline, Ip4AddressInline] + def get_queryset(self, request): + # TODO: optimize + return super(SubnetAdmin, self).get_queryset(request) + class DomainSrvRecordInline(admin.TabularInline): model = DomainSrvRecord @@ -90,7 +101,7 @@ class HostTypeAdmin(admin.ModelAdmin): class OperatingSystemAdmin(admin.ModelAdmin): - list_display = ['name', 'version', 'architecture'] + list_display = ['name', 'version', 'arch'] admin.site.register(Domain, DomainAdmin) @@ -101,7 +112,6 @@ admin.site.register(Ip6Address) admin.site.register(Nameserver) admin.site.register(MailExchange) admin.site.register(OperatingSystem, OperatingSystemAdmin) -admin.site.register(OsArchitecture) admin.site.register(HostType, HostTypeAdmin) admin.site.register(DhcpConfig) admin.site.register(DhcpOption) diff --git a/mdb/migrations/0003_auto_20150822_2156.py b/mdb/migrations/0003_auto_20150822_2156.py new file mode 100644 index 0000000..30082c3 --- /dev/null +++ b/mdb/migrations/0003_auto_20150822_2156.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +import django.core.validators +import re + + +class Migration(migrations.Migration): + + dependencies = [ + ('mdb', '0002_auto_20141229_2309'), + ] + + operations = [ + migrations.AddField( + model_name='operatingsystem', + name='arch', + field=models.CharField(blank=True, max_length=255, null=True, choices=[(b'mips', 'MIPS'), (b'arm', 'ARM'), (b'broadcom', 'Broadcom'), (b'rc32300', 'RC32300'), (b'powerpc', 'PowerPC'), (b'powerpc403ga', 'PowerPC403GA'), (b'unknown', 'Unknown'), (b'x86_64', 'x86-64'), (b'i386', 'x86')]), + preserve_default=True, + ), + migrations.AlterField( + model_name='interface', + name='macaddr', + field=models.CharField(max_length=17, validators=[django.core.validators.RegexValidator(regex=re.compile(b'^([0-9A-F]{2}[:]){5}([0-9A-F]{2})$', 2), message='Enter a valid MAC address', code=b'invalid')]), + ), + ] diff --git a/mdb/migrations/0004_auto_20150822_2157.py b/mdb/migrations/0004_auto_20150822_2157.py new file mode 100644 index 0000000..09bab8b --- /dev/null +++ b/mdb/migrations/0004_auto_20150822_2157.py @@ -0,0 +1,22 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations +from django.db.migrations import RunPython + + +def flatten_architecture(apps, schema_editor): + OperatingSystem = apps.get_model("mdb", "OperatingSystem") + for os in OperatingSystem.objects.all(): + os.arch = os.architecture.architecture + os.save() + + +class Migration(migrations.Migration): + dependencies = [ + ('mdb', '0003_auto_20150822_2156'), + ] + + operations = [ + RunPython(flatten_architecture), + ] diff --git a/mdb/migrations/0005_auto_20150822_2204.py b/mdb/migrations/0005_auto_20150822_2204.py new file mode 100644 index 0000000..49e8df3 --- /dev/null +++ b/mdb/migrations/0005_auto_20150822_2204.py @@ -0,0 +1,21 @@ +# -*- coding: utf-8 -*- +from __future__ import unicode_literals + +from django.db import models, migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('mdb', '0004_auto_20150822_2157'), + ] + + operations = [ + migrations.RemoveField( + model_name='operatingsystem', + name='architecture', + ), + migrations.DeleteModel( + name='OsArchitecture', + ), + ] diff --git a/mdb/models.py b/mdb/models.py index 3f2dff1..9efa001 100644 --- a/mdb/models.py +++ b/mdb/models.py @@ -1,6 +1,9 @@ from django.db import models +from django.db.models import Count from django.db.models.signals import post_save, pre_save, pre_delete from django.dispatch import receiver +from django.utils.translation import ugettext_lazy as _ + from validators import validate_hostname, validate_macaddr @@ -418,7 +421,7 @@ class DhcpCustomField(models.Model): value = models.CharField(max_length=255) ip4subnet = models.ForeignKey(Ip4Subnet) - class meta: + class Meta: verbose_name = 'DHCP custom field' @@ -429,16 +432,18 @@ class Ip4Address(models.Model): ping_avg_rtt = models.FloatField(null=True, blank=True) def __unicode__(self): - # FIXME needs optimizing, runs a SQL query for each run - if self.interface_set.count() == 0: + # FIXME: generates 2 SQL queries + _if = self.interface_set.all() + if not _if: return self.address - else: - return "%s (%s)" % ( - self.address, - self.interface_set.get().host.hostname) + + return "{} ({})".format( + self.address, + _if.get().host.hostname + ) def assigned_to_host(self): - self.interface_set.get().host + return self.interface_set.get().host assigned_to_host.short_description = "Assigned to Host" @@ -461,23 +466,24 @@ class HostType(models.Model): ordering = ("host_type",) -class OsArchitecture(models.Model): - architecture = models.CharField(max_length=64) - - def __unicode__(self): - return self.architecture - - class Meta: - verbose_name = 'OS architecture' - - class OperatingSystem(models.Model): + OS_ARCHITECTURES = ( + ('mips', _('MIPS')), + ('arm', _('ARM')), + ('broadcom', _('Broadcom')), + ('rc32300', _('RC32300')), + ('powerpc', _('PowerPC')), + ('powerpc403ga', _('PowerPC403GA')), + ('unknown', _('Unknown')), + ('x86_64', _('x86-64')), + ('i386', _('x86')), + ) name = models.CharField(max_length=256) version = models.CharField(max_length=64) - architecture = models.ForeignKey(OsArchitecture) + arch = models.CharField(max_length=255, choices=OS_ARCHITECTURES, blank=True, null=True) def __unicode__(self): - return self.name + " " + self.version + " (" + unicode(self.architecture) + ")" + return self.name + " " + self.version + " (" + self.arch + ")" class Meta: ordering = ("name", "version") @@ -498,61 +504,32 @@ class Host(models.Model): request_kerberos_principal = models.BooleanField(default=False) kerberos_principal_created = models.BooleanField(default=False, editable=False) - kerberos_principal_name = models.CharField(max_length = 256, editable=False) + kerberos_principal_name = models.CharField(max_length=256, editable=False) kerberos_principal_created_date = models.DateTimeField(null=True, blank=True, editable=False) def __unicode__(self): return self.hostname def in_domain(self): - domains = [] - for interface in self.interface_set.all(): - if interface.domain: - domains.append(unicode(interface.domain)) - return ",".join(domains) + domains = self.interface_set.values_list('domain__domain_name', flat=True) + return u",".join(domains) in_domain.short_description = "in domains" def ipv6_enabled(self): - for interface in self.interface_set.all(): - if interface.ipv6_enabled(): - return True - return False + ipv6_ifs = self.interface_set.annotate(num_ipv6=Count('ip6address')).filter(num_ipv6__gt=0) + return ipv6_ifs.exists() - # ipv6_enabled.boolean = True + ipv6_enabled.boolean = True def mac_addresses(self): - addresses = [] - for interface in self.interface_set.all(): - if interface.macaddr is None: - continue - addresses.append(interface.macaddr) + addresses = self.interface_set.filter(macaddr__isnull=False).values_list('macaddr', flat=True) return ",".join(addresses) def ip_addresses(self): - addresses = [] - for interface in self.interface_set.all(): - if interface.ip4address is None: - continue - addresses.append(interface.ip4address.address) + addresses = self.interface_set.filter(ip4address__isnull=False).values_list('ip4address__address', flat=True) return ",".join(addresses) - def get_ip_addresses(self): - addresses = [] - for interface in self.interface_set.all(): - if interface.ip4address is None: - continue - addresses.append(interface.ip4address.address) - return addresses - - def get_ip_addresses_for_domain(self, domain): - addresses = [] - for interface in self.interface_set.all(): - if interface.ip4address is None: - continue - addresses.append(interface.ip4address.address) - return addresses - class Interface(models.Model): name = models.CharField(max_length=128) @@ -649,13 +626,13 @@ def set_domain_name_for_subnet(sender, instance, **kwargs): instance.domain_name = "%s.in-addr.arpa" % rev # update it's own serial - #if instance.domain_serial is not None: - # instance.domain_serial = format_domain_serial_and_add_one(instance.domain_serial) + # if instance.domain_serial is not None: + # instance.domain_serial = format_domain_serial_and_add_one(instance.domain_serial) # lets update the serial of the dhcp config # when the subnet is changed if instance.dhcp_config: -# instance.dhcp_config.serial = instance.dhcp_config.serial + 1 + # instance.dhcp_config.serial = instance.dhcp_config.serial + 1 instance.dhcp_config.serial = format_domain_serial_and_add_one(instance.dhcp_config.serial) instance.dhcp_config.save() @@ -687,13 +664,11 @@ def update_domain_serial_when_change_to_host(sender, instance, created, **kwargs for interface in instance.interface_set.all(): if interface.domain is not None: domain = interface.domain - domain.domain_serial = \ - format_domain_serial_and_add_one(domain.domain_serial) + domain.domain_serial = format_domain_serial_and_add_one(domain.domain_serial) domain.save() if interface.ip4address is not None: subnet = interface.ip4address.subnet - subnet.domain_serial = \ - format_domain_serial_and_add_one(subnet.domain_serial) + subnet.domain_serial = format_domain_serial_and_add_one(subnet.domain_serial) subnet.save() @@ -701,14 +676,14 @@ def update_domain_serial_when_change_to_host(sender, instance, created, **kwargs def update_domain_serial_when_interface_deleted(sender, instance, **kwargs): if instance.domain is not None: domain = instance.domain - domain.domain_serial = domain.domain_serial + 1 + domain.domain_serial += 1 domain.save() if instance.ip4address is not None: subnet = instance.ip4address.subnet - subnet.domain_serial = subnet.domain_serial + 1 + subnet.domain_serial += 1 subnet.save() -#@receiver(pre_save, sender=Domain) -#def update_domain_serial_when_domain_is_saved(sender, instance, **kwargs): -# instance.domain_serial = format_domain_serial_and_add_one(instance.domain_serial) +# @receiver(pre_save, sender=Domain) +# def update_domain_serial_when_domain_is_saved(sender, instance, **kwargs): +# instance.domain_serial = format_domain_serial_and_add_one(instance.domain_serial) diff --git a/requirements.txt b/requirements.txt index 7441a45..9bf7dda 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,4 +1,4 @@ -Django==1.7 +Django>=1.7,<1.8 MySQL-python==1.2.5 ipaddr==2.1.11 django-auth-ldap