#!/usr/bin/env python3 # ***************************************************************************** # Copyright (c) 2015-2024 by the authors, see LICENSE # # This program is free software; you can redistribute it and/or modify it under # the terms of the GNU General Public License as published by the Free Software # Foundation; either version 2 of the License, or (at your option) any later # version. # # This program is distributed in the hope that it will be useful, but WITHOUT # ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS # FOR A PARTICULAR PURPOSE. See the GNU General Public License for more # details. # # You should have received a copy of the GNU General Public License along with # this program; if not, write to the Free Software Foundation, Inc., # 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Module authors: # Alexander Lenz # Markus Zolliker # # ***************************************************************************** import sys import argparse import socket from pathlib import Path # Add import path for inplace usage sys.path.insert(0, str(Path(__file__).absolute().parents[1])) from frappy.client.interactive import init, run, clientenv, interact from frappy.protocol.discovery import scan def parseArgv(argv): parser = argparse.ArgumentParser() parser.add_argument('-i', '--include', help='file to execute after connecting to the clients', metavar='file', type=Path, action='append', default=[]) parser.add_argument('-s', '--scan', help='hosts to scan for (-s subnet for all nodes in subnet)', action='append', default=[]) parser.add_argument('-o', '--only-execute', help='Do not go into interactive mode after executing files. \ Has no effect without --include.', action='store_true') parser.add_argument('node', help='Nodes the client should connect to.\n', metavar='host:port', nargs='*', type=str, default=[]) return parser.parse_args(argv) def own_ip(): s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) s.settimeout(0) try: # doesn't even have to be reachable s.connect(('10.254.254.254', 1)) return s.getsockname()[0] except Exception: return '127.0.0.1' finally: s.close() args = parseArgv(sys.argv[1:]) nodes = args.node hosts = args.scan if not nodes and not hosts: hosts = ['localhost'] if hosts: answers = [] for host in hosts: ans = scan() if host == 'subnet': # all in subnet answers.extend(ans) else: # filter by ip ip = socket.gethostbyname(host) if ip == '127.0.0.1': ip = own_ip() answers.extend(a for a in ans if a.address == ip) nodes.extend(f'{h.hostname}:{h.port}' for h in answers) success = init(*nodes) run_error = '' file_success = False try: for file in args.include: run(file) file_success = True except Exception as e: run_error = f'\n{clientenv.short_traceback()}' if success: if args.include and file_success and args.only_execute: print('skipping interactive mode') exit() interact(run_error)