"""
-# pylint: disable-msg=C0103
+# pylint: disable=C0103
# C0103: Invalid name ganeti-listrunner
import errno
import time
import traceback
-import paramiko
+try:
+ import paramiko
+except ImportError:
+ print >> sys.stderr, \
+ ("The \"paramiko\" module could not be imported. Install it from your"
+ " distribution's repository. The package is usually named"
+ " \"python-paramiko\".")
+ sys.exit(1)
REMOTE_PATH_BASE = "/tmp/listrunner"
log = logging.getLogger(transport.get_log_channel())
log.addHandler(handler)
- transport.connect(username=username, **kwargs) # pylint: disable-msg=W0142
+ transport.connect(username=username, **kwargs) # pylint: disable=W0142
WriteLog("ssh connection established using %s" % desc, logfile)
# strange ... when establishing the session and the immediately
# setting up the channels for sftp & shell from that, it sometimes
sftp = paramiko.SFTPClient.from_transport(connection)
sftp.mkdir(remote_dir, mode=0700)
for item in filelist:
- remote_file = "%s/%s" % (remote_dir, item.split("/").pop())
+ remote_file = "%s/%s" % (remote_dir, os.path.basename(item))
WriteLog("uploading %s to remote %s" % (item, remote_file), logfile)
sftp.put(item, remote_file)
if item == executable:
try:
sftp = paramiko.SFTPClient.from_transport(connection)
for item in filelist:
- fullpath = "%s/%s" % (upload_dir, item.split("/").pop())
+ fullpath = "%s/%s" % (upload_dir, os.path.basename(item))
WriteLog("removing remote %s" % fullpath, logfile)
sftp.remove(fullpath)
sftp.rmdir(upload_dir)
select.select([], [], [], .1)
WriteLog("SUCCESS: command output follows", logfile)
- for line in output.split("\n"):
- WriteLog("output = %s" %line, logfile)
+ for line in output.splitlines():
+ WriteLog("output = %s" % line, logfile)
WriteLog("command execution completed", logfile)
session.close()
def HostWorker(logdir, username, password, use_agent, hostname,
- executable, command, filelist):
+ executable, exec_args, command, filelist):
"""Per-host worker.
This function does not return - it's the main code of the childs,
@param use_agent: whether we should instead use an agent
@param hostname: the hostname to connect to
@param executable: the executable to upload, if not None
+ @param exec_args: Additional arguments for executable
@param command: the command to run
@param filelist: auxiliary files to upload
print " %s: uploading files" % hostname
upload_dir = UploadFiles(connection, executable,
filelist, logfile)
- command = "cd %s && ./%s" % (upload_dir,
- executable.split("/").pop())
+ command = ("cd %s && ./%s" %
+ (upload_dir, os.path.basename(executable)))
+ if exec_args:
+ command += " %s" % exec_args
print " %s: executing remote command" % hostname
cmd_result = RunRemoteCommand(connection, command, logfile)
if cmd_result is True:
def LaunchWorker(child_pids, logdir, username, password, use_agent, hostname,
- executable, command, filelist):
+ executable, exec_args, command, filelist):
"""Launch the per-host worker.
Arguments are the same as for HostWorker, except for child_pids,
child_pids[pid] = hostname
else:
HostWorker(logdir, username, password, use_agent, hostname,
- executable, command, filelist)
+ executable, exec_args, command, filelist)
def ParseOptions():
help="comma-separated list of hosts or single hostname",)
parser.add_option("-a", dest="auxfiles", action="append", default=[],
help="optional auxiliary file to upload"
- " (can be given multiple times",
+ " (can be given multiple times)",
metavar="FILE")
parser.add_option("-c", dest="command", default=None,
help="shell command to run on remote host(s)")
" using an agent)")
parser.add_option("-A", dest="use_agent", default=False, action="store_true",
help="instead of password, use keys from an SSH agent")
+ parser.add_option("--args", dest="exec_args", default=None,
+ help="Arguments to be passed to executable (-x)")
opts, args = parser.parse_args()
parser.error("Options -x and -c conflict with each other")
if not (opts.executable or opts.command):
parser.error("One of -x and -c must be given")
+ if opts.command and opts.exec_args:
+ parser.error("Can't specify arguments when using custom command")
if not opts.logdir:
parser.error("Option -l is required")
if opts.hostfile and opts.hostlist:
parser.error("This program doesn't take any arguments, passed in: %s" %
", ".join(args))
- return (opts.logdir, opts.executable, opts.hostfile, opts.hostlist,
+ return (opts.logdir, opts.executable, opts.exec_args,
+ opts.hostfile, opts.hostlist,
opts.command, opts.use_agent, opts.auxfiles, opts.username,
opts.password, opts.batch_size)
def main():
"""main."""
- (logdir, executable, hostfile, hostlist,
+ (logdir, executable, exec_args, hostfile, hostlist,
command, use_agent, auxfiles, username,
password, batch_size) = ParseOptions()
child_pids = {}
for hostname in batch:
LaunchWorker(child_pids, logdir, username, password, use_agent, hostname,
- executable, command, filelist)
+ executable, exec_args, command, filelist)
while child_pids:
pid, status = os.wait()
failures += 1
if hosts:
LaunchWorker(child_pids, logdir, username, password, use_agent,
- hosts.pop(0), executable, command, filelist)
+ hosts.pop(0), executable, exec_args, command, filelist)
print
print "All done, %s successful and %s failed hosts" % (successes, failures)