]> scripts.mit.edu Git - wizard.git/blobdiff - wizard/command/massmigrate.py
Rename lib/wizard to wizard for compactness.
[wizard.git] / wizard / command / massmigrate.py
diff --git a/wizard/command/massmigrate.py b/wizard/command/massmigrate.py
new file mode 100644 (file)
index 0000000..f247a0d
--- /dev/null
@@ -0,0 +1,70 @@
+import optparse
+import os
+
+import wizard
+from wizard import deploy
+from wizard import util
+from wizard import shell
+from wizard.command import _base
+from wizard.command import migrate
+
+def main(argv, global_options, logger = None):
+    usage = """usage: %prog massmigrate [ARGS] APPLICATION
+
+Mass migrates an application to the new repository format.
+Essentially equivalent to running '%prog migrate' on all
+autoinstalls for a particular application found by parallel-find,
+but with advanced reporting.
+"""
+    parser = _base.WizardOptionParser(usage)
+    parser.add_option("--no-parallelize", dest="no_parallelize", action="store_true",
+            default=False, help="Turn off parallelization")
+    parser.add_option("--dry-run", dest="dry_run", action="store_true",
+            default=False, help="Print commands that would be run. Implies --no-parallelize")
+    parser.add_option("--max", dest="max",
+            default=10, help="Maximum subprocesses to run concurrently")
+    options, args, logger = parser.parse_all(argv, logger)
+    if len(args) > 1:
+        parser.error("too many arguments")
+    elif not args:
+        parser.error("must specify application to migrate")
+    if options.verbose or options.dry_run:
+        options.no_parallelize = True
+    app = args[0]
+    errors = {}
+    base_args = _base.makeBaseArgs(options, dry_run="--dry-run")
+    # check if we have root
+    uid = os.getuid()
+    user = None
+    # dry run is purposely omitted
+    if options.no_parallelize:
+        sh = shell.DummyParallelShell(logger=logger)
+    else:
+        sh = shell.ParallelShell(logger=logger, max=int(options.max))
+    for line in deploy.getInstallLines(global_options):
+        # validate and filter the deployments
+        try:
+            d = deploy.Deployment.parse(line)
+        except deploy.DeploymentParseError, deploy.NoSuchApplication:
+            continue
+        name = d.getApplication().name
+        if name != app: continue
+        # actual meat
+        def on_success(stdout, stderr): pass # yay! don't do anything
+        def make_on_error(d): # make it early binding
+            def on_error(e):
+                if e.name == "wizard.command.migrate.AlreadyMigratedError":
+                    logger.info("Skipped already migrated %s" % d.location)
+                else:
+                    name = e.name
+                    if name not in errors: errors[name] = []
+                    errors[name].append(d)
+                    logger.error("ERROR [%s] in %s" % (name, d.location))
+            return on_error
+        on_error = make_on_error(d)
+        sh.wait() # wait for a parallel processing slot to be available
+        sh.callAsUser(shell.wizard, "migrate", d.location, *base_args,
+                      user=user, on_success=on_success, on_error=on_error)
+    sh.join()
+    for name, deploys in errors.items():
+        logger.warning("ERROR [%s] from %d installs" % (name, len(deploys)))