Ejemplo n.º 1
0
class KeysTransform(Transform):
    lookup_name = 'keys'
    function = 'akeys'
    output_field = ArrayField(TextField())
Ejemplo n.º 2
0
class JobListing(Model):
    jobid = CharField(max_length=50, unique=True,)
    title = TextField(null=True, blank=False)
    description = TextField(null=True, blank=True)
    def __str__(self):
        return str(self.title)
Ejemplo n.º 3
0
class KeyTransform(Transform):

    SPEC_MAP = {
        date: 'DATE',
        datetime: 'DATETIME',
        float: 'DOUBLE',
        int: 'INTEGER',
        six.text_type: 'CHAR',
        time: 'TIME',
        dict: 'BINARY',
    }
    if six.PY2:
        from __builtin__ import long  # make source lintable on Python 3
        SPEC_MAP[long] = 'INTEGER'

    SPEC_MAP_NAMES = ', '.join(sorted(x.__name__ for x in
                                      six.iterkeys(SPEC_MAP)))

    TYPE_MAP = {
        'BINARY': DynamicField,
        'CHAR': TextField(),
        'DATE': DateField(),
        'DATETIME': DateTimeField(),
        'DOUBLE': FloatField(),
        'INTEGER': IntegerField(),
        'TIME': TimeField(),
    }

    def __init__(self, key_name, data_type, *args, **kwargs):
        subspec = kwargs.pop('subspec', None)
        super(KeyTransform, self).__init__(*args, **kwargs)
        self.key_name = key_name
        self.data_type = data_type

        try:
            output_field = self.TYPE_MAP[data_type]
        except KeyError:  # pragma: no cover
            raise ValueError("Invalid data_type '{}'".format(data_type))

        if data_type == 'BINARY':
            self.output_field = output_field(spec=subspec)
        else:
            self.output_field = output_field

    def as_sql(self, compiler, connection):
        lhs, params = compiler.compile(self.lhs)
        return (
            "COLUMN_GET({}, %s AS {})".format(lhs, self.data_type),
            params + [self.key_name],
        )

    if django.VERSION[:3] <= (1, 8, 2):  # pragma: no cover
        # Backport of bugfix for transforms with arguments, taken from:
        # https://code.djangoproject.com/ticket/24744
        def copy(self):
            return copy(self)

        def relabeled_clone(self, relabels):
            copy = self.copy()
            copy.lhs = self.lhs.relabeled_clone(relabels)
            return copy
Ejemplo n.º 4
0
    def total_queryset(self):
        filters = [
            self.is_in_provided_def_codes,
            self.is_non_zero_total_spending,
            self.all_closed_defc_submissions,
            Q(object_class__isnull=False),
        ]

        object_class_annotations = {
            "major_code": F("object_class__major_object_class"),
            "description": F("object_class__object_class_name"),
            "code": F("object_class__object_class"),
            "id": Cast(Min("object_class_id"), TextField()),
            "major_description": F("object_class__major_object_class_name"),
        }

        annotations = {
            **object_class_annotations,
            "obligation":
            Coalesce(
                Sum(
                    Case(
                        When(
                            self.final_period_submission_query_filters,
                            then=
                            F("obligations_incurred_by_program_object_class_cpe"
                              ) +
                            F("deobligations_recoveries_refund_pri_program_object_class_cpe"
                              ),
                        ),
                        default=Value(0),
                    )),
                0,
            ),
            "outlay":
            Coalesce(
                Sum(
                    Case(
                        When(
                            self.final_period_submission_query_filters,
                            then=
                            F("gross_outlay_amount_by_program_object_class_cpe"
                              ) +
                            F("ussgl487200_down_adj_pri_ppaid_undel_orders_oblig_refund_cpe"
                              ) +
                            F("ussgl497200_down_adj_pri_paid_deliv_orders_oblig_refund_cpe"
                              ),
                        ),
                        default=Value(0),
                    )),
                0,
            ),
            "award_count":
            Value(None, output_field=IntegerField()),
        }

        # Assuming it is more performant to fetch all rows once rather than
        #  run a count query and fetch only a page's worth of results
        return (FinancialAccountsByProgramActivityObjectClass.objects.filter(
            *filters).values("object_class__major_object_class",
                             "object_class__major_object_class_name").annotate(
                                 **annotations).values(*annotations.keys()))
Ejemplo n.º 5
0
class Subscription(Model):
    SUBSCRIPTION_CHOICES = (
        ('PAP', u'Edición papel + Digital'),
        ('DIG', u'Digital (Edición web)'),
    )
    MONTHLY = u'MO'
    QUARTERLY = u'QU'
    BIANNUAL = u'BA'
    ANNUAL = u'AN'
    SUBSCRIPTION_PLAN_CHOICES = (
        (MONTHLY, u'Mensual'),
        (QUARTERLY, u'Trimestral'),
        (BIANNUAL, u'Semestral'),
        (ANNUAL, u'Anual'),
    )

    subscriber = ForeignKey(User,
                            related_name='suscripciones',
                            verbose_name=u'usuario',
                            null=True,
                            blank=True)
    first_name = CharField(u'nombres', max_length=150)
    last_name = CharField(u'apellidos', max_length=150)
    document = CharField(u'documento', max_length=11, blank=False, null=True)
    telephone = CharField(u'teléfono', max_length=20, blank=False, null=False)
    email = EmailField(u'email')
    address = CharField(u'dirección', max_length=255, blank=True, null=True)
    country = CharField(u'país', max_length=50)
    city = CharField(u'ciudad', max_length=64, blank=True, null=True)
    province = CharField(u'departamento',
                         max_length=20,
                         choices=settings.THEDAILY_PROVINCE_CHOICES,
                         blank=True,
                         null=True)

    observations = TextField(u'observaciones para la entrega',
                             blank=True,
                             null=True)
    subscription_type = CharField(u'suscripción',
                                  max_length=3,
                                  choices=SUBSCRIPTION_CHOICES,
                                  default='DIG')
    subscription_plan = CharField(u'plan',
                                  max_length=2,
                                  choices=SUBSCRIPTION_PLAN_CHOICES)
    subscription_end = DateTimeField(u'última fecha de suscripción',
                                     auto_now_add=True,
                                     editable=False)
    friend1_name = CharField(u'nombre', max_length=150, blank=True, null=True)
    friend1_email = CharField(u'email', max_length=150, blank=True, null=True)
    friend1_telephone = CharField(u'teléfono',
                                  max_length=20,
                                  blank=True,
                                  null=True)
    friend2_name = CharField(u'nombre', max_length=150, blank=True, null=True)
    friend2_email = CharField(u'email', max_length=150, blank=True, null=True)
    friend2_telephone = CharField(u'teléfono',
                                  max_length=20,
                                  blank=True,
                                  null=True)
    friend3_name = CharField(u'nombre', max_length=150, blank=True, null=True)
    friend3_email = CharField(u'email', max_length=150, blank=True, null=True)
    friend3_telephone = CharField(u'teléfono',
                                  max_length=20,
                                  blank=True,
                                  null=True)
    date_created = DateTimeField(u'fecha de creación',
                                 auto_now_add=True,
                                 editable=False)

    public_profile = BooleanField(u'perfíl comunidad', default=True)

    subscription_type_prices = ManyToManyField(
        SubscriptionPrices, verbose_name=u'tipo de subscripcion', blank=True)
    promo_code = CharField(max_length=8, blank=True, null=True)

    def __unicode__(self):
        return self.get_full_name()

    def get_full_name(self):
        if not self.first_name and not self.last_name:
            return "Usuario sin nombre"
        else:
            return u'%s %s' % (self.first_name, self.last_name)

    def get_subscription_type_prices(self):
        return u', '.join(u'%s' % stp
                          for stp in self.subscription_type_prices.all())

    def get_absolute_url(self):
        return '/admin/thedaily/subscription/%i/' % self.id

    class Meta:
        get_latest_by = 'date_created'
        ordering = ('-date_created', 'first_name', 'last_name')
        verbose_name = u'suscripción'
        verbose_name_plural = u'suscripciones'
Ejemplo n.º 6
0
class Song(Model):
    """Song model"""
    name = CharField(verbose_name=_("Name"), max_length=100)
    date = DateField(auto_now_add=True, editable=False)
    capo = PositiveSmallIntegerField(verbose_name="Capo", default=0)
    author = CharField(verbose_name=_("Author"), max_length=100, null=True, blank=True)
    link = URLField(verbose_name=_("Youtube Link"), null=True, blank=True)
    categories = ManyToManyField(Category, verbose_name=_("Categories"))
    text = MarkdownxField(verbose_name=_("Lyrics"))
    prerendered_web = TextField(null=True)
    prerendered_pdf = TextField(null=True)

    def _get_rendered_markdown(self, markdown, markdown_type: MarkdownTypes):
        """
        Returns rendered markdown for specific type of application.
        If USE_PRERENDERED_MARKDOWN is True, it will cache the changes indefinitely.
        If USE_DYNAMIC_PRERENDER is False, it will fail if there is no cached version ready
        """
        if not settings.USE_PRERENDERED_MARKDOWN:
            return markdown.convert(self.text)

        if markdown_type == MarkdownTypes.WEB:
            field = self.prerendered_web
        else:
            field = self.prerendered_pdf

        if field is None:
            if settings.USE_DYNAMIC_PRERENDER:
                return self._prerender(markdown, markdown_type)
            raise ValueError("No prerendered song found and USE_DYNAMIC_PRERENDER is set to false")
        return field

    def _prerender(self, markdown, markdown_type: MarkdownTypes, save: bool = True):
        """Generates prerendered html for specific type with a specific rendered"""
        html = markdown.convert(self.text)
        if markdown_type == MarkdownTypes.WEB:
            self.prerendered_web = html
        else:
            self.prerendered_pdf = html

        if save:
            self.save()
        return html

    def prerender_all(self, save: bool = True):
        """Generates prerendered html from both web and pdf markdown"""
        self._prerender(template_markdown, MarkdownTypes.WEB, save)
        self._prerender(pdf_markdown, MarkdownTypes.PDF, save)

    @property
    def rendered_web_markdown(self):
        """Returns rendered markdown for web"""
        return self._get_rendered_markdown(template_markdown, MarkdownTypes.WEB)

    @property
    def rendered_pdf_markdown(self):
        """Returns rendered markdown for pdf"""
        return self._get_rendered_markdown(pdf_markdown, MarkdownTypes.PDF)

    class Meta:
        verbose_name = _('Song')
        verbose_name_plural = _('Songs')
        ordering = ['date', 'id']

    def __hash__(self):
        values = [self.name, self.date, self.capo, self.author,
                  self.link, self.categories, self.text]
        if hasattr(self, "song_number"):
            # pylint: disable=no-member
            values.append(self.song_number)
        return hash(frozenset(values))
Ejemplo n.º 7
0
class LibuserConfig(Model):
    key = TextField(primary_key=True, null=False)
    value = TextField(null=False)
Ejemplo n.º 8
0
class Subnet(CleanSave, TimestampedModel):
    def __init__(self, *args, **kwargs):
        assert "space" not in kwargs, "Subnets can no longer be in spaces."
        super().__init__(*args, **kwargs)

    objects = SubnetManager()

    name = CharField(
        blank=False,
        editable=True,
        max_length=255,
        validators=[SUBNET_NAME_VALIDATOR],
        help_text="Identifying name for this subnet.",
    )

    description = TextField(null=False, blank=True)

    vlan = ForeignKey("VLAN",
                      editable=True,
                      blank=False,
                      null=False,
                      on_delete=PROTECT)

    # XXX:fabric: unique constraint should be relaxed once proper support for
    # fabrics is implemented. The CIDR must be unique within a Fabric, not
    # globally unique.
    cidr = CIDRField(blank=False, unique=True, editable=True, null=False)

    rdns_mode = IntegerField(choices=RDNS_MODE_CHOICES,
                             editable=True,
                             default=RDNS_MODE.DEFAULT)

    gateway_ip = GenericIPAddressField(blank=True, editable=True, null=True)

    dns_servers = ArrayField(TextField(),
                             blank=True,
                             editable=True,
                             null=True,
                             default=list)

    allow_dns = BooleanField(editable=True,
                             blank=False,
                             null=False,
                             default=True)

    allow_proxy = BooleanField(editable=True,
                               blank=False,
                               null=False,
                               default=True)

    active_discovery = BooleanField(editable=True,
                                    blank=False,
                                    null=False,
                                    default=False)

    managed = BooleanField(editable=True,
                           blank=False,
                           null=False,
                           default=True)

    @property
    def label(self):
        """Returns a human-friendly label for this subnet."""
        cidr = str(self.cidr)
        # Note: there is a not-NULL check for the 'name' field, so this only
        # applies to unsaved objects.
        if self.name is None or self.name == "":
            return cidr
        if cidr not in self.name:
            return "%s (%s)" % (self.name, self.cidr)
        else:
            return self.name

    @property
    def space(self):
        """Backward compatibility shim to get the space for this subnet."""
        return self.vlan.space

    def get_ipnetwork(self) -> IPNetwork:
        return IPNetwork(self.cidr)

    def get_ip_version(self) -> int:
        return self.get_ipnetwork().version

    def update_cidr(self, cidr):
        cidr = str(cidr)
        # If the old name had the CIDR embedded in it, update that first.
        if self.name:
            self.name = self.name.replace(str(self.cidr), cidr)
        else:
            self.name = cidr
        self.cidr = cidr

    def __str__(self):
        return "%s:%s(vid=%s)" % (self.name, self.cidr, self.vlan.vid)

    def validate_gateway_ip(self):
        if self.gateway_ip is None or self.gateway_ip == "":
            return
        gateway_addr = IPAddress(self.gateway_ip)
        network = self.get_ipnetwork()
        if gateway_addr in network:
            # If the gateway is in the network, it is fine.
            return
        elif network.version == 6 and gateway_addr.is_link_local():
            # If this is an IPv6 network and the gateway is in the link-local
            # network (fe80::/64 -- required to be configured by the spec),
            # then it is also valid.
            return
        else:
            # The gateway is not valid for the network.
            message = "Gateway IP must be within CIDR range."
            raise ValidationError({"gateway_ip": [message]})

    def clean_fields(self, *args, **kwargs):
        # XXX mpontillo 2016-03-16: this function exists due to bug #1557767.
        # This workaround exists to prevent potential unintended consequences
        # of making the name optional.
        if (self.name is None or self.name == "") and self.cidr is not None:
            self.name = str(self.cidr)
        super().clean_fields(*args, **kwargs)

    def clean(self, *args, **kwargs):
        self.validate_gateway_ip()

    def delete(self, *args, **kwargs):
        # Check if DHCP is enabled on the VLAN this subnet is attached to.
        if self.vlan.dhcp_on and self.get_dynamic_ranges().exists():
            raise ValidationError(
                "Cannot delete a subnet that is actively servicing a dynamic "
                "IP range. (Delete the dynamic range or disable DHCP first.)")
        super().delete(*args, **kwargs)

    def get_allocated_ips(self):
        """Get all the IPs for the given subnets

        Any StaticIPAddress record that has a non-emtpy ip is considered to
        be allocated.

        It returns a generator producing a 2-tuple with the subnet and a
        list of IP tuples

        An IP tuple consist of the IP as a string and its allocation type.

        The result can be cached by calling cache_allocated_ips().
        """
        ips = getattr(self, "_cached_allocated_ips", None)
        if ips is None:
            [(_, ips)] = list(get_allocated_ips([self]))
        return ips

    def cache_allocated_ips(self, ips):
        """Cache the results of get_allocated_ips().

        This is to be used similar to how prefetching objects on
        queryset works.
        """
        self._cached_allocated_ips = ips

    def _get_ranges_for_allocated_ips(self, ipnetwork: IPNetwork,
                                      ignore_discovered_ips: bool) -> set:
        """Returns a set of MAASIPRange objects created from the set of allocated
        StaticIPAddress objects.
        """
        ranges = set()
        # We work with tuple rather than real model objects, since a
        # subnet may many IPs and creating a model object for each IP is
        # slow.
        ips = self.get_allocated_ips()
        for ip, alloc_type in ips:
            if ip and not (ignore_discovered_ips and
                           (alloc_type == IPADDRESS_TYPE.DISCOVERED)):
                ip = IPAddress(ip)
                if ip in ipnetwork:
                    ranges.add(make_iprange(ip, purpose="assigned-ip"))
        return ranges

    def get_ipranges_in_use(
        self,
        exclude_addresses: IPAddressExcludeList = None,
        ranges_only: bool = False,
        include_reserved: bool = True,
        with_neighbours: bool = False,
        ignore_discovered_ips: bool = False,
        exclude_ip_ranges: list = None,
        cached_staticroutes: list = None,
    ) -> MAASIPSet:
        """Returns a `MAASIPSet` of `MAASIPRange` objects which are currently
        in use on this `Subnet`.

        :param exclude_addresses: Additional addresses to consider "in use".
        :param ignore_discovered_ips: DISCOVERED addresses are not "in use".
        :param ranges_only: if True, filters out gateway IPs, static routes,
            DNS servers, and `exclude_addresses`.
        :param with_neighbours: If True, includes addresses learned from
            neighbour observation.
        """
        if exclude_addresses is None:
            exclude_addresses = []
        ranges = set()
        network = self.get_ipnetwork()
        if network.version == 6:
            # For most IPv6 networks, automatically reserve the range:
            #     ::1 - ::ffff:ffff
            # We expect the administrator will be using ::1 through ::ffff.
            # We plan to reserve ::1:0 through ::ffff:ffff for use by MAAS,
            # so that we can allocate addresses in the form:
            #     ::<node>:<child>
            # For now, just make sure IPv6 addresses are allocated from
            # *outside* both ranges, so that they won't conflict with addresses
            # reserved from this scheme in the future.
            first = str(IPAddress(network.first))
            first_plus_one = str(IPAddress(network.first + 1))
            second = str(IPAddress(network.first + 0xFFFFFFFF))
            if network.prefixlen == 64:
                ranges |= {
                    make_iprange(first_plus_one, second, purpose="reserved")
                }
            # Reserve the subnet router anycast address, except for /127 and
            # /128 networks. (See RFC 6164, and RFC 4291 section 2.6.1.)
            if network.prefixlen < 127:
                ranges |= {
                    make_iprange(first, first, purpose="rfc-4291-2.6.1")
                }
        if not ranges_only:
            if (self.gateway_ip is not None and self.gateway_ip != ""
                    and self.gateway_ip in network):
                ranges |= {make_iprange(self.gateway_ip, purpose="gateway-ip")}
            if self.dns_servers is not None:
                ranges |= set(
                    make_iprange(server, purpose="dns-server")
                    for server in self.dns_servers if server in network)
            if cached_staticroutes is not None:
                static_routes = [
                    static_route for static_route in cached_staticroutes
                    if static_route.source == self
                ]
            else:
                static_routes = StaticRoute.objects.filter(source=self)
            for static_route in static_routes:
                ranges |= {
                    make_iprange(static_route.gateway_ip, purpose="gateway-ip")
                }
            ranges |= self._get_ranges_for_allocated_ips(
                network, ignore_discovered_ips)
            ranges |= set(
                make_iprange(address, purpose="excluded")
                for address in exclude_addresses if address in network)
        if include_reserved:
            ranges |= self.get_reserved_maasipset(
                exclude_ip_ranges=exclude_ip_ranges)
        ranges |= self.get_dynamic_maasipset(
            exclude_ip_ranges=exclude_ip_ranges)
        if with_neighbours:
            ranges |= self.get_maasipset_for_neighbours()
        return MAASIPSet(ranges)

    def get_ipranges_available_for_reserved_range(
            self, exclude_ip_ranges: list = None):
        return self.get_ipranges_not_in_use(
            ranges_only=True, exclude_ip_ranges=exclude_ip_ranges)

    def get_ipranges_available_for_dynamic_range(self,
                                                 exclude_ip_ranges: list = None
                                                 ):
        return self.get_ipranges_not_in_use(
            ranges_only=False,
            ignore_discovered_ips=True,
            exclude_ip_ranges=exclude_ip_ranges,
        )

    def get_ipranges_not_in_use(
        self,
        exclude_addresses: IPAddressExcludeList = None,
        ranges_only: bool = False,
        ignore_discovered_ips: bool = False,
        with_neighbours: bool = False,
        exclude_ip_ranges: list = None,
    ) -> MAASIPSet:
        """Returns a `MAASIPSet` of ranges which are currently free on this
        `Subnet`.

        :param ranges_only: if True, filters out gateway IPs, static routes,
            DNS servers, and `exclude_addresses`.
        :param exclude_addresses: An iterable of addresses not to use.
        :param ignore_discovered_ips: DISCOVERED addresses are not "in use".
        :param with_neighbours: If True, includes addresses learned from
            neighbour observation.
        """
        if exclude_addresses is None:
            exclude_addresses = []
        in_use = self.get_ipranges_in_use(
            exclude_addresses=exclude_addresses,
            ranges_only=ranges_only,
            with_neighbours=with_neighbours,
            ignore_discovered_ips=ignore_discovered_ips,
            exclude_ip_ranges=exclude_ip_ranges,
        )
        if self.managed or ranges_only:
            not_in_use = in_use.get_unused_ranges(self.get_ipnetwork())
        else:
            # The end result we want is a list of unused IP addresses *within*
            # reserved ranges. To get that result, we first need the full list
            # of unused IP addresses on the subnet. This is better illustrated
            # visually below.
            #
            # Legend:
            #     X:  in-use IP addresses
            #     R:  reserved range
            #     Rx: reserved range (with allocated, in-use IP address)
            #
            #             +----+----+----+----+----+----+
            # IP address: | 1  | 2  | 3  | 4  | 5  | 6  |
            #             +----+----+----+----+----+----+
            #     Usages: | X  |    | R  | Rx |    | X  |
            #             +----+----+----+----+----+----+
            #
            # We need a set that just contains `3` in this case. To get there,
            # first calculate the set of all unused addresses on the subnet,
            # then intersect that set with set of in-use addresses *excluding*
            # the reserved range, then calculate which addresses within *that*
            # set are unused:
            #                               +----+----+----+----+----+----+
            #                   IP address: | 1  | 2  | 3  | 4  | 5  | 6  |
            #                               +----+----+----+----+----+----+
            #                       unused: |    | U  |    |    | U  |    |
            #                               +----+----+----+----+----+----+
            #             unmanaged_in_use: | u  |    |    | u  |    | u  |
            #                               +----+----+----+----+----+----+
            #                 |= unmanaged: ===============================
            #                               +----+----+----+----+----+----+
            #             unmanaged_in_use: | u  | U  |    | u  | U  | u  |
            #                               +----+----+----+----+----+----+
            #          get_unused_ranges(): ===============================
            #                               +----+----+----+----+----+----+
            #                   not_in_use: |    |    | n  |    |    |    |
            #                               +----+----+----+----+----+----+
            unused = in_use.get_unused_ranges(
                self.get_ipnetwork(), purpose=MAASIPRANGE_TYPE.UNMANAGED)
            unmanaged_in_use = self.get_ipranges_in_use(
                exclude_addresses=exclude_addresses,
                ranges_only=ranges_only,
                include_reserved=False,
                with_neighbours=with_neighbours,
                ignore_discovered_ips=ignore_discovered_ips,
                exclude_ip_ranges=exclude_ip_ranges,
            )
            unmanaged_in_use |= unused
            not_in_use = unmanaged_in_use.get_unused_ranges(
                self.get_ipnetwork(), purpose=MAASIPRANGE_TYPE.UNUSED)
        return not_in_use

    def get_maasipset_for_neighbours(self) -> MAASIPSet:
        """Return the observed neighbours in this subnet.

        :return: MAASIPSet of neighbours (with the "neighbour" purpose).
        """
        # Circular imports.
        from maasserver.models import Discovery

        # Note: we only need unknown IP addresses here, because the known
        # IP addresses should already be covered by get_ipranges_in_use().
        neighbours = Discovery.objects.filter(subnet=self).by_unknown_ip()
        neighbour_set = {
            make_iprange(neighbour.ip, purpose="neighbour")
            for neighbour in neighbours
        }
        return MAASIPSet(neighbour_set)

    def get_least_recently_seen_unknown_neighbour(self):
        """
        Returns the least recently seen unknown neighbour or this subnet.

        Useful when allocating an IP address, to safeguard against assigning
        an address another host is still using.

        :return: a `maasserver.models.Discovery` object
        """
        # Circular imports.
        from maasserver.models import Discovery

        # Note: for the purposes of this function, being in part of a "used"
        # range (such as a router IP address or reserved range) makes it
        # "known". So we need to avoid those here in order to avoid stepping
        # on network infrastructure, reserved ranges, etc.
        unused = self.get_ipranges_not_in_use(ignore_discovered_ips=True)
        least_recent_neighbours = (Discovery.objects.filter(
            subnet=self).by_unknown_ip().order_by("last_seen"))
        for neighbor in least_recent_neighbours:
            if neighbor.ip in unused:
                return neighbor
        return None

    def get_iprange_usage(self,
                          with_neighbours=False,
                          cached_staticroutes=None) -> MAASIPSet:
        """Returns both the reserved and unreserved IP ranges in this Subnet.
        (This prevents a potential race condition that could occur if an IP
        address is allocated or deallocated between calls.)

        :returns: A tuple indicating the (reserved, unreserved) ranges.
        """
        reserved_ranges = self.get_ipranges_in_use(
            cached_staticroutes=cached_staticroutes)
        if with_neighbours is True:
            reserved_ranges |= self.get_maasipset_for_neighbours()
        return reserved_ranges.get_full_range(self.get_ipnetwork())

    def get_next_ip_for_allocation(
        self,
        exclude_addresses: Optional[Iterable] = None,
        avoid_observed_neighbours: bool = True,
    ):
        """Heuristic to return the "best" address from this subnet to use next.

        :param exclude_addresses: Optional list of addresses to exclude.
        :param avoid_observed_neighbours: Optional parameter to specify if
            known observed neighbours should be avoided. This parameter is not
            intended to be specified by a caller in production code; it is used
            internally to recursively call this method if the first allocation
            attempt fails.
        """
        if exclude_addresses is None:
            exclude_addresses = []
        free_ranges = self.get_ipranges_not_in_use(
            exclude_addresses=exclude_addresses,
            with_neighbours=avoid_observed_neighbours,
        )
        if len(free_ranges) == 0 and avoid_observed_neighbours is True:
            # Try again recursively, but this time consider neighbours to be
            # "free" IP addresses. (We'll pick the least recently seen IP.)
            return self.get_next_ip_for_allocation(
                exclude_addresses, avoid_observed_neighbours=False)
        elif len(free_ranges) == 0:
            raise StaticIPAddressExhaustion(
                "No more IPs available in subnet: %s." % self.cidr)
        # The first time through this function, we aren't trying to avoid
        # observed neighbours. In fact, `free_ranges` only contains completely
        # unused ranges. So we don't need to check for the least recently seen
        # neighbour on the first pass.
        if avoid_observed_neighbours is False:
            # We tried considering neighbours as "in-use" addresses, but the
            # subnet is still full. So make an educated guess about which IP
            # address is least likely to be in-use.
            discovery = self.get_least_recently_seen_unknown_neighbour()
            if discovery is not None:
                maaslog.warning(
                    "Next IP address to allocate from '%s' has been observed "
                    "previously: %s was last claimed by %s via %s at %s." % (
                        self.label,
                        discovery.ip,
                        discovery.mac_address,
                        discovery.observer_interface.get_log_string(),
                        discovery.last_seen,
                    ))
                return str(discovery.ip)
        # The purpose of this is to that we ensure we always get an IP address
        # from the *smallest* free contiguous range. This way, larger ranges
        # can be preserved in case they need to be used for applications
        # requiring them. If two ranges have the same number of IPs, choose the
        # lowest one.
        free_range = min(free_ranges, key=attrgetter("num_addresses", "first"))
        return str(IPAddress(free_range.first))

    def render_json_for_related_ips(self,
                                    with_username=True,
                                    with_summary=True):
        """Render a representation of this subnet's related IP addresses,
        suitable for converting to JSON. Optionally exclude user and node
        information."""
        ip_addresses = self.staticipaddress_set.all()
        if with_username:
            ip_addresses = ip_addresses.prefetch_related("user")
        if with_summary:
            ip_addresses = ip_addresses.prefetch_related(
                "interface_set",
                "interface_set__node",
                "interface_set__node__domain",
                "bmc_set",
                "bmc_set__node_set",
                "dnsresource_set",
                "dnsresource_set__domain",
            )
        return sorted(
            [
                ip.render_json(with_username=with_username,
                               with_summary=with_summary)
                for ip in ip_addresses if ip.ip
            ],
            key=lambda json: IPAddress(json["ip"]),
        )

    def get_dynamic_ranges(self):
        return self.iprange_set.filter(type=IPRANGE_TYPE.DYNAMIC)

    def get_reserved_ranges(self):
        return self.iprange_set.filter(type=IPRANGE_TYPE.RESERVED)

    def is_valid_static_ip(self, *args, **kwargs):
        """Validates that the requested IP address is acceptable for allocation
        in this `Subnet` (assuming it has not already been allocated).

        Returns `True` if the IP address is acceptable, and `False` if not.

        Does not consider whether or not the IP address is already allocated,
        only whether or not it is in the proper network and range.

        :return: bool
        """
        try:
            self.validate_static_ip(*args, **kwargs)
        except MAASAPIException:
            return False
        return True

    def validate_static_ip(self, ip):
        """Validates that the requested IP address is acceptable for allocation
        in this `Subnet` (assuming it has not already been allocated).

        Raises `StaticIPAddressUnavailable` if the address is not acceptable.

        Does not consider whether or not the IP address is already allocated,
        only whether or not it is in the proper network and range.

        :raises StaticIPAddressUnavailable: If the IP address specified is not
            available for allocation.
        """
        if ip not in self.get_ipnetwork():
            raise StaticIPAddressOutOfRange(
                "%s is not within subnet CIDR: %s" % (ip, self.cidr))
        for iprange in self.get_reserved_maasipset():
            if ip in iprange:
                raise StaticIPAddressUnavailable(
                    "%s is within the reserved range from %s to %s" %
                    (ip, IPAddress(iprange.first), IPAddress(iprange.last)))
        for iprange in self.get_dynamic_maasipset():
            if ip in iprange:
                raise StaticIPAddressUnavailable(
                    "%s is within the dynamic range from %s to %s" %
                    (ip, IPAddress(iprange.first), IPAddress(iprange.last)))

    def get_reserved_maasipset(self, exclude_ip_ranges: list = None):
        if exclude_ip_ranges is None:
            exclude_ip_ranges = []
        reserved_ranges = MAASIPSet(iprange.get_MAASIPRange()
                                    for iprange in self.iprange_set.all()
                                    if iprange.type == IPRANGE_TYPE.RESERVED
                                    and iprange not in exclude_ip_ranges)
        return reserved_ranges

    def get_dynamic_maasipset(self, exclude_ip_ranges: list = None):
        if exclude_ip_ranges is None:
            exclude_ip_ranges = []
        dynamic_ranges = MAASIPSet(iprange.get_MAASIPRange()
                                   for iprange in self.iprange_set.all()
                                   if iprange.type == IPRANGE_TYPE.DYNAMIC
                                   and iprange not in exclude_ip_ranges)
        return dynamic_ranges

    def get_dynamic_range_for_ip(self, ip):
        """Return `IPRange` for the provided `ip`."""
        # XXX mpontillo 2016-01-21: for some reason this query doesn't work.
        # I tried it both like this, and with:
        #     start_ip__gte=ip, and end_ip__lte=ip
        # return get_one(self.get_dynamic_ranges().extra(
        #        where=["start_ip >= inet '%s'" % ip,
        # ... which sounds a lot like comment 15 in:
        #     https://code.djangoproject.com/ticket/11442
        for iprange in self.get_dynamic_ranges():
            if ip in iprange.netaddr_iprange:
                return iprange
        return None

    def get_smallest_enclosing_sane_subnet(self):
        """Return the subnet that includes this subnet.

        It must also be at least big enough to be a parent in the RFC2317
        world (/24 in IPv4, /124 in IPv6).

        If no such subnet exists, return None.
        """
        find_rfc2137_parent_query = """
            SELECT * FROM maasserver_subnet
            WHERE
                %s << cidr AND (
                    (family(cidr) = 6 and masklen(cidr) <= 124) OR
                    (family(cidr) = 4 and masklen(cidr) <= 24))
            ORDER BY
                masklen(cidr) DESC
            LIMIT 1
            """
        for s in Subnet.objects.raw(find_rfc2137_parent_query, (self.cidr, )):
            return s
        return None

    def update_allocation_notification(self):
        # Workaround for edge cases in Django. (See bug #1702527.)
        if self.id is None:
            return
        ident = "ip_exhaustion__subnet_%d" % self.id
        # Circular imports.
        from maasserver.models import Config, Notification

        threshold = Config.objects.get_config(
            "subnet_ip_exhaustion_threshold_count")
        notification = Notification.objects.filter(ident=ident).first()
        delete_notification = False
        if threshold > 0:
            full_iprange = self.get_iprange_usage()
            statistics = IPRangeStatistics(full_iprange)
            # Check if there are less available IPs in the subnet than the
            # warning threshold.
            meets_warning_threshold = statistics.num_available <= threshold
            # Check if the warning threshold is appropriate relative to the
            # size of the subnet. It's pointless to warn about address
            # exhaustion on a /30, for example: the admin already knows it's
            # small, so we would just be annoying them.
            subnet_is_reasonably_large_relative_to_threshold = (
                threshold * 3 <= statistics.total_addresses)
            if (meets_warning_threshold
                    and subnet_is_reasonably_large_relative_to_threshold):
                notification_text = (
                    "IP address exhaustion imminent on subnet: %s. "
                    "There are %d free addresses out of %d "
                    "(%s used).") % (
                        self.label,
                        statistics.num_available,
                        statistics.total_addresses,
                        statistics.usage_percentage_string,
                    )
                if notification is None:
                    Notification.objects.create_warning_for_admins(
                        notification_text, ident=ident)
                else:
                    # Note: This will update the notification, but will not
                    # bring it back for those who have dismissed it. Maybe we
                    # should consider creating a new notification if the
                    # situation is now more severe, such as raise it to an
                    # error if it's half remaining threshold.
                    notification.message = notification_text
                    notification.save()
            else:
                delete_notification = True
        else:
            delete_notification = True
        if notification is not None and delete_notification:
            notification.delete()
Ejemplo n.º 9
0
class Laboratory(SearchableModel):
    name = CharField(max_length=255,
                     db_index=True,
                     verbose_name=_(u'Laboratory'),
                     help_text=_(u'Required field, max 255 characters.'))
    slug = SlugField(db_index=True,
                     max_length=255,
                     help_text=_(u'Required \
		field. It autocompletes as you write the name. Edit only if necessary.'))
    m = ForeignKey(Programme,
                   blank=True,
                   null=True,
                   verbose_name=_(u'Master'),
                   help_text=_(u'Optional field'),
                   related_name='mlabs')
    undergrad = BooleanField(default=False,
                             db_index=True,
                             verbose_name=_(u'\
		Undergraduate curriculum'),
                             help_text=_(u'Check if the lab does not \
		belong to a master programme.'))
    r = ManyToManyField(
        Room,
        blank=True,
        null=True,
        verbose_name=_(u'Rooms'),
        help_text=_(u'Optional field, the rooms where this course is being \
		taught'),
        related_name='labs')
    coordinators = ManyToManyField(Member,
                                   blank=True,
                                   null=True,
                                   verbose_name=_(u'Coordinators'),
                                   help_text=_(u'Please select from the \
		Department members list'),
                                   related_name='clabs')
    people = ManyToManyField(Member,
                             blank=True,
                             null=True,
                             verbose_name=_(u'Members involved'),
                             help_text=_(u'Please select from \
		the Department members list'),
                             related_name='plabs')
    url = URLField(max_length=255,
                   verbose_name=_('Link to the lab support'),
                   blank=True,
                   help_text=_(u'Optional field, max 255 characters'))
    ro_url = URLField(max_length=255,
                      verbose_name=_('Link to the lab support,\
		romanian version'),
                      blank=True,
                      help_text=_(u'Optional field, max 255 characters'))
    description = TextField(blank=True,
                            help_text=_(u'Optional field'),
                            verbose_name=_(u'Description'))
    timestamp = DateTimeField(auto_now=True, db_index=True)
    views = IntegerField(default=0, editable=False)
    public = BooleanField(default=False,
                          help_text=_(u'Check to make the \
		course visible on the website'))

    search_objects = SearchManager(fields=(
        'name',
        'description',
    ))

    class Meta:
        verbose_name = _(u'Laboratory')
        verbose_name_plural = _(u'Laboratories')
        ordering = (
            'name',
            'id',
        )

    def __unicode__(self):
        return u'%s' % self.name

    def save(self, *args, **kwargs):
        # before save, make sure there is a slug
        if not self.slug:
            self.slug = slugify(self.name)

        # send data to the database
        super(Laboratory, self).save(*args, **kwargs)

    def get_absolute_url(self):
        return '/laboratoare/%s/%s/' % (self.id, self.slug)
Ejemplo n.º 10
0
class People(Page):
    parent_page_types = ['home.HomePage', 'content.ContentPage']
    subpage_types = ['Person']
    template = 'people.html'

    # Content fields
    description = TextField(
        blank=True,
        default='',
        help_text='Optional short text description, max. 400 characters',
        max_length=400,
    )

    # Meta fields
    keywords = ClusterTaggableManager(through=PeopleTag, blank=True)

    # Content panels
    content_panels = Page.content_panels + [
        FieldPanel('description'),
    ]

    # Meta panels
    meta_panels = [
        MultiFieldPanel([
            FieldPanel('seo_title'),
            FieldPanel('search_description'),
            FieldPanel('keywords'),
        ], heading='SEO', help_text='Optional fields to override the default title and description for SEO purposes'),
    ]

    # Settings panels
    settings_panels = [
        FieldPanel('slug'),
        FieldPanel('show_in_menus'),
    ]

    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading='Content'),
        ObjectList(meta_panels, heading='Meta'),
        ObjectList(settings_panels, heading='Settings', classname='settings'),
    ])

    class Meta:
        verbose_name_plural = 'People'

    @classmethod
    def can_create_at(cls, parent):
        # Allow only one instance of this page type
        return super().can_create_at(parent) and not cls.objects.exists()

    def get_context(self, request):
        context = super().get_context(request)
        context['filters'] = self.get_filters()
        return context

    @property
    def people(self):
        return Person.objects.all().public().live().order_by('title')

    def get_filters(self):
        from ..topics.models import Topic
        return {
            'roles': True,
            'topics': Topic.objects.live().public().order_by('title'),
        }
Ejemplo n.º 11
0
class Fabric(CleanSave, TimestampedModel):
    """A `Fabric`.

    :ivar name: The short-human-identifiable name for this fabric.
    :ivar objects: An instance of the class :class:`FabricManager`.
    """

    class Meta(DefaultMeta):
        """Needed for South to recognize this model."""

        verbose_name = "Fabric"
        verbose_name_plural = "Fabrics"

    objects = FabricManager()

    # We don't actually allow blank or null name, but that is enforced in
    # clean() and save().
    name = CharField(
        max_length=256,
        editable=True,
        null=True,
        blank=True,
        unique=True,
        validators=[validate_fabric_name],
    )

    description = TextField(null=False, blank=True)

    class_type = CharField(
        max_length=256,
        editable=True,
        null=True,
        blank=True,
        validators=[MODEL_NAME_VALIDATOR],
    )

    def __str__(self):
        return "name=%s" % self.get_name()

    def is_default(self):
        """Is this the default fabric?"""
        return self.id == 0

    def get_default_vlan(self):
        # This logic is replicated in the dehydrate() function of the
        # websockets handler.
        return sorted(self.vlan_set.all(), key=attrgetter("id"))[0]

    def get_name(self):
        """Return the name of the fabric."""
        if self.name:
            return self.name
        else:
            return "fabric-%s" % self.id

    def delete(self):
        if self.is_default():
            raise ValidationError(
                "This fabric is the default fabric, it cannot be deleted."
            )
        if Subnet.objects.filter(vlan__fabric=self).exists():
            subnets = Subnet.objects.filter(vlan__fabric=self).order_by("cidr")
            descriptions = [str(subnet.cidr) for subnet in subnets]
            raise ValidationError(
                "Can't delete fabric; the following subnets are "
                "still present: %s" % (", ".join(descriptions))
            )
        if Interface.objects.filter(vlan__fabric=self).exists():
            interfaces = Interface.objects.filter(vlan__fabric=self).order_by(
                "node", "name"
            )
            descriptions = [iface.get_log_string() for iface in interfaces]
            raise ValidationError(
                "Can't delete fabric; the following interfaces are "
                "still connected: %s" % (", ".join(descriptions))
            )
        super().delete()

    def _create_default_vlan(self):
        # Circular imports.
        from maasserver.models.vlan import VLAN, DEFAULT_VLAN_NAME, DEFAULT_VID

        VLAN.objects.create(
            name=DEFAULT_VLAN_NAME, vid=DEFAULT_VID, fabric=self
        )

    def save(self, *args, **kwargs):
        # Name will get set by clean_name() if None or empty, and there is an
        # id. We just need to handle names here for creation.
        created = self.id is None
        super().save(*args, **kwargs)
        if self.name is None or self.name == "":
            # If we got here, then we have a newly created fabric that needs a
            # default name.
            self.name = "fabric-%d" % self.id
            self.save()
        # Create default VLAN if this is a fabric creation.
        if created:
            self._create_default_vlan()

    def clean_name(self):
        reserved = re.compile(r"^fabric-\d+$")
        if self.name is not None and self.name != "":
            if reserved.search(self.name):
                if self.id is None or self.name != "fabric-%d" % self.id:
                    raise ValidationError({"name": ["Reserved fabric name."]})
        elif self.id is not None:
            # Since we are not creating the fabric, force the (null or empty)
            # name to be the default name.
            self.name = "fabric-%d" % self.id

    def clean(self, *args, **kwargs):
        super().clean(*args, **kwargs)
        if self._state.has_changed("name"):
            self.clean_name()
Ejemplo n.º 12
0
class Person(Page):
    resource_type = 'person'
    parent_page_types = ['People']
    subpage_types = []
    template = 'person.html'

    # Content fields
    job_title = CharField(max_length=250)
    role = CharField(max_length=250, choices=ROLE_CHOICES, default='staff')
    description = RichTextField(
        'About',
        blank=True,
        default='',
        help_text='Optional ‘About me’ section content, supports rich text',
    )
    image = ForeignKey(
        'mozimages.MozImage',
        null=True,
        blank=True,
        on_delete=SET_NULL,
        related_name='+',
    )

    # Card fields
    card_title = CharField('Title', max_length=140, blank=True, default='')
    card_description = TextField('Description', max_length=400, blank=True, default='')
    card_image = ForeignKey(
        'mozimages.MozImage',
        null=True,
        blank=True,
        on_delete=SET_NULL,
        related_name='+',
        verbose_name='Image',
    )

    # Meta
    twitter = CharField(max_length=250, blank=True, default='')
    facebook = CharField(max_length=250, blank=True, default='')
    linkedin = CharField(max_length=250, blank=True, default='')
    github = CharField(max_length=250, blank=True, default='')
    email = CharField(max_length=250, blank=True, default='')
    websites = StreamField(
        StreamBlock([
            ('website', PersonalWebsiteBlock())
        ], max_num=3, required=False),
        null=True,
        blank=True,
        help_text='Optional links to any other personal websites',
    )
    keywords = ClusterTaggableManager(through=PersonTag, blank=True)

     # Content panels
    content_panels = [
        MultiFieldPanel([
            CustomLabelFieldPanel('title', label='Full name'),
            FieldPanel('job_title'),
            FieldPanel('role'),
        ], heading='Details'),
        FieldPanel('description'),
        MultiFieldPanel([
            ImageChooserPanel('image'),
        ], heading='Image', help_text=(
            'Optional header image. If not specified a fallback will be used. This image is also shown when sharing '
            'this page via social media'
        )),
    ]

    # Card panels
    card_panels = [
        FieldPanel('card_title'),
        FieldPanel('card_description'),
        ImageChooserPanel('card_image'),
    ]

    # Meta panels
    meta_panels = [
        MultiFieldPanel([
            InlinePanel('topics'),
        ], heading='Topics interested in'),
        MultiFieldPanel([
            FieldPanel('twitter'),
            FieldPanel('facebook'),
            FieldPanel('linkedin'),
            FieldPanel('github'),
            FieldPanel('email'),
        ], heading='Profiles', help_text=''),
        StreamFieldPanel('websites'),
        MultiFieldPanel([
            FieldPanel('seo_title'),
            FieldPanel('search_description'),
            FieldPanel('keywords'),
        ], heading='SEO', help_text='Optional fields to override the default title and description for SEO purposes'),
    ]

    # Settings panels
    settings_panels = [
        FieldPanel('slug'),
    ]

    # Tabs
    edit_handler = TabbedInterface([
        ObjectList(content_panels, heading='Content'),
        ObjectList(card_panels, heading='Card'),
        ObjectList(meta_panels, heading='Meta'),
        ObjectList(settings_panels, heading='Settings', classname='settings'),
    ])

    @property
    def events(self):
        '''
        Return upcoming events where this person is a speaker,
        ordered by start date
        '''
        from ..events.models import Event

        upcoming_events = (Event
                .objects
                .filter(start_date__gte=datetime.datetime.now())
                .live()
                .public()
        )

        speaker_events = Event.objects.none()

        for event in upcoming_events.all():
            # add the event to the list if the current person is a speaker
            if event.has_speaker(self):
                speaker_events = speaker_events | Event.objects.page(event)

        return speaker_events.order_by('start_date')

    @property
    def articles(self):
        '''
        Return articles and external articles where this person is (one of) the authors,
        ordered by article date, most recent first
        '''
        from ..articles.models import Article
        from ..externalcontent.models import ExternalArticle

        articles = Article.objects.none()
        external_articles = ExternalArticle.objects.none()

        all_articles = Article.objects.live().public().all()
        all_external_articles = ExternalArticle.objects.live().public().all()

        for article in all_articles:
            if article.has_author(self):
                articles = articles | Article.objects.page(article)

        for external_article in all_external_articles:
            if external_article.has_author(self):
                external_articles = external_articles | ExternalArticle.objects.page(external_article)

        return sorted(chain(articles, external_articles), key=attrgetter('date'), reverse=True)

    @property
    def videos(self):
        '''
        Return the most recent videos and external videos where this person is (one of)
        the speakers.
        '''
        from ..videos.models import Video
        from ..externalcontent.models import ExternalVideo

        videos = Video.objects.none()
        external_videos = ExternalVideo.objects.none()

        all_videos = Video.objects.live().public().all()
        all_external_videos = ExternalVideo.objects.live().public().all()

        for video in all_videos:
            if video.has_speaker(self):
                videos = videos | Video.objects.page(video)

        for external_video in all_external_videos:
            if external_video.has_speaker(self):
                external_videos = external_videos | ExternalVideo.objects.page(external_video)

        return sorted(chain(videos, external_videos), key=attrgetter('date'), reverse=True)

    @property
    def role_group(self):
        return {
            'slug': self.role,
            'title': dict(ROLE_CHOICES).get(self.role, ''),
        }
Ejemplo n.º 13
0
Archivo: vlan.py Proyecto: zhangrb/maas
class VLAN(CleanSave, TimestampedModel):
    """A `VLAN`.

    :ivar name: The short-human-identifiable name for this VLAN.
    :ivar vid: The VLAN ID of this VLAN.
    :ivar fabric: The `Fabric` this VLAN belongs to.
    """

    objects = VLANManager()

    class Meta(DefaultMeta):
        """Needed for South to recognize this model."""
        verbose_name = "VLAN"
        verbose_name_plural = "VLANs"
        unique_together = (('vid', 'fabric'), )

    name = CharField(max_length=256,
                     editable=True,
                     null=True,
                     blank=True,
                     validators=[MODEL_NAME_VALIDATOR])

    description = TextField(null=False, blank=True)

    vid = IntegerField(editable=True)

    fabric = ForeignKey('Fabric',
                        blank=False,
                        editable=True,
                        on_delete=CASCADE)

    mtu = IntegerField(default=DEFAULT_MTU)

    dhcp_on = BooleanField(default=False, editable=True)

    external_dhcp = MAASIPAddressField(null=True,
                                       editable=False,
                                       blank=True,
                                       default=None)

    primary_rack = ForeignKey('RackController',
                              null=True,
                              blank=True,
                              editable=True,
                              related_name='+',
                              on_delete=CASCADE)

    secondary_rack = ForeignKey('RackController',
                                null=True,
                                blank=True,
                                editable=True,
                                related_name='+',
                                on_delete=CASCADE)

    relay_vlan = ForeignKey('self',
                            null=True,
                            blank=True,
                            editable=True,
                            related_name='relay_vlans',
                            on_delete=deletion.SET_NULL)

    space = ForeignKey('Space',
                       editable=True,
                       blank=True,
                       null=True,
                       on_delete=SET_NULL)

    def __str__(self):
        return "%s.%s" % (self.fabric.get_name(), self.get_name())

    def clean_vid(self):
        if self.vid is None or self.vid < 0 or self.vid > 4094:
            raise ValidationError({'vid': ["VID must be between 0 and 4094."]})

    def clean_mtu(self):
        # Linux doesn't allow lower than 552 for the MTU.
        if self.mtu < 552 or self.mtu > 65535:
            raise ValidationError(
                {'mtu': ["MTU must be between 552 and 65535."]})

    def clean(self):
        self.clean_vid()
        self.clean_mtu()

    def is_fabric_default(self):
        """Is this the default VLAN in the fabric?"""
        return self.fabric.get_default_vlan() == self

    def get_name(self):
        """Return the name of the VLAN."""
        if self.is_fabric_default():
            return "untagged"
        elif self.name is not None:
            return self.name
        else:
            return str(self.vid)

    def manage_connected_interfaces(self):
        """Deal with connected interfaces:

        - delete all VLAN interfaces.
        - reconnect the other interfaces to the default VLAN of the fabric.
        """
        for interface in self.interface_set.all():
            if isinstance(interface, VLANInterface):
                interface.delete()
            else:
                interface.vlan = self.fabric.get_default_vlan()
                interface.save()

    def manage_connected_subnets(self):
        """Reconnect subnets the default VLAN of the fabric."""
        for subnet in self.subnet_set.all():
            subnet.vlan = self.fabric.get_default_vlan()
            subnet.save()

    def unique_error_message(self, model_class, unique_check):
        if set(unique_check) == {'vid', 'fabric'}:
            return ('A VLAN with the specified VID already exists in the '
                    'destination fabric.')
        else:
            return super().unique_error_message(model_class, unique_check)

    def delete(self):
        if self.is_fabric_default():
            raise ValidationError(
                "This VLAN is the default VLAN in the fabric, "
                "it cannot be deleted.")
        self.manage_connected_interfaces()
        self.manage_connected_subnets()
        super(VLAN, self).delete()

    def save(self, *args, **kwargs):
        # Bug 1555759: Raise a Notification if there are no VLANs with DHCP
        # enabled.  Clear it when one gets enabled.
        notifications = Notification.objects.filter(
            ident="dhcp_disabled_all_vlans")
        if self.dhcp_on:
            # No longer true.  Delete the notification.
            notifications.delete()
        elif (not notifications.exists()
              and not VLAN.objects.filter(dhcp_on=True).exists()):
            Notification.objects.create_warning_for_admins(
                "DHCP is not enabled on any VLAN.  This will prevent "
                "machines from being able to PXE boot, unless an external "
                "DHCP server is being used.",
                ident="dhcp_disabled_all_vlans")
        super().save(*args, **kwargs)
        # Circular dependencies.
        from maasserver.models import Fabric
        # Delete any now-empty fabrics.
        fabrics_with_vlan_count = Fabric.objects.annotate(
            vlan_count=Count("vlan"))
        fabrics_with_vlan_count.filter(vlan_count=0).delete()

    def connected_rack_controllers(self, exclude_racks=None):
        """Return list of rack controllers that are connected to this VLAN.

        :param exclude_racks: Exclude these rack controllers from the returned
            connected list.
        :returns: Returns a list of rack controllers that have a connection
            to this VLAN.
        """
        query = self.interface_set.filter(node__node_type__in=[
            NODE_TYPE.RACK_CONTROLLER, NODE_TYPE.REGION_AND_RACK_CONTROLLER
        ])
        if exclude_racks is not None:
            query = query.exclude(node__in=exclude_racks)
        return [nic.node.as_rack_controller() for nic in query]
Ejemplo n.º 14
0
class ValuesTransform(Transform):
    lookup_name = 'values'
    function = 'avals'
    output_field = ArrayField(TextField())
Ejemplo n.º 15
0
class BlockDevice(CleanSave, TimestampedModel):
    """A block device attached to a node."""
    class Meta(DefaultMeta):
        """Needed for South to recognize this model."""

        unique_together = ("node", "name")
        ordering = ["id"]

    objects = BlockDeviceManager()

    node = ForeignKey("Node", null=False, editable=False, on_delete=CASCADE)

    name = CharField(
        max_length=255,
        blank=False,
        help_text="Name of block device. (e.g. sda)",
    )

    id_path = CharField(
        blank=True,
        null=True,
        max_length=4096,
        help_text="Path of by-id alias. (e.g. /dev/disk/by-id/wwn-0x50004...)",
    )

    size = BigIntegerField(
        blank=False,
        null=False,
        validators=[MinValueValidator(MIN_BLOCK_DEVICE_SIZE)],
        help_text="Size of block device in bytes.",
    )

    block_size = IntegerField(
        blank=False,
        null=False,
        validators=[MinValueValidator(MIN_BLOCK_DEVICE_BLOCK_SIZE)],
        help_text="Size of a block on the device in bytes.",
    )

    tags = ArrayField(TextField(), blank=True, null=True, default=list)

    def get_name(self):
        """Return the name.

        This exists so `VirtualBlockDevice` can override this method.
        """
        return self.name

    def get_node(self):
        """Return the name."""
        return self.node

    @property
    def path(self):
        # Path is persistent and set by curtin on deploy.
        return "/dev/disk/by-dname/%s" % self.get_name()

    @property
    def type(self):
        # Circular imports, since ISCSIBlockDevice, PhysicalBlockDevice and
        # VirtualBlockDevice extend from this calss.
        from maasserver.models.iscsiblockdevice import ISCSIBlockDevice
        from maasserver.models.physicalblockdevice import PhysicalBlockDevice
        from maasserver.models.virtualblockdevice import VirtualBlockDevice

        actual_instance = self.actual_instance
        if isinstance(actual_instance, ISCSIBlockDevice):
            return "iscsi"
        elif isinstance(actual_instance, PhysicalBlockDevice):
            return "physical"
        elif isinstance(actual_instance, VirtualBlockDevice):
            return "virtual"
        else:
            raise ValueError(
                "BlockDevice is not a subclass of "
                "ISCSIBlockDevice, PhysicalBlockDevice or VirtualBlockDevice")

    @property
    def actual_instance(self):
        """Return the instance as its correct type.

        By default all references from Django will be to `BlockDevice`, when
        the native type `ISCSIBlockDevice`, `PhysicalBlockDevice` or
        `VirtualBlockDevice` is needed use this property to get its actual
        instance.
        """
        # Circular imports, since ISCSIBlockDevice, PhysicalBlockDevice and
        # VirtualBlockDevice extend from this calss.
        from maasserver.models.iscsiblockdevice import ISCSIBlockDevice
        from maasserver.models.physicalblockdevice import PhysicalBlockDevice
        from maasserver.models.virtualblockdevice import VirtualBlockDevice

        if (isinstance(self, ISCSIBlockDevice)
                or isinstance(self, PhysicalBlockDevice)
                or isinstance(self, VirtualBlockDevice)):
            return self
        try:
            return self.iscsiblockdevice
        except Exception:
            try:
                return self.physicalblockdevice
            except PhysicalBlockDevice.DoesNotExist:
                try:
                    return self.virtualblockdevice
                except VirtualBlockDevice.DoesNotExist:
                    pass
        return self

    def get_effective_filesystem(self):
        """Return the filesystem that is placed on this block device."""
        return get_effective_filesystem(self)

    def get_partitiontable(self):
        """Returns this device's partition table (or None, if none exists."""
        partition_tables = self.partitiontable_set.all()
        if len(partition_tables) > 0:
            return partition_tables[0]
        else:
            return None

    def display_size(self, include_suffix=True):
        return human_readable_bytes(self.size, include_suffix=include_suffix)

    def add_tag(self, tag):
        """Add tag to block device."""
        if tag not in self.tags:
            self.tags = self.tags + [tag]

    def remove_tag(self, tag):
        """Remove tag from block device."""
        if tag in self.tags:
            tags = self.tags.copy()
            tags.remove(tag)
            self.tags = tags

    @property
    def used_size(self):
        """Return the used size on the block device."""
        return self.get_used_size()

    @property
    def available_size(self):
        """Return the available size on the block device."""
        return self.get_available_size()

    @property
    def used_for(self):
        """Return what the block device is being used for."""
        return used_for(self)

    def __str__(self):
        return "{size} attached to {node}".format(size=human_readable_bytes(
            self.size),
                                                  node=self.node)

    def get_block_size(self):
        """Return the block size for the block device."""
        return self.block_size

    def get_used_size(self):
        """Return the used size on the block device."""
        filesystem = self.get_effective_filesystem()
        if filesystem is not None:
            return self.size
        partitiontable = self.get_partitiontable()
        if partitiontable is not None:
            return partitiontable.get_used_size()
        return 0

    def get_available_size(self):
        """Return the available size on the block device."""
        filesystem = self.get_effective_filesystem()
        if filesystem is not None:
            return 0
        partitiontable = self.get_partitiontable()
        if partitiontable is not None:
            return partitiontable.get_available_size()
        return self.size

    def is_boot_disk(self):
        """Return true if block device is the boot disk."""
        boot_disk = self.node.get_boot_disk()
        return boot_disk.id == self.id if boot_disk else False

    def create_partition(self):
        """Creates a partition that uses the whole disk."""
        if self.get_partitiontable() is not None:
            raise ValueError(
                "Cannot call create_partition_if_boot_disk when a "
                "partition table already exists on the block device.")
        # Circular imports.
        from maasserver.models.partitiontable import PartitionTable

        partition_table = PartitionTable.objects.create(block_device=self)
        return partition_table.add_partition()

    def create_partition_if_boot_disk(self):
        """Creates a partition that uses the whole disk if this block device
        is the boot disk."""
        if self.is_boot_disk():
            return self.create_partition()
        else:
            return None

    def delete(self):
        """Delete the block device.

        If this block device is part of a filesystem group then it cannot be
        deleted.
        """
        filesystem = self.get_effective_filesystem()
        if filesystem is not None:
            filesystem_group = filesystem.filesystem_group
            if filesystem_group is not None:
                raise ValidationError(
                    "Cannot delete block device because its part of "
                    "a %s." % filesystem_group.get_nice_name())
        super().delete()

    @staticmethod
    def _get_block_name_from_idx(idx, prefix="sd"):
        """Calculate a block name based on the `idx`.

        Drive#  Name
        0	    sda
        25	    sdz
        26	    sdaa
        27	    sdab
        51	    sdaz
        52	    sdba
        53	    sdbb
        701	    sdzz
        702	    sdaaa
        703	    sdaab
        18277   sdzzz
        """
        name = ""
        while idx >= 0:
            name = string.ascii_lowercase[idx % 26] + name
            idx = (idx // 26) - 1
        return prefix + name

    @staticmethod
    def _get_idx_from_block_name(name, prefix="sd"):
        """Calculate a idx based on `name`.

        Name   Drive#
        sda    0
        sdz    25
        sdaa   26
        sdab   27
        sdaz   51
        sdba   52
        sdbb   53
        sdzz   701
        sdaaa  702
        sdaab  703
        sdzzz  18277
        """
        match = re.match("%s([a-z]+)" % prefix, name)
        if match is None:
            return None
        else:
            idx = 0
            suffix = match.group(1)
            for col, char in enumerate(reversed(suffix)):
                digit = ord(char) + (0 if col == 0 else 1) - ord("a")
                idx += digit * (26**col)
            return idx
Ejemplo n.º 16
0
class JobListing(Model):
    title = TextField(null=True, blank=True)
    description = TextField(null=True, blank=True)
Ejemplo n.º 17
0
    F,
    OuterRef,
    Q,
    Value,
    When,
)
from django.db.models.functions import Concat
from django.db.utils import DatabaseError
from django.test import TestCase

from django_cte import With

from .models import KeyPair, Region

int_field = IntegerField()
text_field = TextField()


class TestRecursiveCTE(TestCase):

    def test_recursive_cte_query(self):
        def make_regions_cte(cte):
            return Region.objects.filter(
                parent__isnull=True
            ).values(
                "name",
                path=F("name"),
                depth=Value(0, output_field=int_field),
            ).union(
                cte.join(Region, parent=cte.col.name).values(
                    "name",
Ejemplo n.º 18
0
class Label(Model):
    '''
    A decorator allowing free-entry "tags" on allocations
    '''
    text = TextField(default='')
    allocation = OneToOneField(ResourceAllocation, on_delete=CASCADE)
Ejemplo n.º 19
0
 def test_TextField(self):
     lazy_func = lazy(lambda: 'Abc', str)
     self.assertIsInstance(TextField().get_prep_value(lazy_func()), str)
     lazy_func = lazy(lambda: 0, int)
     self.assertIsInstance(TextField().get_prep_value(lazy_func()), str)
Ejemplo n.º 20
0
class CustomUser(SearchableMixin, AbstractUser, CustomModel):
    # NOTE: if we need some additional fields we might add those fields into the settings/common.py
    # NOTE: into the UserAttributeSimilarityValidator
    # NOTE: plus if one field name is changed, we also need to adjust ^this
    class Meta:
        # the verbose values are required to identify the user model as "user" and not "customuser"
        verbose_name = getattr(AbstractUser, "_meta").verbose_name
        verbose_name_plural = getattr(AbstractUser, "_meta").verbose_name_plural

        # avoid 'UnorderedObjectListWarning'
        ordering = ["username"]

    email = models.EmailField(_('email address'), unique=True)

    # additional fields (first and last name are already part of AbstractUser)
    avatar = models.ImageField(_('avatar'), upload_to=avatarDir, default=os.path.join("avatars", "default.svg"))
    LANGUAGES = (
        ('en', _("English")),
        ('de', _("German")),
    )
    language = models.CharField(_('language'), max_length=2, choices=LANGUAGES, default='en')
    timezone = models.CharField(_('timezone'), max_length=255, default='UTC',
                                choices=[(x, x) for x in pytz.common_timezones])

    show_activity_to_other_users = models.BooleanField(_("""Others can see your activity
                                                         heatmap on your profile page"""),
                                                       default=True)
    # TODO: any other fields wanted?

    activity = TextField(default='{}')

    def get_projects(self):
        return (self.manager.all() | self.dev_projects.all()).distinct()

    def get_absolute_url(self):
        return reverse('user_profile:user_profile_page', kwargs={"username": self.username})

    def __str__(self):
        username = self.first_name
        if self.first_name and self.last_name:
            username += ' '
        username += self.last_name
        if not username:
            username = self.username
        return username

    def get_preference(self, key, default=None):
        result = self.preferences.filter(key=key)
        if len(result) == 1:
            return result.first().value
        return default

    def set_preference(self, key, value):
        result = self.preferences.filter(key=key)
        if len(result) == 1:
            # key already present => update value
            pref = result.first()
            if value is None:
                # delete key if value is None
                pref.delete()
            else:
                pref.value = value
                pref.save()
        else:
            # key not present => create new preference object
            pref = UserPreference(user=self, key=key, value=value)
            pref.save()
            self.preferences.add(pref)

    @classmethod
    def get_search_name(cls):
        return "User"

    searchable_fields = ['first_name', 'last_name', 'username']

    # every user profile is public
    def user_has_read_permissions(self, user):
        return True

    def user_has_write_permissions(self, user):
        return self == user
Ejemplo n.º 21
0
class Publication(SearchableModel):
    gen = ForeignKey(Genre, blank=True, null=True, verbose_name=_(u'Type'))
    authors = ManyToManyField(
        Member,
        blank=True,
        null=True,
        verbose_name=_(u'\
		Authors'),
        related_name='mpublications',
        help_text=_(u'Please select from the Department members list'))
    oauthors = TextField(
        blank=True,
        verbose_name=_(u'Other authors'),
        help_text=_(u'Optional field, in case some of the authors are not \
		members of the department, e.g. DOE, John, DOE, Jane'))
    title = TextField(verbose_name=_(u'Title'), help_text=u'Required field')
    publisher = CharField(max_length=255,
                          blank=True,
                          verbose_name=u'\
		Publishing house',
                          help_text=_(u'Optional field.'))
    city = CharField(max_length=255,
                     blank=True,
                     verbose_name=_(u'City'),
                     help_text=_(u'Optional field.'))
    review = TextField(
        blank=True,
        verbose_name=_(u'Review info'),
        help_text=_(u'Optional field. Please fill pages and year below.'))
    conference = TextField(
        blank=True,
        verbose_name=_(u'Conference info'),
        help_text=_(u'Optional field.  Please fill pages and year below.'))
    other = TextField(blank=True,
                      verbose_name=_(u'Other info'),
                      help_text=_(u'Optional field.'))
    pp = CharField(max_length=100,
                   blank=True,
                   verbose_name=_(u'Pages'),
                   help_text=_(u'Optional fiels, eg. 127-152'))
    year = SmallIntegerField(max_length=4,
                             verbose_name=_(u'Year'),
                             help_text=_(u'Required field.'))
    book = BooleanField(default=False,
                        help_text=_(u'Check if the publication \
		is a book; important for formatting.'),
                        verbose_name=_(u'The \
		publication is a book.'))
    abstract = TextField(blank=True,
                         help_text=_(u'Optional field'),
                         verbose_name=_(u'Abstract'))
    url = URLField(max_length=255,
                   verbose_name=_('Link to the document'),
                   blank=True,
                   help_text=_(u'Optional field, max 255 characters'))
    timestamp = DateTimeField(auto_now=True, db_index=True)
    public = BooleanField(default=False,
                          help_text=_(u'Check to make the \
		publication visible on the website'))

    objects = PublicationManager()
    search_objects = SearchManager(fields=(
        'title',
        'oauthors',
        'publisher',
        'city',
        'review',
        'conference',
        'other',
        'abstract',
    ))

    class Meta:
        verbose_name = _(u'Publication')
        verbose_name_plural = _(u'Publications')
        ordering = (
            'year',
            'title',
        )
        get_latest_by = 'timestamp'

    def __unicode__(self):
        return u'%s' % self.title
Ejemplo n.º 22
0
class Job(Model):
    """A job represents a work which has to be done in the 'background' (ie:
    another process than the processes which respond to the clients). They are
    useful for periodic tasks (eg: polling data, like emails, from another server)
    or long tasks (eg: generating a lot of data).

    The type of the job (see creme_core.creme_jobs.base.JobType) determines if
    the job is periodic, pseudo-periodic or not periodic.

    Periodic & pseudo-periodic (see JobType for the difference between them) Jobs
    must be 'system' Job:
        - they are created in 'populate' scripts.
        - they have no user.
        - they can not be deleted, but they can be disabled (see 'enabled' field).
        - periodic Jobs must have their 'periodicity' field filled.
        - pseudo-periodic Jobs should not have their 'periodicity' field filled,
          because it is useless ; the value settings.PSEUDO_PERIOD is used as
          security period instead.

    Not periodic Jobs are user Jobs:
        - they are dynamically created by a view.
        - they must have their 'user' filled; it correspond to the User which
          have created the Job, & who owns it. The Job should act with the
          credentials of this User.
        - A view which creates a Job should check settings.MAX_JOBS_PER_USER
          before creating a Job, and redirect to the jobs list view if the Job
          can not be created (tip: you can use Job.not_finished_jobs()).
        - They have to be deleted once they are finished, in order to create
          other user Jobs.

    The 'reference_run' field is always filled (in an automatic way at least),
    but does not means anything for not periodic Jobs ; in this case it is only
    the creation date, which is not very useful. The 'reference_run' is used to
    compute the time of each execution, which must be something like:
        reference_run + N * periodicity
    """
    STATUS_WAIT = 1
    STATUS_ERROR = 10
    STATUS_OK = 20

    type_id = CharField(_('Type of job'), max_length=48, editable=False)
    user = CremeUserForeignKey(verbose_name=_('User'),
                               null=True,
                               editable=False)
    enabled = BooleanField(_('Enabled'), default=True, editable=False)
    language = CharField(_('Language'), max_length=10, editable=False)
    # created      = CreationDateTimeField(_('Creation date'))
    reference_run = DateTimeField(_('Reference run'))
    periodicity = DatePeriodField(_('Periodicity'), null=True)
    last_run = DateTimeField(_('Last run'), null=True, editable=False)
    ack_errors = PositiveIntegerField(
        default=0,
        editable=False)  # Number of errors of communication with the queue.
    status = PositiveSmallIntegerField(
        _('Status'),
        editable=False,
        default=STATUS_WAIT,
        choices=(
            (STATUS_WAIT, _('Waiting')),
            (STATUS_ERROR, _('Error')),
            (STATUS_OK, _('Completed successfully')),
        ),
    )
    error = TextField(_('Error'), null=True, editable=False)
    raw_data = TextField(
        editable=False
    )  # It stores the Job's parameters  # TODO: use a JSONField ?

    class Meta:
        app_label = 'creme_core'
        verbose_name = _('Job')
        verbose_name_plural = _('Jobs')
        ordering = ('id', )

    def __init__(self, *args, **kwargs):
        super().__init__(*args, **kwargs)
        if not self.language:
            self.language = get_language()

        self.__init_refreshing_cache()

    def __init_refreshing_cache(self):
        self._old_periodicity = self.periodicity
        self._old_reference_run = self.reference_run
        self._old_enabled = self.enabled

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

    def __repr__(self):
        return '<Job type="{type}" id="{id}">'.format(type=self.type_id,
                                                      id=self.id)

    def get_absolute_url(self):
        return reverse('creme_core__job', args=(self.id, ))

    def get_delete_absolute_url(self):
        return reverse('creme_core__delete_job', args=(self.id, ))

    def get_edit_absolute_url(self):
        return reverse('creme_core__edit_job', args=(self.id, ))

    @property
    def data(self):
        return jsonloads(self.raw_data)  # TODO: cache

    @data.setter
    def data(self, value):
        self.raw_data = jsondumps(value)

    @property
    def description(self):  # TODO: cache ?
        try:
            return self.type.get_description(self)
        except Exception:
            logger.exception(
                'Error when building the description of the job id="%s"',
                self.id)

        return ()

    def check_owner(self, user):
        return user.is_superuser or self.user == user

    def check_owner_or_die(self, user):
        if not self.check_owner(user):
            raise PermissionDenied('You are not the owner of this job')

    @property
    def is_finished(self):
        return self.status != self.STATUS_WAIT

    @classmethod
    def not_finished_jobs(cls, user):
        return cls.objects.filter(user=user, status=cls.STATUS_WAIT)

    @property
    def progress(self):
        jtype = self.type

        if jtype is not None:
            return jtype.progress(self)

    @property
    def real_periodicity(self):
        periodicity = self.periodicity

        if periodicity is None and self.user_id is None:
            periodicity = HoursPeriod(value=settings.PSEUDO_PERIOD)

        return periodicity

    def _update_ack_errors(self, incr):
        Job.objects.filter(id=self.id).update(ack_errors=F('ack_errors') +
                                              incr)

    def forget_ack_errors(self):
        self._update_ack_errors(-self.ack_errors)

    def get_config_form_class(self):
        "@see JobType.get_config_form_class()"
        jtype = self.type
        return jtype.get_config_form_class(self) if jtype is not None else None

    def refresh(self, force=False):
        """Ask to the JobManager to refresh the job if it's needed, because
        the next runs should be earlier, or disabled.
        @param force: Boolean ; <True> means the message is sent even if no field has changed.
        """
        from ..core.job import JobManagerQueue

        queue_error = False
        enabled = self.enabled
        reference_run = self.reference_run
        periodicity = self.periodicity

        if self._old_enabled != enabled or \
           self._old_reference_run != reference_run or \
           self._old_periodicity != periodicity or \
           force:
            # NB: we sent all the fields values in order to get a more robust system
            #     (even if a REFRESH-message is lost, the next one is complete).
            data = {
                'enabled': enabled,
                'reference_run': dt_to_ISO8601(reference_run),
            }

            if periodicity:
                data['periodicity'] = periodicity.as_dict()

            queue_error = JobManagerQueue.get_main_queue().refresh_job(
                self, data)
            self.__init_refreshing_cache()

        return queue_error

    def update(self, refresh_data, date_period_registry=date_period_registry):
        """Update the fields with information generated by refresh().

        Notice that the instance is not saved.

        @param refresh_data: Dictionary. See data sent on queue by refresh().
        @param date_period_registry: Instance of creme_core.utils.date_period.DatePeriodRegistry.
        @return: True if the instance has changed.
        """
        changed = False
        get = refresh_data.get

        enabled = get('enabled')
        if enabled is not None:
            if self.enabled != enabled:
                self.enabled = enabled
                changed = True

        ref_run_str = get('reference_run')
        if ref_run_str is not None:
            ref_run = dt_from_ISO8601(ref_run_str)

            if self.reference_run != ref_run:
                self.reference_run = ref_run
                changed = True

        periodicity_dict = get('periodicity')
        if periodicity_dict is not None:
            periodicity = date_period_registry.deserialize(periodicity_dict)

            if self.periodicity != periodicity:
                self.periodicity = periodicity
                changed = True

        return changed

    @atomic
    def save(self, *args, **kwargs):
        from ..core.job import JobManagerQueue

        created = self.pk is None

        if created and self.reference_run is None:
            self.reference_run = now()

            if self.user_id is None:  # System job
                self.reference_run = round_hour(self.reference_run)

        # super(Job, self).save(*args, **kwargs)
        super().save(*args, **kwargs)

        queue_error = False

        if created:
            if self.user_id is not None:
                queue_error = JobManagerQueue.get_main_queue().start_job(self)
        elif self.user_id is None:  # System job
            queue_error = self.refresh()

        if queue_error:
            self._update_ack_errors(1)

    @property
    def stats(self):
        jtype = self.type
        return jtype.get_stats(self) if jtype is not None else []

    @property
    def type(self):
        from ..core.job import job_type_registry
        return job_type_registry.get(self.type_id)

    @type.setter
    def type(self, value):
        # TODO: check that it is in job_type_registry ?
        self.type_id = value.id
Ejemplo n.º 23
0
class Report(CerberusModel):
    """
        Cerberus report model: basically an extraction of
        usefull relevant informations of an email report

        A report can be attached to a `abuse.models.Ticket`
    """

    REPORT_STATUS = (
        ("New", "New"),
        ("Archived", "Archived"),
        ("Attached", "Attached"),
        ("PhishToCheck", "PhishToCheck"),
        ("ToValidate", "ToValidate"),
    )

    REPORT_TREATED_MODE = (("NONE", "None"), ("MANU", "Manual"), ("AUTO", "Auto"))

    body = TextField(null=False)

    provider = ForeignKey(
        "Provider", null=False, related_name="provider", on_delete=PROTECT
    )

    defendant = ForeignKey(
        "Defendant", null=True, related_name="reportDefendant", on_delete=PROTECT
    )

    category = ForeignKey(
        "Category", null=True, related_name="reportCategory", on_delete=PROTECT
    )

    service = ForeignKey("Service", null=True)

    ticket = ForeignKey(
        "Ticket", null=True, related_name="reportTicket", on_delete=SET_NULL
    )

    receivedDate = DateTimeField(null=False)

    subject = TextField(null=True)

    status = TruncatedCharField(
        db_index=True, max_length=32, null=False, choices=REPORT_STATUS, default="New"
    )

    filename = TruncatedCharField(max_length=1023, null=False)

    tags = ManyToManyField("Tag", null=True)

    attachments = ManyToManyField("AttachedDocument", null=True)

    treatedMode = TruncatedCharField(
        max_length=4, null=False, choices=REPORT_TREATED_MODE, default="NONE"
    )

    def get_attached_ipaddr(self):
        """
            Returns all attached IP addresses
        """
        from ..utils.networking import get_ip_network

        items = (
            self.reportItemRelatedReport.all()
            .values_list("ip", "fqdnResolved")
            .distinct()
        )

        ips = [ip_addr for sub in items for ip_addr in sub if ip_addr]
        ips = [ip for ip in ips if get_ip_network(ip) == "managed"]
        return list(set(ips))

    def get_attached_urls(self):
        """
            Returns all attached URL
        """
        urls = self.reportItemRelatedReport.filter(itemType="URL").values_list(
            "rawItem", flat=True
        )

        return list(set(urls))

    def get_attached_fqdn(self):
        """
            Returns all attached FQDN
        """
        fqdn = self.reportItemRelatedReport.filter(itemType="FQDN").values_list(
            "rawItem", flat=True
        )

        return list(set(fqdn))

    def attach_url_matching_domain(self, domain):

        from .reportitem import ReportItem
        from ..utils import networking
        from ..parsers import ParsedEmail, Parser

        parser = Parser()
        parsed_email = ParsedEmail()

        template = parser.get_template("default")
        parser._apply_template(parsed_email, self.body, template)
        parsed_email.clean_items()

        for url in parsed_email.urls:
            if self.reportItemRelatedReport.filter(rawItem=url).exists():
                continue
            _domain = networking.get_url_hostname(url)
            if domain == _domain:
                item = {"itemType": "URL", "report_id": self.id, "rawItem": url[:4000]}
                item.update(networking.get_reverses_for_item(url, nature="URL"))
                ReportItem.create(**item)

    def add_tag(self, tag_name):
        """
            Add workflow tag to `abuse.models.Report`
        """
        from .tag import Tag
        from ..utils.text import string_to_underscore_case

        name = string_to_underscore_case(tag_name)

        self.tags.add(
            Tag.get_or_create(
                codename=name, name="report:{}".format(name), tagType="Report"
            )[0]
        )
Ejemplo n.º 24
0
class FailedUrl(Model):

	path = TextField(unique=True)
	num_occurrences = PositiveIntegerField(default=0)
Ejemplo n.º 25
0
class Subscriber(Model):
    """
    TODO:
     - Create an "extra" JSON field to save custom-bussiness-related subscriber data. (using plan_id for this now)
     - Many ladiaria custom fields like "lento_pdf" should be removed (ladiaria will be using them in "extra").
     - Keep newsletters M2M relations for those newsletters that were discontinued (their publication or category have
       changed its has_newsletter attr from True to False) now this M2M rows are removed when the subscriber is saved,
       for example in the admin or by the user itself using the edit profile page.
    """
    contact_id = PositiveIntegerField(u'CRM id',
                                      unique=True,
                                      editable=True,
                                      blank=True,
                                      null=True)
    user = OneToOneField(User,
                         verbose_name=u'usuario',
                         related_name='subscriber',
                         blank=True,
                         null=True)

    # TODO: ver la posibilidad de eliminarlo ya que es el "first_name" del modelo django.contrib.auth.models.User
    name = CharField(u'nombre', max_length=255, validators=[alphanumeric])

    # agregamos estos campos para unificar la info de User y Subscriber
    address = CharField(u'dirección', max_length=255, blank=True, null=True)
    country = CharField(u'país', max_length=50, blank=True, null=True)
    city = CharField(u'ciudad', max_length=64, blank=True, null=True)
    province = CharField(u'departamento',
                         max_length=20,
                         choices=settings.THEDAILY_PROVINCE_CHOICES,
                         blank=True,
                         null=True)

    profile_photo = ImageField(upload_to='perfiles', blank=True, null=True)
    document = CharField(u'documento', max_length=50, blank=True, null=True)
    phone = CharField(u'teléfono', max_length=20)

    date_created = DateTimeField(u'fecha de registro',
                                 auto_now_add=True,
                                 editable=False)
    downloads = PositiveIntegerField(u'descargas',
                                     default=0,
                                     blank=True,
                                     null=True)

    pdf = BooleanField(default=False)
    lento_pdf = BooleanField(u'pdf L.', default=False)
    ruta = PositiveSmallIntegerField(blank=True, null=True)
    plan_id = TextField(blank=True, null=True)
    ruta_lento = PositiveSmallIntegerField(blank=True, null=True)
    ruta_fs = PositiveSmallIntegerField(blank=True, null=True)
    newsletters = ManyToManyField(Publication,
                                  blank=True,
                                  limit_choices_to={'has_newsletter': True})
    category_newsletters = ManyToManyField(
        Category, blank=True, limit_choices_to={'has_newsletter': True})
    allow_news = BooleanField(u'acepta novedades', default=True)
    allow_promotions = BooleanField(u'acepta promociones', default=True)
    allow_polls = BooleanField(u'acepta encuestas', default=True)
    # TODO: explain the utility of this field or remove it.
    subscription_mode = CharField(max_length=1,
                                  null=True,
                                  blank=True,
                                  default=None)
    last_paid_subscription = DateTimeField(u'Ultima subscripcion comienzo',
                                           null=True,
                                           blank=True)

    def save(self, *args, **kwargs):
        if self.document:
            non_decimal = re.compile(r'[^\d]+')
            self.document = non_decimal.sub('', self.document)
        super(Subscriber, self).save(*args, **kwargs)

    def download(self, pdfinstance):
        try:
            download = SubscriberEditionDownloads.objects.get(
                subscriber=self, edition=pdfinstance)
            download.downloads += 1
        except SubscriberEditionDownloads.DoesNotExist:
            download = SubscriberEditionDownloads()
            download.subscriber = self
            download.edition = pdfinstance
            download.downloads = 1
        download.save()
        self.downloads += 1
        self.save()

    def is_subscriber(self, pub_slug=settings.DEFAULT_PUB):
        try:

            if self.user:

                if self.user.is_staff:
                    return True

                elif pub_slug in getattr(
                        settings, 'THEDAILY_IS_SUBSCRIBER_CUSTOM_PUBLICATIONS',
                    ()):
                    is_subscriber_custom = __import__(
                        settings.THEDAILY_IS_SUBSCRIBER_CUSTOM_MODULE,
                        fromlist=['is_subscriber']).is_subscriber
                    return is_subscriber_custom(self, pub_slug)

                else:
                    return self.user.has_perm('thedaily.es_suscriptor_%s' %
                                              pub_slug)

        except User.DoesNotExist:
            # rare, but we saw once this exception happen
            pass

        return False

    def is_digital_only(self):
        """ Returns True only if this subcriber is subscribed only to the "digital" edition """
        # TODO 2nd release: implement
        return self.is_subscriber()

    def make_token(self):
        return TimestampSigner().sign(self.user.username)

    def check_token(self, token):
        try:
            key = '%s:%s' % (self.user.username, token)
            TimestampSigner().unsign(key, max_age=60 * 60 * 48)  # Valid 2 days
        except (BadSignature, SignatureExpired):
            return False
        return True

    def user_is_active(self):
        return self.user and self.user.is_active

    user_is_active.short_description = u'user act.'
    user_is_active.boolean = True

    def is_subscriber_any(self):
        return any(
            self.is_subscriber(pub_slug) for pub_slug in getattr(
                settings, 'THEDAILY_IS_SUBSCRIBER_ANY',
                Publication.objects.values_list('slug', flat=True)))

    def get_publication_newsletters_ids(self, exclude_slugs=[]):
        return list(
            self.newsletters.filter(has_newsletter=True).exclude(
                slug__in=exclude_slugs).values_list('id', flat=True))

    def get_category_newsletters_ids(self, exclude_slugs=[]):
        return list(
            self.category_newsletters.filter(has_newsletter=True).exclude(
                slug__in=exclude_slugs).values_list('id', flat=True))

    def get_newsletters_slugs(self):
        return list(self.newsletters.values_list('slug', flat=True)) + \
            list(self.category_newsletters.values_list('slug', flat=True))

    def get_newsletters(self):
        return ', '.join(self.get_newsletters_slugs())

    get_newsletters.short_description = u'newsletters'

    def updatecrmuser_publication_newsletters(self, exclude_slugs=[]):
        if self.contact_id:
            try:
                updatecrmuser(
                    self.contact_id, u'newsletters',
                    json.dumps(
                        self.get_publication_newsletters_ids(exclude_slugs)))
            except requests.exceptions.RequestException:
                pass

    def updatecrmuser_category_newsletters(self, exclude_slugs=[]):
        if self.contact_id:
            try:
                updatecrmuser(
                    self.contact_id, u'area_newsletters',
                    json.dumps(
                        self.get_category_newsletters_ids(exclude_slugs)))
            except requests.exceptions.RequestException:
                pass

    def get_downloads(self, edition=None):
        if not edition:
            return self.downloads
        else:
            qs = self.edition_downloads.filter(edition=edition)
            if qs.count() == 0:
                return 0
            else:
                return qs[0].downloads

    def __unicode__(self):
        return self.name or self.get_full_name()

    def get_full_name(self):
        if not self.user.first_name and not self.user.last_name:
            return u"Usuario sin nombre"
        else:
            return self.user.get_full_name()

    def get_latest_article_visited(self):
        """
        Returns info about the latest visit to an article from this subscriber.
        """
        # Search in mongodb first, if none found then search in db-table
        mdb = core_articleviewedby_mdb.posts.find({
            'user': self.user.id
        }).sort('viewed_at', pymongo.DESCENDING)
        if mdb.count():
            latest = mdb[0]
            return (latest.get('article'), latest.get('viewed_at'))
        else:
            try:
                latest = self.user.articleviewedby_set.latest('viewed_at')
            except ArticleViewedBy.DoesNotExist:
                pass
            else:
                return (latest.article_id, latest.viewed_at)

    @property
    def user_email(self):
        return self.user.email if self.user else None

    @permalink
    def get_absolute_url(self):
        return '/admin/thedaily/subscriber/%i/' % self.id

    class Meta:
        verbose_name = u'suscriptor'
        permissions = (("es_suscriptor_%s" % settings.DEFAULT_PUB,
                        "Es suscriptor actualmente"), )
Ejemplo n.º 26
0
def migrate_duration_extra(apps, schema_editor):
    ChannelEvent = apps.get_model('channels', 'ChannelEvent')

    # find all events with a duration and convert them to extra
    ids = ChannelEvent.objects.filter(duration__gte=0).values_list('id', flat=True)
    if ids:
        print("Found %d channel events to set extra on" % len(ids))

    count = 0
    for chunk in chunk_list(ids, 250):
        ChannelEvent.objects.filter(id__in=chunk).update(extra=Concat(Value('{"duration":'), F('duration'), Value('}'), output_field=TextField()))
        count += len(chunk)
        print("Updated %d" % count)
Ejemplo n.º 27
0
class KeyTextTransform(KeyTransform):
    operator = '->>'
    nested_operator = '#>>'
    output_field = TextField()
Ejemplo n.º 28
0
class Script(CleanSave, TimestampedModel):

    # Force model into the metadataserver namespace.
    class Meta(DefaultMeta):
        pass

    objects = ScriptManager()

    name = CharField(max_length=255, unique=True)

    title = CharField(max_length=255, blank=True)

    description = TextField(blank=True)

    tags = ArrayField(TextField(), blank=True, null=True, default=list)

    script_type = IntegerField(choices=SCRIPT_TYPE_CHOICES,
                               default=SCRIPT_TYPE.TESTING)

    # The hardware the script configures or tests.
    hardware_type = IntegerField(choices=HARDWARE_TYPE_CHOICES,
                                 default=HARDWARE_TYPE.NODE)

    # Whether the script can run in parallel with other scripts.
    parallel = IntegerField(choices=SCRIPT_PARALLEL_CHOICES,
                            default=SCRIPT_PARALLEL.DISABLED)

    # Any results which will be made availble after the script is run.
    results = JSONObjectField(blank=True, default={})

    # Parameters which may be passed to the script and their constraints.
    parameters = JSONObjectField(blank=True, default={})

    # apt, snap, dpkg, to install or archives to extract.
    packages = JSONObjectField(blank=True, default={})

    # 0 is no timeout
    timeout = DurationField(default=datetime.timedelta())

    destructive = BooleanField(default=False)

    # True only if the script is shipped with MAAS
    default = BooleanField(default=False)

    script = OneToOneField(VersionedTextFile, on_delete=CASCADE)

    @property
    def script_type_name(self):
        for script_type, script_type_name in SCRIPT_TYPE_CHOICES:
            if self.script_type == script_type:
                return script_type_name
        return 'unknown'

    @property
    def hardware_type_name(self):
        return HARDWARE_TYPE_CHOICES[self.hardware_type][1]

    @property
    def parallel_name(self):
        return SCRIPT_PARALLEL_CHOICES[self.parallel][1]

    def __str__(self):
        return self.name

    def add_tag(self, tag):
        """Add tag to Script."""
        if tag not in self.tags:
            self.tags.append(tag)

    def remove_tag(self, tag):
        """Remove tag from Script."""
        if tag in self.tags:
            self.tags.remove(tag)

    def save(self, *args, **kwargs):
        if self.destructive:
            self.add_tag('destructive')
        else:
            self.remove_tag('destructive')

        for hw_type, hw_type_label in HARDWARE_TYPE_CHOICES:
            if hw_type == self.hardware_type:
                self.add_tag(hw_type_label.lower())
            else:
                self.remove_tag(hw_type_label.lower())

        return super().save(*args, **kwargs)
Ejemplo n.º 29
0
class RepairProblem(SoftDeletionModel):
    description = TextField(max_length=255)
Ejemplo n.º 30
0
class KeyTextTransform(KeyTransform):
    operator = "->>"
    nested_operator = "#>>"
    output_field = TextField()