#!/usr/bin/env python # # plessh will run the given shell command on all Planetlab Europe # nodes. # import sys import os import xmlrpclib from threading import Thread from getpass import getpass PLC_HOST = "www.planet-lab.eu" SLICE_NAME = "" KEY_FILE = "~/.ssh/id_rsa" BASE_CMD = 'ssh -q %(options)s -i %(key)s -l %(slice_name)s %(host)s "%(cmd)s" 2> /dev/null' THREAD_COUNT = 10 SSH_OPTIONS = { "BatchMode": "yes", "StrictHostKeyChecking": "no", "ConnectTimeout": 15, "UserKnownHostsFile": "/dev/null", "CheckHostIP": "no" } class API: def __init__(self, hostname, username, password): self.host = hostname self.api = xmlrpclib.ServerProxy("https://%s/PLCAPI/" % self.host, allow_none=True) self.auth = { "Username": username, "AuthString": password, "AuthMethod": "password" } # this will raise an exception if not successful self.api.AuthCheck(self.auth) def wrap(self, function): def wrapper(*args): args = (self.auth, ) + args return function(*args) return wrapper def __getattr__(self, attr): return self.wrap(getattr(self.api, attr)) def getAPI(hostname, username=None, password=None): if not username: username=raw_input("Please enter your PLC username: ") if not password: password=getpass("Please enter your PLC password: ") while True: try: return API(hostname=hostname, username=username, password=password) except xmlrpclib.Fault,e: print e print "Please try again." username=raw_input("Please enter your PLC username: ") password=getpass("Please enter your PLC password: ") continue def change_key_comment(output, hostname): fields = output.split() new_fields = [] in_key = False in_comment = False for f in fields: f = f.strip() if f.startswith("ssh-"): in_key = True elif in_key: in_key = False in_comment = True elif in_comment: f = hostname new_fields.append(f) return " ".join(new_fields) class Command(Thread): def __init__(self, cmd, hosts=[]): Thread.__init__(self) self.hosts = hosts self.cmd = cmd self.results = {} def runCmd(self, host): cmd = BASE_CMD % {'cmd': self.cmd, 'host': host, 'key': KEY_FILE, 'slice_name': SLICE_NAME, 'options': " ".join(["-o %s=%s" % (o,v) for o,v in SSH_OPTIONS.items()])} p = os.popen(cmd) output = p.read().strip() if output: output = output.split('\n')[0] # only get the first line # keep the comment for the keys # output = change_key_comment(output, host) return output return None def run(self): for host in self.hosts: self.results[host] = self.runCmd(host) def distributeHosts(num_hosts): dist_list = [] host_per_thread = num_hosts / THREAD_COUNT rest = num_hosts % THREAD_COUNT for i in range(THREAD_COUNT): c = host_per_thread if rest: c += 1 rest -= 1 dist_list.append(c) return dist_list def main(plchost, slice_name, print_report=lambda:None): ple = getAPI(plchost) slice_nodes = [] try: node_ids = ple.GetSlices(slice_name)[0]['node_ids'] slice_nodes = [n['hostname'] for n in ple.GetNodes(node_ids)] except IndexError: print "Can not get nodes for slice: %s" % slice_name sys.exit(1) dist_list = distributeHosts(len(slice_nodes)) thread_list = [] index = 0 cmd = "cat /home/%s/.ssh/id_rsa.pub" % slice_name print "Please wait gathering public keys from %d PlanetLab nodes. This may take a while..." % len(slice_nodes) for i in range(THREAD_COUNT): current = Command(cmd, slice_nodes[index:index+dist_list[i]]) index += dist_list[i] thread_list.append(current) current.start() for i in thread_list: i.join() results = {} for i in thread_list: results.update(i.results) print_report(results) if __name__ == "__main__": def save_report(results, fname="slice_keys"): ok = 0 f = open(fname, "w") for node in results.keys(): if results[node]: f.write("%s\n" % results[node]) ok += 1 f.close() print "Could get keys from %d of %d nodes" % (ok, len(results)) print "Please see %s file." % fname def get_option(msg, default): ret = raw_input("%s [%s] : " % (msg, default)) if not ret: ret = default return ret PLC_HOST=get_option("Please enter your PLC host", PLC_HOST) SLICE_NAME=get_option("Please enter your slice name", SLICE_NAME) KEY_FILE=get_option("Please enter the path of your key", KEY_FILE) main(PLC_HOST, SLICE_NAME, print_report=save_report)