Merge ~blake-rouse/maas:fix-1815091-2.5 into maas:2.5

Proposed by Blake Rouse
Status: Merged
Approved by: Blake Rouse
Approved revision: cd8a579fbdc3818282884a44e7f4c96925d77971
Merge reported by: MAAS Lander
Merged at revision: not available
Proposed branch: ~blake-rouse/maas:fix-1815091-2.5
Merge into: maas:2.5
Diff against target: 111 lines (+66/-8)
3 files modified
src/maasserver/exceptions.py (+5/-0)
src/maasserver/models/node.py (+26/-8)
src/maasserver/models/tests/test_node.py (+35/-0)
Reviewer Review Type Date Requested Status
Blake Rouse (community) Approve
Review via email: mp+363739@code.launchpad.net

Commit message

Fixes LP: #1815091 - Unable to clear the nodes storage configuration when a virtual block device exists on top of another virtual block device.

Backport of 5fcf2bf0a46767b2aab9d76603768818464251f7.

To post a comment you must log in.
Revision history for this message
Blake Rouse (blake-rouse) wrote :

Self-approving backport.

review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1diff --git a/src/maasserver/exceptions.py b/src/maasserver/exceptions.py
2index 49fcb48..cc6328a 100644
3--- a/src/maasserver/exceptions.py
4+++ b/src/maasserver/exceptions.py
5@@ -214,3 +214,8 @@ class PodProblem(MAASAPIException):
6
7 class NoScriptsFound(MAASException):
8 """Raised when no Scripts are found based on user input."""
9+
10+
11+class StorageClearProblem(MAASAPIException):
12+ """Raised when an issue occur's that prevents the clearing of a machine's
13+ storage configuration."""
14diff --git a/src/maasserver/models/node.py b/src/maasserver/models/node.py
15index 03c890e..87b5042 100644
16--- a/src/maasserver/models/node.py
17+++ b/src/maasserver/models/node.py
18@@ -95,6 +95,7 @@ from maasserver.exceptions import (
19 NodeStateViolation,
20 NoScriptsFound,
21 PowerProblem,
22+ StorageClearProblem,
23 )
24 from maasserver.fields import (
25 MAASIPAddressField,
26@@ -3271,14 +3272,31 @@ class Node(CleanSave, TimestampedModel):
27 block_device__id__in=block_device_ids).delete()
28 Filesystem.objects.filter(
29 block_device__id__in=block_device_ids).delete()
30- for block_device in self.virtualblockdevice_set.all():
31- try:
32- block_device.filesystem_group.delete(force=True)
33- except FilesystemGroup.DoesNotExist:
34- # When a filesystem group has multiple virtual block devices
35- # it is possible that accessing `filesystem_group` will
36- # result in it already being deleted.
37- pass
38+ virtual_devices = list(reversed(self.virtualblockdevice_set.all()))
39+ for _ in range(10): # 10 times gives enough tries to remove.
40+ for virtual_bd in virtual_devices[:]: # Iterate on copy.
41+ try:
42+ virtual_bd.filesystem_group.delete(force=True)
43+ virtual_devices.remove(virtual_bd)
44+ except FilesystemGroup.DoesNotExist:
45+ # When a filesystem group has multiple virtual block
46+ # devices it is possible that accessing `filesystem_group`
47+ # will result in it already being deleted.
48+ virtual_devices.remove(virtual_bd)
49+ except ValidationError:
50+ # Cannot be deleted because another device depends on it.
51+ # Next loop through will delete the device.
52+ pass
53+ if not virtual_devices:
54+ # Done, all have been removed.
55+ break
56+ if virtual_devices:
57+ raise StorageClearProblem(
58+ "Failed to remove %d virtual devices: %s" % (
59+ len(virtual_devices), ', '.join([
60+ virtual_bd.get_name()
61+ for virtual_bd in virtual_devices
62+ ])))
63
64 def _create_acquired_filesystems(self):
65 """Copy all filesystems that have a user mountable filesystem to be
66diff --git a/src/maasserver/models/tests/test_node.py b/src/maasserver/models/tests/test_node.py
67index 2db8120..dd964f4 100644
68--- a/src/maasserver/models/tests/test_node.py
69+++ b/src/maasserver/models/tests/test_node.py
70@@ -4075,6 +4075,41 @@ class TestNode(MAASServerTestCase):
71 reload_object(filesystem_on_iscsi_vbd1), Is(None),
72 "Filesystem on virtual block device should have been removed.")
73
74+ def test__clear_full_storage_configuration_lp1815091(self):
75+ node = factory.make_Node()
76+ physical_block_devices = [
77+ factory.make_PhysicalBlockDevice(node=node, size=10 * 1000 ** 3)
78+ for _ in range(4)
79+ ]
80+ raid5_filesystems = [
81+ factory.make_Filesystem(
82+ block_device=bd,
83+ fstype=FILESYSTEM_TYPE.RAID)
84+ for bd in physical_block_devices[:3]
85+ ]
86+ raid5 = factory.make_FilesystemGroup(
87+ group_type=FILESYSTEM_GROUP_TYPE.RAID_5,
88+ filesystems=raid5_filesystems).virtual_device
89+ backing_filesystem = factory.make_Filesystem(
90+ block_device=raid5,
91+ fstype=FILESYSTEM_TYPE.BCACHE_BACKING)
92+ cacheset = factory.make_CacheSet(
93+ block_device=physical_block_devices[3])
94+ root = factory.make_FilesystemGroup(
95+ group_type=FILESYSTEM_GROUP_TYPE.BCACHE,
96+ filesystems=[backing_filesystem], cache_set=cacheset)
97+ node._clear_full_storage_configuration()
98+ for pbd in physical_block_devices:
99+ self.expectThat(
100+ reload_object(pbd), Not(Is(None)),
101+ "Physical block device should not have been deleted.")
102+ self.expectThat(
103+ reload_object(root), Is(None),
104+ "Bcache should have been removed.")
105+ self.expectThat(
106+ reload_object(raid5), Is(None),
107+ "Raid should have been removed.")
108+
109 def test__create_acquired_filesystems(self):
110 node = factory.make_Node(status=NODE_STATUS.ALLOCATED)
111 block_device = factory.make_PhysicalBlockDevice(node=node)

Subscribers

People subscribed via source and target branches