From 0524d21c91d8213216104007d108ed2afee7f63e Mon Sep 17 00:00:00 2001 From: Roman Prykhodchenko Date: Mon, 27 Apr 2020 17:12:47 +0200 Subject: [PATCH] Disable deletion of used cloud flavors --- src/ralph/lib/api/exceptions.py | 10 +++++++++ src/ralph/virtual/api.py | 10 +++++++++ src/ralph/virtual/tests/test_api.py | 34 +++++++++++++++++++++++++++++ 3 files changed, 54 insertions(+) create mode 100644 src/ralph/lib/api/exceptions.py diff --git a/src/ralph/lib/api/exceptions.py b/src/ralph/lib/api/exceptions.py new file mode 100644 index 0000000000..84ae4a76ca --- /dev/null +++ b/src/ralph/lib/api/exceptions.py @@ -0,0 +1,10 @@ +from django.utils.translation import ugettext_lazy as _ +from rest_framework import status +from rest_framework.exceptions import APIException + + +class Conflict(APIException): + status_code = status.HTTP_409_CONFLICT + default_detail = _( + 'State of the resource is in conflict with the request.' + ) diff --git a/src/ralph/virtual/api.py b/src/ralph/virtual/api.py index ed683359d7..f3944e60da 100644 --- a/src/ralph/virtual/api.py +++ b/src/ralph/virtual/api.py @@ -1,6 +1,7 @@ # -*- coding: utf-8 -*- from django.db import transaction from django.db.models import Prefetch +from django.utils.translation import ugettext_lazy as _ from rest_framework import relations, serializers from ralph.api import RalphAPISerializer, RalphAPIViewSet, router @@ -20,6 +21,7 @@ from ralph.configuration_management.api import SCMInfoSerializer from ralph.data_center.api.serializers import DataCenterAssetSimpleSerializer from ralph.data_center.models import DCHost +from ralph.lib.api.exceptions import Conflict from ralph.security.api import SecurityScanSerializer from ralph.security.models import SecurityScan from ralph.virtual.admin import VirtualServerAdmin @@ -180,6 +182,14 @@ class CloudFlavorViewSet(RalphAPIViewSet): prefetch_related = ['tags', 'virtualcomponent_set__model'] filter_fields = ['flavor_id'] + def perform_destroy(self, instance): + if instance.cloudhost_set.count() != 0: + raise Conflict( + _("Cloud flavor is in use and hence is not delible.") + ) + + return super().perform_destroy(instance) + class CloudProviderViewSet(RalphAPIViewSet): queryset = CloudProvider.objects.all() diff --git a/src/ralph/virtual/tests/test_api.py b/src/ralph/virtual/tests/test_api.py index f1adc03200..dbd53d7220 100644 --- a/src/ralph/virtual/tests/test_api.py +++ b/src/ralph/virtual/tests/test_api.py @@ -298,6 +298,40 @@ def test_patch_cloudprovider(self): provider = CloudProvider.objects.get(name=args['name']) self.assertEqual(provider.name, args['name']) + def test_delete_cloud_flavor_returns_409_if_is_used_by_cloud_hosts(self): + # given + cloud_flavor = CloudFlavorFactory() + CloudHostFactory(cloudflavor=cloud_flavor) + + # when + url = reverse('cloudflavor-detail', args=(cloud_flavor.pk,)) + resp = self.client.delete(url) + + # then + self.assertEqual(resp.status_code, status.HTTP_409_CONFLICT) + self.assertEqual( + resp.data['detail'], + 'Cloud flavor is in use and hence is not delible.' + ) + self.assertTrue( + CloudFlavor.objects.filter(pk=cloud_flavor.pk).exists() + ) + + def test_unused_cloud_flavor_can_be_deleted(self): + # given + cloud_flavor = CloudFlavorFactory() + + # when + url = reverse('cloudflavor-detail', args=(cloud_flavor.pk,)) + resp = self.client.delete(url) + + # then + self.assertEqual(resp.status_code, status.HTTP_204_NO_CONTENT) + self.assertRaises( + CloudFlavor.DoesNotExist, + cloud_flavor.refresh_from_db + ) + def test_inheritance_of_service_env_on_change_in_a_cloud_project(self): url = reverse('cloudproject-detail', args=(self.cloud_project.id,)) args = {