Commit eb4d70fa authored by Christos Stavrakakis's avatar Christos Stavrakakis
Browse files

cyclades: Remove custom ProtectedDeletedManager

Remove custom ProtectedDeletedManager. Instead use the
ForeignKey.on_delete attribute that has been introduced in Django 1.4.
parent 6c227a2c
# Copyright 2012, 2013 GRNET S.A. All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions
# are met:
#
# 1. Redistributions of source code must retain the above copyright
# notice, this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
# ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
# SUCH DAMAGE.
#
# The views and conclusions contained in the software and documentation are
# those of the authors and should not be interpreted as representing official
# policies, either expressed or implied, of GRNET S.A.
from django.db.models import Manager
from django.db.models.query import QuerySet
class ProtectedDeleteManager(Manager):
"""Manager for protecting Backend deletion.
Call Backend delete() method in order to prevent deletion
of Backends that host non-deleted VirtualMachines.
"""
def get_query_set(self):
return BackendQuerySet(self.model, using=self._db)
class BackendQuerySet(QuerySet):
def delete(self):
for backend in self._clone():
backend.delete()
......@@ -32,7 +32,6 @@ import datetime
from copy import deepcopy
from django.conf import settings
from django.db import models
from django.db import IntegrityError
import utils
from contextlib import contextmanager
......@@ -41,7 +40,6 @@ from snf_django.lib.api import faults
from django.conf import settings as snf_settings
from aes_encrypt import encrypt_db_charfield, decrypt_db_charfield
from synnefo.db.managers import ProtectedDeleteManager
from synnefo.db import pools, fields
from synnefo.logic.rapi_pool import (get_rapi_client,
......@@ -102,8 +100,6 @@ class Backend(models.Model):
null=False)
ctotal = models.PositiveIntegerField('Total number of logical processors',
default=0, null=False)
# Custom object manager to protect from cascade delete
objects = ProtectedDeleteManager()
HYPERVISORS = (
("kvm", "Linux KVM hypervisor"),
......@@ -160,24 +156,6 @@ class Backend(models.Model):
self.virtual_machines.filter(deleted=False)\
.update(backend_hash=self.hash)
def delete(self, *args, **kwargs):
# Integrity Error if non-deleted VMs are associated with Backend
if self.virtual_machines.filter(deleted=False).count():
raise IntegrityError("Non-deleted virtual machines are associated "
"with backend: %s" % self)
else:
# ON_DELETE = SET NULL
for vm in self.virtual_machines.all():
vm.backend = None
vm.save()
self.virtual_machines.all().backend = None
# Remove BackendNetworks of this Backend.
# Do not use networks.all().delete(), since delete() method of
# BackendNetwork will not be called!
for net in self.networks.all():
net.delete()
super(Backend, self).delete(*args, **kwargs)
def __init__(self, *args, **kwargs):
super(Backend, self).__init__(*args, **kwargs)
if not self.pk:
......@@ -320,7 +298,8 @@ class VirtualMachine(models.Model):
userid = models.CharField('User ID of the owner', max_length=100,
db_index=True, null=False)
backend = models.ForeignKey(Backend, null=True,
related_name="virtual_machines",)
related_name="virtual_machines",
on_delete=models.PROTECT)
backend_hash = models.CharField(max_length=128, null=True, editable=False)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
......@@ -639,7 +618,8 @@ class BackendNetwork(models.Model):
}
network = models.ForeignKey(Network, related_name='backend_networks')
backend = models.ForeignKey(Backend, related_name='networks')
backend = models.ForeignKey(Backend, related_name='networks',
on_delete=models.PROTECT)
created = models.DateTimeField(auto_now_add=True)
updated = models.DateTimeField(auto_now=True)
deleted = models.BooleanField('Deleted', default=False)
......
......@@ -96,24 +96,14 @@ class BackendTest(TestCase):
mfact.BackendFactory()
self.assertRaises(Exception, mfact.BackendFactory, ())
def test_delete_backend(self):
vm = mfact.VirtualMachineFactory(backend=self.backend, deleted=True)
bnet = mfact.BackendNetworkFactory(backend=self.backend)
self.backend.delete()
self.assertRaises(Backend.DoesNotExist, Backend.objects.get,
id=self.backend.id)
# Test that VM is not deleted
vm2 = VirtualMachine.objects.get(id=vm.id)
self.assertEqual(vm2.backend, None)
# Test tha backend networks are deleted, but not the network
self.assertRaises(BackendNetwork.DoesNotExist,
BackendNetwork.objects.get, id=bnet.id)
Network.objects.get(id=bnet.network.id)
def test_delete_active_backend(self):
"""Test that a backend with non-deleted VMS is not deleted"""
mfact.VirtualMachineFactory(backend=self.backend)
self.assertRaises(IntegrityError, self.backend.delete, ())
backend = mfact.BackendFactory()
vm = mfact.VirtualMachineFactory(backend=backend)
self.assertRaises(IntegrityError, backend.delete, ())
vm.backend = None
vm.save()
backend.delete()
def test_password_encryption(self):
password_hash = self.backend.password
......
......@@ -30,20 +30,20 @@
from django.core.management.base import BaseCommand, CommandError
from synnefo.management.common import get_backend
from synnefo.db.models import VirtualMachine, BackendNetwork
from synnefo.logic import backend as backend_mod
from synnefo.db.models import Backend
from django.db import transaction, models
class Command(BaseCommand):
can_import_settings = True
HELP_MSG = """\
Remove a backend from the Database. Backend should be set to drained before
trying to remove it, in order to avoid the allocation of a new instances in
this Backend. Removal of a backend will fail if the backend hosts any
non-deleted instances."""
help = "Remove a backend from the Database. Backend should be set\n" \
"to drained before trying to remove it, in order to avoid the\n" \
"allocation of a new instances in this Backend.\n\n" \
"Removal of a backend will fail if the backend hosts any\n" \
"non-deleted instances."
output_transaction = True # The management command runs inside
# an SQL transaction
class Command(BaseCommand):
help = HELP_MSG
def handle(self, *args, **options):
write = self.stdout.write
......@@ -52,23 +52,37 @@ class Command(BaseCommand):
backend = get_backend(args[0])
write('Trying to remove backend: %s\n' % backend.clustername)
vms_in_backend = VirtualMachine.objects.filter(backend=backend,
deleted=False)
write("Trying to remove backend: %s\n" % backend.clustername)
if vms_in_backend:
if backend.virtual_machines.filter(deleted=False).exists():
raise CommandError('Backend hosts non-deleted vms. Can not delete')
networks = BackendNetwork.objects.filter(backend=backend,
deleted=False)
networks = [net.network.backend_id for net in networks]
# Get networks before deleting backend, because after deleting the
# backend, all BackendNetwork objects are deleted!
networks = [bn.network for bn in backend.networks.all()]
backend.delete()
try:
delete_backend(backend)
except models.ProtectedError as e:
msg = ("Can not delete backend because it contains"
"non-deleted VMs:\n%s" % e)
raise CommandError(msg)
write('Successfully removed backend.\n')
write('Successfully removed backend from DB.\n')
if networks:
write('Left the following orphans networks in Ganeti:\n')
write(' ' + '\n * '.join(networks) + '\n')
write('Manually remove them.\n')
write("Clearing networks from %s..\n" % backend.clustername)
for network in networks:
backend_mod.delete_network(network=network, backend=backend)
write("Successfully issued jobs to remove all networks.\n")
@transaction.commit_on_success
def delete_backend(backend):
# Get X-Lock
backend = Backend.objects.select_for_update().get(id=backend.id)
# Clear 'backend' field of 'deleted' VirtualMachines
backend.virtual_machines.filter(deleted=True).update(backend=None)
# Delete all BackendNetwork objects of this backend
backend.networks.all().delete()
backend.delete()
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment