File: //proc/self/root/usr/lib/python2.7/site-packages/salt/client/ssh/wrapper/__init__.py
# -*- coding: utf-8 -*-
'''
The ssh client wrapper system contains the routines that are used to alter
how executions are run in the salt-ssh system, this allows for state routines
to be easily rewritten to execute in a way that makes them do the same tasks
as ZeroMQ salt, but via ssh.
'''
# Import python libs
from __future__ import absolute_import, print_function
import copy
# Import salt libs
import salt.loader
import salt.utils.data
import salt.utils.json
import salt.client.ssh
# Import 3rd-party libs
from salt.ext import six
class FunctionWrapper(object):
'''
Create an object that acts like the salt function dict and makes function
calls remotely via the SSH shell system
'''
def __init__(
self,
opts,
id_,
host,
wfuncs=None,
mods=None,
fsclient=None,
cmd_prefix=None,
aliases=None,
minion_opts=None,
**kwargs):
super(FunctionWrapper, self).__init__()
self.cmd_prefix = cmd_prefix
self.wfuncs = wfuncs if isinstance(wfuncs, dict) else {}
self.opts = opts
self.mods = mods if isinstance(mods, dict) else {}
self.kwargs = {'id_': id_,
'host': host}
self.fsclient = fsclient
self.kwargs.update(kwargs)
self.aliases = aliases
if self.aliases is None:
self.aliases = {}
self.minion_opts = minion_opts
def __contains__(self, key):
'''
We need to implement a __contains__ method, othwerwise when someone
does a contains comparison python assumes this is a sequence, and does
__getitem__ keys 0 and up until IndexError
'''
try:
self[key] # pylint: disable=W0104
return True
except KeyError:
return False
def __getitem__(self, cmd):
'''
Return the function call to simulate the salt local lookup system
'''
if '.' not in cmd and not self.cmd_prefix:
# Form of salt.cmd.run in Jinja -- it's expecting a subdictionary
# containing only 'cmd' module calls, in that case. Create a new
# FunctionWrapper which contains the prefix 'cmd' (again, for the
# salt.cmd.run example)
kwargs = copy.deepcopy(self.kwargs)
id_ = kwargs.pop('id_')
host = kwargs.pop('host')
return FunctionWrapper(self.opts,
id_,
host,
wfuncs=self.wfuncs,
mods=self.mods,
fsclient=self.fsclient,
cmd_prefix=cmd,
aliases=self.aliases,
minion_opts=self.minion_opts,
**kwargs)
if self.cmd_prefix:
# We're in an inner FunctionWrapper as created by the code block
# above. Reconstruct the original cmd in the form 'cmd.run' and
# then evaluate as normal
cmd = '{0}.{1}'.format(self.cmd_prefix, cmd)
if cmd in self.wfuncs:
return self.wfuncs[cmd]
if cmd in self.aliases:
return self.aliases[cmd]
def caller(*args, **kwargs):
'''
The remote execution function
'''
argv = [cmd]
argv.extend([salt.utils.json.dumps(arg) for arg in args])
argv.extend(
['{0}={1}'.format(salt.utils.stringutils.to_str(key),
salt.utils.json.dumps(val))
for key, val in six.iteritems(kwargs)]
)
single = salt.client.ssh.Single(
self.opts,
argv,
mods=self.mods,
disable_wipe=True,
fsclient=self.fsclient,
minion_opts=self.minion_opts,
**self.kwargs
)
stdout, stderr, retcode = single.cmd_block()
if stderr.count('Permission Denied'):
return {'_error': 'Permission Denied',
'stdout': stdout,
'stderr': stderr,
'retcode': retcode}
try:
ret = salt.utils.json.loads(stdout)
if len(ret) < 2 and 'local' in ret:
ret = ret['local']
ret = ret.get('return', {})
except ValueError:
ret = {'_error': 'Failed to return clean data',
'stderr': stderr,
'stdout': stdout,
'retcode': retcode}
return ret
return caller
def __setitem__(self, cmd, value):
'''
Set aliases for functions
'''
if '.' not in cmd and not self.cmd_prefix:
# Form of salt.cmd.run in Jinja -- it's expecting a subdictionary
# containing only 'cmd' module calls, in that case. We don't
# support assigning directly to prefixes in this way
raise KeyError('Cannot assign to module key {0} in the '
'FunctionWrapper'.format(cmd))
if self.cmd_prefix:
# We're in an inner FunctionWrapper as created by the first code
# block in __getitem__. Reconstruct the original cmd in the form
# 'cmd.run' and then evaluate as normal
cmd = '{0}.{1}'.format(self.cmd_prefix, cmd)
if cmd in self.wfuncs:
self.wfuncs[cmd] = value
# Here was assume `value` is a `caller` function from __getitem__.
# We save it as an alias and then can return it when referenced
# later in __getitem__
self.aliases[cmd] = value
def get(self, cmd, default):
'''
Mirrors behavior of dict.get
'''
if cmd in self:
return self[cmd]
else:
return default