Esempio n. 1
0
class DiscoveryResult(models.Model):
    discovery_request = models.ForeignKey(
        to=DiscoveryRequest,
        related_name='results',
        on_delete=models.CASCADE,
    )
    device = models.ForeignKey(
        to=Device,
        related_name='+',
        null=True,
        blank=True,
        on_delete=models.SET_NULL,
    )
    address = IPAddressField()
    status = models.PositiveSmallIntegerField(
        choices=RESULT_STATUS_CHOICES,
        default=RESULT_STATUS_TRYING
    )

    def __str__(self):
        return '{} {}'.format(self.address, self.status)

    def get_absolute_url(self):
        return reverse('np_autodiscovery:discoveryresult', args=[self.pk])

    def get_status_class(self):
        return RESULT_STATUS_CLASSES[self.status]
class ISPActiveDevice(ChangeLoggedModel):
    name = models.CharField(max_length=255)
    comments = models.TextField(null=True, blank=True)
    manufacturer = models.ForeignKey(Manufacturer, on_delete=models.PROTECT)
    device_type = models.ForeignKey(DeviceType, on_delete=models.PROTECT)
    site = models.ForeignKey(Site, on_delete=models.PROTECT)
    ip_address = IPAddressField(blank=True, null=True, default="")
    uuid = models.CharField(max_length=255)
    type = models.CharField(max_length=255, null=True, blank=True)
class BgpPeering(ChangeLoggedModel):
    site = models.ForeignKey(to="dcim.Site",
                             on_delete=models.SET_NULL,
                             blank=True,
                             null=True)
    device = models.ForeignKey(to="dcim.Device", on_delete=models.PROTECT)
    local_ip = models.ForeignKey(to="ipam.IPAddress", on_delete=models.PROTECT)
    local_as = ASNField(help_text="32-bit ASN used locally")
    remote_ip = IPAddressField(help_text="IPv4 or IPv6 address (with mask)")
    remote_as = ASNField(help_text="32-bit ASN used by peer")
    peer_name = models.CharField(max_length=64, blank=True)
    description = models.CharField(max_length=200, blank=True)
Esempio n. 4
0
class RadioAccessPoint(Equipment):
    ANTENNA_FREQUENCY_CHOICES = (
        ("900mhz", "900"),
        ("2.4ghz", "2.4"),
        ("3.5ghz", "3.5"),
        ("5ghz", "5"),
    )

    frequency = models.CharField(max_length=30, choices=ANTENNA_FREQUENCY_CHOICES)
    name = models.CharField(max_length=30)
    antenna = models.ForeignKey(AntennaProfile, on_delete=models.PROTECT)
    ip_address = IPAddressField(blank=True, null=True, default="")

    def get_absolute_url(self):
        return reverse("plugins:netbox_netisp:radioaccesspoint", args=[self.pk])

    def __str__(self):
        return "{0}".format(self.name)
Esempio n. 5
0
File: ip.py Progetto: zhyh329/netbox
class IPAddress(PrimaryModel):
    """
    An IPAddress represents an individual IPv4 or IPv6 address and its mask. The mask length should match what is
    configured in the real world. (Typically, only loopback interfaces are configured with /32 or /128 masks.) Like
    Prefixes, IPAddresses can optionally be assigned to a VRF. An IPAddress can optionally be assigned to an Interface.
    Interfaces can have zero or more IPAddresses assigned to them.

    An IPAddress can also optionally point to a NAT inside IP, designating itself as a NAT outside IP. This is useful,
    for example, when mapping public addresses to private addresses. When an Interface has been assigned an IPAddress
    which has a NAT outside IP, that Interface's Device can use either the inside or outside IP as its primary IP.
    """
    address = IPAddressField(help_text='IPv4 or IPv6 address (with mask)')
    vrf = models.ForeignKey(to='ipam.VRF',
                            on_delete=models.PROTECT,
                            related_name='ip_addresses',
                            blank=True,
                            null=True,
                            verbose_name='VRF')
    tenant = models.ForeignKey(to='tenancy.Tenant',
                               on_delete=models.PROTECT,
                               related_name='ip_addresses',
                               blank=True,
                               null=True)
    status = models.CharField(max_length=50,
                              choices=IPAddressStatusChoices,
                              default=IPAddressStatusChoices.STATUS_ACTIVE,
                              help_text='The operational status of this IP')
    role = models.CharField(max_length=50,
                            choices=IPAddressRoleChoices,
                            blank=True,
                            help_text='The functional role of this IP')
    assigned_object_type = models.ForeignKey(
        to=ContentType,
        limit_choices_to=IPADDRESS_ASSIGNMENT_MODELS,
        on_delete=models.PROTECT,
        related_name='+',
        blank=True,
        null=True)
    assigned_object_id = models.PositiveIntegerField(blank=True, null=True)
    assigned_object = GenericForeignKey(ct_field='assigned_object_type',
                                        fk_field='assigned_object_id')
    nat_inside = models.OneToOneField(
        to='self',
        on_delete=models.SET_NULL,
        related_name='nat_outside',
        blank=True,
        null=True,
        verbose_name='NAT (Inside)',
        help_text='The IP for which this address is the "outside" IP')
    dns_name = models.CharField(
        max_length=255,
        blank=True,
        validators=[DNSValidator],
        verbose_name='DNS Name',
        help_text='Hostname or FQDN (not case-sensitive)')
    description = models.CharField(max_length=200, blank=True)

    objects = IPAddressManager()

    csv_headers = [
        'address',
        'vrf',
        'tenant',
        'status',
        'role',
        'assigned_object_type',
        'assigned_object_id',
        'is_primary',
        'dns_name',
        'description',
    ]
    clone_fields = [
        'vrf',
        'tenant',
        'status',
        'role',
        'description',
    ]

    class Meta:
        ordering = ('address', 'pk')  # address may be non-unique
        verbose_name = 'IP address'
        verbose_name_plural = 'IP addresses'

    def __str__(self):
        return str(self.address)

    def get_absolute_url(self):
        return reverse('ipam:ipaddress', args=[self.pk])

    def get_duplicates(self):
        return IPAddress.objects.filter(
            vrf=self.vrf,
            address__net_host=str(self.address.ip)).exclude(pk=self.pk)

    def clean(self):
        super().clean()

        if self.address:

            # /0 masks are not acceptable
            if self.address.prefixlen == 0:
                raise ValidationError(
                    {'address': "Cannot create IP address with /0 mask."})

            # Enforce unique IP space (if applicable)
            if (self.vrf is None and settings.ENFORCE_GLOBAL_UNIQUE) or (
                    self.vrf and self.vrf.enforce_unique):
                duplicate_ips = self.get_duplicates()
                if duplicate_ips and (
                        self.role not in IPADDRESS_ROLES_NONUNIQUE
                        or any(dip.role not in IPADDRESS_ROLES_NONUNIQUE
                               for dip in duplicate_ips)):
                    raise ValidationError({
                        'address':
                        "Duplicate IP address found in {}: {}".format(
                            "VRF {}".format(self.vrf)
                            if self.vrf else "global table",
                            duplicate_ips.first(),
                        )
                    })

        # Check for primary IP assignment that doesn't match the assigned device/VM
        if self.pk:
            device = Device.objects.filter(
                Q(primary_ip4=self) | Q(primary_ip6=self)).first()
            if device:
                if getattr(self.assigned_object, 'device', None) != device:
                    raise ValidationError({
                        'interface':
                        f"IP address is primary for device {device} but not assigned to it!"
                    })
            vm = VirtualMachine.objects.filter(
                Q(primary_ip4=self) | Q(primary_ip6=self)).first()
            if vm:
                if getattr(self.assigned_object, 'virtual_machine',
                           None) != vm:
                    raise ValidationError({
                        'vminterface':
                        f"IP address is primary for virtual machine {vm} but not assigned to it!"
                    })

        # Validate IP status selection
        if self.status == IPAddressStatusChoices.STATUS_SLAAC and self.family != 6:
            raise ValidationError(
                {'status': "Only IPv6 addresses can be assigned SLAAC status"})

    def save(self, *args, **kwargs):

        # Force dns_name to lowercase
        self.dns_name = self.dns_name.lower()

        super().save(*args, **kwargs)

    def to_objectchange(self, action):
        # Annotate the assigned object, if any
        return super().to_objectchange(action,
                                       related_object=self.assigned_object)

    def to_csv(self):

        # Determine if this IP is primary for a Device
        is_primary = False
        if self.address.version == 4 and getattr(self, 'primary_ip4_for',
                                                 False):
            is_primary = True
        elif self.address.version == 6 and getattr(self, 'primary_ip6_for',
                                                   False):
            is_primary = True

        obj_type = None
        if self.assigned_object_type:
            obj_type = f'{self.assigned_object_type.app_label}.{self.assigned_object_type.model}'

        return (
            self.address,
            self.vrf.name if self.vrf else None,
            self.tenant.name if self.tenant else None,
            self.get_status_display(),
            self.get_role_display(),
            obj_type,
            self.assigned_object_id,
            is_primary,
            self.dns_name,
            self.description,
        )

    @property
    def family(self):
        if self.address:
            return self.address.version
        return None

    def _set_mask_length(self, value):
        """
        Expose the IPNetwork object's prefixlen attribute on the parent model so that it can be manipulated directly,
        e.g. for bulk editing.
        """
        if self.address is not None:
            self.address.prefixlen = value

    mask_length = property(fset=_set_mask_length)

    def get_status_class(self):
        return IPAddressStatusChoices.CSS_CLASSES.get(self.status)

    def get_role_class(self):
        return IPAddressRoleChoices.CSS_CLASSES.get(self.role)
class CustomerPremiseEquipment(Equipment):
    ip_address = IPAddressField()
Esempio n. 7
0
class IPRange(PrimaryModel):
    """
    A range of IP addresses, defined by start and end addresses.
    """
    start_address = IPAddressField(
        help_text='IPv4 or IPv6 address (with mask)')
    end_address = IPAddressField(help_text='IPv4 or IPv6 address (with mask)')
    size = models.PositiveIntegerField(editable=False)
    vrf = models.ForeignKey(to='ipam.VRF',
                            on_delete=models.PROTECT,
                            related_name='ip_ranges',
                            blank=True,
                            null=True,
                            verbose_name='VRF')
    tenant = models.ForeignKey(to='tenancy.Tenant',
                               on_delete=models.PROTECT,
                               related_name='ip_ranges',
                               blank=True,
                               null=True)
    status = models.CharField(max_length=50,
                              choices=IPRangeStatusChoices,
                              default=IPRangeStatusChoices.STATUS_ACTIVE,
                              help_text='Operational status of this range')
    role = models.ForeignKey(to='ipam.Role',
                             on_delete=models.SET_NULL,
                             related_name='ip_ranges',
                             blank=True,
                             null=True,
                             help_text='The primary function of this range')
    description = models.CharField(max_length=200, blank=True)

    clone_fields = [
        'vrf',
        'tenant',
        'status',
        'role',
        'description',
    ]

    class Meta:
        ordering = (F('vrf').asc(nulls_first=True), 'start_address', 'pk'
                    )  # (vrf, start_address) may be non-unique
        verbose_name = 'IP range'
        verbose_name_plural = 'IP ranges'

    def __str__(self):
        return self.name

    def get_absolute_url(self):
        return reverse('ipam:iprange', args=[self.pk])

    def clean(self):
        super().clean()

        if self.start_address and self.end_address:

            # Check that start & end IP versions match
            if self.start_address.version != self.end_address.version:
                raise ValidationError({
                    'end_address':
                    f"Ending address version (IPv{self.end_address.version}) does not match starting "
                    f"address (IPv{self.start_address.version})"
                })

            # Check that the start & end IP prefix lengths match
            if self.start_address.prefixlen != self.end_address.prefixlen:
                raise ValidationError({
                    'end_address':
                    f"Ending address mask (/{self.end_address.prefixlen}) does not match starting "
                    f"address mask (/{self.start_address.prefixlen})"
                })

            # Check that the ending address is greater than the starting address
            if not self.end_address > self.start_address:
                raise ValidationError({
                    'end_address':
                    f"Ending address must be lower than the starting address ({self.start_address})"
                })

            # Check for overlapping ranges
            overlapping_range = IPRange.objects.exclude(pk=self.pk).filter(
                vrf=self.vrf
            ).filter(
                Q(start_address__gte=self.start_address,
                  start_address__lte=self.end_address) |  # Starts inside
                Q(end_address__gte=self.start_address,
                  end_address__lte=self.end_address) |  # Ends inside
                Q(start_address__lte=self.start_address,
                  end_address__gte=self.end_address)  # Starts & ends outside
            ).first()
            if overlapping_range:
                raise ValidationError(
                    f"Defined addresses overlap with range {overlapping_range} in VRF {self.vrf}"
                )

            # Validate maximum size
            MAX_SIZE = 2**32 - 1
            if int(self.end_address.ip - self.start_address.ip) + 1 > MAX_SIZE:
                raise ValidationError(
                    f"Defined range exceeds maximum supported size ({MAX_SIZE})"
                )

    def save(self, *args, **kwargs):

        # Record the range's size (number of IP addresses)
        self.size = int(self.end_address.ip - self.start_address.ip) + 1

        super().save(*args, **kwargs)

    @property
    def family(self):
        return self.start_address.version if self.start_address else None

    @property
    def range(self):
        return netaddr.IPRange(self.start_address.ip, self.end_address.ip)

    @property
    def mask_length(self):
        return self.start_address.prefixlen if self.start_address else None

    @cached_property
    def name(self):
        """
        Return an efficient string representation of the IP range.
        """
        separator = ':' if self.family == 6 else '.'
        start_chunks = str(self.start_address.ip).split(separator)
        end_chunks = str(self.end_address.ip).split(separator)

        base_chunks = []
        for a, b in zip(start_chunks, end_chunks):
            if a == b:
                base_chunks.append(a)

        base_str = separator.join(base_chunks)
        start_str = separator.join(start_chunks[len(base_chunks):])
        end_str = separator.join(end_chunks[len(base_chunks):])

        return f'{base_str}{separator}{start_str}-{end_str}/{self.start_address.prefixlen}'

    def _set_prefix_length(self, value):
        """
        Expose the IPRange object's prefixlen attribute on the parent model so that it can be manipulated directly,
        e.g. for bulk editing.
        """
        self.start_address.prefixlen = value
        self.end_address.prefixlen = value

    prefix_length = property(fset=_set_prefix_length)

    def get_status_class(self):
        return IPRangeStatusChoices.CSS_CLASSES.get(self.status)

    def get_child_ips(self):
        """
        Return all IPAddresses within this IPRange and VRF.
        """
        return IPAddress.objects.filter(address__gte=self.start_address,
                                        address__lte=self.end_address,
                                        vrf=self.vrf)

    def get_available_ips(self):
        """
        Return all available IPs within this range as an IPSet.
        """
        range = netaddr.IPRange(self.start_address.ip, self.end_address.ip)
        child_ips = netaddr.IPSet(
            [ip.address.ip for ip in self.get_child_ips()])

        return netaddr.IPSet(range) - child_ips

    @cached_property
    def first_available_ip(self):
        """
        Return the first available IP within the range (or None).
        """
        available_ips = self.get_available_ips()
        if not available_ips:
            return None

        return '{}/{}'.format(next(available_ips.__iter__()),
                              self.start_address.prefixlen)

    @cached_property
    def utilization(self):
        """
        Determine the utilization of the range and return it as a percentage.
        """
        # Compile an IPSet to avoid counting duplicate IPs
        child_count = netaddr.IPSet(
            [ip.address.ip for ip in self.get_child_ips()]).size

        return int(float(child_count) / self.size * 100)