def run(self): ''' Execute the salt command line ''' import salt.auth import salt.client self.parse_args() # Setup file logging! self.setup_logfile_logger() try: # We don't need to bail on config file permission errors # if the CLI # process is run with the -a flag skip_perm_errors = self.options.eauth != '' local = salt.client.get_local_client( self.get_config_file_path(), skip_perm_errors=skip_perm_errors) except SaltClientError as exc: self.exit(2, '{0}\n'.format(exc)) return if self.options.batch or self.options.static: import salt.cli.batch eauth = {} if 'token' in self.config: eauth['token'] = self.config['token'] # If using eauth and a token hasn't already been loaded into # kwargs, prompt the user to enter auth credentials if 'token' not in eauth and self.options.eauth: resolver = salt.auth.Resolver(self.config) res = resolver.cli(self.options.eauth) if self.options.mktoken and res: tok = resolver.token_cli(self.options.eauth, res) if tok: eauth['token'] = tok.get('token', '') if not res: sys.stderr.write('ERROR: Authentication failed\n') sys.exit(2) eauth.update(res) eauth['eauth'] = self.options.eauth if self.options.static: if not self.options.batch: self.config['batch'] = '100%' batch = salt.cli.batch.Batch(self.config, eauth=eauth, quiet=True) ret = {} for res in batch.run(): ret.update(res) self._output_ret(ret, '') else: try: batch = salt.cli.batch.Batch(self.config, eauth=eauth) except salt.exceptions.SaltClientError as exc: # We will print errors to the console further down the stack sys.exit(1) # Printing the output is already taken care of in run() itself for res in batch.run(): if self.options.failhard: for ret in six.itervalues(res): retcode = salt.utils.job.get_retcode(ret) if retcode != 0: sys.stderr.write( 'ERROR: Minions returned with non-zero exit code\n' ) sys.exit(retcode) else: if self.options.timeout <= 0: self.options.timeout = local.opts['timeout'] kwargs = { 'tgt': self.config['tgt'], 'fun': self.config['fun'], 'arg': self.config['arg'], 'timeout': self.options.timeout, 'show_timeout': self.options.show_timeout, 'show_jid': self.options.show_jid } if 'token' in self.config: try: with salt.utils.fopen( os.path.join(self.config['cachedir'], '.root_key'), 'r') as fp_: kwargs['key'] = fp_.readline() except IOError: kwargs['token'] = self.config['token'] kwargs['delimiter'] = self.options.delimiter if self.selected_target_option: kwargs['expr_form'] = self.selected_target_option else: kwargs['expr_form'] = 'glob' if getattr(self.options, 'return'): kwargs['ret'] = getattr(self.options, 'return') if getattr(self.options, 'return_config'): kwargs['ret_config'] = getattr(self.options, 'return_config') if getattr(self.options, 'module_executors'): kwargs['module_executors'] = yamlify_arg( getattr(self.options, 'module_executors')) if getattr(self.options, 'metadata'): kwargs['metadata'] = yamlify_arg( getattr(self.options, 'metadata')) # If using eauth and a token hasn't already been loaded into # kwargs, prompt the user to enter auth credentials if 'token' not in kwargs and self.options.eauth: resolver = salt.auth.Resolver(self.config) res = resolver.cli(self.options.eauth) if self.options.mktoken and res: tok = resolver.token_cli(self.options.eauth, res) if tok: kwargs['token'] = tok.get('token', '') if not res: sys.stderr.write('ERROR: Authentication failed\n') sys.exit(2) kwargs.update(res) kwargs['eauth'] = self.options.eauth if self.config['async']: jid = local.cmd_async(**kwargs) print_cli('Executed command with job ID: {0}'.format(jid)) return retcodes = [] try: # local will be None when there was an error errors = [] if local: if self.options.subset: cmd_func = local.cmd_subset kwargs['sub'] = self.options.subset kwargs['cli'] = True else: cmd_func = local.cmd_cli if self.options.progress: kwargs['progress'] = True self.config['progress'] = True ret = {} for progress in cmd_func(**kwargs): out = 'progress' try: self._progress_ret(progress, out) except salt.exceptions.LoaderError as exc: raise salt.exceptions.SaltSystemExit(exc) if 'return_count' not in progress: ret.update(progress) self._progress_end(out) self._print_returns_summary(ret) elif self.config['fun'] == 'sys.doc': ret = {} out = '' for full_ret in local.cmd_cli(**kwargs): ret_, out, retcode = self._format_ret(full_ret) ret.update(ret_) self._output_ret(ret, out) else: if self.options.verbose: kwargs['verbose'] = True ret = {} for full_ret in cmd_func(**kwargs): try: ret_, out, retcode = self._format_ret(full_ret) retcodes.append(retcode) self._output_ret(ret_, out) ret.update(full_ret) except KeyError: errors.append(full_ret) # Returns summary if self.config['cli_summary'] is True: if self.config['fun'] != 'sys.doc': if self.options.output is None: self._print_returns_summary(ret) self._print_errors_summary(errors) # NOTE: Return code is set here based on if all minions # returned 'ok' with a retcode of 0. # This is the final point before the 'salt' cmd returns, # which is why we set the retcode here. if retcodes.count(0) < len(retcodes): sys.stderr.write( 'ERROR: Minions returned with non-zero exit code\n' ) sys.exit(11) except (SaltInvocationError, EauthAuthenticationError, SaltClientError) as exc: ret = str(exc) out = '' self._output_ret(ret, out)
def run(self): """ Execute the salt command line """ import salt.client self.parse_args() if self.config["log_level"] not in ("quiet", ): # Setup file logging! self.setup_logfile_logger() verify_log(self.config) try: # We don't need to bail on config file permission errors # if the CLI process is run with the -a flag skip_perm_errors = self.options.eauth != "" self.local_client = salt.client.get_local_client( self.get_config_file_path(), skip_perm_errors=skip_perm_errors, auto_reconnect=True, ) except SaltClientError as exc: self.exit(2, "{}\n".format(exc)) return if self.options.batch or self.options.static: # _run_batch() will handle all output and # exit with the appropriate error condition # Execution will not continue past this point # in batch mode. self._run_batch() return if self.options.preview_target: minion_list = self._preview_target() self._output_ret(minion_list, self.config.get("output", "nested")) return if self.options.timeout <= 0: self.options.timeout = self.local_client.opts["timeout"] kwargs = { "tgt": self.config["tgt"], "fun": self.config["fun"], "arg": self.config["arg"], "timeout": self.options.timeout, "show_timeout": self.options.show_timeout, "show_jid": self.options.show_jid, } if "token" in self.config: import salt.utils.files try: with salt.utils.files.fopen( os.path.join(self.config["cachedir"], ".root_key"), "r") as fp_: kwargs["key"] = fp_.readline() except OSError: kwargs["token"] = self.config["token"] kwargs["delimiter"] = self.options.delimiter if self.selected_target_option: kwargs["tgt_type"] = self.selected_target_option else: kwargs["tgt_type"] = "glob" # If batch_safe_limit is set, check minions matching target and # potentially switch to batch execution if self.options.batch_safe_limit > 1: if len(self._preview_target()) >= self.options.batch_safe_limit: salt.utils.stringutils.print_cli( "\nNOTICE: Too many minions targeted, switching to batch execution." ) self.options.batch = self.options.batch_safe_size try: self._run_batch() finally: self.local_client.destroy() return if getattr(self.options, "return"): kwargs["ret"] = getattr(self.options, "return") if getattr(self.options, "return_config"): kwargs["ret_config"] = getattr(self.options, "return_config") if getattr(self.options, "return_kwargs"): kwargs["ret_kwargs"] = yamlify_arg( getattr(self.options, "return_kwargs")) if getattr(self.options, "module_executors"): kwargs["module_executors"] = yamlify_arg( getattr(self.options, "module_executors")) if getattr(self.options, "executor_opts"): kwargs["executor_opts"] = yamlify_arg( getattr(self.options, "executor_opts")) if getattr(self.options, "metadata"): kwargs["metadata"] = yamlify_arg(getattr(self.options, "metadata")) # If using eauth and a token hasn't already been loaded into # kwargs, prompt the user to enter auth credentials if "token" not in kwargs and "key" not in kwargs and self.options.eauth: # This is expensive. Don't do it unless we need to. import salt.auth resolver = salt.auth.Resolver(self.config) res = resolver.cli(self.options.eauth) if self.options.mktoken and res: tok = resolver.token_cli(self.options.eauth, res) if tok: kwargs["token"] = tok.get("token", "") if not res: sys.stderr.write("ERROR: Authentication failed\n") sys.exit(2) kwargs.update(res) kwargs["eauth"] = self.options.eauth if self.config["async"]: jid = self.local_client.cmd_async(**kwargs) salt.utils.stringutils.print_cli( "Executed command with job ID: {}".format(jid)) return # local will be None when there was an error if not self.local_client: return retcodes = [] errors = [] try: if self.options.subset: cmd_func = self.local_client.cmd_subset kwargs["sub"] = self.options.subset kwargs["cli"] = True else: cmd_func = self.local_client.cmd_cli if self.options.progress: kwargs["progress"] = True self.config["progress"] = True ret = {} for progress in cmd_func(**kwargs): out = "progress" try: self._progress_ret(progress, out) except LoaderError as exc: raise SaltSystemExit(exc) if "return_count" not in progress: ret.update(progress) self._progress_end(out) self._print_returns_summary(ret) elif self.config["fun"] == "sys.doc": ret = {} out = "" for full_ret in self.local_client.cmd_cli(**kwargs): ret_, out, retcode = self._format_ret(full_ret) ret.update(ret_) self._output_ret(ret, out, retcode=retcode) else: if self.options.verbose: kwargs["verbose"] = True ret = {} for full_ret in cmd_func(**kwargs): try: ret_, out, retcode = self._format_ret(full_ret) retcodes.append(retcode) self._output_ret(ret_, out, retcode=retcode) ret.update(full_ret) except KeyError: errors.append(full_ret) # Returns summary if self.config["cli_summary"] is True: if self.config["fun"] != "sys.doc": if self.options.output is None: self._print_returns_summary(ret) self._print_errors_summary(errors) # NOTE: Return code is set here based on if all minions # returned 'ok' with a retcode of 0. # This is the final point before the 'salt' cmd returns, # which is why we set the retcode here. if not all(exit_code == salt.defaults.exitcodes.EX_OK for exit_code in retcodes): sys.stderr.write( "ERROR: Minions returned with non-zero exit code\n") sys.exit(salt.defaults.exitcodes.EX_GENERIC) except ( AuthenticationError, AuthorizationError, SaltInvocationError, EauthAuthenticationError, SaltClientError, ) as exc: ret = str(exc) self._output_ret(ret, "", retcode=1) finally: self.local_client.destroy()
def run(self): """ Execute the salt command line """ import salt.client self.parse_args() # Setup file logging! self.setup_logfile_logger() verify_log(self.config) try: # We don't need to bail on config file permission errors # if the CLI # process is run with the -a flag skip_perm_errors = self.options.eauth != "" self.local_client = salt.client.get_local_client( self.get_config_file_path(), skip_perm_errors=skip_perm_errors, auto_reconnect=True ) except SaltClientError as exc: self.exit(2, "{0}\n".format(exc)) return if self.options.batch or self.options.static: # _run_batch() will handle all output and # exit with the appropriate error condition # Execution will not continue past this point # in batch mode. self._run_batch() return if self.options.timeout <= 0: self.options.timeout = self.local_client.opts["timeout"] kwargs = { "tgt": self.config["tgt"], "fun": self.config["fun"], "arg": self.config["arg"], "timeout": self.options.timeout, "show_timeout": self.options.show_timeout, "show_jid": self.options.show_jid, } if "token" in self.config: try: with salt.utils.fopen(os.path.join(self.config["cachedir"], ".root_key"), "r") as fp_: kwargs["key"] = fp_.readline() except IOError: kwargs["token"] = self.config["token"] kwargs["delimiter"] = self.options.delimiter if self.selected_target_option: kwargs["tgt_type"] = self.selected_target_option else: kwargs["tgt_type"] = "glob" if getattr(self.options, "return"): kwargs["ret"] = getattr(self.options, "return") if getattr(self.options, "return_config"): kwargs["ret_config"] = getattr(self.options, "return_config") if getattr(self.options, "return_kwargs"): kwargs["ret_kwargs"] = yamlify_arg(getattr(self.options, "return_kwargs")) if getattr(self.options, "module_executors"): kwargs["module_executors"] = yamlify_arg(getattr(self.options, "module_executors")) if getattr(self.options, "metadata"): kwargs["metadata"] = yamlify_arg(getattr(self.options, "metadata")) # If using eauth and a token hasn't already been loaded into # kwargs, prompt the user to enter auth credentials if "token" not in kwargs and "key" not in kwargs and self.options.eauth: # This is expensive. Don't do it unless we need to. import salt.auth resolver = salt.auth.Resolver(self.config) res = resolver.cli(self.options.eauth) if self.options.mktoken and res: tok = resolver.token_cli(self.options.eauth, res) if tok: kwargs["token"] = tok.get("token", "") if not res: sys.stderr.write("ERROR: Authentication failed\n") sys.exit(2) kwargs.update(res) kwargs["eauth"] = self.options.eauth if self.config["async"]: jid = self.local_client.cmd_async(**kwargs) print_cli("Executed command with job ID: {0}".format(jid)) return # local will be None when there was an error if not self.local_client: return retcodes = [] errors = [] try: if self.options.subset: cmd_func = self.local_client.cmd_subset kwargs["sub"] = True kwargs["cli"] = True else: cmd_func = self.local_client.cmd_cli if self.options.progress: kwargs["progress"] = True self.config["progress"] = True ret = {} for progress in cmd_func(**kwargs): out = "progress" try: self._progress_ret(progress, out) except salt.exceptions.LoaderError as exc: raise salt.exceptions.SaltSystemExit(exc) if "return_count" not in progress: ret.update(progress) self._progress_end(out) self._print_returns_summary(ret) elif self.config["fun"] == "sys.doc": ret = {} out = "" for full_ret in self.local_client.cmd_cli(**kwargs): ret_, out, retcode = self._format_ret(full_ret) ret.update(ret_) self._output_ret(ret, out) else: if self.options.verbose: kwargs["verbose"] = True ret = {} for full_ret in cmd_func(**kwargs): try: ret_, out, retcode = self._format_ret(full_ret) retcodes.append(retcode) self._output_ret(ret_, out) ret.update(full_ret) except KeyError: errors.append(full_ret) # Returns summary if self.config["cli_summary"] is True: if self.config["fun"] != "sys.doc": if self.options.output is None: self._print_returns_summary(ret) self._print_errors_summary(errors) # NOTE: Return code is set here based on if all minions # returned 'ok' with a retcode of 0. # This is the final point before the 'salt' cmd returns, # which is why we set the retcode here. if retcodes.count(0) < len(retcodes): sys.stderr.write("ERROR: Minions returned with non-zero exit code\n") sys.exit(11) except (SaltInvocationError, EauthAuthenticationError, SaltClientError) as exc: ret = str(exc) self._output_ret(ret, "")
def run(self): ''' Execute the salt command line ''' import salt.client self.parse_args() if self.config['log_level'] not in ('quiet', ): # Setup file logging! self.setup_logfile_logger() verify_log(self.config) try: # We don't need to bail on config file permission errors # if the CLI process is run with the -a flag skip_perm_errors = self.options.eauth != '' self.local_client = salt.client.get_local_client( self.get_config_file_path(), skip_perm_errors=skip_perm_errors, auto_reconnect=True) except SaltClientError as exc: self.exit(2, '{0}\n'.format(exc)) return if self.options.batch or self.options.static: # _run_batch() will handle all output and # exit with the appropriate error condition # Execution will not continue past this point # in batch mode. self._run_batch() return if self.options.preview_target: minion_list = self._preview_target() self._output_ret(minion_list, self.config.get('output', 'nested')) return if self.options.timeout <= 0: self.options.timeout = self.local_client.opts['timeout'] kwargs = { 'tgt': self.config['tgt'], 'fun': self.config['fun'], 'arg': self.config['arg'], 'timeout': self.options.timeout, 'show_timeout': self.options.show_timeout, 'show_jid': self.options.show_jid } if 'token' in self.config: import salt.utils.files try: with salt.utils.files.fopen( os.path.join(self.config['cachedir'], '.root_key'), 'r') as fp_: kwargs['key'] = fp_.readline() except IOError: kwargs['token'] = self.config['token'] kwargs['delimiter'] = self.options.delimiter if self.selected_target_option: kwargs['tgt_type'] = self.selected_target_option else: kwargs['tgt_type'] = 'glob' # If batch_safe_limit is set, check minions matching target and # potentially switch to batch execution if self.options.batch_safe_limit > 1: if len(self._preview_target()) >= self.options.batch_safe_limit: salt.utils.stringutils.print_cli( '\nNOTICE: Too many minions targeted, switching to batch execution.' ) self.options.batch = self.options.batch_safe_size self._run_batch() return if getattr(self.options, 'return'): kwargs['ret'] = getattr(self.options, 'return') if getattr(self.options, 'return_config'): kwargs['ret_config'] = getattr(self.options, 'return_config') if getattr(self.options, 'return_kwargs'): kwargs['ret_kwargs'] = yamlify_arg( getattr(self.options, 'return_kwargs')) if getattr(self.options, 'module_executors'): kwargs['module_executors'] = yamlify_arg( getattr(self.options, 'module_executors')) if getattr(self.options, 'executor_opts'): kwargs['executor_opts'] = yamlify_arg( getattr(self.options, 'executor_opts')) if getattr(self.options, 'metadata'): kwargs['metadata'] = yamlify_arg(getattr(self.options, 'metadata')) # If using eauth and a token hasn't already been loaded into # kwargs, prompt the user to enter auth credentials if 'token' not in kwargs and 'key' not in kwargs and self.options.eauth: # This is expensive. Don't do it unless we need to. import salt.auth resolver = salt.auth.Resolver(self.config) res = resolver.cli(self.options.eauth) if self.options.mktoken and res: tok = resolver.token_cli(self.options.eauth, res) if tok: kwargs['token'] = tok.get('token', '') if not res: sys.stderr.write('ERROR: Authentication failed\n') sys.exit(2) kwargs.update(res) kwargs['eauth'] = self.options.eauth if self.config['async']: jid = self.local_client.cmd_async(**kwargs) salt.utils.stringutils.print_cli( 'Executed command with job ID: {0}'.format(jid)) return # local will be None when there was an error if not self.local_client: return retcodes = [] errors = [] try: if self.options.subset: cmd_func = self.local_client.cmd_subset kwargs['sub'] = self.options.subset kwargs['cli'] = True else: cmd_func = self.local_client.cmd_cli if self.options.progress: kwargs['progress'] = True self.config['progress'] = True ret = {} for progress in cmd_func(**kwargs): out = 'progress' try: self._progress_ret(progress, out) except LoaderError as exc: raise SaltSystemExit(exc) if 'return_count' not in progress: ret.update(progress) self._progress_end(out) self._print_returns_summary(ret) elif self.config['fun'] == 'sys.doc': ret = {} out = '' for full_ret in self.local_client.cmd_cli(**kwargs): ret_, out, retcode = self._format_ret(full_ret) ret.update(ret_) self._output_ret(ret, out, retcode=retcode) else: if self.options.verbose: kwargs['verbose'] = True ret = {} for full_ret in cmd_func(**kwargs): try: ret_, out, retcode = self._format_ret(full_ret) retcodes.append(retcode) self._output_ret(ret_, out, retcode=retcode) ret.update(full_ret) except KeyError: errors.append(full_ret) # Returns summary if self.config['cli_summary'] is True: if self.config['fun'] != 'sys.doc': if self.options.output is None: self._print_returns_summary(ret) self._print_errors_summary(errors) # NOTE: Return code is set here based on if all minions # returned 'ok' with a retcode of 0. # This is the final point before the 'salt' cmd returns, # which is why we set the retcode here. if retcodes.count(0) < len(retcodes): sys.stderr.write( 'ERROR: Minions returned with non-zero exit code\n') sys.exit(11) except (AuthenticationError, AuthorizationError, SaltInvocationError, EauthAuthenticationError, SaltClientError) as exc: ret = six.text_type(exc) self._output_ret(ret, '', retcode=1)
def run(self): ''' Execute the salt command line ''' import salt.auth import salt.client self.parse_args() # Setup file logging! self.setup_logfile_logger() verify_log(self.config) try: # We don't need to bail on config file permission errors # if the CLI # process is run with the -a flag skip_perm_errors = self.options.eauth != '' local = salt.client.get_local_client( self.get_config_file_path(), skip_perm_errors=skip_perm_errors) except SaltClientError as exc: self.exit(2, '{0}\n'.format(exc)) return if self.options.batch or self.options.static: self._run_batch() else: if self.options.timeout <= 0: self.options.timeout = local.opts['timeout'] kwargs = { 'tgt': self.config['tgt'], 'fun': self.config['fun'], 'arg': self.config['arg'], 'timeout': self.options.timeout, 'show_timeout': self.options.show_timeout, 'show_jid': self.options.show_jid} if 'token' in self.config: try: with salt.utils.fopen(os.path.join(self.config['cachedir'], '.root_key'), 'r') as fp_: kwargs['key'] = fp_.readline() except IOError: kwargs['token'] = self.config['token'] kwargs['delimiter'] = self.options.delimiter if self.selected_target_option: kwargs['expr_form'] = self.selected_target_option else: kwargs['expr_form'] = 'glob' if getattr(self.options, 'return'): kwargs['ret'] = getattr(self.options, 'return') if getattr(self.options, 'return_config'): kwargs['ret_config'] = getattr(self.options, 'return_config') if getattr(self.options, 'return_kwargs'): kwargs['ret_kwargs'] = yamlify_arg( getattr(self.options, 'return_kwargs')) if getattr(self.options, 'module_executors'): kwargs['module_executors'] = yamlify_arg(getattr(self.options, 'module_executors')) if getattr(self.options, 'metadata'): kwargs['metadata'] = yamlify_arg( getattr(self.options, 'metadata')) # If using eauth and a token hasn't already been loaded into # kwargs, prompt the user to enter auth credentials if 'token' not in kwargs and 'key' not in kwargs and self.options.eauth: resolver = salt.auth.Resolver(self.config) res = resolver.cli(self.options.eauth) if self.options.mktoken and res: tok = resolver.token_cli( self.options.eauth, res ) if tok: kwargs['token'] = tok.get('token', '') if not res: sys.stderr.write('ERROR: Authentication failed\n') sys.exit(2) kwargs.update(res) kwargs['eauth'] = self.options.eauth if self.config['async']: jid = local.cmd_async(**kwargs) print_cli('Executed command with job ID: {0}'.format(jid)) return retcodes = [] try: # local will be None when there was an error errors = [] if local: if self.options.subset: cmd_func = local.cmd_subset kwargs['sub'] = self.options.subset kwargs['cli'] = True else: cmd_func = local.cmd_cli if self.options.progress: kwargs['progress'] = True self.config['progress'] = True ret = {} for progress in cmd_func(**kwargs): out = 'progress' try: self._progress_ret(progress, out) except salt.exceptions.LoaderError as exc: raise salt.exceptions.SaltSystemExit(exc) if 'return_count' not in progress: ret.update(progress) self._progress_end(out) self._print_returns_summary(ret) elif self.config['fun'] == 'sys.doc': ret = {} out = '' for full_ret in local.cmd_cli(**kwargs): ret_, out, retcode = self._format_ret(full_ret) ret.update(ret_) self._output_ret(ret, out) else: if self.options.verbose: kwargs['verbose'] = True ret = {} for full_ret in cmd_func(**kwargs): try: ret_, out, retcode = self._format_ret(full_ret) retcodes.append(retcode) self._output_ret(ret_, out) ret.update(full_ret) except KeyError: errors.append(full_ret) # Returns summary if self.config['cli_summary'] is True: if self.config['fun'] != 'sys.doc': if self.options.output is None: self._print_returns_summary(ret) self._print_errors_summary(errors) # NOTE: Return code is set here based on if all minions # returned 'ok' with a retcode of 0. # This is the final point before the 'salt' cmd returns, # which is why we set the retcode here. if retcodes.count(0) < len(retcodes): sys.stderr.write('ERROR: Minions returned with non-zero exit code\n') sys.exit(11) except (SaltInvocationError, EauthAuthenticationError, SaltClientError) as exc: ret = str(exc) out = '' self._output_ret(ret, out)
def _yamlify_arg(item): log.debug('Testing yamlify_arg with %r', item) return args.yamlify_arg(item)
def run(self): # ''' # Execute the salt command line # ''' import salt.client # self.parse_args() # Setup file logging! self.setup_logfile_logger() verify_log(self.config) try: # We don't need to bail on config file permission errors # if the CLI # process is run with the -a flag skip_perm_errors = self.options.eauth != '' self.local_client = salt.client.get_local_client( self.get_config_file_path(), skip_perm_errors=skip_perm_errors, auto_reconnect=True) except SaltClientError as exc: self.exit(2, '{0}\n'.format(exc)) return if self.options.batch or self.options.static: # _run_batch() will handle all output and # exit with the appropriate error condition # Execution will not continue past this point # in batch mode. self._run_batch() return if self.options.preview_target: self._preview_target() return if self.options.timeout <= 0: self.options.timeout = self.local_client.opts['timeout'] kwargs = { 'tgt': self.config['tgt'], 'fun': self.config['fun'], 'arg': self.config['arg'], 'timeout': self.options.timeout, 'show_timeout': self.options.show_timeout, 'show_jid': self.options.show_jid } if 'token' in self.config: try: with salt.utils.fopen( os.path.join(self.config['cachedir'], '.root_key'), 'r') as fp_: kwargs['key'] = fp_.readline() except IOError: kwargs['token'] = self.config['token'] kwargs['delimiter'] = self.options.delimiter if self.selected_target_option: kwargs['tgt_type'] = self.selected_target_option else: kwargs['tgt_type'] = 'glob' if getattr(self.options, 'return'): kwargs['ret'] = getattr(self.options, 'return') if getattr(self.options, 'return_config'): kwargs['ret_config'] = getattr(self.options, 'return_config') if getattr(self.options, 'return_kwargs'): kwargs['ret_kwargs'] = yamlify_arg( getattr(self.options, 'return_kwargs')) if getattr(self.options, 'module_executors'): kwargs['module_executors'] = yamlify_arg( getattr(self.options, 'module_executors')) if getattr(self.options, 'metadata'): kwargs['metadata'] = yamlify_arg(getattr(self.options, 'metadata')) # If using eauth and a token hasn't already been loaded into # kwargs, prompt the user to enter auth credentials if 'token' not in kwargs and 'key' not in kwargs and self.options.eauth: # This is expensive. Don't do it unless we need to. import salt.auth resolver = salt.auth.Resolver(self.config) res = resolver.cli(self.options.eauth) if self.options.mktoken and res: tok = resolver.token_cli(self.options.eauth, res) if tok: kwargs['token'] = tok.get('token', '') if not res: sys.stderr.write('ERROR: Authentication failed\n') sys.exit(2) kwargs.update(res) kwargs['eauth'] = self.options.eauth if self.config['async']: jid = self.local_client.cmd_async(**kwargs) print_cli('Executed command with job ID: {0}'.format(jid)) return # local will be None when there was an error if not self.local_client: return retcodes = [] errors = [] try: if self.options.subset: cmd_func = self.local_client.cmd_subset kwargs['sub'] = self.options.subset kwargs['cli'] = True else: cmd_func = self.local_client.cmd_cli if self.options.progress: kwargs['progress'] = True self.config['progress'] = True ret = {} for progress in cmd_func(**kwargs): out = 'progress' try: self._progress_ret(progress, out) except salt.exceptions.LoaderError as exc: raise salt.exceptions.SaltSystemExit(exc) if 'return_count' not in progress: ret.update(progress) self._progress_end(out) self._print_returns_summary(ret) elif self.config['fun'] == 'sys.doc': ret = {} out = '' for full_ret in self.local_client.cmd_cli(**kwargs): ret_, out, retcode = self._format_ret(full_ret) ret.update(ret_) self._output_ret(ret, out) else: if self.options.verbose: kwargs['verbose'] = True ret = {} # TODO: delete debug code start============================= # import re # import subprocess # from salt.newrun import (json, byteify, MessageType) # oriArr = [] # executeArr = [] # def executeSaltCmd(cmd, msg_in=''): # try: # proc = subprocess.Popen(cmd, shell=True, stdin=subprocess.PIPE, # stdout=subprocess.PIPE, stderr=subprocess.PIPE,) # stdout_value, stderr_value = proc.communicate(msg_in) # return stdout_value, stderr_value # except Exception, e: # log.error(traceback.format_exc()) # #print('traceback.format_exc():\n%s' % traceback.format_exc()) # def getAcceptIp(): # stdout_val, stderr_val = executeSaltCmd("salt-key -l acc") # syndicList = re.findall( # r"\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b", stdout_val, re.M) # return syndicList # oriArr = getAcceptIp() # loopi = 0 # TODO: delete debug code end============================= for full_ret in cmd_func(**kwargs): try: ret_, out, retcode = self._format_ret(full_ret) # print('full_ret=: %s, ret_=: %s, out=: %s' % (full_ret, ret_, out)) retcodes.append(retcode) self._output_ret(ret_, out) # TODO: delete debug code start============================= # ret_ = byteify(ret_) # ssList = re.findall(r"\b(?:[0-9]{1,3}\.){3}[0-9]{1,3}\b", json.dumps(ret_), re.M) # executeArr = executeArr + ssList # loopi = loopi +1 # if loopi > 2516: # eprint = [i for i in oriArr if i not in executeArr] # print(eprint) # TODO: delete debug code end============================= ret.update(full_ret) except KeyError: errors.append(full_ret) # Returns summary if self.config['cli_summary'] is True: if self.config['fun'] != 'sys.doc': if self.options.output is None: self._print_returns_summary(ret) self._print_errors_summary(errors) # NOTE: Return code is set here based on if all minions # returned 'ok' with a retcode of 0. # This is the final point before the 'salt' cmd returns, # which is why we set the retcode here. if retcodes.count(0) < len(retcodes): sys.stderr.write( 'ERROR: Minions returned with non-zero exit code\n') sys.exit(11) except (SaltInvocationError, EauthAuthenticationError, SaltClientError) as exc: ret = str(exc) self._output_ret(ret, '')
def new_run(self): # ''' # Execute the salt command line # ''' import salt.client # self.parse_args() # print('################:%s' % (self.config['order_masters']==True)) signal.signal(signal.SIGINT, self.quit) signal.signal(signal.SIGTERM, self.quit) if self.config['log_level'] not in ('quiet', ): # Setup file logging! self.setup_logfile_logger() verify_log(self.config) try: # We don't need to bail on config file permission errors # if the CLI process is run with the -a flag skip_perm_errors = self.options.eauth != '' self.local_client = salt.client.get_local_client( self.get_config_file_path(), skip_perm_errors=skip_perm_errors, auto_reconnect=True) except SaltClientError as exc: self.exit(2, '{0}\n'.format(exc)) return if self.options.preview_target: minion_list = self._preview_target() self._output_ret(minion_list, self.config.get('output', 'nested')) return if self.options.timeout <= 0: self.options.timeout = self.local_client.opts['timeout'] # read tgt list from file if self.options.file_target: try: with open(self.config['tgt']) as xf: xfContent = xf.read().strip("\n").strip(' ') if xfContent == '': self.exit( 2, 'Find empty ip list from {0}, pls check.\n'.format( self.config['tgt'])) return if ',' in xfContent: self.config['tgt'] = xfContent.split(",") self.selected_target_option = 'list' elif '\n' in xfContent: self.config['tgt'] = xfContent.split("\n") self.selected_target_option = 'list' else: print('Find invalid args with -X.') return except IOError as exc: self.exit(2, '{0}\n'.format(exc)) return kwargs = { 'tgt': self.config['tgt'], 'fun': self.config['fun'], 'arg': self.config['arg'], 'timeout': self.options.timeout, 'show_timeout': self.options.show_timeout, 'show_jid': self.options.show_jid } # kwargs = self.config # kwargs['timeout'] = self.options.timeout # kwargs['show_timeout'] = self.options.show_timeout # kwargs['show_jid'] = self.options.show_jid kwargs['delimiter'] = self.options.delimiter if self.selected_target_option: kwargs['tgt_type'] = self.selected_target_option else: kwargs['tgt_type'] = 'glob' if getattr(self.options, 'return'): kwargs['ret'] = getattr(self.options, 'return') if getattr(self.options, 'return_config'): kwargs['ret_config'] = getattr(self.options, 'return_config') if getattr(self.options, 'return_kwargs'): kwargs['ret_kwargs'] = yamlify_arg( getattr(self.options, 'return_kwargs')) if getattr(self.options, 'module_executors'): kwargs['module_executors'] = yamlify_arg( getattr(self.options, 'module_executors')) if getattr(self.options, 'metadata'): kwargs['metadata'] = yamlify_arg(getattr(self.options, 'metadata')) # If using eauth and a token hasn't already been loaded into # kwargs, prompt the user to enter auth credentials if 'token' not in kwargs and 'key' not in kwargs and self.options.eauth: # This is expensive. Don't do it unless we need to. import salt.auth resolver = salt.auth.Resolver(self.config) res = resolver.cli(self.options.eauth) if self.options.mktoken and res: tok = resolver.token_cli(self.options.eauth, res) if tok: kwargs['token'] = tok.get('token', '') if not res: sys.stderr.write('ERROR: Authentication failed\n') sys.exit(2) kwargs.update(res) kwargs['eauth'] = self.options.eauth self.newopt = self.config sub_timeout = self.newopt['channel_sub_timeout'] if self.options.timeout > sub_timeout: sub_timeout = self.options.timeout self.bootConfig = { '_sub_timeout': sub_timeout, '_sub_node': '', '_channel_redis_sentinel': self.newopt['channel_redis_sentinel'], '_channel_redis_password': self.newopt['channel_redis_password'], '_master_pub_topic': self.newopt['id'] } # NOTE: Only in super master, filter no-response ip, when use saltx def getPassedIp(): import numpy numpy.warnings.filterwarnings('ignore') passed_ip = numpy.loadtxt('/data0/md/ip.md', dtype=numpy.str) return passed_ip missedList = set() runAllminion = False if isinstance(kwargs['tgt'], list): passed_ip = getPassedIp() kwargs['tgt'] = [i for i in kwargs['tgt'] if i not in passed_ip] if len(kwargs['tgt']) == 0: print_cli('There are nothing iplist to be apply.') return else: if kwargs['tgt'] != '*': passed_ip = getPassedIp() if kwargs['tgt'] in passed_ip: print_cli('There are nothing iplist to be apply.') return else: runAllminion = True import salt.newrun clientPub = salt.newrun.MasterPub(**self.bootConfig) if self.config['async']: # NOTE: generate a jid for saltx jid = salt.utils.jid.gen_jid() wrapMesage = { 'type': salt.newrun.FunctionType.ASYNC_RUN, 'jid': jid, 'kwargs': kwargs, 'tempTopic': str(salt.newrun.uuid.uuid1()) } clientPub.publishToSyndicSub(salt.newrun.json.dumps(wrapMesage)) print_cli('Executed command with master job ID: {0}'.format(jid)) return else: wrapMesage = { 'type': salt.newrun.FunctionType.SYNC_RUN, 'kwargs': kwargs, 'tempTopic': str(salt.newrun.uuid.uuid1()) } batch_hold = 0 lossSyndic = [] repeatet = set() emptyRet = [] noResponseRet = [] noConnectRet = [] # running, make sure to be batch count global batch_running batch_running = set() # init batch ip batch_init = set() comeSubList = clientPub.pullAccept() resultPingSet = [] resultExeSet = [] global normalsize normalsize = 0 sucset = set() debugSet = set() def batchExecuteCallback(selfIp, clientPub, redisChannel): while len(batch_running) <= 0: tmpKwargs = wrapMesage['kwargs'] try: for i in range(batch_hold): batch_running.add(batch_init.pop()) # trigger to sub run tmpKwargs['tgt'] = list(batch_running) wrapMesage['kwargs'] = tmpKwargs batchRun(wrapMesage, selfIp, clientPub, redisChannel) except: if len(batch_running) > 0: # trigger to sub run tmpKwargs['tgt'] = list(batch_running) wrapMesage['kwargs'] = tmpKwargs batchRun(wrapMesage, selfIp, clientPub, redisChannel) else: break ##begin print error returns for result in emptyRet: if result['ret_']: # begin print in client console self._output_ret(result['ret_'], result['out']) for result in noResponseRet: if result['ret_']: for k, v in result['ret_'].items(): if k not in sucset: # begin print in client console self._output_ret(result['ret_'], result['out']) for result in noConnectRet: if result['ret_']: for k, v in result['ret_'].items(): if k not in sucset: # begin print in client console self._output_ret(result['ret_'], result['out']) disconnectedSyndic = set(comeSubList).difference(resultPingSet) if disconnectedSyndic: print_cli('With disconnected syndic: %s' % list(disconnectedSyndic)) if len(missedList) > 0 or len(lossSyndic) > 0: print('missed maids: {}\nmissed minions: {}'.format( ",".join(lossSyndic), ",".join(missedList))) if len(repeatet) > 0: print('Find some minion run repeated: {}'.format(repeatet)) global normalsize print( 'normal size: {}\nmissed size: {}\nempty size: {}'.format( normalsize, len(missedList), len(emptyRet))) redisChannel.unsubscribe(wrapMesage['tempTopic']) redisChannel.connection_pool.disconnect() def batchRun(wrapMesage, selfIp, clientPub, redisChannel): # NOTE: batch running mode # handle special syndic if selfIp in comeSubList: comeSubList.remove(selfIp) syndic_count = len(comeSubList) resultCount = 0 pingCount = 0 executeStart = time.time() normalDone = False # NOTE: must publish cmd after registered the redis listen # else we will miss ping message # tmpKwargs1 = wrapMesage['kwargs'] # batch_running = set(tmpKwargs1['tgt']) #print('publish wrapMesage: %s' % wrapMesage) clientPub.publishToSyndicSub( salt.newrun.json.dumps(wrapMesage)) from salt.newrun import (json, byteify, MessageType) for message in redisChannel.listen(): try: messageJson = byteify(message) if messageJson['type'] == 'message': resultMessage = messageJson['data'] try: callResult = json.loads(resultMessage, encoding='utf-8') callResult = byteify(callResult) if isinstance(callResult, dict): if 'type' in callResult: messageType = callResult['type'] messageIp = callResult['sub_ip'] if messageType == MessageType.PING and messageIp in comeSubList: resultPingSet.append(messageIp) elif messageType == MessageType.WORK or messageType == MessageType.INTERRUPT: resultExeSet.append(messageIp) else: main_log.info( 'invalid callresult: %s' % callResult) else: # filter no return received of sub node retJsonObj = callResult['ret_'] if retJsonObj: # reset start time executeStart = time.time() for k, v in retJsonObj.items(): # reset running and wait node batch_running.discard(k) if callResult[ 'out'] == 'no_return': if '[No response]' in json.dumps( retJsonObj): noResponseRet.append( callResult) else: noConnectRet.append( callResult) else: # put successed ip to tmp set for k, v in retJsonObj.items(): sucset.add(k) # NOTE: debug if k in debugSet: repeatet.add( json.dumps( retJsonObj)) else: debugSet.add(k) if callResult['retcode'] == 0: isnil = False for k in retJsonObj.keys(): v = retJsonObj[k] if v == "": isnil = True break if isnil: emptyRet.append( callResult) else: global normalsize normalsize += 1 self._output_ret( callResult['ret_'], callResult['out']) else: emptyRet.append(callResult) else: # TODO handle other messages? pass except: resultCount += 1 print_cli(traceback.format_exc()) pass pingCount = len(resultPingSet) resultCount = len(resultExeSet) #from collections import Counter #main_log.info("%s, %s, %s, %s" % (pingCount, resultCount, Counter(resultPingSet), Counter(resultExeSet))) # if len(batch_init) <= 0: # break if len(batch_running) == 0: break if pingCount != resultCount: if (time.time() - executeStart) > sub_timeout: # main_log.info("---T0 stop") break except: main_log.info(traceback.format_exc()) pass def executeCallback(selfIp): redisChannel = clientPub.getRedisInstance().pubsub() redisChannel.subscribe(wrapMesage['tempTopic']) noResponseRet = [] noConnectRet = [] emptyRet = [] # retcodes = [] comeSubList = clientPub.pullAccept() # handle special syndic if selfIp in comeSubList: comeSubList.remove(selfIp) syndic_count = len(comeSubList) resultCount = 0 pingCount = 0 resultPingSet = set() sucset = set() resultExeSet = set() debugSet = set() repeatet = set() lossSyndic = [] executeStart = time.time() normalDone = False # NOTE: must publish cmd after registered the redis listen # else we will miss ping message clientPub.publishToSyndicSub( salt.newrun.json.dumps(wrapMesage)) from salt.newrun import (json, byteify, MessageType) normalsize = 0 for message in redisChannel.listen(): try: messageJson = byteify(message) if messageJson['type'] == 'message': resultMessage = messageJson['data'] try: callResult = json.loads(resultMessage, encoding='utf-8') callResult = byteify(callResult) if isinstance(callResult, dict): if 'type' in callResult: messageType = callResult['type'] messageIp = callResult['sub_ip'] if messageType == MessageType.PING and messageIp in comeSubList: resultPingSet.add(messageIp) pingCount += 1 elif messageType == MessageType.WORK or messageType == MessageType.INTERRUPT: # main_log.info('work or interurupt: %s' % (messageIp)) resultExeSet.add(messageIp) resultCount += 1 else: main_log.info( 'invalid callresult: %s' % callResult) else: # filter no return received of sub node retJsonObj = callResult['ret_'] if retJsonObj: # reset start time executeStart = time.time() if callResult[ 'out'] == 'no_return': if '[No response]' in json.dumps( retJsonObj): noResponseRet.append( callResult) else: noConnectRet.append( callResult) # add to missed list for k, v in retJsonObj.items(): missedList.add(k) else: # put successed ip to tmp set for k, v in retJsonObj.items(): sucset.add(k) # NOTE: debug if k in debugSet: repeatet.add( json.dumps( retJsonObj)) else: debugSet.add(k) if callResult['retcode'] == 0: isnil = False for k in retJsonObj.keys(): v = retJsonObj[k] if v == "": isnil = True break if isnil: emptyRet.append( callResult) else: normalsize += 1 self._output_ret( callResult['ret_'], callResult['out']) else: emptyRet.append(callResult) else: # TODO handle other messages? pass except: resultCount += 1 print_cli(traceback.format_exc()) pass ##check sub timeout, if no node running again #lossSyndic = [item for item in comeSubList if item not in resultExeSet] #print(lossSyndic) losePingCount = syndic_count - pingCount runningCount = syndic_count - resultCount - losePingCount #lossPing = [item for item in comeSubList if item not in resultPingSet] #main_log.info("%s,%s,%s,%s,%s,%s" % (pingCount, syndic_count, runningCount, sub_timeout, executeStart, lossPing)) if pingCount < syndic_count and runningCount <= 0: if (time.time() - executeStart) > sub_timeout: main_log.info("---T0 stop") break elif syndic_count == pingCount and runningCount > 0: if (time.time() - executeStart) > sub_timeout: # main_log.info("---T1 stop") break elif syndic_count == pingCount and resultCount == syndic_count: # main_log.info("---T2 stop") break except: main_log.info(traceback.format_exc()) pass redisChannel.unsubscribe(wrapMesage['tempTopic']) redisChannel.connection_pool.disconnect() # main_log.info('---T: %s, %s, %s' % (emptyRet, noResponseRet, noConnectRet)) ##begin print error returns for result in emptyRet: if result['ret_']: # begin print in client console self._output_ret(result['ret_'], result['out']) for result in noResponseRet: if result['ret_']: for k, v in result['ret_'].items(): if k not in sucset: # begin print in client console self._output_ret(result['ret_'], result['out']) for result in noConnectRet: if result['ret_']: for k, v in result['ret_'].items(): if k not in sucset: # begin print in client console self._output_ret(result['ret_'], result['out']) disconnectedSyndic = set(comeSubList).difference(resultPingSet) if disconnectedSyndic: print_cli('With disconnected syndic: %s' % list(disconnectedSyndic)) if len(missedList) > 0 or len(lossSyndic) > 0: print('missed maids: {}\nmissed minions: {}'.format( ",".join(lossSyndic), ",".join(missedList))) if len(repeatet) > 0: print('Find some minion run repeated: {}'.format(repeatet)) print( 'normal size: {}\nmissed size: {}\nempty size: {}'.format( normalsize, len(missedList), len(emptyRet))) # NOTE: Return code is set here based on if all minions # returned 'ok' with a retcode of 0. # This is the final point before the 'salt' cmd returns, # which is why we set the retcode here. # if retcodes.count(0) < len(retcodes): # sys.stderr.write('ERROR: Minions returned with non-zero exit code\n') # sys.exit(11) if self.options.batch: bwait = self.config.get('batch_wait', 0) redisChannel = clientPub.getRedisInstance().pubsub() percentBatch = 0.0 try: if self.options.batch.endswith('%'): stripBatch = float(self.options.batch.strip('%')) percentBatch = stripBatch / 100 else: batch_hold = int(self.options.batch) except: print('An Int or Percent can be used for batch.') return # find all ip list if kwargs['tgt'] == '*': reGetAllMinionList = [] wrapFindAcceptMesage = { 'type': salt.newrun.FunctionType.FIND_ACCEPT, 'tempTopic': ('fa_%s' % str(salt.newrun.uuid.uuid1())) } redisChannel.subscribe(wrapFindAcceptMesage['tempTopic']) clientPub.publishToSyndicSub( salt.newrun.json.dumps(wrapFindAcceptMesage)) from salt.newrun import (json, byteify, MessageType) ping1stCount = 0 work1stcount = 0 for message in redisChannel.listen(): try: messageJson = byteify(message) if messageJson['type'] == 'message': resultMessage = messageJson['data'] try: callResult = json.loads(resultMessage, encoding='utf-8') callResult = byteify(callResult) if isinstance(callResult, dict): if 'type' in callResult: messageType = callResult['type'] messageIp = callResult['sub_ip'] if messageType == MessageType.PING and messageIp in comeSubList: ping1stCount += 1 elif messageType == MessageType.WORK or messageType == MessageType.INTERRUPT: work1stcount += 1 if ping1stCount == work1stcount and work1stcount == len( comeSubList): break else: main_log.info( 'invalid callresult: %s' % callResult) else: # filter no return received of sub node retJsonObj = callResult['ip_list'] if retJsonObj: reGetAllMinionList = reGetAllMinionList + retJsonObj else: pass #print('callResult: %s' % callResult) except: main_log.info(traceback.format_exc()) pass except: main_log.info(traceback.format_exc()) pass kwargs['tgt'] = reGetAllMinionList batch_init = set(kwargs['tgt']) redisChannel.unsubscribe(wrapFindAcceptMesage['tempTopic']) redisChannel.connection_pool.disconnect() else: if kwargs['tgt_type'] == 'glob': batch_init.add(kwargs['tgt']) else: batch_init = set(kwargs['tgt']) kwargs['tgt_type'] = 'list' wrapMesage['kwargs'] = kwargs if percentBatch > 0: batch_hold = percentBatch * len(batch_init) redisChannel.subscribe(wrapMesage['tempTopic']) batchExecuteCallback(self.newopt['id'], clientPub, redisChannel) else: executeCallback(self.newopt['id'])