]> git.ipfire.org Git - people/jschlag/pbs.git/blame - src/buildservice/uploads.py
Refactor uploads
[people/jschlag/pbs.git] / src / buildservice / uploads.py
CommitLineData
9137135a
MT
1#!/usr/bin/python
2
f6e6ff79
MT
3from __future__ import division
4
5import datetime
9137135a
MT
6import hashlib
7import logging
8import os
f6e6ff79 9import shutil
9137135a
MT
10import uuid
11
f6e6ff79
MT
12import pakfire.packages
13
2c909128
MT
14from . import base
15from . import misc
16from . import packages
9137135a 17
2c909128 18from .constants import *
2f64fe68 19from .decorators import *
9137135a
MT
20
21class Uploads(base.Object):
2f64fe68
MT
22 def _get_upload(self, query, *args):
23 res = self.db.get(query, *args)
9137135a 24
2f64fe68
MT
25 if res:
26 return Upload(self.backend, res.id, data=res)
9137135a 27
2f64fe68
MT
28 def _get_uploads(self, query, *args):
29 res = self.db.query(query, *args)
9137135a 30
2f64fe68
MT
31 for row in res:
32 yield Upload(self.backend, row.id, data=row)
9137135a 33
2f64fe68
MT
34 def __iter__(self):
35 uploads = self._get_uploads("SELECT * FROM uploads ORDER BY time_started DESC")
9137135a 36
2f64fe68 37 return iter(sorted(uploads))
9137135a 38
2f64fe68
MT
39 def get_by_uuid(self, _uuid):
40 return self._get_upload("SELECT * FROM uploads WHERE uuid = %s", uuid)
9137135a 41
2f64fe68 42 def create(filename, size, hash, builder=None, user=None):
f6e6ff79 43 assert builder or user
9137135a 44
2f64fe68
MT
45 upload = self._get_upload("INSERT INTO uploads(uuid, filename, size, hash) \
46 VALUES(%s, %s, %s, %s) RETURNING *", "%s" % uuid.uuid4(), filename, size, hash)
f6e6ff79
MT
47
48 if builder:
2f64fe68 49 upload.builder = builder
f6e6ff79
MT
50
51 elif user:
2f64fe68 52 upload.user = user
9137135a
MT
53
54 # Create space to where we save the data.
55 dirname = os.path.dirname(upload.path)
56 if not os.path.exists(dirname):
57 os.makedirs(dirname)
58
59 # Create empty file.
60 f = open(upload.path, "w")
61 f.close()
62
63 return upload
64
2f64fe68
MT
65 def cleanup(self):
66 for upload in self.get_all():
67 upload.cleanup()
68
69
70class Upload(base.DataObject):
71 table = "uploads"
72
9137135a
MT
73 @property
74 def uuid(self):
75 return self.data.uuid
76
77 @property
78 def hash(self):
79 return self.data.hash
80
81 @property
82 def filename(self):
83 return self.data.filename
84
85 @property
86 def path(self):
87 return os.path.join(UPLOADS_DIR, self.uuid, self.filename)
88
f6e6ff79
MT
89 @property
90 def size(self):
91 return self.data.size
92
93 @property
94 def progress(self):
95 return self.data.progress / self.size
96
2f64fe68
MT
97 # Builder
98
99 def get_builder(self):
f6e6ff79 100 if self.data.builder_id:
2f64fe68 101 return self.backend.builders.get_by_id(self.data.builder_id)
f6e6ff79 102
2f64fe68
MT
103 def set_builder(self, builder):
104 self._set_attribute("builder", builder.id)
105 self.builder = builder
106
107 builder = lazy_property(get_builder, set_builder)
108
109 # User
110
111 def get_user(self):
f6e6ff79 112 if self.data.user_id:
2f64fe68
MT
113 return self.backend.users.get_by_id(self.data.user_id)
114
115 def set_user(self, user):
116 self._set_attribute("user", user.id)
117 self.user = user
118
119 user = lazy_property(get_user, set_user)
9137135a
MT
120
121 def append(self, data):
f6e6ff79
MT
122 # Check if the filesize was exceeded.
123 size = os.path.getsize(self.path) + len(data)
124 if size > self.data.size:
125 raise Exception, "Given filesize was exceeded for upload %s" % self.uuid
126
9137135a
MT
127 logging.debug("Writing %s bytes to %s" % (len(data), self.path))
128
2f64fe68
MT
129 with open(self.path, "ab") as f:
130 f.write(data)
9137135a 131
2f64fe68 132 self._set_attribute("progress", size)
f6e6ff79 133
9137135a 134 def validate(self):
f6e6ff79
MT
135 size = os.path.getsize(self.path)
136 if not size == self.data.size:
137 logging.error("Filesize is not okay: %s" % (self.uuid))
138 return False
139
9137135a
MT
140 # Calculate a hash to validate the upload.
141 hash = misc.calc_hash1(self.path)
142
f6e6ff79 143 if not self.hash == hash:
9137135a 144 logging.error("Hash did not match: %s != %s" % (self.hash, hash))
f6e6ff79
MT
145 return False
146
147 return True
9137135a 148
f6e6ff79
MT
149 def finished(self):
150 """
151 Update the status of the upload in the database to "finished".
152 """
153 # Check if the file was completely uploaded and the hash is correct.
154 # If not, the upload has failed.
155 if not self.validate():
156 return False
157
2f64fe68
MT
158 self._set_attribute("finished", True)
159 self._set_attribute("time_finished", datetime.datetime.utcnow())
f6e6ff79
MT
160
161 return True
9137135a
MT
162
163 def remove(self):
164 # Remove the uploaded data.
f6e6ff79
MT
165 path = os.path.dirname(self.path)
166 if os.path.exists(path):
167 shutil.rmtree(path, ignore_errors=True)
9137135a
MT
168
169 # Delete the upload from the database.
170 self.db.execute("DELETE FROM uploads WHERE id = %s", self.id)
171
f6e6ff79
MT
172 @property
173 def time_started(self):
174 return self.data.time_started
9137135a 175
f6e6ff79
MT
176 @property
177 def time_running(self):
9137135a
MT
178 # Get the seconds since we are running.
179 try:
f6e6ff79 180 time_running = datetime.datetime.utcnow() - self.time_started
9137135a
MT
181 time_running = time_running.total_seconds()
182 except:
183 time_running = 0
184
f6e6ff79
MT
185 return time_running
186
187 @property
188 def speed(self):
189 if not self.time_running:
190 return 0
191
192 return self.data.progress / self.time_running
193
194 def cleanup(self):
195 # Remove uploads that are older than 2 hours.
196 if self.time_running >= 3600 * 2:
9137135a 197 self.remove()