Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
131 changes: 68 additions & 63 deletions salt/minion.py
Original file line number Diff line number Diff line change
Expand Up @@ -1572,6 +1572,52 @@ def _target(cls, minion_instance, opts, data, connected):
else:
Minion._thread_return(minion_instance, opts, data)

def _execute_job_function(self, function_name, function_args, executors, opts, data):
'''
Executes a function within a job given it's name, the args and the executors.
It also checks if the function is allowed to run if 'blackout mode' is enabled.
'''
minion_blackout_violation = False
if self.connected and self.opts['pillar'].get('minion_blackout', False):
whitelist = self.opts['pillar'].get('minion_blackout_whitelist', [])
# this minion is blacked out. Only allow saltutil.refresh_pillar and the whitelist
if function_name != 'saltutil.refresh_pillar' and function_name not in whitelist:
minion_blackout_violation = True
elif self.opts['grains'].get('minion_blackout', False):
whitelist = self.opts['grains'].get('minion_blackout_whitelist', [])
if function_name != 'saltutil.refresh_pillar' and function_name not in whitelist:
minion_blackout_violation = True
if minion_blackout_violation:
raise SaltInvocationError('Minion in blackout mode. Set \'minion_blackout\' '
'to False in pillar or grains to resume operations. Only '
'saltutil.refresh_pillar allowed in blackout mode.')

func = self.functions[function_name]
args, kwargs = load_args_and_kwargs(
func,
function_args,
data)
self.functions.pack['__context__']['retcode'] = 0

if isinstance(executors, six.string_types):
executors = [executors]
elif not isinstance(executors, list) or not executors:
raise SaltInvocationError("Wrong executors specification: {0}. String or non-empty list expected".
format(executors))
if opts.get('sudo_user', '') and executors[-1] != 'sudo':
executors[-1] = 'sudo' # replace the last one with sudo
log.trace('Executors list %s', executors) # pylint: disable=no-member

for name in executors:
fname = '{0}.execute'.format(name)
if fname not in self.executors:
raise SaltInvocationError("Executor '{0}' is not available".format(name))
return_data = self.executors[fname](opts, data, func, args, kwargs)
if return_data is not None:
return return_data

return None

@classmethod
def _thread_return(cls, minion_instance, opts, data):
'''
Expand All @@ -1591,54 +1637,24 @@ def _thread_return(cls, minion_instance, opts, data):

salt.utils.process.appendproctitle('{0}._thread_return {1}'.format(cls.__name__, data['jid']))

executors = data.get('module_executors') or opts.get('module_executors', ['direct_call'])
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The setup for this function seems very similar if not identical with _thread_multi_return. Maybe it's worth refactoring that too?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

True. But this code was also touched in develop so I tried to keep the changes to a minimum so that the merging to 2019.2 would not be a head-ache.


sdata = {'pid': os.getpid()}
sdata.update(data)
log.info('Starting a new job %s with PID %s', data['jid'], sdata['pid'])
with salt.utils.files.fopen(fn_, 'w+b') as fp_:
fp_.write(minion_instance.serial.dumps(sdata))
ret = {'success': False}
function_name = data['fun']
function_args = data['arg']
if function_name in minion_instance.functions:
try:
minion_blackout_violation = False
if minion_instance.connected and minion_instance.opts['pillar'].get('minion_blackout', False):
whitelist = minion_instance.opts['pillar'].get('minion_blackout_whitelist', [])
# this minion is blacked out. Only allow saltutil.refresh_pillar and the whitelist
if function_name != 'saltutil.refresh_pillar' and function_name not in whitelist:
minion_blackout_violation = True
elif minion_instance.opts['grains'].get('minion_blackout', False):
whitelist = minion_instance.opts['grains'].get('minion_blackout_whitelist', [])
if function_name != 'saltutil.refresh_pillar' and function_name not in whitelist:
minion_blackout_violation = True
if minion_blackout_violation:
raise SaltInvocationError('Minion in blackout mode. Set \'minion_blackout\' '
'to False in pillar or grains to resume operations. Only '
'saltutil.refresh_pillar allowed in blackout mode.')

func = minion_instance.functions[function_name]
args, kwargs = load_args_and_kwargs(
func,
data['arg'],
return_data = minion_instance._execute_job_function(
function_name,
function_args,
executors,
opts,
data)
minion_instance.functions.pack['__context__']['retcode'] = 0

executors = data.get('module_executors') or opts.get('module_executors', ['direct_call'])
if isinstance(executors, six.string_types):
executors = [executors]
elif not isinstance(executors, list) or not executors:
raise SaltInvocationError("Wrong executors specification: {0}. String or non-empty list expected".
format(executors))
if opts.get('sudo_user', '') and executors[-1] != 'sudo':
executors[-1] = 'sudo' # replace the last one with sudo
log.trace('Executors list %s', executors) # pylint: disable=no-member

for name in executors:
fname = '{0}.execute'.format(name)
if fname not in minion_instance.executors:
raise SaltInvocationError("Executor '{0}' is not available".format(name))
return_data = minion_instance.executors[fname](opts, data, func, args, kwargs)
if return_data is not None:
break

if isinstance(return_data, types.GeneratorType):
ind = 0
Expand Down Expand Up @@ -1689,7 +1705,7 @@ def _thread_return(cls, minion_instance, opts, data):
ret['out'] = 'nested'
except TypeError as exc:
msg = 'Passed invalid arguments to {0}: {1}\n{2}'.format(
function_name, exc, func.__doc__ or ''
function_name, exc, minion_instance.functions[function_name].__doc__ or ''
)
log.warning(msg, exc_info_on_loglevel=logging.DEBUG)
ret['return'] = msg
Expand Down Expand Up @@ -1783,6 +1799,8 @@ def _thread_multi_return(cls, minion_instance, opts, data):

salt.utils.process.appendproctitle('{0}._thread_multi_return {1}'.format(cls.__name__, data['jid']))

executors = data.get('module_executors') or opts.get('module_executors', ['direct_call'])

sdata = {'pid': os.getpid()}
sdata.update(data)
log.info('Starting a new job with PID %s', sdata['pid'])
Expand All @@ -1805,40 +1823,27 @@ def _thread_multi_return(cls, minion_instance, opts, data):
}

for ind in range(0, num_funcs):
function_name = data['fun'][ind]
function_args = data['arg'][ind]
if not multifunc_ordered:
ret['success'][data['fun'][ind]] = False
ret['success'][function_name] = False
try:
minion_blackout_violation = False
if minion_instance.connected and minion_instance.opts['pillar'].get('minion_blackout', False):
whitelist = minion_instance.opts['pillar'].get('minion_blackout_whitelist', [])
# this minion is blacked out. Only allow saltutil.refresh_pillar and the whitelist
if data['fun'][ind] != 'saltutil.refresh_pillar' and data['fun'][ind] not in whitelist:
minion_blackout_violation = True
elif minion_instance.opts['grains'].get('minion_blackout', False):
whitelist = minion_instance.opts['grains'].get('minion_blackout_whitelist', [])
if data['fun'][ind] != 'saltutil.refresh_pillar' and data['fun'][ind] not in whitelist:
minion_blackout_violation = True
if minion_blackout_violation:
raise SaltInvocationError('Minion in blackout mode. Set \'minion_blackout\' '
'to False in pillar or grains to resume operations. Only '
'saltutil.refresh_pillar allowed in blackout mode.')

func = minion_instance.functions[data['fun'][ind]]

args, kwargs = load_args_and_kwargs(
func,
data['arg'][ind],
return_data = minion_instance._execute_job_function(
function_name,
function_args,
executors,
opts,
data)
minion_instance.functions.pack['__context__']['retcode'] = 0

if multifunc_ordered:
ret['return'][ind] = func(*args, **kwargs)
ret['return'][ind] = return_data
ret['retcode'][ind] = minion_instance.functions.pack['__context__'].get(
'retcode',
0
)
ret['success'][ind] = True
else:
ret['return'][data['fun'][ind]] = func(*args, **kwargs)
ret['return'][data['fun'][ind]] = return_data
ret['retcode'][data['fun'][ind]] = minion_instance.functions.pack['__context__'].get(
'retcode',
0
Expand Down