Make chunked mode in salt-cp optional (disabled by default).

This reverts breaking of backward compatibility related to the cp.recv
interface change.
This commit is contained in:
Dmitry Kuzmenko 2017-08-11 22:44:42 +03:00
parent 682b4a8d14
commit 999388680c
4 changed files with 117 additions and 11 deletions

View File

@ -39,6 +39,12 @@ specified target expression.
desitination will be assumed to be a directory. Finally, recursion is now desitination will be assumed to be a directory. Finally, recursion is now
supported, allowing for entire directories to be copied. supported, allowing for entire directories to be copied.
.. versionchanged:: 2016.11.7,Nitrogen
Returned tha old mode when salt-cp copies all files in one command back.
The new chunked mode is available with the new ``-C``, ``--cunked`` argument.
The compression and directories copying and large files support is available
in the chunked mode only.
Options Options
======= =======
@ -56,9 +62,16 @@ Options
.. include:: _includes/target-selection.rst .. include:: _includes/target-selection.rst
.. option:: -C, --chunked
Use new chunked mode to copy files. This mode supports large files, recursive
directories copying and compression.
.. versionadded:: 2016.11.7,Nitrogen
.. option:: -n, --no-compression .. option:: -n, --no-compression
Disable gzip compression. Disable gzip compression in chunked mode.
.. versionadded:: 2016.3.7,2016.11.6,Nitrogen .. versionadded:: 2016.3.7,2016.11.6,Nitrogen

View File

@ -19,8 +19,9 @@ import sys
# Import salt libs # Import salt libs
import salt.client import salt.client
import salt.utils.gzip_util import salt.utils.gzip_util
import salt.utils.itertools
import salt.utils.minions import salt.utils.minions
from salt.utils import parsers, to_bytes from salt.utils import parsers, to_bytes, print_cli
from salt.utils.verify import verify_log from salt.utils.verify import verify_log
import salt.output import salt.output
@ -100,10 +101,69 @@ class SaltCP(object):
empty_dirs.update(empty_dirs_) empty_dirs.update(empty_dirs_)
return files, sorted(empty_dirs) return files, sorted(empty_dirs)
def _file_dict(self, fn_):
'''
Take a path and return the contents of the file as a string
'''
if not os.path.isfile(fn_):
err = 'The referenced file, {0} is not available.'.format(fn_)
sys.stderr.write(err + '\n')
sys.exit(42)
with salt.utils.fopen(fn_, 'r') as fp_:
data = fp_.read()
return {fn_: data}
def _load_files(self):
'''
Parse the files indicated in opts['src'] and load them into a python
object for transport
'''
files = {}
for fn_ in self.opts['src']:
if os.path.isfile(fn_):
files.update(self._file_dict(fn_))
elif os.path.isdir(fn_):
print_cli(fn_ + ' is a directory, only files are supported in non-chunked mode. '
'Use "--chunked" command line argument.')
sys.exit(1)
return files
def run(self): def run(self):
''' '''
Make the salt client call Make the salt client call
''' '''
if self.opts['chunked']:
ret = self.run_chunked()
else:
ret = self.run_oldstyle()
salt.output.display_output(
ret,
self.opts.get('output', 'nested'),
self.opts)
def run_oldstyle(self):
'''
Make the salt client call in old-style all-in-one call method
'''
arg = [self._load_files(), self.opts['dest']]
local = salt.client.get_local_client(self.opts['conf_file'])
args = [self.opts['tgt'],
'cp.recv',
arg,
self.opts['timeout'],
]
selected_target_option = self.opts.get('selected_target_option', None)
if selected_target_option is not None:
args.append(selected_target_option)
return local.cmd(*args)
def run_chunked(self):
'''
Make the salt client call in the new fasion chunked multi-call way
'''
files, empty_dirs = self._list_files() files, empty_dirs = self._list_files()
dest = self.opts['dest'] dest = self.opts['dest']
gzip = self.opts['gzip'] gzip = self.opts['gzip']
@ -165,7 +225,7 @@ class SaltCP(object):
) )
args = [ args = [
tgt, tgt,
'cp.recv', 'cp.recv_chunked',
[remote_path, chunk, append, gzip, mode], [remote_path, chunk, append, gzip, mode],
timeout, timeout,
] ]
@ -211,14 +271,11 @@ class SaltCP(object):
else '', else '',
tgt, tgt,
) )
args = [tgt, 'cp.recv', [remote_path, None], timeout] args = [tgt, 'cp.recv_chunked', [remote_path, None], timeout]
if selected_target_option is not None: if selected_target_option is not None:
args.append(selected_target_option) args.append(selected_target_option)
for minion_id, minion_ret in six.iteritems(local.cmd(*args)): for minion_id, minion_ret in six.iteritems(local.cmd(*args)):
ret.setdefault(minion_id, {})[remote_path] = minion_ret ret.setdefault(minion_id, {})[remote_path] = minion_ret
salt.output.display_output( return ret
ret,
self.opts.get('output', 'nested'),
self.opts)

View File

@ -57,7 +57,36 @@ def _gather_pillar(pillarenv, pillar_override):
return ret return ret
def recv(dest, chunk, append=False, compressed=True, mode=None): def recv(files, dest):
'''
Used with salt-cp, pass the files dict, and the destination.
This function receives small fast copy files from the master via salt-cp.
It does not work via the CLI.
'''
ret = {}
for path, data in six.iteritems(files):
if os.path.basename(path) == os.path.basename(dest) \
and not os.path.isdir(dest):
final = dest
elif os.path.isdir(dest):
final = os.path.join(dest, os.path.basename(path))
elif os.path.isdir(os.path.dirname(dest)):
final = dest
else:
return 'Destination unavailable'
try:
with salt.utils.fopen(final, 'w+') as fp_:
fp_.write(data)
ret[final] = True
except IOError:
ret[final] = False
return ret
def recv_chunked(dest, chunk, append=False, compressed=True, mode=None):
''' '''
This function receives files copied to the minion using ``salt-cp`` and is This function receives files copied to the minion using ``salt-cp`` and is
not intended to be used directly on the CLI. not intended to be used directly on the CLI.

View File

@ -2110,10 +2110,18 @@ class SaltCPOptionParser(six.with_metaclass(OptionParserMeta,
def _mixin_setup(self): def _mixin_setup(self):
file_opts_group = optparse.OptionGroup(self, 'File Options') file_opts_group = optparse.OptionGroup(self, 'File Options')
file_opts_group.add_option(
'-C', '--chunked',
default=False,
dest='chunked',
action='store_true',
help='Use chunked files transfer. Supports big files, recursive '
'lookup and directories creation.'
)
file_opts_group.add_option( file_opts_group.add_option(
'-n', '--no-compression', '-n', '--no-compression',
default=True, default=True,
dest='compression', dest='gzip',
action='store_false', action='store_false',
help='Disable gzip compression.' help='Disable gzip compression.'
) )
@ -2134,7 +2142,6 @@ class SaltCPOptionParser(six.with_metaclass(OptionParserMeta,
self.config['tgt'] = self.args[0] self.config['tgt'] = self.args[0]
self.config['src'] = [os.path.realpath(x) for x in self.args[1:-1]] self.config['src'] = [os.path.realpath(x) for x in self.args[1:-1]]
self.config['dest'] = self.args[-1] self.config['dest'] = self.args[-1]
self.config['gzip'] = True
def setup_config(self): def setup_config(self):
return config.master_config(self.get_config_file_path()) return config.master_config(self.get_config_file_path())