seminars.fb

from asyncio import run, gather, sleep
from contextlib import asynccontextmanager

from collections import namedtuple
Connection = namedtuple('Connection', 'hostname')
@asynccontextmanager
async def connect_password(hostname, *, fail=False):
    try:
        print(f'connect to {hostname} with password')
        if fail:
            raise ValueError('failed')
        yield Connection(hostname)
    except Exception as e:
        print(f'failed to connect to {hostname} (pw)')
        raise
    else:
        print(f'disconnect from {hostname} (pw)')

@asynccontextmanager
async def connect_pubkey(hostname, *, fail=False):
    try:
        print(f'connect to {hostname} with pubkey')
        if fail:
            raise ValueError('failed')
        yield Connection(hostname)
    except Exception as e:
        print(f'failed to connect to {hostname} (pk)')
        raise
    else:
        print(f'disconnect from {hostname} (pk)')

@asynccontextmanager
async def connect(hostname, *, fail_with_pk, fail_with_pw):
    try:
        async with connect_pubkey(hostname, fail=fail_with_pk) as conn:
            yield conn
    except ValueError as e1:
        try:
            async with connect_password(hostname, fail=fail_with_pw) as conn:
                yield conn
        except ValueError as e2:
            yield None
            # raise ValueError('failed to connect') from e2

async def task(hostname, *commands, **failures):
    async with connect(hostname, **failures) as conn:
        if conn is not None:
            for cmd in commands:
                print(f'\trunning {cmd} on {conn}')
                await sleep(0)

FailModes = namedtuple('FailureModes', 'fail_with_pk fail_with_pw')
async def main():
    devices = {
        'rsw2': FailModes(False, False),
        'fsw1': FailModes(True, True),
        'ssw3': FailModes(False, True),
    }
    commands = 'ls', 'hostname'
    tasks = [task(hn, *commands, **fs._asdict()) for hn, fs in devices.items()]
    await gather(*tasks)

run(main())