diff --git a/mdb/admin.py b/mdb/admin.py index ee3ce25..e025f20 100644 --- a/mdb/admin.py +++ b/mdb/admin.py @@ -1,5 +1,8 @@ -from mdb.models import * -from django.contrib import admin +from django.contrib import admin, messages + +from mdb.models import Ip6Address, Interface, Ip4Address, DhcpOption, DhcpCustomField, DomainSrvRecord, DomainTxtRecord, \ + DomainCnameRecord, DomainARecord, Domain, Host, Ip4Subnet, Ip6Subnet, Nameserver, MailExchange, OperatingSystem, \ + HostType, DhcpConfig class Ip6AddressInline(admin.TabularInline): @@ -40,6 +43,26 @@ class HostAdmin(admin.ModelAdmin): # ('kerberos_principal_name', 'kerberos_principal_created_date')) # }), ) + actions = ['set_installable'] + + def _get_host_warning_message(self, host, ifs): + ifs_str = ', '.join([_if.name for _if in ifs]) + return 'Host {} might not be installable since interface(s) {} has no IP set.'.format(host, ifs_str) + + def set_installable(self, request, queryset): + invalid_ifs_per_host = [] + + for obj in queryset: + invalid_ifs = obj.interface_set.filter(ip4address__isnull=True) + if invalid_ifs.exists(): + invalid_ifs_per_host.append(self._get_host_warning_message(obj, invalid_ifs)) + obj.pxe_installable = True + obj.save() # triggers post save signal + + if invalid_ifs_per_host: + self.message_user(request, message='\n'.join(invalid_ifs_per_host), level=messages.WARNING) + + set_installable.short_description = "Mark selected hosts as PXE installable" def get_queryset(self, request): return super(HostAdmin, self).get_queryset(request).select_related() diff --git a/mdb/models.py b/mdb/models.py index b23f135..de99e47 100644 --- a/mdb/models.py +++ b/mdb/models.py @@ -53,12 +53,12 @@ class Domain(models.Model): def num_records(self): host_a_records = Host.objects.filter(interface__domain=self).count() size = { - "cname": self.domaincnamerecord_set.count(), - "srv": self.domainsrvrecord_set.count(), - "txt": self.domaintxtrecord_set.count(), - "a": self.domainarecord_set.count() + host_a_records + "CNAME": self.domaincnamerecord_set.count(), + "SRV": self.domainsrvrecord_set.count(), + "TXT": self.domaintxtrecord_set.count(), + "A": self.domainarecord_set.count() + host_a_records } - return size + return ', '.join(['{}: {}'.format(k, v) for k, v in size.items()]) num_records.short_description = "Num Records" @@ -526,11 +526,11 @@ class Host(models.Model): def mac_addresses(self): addresses = self.interface_set.filter(macaddr__isnull=False).values_list('macaddr', flat=True) - return ",".join(addresses) + return ", ".join(addresses) def ip_addresses(self): addresses = self.interface_set.filter(ip4address__isnull=False).values_list('ip4address__address', flat=True) - return ",".join(addresses) + return ", ".join(addresses) def as_pxe_files(self): return host_as_pxe_files(self) diff --git a/mdb/signals.py b/mdb/signals.py index d18e467..bd91bbc 100644 --- a/mdb/signals.py +++ b/mdb/signals.py @@ -87,21 +87,40 @@ def update_domain_serial_when_change_to_host(sender, instance, created, **kwargs @receiver(post_save, sender=Host) def create_pxe_key_and_write_pxe_files_when_host_changes(sender, instance, created, **kwargs): - if not instance.pxe_key: + host = instance + + if not host.pxe_key: # generate key (prevent infinite recursion by using update) pxe_key = uuid.uuid4().hex - Host.objects.filter(pk=instance.pk).update(pxe_key=pxe_key) - instance.pxe_key = pxe_key + Host.objects.filter(pk=host.pk).update(pxe_key=pxe_key) + host.pxe_key = pxe_key - for pxe_file_name, pxe_file in instance.as_pxe_files(): + for pxe_file_name, pxe_file in host.as_pxe_files(): path = os.path.join(settings.MDB_PXE_TFTP_ROOT, pxe_file_name) - if instance.pxe_installable: + if host.pxe_installable: with open(path, 'w+') as f: f.write(pxe_file) logger.info("Created or updated {}".format(path)) + + # Also set necessary fields for pxe installation + interfaces = host.interface_set.exclude(ip4address__isnull=True) + + for _if in interfaces: + changed = False + if not _if.pxe_filename: + _if.pxe_filename = 'pxelinux.0' # don't overwrite + changed = True + if not _if.dhcp_client: + _if.dhcp_client = True + changed = True + + if changed: + _if.save() + else: - os.unlink(path) - logger.info("deleted {}".format(path)) + if os.path.exists(path): + os.unlink(path) + logger.info("deleted {}".format(path)) @receiver(pre_delete, sender=Interface) diff --git a/mdb/utils.py b/mdb/utils.py index 19e734b..766d5c1 100644 --- a/mdb/utils.py +++ b/mdb/utils.py @@ -68,13 +68,12 @@ def get_pxe_filename(interface, filename_format='mac_addr'): def host_as_pxe_files(host): - """ Returns a list of filename,content for each host """ + """ Returns a list of filename,content for each host interface with an IPv4 address """ from mdb.models import Host assert isinstance(host, Host) pxe_files = [] - ifs = host.interface_set.filter(dhcp_client=True) - for _if in ifs: + for _if in host.interface_set.exclude(ip4address__isnull=True): filename = get_pxe_filename(_if) content = render_pxelinux_cfg(_if, host) pxe_files.append((filename, content))