blob: b6b326a098528328691be9aa1f72a11f9cd109f6 [file] [log] [blame]
showard9d02fb52008-08-08 18:20:37 +00001#!/usr/bin/python
2
3"""
4This module defines the BasePackageManager Class which provides an
5implementation of the packaging system API providing methods to fetch,
6upload and remove packages. Site specific extensions to any of these methods
7should inherit this class.
8"""
9
10import re, os, sys, traceback, subprocess, shutil, time, traceback, urlparse
showarda290e982009-03-27 20:57:30 +000011import fcntl, logging
showard9d02fb52008-08-08 18:20:37 +000012from autotest_lib.client.common_lib import error, utils
13
14
15class PackageUploadError(error.AutotestError):
16 'Raised when there is an error uploading the package'
17
18class PackageFetchError(error.AutotestError):
19 'Raised when there is an error fetching the package'
20
21class PackageRemoveError(error.AutotestError):
22 'Raised when there is an error removing the package'
23
24class PackageInstallError(error.AutotestError):
25 'Raised when there is an error installing the package'
26
27# the name of the checksum file that stores the packages' checksums
28CHECKSUM_FILE = "packages.checksum"
29
30class BasePackageManager(object):
31 _repo_exception = {}
32 REPO_OK = object()
33
mbligh76d19f72008-10-15 16:24:43 +000034 def __init__(self, pkgmgr_dir, hostname=None, repo_urls=None,
35 upload_paths=None, do_locking=True, run_function=utils.run,
36 run_function_args=[], run_function_dargs={}):
showard9d02fb52008-08-08 18:20:37 +000037 '''
38 repo_urls: The list of the repository urls which is consulted
39 whilst fetching the package
40 upload_paths: The list of the upload of repositories to which
41 the package is uploaded to
42 pkgmgr_dir : A directory that can be used by the package manager
43 to dump stuff (like checksum files of the repositories
44 etc.).
45 do_locking : Enable locking when the packages are installed.
46
47 run_function is used to execute the commands throughout this file.
48 It defaults to utils.run() but a custom method (if provided) should
49 be of the same schema as utils.run. It should return a CmdResult
50 object and throw a CmdError exception. The reason for using a separate
51 function to run the commands is that the same code can be run to fetch
52 a package on the local machine or on a remote machine (in which case
53 ssh_host's run function is passed in for run_function).
54 '''
55 # In memory dictionary that stores the checksum's of packages
56 self._checksum_dict = {}
57
58 self.pkgmgr_dir = pkgmgr_dir
59 self.do_locking = do_locking
mbligh76d19f72008-10-15 16:24:43 +000060 self.hostname = hostname
showard9d02fb52008-08-08 18:20:37 +000061
62 # Process the repository URLs and the upload paths if specified
63 if not repo_urls:
64 self.repo_urls = []
65 else:
mbligh76d19f72008-10-15 16:24:43 +000066 if hostname:
67 self.repo_urls = repo_urls
68 self.repo_urls = list(self.get_mirror_list())
69 else:
70 self.repo_urls = list(repo_urls)
showard9d02fb52008-08-08 18:20:37 +000071 if not upload_paths:
72 self.upload_paths = []
73 else:
74 self.upload_paths = list(upload_paths)
75
76 # Create an internal function that is a simple wrapper of
77 # run_function and takes in the args and dargs as arguments
78 def _run_command(command, _run_command_args=run_function_args,
79 _run_command_dargs={}):
80 '''
81 Special internal function that takes in a command as
82 argument and passes it on to run_function (if specified).
83 The _run_command_dargs are merged into run_function_dargs
84 with the former having more precedence than the latter.
85 '''
86 new_dargs = dict(run_function_dargs)
87 new_dargs.update(_run_command_dargs)
88
89 return run_function(command, *_run_command_args,
90 **new_dargs)
91
92 self._run_command = _run_command
93
94
95 def install_pkg(self, name, pkg_type, fetch_dir, install_dir,
96 preserve_install_dir=False, repo_url=None):
97 '''
98 Remove install_dir if it already exists and then recreate it unless
99 preserve_install_dir is specified as True.
100 Fetch the package into the pkg_dir. Untar the package into install_dir
101 The assumption is that packages are of the form :
102 <pkg_type>.<pkg_name>.tar.bz2
103 name : name of the package
104 type : type of the package
105 fetch_dir : The directory into which the package tarball will be
106 fetched to.
107 install_dir : the directory where the package files will be untarred to
108 repo_url : the url of the repository to fetch the package from.
109 '''
110
111 # do_locking flag is on by default unless you disable it (typically
112 # in the cases where packages are directly installed from the server
113 # onto the client in which case fcntl stuff wont work as the code
114 # will run on the server in that case..
115 if self.do_locking:
116 lockfile_name = '.%s-%s-lock' % (name, pkg_type)
117 lockfile = open(os.path.join(self.pkgmgr_dir, lockfile_name), 'w')
118
119 try:
120 if self.do_locking:
121 fcntl.flock(lockfile, fcntl.LOCK_EX)
122
123 self._run_command('mkdir -p %s' % fetch_dir)
124
125 pkg_name = self.get_tarball_name(name, pkg_type)
126 fetch_path = os.path.join(fetch_dir, pkg_name)
127 try:
128 # Fetch the package into fetch_dir
mbligh76d19f72008-10-15 16:24:43 +0000129 self.fetch_pkg(pkg_name, fetch_path, use_checksum=True)
showard9d02fb52008-08-08 18:20:37 +0000130
131 # check to see if the install_dir exists and if it does
132 # then check to see if the .checksum file is the latest
133 install_dir_exists = False
134 try:
135 self._run_command("ls %s" % install_dir)
136 install_dir_exists = True
137 except (error.CmdError, error.AutoservRunError):
138 pass
139
140 if (install_dir_exists and
141 not self.untar_required(fetch_path, install_dir)):
142 return
143
144 # untar the package into install_dir and
145 # update the checksum in that directory
146 if not preserve_install_dir:
147 # Make sure we clean up the install_dir
148 self._run_command('rm -rf %s' % install_dir)
149 self._run_command('mkdir -p %s' % install_dir)
150
151 self.untar_pkg(fetch_path, install_dir)
152
153 except PackageFetchError, why:
154 raise PackageInstallError('Installation of %s(type:%s) failed'
155 ' : %s' % (name, pkg_type, why))
156 finally:
157 if self.do_locking:
158 fcntl.flock(lockfile, fcntl.LOCK_UN)
159 lockfile.close()
160
161
mbligh76d19f72008-10-15 16:24:43 +0000162 def fetch_pkg(self, pkg_name, dest_path, repo_url=None, use_checksum=False):
showard9d02fb52008-08-08 18:20:37 +0000163 '''
164 Fetch the package into dest_dir from repo_url. By default repo_url
165 is None and the package is looked in all the repostories specified.
166 Otherwise it fetches it from the specific repo_url.
167 pkg_name : name of the package (ex: test-sleeptest.tar.bz2,
168 dep-gcc.tar.bz2, kernel.1-1.rpm)
169 repo_url : the URL of the repository where the package is located.
170 dest_path : complete path of where the package will be fetched to.
171 use_checksum : This is set to False to fetch the packages.checksum file
172 so that the checksum comparison is bypassed for the
173 checksum file itself. This is used internally by the
174 packaging system. It should be ignored by externals
175 callers of this method who use it fetch custom packages.
176 '''
177
178 try:
179 self._run_command("ls %s" % os.path.dirname(dest_path))
180 except (error.CmdError, error.AutoservRunError):
181 raise PackageFetchError("Please provide a valid "
182 "destination: %s " % dest_path)
183
184 # See if the package was already fetched earlier, if so
185 # the checksums need to be compared and the package is now
186 # fetched only if they differ.
187 pkg_exists = False
188 try:
189 self._run_command("ls %s" % dest_path)
190 pkg_exists = True
191 except (error.CmdError, error.AutoservRunError):
192 pass
193
194 # if a repository location is explicitly provided, fetch the package
195 # from there and return
196 if repo_url:
197 repo_url_list = [repo_url]
198 elif len(self.repo_urls) > 0:
199 repo_url_list = self.repo_urls
200 else:
201 raise PackageFetchError("There are no repository urls specified")
202
203 error_msgs = {}
204 for location in repo_url_list:
205 try:
206 # Fetch the checksum if it not there
207 if not use_checksum:
208 self.fetch_pkg_file(pkg_name, dest_path, location)
209
210 # Fetch the package if a) the pkg does not exist or
211 # b) if the checksum differs for the existing package
212 elif (not pkg_exists or
213 not self.compare_checksum(dest_path, location)):
214 self.fetch_pkg_file(pkg_name, dest_path, location)
215 # Update the checksum of the package in the packages'
216 # checksum file
217 self.update_checksum(dest_path)
218 return
mbligh7a603672009-02-07 01:52:08 +0000219 except (PackageFetchError, error.AutoservRunError):
showard9d02fb52008-08-08 18:20:37 +0000220 # The package could not be found in this repo, continue looking
showarda290e982009-03-27 20:57:30 +0000221 logging.error('%s could not be fetched from %s', pkg_name,
222 location)
showard9d02fb52008-08-08 18:20:37 +0000223
224 # if we got here then that means the package is not found
225 # in any of the repositories.
mbligh76d19f72008-10-15 16:24:43 +0000226 raise PackageFetchError("%s could not be fetched from any of"
227 " the repos %s : %s " % (pkg_name,
228 repo_url_list,
showard9d02fb52008-08-08 18:20:37 +0000229 error_msgs))
230
231
mbligh76d19f72008-10-15 16:24:43 +0000232 def fetch_pkg_file(self, filename, dest_path, source_url):
showard9d02fb52008-08-08 18:20:37 +0000233 """
234 Fetch the file from source_url into dest_path. The package repository
235 url is parsed and the appropriate retrieval method is determined.
236
237 """
238 if source_url.startswith('http://'):
mbligh76d19f72008-10-15 16:24:43 +0000239 self.fetch_file_http(filename, dest_path, source_url)
showard9d02fb52008-08-08 18:20:37 +0000240 else:
mbligh76d19f72008-10-15 16:24:43 +0000241 raise PackageFetchError("Invalid location %s" % source_url)
showard9d02fb52008-08-08 18:20:37 +0000242
243
mbligh76d19f72008-10-15 16:24:43 +0000244 def fetch_file_http(self, filename, dest_path, source_url):
showard9d02fb52008-08-08 18:20:37 +0000245 """
246 Fetch the package using http protocol. Raises a PackageFetchError.
247 """
showarda290e982009-03-27 20:57:30 +0000248 logging.info("Fetching %s from %s to %s", filename, source_url,
249 dest_path)
showard9d02fb52008-08-08 18:20:37 +0000250 # check to see if the source_url is reachable or not
251 self.run_http_test(source_url, os.path.dirname(dest_path))
252
mbligh76d19f72008-10-15 16:24:43 +0000253 pkg_path = os.path.join(source_url, filename)
showard9d02fb52008-08-08 18:20:37 +0000254 try:
mblighdc1e7aa2008-10-10 21:12:15 +0000255 self._run_command('wget -nv %s -O %s' % (pkg_path, dest_path))
showarda290e982009-03-27 20:57:30 +0000256 logging.debug("Successfully fetched %s from %s", filename,
257 source_url)
mbligh7a603672009-02-07 01:52:08 +0000258 except error.CmdError:
259 raise PackageFetchError("%s not found in %s" % (filename,
260 source_url))
showard9d02fb52008-08-08 18:20:37 +0000261
262
263 def run_http_test(self, source_url, dest_dir):
264 '''
265 Run a simple 30 sec wget on source_url
266 just to see if it can be reachable or not. This avoids the need
267 for waiting for a 10min timeout.
268 '''
269 dest_file_path = os.path.join(dest_dir, 'http_test_file')
270
271 BPM = BasePackageManager
272 error_msg = "HTTP test failed. Failed to contact"
273 # We should never get here unless the source_url starts with http://
274 assert(source_url.startswith('http://'))
275
276 # Get the http server name from the URL
277 server_name = urlparse.urlparse(source_url)[1]
mbligh76d19f72008-10-15 16:24:43 +0000278 http_cmd = 'wget -nv %s -O %s' % (server_name, dest_file_path)
mblighabe330e2008-12-09 23:37:52 +0000279
280 # Following repo_exception optimization is disabled for now.
281 # Checksum files are optional. The attempted download of a
282 # missing checksum file erroneously causes the repos to be marked
283 # dead, causing download of its custom kernels to fail.
284 # It also stays dead until Autotest is restarted.
285 if server_name in BPM._repo_exception and False: # <--- TEMP
showard9d02fb52008-08-08 18:20:37 +0000286 if BPM._repo_exception[server_name] == BPM.REPO_OK:
287 # This repository is fine. Simply return
288 return
289 else:
290 raise PackageFetchError("%s - %s : %s "
291 % (error_msg, server_name,
292 BPM._repo_exception[server_name]))
293 try:
294 try:
295 self._run_command(http_cmd,
296 _run_command_dargs={'timeout':30})
297 BPM._repo_exception[server_name] = BPM.REPO_OK
298 finally:
299 self._run_command('rm -f %s' % dest_file_path)
mbligh76d19f72008-10-15 16:24:43 +0000300 except Exception, e:
showard9d02fb52008-08-08 18:20:37 +0000301 BPM._repo_exception[server_name] = e
mbligh76d19f72008-10-15 16:24:43 +0000302 raise PackageFetchError("%s - %s: %s " % (error_msg, server_name,
303 e))
showard9d02fb52008-08-08 18:20:37 +0000304
305
306 # TODO(aganti): Fix the bug with the current checksum logic where
307 # packages' checksums that are not present consistently in all the
308 # repositories are not handled properly. This is a corner case though
309 # but the ideal solution is to make the checksum file repository specific
310 # and then maintain it.
311 def upload_pkg(self, pkg_path, upload_path=None, update_checksum=False):
312 '''
313 Uploads to a specified upload_path or to all the repos.
314 Also uploads the checksum file to all the repos.
315 pkg_path : The complete path to the package file
316 upload_path : the absolute path where the files are copied to.
317 if set to 'None' assumes 'all' repos
318 update_checksum : If set to False, the checksum file is not
319 going to be updated which happens by default.
320 This is necessary for custom
321 packages (like custom kernels and custom tests)
322 that get uploaded which do not need to be part of
323 the checksum file and bloat it.
324 '''
325 if update_checksum:
326 # get the packages' checksum file and update it with the current
327 # package's checksum
328 checksum_path = self._get_checksum_file_path()
329 self.update_checksum(pkg_path)
330
331 if upload_path:
332 upload_path_list = [upload_path]
333 elif len(self.upload_paths) > 0:
334 upload_path_list = self.upload_paths
335 else:
336 raise PackageUploadError("Invalid Upload Path specified")
337
338 # upload the package
339 for path in upload_path_list:
340 self.upload_pkg_file(pkg_path, path)
341 if update_checksum:
342 self.upload_pkg_file(checksum_path, path)
343
344
345 def upload_pkg_file(self, file_path, upload_path):
346 '''
347 Upload a single file. Depending on the upload path, the appropriate
348 method for that protocol is called. Currently this simply copies the
349 file to the target directory (but can be extended for other protocols)
350 This assumes that the web server is running on the same machine where
351 the method is being called from. The upload_path's files are
352 basically served by that web server.
353 '''
354 try:
mbligh93a9e292008-10-10 21:09:53 +0000355 if upload_path.startswith('ssh://'):
356 # parse ssh://user@host/usr/local/autotest/packages
357 hostline, remote_path = self._parse_ssh_path(upload_path)
mbligh1e3b0992008-10-14 16:29:54 +0000358 try:
359 utils.run('scp %s %s:%s' % (file_path, hostline,
360 remote_path))
361 r_path = os.path.join(remote_path,
362 os.path.basename(file_path))
363 utils.run("ssh %s 'chmod 644 %s'" % (hostline, r_path))
364 except error.CmdError:
showarda290e982009-03-27 20:57:30 +0000365 logging.error("Error uploading to repository %s",
366 upload_path)
mbligh93a9e292008-10-10 21:09:53 +0000367 else:
368 shutil.copy(file_path, upload_path)
369 os.chmod(os.path.join(upload_path,
370 os.path.basename(file_path)), 0644)
showard9d02fb52008-08-08 18:20:37 +0000371 except (IOError, os.error), why:
showarda290e982009-03-27 20:57:30 +0000372 logging.error("Upload of %s to %s failed: %s", file_path,
373 upload_path, why)
showard9d02fb52008-08-08 18:20:37 +0000374
375
mbligh9fc77972008-10-02 20:32:09 +0000376 def upload_pkg_dir(self, dir_path, upload_path):
377 '''
378 Upload a full directory. Depending on the upload path, the appropriate
379 method for that protocol is called. Currently this copies the whole
380 tmp package directory to the target directory.
381 This assumes that the web server is running on the same machine where
382 the method is being called from. The upload_path's files are
383 basically served by that web server.
384 '''
mbligh93a9e292008-10-10 21:09:53 +0000385 local_path = os.path.join(dir_path, "*")
mbligh9fc77972008-10-02 20:32:09 +0000386 try:
mbligh93a9e292008-10-10 21:09:53 +0000387 if upload_path.startswith('ssh://'):
388 hostline, remote_path = self._parse_ssh_path(upload_path)
mbligh1e3b0992008-10-14 16:29:54 +0000389 try:
390 utils.run('scp %s %s:%s' % (local_path, hostline,
391 remote_path))
392 ssh_path = os.path.join(remote_path, "*")
393 utils.run("ssh %s 'chmod 644 %s'" % (hostline, ssh_path))
394 except error.CmdError:
showarda290e982009-03-27 20:57:30 +0000395 logging.error("Error uploading to repository: %s",
396 upload_path)
mbligh93a9e292008-10-10 21:09:53 +0000397 else:
398 utils.run("cp %s %s " % (local_path, upload_path))
399 up_path = os.path.join(upload_path, "*")
400 utils.run("chmod 644 %s" % up_path)
mbligh9fc77972008-10-02 20:32:09 +0000401 except (IOError, os.error), why:
402 raise PackageUploadError("Upload of %s to %s failed: %s"
403 % (dir_path, upload_path, why))
404
405
showard9d02fb52008-08-08 18:20:37 +0000406 def remove_pkg(self, pkg_name, remove_path=None, remove_checksum=False):
407 '''
408 Remove the package from the specified remove_path
409 pkg_name : name of the package (ex: test-sleeptest.tar.bz2,
410 dep-gcc.tar.bz2)
411 remove_path : the location to remove the package from.
412
413 '''
414 if remove_path:
415 remove_path_list = [remove_path]
416 elif len(self.upload_paths) > 0:
417 remove_path_list = self.upload_paths
418 else:
419 raise PackageRemoveError("Invalid path to remove the pkg from")
420
421 checksum_path = self._get_checksum_file_path()
422
423 if remove_checksum:
424 self.remove_checksum(pkg_name)
425
426 # remove the package and upload the checksum file to the repos
427 for path in remove_path_list:
428 self.remove_pkg_file(pkg_name, path)
429 self.upload_pkg_file(checksum_path, path)
430
431
mbligh76d19f72008-10-15 16:24:43 +0000432 def remove_pkg_file(self, filename, pkg_dir):
showard9d02fb52008-08-08 18:20:37 +0000433 '''
mbligh76d19f72008-10-15 16:24:43 +0000434 Remove the file named filename from pkg_dir
showard9d02fb52008-08-08 18:20:37 +0000435 '''
436 try:
437 # Remove the file
mbligh93a9e292008-10-10 21:09:53 +0000438 if pkg_dir.startswith('ssh://'):
439 hostline, remote_path = self._parse_ssh_path(pkg_dir)
mbligh76d19f72008-10-15 16:24:43 +0000440 path = os.path.join(remote_path, filename)
mbligh93a9e292008-10-10 21:09:53 +0000441 utils.run("ssh %s 'rm -rf %s/%s'" % (hostline, remote_path,
442 path))
443 else:
mbligh76d19f72008-10-15 16:24:43 +0000444 os.remove(os.path.join(pkg_dir, filename))
showard9d02fb52008-08-08 18:20:37 +0000445 except (IOError, os.error), why:
446 raise PackageRemoveError("Could not remove %s from %s: %s "
mbligh76d19f72008-10-15 16:24:43 +0000447 % (filename, pkg_dir, why))
showard9d02fb52008-08-08 18:20:37 +0000448
449
mbligh76d19f72008-10-15 16:24:43 +0000450 def get_mirror_list(self):
mbligh1e3b0992008-10-14 16:29:54 +0000451 '''
mbligh76d19f72008-10-15 16:24:43 +0000452 Stub function for site specific mirrors.
mbligh1e3b0992008-10-14 16:29:54 +0000453
454 Returns:
455 Priority ordered list
456 '''
457 return self.repo_urls
458
459
showard9d02fb52008-08-08 18:20:37 +0000460 def _get_checksum_file_path(self):
461 '''
462 Return the complete path of the checksum file (assumed to be stored
463 in self.pkgmgr_dir
464 '''
465 return os.path.join(self.pkgmgr_dir, CHECKSUM_FILE)
466
467
468 def _get_checksum_dict(self):
469 '''
470 Fetch the checksum file if not already fetched. If the checksum file
471 cannot be fetched from the repos then a new file is created with
472 the current package's (specified in pkg_path) checksum value in it.
473 Populate the local checksum dictionary with the values read from
474 the checksum file.
475 The checksum file is assumed to be present in self.pkgmgr_dir
476 '''
477 checksum_path = self._get_checksum_file_path()
478 if not self._checksum_dict:
479 # Fetch the checksum file
480 try:
481 try:
482 self._run_command("ls %s" % checksum_path)
483 except (error.CmdError, error.AutoservRunError):
484 # The packages checksum file does not exist locally.
485 # See if it is present in the repositories.
mbligh76d19f72008-10-15 16:24:43 +0000486 self.fetch_pkg(CHECKSUM_FILE, checksum_path)
showard9d02fb52008-08-08 18:20:37 +0000487 except PackageFetchError, e:
488 # This should not happen whilst fetching a package..if a
489 # package is present in the repository, the corresponding
490 # checksum file should also be automatically present. This
491 # case happens only when a package
492 # is being uploaded and if it is the first package to be
493 # uploaded to the repos (hence no checksum file created yet)
494 # Return an empty dictionary in that case
495 return {}
496
497 # Read the checksum file into memory
498 checksum_file_contents = self._run_command('cat '
499 + checksum_path).stdout
500
501 # Return {} if we have an empty checksum file present
502 if not checksum_file_contents.strip():
503 return {}
504
505 # Parse the checksum file contents into self._checksum_dict
506 for line in checksum_file_contents.splitlines():
507 checksum, package_name = line.split(None, 1)
508 self._checksum_dict[package_name] = checksum
509
510 return self._checksum_dict
511
512
513 def _save_checksum_dict(self, checksum_dict):
514 '''
515 Save the checksum dictionary onto the checksum file. Update the
516 local _checksum_dict variable with this new set of values.
517 checksum_dict : New checksum dictionary
518 checksum_dir : The directory in which to store the checksum file to.
519 '''
520 checksum_path = self._get_checksum_file_path()
521 self._checksum_dict = checksum_dict.copy()
522 checksum_contents = '\n'.join(checksum + ' ' + pkg_name
523 for pkg_name,checksum in
524 checksum_dict.iteritems())
525 # Write the checksum file back to disk
526 self._run_command('echo "%s" > %s' % (checksum_contents,
527 checksum_path))
528
mbligh93a9e292008-10-10 21:09:53 +0000529 def _parse_ssh_path(self, pkg_path):
530 '''
531 Parse ssh://xx@xx/path/to/ and return a tuple with host_line and
532 remote path
533 '''
534
535 match = re.search('^ssh://(.*?)(/.*)$', pkg_path)
536 if match:
537 return match.groups()
538 else:
539 raise PackageUploadError("Incorrect SSH path in global_config: %s"
540 % upload_path)
541
showard9d02fb52008-08-08 18:20:37 +0000542
543 def compute_checksum(self, pkg_path):
544 '''
545 Compute the MD5 checksum for the package file and return it.
546 pkg_path : The complete path for the package file
547 '''
548 md5sum_output = self._run_command("md5sum %s " % pkg_path).stdout
549 return md5sum_output.split()[0]
550
551
552 def update_checksum(self, pkg_path):
553 '''
554 Update the checksum of the package in the packages' checksum
555 file. This method is called whenever a package is fetched just
556 to be sure that the checksums in the local file are the latest.
557 pkg_path : The complete path to the package file.
558 '''
559 # Compute the new checksum
560 new_checksum = self.compute_checksum(pkg_path)
561 checksum_dict = self._get_checksum_dict()
562 checksum_dict[os.path.basename(pkg_path)] = new_checksum
563 self._save_checksum_dict(checksum_dict)
564
565
566 def remove_checksum(self, pkg_name):
567 '''
568 Remove the checksum of the package from the packages checksum file.
569 This method is called whenever a package is removed from the
570 repositories in order clean its corresponding checksum.
571 pkg_name : The name of the package to be removed
572 '''
573 checksum_dict = self._get_checksum_dict()
574 if pkg_name in checksum_dict:
575 del checksum_dict[pkg_name]
576 self._save_checksum_dict(checksum_dict)
577
578
579 def compare_checksum(self, pkg_path, repo_url):
580 '''
581 Calculate the checksum of the file specified in pkg_path and
582 compare it with the checksum in the checksum file
583 Return True if both match else return False.
584 pkg_path : The full path to the package file for which the
585 checksum is being compared
586 repo_url : The URL to fetch the checksum from
587 '''
588 checksum_dict = self._get_checksum_dict()
589 package_name = os.path.basename(pkg_path)
590 if not checksum_dict or package_name not in checksum_dict:
591 return False
592
593 repository_checksum = checksum_dict[package_name]
594 local_checksum = self.compute_checksum(pkg_path)
595 return (local_checksum == repository_checksum)
596
597
mblighdbfc4e32008-08-22 18:08:07 +0000598 def tar_package(self, pkg_name, src_dir, dest_dir, exclude_string=None):
showard9d02fb52008-08-08 18:20:37 +0000599 '''
600 Create a tar.bz2 file with the name 'pkg_name' say test-blah.tar.bz2.
mbligh9fc77972008-10-02 20:32:09 +0000601 Excludes the directories specified in exclude_string while tarring
showard9d02fb52008-08-08 18:20:37 +0000602 the source. Returns the tarball path.
603 '''
showard9d02fb52008-08-08 18:20:37 +0000604 tarball_path = os.path.join(dest_dir, pkg_name)
mbligh9fc77972008-10-02 20:32:09 +0000605 cmd = "tar -cvjf %s -C %s %s " % (tarball_path, src_dir, exclude_string)
showard9d02fb52008-08-08 18:20:37 +0000606
mbligh9fc77972008-10-02 20:32:09 +0000607 utils.system(cmd)
showard9d02fb52008-08-08 18:20:37 +0000608 return tarball_path
609
610
611 def untar_required(self, tarball_path, dest_dir):
612 '''
613 Compare the checksum of the tarball_path with the .checksum file
614 in the dest_dir and return False if it matches. The untar
615 of the package happens only if the checksums do not match.
616 '''
617 checksum_path = os.path.join(dest_dir, '.checksum')
618 try:
619 existing_checksum = self._run_command('cat ' + checksum_path).stdout
620 except (error.CmdError, error.AutoservRunError):
621 # If the .checksum file is not present (generally, this should
622 # not be the case) then return True so that the untar happens
623 return True
624
625 new_checksum = self.compute_checksum(tarball_path)
626 return (new_checksum.strip() != existing_checksum.strip())
627
628
629 def untar_pkg(self, tarball_path, dest_dir):
630 '''
631 Untar the package present in the tarball_path and put a
632 ".checksum" file in the dest_dir containing the checksum
633 of the tarball. This method
634 assumes that the package to be untarred is of the form
635 <name>.tar.bz2
636 '''
mbligh96ad8512008-10-03 03:45:26 +0000637 self._run_command('tar xjf %s -C %s' % (tarball_path, dest_dir))
showard9d02fb52008-08-08 18:20:37 +0000638 # Put the .checksum file in the install_dir to note
639 # where the package came from
640 pkg_checksum = self.compute_checksum(tarball_path)
641 pkg_checksum_path = os.path.join(dest_dir,
642 '.checksum')
643 self._run_command('echo "%s" > %s '
644 % (pkg_checksum, pkg_checksum_path))
645
646
647 def get_tarball_name(self, name, pkg_type):
648 return "%s-%s.tar.bz2" % (pkg_type, name)
649
650
651 def is_url(self, url):
652 """Return true if path looks like a URL"""
653 return url.startswith('http://')
654
655
656 def get_package_name(self, url, pkg_type):
657 '''
658 Extract the group and test name for the url. This method is currently
659 used only for tests.
660 '''
661 if pkg_type == 'test':
mblighecbaec32008-10-25 13:37:42 +0000662 regex = '[^:]+://(.*)/([^/]*)$'
showard9d02fb52008-08-08 18:20:37 +0000663 return self._get_package_name(url, regex)
664 else:
665 return ('', url)
666
667
668 def _get_package_name(self, url, regex):
669 if not self.is_url(url):
670 if url.endswith('.tar.bz2'):
671 testname = url.replace('.tar.bz2', '')
672 testname = re.sub(r'(\d*)\.', '', testname)
673 return (testname, testname)
674 else:
675 return ('', url)
676
677 match = re.match(regex, url)
678 if not match:
679 return ('', url)
680 group, filename = match.groups()
681 # Generate the group prefix.
682 group = re.sub(r'\W', '_', group)
683 # Drop the extension to get the raw test name.
684 testname = re.sub(r'\.tar\.bz2', '', filename)
685 # Drop any random numbers at the end of the test name if any
686 testname = re.sub(r'\.(\d*)', '', testname)
687 return (group, testname)
688
689
mbligha7007722009-01-13 00:37:11 +0000690SitePackageManager = utils.import_site_class(
691 __file__, "autotest_lib.client.common_lib.site_packages",
692 "SitePackageManager", BasePackageManager)
showard9d02fb52008-08-08 18:20:37 +0000693
694class PackageManager(SitePackageManager):
695 pass