Merge pull request #18957 from thatch45/basepi-salt-ssh-publish.publish

Merge #18944
This commit is contained in:
Thomas S Hatch 2014-12-12 09:59:59 -07:00
commit c154936b5d
3 changed files with 265 additions and 8 deletions

View File

@ -169,14 +169,26 @@ class SSH(object):
self.targets = self.roster.targets(
self.opts['tgt'],
self.tgt_type)
priv = self.opts.get(
'ssh_priv',
os.path.join(
self.opts['pki_dir'],
'ssh',
'salt-ssh.rsa'
# If we're in a wfunc, we need to get the ssh key location from the
# top level opts, stored in __master_opts__
if '__master_opts__' in self.opts:
priv = self.opts['__master_opts__'].get(
'ssh_priv',
os.path.join(
self.opts['__master_opts__']['pki_dir'],
'ssh',
'salt-ssh.rsa'
)
)
else:
priv = self.opts.get(
'ssh_priv',
os.path.join(
self.opts['pki_dir'],
'ssh',
'salt-ssh.rsa'
)
)
)
if not os.path.isfile(priv):
try:
salt.client.ssh.shell.gen_key(priv)
@ -399,7 +411,40 @@ class SSH(object):
'''
Execute and yield returns as they come in, do not print to the display
'''
fstr = '{0}.prep_jid'.format(self.opts['master_job_cache'])
jid = self.returners[fstr]()
# Save the invocation information
argv = self.opts['argv']
if self.opts['raw_shell']:
fun = 'ssh._raw'
args = argv
else:
fun = argv[0] if argv else ''
args = argv[1:]
job_load = {
'jid': jid,
'tgt_type': self.tgt_type,
'tgt': self.opts['tgt'],
'user': self.opts['user'],
'fun': fun,
'arg': args,
}
# save load to the master job cache
self.returners['{0}.save_load'.format(self.opts['master_job_cache'])](jid, job_load)
for ret in self.handle_ssh():
host = next(ret.iterkeys())
self.cache_job(jid, host, ret[host])
if self.event:
self.event.fire_event(
ret,
salt.utils.event.tagify(
[jid, 'ret', host],
'job'))
yield ret
def cache_job(self, jid, id_, ret):
@ -412,7 +457,7 @@ class SSH(object):
def run(self):
'''
Execute the overall routine
Execute the overall routine, print results via outputters
'''
fstr = '{0}.prep_jid'.format(self.opts['master_job_cache'])
jid = self.returners[fstr]()
@ -681,6 +726,7 @@ class Single(object):
opts_pkg['ext_pillar'] = self.opts['ext_pillar']
opts_pkg['extension_modules'] = self.opts['extension_modules']
opts_pkg['_ssh_version'] = self.opts['_ssh_version']
opts_pkg['__master_opts__'] = self.context['master_opts']
if '_caller_cachedir' in self.opts:
opts_pkg['_caller_cachedir'] = self.opts['_caller_cachedir']
else:

View File

@ -0,0 +1,208 @@
# -*- coding: utf-8 -*-
'''
.. versionadded:: Lithium
Salt-ssh wrapper functions for the publish module.
Publish will never actually execute on the minions, so we just create new
salt-ssh calls and return the data from them.
No access control is needed because calls cannot originate from the minions.
'''
# Import python libs
import copy
import logging
# Import salt libs
import salt.client.ssh
log = logging.getLogger(__name__)
def _publish(tgt,
fun,
arg=None,
expr_form='glob',
returner='',
timeout=None,
form='clean',
roster=None):
'''
Publish a command "from the minion out to other minions". In reality, the
minion does not execute this function, it is executed by the master. Thus,
no access control is enabled, as minions cannot initiate publishes
themselves.
Salt-ssh publishes will default to whichever roster was used for the
initiating salt-ssh call, and can be overridden using the ``roster``
argument
Returners are not currently supported
The arguments sent to the minion publish function are separated with
commas. This means that for a minion executing a command with multiple
args it will look like this::
salt-ssh system.example.com publish.publish '*' user.add 'foo,1020,1020'
CLI Example:
.. code-block:: bash
salt-ssh system.example.com publish.publish '*' cmd.run 'ls -la /tmp'
'''
if fun.startswith('publish.'):
log.info('Cannot publish publish calls. Returning {}')
return {}
# TODO: implement returners? Do they make sense for salt-ssh calls?
if returner:
log.warning('Returners currently not supported in salt-ssh publish')
# Make sure args have been processed
if arg is None:
arg = []
elif not isinstance(arg, list):
arg = [salt.utils.args.yamlify_arg(arg)]
else:
arg = [salt.utils.args.yamlify_arg(x) for x in arg]
if len(arg) == 1 and arg[0] is None:
arg = []
# Set up opts for the SSH object
opts = copy.deepcopy(__opts__)
if roster:
opts['roster'] = roster
if timeout:
opts['timeout'] = timeout
opts['argv'] = [fun] + arg
opts['selected_target_option'] = expr_form
opts['tgt'] = tgt
opts['arg'] = arg
# Create the SSH object to handle the actual call
ssh = salt.client.ssh.SSH(opts)
# Run salt-ssh to get the minion returns
rets = {}
for ret in ssh.run_iter():
rets.update(ret)
if form == 'clean':
cret = {}
for host in rets:
if 'return' in rets[host]:
cret[host] = rets[host]['return']
else:
cret[host] = rets[host]
return cret
else:
return rets
def publish(tgt,
fun,
arg=None,
expr_form='glob',
returner='',
timeout=5,
roster=None):
'''
Publish a command "from the minion out to other minions". In reality, the
minion does not execute this function, it is executed by the master. Thus,
no access control is enabled, as minions cannot initiate publishes
themselves.
Salt-ssh publishes will default to whichever roster was used for the
initiating salt-ssh call, and can be overridden using the ``roster``
argument
Returners are not currently supported
The expr_form argument is used to pass a target other than a glob into
the execution, the available options are:
- glob
- pcre
The arguments sent to the minion publish function are separated with
commas. This means that for a minion executing a command with multiple
args it will look like this:
.. code-block:: bash
salt-ssh system.example.com publish.publish '*' user.add 'foo,1020,1020'
salt-ssh system.example.com publish.publish '127.0.0.1' network.interfaces '' roster=scan
CLI Example:
.. code-block:: bash
salt-ssh system.example.com publish.publish '*' cmd.run 'ls -la /tmp'
.. admonition:: Attention
If you need to pass a value to a function argument and that value
contains an equal sign, you **must** include the argument name.
For example:
.. code-block:: bash
salt-ssh '*' publish.publish test.kwarg arg='cheese=spam'
Multiple keyword arguments should be passed as a list.
.. code-block:: bash
salt-ssh '*' publish.publish test.kwarg arg="['cheese=spam','spam=cheese']"
'''
return _publish(tgt,
fun,
arg=arg,
expr_form=expr_form,
returner=returner,
timeout=timeout,
form='clean',
roster=roster)
def full_data(tgt,
fun,
arg=None,
expr_form='glob',
returner='',
timeout=5,
roster=None):
'''
Return the full data about the publication, this is invoked in the same
way as the publish function
CLI Example:
.. code-block:: bash
salt-ssh system.example.com publish.full_data '*' cmd.run 'ls -la /tmp'
.. admonition:: Attention
If you need to pass a value to a function argument and that value
contains an equal sign, you **must** include the argument name.
For example:
.. code-block:: bash
salt-ssh '*' publish.full_data test.kwarg arg='cheese=spam'
'''
return _publish(tgt,
fun,
arg=arg,
expr_form=expr_form,
returner=returner,
timeout=timeout,
form='full',
roster=roster)

View File

@ -21,6 +21,9 @@ log = logging.getLogger(__name__)
def get_roster_file(options):
if options.get('roster_file'):
template = options.get('roster_file')
elif 'config_dir' in options.get('__master_opts__', {}):
template = os.path.join(options['__master_opts__']['config_dir'],
'roster')
elif 'config_dir' in options:
template = os.path.join(options['config_dir'], 'roster')
else: