• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/python -u
2
3"""
4Utility to upload or remove the packages from the packages repository.
5"""
6
7import logging, optparse, os, shutil, sys, tempfile
8import common
9from autotest_lib.client.common_lib import utils as client_utils
10from autotest_lib.client.common_lib import global_config, error
11from autotest_lib.client.common_lib import packages
12from autotest_lib.server import utils as server_utils
13
14c = global_config.global_config
15logging.basicConfig(level=logging.DEBUG)
16
17ACTION_REMOVE = 'remove'
18ACTION_UPLOAD = 'upload'
19ACTION_TAR_ONLY = 'tar_only'
20
21def get_exclude_string(client_dir):
22    '''
23    Get the exclude string for the tar command to exclude specific
24    subdirectories inside client_dir.
25    For profilers we need to exclude everything except the __init__.py
26    file so that the profilers can be imported.
27    '''
28    exclude_string = ('--exclude=deps/* --exclude=tests/* '
29                      '--exclude=site_tests/* --exclude=**.pyc')
30
31    # Get the profilers directory
32    prof_dir = os.path.join(client_dir, 'profilers')
33
34    # Include the __init__.py file for the profilers and exclude all its
35    # subdirectories
36    for f in os.listdir(prof_dir):
37        if os.path.isdir(os.path.join(prof_dir, f)):
38            exclude_string += ' --exclude=profilers/%s' % f
39
40    # The '.' here is needed to zip the files in the current
41    # directory. We use '-C' for tar to change to the required
42    # directory i.e. src_dir and then zip up the files in that
43    # directory(which is '.') excluding the ones in the exclude_dirs
44    exclude_string += " ."
45
46    # TODO(milleral): This is sad and ugly.  http://crbug.com/258161
47    # Surprisingly, |exclude_string| actually means argument list, and
48    # we'd like to package up the current global_config.ini also, so let's
49    # just tack it on here.
50    # Also note that this only works because tar prevents us from un-tarring
51    # files into parent directories.
52    exclude_string += " ../global_config.ini"
53
54    return exclude_string
55
56
57def parse_args():
58    parser = optparse.OptionParser()
59    parser.add_option("-d", "--dependency", help="package the dependency"
60                      " from client/deps directory and upload to the repo",
61                      dest="dep")
62    parser.add_option("-p", "--profiler", help="package the profiler "
63                      "from client/profilers directory and upload to the repo",
64                      dest="prof")
65    parser.add_option("-t", "--test", help="package the test from client/tests"
66                      " or client/site_tests and upload to the repo.",
67                      dest="test")
68    parser.add_option("-c", "--client", help="package the client "
69                      "directory alone without the tests, deps and profilers",
70                      dest="client", action="store_true", default=False)
71    parser.add_option("-f", "--file", help="simply uploads the specified"
72                      "file on to the repo", dest="file")
73    parser.add_option("-r", "--repository", help="the URL of the packages"
74                      "repository location to upload the packages to.",
75                      dest="repo", default=None)
76    parser.add_option("-o", "--output_dir", help="the output directory"
77                      "to place tarballs and md5sum files in.",
78                      dest="output_dir", default=None)
79    parser.add_option("-a", "--action", help="the action to perform",
80                      dest="action", choices=(ACTION_UPLOAD, ACTION_REMOVE,
81                                              ACTION_TAR_ONLY), default=None)
82    parser.add_option("--all", help="Upload all the files locally "
83                      "to all the repos specified in global_config.ini. "
84                      "(includes the client, tests, deps and profilers)",
85                      dest="all", action="store_true", default=False)
86
87    options, args = parser.parse_args()
88    return options, args
89
90def process_packages(pkgmgr, pkg_type, pkg_names, src_dir,
91                     action, dest_dir=None):
92    """Method to upload or remove package depending on the flag passed to it.
93
94    If tar_only is set to True, this routine is solely used to generate a
95    tarball and compute the md5sum from that tarball.
96    If the tar_only flag is True, then the remove flag is ignored.
97    """
98    exclude_string = ' .'
99    names = [p.strip() for p in pkg_names.split(',')]
100    for name in names:
101        print "Processing %s ... " % name
102        if pkg_type == 'client':
103            pkg_dir = src_dir
104            exclude_string = get_exclude_string(pkg_dir)
105        elif pkg_type == 'test':
106            # if the package is a test then look whether it is in client/tests
107            # or client/site_tests
108            pkg_dir = os.path.join(get_test_dir(name, src_dir), name)
109        else:
110            # for the profilers and deps
111            pkg_dir = os.path.join(src_dir, name)
112
113        pkg_name = pkgmgr.get_tarball_name(name, pkg_type)
114
115        if action == ACTION_TAR_ONLY:
116            # If the package is a test, then the work-dir should have 'client'
117            # appended to it.
118            base_test_dir = os.path.join(dest_dir, 'client')
119            build_dir = os.path.join(get_test_dir(name, base_test_dir), name)
120
121            try:
122                packages.check_diskspace(build_dir)
123            except error.RepoDiskFullError as e:
124                msg = ("Work_dir directory for packages %s does not have "
125                       "enough space available: %s" % (build_dir, e))
126                raise error.RepoDiskFullError(msg)
127            tarball_path = pkgmgr.tar_package(pkg_name, pkg_dir,
128                                              build_dir, exclude_string)
129
130            # Create the md5 hash too.
131            md5sum = pkgmgr.compute_checksum(tarball_path)
132            md5sum_filepath = os.path.join(build_dir, pkg_name + '.checksum')
133            with open(md5sum_filepath, "w") as f:
134                f.write(md5sum)
135
136        elif action == ACTION_UPLOAD:
137            # Tar the source and upload
138            temp_dir = tempfile.mkdtemp()
139            try:
140                try:
141                    packages.check_diskspace(temp_dir)
142                except error.RepoDiskFullError, e:
143                    msg = ("Temporary directory for packages %s does not have "
144                           "enough space available: %s" % (temp_dir, e))
145                    raise error.RepoDiskFullError(msg)
146
147                # Check if tarball already exists. If it does, don't duplicate
148                # the effort.
149                tarball_path = os.path.join(pkg_dir, pkg_name);
150                if os.path.exists(tarball_path):
151                   print("Tarball %s already exists" % tarball_path);
152                else:
153                    tarball_path = pkgmgr.tar_package(pkg_name, pkg_dir,
154                                                      temp_dir, exclude_string)
155                pkgmgr.upload_pkg(tarball_path, update_checksum=True)
156            finally:
157                # remove the temporary directory
158                shutil.rmtree(temp_dir)
159        elif action == ACTION_REMOVE:
160            pkgmgr.remove_pkg(pkg_name, remove_checksum=True)
161        print "Done."
162
163
164def tar_packages(pkgmgr, pkg_type, pkg_names, src_dir, temp_dir):
165    """Tar all packages up and return a list of each tar created"""
166    tarballs = []
167    exclude_string = ' .'
168    names = [p.strip() for p in pkg_names.split(',')]
169    for name in names:
170        print "Processing %s ... " % name
171        if pkg_type == 'client':
172            pkg_dir = src_dir
173            exclude_string = get_exclude_string(pkg_dir)
174        elif pkg_type == 'test':
175            # if the package is a test then look whether it is in client/tests
176            # or client/site_tests
177            pkg_dir = os.path.join(get_test_dir(name, src_dir), name)
178        else:
179            # for the profilers and deps
180            pkg_dir = os.path.join(src_dir, name)
181
182        pkg_name = pkgmgr.get_tarball_name(name, pkg_type)
183
184        # Check if tarball already exists. If it does, don't duplicate
185        # the effort.
186        tarball_path = os.path.join(pkg_dir, pkg_name);
187        if os.path.exists(tarball_path):
188            print("Tarball %s already exists" % tarball_path);
189        else:
190            tarball_path = pkgmgr.tar_package(pkg_name, pkg_dir,
191                                              temp_dir, exclude_string)
192        tarballs.append(tarball_path)
193    return tarballs
194
195
196def process_all_packages(pkgmgr, client_dir, action):
197    """Process a full upload of packages as a directory upload."""
198    dep_dir = os.path.join(client_dir, "deps")
199    prof_dir = os.path.join(client_dir, "profilers")
200    # Directory where all are kept
201    temp_dir = tempfile.mkdtemp()
202    try:
203        packages.check_diskspace(temp_dir)
204    except error.RepoDiskFullError, e:
205        print ("Temp destination for packages is full %s, aborting upload: %s"
206               % (temp_dir, e))
207        os.rmdir(temp_dir)
208        sys.exit(1)
209
210    # process tests
211    tests_list = get_subdir_list('tests', client_dir)
212    tests = ','.join(tests_list)
213
214    # process site_tests
215    site_tests_list = get_subdir_list('site_tests', client_dir)
216    site_tests = ','.join(site_tests_list)
217
218    # process deps
219    deps_list = get_subdir_list('deps', client_dir)
220    deps = ','.join(deps_list)
221
222    # process profilers
223    profilers_list = get_subdir_list('profilers', client_dir)
224    profilers = ','.join(profilers_list)
225
226    # Update md5sum
227    if action == ACTION_UPLOAD:
228        all_packages = []
229        all_packages.extend(tar_packages(pkgmgr, 'profiler', profilers,
230                                         prof_dir, temp_dir))
231        all_packages.extend(tar_packages(pkgmgr, 'dep', deps, dep_dir,
232                                         temp_dir))
233        all_packages.extend(tar_packages(pkgmgr, 'test', site_tests,
234                                         client_dir, temp_dir))
235        all_packages.extend(tar_packages(pkgmgr, 'test', tests, client_dir,
236                                         temp_dir))
237        all_packages.extend(tar_packages(pkgmgr, 'client', 'autotest',
238                                         client_dir, temp_dir))
239        for package in all_packages:
240            pkgmgr.upload_pkg(package, update_checksum=True)
241        client_utils.run('rm -rf ' + temp_dir)
242    elif action == ACTION_REMOVE:
243        process_packages(pkgmgr, 'test', tests, client_dir, action=action)
244        process_packages(pkgmgr, 'test', site_tests, client_dir, action=action)
245        process_packages(pkgmgr, 'client', 'autotest', client_dir,
246                         action=action)
247        process_packages(pkgmgr, 'dep', deps, dep_dir, action=action)
248        process_packages(pkgmgr, 'profiler', profilers, prof_dir,
249                         action=action)
250
251
252# Get the list of sub directories present in a directory
253def get_subdir_list(name, client_dir):
254    dir_name = os.path.join(client_dir, name)
255    return [f for f in
256            os.listdir(dir_name)
257            if os.path.isdir(os.path.join(dir_name, f)) ]
258
259
260# Look whether the test is present in client/tests and client/site_tests dirs
261def get_test_dir(name, client_dir):
262    names_test = os.listdir(os.path.join(client_dir, 'tests'))
263    names_site_test = os.listdir(os.path.join(client_dir, 'site_tests'))
264    if name in names_test:
265        src_dir = os.path.join(client_dir, 'tests')
266    elif name in names_site_test:
267        src_dir = os.path.join(client_dir, 'site_tests')
268    else:
269        print "Test %s not found" % name
270        sys.exit(0)
271    return src_dir
272
273
274def main():
275    # get options and args
276    options, args = parse_args()
277
278    server_dir = server_utils.get_server_dir()
279    autotest_dir = os.path.abspath(os.path.join(server_dir, '..'))
280
281    # extract the pkg locations from global config
282    repo_urls = c.get_config_value('PACKAGES', 'fetch_location',
283                                   type=list, default=[])
284    upload_paths = c.get_config_value('PACKAGES', 'upload_location',
285                                      type=list, default=[])
286
287    if options.repo:
288        upload_paths.append(options.repo)
289
290    # Having no upload paths basically means you're not using packaging.
291    if not upload_paths:
292        print("No upload locations found. Please set upload_location under"
293              " PACKAGES in the global_config.ini or provide a location using"
294              " the --repository option.")
295        return
296
297    client_dir = os.path.join(autotest_dir, "client")
298
299    # Bail out if the client directory does not exist
300    if not os.path.exists(client_dir):
301        sys.exit(0)
302
303    dep_dir = os.path.join(client_dir, "deps")
304    prof_dir = os.path.join(client_dir, "profilers")
305
306    # Due to the delayed uprev-ing of certain ebuilds, we need to support
307    # both the legacy command line and the new one.
308    # So, if the new "action" option isn't specified, try looking for the
309    # old style remove/upload argument
310    if options.action is None:
311        if len(args) == 0 or args[0] not in ['upload', 'remove']:
312            print("Either 'upload' or 'remove' needs to be specified "
313                  "for the package")
314            sys.exit(0)
315        cur_action = args[0]
316    else:
317        cur_action = options.action
318
319    if cur_action == ACTION_TAR_ONLY and options.output_dir is None:
320        print("An output dir has to be specified")
321        sys.exit(0)
322
323    pkgmgr = packages.PackageManager(autotest_dir, repo_urls=repo_urls,
324                                     upload_paths=upload_paths,
325                                     run_function_dargs={'timeout':600})
326
327    if options.all:
328        process_all_packages(pkgmgr, client_dir, action=cur_action)
329
330    if options.client:
331        process_packages(pkgmgr, 'client', 'autotest', client_dir,
332                         action=cur_action)
333
334    if options.dep:
335        process_packages(pkgmgr, 'dep', options.dep, dep_dir,
336                         action=cur_action)
337
338    if options.test:
339        process_packages(pkgmgr, 'test', options.test, client_dir,
340                         action=cur_action, dest_dir=options.output_dir)
341
342    if options.prof:
343        process_packages(pkgmgr, 'profiler', options.prof, prof_dir,
344                         action=cur_action)
345
346    if options.file:
347        if cur_action == ACTION_REMOVE:
348            pkgmgr.remove_pkg(options.file, remove_checksum=True)
349        elif cur_action == ACTION_UPLOAD:
350            pkgmgr.upload_pkg(options.file, update_checksum=True)
351
352
353if __name__ == "__main__":
354    main()
355