forked from ungleich-public/cdist
Merge remote-tracking branch 'ungleich/master' into ssh-mux-sigpipe
This commit is contained in:
commit
01d7f63fcb
44 changed files with 322 additions and 148 deletions
|
@ -22,6 +22,7 @@
|
||||||
import os
|
import os
|
||||||
import hashlib
|
import hashlib
|
||||||
|
|
||||||
|
import cdist.log
|
||||||
import cdist.version
|
import cdist.version
|
||||||
|
|
||||||
VERSION = cdist.version.VERSION
|
VERSION = cdist.version.VERSION
|
||||||
|
|
|
@ -17,13 +17,19 @@ EPILOG = "Get cdist at http://www.nico.schottelius.org/software/cdist/"
|
||||||
parser = None
|
parser = None
|
||||||
|
|
||||||
|
|
||||||
|
_verbosity_level_off = -2
|
||||||
_verbosity_level = {
|
_verbosity_level = {
|
||||||
0: logging.ERROR,
|
_verbosity_level_off: logging.OFF,
|
||||||
1: logging.WARNING,
|
-1: logging.ERROR,
|
||||||
2: logging.INFO,
|
0: logging.WARNING,
|
||||||
|
1: logging.INFO,
|
||||||
|
2: logging.VERBOSE,
|
||||||
|
3: logging.DEBUG,
|
||||||
|
4: logging.TRACE,
|
||||||
}
|
}
|
||||||
|
# All verbosity levels above 4 are TRACE.
|
||||||
_verbosity_level = collections.defaultdict(
|
_verbosity_level = collections.defaultdict(
|
||||||
lambda: logging.DEBUG, _verbosity_level)
|
lambda: logging.TRACE, _verbosity_level)
|
||||||
|
|
||||||
|
|
||||||
def add_beta_command(cmd):
|
def add_beta_command(cmd):
|
||||||
|
@ -80,16 +86,17 @@ def get_parsers():
|
||||||
# Options _all_ parsers have in common
|
# Options _all_ parsers have in common
|
||||||
parser['loglevel'] = argparse.ArgumentParser(add_help=False)
|
parser['loglevel'] = argparse.ArgumentParser(add_help=False)
|
||||||
parser['loglevel'].add_argument(
|
parser['loglevel'].add_argument(
|
||||||
'-d', '--debug',
|
'-q', '--quiet',
|
||||||
help=('Set log level to debug (deprecated, use -vvv instead)'),
|
help='Quiet mode: disables logging, including WARNING and ERROR',
|
||||||
action='store_true', default=False)
|
action='store_true', default=False)
|
||||||
parser['loglevel'].add_argument(
|
parser['loglevel'].add_argument(
|
||||||
'-v', '--verbose',
|
'-v', '--verbose',
|
||||||
help=('Increase the verbosity level. Every instance of -v '
|
help=('Increase the verbosity level. Every instance of -v '
|
||||||
'increments the verbosity level by one. Its default value '
|
'increments the verbosity level by one. Its default value '
|
||||||
'is 0. There are 4 levels of verbosity. The order of levels '
|
'is 0 which includes ERROR and WARNING levels. '
|
||||||
'from the lowest to the highest are: ERROR (0), '
|
'The levels, in order from the lowest to the highest, are: '
|
||||||
'WARNING (1), INFO (2) and DEBUG (3 or higher).'),
|
'ERROR (-1), WARNING (0), INFO (1), VERBOSE (2), DEBUG (3) '
|
||||||
|
'TRACE (4 or higher).'),
|
||||||
action='count', default=0)
|
action='count', default=0)
|
||||||
|
|
||||||
parser['beta'] = argparse.ArgumentParser(add_help=False)
|
parser['beta'] = argparse.ArgumentParser(add_help=False)
|
||||||
|
@ -211,12 +218,7 @@ def get_parsers():
|
||||||
|
|
||||||
|
|
||||||
def handle_loglevel(args):
|
def handle_loglevel(args):
|
||||||
if args.debug:
|
if args.quiet:
|
||||||
retval = "-d/--debug is deprecated, use -vvv instead"
|
args.verbose = _verbosity_level_off
|
||||||
args.verbose = 3
|
|
||||||
else:
|
|
||||||
retval = None
|
|
||||||
|
|
||||||
logging.root.setLevel(_verbosity_level[args.verbose])
|
logging.root.setLevel(_verbosity_level[args.verbose])
|
||||||
|
|
||||||
return retval
|
|
||||||
|
|
|
@ -29,8 +29,11 @@ upload_file=
|
||||||
create_file=
|
create_file=
|
||||||
if [ "$state_should" = "present" -o "$state_should" = "exists" ]; then
|
if [ "$state_should" = "present" -o "$state_should" = "exists" ]; then
|
||||||
if [ ! -f "$__object/parameter/source" ]; then
|
if [ ! -f "$__object/parameter/source" ]; then
|
||||||
|
remote_stat="$(cat "$__object/explorer/stat")"
|
||||||
|
if [ -z "$remote_stat" ]; then
|
||||||
create_file=1
|
create_file=1
|
||||||
echo create >> "$__messages_out"
|
echo create >> "$__messages_out"
|
||||||
|
fi
|
||||||
else
|
else
|
||||||
source="$(cat "$__object/parameter/source")"
|
source="$(cat "$__object/parameter/source")"
|
||||||
if [ "$source" = "-" ]; then
|
if [ "$source" = "-" ]; then
|
||||||
|
|
|
@ -1 +1,3 @@
|
||||||
|
#!/bin/sh -e
|
||||||
|
|
||||||
__line go_in_path --line 'export PATH=/usr/local/go/bin:$PATH' --file /etc/profile
|
__line go_in_path --line 'export PATH=/usr/local/go/bin:$PATH' --file /etc/profile
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
#!/bin/sh -e
|
||||||
|
|
||||||
os=$(cat $__global/explorer/os)
|
os=$(cat $__global/explorer/os)
|
||||||
os_version=$(cat $__global/explorer/os_version)
|
os_version=$(cat $__global/explorer/os_version)
|
||||||
|
|
||||||
|
|
1
cdist/conf/type/__install_chroot_mount/gencode-local
Symbolic link
1
cdist/conf/type/__install_chroot_mount/gencode-local
Symbolic link
|
@ -0,0 +1 @@
|
||||||
|
../__chroot_mount/gencode-local
|
0
cdist/conf/type/__package/nonparallel
Normal file
0
cdist/conf/type/__package/nonparallel
Normal file
0
cdist/conf/type/__package_apt/nonparallel
Normal file
0
cdist/conf/type/__package_apt/nonparallel
Normal file
0
cdist/conf/type/__package_dpkg/nonparallel
Normal file
0
cdist/conf/type/__package_dpkg/nonparallel
Normal file
0
cdist/conf/type/__package_emerge/nonparallel
Normal file
0
cdist/conf/type/__package_emerge/nonparallel
Normal file
0
cdist/conf/type/__package_luarocks/nonparallel
Normal file
0
cdist/conf/type/__package_luarocks/nonparallel
Normal file
0
cdist/conf/type/__package_opkg/nonparallel
Normal file
0
cdist/conf/type/__package_opkg/nonparallel
Normal file
0
cdist/conf/type/__package_pacman/nonparallel
Normal file
0
cdist/conf/type/__package_pacman/nonparallel
Normal file
0
cdist/conf/type/__package_pip/nonparallel
Normal file
0
cdist/conf/type/__package_pip/nonparallel
Normal file
0
cdist/conf/type/__package_pkg_freebsd/nonparallel
Normal file
0
cdist/conf/type/__package_pkg_freebsd/nonparallel
Normal file
0
cdist/conf/type/__package_pkg_openbsd/nonparallel
Normal file
0
cdist/conf/type/__package_pkg_openbsd/nonparallel
Normal file
0
cdist/conf/type/__package_pkgng_freebsd/nonparallel
Normal file
0
cdist/conf/type/__package_pkgng_freebsd/nonparallel
Normal file
0
cdist/conf/type/__package_rubygem/nonparallel
Normal file
0
cdist/conf/type/__package_rubygem/nonparallel
Normal file
0
cdist/conf/type/__package_update_index/nonparallel
Normal file
0
cdist/conf/type/__package_update_index/nonparallel
Normal file
0
cdist/conf/type/__package_upgrade_all/nonparallel
Normal file
0
cdist/conf/type/__package_upgrade_all/nonparallel
Normal file
0
cdist/conf/type/__package_yum/nonparallel
Normal file
0
cdist/conf/type/__package_yum/nonparallel
Normal file
0
cdist/conf/type/__package_zypper/nonparallel
Normal file
0
cdist/conf/type/__package_zypper/nonparallel
Normal file
112
cdist/config.py
112
cdist/config.py
|
@ -151,7 +151,7 @@ class Config(object):
|
||||||
|
|
||||||
hostcnt += 1
|
hostcnt += 1
|
||||||
if args.parallel:
|
if args.parallel:
|
||||||
log.debug("Creating child process for %s", host)
|
log.trace("Creating child process for %s", host)
|
||||||
process[host] = multiprocessing.Process(
|
process[host] = multiprocessing.Process(
|
||||||
target=cls.onehost,
|
target=cls.onehost,
|
||||||
args=(host, host_base_path, hostdir, args, True))
|
args=(host, host_base_path, hostdir, args, True))
|
||||||
|
@ -166,14 +166,14 @@ class Config(object):
|
||||||
# Catch errors in parallel mode when joining
|
# Catch errors in parallel mode when joining
|
||||||
if args.parallel:
|
if args.parallel:
|
||||||
for host in process.keys():
|
for host in process.keys():
|
||||||
log.debug("Joining process %s", host)
|
log.trace("Joining process %s", host)
|
||||||
process[host].join()
|
process[host].join()
|
||||||
|
|
||||||
if not process[host].exitcode == 0:
|
if not process[host].exitcode == 0:
|
||||||
failed_hosts.append(host)
|
failed_hosts.append(host)
|
||||||
|
|
||||||
time_end = time.time()
|
time_end = time.time()
|
||||||
log.info("Total processing time for %s host(s): %s", hostcnt,
|
log.verbose("Total processing time for %s host(s): %s", hostcnt,
|
||||||
(time_end - time_start))
|
(time_end - time_start))
|
||||||
|
|
||||||
if len(failed_hosts) > 0:
|
if len(failed_hosts) > 0:
|
||||||
|
@ -233,13 +233,15 @@ class Config(object):
|
||||||
host_dir_name=host_dir_name,
|
host_dir_name=host_dir_name,
|
||||||
initial_manifest=args.manifest,
|
initial_manifest=args.manifest,
|
||||||
add_conf_dirs=args.conf_dir,
|
add_conf_dirs=args.conf_dir,
|
||||||
cache_path_pattern=args.cache_path_pattern)
|
cache_path_pattern=args.cache_path_pattern,
|
||||||
|
quiet_mode=args.quiet)
|
||||||
|
|
||||||
remote = cdist.exec.remote.Remote(
|
remote = cdist.exec.remote.Remote(
|
||||||
target_host=target_host,
|
target_host=target_host,
|
||||||
remote_exec=remote_exec,
|
remote_exec=remote_exec,
|
||||||
remote_copy=remote_copy,
|
remote_copy=remote_copy,
|
||||||
base_path=args.remote_out_path)
|
base_path=args.remote_out_path,
|
||||||
|
quiet_mode=args.quiet)
|
||||||
|
|
||||||
cleanup_cmds = []
|
cleanup_cmds = []
|
||||||
if cleanup_cmd:
|
if cleanup_cmd:
|
||||||
|
@ -284,6 +286,8 @@ class Config(object):
|
||||||
"""Do what is most often done: deploy & cleanup"""
|
"""Do what is most often done: deploy & cleanup"""
|
||||||
start_time = time.time()
|
start_time = time.time()
|
||||||
|
|
||||||
|
self.log.info("Starting configuration run")
|
||||||
|
|
||||||
self._init_files_dirs()
|
self._init_files_dirs()
|
||||||
|
|
||||||
self.explorer.run_global_explorers(self.local.global_explorer_out_path)
|
self.explorer.run_global_explorers(self.local.global_explorer_out_path)
|
||||||
|
@ -292,8 +296,8 @@ class Config(object):
|
||||||
self.cleanup()
|
self.cleanup()
|
||||||
|
|
||||||
self.local.save_cache(start_time)
|
self.local.save_cache(start_time)
|
||||||
self.log.info("Finished successful run in %s seconds",
|
self.log.info("Finished successful run in {:.2f} seconds".format(
|
||||||
time.time() - start_time)
|
time.time() - start_time))
|
||||||
|
|
||||||
def cleanup(self):
|
def cleanup(self):
|
||||||
self.log.debug("Running cleanup commands")
|
self.log.debug("Running cleanup commands")
|
||||||
|
@ -329,7 +333,7 @@ class Config(object):
|
||||||
return objects_changed
|
return objects_changed
|
||||||
|
|
||||||
def _iterate_once_sequential(self):
|
def _iterate_once_sequential(self):
|
||||||
self.log.info("Iteration in sequential mode")
|
self.log.debug("Iteration in sequential mode")
|
||||||
objects_changed = False
|
objects_changed = False
|
||||||
|
|
||||||
for cdist_object in self.object_list():
|
for cdist_object in self.object_list():
|
||||||
|
@ -356,7 +360,7 @@ class Config(object):
|
||||||
return objects_changed
|
return objects_changed
|
||||||
|
|
||||||
def _iterate_once_parallel(self):
|
def _iterate_once_parallel(self):
|
||||||
self.log.info("Iteration in parallel mode in {} jobs".format(
|
self.log.debug("Iteration in parallel mode in {} jobs".format(
|
||||||
self.jobs))
|
self.jobs))
|
||||||
objects_changed = False
|
objects_changed = False
|
||||||
|
|
||||||
|
@ -379,15 +383,39 @@ class Config(object):
|
||||||
self.object_prepare(cargo[0])
|
self.object_prepare(cargo[0])
|
||||||
objects_changed = True
|
objects_changed = True
|
||||||
elif cargo:
|
elif cargo:
|
||||||
self.log.debug("Multiprocessing start method is {}".format(
|
self.log.trace("Multiprocessing start method is {}".format(
|
||||||
multiprocessing.get_start_method()))
|
multiprocessing.get_start_method()))
|
||||||
self.log.debug(("Starting multiprocessing Pool for {} parallel "
|
|
||||||
|
self.log.trace("Multiprocessing cargo: %s", cargo)
|
||||||
|
|
||||||
|
cargo_types = set()
|
||||||
|
for c in cargo:
|
||||||
|
cargo_types.add(c.cdist_type)
|
||||||
|
self.log.trace("Multiprocessing cargo_types: %s", cargo_types)
|
||||||
|
nt = len(cargo_types)
|
||||||
|
if nt == 1:
|
||||||
|
self.log.debug(("Only one type, transfering explorers "
|
||||||
|
"sequentially"))
|
||||||
|
self.explorer.transfer_type_explorers(cargo_types.pop())
|
||||||
|
else:
|
||||||
|
self.log.trace(("Starting multiprocessing Pool for {} "
|
||||||
|
"parallel transfering types' explorers".format(
|
||||||
|
nt)))
|
||||||
|
args = [
|
||||||
|
(ct, ) for ct in cargo_types
|
||||||
|
]
|
||||||
|
mp_pool_run(self.explorer.transfer_type_explorers, args,
|
||||||
|
jobs=self.jobs)
|
||||||
|
self.log.trace(("Multiprocessing for parallel transfering "
|
||||||
|
"types' explorers finished"))
|
||||||
|
|
||||||
|
self.log.trace(("Starting multiprocessing Pool for {} parallel "
|
||||||
"objects preparation".format(n)))
|
"objects preparation".format(n)))
|
||||||
args = [
|
args = [
|
||||||
(c, ) for c in cargo
|
(c, False, ) for c in cargo
|
||||||
]
|
]
|
||||||
mp_pool_run(self.object_prepare, args, jobs=self.jobs)
|
mp_pool_run(self.object_prepare, args, jobs=self.jobs)
|
||||||
self.log.debug(("Multiprocessing for parallel object "
|
self.log.trace(("Multiprocessing for parallel object "
|
||||||
"preparation finished"))
|
"preparation finished"))
|
||||||
objects_changed = True
|
objects_changed = True
|
||||||
|
|
||||||
|
@ -407,23 +435,42 @@ class Config(object):
|
||||||
|
|
||||||
# self.object_run(cdist_object)
|
# self.object_run(cdist_object)
|
||||||
# objects_changed = True
|
# objects_changed = True
|
||||||
cargo.append(cdist_object)
|
|
||||||
|
|
||||||
n = len(cargo)
|
# put objects in chuncks of distinct types
|
||||||
|
# so that there is no more than one object
|
||||||
|
# of the same type in one chunk because there is a
|
||||||
|
# possibility of object's process locking which
|
||||||
|
# prevents parallel execution at remote
|
||||||
|
# and do this only for nonparallel marked types
|
||||||
|
for chunk in cargo:
|
||||||
|
for obj in chunk:
|
||||||
|
if (obj.cdist_type == cdist_object.cdist_type and
|
||||||
|
cdist_object.cdist_type.is_nonparallel):
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
chunk.append(cdist_object)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
chunk = [cdist_object, ]
|
||||||
|
cargo.append(chunk)
|
||||||
|
|
||||||
|
for chunk in cargo:
|
||||||
|
self.log.trace("Running chunk: %s", chunk)
|
||||||
|
n = len(chunk)
|
||||||
if n == 1:
|
if n == 1:
|
||||||
self.log.debug("Only one object, running sequentially")
|
self.log.debug("Only one object, running sequentially")
|
||||||
self.object_run(cargo[0])
|
self.object_run(chunk[0])
|
||||||
objects_changed = True
|
objects_changed = True
|
||||||
elif cargo:
|
elif chunk:
|
||||||
self.log.debug("Multiprocessing start method is {}".format(
|
self.log.trace("Multiprocessing start method is {}".format(
|
||||||
multiprocessing.get_start_method()))
|
multiprocessing.get_start_method()))
|
||||||
self.log.debug(("Starting multiprocessing Pool for {} parallel "
|
self.log.trace(("Starting multiprocessing Pool for {} "
|
||||||
"object run".format(n)))
|
"parallel object run".format(n)))
|
||||||
args = [
|
args = [
|
||||||
(c, ) for c in cargo
|
(c, ) for c in chunk
|
||||||
]
|
]
|
||||||
mp_pool_run(self.object_run, args, jobs=self.jobs)
|
mp_pool_run(self.object_run, args, jobs=self.jobs)
|
||||||
self.log.debug(("Multiprocessing for parallel object "
|
self.log.trace(("Multiprocessing for parallel object "
|
||||||
"run finished"))
|
"run finished"))
|
||||||
objects_changed = True
|
objects_changed = True
|
||||||
|
|
||||||
|
@ -491,18 +538,19 @@ class Config(object):
|
||||||
("The requirements of the following objects could not be "
|
("The requirements of the following objects could not be "
|
||||||
"resolved:\n%s") % ("\n".join(info_string)))
|
"resolved:\n%s") % ("\n".join(info_string)))
|
||||||
|
|
||||||
def object_prepare(self, cdist_object):
|
def object_prepare(self, cdist_object, transfer_type_explorers=True):
|
||||||
"""Prepare object: Run type explorer + manifest"""
|
"""Prepare object: Run type explorer + manifest"""
|
||||||
self.log.info(
|
self.log.verbose("Preparing object {}".format(cdist_object.name))
|
||||||
|
self.log.verbose(
|
||||||
"Running manifest and explorers for " + cdist_object.name)
|
"Running manifest and explorers for " + cdist_object.name)
|
||||||
self.explorer.run_type_explorers(cdist_object)
|
self.explorer.run_type_explorers(cdist_object, transfer_type_explorers)
|
||||||
self.manifest.run_type_manifest(cdist_object)
|
self.manifest.run_type_manifest(cdist_object)
|
||||||
cdist_object.state = core.CdistObject.STATE_PREPARED
|
cdist_object.state = core.CdistObject.STATE_PREPARED
|
||||||
|
|
||||||
def object_run(self, cdist_object):
|
def object_run(self, cdist_object):
|
||||||
"""Run gencode and code for an object"""
|
"""Run gencode and code for an object"""
|
||||||
|
|
||||||
self.log.debug("Trying to run object %s" % (cdist_object.name))
|
self.log.verbose("Running object " + cdist_object.name)
|
||||||
if cdist_object.state == core.CdistObject.STATE_DONE:
|
if cdist_object.state == core.CdistObject.STATE_DONE:
|
||||||
raise cdist.Error(("Attempting to run an already finished "
|
raise cdist.Error(("Attempting to run an already finished "
|
||||||
"object: %s"), cdist_object)
|
"object: %s"), cdist_object)
|
||||||
|
@ -510,7 +558,7 @@ class Config(object):
|
||||||
cdist_type = cdist_object.cdist_type
|
cdist_type = cdist_object.cdist_type
|
||||||
|
|
||||||
# Generate
|
# Generate
|
||||||
self.log.info("Generating code for %s" % (cdist_object.name))
|
self.log.debug("Generating code for %s" % (cdist_object.name))
|
||||||
cdist_object.code_local = self.code.run_gencode_local(cdist_object)
|
cdist_object.code_local = self.code.run_gencode_local(cdist_object)
|
||||||
cdist_object.code_remote = self.code.run_gencode_remote(cdist_object)
|
cdist_object.code_remote = self.code.run_gencode_remote(cdist_object)
|
||||||
if cdist_object.code_local or cdist_object.code_remote:
|
if cdist_object.code_local or cdist_object.code_remote:
|
||||||
|
@ -519,15 +567,19 @@ class Config(object):
|
||||||
# Execute
|
# Execute
|
||||||
if not self.dry_run:
|
if not self.dry_run:
|
||||||
if cdist_object.code_local or cdist_object.code_remote:
|
if cdist_object.code_local or cdist_object.code_remote:
|
||||||
self.log.info("Executing code for %s" % (cdist_object.name))
|
self.log.info("Processing %s" % (cdist_object.name))
|
||||||
if cdist_object.code_local:
|
if cdist_object.code_local:
|
||||||
|
self.log.trace("Executing local code for %s"
|
||||||
|
% (cdist_object.name))
|
||||||
self.code.run_code_local(cdist_object)
|
self.code.run_code_local(cdist_object)
|
||||||
if cdist_object.code_remote:
|
if cdist_object.code_remote:
|
||||||
|
self.log.trace("Executing remote code for %s"
|
||||||
|
% (cdist_object.name))
|
||||||
self.code.transfer_code_remote(cdist_object)
|
self.code.transfer_code_remote(cdist_object)
|
||||||
self.code.run_code_remote(cdist_object)
|
self.code.run_code_remote(cdist_object)
|
||||||
else:
|
else:
|
||||||
self.log.info("Skipping code execution due to DRY RUN")
|
self.log.verbose("Skipping code execution due to DRY RUN")
|
||||||
|
|
||||||
# Mark this object as done
|
# Mark this object as done
|
||||||
self.log.debug("Finishing run of " + cdist_object.name)
|
self.log.trace("Finishing run of " + cdist_object.name)
|
||||||
cdist_object.state = core.CdistObject.STATE_DONE
|
cdist_object.state = core.CdistObject.STATE_DONE
|
||||||
|
|
|
@ -27,3 +27,4 @@ from cdist.core.cdist_object import IllegalObjectIdError
|
||||||
from cdist.core.explorer import Explorer
|
from cdist.core.explorer import Explorer
|
||||||
from cdist.core.manifest import Manifest
|
from cdist.core.manifest import Manifest
|
||||||
from cdist.core.code import Code
|
from cdist.core.code import Code
|
||||||
|
from cdist.core.util import listdir
|
||||||
|
|
|
@ -22,7 +22,6 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
import fnmatch
|
import fnmatch
|
||||||
import logging
|
|
||||||
import os
|
import os
|
||||||
import collections
|
import collections
|
||||||
|
|
||||||
|
@ -30,8 +29,6 @@ import cdist
|
||||||
import cdist.core
|
import cdist.core
|
||||||
from cdist.util import fsproperty
|
from cdist.util import fsproperty
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
class IllegalObjectIdError(cdist.Error):
|
class IllegalObjectIdError(cdist.Error):
|
||||||
def __init__(self, object_id, message=None):
|
def __init__(self, object_id, message=None):
|
||||||
|
@ -107,7 +104,7 @@ class CdistObject(object):
|
||||||
@classmethod
|
@classmethod
|
||||||
def list_type_names(cls, object_base_path):
|
def list_type_names(cls, object_base_path):
|
||||||
"""Return a list of type names"""
|
"""Return a list of type names"""
|
||||||
return os.listdir(object_base_path)
|
return cdist.core.listdir(object_base_path)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def split_name(object_name):
|
def split_name(object_name):
|
||||||
|
|
|
@ -21,8 +21,8 @@
|
||||||
#
|
#
|
||||||
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import cdist
|
import cdist
|
||||||
|
import cdist.core
|
||||||
|
|
||||||
|
|
||||||
class NoSuchTypeError(cdist.Error):
|
class NoSuchTypeError(cdist.Error):
|
||||||
|
@ -66,6 +66,9 @@ class CdistType(object):
|
||||||
self.__boolean_parameters = None
|
self.__boolean_parameters = None
|
||||||
self.__parameter_defaults = None
|
self.__parameter_defaults = None
|
||||||
|
|
||||||
|
def __hash__(self):
|
||||||
|
return hash(self.name)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def list_types(cls, base_path):
|
def list_types(cls, base_path):
|
||||||
"""Return a list of type instances"""
|
"""Return a list of type instances"""
|
||||||
|
@ -75,7 +78,7 @@ class CdistType(object):
|
||||||
@classmethod
|
@classmethod
|
||||||
def list_type_names(cls, base_path):
|
def list_type_names(cls, base_path):
|
||||||
"""Return a list of type names"""
|
"""Return a list of type names"""
|
||||||
return os.listdir(base_path)
|
return cdist.core.listdir(base_path)
|
||||||
|
|
||||||
_instances = {}
|
_instances = {}
|
||||||
|
|
||||||
|
@ -112,13 +115,19 @@ class CdistType(object):
|
||||||
(if not: for configuration)"""
|
(if not: for configuration)"""
|
||||||
return os.path.isfile(os.path.join(self.absolute_path, "install"))
|
return os.path.isfile(os.path.join(self.absolute_path, "install"))
|
||||||
|
|
||||||
|
@property
|
||||||
|
def is_nonparallel(self):
|
||||||
|
"""Check whether a type is a non parallel, i.e. its objects
|
||||||
|
cannot run in parallel."""
|
||||||
|
return os.path.isfile(os.path.join(self.absolute_path, "nonparallel"))
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def explorers(self):
|
def explorers(self):
|
||||||
"""Return a list of available explorers"""
|
"""Return a list of available explorers"""
|
||||||
if not self.__explorers:
|
if not self.__explorers:
|
||||||
try:
|
try:
|
||||||
self.__explorers = os.listdir(os.path.join(self.absolute_path,
|
self.__explorers = cdist.core.listdir(
|
||||||
"explorer"))
|
os.path.join(self.absolute_path, "explorer"))
|
||||||
except EnvironmentError:
|
except EnvironmentError:
|
||||||
# error ignored
|
# error ignored
|
||||||
self.__explorers = []
|
self.__explorers = []
|
||||||
|
@ -222,7 +231,7 @@ class CdistType(object):
|
||||||
defaults_dir = os.path.join(self.absolute_path,
|
defaults_dir = os.path.join(self.absolute_path,
|
||||||
"parameter",
|
"parameter",
|
||||||
"default")
|
"default")
|
||||||
for name in os.listdir(defaults_dir):
|
for name in cdist.core.listdir(defaults_dir):
|
||||||
try:
|
try:
|
||||||
with open(os.path.join(defaults_dir, name)) as fd:
|
with open(os.path.join(defaults_dir, name)) as fd:
|
||||||
defaults[name] = fd.read().strip()
|
defaults[name] = fd.read().strip()
|
||||||
|
|
|
@ -21,13 +21,10 @@
|
||||||
#
|
#
|
||||||
#
|
#
|
||||||
|
|
||||||
import logging
|
|
||||||
import os
|
import os
|
||||||
|
|
||||||
import cdist
|
import cdist
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
|
|
||||||
'''
|
'''
|
||||||
common:
|
common:
|
||||||
|
@ -143,8 +140,7 @@ class Code(object):
|
||||||
cdist_object.code_remote_path)
|
cdist_object.code_remote_path)
|
||||||
destination = os.path.join(self.remote.object_path,
|
destination = os.path.join(self.remote.object_path,
|
||||||
cdist_object.code_remote_path)
|
cdist_object.code_remote_path)
|
||||||
# FIXME: BUG: do not create destination, but top level of destination!
|
self.remote.mkdir(os.path.dirname(destination))
|
||||||
self.remote.mkdir(destination)
|
|
||||||
self.remote.transfer(source, destination)
|
self.remote.transfer(source, destination)
|
||||||
|
|
||||||
def _run_code(self, cdist_object, which, env=None):
|
def _run_code(self, cdist_object, which, env=None):
|
||||||
|
|
|
@ -95,7 +95,7 @@ class Explorer(object):
|
||||||
out_path directory.
|
out_path directory.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
self.log.info("Running global explorers")
|
self.log.verbose("Running global explorers")
|
||||||
self.transfer_global_explorers()
|
self.transfer_global_explorers()
|
||||||
if self.jobs is None:
|
if self.jobs is None:
|
||||||
self._run_global_explorers_seq(out_path)
|
self._run_global_explorers_seq(out_path)
|
||||||
|
@ -109,22 +109,22 @@ class Explorer(object):
|
||||||
fd.write(output)
|
fd.write(output)
|
||||||
|
|
||||||
def _run_global_explorers_seq(self, out_path):
|
def _run_global_explorers_seq(self, out_path):
|
||||||
self.log.info("Running global explorers sequentially")
|
self.log.debug("Running global explorers sequentially")
|
||||||
for explorer in self.list_global_explorer_names():
|
for explorer in self.list_global_explorer_names():
|
||||||
self._run_global_explorer(explorer, out_path)
|
self._run_global_explorer(explorer, out_path)
|
||||||
|
|
||||||
def _run_global_explorers_parallel(self, out_path):
|
def _run_global_explorers_parallel(self, out_path):
|
||||||
self.log.info("Running global explorers in {} parallel jobs".format(
|
self.log.debug("Running global explorers in {} parallel jobs".format(
|
||||||
self.jobs))
|
self.jobs))
|
||||||
self.log.debug("Multiprocessing start method is {}".format(
|
self.log.trace("Multiprocessing start method is {}".format(
|
||||||
multiprocessing.get_start_method()))
|
multiprocessing.get_start_method()))
|
||||||
self.log.debug(("Starting multiprocessing Pool for global "
|
self.log.trace(("Starting multiprocessing Pool for global "
|
||||||
"explorers run"))
|
"explorers run"))
|
||||||
args = [
|
args = [
|
||||||
(e, out_path, ) for e in self.list_global_explorer_names()
|
(e, out_path, ) for e in self.list_global_explorer_names()
|
||||||
]
|
]
|
||||||
mp_pool_run(self._run_global_explorer, args, jobs=self.jobs)
|
mp_pool_run(self._run_global_explorer, args, jobs=self.jobs)
|
||||||
self.log.debug(("Multiprocessing run for global explorers "
|
self.log.trace(("Multiprocessing run for global explorers "
|
||||||
"finished"))
|
"finished"))
|
||||||
|
|
||||||
# logger is not pickable, so remove it when we pickle
|
# logger is not pickable, so remove it when we pickle
|
||||||
|
@ -163,20 +163,27 @@ class Explorer(object):
|
||||||
except EnvironmentError:
|
except EnvironmentError:
|
||||||
return []
|
return []
|
||||||
|
|
||||||
def run_type_explorers(self, cdist_object):
|
def run_type_explorers(self, cdist_object, transfer_type_explorers=True):
|
||||||
"""Run the type explorers for the given object and save their output
|
"""Run the type explorers for the given object and save their output
|
||||||
in the object.
|
in the object.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
self.log.debug("Transfering type explorers for type: %s",
|
self.log.verbose("Running type explorers for {}".format(
|
||||||
|
cdist_object.cdist_type))
|
||||||
|
if transfer_type_explorers:
|
||||||
|
self.log.trace("Transfering type explorers for type: %s",
|
||||||
cdist_object.cdist_type)
|
cdist_object.cdist_type)
|
||||||
self.transfer_type_explorers(cdist_object.cdist_type)
|
self.transfer_type_explorers(cdist_object.cdist_type)
|
||||||
self.log.debug("Transfering object parameters for object: %s",
|
else:
|
||||||
|
self.log.trace(("No need for transfering type explorers for "
|
||||||
|
"type: %s"),
|
||||||
|
cdist_object.cdist_type)
|
||||||
|
self.log.trace("Transfering object parameters for object: %s",
|
||||||
cdist_object.name)
|
cdist_object.name)
|
||||||
self.transfer_object_parameters(cdist_object)
|
self.transfer_object_parameters(cdist_object)
|
||||||
for explorer in self.list_type_explorer_names(cdist_object.cdist_type):
|
for explorer in self.list_type_explorer_names(cdist_object.cdist_type):
|
||||||
output = self.run_type_explorer(explorer, cdist_object)
|
output = self.run_type_explorer(explorer, cdist_object)
|
||||||
self.log.debug("Running type explorer '%s' for object '%s'",
|
self.log.trace("Running type explorer '%s' for object '%s'",
|
||||||
explorer, cdist_object.name)
|
explorer, cdist_object.name)
|
||||||
cdist_object.explorers[explorer] = output
|
cdist_object.explorers[explorer] = output
|
||||||
|
|
||||||
|
@ -203,7 +210,7 @@ class Explorer(object):
|
||||||
remote side."""
|
remote side."""
|
||||||
if cdist_type.explorers:
|
if cdist_type.explorers:
|
||||||
if cdist_type.name in self._type_explorers_transferred:
|
if cdist_type.name in self._type_explorers_transferred:
|
||||||
self.log.debug("Skipping retransfer of type explorers for: %s",
|
self.log.trace("Skipping retransfer of type explorers for: %s",
|
||||||
cdist_type)
|
cdist_type)
|
||||||
else:
|
else:
|
||||||
source = os.path.join(self.local.type_path,
|
source = os.path.join(self.local.type_path,
|
||||||
|
|
|
@ -145,12 +145,11 @@ class Manifest(object):
|
||||||
else:
|
else:
|
||||||
user_supplied = True
|
user_supplied = True
|
||||||
|
|
||||||
self.log.info("Running initial manifest " + initial_manifest)
|
|
||||||
|
|
||||||
if not os.path.isfile(initial_manifest):
|
if not os.path.isfile(initial_manifest):
|
||||||
raise NoInitialManifestError(initial_manifest, user_supplied)
|
raise NoInitialManifestError(initial_manifest, user_supplied)
|
||||||
|
|
||||||
message_prefix = "initialmanifest"
|
message_prefix = "initialmanifest"
|
||||||
|
self.log.verbose("Running initial manifest " + initial_manifest)
|
||||||
self.local.run_script(initial_manifest,
|
self.local.run_script(initial_manifest,
|
||||||
env=self.env_initial_manifest(initial_manifest),
|
env=self.env_initial_manifest(initial_manifest),
|
||||||
message_prefix=message_prefix,
|
message_prefix=message_prefix,
|
||||||
|
@ -177,6 +176,7 @@ class Manifest(object):
|
||||||
cdist_object.cdist_type.manifest_path)
|
cdist_object.cdist_type.manifest_path)
|
||||||
message_prefix = cdist_object.name
|
message_prefix = cdist_object.name
|
||||||
if os.path.isfile(type_manifest):
|
if os.path.isfile(type_manifest):
|
||||||
|
self.log.verbose("Running type manifest " + type_manifest)
|
||||||
self.local.run_script(type_manifest,
|
self.local.run_script(type_manifest,
|
||||||
env=self.env_type_manifest(cdist_object),
|
env=self.env_type_manifest(cdist_object),
|
||||||
message_prefix=message_prefix,
|
message_prefix=message_prefix,
|
||||||
|
|
36
cdist/core/util.py
Normal file
36
cdist/core/util.py
Normal file
|
@ -0,0 +1,36 @@
|
||||||
|
# -*- coding: utf-8 -*-
|
||||||
|
#
|
||||||
|
# 2017 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
|
||||||
|
|
||||||
|
|
||||||
|
def listdir(path='.', include_dot=False):
|
||||||
|
"""os.listdir but do not include entries whose names begin with a dot('.')
|
||||||
|
if include_dot is False.
|
||||||
|
"""
|
||||||
|
if include_dot:
|
||||||
|
return os.listdir(path)
|
||||||
|
else:
|
||||||
|
return [x for x in os.listdir(path) if not _ishidden(x)]
|
||||||
|
|
||||||
|
|
||||||
|
def _ishidden(path):
|
||||||
|
return path[0] in ('.', b'.'[0])
|
|
@ -98,7 +98,7 @@ class Emulator(object):
|
||||||
self.save_stdin()
|
self.save_stdin()
|
||||||
self.record_requirements()
|
self.record_requirements()
|
||||||
self.record_auto_requirements()
|
self.record_auto_requirements()
|
||||||
self.log.debug("Finished %s %s" % (
|
self.log.trace("Finished %s %s" % (
|
||||||
self.cdist_object.path, self.parameters))
|
self.cdist_object.path, self.parameters))
|
||||||
|
|
||||||
def __init_log(self):
|
def __init_log(self):
|
||||||
|
@ -148,7 +148,7 @@ class Emulator(object):
|
||||||
|
|
||||||
# And finally parse/verify parameter
|
# And finally parse/verify parameter
|
||||||
self.args = parser.parse_args(self.argv[1:])
|
self.args = parser.parse_args(self.argv[1:])
|
||||||
self.log.debug('Args: %s' % self.args)
|
self.log.trace('Args: %s' % self.args)
|
||||||
|
|
||||||
def setup_object(self):
|
def setup_object(self):
|
||||||
# Setup object - and ensure it is not in args
|
# Setup object - and ensure it is not in args
|
||||||
|
@ -256,10 +256,10 @@ class Emulator(object):
|
||||||
# (this would leed to an circular dependency)
|
# (this would leed to an circular dependency)
|
||||||
if ("CDIST_ORDER_DEPENDENCY" in self.env and
|
if ("CDIST_ORDER_DEPENDENCY" in self.env and
|
||||||
'CDIST_OVERRIDE' not in self.env):
|
'CDIST_OVERRIDE' not in self.env):
|
||||||
# load object name created bevor this one from typeorder file ...
|
# load object name created befor this one from typeorder file ...
|
||||||
with open(self.typeorder_path, 'r') as typecreationfile:
|
with open(self.typeorder_path, 'r') as typecreationfile:
|
||||||
typecreationorder = typecreationfile.readlines()
|
typecreationorder = typecreationfile.readlines()
|
||||||
# get the type created bevore this one ...
|
# get the type created before this one ...
|
||||||
try:
|
try:
|
||||||
lastcreatedtype = typecreationorder[-2].strip()
|
lastcreatedtype = typecreationorder[-2].strip()
|
||||||
if 'require' in self.env:
|
if 'require' in self.env:
|
||||||
|
|
|
@ -54,7 +54,8 @@ class Local(object):
|
||||||
exec_path=sys.argv[0],
|
exec_path=sys.argv[0],
|
||||||
initial_manifest=None,
|
initial_manifest=None,
|
||||||
add_conf_dirs=None,
|
add_conf_dirs=None,
|
||||||
cache_path_pattern=None):
|
cache_path_pattern=None,
|
||||||
|
quiet_mode=False):
|
||||||
|
|
||||||
self.target_host = target_host
|
self.target_host = target_host
|
||||||
self.hostdir = host_dir_name
|
self.hostdir = host_dir_name
|
||||||
|
@ -64,6 +65,7 @@ class Local(object):
|
||||||
self.custom_initial_manifest = initial_manifest
|
self.custom_initial_manifest = initial_manifest
|
||||||
self._add_conf_dirs = add_conf_dirs
|
self._add_conf_dirs = add_conf_dirs
|
||||||
self.cache_path_pattern = cache_path_pattern
|
self.cache_path_pattern = cache_path_pattern
|
||||||
|
self.quiet_mode = quiet_mode
|
||||||
|
|
||||||
self._init_log()
|
self._init_log()
|
||||||
self._init_permissions()
|
self._init_permissions()
|
||||||
|
@ -163,7 +165,7 @@ class Local(object):
|
||||||
with open(self.object_marker_file, 'w') as fd:
|
with open(self.object_marker_file, 'w') as fd:
|
||||||
fd.write("%s\n" % self.object_marker_name)
|
fd.write("%s\n" % self.object_marker_name)
|
||||||
|
|
||||||
self.log.debug("Object marker %s saved in %s" % (
|
self.log.trace("Object marker %s saved in %s" % (
|
||||||
self.object_marker_name, self.object_marker_file))
|
self.object_marker_name, self.object_marker_file))
|
||||||
|
|
||||||
def _init_cache_dir(self, cache_dir):
|
def _init_cache_dir(self, cache_dir):
|
||||||
|
@ -178,12 +180,12 @@ class Local(object):
|
||||||
|
|
||||||
def rmdir(self, path):
|
def rmdir(self, path):
|
||||||
"""Remove directory on the local side."""
|
"""Remove directory on the local side."""
|
||||||
self.log.debug("Local rmdir: %s", path)
|
self.log.trace("Local rmdir: %s", path)
|
||||||
shutil.rmtree(path)
|
shutil.rmtree(path)
|
||||||
|
|
||||||
def mkdir(self, path):
|
def mkdir(self, path):
|
||||||
"""Create directory on the local side."""
|
"""Create directory on the local side."""
|
||||||
self.log.debug("Local mkdir: %s", path)
|
self.log.trace("Local mkdir: %s", path)
|
||||||
os.makedirs(path, exist_ok=True)
|
os.makedirs(path, exist_ok=True)
|
||||||
|
|
||||||
def run(self, command, env=None, return_output=False, message_prefix=None,
|
def run(self, command, env=None, return_output=False, message_prefix=None,
|
||||||
|
@ -192,7 +194,6 @@ class Local(object):
|
||||||
Return the output as a string.
|
Return the output as a string.
|
||||||
|
|
||||||
"""
|
"""
|
||||||
self.log.debug("Local run: %s", command)
|
|
||||||
assert isinstance(command, (list, tuple)), (
|
assert isinstance(command, (list, tuple)), (
|
||||||
"list or tuple argument expected, got: %s" % command)
|
"list or tuple argument expected, got: %s" % command)
|
||||||
|
|
||||||
|
@ -211,19 +212,30 @@ class Local(object):
|
||||||
message = cdist.message.Message(message_prefix, self.messages_path)
|
message = cdist.message.Message(message_prefix, self.messages_path)
|
||||||
env.update(message.env)
|
env.update(message.env)
|
||||||
|
|
||||||
|
self.log.trace("Local run: %s", command)
|
||||||
try:
|
try:
|
||||||
|
if self.quiet_mode:
|
||||||
|
stderr = subprocess.DEVNULL
|
||||||
|
else:
|
||||||
|
stderr = None
|
||||||
if save_output:
|
if save_output:
|
||||||
output, errout = exec_util.call_get_output(command, env=env)
|
output, errout = exec_util.call_get_output(
|
||||||
self.log.debug("Local stdout: {}".format(output))
|
command, env=env, stderr=stderr)
|
||||||
|
self.log.trace("Local stdout: {}".format(output))
|
||||||
# Currently, stderr is not captured.
|
# Currently, stderr is not captured.
|
||||||
# self.log.debug("Local stderr: {}".format(errout))
|
# self.log.trace("Local stderr: {}".format(errout))
|
||||||
if return_output:
|
if return_output:
|
||||||
return output.decode()
|
return output.decode()
|
||||||
else:
|
else:
|
||||||
# In some cases no output is saved.
|
# In some cases no output is saved.
|
||||||
# This is used for shell command, stdout and stderr
|
# This is used for shell command, stdout and stderr
|
||||||
# must not be catched.
|
# must not be catched.
|
||||||
subprocess.check_call(command, env=env)
|
if self.quiet_mode:
|
||||||
|
stdout = subprocess.DEVNULL
|
||||||
|
else:
|
||||||
|
stdout = None
|
||||||
|
subprocess.check_call(command, env=env, stderr=stderr,
|
||||||
|
stdout=stdout)
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
exec_util.handle_called_process_error(e, command)
|
exec_util.handle_called_process_error(e, command)
|
||||||
except OSError as error:
|
except OSError as error:
|
||||||
|
@ -279,13 +291,14 @@ class Local(object):
|
||||||
return cache_subpath
|
return cache_subpath
|
||||||
|
|
||||||
def save_cache(self, start_time=time.time()):
|
def save_cache(self, start_time=time.time()):
|
||||||
self.log.debug("cache subpath pattern: {}".format(
|
self.log.trace("cache subpath pattern: {}".format(
|
||||||
self.cache_path_pattern))
|
self.cache_path_pattern))
|
||||||
cache_subpath = self._cache_subpath(start_time,
|
cache_subpath = self._cache_subpath(start_time,
|
||||||
self.cache_path_pattern)
|
self.cache_path_pattern)
|
||||||
self.log.debug("cache subpath: {}".format(cache_subpath))
|
self.log.debug("cache subpath: {}".format(cache_subpath))
|
||||||
destination = os.path.join(self.cache_path, cache_subpath)
|
destination = os.path.join(self.cache_path, cache_subpath)
|
||||||
self.log.debug("Saving " + self.base_path + " to " + destination)
|
self.log.trace(("Saving cache: " + self.base_path + " to " +
|
||||||
|
destination))
|
||||||
|
|
||||||
if not os.path.exists(destination):
|
if not os.path.exists(destination):
|
||||||
shutil.move(self.base_path, destination)
|
shutil.move(self.base_path, destination)
|
||||||
|
@ -340,7 +353,7 @@ class Local(object):
|
||||||
if os.path.exists(dst):
|
if os.path.exists(dst):
|
||||||
os.unlink(dst)
|
os.unlink(dst)
|
||||||
|
|
||||||
self.log.debug("Linking %s to %s ..." % (src, dst))
|
self.log.trace("Linking %s to %s ..." % (src, dst))
|
||||||
try:
|
try:
|
||||||
os.symlink(src, dst)
|
os.symlink(src, dst)
|
||||||
except OSError as e:
|
except OSError as e:
|
||||||
|
@ -352,7 +365,7 @@ class Local(object):
|
||||||
src = os.path.abspath(self.exec_path)
|
src = os.path.abspath(self.exec_path)
|
||||||
for cdist_type in core.CdistType.list_types(self.type_path):
|
for cdist_type in core.CdistType.list_types(self.type_path):
|
||||||
dst = os.path.join(self.bin_path, cdist_type.name)
|
dst = os.path.join(self.bin_path, cdist_type.name)
|
||||||
self.log.debug("Linking emulator: %s to %s", src, dst)
|
self.log.trace("Linking emulator: %s to %s", src, dst)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
os.symlink(src, dst)
|
os.symlink(src, dst)
|
||||||
|
|
|
@ -62,7 +62,8 @@ class Remote(object):
|
||||||
target_host,
|
target_host,
|
||||||
remote_exec,
|
remote_exec,
|
||||||
remote_copy,
|
remote_copy,
|
||||||
base_path=None):
|
base_path=None,
|
||||||
|
quiet_mode=None):
|
||||||
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
|
||||||
|
@ -71,6 +72,7 @@ class Remote(object):
|
||||||
self.base_path = base_path
|
self.base_path = base_path
|
||||||
else:
|
else:
|
||||||
self.base_path = "/var/lib/cdist"
|
self.base_path = "/var/lib/cdist"
|
||||||
|
self.quiet_mode = quiet_mode
|
||||||
|
|
||||||
self.conf_path = os.path.join(self.base_path, "conf")
|
self.conf_path = os.path.join(self.base_path, "conf")
|
||||||
self.object_path = os.path.join(self.base_path, "object")
|
self.object_path = os.path.join(self.base_path, "object")
|
||||||
|
@ -111,18 +113,18 @@ class Remote(object):
|
||||||
|
|
||||||
def rmdir(self, path):
|
def rmdir(self, path):
|
||||||
"""Remove directory on the remote side."""
|
"""Remove directory on the remote side."""
|
||||||
self.log.debug("Remote rmdir: %s", path)
|
self.log.trace("Remote rmdir: %s", path)
|
||||||
self.run(["rm", "-rf", path])
|
self.run(["rm", "-rf", path])
|
||||||
|
|
||||||
def mkdir(self, path):
|
def mkdir(self, path):
|
||||||
"""Create directory on the remote side."""
|
"""Create directory on the remote side."""
|
||||||
self.log.debug("Remote mkdir: %s", path)
|
self.log.trace("Remote mkdir: %s", path)
|
||||||
self.run(["mkdir", "-p", path])
|
self.run(["mkdir", "-p", path])
|
||||||
|
|
||||||
def transfer(self, source, destination, jobs=None):
|
def transfer(self, source, destination, jobs=None):
|
||||||
"""Transfer a file or directory to the remote side."""
|
"""Transfer a file or directory to the remote side."""
|
||||||
self.log.debug("Remote transfer: %s -> %s", source, destination)
|
self.log.trace("Remote transfer: %s -> %s", source, destination)
|
||||||
self.rmdir(destination)
|
# self.rmdir(destination)
|
||||||
if os.path.isdir(source):
|
if os.path.isdir(source):
|
||||||
self.mkdir(destination)
|
self.mkdir(destination)
|
||||||
if jobs:
|
if jobs:
|
||||||
|
@ -147,11 +149,11 @@ class Remote(object):
|
||||||
|
|
||||||
def _transfer_dir_parallel(self, source, destination, jobs):
|
def _transfer_dir_parallel(self, source, destination, jobs):
|
||||||
"""Transfer a directory to the remote side in parallel mode."""
|
"""Transfer a directory to the remote side in parallel mode."""
|
||||||
self.log.info("Remote transfer in {} parallel jobs".format(
|
self.log.debug("Remote transfer in {} parallel jobs".format(
|
||||||
jobs))
|
jobs))
|
||||||
self.log.debug("Multiprocessing start method is {}".format(
|
self.log.trace("Multiprocessing start method is {}".format(
|
||||||
multiprocessing.get_start_method()))
|
multiprocessing.get_start_method()))
|
||||||
self.log.debug(("Starting multiprocessing Pool for parallel "
|
self.log.trace(("Starting multiprocessing Pool for parallel "
|
||||||
"remote transfer"))
|
"remote transfer"))
|
||||||
args = []
|
args = []
|
||||||
for f in glob.glob1(source, '*'):
|
for f in glob.glob1(source, '*'):
|
||||||
|
@ -161,7 +163,7 @@ class Remote(object):
|
||||||
_wrap_addr(self.target_host[0]), destination)])
|
_wrap_addr(self.target_host[0]), destination)])
|
||||||
args.append((command, ))
|
args.append((command, ))
|
||||||
mp_pool_run(self._run_command, args, jobs=jobs)
|
mp_pool_run(self._run_command, args, jobs=jobs)
|
||||||
self.log.debug(("Multiprocessing for parallel transfer "
|
self.log.trace(("Multiprocessing for parallel transfer "
|
||||||
"finished"))
|
"finished"))
|
||||||
|
|
||||||
def run_script(self, script, env=None, return_output=False):
|
def run_script(self, script, env=None, return_output=False):
|
||||||
|
@ -226,12 +228,17 @@ class Remote(object):
|
||||||
os_environ['__target_hostname'] = self.target_host[1]
|
os_environ['__target_hostname'] = self.target_host[1]
|
||||||
os_environ['__target_fqdn'] = self.target_host[2]
|
os_environ['__target_fqdn'] = self.target_host[2]
|
||||||
|
|
||||||
self.log.debug("Remote run: %s", command)
|
self.log.trace("Remote run: %s", command)
|
||||||
try:
|
try:
|
||||||
output, errout = exec_util.call_get_output(command, env=os_environ)
|
if self.quiet_mode:
|
||||||
self.log.debug("Remote stdout: {}".format(output))
|
stderr = subprocess.DEVNULL
|
||||||
|
else:
|
||||||
|
stderr = None
|
||||||
|
output, errout = exec_util.call_get_output(
|
||||||
|
command, env=os_environ, stderr=stderr)
|
||||||
|
self.log.trace("Remote stdout: {}".format(output))
|
||||||
# Currently, stderr is not captured.
|
# Currently, stderr is not captured.
|
||||||
# self.log.debug("Remote stderr: {}".format(errout))
|
# self.log.trace("Remote stderr: {}".format(errout))
|
||||||
if return_output:
|
if return_output:
|
||||||
return output.decode()
|
return output.decode()
|
||||||
except subprocess.CalledProcessError as e:
|
except subprocess.CalledProcessError as e:
|
||||||
|
|
|
@ -116,14 +116,14 @@ import cdist
|
||||||
# return (result.stdout, result.stderr)
|
# return (result.stdout, result.stderr)
|
||||||
|
|
||||||
|
|
||||||
def call_get_output(command, env=None):
|
def call_get_output(command, env=None, stderr=None):
|
||||||
"""Run the given command with the given environment.
|
"""Run the given command with the given environment.
|
||||||
Return the tuple of stdout and stderr output as a byte strings.
|
Return the tuple of stdout and stderr output as a byte strings.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
assert isinstance(command, (list, tuple)), (
|
assert isinstance(command, (list, tuple)), (
|
||||||
"list or tuple argument expected, got: {}".format(command))
|
"list or tuple argument expected, got: {}".format(command))
|
||||||
return (_call_get_stdout(command, env), None)
|
return (_call_get_stdout(command, env, stderr), None)
|
||||||
|
|
||||||
|
|
||||||
def handle_called_process_error(err, command):
|
def handle_called_process_error(err, command):
|
||||||
|
@ -140,7 +140,7 @@ def handle_called_process_error(err, command):
|
||||||
err.returncode, err.output))
|
err.returncode, err.output))
|
||||||
|
|
||||||
|
|
||||||
def _call_get_stdout(command, env=None):
|
def _call_get_stdout(command, env=None, stderr=None):
|
||||||
"""Run the given command with the given environment.
|
"""Run the given command with the given environment.
|
||||||
Return the stdout output as a byte string, stderr is ignored.
|
Return the stdout output as a byte string, stderr is ignored.
|
||||||
"""
|
"""
|
||||||
|
@ -148,7 +148,7 @@ def _call_get_stdout(command, env=None):
|
||||||
"list or tuple argument expected, got: {}".format(command))
|
"list or tuple argument expected, got: {}".format(command))
|
||||||
|
|
||||||
with TemporaryFile() as fout:
|
with TemporaryFile() as fout:
|
||||||
subprocess.check_call(command, env=env, stdout=fout)
|
subprocess.check_call(command, env=env, stdout=fout, stderr=stderr)
|
||||||
fout.seek(0)
|
fout.seek(0)
|
||||||
output = fout.read()
|
output = fout.read()
|
||||||
|
|
||||||
|
|
35
cdist/log.py
35
cdist/log.py
|
@ -23,6 +23,31 @@
|
||||||
import logging
|
import logging
|
||||||
|
|
||||||
|
|
||||||
|
# Define additional cdist logging levels.
|
||||||
|
logging.OFF = logging.CRITICAL + 10 # disable logging
|
||||||
|
logging.addLevelName(logging.OFF, 'OFF')
|
||||||
|
|
||||||
|
logging.VERBOSE = logging.INFO - 5
|
||||||
|
logging.addLevelName(logging.VERBOSE, 'VERBOSE')
|
||||||
|
|
||||||
|
|
||||||
|
def _verbose(msg, *args, **kwargs):
|
||||||
|
logging.log(logging.VERBOSE, msg, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
logging.verbose = _verbose
|
||||||
|
|
||||||
|
logging.TRACE = logging.DEBUG - 5
|
||||||
|
logging.addLevelName(logging.TRACE, 'TRACE')
|
||||||
|
|
||||||
|
|
||||||
|
def _trace(msg, *args, **kwargs):
|
||||||
|
logging.log(logging.TRACE, msg, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
logging.trace = _trace
|
||||||
|
|
||||||
|
|
||||||
class Log(logging.Logger):
|
class Log(logging.Logger):
|
||||||
|
|
||||||
def __init__(self, name):
|
def __init__(self, name):
|
||||||
|
@ -37,3 +62,13 @@ class Log(logging.Logger):
|
||||||
record.msg = self.name + ": " + str(record.msg)
|
record.msg = self.name + ": " + str(record.msg)
|
||||||
|
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def verbose(self, msg, *args, **kwargs):
|
||||||
|
self.log(logging.VERBOSE, msg, *args, **kwargs)
|
||||||
|
|
||||||
|
def trace(self, msg, *args, **kwargs):
|
||||||
|
self.log(logging.TRACE, msg, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
logging.setLoggerClass(Log)
|
||||||
|
logging.basicConfig(format='%(levelname)s: %(message)s')
|
||||||
|
|
|
@ -86,10 +86,10 @@ class Shell(object):
|
||||||
self._init_files_dirs()
|
self._init_files_dirs()
|
||||||
self._init_environment()
|
self._init_environment()
|
||||||
|
|
||||||
log.info("Starting shell...")
|
log.trace("Starting shell...")
|
||||||
# save_output=False -> do not catch stdout and stderr
|
# save_output=False -> do not catch stdout and stderr
|
||||||
self.local.run([self.shell], self.env, save_output=False)
|
self.local.run([self.shell], self.env, save_output=False)
|
||||||
log.info("Finished shell.")
|
log.trace("Finished shell.")
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def commandline(cls, args):
|
def commandline(cls, args):
|
||||||
|
|
|
@ -113,6 +113,16 @@ class TypeTestCase(test.CdistTestCase):
|
||||||
cdist_type = core.CdistType(base_path, '__not_singleton')
|
cdist_type = core.CdistType(base_path, '__not_singleton')
|
||||||
self.assertFalse(cdist_type.is_singleton)
|
self.assertFalse(cdist_type.is_singleton)
|
||||||
|
|
||||||
|
def test_nonparallel_is_nonparallel(self):
|
||||||
|
base_path = fixtures
|
||||||
|
cdist_type = core.CdistType(base_path, '__nonparallel')
|
||||||
|
self.assertTrue(cdist_type.is_nonparallel)
|
||||||
|
|
||||||
|
def test_not_nonparallel_is_nonparallel(self):
|
||||||
|
base_path = fixtures
|
||||||
|
cdist_type = core.CdistType(base_path, '__not_nonparallel')
|
||||||
|
self.assertFalse(cdist_type.is_nonparallel)
|
||||||
|
|
||||||
def test_install_is_install(self):
|
def test_install_is_install(self):
|
||||||
base_path = fixtures
|
base_path = fixtures
|
||||||
cdist_type = core.CdistType(base_path, '__install')
|
cdist_type = core.CdistType(base_path, '__install')
|
||||||
|
|
0
cdist/test/cdist_type/fixtures/__nonparallel/nonparallel
Normal file
0
cdist/test/cdist_type/fixtures/__nonparallel/nonparallel
Normal file
0
cdist/test/cdist_type/fixtures/__not_nonparallel/.keep
Normal file
0
cdist/test/cdist_type/fixtures/__not_nonparallel/.keep
Normal file
|
@ -45,7 +45,7 @@ def resolve_target_host_name(host):
|
||||||
log.debug("derived host_name for host \"{}\": {}".format(
|
log.debug("derived host_name for host \"{}\": {}".format(
|
||||||
host, host_name))
|
host, host_name))
|
||||||
except (socket.gaierror, socket.herror) as e:
|
except (socket.gaierror, socket.herror) as e:
|
||||||
log.warn("Could not derive host_name for {}"
|
log.warning("Could not derive host_name for {}"
|
||||||
", $host_name will be empty. Error is: {}".format(host, e))
|
", $host_name will be empty. Error is: {}".format(host, e))
|
||||||
# in case of error provide empty value
|
# in case of error provide empty value
|
||||||
host_name = ''
|
host_name = ''
|
||||||
|
@ -59,7 +59,7 @@ def resolve_target_fqdn(host):
|
||||||
log.debug("derived host_fqdn for host \"{}\": {}".format(
|
log.debug("derived host_fqdn for host \"{}\": {}".format(
|
||||||
host, host_fqdn))
|
host, host_fqdn))
|
||||||
except socket.herror as e:
|
except socket.herror as e:
|
||||||
log.warn("Could not derive host_fqdn for {}"
|
log.warning("Could not derive host_fqdn for {}"
|
||||||
", $host_fqdn will be empty. Error is: {}".format(host, e))
|
", $host_fqdn will be empty. Error is: {}".format(host, e))
|
||||||
# in case of error provide empty value
|
# in case of error provide empty value
|
||||||
host_fqdn = ''
|
host_fqdn = ''
|
||||||
|
|
|
@ -7,11 +7,16 @@ next:
|
||||||
* Core: Allow manifest and gencode scripts to be written in any language (Darko Poljak)
|
* Core: Allow manifest and gencode scripts to be written in any language (Darko Poljak)
|
||||||
* Documentation: Improvements to the english and fix typos (Mesar Hameed)
|
* Documentation: Improvements to the english and fix typos (Mesar Hameed)
|
||||||
* Core: Merge -C custom cache path pattern option from beta branch (Darko Poljak)
|
* Core: Merge -C custom cache path pattern option from beta branch (Darko Poljak)
|
||||||
|
* Core: Improve and cleanup logging (Darko Poljak, Steven Armstrong)
|
||||||
|
* Core: Remove deprecated -d option (Darko Poljak)
|
||||||
|
* Type __file: If no --source then create only if there is no file (Ander Punnar)
|
||||||
|
* Core: Ignore directory entries that begin with dot('.') (Darko Poljak)
|
||||||
|
* Core: Fix parallel object prepare and run steps and add nonparallel type marker (Darko Poljak)
|
||||||
|
|
||||||
4.4.4: 2017-06-16
|
4.4.4: 2017-06-16
|
||||||
* Core: Support -j parallelization for object prepare and object run (Darko Poljak)
|
* Core: Support -j parallelization for object prepare and object run (Darko Poljak)
|
||||||
* Type __install_mkfs: mkfs.vfat does not support -q (Nico Schottelius)
|
* Type __install_mkfs: mkfs.vfat does not support -q (Nico Schottelius)
|
||||||
* Types __go_get, __daemontools*, __prometheus*: Fix missing dependencies, fix arguments(Kamila Součková)
|
* Types __go_get, __daemontools*, __prometheus*: Fix missing dependencies, fix arguments (Kamila Součková)
|
||||||
|
|
||||||
4.4.3: 2017-06-13
|
4.4.3: 2017-06-13
|
||||||
* Type __golang_from_vendor: Install golang from https://golang.org/dl/ (Kamila Součková)
|
* Type __golang_from_vendor: Install golang from https://golang.org/dl/ (Kamila Součková)
|
||||||
|
|
|
@ -11,23 +11,23 @@ SYNOPSIS
|
||||||
|
|
||||||
::
|
::
|
||||||
|
|
||||||
cdist [-h] [-d] [-v] [-V] {banner,config,shell,install} ...
|
cdist [-h] [-v] [-V] {banner,config,shell,install} ...
|
||||||
|
|
||||||
cdist banner [-h] [-d] [-v]
|
cdist banner [-h] [-v]
|
||||||
|
|
||||||
cdist config [-h] [-d] [-v] [-b] [-C CACHE_PATH_PATTERN] [-c CONF_DIR]
|
cdist config [-h] [-v] [-b] [-C CACHE_PATH_PATTERN] [-c CONF_DIR]
|
||||||
[-i MANIFEST] [-j [JOBS]] [-n] [-o OUT_PATH]
|
[-i MANIFEST] [-j [JOBS]] [-n] [-o OUT_PATH]
|
||||||
[--remote-copy REMOTE_COPY] [--remote-exec REMOTE_EXEC]
|
[--remote-copy REMOTE_COPY] [--remote-exec REMOTE_EXEC]
|
||||||
[-f HOSTFILE] [-p] [-r REMOTE_OUT_PATH] [-s]
|
[-f HOSTFILE] [-p] [-r REMOTE_OUT_PATH] [-s]
|
||||||
[host [host ...]]
|
[host [host ...]]
|
||||||
|
|
||||||
cdist install [-h] [-d] [-v] [-b] [-C CACHE_PATH_PATTERN] [-c CONF_DIR]
|
cdist install [-h] [-v] [-b] [-C CACHE_PATH_PATTERN] [-c CONF_DIR]
|
||||||
[-i MANIFEST] [-j [JOBS]] [-n] [-o OUT_PATH]
|
[-i MANIFEST] [-j [JOBS]] [-n] [-o OUT_PATH]
|
||||||
[--remote-copy REMOTE_COPY] [--remote-exec REMOTE_EXEC]
|
[--remote-copy REMOTE_COPY] [--remote-exec REMOTE_EXEC]
|
||||||
[-f HOSTFILE] [-p] [-r REMOTE_OUT_PATH] [-s]
|
[-f HOSTFILE] [-p] [-r REMOTE_OUT_PATH] [-s]
|
||||||
[host [host ...]]
|
[host [host ...]]
|
||||||
|
|
||||||
cdist shell [-h] [-d] [-v] [-s SHELL]
|
cdist shell [-h] [-v] [-s SHELL]
|
||||||
|
|
||||||
|
|
||||||
DESCRIPTION
|
DESCRIPTION
|
||||||
|
@ -46,16 +46,16 @@ All commands accept the following options:
|
||||||
|
|
||||||
Show the help screen
|
Show the help screen
|
||||||
|
|
||||||
.. option:: -d, --debug
|
.. option:: -q, --quiet
|
||||||
|
|
||||||
Set log level to debug (deprecated, use -vvv instead)
|
Quiet mode: disables logging, including WARNING and ERROR
|
||||||
|
|
||||||
.. option:: -v, --verbose
|
.. option:: -v, --verbose
|
||||||
|
|
||||||
Increase the verbosity level. Every instance of -v increments the verbosity
|
Increase the verbosity level. Every instance of -v increments the verbosity
|
||||||
level by one. Its default value is 0. There are 4 levels of verbosity. The
|
level by one. Its default value is 0 which includes ERROR and WARNING levels.
|
||||||
order of levels from the lowest to the highest are: ERROR (0), WARNING (1),
|
The levels, in order from the lowest to the highest, are:
|
||||||
INFO (2) and DEBUG (3 or higher).
|
ERROR (-1), WARNING (0), INFO (1), VERBOSE (2), DEBUG (3) TRACE (4 or higher).
|
||||||
|
|
||||||
.. option:: -V, --version
|
.. option:: -V, --version
|
||||||
|
|
||||||
|
@ -207,7 +207,7 @@ EXAMPLES
|
||||||
.. code-block:: sh
|
.. code-block:: sh
|
||||||
|
|
||||||
# Configure ikq05.ethz.ch with debug enabled
|
# Configure ikq05.ethz.ch with debug enabled
|
||||||
% cdist config -d ikq05.ethz.ch
|
% cdist config -vvv ikq05.ethz.ch
|
||||||
|
|
||||||
# Configure hosts in parallel and use a different configuration directory
|
# Configure hosts in parallel and use a different configuration directory
|
||||||
% cdist config -c ~/p/cdist-nutzung \
|
% cdist config -c ~/p/cdist-nutzung \
|
||||||
|
@ -241,7 +241,7 @@ EXAMPLES
|
||||||
[--group GROUP] [--owner OWNER] [--mode MODE] object_id
|
[--group GROUP] [--owner OWNER] [--mode MODE] object_id
|
||||||
|
|
||||||
# Install ikq05.ethz.ch with debug enabled
|
# Install ikq05.ethz.ch with debug enabled
|
||||||
% cdist install -d ikq05.ethz.ch
|
% cdist install -vvv ikq05.ethz.ch
|
||||||
|
|
||||||
ENVIRONMENT
|
ENVIRONMENT
|
||||||
-----------
|
-----------
|
||||||
|
|
|
@ -40,12 +40,10 @@ def commandline():
|
||||||
args = parser['main'].parse_args(sys.argv[1:])
|
args = parser['main'].parse_args(sys.argv[1:])
|
||||||
|
|
||||||
# Loglevels are handled globally in here
|
# Loglevels are handled globally in here
|
||||||
retval = cdist.argparse.handle_loglevel(args)
|
cdist.argparse.handle_loglevel(args)
|
||||||
if retval:
|
|
||||||
log.warning(retval)
|
|
||||||
|
|
||||||
log.debug(args)
|
log.verbose("version %s" % cdist.VERSION)
|
||||||
log.info("version %s" % cdist.VERSION)
|
log.trace(args)
|
||||||
|
|
||||||
# Work around python 3.3 bug:
|
# Work around python 3.3 bug:
|
||||||
# http://bugs.python.org/issue16308
|
# http://bugs.python.org/issue16308
|
||||||
|
@ -80,10 +78,7 @@ if __name__ == "__main__":
|
||||||
import os
|
import os
|
||||||
import re
|
import re
|
||||||
import cdist
|
import cdist
|
||||||
import cdist.log
|
|
||||||
|
|
||||||
logging.setLoggerClass(cdist.log.Log)
|
|
||||||
logging.basicConfig(format='%(levelname)s: %(message)s')
|
|
||||||
log = logging.getLogger("cdist")
|
log = logging.getLogger("cdist")
|
||||||
|
|
||||||
if re.match("__", os.path.basename(sys.argv[0])):
|
if re.match("__", os.path.basename(sys.argv[0])):
|
||||||
|
|
Loading…
Reference in a new issue