]>
Commit | Line | Data |
---|---|---|
c62d93f1 MT |
1 | #!/usr/bin/python |
2 | ||
3 | from __future__ import division | |
4 | ||
5 | import os | |
6 | import socket | |
7 | import urlparse | |
8 | import xmlrpclib | |
9 | ||
10 | import pakfire.util | |
11 | import pakfire.packages as packages | |
12 | from pakfire.system import system | |
13 | ||
14 | # Local modules. | |
15 | import transport | |
16 | ||
17 | from pakfire.constants import * | |
38ba21f9 | 18 | from pakfire.i18n import _ |
c62d93f1 MT |
19 | |
20 | import logging | |
21 | log = logging.getLogger("pakfire.client") | |
22 | ||
23 | class PakfireClient(object): | |
24 | type = None | |
25 | ||
26 | def __init__(self, server, username, password): | |
27 | self.url = self._join_url(server, username, password) | |
28 | ||
29 | # Create a secure XMLRPC connection to the server. | |
30 | self.conn = transport.Connection(self.url) | |
31 | ||
32 | def _join_url(self, server, username, password): | |
33 | """ | |
34 | Construct a right URL out of the given | |
35 | server, username and password. | |
36 | ||
37 | Basicly this just adds the credentials | |
38 | to the URL. | |
39 | """ | |
40 | assert self.type | |
41 | ||
42 | # Parse the given URL. | |
43 | url = urlparse.urlparse(server) | |
44 | assert url.scheme in ("http", "https") | |
45 | ||
46 | # Build new URL. | |
47 | ret = "%s://" % url.scheme | |
48 | ||
49 | # Add credentials if provided. | |
50 | if username and password: | |
51 | ret += "%s:%s@" % (username, password) | |
52 | ||
53 | # Add host and path components. | |
0f882359 | 54 | ret += "/".join((url.netloc, self.type)) |
c62d93f1 MT |
55 | |
56 | return ret | |
57 | ||
58 | ### Misc. actions | |
59 | ||
60 | def noop(self): | |
61 | """ | |
62 | No operation. Just to check if the connection is | |
63 | working. Returns a random number. | |
64 | """ | |
65 | return self.conn.noop() | |
66 | ||
37ff3f8f MT |
67 | def test_code(self, error_code): |
68 | assert error_code >= 100 and error_code <= 999 | |
69 | ||
70 | return self.conn.test_code(error_code) | |
71 | ||
c62d93f1 MT |
72 | def get_my_address(self): |
73 | """ | |
74 | Get my own address (as seen by the hub). | |
75 | """ | |
76 | return self.conn.get_my_address() | |
77 | ||
78 | def get_hub_status(self): | |
79 | """ | |
80 | Get some status information about the hub. | |
81 | """ | |
82 | return self.conn.get_hub_status() | |
83 | ||
84 | ||
85 | class BuildMixin(object): | |
86 | ### Build actions | |
87 | ||
88 | def build_create(self, filename, arches=None, distro=None): | |
89 | """ | |
90 | Create a new build on the hub. | |
91 | """ | |
92 | ||
93 | # Upload the source file to the server. | |
94 | upload_id = self._upload_file(filename) | |
95 | ||
96 | # Then create the build. | |
97 | build = self.conn.build_create(upload_id, distro, arches) | |
98 | ||
99 | print build | |
100 | ||
101 | def _upload_file(self, filename): | |
102 | # Get the hash of the file. | |
103 | hash = pakfire.util.calc_hash1(filename) | |
104 | ||
105 | # Get the size of the file. | |
106 | size = os.path.getsize(filename) | |
107 | ||
108 | # Get an upload ID from the server. | |
109 | upload_id = self.conn.upload_create(os.path.basename(filename), | |
110 | size, hash) | |
111 | ||
38ba21f9 MT |
112 | # Make a nice progressbar. |
113 | pb = pakfire.util.make_progress(os.path.basename(filename), size, speed=True, eta=True) | |
114 | ||
c62d93f1 MT |
115 | try: |
116 | # Calculate the number of chunks. | |
117 | chunks = (size // CHUNK_SIZE) + 1 | |
38ba21f9 | 118 | transferred = 0 |
c62d93f1 MT |
119 | |
120 | # Cut the file in pieces and upload them one after another. | |
121 | with open(filename) as f: | |
122 | chunk = 0 | |
123 | while True: | |
124 | data = f.read(CHUNK_SIZE) | |
125 | if not data: | |
126 | break | |
127 | ||
128 | chunk += 1 | |
38ba21f9 MT |
129 | if pb: |
130 | transferred += len(data) | |
131 | pb.update(transferred) | |
c62d93f1 MT |
132 | |
133 | data = xmlrpclib.Binary(data) | |
134 | self.conn.upload_chunk(upload_id, data) | |
135 | ||
136 | # Tell the server, that we finished the upload. | |
137 | ret = self.conn.upload_finished(upload_id) | |
138 | ||
139 | except: | |
140 | # If anything goes wrong, try to delete the upload and raise | |
141 | # the exception. | |
142 | self.conn.upload_remove(upload_id) | |
143 | ||
144 | raise | |
145 | ||
38ba21f9 MT |
146 | finally: |
147 | if pb: | |
148 | pb.finish() | |
149 | ||
c62d93f1 MT |
150 | # If the server sends false, something happened with the upload that |
151 | # could not be recovered. | |
38ba21f9 | 152 | if not ret: |
c62d93f1 MT |
153 | logging.error("Upload of %s was not successful." % filename) |
154 | raise Exception, "Upload failed." | |
155 | ||
156 | return upload_id | |
157 | ||
158 | ||
159 | class PakfireUserClient(BuildMixin, PakfireClient): | |
160 | type = "user" | |
161 | ||
162 | def check_auth(self): | |
163 | """ | |
164 | Check if the user was successfully authenticated. | |
165 | """ | |
166 | return self.conn.check_auth() | |
167 | ||
168 | def get_user_profile(self): | |
169 | """ | |
170 | Get information about the user profile. | |
171 | """ | |
172 | return self.conn.get_user_profile() | |
173 | ||
25a98632 MT |
174 | def get_builds(self, type=None, limit=10, offset=0): |
175 | return self.conn.get_builds(type=type, limit=limit, offset=offset) | |
176 | ||
177 | def get_build(self, build_id): | |
178 | return self.conn.get_build(build_id) | |
179 | ||
180 | def get_builder(self, builder_id): | |
181 | return self.conn.get_builder(builder_id) | |
182 | ||
183 | def get_job(self, job_id): | |
184 | return self.conn.get_job(job_id) | |
185 | ||
186 | def get_latest_jobs(self): | |
187 | return self.conn.get_latest_jobs() | |
188 | ||
189 | def get_active_jobs(self): | |
190 | return self.conn.get_active_jobs() | |
191 | ||
c62d93f1 MT |
192 | |
193 | class PakfireBuilderClient(BuildMixin, PakfireClient): | |
194 | type = "builder" | |
195 | ||
96ccd5d5 | 196 | def send_keepalive(self, force=False, overload=None, free_space=None): |
c62d93f1 MT |
197 | """ |
198 | Sends a little keepalive to the server and | |
199 | updates the hardware information if the server | |
200 | requests it. | |
201 | """ | |
202 | log.debug("Sending keepalive to the hub.") | |
203 | ||
204 | # Collect the current loadavg and send it to the hub. | |
205 | loadavg = ", ".join(("%.2f" % round(l, 2) for l in os.getloadavg())) | |
206 | ||
e3eb9333 MT |
207 | try: |
208 | needs_update = self.conn.send_keepalive(loadavg, overload, free_space) | |
209 | ||
210 | except XMLRPCInternalServerError: | |
211 | # If the keepalive message could not successfully be sent, we don't | |
212 | # bother, because the client will soon retry. | |
213 | log.warning(_("Could not send a keepalive message to the hub.")) | |
214 | ||
215 | return | |
c62d93f1 | 216 | |
96ccd5d5 | 217 | if force or needs_update: |
c62d93f1 MT |
218 | log.debug("The hub is requesting an update.") |
219 | self.send_update() | |
220 | ||
221 | def send_update(self): | |
222 | log.info("Sending host information update to hub...") | |
223 | ||
2dbaea05 MT |
224 | config = pakfire.config.ConfigDaemon() |
225 | ||
e3eb9333 MT |
226 | try: |
227 | self.conn.send_update( | |
228 | # Supported architectures. | |
229 | system.supported_arches, | |
230 | ||
231 | # CPU information. | |
232 | system.cpu_model, | |
233 | system.cpu_count, | |
234 | ||
235 | # Amount of memory in bytes. | |
236 | system.memory / 1024, | |
c62d93f1 | 237 | |
e3eb9333 MT |
238 | # Send the currently running version of Pakfire. |
239 | PAKFIRE_VERSION, | |
c62d93f1 | 240 | |
e3eb9333 MT |
241 | # Send the host key. |
242 | config.get("signatures", "host_key", None), | |
243 | ) | |
fc69babd | 244 | |
e3eb9333 MT |
245 | except XMLRPCInternalServerError: |
246 | # Don't give a shit either. | |
247 | log.warning(_("Could not update the host information.")) | |
2dbaea05 | 248 | |
e3eb9333 | 249 | return |