Skip to the content.
Examples

Scenario

This example is to collect login-able accounts in 4 groups of hosts. In a host-group, hosts are collected one by one. Every host-group has multiple hosts. Host-groups are grouped into 2 top-groups.

The main thread would spawn 2 threads for every top-group. Then the thread of a top-group, spawns threads for every host-groups.

Every host group is run in an individual thread. One more to say is that before starting our data collection , we have to ssh to a bridge host.

image

file: example.spy

import tabulate
import threading

# define top-groups and host-groups
hostgroups = [
    [
        ['host11','hos12','host13','host14'],
        ['host21','hos22','host23','host24','joe@host25'],
    ],[
        ['host31','hos32','host33','host34'],
        ['host41','bill@hos42','host43'],
    ]]

username = 'jenifer'

# connect to bridge-host
$.connect(f'{username}@bridge-host')

$ hostname
print('connected to',$.stdout)

locker = threading.Lock()
def job(accounts,ret,pkey):
    global job, locker
    account = accounts.pop(0)
    $.connect(account,pkey=pkey)
    
    $cat /etc/passwd
    regularAccounts = {}
    nonShellKeywords = ('false','nologin','halt','shutdown','sync')
    for line in $.stdout.split('\n'):
        line = line.strip()
        if not line: continue
        elif line.startswith('#'): continue
        cols = line.split(':')
        shells = cols[-1].split('/')
        nonShell = False
        for keyword in nonShellKeywords:
            if keyword in shells:
                nonShell = True
                break
        if not nonShell: regularAccounts[cols[0]] = cols[-1]

    locker.acquire()
    ret[hostname] = regularAccounts
    locker.release()
    
    $.close()
    
    if len(accounts):
        job(accounts,ret,pkey)       

# in a top-group, every host group has its own thread
def topgroupjob(hostgroup,ret,pkey):
    groupthreads = []
    
    for hosts in hostgroup:
        accounts = [(username + x if x.find('@')==-1 else x ) for x in hosts]
        t = $.thread(target=job,args=(accounts,ret,pkey))
        groupthreads.append(t)
        t.start()
        
    for t in groupthreads:
        t.join()

pkey = $.pkey(f'/home/{username}/.ssh/id_rsa')
ret = {}
threads = []
# dispatch top group to every thread
for topgroup in hostgroups:
    t = $.thread(target=topgroupjob,args=(topgroup,ret,pkey))
    threads.append(t)
    t.start()

# wait for threads to complete
for t in threads:
    t.join()

# output
hostnames = list(ret.keys())
hostnames.sort()
headers = ('usernmae','shell')
for hostname in hostnames:
    usernames = list(ret[hostname].keys())
    usernames.sort()
    rows = []
    for username in usernames:
        rows.append((username,ret[hostname][username]))
    print(hostname)
    print('=' * 40)
    print(tabulate.tabulate(rows,headers=headers))
    print()

Execution

$sshscript blackip.spy
$sshscript blackip.spy --verbose
$sshscript blackip.spy --debug
$sshscript blackip.spy --verbose --debug