diff --git a/bin/build-helper b/bin/build-helper index 69dee4c7..5dcc6989 100755 --- a/bin/build-helper +++ b/bin/build-helper @@ -463,7 +463,7 @@ eof shellcheck-scripts) # shellcheck disable=SC2086 - ${SHELLCHECKCMD} scripts/cdist-dump scripts/cdist-new-type > "${SHELLCHECKTMP}" + ${SHELLCHECKCMD} cdist/conf/libexec/cdist-dump cdist/conf/libexec/cdist-new-type > "${SHELLCHECKTMP}" test ! -s "${SHELLCHECKTMP}" || { cat "${SHELLCHECKTMP}"; exit 1; } ;; diff --git a/cdist/argparse.py b/cdist/argparse.py index 7dc683f3..b1ee99da 100644 --- a/cdist/argparse.py +++ b/cdist/argparse.py @@ -6,6 +6,7 @@ import collections import functools import cdist.configuration import cdist.preos +import cdist.libexec as libexec # set of beta sub-commands @@ -436,6 +437,9 @@ def get_parsers(): ' should be POSIX compatible shell.')) parser['shell'].set_defaults(func=cdist.shell.Shell.commandline) + # Libexec + libexec.create_parsers(parser, parser['sub']) + for p in parser: parser[p].epilog = EPILOG diff --git a/scripts/cdist-dump b/cdist/conf/libexec/cdist-dump similarity index 100% rename from scripts/cdist-dump rename to cdist/conf/libexec/cdist-dump diff --git a/cdist/conf/libexec/cdist-info b/cdist/conf/libexec/cdist-info new file mode 100755 index 00000000..19ec4cad --- /dev/null +++ b/cdist/conf/libexec/cdist-info @@ -0,0 +1,399 @@ +#!/bin/sh + +VERSION="0.0.1" +RELEASE="" + +set -u +# set -x + +verbose=0 +myname=${0##*/} +mydir=${0%/$myname} +dist_conf=${mydir%/libexec} + +print_version() +{ + printf "%s %s %s\n" "${myname}" "${VERSION}" "${RELEASE}" +} + +usage() +{ + cat << eof +${myname}: [options] +eof + + print_version + + cat << eof + +Display information for cdist types and explorers. + +Options + -C display only config types + -c CONF_DIR add configuration directory (can be repeated) + -D display only nondeprecated types + -d display only deprecated types + -E display only types without explorers + -e display only types with explorers + -G display global explorers (turns displaying types off, + unless -t is explicitly specified) + -g GLOB use GLOB as name pattern + -h display this help screen and exit + -i display only install types + -l display only types with gencode-local + -M display only types with man page + -m display only types with manifest + -P display only nonparallel types + -p display only parallel types + -R display type parameters + -r display only types with gencode-remote + -S display only nonsingleton types + -s display only singleton types + -t display types (by default, unless -G is specified) + -V display version and exit + -v increase verbosity (verbose output goes to stderr) + if verbosity is greater than 2 then 'set -x' is used + -x display type explorers + +Notes: + +By default, distribution and home dot cdist directories are used for +configuration directories list. + +This helper tool does not parse cdist configuration files. Configuration +directories should be specifed by command line options and/or CDIST_PATH +environment variable. +eof +} + +exit_err() +{ + printf "%s\n" "$1" + exit 1 +} + +print_verbose() +{ + if [ "${verbose}" -ge "$1" ] + then + printf "%s\n" "$2" >&2 + fi +} + +print_parameters() +{ + param_title=$1 + param_fname=$2 + + if test -f "${param_fname}" + then + printf " %s\n" "${param_title}" + sed "s/^\(.*\)$/ \1/" < "${param_fname}" + fi +} + +type_name_matches() +{ + if test "$2" == "*" + then + return 0 + else + case "$(basename "$1")" in + $2) + return 0 + ;; + *) + return 1 + ;; + esac + fi +} + +# $1 - do check flag. +# $2 - file path. +# Return false only if check flag is 1 and file does not exist. +type_file_exists() +{ + if test "$1" -eq "1" + then + test -f "$2" + else + return 0 + fi +} + +# $1 - do check flag. +# $2 - file path. +# Return false only if check flag is 1 and file does exist. +type_file_not_exists() +{ + if test "$1" -eq "1" + then + test ! -f "$2" + else + return 0 + fi +} + +type_dir_empty() +{ + if test "$1" -eq "1" + then + if test ! -d "$2" + then + return 1 + elif ! [ "$(ls -1 "${fname}/explorer")" ] + then + return 1 + else + return 0 + fi + else + return 0 + fi +} + +type_dir_not_empty() +{ + if test "$1" -eq "1" + then + if test -d "$2" + then + if [ "$(ls -1 "${fname}/explorer")" ] + then + return 1 + fi + else + return 0 + fi + else + return 0 + fi +} + +print_type_params() +{ + if test "$1" -eq "1" + then + print_parameters "required parameters:" "$2/parameter/required" + print_parameters "required multiple parameters:" "$2/parameter/required_multiple" + print_parameters "optional parameters:" "$2/parameter/optional" + print_parameters "optional multiple parameters:" "$2/parameter/optional_multiple" + print_parameters "boolean parameters:" "$2/parameter/boolean" + param_deprecated="$2/parameter/deprecated" + if test -d "${param_deprecated}" + then + printf " deprecated parameters:\n" + # shellcheck disable=SC2012 + ls -1 "${param_deprecated}" | sed "s/^\(.*\)$/ \1/" + fi + fi +} + +print_type_explorers() +{ + if test "$1" -eq "1" + then + find "$2" -path "*/explorer/*" -type f + fi +} + +print_type() +{ + if test "$1" -eq "1" + then + printf "%s\n" "$2" + print_type_params "${display_type_params}" "$2" + print_type_explorers "${display_type_explorers}" "$2" + fi +} + +print_global_explorers() +{ + find "$1" '(' -path "*/explorer/*" -a ! -path "*/type/*/explorer/*" -a ! -path "*/cache/*/explorer/*" ')' -type f -name "$2" +} + +conf_dirs="${dist_conf}:${HOME}/.cdist" +glob="*" +manifest_only=0 +gencode_local_only=0 +gencode_remote_only=0 +install_only=0 +config_only=0 +parallel_only=0 +nonparallel_only=0 +singleton_only=0 +nonsingleton_only=0 +man_only=0 +explorers_only=0 +nonexplorers_only=0 +deprecated_only=0 +nondeprecated_only=0 +display_global_explorers=0 +display_types=1 +display_type_params=0 +display_type_explorers=0 +# parse options +while [ "$#" -ge 1 ] +do + case "$1" in + -C) + config_only=1 + ;; + -c) + if [ "$#" -ge 2 ] + then + case "$2" in + -*) + exit_err "Missing configuration directory argument" + ;; + *) + conf_dirs="${conf_dirs}:$2" + shift + ;; + esac + else + exit_err "Missing configuration directory argument" + fi + ;; + -D) + nondeprecated_only=1 + ;; + -d) + deprecated_only=1 + ;; + -E) + nonexplorers_only=1 + ;; + -e) + explorers_only=1 + ;; + -G) + display_types=0 + display_global_explorers=1 + ;; + -g) + if [ "$#" -ge 2 ] + then + case "$2" in + -*) + exit_err "Missing glob argument" + ;; + *) + glob="$2" + shift + ;; + esac + else + exit_err "Missing glob argument" + fi + ;; + -h) + usage + exit 0 + ;; + -i) + install_only=1 + ;; + -l) + gencode_local_only=1 + ;; + -M) + man_only=1 + ;; + -m) + manifest_only=1 + ;; + -P) + nonparallel_only=1 + ;; + -p) + parallel_only=1 + ;; + -R) + display_type_params=1 + ;; + -r) + gencode_remote_only=1 + ;; + -S) + nonsingleton_only=1 + ;; + -s) + singleton_only=1 + ;; + -t) + display_types=1 + ;; + -V) + print_version + exit 0 + ;; + -v) + verbose=$((verbose + 1)) + ;; + -x) + display_type_explorers=1 + ;; + *) + exit_err "Unknown argument $1" + ;; + esac + shift +done + +if test "${verbose}" -gt "2" +then + set -x +fi + +if test "${CDIST_PATH:-}" +then + conf_dirs="${conf_dirs}:${CDIST_PATH}" +fi + +print_verbose 2 "conf_dirs=${conf_dirs}" + +OLD_IFS="${IFS}" +IFS=':' +for x in ${conf_dirs} +do + print_verbose 1 "Processing conf dir '${x}'" + + if test "${display_global_explorers}" -eq "1" + then + print_verbose 1 "Printing global explorers" + print_global_explorers "${x}" "${glob}" + fi + + if test "${display_types}" -eq "1" + then + print_verbose 1 "Printing types" + find "${x}" -path "*/type/*" -type d -prune | \ + while read -r fname + do + print_verbose 2 "Printing type '${fname}'" + + display_type=1 + + type_name_matches "${fname}" "${glob}" || display_type=0 + type_file_exists "${manifest_only}" "${fname}/manifest" || display_type=0 + type_file_exists "${gencode_local_only}" "${fname}/gencode-local" || display_type=0 + type_file_exists "${gencode_remote_only}" "${fname}/gencode-remote" || display_type=0 + type_file_not_exists "${config_only}" "${fname}/install" || display_type=0 + type_file_exists "${install_only}" "${fname}/install" || display_type=0 + type_file_not_exists "${parallel_only}" "${fname}/nonparallel" || display_type=0 + type_file_exists "${nonparallel_only}" "${fname}/nonparallel" || display_type=0 + type_file_exists "${singleton_only}" "${fname}/singleton" || display_type=0 + type_file_not_exists "${nonsingleton_only}" "${fname}/singleton" || display_type=0 + type_file_exists "${man_only}" "${fname}/man.rst" || display_type=0 + type_file_exists "${deprecated_only}" "${fname}/deprecated" || display_type=0 + type_file_not_exists "${nondeprecated_only}" "${fname}/deprecated" || display_type=0 + type_dir_empty "${explorers_only}" "${fname}/explorer" || display_type=0 + type_dir_not_empty "${nonexplorers_only}" "${fname}/explorer" || display_type=0 + + print_type "${display_type}" "${fname}" + done + fi +done +IFS="${OLD_IFS}" diff --git a/scripts/cdist-new-type b/cdist/conf/libexec/cdist-new-type similarity index 100% rename from scripts/cdist-new-type rename to cdist/conf/libexec/cdist-new-type diff --git a/cdist/libexec.py b/cdist/libexec.py new file mode 100644 index 00000000..bd22eb86 --- /dev/null +++ b/cdist/libexec.py @@ -0,0 +1,45 @@ +import os +import os.path +import cdist.argparse +import subprocess +import sys + + +libexec_delimiter = '-' +libexec_prefix = 'cdist' + libexec_delimiter +libexec_path = os.path.abspath( + os.path.join(os.path.dirname(cdist.__file__), 'conf', 'libexec')) + + +def scan(): + if os.path.isdir(libexec_path): + with os.scandir(libexec_path) as it: + for entry in it: + if (entry.name.startswith(libexec_prefix) and + entry.is_file() and + os.access(entry.path, os.X_OK)): + start = entry.name.find(libexec_delimiter) + 1 + yield entry.name[start:] + + +def is_libexec_command(name): + for x in scan(): + if name == x: + return True + return False + + +def create_parsers(parser, parent_parser): + for name in scan(): + parser[name] = parent_parser.add_parser(name, add_help=False) + + +def run(name, argv): + lib_name = libexec_prefix + name + lib_path = os.path.join(libexec_path, lib_name) + args = [lib_path, ] + args.extend(argv) + try: + subprocess.check_call(args) + except subprocess.CalledProcessError as e: + sys.exit(e.returncode) diff --git a/docs/src/cdist-troubleshooting.rst b/docs/src/cdist-troubleshooting.rst index e639aafd..8ca67515 100644 --- a/docs/src/cdist-troubleshooting.rst +++ b/docs/src/cdist-troubleshooting.rst @@ -48,17 +48,17 @@ Using debug dump helper script ------------------------------ Since cdist stores data to local cache that can be used for debugging there is a helper script that dumps data from local cache, -`cdist-dump `_. +`cdist dump `_. For more info see: .. code-block:: sh - cdist-dump -h + cdist_dump -h Or from cdist git cloned directory: .. code-block:: sh - ./scripts/cdist-dump -h + ./bin/cdist dump -h diff --git a/docs/src/man1/cdist-dump.rst b/docs/src/man1/cdist-dump.rst index 907cd192..5a73b4a8 100644 --- a/docs/src/man1/cdist-dump.rst +++ b/docs/src/man1/cdist-dump.rst @@ -11,17 +11,17 @@ SYNOPSIS :: - cdist-dump [options] [host...] + cdist dump [options] [host...] DESCRIPTION ----------- -cdist-dump is a helper script that dumps data from local cdist cache for +cdist dump is a helper script that dumps data from local cdist cache for specified hosts. If host is not specified then all data from cache directory is dumped. Default cache directory is '~/.cdist/cache'. -cdist-dump can be used for debugging existing types, host configuration and +cdist dump can be used for debugging existing types, host configuration and new types. @@ -88,10 +88,10 @@ EXAMPLES .. code-block:: sh # Dump all - % cdist-dump -a + % cdist dump -a # Dump only code-* output - % cdist-dump -c + % cdist dump -c SEE ALSO diff --git a/scripts/cdist b/scripts/cdist index 7bf12c01..4fc74fb3 100755 --- a/scripts/cdist +++ b/scripts/cdist @@ -30,14 +30,20 @@ import cdist.config import cdist.install import cdist.shell import cdist.inventory +import cdist.libexec as libexec def commandline(): """Parse command line""" - # preos subcommand hack - if len(sys.argv) > 1 and sys.argv[1] == 'preos': - return cdist.preos.PreOS.commandline(sys.argv[1:]) + if len(sys.argv) > 1: + # preos subcommand hack + if sys.argv[1] == 'preos': + return cdist.preos.PreOS.commandline(sys.argv[1:]) + # libexec subcommands hack + if libexec.is_libexec_command(sys.argv[1]): + return libexec.run(sys.argv[1], sys.argv[2:]) + parser, cfg = cdist.argparse.parse_and_configure(sys.argv[1:]) args = cfg.get_args() diff --git a/setup.py b/setup.py index 7b000041..95ce9574 100644 --- a/setup.py +++ b/setup.py @@ -56,7 +56,7 @@ setup( name="cdist", packages=["cdist", "cdist.core", "cdist.exec", "cdist.util", ], package_data={'cdist': package_data}, - scripts=["scripts/cdist", "scripts/cdist-dump", "scripts/cdist-new-type"], + scripts=["scripts/cdist", ], version=cdist.version.VERSION, description="A Usable Configuration Management System", author="Nico Schottelius",