def _validate_options(self): CliCommand._validate_options(self) if not self.options.report_path: print(_("No report location specified.")) self.parser.print_help() sys.exit(1) normalized_path = os.path.normpath(self.options.report_path) if not os.path.isfile(normalized_path): print(_('Report location is invalid.')) sys.exit(1) # perform fact validation input_facts = self.options.facts if input_facts == [] or input_facts == ['default']: self.facts_to_hash = facts.SENSITIVE_FACTS elif os.path.isfile(input_facts[0]): self.facts_to_hash = set(_read_in_file(input_facts[0])) else: assert isinstance(input_facts, list) self.facts_to_hash = set(input_facts) # check facts_to_hash is subset of facts.ALL_FACTS if not self.facts_to_hash.issubset(facts.ALL_FACTS): invalid_facts = self.facts_to_hash.difference(facts.ALL_FACTS) print( _("Invalid facts were supplied to the command: " + ",".join(invalid_facts))) self.parser.print_help() sys.exit(1)
def _do_command(self): if self.options.name: auth_found = False vault = get_vault(self.options.vaultfile) if os.path.isfile(utilities.CREDENTIALS_PATH): cred_list = vault.load_as_json(utilities.CREDENTIALS_PATH) for index, cred in enumerate(cred_list): if cred.get('name') == self.options.name: del cred_list[index] print(_('Auth "%s" was removed' % self.options.name)) auth_found = True break if not auth_found: print(_('Auth "%s" was not found' % self.options.name)) sys.exit(1) vault.dump_as_json_to_file(cred_list, utilities.CREDENTIALS_PATH) else: print(_("All authorization credentials removed")) elif self.options.all: if os.path.isfile(utilities.CREDENTIALS_PATH): os.remove(utilities.CREDENTIALS_PATH) print(_("All authorization credentials removed"))
def _do_command(self): vault = get_vault(self.options.vaultfile) auth_found = False if not os.path.isfile(utilities.CREDENTIALS_PATH): print(_("No auth credentials found")) sys.exit(1) else: cred_list = vault.load_as_json(utilities.CREDENTIALS_PATH) for cred in cred_list: if cred.get('name') == self.options.name: auth_found = True if self.options.username: cred['username'] = self.options.username if self.options.password: print(_('Provide connection password.')) cred['password'] = getpass() if self.options.sudo_password: print(_('Provide password for sudo.')) cred['sudo_password'] = getpass() if self.options.filename: cred['ssh_key_file'] = self.options.filename break if not auth_found: print(_('Auth "%s" does not exist' % self.options.name)) sys.exit(1) vault.dump_as_json_to_file(cred_list, utilities.CREDENTIALS_PATH) print(_("Auth '%s' updated") % self.options.name)
def _do_command(self): vault = get_vault(self.options.vaultfile) auth_found = False if not os.path.isfile(utilities.CREDENTIALS_PATH): print(_("No auth credentials found")) else: cred_list = vault.load_as_json(utilities.CREDENTIALS_PATH) for cred in cred_list: if cred.get('name') == self.options.name: auth_found = True password = cred.get('password') if password is not None: cred['password'] = utilities.PASSWORD_MASKING if cred.get('sudo_password') is not None: cred['sudo_password'] = utilities.PASSWORD_MASKING data = json.dumps(cred, sort_keys=True, indent=4, separators=(',', ': ')) print(data) break if not auth_found: print(_('Auth "%s" does not exist' % self.options.name)) sys.exit(1)
def _validate_options(self): CliCommand._validate_options(self) if not self.options.name: self.parser.print_help() sys.exit(1) if not (self.options.filename or self.options.username or self.options.password or self.options.sudo_password): print(_("Should specify an option to update: " "--username, --password, --sshkeyfile " "or --sudo-password")) sys.exit(1) if self.options.filename and self.options.password: print(_('You must provide either "--password" or a value for ' '"--sshkeyfile". You cannot supply both.')) self.parser.print_help() sys.exit(1) if self.options.filename: keyfile_path = os.path.abspath(os.path.normpath( os.path.expanduser(os.path.expandvars(self.options.filename)))) if os.path.isfile(keyfile_path) is False: print(_('You must provide a valid file path for' ' "--sshkeyfile", "%s" could not be found.' % keyfile_path)) self.parser.print_help() sys.exit(1) else: self.options.filename = keyfile_path
def __init__(self): usage = _('usage: %prog fact hash') shortdesc = _('hash facts within a report created by rho') desc = _('hash sensitive facts within a report created by rho.') CliCommand.__init__(self, 'fact hash', usage, shortdesc, desc) self.parser.add_option("--reportfile", dest="report_path", metavar="REPORTFILE", help=_("Report file path - REQUIRED")) self.parser.add_option("--facts", dest="facts", metavar="FACTS", action="callback", callback=multi_arg, default=[], help=SUPPRESS_HELP) self.parser.add_option("--outputfile", dest="hashed_path", metavar="HASHEDPATH", help=_("Location for the hashed file"), default=None) self.facts_to_hash = None
def _validate_options(self): CliCommand._validate_options(self) if not self.options.report_path: print(_("No report location specified.")) self.parser.print_help() sys.exit(1) normalized_path = os.path.normpath(self.options.report_path) if not os.path.isfile(normalized_path): print(_('Report location is invalid.')) sys.exit(1) # perform fact validation facts = self.options.facts if facts == [] or facts == ['default']: self.facts_to_hash = list(utilities.SENSITIVE_FACTS_TUPLE) elif os.path.isfile(facts[0]): self.facts_to_hash = _read_in_file(facts[0]) else: assert isinstance(facts, list) self.facts_to_hash = facts # check facts_to_hash is subset of utilities.DEFAULT_FACTS all_facts = utilities.DEFAULT_FACTS facts_to_hash_set = set(self.facts_to_hash) if not facts_to_hash_set.issubset(all_facts): invalid_facts = facts_to_hash_set.difference(all_facts) print( _("Invalid facts were supplied to the command: " + ",".join(invalid_facts))) self.parser.print_help() sys.exit(1)
def make_auth_for_options(options): """Construct the OrderedDict auth given our options. :param options: an options object, from optparse :returns: an OrderedDict representing the auth being added """ auth = { 'id': str(uuid.uuid4()), 'name': options.name, 'username': options.username } if options.password: print(_('Provide connection password.')) pass_prompt = getpass() auth['password'] = pass_prompt or None else: auth['password'] = None auth['ssh_key_file'] = options.filename or None if options.sudo_password: print(_('Provide password for sudo.')) sudo_pass_prompt = getpass() auth['sudo_password'] = sudo_pass_prompt or None else: auth['sudo_password'] = None return auth
def _do_command(self): # pylint: disable=too-many-locals, too-many-branches # pylint: disable=too-many-statements, too-many-nested-blocks vault = get_vault(self.options.vaultfile) cred_list = [] profiles_list = [] range_list = [] profile_found = False auth_found = False if not os.path.isfile(utilities.CREDENTIALS_PATH): print(_('No credentials exist yet.')) sys.exit(1) if not os.path.isfile(utilities.PROFILES_PATH): print(_('No profiles exist yet.')) sys.exit(1) cred_list = vault.load_as_json(utilities.CREDENTIALS_PATH) profiles_list = vault.load_as_json(utilities.PROFILES_PATH) if self.options.hosts: range_list = read_ranges(self.options.hosts) for curr_profile in profiles_list: if curr_profile.get('name') == self.options.name: profile_found = True if self.options.hosts: curr_profile['hosts'] = range_list if self.options.sshport: curr_profile['ssh_port'] = str(self.options.sshport) if self.options.auth: new_auths = [] auth_list = self.options.auth for auth in auth_list: for cred in cred_list: if auth == cred.get('name'): auth_found = True store_cred = { 'id': cred.get('id'), 'name': cred.get('name') } new_auths.append(store_cred) if not auth_found: print(_("Auths do not exist.")) sys.exit(1) curr_profile['auth'] = new_auths break if not profile_found: print(_("Profile '%s' does not exist.") % self.options.name) sys.exit(1) vault.dump_as_json_to_file(profiles_list, utilities.PROFILES_PATH) print(_("Profile '%s' edited" % self.options.name))
def read_ranges(ranges_or_path): """Process a range list from the user. This function reads a hosts file if necessary, validates that all IP ranges are in Ansible format, and rewrites CIDR address ranges to Ansible format if necessary. :param ranges_or_path: either a list of IP address ranges or a one-element list where the one element is the path of a file with ranges. :returns: list of IP address ranges in Ansible format """ # pylint: disable=anomalous-backslash-in-string regex_list = [ '[0-9]*.[0-9]*.[0-9]*.\[[0-9]*:[0-9]*\]', '[0-9]*.[0-9]*.\[[0-9]*:[0-9]*\].[0-9]*', '[0-9]*.[0-9]*.\[[0-9]*:[0-9]*\].\[[0-9]*:[0-9]*\]', '[a-zA-Z0-9-\.]+', '[a-zA-Z0-9-\.]*\[[0-9]*:[0-9]*\]*[a-zA-Z0-9-\.]*', '[a-zA-Z0-9-\.]*\[[a-zA-Z]*:[a-zA-Z]*\][a-zA-Z0-9-\.]*' ] invalid_path = check_path_validity([ranges_or_path[0]]) if ranges_or_path and os.path.isfile(ranges_or_path[0]): range_list = _read_in_file(ranges_or_path[0]) elif invalid_path == [] and len(ranges_or_path) == 1: log.error( _("Couldn't interpret %s as host file because " "no such file exists."), ranges_or_path[0]) sys.exit(1) else: range_list = ranges_or_path normalized = [] for reg_item in range_list: match_found = False for reg in regex_list: match = re.match(reg, reg_item) if match and match.end() == len(reg_item): normalized.append(reg_item) match_found = True break if not match_found: try: normalized.append(cidr_to_ansible(reg_item)) match_found = True except NotCIDRException: pass if not match_found: log.error(_("Bad host name/range : '%s'" % (reg_item))) sys.exit(1) return normalized
def __init__(self): usage = _("usage: %prog auth edit [options]") shortdesc = _("edits a given auth") desc = _("edit a given auth") CliCommand.__init__(self, "auth edit", usage, shortdesc, desc) self.parser.add_option("--name", dest="name", metavar="NAME", help=_("NAME of the auth - REQUIRED")) self.parser.add_option("--username", dest="username", metavar="USERNAME", help=_("user name for authenticating " "against target machine" " - REQUIRED")) self.parser.add_option("--password", dest="password", action="store_true", help=_("password for authenticating" " against target machine")) self.parser.add_option("--sudo-password", dest="sudo_password", action="store_true", help=_("password for running sudo")) self.parser.add_option("--sshkeyfile", dest="filename", metavar="FILENAME", help=_("file containing SSH key")) self.parser.add_option("--vault", dest="vaultfile", metavar="VAULT", help=_("file containing vault password for" " scripting")) self.parser.set_defaults(password=False) self.parser.set_defaults(sudo_password=False)
def __init__(self): usage = _("usage: %prog auth add [options]") shortdesc = _("add auth credentials to config") desc = _("adds the authorization credentials to the config") CliCommand.__init__(self, "auth add", usage, shortdesc, desc) self.parser.add_option("--name", dest="name", metavar="NAME", help=_("auth credential name - REQUIRED")) self.parser.add_option("--sshkeyfile", dest="filename", metavar="FILENAME", help=_("file containing SSH key")) self.parser.add_option("--username", dest="username", metavar="USERNAME", help=_("user name for authenticating" " against target machine - REQUIRED")) self.parser.add_option("--password", dest="password", action="store_true", help=_("password for authenticating against" " target machine")) self.parser.add_option("--sudo-password", dest="sudo_password", action="store_true", help=_("password for running sudo")) self.parser.add_option("--vault", dest="vaultfile", metavar="VAULT", help=_("file containing vault password for" " scripting"))
def __init__(self): usage = _("usage: %prog profile list [options]") shortdesc = _("list the network profiles") desc = _("list the network profiles") CliCommand.__init__(self, "profile list", usage, shortdesc, desc) self.parser.add_option("--vault", dest="vaultfile", metavar="VAULT", help=_("file containing vault password for" " scripting"))
def __init__(self): usage = _("usage: %prog auth list [options]") shortdesc = _("list auth credentials") desc = _("list authentication credentials") CliCommand.__init__(self, "auth list", usage, shortdesc, desc) self.parser.add_option("--vault", dest="vaultfile", metavar="VAULT", help=_("file containing vault password for" " scripting"))
def __init__(self): usage = _("usage: %prog auth show [options]") shortdesc = _("show auth credential") desc = _("show authentication credential") CliCommand.__init__(self, "auth show", usage, shortdesc, desc) self.parser.add_option("--name", dest="name", metavar="NAME", help=_("auth credential name - REQUIRED")) self.parser.add_option("--vault", dest="vaultfile", metavar="VAULT", help=_("file containing vault password for" " scripting"))
def __init__(self): usage = _('usage: %prog fact list') shortdesc = _('list facts that rho can detect') desc = _('list facts that rho can detect. Filter fact names with ' '--filter <regex>') CliCommand.__init__(self, 'fact list', usage, shortdesc, desc) self.parser.add_option('--filter', dest='filter', metavar='filter', help=_('regexp to filter facts - optional'))
def process_discovery_scan(line): """Process the output of a discovery scan. :param line: a line from the discovery scan_log """ log_path = os.environ.get('RHO_ANSIBLE_LOG', None) rho_cred = os.environ.get('RHO_CREDENTIAL_NAME', '') hosts_processed = int(os.environ.get('RHO_HOST_PROCESSED', '0')) hosts_successful = int(os.environ.get('RHO_HOST_SUCCESSFUL', '0')) hosts_unreachable = int(os.environ.get('RHO_HOST_UNREACHABLE', '0')) hosts_failed = int(os.environ.get('RHO_HOST_FAILED', '0')) print_status = False line = line.strip('\n') if 'SUCCESS' in line: hosts_successful += 1 hosts_processed += 1 os.environ['RHO_HOST_SUCCESSFUL'] = str(hosts_successful) os.environ['RHO_HOST_PROCESSED'] = str(hosts_processed) print_status = True elif 'FAILED' in line: hosts_failed += 1 hosts_processed += 1 os.environ['RHO_HOST_FAILED'] = str(hosts_failed) os.environ['RHO_HOST_PROCESSED'] = str(hosts_processed) print_status = True elif 'UNREACHABLE' in line: hosts_unreachable += 1 hosts_processed += 1 os.environ['RHO_HOST_UNREACHABLE'] = str(hosts_unreachable) os.environ['RHO_HOST_PROCESSED'] = str(hosts_processed) print_status = True # Display every 5 processed if hosts_processed % 5 == 0 and print_status: if log_path is not None: with open(log_path, 'ab') as logfile: logfile.write('******* %s *******' % (str(datetime.now()).encode('utf-8'))) logfile.flush() print(_('%d hosts processed with credential %s. ' % (hosts_processed, rho_cred))) if hosts_successful > 0: print(_('%d hosts connected successfully with ' 'credential %s.' % (hosts_successful, rho_cred))) if hosts_failed > 0: print(_('%d hosts failed to connect with ' 'credential %s.' % (hosts_failed, rho_cred))) if hosts_unreachable > 0: print(_('%d hosts were unreachable.' % hosts_unreachable)) print('\n')
def _do_command(self): vault = get_vault(self.options.vaultfile) auth_name = self.options.name cred_list = [] if os.path.isfile(utilities.CREDENTIALS_PATH): cred_list = vault.load_as_json(utilities.CREDENTIALS_PATH) auth_found = auth_exists(cred_list, auth_name) if auth_found: print(_("Auth with name exists")) sys.exit(1) cred = make_auth_for_options(self.options) _save_cred(vault, cred, cred_list) print(_('Auth "%s" was added' % self.options.name))
def _validate_options(self): CliCommand._validate_options(self) if not self.options.profile: print(_("No profile specified.")) self.parser.print_help() sys.exit(1) if not self.options.report_path: print(_("No report location specified.")) self.parser.print_help() sys.exit(1) if self.options.ansible_forks: try: if int(self.options.ansible_forks) <= 0: print(_("--ansible-forks can only be a positive integer.")) self.parser.print_help() sys.exit(1) except ValueError: print(_("--ansible-forks can only be a positive integer.")) self.parser.print_help() sys.exit(1) # perform fact validation input_facts = self.options.facts assert isinstance(input_facts, list) if input_facts and os.path.isfile(input_facts[0]): input_facts = _read_in_file(input_facts[0]) self.facts_to_collect = facts.expand_facts(input_facts) if self.options.scan_dirs == []: self.options.scan_dirs = ['/', '/opt', '/app', '/home', '/usr'] elif os.path.isfile(self.options.scan_dirs[0]): self.options.scan_dirs = \ _read_in_file(self.options.scan_dirs[0]) else: assert isinstance(self.options.scan_dirs, list) # check that all values in scan_dirs are valid abs paths invalid_paths = utilities.check_path_validity(self.options.scan_dirs) if invalid_paths != []: print( _("Invalid paths were supplied to the --scan-dirs option: " + ",".join(invalid_paths))) self.parser.print_help() sys.exit(1)
def tail_and_follow(path, ansible_verbosity): """Follow and provide output :param path: tuple containing thepath to file to follow :param ansible_verbosity: the verbosity level """ if len(path) > 0: # pylint: disable=len-as-condition truncate = 1 if ansible_verbosity: truncate = ansible_verbosity print_line = truncate plabook_started = False truncated = False # pylint: disable=no-member for line in sh.tail('-f', '-n', '+0', path, _iter=True): line = line.strip('\n') if line.startswith('TASK') or line.startswith('PLAY'): print(line) print_line = truncate plabook_started = True truncated = False elif print_line > 0: line_len = len(line) char_truncate = truncate * 100 if line_len > char_truncate: print(line[0:char_truncate] + '...') else: print(line) print_line = print_line - 1 elif print_line == 0 and not truncated and plabook_started: print(_('-- output truncated --')) truncated = True
def _validate_options(self): CliCommand._validate_options(self) if not self.options.name and not self.options.all: print( _('You must provide either "--all" or a value for ' '"--name".')) self.parser.print_help() sys.exit(1) if self.options.name and self.options.all: print( _('You must provide either "--all" or a value for ' '"--name". You cannot supply both.')) self.parser.print_help() sys.exit(1)
def main(self): """ The method that does a basic check for command validity and set's the process in motion. """ (self.options, self.args) = self.parser.parse_args() # we dont need argv[0] in this list... self.args = self.args[1:] # Verbosity propagates in two ways to the individual commands: # first, as self.verbosity, and second, through the log level # for the 'rho' logger. self.verbosity = self.options.verbosity ensure_config_dir_exists() ensure_data_dir_exists() setup_logging(self.verbosity) self._validate_options() if len(sys.argv) < 2: print(self.parser.error(_("Please enter at least 2 args"))) # do the work, catch most common errors here: self._do_command()
def _do_command(self): vault = get_vault(self.options.vaultfile) if not os.path.isfile(utilities.PROFILES_PATH): print(_('No profiles exist yet.')) sys.exit(1) profiles_list = vault.load_as_json(utilities.PROFILES_PATH) if not profiles_list: print(_('No profiles exist yet.')) sys.exit(1) else: data = json.dumps(profiles_list, sort_keys=True, indent=4, separators=(',', ': ')) print(data)
def _do_command(self): vault = get_vault(self.options.vaultfile) profiles_list = [] ssh_port = self.options.sshport if os.path.isfile(utilities.PROFILES_PATH): profiles_list = vault.load_as_json(utilities.PROFILES_PATH) profile_found = profile_exists(profiles_list, self.options.name) if profile_found: print(_("Profile '%s' already exists.") % self.options.name) sys.exit(1) range_list = read_ranges(self.options.hosts) if not os.path.isfile(utilities.CREDENTIALS_PATH): print(_('No credentials exist yet.')) sys.exit(1) creds = [] cred_list = vault.load_as_json(utilities.CREDENTIALS_PATH) for auth in self.options.auth: for auth_item in auth.strip().split(","): valid = False for cred in cred_list: if cred.get('name') == auth: valid = True # add the uuids of credentials store_cred = { 'id': cred.get('id'), 'name': cred.get('name') } creds.append(store_cred) if not valid: print("Auth " + auth_item + " does not exist") sys.exit(1) new_profile = OrderedDict([("name", self.options.name), ("hosts", range_list), ("ssh_port", str(ssh_port)), ("auth", creds)]) _save_profile(vault, new_profile, profiles_list) print(_('Profile "%s" was added' % self.options.name))
def _validate_options(self): CliCommand._validate_options(self) if not self.options.name: self.parser.print_help() sys.exit(1) if not (self.options.filename or self.options.username or self.options.password or self.options.sudo_password): print(_("Should specify an option to update: " "--username, --password, --sshkeyfile " "or --sudo-password")) sys.exit(1) if self.options.filename and self.options.password: print(_('You must provide either "--password" or a value for ' '"--sshkeyfile". You cannot supply both.')) self.parser.print_help() sys.exit(1)
def _do_command(self): vault = get_vault(self.options.vaultfile) if not os.path.isfile(utilities.PROFILES_PATH): print(_('No profiles exist yet.')) sys.exit(1) profile_found = False profiles_list = vault.load_as_json(utilities.PROFILES_PATH) for profile in profiles_list: if self.options.name == profile.get('name'): profile_found = True data = json.dumps(profile, sort_keys=True, indent=4, separators=(',', ': ')) print(data) break if not profile_found: print(_("Profile '%s' does not exist.") % self.options.name) sys.exit(1)
def _do_command(self): if not os.path.isfile(utilities.PROFILES_PATH): print(_("All network profiles removed")) return if self.options.name: vault = get_vault(self.options.vaultfile) profile = self.options.name profiles_list = vault.load_as_json(utilities.PROFILES_PATH) profile_found = False for index, curr_profile in enumerate(profiles_list): if curr_profile.get('name') == profile: del profiles_list[index] print(_('Profile "%s" was removed' % profile)) profile_found = True break if not profile_found: print(_("No such profile: '%s'") % profile) sys.exit(1) vault.dump_as_json_to_file(profiles_list, utilities.PROFILES_PATH) # removes inventory associated with the profile profile_hosts_path = get_config_path(profile + PROFILE_HOSTS_SUFIX) if os.path.isfile(profile_hosts_path): os.remove(profile_hosts_path) _backup_host_auth_mapping(profile) # removes all inventories ever. elif self.options.all: os.remove(utilities.PROFILES_PATH) wildcard_hosts_path = get_config_path('*' + PROFILE_HOSTS_SUFIX) for file_list in glob.glob(wildcard_hosts_path): os.remove(file_list) file_list = os.path.basename(file_list) profile = file_list[:file_list.rfind(PROFILE_HOSTS_SUFIX)] _backup_host_auth_mapping(profile) print(_("All network profiles removed"))
def _validate_options(self): CliCommand._validate_options(self) if not (self.options.name and self.options.username): print(_('You must provide a value for "--name" and "--username".')) self.parser.print_help() sys.exit(1) if not self.options.username: self.parser.print_help() sys.exit(1) # need to pass in file or password: if not (self.options.filename or self.options.password): print( _('You must provide either "--password" or a value for ' '"--sshkeyfile".')) self.parser.print_help() sys.exit(1) if self.options.filename and self.options.password: print( _('You must provide either "--password" or a value for ' '"--sshkeyfile". You cannot supply both.')) self.parser.print_help() sys.exit(1) if self.options.filename: keyfile_path = os.path.abspath( os.path.normpath( os.path.expanduser( os.path.expandvars(self.options.filename)))) if os.path.isfile(keyfile_path) is False: print( _('You must provide a valid file path for' ' "--sshkeyfile", "%s" could not be found.' % keyfile_path)) self.parser.print_help() sys.exit(1) else: self.options.filename = keyfile_path
def _do_command(self): vault = get_vault(self.options.vaultfile) if not os.path.isfile(utilities.CREDENTIALS_PATH): print(_('No credentials exist yet.')) sys.exit(1) cred_list = vault.load_as_json(utilities.CREDENTIALS_PATH) if not cred_list: print(_('No credentials exist yet.')) sys.exit(1) else: for cred in cred_list: if cred.get('password') is not None: cred['password'] = utilities.PASSWORD_MASKING if cred.get('sudo_password') is not None: cred['sudo_password'] = utilities.PASSWORD_MASKING data = json.dumps(cred_list, sort_keys=True, indent=4, separators=(',', ': ')) print(data)
def _validate_options(self): CliCommand._validate_options(self) if not (self.options.name and self.options.username): print(_('You must provide a value for "--name" and "--username".')) self.parser.print_help() sys.exit(1) if not self.options.username: self.parser.print_help() sys.exit(1) # need to pass in file or password: if not (self.options.filename or self.options.password): print(_('You must provide either "--password" or a value for ' '"--sshkeyfile".')) self.parser.print_help() sys.exit(1) if self.options.filename and self.options.password: print(_('You must provide either "--password" or a value for ' '"--sshkeyfile". You cannot supply both.')) self.parser.print_help() sys.exit(1)