Support disabling saving output streams
This commit is contained in:
parent
47399bfa9f
commit
a993e0f5a9
19 changed files with 460 additions and 43 deletions
|
@ -251,6 +251,10 @@ def get_parsers():
|
||||||
'default.'),
|
'default.'),
|
||||||
action='store', dest='parallel',
|
action='store', dest='parallel',
|
||||||
const=multiprocessing.cpu_count())
|
const=multiprocessing.cpu_count())
|
||||||
|
parser['config_args'].add_argument(
|
||||||
|
'-S', '--disable-saving-output-streams',
|
||||||
|
help='Disable saving output streams.',
|
||||||
|
action='store_false', dest='save_output_streams', default=True)
|
||||||
parser['config_args'].add_argument(
|
parser['config_args'].add_argument(
|
||||||
'-s', '--sequential',
|
'-s', '--sequential',
|
||||||
help='Operate on multiple hosts sequentially (default).',
|
help='Operate on multiple hosts sequentially (default).',
|
||||||
|
|
|
@ -345,7 +345,8 @@ class Config(object):
|
||||||
cache_path_pattern=args.cache_path_pattern,
|
cache_path_pattern=args.cache_path_pattern,
|
||||||
quiet_mode=args.quiet,
|
quiet_mode=args.quiet,
|
||||||
configuration=configuration,
|
configuration=configuration,
|
||||||
exec_path=sys.argv[0])
|
exec_path=sys.argv[0],
|
||||||
|
save_output_streams=args.save_output_streams)
|
||||||
|
|
||||||
remote = cdist.exec.remote.Remote(
|
remote = cdist.exec.remote.Remote(
|
||||||
target_host=target_host,
|
target_host=target_host,
|
||||||
|
@ -356,7 +357,8 @@ class Config(object):
|
||||||
archiving_mode=args.use_archiving,
|
archiving_mode=args.use_archiving,
|
||||||
configuration=configuration,
|
configuration=configuration,
|
||||||
stdout_base_path=local.stdout_base_path,
|
stdout_base_path=local.stdout_base_path,
|
||||||
stderr_base_path=local.stderr_base_path)
|
stderr_base_path=local.stderr_base_path,
|
||||||
|
save_output_streams=args.save_output_streams)
|
||||||
|
|
||||||
cleanup_cmds = []
|
cleanup_cmds = []
|
||||||
if cleanup_cmd:
|
if cleanup_cmd:
|
||||||
|
|
|
@ -248,6 +248,7 @@ _ARG_OPTION_MAPPING = {
|
||||||
'parallel': 'parallel',
|
'parallel': 'parallel',
|
||||||
'verbose': 'verbosity',
|
'verbose': 'verbosity',
|
||||||
'use_archiving': 'archiving',
|
'use_archiving': 'archiving',
|
||||||
|
'save_output_streams': 'save_output_streams',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -285,6 +286,7 @@ class Configuration(metaclass=Singleton):
|
||||||
'parallel': JobsOption('parallel'),
|
'parallel': JobsOption('parallel'),
|
||||||
'verbosity': VerbosityOption(),
|
'verbosity': VerbosityOption(),
|
||||||
'archiving': ArchivingOption(),
|
'archiving': ArchivingOption(),
|
||||||
|
'save_output_streams': BooleanOption('save_output_streams'),
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -328,7 +330,10 @@ class Configuration(metaclass=Singleton):
|
||||||
config_files=default_config_files, singleton=True):
|
config_files=default_config_files, singleton=True):
|
||||||
self.command_line_args = command_line_args
|
self.command_line_args = command_line_args
|
||||||
self.args = self._convert_args(command_line_args)
|
self.args = self._convert_args(command_line_args)
|
||||||
self.env = env
|
if env is None:
|
||||||
|
self.env = {}
|
||||||
|
else:
|
||||||
|
self.env = env
|
||||||
self.config_files = config_files
|
self.config_files = config_files
|
||||||
self.config = self._get_config()
|
self.config = self._get_config()
|
||||||
|
|
||||||
|
@ -403,7 +408,8 @@ class Configuration(metaclass=Singleton):
|
||||||
for option in self.ARG_OPTION_MAPPING:
|
for option in self.ARG_OPTION_MAPPING:
|
||||||
if option in args:
|
if option in args:
|
||||||
dst_opt = self.ARG_OPTION_MAPPING[option]
|
dst_opt = self.ARG_OPTION_MAPPING[option]
|
||||||
if args[option]:
|
option_object = self.CONFIG_FILE_OPTIONS['GLOBAL'][dst_opt]
|
||||||
|
if args[option] or isinstance(option_object, BooleanOption):
|
||||||
d[dst_opt] = args[option]
|
d[dst_opt] = args[option]
|
||||||
return d
|
return d
|
||||||
|
|
||||||
|
|
|
@ -127,13 +127,18 @@ class Code(object):
|
||||||
'__object_name': cdist_object.name,
|
'__object_name': cdist_object.name,
|
||||||
})
|
})
|
||||||
message_prefix = cdist_object.name
|
message_prefix = cdist_object.name
|
||||||
stderr_path = os.path.join(cdist_object.stderr_path,
|
if self.local.save_output_streams:
|
||||||
'gencode-' + which)
|
stderr_path = os.path.join(cdist_object.stderr_path,
|
||||||
with open(stderr_path, 'ba+') as stderr:
|
'gencode-' + which)
|
||||||
|
with open(stderr_path, 'ba+') as stderr:
|
||||||
|
return self.local.run_script(script, env=env,
|
||||||
|
return_output=True,
|
||||||
|
message_prefix=message_prefix,
|
||||||
|
stderr=stderr)
|
||||||
|
else:
|
||||||
return self.local.run_script(script, env=env,
|
return self.local.run_script(script, env=env,
|
||||||
return_output=True,
|
return_output=True,
|
||||||
message_prefix=message_prefix,
|
message_prefix=message_prefix)
|
||||||
stderr=stderr)
|
|
||||||
|
|
||||||
def run_gencode_local(self, cdist_object):
|
def run_gencode_local(self, cdist_object):
|
||||||
"""Run the gencode-local script for the given cdist object."""
|
"""Run the gencode-local script for the given cdist object."""
|
||||||
|
@ -157,12 +162,17 @@ class Code(object):
|
||||||
which_exec = getattr(self, which)
|
which_exec = getattr(self, which)
|
||||||
script = os.path.join(which_exec.object_path,
|
script = os.path.join(which_exec.object_path,
|
||||||
getattr(cdist_object, 'code_%s_path' % which))
|
getattr(cdist_object, 'code_%s_path' % which))
|
||||||
stderr_path = os.path.join(cdist_object.stderr_path, 'code-' + which)
|
if which_exec.save_output_streams:
|
||||||
stdout_path = os.path.join(cdist_object.stdout_path, 'code-' + which)
|
stderr_path = os.path.join(cdist_object.stderr_path,
|
||||||
with open(stderr_path, 'ba+') as stderr, \
|
'code-' + which)
|
||||||
open(stdout_path, 'ba+') as stdout:
|
stdout_path = os.path.join(cdist_object.stdout_path,
|
||||||
return which_exec.run_script(script, env=env, stdout=stdout,
|
'code-' + which)
|
||||||
stderr=stderr)
|
with open(stderr_path, 'ba+') as stderr, \
|
||||||
|
open(stdout_path, 'ba+') as stdout:
|
||||||
|
return which_exec.run_script(script, env=env, stdout=stdout,
|
||||||
|
stderr=stderr)
|
||||||
|
else:
|
||||||
|
return which_exec.run_script(script, env=env)
|
||||||
|
|
||||||
def run_code_local(self, cdist_object):
|
def run_code_local(self, cdist_object):
|
||||||
"""Run the code-local script for the given cdist object."""
|
"""Run the code-local script for the given cdist object."""
|
||||||
|
|
|
@ -154,15 +154,21 @@ class Manifest(object):
|
||||||
message_prefix = "initialmanifest"
|
message_prefix = "initialmanifest"
|
||||||
self.log.verbose("Running initial manifest " + initial_manifest)
|
self.log.verbose("Running initial manifest " + initial_manifest)
|
||||||
which = "init"
|
which = "init"
|
||||||
stderr_path = os.path.join(self.local.stderr_base_path, which)
|
if self.local.save_output_streams:
|
||||||
stdout_path = os.path.join(self.local.stdout_base_path, which)
|
stderr_path = os.path.join(self.local.stderr_base_path, which)
|
||||||
with open(stderr_path, 'ba+') as stderr, \
|
stdout_path = os.path.join(self.local.stdout_base_path, which)
|
||||||
open(stdout_path, 'ba+') as stdout:
|
with open(stderr_path, 'ba+') as stderr, \
|
||||||
|
open(stdout_path, 'ba+') as stdout:
|
||||||
|
self.local.run_script(
|
||||||
|
initial_manifest,
|
||||||
|
env=self.env_initial_manifest(initial_manifest),
|
||||||
|
message_prefix=message_prefix,
|
||||||
|
stdout=stdout, stderr=stderr)
|
||||||
|
else:
|
||||||
self.local.run_script(
|
self.local.run_script(
|
||||||
initial_manifest,
|
initial_manifest,
|
||||||
env=self.env_initial_manifest(initial_manifest),
|
env=self.env_initial_manifest(initial_manifest),
|
||||||
message_prefix=message_prefix,
|
message_prefix=message_prefix)
|
||||||
stdout=stdout, stderr=stderr)
|
|
||||||
|
|
||||||
def env_type_manifest(self, cdist_object):
|
def env_type_manifest(self, cdist_object):
|
||||||
type_manifest = os.path.join(self.local.type_path,
|
type_manifest = os.path.join(self.local.type_path,
|
||||||
|
@ -188,12 +194,18 @@ class Manifest(object):
|
||||||
if os.path.isfile(type_manifest):
|
if os.path.isfile(type_manifest):
|
||||||
self.log.verbose("Running type manifest %s for object %s",
|
self.log.verbose("Running type manifest %s for object %s",
|
||||||
type_manifest, cdist_object.name)
|
type_manifest, cdist_object.name)
|
||||||
stderr_path = os.path.join(cdist_object.stderr_path, which)
|
if self.local.save_output_streams:
|
||||||
stdout_path = os.path.join(cdist_object.stdout_path, which)
|
stderr_path = os.path.join(cdist_object.stderr_path, which)
|
||||||
with open(stderr_path, 'ba+') as stderr, \
|
stdout_path = os.path.join(cdist_object.stdout_path, which)
|
||||||
open(stdout_path, 'ba+') as stdout:
|
with open(stderr_path, 'ba+') as stderr, \
|
||||||
|
open(stdout_path, 'ba+') as stdout:
|
||||||
|
self.local.run_script(
|
||||||
|
type_manifest,
|
||||||
|
env=self.env_type_manifest(cdist_object),
|
||||||
|
message_prefix=message_prefix,
|
||||||
|
stdout=stdout, stderr=stderr)
|
||||||
|
else:
|
||||||
self.local.run_script(
|
self.local.run_script(
|
||||||
type_manifest,
|
type_manifest,
|
||||||
env=self.env_type_manifest(cdist_object),
|
env=self.env_type_manifest(cdist_object),
|
||||||
message_prefix=message_prefix,
|
message_prefix=message_prefix)
|
||||||
stdout=stdout, stderr=stderr)
|
|
||||||
|
|
|
@ -56,7 +56,8 @@ class Local(object):
|
||||||
add_conf_dirs=None,
|
add_conf_dirs=None,
|
||||||
cache_path_pattern=None,
|
cache_path_pattern=None,
|
||||||
quiet_mode=False,
|
quiet_mode=False,
|
||||||
configuration=None):
|
configuration=None,
|
||||||
|
save_output_streams=True):
|
||||||
|
|
||||||
self.target_host = target_host
|
self.target_host = target_host
|
||||||
if target_host_tags is None:
|
if target_host_tags is None:
|
||||||
|
@ -75,6 +76,7 @@ class Local(object):
|
||||||
self.configuration = configuration
|
self.configuration = configuration
|
||||||
else:
|
else:
|
||||||
self.configuration = {}
|
self.configuration = {}
|
||||||
|
self.save_output_streams = save_output_streams
|
||||||
|
|
||||||
self._init_log()
|
self._init_log()
|
||||||
self._init_permissions()
|
self._init_permissions()
|
||||||
|
@ -213,7 +215,7 @@ class Local(object):
|
||||||
"list or tuple argument expected, got: %s" % command)
|
"list or tuple argument expected, got: %s" % command)
|
||||||
|
|
||||||
quiet = self.quiet_mode or quiet_mode
|
quiet = self.quiet_mode or quiet_mode
|
||||||
do_save_output = save_output and not quiet
|
do_save_output = save_output and not quiet and self.save_output_streams
|
||||||
|
|
||||||
close_stdout = False
|
close_stdout = False
|
||||||
close_stderr = False
|
close_stderr = False
|
||||||
|
@ -256,7 +258,6 @@ class Local(object):
|
||||||
if do_save_output:
|
if do_save_output:
|
||||||
util.log_std_fd(self.log, command, stderr, 'Local stderr')
|
util.log_std_fd(self.log, command, stderr, 'Local stderr')
|
||||||
util.log_std_fd(self.log, command, stdout, 'Local stdout')
|
util.log_std_fd(self.log, command, stdout, 'Local stdout')
|
||||||
|
|
||||||
return output
|
return output
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
util.handle_called_process_error(e, command)
|
util.handle_called_process_error(e, command)
|
||||||
|
|
|
@ -65,7 +65,8 @@ class Remote(object):
|
||||||
archiving_mode=None,
|
archiving_mode=None,
|
||||||
configuration=None,
|
configuration=None,
|
||||||
stdout_base_path=None,
|
stdout_base_path=None,
|
||||||
stderr_base_path=None):
|
stderr_base_path=None,
|
||||||
|
save_output_streams=True):
|
||||||
self.target_host = target_host
|
self.target_host = target_host
|
||||||
self._exec = remote_exec
|
self._exec = remote_exec
|
||||||
self._copy = remote_copy
|
self._copy = remote_copy
|
||||||
|
@ -80,6 +81,7 @@ class Remote(object):
|
||||||
self.configuration = configuration
|
self.configuration = configuration
|
||||||
else:
|
else:
|
||||||
self.configuration = {}
|
self.configuration = {}
|
||||||
|
self.save_output_streams = save_output_streams
|
||||||
|
|
||||||
self.stdout_base_path = stdout_base_path
|
self.stdout_base_path = stdout_base_path
|
||||||
self.stderr_base_path = stderr_base_path
|
self.stderr_base_path = stderr_base_path
|
||||||
|
@ -309,12 +311,13 @@ class Remote(object):
|
||||||
|
|
||||||
close_stdout = False
|
close_stdout = False
|
||||||
close_stderr = False
|
close_stderr = False
|
||||||
if not return_output and stdout is None:
|
if self.save_output_streams:
|
||||||
stdout = util.get_std_fd(self.stdout_base_path, 'remote')
|
if not return_output and stdout is None:
|
||||||
close_stdout = True
|
stdout = util.get_std_fd(self.stdout_base_path, 'remote')
|
||||||
if stderr is None:
|
close_stdout = True
|
||||||
stderr = util.get_std_fd(self.stderr_base_path, 'remote')
|
if stderr is None:
|
||||||
close_stderr = True
|
stderr = util.get_std_fd(self.stderr_base_path, 'remote')
|
||||||
|
close_stderr = True
|
||||||
|
|
||||||
# export target_host, target_hostname, target_fqdn
|
# export target_host, target_hostname, target_fqdn
|
||||||
# for use in __remote_{exec,copy} scripts
|
# for use in __remote_{exec,copy} scripts
|
||||||
|
@ -335,8 +338,9 @@ class Remote(object):
|
||||||
stderr=stderr)
|
stderr=stderr)
|
||||||
output = None
|
output = None
|
||||||
|
|
||||||
util.log_std_fd(self.log, command, stderr, 'Remote stderr')
|
if self.save_output_streams:
|
||||||
util.log_std_fd(self.log, command, stdout, 'Remote stdout')
|
util.log_std_fd(self.log, command, stderr, 'Remote stderr')
|
||||||
|
util.log_std_fd(self.log, command, stdout, 'Remote stdout')
|
||||||
|
|
||||||
return output
|
return output
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
|
|
140
cdist/test/capture_output_disabled/__init__.py
Normal file
140
cdist/test/capture_output_disabled/__init__.py
Normal file
|
@ -0,0 +1,140 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# 2018 Darko Poljak (darko.poljak at gmail.com)
|
||||||
|
#
|
||||||
|
# This file is part of cdist.
|
||||||
|
#
|
||||||
|
# cdist is free software: you can redistribute it and/or modify
|
||||||
|
# it under the terms of the GNU General Public License as published by
|
||||||
|
# the Free Software Foundation, either version 3 of the License, or
|
||||||
|
# (at your option) any later version.
|
||||||
|
#
|
||||||
|
# cdist is distributed in the hope that it will be useful,
|
||||||
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
# GNU General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU General Public License
|
||||||
|
# along with cdist. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
#
|
||||||
|
|
||||||
|
import os
|
||||||
|
import shutil
|
||||||
|
|
||||||
|
import cdist
|
||||||
|
from cdist import core
|
||||||
|
from cdist import test
|
||||||
|
from cdist.exec import local
|
||||||
|
from cdist.exec import remote
|
||||||
|
from cdist.core import code
|
||||||
|
from cdist.core import manifest
|
||||||
|
|
||||||
|
import os.path as op
|
||||||
|
my_dir = op.abspath(op.dirname(__file__))
|
||||||
|
fixtures = op.join(my_dir, 'fixtures')
|
||||||
|
conf_dir = op.join(fixtures, 'conf')
|
||||||
|
|
||||||
|
|
||||||
|
class CaptureOutputDisabledTestCase(test.CdistTestCase):
|
||||||
|
|
||||||
|
def setUp(self):
|
||||||
|
# logging.root.setLevel(logging.TRACE)
|
||||||
|
save_output_streams = False
|
||||||
|
self.temp_dir = self.mkdtemp()
|
||||||
|
|
||||||
|
self.local_dir = os.path.join(self.temp_dir, "local")
|
||||||
|
self.hostdir = cdist.str_hash(self.target_host[0])
|
||||||
|
self.host_base_path = os.path.join(self.local_dir, self.hostdir)
|
||||||
|
os.makedirs(self.host_base_path)
|
||||||
|
self.local = local.Local(
|
||||||
|
target_host=self.target_host,
|
||||||
|
target_host_tags=None,
|
||||||
|
base_root_path=self.host_base_path,
|
||||||
|
host_dir_name=self.hostdir,
|
||||||
|
exec_path=cdist.test.cdist_exec_path,
|
||||||
|
add_conf_dirs=[conf_dir],
|
||||||
|
save_output_streams=save_output_streams)
|
||||||
|
self.local.create_files_dirs()
|
||||||
|
|
||||||
|
self.remote_dir = self.mkdtemp()
|
||||||
|
remote_exec = self.remote_exec
|
||||||
|
remote_copy = self.remote_copy
|
||||||
|
self.remote = remote.Remote(
|
||||||
|
target_host=self.target_host,
|
||||||
|
remote_exec=remote_exec,
|
||||||
|
remote_copy=remote_copy,
|
||||||
|
base_path=self.remote_dir,
|
||||||
|
stdout_base_path=self.local.stdout_base_path,
|
||||||
|
stderr_base_path=self.local.stderr_base_path,
|
||||||
|
save_output_streams=save_output_streams)
|
||||||
|
self.remote.create_files_dirs()
|
||||||
|
|
||||||
|
self.code = code.Code(self.target_host, self.local, self.remote)
|
||||||
|
|
||||||
|
self.manifest = manifest.Manifest(self.target_host, self.local)
|
||||||
|
|
||||||
|
self.cdist_type = core.CdistType(self.local.type_path,
|
||||||
|
'__write_to_stdout_and_stderr')
|
||||||
|
self.cdist_object = core.CdistObject(self.cdist_type,
|
||||||
|
self.local.object_path,
|
||||||
|
self.local.object_marker_name,
|
||||||
|
'')
|
||||||
|
self.cdist_object.create()
|
||||||
|
self.output_dirs = {
|
||||||
|
'object': {
|
||||||
|
'stdout': os.path.join(self.cdist_object.absolute_path,
|
||||||
|
'stdout'),
|
||||||
|
'stderr': os.path.join(self.cdist_object.absolute_path,
|
||||||
|
'stderr'),
|
||||||
|
},
|
||||||
|
'init': {
|
||||||
|
'stdout': os.path.join(self.local.base_path, 'stdout'),
|
||||||
|
'stderr': os.path.join(self.local.base_path, 'stderr'),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
def tearDown(self):
|
||||||
|
shutil.rmtree(self.local_dir)
|
||||||
|
shutil.rmtree(self.remote_dir)
|
||||||
|
shutil.rmtree(self.temp_dir)
|
||||||
|
|
||||||
|
def _test_output(self, which, target, streams=('stdout', 'stderr')):
|
||||||
|
for stream in streams:
|
||||||
|
stream_path = os.path.join(self.output_dirs[target][stream], which)
|
||||||
|
if os.path.exists(stream_path):
|
||||||
|
with open(stream_path, 'r') as fd:
|
||||||
|
_is = fd.read()
|
||||||
|
self.assertEqual("", _is)
|
||||||
|
# else ok when not exists
|
||||||
|
|
||||||
|
def test_capture_code_output_disabled(self):
|
||||||
|
self.cdist_object.code_local = self.code.run_gencode_local(
|
||||||
|
self.cdist_object)
|
||||||
|
self._test_output('gencode-local', 'object', ('stderr',))
|
||||||
|
|
||||||
|
self.code.run_code_local(self.cdist_object)
|
||||||
|
self._test_output('code-local', 'object')
|
||||||
|
|
||||||
|
self.cdist_object.code_remote = self.code.run_gencode_remote(
|
||||||
|
self.cdist_object)
|
||||||
|
self._test_output('gencode-remote', 'object', ('stderr',))
|
||||||
|
|
||||||
|
self.code.transfer_code_remote(self.cdist_object)
|
||||||
|
self.code.run_code_remote(self.cdist_object)
|
||||||
|
self._test_output('code-remote', 'object')
|
||||||
|
|
||||||
|
def test_capture_manifest_output_disabled(self):
|
||||||
|
self.manifest.run_type_manifest(self.cdist_object)
|
||||||
|
self._test_output('manifest', 'object')
|
||||||
|
|
||||||
|
def test_capture_init_manifest_output_disabled(self):
|
||||||
|
initial_manifest = os.path.join(conf_dir, 'manifest', 'init')
|
||||||
|
self.manifest.run_initial_manifest(initial_manifest)
|
||||||
|
self._test_output('init', 'init')
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
import unittest
|
||||||
|
|
||||||
|
unittest.main()
|
4
cdist/test/capture_output_disabled/fixtures/conf/manifest/init
Executable file
4
cdist/test/capture_output_disabled/fixtures/conf/manifest/init
Executable file
|
@ -0,0 +1,4 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
echo "init: stdout"
|
||||||
|
echo "init: stderr" >&2
|
|
@ -0,0 +1,6 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
echo "gencode-local: stderr" >&2
|
||||||
|
|
||||||
|
echo "echo \"code-local: stdout\""
|
||||||
|
echo "echo \"code-local: stderr\" >&2"
|
|
@ -0,0 +1,6 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
echo "gencode-remote: stderr" >&2
|
||||||
|
|
||||||
|
echo "echo \"code-remote: stdout\""
|
||||||
|
echo "echo \"code-remote: stderr\" >&2"
|
|
@ -0,0 +1,4 @@
|
||||||
|
#!/bin/sh
|
||||||
|
|
||||||
|
echo "manifest: stdout"
|
||||||
|
echo "manifest: stderr" >&2
|
|
@ -300,6 +300,7 @@ class ConfigurationTestCase(test.CdistTestCase):
|
||||||
expected = {
|
expected = {
|
||||||
'conf_dir': ['/usr/local/cdist1', ],
|
'conf_dir': ['/usr/local/cdist1', ],
|
||||||
'verbosity': 3,
|
'verbosity': 3,
|
||||||
|
'beta': False,
|
||||||
}
|
}
|
||||||
args_dict = vars(args)
|
args_dict = vars(args)
|
||||||
d = config._read_args_config(args_dict)
|
d = config._read_args_config(args_dict)
|
||||||
|
@ -1167,6 +1168,118 @@ class ConfigurationTestCase(test.CdistTestCase):
|
||||||
configuration = cc.Configuration(args, env=env,
|
configuration = cc.Configuration(args, env=env,
|
||||||
config_files=())
|
config_files=())
|
||||||
|
|
||||||
|
def test_configuration_disable_saving_output_streams1(self):
|
||||||
|
config = configparser.ConfigParser()
|
||||||
|
config['GLOBAL'] = {
|
||||||
|
'save_output_streams': 'True',
|
||||||
|
}
|
||||||
|
|
||||||
|
global_config_file = os.path.join(fixtures, 'cdist-global.cfg')
|
||||||
|
with open(global_config_file, 'w') as f:
|
||||||
|
config.write(f)
|
||||||
|
|
||||||
|
expected_config_dict = {
|
||||||
|
'GLOBAL': {
|
||||||
|
'save_output_streams': True,
|
||||||
|
'verbosity': 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
config_files = (global_config_file, )
|
||||||
|
|
||||||
|
# bypass singleton so we can test further
|
||||||
|
cc.Configuration.instance = None
|
||||||
|
|
||||||
|
args = argparse.Namespace()
|
||||||
|
args.save_output_streams = True
|
||||||
|
configuration = cc.Configuration(args, env=None,
|
||||||
|
config_files=config_files)
|
||||||
|
self.assertEqual(configuration.config, expected_config_dict)
|
||||||
|
|
||||||
|
def test_configuration_disable_saving_output_streams2(self):
|
||||||
|
config = configparser.ConfigParser()
|
||||||
|
config['GLOBAL'] = {
|
||||||
|
'save_output_streams': 'False',
|
||||||
|
}
|
||||||
|
|
||||||
|
global_config_file = os.path.join(fixtures, 'cdist-global.cfg')
|
||||||
|
with open(global_config_file, 'w') as f:
|
||||||
|
config.write(f)
|
||||||
|
|
||||||
|
expected_config_dict = {
|
||||||
|
'GLOBAL': {
|
||||||
|
'save_output_streams': True,
|
||||||
|
'verbosity': 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
config_files = (global_config_file, )
|
||||||
|
|
||||||
|
# bypass singleton so we can test further
|
||||||
|
cc.Configuration.instance = None
|
||||||
|
|
||||||
|
args = argparse.Namespace()
|
||||||
|
args.save_output_streams = True
|
||||||
|
configuration = cc.Configuration(args, env=None,
|
||||||
|
config_files=config_files)
|
||||||
|
self.assertEqual(configuration.config, expected_config_dict)
|
||||||
|
|
||||||
|
def test_configuration_disable_saving_output_streams3(self):
|
||||||
|
config = configparser.ConfigParser()
|
||||||
|
config['GLOBAL'] = {
|
||||||
|
'save_output_streams': 'False',
|
||||||
|
}
|
||||||
|
|
||||||
|
global_config_file = os.path.join(fixtures, 'cdist-global.cfg')
|
||||||
|
with open(global_config_file, 'w') as f:
|
||||||
|
config.write(f)
|
||||||
|
|
||||||
|
expected_config_dict = {
|
||||||
|
'GLOBAL': {
|
||||||
|
'save_output_streams': False,
|
||||||
|
'verbosity': 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
config_files = (global_config_file, )
|
||||||
|
|
||||||
|
# bypass singleton so we can test further
|
||||||
|
cc.Configuration.instance = None
|
||||||
|
|
||||||
|
args = argparse.Namespace()
|
||||||
|
args.save_output_streams = False
|
||||||
|
configuration = cc.Configuration(args, env=None,
|
||||||
|
config_files=config_files)
|
||||||
|
self.assertEqual(configuration.config, expected_config_dict)
|
||||||
|
|
||||||
|
def test_configuration_disable_saving_output_streams4(self):
|
||||||
|
config = configparser.ConfigParser()
|
||||||
|
config['GLOBAL'] = {
|
||||||
|
'save_output_streams': 'True',
|
||||||
|
}
|
||||||
|
|
||||||
|
global_config_file = os.path.join(fixtures, 'cdist-global.cfg')
|
||||||
|
with open(global_config_file, 'w') as f:
|
||||||
|
config.write(f)
|
||||||
|
|
||||||
|
expected_config_dict = {
|
||||||
|
'GLOBAL': {
|
||||||
|
'save_output_streams': False,
|
||||||
|
'verbosity': 0,
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
config_files = (global_config_file, )
|
||||||
|
|
||||||
|
# bypass singleton so we can test further
|
||||||
|
cc.Configuration.instance = None
|
||||||
|
|
||||||
|
args = argparse.Namespace()
|
||||||
|
args.save_output_streams = False
|
||||||
|
configuration = cc.Configuration(args, env=None,
|
||||||
|
config_files=config_files)
|
||||||
|
self.assertEqual(configuration.config, expected_config_dict)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == "__main__":
|
||||||
import unittest
|
import unittest
|
||||||
|
|
|
@ -87,10 +87,12 @@ state
|
||||||
this type execution state ('done' when finished)
|
this type execution state ('done' when finished)
|
||||||
|
|
||||||
stderr
|
stderr
|
||||||
directory containing type's gencode-* and code-* stderr stream outputs
|
directory containing type's manifest, gencode-* and code-* stderr stream
|
||||||
|
outputs
|
||||||
|
|
||||||
stdin
|
stdin
|
||||||
this type stdin content
|
this type stdin content
|
||||||
|
|
||||||
stdout
|
stdout
|
||||||
directory containing type's gencode-* and code-* stdout stream outputs.
|
directory containing type's manifest, gencode-* and code-* stdout stream
|
||||||
|
outputs.
|
||||||
|
|
|
@ -88,6 +88,11 @@ The possible keywords and their meanings are as follows:
|
||||||
:strong:`remote_shell`
|
:strong:`remote_shell`
|
||||||
Shell command at remote host used for remote execution.
|
Shell command at remote host used for remote execution.
|
||||||
|
|
||||||
|
:strong:`save_output_streams`
|
||||||
|
Enable/disable saving output streams (enabled by default).
|
||||||
|
It recognizes boolean values from 'yes'/'no', 'on'/'off', 'true'/'false'
|
||||||
|
and '1'/'0'.
|
||||||
|
|
||||||
:strong:`verbosity`
|
:strong:`verbosity`
|
||||||
Set verbosity level. Valid values are:
|
Set verbosity level. Valid values are:
|
||||||
'ERROR', 'WARNING', 'INFO', 'VERBOSE', 'DEBUG', 'TRACE' and 'OFF'.
|
'ERROR', 'WARNING', 'INFO', 'VERBOSE', 'DEBUG', 'TRACE' and 'OFF'.
|
||||||
|
|
88
docs/src/cdist-saving-output-streams.rst
Normal file
88
docs/src/cdist-saving-output-streams.rst
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
Saving output streams
|
||||||
|
=====================
|
||||||
|
|
||||||
|
Description
|
||||||
|
-----------
|
||||||
|
Since version 4.8.0 cdist, by default, saves output streams to local cache.
|
||||||
|
Saving output streams is implemented because important information was lost
|
||||||
|
during a config run, hidden in all other output.
|
||||||
|
Now all created output is bound to the context where it was produced.
|
||||||
|
|
||||||
|
Saving output streams include stdout and stderr of init manifest, remote
|
||||||
|
commands and for each object stdout and stderr of manifest, gencode-* and code-*.
|
||||||
|
Output stream files are created only if some output is produced. For more info
|
||||||
|
on these cache files see `Local cache overview <cdist-cache.html>`_.
|
||||||
|
|
||||||
|
Also, in case of an error, cdist can now exit and show all information it has
|
||||||
|
about the error.
|
||||||
|
|
||||||
|
For example:
|
||||||
|
|
||||||
|
.. code-block:: sh
|
||||||
|
|
||||||
|
$ ./bin/cdist config -v -i ~/.cdist/manifest/init-output-streams $(cat ~/ungleich/data/opennebula-debian9-test )
|
||||||
|
INFO: 185.203.112.42: Starting configuration run
|
||||||
|
INFO: 185.203.112.42: Processing __myline/test
|
||||||
|
ERROR: 185.203.112.42: Command failed: '/bin/sh -e /tmp/tmpow6cwemh/75ee6a79e32da093da23fe4a13dd104b/data/object/__myline/test/.cdist-kisrqlpw/code-local'
|
||||||
|
return code: 1
|
||||||
|
---- BEGIN stdout ----
|
||||||
|
---- END stdout ----
|
||||||
|
|
||||||
|
Error processing object '__myline/test'
|
||||||
|
========================================
|
||||||
|
name: __myline/test
|
||||||
|
path: /tmp/tmpow6cwemh/75ee6a79e32da093da23fe4a13dd104b/data/object/__myline/test/.cdist-kisrqlpw
|
||||||
|
source: /home/darko/.cdist/manifest/init-output-streams
|
||||||
|
type: /tmp/tmpow6cwemh/75ee6a79e32da093da23fe4a13dd104b/data/conf/type/__myline
|
||||||
|
|
||||||
|
---- BEGIN manifest:stderr ----
|
||||||
|
myline manifest stderr
|
||||||
|
|
||||||
|
---- END manifest:stderr ----
|
||||||
|
|
||||||
|
---- BEGIN gencode-remote:stderr ----
|
||||||
|
test gencode-remote error
|
||||||
|
|
||||||
|
---- END gencode-remote:stderr ----
|
||||||
|
|
||||||
|
---- BEGIN code-local:stderr ----
|
||||||
|
error
|
||||||
|
|
||||||
|
---- END code-local:stderr ----
|
||||||
|
|
||||||
|
ERROR: cdist: Failed to configure the following hosts: 185.203.112.42
|
||||||
|
|
||||||
|
Upon successful run execution state is saved to local cache and temporary
|
||||||
|
directory is removed.
|
||||||
|
In case of an error temporary directory is not removed and can be further
|
||||||
|
discovered.
|
||||||
|
|
||||||
|
There is also an option :strong:`-S/--disable-saving-output-streams` for
|
||||||
|
disabling saving output streams. In this case error reporting can look
|
||||||
|
like this:
|
||||||
|
|
||||||
|
.. code-block:: sh
|
||||||
|
|
||||||
|
$ ./bin/cdist config -v -S -i ~/.cdist/manifest/init-output-streams $(cat ~/ungleich/data/opennebula-debian9-test )
|
||||||
|
INFO: 185.203.112.42: Starting configuration run
|
||||||
|
test stdout output streams
|
||||||
|
test stderr output streams
|
||||||
|
myline manifest stdout
|
||||||
|
myline manifest stderr
|
||||||
|
test gencode-remote error
|
||||||
|
INFO: 185.203.112.42: Processing __myline/test
|
||||||
|
error
|
||||||
|
ERROR: 185.203.112.42: Command failed: '/bin/sh -e /tmp/tmpzomy0wis/75ee6a79e32da093da23fe4a13dd104b/data/object/__myline/test/.cdist-n566pqut/code-local'
|
||||||
|
return code: 1
|
||||||
|
---- BEGIN stdout ----
|
||||||
|
---- END stdout ----
|
||||||
|
|
||||||
|
Error processing object '__myline/test'
|
||||||
|
========================================
|
||||||
|
name: __myline/test
|
||||||
|
path: /tmp/tmpzomy0wis/75ee6a79e32da093da23fe4a13dd104b/data/object/__myline/test/.cdist-n566pqut
|
||||||
|
source: /home/darko/.cdist/manifest/init-output-streams
|
||||||
|
type: /tmp/tmpzomy0wis/75ee6a79e32da093da23fe4a13dd104b/data/conf/type/__myline
|
||||||
|
|
||||||
|
|
||||||
|
ERROR: cdist: Failed to configure the following hosts: 185.203.112.42
|
|
@ -31,6 +31,7 @@ Contents:
|
||||||
cdist-best-practice
|
cdist-best-practice
|
||||||
cdist-stages
|
cdist-stages
|
||||||
cdist-cache
|
cdist-cache
|
||||||
|
cdist-saving-output-streams
|
||||||
cdist-remote-exec-copy
|
cdist-remote-exec-copy
|
||||||
cdist-hacker
|
cdist-hacker
|
||||||
cdist-troubleshooting
|
cdist-troubleshooting
|
||||||
|
|
|
@ -21,7 +21,7 @@ SYNOPSIS
|
||||||
[-j [JOBS]] [-n] [-o OUT_PATH] [-R [{tar,tgz,tbz2,txz}]]
|
[-j [JOBS]] [-n] [-o OUT_PATH] [-R [{tar,tgz,tbz2,txz}]]
|
||||||
[-r REMOTE_OUT_DIR] [--remote-copy REMOTE_COPY]
|
[-r REMOTE_OUT_DIR] [--remote-copy REMOTE_COPY]
|
||||||
[--remote-exec REMOTE_EXEC] [-I INVENTORY_DIR] [-A] [-a]
|
[--remote-exec REMOTE_EXEC] [-I INVENTORY_DIR] [-A] [-a]
|
||||||
[-f HOSTFILE] [-p [HOST_MAX]] [-s] [-t]
|
[-f HOSTFILE] [-p [HOST_MAX]] [-S] [-s] [-t]
|
||||||
[host [host ...]]
|
[host [host ...]]
|
||||||
|
|
||||||
cdist install [-h] [-l LOGLEVEL] [-q] [-v] [-b] [-g CONFIG_FILE]
|
cdist install [-h] [-l LOGLEVEL] [-q] [-v] [-b] [-g CONFIG_FILE]
|
||||||
|
@ -29,7 +29,7 @@ SYNOPSIS
|
||||||
[-j [JOBS]] [-n] [-o OUT_PATH] [-R [{tar,tgz,tbz2,txz}]]
|
[-j [JOBS]] [-n] [-o OUT_PATH] [-R [{tar,tgz,tbz2,txz}]]
|
||||||
[-r REMOTE_OUT_DIR] [--remote-copy REMOTE_COPY]
|
[-r REMOTE_OUT_DIR] [--remote-copy REMOTE_COPY]
|
||||||
[--remote-exec REMOTE_EXEC] [-I INVENTORY_DIR] [-A] [-a]
|
[--remote-exec REMOTE_EXEC] [-I INVENTORY_DIR] [-A] [-a]
|
||||||
[-f HOSTFILE] [-p [HOST_MAX]] [-s] [-t]
|
[-f HOSTFILE] [-p [HOST_MAX]] [-S] [-s] [-t]
|
||||||
[host [host ...]]
|
[host [host ...]]
|
||||||
|
|
||||||
cdist inventory [-h] [-l LOGLEVEL] [-q] [-v] [-b] [-g CONFIG_FILE]
|
cdist inventory [-h] [-l LOGLEVEL] [-q] [-v] [-b] [-g CONFIG_FILE]
|
||||||
|
@ -200,6 +200,10 @@ Install command is currently in beta.
|
||||||
|
|
||||||
Directory to save cdist output in on the target host.
|
Directory to save cdist output in on the target host.
|
||||||
|
|
||||||
|
.. option:: -S, --disable-saving-output-streams
|
||||||
|
|
||||||
|
Disable saving output streams.
|
||||||
|
|
||||||
.. option:: -s, --sequential
|
.. option:: -s, --sequential
|
||||||
|
|
||||||
Operate on multiple hosts sequentially (default).
|
Operate on multiple hosts sequentially (default).
|
||||||
|
@ -561,6 +565,11 @@ The possible keywords and their meanings are as follows:
|
||||||
:strong:`remote_shell`
|
:strong:`remote_shell`
|
||||||
Shell command at remote host used for remote execution.
|
Shell command at remote host used for remote execution.
|
||||||
|
|
||||||
|
:strong:`save_output_streams`
|
||||||
|
Enable/disable saving output streams (enabled by default).
|
||||||
|
It recognizes boolean values from 'yes'/'no', 'on'/'off', 'true'/'false'
|
||||||
|
and '1'/'0'.
|
||||||
|
|
||||||
:strong:`verbosity`
|
:strong:`verbosity`
|
||||||
Set verbosity level. Valid values are:
|
Set verbosity level. Valid values are:
|
||||||
'ERROR', 'WARNING', 'INFO', 'VERBOSE', 'DEBUG', 'TRACE' and 'OFF'.
|
'ERROR', 'WARNING', 'INFO', 'VERBOSE', 'DEBUG', 'TRACE' and 'OFF'.
|
||||||
|
|
Loading…
Reference in a new issue