示例#1
0
    def test__can_watch_any_signal(self):
        django_signal = pick_django_signal()

        manager = SignalsManager()
        manager.watch(
            django_signal,
            sentinel.callback,
            sender=sentinel.sender,
            weak=sentinel.weak,
            dispatch_uid=sentinel.dispatch_uid,
        )

        self.assertThat(manager._signals, HasLength(1))
        [signal] = manager._signals
        self.assertThat(
            signal.connect,
            MatchesPartialCall(
                django_signal.connect,
                sentinel.callback,
                sender=sentinel.sender,
                weak=sentinel.weak,
                dispatch_uid=sentinel.dispatch_uid,
            ),
        )
        self.assertThat(
            signal.disconnect,
            MatchesPartialCall(
                django_signal.disconnect,
                sentinel.callback,
                sender=sentinel.sender,
                dispatch_uid=sentinel.dispatch_uid,
            ),
        )
示例#2
0
log = LegacyLogger()
signals = SignalsManager()


def post_save__update_version_notifications(
    sender, instance, created, **kwargs
):
    update_version_notifications()


def post_delete__update_version_notifications(sender, instance, **kwargs):
    update_version_notifications()


signals.watch(
    post_save, post_save__update_version_notifications, sender=ControllerInfo
)
signals.watch(
    post_delete,
    post_delete__update_version_notifications,
    sender=ControllerInfo,
)

# For some reason (maube due to the cascading delete behavior), we need to
# place signals not only on ControllerInfo, but also RegionController and
# RackController.
signals.watch(
    post_delete,
    post_delete__update_version_notifications,
    sender=RegionController,
)
示例#3
0
                    parent.vlan = instance.vlan
                    parent.save()
                    log.msg(
                        "%s: VLAN updated to match %s (vlan=%s)."
                        % (
                            parent.get_log_string(),
                            instance.get_log_string(),
                            parent.vlan_id,
                        )
                    )
                finally:
                    visiting.discard(parent.id)


for klass in INTERFACE_CLASSES:
    signals.watch(post_save, update_interface_parents, sender=klass)


def interface_vlan_update(instance, old_values, **kwargs):
    """When an interfaces VLAN is changed we need to do the following.

        * If its a controller move all assigned subnets to the new VLAN. This
          is done because the subnets defined are discovered on the
          controller and an administrator cannot change them.
        * If its a machine or device then we need to remove all links if the
          VLAN is different.
    """
    new_vlan_id = instance.vlan_id
    [old_vlan_id] = old_values
    if new_vlan_id == old_vlan_id:
        # Nothing changed do nothing.
示例#4
0
__all__ = ["signals"]

from django.db.models.signals import post_delete

from maasserver.models.largefile import (
    delete_large_object_content_later,
    LargeFile,
)
from maasserver.utils.orm import post_commit_do
from maasserver.utils.signals import SignalsManager

signals = SignalsManager()


def delete_large_object(sender, instance, **kwargs):
    """Delete the large object when the `LargeFile` is deleted.

    This is done using the `post_delete` signal instead of overriding delete
    on `LargeFile`, so it works correctly for both the model and `QuerySet`.
    """
    if instance.content is not None:
        post_commit_do(delete_large_object_content_later, instance.content)


signals.watch(post_delete, delete_large_object, LargeFile)


# Enable all signals by default.
signals.enable()
示例#5
0
    """Store the instance's bmc_set for use in post_delete.

    It is coerced to a set to force it to be evaluated here in the pre_delete
    handler. Otherwise, the call will be deferred until evaluated in
    post_delete, where the results will be invalid because the instance will be
    gone.

    This information is necessary so any BMC's using this deleted IP address
    can be called on in post_delete to make their own StaticIPAddresses.
    """
    instance.__previous_bmcs = set(instance.bmc_set.all())
    instance.__previous_dnsresources = set(instance.dnsresource_set.all())


signals.watch(pre_delete,
              pre_delete_record_relations_on_delete,
              sender=StaticIPAddress)


def post_delete_remake_sip_for_bmc(sender, instance, **kwargs):
    """Now that the StaticIPAddress instance is gone, ask each BMC that was
    using it to make a new one.

    When a StaticIPAddress is deleted, any BMC models sharing it will
    automatically set their ip_address links to None. They are then recreated
    here in post_delete.
    """
    for bmc in instance.__previous_bmcs:
        # This BMC model instance was created in pre_delete and hasn't been
        # updated to reflect the just executed deletion. Set the ip_address to
        # None to replicate this. We can avoid the DB hit as we always want a
示例#6
0
文件: bmc.py 项目: th3architect/maas
from maasserver.enum import BMC_TYPE
from maasserver.models import BMC, Pod, PodHints
from maasserver.utils.signals import SignalsManager

BMC_CLASSES = [BMC, Pod]

signals = SignalsManager()


def pre_delete_bmc_clean_orphaned_ip(sender, instance, **kwargs):
    """Stash the soon-to-be-deleted BMC's ip_address for use in post_delete."""
    instance.__previous_ip_address = instance.ip_address


for klass in BMC_CLASSES:
    signals.watch(pre_delete, pre_delete_bmc_clean_orphaned_ip, sender=klass)


def post_delete_bmc_clean_orphaned_ip(sender, instance, **kwargs):
    """Removes the just-deleted BMC's ip_address if nobody else is using it.

    The potentially orphaned ip_address was stashed in the instance by the
    pre-delete signal handler.
    """
    if instance.__previous_ip_address is None:
        return
    if instance.__previous_ip_address.get_node() is not None:
        return
    if instance.__previous_ip_address.bmc_set.count() > 0:
        return
    # Delete the orphaned interface.
示例#7
0
# Copyright 2016 Canonical Ltd.  This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Respond to DHCPSnippet changes."""

__all__ = ["signals"]

from django.db.models.signals import post_delete
from maasserver.models import DHCPSnippet
from maasserver.utils.signals import SignalsManager

signals = SignalsManager()


def post_delete_dhcp_snippet_clean_values(sender, instance, **kwargs):
    """Removes the just-deleted DHCPSnippet's set of values."""
    for value in instance.value.previous_versions():
        value.delete()


signals.watch(post_delete,
              post_delete_dhcp_snippet_clean_values,
              sender=DHCPSnippet)

# Enable all signals by default.
signals.enable()
示例#8
0
"""Delete KeySource when no more keys are present."""

__all__ = ["signals"]

from django.db.models.signals import post_delete
from maasserver.models import KeySource, SSHKey
from maasserver.utils.signals import SignalsManager

signals = SignalsManager()


def post_delete_keysource_when_no_more_keys(sender, instance, **kwargs):
    """Delete Keysource when no more keys."""
    keysource = None
    try:
        keysource = instance.keysource
    except KeySource.DoesNotExist:
        pass  # Nothing to do.
    else:
        if keysource is not None:
            if not keysource.sshkey_set.exists():
                keysource.delete()


signals.watch(post_delete,
              post_delete_keysource_when_no_more_keys,
              sender=SSHKey)

# Enable all signals by default.
signals.enable()
示例#9
0
文件: users.py 项目: zhangrb/maas
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Respond to user changes."""

__all__ = [
    "signals",
]

from django.contrib.auth.models import User
from django.db.models.signals import pre_delete
from maasserver.utils.signals import SignalsManager

signals = SignalsManager()

USER_CLASSES = [
    User,
]


def pre_delete_set_event_username(sender, instance, **kwargs):
    """Set username for events that reference user being deleted."""
    for event in instance.event_set.all():
        event.username = instance.username
        event.save()


for klass in USER_CLASSES:
    signals.watch(pre_delete, pre_delete_set_event_username, sender=klass)

# Enable all signals by default.
signals.enable()
示例#10
0
# Copyright 2020 Canonical Ltd.  This software is licensed under the
# GNU Affero General Public License version 3 (see the file LICENSE).
"""Respond to PodHints changes."""

from django.db.models.signals import m2m_changed

from maasserver.models import PodHints
from maasserver.utils.signals import SignalsManager

signals = SignalsManager()


def pod_nodes_changed(sender, instance, action, reverse, model, pk_set,
                      **kwargs):
    if action == "post_remove":
        # Recalculate resources based on the remaining Nodes.
        instance.pod.sync_hints_from_nodes()


signals.watch(m2m_changed, pod_nodes_changed, sender=PodHints.nodes.through)

# Enable all signals by default
signals.enable()
示例#11
0
from maasserver.models.regionrackrpcconnection import RegionRackRPCConnection
from maasserver.models.service import Service
from maasserver.utils.signals import SignalsManager

signals = SignalsManager()


def update_rackd_status(sender, instance, **kwargs):
    """Update status of the rackd service for the rack controller the
    RPC connection was added or removed.
    """
    Service.objects.create_services_for(instance.rack_controller)
    instance.rack_controller.update_rackd_status()


signals.watch(post_save, update_rackd_status, sender=RegionRackRPCConnection)

signals.watch(post_delete, update_rackd_status, sender=RegionRackRPCConnection)


def update_all_rackd_status(sender, instance, **kwargs):
    """Update status of all rackd services when a region controller process is
    added or removed.
    """
    for rack_controller in RackController.objects.all():
        Service.objects.create_services_for(rack_controller)
        rack_controller.update_rackd_status()


signals.watch(post_save,
              update_all_rackd_status,
示例#12
0
"""Respond to partition changes."""


from django.db.models.signals import post_delete

from maasserver.models.partition import Partition
from maasserver.models.partitiontable import PartitionTable
from maasserver.utils.signals import SignalsManager

signals = SignalsManager()


def delete_partition_table(sender, instance, **kwargs):
    """Delete the partition table if this is the last partition on the
    partition table."""
    try:
        partition_table = instance.partition_table
    except PartitionTable.DoesNotExist:
        pass  # Nothing to do.
    else:
        if partition_table.partitions.count() == 0:
            partition_table.delete()


signals.watch(post_delete, delete_partition_table, Partition)


# Enable all signals by default.
signals.enable()
示例#13
0
文件: iprange.py 项目: zhangrb/maas
    try:
        if instance.subnet is None:
            return
    except ObjectDoesNotExist:
        return
    instance.subnet.update_allocation_notification()


def post_delete_check_range_utilization(sender, instance, **kwargs):
    # Be careful when checking for the subnet. In rare cases, such as a
    # cascading delete, Django can sometimes pass stale model objects into
    # signal handlers, which will raise unexpected DoesNotExist exceptions,
    # and/or otherwise invalidate foreign key fields.
    # See bug #1702527 for more details.
    try:
        if instance.subnet is None:
            return
    except ObjectDoesNotExist:
        return
    instance.subnet.update_allocation_notification()


signals.watch(
    post_save, post_save_check_range_utilization, sender=IPRange)
signals.watch(
    post_delete, post_delete_check_range_utilization, sender=IPRange)


# Enable all signals by default.
signals.enable()
示例#14
0
        # fix the size of the VirtualBlockDevice if the size of this block
        # device has changed.
        group.save()

    if isinstance(block_device, VirtualBlockDevice):
        # When not LVM the name of the block devices should stay in sync
        # with the name of the filesystem group.
        filesystem_group = block_device.filesystem_group
        if (filesystem_group.group_type != FILESYSTEM_GROUP_TYPE.LVM_VG
                and filesystem_group.name != block_device.name):
            filesystem_group.name = block_device.name
            filesystem_group.save()


for sender in senders:
    signals.watch(post_save, update_filesystem_group, sender)


def delete_filesystem_group(sender, instance, **kwargs):
    """Delete the attached `FilesystemGroup` when it is not LVM."""
    block_device = instance.actual_instance
    if isinstance(block_device, VirtualBlockDevice):
        try:
            filesystem_group = block_device.filesystem_group
        except FilesystemGroup.DoesNotExist:
            # Possible that it was deleted the same time this
            # virtual block device was deleted.
            return
        not_volume_group = (filesystem_group.group_type !=
                            FILESYSTEM_GROUP_TYPE.LVM_VG)
        if filesystem_group.id is not None and not_volume_group:
示例#15
0
from django.db.models.signals import post_delete

from maasserver.models.bootresourcefile import BootResourceFile
from maasserver.models.largefile import LargeFile
from maasserver.utils.signals import SignalsManager

signals = SignalsManager()


def delete_large_file(sender, instance, **kwargs):
    """Call delete on the LargeFile, now that the relation has been removed.
    If this was the only resource file referencing this LargeFile then it will
    be delete.

    This is done using the `post_delete` signal because only then has the
    relation been removed.
    """
    try:
        largefile = instance.largefile
    except LargeFile.DoesNotExist:
        pass  # Nothing to do.
    else:
        if largefile is not None:
            largefile.delete()


signals.watch(post_delete, delete_large_file, BootResourceFile)

# Enable all signals by default.
signals.enable()
示例#16
0
signals = SignalsManager()


def save_boot_source_cache(sender, instance, *args, **kwargs):
    """On first run the ImportResourceService sets the default BootSource then
    caches the stream's contents as normal. Setting the default BootSource
    triggers this signal. This prevents updating the cache twice.
    """
    # Don't run if the first row and newly created.
    if instance.id != 1 and BootSource.objects.count() != 0:
        update_boot_source_cache(sender, instance, *args, **kwargs)


def update_boot_source_cache(sender, instance, *args, **kwargs):
    """Update the `BootSourceCache` using the updated source.

    This only begins after a successful commit to the database, and is then
    run in a thread. Nothing waits for its completion.
    """
    post_commit_do(reactor.callLater, 0, cache_boot_sources)


signals.watch(post_save, save_boot_source_cache, BootSource)
signals.watch(post_delete, update_boot_source_cache, BootSource)
signals.watch_config(update_boot_source_cache, "enable_http_proxy")
signals.watch_config(update_boot_source_cache, "http_proxy")

# Enable all signals by default.
signals.enable()
示例#17
0
文件: subnet.py 项目: tai271828/maas
    """Updates the `StaticIPAddress`'s to ensure that they are linked to the
    correct subnet."""

    # Remove the IP addresses that no longer fall with in the CIDR.
    remove_ips = StaticIPAddress.objects.filter(
        alloc_type=IPADDRESS_TYPE.USER_RESERVED, subnet_id=subnet.id)
    remove_ips = remove_ips.extra(where=["NOT(ip << %s)"],
                                  params=[subnet.cidr])
    remove_ips.update(subnet=None)

    # Add the IP addresses that now fall into CIDR.
    add_ips = StaticIPAddress.objects.filter(subnet__isnull=True)
    add_ips = add_ips.extra(where=["ip << %s"], params=[subnet.cidr])
    add_ips.update(subnet_id=subnet.id)


def post_created(sender, instance, created, **kwargs):
    if created:
        update_referenced_ip_addresses(instance)


def updated_cidr(instance, old_values, **kwargs):
    update_referenced_ip_addresses(instance)


signals.watch(post_save, post_created, sender=Subnet)
signals.watch_fields(updated_cidr, Subnet, ["cidr"], delete=False)

# Enable all signals by default.
signals.enable()
示例#18
0
    Controller,
    RackController,
    RegionController,
]

signals = SignalsManager()


def pre_delete_update_events(sender, instance, **kwargs):
    """Update node hostname and id for events related to the node."""
    instance.event_set.all().update(node_hostname=instance.hostname,
                                    node_id=None)


for klass in NODE_CLASSES:
    signals.watch(pre_delete, pre_delete_update_events, sender=klass)


def post_init_store_previous_status(sender, instance, **kwargs):
    """Store the pre_save status of the instance."""
    instance.__previous_status = instance.status


for klass in NODE_CLASSES:
    signals.watch(post_init, post_init_store_previous_status, sender=klass)


def pre_save_update_status(sender, instance, **kwargs):
    """Update node previous_status when node status changes."""
    if (instance.__previous_status != instance.status
            and instance.__previous_status