def registration(actions: List, validator_info: EntityResponse, health): value_to_set = None print("\n--------Registration-----\n") registration = [ x for x in validator_info.data_objects if x.type == 'PreparedValidatorRegistered' ] Helpers.print_coloured_line( f"Current registration status: {registration[0].registered}", bcolors.OKBLUE) ask_registration = input( Helpers.print_coloured_line( "\nEnter the new registration setting [true/false].Press enter if no change required ", bcolors.BOLD, return_string=True)) if ask_registration.lower() == "true" or ask_registration.lower( ) == "false": value_to_set = json.loads(ask_registration.lower()) actions.append(Action.set_validator_registeration(value_to_set)) return actions else: Helpers.print_coloured_line( "There are no changes to apply or user input is wrong", bcolors.WARNING) return actions
def construction_build(self, actions, print_response=False, ask_user=False): with core_api.ApiClient(self.system_config) as api_client: api_client = self.set_basic_auth(api_client, "admin", "admin") try: network_configuration: NetworkConfigurationResponse = self.network_configuration( ) key_list: KeyListResponse = self.key_list() operation_groups = ValidatorConfig.build_operations( actions, key_list, ask_user=ask_user) if len(operation_groups) == 0: return api = construction_api.ConstructionApi(api_client) build_request = ConstructionBuildRequest( network_identifier=network_configuration. network_identifier, fee_payer=key_list.public_keys[0].identifiers. account_entity_identifier, operation_groups=operation_groups) Helpers.print_request_body(build_request, "/construction/build") build: ConstructionBuildResponse = api.construction_build_post( build_request) return self.handle_response(build, print_response) except ApiException as e: Helpers.handleApiException(e)
def add_change_ownerid(actions: List, validator_info: EntityResponse): print("--------Change owner id-----\n") print( f"{bcolors.WARNING}\nPlease ensure you set owner account to a valid Radix account that you control (such " f"as one created with the Desktop Wallet), as this will also be where any validator fee emissions will be " f"credited. It is strongly advised to NOT use the Radix account of your node itself.{bcolors.ENDC} " ) owner = [ x for x in validator_info.data_objects if x.type == 'PreparedValidatorOwner' ] current_value = owner[0]["owner"]["address"] Helpers.print_coloured_line( f"\nCurrent settings for owner id={current_value}") owner_id = input( "\nEnter the new owner id or press Enter not to change:").strip() if owner_id != "": if owner_id != current_value: actions.append(Action.set_validator_owner(owner_id)) return actions Helpers.print_coloured_line( "Owner entered is same . So action will not be applied", bcolors.WARNING) return actions
def prometheus_metrics(self): with system_api.ApiClient(self.system_config) as api_client: api_client = self.set_basic_auth(api_client, "metrics", "metrics") try: api = default_api.DefaultApi(api_client) print(api.prometheus_metrics_get()) except ApiException as e: Helpers.handleApiException(e)
def version(self): with system_api.ApiClient(self.system_config) as api_client: api_client = self.set_basic_auth(api_client, "admin", "admin") try: api = default_api.DefaultApi(api_client) print(api.system_version_get()) except ApiException as e: Helpers.handleApiException(e)
def health(self, print_response=False): with system_api.ApiClient(self.system_config) as api_client: api_client = self.set_basic_auth(api_client, "admin", "admin") try: api = default_api.DefaultApi(api_client) health_response = api.system_health_get() if print_response: print(health_response) return health_response except ApiException as e: Helpers.handleApiException(e)
def mempool(self, print_response=False): with core_api.ApiClient(self.system_config) as api_client: api_client = self.set_basic_auth(api_client, "admin", "admin") try: api = mempool_api.MempoolApi(api_client) response: MempoolResponse = api.mempool_post( MempoolRequest(network_identifier=self. network_configuration().network_identifier)) return self.handle_response(response, print_response) except ApiException as e: Helpers.handleApiException(e)
def network_status(self, print_response=False): with core_api.ApiClient(self.system_config) as api_client: api_client = self.set_basic_auth(api_client, "admin", "admin") try: api = network_api.NetworkApi(api_client) response = api.network_status_post( NetworkStatusRequest( self.network_configuration().network_identifier)) return self.handle_response(response, print_response) except ApiException as e: Helpers.handleApiException(e)
def engine_configuration(self, print_response=False): with core_api.ApiClient(self.system_config) as api_client: api_client = self.set_basic_auth(api_client, "admin", "admin") try: api = engine_api.EngineApi(api_client) response: EngineConfigurationResponse = api.engine_configuration_post( EngineConfigurationRequest( self.network_configuration().network_identifier)) return self.handle_response(response, print_response) except ApiException as e: Helpers.handleApiException(e)
def setup_compose_file(composefileurl, file_location, enable_transactions=False): compose_file_name = composefileurl.rsplit('/', 1)[-1] if os.path.isfile(compose_file_name): backup_file_name = f"{Helpers.get_current_date_time()}_{compose_file_name}" print( f"Docker compose file {compose_file_name} exists. Backing it up as {backup_file_name}" ) run_shell_command(f"cp {compose_file_name} {backup_file_name}", shell=True) print(f"Downloading new compose file from {composefileurl}") req = requests.Request('GET', f'{composefileurl}') prepared = req.prepare() resp = Helpers.send_request(prepared, print_response=False) if not resp.ok: print(f" Errored downloading file {composefileurl}. Exitting ... ") sys.exit() composefile_yaml = yaml.safe_load(resp.content) # TODO AutoApprove prompt_external_db = input( "Do you want to configure data directory for the ledger [Y/n]?:") if Helpers.check_Yes(prompt_external_db): composefile_yaml = Docker.merge_external_db_config( composefile_yaml) def represent_none(self, _): return self.represent_scalar('tag:yaml.org,2002:null', '') yaml.add_representer(type(None), represent_none) network_id = Base.get_network_id() genesis_json_location = Base.path_to_genesis_json(network_id) composefile_yaml = Docker.merge_network_info(composefile_yaml, network_id, genesis_json_location) composefile_yaml = Docker.merge_keyfile_path(composefile_yaml, file_location) composefile_yaml = Docker.merge_transactions_env_var( composefile_yaml, "true" if enable_transactions else "false") if os.getenv(IMAGE_OVERRIDE, "False") in ("true", "yes"): composefile_yaml = Docker.merge_image_overrides(composefile_yaml) with open(compose_file_name, 'w') as f: yaml.dump(composefile_yaml, f, default_flow_style=False, explicit_start=True, allow_unicode=True)
def check_health(self): Helpers.print_coloured_line("Checking status of the node\n", bcolors.BOLD) health = self.health() if health["status"] != "UP": Helpers.print_coloured_line( f"Node status is {health['status']} Rerun the command once node is completely synced", bcolors.WARNING) proceed = input(print("Do you want to continue [Y/n]?")) if not Helpers.check_Yes(proceed): sys.exit() return health
def network_configuration(self, print_response=False): with core_api.ApiClient(self.system_config) as api_client: api_client = self.set_basic_auth(api_client, "admin", "admin") try: api = network_api.NetworkApi(api_client) response: NetworkConfigurationResponse = api.network_configuration_post( dict()) if print_response: print(response) return response except ApiException as e: Helpers.handleApiException(e)
def vote(self, print_response=False): with core_api.ApiClient(self.system_config) as api_client: api_client = self.set_basic_auth(api_client, "superadmin", "superadmin") try: api = key_api.KeyApi(api_client) request = UpdateVoteRequest( network_identifier=self.network_configuration( ).network_identifier) Helpers.print_request_body(request, "/key/vote") response: UpdateVoteResponse = api.key_vote_post(request) return self.handle_response(response, print_response) except ApiException as e: Helpers.handleApiException(e)
def entity(self, entity_identifier, print_response=False): with core_api.ApiClient(self.system_config) as api_client: api_client = self.set_basic_auth(api_client, "admin", "admin") try: api = entity_api.EntityApi(api_client) entityRequest = EntityRequest( network_identifier=self.network_configuration( ).network_identifier, entity_identifier=entity_identifier) Helpers.print_request_body(entityRequest, "/entity") response: EntityResponse = api.entity_post(entityRequest) return self.handle_response(response, print_response) except ApiException as e: Helpers.handleApiException(e)
def update_validator_config(args): health = DefaultApiHelper(verify_ssl=False).check_health() core_api_helper = CoreApiHelper(verify_ssl=False) key_list_response: KeyListResponse = core_api_helper.key_list() validator_info: EntityResponse = core_api_helper.entity( key_list_response.public_keys[0].identifiers. validator_entity_identifier) actions = [] actions = ValidatorConfig.registration(actions, validator_info, health) actions = ValidatorConfig.validator_metadata(actions, validator_info, health) actions = ValidatorConfig.add_validation_fee(actions, validator_info) actions = ValidatorConfig.setup_update_delegation(actions, validator_info) actions = ValidatorConfig.add_change_ownerid(actions, validator_info) build_response: ConstructionBuildResponse = core_api_helper.construction_build( actions, ask_user=True) if build_response: signed_transaction: KeySignResponse = core_api_helper.key_sign( build_response.unsigned_transaction) core_api_helper.construction_submit( signed_transaction.signed_transaction, print_response=True) if health['fork_vote_status'] == 'VOTE_REQUIRED': print("\n------Candidate fork detected------") engine_configuration = core_api_helper.engine_configuration() print_vote_and_fork_info(health, engine_configuration) should_vote = input( f"Do you want to signal the readiness for {core_api_helper.engine_configuration().forks[-1]['name']} now? [Y/n]{bcolors.ENDC}" ) if Helpers.check_Yes(should_vote): core_api_helper.vote(print_response=True)
def construction_submit(self, signed_transaction, print_response=False): with core_api.ApiClient(self.system_config) as api_client: api_client = self.set_basic_auth(api_client, "admin", "admin") try: api = construction_api.ConstructionApi(api_client) network_configuration: NetworkConfigurationResponse = self.network_configuration( ) request = ConstructionSubmitRequest( network_identifier=network_configuration. network_identifier, signed_transaction=signed_transaction) Helpers.print_request_body(request, "/construction/submit") response = api.construction_submit_post(request) return self.handle_response(response, print_response) except ApiException as e: Helpers.handleApiException(e)
def retract_candidate_fork_readiness_signal(args): core_api_helper = CoreApiHelper(False) should_vote = input( f"This action will retract your candidate fork readiness signal (if there was one), continue? [Y/n]{bcolors.ENDC}" ) if Helpers.check_Yes(should_vote): core_api_helper.withdraw_vote(print_response=True)
def setup_node_optimisation_config(version): check_ansible = run_shell_command(f"pip list | grep ansible", shell=True, fail_on_error=False) import subprocess user = subprocess.check_output('whoami', shell=True).strip() if check_ansible.returncode != 0: print( f"Ansible not found for the user {user.decode('utf-8')}. Installing ansible now" ) check_pip = run_shell_command("pip -V ", shell=True, fail_on_error=False) if check_pip.returncode != 0: print(f"Pip is not installed. Installing pip now") run_shell_command('sudo apt install python3-pip', shell=True) run_shell_command(f"pip install --user ansible==2.10.0", shell=True) print(""" ---------------------------------------------------------------------------------------- Ansible installed successfully. You need exit shell and login back""" ) sys.exit() ansible_dir = f'https://raw.githubusercontent.com/radixdlt/node-runner/{version}/node-runner-cli' print(f"Downloading artifacts from {ansible_dir}\n") Base.download_ansible_file(ansible_dir, 'ansible/project/provision.yml') ask_setup_limits = input \ ("Do you want to setup ulimits [Y/n]?:") setup_limits = "true" if Helpers.check_Yes( ask_setup_limits) else "false" run_shell_command( f"ansible-playbook ansible/project/provision.yml -e setup_limits={setup_limits}", shell=True) ask_setup_swap = input \ ("Do you want to setup swap space [Y/n]?:") if Helpers.check_Yes(ask_setup_swap): setup_swap = "true" ask_swap_size = input \ ("Enter swap size in GB. Example - 1G or 3G or 8G ?:") run_shell_command( f"ansible-playbook ansible/project/provision.yml -e setup_swap={setup_swap} -e swap_size={ask_swap_size}", shell=True) else: setup_swap = "false"
def validator_metadata(actions: List, validator_info: EntityResponse, health): print("\n--------Update validator meta info-----\n") validatorMetadata = [ x for x in validator_info.data_objects if x.type == 'ValidatorMetadata' ] Helpers.print_coloured_line( f"Current name: {validatorMetadata[0]['name']}", bcolors.OKBLUE) Helpers.print_coloured_line( f"Current url: {validatorMetadata[0]['url']}", bcolors.OKBLUE) ask_add_or_change_info = input( "\nDo you want add/change the validator name and info url [Y/n]?") if Helpers.check_Yes(ask_add_or_change_info): validator_name = input( Helpers.print_coloured_line( f"Enter the Name of your validator to be updated:", bcolors.OKBLUE, return_string=True)) validator_url = input( Helpers.print_coloured_line( f"Enter Info URL of your validator to be updated:", bcolors.OKBLUE, return_string=True)) actions.append( Action.set_validator_metadata(validator_name, validator_url)) return actions return actions
def key_sign(self, unsigned_transaction, print_response=False): with core_api.ApiClient(self.system_config) as api_client: api_client = self.set_basic_auth(api_client, "superadmin", "superadmin") try: network_configuration: NetworkConfigurationResponse = self.network_configuration( ) key_list: KeyListResponse = self.key_list() api = key_api.KeyApi(api_client) request = KeySignRequest( network_identifier=network_configuration. network_identifier, public_key=key_list.public_keys[0].public_key, unsigned_transaction=unsigned_transaction) Helpers.print_request_body(request, "/key/sign") response = api.key_sign_post(request) return self.handle_response(response, print_response) except ApiException as e: Helpers.handleApiException(e)
def backup_file(filepath, filename, backup_time): if os.path.isfile(f"{filepath}/{filename}"): # TODO AutoApprove backup_yes = input( f"{filename} file exists. Do you want to back up [Y/n]:") if Helpers.check_Yes(backup_yes): Path(f"{backup_time}").mkdir(parents=True, exist_ok=True) run_shell_command( f"cp {filepath}/{filename} {backup_time}/{filename}", shell=True)
def add_validation_fee(actions: List, validator_info: EntityResponse): print("\n--------Validator fees-----\n") print( f"{bcolors.WARNING}\nValidator fee may be decreased at any time, but increasing it incurs a delay of " f"approx. 2 weeks. Please set it carefully{bcolors.ENDC}") validator_fee = [ x for x in validator_info.data_objects if x.type == 'PreparedValidatorFee' ] Helpers.print_coloured_line( f"Current validator fees are {int(validator_fee[0]['fee']) / 100}", bcolors.OKBLUE) ask_validator_fee_setup = input( "Do you want to setup or update validator fees [Y/n]?:") if Helpers.check_Yes(ask_validator_fee_setup): validatorFee = int(Helpers.check_validatorFee_input() * 100) actions.append(Action.set_validator_fee(validatorFee)) return actions return actions
def setup_nginx_config(nginx_config_location_Url, node_type, nginx_etc_dir, backup_time): SystemD.install_nginx() if node_type == "archivenode": conf_file = 'nginx-archive.conf' elif node_type == "fullnode": conf_file = 'nginx-fullnode.conf' else: print( f"Node type - {node_type} specificed should be either archivenode or fullnode" ) sys.exit() backup_yes = input( "Do you want to backup existing nginx config [Y/n]?:") if Helpers.check_Yes(backup_yes): Path(f"{backup_time}/nginx-config").mkdir(parents=True, exist_ok=True) run_shell_command( f"sudo cp -r {nginx_etc_dir} {backup_time}/nginx-config", shell=True) # TODO AutoApprove continue_nginx = input( "Do you want to continue with nginx setup [Y/n]?:") if Helpers.check_Yes(continue_nginx): run_shell_command([ 'wget', '--no-check-certificate', '-O', 'radixdlt-nginx.zip', nginx_config_location_Url ]) run_shell_command( f'sudo unzip radixdlt-nginx.zip -d {nginx_etc_dir}', shell=True) run_shell_command( f'sudo mv {nginx_etc_dir}/{conf_file} /etc/nginx/nginx.conf', shell=True) run_shell_command(f'sudo mkdir -p /var/cache/nginx/radixdlt-hot', shell=True) return True else: return False
def download_ansible_file(ansible_dir, file): req = requests.Request('GET', f'{ansible_dir}/{file}') prepared = req.prepare() resp = Helpers.send_request(prepared, print_response=False) if not resp.ok: print( f"{resp.status_code} error retrieving ansible playbook.. Existing the command..." ) sys.exit() directory = file.rsplit('/', 1)[0] Path(directory).mkdir(parents=True, exist_ok=True) with open(file, 'wb') as f: f.write(resp.content)
def ask_permission_build(operation_groups): print( f"{bcolors.WARNING}\nAbout to update node with following operations{bcolors.ENDC}" ) print(f"") print(f"{bcolors.BOLD}{print(operation_groups)}{bcolors.ENDC}") submit_changes = input( f"{bcolors.BOLD}\nDo you want to continue [Y/n]{bcolors.ENDC}") if Helpers.check_Yes(submit_changes) and len(operation_groups) != 0: return operation_groups else: print( f"{bcolors.WARNING}Changes were not submitted.{bcolors.ENDC} or there are no actions to submit" ) return operation_groups
def create_ssl_certs(secrets_dir): SystemD.make_nginx_secrets_directory() if os.path.isfile(f'{secrets_dir}/server.key') and os.path.isfile( f'{secrets_dir}/server.pem'): print( f"Files {secrets_dir}/server.key and os.path.isfile(f'{secrets_dir}/server.pem already exists" ) answer = input("Do you want to regenerate y/n :") if Helpers.check_Yes(answer): run_shell_command(f""" sudo openssl req -nodes -new -x509 -nodes -subj '/CN=localhost' \ -keyout "{secrets_dir}/server.key" \ -out "{secrets_dir}/server.pem" """, shell=True) else: run_shell_command(f""" sudo openssl req -nodes -new -x509 -nodes -subj '/CN=localhost' \ -keyout "{secrets_dir}/server.key" \ -out "{secrets_dir}/server.pem" """, shell=True) if os.path.isfile(f'{secrets_dir}/dhparam.pem'): print(f"File {secrets_dir}/dhparam.pem already exists") answer = input("Do you want to regenerate y/n :") if Helpers.check_Yes(answer): run_shell_command( f"sudo openssl dhparam -out {secrets_dir}/dhparam.pem 4096", shell=True) else: print("Generating a dhparam.pem file") run_shell_command( f"sudo openssl dhparam -out {secrets_dir}/dhparam.pem 4096", shell=True)
def latest_release(repo_name="radixdlt/radixdlt"): req = requests.Request( 'GET', f'https://api.github.com/repos/{repo_name}/releases/latest') prepared = req.prepare() prepared.headers['Content-Type'] = 'application/json' prepared.headers['user-agent'] = 'radixnode-cli' resp = Helpers.send_request(prepared, print_response=False) if not resp.ok: print( "Failed to get latest release from github. Exitting the command..." ) sys.exit() json_response = json.loads(resp.content) return json_response["tag_name"]
def download_binaries(binarylocationUrl, node_dir, node_version): run_shell_command([ 'wget', '--no-check-certificate', '-O', 'radixdlt-dist.zip', binarylocationUrl ]) run_shell_command('unzip radixdlt-dist.zip', shell=True) run_shell_command(f'mkdir -p {node_dir}/{node_version}', shell=True) if os.listdir(f'{node_dir}/{node_version}'): print(f"Directory {node_dir}/{node_version} is not empty") okay = input("Should the directory be removed [Y/n]?:") if Helpers.check_Yes(okay): run_shell_command(f"rm -rf {node_dir}/{node_version}/*", shell=True) unzipped_folder_name = os.getenv(UNZIPPED_NODE_DIST_FOLDER, f"radixdlt-{node_version}") run_shell_command( f'mv {unzipped_folder_name}/* {node_dir}/{node_version}', shell=True)
def setup(args): release = latest_release() if args.nodetype == "archivenode": Helpers.archivenode_deprecate_message() composefileurl = os.getenv( COMPOSE_FILE_OVERIDE, f"https://raw.githubusercontent.com/radixdlt/node-runner/{cli_version()}/node-runner-cli/release_ymls/radix-{args.nodetype}-compose.yml" ) print( f"Going to setup node type {args.nodetype} from location {composefileurl}.\n" ) # TODO autoapprove continue_setup = input("Do you want to continue [Y/n]?:") if not Helpers.check_Yes(continue_setup): print(" Quitting ....") sys.exit() keystore_password, file_location = Base.generatekey( keyfile_path=Helpers.get_keyfile_path(), keygen_tag=release) Docker.setup_compose_file(composefileurl, file_location, args.enabletransactions) trustednode_ip = Helpers.parse_trustednode(args.trustednode) compose_file_name = composefileurl.rsplit('/', 1)[-1] action = "update" if args.update else "start" print( f"About to {action} the node using docker-compose file {compose_file_name}, which is as below" ) run_shell_command(f"cat {compose_file_name}", shell=True) # TODO AutoApprove should_start = input(f"\nOkay to start the node [Y/n]?:") if Helpers.check_Yes(should_start): if action == "update": print( f"For update, bringing down the node using compose file {compose_file_name}" ) Docker.run_docker_compose_down(compose_file_name) Docker.run_docker_compose_up(keystore_password, compose_file_name, args.trustednode) else: print(f""" --------------------------------------------------------------- Bring up node by updating the file {compose_file_name} You can do it through cli using below command radixnode docker stop -f {compose_file_name} radixnode docker start -f {compose_file_name} -t {args.trustednode} ---------------------------------------------------------------- """)
def check_latest_cli(): cli_latest_version = latest_release("radixdlt/node-runner") if os.getenv(DISABLE_VERSION_CHECK, "False").lower() not in ("true", "yes"): if cli_version() != cli_latest_version: os_name = "ubuntu-20.04" print( f"Radixnode CLI latest version is {cli_latest_version} and current version of the binary is {cli_version()}.\n." ) print(f""" --------------------------------------------------------------- Update the CLI by running these commands wget -O radixnode https://github.com/radixdlt/node-runner/releases/download/{cli_latest_version}/radixnode-{os_name} chmod +x radixnode sudo mv radixnode /usr/local/bin """) abort = input( "Do you want to ABORT the command now to update the cli Y/n?:") if Helpers.check_Yes(abort): sys.exit()