class Host(models.Model): fqdn = models.CharField(unique=True, max_length=255, verbose_name=_("FQDN")) external_hostname = models.CharField(max_length=255, blank=True, verbose_name=_("External hostname")) external_ipv4 = netfields.InetAddressField( blank=True, null=True, store_prefix_length=False, verbose_name=_("External IPv4 address")) internal_ipv4 = netfields.InetAddressField( blank=True, null=True, verbose_name=_("Internal IPv4 address")) tunnel_ipv4 = netfields.InetAddressField( blank=True, null=True, verbose_name=_("Tunnel IPv4 address")) comment = models.CharField(max_length=255, blank=True, verbose_name=_("Comment")) private = models.BooleanField(default=False, verbose_name=_("Private")) autonomous_system = models.ForeignKey(AutonomousSystem, on_delete=models.CASCADE, related_name='hosts', verbose_name=_("Autonomous System")) objects = netfields.NetManager() class Meta: ordering = ['fqdn'] verbose_name = ungettext_lazy("Host", "Hosts", 1) verbose_name_plural = ungettext_lazy("Host", "Hosts", 2) @classmethod def get_view_qs(cls, user): return cls.objects.filter( Q(private=False) | Q(autonomous_system__institution__owners=user)).distinct('fqdn') @property def institution(self): return self.autonomous_system.institution @property def has_geo(self): return self.autonomous_system.has_geo def __str__(self): return self.fqdn def can_edit(self, user): return self.autonomous_system.can_edit(user)
class IPv4Subnet(models.Model): network = netfields.CidrAddressField(unique=True, verbose_name=_("Network")) dns_server = netfields.InetAddressField(blank=True, null=True, store_prefix_length=False, verbose_name=_("DNS Server")) comment = models.CharField(max_length=255, blank=True, null=True, verbose_name=_("Comment")) private = models.BooleanField(default=False, verbose_name=_("Private")) institution = models.ForeignKey(Institution, on_delete=models.CASCADE, related_name='ipv4_subnets', verbose_name=_("Institution")) objects = netfields.NetManager() class Meta: ordering = ['network'] verbose_name = ungettext_lazy("IPv4 Subnet", "IPv4 Subnets", 1) verbose_name_plural = ungettext_lazy("IPv4 Subnet", "IPv4 Subnets", 2) @classmethod def get_view_qs(cls, user): return cls.objects.filter( Q(private=False) | Q(institution__owners=user)).distinct('network') def __str__(self): return str(self.network) def can_edit(self, user): return self.institution.can_edit(user)
class IPAddress(models.Model): address = netfields.InetAddressField(primary_key=True) ptr = models.ForeignKey('DomainName', null=True, blank=True) class Meta: app_label = 'admtooCore' verbose_name_plural = 'IP Addresses' def __str__(self): return str(self.address)
class Vlan(models.Model): vlan_id = models.IntegerField(primary_key=True) name = models.CharField(max_length=64, null=False) ip_block = netfields.CidrAddressField(unique=True, null=False) gateway = netfields.InetAddressField(unique=True, null=False) class Meta: app_label = 'admtooCore' def __str__(self): return str(self.name)
class ServerInetAttribute(ServerAttribute): attribute = models.ForeignKey( Attribute, db_index=False, on_delete=models.CASCADE, limit_choices_to=dict(type='inet'), ) value = netfields.InetAddressField() class Meta: app_label = 'serverdb' db_table = 'server_inet_attribute' unique_together = [['server', 'attribute', 'value']] index_together = [['attribute', 'value']]
class TunnelEndpoint(models.Model): override_internal_ipv4 = netfields.InetAddressField( blank=True, null=True, verbose_name=_("Override internal IPv4 address")) dynamic_ipv4 = models.BooleanField(default=False, verbose_name=_("Dynamic IPv4 address")) host = models.ForeignKey(Host, models.CASCADE, related_name='tunnel_endpoints', verbose_name=_("Host")) objects = netfields.NetManager() @property def tunnel(self): if hasattr(self, 'tunnel1'): return self.tunnel1 if hasattr(self, 'tunnel2'): return self.tunnel2 return None @property def autonomous_system(self): return self.host.autonomous_system @property def institution(self): return self.autonomous_system.institution @property def internal_ipv4(self): return self.override_internal_ipv4 or self.host.tunnel_ipv4 @property def has_geo(self): return self.autonomous_system.has_geo def can_edit(self, user): return self.host.can_edit(user) def is_config_complete(self): return False
class ServerInetAttribute(ServerAttribute): attribute = models.ForeignKey( Attribute, db_index=False, on_delete=models.CASCADE, limit_choices_to=dict(type='inet'), ) value = netfields.InetAddressField() class Meta: app_label = 'serverdb' db_table = 'server_inet_attribute' unique_together = [['server', 'attribute', 'value']] index_together = [['attribute', 'value']] def clean(self): super(ServerAttribute, self).clean() if type(self.value) not in [IPv4Interface, IPv6Interface]: self.value = inet_to_python(self.value) # Get the ip_addr_type of the servertype ip_addr_type = self.server.servertype.ip_addr_type if ip_addr_type == 'null': # A Servertype with ip_addr_type "null" and attributes of type # inet must be denied per configuration. This is just a safety net # in case e.g. somebody creates them programmatically. raise ValidationError(_('%(attribute_id)s must be null'), code='invalid value', params={'attribute_id': self.attribute_id}) elif ip_addr_type == 'host': is_ip_address(self.value) is_unique_ip(self.value, self.server.server_id) elif ip_addr_type == 'loadbalancer': is_ip_address(self.value) elif ip_addr_type == 'network': is_network(self.value) network_overlaps(self.value, self.server.servertype_id, self.server.server_id)
class Server(models.Model): """Servers are the main objects of the system. They are stored in entity-attribute-value schema. There are multiple models to store the attribute values of the servers by different data types. """ objects = netfields.NetManager() server_id = models.AutoField(primary_key=True) hostname = models.CharField(max_length=64, unique=True, validators=HOSTNAME_VALIDATORS) intern_ip = netfields.InetAddressField(null=True, blank=True) servertype = models.ForeignKey(Servertype, on_delete=models.PROTECT) class Meta: app_label = 'serverdb' db_table = 'server' def __str__(self): return self.hostname def get_supernet(self, servertype): return Server.objects.get( servertype=servertype, intern_ip__net_contains_or_equals=self.intern_ip, ) def clean(self, *args, **kwargs): super(Server, self).clean(*args, **kwargs) if self.servertype.ip_addr_type == 'null': if self.intern_ip is not None: raise ValidationError('IP address must be null.') else: if self.intern_ip is None: raise ValidationError('IP address must not be null.') if self.servertype.ip_addr_type == 'network': self._validate_network_intern_ip() else: self._validate_host_intern_ip() def _validate_host_intern_ip(self): if self.intern_ip.max_prefixlen != self.netmask_len(): raise ValidationError('Netmask length must be {0}.'.format( self.intern_ip.max_prefixlen)) # Check for other server with overlapping addresses for server in Server.objects.filter( intern_ip__net_overlaps=self.intern_ip).exclude( server_id=self.server_id): if server.servertype.ip_addr_type == 'host': raise ValidationError( 'IP address already taken by the host "{0}".'.format( server.hostname)) def _validate_network_intern_ip(self): try: ip_network(str(self.intern_ip)) except ValueError as error: raise ValidationError(str(error)) # Check for other server with overlapping addresses for server in Server.objects.filter( intern_ip__net_overlaps=self.intern_ip).exclude( server_id=self.server_id): if self.servertype == server.servertype: raise ValidationError( 'IP address overlaps with "{0}" in the same ' 'servertype.'.format(server.hostname)) def netmask_len(self): return self.intern_ip.network.prefixlen def get_attributes(self, attribute): model = ServerAttribute.get_model(attribute.type) return model.objects.filter(server=self, attribute=attribute) def add_attribute(self, attribute, value): model = ServerAttribute.get_model(attribute.type) if model is ServerBooleanAttribute and not value: return server_attribute = model(server=self, attribute=attribute) server_attribute.save_value(value)
class Server(models.Model): """Servers are the main objects of the system. They are stored in entity-attribute-value schema. There are multiple models to store the attribute values of the servers by different data types. """ objects = netfields.NetManager() server_id = models.AutoField(primary_key=True) hostname = models.CharField(max_length=254, unique=True, validators=HOSTNAME_VALIDATORS) intern_ip = netfields.InetAddressField(null=True, blank=True) servertype = models.ForeignKey(Servertype, on_delete=models.PROTECT) class Meta: app_label = 'serverdb' db_table = 'server' def __str__(self): return self.hostname def get_supernet(self, servertype): return Server.objects.get( servertype=servertype, intern_ip__net_contains_or_equals=self.intern_ip, ) def clean(self): super(Server, self).clean() ip_addr_type = self.servertype.ip_addr_type if ip_addr_type == 'null': if self.intern_ip is not None: raise ValidationError(_('intern_ip must be null'), code='invalid value') else: # This is special to intern_ip for inet attributes this is covered # by making them required. if self.intern_ip is None: raise ValidationError(_('intern_ip must not be null'), code='missing value') # TODO: This logic is duplicated to the ServerInetAttribute clean # method but can be removed when we remove the special # intern_ip. if type(self.intern_ip) not in [IPv4Interface, IPv6Interface]: self.intern_ip = inet_to_python(self.intern_ip) if ip_addr_type == 'host': is_ip_address(self.intern_ip) is_unique_ip(self.intern_ip, self.server_id) elif ip_addr_type == 'loadbalancer': is_ip_address(self.intern_ip) elif ip_addr_type == 'network': is_network(self.intern_ip) network_overlaps(self.intern_ip, self.servertype.servertype_id, self.server_id) def get_attributes(self, attribute): model = ServerAttribute.get_model(attribute.type) return model.objects.filter(server=self, attribute=attribute) def add_attribute(self, attribute, value): model = ServerAttribute.get_model(attribute.type) if model is ServerBooleanAttribute and not value: return server_attribute = model(server=self, attribute=attribute) server_attribute.save_value(value)