Merge lp:~ltrager/maas/validate_storage into lp:~maas-committers/maas/trunk
- validate_storage
- Merge into trunk
Status: | Merged |
---|---|
Approved by: | Blake Rouse |
Approved revision: | no longer in the source branch. |
Merged at revision: | 4506 |
Proposed branch: | lp:~ltrager/maas/validate_storage |
Merge into: | lp:~maas-committers/maas/trunk |
Diff against target: |
411 lines (+212/-13) 11 files modified
src/maasserver/models/node.py (+74/-1) src/maasserver/models/tests/test_filesystem.py (+4/-1) src/maasserver/models/tests/test_node.py (+33/-2) src/maasserver/models/tests/test_virtualblockdevice.py (+14/-0) src/maasserver/models/virtualblockdevice.py (+34/-0) src/maasserver/static/js/angular/controllers/node_details_storage.js (+8/-0) src/maasserver/static/js/angular/controllers/tests/test_node_details_storage.js (+14/-0) src/maasserver/static/partials/node-details.html (+9/-0) src/maasserver/testing/factory.py (+5/-1) src/maasserver/websockets/handlers/node.py (+1/-0) src/maasserver/websockets/handlers/tests/test_node.py (+16/-8) |
To merge this branch: | bzr merge lp:~ltrager/maas/validate_storage |
Related bugs: |
Reviewer | Review Type | Date Requested | Status |
---|---|---|---|
Gavin Panella (community) | Approve | ||
Review via email: mp+277402@code.launchpad.net |
Commit message
Validate a node has a boot device and / is mounted before deploying
Description of the change
This patch validates that the node has a boot device and has / mounted somewhere before deploying. This also adds an error message to the UI when this is not the case. I modified the make_Node method to also create a partition and mount it at '/' since we now require it to be a valid bootable node.
I also made two small fixes for the previous validate node on network patch
* I was raising a validation error with just the error message. If we don't return a dictionary the error doesn't display in the UI.
* Rename the on_network tests to not include 'managed'
Blake Rouse (blake-rouse) wrote : | # |
Lee Trager (ltrager) wrote : | # |
We now check if / is on a bcache block device, if we are we check if /boot is mounted not on a bcache device. I also made the error message more specific to why the storage layout isn't valid.
Gavin Panella (allenap) wrote : | # |
Lots of comments. I think there are a few things that need fixing before it can be landed. I'm worried about how the new methods will perform. I won't block landing based on that but it would be good if you could do something about it.
Lee Trager (ltrager) wrote : | # |
Thanks for the review. I've gone over and responded to your comments below. The performance issues you are seeing are all due to validating that / isn't on bcache. In order to check if a partition/block device is on bcache I need to check the parent devices. There isn't currently a relation between child and parent so I have to iterate over all the blockdevices the node has until I find the partition/block device in question.
There are only two ways I can think of that will improve performance
1. Modify the schema so we have a relation between child(virtual block devices, and partitions), and parent(block devices, partitions)
2. Don't check if / is on bcache.
Lee Trager (ltrager) wrote : | # |
I stand corrected. A partition/block device can have two file systems. One is the filesystem the administrator sets another is the one a user sets. I now use the get_effective_
The only place I use the first() method is getting the filesystem_group for a cache_set.
Gavin Panella (allenap) wrote : | # |
Replies to diff comments.
Gavin Panella (allenap) wrote : | # |
This seems sane to me. Just change storage_
Actually, hold up, there's no test for the change to node-details.html. Better get your Angular boots on. If you really want to land this now, undo the change to node-details.html and propose it separately with a test.
Lee Trager (ltrager) wrote : | # |
I took your suggestion and renamed storage_
MAAS Lander (maas-lander) wrote : | # |
The attempt to merge lp:~ltrager/maas/validate_storage into lp:maas failed. Below is the output from the failed tests.
Hit http://
Get:1 http://
Hit http://
Hit http://
Hit http://
Hit http://
Hit http://
Hit http://
Hit http://
Get:2 http://
Get:3 http://
Get:4 http://
Get:5 http://
Get:6 http://
Get:7 http://
Hit http://
Hit http://
Hit http://
Hit http://
Hit http://
Hit http://
Fetched 22.7 MB in 13s (1,725 kB/s)
Reading package lists...
sudo DEBIAN_
--no-
Reading package lists...
Building dependency tree...
Reading state information...
apache2 is...
Preview Diff
1 | === modified file 'src/maasserver/models/node.py' | |||
2 | --- src/maasserver/models/node.py 2015-11-13 20:01:04 +0000 | |||
3 | +++ src/maasserver/models/node.py 2015-11-17 23:04:09 +0000 | |||
4 | @@ -63,6 +63,7 @@ | |||
5 | 63 | from maasserver.enum import ( | 63 | from maasserver.enum import ( |
6 | 64 | ALLOCATED_NODE_STATUSES, | 64 | ALLOCATED_NODE_STATUSES, |
7 | 65 | FILESYSTEM_FORMAT_TYPE_CHOICES_DICT, | 65 | FILESYSTEM_FORMAT_TYPE_CHOICES_DICT, |
8 | 66 | FILESYSTEM_TYPE, | ||
9 | 66 | INTERFACE_LINK_TYPE, | 67 | INTERFACE_LINK_TYPE, |
10 | 67 | INTERFACE_TYPE, | 68 | INTERFACE_TYPE, |
11 | 68 | IPADDRESS_FAMILY, | 69 | IPADDRESS_FAMILY, |
12 | @@ -760,6 +761,73 @@ | |||
13 | 760 | event_action=action, | 761 | event_action=action, |
14 | 761 | event_description=description) | 762 | event_description=description) |
15 | 762 | 763 | ||
16 | 764 | def storage_layout_issues(self): | ||
17 | 765 | """Return any errors with the storage layout. | ||
18 | 766 | |||
19 | 767 | Checks that the node has / mounted. If / is mounted on bcache check | ||
20 | 768 | that /boot is mounted and is not on bcache.""" | ||
21 | 769 | def on_bcache(obj): | ||
22 | 770 | if obj.type == "physical": | ||
23 | 771 | return False | ||
24 | 772 | elif obj.type == "partition": | ||
25 | 773 | return on_bcache(obj.partition_table.block_device) | ||
26 | 774 | for parent in obj.virtualblockdevice.get_parents(): | ||
27 | 775 | if((parent.type != "physical" and on_bcache(parent)) or | ||
28 | 776 | (parent.get_effective_filesystem().fstype in | ||
29 | 777 | [FILESYSTEM_TYPE.BCACHE_CACHE, | ||
30 | 778 | FILESYSTEM_TYPE.BCACHE_BACKING])): | ||
31 | 779 | return True | ||
32 | 780 | return False | ||
33 | 781 | |||
34 | 782 | has_boot = False | ||
35 | 783 | root_mounted = False | ||
36 | 784 | root_on_bcache = False | ||
37 | 785 | boot_mounted = False | ||
38 | 786 | |||
39 | 787 | for block_device in self.blockdevice_set.all(): | ||
40 | 788 | if block_device.is_boot_disk(): | ||
41 | 789 | has_boot = True | ||
42 | 790 | pt = block_device.get_partitiontable() | ||
43 | 791 | if pt is not None: | ||
44 | 792 | for partition in pt.partitions.all(): | ||
45 | 793 | fs = partition.get_effective_filesystem() | ||
46 | 794 | if fs is None: | ||
47 | 795 | continue | ||
48 | 796 | if fs.mount_point == '/': | ||
49 | 797 | root_mounted = True | ||
50 | 798 | if on_bcache(block_device): | ||
51 | 799 | root_on_bcache = True | ||
52 | 800 | else: | ||
53 | 801 | # If / is mounted and its not on bcache the system | ||
54 | 802 | # is bootable | ||
55 | 803 | return [] | ||
56 | 804 | elif (fs.mount_point == '/boot' and | ||
57 | 805 | not on_bcache(block_device)): | ||
58 | 806 | boot_mounted = True | ||
59 | 807 | else: | ||
60 | 808 | fs = block_device.get_effective_filesystem() | ||
61 | 809 | if fs is None: | ||
62 | 810 | continue | ||
63 | 811 | if fs.mount_point == '/': | ||
64 | 812 | root_mounted = True | ||
65 | 813 | if on_bcache(block_device): | ||
66 | 814 | root_on_bcache = True | ||
67 | 815 | else: | ||
68 | 816 | # If / is mounted and its not on bcache the system | ||
69 | 817 | # is bootable. | ||
70 | 818 | return [] | ||
71 | 819 | elif fs.mount_point == '/boot' and not on_bcache(block_device): | ||
72 | 820 | boot_mounted = True | ||
73 | 821 | issues = [] | ||
74 | 822 | if not has_boot: | ||
75 | 823 | issues.append("Node must have boot disk.") | ||
76 | 824 | if not root_mounted: | ||
77 | 825 | issues.append("Node must have / mounted.") | ||
78 | 826 | if root_mounted and root_on_bcache and not boot_mounted: | ||
79 | 827 | issues.append("Because / is on a bcache volume you must create " | ||
80 | 828 | "/boot on a non-bcache volume") | ||
81 | 829 | return issues | ||
82 | 830 | |||
83 | 763 | def on_network(self): | 831 | def on_network(self): |
84 | 764 | """Return true if the node is connected to a managed network.""" | 832 | """Return true if the node is connected to a managed network.""" |
85 | 765 | for interface in self.interface_set.all(): | 833 | for interface in self.interface_set.all(): |
86 | @@ -772,7 +840,12 @@ | |||
87 | 772 | def _start_deployment(self): | 840 | def _start_deployment(self): |
88 | 773 | """Mark a node as being deployed.""" | 841 | """Mark a node as being deployed.""" |
89 | 774 | if self.on_network() is False: | 842 | if self.on_network() is False: |
91 | 775 | raise ValidationError("Node must be configured to use a network") | 843 | raise ValidationError( |
92 | 844 | {"network": | ||
93 | 845 | ["Node must be configured to use a network"]}) | ||
94 | 846 | storage_layout_issues = self.storage_layout_issues() | ||
95 | 847 | if len(storage_layout_issues) > 0: | ||
96 | 848 | raise ValidationError({"storage": storage_layout_issues}) | ||
97 | 776 | self.status = NODE_STATUS.DEPLOYING | 849 | self.status = NODE_STATUS.DEPLOYING |
98 | 777 | self.save() | 850 | self.save() |
99 | 778 | 851 | ||
100 | 779 | 852 | ||
101 | === modified file 'src/maasserver/models/tests/test_filesystem.py' | |||
102 | --- src/maasserver/models/tests/test_filesystem.py 2015-10-27 20:53:16 +0000 | |||
103 | +++ src/maasserver/models/tests/test_filesystem.py 2015-11-17 23:04:09 +0000 | |||
104 | @@ -29,6 +29,9 @@ | |||
105 | 29 | 29 | ||
106 | 30 | def test_filter_by_node(self): | 30 | def test_filter_by_node(self): |
107 | 31 | node = factory.make_Node() | 31 | node = factory.make_Node() |
108 | 32 | boot_bd = node.blockdevice_set.first() | ||
109 | 33 | root_partition = boot_bd.partitiontable_set.first().partitions.first() | ||
110 | 34 | root_fs = root_partition.filesystem_set.first() | ||
111 | 32 | block_device = factory.make_PhysicalBlockDevice(node=node) | 35 | block_device = factory.make_PhysicalBlockDevice(node=node) |
112 | 33 | bd_for_partitions = factory.make_PhysicalBlockDevice(node=node) | 36 | bd_for_partitions = factory.make_PhysicalBlockDevice(node=node) |
113 | 34 | partition_table = factory.make_PartitionTable( | 37 | partition_table = factory.make_PartitionTable( |
114 | @@ -37,7 +40,7 @@ | |||
115 | 37 | filesystem_on_bd = factory.make_Filesystem(block_device=block_device) | 40 | filesystem_on_bd = factory.make_Filesystem(block_device=block_device) |
116 | 38 | filesystem_on_partition = factory.make_Filesystem(partition=partition) | 41 | filesystem_on_partition = factory.make_Filesystem(partition=partition) |
117 | 39 | self.assertItemsEqual( | 42 | self.assertItemsEqual( |
119 | 40 | [filesystem_on_bd, filesystem_on_partition], | 43 | [root_fs, filesystem_on_bd, filesystem_on_partition], |
120 | 41 | Filesystem.objects.filter_by_node(node)) | 44 | Filesystem.objects.filter_by_node(node)) |
121 | 42 | 45 | ||
122 | 43 | 46 | ||
123 | 44 | 47 | ||
124 | === modified file 'src/maasserver/models/tests/test_node.py' | |||
125 | --- src/maasserver/models/tests/test_node.py 2015-11-10 18:12:57 +0000 | |||
126 | +++ src/maasserver/models/tests/test_node.py 2015-11-17 23:04:09 +0000 | |||
127 | @@ -3774,15 +3774,46 @@ | |||
128 | 3774 | 3774 | ||
129 | 3775 | self.assertThat(release_auto_ips, MockCalledOnceWith()) | 3775 | self.assertThat(release_auto_ips, MockCalledOnceWith()) |
130 | 3776 | 3776 | ||
132 | 3777 | def test_connected_to_managed_network_true_when_connected(self): | 3777 | def test_on_network_returns_true_when_connected(self): |
133 | 3778 | node = factory.make_Node_with_Interface_on_Subnet( | 3778 | node = factory.make_Node_with_Interface_on_Subnet( |
134 | 3779 | status=NODE_STATUS.ALLOCATED) | 3779 | status=NODE_STATUS.ALLOCATED) |
135 | 3780 | self.assertTrue(node.on_network()) | 3780 | self.assertTrue(node.on_network()) |
136 | 3781 | 3781 | ||
138 | 3782 | def test_connected_to_managed_network_false_when_not_connected(self): | 3782 | def test_on_network_returns_false_when_not_connected(self): |
139 | 3783 | node = factory.make_Node(status=NODE_STATUS.ALLOCATED) | 3783 | node = factory.make_Node(status=NODE_STATUS.ALLOCATED) |
140 | 3784 | self.assertFalse(node.on_network()) | 3784 | self.assertFalse(node.on_network()) |
141 | 3785 | 3785 | ||
142 | 3786 | def test_storage_layout_issues_is_valid_when_flat(self): | ||
143 | 3787 | node = factory.make_Node() | ||
144 | 3788 | self.assertEquals([], node.storage_layout_issues()) | ||
145 | 3789 | |||
146 | 3790 | def test_storage_layout_issues_returns_valid_with_boot_and_bcache(self): | ||
147 | 3791 | node = factory.make_Node(with_boot_disk=False) | ||
148 | 3792 | boot_partition = factory.make_Partition(node=node) | ||
149 | 3793 | factory.make_Filesystem(partition=boot_partition, mount_point='/boot') | ||
150 | 3794 | fs_group = factory.make_FilesystemGroup( | ||
151 | 3795 | node=node, group_type=FILESYSTEM_GROUP_TYPE.BCACHE) | ||
152 | 3796 | bcache = fs_group.virtual_device | ||
153 | 3797 | factory.make_Filesystem(block_device=bcache, mount_point="/") | ||
154 | 3798 | self.assertEquals([], node.storage_layout_issues()) | ||
155 | 3799 | |||
156 | 3800 | def test_storage_layout_issues_returns_invalid_when_no_disk(self): | ||
157 | 3801 | node = factory.make_Node(with_boot_disk=False) | ||
158 | 3802 | self.assertEquals(["Node must have boot disk.", | ||
159 | 3803 | "Node must have / mounted."], | ||
160 | 3804 | node.storage_layout_issues()) | ||
161 | 3805 | |||
162 | 3806 | def test_storage_layout_issues_returns_invalid_when_root_on_bcache(self): | ||
163 | 3807 | node = factory.make_Node(with_boot_disk=False) | ||
164 | 3808 | factory.make_Partition(node=node) | ||
165 | 3809 | fs_group = factory.make_FilesystemGroup( | ||
166 | 3810 | node=node, group_type=FILESYSTEM_GROUP_TYPE.BCACHE) | ||
167 | 3811 | bcache = fs_group.virtual_device | ||
168 | 3812 | factory.make_Filesystem(block_device=bcache, mount_point="/") | ||
169 | 3813 | self.assertEquals(["Because / is on a bcache volume you must create " | ||
170 | 3814 | "/boot on a non-bcache volume"], | ||
171 | 3815 | node.storage_layout_issues()) | ||
172 | 3816 | |||
173 | 3786 | 3817 | ||
174 | 3787 | class TestNode_Stop(MAASServerTestCase): | 3818 | class TestNode_Stop(MAASServerTestCase): |
175 | 3788 | """Tests for Node.stop().""" | 3819 | """Tests for Node.stop().""" |
176 | 3789 | 3820 | ||
177 | === modified file 'src/maasserver/models/tests/test_virtualblockdevice.py' | |||
178 | --- src/maasserver/models/tests/test_virtualblockdevice.py 2015-08-14 13:48:59 +0000 | |||
179 | +++ src/maasserver/models/tests/test_virtualblockdevice.py 2015-11-17 23:04:09 +0000 | |||
180 | @@ -159,3 +159,17 @@ | |||
181 | 159 | uuid = uuid4() | 159 | uuid = uuid4() |
182 | 160 | block_device = factory.make_VirtualBlockDevice(uuid=uuid) | 160 | block_device = factory.make_VirtualBlockDevice(uuid=uuid) |
183 | 161 | self.assertEquals('%s' % uuid, block_device.uuid) | 161 | self.assertEquals('%s' % uuid, block_device.uuid) |
184 | 162 | |||
185 | 163 | def test_get_parents_finds_devices(self): | ||
186 | 164 | node = factory.make_Node() | ||
187 | 165 | factory.make_FilesystemGroup( | ||
188 | 166 | node=node, | ||
189 | 167 | group_type=factory.pick_enum( | ||
190 | 168 | FILESYSTEM_GROUP_TYPE, but_not=FILESYSTEM_GROUP_TYPE.LVM_VG)) | ||
191 | 169 | fs_group_disks = [block_device.blockdevice_ptr | ||
192 | 170 | for block_device in | ||
193 | 171 | node.physicalblockdevice_set.all() | ||
194 | 172 | if not block_device.is_boot_disk()] | ||
195 | 173 | virtualblockdevice = node.virtualblockdevice_set.first() | ||
196 | 174 | self.assertEqual( | ||
197 | 175 | len(fs_group_disks), len(virtualblockdevice.get_parents())) | ||
198 | 162 | 176 | ||
199 | === modified file 'src/maasserver/models/virtualblockdevice.py' | |||
200 | --- src/maasserver/models/virtualblockdevice.py 2015-11-16 06:26:17 +0000 | |||
201 | +++ src/maasserver/models/virtualblockdevice.py 2015-11-17 23:04:09 +0000 | |||
202 | @@ -137,3 +137,37 @@ | |||
203 | 137 | if not self.uuid: | 137 | if not self.uuid: |
204 | 138 | self.uuid = uuid4() | 138 | self.uuid = uuid4() |
205 | 139 | return super(VirtualBlockDevice, self).save(*args, **kwargs) | 139 | return super(VirtualBlockDevice, self).save(*args, **kwargs) |
206 | 140 | |||
207 | 141 | def get_parents(self): | ||
208 | 142 | """Return the blockdevices and partition which make up this device.""" | ||
209 | 143 | def check_fs_group(obj): | ||
210 | 144 | fs = obj.get_effective_filesystem() | ||
211 | 145 | if fs is None: | ||
212 | 146 | return False | ||
213 | 147 | if fs.filesystem_group is not None: | ||
214 | 148 | fs_group = fs.filesystem_group | ||
215 | 149 | elif fs.cache_set is not None: | ||
216 | 150 | # A block device/partition can only have one cache_set | ||
217 | 151 | fs_group = fs.cache_set.filesystemgroup_set.first() | ||
218 | 152 | else: | ||
219 | 153 | return False | ||
220 | 154 | for virtual_device in fs_group.virtual_devices.all(): | ||
221 | 155 | if virtual_device.id == self.id: | ||
222 | 156 | return True | ||
223 | 157 | return False | ||
224 | 158 | |||
225 | 159 | parents = [] | ||
226 | 160 | # We need to check all of the nodes block devices incase | ||
227 | 161 | # we have nested virtual block devices. | ||
228 | 162 | for block_device in self.node.blockdevice_set.all(): | ||
229 | 163 | if block_device.id == self.id: | ||
230 | 164 | continue | ||
231 | 165 | if check_fs_group(block_device): | ||
232 | 166 | parents.append(block_device) | ||
233 | 167 | pt = block_device.get_partitiontable() | ||
234 | 168 | if pt is None: | ||
235 | 169 | continue | ||
236 | 170 | for partition in pt.partitions.all(): | ||
237 | 171 | if check_fs_group(partition): | ||
238 | 172 | parents.append(partition) | ||
239 | 173 | return parents | ||
240 | 140 | 174 | ||
241 | === modified file 'src/maasserver/static/js/angular/controllers/node_details_storage.js' | |||
242 | --- src/maasserver/static/js/angular/controllers/node_details_storage.js 2015-11-10 17:36:38 +0000 | |||
243 | +++ src/maasserver/static/js/angular/controllers/node_details_storage.js 2015-11-17 23:04:09 +0000 | |||
244 | @@ -1913,4 +1913,12 @@ | |||
245 | 1913 | return false; | 1913 | return false; |
246 | 1914 | } | 1914 | } |
247 | 1915 | }; | 1915 | }; |
248 | 1916 | |||
249 | 1917 | // Returns true if there are storage layout errors | ||
250 | 1918 | $scope.hasStorageLayoutIssues = function() { | ||
251 | 1919 | if(angular.isObject($scope.node)) { | ||
252 | 1920 | return $scope.node.storage_layout_issues.length > 0; | ||
253 | 1921 | } | ||
254 | 1922 | return false; | ||
255 | 1923 | }; | ||
256 | 1916 | }]); | 1924 | }]); |
257 | 1917 | 1925 | ||
258 | === modified file 'src/maasserver/static/js/angular/controllers/tests/test_node_details_storage.js' | |||
259 | --- src/maasserver/static/js/angular/controllers/tests/test_node_details_storage.js 2015-11-10 17:36:38 +0000 | |||
260 | +++ src/maasserver/static/js/angular/controllers/tests/test_node_details_storage.js 2015-11-17 23:04:09 +0000 | |||
261 | @@ -4744,4 +4744,18 @@ | |||
262 | 4744 | expect($scope.isAllStorageDisabled()).toBe(true); | 4744 | expect($scope.isAllStorageDisabled()).toBe(true); |
263 | 4745 | }); | 4745 | }); |
264 | 4746 | }); | 4746 | }); |
265 | 4747 | |||
266 | 4748 | describe("hasStorageLayoutIssues", function() { | ||
267 | 4749 | it("true when node.storage_layout_issues has issues", function() { | ||
268 | 4750 | var controller = makeController(); | ||
269 | 4751 | $scope.node.storage_layout_issues = [makeName("issue")]; | ||
270 | 4752 | expect($scope.hasStorageLayoutIssues()).toBe(true); | ||
271 | 4753 | }); | ||
272 | 4754 | |||
273 | 4755 | it("false when node.storage_layout_issues has no issues", function() { | ||
274 | 4756 | var controller = makeController(); | ||
275 | 4757 | $scope.node.storage_layout_issues = []; | ||
276 | 4758 | expect($scope.hasStorageLayoutIssues()).toBe(false); | ||
277 | 4759 | }); | ||
278 | 4760 | }); | ||
279 | 4747 | }); | 4761 | }); |
280 | 4748 | 4762 | ||
281 | === modified file 'src/maasserver/static/partials/node-details.html' | |||
282 | --- src/maasserver/static/partials/node-details.html 2015-11-14 00:32:02 +0000 | |||
283 | +++ src/maasserver/static/partials/node-details.html 2015-11-17 23:04:09 +0000 | |||
284 | @@ -772,6 +772,15 @@ | |||
285 | 772 | </li> | 772 | </li> |
286 | 773 | </ul> | 773 | </ul> |
287 | 774 | </div> | 774 | </div> |
288 | 775 | <div class="twelve-col error ng-hide" data-ng-show="hasStorageLayoutIssues()"> | ||
289 | 776 | <div data-ng-repeat="issue in node.storage_layout_issues"> | ||
290 | 777 | <ul class="flash-messages"> | ||
291 | 778 | <li class="flash-messages__item error"> | ||
292 | 779 | {$ issue $} | ||
293 | 780 | </li> | ||
294 | 781 | </ul> | ||
295 | 782 | </div> | ||
296 | 783 | </div> | ||
297 | 775 | <div class="twelve-col padding-bottom no-margin"> | 784 | <div class="twelve-col padding-bottom no-margin"> |
298 | 776 | <h3>File systems</h3> | 785 | <h3>File systems</h3> |
299 | 777 | <section class="table"> | 786 | <section class="table"> |
300 | 778 | 787 | ||
301 | === modified file 'src/maasserver/testing/factory.py' | |||
302 | --- src/maasserver/testing/factory.py 2015-11-12 19:57:04 +0000 | |||
303 | +++ src/maasserver/testing/factory.py 2015-11-17 23:04:09 +0000 | |||
304 | @@ -32,6 +32,7 @@ | |||
305 | 32 | from django.utils import timezone | 32 | from django.utils import timezone |
306 | 33 | from maasserver.clusterrpc.power_parameters import get_power_types | 33 | from maasserver.clusterrpc.power_parameters import get_power_types |
307 | 34 | from maasserver.enum import ( | 34 | from maasserver.enum import ( |
308 | 35 | ALLOCATED_NODE_STATUSES, | ||
309 | 35 | BOOT_RESOURCE_FILE_TYPE, | 36 | BOOT_RESOURCE_FILE_TYPE, |
310 | 36 | BOOT_RESOURCE_TYPE, | 37 | BOOT_RESOURCE_TYPE, |
311 | 37 | CACHE_MODE_TYPE, | 38 | CACHE_MODE_TYPE, |
312 | @@ -330,7 +331,10 @@ | |||
313 | 330 | self.make_Interface( | 331 | self.make_Interface( |
314 | 331 | INTERFACE_TYPE.PHYSICAL, node=node, vlan=vlan, fabric=fabric) | 332 | INTERFACE_TYPE.PHYSICAL, node=node, vlan=vlan, fabric=fabric) |
315 | 332 | if installable and with_boot_disk: | 333 | if installable and with_boot_disk: |
317 | 333 | self.make_PhysicalBlockDevice(node=node) | 334 | root_partition = self.make_Partition(node=node) |
318 | 335 | acquired = node.status in ALLOCATED_NODE_STATUSES | ||
319 | 336 | self.make_Filesystem( | ||
320 | 337 | partition=root_partition, mount_point='/', acquired=acquired) | ||
321 | 334 | 338 | ||
322 | 335 | # Update the 'updated'/'created' fields with a call to 'update' | 339 | # Update the 'updated'/'created' fields with a call to 'update' |
323 | 336 | # preventing a call to save() from overriding the values. | 340 | # preventing a call to save() from overriding the values. |
324 | 337 | 341 | ||
325 | === modified file 'src/maasserver/websockets/handlers/node.py' | |||
326 | --- src/maasserver/websockets/handlers/node.py 2015-11-10 23:41:49 +0000 | |||
327 | +++ src/maasserver/websockets/handlers/node.py 2015-11-17 23:04:09 +0000 | |||
328 | @@ -313,6 +313,7 @@ | |||
329 | 313 | {'key': key, 'ui': ui} | 313 | {'key': key, 'ui': ui} |
330 | 314 | for key, ui in FILESYSTEM_FORMAT_TYPE_CHOICES | 314 | for key, ui in FILESYSTEM_FORMAT_TYPE_CHOICES |
331 | 315 | ] | 315 | ] |
332 | 316 | data["storage_layout_issues"] = obj.storage_layout_issues() | ||
333 | 316 | 317 | ||
334 | 317 | # Events | 318 | # Events |
335 | 318 | data["events"] = self.dehydrate_events(obj) | 319 | data["events"] = self.dehydrate_events(obj) |
336 | 319 | 320 | ||
337 | === modified file 'src/maasserver/websockets/handlers/tests/test_node.py' | |||
338 | --- src/maasserver/websockets/handlers/tests/test_node.py 2015-11-12 23:20:54 +0000 | |||
339 | +++ src/maasserver/websockets/handlers/tests/test_node.py 2015-11-17 23:04:09 +0000 | |||
340 | @@ -165,6 +165,7 @@ | |||
341 | 165 | "disable_ipv4": node.disable_ipv4, | 165 | "disable_ipv4": node.disable_ipv4, |
342 | 166 | "physical_disk_count": node.physicalblockdevice_set.count(), | 166 | "physical_disk_count": node.physicalblockdevice_set.count(), |
343 | 167 | "disks": disks, | 167 | "disks": disks, |
344 | 168 | "storage_layout_issues": node.storage_layout_issues(), | ||
345 | 168 | "supported_filesystems": [ | 169 | "supported_filesystems": [ |
346 | 169 | {'key': key, 'ui': ui} | 170 | {'key': key, 'ui': ui} |
347 | 170 | for key, ui in FILESYSTEM_FORMAT_TYPE_CHOICES], | 171 | for key, ui in FILESYSTEM_FORMAT_TYPE_CHOICES], |
348 | @@ -1471,20 +1472,23 @@ | |||
349 | 1471 | interface=True, | 1472 | interface=True, |
350 | 1472 | architecture=architecture, | 1473 | architecture=architecture, |
351 | 1473 | status=NODE_STATUS.ALLOCATED) | 1474 | status=NODE_STATUS.ALLOCATED) |
353 | 1474 | partition_table = factory.make_PartitionTable(node=node) | 1475 | block_device = factory.make_BlockDevice(node=node) |
354 | 1476 | partition_table = factory.make_PartitionTable( | ||
355 | 1477 | block_device=block_device, node=node) | ||
356 | 1475 | size = partition_table.block_device.size / 2 | 1478 | size = partition_table.block_device.size / 2 |
357 | 1476 | handler.create_partition({ | 1479 | handler.create_partition({ |
358 | 1477 | 'system_id': node.system_id, | 1480 | 'system_id': node.system_id, |
359 | 1478 | 'block_id': partition_table.block_device_id, | 1481 | 'block_id': partition_table.block_device_id, |
360 | 1479 | 'partition_size': size | 1482 | 'partition_size': size |
361 | 1480 | }) | 1483 | }) |
362 | 1484 | partition = partition_table.partitions.first() | ||
363 | 1481 | self.assertEquals( | 1485 | self.assertEquals( |
365 | 1482 | 1, Partition.objects.count()) | 1486 | 2, Partition.objects.count()) |
366 | 1483 | self.assertEquals( | 1487 | self.assertEquals( |
367 | 1484 | human_readable_bytes( | 1488 | human_readable_bytes( |
368 | 1485 | round_size_to_nearest_block( | 1489 | round_size_to_nearest_block( |
369 | 1486 | size, PARTITION_ALIGNMENT_SIZE, False)), | 1490 | size, PARTITION_ALIGNMENT_SIZE, False)), |
371 | 1487 | human_readable_bytes(Partition.objects.first().size)) | 1491 | human_readable_bytes(partition.size)) |
372 | 1488 | 1492 | ||
373 | 1489 | def test_create_partition_with_filesystem(self): | 1493 | def test_create_partition_with_filesystem(self): |
374 | 1490 | user = factory.make_admin() | 1494 | user = factory.make_admin() |
375 | @@ -1494,7 +1498,10 @@ | |||
376 | 1494 | interface=True, | 1498 | interface=True, |
377 | 1495 | architecture=architecture, | 1499 | architecture=architecture, |
378 | 1496 | status=NODE_STATUS.ALLOCATED) | 1500 | status=NODE_STATUS.ALLOCATED) |
380 | 1497 | partition_table = factory.make_PartitionTable(node=node) | 1501 | block_device = factory.make_BlockDevice(node=node) |
381 | 1502 | partition_table = factory.make_PartitionTable( | ||
382 | 1503 | block_device=block_device, node=node) | ||
383 | 1504 | partition = partition_table.partitions.first() | ||
384 | 1498 | size = partition_table.block_device.size / 2 | 1505 | size = partition_table.block_device.size / 2 |
385 | 1499 | fstype = factory.pick_choice(FILESYSTEM_FORMAT_TYPE_CHOICES) | 1506 | fstype = factory.pick_choice(FILESYSTEM_FORMAT_TYPE_CHOICES) |
386 | 1500 | mount_point = factory.make_absolute_path() | 1507 | mount_point = factory.make_absolute_path() |
387 | @@ -1505,19 +1512,20 @@ | |||
388 | 1505 | 'fstype': fstype, | 1512 | 'fstype': fstype, |
389 | 1506 | 'mount_point': mount_point, | 1513 | 'mount_point': mount_point, |
390 | 1507 | }) | 1514 | }) |
391 | 1515 | partition = partition_table.partitions.first() | ||
392 | 1508 | self.assertEquals( | 1516 | self.assertEquals( |
394 | 1509 | 1, Partition.objects.count()) | 1517 | 2, Partition.objects.count()) |
395 | 1510 | self.assertEquals( | 1518 | self.assertEquals( |
396 | 1511 | human_readable_bytes( | 1519 | human_readable_bytes( |
397 | 1512 | round_size_to_nearest_block( | 1520 | round_size_to_nearest_block( |
398 | 1513 | size, PARTITION_ALIGNMENT_SIZE, False)), | 1521 | size, PARTITION_ALIGNMENT_SIZE, False)), |
400 | 1514 | human_readable_bytes(Partition.objects.first().size)) | 1522 | human_readable_bytes(partition.size)) |
401 | 1515 | self.assertEquals( | 1523 | self.assertEquals( |
402 | 1516 | fstype, | 1524 | fstype, |
404 | 1517 | Partition.objects.first().get_effective_filesystem().fstype) | 1525 | partition.get_effective_filesystem().fstype) |
405 | 1518 | self.assertEquals( | 1526 | self.assertEquals( |
406 | 1519 | mount_point, | 1527 | mount_point, |
408 | 1520 | Partition.objects.first().get_effective_filesystem().mount_point) | 1528 | partition.get_effective_filesystem().mount_point) |
409 | 1521 | 1529 | ||
410 | 1522 | def test_create_cache_set_for_partition(self): | 1530 | def test_create_cache_set_for_partition(self): |
411 | 1523 | user = factory.make_admin() | 1531 | user = factory.make_admin() |
Haven't reviewed the code but please add one more check.
Make sure that a /boot partition exists not on a bcache device if the / partition is on a bcache devices.