Esempio n. 1
0
 def forwards(self):
     db.delete_column("ip_ipv4block", "prefix")
     db.add_column("ip_ipv4block", "prefix", CIDRField("prefix", null=True))
     db.execute("UPDATE ip_ipv4block SET prefix=prefix_cidr")
     db.delete_column("ip_ipv4block", "prefix_cidr")
     db.execute("ALTER TABLE ip_ipv4block ALTER prefix SET NOT NULL")
     db.execute("DROP TRIGGER t_ip_ipv4block_modify ON ip_ipv4block")
     db.execute("DROP FUNCTION f_trigger_ip_ipv4block()")
     db.delete_column("ip_ipv4blockaccess", "prefix")
     db.add_column("ip_ipv4blockaccess", "prefix",
                   CIDRField("prefix", null=True))
     db.execute("UPDATE ip_ipv4blockaccess SET prefix=prefix_cidr")
     db.delete_column("ip_ipv4blockaccess", "prefix_cidr")
     db.execute("ALTER TABLE ip_ipv4blockaccess ALTER prefix SET NOT NULL")
     db.execute(
         "DROP TRIGGER t_ip_ipv4blockaccess_modify ON ip_ipv4blockaccess")
     db.execute("DROP FUNCTION f_trigger_ip_ipv4blockaccess()")
     db.execute(RAW_SQL)
Esempio n. 2
0
class IPPool(models.Model):
    class Meta:
        verbose_name = _("IP Pool")
        verbose_name_plural = _("IP Pools")
        db_table = "ip_ippool"
        app_label = "ip"

    termination_group = models.ForeignKey(TerminationGroup,
                                          verbose_name=_("Termination Group"))
    name = models.CharField(_("Pool name"), max_length=64, default="default")
    vrf = models.ForeignKey(VRF, verbose_name=_("VRF"))
    afi = models.CharField(_("Address Family"),
                           max_length=1,
                           choices=AFI_CHOICES)
    type = models.CharField(_("Type"),
                            max_length=1,
                            choices=[("D", "Dynamic"), ("S", "Static")])
    technologies = TextArrayField(_("Technologies"), default=["IPoE"])
    from_address = CIDRField(_("From Address"))
    to_address = CIDRField(_("To Address"))

    def __unicode__(self):
        return u"%s %s %s %s -- %s" % (self.termination_group.name, self.type,
                                       self.name, self.from_address,
                                       self.to_address)

    def clean(self):
        """
        Field validation
        """
        print "@@@ CLEAN"
        super(IPPool, self).clean()
        # Check prefix is of AFI type
        if self.afi == "4":
            check_ipv4(self.from_address)
            check_ipv4(self.to_address)
        elif self.afi == "6":
            check_ipv6(self.from_address)
            check_ipv6(self.to_address)
Esempio n. 3
0
class VCBindFilter(models.Model):
    class Meta:
        verbose_name = "VC Bind Filter"
        verbose_name_plural = "VC Bind Filters"
        db_table = "vc_vcbindfilter"
        app_label = "vc"

    vc_domain = models.ForeignKey(VCDomain, verbose_name="VC Domain")
    vrf = models.ForeignKey("ip.VRF", verbose_name="VRF")
    afi = models.CharField("Address Family",
                           max_length=1,
                           choices=AFI_CHOICES,
                           default="4")
    prefix = CIDRField("Prefix")
    vc_filter = models.ForeignKey(VCFilter, verbose_name="VC Filter")

    def __unicode__(self):
        return u"%s %s %s %s" % (self.vc_domain, self.vrf, self.prefix,
                                 self.vc_filter)

    @classmethod
    def get_vcs(cls, vrf, afi, prefix):
        """
        Returns queryset with all suitable VCs
        """
        if hasattr(prefix, "prefix"):
            prefix = prefix.prefix
        c = connection.cursor()
        c.execute(
            """
            SELECT v.id,v.l1,vf.id
            FROM
                vc_vcdomain d JOIN vc_vcbindfilter f ON (d.id=f.vc_domain_id)
                JOIN vc_vcfilter vf ON (f.vc_filter_id=vf.id)
                JOIN vc_vc v ON (v.vc_domain_id=d.id)
            WHERE
                    f.vrf_id=%s
                AND f.afi=%s
                AND f.prefix>>=%s
        """, [vrf.id, afi, prefix])
        vcs = set()  # vc.id
        F = {}  # id -> filter
        for vc_id, l1, vf_id in c.fetchall():
            try:
                f = F[vf_id]
            except KeyError:
                f = VCFilter.objects.get(id=vf_id)
                F[vf_id] = f
            if f.check(l1):
                vcs.add(vc_id)
        return VC.objects.filter(id__in=vcs).order_by("l1")
Esempio n. 4
0
class PrefixTablePrefix(models.Model):
    class Meta:
        verbose_name = _("Prefix")
        verbose_name_plural = _("Prefixes")
        db_table = "main_prefixtableprefix"
        unique_together = [("table", "afi", "prefix")]
        ordering = ["table", "afi", "prefix"]

    table = models.ForeignKey(PrefixTable,
        verbose_name=_("Prefix Table"))
    afi = models.CharField(_("Address Family"), max_length=1,
            choices=[("4", _("IPv4")), ("6", _("IPv6"))])
    prefix = CIDRField(_("Prefix"))

    def __unicode__(self):
        return u"%s %s" % (self.table.name, self.prefix)

    def save(self, *args, **kwargs):
        # Set AFI
        self.afi = IP.prefix(self.prefix).afi
        return super(PrefixTablePrefix, self).save(*args, **kwargs)
Esempio n. 5
0
class Prefix(models.Model):
    """
    Allocated prefix
    """
    class Meta:
        verbose_name = _("Prefix")
        verbose_name_plural = _("Prefixes")
        db_table = "ip_prefix"
        app_label = "ip"
        unique_together = [("vrf", "afi", "prefix")]

    parent = models.ForeignKey("self",
                               related_name="children_set",
                               verbose_name=_("Parent"),
                               null=True,
                               blank=True)
    vrf = models.ForeignKey(VRF, verbose_name=_("VRF"), default=VRF.get_global)
    afi = models.CharField(_("Address Family"),
                           max_length=1,
                           choices=AFI_CHOICES)
    prefix = CIDRField(_("Prefix"))
    asn = models.ForeignKey(
        AS,
        verbose_name=_("AS"),
        help_text=_("Autonomous system granted with prefix"),
        default=AS.default_as)
    project = models.ForeignKey(Project,
                                verbose_name="Project",
                                on_delete=models.SET_NULL,
                                null=True,
                                blank=True,
                                related_name="prefix_set")
    vc = models.ForeignKey(VC,
                           verbose_name=_("VC"),
                           null=True,
                           blank=True,
                           on_delete=models.SET_NULL,
                           help_text=_("VC bound to prefix"))
    description = models.TextField(_("Description"), blank=True, null=True)
    tags = TagsField("Tags", null=True, blank=True)
    tt = models.IntegerField("TT",
                             blank=True,
                             null=True,
                             help_text=_("Ticket #"))
    style = models.ForeignKey(Style,
                              verbose_name=_("Style"),
                              blank=True,
                              null=True)
    state = models.ForeignKey(ResourceState,
                              verbose_name=_("State"),
                              default=ResourceState.get_default)
    allocated_till = models.DateField(
        _("Allocated till"),
        null=True,
        blank=True,
        help_text=_("Prefix temporary allocated till the date"))
    ipv6_transition = models.OneToOneField("self",
                                           related_name="ipv4_transition",
                                           null=True,
                                           blank=True,
                                           limit_choices_to={"afi": "6"},
                                           on_delete=models.SET_NULL)
    enable_ip_discovery = models.CharField(_("Enable IP Discovery"),
                                           max_length=1,
                                           choices=[("I", "Inherit"),
                                                    ("E", "Enable"),
                                                    ("D", "Disable")],
                                           default="I",
                                           blank=False,
                                           null=False)

    csv_ignored_fields = ["parent"]

    def __unicode__(self):
        return u"%s(%s): %s" % (self.vrf.name, self.afi, self.prefix)

    def get_absolute_url(self):
        return site.reverse("ip:ipam:vrf_index", self.vrf.id, self.afi,
                            self.prefix)

    @property
    def has_transition(self):
        """
        Check prefix has ipv4/ipv6 transition
        :return:
        """
        if self.afi == "4":
            return bool(self.ipv6_transition)
        else:
            try:
                self.ipv4_transition
                return True
            except Prefix.DoesNotExist:
                return False

    def clear_transition(self):
        if self.has_transition:
            if self.afi == "4":
                self.ipv6_transition = None
                self.save()
            else:
                self.ipv4_transition.ipv6_transition = None
                self.ipv4_transition.save()

    @classmethod
    def get_parent(cls, vrf, afi, prefix):
        """
        Get nearest closing prefix
        """
        r = list(
            Prefix.objects.raw(
                """
                SELECT id, prefix
                FROM ip_prefix
                WHERE
                        vrf_id=%s
                    AND afi=%s
                    AND prefix >> %s
                ORDER BY masklen(prefix) DESC
                LIMIT 1
                """, [vrf.id, str(afi), str(prefix)]))
        if not r:
            return None
        return r[0]

    @property
    def is_root(self):
        """
        Returns true if the prefix is a root of VRF
        """
        return (self.afi == "4"
                and self.prefix == "0.0.0.0/0") or (self.afi == "6"
                                                    and self.prefix == "::/0")

    def clean(self):
        """
        Field validation
        """
        super(Prefix, self).clean()
        # Check prefix is of AFI type
        if self.afi == "4":
            check_ipv4_prefix(self.prefix)
        elif self.afi == "6":
            check_ipv6_prefix(self.prefix)
        # Check root prefix have no parent
        if self.is_root and self.parent:
            raise ValidationError("Root prefix cannot have parent")

    def save(self, **kwargs):
        """
        Save prefix
        """
        # Set defaults
        self.afi = "6" if ":" in self.prefix else "4"
        if not self.vrf:
            self.vrf = VRF.get_global()
        if not self.asn:
            self.asn = AS.default_as()
        if not self.is_root:
            # Set proper parent
            self.parent = Prefix.get_parent(self.vrf, self.afi, self.prefix)
        super(Prefix, self).save(**kwargs)
        # Rebuild tree if necessary
        # Reconnect children children prefixes
        c = connection.cursor()
        c.execute(
            """
            UPDATE %s
            SET    parent_id=%%s
            WHERE
                    vrf_id=%%s
                AND afi=%%s
                AND prefix << %%s
                AND parent_id=%%s
        """ % Prefix._meta.db_table, [
                self.id, self.vrf.id, self.afi, self.prefix,
                self.parent.id if self.parent else None
            ])
        # Reconnect children addresses
        c.execute(
            """
            UPDATE %s
            SET prefix_id=%%s
            WHERE
                    prefix_id=%%s
                AND address << %%s
                """ % Address._meta.db_table,
            [self.id, self.parent.id if self.parent else None, self.prefix])

    def delete(self, *args, **kwargs):
        """
        Delete prefix
        """
        if self.is_root:
            raise ValidationError("Cannot delete root prefix")
        # Reconnect children prefixes
        self.children_set.update(parent=self.parent)
        # Reconnect children addresses
        self.address_set.update(prefix=self.parent)
        # Unlink dual-stack allocations
        self.clear_transition()
        # Remove bookmarks
        self.prefixbookmark_set.all().delete()
        # Finally delete
        super(Prefix, self).delete(*args, **kwargs)

    def delete_recursive(self):
        """
        Delete prefix and all descendancies
        """
        # Unlink dual-stack allocations
        self.clear_transition()
        # Recursive delete
        # Get nested prefixes
        ids = Prefix.objects.filter(vrf=self.vrf, afi=self.afi).extra(
            where=["prefix <<= %s"],
            params=[self.prefix]).values_list("id", flat=True)
        #
        zones = set()
        for a in Address.objects.filter(prefix__in=ids):
            zones.add(a.address)
            zones.add(a.fqdn)
        # Delete nested addresses
        Address.objects.filter(prefix__in=ids).delete()
        # Delete nested prefixes
        Prefix.objects.filter(id__in=ids).delete()
        # Delete permissions
        PrefixAccess.objects.filter(vrf=self.vrf, afi=self.afi).extra(
            where=["prefix <<= %s"], params=[self.prefix])
        # Touch dns zones
        for z in zones:
            DNSZone.touch(z)

    @property
    def maintainers(self):
        """
        List of persons having write access
        @todo: PostgreSQL-independent implementation
        """
        return User.objects.raw(
            """
            SELECT id,username,first_name,last_name
            FROM %s u
            WHERE
                is_active=TRUE
                AND
                    (is_superuser=TRUE
                    OR
                    EXISTS(SELECT id
                           FROM %s a
                           WHERE
                                    user_id=u.id
                                AND vrf_id=%%s
                                AND afi=%%s
                                AND prefix>>=%%s
                                AND can_change=TRUE
                           ))
            ORDER BY username""" %
            (User._meta.db_table, PrefixAccess._meta.db_table),
            [self.vrf.id, self.afi, self.prefix])

    ##
    ## First line of description
    ##
    @property
    def short_description(self):
        if self.description:
            return self.description.split("\n", 1)[0].strip()
        else:
            return ""

    ##
    ## Netmask for IPv4
    ##
    @property
    def netmask(self):
        if self.afi == "4":
            return IPv4(self.prefix).netmask.address
        else:
            return None

    ##
    ## Broadcast for IPv4
    ##
    @property
    def broadcast(self):
        if self.afi == "4":
            return IPv4(self.prefix).last.address
        else:
            return None

    ##
    ## Cisco wildcard for IPv4
    ##
    @property
    def wildcard(self):
        if self.afi == "4":
            return IPv4(self.prefix).wildcard.address
        else:
            return ""

    ##
    ## IPv4 prefix size
    ##
    @property
    def size(self):
        if self.afi == "4":
            return IPv4(self.prefix).size
        else:
            return None

    ##
    ## Return True if user has view access
    ##
    def can_view(self, user):
        return PrefixAccess.user_can_view(user, self.vrf, self.afi,
                                          self.prefix)

    ##
    ## Return True if user has change access
    ##
    def can_change(self, user):
        return PrefixAccess.user_can_change(user, self.vrf, self.afi,
                                            self.prefix)

    ##
    ## Check the user has bookmark on prefix
    ##
    def has_bookmark(self, user):
        try:
            PrefixBookmark.objects.get(user=user, prefix=self)
            return True
        except PrefixBookmark.DoesNotExist:
            return False

    ##
    ## Toggle user bookmark. Returns new bookmark state
    ##
    def toggle_bookmark(self, user):
        b, created = PrefixBookmark.objects.get_or_create(user=user,
                                                          prefix=self)
        if created:
            return True
        else:
            b.delete()
            return False

    def get_index(self):
        """
        Full-text search
        """
        content = [self.prefix]
        card = "Prefix %s" % self.prefix
        if self.description:
            content += [self.description]
            card += " (%s)" % self.description
        r = {
            "id": "ip.prefix:%s" % self.id,
            "title": self.prefix,
            "content": "\n".join(content),
            "card": card
        }
        if self.tags:
            r["tags"] = self.tags
        return r

    def get_search_info(self, user):
        # @todo: Check user access
        return ("iframe", None, {
            "title":
            "Assigned addresses",
            "url":
            "/ip/ipam/%s/%s/%s/" % (self.vrf.id, self.afi, self.prefix)
        })

    ##
    ## All prefix-related address ranges
    ##
    @property
    def address_ranges(self):
        return list(
            AddressRange.objects.raw(
                """
            SELECT *
            FROM ip_addressrange
            WHERE
                    vrf_id=%s
                AND afi=%s
                AND is_active=TRUE
                AND
                    (
                            from_address << %s
                        OR  to_address << %s
                        OR  %s BETWEEN from_address AND to_address
                    )
            ORDER BY from_address, to_address
            """,
                [self.vrf.id, self.afi, self.prefix, self.prefix, self.prefix
                 ]))

    @property
    def ippools(self):
        """
        All nested IP Pools
        """
        return list(
            IPPool.objects.raw(
                """
            SELECT *
            FROM ip_ippool i
            WHERE
                  vrf_id = %s
              AND afi = %s
              AND from_address << %s
              AND to_address << %s
              AND NOT EXISTS (
                SELECT id
                FROM ip_prefix p
                WHERE
                      vrf_id = i.vrf_id
                  AND afi = i.afi
                  AND prefix << %s
                  AND
                    (
                      from_address << p.prefix
                      OR to_address << p.prefix
                    )
              )
            ORDER BY from_address
        """, [self.vrf.id, self.afi, self.prefix, self.prefix, self.prefix]))

    def rebase(self, vrf, new_prefix):
        """
        Rebase prefix to a new location
        :param vrf:
        :param new_prefix:
        :return:
        """
        b = IP.prefix(self.prefix)
        nb = IP.prefix(new_prefix)
        # Rebase prefix and all nested prefixes
        # Parents are left untouched
        for p in Prefix.objects.filter(vrf=self.vrf, afi=self.afi).extra(
                where=["prefix <<= %s"], params=[self.prefix]):
            np = IP.prefix(p.prefix).rebase(b, nb).prefix
            # Prefix.objects.filter(pk=p.pk).update(prefix=np, vrf=vrf)
            p.prefix = np
            p.vrf = vrf
            p.save()  # Raise events
        # Rebase addresses
        # Parents are left untouched
        for a in Address.objects.filter(vrf=self.vrf, afi=self.afi).extra(
                where=["address <<= %s"], params=[self.prefix]):
            na = IP.prefix(a.address).rebase(b, nb).address
            # Address.objects.filter(pk=a.pk).update(address=na, vrf=vrf)
            a.address = na
            a.vrf = vrf
            a.save()  # Raise events
        # Rebase permissions
        # move all permissions to the nested blocks
        for pa in PrefixAccess.objects.filter(vrf=self.vrf).extra(
                where=["prefix <<= %s"], params=[self.prefix]):
            np = IP.prefix(pa.prefix).rebase(b, nb).prefix
            PrefixAccess.objects.filter(pk=pa.pk).update(prefix=np, vrf=vrf)
        # create permissions for covered blocks
        for pa in PrefixAccess.objects.filter(vrf=self.vrf).extra(
                where=["prefix >> %s"], params=[self.prefix]):
            PrefixAccess(user=pa.user,
                         vrf=vrf,
                         afi=pa.afi,
                         prefix=new_prefix,
                         can_view=pa.can_view,
                         can_change=pa.can_change).save()
        # @todo: Rebase bookmarks
        # Return rebased prefix
        return Prefix.objects.get(pk=self.pk)  # Updated object

    @property
    def nested_prefix_set(self):
        """
        Queryset returning all nested prefixes inside the prefix
        """
        return Prefix.objects.filter(vrf=self.vrf, afi=self.afi).extra(
            where=["prefix <<= %s"], params=[self.prefix])

    @property
    def nested_address_set(self):
        """
        Queryset returning all nested addresses inside the prefix
        """
        return Address.objects.filter(vrf=self.vrf, afi=self.afi).extra(
            where=["address <<= %s"], params=[self.prefix])

    def iter_free(self):
        """
        Generator returning all available free prefixes inside
        :return:
        """
        for fp in IP.prefix(self.prefix).iter_free(
            [p.prefix for p in self.children_set.all()]):
            yield str(fp)

    @property
    def effective_ip_discovery(self):
        if self.enable_ip_discovery == "I":
            if self.parent:
                return self.parent.effective_ip_discovery
            else:
                return "E"
        else:
            return self.enable_ip_discovery

    @property
    def usage(self):
        if self.afi == "4":
            size = IPv4(self.prefix).size
            if not size:
                return 100.0
            n_ips = Address.objects.filter(prefix=self).count()
            n_pfx = sum(
                IPv4(p).size for p in Prefix.objects.filter(parent=self).only(
                    "prefix").values_list("prefix", flat=True))
            if n_ips:
                if size > 2:  # Not /31 or /32
                    size -= 2  # Exclude broadcast and network
            return float(n_ips + n_pfx) * 100.0 / float(size)
        else:
            return None

    @property
    def usage_percent(self):
        u = self.usage
        if u is None:
            return ""
        else:
            return "%.2f%%" % u
Esempio n. 6
0
class PrefixAccess(models.Model):
    class Meta:
        verbose_name = _("Prefix Access")
        verbose_name_plural = _("Prefix Access")
        db_table = "ip_prefixaccess"
        app_label = "ip"
        unique_together = [("user", "vrf", "afi", "prefix")]
        ordering = ["user", "vrf", "afi", "prefix"]

    user = models.ForeignKey(User, verbose_name=_("User"))
    vrf = models.ForeignKey(VRF, verbose_name=_("VRF"))
    afi = models.CharField(
        _("Address Family"),
        max_length=1,
        choices=AFI_CHOICES)
    prefix = CIDRField(_("Prefix"))
    can_view = models.BooleanField(_("Can View"), default=False)
    can_change = models.BooleanField(_("Can Change"), default=False)

    def __unicode__(self):
        perms = []
        if self.can_view:
            perms += ["View"]
        if self.can_change:
            perms += ["Change"]
        return u"%s: %s(%s): %s: %s" % (
        self.user.username, self.vrf.name, self.afi, self.prefix,
        ", ".join(perms))

    def clean(self):
        """
        Field validation
        :return:
        """
        super(PrefixAccess, self).clean()
        # Check prefix is of AFI type
        if self.afi == "4":
            check_ipv4_prefix(self.prefix)
        elif self.afi == "6":
            check_ipv6_prefix(self.prefix)

    @classmethod
    def user_can_view(cls, user, vrf, afi, prefix):
        """
        Check user has read access to prefix
        :param user:
        :param vrf:
        :param afi:
        :param prefix:
        :return:
        """
        if user.is_superuser:
            return True
        if isinstance(prefix, Prefix):
            prefix = prefix.prefix
        else:
            prefix = str(prefix)
        # @todo: PostgreSQL-independed implementation
        c = connection.cursor()
        c.execute("""SELECT COUNT(*)
                     FROM %s
                     WHERE prefix >>= %%s
                        AND vrf_id=%%s
                        AND afi=%%s
                        AND user_id=%%s
                        AND can_view=TRUE
                 """ % PrefixAccess._meta.db_table,
            [str(prefix), vrf.id, afi, user.id])
        return c.fetchall()[0][0] > 0

    @classmethod
    def user_can_change(cls, user, vrf, afi, prefix):
        """
        Check user has write access to prefix
        :param cls:
        :param user:
        :param vrf:
        :param afi:
        :param prefix:
        :return:
        """
        if user.is_superuser:
            return True
            # @todo: PostgreSQL-independed implementation
        c = connection.cursor()
        c.execute("""SELECT COUNT(*)
                     FROM %s
                     WHERE prefix >>= %%s
                        AND vrf_id=%%s
                        AND afi=%%s
                        AND user_id=%%s
                        AND can_change=TRUE
                 """ % PrefixAccess._meta.db_table,
            [str(prefix), vrf.id, afi, user.id])
        return c.fetchall()[0][0] > 0
Esempio n. 7
0
class AddressRange(models.Model):
    class Meta:
        verbose_name = _("Address Range")
        verbose_name = _("Address Ranges")
        db_table = "ip_addressrange"
        app_label = "ip"
        unique_together = [("vrf", "afi", "from_address", "to_address")]

    name = models.CharField(_("Name"), max_length=64, unique=True)
    is_active = models.BooleanField(_("Is Active"), default=True)
    vrf = models.ForeignKey(VRF, verbose_name=_("VRF"))
    afi = models.CharField(
        _("Address Family"),
        max_length=1,
        choices=AFI_CHOICES)
    from_address = CIDRField(_("From Address"))
    to_address = CIDRField(_("To address"))
    description = models.TextField(_("Description"), blank=True, null=True)
    is_locked = models.BooleanField(
        _("Is Locked"),
        default=False,
        help_text=_("Check to deny address creation or editing within the range"))
    action = models.CharField(
        _("Action"),
        max_length=1,
        choices=[
            ("N", _("Do nothing")),
            ("G", _("Generate FQDNs")),
            ("D", _("Partial reverse zone delegation"))
        ],
        default="N")
    fqdn_template = models.CharField(
        _("FQDN Template"),
        max_length=255,
        null=True, blank=True,
        help_text=_("Template to generate FQDNs when 'Action' set to 'Generate FQDNs'"))
    reverse_nses = models.CharField(
        _("Reverse NSes"),
        max_length=255,
        null=True, blank=True,
        help_text=_("Comma-separated list of NSes to partial reverse zone delegation when 'Action' set to 'Partial reverse zone delegation"))
    tags = TagsField(_("Tags"), null=True, blank=True)
    tt = models.IntegerField(
        "TT",
        blank=True, null=True,
        help_text=_("Ticket #"))
    allocated_till = models.DateField(
        _("Allocated till"),
        null=True, blank=True,
        help_text=_("VRF temporary allocated till the date"))

    def __unicode__(self):
        return u"%s (IPv%s): %s -- %s" % (
        self.vrf.name, self.afi, self.from_address, self.to_address)

    def clean(self):
        """
        Field validation
        """
        super(AddressRange, self).clean()
        # Check prefix is of AFI type
        if self.afi == "4":
            check_ipv4(self.from_address)
            check_ipv4(self.to_address)
        elif self.afi == "6":
            check_ipv6(self.from_address)
            check_ipv6(self.to_address)

    def get_absolute_url(self):
        return site.reverse("ip:addressrange:change", self.id)

    ##
    ## Save instance
    ##
    def save(self, **kwargs):
        def generate_fqdns():
            # Prepare FQDN template
            t = Template(self.fqdn_template)
            # Sync FQDNs
            sn = 0
            for ip in self.addresses:
                # Generage FQDN
                vars = {
                    "afi": self.afi,
                    "vrf": self.vrf,
                    "range": self,
                    "n": sn
                }
                sn += 1
                if self.afi == "4":
                    i = ip.address.split(".")
                    vars["ip"] = i  # ip.0 .. ip.3
                    # ip1, ip2, ip3, ip4 for backward compatibility
                    for n, i in enumerate(i):
                        vars["ip%d" % (n + 1)] = i
                elif self.afi == "6":
                    vars["ip"] = ip.digits  # ip.0 .. ip.31
                fqdn = t.render(Context(vars))
                description = "Generated by address range '%s'" % self.name
                # Create or update address record when necessary
                a, created = Address.objects.get_or_create(
                    vrf=self.vrf, afi=self.afi, address=ip.address)
                if created:
                    a.fqdn = fqdn
                    a.description = description
                    a.save()
                elif a.fqdn != fqdn or a.description != a.description:
                    a.fqdn = fqdn
                    a.description = description
                    a.save()

        created = self.id is None
        if not created:
            # Get old values
            old = AddressRange.objects.get(id=self.id)
        super(AddressRange, self).save(**kwargs)
        if created:
            # New
            if self.action == "G":
                generate_fqdns()
        else:
            # Changed
            if old.action == "G" and self.action != "G":
                # Drop all auto-generated IPs
                Address.objects.filter(vrf=self.vrf, afi=self.afi,
                                       address__gte=self.from_address,
                                       address__lte=self.to_address).delete()
            elif old.action != "G" and self.action == "G":
                # Generate IPs
                generate_fqdns()
            elif self.action == "G":
                # Check for boundaries change
                if IP.prefix(old.from_address) < IP.prefix(self.from_address):
                    # Lower boundary raised up. Clean up addresses falled out of range
                    Address.objects.filter(
                        vrf=self.vrf, afi=self.afi,
                        address__gte=old.from_address,
                        address__lt=self.to_address).delete()
                if IP.prefix(old.to_address) > IP.prefix(self.to_address):
                    # Upper boundary is lowered. Clean up addressess falled out of range
                    Address.objects.filter(
                        vrf=self.vrf, afi=self.afi,
                                           address__gt=self.to_address,
                                           address__lte=old.to_address).delete()
                    # Finally recheck FQDNs
                generate_fqdns()

    @property
    def short_description(self):
        """
        First line of description
        """
        if self.description:
            return self.description.split("\n", 1)[0].strip()
        else:
            return ""

    @property
    def addresses(self):
        """
        Generator returning all addresses in range
        """
        return IP.prefix(self.from_address).iter_address(
            until=IP.prefix(self.to_address))

    ##
    ## Returns a list of overlapping ranges
    ##
    @classmethod
    def get_overlapping_ranges(cls, vrf, afi, from_address, to_address):
        return AddressRange.objects.raw("""
            SELECT *
            FROM ip_addressrange
            WHERE
                    vrf_id=%(vrf)s
                AND afi=%(afi)s
                AND is_active
                AND (
                        from_address BETWEEN %(from_address)s AND %(to_address)s
                    OR  to_address BETWEEN %(from_address)s AND %(to_address)s
                    OR  %(from_address)s BETWEEN from_address AND to_address
                    OR  %(to_address)s BETWEEN from_address AND to_address
                )
        """, {
            "vrf": vrf.id,
            "afi": afi,
            "from_address": from_address,
            "to_address": to_address
        })

    ##
    ## Returns a queryset with overlapped ranges
    ##
    @property
    def overlapping_ranges(self):
        return self.get_overlapping_ranges(
            self.vrf, self.afi, self.from_address, self.to_address)

    @classmethod
    def address_is_locked(cls, vrf, afi, address):
        """
        Check wrether address is locked by any range
        """
        return AddressRange.objects.filter(
            vrf=vrf, afi=afi, is_locked=True,
            is_active=True,
            from_address__lte=address,
            to_address__gte=address).exists()
Esempio n. 8
0
    def forwards(self):
        AFI_CHOICES = [("4", "IPv4"), ("6", "IPv6")]
        # Style
        Style = db.mock_model(model_name='Style',
                              db_table='main_style',
                              db_tablespace='',
                              pk_field_name='id',
                              pk_field_type=models.AutoField)
        # VRF Group
        db.add_column(
            "ip_vrfgroup", "address_constraint",
            models.CharField("Address Constraint",
                             max_length=1,
                             choices=[("V", "Addresses are unique per VRF"),
                                      ("G",
                                       "Addresses are unique per VRF Group")],
                             default="V"))
        db.alter_column(
            "ip_vrfgroup", "description",
            models.TextField("Description", blank=True, null=True,
                             default="V"))
        db.add_column("ip_vrfgroup", "tags",
                      AutoCompleteTagsField("Tags", null=True, blank=True))
        # VRF
        db.add_column("ip_vrf", "is_active",
                      models.BooleanField("Is Active", default=True))
        db.add_column("ip_vrf", "afi_ipv4",
                      models.BooleanField("IPv4", default=True))
        db.add_column("ip_vrf", "afi_ipv6",
                      models.BooleanField("IPv6", default=False))
        db.alter_column(
            "ip_vrf", "description",
            models.TextField("Description", blank=True, null=True,
                             default="V"))
        db.add_column(
            "ip_vrf", "style",
            models.ForeignKey(Style,
                              verbose_name="Style",
                              blank=True,
                              null=True))
        db.add_column(
            "ip_vrf", "allocated_till",
            models.DateField("Allocated till", null=True, blank=True))
        # Prefix
        VRF = db.mock_model(model_name='VRF',
                            db_table='ip_vrf',
                            db_tablespace='',
                            pk_field_name='id',
                            pk_field_type=models.AutoField)
        AS = db.mock_model(model_name='AS',
                           db_table='peer_as',
                           db_tablespace='',
                           pk_field_name='id',
                           pk_field_type=models.AutoField)
        VC = db.mock_model(model_name='VC',
                           db_table='vc_vc',
                           db_tablespace='',
                           pk_field_name='id',
                           pk_field_type=models.AutoField)
        ManagedObject = db.mock_model(model_name='ManagedObject',
                                      db_table='sa_managedobject',
                                      db_tablespace='',
                                      pk_field_name='id',
                                      pk_field_type=models.AutoField)
        Prefix = db.mock_model(model_name='Prefix',
                               db_table='ip_prefix',
                               db_tablespace='',
                               pk_field_name='id',
                               pk_field_type=models.AutoField)

        db.create_table(
            'ip_prefix',
            (('id',
              models.AutoField(
                  verbose_name='ID', primary_key=True, auto_created=True)),
             ("parent",
              models.ForeignKey(Prefix,
                                related_name="children_set",
                                verbose_name="Parent",
                                null=True,
                                blank=True)),
             ("vrf", models.ForeignKey(VRF, verbose_name="VRF")),
             ("afi",
              models.CharField(
                  "Address Family", max_length=1, choices=AFI_CHOICES)),
             ("prefix", CIDRField("Prefix")),
             ("asn", models.ForeignKey(AS, verbose_name="AS")),
             ("vc",
              models.ForeignKey(VC, verbose_name="VC", null=True, blank=True)),
             ("description",
              models.TextField("Description", blank=True, null=True)),
             ("tags", AutoCompleteTagsField("Tags", null=True, blank=True)),
             ("tt", models.IntegerField("TT", blank=True, null=True)),
             ('style',
              models.ForeignKey(
                  Style, verbose_name="Style", blank=True, null=True)),
             ("allocated_till",
              models.DateField("Allocated till", null=True, blank=True))))
        db.create_index('ip_prefix', ['vrf_id', 'afi', 'prefix'],
                        unique=True,
                        db_tablespace='')
        # Address
        db.create_table('ip_address', (
            ('id',
             models.AutoField(
                 verbose_name='ID', primary_key=True, auto_created=True)),
            ("prefix", models.ForeignKey(Prefix, verbose_name="Prefix")),
            ("vrf", models.ForeignKey(VRF, verbose_name="VRF")),
            ("afi",
             models.CharField(
                 "Address Family", max_length=1, choices=AFI_CHOICES)),
            ("address", INETField("Address")),
            ("fqdn", models.CharField("FQDN", max_length=255)),
            ("mac", MACField("MAC", null=True, blank=True)),
            ("auto_update_mac",
             models.BooleanField("Auto Update MAC", default=False)),
            ("managed_object",
             models.ForeignKey(ManagedObject,
                               verbose_name="Managed Object",
                               null=True,
                               blank=True,
                               related_name="address_set")),
            ("description",
             models.TextField("Description", blank=True, null=True)),
            ("tags", AutoCompleteTagsField("Tags", null=True, blank=True)),
            ("tt", models.IntegerField("TT", blank=True, null=True)),
            ('style',
             models.ForeignKey(
                 Style, verbose_name="Style", blank=True, null=True)),
            ("allocated_till",
             models.DateField("Allocated till", null=True, blank=True)),
        ))
        db.create_index('ip_address',
                        ['prefix_id', 'vrf_id', 'afi', 'address'],
                        unique=True,
                        db_tablespace='')
        # PrefixAccess
        User = db.mock_model(model_name='User',
                             db_table='auth_user',
                             db_tablespace='',
                             pk_field_name='id',
                             pk_field_type=models.AutoField)
        db.create_table('ip_prefixaccess', (
            ('id',
             models.AutoField(
                 verbose_name='ID', primary_key=True, auto_created=True)),
            ("user", models.ForeignKey(User, verbose_name="User")),
            ("vrf", models.ForeignKey(VRF, verbose_name="VRF")),
            ("afi",
             models.CharField(
                 "Address Family", max_length=1, choices=AFI_CHOICES)),
            ("prefix", CIDRField("Prefix")),
            ("can_view", models.BooleanField("Can View", default=False)),
            ("can_change", models.BooleanField("Can Change", default=False)),
        ))
        db.create_index('ip_prefixaccess',
                        ['user_id', 'vrf_id', 'afi', 'prefix'],
                        unique=True,
                        db_tablespace='')
        # AddressRange
        db.create_table('ip_addressrange', (
            ('id',
             models.AutoField(
                 verbose_name='ID', primary_key=True, auto_created=True)),
            ('name', models.CharField(_("Name"), max_length=64, unique=True)),
            ('is_active', models.BooleanField(_("Is Active"), default=True)),
            ("vrf", models.ForeignKey(VRF, verbose_name="VRF")),
            ("afi",
             models.CharField(
                 "Address Family", max_length=1, choices=AFI_CHOICES)),
            ("from_address", INETField("Address")),
            ("to_address", INETField("Address")),
            ("description",
             models.TextField("Description", blank=True, null=True)),
            ('is_locked', models.BooleanField(_("Is Active"), default=True)),
            ('action',
             models.CharField(_("FQDN Action"),
                              max_length=1,
                              choices=[("N", "Do nothing"),
                                       ("G", "Generate FQDNs"),
                                       ("D", "Partial reverse zone delegation")
                                       ],
                              default="N")),
            ('fqdn_template',
             models.CharField(
                 _("FQDN Template"), max_length=255, null=True, blank=True)),
            ('reverse_nses',
             models.CharField(
                 _("Reverse NSes"), max_length=255, null=True, blank=True)),
            ("tags", AutoCompleteTagsField("Tags", null=True, blank=True)),
            ("tt", models.IntegerField("TT", blank=True, null=True)),
            ("allocated_till",
             models.DateField("Allocated till", null=True, blank=True)),
        ))
        db.create_index('ip_addressrange',
                        ['vrf_id', 'afi', 'from_address', 'to_address'],
                        unique=True,
                        db_tablespace='')

        # PrefixBookmark
        db.create_table(
            'ip_prefixbookmark',
            (('id',
              models.AutoField(
                  verbose_name='ID', primary_key=True, auto_created=True)),
             ("user", models.ForeignKey(User, verbose_name="User")),
             ("prefix", models.ForeignKey(Prefix, verbose_name="Prefix"))))
        db.create_index('ip_prefixbookmark', ['user_id', 'prefix_id'],
                        unique=True,
                        db_tablespace='')

        db.send_create_signal('ip', ["Prefix", "Address", "PrefixBookmark"])