+def Err(msg, exit_code=1):
+ """Simple error logging that prints to stderr.
+
+ """
+ sys.stderr.write(msg + "\n")
+ sys.stderr.flush()
+ sys.exit(exit_code)
+
+
+class SimpleOpener(urllib.FancyURLopener):
+ """A simple url opener"""
+ # pylint: disable=W0221
+
+ def prompt_user_passwd(self, host, realm, clear_cache=0):
+ """No-interaction version of prompt_user_passwd."""
+ # we follow parent class' API
+ # pylint: disable=W0613
+ return None, None
+
+ def http_error_default(self, url, fp, errcode, errmsg, headers):
+ """Custom error handling"""
+ # make sure sockets are not left in CLOSE_WAIT, this is similar
+ # but with a different exception to the BasicURLOpener class
+ _ = fp.read() # throw away data
+ fp.close()
+ raise InstanceDown("HTTP error returned: code %s, msg %s" %
+ (errcode, errmsg))
+
+
+OPTIONS = [
+ cli.cli_option("-o", "--os", dest="os", default=None,
+ help="OS to use during burnin",
+ metavar="<OS>",
+ completion_suggest=cli.OPT_COMPL_ONE_OS),
+ cli.HYPERVISOR_OPT,
+ cli.OSPARAMS_OPT,
+ cli.cli_option("--disk-size", dest="disk_size",
+ help="Disk size (determines disk count)",
+ default="128m", type="string", metavar="<size,size,...>",
+ completion_suggest=("128M 512M 1G 4G 1G,256M"
+ " 4G,1G,1G 10G").split()),
+ cli.cli_option("--disk-growth", dest="disk_growth", help="Disk growth",
+ default="128m", type="string", metavar="<size,size,...>"),
+ cli.cli_option("--mem-size", dest="mem_size", help="Memory size",
+ default=128, type="unit", metavar="<size>",
+ completion_suggest=("128M 256M 512M 1G 4G 8G"
+ " 12G 16G").split()),
+ cli.cli_option("--vcpu-count", dest="vcpu_count", help="VCPU count",
+ default=3, type="unit", metavar="<count>",
+ completion_suggest=("1 2 3 4").split()),
+ cli.DEBUG_OPT,
+ cli.VERBOSE_OPT,
+ cli.NOIPCHECK_OPT,
+ cli.NONAMECHECK_OPT,
+ cli.EARLY_RELEASE_OPT,
+ cli.cli_option("--no-replace1", dest="do_replace1",
+ help="Skip disk replacement with the same secondary",
+ action="store_false", default=True),
+ cli.cli_option("--no-replace2", dest="do_replace2",
+ help="Skip disk replacement with a different secondary",
+ action="store_false", default=True),
+ cli.cli_option("--no-failover", dest="do_failover",
+ help="Skip instance failovers", action="store_false",
+ default=True),
+ cli.cli_option("--no-migrate", dest="do_migrate",
+ help="Skip instance live migration",
+ action="store_false", default=True),
+ cli.cli_option("--no-move", dest="do_move",
+ help="Skip instance moves", action="store_false",
+ default=True),
+ cli.cli_option("--no-importexport", dest="do_importexport",
+ help="Skip instance export/import", action="store_false",
+ default=True),
+ cli.cli_option("--no-startstop", dest="do_startstop",
+ help="Skip instance stop/start", action="store_false",
+ default=True),
+ cli.cli_option("--no-reinstall", dest="do_reinstall",
+ help="Skip instance reinstall", action="store_false",
+ default=True),
+ cli.cli_option("--no-reboot", dest="do_reboot",
+ help="Skip instance reboot", action="store_false",
+ default=True),
+ cli.cli_option("--reboot-types", dest="reboot_types",
+ help="Specify the reboot types", default=None),
+ cli.cli_option("--no-activate-disks", dest="do_activate_disks",
+ help="Skip disk activation/deactivation",
+ action="store_false", default=True),
+ cli.cli_option("--no-add-disks", dest="do_addremove_disks",
+ help="Skip disk addition/removal",
+ action="store_false", default=True),
+ cli.cli_option("--no-add-nics", dest="do_addremove_nics",
+ help="Skip NIC addition/removal",
+ action="store_false", default=True),
+ cli.cli_option("--no-nics", dest="nics",
+ help="No network interfaces", action="store_const",
+ const=[], default=[{}]),
+ cli.cli_option("--no-confd", dest="do_confd_tests",
+ help="Skip confd queries",
+ action="store_false", default=True),
+ cli.cli_option("--rename", dest="rename", default=None,
+ help=("Give one unused instance name which is taken"
+ " to start the renaming sequence"),
+ metavar="<instance_name>"),
+ cli.cli_option("-t", "--disk-template", dest="disk_template",
+ choices=list(constants.DISK_TEMPLATES),
+ default=constants.DT_DRBD8,
+ help="Disk template (diskless, file, plain, sharedfile"
+ " or drbd) [drbd]"),
+ cli.cli_option("-n", "--nodes", dest="nodes", default="",
+ help=("Comma separated list of nodes to perform"
+ " the burnin on (defaults to all nodes)"),
+ completion_suggest=cli.OPT_COMPL_MANY_NODES),
+ cli.cli_option("-I", "--iallocator", dest="iallocator",
+ default=None, type="string",
+ help=("Perform the allocation using an iallocator"
+ " instead of fixed node spread (node restrictions no"
+ " longer apply, therefore -n/--nodes must not be"
+ " used"),
+ completion_suggest=cli.OPT_COMPL_ONE_IALLOCATOR),
+ cli.cli_option("-p", "--parallel", default=False, action="store_true",
+ dest="parallel",
+ help=("Enable parallelization of some operations in"
+ " order to speed burnin or to test granular locking")),
+ cli.cli_option("--net-timeout", default=15, type="int",
+ dest="net_timeout",
+ help=("The instance check network timeout in seconds"
+ " (defaults to 15 seconds)"),
+ completion_suggest="15 60 300 900".split()),
+ cli.cli_option("-C", "--http-check", default=False, action="store_true",
+ dest="http_check",
+ help=("Enable checking of instance status via http,"
+ " looking for /hostname.txt that should contain the"
+ " name of the instance")),
+ cli.cli_option("-K", "--keep-instances", default=False,
+ action="store_true",
+ dest="keep_instances",
+ help=("Leave instances on the cluster after burnin,"
+ " for investigation in case of errors or simply"
+ " to use them")),
+ ]
+
+# Mainly used for bash completion
+ARGUMENTS = [cli.ArgInstance(min=1)]
+
+
+def _DoCheckInstances(fn):
+ """Decorator for checking instances.
+
+ """
+ def wrapper(self, *args, **kwargs):
+ val = fn(self, *args, **kwargs)
+ for instance in self.instances:
+ self._CheckInstanceAlive(instance) # pylint: disable=W0212
+ return val
+
+ return wrapper
+
+
+def _DoBatch(retry):
+ """Decorator for possible batch operations.
+
+ Must come after the _DoCheckInstances decorator (if any).
+
+ @param retry: whether this is a retryable batch, will be
+ passed to StartBatch
+
+ """
+ def wrap(fn):
+ def batched(self, *args, **kwargs):
+ self.StartBatch(retry)
+ val = fn(self, *args, **kwargs)
+ self.CommitQueue()
+ return val
+ return batched
+
+ return wrap
+
+