Merge lp:~blake-rouse/maas/fix-1604465-2.0 into lp:maas/2.0

Proposed by Blake Rouse
Status: Merged
Approved by: Blake Rouse
Approved revision: no longer in the source branch.
Merged at revision: 5170
Proposed branch: lp:~blake-rouse/maas/fix-1604465-2.0
Merge into: lp:maas/2.0
Diff against target: 322 lines (+95/-35)
11 files modified
src/maasserver/api/boot_resources.py (+2/-0)
src/maasserver/bootresources.py (+8/-1)
src/maasserver/migrations/builtin/maasserver/0067_add_size_to_largefile.py (+36/-0)
src/maasserver/models/bootresource.py (+9/-2)
src/maasserver/models/bootresourceset.py (+23/-13)
src/maasserver/models/largefile.py (+4/-10)
src/maasserver/models/tests/test_bootresourceset.py (+2/-0)
src/maasserver/models/tests/test_largefile.py (+2/-7)
src/maasserver/static/js/angular/directives/controller_image_status.js (+4/-1)
src/maasserver/testing/factory.py (+2/-1)
src/maasserver/tests/test_bootresources.py (+3/-0)
To merge this branch: bzr merge lp:~blake-rouse/maas/fix-1604465-2.0
Reviewer Review Type Date Requested Status
Blake Rouse (community) Approve
Review via email: mp+300793@code.launchpad.net

Commit message

Add size to the largefile model. Add migration that fills in the size on all largefiles that already exist in MAAS. Update the current size field when images are imported. Use aggragated queries in boot resource code to speep up the database queries. Adjust image status interval check to every 30 seconds from 10 seconds.

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
1=== modified file 'src/maasserver/api/boot_resources.py'
2--- src/maasserver/api/boot_resources.py 2016-04-12 17:36:35 +0000
3+++ src/maasserver/api/boot_resources.py 2016-07-21 16:44:22 +0000
4@@ -300,6 +300,8 @@
5 "Too much data recieved.")
6
7 stream.write(data)
8+ rfile.largefile.size = current_size + size
9+ rfile.largefile.save()
10
11 if rfile.largefile.complete:
12 if not rfile.largefile.valid:
13
14=== modified file 'src/maasserver/bootresources.py'
15--- src/maasserver/bootresources.py 2016-07-07 21:57:57 +0000
16+++ src/maasserver/bootresources.py 2016-07-21 16:44:22 +0000
17@@ -669,6 +669,10 @@
18 {'sha256': rfile.largefile.sha256})
19 maaslog.debug("Finalizing boot image %s.", ident)
20
21+ # Ensure that the size of the largefile starts at zero.
22+ rfile.largefile.size = 0
23+ rfile.largefile.save(update_fields=['size'])
24+
25 # Write the contents into the database, while calculating the sha256
26 # hash for the read data.
27 with rfile.largefile.content.open('wb') as stream:
28@@ -676,7 +680,10 @@
29 buf = reader.read(self.read_size)
30 stream.write(buf)
31 cksummer.update(buf)
32- if len(buf) != self.read_size:
33+ buf_len = len(buf)
34+ rfile.largefile.size += buf_len
35+ rfile.largefile.save(update_fields=['size'])
36+ if buf_len != self.read_size:
37 break
38
39 if not cksummer.check():
40
41=== added file 'src/maasserver/migrations/builtin/maasserver/0067_add_size_to_largefile.py'
42--- src/maasserver/migrations/builtin/maasserver/0067_add_size_to_largefile.py 1970-01-01 00:00:00 +0000
43+++ src/maasserver/migrations/builtin/maasserver/0067_add_size_to_largefile.py 2016-07-21 16:44:22 +0000
44@@ -0,0 +1,36 @@
45+# -*- coding: utf-8 -*-
46+from __future__ import unicode_literals
47+
48+from django.db import (
49+ migrations,
50+ models,
51+)
52+
53+
54+def get_size_of_content(large_file):
55+ with self.content.open('rb') as stream:
56+ stream.seek(0, os.SEEK_END)
57+ return stream.tell()
58+
59+
60+def set_size_on_all_largefiles(apps, schema_editor):
61+ LargeFile = apps.get_model("maasserver", "LargeFile")
62+ for large_file in LargeFile.objects.all():
63+ large_file.size = get_size_of_content(large_file)
64+ large_file.save()
65+
66+
67+class Migration(migrations.Migration):
68+
69+ dependencies = [
70+ ('maasserver', '0066_allow_squashfs'),
71+ ]
72+
73+ operations = [
74+ migrations.AddField(
75+ model_name='largefile',
76+ name='size',
77+ field=models.BigIntegerField(default=0, editable=False),
78+ ),
79+ migrations.RunPython(set_size_on_all_largefiles),
80+ ]
81
82=== modified file 'src/maasserver/models/bootresource.py'
83--- src/maasserver/models/bootresource.py 2016-04-12 17:36:35 +0000
84+++ src/maasserver/models/bootresource.py 2016-07-21 16:44:22 +0000
85@@ -10,8 +10,10 @@
86 from django.core.exceptions import ValidationError
87 from django.db.models import (
88 CharField,
89+ Count,
90 IntegerField,
91 Manager,
92+ Sum,
93 )
94 from maasserver import DefaultMeta
95 from maasserver.enum import (
96@@ -381,8 +383,13 @@
97 def get_latest_complete_set(self):
98 """Return latest `BootResourceSet` where all `BootResouceFile`'s
99 are complete."""
100- for resource_set in self.sets.order_by('id').reverse():
101- if resource_set.complete:
102+ resource_sets = self.sets.order_by('id').annotate(
103+ files_count=Count('files__id'),
104+ files_size=Sum('files__largefile__size'),
105+ files_total_size=Sum('files__largefile__total_size'))
106+ for resource_set in resource_sets.reverse():
107+ if (resource_set.files_count > 0 and
108+ resource_set.files_size == resource_set.files_total_size):
109 return resource_set
110 return None
111
112
113=== modified file 'src/maasserver/models/bootresourceset.py'
114--- src/maasserver/models/bootresourceset.py 2016-07-07 21:57:57 +0000
115+++ src/maasserver/models/bootresourceset.py 2016-07-21 16:44:22 +0000
116@@ -10,6 +10,7 @@
117 from django.db.models import (
118 CharField,
119 ForeignKey,
120+ Sum,
121 )
122 from maasserver import DefaultMeta
123 from maasserver.enum import BOOT_RESOURCE_FILE_TYPE
124@@ -89,32 +90,41 @@
125 @property
126 def total_size(self):
127 """Total amount of space this set will consume."""
128- return sum(
129- resource_file.largefile.total_size
130- for resource_file in self.files.all())
131+ total_size = self.files.all().aggregate(
132+ total_size=Sum('largefile__total_size'))['total_size']
133+ if total_size is None:
134+ total_size = 0
135+ return total_size
136
137 @property
138 def size(self):
139 """Amount of space this set currently consumes."""
140- return sum(
141- resource_file.largefile.size
142- for resource_file in self.files.all())
143+ size = self.files.all().aggregate(size=Sum('largefile__size'))['size']
144+ if size is None:
145+ size = 0
146+ return size
147
148 @property
149 def progress(self):
150 """Percentage complete for all files in the set."""
151- size = self.size
152- if size <= 0:
153+ size_info = self.files.all().aggregate(
154+ total_size=Sum('largefile__total_size'),
155+ size=Sum('largefile__size'))
156+ if size_info['size'] is None:
157+ size_info['size'] = 0
158+ if size_info['total_size'] is None:
159+ size_info['total_size'] = 0
160+ if size_info['size'] <= 0:
161 # Handle division by zero
162 return 0
163- return 100.0 * size / float(self.total_size)
164+ return 100.0 * size_info['size'] / float(size_info['total_size'])
165
166 @property
167 def complete(self):
168 """True if all files in the set are complete."""
169 if not self.files.exists():
170 return False
171- for resource_file in self.files.all():
172- if not resource_file.largefile.complete:
173- return False
174- return True
175+ size_info = self.files.all().aggregate(
176+ total_size=Sum('largefile__total_size'),
177+ size=Sum('largefile__size'))
178+ return size_info['total_size'] == size_info['size']
179
180=== modified file 'src/maasserver/models/largefile.py'
181--- src/maasserver/models/largefile.py 2016-05-12 19:07:37 +0000
182+++ src/maasserver/models/largefile.py 2016-07-21 16:44:22 +0000
183@@ -8,7 +8,6 @@
184 ]
185
186 import hashlib
187-import os
188
189 from django.db.models import (
190 BigIntegerField,
191@@ -72,7 +71,7 @@
192 objstream.write(data)
193 length += len(data)
194 return self.create(
195- sha256=hexdigest, total_size=length, content=objfile)
196+ sha256=hexdigest, size=length, total_size=length, content=objfile)
197
198
199 class LargeFile(CleanSave, TimestampedModel):
200@@ -85,6 +84,7 @@
201 process by only saving unique files.
202
203 :ivar sha256: Calculated SHA256 value of `content`.
204+ :ivar size: Current size of `content`.
205 :ivar total_size: Final size of `content`. The data might currently
206 be saving, so total_size could be larger than `size`. `size` should
207 never be larger than `total_size`.
208@@ -98,6 +98,8 @@
209
210 sha256 = CharField(max_length=64, unique=True, editable=False)
211
212+ size = BigIntegerField(default=0, editable=False)
213+
214 total_size = BigIntegerField(editable=False)
215
216 # content is stored directly in the database, in the large object storage.
217@@ -108,14 +110,6 @@
218 return "<LargeFile size=%d sha256=%s>" % (self.total_size, self.sha256)
219
220 @property
221- def size(self):
222- """Size of content."""
223- with self.content.open('rb') as stream:
224- stream.seek(0, os.SEEK_END)
225- size = stream.tell()
226- return size
227-
228- @property
229 def progress(self):
230 """Percentage of `content` saved."""
231 if self.size <= 0:
232
233=== modified file 'src/maasserver/models/tests/test_bootresourceset.py'
234--- src/maasserver/models/tests/test_bootresourceset.py 2016-07-07 21:57:57 +0000
235+++ src/maasserver/models/tests/test_bootresourceset.py 2016-07-21 16:44:22 +0000
236@@ -135,6 +135,8 @@
237 self.assertEqual(0, resource_set.progress)
238 for _ in range(total_size):
239 stream.write(b"a")
240+ largefile.size += 1
241+ largefile.save()
242 current_size += 1
243 self.assertAlmostEqual(
244 100.0 * current_size / float(total_size),
245
246=== modified file 'src/maasserver/models/tests/test_largefile.py'
247--- src/maasserver/models/tests/test_largefile.py 2016-05-12 19:07:37 +0000
248+++ src/maasserver/models/tests/test_largefile.py 2016-07-21 16:44:22 +0000
249@@ -68,6 +68,8 @@
250 written_content = stream.read()
251 self.assertEqual(
252 content, written_content)
253+ self.assertEqual(
254+ len(content), largefile.size)
255
256
257 class TestLargeFile(MAASServerTestCase):
258@@ -88,13 +90,6 @@
259 data = stream.read()
260 self.assertEqual(content, data)
261
262- def test_size(self):
263- size = randint(512, 1024)
264- total_size = randint(1025, 2048)
265- content = factory.make_bytes(size=size)
266- largefile = factory.make_LargeFile(content, size=total_size)
267- self.assertEqual(size, largefile.size)
268-
269 def test_progress(self):
270 size = randint(512, 1024)
271 total_size = randint(1025, 2048)
272
273=== modified file 'src/maasserver/static/js/angular/directives/controller_image_status.js'
274--- src/maasserver/static/js/angular/directives/controller_image_status.js 2016-05-25 20:27:50 +0000
275+++ src/maasserver/static/js/angular/directives/controller_image_status.js 2016-07-21 16:44:22 +0000
276@@ -11,6 +11,9 @@
277 $timeout, $interval, ControllersManager) {
278 var self = this;
279
280+ // How often to check the sync status of a controller in seconds.
281+ var CHECK_INTERVAL = 30;
282+
283 // List of controllers that need to have the image status updated.
284 this.controllers = [];
285
286@@ -67,7 +70,7 @@
287 self.startTimeout = undefined;
288 self.runningInterval = $interval(function() {
289 self.updateStatuses();
290- }, 10 * 1000);
291+ }, CHECK_INTERVAL * 1000);
292 self.updateStatuses();
293 }, 100);
294 };
295
296=== modified file 'src/maasserver/testing/factory.py'
297--- src/maasserver/testing/factory.py 2016-07-07 21:57:57 +0000
298+++ src/maasserver/testing/factory.py 2016-07-21 16:44:22 +0000
299@@ -1320,7 +1320,8 @@
300 with largeobject.open('wb') as stream:
301 stream.write(content)
302 return LargeFile.objects.create(
303- sha256=sha256, total_size=size, content=largeobject)
304+ sha256=sha256, size=len(content),
305+ total_size=size, content=largeobject)
306
307 def make_BootResource(self, rtype=None, name=None, architecture=None,
308 extra=None, kflavor=None):
309
310=== modified file 'src/maasserver/tests/test_bootresources.py'
311--- src/maasserver/tests/test_bootresources.py 2016-07-07 21:57:57 +0000
312+++ src/maasserver/tests/test_bootresources.py 2016-07-21 16:44:22 +0000
313@@ -810,6 +810,9 @@
314 with rfile.largefile.content.open('rb') as stream:
315 written_data = stream.read()
316 self.assertEqual(content, written_data)
317+ rfile.largefile = reload_object(rfile.largefile)
318+ self.assertEqual(rfile.largefile.size, len(written_data))
319+ self.assertEqual(rfile.largefile.size, rfile.largefile.total_size)
320
321 @skip(
322 "XXX blake_r: Skipped because it causes the test that runs after this "

Subscribers

People subscribed via source and target branches

to all changes: