class Resources(object): client = None provider = Argument("--provider", optional=True, help="For provider role") requestor = Argument("--requestor", optional=True, help="For requestor role") @doc("Show information on used resources") def show(self): return sync_wait(Resources.client.get_res_dirs_sizes(), timeout=None) @command(arguments=(provider, requestor), help="Clear provider / requestor resources") def clear(self, provider, requestor): if not provider and not requestor: return CommandResult(error="Target role was not specified " "(provider / requestor)") clear = Resources.client.clear_dir if provider: sync_wait(clear(DirectoryType.RECEIVED), timeout=None) return sync_wait(clear(DirectoryType.COMPUTED), timeout=None) elif requestor: return sync_wait(clear(DirectoryType.DISTRIBUTED), timeout=None)
def test_simplify_flag(self): argument = Argument('--flag', optional=True) simplified = argument.simplify() kw = simplified.kwargs self.assertNotIn('default', kw) self.assertNotIn('nargs', kw) self.assertEqual(kw['action'], 'store_true')
def test_extend(self): argument = Argument('arg', optional=True) extended = Argument.extend(argument, 'narg', optional=False, default=7) self.assertEqual(len(extended.args), 2) self.assertEqual(len(extended.kwargs), 2) self.assertFalse(extended.kwargs['optional']) self.assertEqual(extended.kwargs['default'], 7)
def test_simply_arg(self): argument = Argument('arg', optional=True) simplified = argument.simplify() kw = simplified.kwargs self.assertIn('default', kw) self.assertEqual(kw['nargs'], '?') self.assertIsNone(kw['default']) self.assertEqual(kw['action'], 'store')
class Environments(object): name = Argument('name', help="Environment name") table_headers = ['name', 'supported', 'active', 'performance', 'description'] sort = Argument( '--sort', choices=table_headers, optional=True, default=None, help="Sort environments" ) @command(argument=sort, help="Show environments") def show(self, sort): deferred = Environments.client.get_environments() result = sync_wait(deferred) or [] values = [] for env in result: values.append([ env['id'], str(env['supported']), str(env['accepted']), str(env['performance']), env['description'] ]) return CommandResult.to_tabular(Environments.table_headers, values, sort=sort) @command(argument=name, help="Enable environment") def enable(self, name): deferred = Environments.client.enable_environment(name) return sync_wait(deferred) @command(argument=name, help="Disable environment") def disable(self, name): deferred = Environments.client.disable_environment(name) return sync_wait(deferred) @command(argument=name, help="Recount performance for an environment") def recount(self, name): deferred = Environments.client.run_benchmark(name) return sync_wait(deferred, timeout=1800)
class Subtasks(object): client = None subtask_id = Argument('subtask_id', help="Subtask identifier") @command(argument=subtask_id, help="Show subtask details") def show(self, subtask_id): deferred = Subtasks.client.get_subtask(subtask_id) return sync_wait(deferred) @command(argument=subtask_id, help="Restart a subtask") def restart(self, subtask_id): deferred = Subtasks.client.restart_subtask(subtask_id) return sync_wait(deferred)
class Debug(object): client = None vargs = Argument('vargs', vargs=True, help='RPC call parameters') @command(arguments=(vargs, ), help="Debug RPC calls") def rpc(self, vargs): vargs = list(vargs) alias = vargs.pop(0) status = sync_wait(self.client._call(alias, *vargs)) # noqa pylint: disable=protected-access return CommandResult(status) @command(help="Dump uri to procedure mapping") def exposed_procedures(self): result = sync_wait(self.client._call('sys.exposed_procedures')) # noqa pylint: disable=protected-access return result
class Network(object): client = None node_table_headers = ['ip', 'port', 'id', 'name'] ip_arg = Argument('ip', help='Remote IP address') port_arg = Argument('port', help='Remote TCP port') full_table = Argument('--full', optional=True, help="Show full table contents") sort_nodes = Argument('--sort', choices=node_table_headers, optional=True, help="Sort nodes") @doc("Show client status") def status(self): deferred = Network.client.connection_status() status = sync_wait(deferred) or "unknown" return status @command(arguments=(ip_arg, port_arg), help="Connect to a node") def connect(self, ip, port): try: sa = SocketAddress(ip, int(port)) Network.client.connect((sa.address, sa.port)) except Exception as exc: return CommandResult( error="Cannot connect to {}:{}: {}".format(ip, port, exc)) @command(arguments=(sort_nodes, full_table), help="Show connected nodes") def show(self, sort, full): deferred = Network.client.get_connected_peers() peers = sync_wait(deferred) or [] return self.__peers(peers, sort, full) @command(arguments=(sort_nodes, full_table), help="Show known nodes") def dht(self, sort, full): deferred = Network.client.get_known_peers() peers = sync_wait(deferred) or [] return self.__peers(peers, sort, full) @staticmethod def __peers(peers, sort, full): values = [] for peer in peers: values.append([ str(peer['address']), str(peer['port']), Network.__key_id(peer['key_id'], full), unicode(peer['node_name']) ]) return CommandResult.to_tabular(Network.node_table_headers, values, sort=sort) @staticmethod def __key_id(key_id, full=False): if full: return key_id return key_id[:16] + "..." + key_id[-16:]
class Account: client = None amount_arg = Argument('amount', help='Amount to withdraw, eg 1.45') address_arg = Argument('destination', help='Address to send the funds to') currency_arg = Argument('currency', help='ETH or GNT') @command(help="Display account & financial info") def info(self) -> Dict[str, Any]: # pylint: disable=no-self-use client = Account.client node = sync_wait(client.get_node()) node_key = node['key'] computing_trust = sync_wait(client.get_computing_trust(node_key)) requesting_trust = sync_wait(client.get_requesting_trust(node_key)) payment_address = sync_wait(client.get_payment_address()) balance = sync_wait(client.get_balance()) gnt_available = int(balance['av_gnt']) gnt_nonconverted = int(balance['gnt_nonconverted']) eth_balance = int(balance['eth']) gnt_locked = int(balance['gnt_lock']) eth_locked = int(balance['eth_lock']) deposit_balance = sync_wait(client.get_deposit_balance()) return dict(node_name=node['node_name'], Golem_ID=node_key, requestor_reputation=int(requesting_trust * 100), provider_reputation=int(computing_trust * 100), finances=dict( eth_address=payment_address, eth_available=_fmt(eth_balance, unit="ETH"), eth_locked=_fmt(eth_locked, unit="ETH"), gnt_available=_fmt(gnt_available), gnt_locked=_fmt(gnt_locked), gnt_unadopted=_fmt(gnt_nonconverted), deposit_balance=_fmt_deposit(deposit_balance), )) @command(help="Unlock account, will prompt for your password") def unlock(self) -> str: # pylint: disable=no-self-use from twisted.internet import threads client = Account.client is_account_unlocked: bool = sync_wait(client.is_account_unlocked()) if is_account_unlocked: return "Account already unlocked" has_key = sync_wait(client.key_exists()) if not has_key: print("No account found, generate one by setting a password") else: print("Unlock your account to start golem") print("This command will time out in 30 seconds.") defer_getpass = threads.deferToThread(getpass.getpass, 'Password:'******'' result = zxcvbn.zxcvbn(pswd, user_inputs=['Golem', account_name]) # print(result['score']) if result['score'] < MIN_SCORE: return "Password is not strong enough. " \ "Please use capitals, numbers and special characters." # Confirm the password confirm = getpass.getpass('Confirm password:') if confirm != pswd: return "Password and confirmation do not match." print("Generating keys, this can take up to 10 minutes...") success = sync_wait(client.set_password(pswd), timeout=15 * 60) if not success: return "Incorrect password" return "Account unlock success" @command(arguments=(address_arg, amount_arg, currency_arg), help=("Withdraw GNT/ETH\n" "(withdrawals are not available for the testnet)")) def withdraw( # pylint: disable=no-self-use self, destination, amount, currency) -> str: amount = str(int(Decimal(amount) * denoms.ether)) return sync_wait(Account.client.withdraw(amount, destination, currency)) @command(help="Trigger graceful shutdown of your golem") def shutdown(self) -> str: # pylint: disable=no-self-use result = sync_wait(Account.client.graceful_shutdown()) readable_result = repr(ShutdownResponse(result)) return "Graceful shutdown triggered result: {}".format(readable_result)
class Settings(object): BOOL_CONVERTIBLE_KEY_PREFIXES = [ 'accept_', 'debug_', 'use_', 'enable_', 'in_shutdown', 'net_masking_enabled' ] client = None basic = Argument('--basic', optional=True, default=False, help="Show basic settings") provider = Argument('--provider', optional=True, default=False, help="Show provider settings") requestor = Argument('--requestor', optional=True, default=False, help="Show requestor settings") settings = { 'node_name': Setting('Node name', 'non-empty string', lambda x: x if isinstance(x, str) else None, lambda x: x and len(x) > 0), 'accept_tasks': Setting( 'Accept tasks', 'flag {0, 1}', lambda x: bool(int(x)), lambda x: x in [True, False], ), 'max_resource_size': Setting('Maximal resource size', 'int > 0 [kB]', int, lambda x: x > 0), 'getting_tasks_interval': Setting('Interval between task requests', 'int > 0 [s]', int, lambda x: x > 0), 'getting_peers_interval': Setting('Interval between peer requests', 'int > 0 [s]', int, lambda x: x > 0), 'task_session_timeout': Setting('Task session timeout', 'int > 0 [s]', int, lambda x: x > 0), 'p2p_session_timeout': Setting('P2P session timeout', 'int > 0 [s]', int, lambda x: x > 0), 'requesting_trust': Setting('Minimal requestor trust', 'float [-1., 1.]', float, lambda x: -1. <= x <= 1.), 'computing_trust': Setting('Minimal provider trust', 'float [-1., 1.]', float, lambda x: -1. <= x <= 1.), 'min_price': Setting('Min GNT/h price (provider)', 'float >= 0', lambda x: float(x) * denoms.ether, lambda x: float(x) >= 0, formatter=lambda x: float(x) / denoms.ether), 'max_price': Setting('Max GNT/h price (requestor)', 'float >= 0', lambda x: float(x) * denoms.ether, lambda x: float(x) >= 0, formatter=lambda x: float(x) / denoms.ether), 'use_ipv6': Setting( 'Use IPv6', 'flag {0, 1}', lambda x: bool(int(x)), lambda x: x in [True, False], ), 'opt_peer_num': Setting('Number of peers to keep', 'int > 0', int, lambda x: x > 0), 'send_pings': Setting( 'Send ping messages to peers', 'flag {0, 1}', lambda x: bool(int(x)), lambda x: x in [True, False], ), 'pings_interval': Setting('Interval between ping messages', 'int > 0', int, lambda x: x > 0), 'max_memory_size': Setting('Max memory size', '{} > int >= {} [kB]'.format(_virtual_mem, MIN_MEMORY_SIZE), int, lambda x: _virtual_mem > x >= MIN_MEMORY_SIZE), 'num_cores': Setting('Number of CPU cores to use', '{} >= int >= 1'.format(_cpu_count), int, lambda x: _cpu_count >= x >= 1), 'enable_talkback': Setting( 'Enable error reporting with talkback service', 'flag {0, 1}', lambda x: bool(int(x)), lambda x: x in [True, False], ) } settings_message = '\n'.join([ '\t{:32} {:>32}\t{}'.format(k, s.type, s.help) for k, s in settings.items() ]) invalid_key_message =\ """Invalid key Available settings:\n """ + settings_message basic_settings = [ 'use_ipv6', 'opt_peer_num', 'getting_peers_interval', 'p2p_session_timeout', 'send_pings', 'pings_interval' ] requestor_settings = ['max_price', 'computing_trust'] key = Argument('key', help='Setting name', optional=True) value = Argument('value', help='Setting value', optional=True) @command(arguments=(basic, provider, requestor), help="Show current settings") def show(self, basic, provider, requestor): def fmt(k, v): if k in self.settings: return self.settings[k].format(v) return v def convert(k, v): if k in self.settings: return self.settings[k].converter(v) elif any( k.startswith(prefix) for prefix in Settings.BOOL_CONVERTIBLE_KEY_PREFIXES): return bool(v) return v config = sync_wait(Settings.client.get_settings()) config = {k: convert(k, v) for k, v in config.items()} config = {k: fmt(k, v) for k, v in config.items()} if not (basic ^ provider) and not (provider ^ requestor): return config result = dict() if basic: result.update({ k: v for k, v in config.items() if k in Settings.basic_settings }) if requestor: result.update({ k: v for k, v in config.items() if k in Settings.requestor_settings }) if provider: result.update({ k: v for k, v in config.items() if k not in Settings.basic_settings and k not in Settings.requestor_settings }) return result @command(arguments=(key, value), help="Change settings") def set(self, key, value): if not key or key not in Settings.settings: return CommandResult(error=Settings.invalid_key_message) setting = Settings.settings[key] try: value = setting.converter(value) if not setting.validator(value): raise Exception(value) except Exception as exc: return CommandResult( error="Invalid value for {} " "(should be {}): {}".format(key, setting.type, exc)) else: return sync_wait(Settings.client.update_setting(key, value))
class Tasks(object): client = None task_table_headers = [ 'id', 'remaining', 'subtasks', 'status', 'completion' ] subtask_table_headers = ['node', 'id', 'remaining', 'status', 'completion'] id_req = Argument('id', help="Task identifier") id_opt = Argument.extend(id_req, optional=True) sort_task = Argument('--sort', choices=task_table_headers, optional=True, help="Sort tasks") sort_subtask = Argument('--sort', choices=subtask_table_headers, optional=True, help="Sort subtasks") file_name = Argument('file_name', help="Task file") skip_test = Argument('--skip-test', default=False, help="Skip task testing phase") application_logic = None @command(arguments=(id_opt, sort_task), help="Show task details") def show(self, id, sort): deferred = Tasks.client.get_tasks(id) result = sync_wait(deferred) if not id: values = [] for task in result or []: values.append([ task['id'], str(task['time_remaining']), str(task['subtasks']), task['status'], Tasks.__progress_str(task['progress']) ]) return CommandResult.to_tabular(Tasks.task_table_headers, values, sort=sort) if isinstance(result, dict): result['progress'] = Tasks.__progress_str(result['progress']) return result @command(arguments=(id_req, sort_subtask), help="Show sub-tasks") def subtasks(self, id, sort): values = [] deferred = Tasks.client.get_subtasks(id) result = sync_wait(deferred) if isinstance(result, list): for subtask in result: values.append([ subtask['node_name'], subtask['subtask_id'], str(subtask['time_remaining']), subtask['status'], Tasks.__progress_str(subtask['progress']) ]) return CommandResult.to_tabular(Tasks.subtask_table_headers, values, sort=sort) @command(arguments=(file_name, skip_test), help="Load a task from file") def load(self, file_name, skip_test): try: definition = self.__read_from_file(file_name) except Exception as exc: return CommandResult( error="Error reading task from file '{}': {}".format( file_name, exc)) if hasattr(definition, 'resources'): definition.resources = { os.path.normpath(res) for res in definition.resources } datadir = sync_wait(Tasks.client.get_datadir()) # TODO: unify GUI and CLI logic rendering_task_state = TaskDesc() rendering_task_state.definition = definition rendering_task_state.task_state.status = TaskStatus.starting if not Tasks.application_logic: Tasks.application_logic = CommandAppLogic.instantiate( Tasks.client, datadir) task_builder = Tasks.application_logic.get_builder( rendering_task_state) task = Task.build_task(task_builder) rendering_task_state.task_state.outputs = task.get_output_names() rendering_task_state.task_state.total_subtasks = task.get_total_tasks() task.header.task_id = str(uuid.uuid4()) if not skip_test: test_task = Task.build_task(task_builder) test_task.header.task_id = str(uuid.uuid4()) queue = Queue() TaskTester(test_task, datadir, success_callback=lambda *a, **kw: queue.put(True), error_callback=lambda *a, **kw: queue.put(a)).run() test_result = queue.get() if test_result is not True: return CommandResult( error="Test failed: {}".format(test_result)) task_dict = DictSerializer.dump(task) task_def = task_dict['task_definition'] task_def['resources'] = list(task_def.get('task_definition', [])) deferred = Tasks.client.create_task(task_dict) return sync_wait(deferred, timeout=1800) @command(argument=id_req, help="Restart a task") def restart(self, id): deferred = Tasks.client.restart_task(id) return sync_wait(deferred) @command(argument=id_req, help="Abort a task") def abort(self, id): deferred = Tasks.client.abort_task(id) return sync_wait(deferred) @command(argument=id_req, help="Delete a task") def delete(self, id): deferred = Tasks.client.delete_task(id) return sync_wait(deferred) @command(argument=id_req, help="Pause a task") def pause(self, id): deferred = Tasks.client.pause_task(id) return sync_wait(deferred) @command(argument=id_req, help="Resume a task") def resume(self, id): deferred = Tasks.client.resume_task(id) return sync_wait(deferred) @doc("Show statistics for tasks") def stats(self): deferred = Tasks.client.get_task_stats() return sync_wait(deferred) @staticmethod def __progress_str(progress): if progress is None: progress = 0 elif isinstance(progress, basestring) and progress.endswith('%'): return progress return '{:.2f} %'.format(progress * 100.0) @staticmethod def __read_from_file(file_name): with open(file_name) as task_file: return jsonpickle.loads(task_file.read())
class Environments(object): client: 'ClientProxy' name = Argument('name', help="Environment name") multiplier = Argument('multiplier', help='Multiplier; float value within range ' f'[{MinPerformanceMultiplier.MIN},' f' {MinPerformanceMultiplier.MAX}]') table_headers = [ 'name', 'supported', 'active', 'performance', 'min accept. perf.', 'description' ] sort = Argument('--sort', choices=table_headers, optional=True, default=None, help="Sort environments") @command(argument=sort, help="Show environments") def show(self, sort): deferred = Environments.client.get_environments() result = sync_wait(deferred) or [] values = [] for env in result: values.append([ env['id'], str(env['supported']), str(env['accepted']), str(env['performance']), str(env['min_accepted']), env['description'] ]) return CommandResult.to_tabular(Environments.table_headers, values, sort=sort) @command(argument=name, help="Enable environment") def enable(self, name): deferred = Environments.client.enable_environment(name) return sync_wait(deferred) @command(argument=name, help="Disable environment") def disable(self, name): deferred = Environments.client.disable_environment(name) return sync_wait(deferred) @command(argument=name, help="Recount performance for an environment") def recount(self, name): deferred = Environments.client.run_benchmark(name) return sync_wait(deferred, timeout=1800) @command(argument=multiplier, help="Sets accepted performance multiplier") def perf_mult_set(self, multiplier): return sync_wait( Environments.client._call( 'performance.multiplier.update', float(multiplier), ), timeout=3, ) @command(help="Gets accepted performance multiplier") def perf_mult(self): result = sync_wait( Environments.client._call('performance.multiplier'), timeout=3, ) return f'minimal performance multiplier is: {result}'
from ethereum.utils import denoms from golem.core.deferred import sync_wait from golem.interface.command import command, Argument, CommandResult incomes_table_headers = ['payer', 'status', 'value', 'block'] payments_table_headers = ['subtask', 'payee', 'status', 'value', 'fee'] sort_incomes = Argument( '--sort', choices=incomes_table_headers, optional=True, default=None, help="Sort incomes" ) sort_payments = Argument( '--sort', choices=payments_table_headers, optional=True, default=None, help="Sort payments" ) def __status(info): return unicode(info["status"]).replace(u"PaymentStatus.", u"") def __value(value): return u"{:.6f} GNT".format(value / denoms.ether)
from ethereum.utils import denoms from golem.core.common import to_unicode from golem.core.deferred import sync_wait from golem.interface.command import command, Argument, CommandResult incomes_table_headers = ['payer', 'status', 'value'] payments_table_headers = ['subtask', 'payee', 'status', 'value', 'fee'] deposit_payments_table_headers = ['tx', 'status', 'value', 'fee'] filterable_statuses = ['awaiting', 'confirmed'] sort_incomes = Argument('--sort', choices=incomes_table_headers, optional=True, default=None, help="Sort incomes") sort_payments = Argument('--sort', choices=payments_table_headers, optional=True, default=None, help="Sort payments") sort_deposit_payments = Argument( '--sort', choices=deposit_payments_table_headers, optional=True, default=None, help="Sort deposit payments", )
class Settings(object): client = None basic = Argument('--basic', optional=True, default=False, help="Show basic settings") provider = Argument('--provider', optional=True, default=False, help="Show provider settings") requestor = Argument('--requestor', optional=True, default=False, help="Show requestor settings") settings = { 'node_name': Setting('Node name', 'non-empty string', lambda x: unicode(x) if isinstance(x, basestring) else None, lambda x: x and len(x) > 0), 'accept_task': Setting('Accept tasks', 'int {0, 1}', _int, lambda x: x in [0, 1]), 'max_resource_size': Setting('Maximal resource size', 'int > 0 [kB]', _int, lambda x: x > 0), 'use_waiting_for_task_timeout': Setting('Use timeouts when waiting for tasks', 'int {0, 1}', _int, lambda x: x in [0, 1]), 'waiting_for_task_timeout': Setting('Timeout value to use when waiting for task', 'int > 0 [s]', _int, lambda x: x > 0), 'getting_tasks_interval': Setting('Interval between task requests', 'int > 0 [s]', _int, lambda x: x > 0), 'getting_peers_interval': Setting('Interval between peer requests', 'int > 0 [s]', _int, lambda x: x > 0), 'task_session_timeout': Setting('Task session timeout', 'int > 0 [s]', _int, lambda x: x > 0), 'p2p_session_timeout': Setting('P2P session timeout', 'int > 0 [s]', _int, lambda x: x > 0), 'requesting_trust': Setting('Minimal requestor trust', 'float [-1., 1.]', _float, lambda x: -1. <= x <= 1.), 'computing_trust': Setting('Minimal provider trust', 'float [-1., 1.]', _float, lambda x: -1. <= x <= 1.), 'min_price': Setting('Min GNT/h price (provider)', 'float >= 0', lambda x: float(x) * denoms.ether, lambda x: x >= 0), 'max_price': Setting('Max GNT/h price (requestor)', 'float >= 0', lambda x: float(x) * denoms.ether, lambda x: x >= 0), 'use_ipv6': Setting('Use IPv6', 'int {0, 1}', _int, lambda x: x in [0, 1]), 'opt_peer_num': Setting('Number of peers to keep', 'int > 0', _int, lambda x: x > 0), 'send_pings': Setting('Send ping messages to peers', 'int {0, 1}', _int, lambda x: x in [0, 1]), 'pings_interval': Setting('Interval between ping messages', 'int > 0', _int, lambda x: x > 0), 'max_memory_size': Setting('Max memory size', '{} > int >= {} [kB]'.format(_virtual_mem, MIN_MEMORY_SIZE), _int, lambda x: _virtual_mem > x >= MIN_MEMORY_SIZE), 'num_cores': Setting('Number of CPU cores to use', '{} >= int >= 1'.format(_cpu_count), _int, lambda x: _cpu_count >= x >= 1) } settings_message = '\n'.join([ '\t{:32} {:>32}\t{}'.format(k, s.type, s.help) for k, s in settings.iteritems() ]) invalid_key_message =\ """Invalid key Available settings:\n """ + settings_message basic_settings = [ 'use_ipv6', 'opt_peer_num', 'getting_peers_interval', 'p2p_session_timeout', 'send_pings', 'pings_interval' ] requestor_settings = ['max_price', 'computing_trust'] key = Argument('key', help='Setting name', optional=True) value = Argument('value', help='Setting value', optional=True) @command(arguments=(basic, provider, requestor), help="Show current settings") def show(self, basic, provider, requestor): config = sync_wait(Settings.client.get_settings()) if not (basic ^ provider) and not (provider ^ requestor): return config result = dict() if basic: result.update({ k: v for k, v in config.iteritems() if k in Settings.basic_settings }) if requestor: result.update({ k: v for k, v in config.iteritems() if k in Settings.requestor_settings }) if provider: result.update({ k: v for k, v in config.iteritems() if k not in Settings.basic_settings and k not in Settings.requestor_settings }) return result @command(arguments=(key, value), help="Change settings") def set(self, key, value): if not key or key not in Settings.settings: return CommandResult(error=Settings.invalid_key_message) setting = Settings.settings[key] try: value = setting.converter(value) if not setting.validator(value): raise Exception(value) except Exception as exc: return CommandResult( error="Invalid value for {} " "(should be {}): {}".format(key, setting.type, exc)) else: return sync_wait(Settings.client.update_setting(key, value))
class Network(object): client = None node_table_headers = ['ip', 'port', 'id', 'name'] ip_arg = Argument('ip', help='Remote IP address') port_arg = Argument('port_', help='Remote TCP port') node_id = Argument('node_id', help='ID of a node') full_table = Argument('--full', optional=True, help="Show full table contents") sort_nodes = Argument('--sort', choices=node_table_headers, optional=True, help="Sort nodes") @doc("Show client status") def status(self): deferred = Network.client.connection_status() status = sync_wait(deferred) return status['msg'] @command(arguments=(ip_arg, port_arg), help="Connect to a node") def connect(self, ip, port_): try: sa = SocketAddress(ip, int(port_)) Network.client.connect((sa.address, sa.port)) except Exception as exc: return CommandResult( error="Cannot connect to {}:{}: {}".format(ip, port_, exc)) @command(arguments=(sort_nodes, full_table), help="Show connected nodes") def show(self, sort, full): deferred = Network.client.get_connected_peers() peers = sync_wait(deferred) or [] return self.__peers(peers, sort, full) @command(arguments=(sort_nodes, full_table), help="Show known nodes") def dht(self, sort, full): deferred = Network.client.get_known_peers() peers = sync_wait(deferred) or [] return self.__peers(peers, sort, full) @command(argument=node_id, help="Block provider") def block(self, node_id): success, error = sync_wait(self.client.block_node(node_id)) if not success: return error @staticmethod def __peers(peers, sort, full): values = [] for p in peers: addr = Network.__one_of(p, 'address', 'pub_addr') port = Network.__one_of(p, 'port', 'p2p_pub_port', 'p2p_prv_port') key = Network.__one_of(p, 'key_id', 'key') values.append([ str(addr), str(port), Network.__key_id(key, full), str(p['node_name']), ]) return CommandResult.to_tabular(Network.node_table_headers, values, sort=sort) @staticmethod def __one_of(dictionary, *keys): for key in keys: value = dictionary.get(key) if value is not None: return value @staticmethod def __key_id(key_id, full=False): if full: return key_id return key_id[:16] + "..." + key_id[-16:]
class Tasks: client: 'ClientProxy' task_table_headers = [ 'id', 'ETA', 'subtasks_count', 'status', 'completion' ] subtask_table_headers = ['node', 'id', 'ETA', 'status', 'completion'] unsupport_reasons_table_headers = [ 'reason', 'no of tasks', 'avg for all tasks' ] id_req = Argument('id', help="Task identifier") id_opt = Argument.extend(id_req, optional=True) subtask_ids = Argument('subtask_ids', vargs=True, help="Subtask ids") sort_task = Argument('--sort', choices=task_table_headers, optional=True, help="Sort tasks") sort_subtask = Argument('--sort', choices=subtask_table_headers, optional=True, help="Sort subtasks") file_name = Argument('file_name', help="Task file") force_arg = Argument( 'force', help="Ignore warnings", default=False, optional=True, ) outfile = Argument( 'outfile', help="Output file", optional=True, ) current_task = Argument( 'current', help='Show only current tasks', optional=True, boolean=True, default=False, ) last_days = Argument('last_days', optional=True, default="0", help="Number of last days to compute statistics on") application_logic = None @command(arguments=(id_opt, sort_task, current_task), help="Show task details") def show(self, id, sort, current): deferred = Tasks.client.get_tasks(id) result = sync_wait(deferred) if not id: values = [] if current: result = [ t for t in result if TaskStatus(t['status']).is_active() ] for task in result: values.append([ task['id'], Tasks.__format_seconds(task['time_remaining']), str(task['subtasks_count']), task['status'], Tasks.__progress_str(task['progress']) ]) return CommandResult.to_tabular(Tasks.task_table_headers, values, sort=sort) if isinstance(result, dict): result['time_remaining'] = \ Tasks.__format_seconds(result['time_remaining']) result['progress'] = Tasks.__progress_str(result['progress']) return result @command(arguments=(id_req, sort_subtask), help="Show sub-tasks") def subtasks(self, id, sort): values = [] deferred = Tasks.client.get_subtasks(id) result = sync_wait(deferred) if result is None: return "No subtasks" if isinstance(result, list): for subtask in result: values.append([ subtask['node_name'], subtask['subtask_id'], Tasks.__format_seconds(subtask['time_remaining']), subtask['status'], Tasks.__progress_str(subtask['progress']) ]) return CommandResult.to_tabular(Tasks.subtask_table_headers, values, sort=sort) @command(arguments=( id_req, force_arg, ), help="Restart a task") def restart(self, id, force: bool = False): deferred = Tasks.client._call('comp.task.restart', id, force=force) # noqa pylint: disable=protected-access new_task_id, error = sync_wait(deferred) if error: return CommandResult(error=error) return new_task_id @command(arguments=( id_req, subtask_ids, force_arg, ), help="Restart given subtasks from a task") def restart_subtasks(self, id, subtask_ids, force: bool): deferred = Tasks.client._call( # pylint: disable=protected-access 'comp.task.restart_subtasks', id, subtask_ids, force=force, ) return sync_wait(deferred) @command(argument=id_req, help="Abort a task") def abort(self, id): deferred = Tasks.client.abort_task(id) return sync_wait(deferred) @command(argument=id_req, help="Delete a task") def delete(self, id): deferred = Tasks.client.delete_task(id) return sync_wait(deferred) @command(help="Deletes all tasks") def purge(self): deferred = Tasks.client.purge_tasks() return sync_wait(deferred) @command(arguments=( file_name, force_arg, ), help=""" Create a task from file. Note: no client-side validation is performed yet. This will change in the future """) def create(self, file_name: str, force: bool = False) -> Any: with open(file_name) as f: task_id, error = self.__create_from_json(f.read(), force=force) if error: if task_id: return CommandResult( error="task {} failed: {}".format(task_id, error)) return CommandResult(error=error) return task_id @command(arguments=(id_req, outfile), help="Dump an existing task") def dump(self, id: str, outfile: Optional[str]) -> None: task_dict = sync_wait(self.client.get_task(id)) self.__dump_dict(task_dict, outfile) @command(argument=outfile, help="Dump a task template") def template(self, outfile: Optional[str]) -> None: template = TaskDefinition() self.__dump_dict(template.to_dict(), outfile) @doc("Show statistics for tasks") def stats(self): deferred = Tasks.client.get_task_stats() return sync_wait(deferred) @command(argument=last_days, help="Show statistics for unsupported tasks") def unsupport(self, last_days): deferred = Tasks.client.get_unsupport_reasons(int(last_days)) result = sync_wait(deferred) values = [[r['reason'], r['ntasks'], r['avg']] for r in result] return CommandResult.to_tabular(Tasks.unsupport_reasons_table_headers, values) @staticmethod def __format_seconds(seconds: float) -> str: try: delta = timedelta(seconds=int(seconds)) return str(delta) except TypeError: return '???' @staticmethod def __dump_dict(dictionary: dict, outfile: Optional[str]) -> None: template_str = json.dumps(dictionary, indent=4) if outfile: with open(outfile, 'w') as dest: print(template_str, file=dest) else: print(template_str) @staticmethod def __progress_str(progress): if progress is None: progress = 0 elif isinstance(progress, str) and progress.endswith('%'): return progress return '{:.2f} %'.format(progress * 100.0) def __create_from_json(self, jsondata: str, **kwargs) \ -> Tuple[Optional[str], Optional[str]]: dictionary = json.loads(jsondata) # pylint: disable=protected-access deferred = Tasks.client._call('comp.task.create', dictionary, **kwargs) return sync_wait(deferred, CREATE_TASK_TIMEOUT)