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