def run(argv): data = cloudseed.agent.commands.status() cloud = env.cloud() cloud.opts["color"] = True display_output(data, out="nested", opts=cloud.opts)
def run(argv): data = cloudseed.agent.commands.status() cloud = env.cloud() cloud.opts['color'] = True display_output(data, out='nested', opts=cloud.opts)
def test_output_unicodebad(self): """ Tests outputter reliability with utf8 """ opts = salt.config.minion_config( os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "minion")) opts["output_file"] = os.path.join(RUNTIME_VARS.TMP, "outputtest") data = { "foo": { "result": False, "aaa": "azerzaeréééé", "comment": "ééééàààà" } } try: # this should not raises UnicodeEncodeError display_output(data, opts=opts) except Exception: # pylint: disable=broad-except # display trace in error message for debugging on jenkins trace = traceback.format_exc() sentinel = object() old_max_diff = getattr(self, "maxDiff", sentinel) try: self.maxDiff = None self.assertEqual(trace, "") finally: if old_max_diff is sentinel: delattr(self, "maxDiff") else: self.maxDiff = old_max_diff
def test_output_unicodebad(self): ''' Tests outputter reliability with utf8 ''' opts = salt.config.minion_config(os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'minion')) opts['output_file'] = os.path.join( RUNTIME_VARS.TMP, 'outputtest' ) data = {'foo': {'result': False, 'aaa': 'azerzaeréééé', 'comment': u'ééééàààà'}} try: # this should not raises UnicodeEncodeError display_output(data, opts=opts) except Exception: # display trace in error message for debugging on jenkins trace = traceback.format_exc() sentinel = object() old_max_diff = getattr(self, 'maxDiff', sentinel) try: self.maxDiff = None self.assertEqual(trace, '') finally: if old_max_diff is sentinel: delattr(self, 'maxDiff') else: self.maxDiff = old_max_diff
def print_docs(self): ''' Print out the documentation! ''' arg = self.opts.get('fun', None) docs = super(Runner, self).get_docs(arg) for fun in sorted(docs): display_output('{0}:'.format(fun), 'text', self.opts) print(docs[fun])
def output_run(run_data, name): """ Outputs a salt run to stdout, in the usual SaltStack style. """ print(json.dumps(run_data, indent=4)) ret = run_data.get('return', {}) display_output({name: ret}, out=run_data.get('out', 'nested'), opts=salt.config.minion_config('/dev/null'))
def print_docs(self): """ Print out the documentation! """ arg = self.opts.get("fun", None) docs = super(Runner, self).get_docs(arg) for fun in sorted(docs): display_output("{0}:".format(fun), "text", self.opts) print(docs[fun])
def _print_docs(self): ''' Print out the documentation! ''' arg = self.opts.get('fun', None) docs = super(Runner, self).get_docs(arg) for fun in sorted(docs): display_output('{0}:'.format(fun), 'text', self.opts) print(docs[fun])
def __call__(self, argv, help): parser = argparse.ArgumentParser( prog="%s salt" % self.ctrl.progname, description=help, ) instances = self.ctrl.get_instances(command='init_ssh_key') parser.add_argument("-r", "--raw", "--raw-shell", dest="raw_shell", default=False, action="store_true", help="Don't execute a salt routine on the targets," " execute a raw shell command") parser.add_argument("instance", nargs=1, metavar="instance", help="Name of the instance from the config.", choices=list(instances)) parser.add_argument("arguments", nargs='+', metavar="arguments", help="Arguments for salt.") args = parser.parse_args(argv) instance = instances[args.instance[0]] from salt.client.ssh import Single from salt.output import display_output from salt.utils import find_json salt_path = os.path.join(self.ctrl.config.path, 'salt') opts = dict( cython_enable=False, cachedir=os.path.join(salt_path, 'cache'), extension_modules=os.path.join(salt_path, 'extmods'), known_hosts_file=self.ctrl.known_hosts, raw_shell=args.raw_shell) single = Single( opts, args.arguments, instance.id, host=instance.get_host(), port=instance.get_port()) single.shell = Shell(instance) (stdout, stderr, retcode) = single.run() ret = { 'stdout': stdout, 'stderr': stderr, 'retcode': retcode, } try: data = find_json(stdout) if len(data) < 2 and 'local' in data: ret = data['local'] except Exception: pass if not isinstance(ret, dict): p_data = {single.id: ret} elif 'return' not in ret: p_data = ret else: p_data = {single.id: ret.get('return', {})} display_output(p_data, 'nested', opts)
def run(self): ''' Execute the runner sequence ''' ret, async_pub = {}, {} if self.opts.get('doc', False): self.print_docs() else: low = {'fun': self.opts['fun']} try: verify_fun(self.functions, low['fun']) args, kwargs = salt.minion.load_args_and_kwargs( self.functions[low['fun']], salt.utils.args.parse_input(self.opts['arg']), self.opts, ) low['arg'] = args low['kwarg'] = kwargs user = salt.utils.get_specific_user() # Run the runner! if self.opts.get('async', False): async_pub = self. async (self.opts['fun'], low, user=user) # by default: info will be not enougth to be printed out ! log.warning( 'Running in async mode. Results of this execution may ' 'be collected by attaching to the master event bus or ' 'by examing the master job cache, if configured. ' 'This execution is running under tag {tag}'.format( **async_pub)) return async_pub['jid'] # return the jid # otherwise run it in the main process async_pub = self._gen_async_pub() ret = self._proc_function(self.opts['fun'], low, user, async_pub['tag'], async_pub['jid'], daemonize=False) except salt.exceptions.SaltException as exc: ret = '{0}'.format(exc) if not self.opts.get('quiet', False): display_output(ret, 'nested', self.opts) return ret log.debug('Runner return: {0}'.format(ret)) return ret
def run(self): ''' Execute the runner sequence ''' ret, async_pub = {}, {} if self.opts.get('doc', False): self.print_docs() else: low = {'fun': self.opts['fun']} try: verify_fun(self.functions, low['fun']) args, kwargs = salt.minion.load_args_and_kwargs( self.functions[low['fun']], salt.utils.args.parse_input(self.opts['arg']), self.opts, ) low['args'] = args low['kwargs'] = kwargs user = salt.utils.get_specific_user() # Run the runner! if self.opts.get('async', False): async_pub = self.async(self.opts['fun'], low, user=user) # by default: info will be not enougth to be printed out ! log.warn('Running in async mode. Results of this execution may ' 'be collected by attaching to the master event bus or ' 'by examing the master job cache, if configured. ' 'This execution is running under tag {tag}'.format(**async_pub)) return async_pub['jid'] # return the jid # otherwise run it in the main process async_pub = self._gen_async_pub() ret = self._proc_function(self.opts['fun'], low, user, async_pub['tag'], async_pub['jid'], False) # Don't daemonize except salt.exceptions.SaltException as exc: ret = '{0}'.format(exc) if not self.opts.get('quiet', False): display_output(ret, 'nested', self.opts) return ret log.debug('Runner return: {0}'.format(ret)) return ret
def test_output_unicodebad(self): ''' Tests outputter reliability with utf8 ''' opts = copy.deepcopy(self.minion_opts) opts['output_file'] = os.path.join( self.minion_opts['root_dir'], 'outputtest') data = {'foo': {'result': False, 'aaa': 'azerzaeréééé', 'comment': u'ééééàààà'}} try: # this should not raises UnicodeEncodeError display_output(data, opts=self.minion_opts) self.assertTrue(True) except Exception: # display trace in error message for debugging on jenkins trace = traceback.format_exc() self.assertEqual(trace, '')
def test_output_unicodebad(self): ''' Tests outputter reliability with utf8 ''' opts = salt.config.minion_config(os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'minion')) opts['output_file'] = os.path.join( opts['root_dir'], 'outputtest') data = {'foo': {'result': False, 'aaa': 'azerzaeréééé', 'comment': u'ééééàààà'}} try: # this should not raises UnicodeEncodeError display_output(data, opts=opts) self.assertTrue(True) except Exception: # display trace in error message for debugging on jenkins trace = traceback.format_exc() self.assertEqual(trace, '')
def handler(event, context): __init__(event) #Always succeed on delete events on non-state executions if event['RequestType'] == "Delete" and not function.startswith('state'): return_s3_response("SUCCESS", data=None) if saltclient == 'local': results = local_client() if not results: sys.stderr.write('ERROR: No return received\n') return_s3_response("FAILED", data=None, reason="ERROR: No return received") opts = {"color": True, "color_theme": None, "extension_modules": "/"} if state_output == "changes": opts.update({"state_verbose": False}) else: opts.update({"state_verbose": True}) if function.startswith('state'): out = "highstate" else: out = None if type(results['return'][0]) is not dict: salt_outputter.display_output(results, out=out, opts=opts) else: for minion_result in results['return']: salt_outputter.display_output(minion_result, out=out, opts=opts) failure = valid_return(results) if failure: return_s3_response("FAILED", data=listdict_to_dict(results.get('return')), reason="False results found in return data") else: return_s3_response("SUCCESS", data=listdict_to_dict(results.get('return')))
def run(self): """ Execute the runner sequence """ ret, async_pub = {}, {} if self.opts.get("doc", False): self.print_docs() else: low = {"fun": self.opts["fun"]} try: verify_fun(self.functions, low["fun"]) args, kwargs = salt.minion.load_args_and_kwargs( self.functions[low["fun"]], salt.utils.args.parse_input(self.opts["arg"]), self.opts ) low["args"] = args low["kwargs"] = kwargs user = salt.utils.get_specific_user() # Run the runner! if self.opts.get("async", False): async_pub = self.async(self.opts["fun"], low, user=user) # by default: info will be not enougth to be printed out ! log.warn( "Running in async mode. Results of this execution may " "be collected by attaching to the master event bus or " "by examing the master job cache, if configured. " "This execution is running under tag {tag}".format(**async_pub) ) return async_pub["jid"] # return the jid # otherwise run it in the main process async_pub = self._gen_async_pub() ret = self._proc_function( self.opts["fun"], low, user, async_pub["tag"], async_pub["jid"], False ) # Don't daemonize except salt.exceptions.SaltException as exc: ret = "{0}".format(exc) if not self.opts.get("quiet", False): display_output(ret, "nested", self.opts) return ret log.debug("Runner return: {0}".format(ret)) return ret
def test_output_unicodebad(self): ''' Tests outputter reliability with utf8 ''' opts = salt.config.minion_config(os.path.join(RUNTIME_VARS.TMP_CONF_DIR, 'minion')) opts['output_file'] = os.path.join( integration.SYS_TMP_DIR, 'salt-tests-tmpdir', 'outputtest' ) data = {'foo': {'result': False, 'aaa': 'azerzaeréééé', 'comment': u'ééééàààà'}} try: # this should not raises UnicodeEncodeError display_output(data, opts=opts) self.assertTrue(True) except Exception: # display trace in error message for debugging on jenkins trace = traceback.format_exc() self.assertEqual(trace, '')
def highstate_redis(request): opts = {'extension_modules': '/var/cache/salt/master/extmods', 'color': False, 'state_verbose': True, } jid = request.GET.get("jid") content = get_redis_result(jid) content_data = {} from salt.output import display_output for i in content: s = {i: content[i]} data = display_output(s, "highstate", opts=opts) content_data[i] = data return render_to_response('autoinstall/redis_highstate.html', locals(), context_instance=RequestContext(request))
def highstate_redis(request): opts = { 'extension_modules': '/var/cache/salt/master/extmods', 'color': False, 'state_verbose': True, } jid = request.GET.get("jid") content = get_redis_result(jid) content_data = {} from salt.output import display_output for i in content: s = {i: content[i]} data = display_output(s, "highstate", opts=opts) content_data[i] = data return render_to_response('autoinstall/redis_highstate.html', locals(), context_instance=RequestContext(request))
'__run_num__': 1, 'changes': {} }, 'group_|-www-user_|-www_|-present': { 'comment': 'Group www is present and up to date', 'name': 'www', 'start_time': '14:12:12.471103', 'result': True, 'duration': 0.807, '__run_num__': 0, 'changes': {} } }, 'retcode': 2, 'success': True, 'cmd': '_return', '_stamp': '2015-04-08T14:15:06.217887', 'fun': 'state.sls', 'id': 'salt_test', 'out': 'highstate' } from salt.output import display_output opts = { 'extension_modules': '', 'color': True, 'state_verbose': True, } data = display_output(s["return"], "highstate", opts=opts) print data
__opts__ = salt.config.client_config('/etc/salt/master') # redis key pubsub rc = redis.Redis(host='192.168.111.101', port=6379, db=1) # ps = r.pubsub() # Listen Salt Master Event System event = salt.utils.event.MasterEvent(__opts__['sock_dir']) for eachevent in event.iter_events(full=True): ret = eachevent['data'] #print ret if "salt/job/" in eachevent['tag']: # Return Event print ret print ret.has_key('id') if ret.has_key('id') and ret.has_key('return'): # Igonre saltutil.find_job event if ret['fun'] == "saltutil.find_job": continue print "*" * 50 display_output(ret["return"], "highstate", opts=opts) print json.dumps(ret["return"]) #r_sub_key = "%s" %(ret['jid']) #ps = rc.pubsub() #ps.subscribe("web_chat") #rc.publish("web_chat", json.dumps({"message": ret["return"], "to_email": r_sub_key, "id": ret["id"]})) #print "redis is ok" # Other Event else: pass
def run(self): ''' Execute the runner sequence ''' import salt.minion ret = {} if self.opts.get('doc', False): self.print_docs() else: low = {'fun': self.opts['fun']} try: verify_fun(self.functions, low['fun']) args, kwargs = salt.minion.load_args_and_kwargs( self.functions[low['fun']], salt.utils.args.parse_input(self.opts['arg']), self.opts, ) low['arg'] = args low['kwarg'] = kwargs if self.opts.get('eauth'): if 'token' in self.opts: try: with salt.utils.fopen(os.path.join(self.opts['cachedir'], '.root_key'), 'r') as fp_: low['key'] = fp_.readline() except IOError: low['token'] = self.opts['token'] # If using eauth and a token hasn't already been loaded into # low, prompt the user to enter auth credentials if 'token' not in low and 'key' not in low and self.opts['eauth']: # This is expensive. Don't do it unless we need to. import salt.auth resolver = salt.auth.Resolver(self.opts) res = resolver.cli(self.opts['eauth']) if self.opts['mktoken'] and res: tok = resolver.token_cli( self.opts['eauth'], res ) if tok: low['token'] = tok.get('token', '') if not res: log.error('Authentication failed') return ret low.update(res) low['eauth'] = self.opts['eauth'] else: user = salt.utils.get_specific_user() # Allocate a jid async_pub = self._gen_async_pub() self.jid = async_pub['jid'] if low['fun'] == 'state.orchestrate': low['kwarg']['orchestration_jid'] = async_pub['jid'] # Run the runner! if self.opts.get('async', False): if self.opts.get('eauth'): async_pub = self.cmd_async(low) else: async_pub = self.async(self.opts['fun'], low, user=user, pub=async_pub) # by default: info will be not enougth to be printed out ! log.warning('Running in async mode. Results of this execution may ' 'be collected by attaching to the master event bus or ' 'by examing the master job cache, if configured. ' 'This execution is running under tag {tag}'.format(**async_pub)) return async_pub['jid'] # return the jid # otherwise run it in the main process if self.opts.get('eauth'): ret = self.cmd_sync(low) if isinstance(ret, dict) and set(ret) == set(('data', 'outputter')): outputter = ret['outputter'] ret = ret['data'] else: outputter = None display_output(ret, outputter, self.opts) else: ret = self._proc_function(self.opts['fun'], low, user, async_pub['tag'], async_pub['jid'], daemonize=False) except salt.exceptions.SaltException as exc: ret = '{0}'.format(exc) if not self.opts.get('quiet', False): display_output(ret, 'nested', self.opts) else: log.debug('Runner return: {0}'.format(ret)) return ret
def run(self): """ Execute the runner sequence """ import salt.minion ret = {} if self.opts.get("doc", False): self.print_docs() else: low = {"fun": self.opts["fun"]} try: # Allocate a jid async_pub = self._gen_async_pub() self.jid = async_pub["jid"] fun_args = salt.utils.args.parse_input(self.opts["arg"], no_parse=self.opts.get( "no_parse", [])) verify_fun(self.functions, low["fun"]) args, kwargs = salt.minion.load_args_and_kwargs( self.functions[low["fun"]], fun_args) low["arg"] = args low["kwarg"] = kwargs if self.opts.get("eauth"): if "token" in self.opts: try: with salt.utils.files.fopen( os.path.join(self.opts["cachedir"], ".root_key"), "r") as fp_: low["key"] = salt.utils.stringutils.to_unicode( fp_.readline()) except IOError: low["token"] = self.opts["token"] # If using eauth and a token hasn't already been loaded into # low, prompt the user to enter auth credentials if "token" not in low and "key" not in low and self.opts[ "eauth"]: # This is expensive. Don't do it unless we need to. import salt.auth resolver = salt.auth.Resolver(self.opts) res = resolver.cli(self.opts["eauth"]) if self.opts["mktoken"] and res: tok = resolver.token_cli(self.opts["eauth"], res) if tok: low["token"] = tok.get("token", "") if not res: log.error("Authentication failed") return ret low.update(res) low["eauth"] = self.opts["eauth"] else: user = salt.utils.user.get_specific_user() if low["fun"] in [ "state.orchestrate", "state.orch", "state.sls" ]: low["kwarg"]["orchestration_jid"] = async_pub["jid"] # Run the runner! if self.opts.get("async", False): if self.opts.get("eauth"): async_pub = self.cmd_async(low) else: async_pub = self.asynchronous(self.opts["fun"], low, user=user, pub=async_pub) # by default: info will be not enougth to be printed out ! log.warning( "Running in asynchronous mode. Results of this execution may " "be collected by attaching to the master event bus or " "by examing the master job cache, if configured. " "This execution is running under tag %s", async_pub["tag"], ) return async_pub["jid"] # return the jid # otherwise run it in the main process if self.opts.get("eauth"): ret = self.cmd_sync(low) if isinstance(ret, dict) and set(ret) == {"data", "outputter"}: outputter = ret["outputter"] ret = ret["data"] else: outputter = None display_output(ret, outputter, self.opts) else: ret = self._proc_function( self.opts["fun"], low, user, async_pub["tag"], async_pub["jid"], daemonize=False, ) except salt.exceptions.SaltException as exc: with salt.utils.event.get_event("master", opts=self.opts) as evt: evt.fire_event( { "success": False, "return": "{0}".format(exc), "retcode": 254, "fun": self.opts["fun"], "fun_args": fun_args, "jid": self.jid, }, tag="salt/run/{0}/ret".format(self.jid), ) # Attempt to grab documentation if "fun" in low: ret = self.get_docs("{0}*".format(low["fun"])) else: ret = None # If we didn't get docs returned then # return the `not availble` message. if not ret: ret = "{0}".format(exc) if not self.opts.get("quiet", False): display_output(ret, "nested", self.opts) else: log.debug("Runner return: %s", ret) return ret
def run(self): ''' Execute the runner sequence ''' import salt.minion ret = {} if self.opts.get('doc', False): self.print_docs() else: low = {'fun': self.opts['fun']} try: verify_fun(self.functions, low['fun']) args, kwargs = salt.minion.load_args_and_kwargs( self.functions[low['fun']], salt.utils.args.parse_input(self.opts['arg']), self.opts, ) low['arg'] = args low['kwarg'] = kwargs if self.opts.get('eauth'): if 'token' in self.opts: try: with salt.utils.fopen( os.path.join(self.opts['cachedir'], '.root_key'), 'r') as fp_: low['key'] = fp_.readline() except IOError: low['token'] = self.opts['token'] # If using eauth and a token hasn't already been loaded into # low, prompt the user to enter auth credentials if 'token' not in low and 'key' not in low and self.opts[ 'eauth']: # This is expensive. Don't do it unless we need to. import salt.auth resolver = salt.auth.Resolver(self.opts) res = resolver.cli(self.opts['eauth']) if self.opts['mktoken'] and res: tok = resolver.token_cli(self.opts['eauth'], res) if tok: low['token'] = tok.get('token', '') if not res: log.error('Authentication failed') return ret low.update(res) low['eauth'] = self.opts['eauth'] else: user = salt.utils.get_specific_user() # Allocate a jid async_pub = self._gen_async_pub() self.jid = async_pub['jid'] if low['fun'] == 'state.orchestrate': low['kwarg']['orchestration_jid'] = async_pub['jid'] # Run the runner! if self.opts.get('async', False): if self.opts.get('eauth'): async_pub = self.cmd_async(low) else: async_pub = self. async (self.opts['fun'], low, user=user, pub=async_pub) # by default: info will be not enougth to be printed out ! log.warning( 'Running in async mode. Results of this execution may ' 'be collected by attaching to the master event bus or ' 'by examing the master job cache, if configured. ' 'This execution is running under tag {tag}'.format( **async_pub)) return async_pub['jid'] # return the jid # otherwise run it in the main process if self.opts.get('eauth'): ret = self.cmd_sync(low) if isinstance(ret, dict) and set(ret) == set( ('data', 'outputter')): outputter = ret['outputter'] ret = ret['data'] else: outputter = None display_output(ret, outputter, self.opts) else: ret = self._proc_function(self.opts['fun'], low, user, async_pub['tag'], async_pub['jid'], daemonize=False) except salt.exceptions.SaltException as exc: ret = '{0}'.format(exc) if not self.opts.get('quiet', False): display_output(ret, 'nested', self.opts) else: log.debug('Runner return: {0}'.format(ret)) return ret
def run(self): ''' Execute the runner sequence ''' import salt.minion ret = {} if self.opts.get('doc', False): self.print_docs() else: low = {'fun': self.opts['fun']} try: # Allocate a jid async_pub = self._gen_async_pub() self.jid = async_pub['jid'] fun_args = salt.utils.args.parse_input(self.opts['arg'], no_parse=self.opts.get( 'no_parse', [])) verify_fun(self.functions, low['fun']) args, kwargs = salt.minion.load_args_and_kwargs( self.functions[low['fun']], fun_args) low['arg'] = args low['kwarg'] = kwargs if self.opts.get('eauth'): if 'token' in self.opts: try: with salt.utils.files.fopen( os.path.join(self.opts['cachedir'], '.root_key'), 'r') as fp_: low['key'] = salt.utils.stringutils.to_unicode( fp_.readline()) except IOError: low['token'] = self.opts['token'] # If using eauth and a token hasn't already been loaded into # low, prompt the user to enter auth credentials if 'token' not in low and 'key' not in low and self.opts[ 'eauth']: # This is expensive. Don't do it unless we need to. import salt.auth resolver = salt.auth.Resolver(self.opts) res = resolver.cli(self.opts['eauth']) if self.opts['mktoken'] and res: tok = resolver.token_cli(self.opts['eauth'], res) if tok: low['token'] = tok.get('token', '') if not res: log.error('Authentication failed') return ret low.update(res) low['eauth'] = self.opts['eauth'] else: user = salt.utils.user.get_specific_user() if low['fun'] in [ 'state.orchestrate', 'state.orch', 'state.sls' ]: low['kwarg']['orchestration_jid'] = async_pub['jid'] # Run the runner! if self.opts.get('async', False): if self.opts.get('eauth'): async_pub = self.cmd_async(low) else: async_pub = self.asynchronous(self.opts['fun'], low, user=user, pub=async_pub) # by default: info will be not enougth to be printed out ! log.warning( 'Running in asynchronous mode. Results of this execution may ' 'be collected by attaching to the master event bus or ' 'by examing the master job cache, if configured. ' 'This execution is running under tag %s', async_pub['tag']) return async_pub['jid'] # return the jid # otherwise run it in the main process if self.opts.get('eauth'): ret = self.cmd_sync(low) if isinstance(ret, dict) and set(ret) == set( ('data', 'outputter')): outputter = ret['outputter'] ret = ret['data'] else: outputter = None display_output(ret, outputter, self.opts) else: ret = self._proc_function(self.opts['fun'], low, user, async_pub['tag'], async_pub['jid'], daemonize=False) except salt.exceptions.SaltException as exc: evt = salt.utils.event.get_event('master', opts=self.opts) evt.fire_event( { 'success': False, 'return': '{0}'.format(exc), 'retcode': 254, 'fun': self.opts['fun'], 'fun_args': fun_args, 'jid': self.jid }, tag='salt/run/{0}/ret'.format(self.jid)) # Attempt to grab documentation if 'fun' in low: ret = self.get_docs('{0}*'.format(low['fun'])) else: ret = None # If we didn't get docs returned then # return the `not availble` message. if not ret: ret = '{0}'.format(exc) if not self.opts.get('quiet', False): display_output(ret, 'nested', self.opts) else: log.debug('Runner return: %s', ret) return ret