]> scripts.mit.edu Git - wizard.git/blob - wizard/command/massmigrate.py
5feaa78172834be36be29b7e09f1f032597ed9c2
[wizard.git] / wizard / command / massmigrate.py
1 import optparse
2 import os
3
4 import wizard
5 from wizard import deploy
6 from wizard import util
7 from wizard import shell
8 from wizard import cache
9 from wizard.command import _base
10 from wizard.command import migrate
11
12 def main(argv, global_options, logger = None):
13     usage = """usage: %prog massmigrate [ARGS] APPLICATION
14
15 Mass migrates an application to the new repository format.
16 Essentially equivalent to running '%prog migrate' on all
17 autoinstalls for a particular application found by parallel-find,
18 but with advanced reporting.
19
20 When doing an actual run, it is recommended to use --cache to
21 be able to resume gracefully (without it, massmigrate must
22 stat every install to find out if it migrated it yet).
23 """
24     parser = _base.WizardOptionParser(usage)
25     parser.add_option("--no-parallelize", dest="no_parallelize", action="store_true",
26             default=False, help="Turn off parallelization")
27     parser.add_option("--dry-run", dest="dry_run", action="store_true",
28             default=False, help="Print commands that would be run. Implies --no-parallelize")
29     parser.add_option("--max", dest="max",
30             default=10, help="Maximum subprocesses to run concurrently")
31     parser.add_option("--cache", dest="cache",
32             default=None, help="Cache file to read/write paths of already processed installs. These will be skipped.")
33     options, args, logger = parser.parse_all(argv, logger)
34     if len(args) > 1:
35         parser.error("too many arguments")
36     elif not args:
37         parser.error("must specify application to migrate")
38     if options.dry_run:
39         options.no_parallelize = True
40     app = args[0]
41     errors = {}
42     base_args = _base.makeBaseArgs(options, dry_run="--dry-run")
43     # check if we have root
44     uid = os.getuid()
45     user = None
46     # dry run is purposely omitted
47     if options.no_parallelize:
48         sh = shell.DummyParallelShell(logger=logger)
49     else:
50         sh = shell.ParallelShell(logger=logger, max=int(options.max))
51     if options.cache:
52         seen = cache.Cache(options.cache)
53     else:
54         seen = cache.DummyCache()
55     for line in deploy.getInstallLines(global_options):
56         # validate and filter the deployments
57         try:
58             d = deploy.Deployment.parse(line)
59         except deploy.DeploymentParseError, deploy.NoSuchApplication:
60             continue
61         name = d.getApplication().name
62         if name != app: continue
63         if d.location in seen:
64             continue
65         # actual meat
66         def make_on_pair(d):
67             def on_success(stdout, stderr):
68                 seen.add(d.location)
69             def on_error(e):
70                 if e.name == "wizard.command.migrate.AlreadyMigratedError":
71                     seen.add(d.location)
72                     logger.info("Skipped already migrated %s" % d.location)
73                 else:
74                     name = e.name
75                     if name not in errors: errors[name] = []
76                     errors[name].append(d)
77                     logger.error("%s in %s" % (name, d.location))
78             return (on_success, on_error)
79         on_success, on_error = make_on_pair(d)
80         sh.wait() # wait for a parallel processing slot to be available
81         sh.callAsUser(shell.wizard, "migrate", d.location, *base_args,
82                       user=user, on_success=on_success, on_error=on_error)
83     sh.join()
84     for name, deploys in errors.items():
85         logger.warning("%s from %d installs" % (name, len(deploys)))