From 9a2e5758f59b27fa17799bd67ba5accf82168f38 Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Sat, 26 Jan 2019 17:00:03 +0100 Subject: [PATCH 001/366] Implement triggering functionality --- cdist/argparse.py | 24 +- .../type/__cdist_preos_trigger/gencode-remote | 12 + cdist/conf/type/__cdist_preos_trigger/man.rst | 45 ++++ .../conf/type/__cdist_preos_trigger/manifest | 67 ++++++ .../__cdist_preos_trigger/parameter/required | 1 + cdist/preos/debootstrap/debootstrap.py | 6 + cdist/preos/debootstrap/files/code | 9 +- cdist/trigger.py | 225 ++++++++++++++++++ completions/bash/cdist-completion.bash | 10 +- completions/zsh/_cdist | 2 +- docs/changelog | 3 + docs/src/cdist-preos.rst | 55 ++++- docs/src/cdist-trigger.rst | 33 +++ docs/src/index.rst | 1 + docs/src/man1/cdist.rst | 121 +++++++++- 15 files changed, 593 insertions(+), 21 deletions(-) create mode 100644 cdist/conf/type/__cdist_preos_trigger/gencode-remote create mode 100644 cdist/conf/type/__cdist_preos_trigger/man.rst create mode 100644 cdist/conf/type/__cdist_preos_trigger/manifest create mode 100644 cdist/conf/type/__cdist_preos_trigger/parameter/required create mode 100644 cdist/trigger.py create mode 100644 docs/src/cdist-trigger.rst diff --git a/cdist/argparse.py b/cdist/argparse.py index 611c484a..47320e01 100644 --- a/cdist/argparse.py +++ b/cdist/argparse.py @@ -5,12 +5,13 @@ import logging import collections import functools import cdist.configuration +import cdist.trigger import cdist.preos import cdist.info # set of beta sub-commands -BETA_COMMANDS = set(('install', 'inventory', )) +BETA_COMMANDS = set(('install', 'inventory', 'preos', 'trigger', )) # set of beta arguments for sub-commands BETA_ARGS = { 'config': set(('tag', 'all_tagged_hosts', 'use_archiving', )), @@ -468,6 +469,27 @@ def get_parsers(): 'pattern', nargs='?', help='Glob pattern.') parser['info'].set_defaults(func=cdist.info.Info.commandline) + # Trigger + parser['trigger'] = parser['sub'].add_parser( + 'trigger', parents=[parser['loglevel'], + parser['beta'], + parser['config_main']]) + parser['trigger'].add_argument( + '-6', '--ipv6', default=False, + help=('Listen to both IPv4 and IPv6 (instead of only IPv4)'), + action='store_true') + parser['trigger'].add_argument( + '-D', '--directory', action='store', required=False, + help=('Where to create local files')) + parser['trigger'].add_argument( + '-H', '--http-port', action='store', default=3000, required=False, + help=('Create trigger listener via http on specified port')) + parser['trigger'].add_argument( + '-S', '--source', action='store', required=False, + help=('Which file to copy for creation')) + + parser['trigger'].set_defaults(func=cdist.trigger.Trigger.commandline) + for p in parser: parser[p].epilog = EPILOG diff --git a/cdist/conf/type/__cdist_preos_trigger/gencode-remote b/cdist/conf/type/__cdist_preos_trigger/gencode-remote new file mode 100644 index 00000000..d5e9fe5c --- /dev/null +++ b/cdist/conf/type/__cdist_preos_trigger/gencode-remote @@ -0,0 +1,12 @@ +#!/bin/sh + +os=$(cat "$__global/explorer/os") + +case "$os" in + devuan) + echo "update-rc.d cdist-preos-trigger defaults > /dev/null" + ;; + *) + ;; +esac + diff --git a/cdist/conf/type/__cdist_preos_trigger/man.rst b/cdist/conf/type/__cdist_preos_trigger/man.rst new file mode 100644 index 00000000..abbd553b --- /dev/null +++ b/cdist/conf/type/__cdist_preos_trigger/man.rst @@ -0,0 +1,45 @@ +cdist-type__cdist_preos_trigger(7) +================================== + +NAME +---- +cdist-type__cdist_preos_trigger - configure cdist preos trigger + + +DESCRIPTION +----------- +Create cdist PreOS trigger by creating systemd unit file that will be started +at boot and will execute trigger command - connect to specified host and port. + + +REQUIRED PARAMETERS +------------------- +trigger-command + Command that will be executed as a PreOS cdist trigger. + + +OPTIONAL PARAMETERS +------------------- +None + + +EXAMPLES +-------- + +.. code-block:: sh + + # Configure default curl trigger for host cdist.ungleich.ch at port 80. + __cdist_preos_trigger http --trigger-command '/usr/bin/curl cdist.ungleich.ch:80' + + +AUTHORS +------- +Darko Poljak + + +COPYING +------- +Copyright \(C) 2016 Darko Poljak. 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. diff --git a/cdist/conf/type/__cdist_preos_trigger/manifest b/cdist/conf/type/__cdist_preos_trigger/manifest new file mode 100644 index 00000000..750a0548 --- /dev/null +++ b/cdist/conf/type/__cdist_preos_trigger/manifest @@ -0,0 +1,67 @@ +#!/bin/sh + +os="$(cat "$__global/explorer/os")" +trigger_command=$(cat "$__object/parameter/trigger-command") + +case "$os" in + devuan) + __file /etc/init.d/cdist-preos-trigger --owner root \ + --group root \ + --mode 755 \ + --source - << EOF +#!/bin/sh +# /etc/init.d/cdist-preos-trigger + +### BEGIN INIT INFO +# Provides: cdist-preos-trigger +# Required-Start: \$all +# Required-Stop: +# Default-Start: 2 3 4 5 S +# Default-Stop: 0 1 6 +# Short-Description: Execute cdist preos trigger command +# Description: Execute cdist preos trigger commnad. +### END INIT INFO + +case "\$1" in + start) + echo "Starting cdist-preos-trigger command" + ${trigger_command} & + ;; + stop) + # no-op + ;; + *) + echo "Usage: /etc/init.d/cdist-preos-trigger {start|stop}" + exit 1 + ;; +esac + +exit 0 +EOF + ;; + *) + __file /etc/systemd/system/cdist-preos-trigger.service --owner root \ + --group root \ + --mode 644 \ + --source - << EOF +[Unit] +Description=preos trigger +Wants=network-online.target +After=network.target network-online.target + +[Service] +Type=simple +Restart=no +# Broken systemd +ExecStartPre=/bin/sleep 5 +ExecStart=${trigger_command} + +[Install] +WantedBy=multi-user.target +EOF + + require="__file/etc/systemd/system/cdist-preos-trigger.service" \ + __start_on_boot cdist-preos-trigger + ;; +esac + diff --git a/cdist/conf/type/__cdist_preos_trigger/parameter/required b/cdist/conf/type/__cdist_preos_trigger/parameter/required new file mode 100644 index 00000000..3407a482 --- /dev/null +++ b/cdist/conf/type/__cdist_preos_trigger/parameter/required @@ -0,0 +1 @@ +trigger-command diff --git a/cdist/preos/debootstrap/debootstrap.py b/cdist/preos/debootstrap/debootstrap.py index f53dd4a7..7b56008d 100644 --- a/cdist/preos/debootstrap/debootstrap.py +++ b/cdist/preos/debootstrap/debootstrap.py @@ -127,6 +127,12 @@ class Debian(object): help="suite used for debootstrap, " "by default '{}'".format(defargs.suite), dest='suite', default=defargs.suite) + parser.add_argument( + '-t', '--trigger-command', + help=("trigger command that will be added to cdist config; " + "'__cdist_preos_trigger http ...' type is appended to " + "initial manifest"), + dest='trigger_command') parser.add_argument( '-y', '--remote-copy', help=("remote copy that cdist config will use, by default " diff --git a/cdist/preos/debootstrap/files/code b/cdist/preos/debootstrap/files/code index 9e37003b..50e73972 100755 --- a/cdist/preos/debootstrap/files/code +++ b/cdist/preos/debootstrap/files/code @@ -127,6 +127,13 @@ then exit 1 fi + if [ "${trigger_command}" ] + then + trigger_line="__cdist_preos_trigger http --trigger-command '${trigger_command}'\n" + else + trigger_line="" + fi + if [ "${keyfile_cnt}" -a "${keyfile_cnt}" -gt 0 ] then i="$((keyfile_cnt - 1))" @@ -174,7 +181,7 @@ then fi grub_lines="${grub_manifest_line}${grub_kern_params_line}" - printf "${ssh_auth_keys_line}${grub_lines}" \ + printf "${trigger_line}${ssh_auth_keys_line}${grub_lines}" \ | cat "${manifest}" - |\ cdist config \ ${cdist_params} -i - \ diff --git a/cdist/trigger.py b/cdist/trigger.py new file mode 100644 index 00000000..4e66ac6b --- /dev/null +++ b/cdist/trigger.py @@ -0,0 +1,225 @@ +#!/usr/bin/env python3 +# -*- coding: utf-8 -*- +# +# 2016 Nico Schottelius (nico-cdist at schottelius.org) +# +# 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 . +# +# + +import ipaddress +import logging +import re +import socket +import http.server +import os +import socketserver +import shutil + +import cdist.config +import cdist.log +import cdist.util.ipaddr as ipaddr + + +class Trigger(): + """cdist trigger handling""" + + # Arguments that are only trigger specific + triggers_args = ["http_port", "ipv6", "directory", "source", ] + + def __init__(self, http_port=None, dry_run=False, ipv6=False, + directory=None, source=None, cdistargs=None): + self.dry_run = dry_run + self.http_port = int(http_port) + self.ipv6 = ipv6 + self.args = cdistargs + + self.directory = directory + self.source = source + + log.debug("IPv6: %s", self.ipv6) + + def run_httpd(self): + server_address = ('', self.http_port) + + if self.ipv6: + httpdcls = HTTPServerV6 + else: + httpdcls = HTTPServerV4 + httpd = httpdcls(self.args, self.directory, self.source, + server_address, TriggerHttp) + + log.debug("Starting server at port %d", self.http_port) + if self.dry_run: + log.debug("Running in dry run mode") + httpd.serve_forever() + + def run(self): + if self.http_port: + self.run_httpd() + + @classmethod + def commandline(cls, args): + global log + + # remove root logger default cdist handler and configure trigger's own + logging.getLogger().handlers = [] + logging.basicConfig(format='%(asctime)s %(levelname)s: %(message)s') + + log = logging.getLogger("trigger") + ownargs = {} + for targ in cls.triggers_args: + arg = getattr(args, targ) + ownargs[targ] = arg + + del arg + + t = cls(dry_run=args.dry_run, cdistargs=args, **ownargs) + t.run() + + +class TriggerHttp(http.server.BaseHTTPRequestHandler): + actions = { + "cdist": ["config", "install", ], + "file": ["present", "absent", ], + } + + def do_HEAD(self): + self.dispatch_request() + + def do_POST(self): + self.dispatch_request() + + def do_GET(self): + self.dispatch_request() + + def _actions_regex(self): + regex = ["^/(?P", ] + regex.extend("|".join(self.actions.keys())) + regex.append(")/(?P") + regex.extend("|".join("|".join(self.actions[x]) for x in self.actions)) + regex.append(")/") + + return "".join(regex) + + def dispatch_request(self): + host = self.client_address[0] + code = 200 + message = None + + self.cdistargs = self.server.cdistargs + + actions_regex = self._actions_regex() + m = re.match(actions_regex, self.path) + + if m: + subsystem = m.group('subsystem') + action = m.group('action') + handler = getattr(self, "handler_" + subsystem) + + if action not in self.actions[subsystem]: + code = 404 + else: + code = 404 + + if code == 200: + log.debug("Calling {} -> {}".format(subsystem, action)) + try: + handler(action, host) + except cdist.Error as e: + # cdist is not broken, cdist run is broken + code = 599 # use arbitrary unassigned error code + message = str(e) + except Exception as e: + # cdist/trigger server is broken + code = 500 + + self.send_response(code=code, message=message) + self.end_headers() + + def handler_file(self, action, host): + if not self.server.directory or not self.server.source: + log.info("Cannot serve file request: directory or source " + "not setup") + return + + try: + ipaddress.ip_address(host) + except ValueError: + log.error("Host is not a valid IP address - aborting") + return + + dst = os.path.join(self.server.directory, host) + + if action == "present": + shutil.copyfile(self.server.source, dst) + if action == "absent": + if os.path.exists(dst): + os.remove(dst) + + def handler_cdist(self, action, host): + log.debug("Running cdist action %s for %s", action, host) + + if self.server.dry_run: + log.info("Dry run, skipping cdist execution") + return + + cname = action.title() + module = getattr(cdist, action) + theclass = getattr(module, cname) + + if hasattr(self.cdistargs, 'out_path'): + out_path = self.cdistargs.out_path + else: + out_path = None + host_base_path, hostdir = theclass.create_host_base_dirs( + host, theclass.create_base_root_path(out_path)) + theclass.construct_remote_exec_copy_patterns(self.cdistargs) + host_tags = None + host_name = ipaddr.resolve_target_host_name(host) + log.debug('Resolved target host name: %s', host_name) + if host_name: + target_host = host_name + else: + target_host = host + log.debug('Using target_host: %s', target_host) + log.debug("Executing cdist onehost with params: %s, %s, %s, %s, %s, ", + target_host, host_tags, host_base_path, hostdir, + self.cdistargs) + theclass.onehost(target_host, host_tags, host_base_path, hostdir, + self.cdistargs, parallel=False) + + +class HTTPServerV6(socketserver.ForkingMixIn, http.server.HTTPServer): + """ + Server that listens to both IPv4 and IPv6 requests. + """ + address_family = socket.AF_INET6 + + def __init__(self, cdistargs, directory, source, *args, **kwargs): + self.cdistargs = cdistargs + self.dry_run = cdistargs.dry_run + self.directory = directory + self.source = source + + http.server.HTTPServer.__init__(self, *args, **kwargs) + + +class HTTPServerV4(HTTPServerV6): + """ + Server that listens to IPv4 requests. + """ + address_family = socket.AF_INET diff --git a/completions/bash/cdist-completion.bash b/completions/bash/cdist-completion.bash index cdac7f29..ed5c6a55 100644 --- a/completions/bash/cdist-completion.bash +++ b/completions/bash/cdist-completion.bash @@ -6,7 +6,7 @@ _cdist() prev="${COMP_WORDS[COMP_CWORD-1]}" prevprev="${COMP_WORDS[COMP_CWORD-2]}" opts="-h --help -q --quiet -v --verbose -V --version" - cmds="banner config install inventory preos shell" + cmds="banner config install inventory preos shell trigger" case "${prevprev}" in shell) @@ -80,6 +80,14 @@ _cdist() COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) return 0 ;; + trigger) + opts="-h --help -d --debug -v --verbose -b --beta \ + -C --cache-path-pattern -c --conf-dir -i --initial-manifest \ + -j --jobs -n --dry-run -o --out-dir --remote-copy \ + --remote-exec -6 --ipv6 -H --http-port -D --directory -S --source" + COMPREPLY=( $(compgen -W "${opts}" -- ${cur}) ) + return 0 + ;; inventory) cmds="list add-host del-host add-tag del-tag" opts="-h --help -q --quiet -v --verbose" diff --git a/completions/zsh/_cdist b/completions/zsh/_cdist index 1bd275ef..5d653095 100644 --- a/completions/zsh/_cdist +++ b/completions/zsh/_cdist @@ -11,7 +11,7 @@ _cdist() case $state in opts_cmds) - _arguments '1:Options and commands:(banner config install inventory preos shell -h --help -q --quiet -v --verbose -V --version)' + _arguments '1:Options and commands:(banner config install inventory preos shell trigger -h --help -q --quiet -v --verbose -V --version)' ;; *) case $words[2] in diff --git a/docs/changelog b/docs/changelog index 9f1e6245..25566aa8 100644 --- a/docs/changelog +++ b/docs/changelog @@ -1,6 +1,9 @@ Changelog --------- +next: + * Core: Add trigger functionality (Nico Schottelius, Darko Poljak) + 6.5.1: 2020-02-15 * Type __consul_agent: Add Debian 10 support (Nico Schottelius) * Explorer os_release: Add fallbacks (Dennis Camera) diff --git a/docs/src/cdist-preos.rst b/docs/src/cdist-preos.rst index 9570bcfc..59d0d13e 100644 --- a/docs/src/cdist-preos.rst +++ b/docs/src/cdist-preos.rst @@ -25,15 +25,16 @@ For example, to create an ubuntu PreOS: .. code-block:: sh $ cdist preos ubuntu /preos/preos-ubuntu -B -C \ - -k ~/.ssh/id_rsa.pub -p /preos/pxe-ubuntu + -k ~/.ssh/id_rsa.pub -p /preos/pxe-ubuntu \ + -t "/usr/bin/curl 192.168.111.5:3000/install/" For more info about the available options see the cdist manual page. -This will bootstrap (``-B``) ubuntu PreOS in the ``/preos/preos-ubuntu`` -directory, it will be configured (``-C``) using default the built-in initial -manifest and with specified ssh authorized key (``-k``). -After bootstrapping and configuration, the PXE boot directory will be -created (``-p``) in ``/preos/pxe-ubuntu``. +This will bootstrap (``-B``) ubuntu PreOS in ``/preos/preos-ubuntu`` directory, it +will be configured (``-C``) using default built-in initial manifest and with +specified ssh authorized key (``-k``) and with specified trigger command (``-t``). +After bootstrapping and configuration PXE +boot directory will be created (``-p``) in ``/preos/pxe-ubuntu``. After PreOS is created, new machines can be booted using the created PXE (after proper dhcp and tftp settings). @@ -41,8 +42,17 @@ After PreOS is created, new machines can be booted using the created PXE Since PreOS is configured with ssh authorized key it can be accessed through ssh, i.e. it can be further installed and configured with cdist. -Implementing a new PreOS sub-command ------------------------------------- +When installing and configuring new machines using cdist's PreOS concept +cdist can use triggering for host installation/configuration, which is described +in the previous chapter. + +When new machine is booted with PreOS then trigger command is executed. +Machine will connect to cdist trigger server. If the request is, for example, +for installation then cdist trigger server will start install command for the +client host using parameters specified at trigger server startup. + +Implementing new PreOS sub-command +---------------------------------- preos command is implemented as a plugin system. This plugin system scans for preos subcommands in the ``cdist/preos/`` distribution directory and also in ``~/.cdist/preos/`` directory if it exists. @@ -127,3 +137,32 @@ When you try to run this new preos you will get: In the ``commandline`` function/method you have all the freedom to actually create a PreOS. + +Simple tipical use case for using PreOS and trigger +--------------------------------------------------- +Tipical use case for using PreOS and trigger command include the following steps. + +#. Create PreOS PXE with ssh key and trigger command for installation. + + .. code-block:: sh + + $ cdist preos ubuntu /preos/ubuntu -b -C \ + -k ~/.ssh/id_rsa.pub -p /preos/pxe \ + -t "/usr/bin/curl 192.168.111.5:3000/install/" + +#. Configure dhcp server and tftp server. + +#. On cdist host (192.168.111.5 from above) start trigger command (it will use + default init manifest for installation). + + .. code-block:: sh + + $ cdist trigger -b -v + +#. After all is set up start new machines (PXE boot). + +#. New machine boots and executes trigger command, i.e. triggers installation. + +#. Cdist trigger server starts installing host that has triggered it. + +#. After cdist install is finished new host is installed. diff --git a/docs/src/cdist-trigger.rst b/docs/src/cdist-trigger.rst new file mode 100644 index 00000000..94477c29 --- /dev/null +++ b/docs/src/cdist-trigger.rst @@ -0,0 +1,33 @@ +Trigger +======= + +Description +----------- +cdist supports triggering for host installation/configuration using trigger command. +This command starts trigger server at management node, for example: + +.. code-block:: sh + + $ cdist trigger -b -v + +This will start cdist trigger server in verbose mode. cdist trigger server accepts +simple requests for configuration and for installation: + +* :strong:`/cdist/install/.*` for installation +* :strong:`/cdist/config/.*` for configuration. + +Machines can then trigger cdist trigger server with appropriate requests. +If the request is, for example, for installation (:strong:`/cdist/install/`) +then cdist trigger server will start install command for the client host using +parameters specified at trigger server startup. For the above example that means +that client will be installed using default initial manifest. + +When triggered cdist will try to reverse DNS lookup for host name and if +host name is dervied then it is used for running cdist config. If no +host name is resolved then IP address is used. + +This command returns the following response codes to client requests: + +* 200 for success +* 599 for cdist run errors +* 500 for cdist/server errors. diff --git a/docs/src/index.rst b/docs/src/index.rst index 31c044dc..652a018a 100644 --- a/docs/src/index.rst +++ b/docs/src/index.rst @@ -33,6 +33,7 @@ It natively supports IPv6 since the first release. cdist-messaging cdist-parallelization cdist-inventory + cdist-trigger cdist-preos cdist-integration cdist-reference diff --git a/docs/src/man1/cdist.rst b/docs/src/man1/cdist.rst index 38248821..fba2b322 100644 --- a/docs/src/man1/cdist.rst +++ b/docs/src/man1/cdist.rst @@ -11,7 +11,7 @@ SYNOPSIS :: - cdist [-h] [-V] {banner,config,install,inventory,preos,shell,info} ... + cdist [-h] [-V] {banner,config,install,inventory,preos,shell,info,trigger} ... cdist banner [-h] [-l LOGLEVEL] [-q] [-v] @@ -67,27 +67,37 @@ SYNOPSIS [-C] [-c CDIST_PARAMS] [-D DRIVE] [-e REMOTE_EXEC] [-i MANIFEST] [-k KEYFILE ] [-m MIRROR] [-P ROOT_PASSWORD] [-p PXE_BOOT_DIR] [-r] - [-S SCRIPT] [-s SUITE] [-y REMOTE_COPY] + [-S SCRIPT] [-s SUITE] [-t TRIGGER_COMMAND] + [-y REMOTE_COPY] target_dir cdist preos [preos-options] devuan [-h] [-l LOGLEVEL] [-q] [-v] [-b] [-a ARCH] [-B] [-C] [-c CDIST_PARAMS] [-D DRIVE] [-e REMOTE_EXEC] [-i MANIFEST] [-k KEYFILE ] [-m MIRROR] [-P ROOT_PASSWORD] [-p PXE_BOOT_DIR] [-r] - [-S SCRIPT] [-s SUITE] [-y REMOTE_COPY] + [-S SCRIPT] [-s SUITE] [-t TRIGGER_COMMAND] + [-y REMOTE_COPY] target_dir cdist preos [preos-options] ubuntu [-h] [-l LOGLEVEL] [-q] [-v] [-b] [-a ARCH] [-B] [-C] [-c CDIST_PARAMS] [-D DRIVE] [-e REMOTE_EXEC] [-i MANIFEST] [-k KEYFILE ] [-m MIRROR] [-P ROOT_PASSWORD] [-p PXE_BOOT_DIR] [-r] - [-S SCRIPT] [-s SUITE] [-y REMOTE_COPY] + [-S SCRIPT] [-s SUITE] [-t TRIGGER_COMMAND] + [-y REMOTE_COPY] target_dir cdist shell [-h] [-l LOGLEVEL] [-q] [-v] [-s SHELL] cdist info [-h] [-a] [-c CONF_DIR] [-e] [-F] [-f] [-g CONFIG_FILE] [-t] [pattern] + cdist trigger [-h] [-l LOGLEVEL] [-q] [-v] [-b] [-C CACHE_PATH_PATTERN] + [-c CONF_DIR] [-i MANIFEST] [-j [JOBS]] [-n] + [-o OUT_PATH] [-R [{tar,tgz,tbz2,txz}]] + [-r REMOTE_OUT_PATH] [--remote-copy REMOTE_COPY] + [--remote-exec REMOTE_EXEC] [-6] [-D DIRECTORY] + [-H HTTP_PORT] [-S SOURCE] + DESCRIPTION ----------- @@ -534,6 +544,10 @@ PREOS DEBIAN/DEVUAN **-s SUITE, --suite SUITE** suite used for debootstrap, by default 'stable' +**-t TRIGGER_COMMAND, --trigger-command TRIGGER_COMMAND** + trigger command that will be added to cdist config; + '``__cdist_preos_trigger http ...``' type is appended to initial manifest + **-y REMOTE_COPY, --remote-copy REMOTE_COPY** remote copy that cdist config will use, by default internal script is used @@ -594,6 +608,10 @@ PREOS UBUNTU **-s SUITE, --suite SUITE** suite used for debootstrap, by default 'xenial' +**-t TRIGGER_COMMAND, --trigger-command TRIGGER_COMMAND** + trigger command that will be added to cdist config; + '``__cdist_preos_trigger http ...``' type is appended to initial manifest + **-y REMOTE_COPY, --remote-copy REMOTE_COPY** remote copy that cdist config will use, by default internal script is used @@ -643,6 +661,83 @@ Display information for cdist (global explorers, types). **-t, --types** Display info for types. +TRIGGER +------- +Start trigger (simple http server) that waits for connections. When host +connects then it triggers config or install command and then cdist +config/install is executed which configures/installs host. +When triggered cdist will try to reverse DNS lookup for host name and if +host name is dervied then it is used for running cdist config. If no +host name is resolved then IP address is used. +Request path recognizes following requests: + +* :strong:`/cdist/config/.*` for config +* :strong:`/cdist/install/.*` for install. + +This command returns the following response codes to client requests: + +* 200 for success +* 599 for cdist run errors +* 500 for cdist/server errors. + + +**-6, --ipv6** + + Listen to both IPv4 and IPv6 (instead of only IPv4) + +**-b, --beta** + + Enable beta functionality. + +**-C CACHE_PATH_PATTERN, --cache-path-pattern CACHE_PATH_PATTERN** + + Sepcify custom cache path pattern. It can also be set by + CDIST_CACHE_PATH_PATTERN environment variable. If it is not set then + default hostdir is used. For more info on format see + :strong:`CACHE PATH PATTERN FORMAT` below. + +**-c CONF_DIR, --conf-dir CONF_DIR** + + Add configuration directory (can be repeated, last one wins) + +**-D DIRECTORY, --directory DIRECTORY** + Where to create local files + +**-H HTTP_PORT, --http-port HTTP_PORT** + + Create trigger listener via http on specified port + +**-i MANIFEST, --initial-manifest MANIFEST** + + path to a cdist manifest or '-' to read from stdin. + +**-j [JOBS], --jobs [JOBS]** + + Specify the maximum number of parallel jobs, currently + only global explorers are supported + +**-n, --dry-run** + + do not execute code + +**-o OUT_PATH, --out-dir OUT_PATH** + + directory to save cdist output in + +**-r REMOTE_OUT_PATH, --remote-out-dir REMOTE_OUT_PATH** + + Directory to save cdist output in on the target host + +**--remote-copy REMOTE_COPY** + + Command to use for remote copy (should behave like scp) + +**--remote-exec REMOTE_EXEC** + + Command to use for remote execution (should behave like ssh) + +**-S SOURCE, --source SOURCE** + Which file to copy for creation CONFIGURATION ------------- @@ -838,20 +933,28 @@ EXAMPLES # Configure all hosts from inventory db $ cdist config -b -A - # Create default debian PreOS in debug mode + # Create default debian PreOS in debug mode with config + # trigger command $ cdist preos debian /preos/preos-debian -vvvv -C \ - -k ~/.ssh/id_rsa.pub -p /preos/pxe-debian + -k ~/.ssh/id_rsa.pub -p /preos/pxe-debian \ + -t "/usr/bin/curl 192.168.111.5:3000/config/" - # Create ubuntu PreOS + # Create ubuntu PreOS with install trigger command $ cdist preos ubuntu /preos/preos-ubuntu -C \ - -k ~/.ssh/id_rsa.pub -p /preos/pxe-ubuntu + -k ~/.ssh/id_rsa.pub -p /preos/pxe-ubuntu \ + -t "/usr/bin/curl 192.168.111.5:3000/install/" - # Create ubuntu PreOS on drive /dev/sdb + # Create ubuntu PreOS on drive /dev/sdb with install trigger command # and set root password to 'password'. $ cdist preos ubuntu /mnt -B -C \ -k ~/.ssh/id_rsa.pub -D /dev/sdb \ + -t "/usr/bin/curl 192.168.111.5:3000/install/" \ -P password + # Start trigger in verbose mode that will configure host using specified + # init manifest + % cdist trigger -v -i ~/.cdist/manifest/init-for-triggered + ENVIRONMENT ----------- From 9d030bddc15f32177854ca4a54c8018b6f492497 Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Fri, 20 Sep 2019 20:08:55 +0200 Subject: [PATCH 002/366] ++ --- cdist/argparse.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cdist/argparse.py b/cdist/argparse.py index 47320e01..323383ae 100644 --- a/cdist/argparse.py +++ b/cdist/argparse.py @@ -11,7 +11,7 @@ import cdist.info # set of beta sub-commands -BETA_COMMANDS = set(('install', 'inventory', 'preos', 'trigger', )) +BETA_COMMANDS = set(('install', 'inventory', 'trigger', )) # set of beta arguments for sub-commands BETA_ARGS = { 'config': set(('tag', 'all_tagged_hosts', 'use_archiving', )), From 912c6759034f7034d2299fc4e63e09e342b49b17 Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Mon, 28 Jan 2019 16:06:36 +0100 Subject: [PATCH 003/366] Log trigger server error --- cdist/trigger.py | 1 + 1 file changed, 1 insertion(+) diff --git a/cdist/trigger.py b/cdist/trigger.py index 4e66ac6b..1b9a384b 100644 --- a/cdist/trigger.py +++ b/cdist/trigger.py @@ -145,6 +145,7 @@ class TriggerHttp(http.server.BaseHTTPRequestHandler): message = str(e) except Exception as e: # cdist/trigger server is broken + log.exception(e) code = 500 self.send_response(code=code, message=message) From a09467917374edc91fb3c6df3d5ea5abd2a585d1 Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Mon, 28 Jan 2019 16:46:40 +0100 Subject: [PATCH 004/366] Update trigger to config --- cdist/argparse.py | 11 +++--- cdist/config.py | 13 ++++--- docs/src/man1/cdist.rst | 80 +++++++++++++++++++++++------------------ 3 files changed, 60 insertions(+), 44 deletions(-) diff --git a/cdist/argparse.py b/cdist/argparse.py index 323383ae..ece7ff10 100644 --- a/cdist/argparse.py +++ b/cdist/argparse.py @@ -473,11 +473,8 @@ def get_parsers(): parser['trigger'] = parser['sub'].add_parser( 'trigger', parents=[parser['loglevel'], parser['beta'], + parser['common'], parser['config_main']]) - parser['trigger'].add_argument( - '-6', '--ipv6', default=False, - help=('Listen to both IPv4 and IPv6 (instead of only IPv4)'), - action='store_true') parser['trigger'].add_argument( '-D', '--directory', action='store', required=False, help=('Where to create local files')) @@ -485,7 +482,11 @@ def get_parsers(): '-H', '--http-port', action='store', default=3000, required=False, help=('Create trigger listener via http on specified port')) parser['trigger'].add_argument( - '-S', '--source', action='store', required=False, + '--ipv6', default=False, + help=('Listen to both IPv4 and IPv6 (instead of only IPv4)'), + action='store_true') + parser['trigger'].add_argument( + '-O', '--source', action='store', required=False, help=('Which file to copy for creation')) parser['trigger'].set_defaults(func=cdist.trigger.Trigger.commandline) diff --git a/cdist/config.py b/cdist/config.py index 97cc1da6..44afe7d5 100644 --- a/cdist/config.py +++ b/cdist/config.py @@ -144,7 +144,7 @@ class Config(object): # Determine forcing IPv4/IPv6 options if any, only for # default remote commands. - if args.force_ipv: + if hasattr(args, 'force_ipv') and args.force_ipv: force_addr_opt = " -{}".format(args.force_ipv) else: force_addr_opt = "" @@ -357,10 +357,13 @@ class Config(object): @staticmethod def _address_family(args): - if args.force_ipv == 4: - family = socket.AF_INET - elif args.force_ipv == 6: - family = socket.AF_INET6 + if hasattr(args, 'force_ipv'): + if args.force_ipv == 4: + family = socket.AF_INET + elif args.force_ipv == 6: + family = socket.AF_INET6 + else: + family = 0 else: family = 0 return family diff --git a/docs/src/man1/cdist.rst b/docs/src/man1/cdist.rst index fba2b322..01e25c4e 100644 --- a/docs/src/man1/cdist.rst +++ b/docs/src/man1/cdist.rst @@ -91,12 +91,12 @@ SYNOPSIS cdist info [-h] [-a] [-c CONF_DIR] [-e] [-F] [-f] [-g CONFIG_FILE] [-t] [pattern] - cdist trigger [-h] [-l LOGLEVEL] [-q] [-v] [-b] [-C CACHE_PATH_PATTERN] - [-c CONF_DIR] [-i MANIFEST] [-j [JOBS]] [-n] - [-o OUT_PATH] [-R [{tar,tgz,tbz2,txz}]] - [-r REMOTE_OUT_PATH] [--remote-copy REMOTE_COPY] - [--remote-exec REMOTE_EXEC] [-6] [-D DIRECTORY] - [-H HTTP_PORT] [-S SOURCE] + cdist trigger [-h] [-l LOGLEVEL] [-q] [-v] [-b] [-g CONFIG_FILE] [-4] + [-6] [-C CACHE_PATH_PATTERN] [-c CONF_DIR] [-i MANIFEST] + [-j [JOBS]] [-n] [-o OUT_PATH] [-P] + [-R [{tar,tgz,tbz2,txz}]] [-r REMOTE_OUT_PATH] + [--remote-copy REMOTE_COPY] [--remote-exec REMOTE_EXEC] + [-S] [-D DIRECTORY] [-H HTTP_PORT] [--ipv6] [-O SOURCE] DESCRIPTION @@ -680,64 +680,76 @@ This command returns the following response codes to client requests: * 599 for cdist run errors * 500 for cdist/server errors. +**-4, -force-ipv4** + Force to use IPv4 addresses only. No influence for + custom remote commands. -**-6, --ipv6** - - Listen to both IPv4 and IPv6 (instead of only IPv4) - -**-b, --beta** - - Enable beta functionality. +**-6, --force-ipv6** + Force to use IPv6 addresses only. No influence for + custom remote commands. **-C CACHE_PATH_PATTERN, --cache-path-pattern CACHE_PATH_PATTERN** - - Sepcify custom cache path pattern. It can also be set by - CDIST_CACHE_PATH_PATTERN environment variable. If it is not set then - default hostdir is used. For more info on format see - :strong:`CACHE PATH PATTERN FORMAT` below. + Specify custom cache path pattern. If it is not set + then default hostdir is used. **-c CONF_DIR, --conf-dir CONF_DIR** - - Add configuration directory (can be repeated, last one wins) + Add configuration directory (can be repeated, last one + wins). **-D DIRECTORY, --directory DIRECTORY** Where to create local files -**-H HTTP_PORT, --http-port HTTP_PORT** +**-g CONFIG_FILE, --config-file CONFIG_FILE** + Use specified custom configuration file. +**-H HTTP_PORT, --http-port HTTP_PORT** Create trigger listener via http on specified port **-i MANIFEST, --initial-manifest MANIFEST** + Path to a cdist manifest or '-' to read from stdin. - path to a cdist manifest or '-' to read from stdin. +**--ipv6** + Listen to both IPv4 and IPv6 (instead of only IPv4) **-j [JOBS], --jobs [JOBS]** - - Specify the maximum number of parallel jobs, currently - only global explorers are supported + Operate in parallel in specified maximum number of + jobs. Global explorers, object prepare and object run + are supported. Without argument CPU count is used by + default. Currently in beta. **-n, --dry-run** + Do not execute code. - do not execute code +**-O SOURCE, --source SOURCE** + Which file to copy for creation **-o OUT_PATH, --out-dir OUT_PATH** + Directory to save cdist output in. - directory to save cdist output in +**-P, --timestamp** + Timestamp log messages with the current local date and + time in the format: YYYYMMDDHHMMSS.us. + +**-R [{tar,tgz,tbz2,txz}], --use-archiving [{tar,tgz,tbz2,txz}]** + Operate by using archiving with compression where + appropriate. Supported values are: tar - tar archive, + tgz - gzip tar archive (the default), tbz2 - bzip2 tar + archive and txz - lzma tar archive. Currently in beta. **-r REMOTE_OUT_PATH, --remote-out-dir REMOTE_OUT_PATH** - - Directory to save cdist output in on the target host + Directory to save cdist output in on the target host. **--remote-copy REMOTE_COPY** - - Command to use for remote copy (should behave like scp) + Command to use for remote copy (should behave like + scp). **--remote-exec REMOTE_EXEC** + Command to use for remote execution (should behave + like ssh). - Command to use for remote execution (should behave like ssh) +**-S, --disable-saving-output-streams** + Disable saving output streams. -**-S SOURCE, --source SOURCE** - Which file to copy for creation CONFIGURATION ------------- From 1a5cc67cc8f16262806697978313f0855872eab6 Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Mon, 28 Jan 2019 16:35:02 +0100 Subject: [PATCH 005/366] Add missing configuration arg --- cdist/config.py | 13 +++++-------- cdist/trigger.py | 5 ++++- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/cdist/config.py b/cdist/config.py index 44afe7d5..97cc1da6 100644 --- a/cdist/config.py +++ b/cdist/config.py @@ -144,7 +144,7 @@ class Config(object): # Determine forcing IPv4/IPv6 options if any, only for # default remote commands. - if hasattr(args, 'force_ipv') and args.force_ipv: + if args.force_ipv: force_addr_opt = " -{}".format(args.force_ipv) else: force_addr_opt = "" @@ -357,13 +357,10 @@ class Config(object): @staticmethod def _address_family(args): - if hasattr(args, 'force_ipv'): - if args.force_ipv == 4: - family = socket.AF_INET - elif args.force_ipv == 6: - family = socket.AF_INET6 - else: - family = 0 + if args.force_ipv == 4: + family = socket.AF_INET + elif args.force_ipv == 6: + family = socket.AF_INET6 else: family = 0 return family diff --git a/cdist/trigger.py b/cdist/trigger.py index 1b9a384b..58f98dca 100644 --- a/cdist/trigger.py +++ b/cdist/trigger.py @@ -200,8 +200,11 @@ class TriggerHttp(http.server.BaseHTTPRequestHandler): log.debug("Executing cdist onehost with params: %s, %s, %s, %s, %s, ", target_host, host_tags, host_base_path, hostdir, self.cdistargs) + cfg = cdist.configuration.Configuration(self.cdistargs) + configuration = cfg.get_config(section='GLOBAL') theclass.onehost(target_host, host_tags, host_base_path, hostdir, - self.cdistargs, parallel=False) + self.cdistargs, parallel=False, + configuration=configuration) class HTTPServerV6(socketserver.ForkingMixIn, http.server.HTTPServer): From 4a5a475e3e79d4a1b8d50b0b96b5291840ff5bda Mon Sep 17 00:00:00 2001 From: Dominique Roux Date: Fri, 1 Feb 2019 17:49:14 +0100 Subject: [PATCH 006/366] Updated the man pages for the cdist trigger and preos --- docs/src/cdist-preos.rst | 4 ++-- docs/src/cdist-trigger.rst | 6 ++++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/src/cdist-preos.rst b/docs/src/cdist-preos.rst index 59d0d13e..ad88b277 100644 --- a/docs/src/cdist-preos.rst +++ b/docs/src/cdist-preos.rst @@ -26,7 +26,7 @@ For example, to create an ubuntu PreOS: $ cdist preos ubuntu /preos/preos-ubuntu -B -C \ -k ~/.ssh/id_rsa.pub -p /preos/pxe-ubuntu \ - -t "/usr/bin/curl 192.168.111.5:3000/install/" + -t "/usr/bin/curl 192.168.111.5:3000/cdist/install/" For more info about the available options see the cdist manual page. @@ -148,7 +148,7 @@ Tipical use case for using PreOS and trigger command include the following steps $ cdist preos ubuntu /preos/ubuntu -b -C \ -k ~/.ssh/id_rsa.pub -p /preos/pxe \ - -t "/usr/bin/curl 192.168.111.5:3000/install/" + -t "/usr/bin/curl 192.168.111.5:3000/cdist/install/" #. Configure dhcp server and tftp server. diff --git a/docs/src/cdist-trigger.rst b/docs/src/cdist-trigger.rst index 94477c29..93e9d920 100644 --- a/docs/src/cdist-trigger.rst +++ b/docs/src/cdist-trigger.rst @@ -13,8 +13,10 @@ This command starts trigger server at management node, for example: This will start cdist trigger server in verbose mode. cdist trigger server accepts simple requests for configuration and for installation: -* :strong:`/cdist/install/.*` for installation -* :strong:`/cdist/config/.*` for configuration. +* :strong:`/cdist/install/` for installation +* :strong:`/cdist/config/` for configuration. + +Other configuration parameters are the same as in like cdist config (See `cdist `_). Machines can then trigger cdist trigger server with appropriate requests. If the request is, for example, for installation (:strong:`/cdist/install/`) From 3dcd27015811e1e6659c3d13e885f28815a7e9a8 Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Thu, 1 Nov 2018 17:56:40 +0100 Subject: [PATCH 007/366] Implement python types --- cdist/conf/type/__file_py/__init__.py | 103 +++++ cdist/conf/type/__file_py/explorer/cksum | 34 ++ cdist/conf/type/__file_py/explorer/stat | 56 +++ cdist/conf/type/__file_py/explorer/type | 33 ++ .../type/__file_py/parameter/default/state | 1 + cdist/conf/type/__file_py/parameter/optional | 5 + cdist/config.py | 65 ++- cdist/core/__init__.py | 1 + cdist/core/code.py | 42 ++ cdist/core/manifest.py | 75 +++- cdist/core/pytypes.py | 162 ++++++++ cdist/exec/local.py | 16 + cdist/util/python_type_util.py | 27 ++ docs/changelog | 1 + docs/dev/python-types/benchmark | 376 ++++++++++++++++++ docs/dev/python-types/benchmark.sh | 43 ++ docs/dev/python-types/conf/manifest/pyinit | 7 + docs/dev/python-types/conf/manifest/shinit | 7 + .../conf/type/__dummy_config_py/__init__.py | 22 + .../type/__dummy_config_py/files/dummy.conf | 1 + .../type/__dummy_config_sh/files/dummy.conf | 1 + .../conf/type/__dummy_config_sh/manifest | 6 + docs/dev/python-types/test.sh | 27 ++ docs/dev/python-types/timeit.sh | 36 ++ docs/src/cdist-type.rst | 86 ++++ scripts/cdist | 2 +- 26 files changed, 1224 insertions(+), 11 deletions(-) create mode 100644 cdist/conf/type/__file_py/__init__.py create mode 100755 cdist/conf/type/__file_py/explorer/cksum create mode 100755 cdist/conf/type/__file_py/explorer/stat create mode 100755 cdist/conf/type/__file_py/explorer/type create mode 100644 cdist/conf/type/__file_py/parameter/default/state create mode 100644 cdist/conf/type/__file_py/parameter/optional create mode 100644 cdist/core/pytypes.py create mode 100644 cdist/util/python_type_util.py create mode 100644 docs/dev/python-types/benchmark create mode 100755 docs/dev/python-types/benchmark.sh create mode 100644 docs/dev/python-types/conf/manifest/pyinit create mode 100644 docs/dev/python-types/conf/manifest/shinit create mode 100644 docs/dev/python-types/conf/type/__dummy_config_py/__init__.py create mode 100644 docs/dev/python-types/conf/type/__dummy_config_py/files/dummy.conf create mode 100644 docs/dev/python-types/conf/type/__dummy_config_sh/files/dummy.conf create mode 100644 docs/dev/python-types/conf/type/__dummy_config_sh/manifest create mode 100755 docs/dev/python-types/test.sh create mode 100755 docs/dev/python-types/timeit.sh diff --git a/cdist/conf/type/__file_py/__init__.py b/cdist/conf/type/__file_py/__init__.py new file mode 100644 index 00000000..1212f8fd --- /dev/null +++ b/cdist/conf/type/__file_py/__init__.py @@ -0,0 +1,103 @@ +import os +import re +import sys +from cdist.core.pytypes import * + + +class FileType(PythonType): + def get_attribute(self, stat_file, attribute, value_should): + if os.path.exists(stat_file): + if re.match('[0-9]', value_should): + index = 1 + else: + index = 2 + with open(stat_file, 'r') as f: + for line in f: + if re.match(attribute + ":", line): + fields = line.split() + return fields[index] + return None + + def set_attribute(self, attribute, value_should, destination): + cmd = { + 'group': 'chgrp', + 'owner': 'chown', + 'mode': 'chmod', + } + self.send_message("{} '{}'".format(cmd[attribute], value_should)) + return "{} '{}' '{}'".format(cmd[attribute], value_should, destination) + + def type_manifest(self): + yield from () + + def type_gencode(self): + typeis = self.get_explorer('type') + state_should = self.get_parameter('state') + + if state_should == 'exists' and typeis == 'file': + return + + source = self.get_parameter('source') + if source == '-': + source = self.stdin_path + destination = '/' + self.object_id + if state_should == 'pre-exists': + if source is not None: + self.die('--source cannot be used with --state pre-exists') + if typeis == 'file': + return None + else: + self.die('File {} does not exist'.format(destination)) + + create_file = False + upload_file = False + set_attributes = False + code = [] + if state_should == 'present' or state_should == 'exists': + if source is None: + remote_stat = self.get_explorer('stat') + if not remote_stat: + create_file = True + else: + if os.path.exists(source): + if typeis == 'file': + local_cksum = self.run_local(['cksum', source, ]) + local_cksum = local_cksum.split()[0] + remote_cksum = self.get_explorer('cksum') + remote_cksum = remote_cksum.split()[0] + upload_file = local_cksum != remote_cksum + else: + upload_file = True + else: + self.die('Source {} does not exist'.format(source)) + if create_file or upload_file: + set_attributes = True + tempfile_template = '{}.cdist.XXXXXXXXXX'.format(destination) + destination_upload = self.run_remote( + ["mktemp", tempfile_template, ]) + if upload_file: + self.transfer(source, destination_upload) + code.append('rm -rf {}'.format(destination)) + code.append('mv {} {}'.format(destination_upload, destination)) + + if state_should in ('present', 'exists', 'pre-exists', ): + for attribute in ('group', 'owner', 'mode', ): + if attribute in self.parameters: + value_should = self.get_parameter(attribute) + if attribute == 'mode': + value_should = re.sub('^0', '', value_should) + stat_file = self.get_explorer_file('stat') + value_is = self.get_attribute(stat_file, attribute, + value_should) + if set_attributes or value_should != value_is: + code.append(self.set_attribute(attribute, + value_should, + destination)) + elif state_should == 'absent': + if typeis == 'file': + code.append('rm -f {}'.format(destination)) + self.send_message('remove') + else: + self.die('Unknown state {}'.format(state_should)) + + return "\n".join(code) diff --git a/cdist/conf/type/__file_py/explorer/cksum b/cdist/conf/type/__file_py/explorer/cksum new file mode 100755 index 00000000..335e4e7a --- /dev/null +++ b/cdist/conf/type/__file_py/explorer/cksum @@ -0,0 +1,34 @@ +#!/bin/sh +# +# 2011-2012 Nico Schottelius (nico-cdist at schottelius.org) +# +# 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 . +# +# +# Retrieve the md5sum of a file to be created, if it is already existing. +# + +destination="/$__object_id" + +if [ -e "$destination" ]; then + if [ -f "$destination" ]; then + cksum < "$destination" + else + echo "NO REGULAR FILE" + fi +else + echo "NO FILE FOUND, NO CHECKSUM CALCULATED." +fi diff --git a/cdist/conf/type/__file_py/explorer/stat b/cdist/conf/type/__file_py/explorer/stat new file mode 100755 index 00000000..8a917556 --- /dev/null +++ b/cdist/conf/type/__file_py/explorer/stat @@ -0,0 +1,56 @@ +#!/bin/sh +# +# 2013 Steven Armstrong (steven-cdist armstrong.cc) +# +# 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 . +# + +destination="/$__object_id" + +# nothing to work with, nothing we could do +[ -e "$destination" ] || exit 0 + +os=$("$__explorer/os") +case "$os" in + "freebsd"|"netbsd"|"openbsd") + # FIXME: should be something like this based on man page, but can not test + stat -f "type: %ST +owner: %Du %Su +group: %Dg %Sg +mode: %Op %Sp +size: %Dz +links: %Dl +" "$destination" + ;; + "macosx") + stat -f "type: %HT +owner: %Du %Su +group: %Dg %Sg +mode: %Lp %Sp +size: %Dz +links: %Dl +" "$destination" + ;; + *) + stat --printf="type: %F +owner: %u %U +group: %g %G +mode: %a %A +size: %s +links: %h +" "$destination" + ;; +esac diff --git a/cdist/conf/type/__file_py/explorer/type b/cdist/conf/type/__file_py/explorer/type new file mode 100755 index 00000000..e723047c --- /dev/null +++ b/cdist/conf/type/__file_py/explorer/type @@ -0,0 +1,33 @@ +#!/bin/sh +# +# 2013 Steven Armstrong (steven-cdist armstrong.cc) +# +# 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 . +# + +destination="/$__object_id" + +if [ ! -e "$destination" ]; then + echo none +elif [ -h "$destination" ]; then + echo symlink +elif [ -f "$destination" ]; then + echo file +elif [ -d "$destination" ]; then + echo directory +else + echo unknown +fi diff --git a/cdist/conf/type/__file_py/parameter/default/state b/cdist/conf/type/__file_py/parameter/default/state new file mode 100644 index 00000000..e7f6134f --- /dev/null +++ b/cdist/conf/type/__file_py/parameter/default/state @@ -0,0 +1 @@ +present diff --git a/cdist/conf/type/__file_py/parameter/optional b/cdist/conf/type/__file_py/parameter/optional new file mode 100644 index 00000000..c696d592 --- /dev/null +++ b/cdist/conf/type/__file_py/parameter/optional @@ -0,0 +1,5 @@ +state +group +mode +owner +source diff --git a/cdist/config.py b/cdist/config.py index 97cc1da6..74cd1ae2 100644 --- a/cdist/config.py +++ b/cdist/config.py @@ -38,6 +38,7 @@ import cdist.hostsource import cdist.exec.local import cdist.exec.remote import cdist.util.ipaddr as ipaddr +import cdist.util.python_type_util as pytype_util import cdist.configuration from cdist import core, inventory from cdist.util.remoteutil import inspect_ssh_mux_opts @@ -90,13 +91,15 @@ class Config(object): shutil.rmtree(path) def __init__(self, local, remote, dry_run=False, jobs=None, - cleanup_cmds=None, remove_remote_files_dirs=False): + cleanup_cmds=None, remove_remote_files_dirs=False, + timestamp=False): self.local = local self.remote = remote self._open_logger() self.dry_run = dry_run self.jobs = jobs + self.timestamp = timestamp if cleanup_cmds: self.cleanup_cmds = cleanup_cmds else: @@ -428,7 +431,8 @@ class Config(object): cleanup_cmds.append(cleanup_cmd) c = cls(local, remote, dry_run=args.dry_run, jobs=args.jobs, cleanup_cmds=cleanup_cmds, - remove_remote_files_dirs=remove_remote_files_dirs) + remove_remote_files_dirs=remove_remote_files_dirs, + timestamp=args.timestamp) c.run() cls._remove_paths() @@ -466,6 +470,7 @@ class Config(object): 'dry' if self.dry_run else 'configuration')) self._init_files_dirs() + self.local.collect_python_types() self.explorer.run_global_explorers(self.local.global_explorer_out_path) try: @@ -779,15 +784,41 @@ class Config(object): args = [param, cdist_type.name] self.log.warning(format, *args) + def _timeit(self, func, msg_prefix): + def wrapper_func(*args, **kwargs): + loglevel = self.log.getEffectiveLevel() + if loglevel >= logging.VERBOSE and self.timestamp: + start_time = time.time() + rv = func(*args, **kwargs) + end_time = time.time() + duration = end_time - start_time + self.log.verbose("%s duration: %.6f seconds", + msg_prefix, duration) + else: + rv = func(*args, **kwargs) + return rv + return wrapper_func + def object_prepare(self, cdist_object, transfer_type_explorers=True): """Prepare object: Run type explorer + manifest""" self._handle_deprecation(cdist_object) self.log.verbose("Preparing object {}".format(cdist_object.name)) self.log.verbose( "Running manifest and explorers for " + cdist_object.name) - self.explorer.run_type_explorers(cdist_object, transfer_type_explorers) try: - self.manifest.run_type_manifest(cdist_object) + self.log.verbose("Preparing object {}".format(cdist_object.name)) + self.log.verbose( + "Running manifest and explorers for " + cdist_object.name) + self.explorer.run_type_explorers(cdist_object, + transfer_type_explorers) + if pytype_util.is_python_type(cdist_object.cdist_type): + self._timeit(self.manifest.run_py_type_manifest, + "Python type manifest for {}".format( + cdist_object.name))(cdist_object) + else: + self._timeit(self.manifest.run_type_manifest, + "Type manifest for {}".format( + cdist_object.name))(cdist_object) self.log.trace("[ORDER_DEP] Removing order dep files for %s", cdist_object) cdist_object.cleanup() @@ -805,9 +836,21 @@ class Config(object): # Generate self.log.debug("Generating code for %s" % (cdist_object.name)) - cdist_object.code_local = self.code.run_gencode_local(cdist_object) - cdist_object.code_remote = self.code.run_gencode_remote( - cdist_object) + if pytype_util.is_python_type(cdist_object.cdist_type): + cdist_object.code_local = '' + cdist_object.code_remote = self._timeit( + self.code.run_py, + "Python type generate code for {}".format( + cdist_object.name))(cdist_object) + else: + cdist_object.code_local = self._timeit( + self.code.run_gencode_local, + "Type generate code local for {}".format( + cdist_object.name))(cdist_object) + cdist_object.code_remote = self._timeit( + self.code.run_gencode_remote, + "Type generate code remote for {}".format( + cdist_object.name))(cdist_object) if cdist_object.code_local or cdist_object.code_remote: cdist_object.changed = True @@ -818,12 +861,16 @@ class Config(object): if cdist_object.code_local: self.log.trace("Executing local code for %s" % (cdist_object.name)) - self.code.run_code_local(cdist_object) + self._timeit(self.code.run_code_local, + "Type run code local for {}".format( + cdist_object.name))(cdist_object) 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.run_code_remote(cdist_object) + self._timeit(self.code.run_code_remote, + "Type run code remote for {}".format( + cdist_object.name))(cdist_object) # Mark this object as done self.log.trace("Finishing run of " + cdist_object.name) diff --git a/cdist/core/__init__.py b/cdist/core/__init__.py index b79cdb21..de2fbbbf 100644 --- a/cdist/core/__init__.py +++ b/cdist/core/__init__.py @@ -29,3 +29,4 @@ from cdist.core.manifest import Manifest from cdist.core.code import Code from cdist.core.util import listdir from cdist.core.util import log_level_env_var_val, log_level_name_env_var_val +import cdist.core.pytypes diff --git a/cdist/core/code.py b/cdist/core/code.py index 1550880a..34d5bd97 100644 --- a/cdist/core/code.py +++ b/cdist/core/code.py @@ -22,6 +22,10 @@ # import os +import importlib.util +import inspect +import cdist +from cdist.core.pytypes import PythonType from . import util @@ -116,6 +120,44 @@ class Code(object): if dry_run: self.env['__cdist_dry_run'] = '1' + def run_py(self, cdist_object): + cdist_type = cdist_object.cdist_type + module_name = cdist_type.name + file_path = os.path.join(cdist_type.absolute_path, '__init__.py') + + if os.path.isfile(file_path): + spec = importlib.util.spec_from_file_location(module_name, + file_path) + m = importlib.util.module_from_spec(spec) + spec.loader.exec_module(m) + classes = inspect.getmembers(m, inspect.isclass) + type_class = None + for _, cl in classes: + if cl != PythonType and issubclass(cl, PythonType): + if type_class: + raise cdist.Error("Only one python type class is " + "supported, but at least two " + "found: {}".format((type_class, + cl, ))) + else: + type_class = cl + env = os.environ.copy() + env.update(self.env) + message_prefix = cdist_object.name + type_obj = type_class(env=env, cdist_object=cdist_object, + local=self.local, remote=self.remote, + message_prefix=message_prefix) + if hasattr(type_obj, 'run') and inspect.ismethod(type_obj.run): + if self.local.save_output_streams: + which = 'gencode-py' + stderr_path = os.path.join(cdist_object.stderr_path, which) + stdout_path = os.path.join(cdist_object.stdout_path, which) + with open(stderr_path, 'a+') as stderr, \ + open(stdout_path, 'a+') as stdout: + return type_obj.run(stdout=stdout, stderr=stderr) + else: + return type_obj.run() + def _run_gencode(self, cdist_object, which): cdist_type = cdist_object.cdist_type script = os.path.join(self.local.type_path, diff --git a/cdist/core/manifest.py b/cdist/core/manifest.py index 8aeaf860..546fba25 100644 --- a/cdist/core/manifest.py +++ b/cdist/core/manifest.py @@ -22,9 +22,13 @@ import logging import os - +import importlib.util +import inspect import cdist +import cdist.emulator from . import util +from cdist.core.pytypes import PythonType, Command + ''' common: @@ -226,3 +230,72 @@ class Manifest(object): pass _rm_file(Manifest.ORDER_DEP_STATE_NAME) _rm_file(Manifest.TYPEORDER_DEP_NAME) + + def env_py_type_manifest(self, cdist_object): + env = os.environ.copy() + env.update(self.env) + env.update({ + '__cdist_object_marker': self.local.object_marker_name, + '__cdist_manifest': cdist_object.cdist_type, + '__manifest': self.local.manifest_path, + '__object': cdist_object.absolute_path, + '__object_id': cdist_object.object_id, + '__object_name': cdist_object.name, + '__type': cdist_object.cdist_type.absolute_path, + }) + + return env + + def run_py_type_manifest(self, cdist_object): + cdist_type = cdist_object.cdist_type + module_name = cdist_type.name + file_path = os.path.join(cdist_type.absolute_path, '__init__.py') + message_prefix = cdist_object.name + if os.path.isfile(file_path): + self.log.verbose("Running python type manifest for object %s", + cdist_object.name) + spec = importlib.util.spec_from_file_location(module_name, + file_path) + m = importlib.util.module_from_spec(spec) + spec.loader.exec_module(m) + classes = inspect.getmembers(m, inspect.isclass) + type_class = None + for _, cl in classes: + if cl != PythonType and issubclass(cl, PythonType): + if type_class: + raise cdist.Error("Only one python type class is " + "supported, but at least two " + "found: {}".format((type_class, + cl, ))) + else: + type_class = cl + env = self.env_py_type_manifest(cdist_object) + type_obj = type_class(env=env, cdist_object=cdist_object, + local=self.local, remote=None, + message_prefix=message_prefix) + if self.local.save_output_streams: + which = 'manifest' + stderr_path = os.path.join(cdist_object.stderr_path, which) + stdout_path = os.path.join(cdist_object.stdout_path, which) + with open(stderr_path, 'a+') as stderr, \ + open(stdout_path, 'a+') as stdout: + self._process_py_type_manifest_entries( + type_obj, env, stdout=stdout, stderr=stderr) + else: + self._process_py_type_manifest_entries(type_obj, env) + + def _process_py_type_manifest_entries(self, type_obj, env, stdout=None, + stderr=None): + if hasattr(type_obj, 'manifest') and \ + inspect.ismethod(type_obj.manifest): + for cmd in type_obj.manifest(stdout=stdout, stderr=stderr): + if not isinstance(cmd, Command): + raise TypeError("Manifest command must be of type Command") + kwargs = { + 'argv': cmd.cmd_line(), + 'env': env, + } + if cmd.stdin: + kwargs['stdin'] = cmd.stdin + emulator = cdist.emulator.Emulator(**kwargs) + emulator.run() diff --git a/cdist/core/pytypes.py b/cdist/core/pytypes.py new file mode 100644 index 00000000..ae4164ec --- /dev/null +++ b/cdist/core/pytypes.py @@ -0,0 +1,162 @@ +import logging +import os +import io +import sys +import re +from cdist import message, Error + + +__all__ = ["PythonType", "Command", "command"] + + +class PythonType: + def __init__(self, env, cdist_object, local, remote, message_prefix=None): + self.env = env + self.cdist_object = cdist_object + self.object_id = cdist_object.object_id + self.object_name = cdist_object.name + self.cdist_type = cdist_object.cdist_type + self.local = local + self.remote = remote + self.object_path = cdist_object.absolute_path + self.type_path = cdist_object.cdist_type.absolute_path + self.explorer_path = os.path.join(self.object_path, 'explorer') + self.parameters = cdist_object.parameters + self.stdin_path = os.path.join(self.object_path, 'stdin') + self.log = logging.getLogger( + self.local.target_host[0] + ':' + self.object_name) + + self.message_prefix = message_prefix + self.message = None + + def get_parameter(self, name): + return self.parameters.get(name) + + def get_explorer_file(self, name): + path = os.path.join(self.explorer_path, name) + return path + + def get_explorer(self, name): + path = self.get_explorer_file(name) + with open(path, 'r') as f: + value = f.read() + if value: + value = value.strip() + return value + + def run_local(self, command, env=None): + rv = self.local.run(command, env=env, return_output=True) + if rv: + rv = rv.rstrip('\n') + return rv + + def run_remote(self, command, env=None): + rv = self.remote.run(command, env=env, return_output=True) + if rv: + rv = rv.rstrip('\n') + return rv + + def transfer(self, source, destination): + self.remote.transfer(source, destination) + + def die(self, msg): + raise Error("{}: {}".format(self.cdist_object, msg)) + + def type_manifest(self): + pass + + def type_gencode(self): + pass + + def manifest(self, stdout=None, stderr=None): + try: + if self.message_prefix: + self.message = message.Message(self.message_prefix, + self.local.messages_path) + self.env.update(self.message.env) + if stdout is not None: + stdout_save = sys.stdout + sys.stdout = stdout + if stderr is not None: + stderr_save = sys.stderr + sys.stderr = stderr + yield from self.type_manifest() + finally: + if self.message: + self.message.merge_messages() + if stdout is not None: + sys.stdout = stdout_save + if stderr is not None: + sys.stderr = stderr_save + + def run(self, stdout=None, stderr=None): + try: + if self.message_prefix: + self.message = message.Message(self.message_prefix, + self.local.messages_path) + if stdout is not None: + stdout_save = sys.stdout + sys.stdout = stdout + if stderr is not None: + stderr_save = sys.stderr + sys.stderr = stderr + return self.type_gencode() + finally: + if self.message: + self.message.merge_messages() + if stdout is not None: + sys.stdout = stdout_save + if stderr is not None: + sys.stderr = stderr_save + + def send_message(self, msg): + if self.message: + with open(self.message.messages_out, 'a') as f: + print(msg, file=f) + + def receive_message(self, pattern): + if self.message: + with open(self.message.messages_in, 'r') as f: + for line in f: + match = re.search(pattern, line) + if match: + return match + return None + + +class Command: + def __init__(self, name, *args, **kwargs): + self.name = name + self.args = args + self.kwargs = kwargs + self.stdin = None + + def feed_stdin(self, value): + # If file-like object then read its value. + if value is not None and isinstance(value, io.IOBase): + value = value.read() + + # Convert to bytes file-like object. + if value is None: + self.stdin = None + elif isinstance(value, str): + self.stdin = io.BytesIO(value.encode('utf-8')) + elif isinstance(value, bytes) or isinstance(value, bytearray): + self.stdin = io.BytesIO(value) + else: + raise TypeError("value must be str, bytes, bytearray, file-like " + "object or None") + return self + + def cmd_line(self): + argv = [self.name, ] + for param in self.args: + argv.append(param) + for key, value in self.kwargs.items(): + argv.append("--{}".format(key)) + argv.append(value) + return argv + + +def command(name, *args, **kwargs): + return Command(name, *args, **kwargs) diff --git a/cdist/exec/local.py b/cdist/exec/local.py index ad6c6e36..c1877c90 100644 --- a/cdist/exec/local.py +++ b/cdist/exec/local.py @@ -35,6 +35,9 @@ import cdist import cdist.message from cdist import core import cdist.exec.util as util +import cdist.util.python_type_util as pytype_util +from cdist.core import pytypes +import functools CONF_SUBDIRS_LINKED = ["explorer", "files", "manifest", "type", ] @@ -370,3 +373,16 @@ class Local(object): raise cdist.Error( "Linking emulator from %s to %s failed: %s" % ( src, dst, e.__str__())) + + def collect_python_types(self): + for cdist_type in core.CdistType.list_types(self.type_path): + if pytype_util.is_python_type(cdist_type): + self.log.trace("Detected python type %s, collecting it".format( + cdist_type.name)) + f = functools.partial(pytypes.command, cdist_type.name) + if cdist_type.name.startswith('__'): + attr_name = cdist_type.name.replace('__', '', 1) + else: + attr_name = cdist_type.name + setattr(pytypes, attr_name, f) + pytypes.__all__.append(attr_name) diff --git a/cdist/util/python_type_util.py b/cdist/util/python_type_util.py new file mode 100644 index 00000000..fd97684e --- /dev/null +++ b/cdist/util/python_type_util.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +# +# 2019 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 . +# +# + +import os + + +def is_python_type(cdist_type): + init_path = os.path.join(cdist_type.absolute_path, '__init__.py') + return os.path.exists(init_path) diff --git a/docs/changelog b/docs/changelog index 25566aa8..9d3b94a1 100644 --- a/docs/changelog +++ b/docs/changelog @@ -3,6 +3,7 @@ Changelog next: * Core: Add trigger functionality (Nico Schottelius, Darko Poljak) + * Core: Implement core support for python types (Darko Poljak) 6.5.1: 2020-02-15 * Type __consul_agent: Add Debian 10 support (Nico Schottelius) diff --git a/docs/dev/python-types/benchmark b/docs/dev/python-types/benchmark new file mode 100644 index 00000000..8fcf69f3 --- /dev/null +++ b/docs/dev/python-types/benchmark @@ -0,0 +1,376 @@ +# sh type, no file at remote +echo 'x=0; while [ $x -lt 50 ]; do head -c 102400 /dev/random | __file /root/foo${x}.bin --source - --mode 0640 --owner root --group root; x=$((x + 1)); done' | ./bin/cdist config -v -P -i - 185.203.112.26 + +INFO: [20181121125154.045799] 185.203.112.26: Starting configuration run +INFO: [20181121125237.029892] 185.203.112.26: Processing __file/root/foo0.bin +INFO: [20181121125239.881529] 185.203.112.26: Processing __file/root/foo1.bin +INFO: [20181121125243.265672] 185.203.112.26: Processing __file/root/foo2.bin +INFO: [20181121125246.929903] 185.203.112.26: Processing __file/root/foo3.bin +INFO: [20181121125251.811258] 185.203.112.26: Processing __file/root/foo4.bin +INFO: [20181121125257.784416] 185.203.112.26: Processing __file/root/foo5.bin +INFO: [20181121125302.686275] 185.203.112.26: Processing __file/root/foo6.bin +INFO: [20181121125306.394030] 185.203.112.26: Processing __file/root/foo7.bin +INFO: [20181121125308.610023] 185.203.112.26: Processing __file/root/foo8.bin +INFO: [20181121125310.868538] 185.203.112.26: Processing __file/root/foo9.bin +INFO: [20181121125313.017972] 185.203.112.26: Processing __file/root/foo10.bin +INFO: [20181121125315.201342] 185.203.112.26: Processing __file/root/foo11.bin +INFO: [20181121125317.333055] 185.203.112.26: Processing __file/root/foo12.bin +INFO: [20181121125319.463929] 185.203.112.26: Processing __file/root/foo13.bin +INFO: [20181121125321.595410] 185.203.112.26: Processing __file/root/foo14.bin +INFO: [20181121125323.689697] 185.203.112.26: Processing __file/root/foo15.bin +INFO: [20181121125325.768283] 185.203.112.26: Processing __file/root/foo16.bin +INFO: [20181121125327.814793] 185.203.112.26: Processing __file/root/foo17.bin +INFO: [20181121125329.873073] 185.203.112.26: Processing __file/root/foo18.bin +INFO: [20181121125331.953886] 185.203.112.26: Processing __file/root/foo19.bin +INFO: [20181121125334.118290] 185.203.112.26: Processing __file/root/foo20.bin +INFO: [20181121125336.390849] 185.203.112.26: Processing __file/root/foo21.bin +INFO: [20181121125338.576698] 185.203.112.26: Processing __file/root/foo22.bin +INFO: [20181121125340.819044] 185.203.112.26: Processing __file/root/foo23.bin +INFO: [20181121125343.680419] 185.203.112.26: Processing __file/root/foo24.bin +INFO: [20181121125346.044907] 185.203.112.26: Processing __file/root/foo25.bin +INFO: [20181121125348.179574] 185.203.112.26: Processing __file/root/foo26.bin +INFO: [20181121125350.314970] 185.203.112.26: Processing __file/root/foo27.bin +INFO: [20181121125352.447394] 185.203.112.26: Processing __file/root/foo28.bin +INFO: [20181121125354.586637] 185.203.112.26: Processing __file/root/foo29.bin +INFO: [20181121125356.722699] 185.203.112.26: Processing __file/root/foo30.bin +INFO: [20181121125358.883538] 185.203.112.26: Processing __file/root/foo31.bin +INFO: [20181121125401.020967] 185.203.112.26: Processing __file/root/foo32.bin +INFO: [20181121125403.160146] 185.203.112.26: Processing __file/root/foo33.bin +INFO: [20181121125405.289048] 185.203.112.26: Processing __file/root/foo34.bin +INFO: [20181121125407.423994] 185.203.112.26: Processing __file/root/foo35.bin +INFO: [20181121125409.530135] 185.203.112.26: Processing __file/root/foo36.bin +INFO: [20181121125411.659683] 185.203.112.26: Processing __file/root/foo37.bin +INFO: [20181121125413.786177] 185.203.112.26: Processing __file/root/foo38.bin +INFO: [20181121125415.919152] 185.203.112.26: Processing __file/root/foo39.bin +INFO: [20181121125418.051496] 185.203.112.26: Processing __file/root/foo40.bin +INFO: [20181121125420.204577] 185.203.112.26: Processing __file/root/foo41.bin +INFO: [20181121125422.339697] 185.203.112.26: Processing __file/root/foo42.bin +INFO: [20181121125424.450966] 185.203.112.26: Processing __file/root/foo43.bin +INFO: [20181121125426.487831] 185.203.112.26: Processing __file/root/foo44.bin +INFO: [20181121125428.585516] 185.203.112.26: Processing __file/root/foo45.bin +INFO: [20181121125430.749002] 185.203.112.26: Processing __file/root/foo46.bin +INFO: [20181121125432.865290] 185.203.112.26: Processing __file/root/foo47.bin +INFO: [20181121125435.004009] 185.203.112.26: Processing __file/root/foo48.bin +INFO: [20181121125437.228566] 185.203.112.26: Processing __file/root/foo49.bin +INFO: [20181121125439.429440] 185.203.112.26: Finished successful run in 165.38 seconds + +# sh type, files exist at remote but content changes +echo 'x=0; while [ $x -lt 50 ]; do head -c 102400 /dev/random | __file /root/foo${x}.bin --source - --mode 0640 --owner root --group root; x=$((x + 1)); done' | ./bin/cdist config -v -P -i - 185.203.112.26 + +INFO: [20181121125529.952800] 185.203.112.26: Starting configuration run +INFO: [20181121125541.175180] 185.203.112.26: Processing __file/root/foo0.bin +INFO: [20181121125543.219561] 185.203.112.26: Processing __file/root/foo1.bin +INFO: [20181121125545.116073] 185.203.112.26: Processing __file/root/foo2.bin +INFO: [20181121125547.011359] 185.203.112.26: Processing __file/root/foo3.bin +INFO: [20181121125548.916288] 185.203.112.26: Processing __file/root/foo4.bin +INFO: [20181121125550.821351] 185.203.112.26: Processing __file/root/foo5.bin +INFO: [20181121125552.723887] 185.203.112.26: Processing __file/root/foo6.bin +INFO: [20181121125554.635662] 185.203.112.26: Processing __file/root/foo7.bin +INFO: [20181121125556.568639] 185.203.112.26: Processing __file/root/foo8.bin +INFO: [20181121125558.508852] 185.203.112.26: Processing __file/root/foo9.bin +INFO: [20181121125600.464475] 185.203.112.26: Processing __file/root/foo10.bin +INFO: [20181121125602.429261] 185.203.112.26: Processing __file/root/foo11.bin +INFO: [20181121125604.428942] 185.203.112.26: Processing __file/root/foo12.bin +INFO: [20181121125606.442193] 185.203.112.26: Processing __file/root/foo13.bin +INFO: [20181121125608.474473] 185.203.112.26: Processing __file/root/foo14.bin +INFO: [20181121125610.535252] 185.203.112.26: Processing __file/root/foo15.bin +INFO: [20181121125612.609560] 185.203.112.26: Processing __file/root/foo16.bin +INFO: [20181121125614.708507] 185.203.112.26: Processing __file/root/foo17.bin +INFO: [20181121125616.824721] 185.203.112.26: Processing __file/root/foo18.bin +INFO: [20181121125618.924521] 185.203.112.26: Processing __file/root/foo19.bin +INFO: [20181121125621.007543] 185.203.112.26: Processing __file/root/foo20.bin +INFO: [20181121125623.133204] 185.203.112.26: Processing __file/root/foo21.bin +INFO: [20181121125625.333471] 185.203.112.26: Processing __file/root/foo22.bin +INFO: [20181121125627.396334] 185.203.112.26: Processing __file/root/foo23.bin +INFO: [20181121125629.526492] 185.203.112.26: Processing __file/root/foo24.bin +INFO: [20181121125631.628454] 185.203.112.26: Processing __file/root/foo25.bin +INFO: [20181121125633.743142] 185.203.112.26: Processing __file/root/foo26.bin +INFO: [20181121125635.952547] 185.203.112.26: Processing __file/root/foo27.bin +INFO: [20181121125637.986746] 185.203.112.26: Processing __file/root/foo28.bin +INFO: [20181121125640.020415] 185.203.112.26: Processing __file/root/foo29.bin +INFO: [20181121125642.081373] 185.203.112.26: Processing __file/root/foo30.bin +INFO: [20181121125644.174744] 185.203.112.26: Processing __file/root/foo31.bin +INFO: [20181121125646.286532] 185.203.112.26: Processing __file/root/foo32.bin +INFO: [20181121125648.396447] 185.203.112.26: Processing __file/root/foo33.bin +INFO: [20181121125650.460107] 185.203.112.26: Processing __file/root/foo34.bin +INFO: [20181121125652.557125] 185.203.112.26: Processing __file/root/foo35.bin +INFO: [20181121125654.667456] 185.203.112.26: Processing __file/root/foo36.bin +INFO: [20181121125656.746960] 185.203.112.26: Processing __file/root/foo37.bin +INFO: [20181121125658.854229] 185.203.112.26: Processing __file/root/foo38.bin +INFO: [20181121125700.968145] 185.203.112.26: Processing __file/root/foo39.bin +INFO: [20181121125703.109376] 185.203.112.26: Processing __file/root/foo40.bin +INFO: [20181121125705.318163] 185.203.112.26: Processing __file/root/foo41.bin +INFO: [20181121125707.440575] 185.203.112.26: Processing __file/root/foo42.bin +INFO: [20181121125709.551261] 185.203.112.26: Processing __file/root/foo43.bin +INFO: [20181121125711.657753] 185.203.112.26: Processing __file/root/foo44.bin +INFO: [20181121125713.774819] 185.203.112.26: Processing __file/root/foo45.bin +INFO: [20181121125715.887428] 185.203.112.26: Processing __file/root/foo46.bin +INFO: [20181121125717.995104] 185.203.112.26: Processing __file/root/foo47.bin +INFO: [20181121125720.110196] 185.203.112.26: Processing __file/root/foo48.bin +INFO: [20181121125722.232932] 185.203.112.26: Processing __file/root/foo49.bin +INFO: [20181121125724.451523] 185.203.112.26: Finished successful run in 114.50 seconds + +# py type, no file at remote +echo 'x=0; while [ $x -lt 50 ]; do head -c 102400 /dev/random | __file_py /root/foo${x}.bin --source - --mode 0640 --owner root --group root; x=$((x + 1)); done' | ./bin/cdist config -v -P -i - 185.203.112.26 + +INFO: [20181121125812.034197] 185.203.112.26: Starting configuration run +INFO: [20181121125823.927353] 185.203.112.26: Processing __file_py/root/foo0.bin +INFO: [20181121125825.715361] 185.203.112.26: Processing __file_py/root/foo1.bin +INFO: [20181121125827.511296] 185.203.112.26: Processing __file_py/root/foo2.bin +INFO: [20181121125829.293455] 185.203.112.26: Processing __file_py/root/foo3.bin +INFO: [20181121125831.086696] 185.203.112.26: Processing __file_py/root/foo4.bin +INFO: [20181121125832.867582] 185.203.112.26: Processing __file_py/root/foo5.bin +INFO: [20181121125834.652511] 185.203.112.26: Processing __file_py/root/foo6.bin +INFO: [20181121125836.450393] 185.203.112.26: Processing __file_py/root/foo7.bin +INFO: [20181121125838.255152] 185.203.112.26: Processing __file_py/root/foo8.bin +INFO: [20181121125840.065808] 185.203.112.26: Processing __file_py/root/foo9.bin +INFO: [20181121125841.889049] 185.203.112.26: Processing __file_py/root/foo10.bin +INFO: [20181121125843.719280] 185.203.112.26: Processing __file_py/root/foo11.bin +INFO: [20181121125845.560165] 185.203.112.26: Processing __file_py/root/foo12.bin +INFO: [20181121125847.416138] 185.203.112.26: Processing __file_py/root/foo13.bin +INFO: [20181121125849.289851] 185.203.112.26: Processing __file_py/root/foo14.bin +INFO: [20181121125851.180203] 185.203.112.26: Processing __file_py/root/foo15.bin +INFO: [20181121125853.074978] 185.203.112.26: Processing __file_py/root/foo16.bin +INFO: [20181121125855.086107] 185.203.112.26: Processing __file_py/root/foo17.bin +INFO: [20181121125857.041100] 185.203.112.26: Processing __file_py/root/foo18.bin +INFO: [20181121125859.025581] 185.203.112.26: Processing __file_py/root/foo19.bin +INFO: [20181121125901.072067] 185.203.112.26: Processing __file_py/root/foo20.bin +INFO: [20181121125903.026711] 185.203.112.26: Processing __file_py/root/foo21.bin +INFO: [20181121125904.994824] 185.203.112.26: Processing __file_py/root/foo22.bin +INFO: [20181121125906.956296] 185.203.112.26: Processing __file_py/root/foo23.bin +INFO: [20181121125908.929231] 185.203.112.26: Processing __file_py/root/foo24.bin +INFO: [20181121125910.882672] 185.203.112.26: Processing __file_py/root/foo25.bin +INFO: [20181121125912.839834] 185.203.112.26: Processing __file_py/root/foo26.bin +INFO: [20181121125914.789904] 185.203.112.26: Processing __file_py/root/foo27.bin +INFO: [20181121125916.743930] 185.203.112.26: Processing __file_py/root/foo28.bin +INFO: [20181121125918.698258] 185.203.112.26: Processing __file_py/root/foo29.bin +INFO: [20181121125920.657118] 185.203.112.26: Processing __file_py/root/foo30.bin +INFO: [20181121125922.618898] 185.203.112.26: Processing __file_py/root/foo31.bin +INFO: [20181121125924.567847] 185.203.112.26: Processing __file_py/root/foo32.bin +INFO: [20181121125926.524617] 185.203.112.26: Processing __file_py/root/foo33.bin +INFO: [20181121125928.396400] 185.203.112.26: Processing __file_py/root/foo34.bin +INFO: [20181121125930.209237] 185.203.112.26: Processing __file_py/root/foo35.bin +INFO: [20181121125931.998377] 185.203.112.26: Processing __file_py/root/foo36.bin +INFO: [20181121125933.786883] 185.203.112.26: Processing __file_py/root/foo37.bin +INFO: [20181121125935.579348] 185.203.112.26: Processing __file_py/root/foo38.bin +INFO: [20181121125937.366197] 185.203.112.26: Processing __file_py/root/foo39.bin +INFO: [20181121125939.155643] 185.203.112.26: Processing __file_py/root/foo40.bin +INFO: [20181121125941.052837] 185.203.112.26: Processing __file_py/root/foo41.bin +INFO: [20181121125942.953670] 185.203.112.26: Processing __file_py/root/foo42.bin +INFO: [20181121125944.781567] 185.203.112.26: Processing __file_py/root/foo43.bin +INFO: [20181121125946.622485] 185.203.112.26: Processing __file_py/root/foo44.bin +INFO: [20181121125948.470701] 185.203.112.26: Processing __file_py/root/foo45.bin +INFO: [20181121125950.356949] 185.203.112.26: Processing __file_py/root/foo46.bin +INFO: [20181121125952.232014] 185.203.112.26: Processing __file_py/root/foo47.bin +INFO: [20181121125954.128887] 185.203.112.26: Processing __file_py/root/foo48.bin +INFO: [20181121125956.037541] 185.203.112.26: Processing __file_py/root/foo49.bin +INFO: [20181121125957.514738] 185.203.112.26: Finished successful run in 105.48 seconds + +# py type, files exist at remote but content changes +echo 'x=0; while [ $x -lt 50 ]; do head -c 102400 /dev/random | __file_py /root/foo${x}.bin --source - --mode 0640 --owner root --group root; x=$((x + 1)); done' | ./bin/cdist config -v -P -i - 185.203.112.26 + +INFO: [20181121130056.484643] 185.203.112.26: Starting configuration run +INFO: [20181121130108.545059] 185.203.112.26: Processing __file_py/root/foo0.bin +INFO: [20181121130110.339217] 185.203.112.26: Processing __file_py/root/foo1.bin +INFO: [20181121130112.136448] 185.203.112.26: Processing __file_py/root/foo2.bin +INFO: [20181121130113.923820] 185.203.112.26: Processing __file_py/root/foo3.bin +INFO: [20181121130115.715667] 185.203.112.26: Processing __file_py/root/foo4.bin +INFO: [20181121130117.508696] 185.203.112.26: Processing __file_py/root/foo5.bin +INFO: [20181121130119.300839] 185.203.112.26: Processing __file_py/root/foo6.bin +INFO: [20181121130124.296312] 185.203.112.26: Processing __file_py/root/foo7.bin +INFO: [20181121130131.109195] 185.203.112.26: Processing __file_py/root/foo8.bin +INFO: [20181121130133.303817] 185.203.112.26: Processing __file_py/root/foo9.bin +INFO: [20181121130136.396440] 185.203.112.26: Processing __file_py/root/foo10.bin +INFO: [20181121130138.443128] 185.203.112.26: Processing __file_py/root/foo11.bin +INFO: [20181121130140.462868] 185.203.112.26: Processing __file_py/root/foo12.bin +INFO: [20181121130142.476196] 185.203.112.26: Processing __file_py/root/foo13.bin +INFO: [20181121130145.937900] 185.203.112.26: Processing __file_py/root/foo14.bin +INFO: [20181121130148.013672] 185.203.112.26: Processing __file_py/root/foo15.bin +INFO: [20181121130150.042588] 185.203.112.26: Processing __file_py/root/foo16.bin +INFO: [20181121130152.050793] 185.203.112.26: Processing __file_py/root/foo17.bin +INFO: [20181121130154.083089] 185.203.112.26: Processing __file_py/root/foo18.bin +INFO: [20181121130156.100091] 185.203.112.26: Processing __file_py/root/foo19.bin +INFO: [20181121130158.103005] 185.203.112.26: Processing __file_py/root/foo20.bin +INFO: [20181121130200.188390] 185.203.112.26: Processing __file_py/root/foo21.bin +INFO: [20181121130202.197574] 185.203.112.26: Processing __file_py/root/foo22.bin +INFO: [20181121130205.269102] 185.203.112.26: Processing __file_py/root/foo23.bin +INFO: [20181121130208.457011] 185.203.112.26: Processing __file_py/root/foo24.bin +INFO: [20181121130211.574321] 185.203.112.26: Processing __file_py/root/foo25.bin +INFO: [20181121130213.719894] 185.203.112.26: Processing __file_py/root/foo26.bin +INFO: [20181121130215.762977] 185.203.112.26: Processing __file_py/root/foo27.bin +INFO: [20181121130217.778624] 185.203.112.26: Processing __file_py/root/foo28.bin +INFO: [20181121130219.840477] 185.203.112.26: Processing __file_py/root/foo29.bin +INFO: [20181121130221.852389] 185.203.112.26: Processing __file_py/root/foo30.bin +INFO: [20181121130223.850898] 185.203.112.26: Processing __file_py/root/foo31.bin +INFO: [20181121130225.858812] 185.203.112.26: Processing __file_py/root/foo32.bin +INFO: [20181121130227.855295] 185.203.112.26: Processing __file_py/root/foo33.bin +INFO: [20181121130229.952673] 185.203.112.26: Processing __file_py/root/foo34.bin +INFO: [20181121130231.956904] 185.203.112.26: Processing __file_py/root/foo35.bin +INFO: [20181121130233.961954] 185.203.112.26: Processing __file_py/root/foo36.bin +INFO: [20181121130236.012158] 185.203.112.26: Processing __file_py/root/foo37.bin +INFO: [20181121130238.024422] 185.203.112.26: Processing __file_py/root/foo38.bin +INFO: [20181121130241.238800] 185.203.112.26: Processing __file_py/root/foo39.bin +INFO: [20181121130243.463237] 185.203.112.26: Processing __file_py/root/foo40.bin +INFO: [20181121130245.610314] 185.203.112.26: Processing __file_py/root/foo41.bin +INFO: [20181121130247.661385] 185.203.112.26: Processing __file_py/root/foo42.bin +INFO: [20181121130250.399845] 185.203.112.26: Processing __file_py/root/foo43.bin +INFO: [20181121130252.832133] 185.203.112.26: Processing __file_py/root/foo44.bin +INFO: [20181121130254.955658] 185.203.112.26: Processing __file_py/root/foo45.bin +INFO: [20181121130257.039587] 185.203.112.26: Processing __file_py/root/foo46.bin +INFO: [20181121130259.178847] 185.203.112.26: Processing __file_py/root/foo47.bin +INFO: [20181121130301.357922] 185.203.112.26: Processing __file_py/root/foo48.bin +INFO: [20181121130303.356299] 185.203.112.26: Processing __file_py/root/foo49.bin +INFO: [20181121130305.144393] 185.203.112.26: Finished successful run in 128.66 seconds + + + +# init test file content +head -c 102400 /dev/random > /tmp/test.file + +# sh type, no file at remote +echo 'x=0; while [ $x -lt 50 ]; do __file /root/foo${x}.bin --source /tmp/test.file --mode 0640 --owner root --group root; x=$((x + 1)); done' | ./bin/cdist config -v -P -i - 185.203.112.26 + +INFO: [20181121130612.519698] 185.203.112.26: Starting configuration run +INFO: [20181121130624.219344] 185.203.112.26: Processing __file/root/foo0.bin +INFO: [20181121130626.980052] 185.203.112.26: Processing __file/root/foo1.bin +INFO: [20181121130631.200669] 185.203.112.26: Processing __file/root/foo2.bin +INFO: [20181121130642.790229] 185.203.112.26: Processing __file/root/foo3.bin +INFO: [20181121130646.565599] 185.203.112.26: Processing __file/root/foo4.bin +INFO: [20181121130648.724875] 185.203.112.26: Processing __file/root/foo5.bin +INFO: [20181121130651.464686] 185.203.112.26: Processing __file/root/foo6.bin +INFO: [20181121130653.639581] 185.203.112.26: Processing __file/root/foo7.bin +INFO: [20181121130655.773987] 185.203.112.26: Processing __file/root/foo8.bin +INFO: [20181121130657.933136] 185.203.112.26: Processing __file/root/foo9.bin +INFO: [20181121130700.065158] 185.203.112.26: Processing __file/root/foo10.bin +INFO: [20181121130702.216456] 185.203.112.26: Processing __file/root/foo11.bin +INFO: [20181121130704.429030] 185.203.112.26: Processing __file/root/foo12.bin +INFO: [20181121130706.562114] 185.203.112.26: Processing __file/root/foo13.bin +INFO: [20181121130708.696584] 185.203.112.26: Processing __file/root/foo14.bin +INFO: [20181121130710.830002] 185.203.112.26: Processing __file/root/foo15.bin +INFO: [20181121130712.966631] 185.203.112.26: Processing __file/root/foo16.bin +INFO: [20181121130715.151833] 185.203.112.26: Processing __file/root/foo17.bin +INFO: [20181121130717.355196] 185.203.112.26: Processing __file/root/foo18.bin +INFO: [20181121130719.486316] 185.203.112.26: Processing __file/root/foo19.bin +INFO: [20181121130721.619933] 185.203.112.26: Processing __file/root/foo20.bin +INFO: [20181121130723.786670] 185.203.112.26: Processing __file/root/foo21.bin +INFO: [20181121130725.924736] 185.203.112.26: Processing __file/root/foo22.bin +INFO: [20181121130728.060224] 185.203.112.26: Processing __file/root/foo23.bin +INFO: [20181121130730.178729] 185.203.112.26: Processing __file/root/foo24.bin +INFO: [20181121130732.309264] 185.203.112.26: Processing __file/root/foo25.bin +INFO: [20181121130734.479895] 185.203.112.26: Processing __file/root/foo26.bin +INFO: [20181121130736.653085] 185.203.112.26: Processing __file/root/foo27.bin +INFO: [20181121130738.814291] 185.203.112.26: Processing __file/root/foo28.bin +INFO: [20181121130741.029646] 185.203.112.26: Processing __file/root/foo29.bin +INFO: [20181121130743.128717] 185.203.112.26: Processing __file/root/foo30.bin +INFO: [20181121130745.233272] 185.203.112.26: Processing __file/root/foo31.bin +INFO: [20181121130747.364681] 185.203.112.26: Processing __file/root/foo32.bin +INFO: [20181121130749.491793] 185.203.112.26: Processing __file/root/foo33.bin +INFO: [20181121130751.620492] 185.203.112.26: Processing __file/root/foo34.bin +INFO: [20181121130753.743519] 185.203.112.26: Processing __file/root/foo35.bin +INFO: [20181121130755.862169] 185.203.112.26: Processing __file/root/foo36.bin +INFO: [20181121130758.000172] 185.203.112.26: Processing __file/root/foo37.bin +INFO: [20181121130800.090405] 185.203.112.26: Processing __file/root/foo38.bin +INFO: [20181121130802.211849] 185.203.112.26: Processing __file/root/foo39.bin +INFO: [20181121130804.356363] 185.203.112.26: Processing __file/root/foo40.bin +INFO: [20181121130806.548412] 185.203.112.26: Processing __file/root/foo41.bin +INFO: [20181121130808.671279] 185.203.112.26: Processing __file/root/foo42.bin +INFO: [20181121130810.752813] 185.203.112.26: Processing __file/root/foo43.bin +INFO: [20181121130812.844502] 185.203.112.26: Processing __file/root/foo44.bin +INFO: [20181121130814.950501] 185.203.112.26: Processing __file/root/foo45.bin +INFO: [20181121130817.040587] 185.203.112.26: Processing __file/root/foo46.bin +INFO: [20181121130819.175850] 185.203.112.26: Processing __file/root/foo47.bin +INFO: [20181121130821.332900] 185.203.112.26: Processing __file/root/foo48.bin +INFO: [20181121130823.543119] 185.203.112.26: Processing __file/root/foo49.bin +INFO: [20181121130825.833163] 185.203.112.26: Finished successful run in 133.31 seconds + +# sh type, files exist at remote +echo 'x=0; while [ $x -lt 50 ]; do __file /root/foo${x}.bin --source /tmp/test.file --mode 0640 --owner root --group root; x=$((x + 1)); done' | ./bin/cdist config -v -P -i - 185.203.112.26 + +INFO: [20181121130854.980007] 185.203.112.26: Starting configuration run +INFO: [20181121130957.927705] 185.203.112.26: Finished successful run in 62.95 seconds + +# py type, no file at remote +echo 'x=0; while [ $x -lt 50 ]; do __file_py /root/foo${x}.bin --source /tmp/test.file --mode 0640 --owner root --group root; x=$((x + 1)); done' | ./bin/cdist config -v -P -i - 185.203.112.26 + +INFO: [20181121131110.179480] 185.203.112.26: Starting configuration run +INFO: [20181121131122.086849] 185.203.112.26: Processing __file_py/root/foo0.bin +INFO: [20181121131123.876029] 185.203.112.26: Processing __file_py/root/foo1.bin +INFO: [20181121131125.668664] 185.203.112.26: Processing __file_py/root/foo2.bin +INFO: [20181121131127.460721] 185.203.112.26: Processing __file_py/root/foo3.bin +INFO: [20181121131129.591229] 185.203.112.26: Processing __file_py/root/foo4.bin +INFO: [20181121131131.390379] 185.203.112.26: Processing __file_py/root/foo5.bin +INFO: [20181121131133.195275] 185.203.112.26: Processing __file_py/root/foo6.bin +INFO: [20181121131135.006282] 185.203.112.26: Processing __file_py/root/foo7.bin +INFO: [20181121131136.834448] 185.203.112.26: Processing __file_py/root/foo8.bin +INFO: [20181121131138.659301] 185.203.112.26: Processing __file_py/root/foo9.bin +INFO: [20181121131140.496856] 185.203.112.26: Processing __file_py/root/foo10.bin +INFO: [20181121131142.367813] 185.203.112.26: Processing __file_py/root/foo11.bin +INFO: [20181121131144.239817] 185.203.112.26: Processing __file_py/root/foo12.bin +INFO: [20181121131146.133314] 185.203.112.26: Processing __file_py/root/foo13.bin +INFO: [20181121131148.049380] 185.203.112.26: Processing __file_py/root/foo14.bin +INFO: [20181121131149.974696] 185.203.112.26: Processing __file_py/root/foo15.bin +INFO: [20181121131151.929083] 185.203.112.26: Processing __file_py/root/foo16.bin +INFO: [20181121131153.923590] 185.203.112.26: Processing __file_py/root/foo17.bin +INFO: [20181121131155.874910] 185.203.112.26: Processing __file_py/root/foo18.bin +INFO: [20181121131157.857904] 185.203.112.26: Processing __file_py/root/foo19.bin +INFO: [20181121131159.902006] 185.203.112.26: Processing __file_py/root/foo20.bin +INFO: [20181121131201.859840] 185.203.112.26: Processing __file_py/root/foo21.bin +INFO: [20181121131203.810875] 185.203.112.26: Processing __file_py/root/foo22.bin +INFO: [20181121131205.763291] 185.203.112.26: Processing __file_py/root/foo23.bin +INFO: [20181121131207.710932] 185.203.112.26: Processing __file_py/root/foo24.bin +INFO: [20181121131209.658154] 185.203.112.26: Processing __file_py/root/foo25.bin +INFO: [20181121131211.615374] 185.203.112.26: Processing __file_py/root/foo26.bin +INFO: [20181121131213.569721] 185.203.112.26: Processing __file_py/root/foo27.bin +INFO: [20181121131215.522624] 185.203.112.26: Processing __file_py/root/foo28.bin +INFO: [20181121131217.471128] 185.203.112.26: Processing __file_py/root/foo29.bin +INFO: [20181121131219.421712] 185.203.112.26: Processing __file_py/root/foo30.bin +INFO: [20181121131221.375699] 185.203.112.26: Processing __file_py/root/foo31.bin +INFO: [20181121131223.327672] 185.203.112.26: Processing __file_py/root/foo32.bin +INFO: [20181121131225.281373] 185.203.112.26: Processing __file_py/root/foo33.bin +INFO: [20181121131227.256711] 185.203.112.26: Processing __file_py/root/foo34.bin +INFO: [20181121131229.209255] 185.203.112.26: Processing __file_py/root/foo35.bin +INFO: [20181121131231.170170] 185.203.112.26: Processing __file_py/root/foo36.bin +INFO: [20181121131233.123407] 185.203.112.26: Processing __file_py/root/foo37.bin +INFO: [20181121131235.077713] 185.203.112.26: Processing __file_py/root/foo38.bin +INFO: [20181121131237.017138] 185.203.112.26: Processing __file_py/root/foo39.bin +INFO: [20181121131238.988189] 185.203.112.26: Processing __file_py/root/foo40.bin +INFO: [20181121131241.026849] 185.203.112.26: Processing __file_py/root/foo41.bin +INFO: [20181121131242.978335] 185.203.112.26: Processing __file_py/root/foo42.bin +INFO: [20181121131244.934562] 185.203.112.26: Processing __file_py/root/foo43.bin +INFO: [20181121131246.885320] 185.203.112.26: Processing __file_py/root/foo44.bin +INFO: [20181121131248.835008] 185.203.112.26: Processing __file_py/root/foo45.bin +INFO: [20181121131250.789727] 185.203.112.26: Processing __file_py/root/foo46.bin +INFO: [20181121131252.738686] 185.203.112.26: Processing __file_py/root/foo47.bin +INFO: [20181121131254.691465] 185.203.112.26: Processing __file_py/root/foo48.bin +INFO: [20181121131256.640896] 185.203.112.26: Processing __file_py/root/foo49.bin +INFO: [20181121131258.194372] 185.203.112.26: Finished successful run in 108.01 seconds + +# py type, files exist at remote +echo 'x=0; while [ $x -lt 50 ]; do __file_py /root/foo${x}.bin --source /tmp/test.file --mode 0640 --owner root --group root; x=$((x + 1)); done' | ./bin/cdist config -v -P -i - 185.203.112.26 + +INFO: [20181121131327.054523] 185.203.112.26: Starting configuration run +INFO: [20181121131428.031761] 185.203.112.26: Finished successful run in 60.98 seconds + + +# Summary + +# sh type, no file at remote +INFO: [20181121125439.429440] 185.203.112.26: Finished successful run in 165.38 seconds +# py type, no file at remote +INFO: [20181121125957.514738] 185.203.112.26: Finished successful run in 105.48 seconds + +# sh type, files exist at remote but content changes +INFO: [20181121125724.451523] 185.203.112.26: Finished successful run in 114.50 seconds +# py type, files exist at remote but content changes +INFO: [20181121130305.144393] 185.203.112.26: Finished successful run in 128.66 seconds + + +# sh type, no file at remote +INFO: [20181121130825.833163] 185.203.112.26: Finished successful run in 133.31 seconds +# py type, no file at remote +INFO: [20181121131258.194372] 185.203.112.26: Finished successful run in 108.01 seconds + +# sh type, files exist at remote +INFO: [20181121130957.927705] 185.203.112.26: Finished successful run in 62.95 seconds +# py type, files exist at remote +INFO: [20181121131428.031761] 185.203.112.26: Finished successful run in 60.98 seconds diff --git a/docs/dev/python-types/benchmark.sh b/docs/dev/python-types/benchmark.sh new file mode 100755 index 00000000..3e01941f --- /dev/null +++ b/docs/dev/python-types/benchmark.sh @@ -0,0 +1,43 @@ +#!/bin/sh + +# Addapt to your env. +CDIST_PATH="$CDIST_PATH:./docs/dev/python-types/conf" +export CDIST_PATH +TARGET_HOST=185.203.112.26 + +if [ $# -eq 0 ] +then + N=1 +else + N=$1 +fi + +manifest() { + bytes=$(echo "$1 * 1024" | bc) + echo "head -c ${bytes} /dev/random | __file$2 /root/foo$3.bin --source - --mode 0640 --owner root --group root" +} + +verbosity="-vv" #"-vvv" +i=0 +while [ "$i" -lt "$N" ] +do + if [ "$N" -ne 1 ] + then + printf "iteration %d\\n" "$i" + fi + printf "shinit clean state...\\n" + ssh root@${TARGET_HOST} 'rm foo$i.bin;' + manifest 50 "" $i | ./bin/cdist config "${verbosity}" -P -i - ${TARGET_HOST} + + printf "pyinit clean state...\\n" + ssh root@${TARGET_HOST} 'rm foo$i.bin;' + manifest 50 '_py' $i | ./bin/cdist config "${verbosity}" -P -i - ${TARGET_HOST} + + printf "shinit present state...\\n" + manifest 50 "" $i | ./bin/cdist config "${verbosity}" -P -i - ${TARGET_HOST} + + printf "pyinit present state...\\n" + manifest 50 '_py' $i | ./bin/cdist config "${verbosity}" -P -i - ${TARGET_HOST} + + i=$((i + 1)) +done diff --git a/docs/dev/python-types/conf/manifest/pyinit b/docs/dev/python-types/conf/manifest/pyinit new file mode 100644 index 00000000..53f15a97 --- /dev/null +++ b/docs/dev/python-types/conf/manifest/pyinit @@ -0,0 +1,7 @@ +#for x in 1; do +# echo xxx${x} | __file_py /root/foobar${x} --source - --mode 0640 --owner root --group root; +#done +#__dummy_config_py test1 + +echo xxx | __file_py /root/foobar --source - --mode 0640 --owner root --group root +__dummy_config_py test1 diff --git a/docs/dev/python-types/conf/manifest/shinit b/docs/dev/python-types/conf/manifest/shinit new file mode 100644 index 00000000..44129546 --- /dev/null +++ b/docs/dev/python-types/conf/manifest/shinit @@ -0,0 +1,7 @@ +#for x in 1; do +# echo xxx${x} | __file /root/foobar${x} --source - --mode 0640 --owner root --group root; +#done +#__dummy_config_sh test1 + +echo xxx | __file /root/foobar --source - --mode 0640 --owner root --group root +__dummy_config_sh test1 diff --git a/docs/dev/python-types/conf/type/__dummy_config_py/__init__.py b/docs/dev/python-types/conf/type/__dummy_config_py/__init__.py new file mode 100644 index 00000000..0b5fa692 --- /dev/null +++ b/docs/dev/python-types/conf/type/__dummy_config_py/__init__.py @@ -0,0 +1,22 @@ +import os +import sys +from cdist.core.pytypes import * + + +class DummyConfig(PythonType): + def type_manifest(self): + print('dummy manifest stdout') + print('dummy manifest stderr\n', file=sys.stderr) + yield file_py('/root/dummy1.conf', + mode='0640', + owner='root', + group='root', + source='-').feed_stdin('dummy=1\n') + + self_path = os.path.dirname(os.path.realpath(__file__)) + conf_path = os.path.join(self_path, 'files', 'dummy.conf') + yield file_py('/root/dummy2.conf', + mode='0640', + owner='root', + group='root', + source=conf_path) diff --git a/docs/dev/python-types/conf/type/__dummy_config_py/files/dummy.conf b/docs/dev/python-types/conf/type/__dummy_config_py/files/dummy.conf new file mode 100644 index 00000000..972d11ca --- /dev/null +++ b/docs/dev/python-types/conf/type/__dummy_config_py/files/dummy.conf @@ -0,0 +1 @@ +dummy=2 diff --git a/docs/dev/python-types/conf/type/__dummy_config_sh/files/dummy.conf b/docs/dev/python-types/conf/type/__dummy_config_sh/files/dummy.conf new file mode 100644 index 00000000..972d11ca --- /dev/null +++ b/docs/dev/python-types/conf/type/__dummy_config_sh/files/dummy.conf @@ -0,0 +1 @@ +dummy=2 diff --git a/docs/dev/python-types/conf/type/__dummy_config_sh/manifest b/docs/dev/python-types/conf/type/__dummy_config_sh/manifest new file mode 100644 index 00000000..d675d6a3 --- /dev/null +++ b/docs/dev/python-types/conf/type/__dummy_config_sh/manifest @@ -0,0 +1,6 @@ +printf 'dummy manifest stdout\n' +printf 'dummy manifest stderr\n' >&2 + +printf "dummy=1\\n" | __file /root/dummy1.conf --mode 0640 --owner root --group root --source - + +__file /root/dummy2.conf --mode 0600 --owner root --group root --source "$__type/files/dummy.conf" diff --git a/docs/dev/python-types/test.sh b/docs/dev/python-types/test.sh new file mode 100755 index 00000000..941ea264 --- /dev/null +++ b/docs/dev/python-types/test.sh @@ -0,0 +1,27 @@ +#!/bin/sh + +# Addapt to your env. +CDIST_PATH="$CDIST_PATH:./docs/dev/python-types/conf" +export CDIST_PATH +TARGET_HOST=185.203.112.26 +env | grep CDIST_PATH + +for streams in ' ' '-S' +do + for x in sh py + do + printf "[%s] Removing old foobar* files\\n" "$x" + printf -- "----------------\\n" + ssh root@${TARGET_HOST} 'rm foobar*; rm dummy*' + printf "[%s] Listing foobar* files\\n" "$x" + printf -- "----------------\\n" + ssh root@${TARGET_HOST} 'ls foobar* dummy*' + printf "[%s] Running cdist config, streams: %s\\n" "$x" "$streams" + printf -- "----------------\\n" + ./bin/cdist config -P ${streams} -v -i ./docs/dev/python-types/conf/manifest/${x}init -- ${TARGET_HOST} + printf "[%s] Listing foobar* files\\n" "$x" + printf -- "----------------\\n" + ssh root@${TARGET_HOST} 'ls foobar* dummy*' + ./bin/cdist config -P ${streams} -v -i ./docs/dev/python-types/conf/manifest/${x}init -- ${TARGET_HOST} + done +done diff --git a/docs/dev/python-types/timeit.sh b/docs/dev/python-types/timeit.sh new file mode 100755 index 00000000..f1b6c9fb --- /dev/null +++ b/docs/dev/python-types/timeit.sh @@ -0,0 +1,36 @@ +#!/bin/sh + +# Addapt to your env. +CDIST_PATH="$CDIST_PATH:./docs/dev/python-types/conf" +export CDIST_PATH +TARGET_HOST=185.203.112.26 + +if [ $# -eq 0 ] +then + N=1 +else + N=$1 +fi + +i=0 +while [ "$i" -lt "$N" ] +do + if [ "$N" -ne 1 ] + then + printf "iteration %d\\n" "$i" + fi + printf "shinit clean state...\\n" + ssh root@${TARGET_HOST} 'rm foobar*; rm dummy*;' + + time ./bin/cdist config -vv -P -i ./docs/dev/python-types/conf/manifest/shinit ${TARGET_HOST} + printf "pyinit clean state...\\n" + ssh root@$${TARGET_HOST} 'rm foobar*; rm dummy*;' + time ./bin/cdist config -vv -P -i ./docs/dev/python-types/conf/manifest/pyinit ${TARGET_HOST} + + printf "shinit present state...\\n" + time ./bin/cdist config -vv -P -i ./docs/dev/python-types/conf/manifest/shinit ${TARGET_HOST} + + printf "pyinit present state...\\n" + time ./bin/cdist config -vv -P -i ./docs/dev/python-types/conf/manifest/pyinit ${TARGET_HOST} + i=$((i + 1)) +done diff --git a/docs/src/cdist-type.rst b/docs/src/cdist-type.rst index 582c0938..3a4a0b13 100644 --- a/docs/src/cdist-type.rst +++ b/docs/src/cdist-type.rst @@ -522,3 +522,89 @@ How to include a type into upstream cdist If you think your type may be useful for others, ensure it works with the current master branch of cdist and have a look at `cdist hacking `_ on how to submit it. + + +Python types +------------ +From version/branch **beta** cdist support python types, types that are written +in python language with cdist's core support. cdist detects such type if type is +detectable as a python package, i.e. if **__init__.py** file is present in type's +root directory. Upon that detection cdist will try to run such type as core python +type. + +Note that this differs from plain cdist type where scripts are written in pure +python and have a proper shebang. + +Core python types replace manifest and gencode scripts. Parameters, singleton, +nonparallel are still defined as for common types. Explorer code is also written +in shell, since this is the code that is directly executed at target host. + +When writing python type you can extend **cdist.core.pytypes.PythonType** class. +You need to implement the following methods: + +* **type_manifest**: implementation should yield **cdist.core.pytypes.** + attribute function call result, or **yield from ()** if type does not use other types +* **type_gencode**: implementation should return a string consisting of lines + of shell code that will be executed at target host. + +**cdist.core.pytypes.** attributes correspond to detected python types. +**Note** that double underscore ('__') at the beginning of type name is removed. + +Example: + +.. code-block:: sh + + import os + import sys + from cdist.core.pytypes import * + + + class DummyConfig(PythonType): + def type_manifest(self): + print('dummy manifest stdout') + print('dummy manifest stderr\n', file=sys.stderr) + yield file_py('/root/dummy1.conf', + mode='0640', + owner='root', + group='root', + source='-').feed_stdin('dummy=1\n') + + self_path = os.path.dirname(os.path.realpath(__file__)) + conf_path = os.path.join(self_path, 'files', 'dummy.conf') + yield file_py('/root/dummy2.conf', + mode='0640', + owner='root', + group='root', + source=conf_path) + +**cdist.core.PythonType** class provides the following methods: + +* **get_parameter**: get type parameter +* **get_explorer_file**: get path to file for specified explorer +* **get_explorer**: get value for specified explorer +* **run_local**: run specified command locally +* **run_remote**: run specified command remotely +* **transfer**: transfer specified source to the remote +* **die**: raise error +* **send_message**: send message +* **receive_message**: get message. + +When running python type, cdist will save output streams to **gencode-py**, +stdout and stderr output files. + +As a reference implementation you can take a look at **__file_py** type, +which is re-implementation of **__file** type. + +Furthermore, under **docs/dev/python-types** there are sample cdist conf directory, +init manifests and scripts for running and measuring duration of samples. +There, under **conf/type/__dummy_config** you can find another example of +python type, which (unlike **__file_py** type) also uses new manifest implementation +that yields **cdist.core.pytypes.** attribute function call results. + +**NOTE** that python types implementation is under the beta, not directly controled by +the **-b/--beta** option. It is controled by the explicit usage of python types in +your config. + +Also, this documenation is only an introduction, and not a complete guide to python +types. Currently, it is just a short introduction so one can start to write and use +python types. diff --git a/scripts/cdist b/scripts/cdist index 7bf12c01..664504a0 100755 --- a/scripts/cdist +++ b/scripts/cdist @@ -60,7 +60,7 @@ def commandline(): if __name__ == "__main__": - cdistpythonversion = '3.2' + cdistpythonversion = '3.5' if sys.version < cdistpythonversion: print('Python >= {} is required on the source host.'.format( cdistpythonversion), file=sys.stderr) From a2243cf59edb145bb3341b9ad56b14428bc90197 Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Sat, 22 Jun 2019 12:55:56 +0200 Subject: [PATCH 008/366] Add support for python type defined argument parser --- cdist/conf/type/__file_py/__init__.py | 14 ++++++- cdist/core/code.py | 25 ++--------- cdist/core/manifest.py | 25 ++--------- cdist/core/pytypes.py | 60 ++++++++++++++++++++------- cdist/emulator.py | 34 ++++++++++++--- docs/src/cdist-type.rst | 3 ++ 6 files changed, 96 insertions(+), 65 deletions(-) diff --git a/cdist/conf/type/__file_py/__init__.py b/cdist/conf/type/__file_py/__init__.py index 1212f8fd..3397b417 100644 --- a/cdist/conf/type/__file_py/__init__.py +++ b/cdist/conf/type/__file_py/__init__.py @@ -1,7 +1,7 @@ import os import re -import sys from cdist.core.pytypes import * +import argparse class FileType(PythonType): @@ -101,3 +101,15 @@ class FileType(PythonType): self.die('Unknown state {}'.format(state_should)) return "\n".join(code) + + def get_args_parser(self): + parser = argparse.ArgumentParser(add_help=False, + argument_default=argparse.SUPPRESS) + parser.add_argument('--state', dest='state', action='store', + required=False, default='present') + for param in ('group', 'mode', 'owner', 'source'): + parser.add_argument('--' + param, dest=param, action='store', + required=False, default=None) + + parser.add_argument("object_id", nargs=1) + return parser diff --git a/cdist/core/code.py b/cdist/core/code.py index 34d5bd97..ae1fddf2 100644 --- a/cdist/core/code.py +++ b/cdist/core/code.py @@ -22,10 +22,8 @@ # import os -import importlib.util import inspect -import cdist -from cdist.core.pytypes import PythonType +from cdist.core.pytypes import get_pytype_class from . import util @@ -122,25 +120,8 @@ class Code(object): def run_py(self, cdist_object): cdist_type = cdist_object.cdist_type - module_name = cdist_type.name - file_path = os.path.join(cdist_type.absolute_path, '__init__.py') - - if os.path.isfile(file_path): - spec = importlib.util.spec_from_file_location(module_name, - file_path) - m = importlib.util.module_from_spec(spec) - spec.loader.exec_module(m) - classes = inspect.getmembers(m, inspect.isclass) - type_class = None - for _, cl in classes: - if cl != PythonType and issubclass(cl, PythonType): - if type_class: - raise cdist.Error("Only one python type class is " - "supported, but at least two " - "found: {}".format((type_class, - cl, ))) - else: - type_class = cl + type_class = get_pytype_class(cdist_type) + if type_class is not None: env = os.environ.copy() env.update(self.env) message_prefix = cdist_object.name diff --git a/cdist/core/manifest.py b/cdist/core/manifest.py index 546fba25..16dec465 100644 --- a/cdist/core/manifest.py +++ b/cdist/core/manifest.py @@ -22,12 +22,11 @@ import logging import os -import importlib.util import inspect import cdist import cdist.emulator from . import util -from cdist.core.pytypes import PythonType, Command +from cdist.core.pytypes import Command, get_pytype_class ''' @@ -248,27 +247,11 @@ class Manifest(object): def run_py_type_manifest(self, cdist_object): cdist_type = cdist_object.cdist_type - module_name = cdist_type.name - file_path = os.path.join(cdist_type.absolute_path, '__init__.py') - message_prefix = cdist_object.name - if os.path.isfile(file_path): + type_class = get_pytype_class(cdist_type) + if type_class is not None: self.log.verbose("Running python type manifest for object %s", cdist_object.name) - spec = importlib.util.spec_from_file_location(module_name, - file_path) - m = importlib.util.module_from_spec(spec) - spec.loader.exec_module(m) - classes = inspect.getmembers(m, inspect.isclass) - type_class = None - for _, cl in classes: - if cl != PythonType and issubclass(cl, PythonType): - if type_class: - raise cdist.Error("Only one python type class is " - "supported, but at least two " - "found: {}".format((type_class, - cl, ))) - else: - type_class = cl + message_prefix = cdist_object.name env = self.env_py_type_manifest(cdist_object) type_obj = type_class(env=env, cdist_object=cdist_object, local=self.local, remote=None, diff --git a/cdist/core/pytypes.py b/cdist/core/pytypes.py index ae4164ec..9dfc8df9 100644 --- a/cdist/core/pytypes.py +++ b/cdist/core/pytypes.py @@ -4,6 +4,9 @@ import io import sys import re from cdist import message, Error +import importlib.util +import inspect +import cdist __all__ = ["PythonType", "Command", "command"] @@ -13,18 +16,20 @@ class PythonType: def __init__(self, env, cdist_object, local, remote, message_prefix=None): self.env = env self.cdist_object = cdist_object - self.object_id = cdist_object.object_id - self.object_name = cdist_object.name - self.cdist_type = cdist_object.cdist_type self.local = local self.remote = remote - self.object_path = cdist_object.absolute_path - self.type_path = cdist_object.cdist_type.absolute_path - self.explorer_path = os.path.join(self.object_path, 'explorer') - self.parameters = cdist_object.parameters - self.stdin_path = os.path.join(self.object_path, 'stdin') - self.log = logging.getLogger( - self.local.target_host[0] + ':' + self.object_name) + if self.cdist_object: + self.object_id = cdist_object.object_id + self.object_name = cdist_object.name + self.cdist_type = cdist_object.cdist_type + self.object_path = cdist_object.absolute_path + self.explorer_path = os.path.join(self.object_path, 'explorer') + self.type_path = cdist_object.cdist_type.absolute_path + self.parameters = cdist_object.parameters + self.stdin_path = os.path.join(self.object_path, 'stdin') + if self.local: + self.log = logging.getLogger( + self.local.target_host[0] + ':' + self.object_name) self.message_prefix = message_prefix self.message = None @@ -62,12 +67,6 @@ class PythonType: def die(self, msg): raise Error("{}: {}".format(self.cdist_object, msg)) - def type_manifest(self): - pass - - def type_gencode(self): - pass - def manifest(self, stdout=None, stderr=None): try: if self.message_prefix: @@ -123,6 +122,15 @@ class PythonType: return match return None + def get_args_parser(self): + pass + + def type_manifest(self): + pass + + def type_gencode(self): + pass + class Command: def __init__(self, name, *args, **kwargs): @@ -160,3 +168,23 @@ class Command: def command(name, *args, **kwargs): return Command(name, *args, **kwargs) + + +def get_pytype_class(cdist_type): + module_name = cdist_type.name + file_path = os.path.join(cdist_type.absolute_path, '__init__.py') + type_class = None + if os.path.isfile(file_path): + spec = importlib.util.spec_from_file_location(module_name, file_path) + m = importlib.util.module_from_spec(spec) + spec.loader.exec_module(m) + classes = inspect.getmembers(m, inspect.isclass) + for _, cl in classes: + if cl != PythonType and issubclass(cl, PythonType): + if type_class: + raise cdist.Error( + "Only one python type class is supported, but at least" + " two found: {}".format((type_class, cl, ))) + else: + type_class = cl + return type_class diff --git a/cdist/emulator.py b/cdist/emulator.py index 4800e2a3..7dc24a83 100644 --- a/cdist/emulator.py +++ b/cdist/emulator.py @@ -30,6 +30,9 @@ import cdist from cdist import core from cdist import flock from cdist.core.manifest import Manifest +import cdist.util.python_type_util as pytype_util +from cdist.core.pytypes import get_pytype_class +import inspect class MissingRequiredEnvironmentVariableError(cdist.Error): @@ -100,7 +103,28 @@ class Emulator(object): def run(self): """Emulate type commands (i.e. __file and co)""" - self.commandline() + args_parser = None + if pytype_util.is_python_type(self.cdist_type): + type_class = get_pytype_class(self.cdist_type) + if type_class is not None: + # We only need to call parse_args so we need plain instance. + type_obj = type_class(env=None, cdist_object=None, local=None, + remote=None) + if (hasattr(type_obj, 'get_args_parser') and + inspect.ismethod(type_obj.get_args_parser)): + args_parser = type_obj.get_args_parser() + self.log.trace("Using python type argument parser") + print("Using python type argument parser") + if args_parser is None: + # Fallback to classic way. + args_parser = self.get_args_parser() + self.log.trace("Fallback to classic argument parser") + print("Fallback to classic argument parser") + else: + args_parser = self.get_args_parser() + self.log.trace("Using emulator classic argument parser") + print("Using emulator classic argument parser") + self.commandline(args_parser) self.init_object() # locking for parallel execution @@ -131,9 +155,7 @@ class Emulator(object): self.log = logging.getLogger(self.target_host[0]) - def commandline(self): - """Parse command line""" - + def get_args_parser(self): parser = argparse.ArgumentParser(add_help=False, argument_default=argparse.SUPPRESS) @@ -165,8 +187,10 @@ class Emulator(object): # If not singleton support one positional parameter if not self.cdist_type.is_singleton: parser.add_argument("object_id", nargs=1) + return parser - # And finally parse/verify parameter + def commandline(self, parser): + """Parse command line""" self.args = parser.parse_args(self.argv[1:]) self.log.trace('Args: %s' % self.args) diff --git a/docs/src/cdist-type.rst b/docs/src/cdist-type.rst index 3a4a0b13..388c3caf 100644 --- a/docs/src/cdist-type.rst +++ b/docs/src/cdist-type.rst @@ -542,6 +542,9 @@ in shell, since this is the code that is directly executed at target host. When writing python type you can extend **cdist.core.pytypes.PythonType** class. You need to implement the following methods: +* **get_args_parser**: implementation should return **argparse.ArgumentParser** and if + it is undefined or returned None then cdist falls back to classic type parameter + definition and argument parsing * **type_manifest**: implementation should yield **cdist.core.pytypes.** attribute function call result, or **yield from ()** if type does not use other types * **type_gencode**: implementation should return a string consisting of lines From 420aaaee164e8ed22ed03002db64a048a20615c5 Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Fri, 20 Sep 2019 20:16:55 +0200 Subject: [PATCH 009/366] Make python file type default, mv old to __file_old --- .../type/{__file_py => __file}/__init__.py | 0 cdist/conf/type/__file/explorer/stat | 35 +------- cdist/conf/type/__file/parameter/optional | 1 - .../{__file_py => __file_old}/explorer/cksum | 0 cdist/conf/type/__file_old/explorer/stat | 88 +++++++++++++++++++ .../{__file_py => __file_old}/explorer/type | 0 .../type/{__file => __file_old}/gencode-local | 0 .../{__file => __file_old}/gencode-remote | 0 .../conf/type/{__file => __file_old}/man.rst | 0 .../parameter/default/state | 0 .../parameter/optional | 1 + cdist/conf/type/__file_py/explorer/stat | 56 ------------ 12 files changed, 90 insertions(+), 91 deletions(-) rename cdist/conf/type/{__file_py => __file}/__init__.py (100%) rename cdist/conf/type/{__file_py => __file_old}/explorer/cksum (100%) create mode 100755 cdist/conf/type/__file_old/explorer/stat rename cdist/conf/type/{__file_py => __file_old}/explorer/type (100%) rename cdist/conf/type/{__file => __file_old}/gencode-local (100%) rename cdist/conf/type/{__file => __file_old}/gencode-remote (100%) rename cdist/conf/type/{__file => __file_old}/man.rst (100%) rename cdist/conf/type/{__file_py => __file_old}/parameter/default/state (100%) rename cdist/conf/type/{__file_py => __file_old}/parameter/optional (76%) delete mode 100755 cdist/conf/type/__file_py/explorer/stat diff --git a/cdist/conf/type/__file_py/__init__.py b/cdist/conf/type/__file/__init__.py similarity index 100% rename from cdist/conf/type/__file_py/__init__.py rename to cdist/conf/type/__file/__init__.py diff --git a/cdist/conf/type/__file/explorer/stat b/cdist/conf/type/__file/explorer/stat index 91c8cc84..c772abcf 100755 --- a/cdist/conf/type/__file/explorer/stat +++ b/cdist/conf/type/__file/explorer/stat @@ -52,7 +52,6 @@ fallback() { # nothing to work with, nothing we could do [ -e "$destination" ] || exit 0 - if ! command -v stat >/dev/null then fallback @@ -69,39 +68,7 @@ group: %Dg %Sg mode: %Lp %Sp size: %Dz links: %Dl -" "$destination" | awk '/^type/ { print tolower($0); next } { print }' - ;; - solaris) - ls1="$( ls -ld "$destination" )" - ls2="$( ls -ldn "$destination" )" - - if [ -f "$__object/parameter/mode" ] - then mode_should="$( cat "$__object/parameter/mode" )" - fi - - # yes, it is ugly hack, but if you know better way... - if [ -z "$( find "$destination" -perm "$mode_should" )" ] - then octets=888 - else octets="$( echo "$mode_should" | sed 's/^0//' )" - fi - - case "$( echo "$ls1" | cut -c1-1 )" in - -) echo 'type: regular file' ;; - d) echo 'type: directory' ;; - esac - - echo "owner: $( echo "$ls2" \ - | awk '{print $3}' ) $( echo "$ls1" \ - | awk '{print $3}' )" - - echo "group: $( echo "$ls2" \ - | awk '{print $4}' ) $( echo "$ls1" \ - | awk '{print $4}' )" - - echo "mode: $octets $( echo "$ls1" | awk '{print $1}' )" - echo "size: $( echo "$ls1" | awk '{print $5}' )" - echo "links: $( echo "$ls1" | awk '{print $2}' )" - ;; + ;; *) # NOTE: Do not use --printf here as it is not supported by BusyBox stat. # NOTE: BusyBox's stat might not support the "-c" option, in which case diff --git a/cdist/conf/type/__file/parameter/optional b/cdist/conf/type/__file/parameter/optional index 9b98352c..c696d592 100644 --- a/cdist/conf/type/__file/parameter/optional +++ b/cdist/conf/type/__file/parameter/optional @@ -3,4 +3,3 @@ group mode owner source -onchange diff --git a/cdist/conf/type/__file_py/explorer/cksum b/cdist/conf/type/__file_old/explorer/cksum similarity index 100% rename from cdist/conf/type/__file_py/explorer/cksum rename to cdist/conf/type/__file_old/explorer/cksum diff --git a/cdist/conf/type/__file_old/explorer/stat b/cdist/conf/type/__file_old/explorer/stat new file mode 100755 index 00000000..13c1c208 --- /dev/null +++ b/cdist/conf/type/__file_old/explorer/stat @@ -0,0 +1,88 @@ +#!/bin/sh +# +# 2013 Steven Armstrong (steven-cdist armstrong.cc) +# 2019 Nico Schottelius (nico-cdist at schottelius.org) +# +# 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 . +# + +destination="/$__object_id" + +# nothing to work with, nothing we could do +[ -e "$destination" ] || exit 0 + +os=$("$__explorer/os") +case "$os" in + "freebsd"|"netbsd"|"openbsd"|"macosx") + stat -f "type: %HT +owner: %Du %Su +group: %Dg %Sg +mode: %Lp %Sp +size: %Dz +links: %Dl +" "$destination" | awk '/^type/ { print tolower($0); next; } { print; }' + ;; + alpine) + # busybox stat + stat -c "type: %F +owner: %u %U +group: %g %G +mode: %a %A +size: %s +links: %h +" "$destination" + ;; + solaris) + ls1="$( ls -ld "$destination" )" + ls2="$( ls -ldn "$destination" )" + + if [ -f "$__object/parameter/mode" ] + then mode_should="$( cat "$__object/parameter/mode" )" + fi + + # yes, it is ugly hack, but if you know better way... + if [ -z "$( find "$destination" -perm "$mode_should" )" ] + then octets=888 + else octets="$( echo "$mode_should" | sed 's/^0//' )" + fi + + case "$( echo "$ls1" | cut -c1-1 )" in + -) echo 'type: regular file' ;; + d) echo 'type: directory' ;; + esac + + echo "owner: $( echo "$ls2" \ + | awk '{print $3}' ) $( echo "$ls1" \ + | awk '{print $3}' )" + + echo "group: $( echo "$ls2" \ + | awk '{print $4}' ) $( echo "$ls1" \ + | awk '{print $4}' )" + + echo "mode: $octets $( echo "$ls1" | awk '{print $1}' )" + echo "size: $( echo "$ls1" | awk '{print $5}' )" + echo "links: $( echo "$ls1" | awk '{print $2}' )" + ;; + *) + stat --printf="type: %F +owner: %u %U +group: %g %G +mode: %a %A +size: %s +links: %h +" "$destination" + ;; +esac diff --git a/cdist/conf/type/__file_py/explorer/type b/cdist/conf/type/__file_old/explorer/type similarity index 100% rename from cdist/conf/type/__file_py/explorer/type rename to cdist/conf/type/__file_old/explorer/type diff --git a/cdist/conf/type/__file/gencode-local b/cdist/conf/type/__file_old/gencode-local similarity index 100% rename from cdist/conf/type/__file/gencode-local rename to cdist/conf/type/__file_old/gencode-local diff --git a/cdist/conf/type/__file/gencode-remote b/cdist/conf/type/__file_old/gencode-remote similarity index 100% rename from cdist/conf/type/__file/gencode-remote rename to cdist/conf/type/__file_old/gencode-remote diff --git a/cdist/conf/type/__file/man.rst b/cdist/conf/type/__file_old/man.rst similarity index 100% rename from cdist/conf/type/__file/man.rst rename to cdist/conf/type/__file_old/man.rst diff --git a/cdist/conf/type/__file_py/parameter/default/state b/cdist/conf/type/__file_old/parameter/default/state similarity index 100% rename from cdist/conf/type/__file_py/parameter/default/state rename to cdist/conf/type/__file_old/parameter/default/state diff --git a/cdist/conf/type/__file_py/parameter/optional b/cdist/conf/type/__file_old/parameter/optional similarity index 76% rename from cdist/conf/type/__file_py/parameter/optional rename to cdist/conf/type/__file_old/parameter/optional index c696d592..9b98352c 100644 --- a/cdist/conf/type/__file_py/parameter/optional +++ b/cdist/conf/type/__file_old/parameter/optional @@ -3,3 +3,4 @@ group mode owner source +onchange diff --git a/cdist/conf/type/__file_py/explorer/stat b/cdist/conf/type/__file_py/explorer/stat deleted file mode 100755 index 8a917556..00000000 --- a/cdist/conf/type/__file_py/explorer/stat +++ /dev/null @@ -1,56 +0,0 @@ -#!/bin/sh -# -# 2013 Steven Armstrong (steven-cdist armstrong.cc) -# -# 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 . -# - -destination="/$__object_id" - -# nothing to work with, nothing we could do -[ -e "$destination" ] || exit 0 - -os=$("$__explorer/os") -case "$os" in - "freebsd"|"netbsd"|"openbsd") - # FIXME: should be something like this based on man page, but can not test - stat -f "type: %ST -owner: %Du %Su -group: %Dg %Sg -mode: %Op %Sp -size: %Dz -links: %Dl -" "$destination" - ;; - "macosx") - stat -f "type: %HT -owner: %Du %Su -group: %Dg %Sg -mode: %Lp %Sp -size: %Dz -links: %Dl -" "$destination" - ;; - *) - stat --printf="type: %F -owner: %u %U -group: %g %G -mode: %a %A -size: %s -links: %h -" "$destination" - ;; -esac From 1588476a91fb620a0ac80c1254bf703623a7225c Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Tue, 1 Oct 2019 08:14:24 +0200 Subject: [PATCH 010/366] Remove unnecessary files --- cdist/conf/type/__file/parameter/default/state | 1 - cdist/conf/type/__file/parameter/optional | 5 ----- 2 files changed, 6 deletions(-) delete mode 100644 cdist/conf/type/__file/parameter/default/state delete mode 100644 cdist/conf/type/__file/parameter/optional diff --git a/cdist/conf/type/__file/parameter/default/state b/cdist/conf/type/__file/parameter/default/state deleted file mode 100644 index e7f6134f..00000000 --- a/cdist/conf/type/__file/parameter/default/state +++ /dev/null @@ -1 +0,0 @@ -present diff --git a/cdist/conf/type/__file/parameter/optional b/cdist/conf/type/__file/parameter/optional deleted file mode 100644 index c696d592..00000000 --- a/cdist/conf/type/__file/parameter/optional +++ /dev/null @@ -1,5 +0,0 @@ -state -group -mode -owner -source From b7af305636aaba6f1b3872b36382c8fbb802a605 Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Tue, 1 Oct 2019 08:15:01 +0200 Subject: [PATCH 011/366] Align with shell type: implement onchange --- cdist/conf/type/__file/__init__.py | 11 ++++++++++- cdist/conf/type/__file_py | 1 + 2 files changed, 11 insertions(+), 1 deletion(-) create mode 120000 cdist/conf/type/__file_py diff --git a/cdist/conf/type/__file/__init__.py b/cdist/conf/type/__file/__init__.py index 3397b417..bcb525fc 100644 --- a/cdist/conf/type/__file/__init__.py +++ b/cdist/conf/type/__file/__init__.py @@ -52,6 +52,7 @@ class FileType(PythonType): create_file = False upload_file = False set_attributes = False + fire_onchange = False code = [] if state_should == 'present' or state_should == 'exists': if source is None: @@ -72,6 +73,7 @@ class FileType(PythonType): self.die('Source {} does not exist'.format(source)) if create_file or upload_file: set_attributes = True + fire_onchange = True tempfile_template = '{}.cdist.XXXXXXXXXX'.format(destination) destination_upload = self.run_remote( ["mktemp", tempfile_template, ]) @@ -90,6 +92,7 @@ class FileType(PythonType): value_is = self.get_attribute(stat_file, attribute, value_should) if set_attributes or value_should != value_is: + fire_onchange = True code.append(self.set_attribute(attribute, value_should, destination)) @@ -97,9 +100,15 @@ class FileType(PythonType): if typeis == 'file': code.append('rm -f {}'.format(destination)) self.send_message('remove') + fire_onchange = True else: self.die('Unknown state {}'.format(state_should)) + if fire_onchange: + onchange = self.get_parameter('onchange') + if onchange: + code.append(onchange) + return "\n".join(code) def get_args_parser(self): @@ -107,7 +116,7 @@ class FileType(PythonType): argument_default=argparse.SUPPRESS) parser.add_argument('--state', dest='state', action='store', required=False, default='present') - for param in ('group', 'mode', 'owner', 'source'): + for param in ('group', 'mode', 'owner', 'source', 'onchange'): parser.add_argument('--' + param, dest=param, action='store', required=False, default=None) diff --git a/cdist/conf/type/__file_py b/cdist/conf/type/__file_py new file mode 120000 index 00000000..efa910bd --- /dev/null +++ b/cdist/conf/type/__file_py @@ -0,0 +1 @@ +__file \ No newline at end of file From 595cfec57233728feeafbdb3131daeffdb95b778 Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Sat, 30 Nov 2019 23:29:16 +0100 Subject: [PATCH 012/366] Adapt order dependency impl to python types impl --- cdist/__init__.py | 3 +++ cdist/core/manifest.py | 7 ++----- cdist/emulator.py | 5 ++--- 3 files changed, 7 insertions(+), 8 deletions(-) diff --git a/cdist/__init__.py b/cdist/__init__.py index c673b3ba..279fd502 100644 --- a/cdist/__init__.py +++ b/cdist/__init__.py @@ -47,6 +47,9 @@ REMOTE_COPY = "scp -o User=root -q" REMOTE_EXEC = "ssh -o User=root" REMOTE_CMDS_CLEANUP_PATTERN = "ssh -o User=root -O exit -S {}" +ORDER_DEP_STATE_NAME = 'order_dep_state' +TYPEORDER_DEP_NAME = 'typeorder_dep' + class Error(Exception): """Base exception class for this project""" diff --git a/cdist/core/manifest.py b/cdist/core/manifest.py index 16dec465..84bc813f 100644 --- a/cdist/core/manifest.py +++ b/cdist/core/manifest.py @@ -100,9 +100,6 @@ class Manifest(object): """ - ORDER_DEP_STATE_NAME = 'order_dep_state' - TYPEORDER_DEP_NAME = 'typeorder_dep' - def __init__(self, target_host, local, dry_run=False): self.target_host = target_host self.local = local @@ -227,8 +224,8 @@ class Manifest(object): os.remove(os.path.join(self.local.base_path, fname)) except FileNotFoundError: pass - _rm_file(Manifest.ORDER_DEP_STATE_NAME) - _rm_file(Manifest.TYPEORDER_DEP_NAME) + _rm_file(cdist.ORDER_DEP_STATE_NAME) + _rm_file(cdist.TYPEORDER_DEP_NAME) def env_py_type_manifest(self, cdist_object): env = os.environ.copy() diff --git a/cdist/emulator.py b/cdist/emulator.py index 7dc24a83..00060a10 100644 --- a/cdist/emulator.py +++ b/cdist/emulator.py @@ -29,7 +29,6 @@ import sys import cdist from cdist import core from cdist import flock -from cdist.core.manifest import Manifest import cdist.util.python_type_util as pytype_util from cdist.core.pytypes import get_pytype_class import inspect @@ -87,9 +86,9 @@ class Emulator(object): self.typeorder_path = os.path.join(self.global_path, "typeorder") self.typeorder_dep_path = os.path.join(self.global_path, - Manifest.TYPEORDER_DEP_NAME) + cdist.TYPEORDER_DEP_NAME) self.order_dep_state_path = os.path.join(self.global_path, - Manifest.ORDER_DEP_STATE_NAME) + cdist.ORDER_DEP_STATE_NAME) self.type_name = os.path.basename(argv[0]) self.cdist_type = core.CdistType(self.type_base_path, self.type_name) From 77d704113ce7c1065a77b9b01ab3f9954e8b80ca Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Sat, 15 Feb 2020 14:52:10 +0100 Subject: [PATCH 013/366] Update file stat from master --- cdist/conf/type/__file/explorer/stat | 35 ++++++++++++- cdist/conf/type/__file_old/explorer/stat | 64 +++++++++++++++++------- 2 files changed, 80 insertions(+), 19 deletions(-) diff --git a/cdist/conf/type/__file/explorer/stat b/cdist/conf/type/__file/explorer/stat index c772abcf..91c8cc84 100755 --- a/cdist/conf/type/__file/explorer/stat +++ b/cdist/conf/type/__file/explorer/stat @@ -52,6 +52,7 @@ fallback() { # nothing to work with, nothing we could do [ -e "$destination" ] || exit 0 + if ! command -v stat >/dev/null then fallback @@ -68,7 +69,39 @@ group: %Dg %Sg mode: %Lp %Sp size: %Dz links: %Dl - ;; +" "$destination" | awk '/^type/ { print tolower($0); next } { print }' + ;; + solaris) + ls1="$( ls -ld "$destination" )" + ls2="$( ls -ldn "$destination" )" + + if [ -f "$__object/parameter/mode" ] + then mode_should="$( cat "$__object/parameter/mode" )" + fi + + # yes, it is ugly hack, but if you know better way... + if [ -z "$( find "$destination" -perm "$mode_should" )" ] + then octets=888 + else octets="$( echo "$mode_should" | sed 's/^0//' )" + fi + + case "$( echo "$ls1" | cut -c1-1 )" in + -) echo 'type: regular file' ;; + d) echo 'type: directory' ;; + esac + + echo "owner: $( echo "$ls2" \ + | awk '{print $3}' ) $( echo "$ls1" \ + | awk '{print $3}' )" + + echo "group: $( echo "$ls2" \ + | awk '{print $4}' ) $( echo "$ls1" \ + | awk '{print $4}' )" + + echo "mode: $octets $( echo "$ls1" | awk '{print $1}' )" + echo "size: $( echo "$ls1" | awk '{print $5}' )" + echo "links: $( echo "$ls1" | awk '{print $2}' )" + ;; *) # NOTE: Do not use --printf here as it is not supported by BusyBox stat. # NOTE: BusyBox's stat might not support the "-c" option, in which case diff --git a/cdist/conf/type/__file_old/explorer/stat b/cdist/conf/type/__file_old/explorer/stat index 13c1c208..91c8cc84 100755 --- a/cdist/conf/type/__file_old/explorer/stat +++ b/cdist/conf/type/__file_old/explorer/stat @@ -2,6 +2,7 @@ # # 2013 Steven Armstrong (steven-cdist armstrong.cc) # 2019 Nico Schottelius (nico-cdist at schottelius.org) +# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) # # This file is part of cdist. # @@ -21,29 +22,54 @@ destination="/$__object_id" +fallback() { + # Fallback: Patch the output together, manually. + + ls_line=$(ls -ldn "$destination") + + uid=$(echo "$ls_line" | awk '{ print $3 }') + gid=$(echo "$ls_line" | awk '{ print $4 }') + + owner=$(awk -F: -v uid="$uid" '$3 == uid { print $1; f=1 } END { if (!f) print "UNKNOWN" }' /etc/passwd) + group=$(awk -F: -v uid="$uid" '$3 == uid { print $1; f=1 } END { if (!f) print "UNKNOWN" }' /etc/group) + + mode_text=$(echo "$ls_line" | awk '{ print $1 }') + mode=$(echo "$mode_text" | awk '{ k=0; for (i=0; i<=8; i++) k += ((substr($1, i+2, 1) ~ /[rwx]/) * 2^(8-i)); printf("%0o", k) }') + + size=$(echo "$ls_line" | awk '{ print $5 }') + links=$(echo "$ls_line" | awk '{ print $2 }') + + printf 'type: %s\nowner: %d %s\ngroup: %d %s\nmode: %s %s\nsize: %d\nlinks: %d\n' \ + "$("$__type_explorer/type")" \ + "$uid" "$owner" \ + "$gid" "$group" \ + "$mode" "$mode_text" \ + "$size" \ + "$links" +} + + # nothing to work with, nothing we could do [ -e "$destination" ] || exit 0 -os=$("$__explorer/os") -case "$os" in - "freebsd"|"netbsd"|"openbsd"|"macosx") + +if ! command -v stat >/dev/null +then + fallback + exit +fi + + +case $("$__explorer/os") +in + freebsd|netbsd|openbsd|macosx) stat -f "type: %HT owner: %Du %Su group: %Dg %Sg mode: %Lp %Sp size: %Dz links: %Dl -" "$destination" | awk '/^type/ { print tolower($0); next; } { print; }' - ;; - alpine) - # busybox stat - stat -c "type: %F -owner: %u %U -group: %g %G -mode: %a %A -size: %s -links: %h -" "$destination" +" "$destination" | awk '/^type/ { print tolower($0); next } { print }' ;; solaris) ls1="$( ls -ld "$destination" )" @@ -77,12 +103,14 @@ links: %h echo "links: $( echo "$ls1" | awk '{print $2}' )" ;; *) - stat --printf="type: %F + # NOTE: Do not use --printf here as it is not supported by BusyBox stat. + # NOTE: BusyBox's stat might not support the "-c" option, in which case + # we fall through to the shell fallback. + stat -c "type: %F owner: %u %U group: %g %G mode: %a %A size: %s -links: %h -" "$destination" - ;; +links: %h" "$destination" 2>/dev/null || fallback + ;; esac From d26c36914a4fa9889af57f549cb2854968663a0b Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Mon, 27 Jul 2020 10:55:28 +0200 Subject: [PATCH 014/366] [__timezone] Make type singleton --- cdist/conf/type/__timezone/gencode-remote | 2 +- cdist/conf/type/__timezone/man.rst | 22 ++++++++++++------- cdist/conf/type/__timezone/manifest | 2 +- cdist/conf/type/__timezone/parameter/required | 1 + cdist/conf/type/__timezone/singleton | 0 5 files changed, 17 insertions(+), 10 deletions(-) create mode 100644 cdist/conf/type/__timezone/parameter/required create mode 100644 cdist/conf/type/__timezone/singleton diff --git a/cdist/conf/type/__timezone/gencode-remote b/cdist/conf/type/__timezone/gencode-remote index 5299f548..b685c990 100755 --- a/cdist/conf/type/__timezone/gencode-remote +++ b/cdist/conf/type/__timezone/gencode-remote @@ -22,7 +22,7 @@ # This type allows to configure the desired localtime timezone. timezone_is=$(cat "$__object/explorer/timezone_is") -timezone_should="$__object_id" +timezone_should=$(cat "$__object/parameter/tz") os=$(cat "$__global/explorer/os") if [ "$timezone_is" = "$timezone_should" ]; then diff --git a/cdist/conf/type/__timezone/man.rst b/cdist/conf/type/__timezone/man.rst index 8a945c16..6012c552 100644 --- a/cdist/conf/type/__timezone/man.rst +++ b/cdist/conf/type/__timezone/man.rst @@ -14,7 +14,8 @@ This type creates a symlink (/etc/localtime) to the selected timezone REQUIRED PARAMETERS ------------------- -None. +tz + The name of timezone to set. OPTIONAL PARAMETERS @@ -27,19 +28,24 @@ EXAMPLES .. code-block:: sh - #Set up Europe/Andorra as our timezone. - __timezone Europe/Andorra + # Set up Europe/Andorra as our timezone. + __timezone --tz Europe/Andorra - #Set up US/Central as our timezone. - __timezone US/Central + # Set up US/Central as our timezone. + __timezone --tz US/Central AUTHORS ------- -Ramon Salvadó +| Steven Armstrong +| Nico Schottelius +| Ramon Salvadó +| Dennis Camera COPYING ------- -Free use of this software is -granted under the terms of the GNU General Public License version 3 (GPLv3). +Copyright \(C) 2012-2020 the `AUTHORS`_. 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. diff --git a/cdist/conf/type/__timezone/manifest b/cdist/conf/type/__timezone/manifest index 3d28ccba..0eb7fb9c 100755 --- a/cdist/conf/type/__timezone/manifest +++ b/cdist/conf/type/__timezone/manifest @@ -22,7 +22,7 @@ # # This type allows to configure the desired localtime timezone. -timezone="$__object_id" +timezone=$(cat "$__object/parameter/tz") os=$(cat "$__global/explorer/os") case "$os" in diff --git a/cdist/conf/type/__timezone/parameter/required b/cdist/conf/type/__timezone/parameter/required new file mode 100644 index 00000000..975445e4 --- /dev/null +++ b/cdist/conf/type/__timezone/parameter/required @@ -0,0 +1 @@ +tz diff --git a/cdist/conf/type/__timezone/singleton b/cdist/conf/type/__timezone/singleton new file mode 100644 index 00000000..e69de29b From c053a2c4a000b0c15df186ef2e067ea68ec9499c Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Wed, 29 Jul 2020 11:31:12 +0200 Subject: [PATCH 015/366] Fix building man pages Resolves #830. --- Makefile | 4 ++-- cdist/conf/type/__openldap_server/man.rst | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Makefile b/Makefile index f89ac1e7..3712511c 100644 --- a/Makefile +++ b/Makefile @@ -81,7 +81,7 @@ version: } # Manpages #3: generic part -man: version $(MANTYPES) $(DOCSREF) +man: version configskel $(MANTYPES) $(DOCSREF) $(DOCSTYPESREF) $(SPHINXM) html: version configskel $(MANTYPES) $(DOCSREF) $(DOCSTYPESREF) @@ -104,7 +104,7 @@ DOTMANTYPES=$(subst /man.rst,.rst,$(DOTMANTYPEPREFIX)) $(DOTMAN7DSTDIR)/cdist-type%.rst: $(DOTTYPEDIR)/%/man.rst ln -sf "$^" $@ -dotman: version $(DOTMANTYPES) +dotman: version configskel $(DOTMANTYPES) $(DOCSREF) $(DOCSTYPESREF) $(SPHINXM) ################################################################################ diff --git a/cdist/conf/type/__openldap_server/man.rst b/cdist/conf/type/__openldap_server/man.rst index a96c7dad..fa714ec0 100644 --- a/cdist/conf/type/__openldap_server/man.rst +++ b/cdist/conf/type/__openldap_server/man.rst @@ -31,8 +31,8 @@ manager-password-hash Generate e.g. with: `slappasswd -s weneedgoodsecurity`. See `slappasswd(8C)`, `slapd.conf(5)`. TODO: implement this: http://blog.adamsbros.org/2015/06/09/openldap-ssha-salted-hashes-by-hand/ - to derive from the manager-password parameter and ensure idempotency (care with salts). - At that point, manager-password-hash should be deprecated and ignored. + to derive from the manager-password parameter and ensure idempotency (care with salts). + At that point, manager-password-hash should be deprecated and ignored. serverid The server for the directory. From d37d2dc307d066f4d5f1bbbb93e0480965dfe3b8 Mon Sep 17 00:00:00 2001 From: Ander Punnar Date: Sun, 2 Aug 2020 13:53:38 +0300 Subject: [PATCH 016/366] [__unpack] add parameter --tar-extra-args --- cdist/conf/type/__unpack/gencode-remote | 7 +++++++ cdist/conf/type/__unpack/man.rst | 11 +++++++++++ cdist/conf/type/__unpack/parameter/optional | 1 + 3 files changed, 19 insertions(+) diff --git a/cdist/conf/type/__unpack/gencode-remote b/cdist/conf/type/__unpack/gencode-remote index 45c7173a..3b7a19a7 100755 --- a/cdist/conf/type/__unpack/gencode-remote +++ b/cdist/conf/type/__unpack/gencode-remote @@ -23,6 +23,13 @@ case "$src" in cmd="$cmd --strip-components=$tar_strip" fi + + if [ -f "$__object/parameter/tar-extra-args" ] + then + tar_extra_args="$( cat "$__object/parameter/tar-extra-args" )" + + cmd="$cmd $tar_extra_args" + fi ;; *.7z) case "$os" in diff --git a/cdist/conf/type/__unpack/man.rst b/cdist/conf/type/__unpack/man.rst index 8fe96e43..bd0603cf 100644 --- a/cdist/conf/type/__unpack/man.rst +++ b/cdist/conf/type/__unpack/man.rst @@ -33,6 +33,10 @@ sum-file tar-strip Tarball specific. See ``man tar`` for ``--strip-components``. +tar-extra-args + Tarball sepcific. Append additional arguments to ``tar`` command. + See ``man tar`` for possible arguments. + OPTIONAL BOOLEAN PARAMETERS --------------------------- @@ -65,6 +69,13 @@ EXAMPLES --preserve-archive \ --destination /opt/cpma/server + # example usecase for --tar-* args + __unpack /root/strelaysrv.tar.gz \ + --preserve-archive \ + --destination /usr/local/bin \ + --tar-strip 1 \ + --tar-extra-args '--wildcards "*/strelaysrv"' + AUTHORS ------- diff --git a/cdist/conf/type/__unpack/parameter/optional b/cdist/conf/type/__unpack/parameter/optional index d136dd0c..da26ca63 100644 --- a/cdist/conf/type/__unpack/parameter/optional +++ b/cdist/conf/type/__unpack/parameter/optional @@ -1,2 +1,3 @@ sum-file tar-strip +tar-extra-args From 935f2395bc8f02d373f06d1e22acad813403ead2 Mon Sep 17 00:00:00 2001 From: Ander Punnar Date: Sun, 2 Aug 2020 13:54:30 +0300 Subject: [PATCH 017/366] [__locale_system] fix for debian and ubuntu ubuntu 6.10 and debian etch are 10+ years old and EOL. rather than preserving compatibility I'll just remove it. while /etc/environment works too, correct place is /etc/default/locale (as it was before breaking change). also /etc/debian_version (os_version explorer) may contain minor version with dot (10.5) or string (bullseye/sid). --- cdist/conf/type/__locale_system/manifest | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/cdist/conf/type/__locale_system/manifest b/cdist/conf/type/__locale_system/manifest index e4286ef6..f7d75387 100755 --- a/cdist/conf/type/__locale_system/manifest +++ b/cdist/conf/type/__locale_system/manifest @@ -40,27 +40,13 @@ os=$(cat "$__global/explorer/os") case $os in debian) - os_version=$(cat "${__global}/explorer/os_version") - if expr "${os_version}" '>=' 4 >/dev/null - then - # Debian 4 (etch) and later - locale_conf="/etc/default/locale" - else - locale_conf="/etc/environment" - fi + locale_conf="/etc/default/locale" ;; devuan) locale_conf="/etc/default/locale" ;; ubuntu) - os_version=$(cat "${__global}/explorer/os_version") - if expr "${os_version}" '>=' 6.10 >/dev/null - then - # Ubuntu 6.10 (edgy) and later - locale_conf="/etc/default/locale" - else - locale_conf="/etc/environment" - fi + locale_conf="/etc/default/locale" ;; archlinux) locale_conf="/etc/locale.conf" From 885d5a58f40e5adfd6aa616712e4c7246eca7880 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Sun, 2 Aug 2020 16:49:01 +0200 Subject: [PATCH 018/366] [type/__locale_system] Fix floating point version comparison --- cdist/conf/type/__locale_system/manifest | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/cdist/conf/type/__locale_system/manifest b/cdist/conf/type/__locale_system/manifest index e4286ef6..521f1007 100755 --- a/cdist/conf/type/__locale_system/manifest +++ b/cdist/conf/type/__locale_system/manifest @@ -24,6 +24,9 @@ # Configure system-wide locale by modifying i18n file. # +bccmp() { test "$(bc)" -gt 0; } + + key=$__object_id onchange_cmd= # none, by default quote_value=false @@ -41,7 +44,7 @@ case $os in debian) os_version=$(cat "${__global}/explorer/os_version") - if expr "${os_version}" '>=' 4 >/dev/null + if printf '%f >= 4\n' "${os_version}" | bccmp then # Debian 4 (etch) and later locale_conf="/etc/default/locale" @@ -54,7 +57,7 @@ in ;; ubuntu) os_version=$(cat "${__global}/explorer/os_version") - if expr "${os_version}" '>=' 6.10 >/dev/null + if printf '%f >= 6.10\n' "${os_version}" | bccmp then # Ubuntu 6.10 (edgy) and later locale_conf="/etc/default/locale" @@ -68,7 +71,7 @@ in centos|redhat|scientific) # shellcheck source=/dev/null version_id=$(. "${__global}/explorer/os_release" && echo "${VERSION_ID:-0}") - if expr "${version_id}" '>=' 7 >/dev/null + if printf '%f >= 7\n' "${version_id}" | bccmp then locale_conf="/etc/locale.conf" else @@ -78,7 +81,7 @@ in fedora) # shellcheck source=/dev/null version_id=$(. "${__global}/explorer/os_release" && echo "${VERSION_ID:-0}") - if expr "${version_id}" '>=' 18 >/dev/null + if printf '%f >= 18\n' "${version_id}" | bccmp then locale_conf="/etc/locale.conf" quote_value=false @@ -113,7 +116,8 @@ in locale_conf="/etc/default/init" locale_conf_group="sys" - if expr "$(cat "${__global}/explorer/os_version")" '>=' 5.11 >/dev/null + os_version=$(cat "${__global}/explorer/os_version") + if printf '%f >= 5.11\n' "${os_version}" | bccmp then # mode on Oracle Solaris 11 is actually 0444, # but the write bit makes sense, IMO From 71710fa00a86e3dcda6be82b959956cb88ba9902 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Sun, 2 Aug 2020 20:17:00 +0200 Subject: [PATCH 019/366] [type/__locale_system] Implement "proper" version comparison Proper in the sense that it can handle all numeric version numbers even if they are not floating point (e.g. 16.04.6). --- cdist/conf/type/__locale_system/manifest | 28 ++++++++++++++++-------- 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/cdist/conf/type/__locale_system/manifest b/cdist/conf/type/__locale_system/manifest index 521f1007..88eadb31 100755 --- a/cdist/conf/type/__locale_system/manifest +++ b/cdist/conf/type/__locale_system/manifest @@ -24,7 +24,20 @@ # Configure system-wide locale by modifying i18n file. # -bccmp() { test "$(bc)" -gt 0; } +version_ge() { + awk -F '[^0-9.]' -v target="${1:?}" ' + function max(x, y) { return x > y ? x : y } + BEGIN { + getline + nx = split($1, x, ".") + ny = split(target, y, ".") + for (i = 1; i <= max(nx, ny); ++i) { + diff = int(x[i]) - int(y[i]) + if (diff == 0) continue + exit (diff < 0) + } + }' +} key=$__object_id @@ -43,8 +56,7 @@ os=$(cat "$__global/explorer/os") case $os in debian) - os_version=$(cat "${__global}/explorer/os_version") - if printf '%f >= 4\n' "${os_version}" | bccmp + if version_ge 4 <"${__global}/explorer/os_version" then # Debian 4 (etch) and later locale_conf="/etc/default/locale" @@ -56,8 +68,7 @@ in locale_conf="/etc/default/locale" ;; ubuntu) - os_version=$(cat "${__global}/explorer/os_version") - if printf '%f >= 6.10\n' "${os_version}" | bccmp + if version_ge 6.10 <"${__global}/explorer/os_version" then # Ubuntu 6.10 (edgy) and later locale_conf="/etc/default/locale" @@ -71,7 +82,7 @@ in centos|redhat|scientific) # shellcheck source=/dev/null version_id=$(. "${__global}/explorer/os_release" && echo "${VERSION_ID:-0}") - if printf '%f >= 7\n' "${version_id}" | bccmp + if echo "${version_id}" | version_ge 7 then locale_conf="/etc/locale.conf" else @@ -81,7 +92,7 @@ in fedora) # shellcheck source=/dev/null version_id=$(. "${__global}/explorer/os_release" && echo "${VERSION_ID:-0}") - if printf '%f >= 18\n' "${version_id}" | bccmp + if echo "${version_id}" | version_ge 18 then locale_conf="/etc/locale.conf" quote_value=false @@ -116,8 +127,7 @@ in locale_conf="/etc/default/init" locale_conf_group="sys" - os_version=$(cat "${__global}/explorer/os_version") - if printf '%f >= 5.11\n' "${os_version}" | bccmp + if version_ge 5.11 <"${__global}/explorer/os_version" then # mode on Oracle Solaris 11 is actually 0444, # but the write bit makes sense, IMO From 7b480f4293b84a97850430a9436e28c66ccf5736 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Sun, 2 Aug 2020 22:08:14 +0200 Subject: [PATCH 020/366] [type/__locale_system] Fix version extraction for SuSE --- cdist/conf/type/__locale_system/manifest | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/cdist/conf/type/__locale_system/manifest b/cdist/conf/type/__locale_system/manifest index 88eadb31..4b996ebc 100755 --- a/cdist/conf/type/__locale_system/manifest +++ b/cdist/conf/type/__locale_system/manifest @@ -163,7 +163,13 @@ in key="export ${__object_id}" ;; suse) - os_version=$(cat "${__global}/explorer/os_version") + if test -s "${__global}/explorer/os_release" + then + # shellcheck source=/dev/null + os_version=$(. "${__global}/explorer/os_release" && echo "${VERSION}") + else + os_version=$(sed -n 's/^VERSION\ *=\ *//p' "${__global}/explorer/os_version") + fi os_major=$(expr "${os_version}" : '\([0-9]\{1,\}\)') # https://documentation.suse.com/sles/15-SP2/html/SLES-all/cha-suse.html#sec-suse-l10n From b370b70ff4ccc1c6efca97f61f220a5ce0b191da Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Sun, 2 Aug 2020 22:13:41 +0200 Subject: [PATCH 021/366] [explorer/os] Fix OS detection for openSUSE All distros with ID_LIKE suse should be treated as "suse". My openSUSE Leap 15.1 installation has: ID_LIKE="suse opensuse" This patch doesn't require a strict "suse" value but only the word suse to be in the list. --- cdist/conf/explorer/os | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/cdist/conf/explorer/os b/cdist/conf/explorer/os index 2d2aede6..8f54d9c8 100755 --- a/cdist/conf/explorer/os +++ b/cdist/conf/explorer/os @@ -144,7 +144,8 @@ esac if [ -f /etc/os-release ]; then # after sles15, suse don't provide an /etc/SuSE-release anymore, but there is almost no difference between sles and opensuse leap, so call it suse - if grep -q ^ID_LIKE=\"suse\" /etc/os-release 2>/dev/null; then + if (. /etc/os-release && echo "${ID_LIKE}" | grep -q '\(^\|\ \)suse\($\|\ \)') + then echo suse exit 0 fi From 17ab4bd80c9c9dda2fa4f8ccde5ee454f7b5ba2d Mon Sep 17 00:00:00 2001 From: Joachim Desroches Date: Thu, 6 Aug 2020 11:45:05 +0200 Subject: [PATCH 022/366] Add Alpine Linux as supported for __filesystem. --- cdist/conf/type/__filesystem/explorer/lsblk | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/cdist/conf/type/__filesystem/explorer/lsblk b/cdist/conf/type/__filesystem/explorer/lsblk index 9ae544ac..9be3c575 100644 --- a/cdist/conf/type/__filesystem/explorer/lsblk +++ b/cdist/conf/type/__filesystem/explorer/lsblk @@ -18,16 +18,16 @@ # along with cdist. If not, see . # -os=$("$__explorer/os") +os=$("${__explorer:?}/os") -if [ -f "$__object/parameter/device" ]; then +if [ -f "${__object:?}/parameter/device" ]; then blkdev="$(cat "$__object/parameter/device")" else - blkdev="$__object_id" + blkdev="${__object_id:?}" fi case "$os" in - centos|fedora|redhat|suse|gentoo) + alpine|centos|fedora|redhat|suse|gentoo) if [ ! -x "$(command -v lsblk)" ]; then echo "lsblk is required for __filesystem type" >&2 exit 1 From 74dd47c8c352cc3de3b5684bbe90520f61d95f37 Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Sat, 15 Aug 2020 21:11:43 +0200 Subject: [PATCH 023/366] ++changelog --- docs/changelog | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/docs/changelog b/docs/changelog index 269a2049..2c09bfea 100644 --- a/docs/changelog +++ b/docs/changelog @@ -1,6 +1,12 @@ Changelog --------- +next: + * Type __locale_system: Fix for debian and ubuntu (Ander Punnar) + * Type __unpack: Add --tar-extra-args parameter (Ander Punnar) + * Explorer os: Fix OS detection for openSUSE (Dennis Camera) + * Type __filesystem: Support Alpine Linux (Joachim Desroches) + 6.7.0: 2020-07-28 * Delete deprecated type: __pf_apply (Darko Poljak) * New type: __download (Ander Punnar) From 8f94a226c7f0b875987e25e57682414204380fa2 Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Sat, 15 Aug 2020 21:54:07 +0200 Subject: [PATCH 024/366] ++changelog --- docs/changelog | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog b/docs/changelog index 2c09bfea..9e44cc4d 100644 --- a/docs/changelog +++ b/docs/changelog @@ -6,6 +6,7 @@ next: * Type __unpack: Add --tar-extra-args parameter (Ander Punnar) * Explorer os: Fix OS detection for openSUSE (Dennis Camera) * Type __filesystem: Support Alpine Linux (Joachim Desroches) + * Type __locale_system: Fix version comparison (Dennis Camera) 6.7.0: 2020-07-28 * Delete deprecated type: __pf_apply (Darko Poljak) From 6fed1785298eb506e7ea6cd177efe7d96658769f Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Mon, 17 Aug 2020 09:27:48 +0200 Subject: [PATCH 025/366] [explorer/os_version] Convert Debian sid to version number. Conversion of Debian sid to versions is done based on Debian codenames. The version number is the version number of the final release - 0.01. It is unknown if Debian < 4.0 has any sort of version information available (apart from maybe checking base-files package version). But I don't think any of these systems are still alive, so I think going with 3.99 is fine for those. --- cdist/conf/explorer/os_version | 29 +++++++++++++++++++++++++++-- 1 file changed, 27 insertions(+), 2 deletions(-) diff --git a/cdist/conf/explorer/os_version b/cdist/conf/explorer/os_version index 1d54ea60..a7b1d3bc 100755 --- a/cdist/conf/explorer/os_version +++ b/cdist/conf/explorer/os_version @@ -31,7 +31,32 @@ case "$("$__explorer/os")" in cat /etc/arch-release ;; debian) - cat /etc/debian_version + debian_version=$(cat /etc/debian_version) + case $debian_version + in + testing/unstable) + # previous to Debian 4.0 testing/unstable was used + # cf. https://metadata.ftp-master.debian.org/changelogs/main/b/base-files/base-files_11_changelog + echo 3.99 + ;; + */sid) + # sid versions don't have a number, so we decode by codename: + case $(expr "$debian_version" : '\([a-z]\{1,\}\)/') + in + bullseye) echo 10.99 ;; + buster) echo 9.99 ;; + stretch) echo 8.99 ;; + jessie) echo 7.99 ;; + wheezy) echo 6.99 ;; + squeeze) echo 5.99 ;; + lenny) echo 4.99 ;; + *) exit 1 + esac + ;; + *) + echo "$debian_version" + ;; + esac ;; devuan) cat /etc/devuan_version @@ -73,4 +98,4 @@ case "$("$__explorer/os")" in alpine) cat /etc/alpine-release ;; -esac \ No newline at end of file +esac From 502d75304707704d3ba73374701394b98d259506 Mon Sep 17 00:00:00 2001 From: Ander Punnar Date: Tue, 18 Aug 2020 00:46:07 +0300 Subject: [PATCH 026/366] [__unpack] add --onchange --- cdist/conf/type/__unpack/gencode-remote | 5 +++++ cdist/conf/type/__unpack/man.rst | 3 +++ cdist/conf/type/__unpack/parameter/optional | 1 + 3 files changed, 9 insertions(+) diff --git a/cdist/conf/type/__unpack/gencode-remote b/cdist/conf/type/__unpack/gencode-remote index 3b7a19a7..c4451f73 100755 --- a/cdist/conf/type/__unpack/gencode-remote +++ b/cdist/conf/type/__unpack/gencode-remote @@ -80,3 +80,8 @@ if [ ! -f "$__object/parameter/preserve-archive" ] then echo "rm -f '$src'" fi + +if [ -f "$__object/parameter/onchange" ] +then + cat "$__object/parameter/onchange" +fi diff --git a/cdist/conf/type/__unpack/man.rst b/cdist/conf/type/__unpack/man.rst index bd0603cf..daa03814 100644 --- a/cdist/conf/type/__unpack/man.rst +++ b/cdist/conf/type/__unpack/man.rst @@ -50,6 +50,9 @@ backup-destination preserve-archive Don't delete archive after unpacking. +onchange + Execute this command after unpack. + EXAMPLES -------- diff --git a/cdist/conf/type/__unpack/parameter/optional b/cdist/conf/type/__unpack/parameter/optional index da26ca63..d846ac75 100644 --- a/cdist/conf/type/__unpack/parameter/optional +++ b/cdist/conf/type/__unpack/parameter/optional @@ -1,3 +1,4 @@ sum-file tar-strip tar-extra-args +onchange From d239169c4f91a4ce33be170ed29b08299c51234f Mon Sep 17 00:00:00 2001 From: Ander Punnar Date: Tue, 18 Aug 2020 00:48:58 +0300 Subject: [PATCH 027/366] [__download] fix manual: onchange parameter in wrong section --- cdist/conf/type/__download/man.rst | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cdist/conf/type/__download/man.rst b/cdist/conf/type/__download/man.rst index 6ec0b19a..eb3ac971 100644 --- a/cdist/conf/type/__download/man.rst +++ b/cdist/conf/type/__download/man.rst @@ -30,9 +30,6 @@ sum By default output of ``cksum`` without filename is expected. Other hash formats supported with prefixes: ``md5:``, ``sha1:`` and ``sha256:``. -onchange - Execute this command after download. - OPTIONAL PARAMETERS ------------------- @@ -54,6 +51,9 @@ cmd-sum format specification ``%s`` which will become destination. For example: ``md5sum '%s' | awk '{print $1}'``. +onchange + Execute this command after download. + EXAMPLES -------- From ba26a437be4d8034037e7a6eb42382aee01f829e Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Tue, 18 Aug 2020 11:06:19 +0200 Subject: [PATCH 028/366] ++changelog --- docs/changelog | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/changelog b/docs/changelog index 9e44cc4d..a3e4167a 100644 --- a/docs/changelog +++ b/docs/changelog @@ -7,6 +7,9 @@ next: * Explorer os: Fix OS detection for openSUSE (Dennis Camera) * Type __filesystem: Support Alpine Linux (Joachim Desroches) * Type __locale_system: Fix version comparison (Dennis Camera) + * Type __unpack: Add --onchange parameter (Ander Punnar) + * Type __download: Fix manual (Ander Punnar) + * Explorer os_version: Convert Debian sid to version number (Dennis Camera) 6.7.0: 2020-07-28 * Delete deprecated type: __pf_apply (Darko Poljak) From c17541f24cf118b0989a5899cab3a439ed534a46 Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Sat, 22 Aug 2020 19:53:42 +0200 Subject: [PATCH 029/366] Expand and split by consecutive require delimiters Resolves #832. --- cdist/emulator.py | 6 +++++- cdist/test/emulator/__init__.py | 10 ++++++++++ docs/src/cdist-manifest.rst | 3 ++- 3 files changed, 17 insertions(+), 2 deletions(-) diff --git a/cdist/emulator.py b/cdist/emulator.py index 9fe84056..60b94880 100644 --- a/cdist/emulator.py +++ b/cdist/emulator.py @@ -25,6 +25,7 @@ import argparse import logging import os import sys +import re import cdist from cdist import core @@ -389,12 +390,15 @@ class Emulator: if "require" in self.env: requirements = self.env['require'] self.log.debug("reqs = " + requirements) - for requirement in requirements.split(" "): + for requirement in self._parse_require(requirements): # Ignore empty fields - probably the only field anyway if len(requirement) == 0: continue self.record_requirement(requirement) + def _parse_require(self, require): + return re.split(r'[ \t\n]+', require) + def record_auto_requirements(self): """An object shall automatically depend on all objects that it defined in it's type manifest. diff --git a/cdist/test/emulator/__init__.py b/cdist/test/emulator/__init__.py index e375676c..befd7b57 100644 --- a/cdist/test/emulator/__init__.py +++ b/cdist/test/emulator/__init__.py @@ -685,6 +685,16 @@ class EmulatorAlreadyExistingRequirementsWarnTestCase(test.CdistTestCase): self.env['require'] = '__directory/spam' emu = emulator.Emulator(argv, env=self.env) + def test_parse_require(self): + require = " \t \n \t\t\n\t\na\tb\nc d \te\t\nf\ng\t " + expected = ['', 'a', 'b', 'c', 'd', 'e', 'f', 'g', '', ] + + argv = ['__directory', 'spam'] + emu = emulator.Emulator(argv, env=self.env) + requirements = emu._parse_require(require) + + self.assertEqual(expected, requirements) + if __name__ == '__main__': import unittest diff --git a/docs/src/cdist-manifest.rst b/docs/src/cdist-manifest.rst index 5dbca479..2e49a721 100644 --- a/docs/src/cdist-manifest.rst +++ b/docs/src/cdist-manifest.rst @@ -95,7 +95,8 @@ Dependencies ------------ If you want to describe that something requires something else, just setup the variable "require" to contain the requirements. Multiple -requirements can be added white space separated. +requirements can be added separated with (optionally consecutive) +delimiters including space, tab and newline. :: From b5a40eb0d1c8990f0feb8e918a95e54e4fd35d98 Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Thu, 27 Aug 2020 12:25:11 +0200 Subject: [PATCH 030/366] ++changelog --- docs/changelog | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog b/docs/changelog index a3e4167a..eda77a12 100644 --- a/docs/changelog +++ b/docs/changelog @@ -10,6 +10,7 @@ next: * Type __unpack: Add --onchange parameter (Ander Punnar) * Type __download: Fix manual (Ander Punnar) * Explorer os_version: Convert Debian sid to version number (Dennis Camera) + * Core: Expand require delimiter characters, split by consecutive delimiters (Darko Poljak) 6.7.0: 2020-07-28 * Delete deprecated type: __pf_apply (Darko Poljak) From b1375464cc7507b38dd02f2df52c75489837d129 Mon Sep 17 00:00:00 2001 From: Matthias Stecher Date: Sun, 6 Sep 2020 13:24:58 +0200 Subject: [PATCH 031/366] __systemd_service: fix manpage typos --- cdist/conf/type/__systemd_service/man.rst | 25 +++++++++++++++-------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/cdist/conf/type/__systemd_service/man.rst b/cdist/conf/type/__systemd_service/man.rst index 7eca398b..cd14c985 100644 --- a/cdist/conf/type/__systemd_service/man.rst +++ b/cdist/conf/type/__systemd_service/man.rst @@ -1,9 +1,10 @@ -cdist-type__systemd-service(7) +cdist-type__systemd_service(7) ============================== NAME ---- -cdist-type__systemd-service - Controls a systemd service state +cdist-type__systemd_service - Controls a systemd service state + DESCRIPTION ----------- @@ -14,11 +15,12 @@ service after configuration applied or shutdown one service. The activation or deactivation is out of scope. Look for the :strong:`cdist-type__systemd_util`\ (7) type instead. + REQUIRED PARAMETERS ------------------- - None. + OPTIONAL PARAMETERS ------------------- @@ -31,12 +33,12 @@ state running Service should run (default) - stoppend - Service should stopped + stopped + Service should be stopped action Executes an action on on the service. It will only execute it if the - service keeps the state **running**. There are following actions, where: + service keeps the state ``running``. There are following actions, where: reload Reloads the service @@ -48,11 +50,12 @@ BOOLEAN PARAMETERS ------------------ if-required - Only execute the action if minimum one required type outputs a message to - **$__messages_out**. Through this, the action should only executed if a + Only execute the action if at minimum one required type outputs a message + to ``$__messages_out``. Through this, the action should only executed if a dependency did something. The action will not executed if no dependencies given. + MESSAGES -------- @@ -68,12 +71,14 @@ restart reload Reloaded the service + ABORTS ------ Aborts in following cases: systemd or the service does not exist + EXAMPLES -------- .. code-block:: sh @@ -95,13 +100,15 @@ EXAMPLES # reload the service for a modified configuration file # only reloads the service if the file really changed - require="__config_file/etc/foo.conf" __systemd_service foo \ + require="__file/etc/foo.conf" __systemd_service foo \ --action reload --if-required + AUTHORS ------- Matthias Stecher + COPYRIGHT --------- Copyright \(C) 2020 Matthias Stecher. You can redistribute it From 6b262a61c19234f6450cef11f715020dcd5f40d5 Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Thu, 10 Sep 2020 13:24:58 +0200 Subject: [PATCH 032/366] ++changelog --- docs/changelog | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/changelog b/docs/changelog index eda77a12..e4ec3fdd 100644 --- a/docs/changelog +++ b/docs/changelog @@ -11,6 +11,8 @@ next: * Type __download: Fix manual (Ander Punnar) * Explorer os_version: Convert Debian sid to version number (Dennis Camera) * Core: Expand require delimiter characters, split by consecutive delimiters (Darko Poljak) + * Type __timezone: Make singleton (Dennis Camera) + * Type __systemd_service: Fix manpage typos (Matthias Stecher) 6.7.0: 2020-07-28 * Delete deprecated type: __pf_apply (Darko Poljak) From 53b91adbd8be4d858f1f4da3389b63babc85db0c Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Fri, 11 Sep 2020 14:19:45 +0200 Subject: [PATCH 033/366] Fix shellcheck --- cdist/conf/explorer/os | 1 + 1 file changed, 1 insertion(+) diff --git a/cdist/conf/explorer/os b/cdist/conf/explorer/os index 8f54d9c8..46d87f3e 100755 --- a/cdist/conf/explorer/os +++ b/cdist/conf/explorer/os @@ -144,6 +144,7 @@ esac if [ -f /etc/os-release ]; then # after sles15, suse don't provide an /etc/SuSE-release anymore, but there is almost no difference between sles and opensuse leap, so call it suse + # shellcheck disable=SC1091 if (. /etc/os-release && echo "${ID_LIKE}" | grep -q '\(^\|\ \)suse\($\|\ \)') then echo suse From 2885c6a24838956167ae6c33ebf87067e8790083 Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Fri, 11 Sep 2020 14:15:26 +0200 Subject: [PATCH 034/366] Release 6.8.0 --- docs/changelog | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/changelog b/docs/changelog index e4ec3fdd..7a0d050d 100644 --- a/docs/changelog +++ b/docs/changelog @@ -1,7 +1,7 @@ Changelog --------- -next: +6.8.0: 2020-09-11 * Type __locale_system: Fix for debian and ubuntu (Ander Punnar) * Type __unpack: Add --tar-extra-args parameter (Ander Punnar) * Explorer os: Fix OS detection for openSUSE (Dennis Camera) From decc0ad54dffbafcd7d3644c28ae4d149926e612 Mon Sep 17 00:00:00 2001 From: Ander Punnar Date: Fri, 18 Sep 2020 19:33:36 +0300 Subject: [PATCH 035/366] [__package_pip] detect pip binary --- cdist/conf/type/__package_pip/explorer/pip | 10 ++++++++++ cdist/conf/type/__package_pip/explorer/state | 2 +- cdist/conf/type/__package_pip/gencode-remote | 7 ++++++- 3 files changed, 17 insertions(+), 2 deletions(-) create mode 100755 cdist/conf/type/__package_pip/explorer/pip diff --git a/cdist/conf/type/__package_pip/explorer/pip b/cdist/conf/type/__package_pip/explorer/pip new file mode 100755 index 00000000..cf9fae89 --- /dev/null +++ b/cdist/conf/type/__package_pip/explorer/pip @@ -0,0 +1,10 @@ +#!/bin/sh -e + +for bin in pip3 pip +do + if check="$( command -v "$bin" )" + then + echo "$check" + break + fi +done diff --git a/cdist/conf/type/__package_pip/explorer/state b/cdist/conf/type/__package_pip/explorer/state index 5be07280..3cc98ab9 100644 --- a/cdist/conf/type/__package_pip/explorer/state +++ b/cdist/conf/type/__package_pip/explorer/state @@ -32,7 +32,7 @@ pipparam="$__object/parameter/pip" if [ -f "$pipparam" ]; then pip=$(cat "$pipparam") else - pip="pip" + pip="$( "$__type_explorer/pip" )" fi # If there is no pip, it may get created from somebody else. diff --git a/cdist/conf/type/__package_pip/gencode-remote b/cdist/conf/type/__package_pip/gencode-remote index dcc4fdf9..9c5eb0d1 100755 --- a/cdist/conf/type/__package_pip/gencode-remote +++ b/cdist/conf/type/__package_pip/gencode-remote @@ -38,7 +38,12 @@ pipparam="$__object/parameter/pip" if [ -f "$pipparam" ]; then pip=$(cat "$pipparam") else - pip="pip" + pip="$( cat "$__object/explorer/pip" )" + if [ -z "$pip" ] + then + echo 'pip not found in path' >&2 + exit 1 + fi fi runasparam="$__object/parameter/runas" From 89b621511561105cfcfcf12bbf820b74ab03396d Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Mon, 21 Sep 2020 09:04:05 +0200 Subject: [PATCH 036/366] Clarify stdin input Resolve #836. --- cdist/argparse.py | 36 ++++++-------------------- cdist/config.py | 6 +++-- cdist/inventory.py | 4 +-- cdist/test/inventory/__init__.py | 3 +-- docs/src/man1/cdist.rst | 44 ++++++++++---------------------- 5 files changed, 29 insertions(+), 64 deletions(-) diff --git a/cdist/argparse.py b/cdist/argparse.py index 77303591..1d16bb25 100644 --- a/cdist/argparse.py +++ b/cdist/argparse.py @@ -273,8 +273,7 @@ def get_parsers(): '-f', '--file', help=('Read specified file for a list of additional hosts to ' 'operate on or if \'-\' is given, read stdin (one host per ' - 'line). If no host or host file is specified then, by ' - 'default, read hosts from stdin.'), + 'line).'), dest='hostfile', required=False) parser['config_args'].add_argument( '-p', '--parallel', nargs='?', metavar='HOST_MAX', @@ -326,9 +325,7 @@ def get_parsers(): parser['add-host'].add_argument( '-f', '--file', help=('Read additional hosts to add from specified file ' - 'or from stdin if \'-\' (each host on separate line). ' - 'If no host or host file is specified then, by default, ' - 'read from stdin.'), + 'or from stdin if \'-\' (each host on separate line). '), dest='hostfile', required=False) parser['add-tag'] = parser['invsub'].add_parser( @@ -342,20 +339,12 @@ def get_parsers(): parser['add-tag'].add_argument( '-f', '--file', help=('Read additional hosts to add tags from specified file ' - 'or from stdin if \'-\' (each host on separate line). ' - 'If no host or host file is specified then, by default, ' - 'read from stdin. If no tags/tagfile nor hosts/hostfile' - ' are specified then tags are read from stdin and are' - ' added to all hosts.'), + 'or from stdin if \'-\' (each host on separate line). '), dest='hostfile', required=False) parser['add-tag'].add_argument( '-T', '--tag-file', help=('Read additional tags to add from specified file ' - 'or from stdin if \'-\' (each tag on separate line). ' - 'If no tag or tag file is specified then, by default, ' - 'read from stdin. If no tags/tagfile nor hosts/hostfile' - ' are specified then tags are read from stdin and are' - ' added to all hosts.'), + 'or from stdin if \'-\' (each tag on separate line). '), dest='tagfile', required=False) parser['add-tag'].add_argument( '-t', '--taglist', @@ -376,9 +365,7 @@ def get_parsers(): parser['del-host'].add_argument( '-f', '--file', help=('Read additional hosts to delete from specified file ' - 'or from stdin if \'-\' (each host on separate line). ' - 'If no host or host file is specified then, by default, ' - 'read from stdin.'), + 'or from stdin if \'-\' (each host on separate line). '), dest='hostfile', required=False) parser['del-tag'] = parser['invsub'].add_parser( @@ -396,20 +383,13 @@ def get_parsers(): parser['del-tag'].add_argument( '-f', '--file', help=('Read additional hosts to delete tags for from specified ' - 'file or from stdin if \'-\' (each host on separate line). ' - 'If no host or host file is specified then, by default, ' - 'read from stdin. If no tags/tagfile nor hosts/hostfile' - ' are specified then tags are read from stdin and are' - ' deleted from all hosts.'), + 'file or from stdin if \'-\' (each host on separate ' + 'line). '), dest='hostfile', required=False) parser['del-tag'].add_argument( '-T', '--tag-file', help=('Read additional tags from specified file ' - 'or from stdin if \'-\' (each tag on separate line). ' - 'If no tag or tag file is specified then, by default, ' - 'read from stdin. If no tags/tagfile nor' - ' hosts/hostfile are specified then tags are read from' - ' stdin and are added to all hosts.'), + 'or from stdin if \'-\' (each tag on separate line). '), dest='tagfile', required=False) parser['del-tag'].add_argument( '-t', '--taglist', diff --git a/cdist/config.py b/cdist/config.py index 30416008..e84f6f84 100644 --- a/cdist/config.py +++ b/cdist/config.py @@ -175,9 +175,11 @@ class Config: raise cdist.Error(("Cannot read both, manifest and host file, " "from stdin")) - # if no host source is specified then read hosts from stdin if not (args.hostfile or args.host): - args.hostfile = '-' + if args.tag or args.all_tagged_hosts: + raise cdist.Error(("Target host tag(s) missing")) + else: + raise cdist.Error(("Target host(s) missing")) if args.manifest == '-': # read initial manifest from stdin diff --git a/cdist/inventory.py b/cdist/inventory.py index 6ab20fa7..0387f326 100644 --- a/cdist/inventory.py +++ b/cdist/inventory.py @@ -299,7 +299,7 @@ class InventoryHost(Inventory): self.all = all if not self.hosts and not self.hostfile: - self.hostfile = "-" + raise cdist.Error("Host(s) missing") def _new_hostpath(self, hostpath): # create empty file @@ -355,7 +355,7 @@ class InventoryTag(Inventory): else: self.allhosts = False if not self.tags and not self.tagfile: - self.tagfile = "-" + raise cdist.Error("Tag(s) missing") if self.hostfile == "-" and self.tagfile == "-": raise cdist.Error("Cannot read both, hosts and tags, from stdin") diff --git a/cdist/test/inventory/__init__.py b/cdist/test/inventory/__init__.py index 287c855c..a8cd8bf8 100644 --- a/cdist/test/inventory/__init__.py +++ b/cdist/test/inventory/__init__.py @@ -307,11 +307,10 @@ class InventoryTestCase(test.CdistTestCase): raise e # InventoryTag + @unittest.expectedFailure def test_inventory_tag_init(self): invTag = inventory.InventoryTag(db_basedir=inventory_dir, action="add") - self.assertTrue(invTag.allhosts) - self.assertEqual(invTag.tagfile, "-") def test_inventory_tag_stdin_multiple_hosts(self): try: diff --git a/docs/src/man1/cdist.rst b/docs/src/man1/cdist.rst index aa2607f8..0ecb4a61 100644 --- a/docs/src/man1/cdist.rst +++ b/docs/src/man1/cdist.rst @@ -177,10 +177,8 @@ Install command is currently in beta. **-f HOSTFILE, --file HOSTFILE** Read specified file for a list of additional hosts to operate on - or if '-' is given, read stdin (one host per line). - If no host or host file is specified then, by default, - read hosts from stdin. For the file format see - :strong:`HOSTFILE FORMAT` below. + or if '-' is given, read stdin (one host per line). For the file + format see :strong:`HOSTFILE FORMAT` below. **-g CONFIG_FILE, --config-file CONFIG_FILE** Use specified custom configuration file. @@ -299,9 +297,8 @@ Add host(s) to inventory database. **-f HOSTFILE, --file HOSTFILE** Read additional hosts to add from specified file or - from stdin if '-' (each host on separate line). If no - host or host file is specified then, by default, read - from stdin. Hostfile format is the same as config hostfile format. + from stdin if '-' (each host on separate line). + Hostfile format is the same as config hostfile format. **-g CONFIG_FILE, --config-file CONFIG_FILE** Use specified custom configuration file. @@ -327,11 +324,8 @@ Add tag(s) to inventory database. **-f HOSTFILE, --file HOSTFILE** Read additional hosts to add tags from specified file - or from stdin if '-' (each host on separate line). If - no host or host file is specified then, by default, - read from stdin. If no tags/tagfile nor hosts/hostfile - are specified then tags are read from stdin and are - added to all hosts. Hostfile format is the same as config hostfile format. + or from stdin if '-' (each host on separate line). + Hostfile format is the same as config hostfile format. **-g CONFIG_FILE, --config-file CONFIG_FILE** Use specified custom configuration file. @@ -346,11 +340,8 @@ Add tag(s) to inventory database. **-T TAGFILE, --tag-file TAGFILE** Read additional tags to add from specified file or - from stdin if '-' (each tag on separate line). If no - tag or tag file is specified then, by default, read - from stdin. If no tags/tagfile nor hosts/hostfile are - specified then tags are read from stdin and are added - to all hosts. Tagfile format is the same as config hostfile format. + from stdin if '-' (each tag on separate line). + Tagfile format is the same as config hostfile format. **-t TAGLIST, --taglist TAGLIST** Tag list to be added for specified host(s), comma @@ -372,9 +363,8 @@ Delete host(s) from inventory database. **-f HOSTFILE, --file HOSTFILE** Read additional hosts to delete from specified file or - from stdin if '-' (each host on separate line). If no - host or host file is specified then, by default, read - from stdin. Hostfile format is the same as config hostfile format. + from stdin if '-' (each host on separate line). + Hostfile format is the same as config hostfile format. **-g CONFIG_FILE, --config-file CONFIG_FILE** Use specified custom configuration file. @@ -404,11 +394,8 @@ Delete tag(s) from inventory database. **-f HOSTFILE, --file HOSTFILE** Read additional hosts to delete tags for from specified file or from stdin if '-' (each host on - separate line). If no host or host file is specified - then, by default, read from stdin. If no tags/tagfile - nor hosts/hostfile are specified then tags are read - from stdin and are deleted from all hosts. Hostfile - format is the same as config hostfile format. + separate line). Hostfile format is the same as + config hostfile format. **-g CONFIG_FILE, --config-file CONFIG_FILE** Use specified custom configuration file. @@ -423,11 +410,8 @@ Delete tag(s) from inventory database. **-T TAGFILE, --tag-file TAGFILE** Read additional tags from specified file or from stdin - if '-' (each tag on separate line). If no tag or tag - file is specified then, by default, read from stdin. - If no tags/tagfile nor hosts/hostfile are specified - then tags are read from stdin and are added to all - hosts. Tagfile format is the same as config hostfile format. + if '-' (each tag on separate line). + Tagfile format is the same as config hostfile format. **-t TAGLIST, --taglist TAGLIST** Tag list to be deleted for specified host(s), comma From 89a0080e133052b82ce19e7fdbaf045dcc833da7 Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Mon, 21 Sep 2020 09:09:26 +0200 Subject: [PATCH 037/366] ++changelog --- docs/changelog | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/docs/changelog b/docs/changelog index 7a0d050d..260b4fe4 100644 --- a/docs/changelog +++ b/docs/changelog @@ -1,6 +1,10 @@ Changelog --------- +next: + * Core: Clarify stdin input (Darko Poljak) + * Type __package_pip: Detect pip binary (Ander Punnar) + 6.8.0: 2020-09-11 * Type __locale_system: Fix for debian and ubuntu (Ander Punnar) * Type __unpack: Add --tar-extra-args parameter (Ander Punnar) From 0fc10749edc698d1f70f826603d009fba943d252 Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Mon, 21 Sep 2020 09:11:35 +0200 Subject: [PATCH 038/366] Fix shellcheck --- cdist/conf/type/__package_pip/gencode-remote | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cdist/conf/type/__package_pip/gencode-remote b/cdist/conf/type/__package_pip/gencode-remote index 9c5eb0d1..a1375c2d 100755 --- a/cdist/conf/type/__package_pip/gencode-remote +++ b/cdist/conf/type/__package_pip/gencode-remote @@ -60,7 +60,7 @@ case "$state_should" in then echo "su -c '$pip install -q $name' $runas" else - echo $pip install -q "$name" + echo "$pip" install -q "$name" fi echo "installed" >> "$__messages_out" ;; @@ -69,7 +69,7 @@ case "$state_should" in then echo "su -c '$pip uninstall -q -y $name' $runas" else - echo $pip uninstall -q -y "$name" + echo "$pip" uninstall -q -y "$name" fi echo "removed" >> "$__messages_out" ;; From b6922508b9e07834009a6bfae2d3933b5ea62fbf Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Mon, 21 Sep 2020 09:17:34 +0200 Subject: [PATCH 039/366] Update helper script --- bin/build-helper | 1 - 1 file changed, 1 deletion(-) diff --git a/bin/build-helper b/bin/build-helper index ed41e438..d4d603ed 100755 --- a/bin/build-helper +++ b/bin/build-helper @@ -371,7 +371,6 @@ eof Manual steps post release: - cdist-web - send generated mailinglist.tmp mail - - twitter eof ;; From 84a7818121047cf6bb23d8bd3c055d09bb033d32 Mon Sep 17 00:00:00 2001 From: Matthias Stecher Date: Wed, 23 Sep 2020 20:29:47 +0200 Subject: [PATCH 040/366] docs: make varaibles environment-aware There are all overwriting the environment, even the comment states otherwise. Fixes it. --- docs/src/Makefile | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/src/Makefile b/docs/src/Makefile index 2ecf7a32..ba87d170 100644 --- a/docs/src/Makefile +++ b/docs/src/Makefile @@ -2,10 +2,10 @@ # # You can set these variables from the command line. -SPHINXOPTS = -SPHINXBUILD = sphinx-build -PAPER = -BUILDDIR = ../dist +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +PAPER ?= +BUILDDIR ?= ../dist # for cache, etc. _BUILDDIR = _build From 73d6c9d469d1429999ea2e980b3ac4c63a3e0dd4 Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Sun, 27 Sep 2020 10:17:35 +0200 Subject: [PATCH 041/366] Add custom remote copy/exec examples --- docs/src/cdist-remote-exec-copy.rst | 389 +++++++++++++++++++++++++++- 1 file changed, 388 insertions(+), 1 deletion(-) diff --git a/docs/src/cdist-remote-exec-copy.rst b/docs/src/cdist-remote-exec-copy.rst index bb818310..e7b7b226 100644 --- a/docs/src/cdist-remote-exec-copy.rst +++ b/docs/src/cdist-remote-exec-copy.rst @@ -10,7 +10,7 @@ By default this is accomplished with ssh and scp respectively. The default implementations used by cdist are:: __remote_exec: ssh -o User=root - __remote_copy: scp -o User=root + __remote_copy: scp -o User=root -q The user can override these defaults by providing custom implementations and passing them to cdist with the --remote-exec and/or --remote-copy arguments. @@ -26,3 +26,390 @@ specified by enclosed in square brackets (see :strong:`ssh`\ (1) and With this simple interface the user can take total control of how cdist interacts with the target when required, while the default implementation remains as simple as possible. + + +Examples +-------- + +Here are examples of using alternative __remote_copy and __remote_exec scripts. + +All scripts from below are present in cdist sources in `other/examples/remote` +directory. + +ssh +~~~ + +Same as cdist default. + +**copy** + +Usage: cdist config --remote-copy "/path/to/this/script" target_host + +.. code-block:: sh + + #echo "$@" | logger -t "cdist-ssh-copy" + scp -o User=root -q $@ + +**exec** + +Usage: cdist config --remote-exec "/path/to/this/script" target_host + +.. code-block:: sh + + #echo "$@" | logger -t "cdist-ssh-exec" + ssh -o User=root $@ + +local +~~~~~ + +This effectively turns remote calling into local calling. Probably most useful +for the unit testing. + +**copy** + +.. code-block:: sh + + code="$(echo "$@" | sed "s|\([[:space:]]\)$__target_host:|\1|g")" + cp -L $code + +**exec** + +.. code-block:: sh + + target_host=$1; shift + echo "$@" | /bin/sh + +chroot +~~~~~~ + +**copy** + +Usage: cdist config --remote-copy "/path/to/this/script /path/to/your/chroot" target-id + +.. code-block:: sh + + log() { + #echo "$@" | logger -t "cdist-chroot-copy" + : + } + + chroot="$1"; shift + target_host="$__target_host" + + # replace target_host with chroot location + code="$(echo "$@" | sed "s|$target_host:|$chroot|g")" + + log "target_host: $target_host" + log "chroot: $chroot" + log "$@" + log "$code" + + # copy files into chroot + cp $code + + log "-----" + +**exec** + +Usage: cdist config --remote-exec "/path/to/this/script /path/to/your/chroot" target-id + +.. code-block:: sh + + log() { + #echo "$@" | logger -t "cdist-chroot-exec" + : + } + + chroot="$1"; shift + target_host="$1"; shift + + script=$(mktemp "${chroot}/tmp/chroot-${0##*/}.XXXXXXXXXX") + trap cleanup INT TERM EXIT + cleanup() { + [ $__cdist_debug ] || rm "$script" + } + + log "target_host: $target_host" + log "script: $script" + log "@: $@" + echo "#!/bin/sh -l" > "$script" + echo "$@" >> "$script" + chmod +x "$script" + + relative_script="${script#$chroot}" + log "relative_script: $relative_script" + + # run in chroot + chroot "$chroot" "$relative_script" + + log "-----" + +rsync +~~~~~ + +**copy** + +Usage: cdist config --remote-copy /path/to/this/script target_host + +.. code-block:: sh + + # For rsync to do the right thing, the source has to end with "/" if it is + # a directory. The below preprocessor loop takes care of that. + + # second last argument is the source + source_index=$(($#-1)) + index=0 + for arg in $@; do + if [ $index -eq 0 ]; then + # reset $@ + set -- + fi + index=$((index+=1)) + if [ $index -eq $source_index -a -d "$arg" ]; then + arg="${arg%/}/" + fi + set -- "$@" "$arg" + done + + rsync --backup --suffix=~cdist -e 'ssh -o User=root' $@ + +schroot +~~~~~~~ + +__remote_copy and __remote_exec scripts to run cdist against a chroot on the +target host over ssh. + +**copy** + +Usage: cdist config --remote-copy "/path/to/this/script schroot-chroot-name" target_host + + +.. code-block:: sh + + log() { + #echo "$@" | logger -t "cdist-schroot-copy" + : + } + + chroot_name="$1"; shift + target_host="$__target_host" + + # get directory for given chroot_name + chroot="$(ssh -o User=root -q $target_host schroot -c $chroot_name --config | awk -F = '/directory=/ {print $2}')" + + # prefix destination with chroot + code="$(echo "$@" | sed "s|$target_host:|$target_host:$chroot|g")" + + log "target_host: $target_host" + log "chroot_name: $chroot_name" + log "chroot: $chroot" + log "@: $@" + log "code: $code" + + # copy files into remote chroot + scp -o User=root -q $code + + log "-----" + +**exec** + +Usage: cdist config --remote-exec "/path/to/this/script schroot-chroot-name" target_host + +.. code-block:: sh + + log() { + #echo "$@" | logger -t "cdist-schroot-exec" + : + } + + chroot_name="$1"; shift + target_host="$1"; shift + + code="ssh -o User=root -q $target_host schroot -c $chroot_name -- $@" + + log "target_host: $target_host" + log "chroot_name: $chroot_name" + log "@: $@" + log "code: $code" + + # run in remote chroot + $code + + log "-----" + +schroot-uri +~~~~~~~~~~~ + +__remote_exec/__remote_copy script to run cdist against a schroot target URI. + +Usage:: + + cdist config \ + --remote-exec "/path/to/this/script exec" \ + --remote-copy "/path/to/this/script copy" \ + target_uri + + # target_uri examples: + schroot:///chroot-name + schroot://foo.ethz.ch/chroot-name + schroot://user-name@foo.ethz.ch/chroot-name + + # and how to match them in .../manifest/init + case "$target_host" in + schroot://*) + # any schroot + ;; + schroot://foo.ethz.ch/*) + # any schroot on specific host + ;; + schroot://foo.ethz.ch/chroot-name) + # specific schroot on specific host + ;; + schroot:///chroot-name) + # specific schroot on localhost + ;; + esac + +**copy/exec** + +.. code-block:: sh + + my_name="${0##*/}" + mode="$1"; shift + + log() { + # uncomment me for debugging + #echo "$@" | logger -t "cdist-$my_name-$mode" + : + } + + die() { + echo "$@" >&2 + exit 1 + } + + + uri="$__target_host" + + scheme="${uri%%:*}"; rest="${uri#$scheme:}"; rest="${rest#//}" + authority="${rest%%/*}"; rest="${rest#$authority}" + path="${rest%\?*}"; rest="${rest#$path}" + schroot_name="${path#/}" + + [ "$scheme" = "schroot" ] || die "Failed to parse scheme from __target_host ($__target_host). Expected 'schroot', got '$scheme'" + [ -n "$schroot_name" ] || die "Failed to parse schroot name from __target_host: $__target_host" + + case "$authority" in + '') + # authority is empty, neither user nor host given + user="" + host="" + ;; + *@*) + # authority contains @, take user from authority + user="${authority%@*}" + host="${authority#*@}" + ;; + *) + # no user in authority, default to root + user="root" + host="$authority" + ;; + esac + + log "mode: $mode" + log "@: $@" + log "uri: $uri" + log "scheme: $scheme" + log "authority: $authority" + log "user: $user" + log "host: $host" + log "path: $path" + log "schroot_name: $schroot_name" + + exec_prefix="" + copy_prefix="" + if [ -n "$host" ]; then + # we are working on a remote host + exec_prefix="ssh -o User=$user -q $host" + copy_prefix="scp -o User=$user -q" + copy_destination_prefix="$host:" + else + # working on local machine + copy_prefix="cp" + copy_destination_prefix="" + fi + log "exec_prefix: $exec_prefix" + log "copy_prefix: $copy_prefix" + log "copy_destination_prefix: $copy_destination_prefix" + + case "$mode" in + exec) + # In exec mode the first argument is the __target_host which we already got from env. Get rid of it. + shift + code="$exec_prefix schroot -c $schroot_name -- sh -c '$@'" + ;; + copy) + # get directory for given chroot_name + schroot_directory="$($exec_prefix schroot -c $schroot_name --config | awk -F = '/directory=/ {print $2}')" + [ -n "$schroot_directory" ] || die "Failed to retreive schroot directory for schroot: $schroot_name" + log "schroot_directory: $schroot_directory" + # prefix destination with chroot + code="$copy_prefix $(echo "$@" | sed "s|$uri:|${copy_destination_prefix}${schroot_directory}|g")" + ;; + *) die "Unknown mode: $mode";; + esac + + log "code: $code" + + # Run the code + $code + + log "-----" + +sudo +~~~~ + +**copy** + +Use rsync over ssh to copy files. Uses the "--rsync-path" option +to run the remote rsync instance with sudo. + +This command assumes your ssh configuration is already set up in ~/.ssh/config. + +Usage: cdist config --remote-copy /path/to/this/script target_host + +.. code-block:: sh + + # For rsync to do the right thing, the source has to end with "/" if it is + # a directory. The below preprocessor loop takes care of that. + + # second last argument is the source + source_index=$(($#-1)) + index=0 + for arg in $@; do + if [ $index -eq 0 ]; then + # reset $@ + set -- + fi + index=$((index+=1)) + if [ $index -eq $source_index -a -d "$arg" ]; then + arg="${arg%/}/" + fi + set -- "$@" "$arg" + done + + rsync --copy-links --rsync-path="sudo rsync" -e 'ssh' "$@" + +**exec** + +Prefixes all remote commands with sudo. + +This command assumes your ssh configuration is already set up in ~/.ssh/config. + +Usage: cdist config --remote-exec "/path/to/this/script" target_host + +.. code-block:: sh + + host="$1"; shift + ssh -q "$host" sudo sh -c \""$@"\" From 652c89185816526e5165ff9d5686a0d245ec3e5f Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Tue, 29 Sep 2020 05:57:54 +0200 Subject: [PATCH 042/366] ++changelog --- docs/changelog | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog b/docs/changelog index 260b4fe4..8d380524 100644 --- a/docs/changelog +++ b/docs/changelog @@ -4,6 +4,7 @@ Changelog next: * Core: Clarify stdin input (Darko Poljak) * Type __package_pip: Detect pip binary (Ander Punnar) + * Documentation: Add custom remote copy/exec examples (Darko Poljak) 6.8.0: 2020-09-11 * Type __locale_system: Fix for debian and ubuntu (Ander Punnar) From f994226d0e23b667309fd75116c0c8a14d15f34d Mon Sep 17 00:00:00 2001 From: Evilham Date: Tue, 29 Sep 2020 19:44:47 +0200 Subject: [PATCH 043/366] [__package_pkgng_freebsd] Bootstrap pkg if necessary In a pristine FreeBSD base installation, pkg is really a bootstrapper utility, in such cases the type used to fail instead of automatically bootstrapping pkg. --- .../type/__package_pkgng_freebsd/explorer/pkg_bootstrapped | 4 ++++ cdist/conf/type/__package_pkgng_freebsd/explorer/pkg_version | 5 +++++ cdist/conf/type/__package_pkgng_freebsd/gencode-remote | 5 +++++ 3 files changed, 14 insertions(+) create mode 100755 cdist/conf/type/__package_pkgng_freebsd/explorer/pkg_bootstrapped diff --git a/cdist/conf/type/__package_pkgng_freebsd/explorer/pkg_bootstrapped b/cdist/conf/type/__package_pkgng_freebsd/explorer/pkg_bootstrapped new file mode 100755 index 00000000..429f15d3 --- /dev/null +++ b/cdist/conf/type/__package_pkgng_freebsd/explorer/pkg_bootstrapped @@ -0,0 +1,4 @@ +#!/bin/sh -e +if pkg -N >/dev/null 2>&1; then + echo "YES" +fi diff --git a/cdist/conf/type/__package_pkgng_freebsd/explorer/pkg_version b/cdist/conf/type/__package_pkgng_freebsd/explorer/pkg_version index 92ce0623..f0fb9127 100755 --- a/cdist/conf/type/__package_pkgng_freebsd/explorer/pkg_version +++ b/cdist/conf/type/__package_pkgng_freebsd/explorer/pkg_version @@ -21,6 +21,11 @@ # Retrieve the status of a package - parsed dpkg output # +if ! pkg -N >/dev/null 2>&1; then + # Nothing to do if pkg is not bootstrapped + exit +fi + if [ -f "$__object/parameter/name" ]; then name="$(cat "$__object/parameter/name")" else diff --git a/cdist/conf/type/__package_pkgng_freebsd/gencode-remote b/cdist/conf/type/__package_pkgng_freebsd/gencode-remote index dd36efda..b5944177 100755 --- a/cdist/conf/type/__package_pkgng_freebsd/gencode-remote +++ b/cdist/conf/type/__package_pkgng_freebsd/gencode-remote @@ -43,6 +43,7 @@ fi repo="$(cat "$__object/parameter/repo")" state="$(cat "$__object/parameter/state")" curr_version="$(cat "$__object/explorer/pkg_version")" +pkg_bootstrapped="$(cat "$__object/explorer/pkg_bootstrapped")" add_cmd="pkg install -y" rm_cmd="pkg delete -y" upg_cmd="pkg upgrade -y" @@ -73,6 +74,10 @@ execcmd(){ ;; esac + if [ -z "${pkg_bootstrapped}" ]; then + echo "pkg bootstrap -y >/dev/null 2>&1" + fi + echo "$_cmd >/dev/null 2>&1" # Silence the output of the command echo "status=\$?" echo "if [ \"\$status\" -ne \"0\" ]; then" From 52b5f05163416196403634cb9a52f746cdb96391 Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Wed, 30 Sep 2020 08:56:31 +0200 Subject: [PATCH 044/366] ++changelog --- docs/changelog | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog b/docs/changelog index 8d380524..34c97d67 100644 --- a/docs/changelog +++ b/docs/changelog @@ -5,6 +5,7 @@ next: * Core: Clarify stdin input (Darko Poljak) * Type __package_pip: Detect pip binary (Ander Punnar) * Documentation: Add custom remote copy/exec examples (Darko Poljak) + * Type __package_pkgng_freebsd: Bootstrap pkg if necessary (Evil Ham) 6.8.0: 2020-09-11 * Type __locale_system: Fix for debian and ubuntu (Ander Punnar) From 3fa74b454a9512319e3559a53dd477f38724fd9b Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Wed, 30 Sep 2020 15:43:32 +0200 Subject: [PATCH 045/366] Fix typo --- cdist/conf/type/__package_pkgng_freebsd/explorer/pkg_version | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cdist/conf/type/__package_pkgng_freebsd/explorer/pkg_version b/cdist/conf/type/__package_pkgng_freebsd/explorer/pkg_version index f0fb9127..1c6ba5e5 100755 --- a/cdist/conf/type/__package_pkgng_freebsd/explorer/pkg_version +++ b/cdist/conf/type/__package_pkgng_freebsd/explorer/pkg_version @@ -18,7 +18,7 @@ # along with cdist. If not, see . # # -# Retrieve the status of a package - parsed dpkg output +# Retrieve the status of a package - parsed pkgng output # if ! pkg -N >/dev/null 2>&1; then From 5aeed14b1b8b022f952680cceb12a5099a099558 Mon Sep 17 00:00:00 2001 From: Mark Verboom Date: Thu, 8 Oct 2020 16:15:20 +0200 Subject: [PATCH 046/366] Fixed calling of __systemd_service type with correct arguments. --- cdist/conf/type/__service/manifest | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cdist/conf/type/__service/manifest b/cdist/conf/type/__service/manifest index cb5af234..beb0713c 100644 --- a/cdist/conf/type/__service/manifest +++ b/cdist/conf/type/__service/manifest @@ -7,7 +7,9 @@ action="$(cat "$__object/parameter/action")" case "$manager" in systemd) - __systemd_service "$name" --action "$action" + test "$action" = "start" && action="running" + test "$action" = "stop" && action="stopped" + __systemd_service "$name" --state "$action" ;; *) # Unknown: handled by `service $NAME $action` in gencode-remote. From c030deea3dbccfe73637b730f0d545db7f8d5f35 Mon Sep 17 00:00:00 2001 From: Evil Ham Date: Fri, 9 Oct 2020 06:51:44 +0200 Subject: [PATCH 047/366] [__line] Add support for '--state replace' It is currently counter-intuitive that something like: # File '/thing' contents #SomeSetting WrongValue # Manifest __line '/thing' \ --line 'SomeSeting GoodValue' \ --regex '^(#[[:space:]]*)?SomeSetting[[:space:]]' Produces: # Resulting '/thing' contents #SomeSetting WrongValue This makes sense given the implementation, but it masks a very common use-case. Changing the default behaviour for such a base type is not really an option, so instead we add a `replace` as a valid value for `--state`, which would result in: # Resulting '/thing' contents with: --state replace SomeSetting GoodValue For compatibility, if the regex is missing, `--state replace` behaves just as `--state present`. --- cdist/conf/type/__line/explorer/state | 12 +++++++++++- cdist/conf/type/__line/gencode-remote | 10 +++++++--- cdist/conf/type/__line/man.rst | 13 +++++++++++-- 3 files changed, 29 insertions(+), 6 deletions(-) diff --git a/cdist/conf/type/__line/explorer/state b/cdist/conf/type/__line/explorer/state index e8fc3630..9d480b19 100755 --- a/cdist/conf/type/__line/explorer/state +++ b/cdist/conf/type/__line/explorer/state @@ -53,8 +53,10 @@ function _find(_text, _pattern) { BEGIN { getline anchor < (ENVIRON["__object"] "/parameter/" position) getline pattern < (ENVIRON["__object"] "/parameter/" needle) + getline line < (ENVIRON["__object"] "/parameter/line") found_line = 0 + correct_line = 0 correct_pos = (position != "after" && position != "before") } { @@ -63,15 +65,18 @@ BEGIN { getline if (_find($0, pattern)) { found_line++ + if (index($0, line) == 1) { correct_line++ } correct_pos = 1 exit 0 } } else if (_find($0, pattern)) { found_line++ + if (index($0, line) == 1) { correct_line++ } } } else if (position == "before") { if (_find($0, pattern)) { found_line++ + if (index($0, line) == 1) { correct_line++ } getline if (match($0, anchor)) { correct_pos = 1 @@ -81,13 +86,18 @@ BEGIN { } else { if (_find($0, pattern)) { found_line++ + if (index($0, line) == 1) { correct_line++ } exit 0 } } } END { if (found_line && correct_pos) { - print "present" + if (correct_line) { + print "present" + } else { + print "matching" + } } else if (found_line) { print "wrongposition" } else { diff --git a/cdist/conf/type/__line/gencode-remote b/cdist/conf/type/__line/gencode-remote index 88cae68b..a89886da 100755 --- a/cdist/conf/type/__line/gencode-remote +++ b/cdist/conf/type/__line/gencode-remote @@ -38,7 +38,11 @@ if [ -z "$state_is" ]; then exit 1 fi -if [ "$state_should" = "$state_is" ]; then +if [ "$state_should" = "$state_is" ] || \ + { [ "$state_should" = "present" ] && [ "$state_is" = "matching" ] ;} || \ + { [ "$state_should" = "replace" ] && [ "$state_is" = "present" ] ;} ; then + # If state matches already, or 'present' is used and regex matches + # or 'replace' is used and the exact line is present, then there is # nothing to do exit 0 fi @@ -61,8 +65,8 @@ fi add=0 remove=0 case "$state_should" in - present) - if [ "$state_is" = "wrongposition" ]; then + present|replace) + if [ "$state_is" = "wrongposition" ] || [ "$state_is" = "matching" ]; then echo updated >> "$__messages_out" remove=1 else diff --git a/cdist/conf/type/__line/man.rst b/cdist/conf/type/__line/man.rst index f76cab64..70490f68 100644 --- a/cdist/conf/type/__line/man.rst +++ b/cdist/conf/type/__line/man.rst @@ -31,7 +31,7 @@ file line Specifies the line which should be absent or present. - Must be present, if state is 'present'. + Must be present, if state is 'present' or 'replace'. Ignored if regex is given and state is 'absent'. regex @@ -41,10 +41,13 @@ regex If state is 'absent', ensure all lines matching the regular expression are absent. + If state is 'replace', ensure all lines matching the regular expression + are exactly 'line'. + The regular expression is interpreted by awk's match function. state - 'present' or 'absent', defaults to 'present' + 'present', 'absent' or 'replace', defaults to 'present'. onchange The code to run if line is added, removed or updated. @@ -99,6 +102,12 @@ EXAMPLES --line '-session required pam_exec.so debug log=/tmp/classify.log /usr/local/libexec/classify' \ --after '^session[[:space:]]+include[[:space:]]+password-auth-ac$' + # Uncomment as needed and set a value in a configuration file. + __line /etc/example.conf \ + --line 'SomeSetting SomeValue' \ + --regex '^(#[[:space:]]*)?SomeSetting[[:space:]]' \ + --state replace + SEE ALSO -------- From 4df5c91912edf9f31a6716d7cbe0adaedbc2b0e3 Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Fri, 9 Oct 2020 06:52:52 +0200 Subject: [PATCH 048/366] ++changelog --- docs/changelog | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/changelog b/docs/changelog index 34c97d67..6d237449 100644 --- a/docs/changelog +++ b/docs/changelog @@ -6,6 +6,8 @@ next: * Type __package_pip: Detect pip binary (Ander Punnar) * Documentation: Add custom remote copy/exec examples (Darko Poljak) * Type __package_pkgng_freebsd: Bootstrap pkg if necessary (Evil Ham) + * Type __service: Fix calling __systemd_service (Mark Verboom) + * Type __line: Add 'replace' state (Evil Ham) 6.8.0: 2020-09-11 * Type __locale_system: Fix for debian and ubuntu (Ander Punnar) From 8ecae42199f27693eec771513174ce8143c8ca83 Mon Sep 17 00:00:00 2001 From: Ander Punnar Date: Wed, 14 Oct 2020 02:00:11 +0300 Subject: [PATCH 049/366] remove bin/cdist script --- bin/cdist | 33 --------------------------------- 1 file changed, 33 deletions(-) delete mode 100755 bin/cdist diff --git a/bin/cdist b/bin/cdist deleted file mode 100755 index 645020a1..00000000 --- a/bin/cdist +++ /dev/null @@ -1,33 +0,0 @@ -#!/bin/sh -# -*- coding: utf-8 -*- -# -# 2012 Nico Schottelius (nico-cdist at schottelius.org) -# -# 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 . -# -# - -# Wrapper for real script to allow execution from checkout -dir=${0%/*} - -# Ensure version is present - the bundled/shipped version contains a static version, -# the git version contains a dynamic version -"$dir/build-helper" version - -libdir=$(cd "${dir}/../" && pwd -P) -export PYTHONPATH="${libdir}" - -"$dir/../scripts/cdist" "$@" From 45d51c0e1553ce12fcea09b1b9e0d2fb9c0304c5 Mon Sep 17 00:00:00 2001 From: Ander Punnar Date: Wed, 14 Oct 2020 02:00:44 +0300 Subject: [PATCH 050/366] rename build-helper -> cdist-build-helper --- bin/{build-helper => cdist-build-helper} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename bin/{build-helper => cdist-build-helper} (100%) diff --git a/bin/build-helper b/bin/cdist-build-helper similarity index 100% rename from bin/build-helper rename to bin/cdist-build-helper From 3f1939716f4c4c2cdf538a22170a67fe181c675c Mon Sep 17 00:00:00 2001 From: Ander Punnar Date: Wed, 14 Oct 2020 02:02:45 +0300 Subject: [PATCH 051/366] enable running scripts/cdist directly and symlinked --- scripts/cdist | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/scripts/cdist b/scripts/cdist index b1d782ab..2ce40ae0 100755 --- a/scripts/cdist +++ b/scripts/cdist @@ -22,7 +22,15 @@ # import logging +import os import sys + +cdist_bin = os.path.abspath(__file__) +if os.path.islink(cdist_bin): + cdist_bin = os.readlink(cdist_bin) +cdist_dir = os.path.abspath(os.path.join(os.path.dirname(cdist_bin), os.pardir)) +sys.path.insert(0, cdist_dir) + import cdist import cdist.argparse import cdist.banner From fdc1ab93e968575b1d1f6f3b2a6cefa6ed6bb850 Mon Sep 17 00:00:00 2001 From: Ander Punnar Date: Wed, 14 Oct 2020 02:03:11 +0300 Subject: [PATCH 052/366] move scripts/* to bin/ --- {scripts => bin}/cdist | 0 {scripts => bin}/cdist-dump | 0 {scripts => bin}/cdist-new-type | 0 3 files changed, 0 insertions(+), 0 deletions(-) rename {scripts => bin}/cdist (100%) rename {scripts => bin}/cdist-dump (100%) rename {scripts => bin}/cdist-new-type (100%) diff --git a/scripts/cdist b/bin/cdist similarity index 100% rename from scripts/cdist rename to bin/cdist diff --git a/scripts/cdist-dump b/bin/cdist-dump similarity index 100% rename from scripts/cdist-dump rename to bin/cdist-dump diff --git a/scripts/cdist-new-type b/bin/cdist-new-type similarity index 100% rename from scripts/cdist-new-type rename to bin/cdist-new-type From 86057cef19b10859cd66ceb4e3ebade5986aff89 Mon Sep 17 00:00:00 2001 From: Ander Punnar Date: Wed, 14 Oct 2020 02:05:17 +0300 Subject: [PATCH 053/366] don't die if there is no version.py --- cdist/__init__.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/cdist/__init__.py b/cdist/__init__.py index be573170..26e7d071 100644 --- a/cdist/__init__.py +++ b/cdist/__init__.py @@ -24,10 +24,13 @@ import os import hashlib import cdist.log -import cdist.version -VERSION = cdist.version.VERSION +try: + import cdist.version + VERSION = cdist.version.VERSION +except ModuleNotFoundError: + VERSION = 'from git' BANNER = """ .. . .x+=:. s From fd04c036139f150be79801e5fd3b49086e5d7263 Mon Sep 17 00:00:00 2001 From: Ander Punnar Date: Fri, 16 Oct 2020 13:42:16 +0300 Subject: [PATCH 054/366] add parent dir to module search path only when importing fails --- bin/cdist | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/bin/cdist b/bin/cdist index 2ce40ae0..66ccbb5a 100755 --- a/bin/cdist +++ b/bin/cdist @@ -25,13 +25,19 @@ import logging import os import sys -cdist_bin = os.path.abspath(__file__) -if os.path.islink(cdist_bin): - cdist_bin = os.readlink(cdist_bin) -cdist_dir = os.path.abspath(os.path.join(os.path.dirname(cdist_bin), os.pardir)) -sys.path.insert(0, cdist_dir) +# try to import cdist and if that fails, then add this file's parent dir to +# module search path and try again. additionally check if this file is +# symlinked, so user can symlink this file to, for example, ~/.local/bin. +try: + import cdist +except ModuleNotFoundError: + cdist_bin = os.path.abspath(__file__) + if os.path.islink(cdist_bin): + cdist_bin = os.readlink(cdist_bin) + cdist_dir = os.path.abspath(os.path.join(os.path.dirname(cdist_bin), os.pardir)) + sys.path.insert(0, cdist_dir) + import cdist -import cdist import cdist.argparse import cdist.banner import cdist.config From 1614b62f702a82731b3b9c636894268b4310821b Mon Sep 17 00:00:00 2001 From: Ander Punnar Date: Fri, 16 Oct 2020 13:48:28 +0300 Subject: [PATCH 055/366] fallback VERSION to "unknown version" --- cdist/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cdist/__init__.py b/cdist/__init__.py index 26e7d071..350a2765 100644 --- a/cdist/__init__.py +++ b/cdist/__init__.py @@ -30,7 +30,7 @@ try: import cdist.version VERSION = cdist.version.VERSION except ModuleNotFoundError: - VERSION = 'from git' + VERSION = 'unknown version' BANNER = """ .. . .x+=:. s From 174aa7728065da611dc8887d9f33ead7e27c473a Mon Sep 17 00:00:00 2001 From: Ander Punnar Date: Fri, 16 Oct 2020 14:11:00 +0300 Subject: [PATCH 056/366] __file__ already is absolute --- bin/cdist | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/cdist b/bin/cdist index 66ccbb5a..d7eae312 100755 --- a/bin/cdist +++ b/bin/cdist @@ -31,7 +31,7 @@ import sys try: import cdist except ModuleNotFoundError: - cdist_bin = os.path.abspath(__file__) + cdist_bin = __file__ if os.path.islink(cdist_bin): cdist_bin = os.readlink(cdist_bin) cdist_dir = os.path.abspath(os.path.join(os.path.dirname(cdist_bin), os.pardir)) From 65c8af4ba3751dddbfe07fd5077e5a99b5d240b8 Mon Sep 17 00:00:00 2001 From: Ander Punnar Date: Fri, 16 Oct 2020 14:11:12 +0300 Subject: [PATCH 057/366] overengineered version discovery --- cdist/__init__.py | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/cdist/__init__.py b/cdist/__init__.py index 350a2765..f659506f 100644 --- a/cdist/__init__.py +++ b/cdist/__init__.py @@ -22,6 +22,7 @@ import os import hashlib +import subprocess import cdist.log @@ -30,7 +31,19 @@ try: import cdist.version VERSION = cdist.version.VERSION except ModuleNotFoundError: - VERSION = 'unknown version' + cdist_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir)) + if os.path.isdir(os.path.join(cdist_dir, '.git')): + run_git = subprocess.run( + ['git', 'describe', '--always'], + cwd=cdist_dir, + capture_output=True, + text=True) + if run_git.returncode == 0: + VERSION = str(run_git.stdout) + else: + VERSION = 'from git' + else: + VERSION = 'unknown version' BANNER = """ .. . .x+=:. s From 42d5d6c3e29f6500c3f6321b01c3b268e6886ec6 Mon Sep 17 00:00:00 2001 From: Ander Punnar Date: Fri, 16 Oct 2020 14:12:39 +0300 Subject: [PATCH 058/366] redundant str() --- cdist/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cdist/__init__.py b/cdist/__init__.py index f659506f..c16562e7 100644 --- a/cdist/__init__.py +++ b/cdist/__init__.py @@ -39,7 +39,7 @@ except ModuleNotFoundError: capture_output=True, text=True) if run_git.returncode == 0: - VERSION = str(run_git.stdout) + VERSION = run_git.stdout else: VERSION = 'from git' else: From b41d80075a616a1c7ac3a361079c748424409995 Mon Sep 17 00:00:00 2001 From: Ander Punnar Date: Fri, 16 Oct 2020 14:16:04 +0300 Subject: [PATCH 059/366] update paths in setup.py --- setup.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/setup.py b/setup.py index 7b000041..002be19c 100644 --- a/setup.py +++ b/setup.py @@ -6,7 +6,7 @@ import subprocess # We have it only if it is a git cloned repo. -build_helper = os.path.join('bin', 'build-helper') +build_helper = os.path.join('bin', 'cdist-build-helper') # Version file path. version_file = os.path.join('cdist', 'version.py') # If we have build-helper we could be a git repo. @@ -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=["bin/cdist", "bin/cdist-dump", "bin/cdist-new-type"], version=cdist.version.VERSION, description="A Usable Configuration Management System", author="Nico Schottelius", From e55db1b427fcc7f750adfcb3646a3eeb446113de Mon Sep 17 00:00:00 2001 From: Ander Punnar Date: Fri, 16 Oct 2020 15:41:38 +0300 Subject: [PATCH 060/366] use check_output for git describe execution and define fallback VERSION earlier --- cdist/__init__.py | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/cdist/__init__.py b/cdist/__init__.py index c16562e7..1c60ae0f 100644 --- a/cdist/__init__.py +++ b/cdist/__init__.py @@ -27,23 +27,21 @@ import subprocess import cdist.log +VERSION = 'unknown version' + try: import cdist.version VERSION = cdist.version.VERSION except ModuleNotFoundError: cdist_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), os.pardir)) if os.path.isdir(os.path.join(cdist_dir, '.git')): - run_git = subprocess.run( - ['git', 'describe', '--always'], - cwd=cdist_dir, - capture_output=True, - text=True) - if run_git.returncode == 0: - VERSION = run_git.stdout - else: - VERSION = 'from git' - else: - VERSION = 'unknown version' + try: + VERSION = subprocess.check_output( + ['git', 'describe', '--always'], + cwd=cdist_dir, + universal_newlines=True) + except: + pass BANNER = """ .. . .x+=:. s From 54d83a62118ed3f0c6328c4b25a3b9ee56adf27a Mon Sep 17 00:00:00 2001 From: Ander Punnar Date: Fri, 16 Oct 2020 15:50:50 +0300 Subject: [PATCH 061/366] there is no single author anymore, also remove www. --- setup.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/setup.py b/setup.py index 002be19c..858c2c17 100644 --- a/setup.py +++ b/setup.py @@ -59,9 +59,8 @@ setup( scripts=["bin/cdist", "bin/cdist-dump", "bin/cdist-new-type"], version=cdist.version.VERSION, description="A Usable Configuration Management System", - author="Nico Schottelius", - author_email="nico-cdist-pypi@schottelius.org", - url="https://www.cdi.st/", + author="cdist contributors", + url="https://cdi.st", classifiers=[ "Development Status :: 6 - Mature", "Environment :: Console", From 507fa6fa93210959b52bb639a9394c8ac52b40e5 Mon Sep 17 00:00:00 2001 From: Matthias Stecher Date: Sat, 17 Oct 2020 17:05:09 +0200 Subject: [PATCH 062/366] __download: fix non-existent parameter of __unpack Probably happened due to renaming .. guess it's correct now. --- cdist/conf/type/__download/man.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cdist/conf/type/__download/man.rst b/cdist/conf/type/__download/man.rst index eb3ac971..d8814683 100644 --- a/cdist/conf/type/__download/man.rst +++ b/cdist/conf/type/__download/man.rst @@ -69,7 +69,7 @@ EXAMPLES require='__download/opt/cpma/cnq3.zip' \ __unpack /opt/cpma/cnq3.zip \ - --move-existing-destination \ + --backup-destination \ --destination /opt/cpma/server From d20fb743243f0341820a5b41a5725fb705eb5aae Mon Sep 17 00:00:00 2001 From: Ander Punnar Date: Sat, 17 Oct 2020 23:16:42 +0300 Subject: [PATCH 063/366] use os.path.realpath instead, because it eliminates any symbolic links encountered in the path --- bin/cdist | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/bin/cdist b/bin/cdist index d7eae312..1f92f157 100755 --- a/bin/cdist +++ b/bin/cdist @@ -25,16 +25,16 @@ import logging import os import sys -# try to import cdist and if that fails, then add this file's parent dir to -# module search path and try again. additionally check if this file is -# symlinked, so user can symlink this file to, for example, ~/.local/bin. +# try to import cdist and if that fails, +# then add this file's parent dir to +# module search path and try again. try: import cdist except ModuleNotFoundError: - cdist_bin = __file__ - if os.path.islink(cdist_bin): - cdist_bin = os.readlink(cdist_bin) - cdist_dir = os.path.abspath(os.path.join(os.path.dirname(cdist_bin), os.pardir)) + cdist_dir = os.path.realpath( + os.path.join( + os.path.dirname(os.path.realpath(__file__)), + os.pardir)) sys.path.insert(0, cdist_dir) import cdist From b2e6afb57e6799b934a2eaa387cea337d65d8aac Mon Sep 17 00:00:00 2001 From: Matthias Stecher Date: Sat, 17 Oct 2020 23:01:36 +0200 Subject: [PATCH 064/366] __download: adapt download+unpack example in manpage --- cdist/conf/type/__download/man.rst | 1 + 1 file changed, 1 insertion(+) diff --git a/cdist/conf/type/__download/man.rst b/cdist/conf/type/__download/man.rst index d8814683..54503470 100644 --- a/cdist/conf/type/__download/man.rst +++ b/cdist/conf/type/__download/man.rst @@ -70,6 +70,7 @@ EXAMPLES require='__download/opt/cpma/cnq3.zip' \ __unpack /opt/cpma/cnq3.zip \ --backup-destination \ + --preserve-archive \ --destination /opt/cpma/server From 955b84727611caa5aa05e4521af326c94c649e89 Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Sun, 18 Oct 2020 15:55:14 +0200 Subject: [PATCH 065/366] ++changelog --- docs/changelog | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog b/docs/changelog index 6d237449..8ad2c624 100644 --- a/docs/changelog +++ b/docs/changelog @@ -8,6 +8,7 @@ next: * Type __package_pkgng_freebsd: Bootstrap pkg if necessary (Evil Ham) * Type __service: Fix calling __systemd_service (Mark Verboom) * Type __line: Add 'replace' state (Evil Ham) + * Type __download: Fix man page (Matthias Stecher) 6.8.0: 2020-09-11 * Type __locale_system: Fix for debian and ubuntu (Ander Punnar) From 6964070282a6c5d09a458017623012fc17e3008d Mon Sep 17 00:00:00 2001 From: Ander Punnar Date: Sun, 18 Oct 2020 17:13:22 +0300 Subject: [PATCH 066/366] s/build-helper/cdist-build-helper/ --- .gitlab-ci.yml | 8 ++++---- README-maintainers | 2 +- bin/cdist-build-helper | 2 +- docs/src/cdist-install.rst | 4 ++-- 4 files changed, 8 insertions(+), 8 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index e215652c..e48355ea 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -6,15 +6,15 @@ image: code.ungleich.ch:5050/ungleich-public/cdist/cdist-ci:latest unit_tests: stage: test script: - - ./bin/build-helper version - - ./bin/build-helper test + - ./bin/cdist-build-helper version + - ./bin/cdist-build-helper test pycodestyle: stage: test script: - - ./bin/build-helper pycodestyle + - ./bin/cdist-build-helper pycodestyle shellcheck: stage: test script: - - ./bin/build-helper shellcheck + - ./bin/cdist-build-helper shellcheck diff --git a/README-maintainers b/README-maintainers index af57f475..5766dd7d 100644 --- a/README-maintainers +++ b/README-maintainers @@ -1,4 +1,4 @@ -Maintainers should use ./bin/build-helper script. +Maintainers should use ./bin/cdist-build-helper script. Makefile is intended for end users. It can be used for non-maintaining targets that can be run from pure source (without git repository). diff --git a/bin/cdist-build-helper b/bin/cdist-build-helper index d4d603ed..bdef0dbb 100755 --- a/bin/cdist-build-helper +++ b/bin/cdist-build-helper @@ -495,7 +495,7 @@ eof ;; shellcheck-build-helper) - ${SHELLCHECKCMD} ./bin/build-helper + ${SHELLCHECKCMD} ./bin/cdist-build-helper ;; check-shellcheck) diff --git a/docs/src/cdist-install.rst b/docs/src/cdist-install.rst index 6f4f14d7..18863145 100644 --- a/docs/src/cdist-install.rst +++ b/docs/src/cdist-install.rst @@ -49,7 +49,7 @@ create version.py: .. code-block:: sh - ./bin/build-helper version + ./bin/cdist-build-helper version Then you install it with: @@ -70,7 +70,7 @@ Or directly with distutils: python setup.py install -Note that `bin/build-helper` script is intended for cdist maintainers. +Note that `bin/cdist-build-helper` script is intended for cdist maintainers. Available versions in git From e3d906a85fe5e0c9f254d3620e8568437c93d97c Mon Sep 17 00:00:00 2001 From: Ander Punnar Date: Tue, 15 Sep 2020 00:55:26 +0300 Subject: [PATCH 067/366] [__acl] remove deprecated parameters, fix some bugs and improve manual --- cdist/conf/type/__acl/explorer/checks | 39 ------------------- cdist/conf/type/__acl/explorer/getent | 4 ++ cdist/conf/type/__acl/gencode-remote | 36 ++++++++--------- cdist/conf/type/__acl/man.rst | 12 +++--- .../conf/type/__acl/parameter/deprecated/acl | 1 - .../type/__acl/parameter/deprecated/group | 1 - .../conf/type/__acl/parameter/deprecated/mask | 1 - .../type/__acl/parameter/deprecated/other | 1 - .../conf/type/__acl/parameter/deprecated/user | 1 - cdist/conf/type/__acl/parameter/optional | 2 - .../type/__acl/parameter/optional_multiple | 3 -- 11 files changed, 26 insertions(+), 75 deletions(-) delete mode 100755 cdist/conf/type/__acl/explorer/checks create mode 100755 cdist/conf/type/__acl/explorer/getent delete mode 100644 cdist/conf/type/__acl/parameter/deprecated/acl delete mode 100644 cdist/conf/type/__acl/parameter/deprecated/group delete mode 100644 cdist/conf/type/__acl/parameter/deprecated/mask delete mode 100644 cdist/conf/type/__acl/parameter/deprecated/other delete mode 100644 cdist/conf/type/__acl/parameter/deprecated/user diff --git a/cdist/conf/type/__acl/explorer/checks b/cdist/conf/type/__acl/explorer/checks deleted file mode 100755 index 70bb0412..00000000 --- a/cdist/conf/type/__acl/explorer/checks +++ /dev/null @@ -1,39 +0,0 @@ -#!/bin/sh -e -# -# 2019 Ander Punnar (ander-at-kvlt-dot-ee) -# -# 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 . -# - -# TODO check if filesystem has ACL turned on etc - -if [ -f "$__object/parameter/acl" ] -then - grep -E '^(default:)?(user|group):' "$__object/parameter/acl" \ - | while read -r acl - do - param="$( echo "$acl" | awk -F: '{print $(NF-2)}' )" - check="$( echo "$acl" | awk -F: '{print $(NF-1)}' )" - - [ "$param" = 'user' ] && db=passwd || db="$param" - - if ! getent "$db" "$check" > /dev/null - then - echo "missing $param '$check'" >&2 - exit 1 - fi - done -fi diff --git a/cdist/conf/type/__acl/explorer/getent b/cdist/conf/type/__acl/explorer/getent new file mode 100755 index 00000000..7e6c2c30 --- /dev/null +++ b/cdist/conf/type/__acl/explorer/getent @@ -0,0 +1,4 @@ +#!/bin/sh -e + +getent passwd | awk -F: '{print "user:"$1}' +getent group | awk -F: '{print "group:"$1}' diff --git a/cdist/conf/type/__acl/gencode-remote b/cdist/conf/type/__acl/gencode-remote index e5404a9d..32318e91 100755 --- a/cdist/conf/type/__acl/gencode-remote +++ b/cdist/conf/type/__acl/gencode-remote @@ -22,8 +22,8 @@ file_is="$( cat "$__object/explorer/file_is" )" if [ "$file_is" = 'missing' ] \ && [ -z "$__cdist_dry_run" ] \ - && \( [ ! -f "$__object/parameter/file" ] \ - || [ ! -f "$__object/parameter/directory" ] \) + && [ ! -f "$__object/parameter/file" ] \ + && [ ! -f "$__object/parameter/directory" ] then exit 0 fi @@ -47,28 +47,26 @@ then elif [ -f "$__object/parameter/entry" ] then acl_should="$( cat "$__object/parameter/entry" )" -elif [ -f "$__object/parameter/acl" ] -then - acl_should="$( cat "$__object/parameter/acl" )" -elif - [ -f "$__object/parameter/user" ] \ - || [ -f "$__object/parameter/group" ] \ - || [ -f "$__object/parameter/mask" ] \ - || [ -f "$__object/parameter/other" ] -then - acl_should="$( for param in user group mask other - do - [ ! -f "$__object/parameter/$param" ] && continue - - echo "$param" | grep -Eq 'mask|other' && sep=:: || sep=: - - echo "$param$sep$( cat "$__object/parameter/$param" )" - done )" else echo 'no parameters set' >&2 exit 1 fi +# instead of setfacl's non-helpful message "Option -m: Invalid argument near character X" +# let's check if target has necessary users and groups, since mistyped or missing +# users/groups in target is most common reason. +echo "$acl_should" \ + | grep -Po '(user|group):[^:]+' \ + | sort -u \ + | while read -r l + do + if ! grep "$l" -Fxq "$__object/explorer/getent" + then + echo "no $l' in target" | sed "s/:/ '/" >&2 + exit 1 + fi + done + if [ -f "$__object/parameter/default" ] then acl_should="$( echo "$acl_should" \ diff --git a/cdist/conf/type/__acl/man.rst b/cdist/conf/type/__acl/man.rst index 28412871..307be72b 100644 --- a/cdist/conf/type/__acl/man.rst +++ b/cdist/conf/type/__acl/man.rst @@ -12,11 +12,14 @@ Fully supported and tested on Linux (ext4 filesystem), partial support for FreeB See ``setfacl`` and ``acl`` manpages for more details. +One of ``--entry`` or ``--source`` must be used. -REQUIRED MULTIPLE PARAMETERS + +OPTIONAL MULTIPLE PARAMETERS ---------------------------- entry Set ACL entry following ``getfacl`` output syntax. + Must be used if ``--source`` is not used. OPTIONAL PARAMETERS @@ -25,6 +28,7 @@ source Read ACL entries from stdin or file. Ordering of entries is not important. When reading from file, comments and empty lines are ignored. + Must be used if ``--entry`` is not used. file Create/change file with ``__file`` using ``user:group:mode`` pattern. @@ -48,12 +52,6 @@ remove ``mask`` and ``other`` entries can't be removed, but only changed. -DEPRECATED PARAMETERS ---------------------- -Parameters ``acl``, ``user``, ``group``, ``mask`` and ``other`` are deprecated and they -will be removed in future versions. Please use ``entry`` parameter instead. - - EXAMPLES -------- diff --git a/cdist/conf/type/__acl/parameter/deprecated/acl b/cdist/conf/type/__acl/parameter/deprecated/acl deleted file mode 100644 index 94e14159..00000000 --- a/cdist/conf/type/__acl/parameter/deprecated/acl +++ /dev/null @@ -1 +0,0 @@ -see manual for details diff --git a/cdist/conf/type/__acl/parameter/deprecated/group b/cdist/conf/type/__acl/parameter/deprecated/group deleted file mode 100644 index 94e14159..00000000 --- a/cdist/conf/type/__acl/parameter/deprecated/group +++ /dev/null @@ -1 +0,0 @@ -see manual for details diff --git a/cdist/conf/type/__acl/parameter/deprecated/mask b/cdist/conf/type/__acl/parameter/deprecated/mask deleted file mode 100644 index 94e14159..00000000 --- a/cdist/conf/type/__acl/parameter/deprecated/mask +++ /dev/null @@ -1 +0,0 @@ -see manual for details diff --git a/cdist/conf/type/__acl/parameter/deprecated/other b/cdist/conf/type/__acl/parameter/deprecated/other deleted file mode 100644 index 94e14159..00000000 --- a/cdist/conf/type/__acl/parameter/deprecated/other +++ /dev/null @@ -1 +0,0 @@ -see manual for details diff --git a/cdist/conf/type/__acl/parameter/deprecated/user b/cdist/conf/type/__acl/parameter/deprecated/user deleted file mode 100644 index 94e14159..00000000 --- a/cdist/conf/type/__acl/parameter/deprecated/user +++ /dev/null @@ -1 +0,0 @@ -see manual for details diff --git a/cdist/conf/type/__acl/parameter/optional b/cdist/conf/type/__acl/parameter/optional index cdcbc0b8..5a0c29a3 100644 --- a/cdist/conf/type/__acl/parameter/optional +++ b/cdist/conf/type/__acl/parameter/optional @@ -1,5 +1,3 @@ -mask -other source file directory diff --git a/cdist/conf/type/__acl/parameter/optional_multiple b/cdist/conf/type/__acl/parameter/optional_multiple index c615d507..4c884f03 100644 --- a/cdist/conf/type/__acl/parameter/optional_multiple +++ b/cdist/conf/type/__acl/parameter/optional_multiple @@ -1,4 +1 @@ entry -acl -user -group From 716cd37281903aafd7d65df539265a6722e809b1 Mon Sep 17 00:00:00 2001 From: Ander Punnar Date: Mon, 21 Sep 2020 11:18:39 +0300 Subject: [PATCH 068/366] [__update_alternatives] rewrite and support --install --- .../explorer/alternatives | 4 ++ .../type/__update_alternatives/explorer/link | 40 +++++++++++++++++++ .../__update_alternatives/explorer/path_is | 12 ++++++ .../explorer/path_should_state | 8 ++++ .../type/__update_alternatives/explorer/state | 8 ---- .../type/__update_alternatives/gencode-remote | 37 ++++++++++++++--- cdist/conf/type/__update_alternatives/man.rst | 13 ++++-- .../__update_alternatives/parameter/boolean | 1 + 8 files changed, 107 insertions(+), 16 deletions(-) create mode 100755 cdist/conf/type/__update_alternatives/explorer/alternatives create mode 100755 cdist/conf/type/__update_alternatives/explorer/link create mode 100755 cdist/conf/type/__update_alternatives/explorer/path_is create mode 100755 cdist/conf/type/__update_alternatives/explorer/path_should_state delete mode 100755 cdist/conf/type/__update_alternatives/explorer/state create mode 100644 cdist/conf/type/__update_alternatives/parameter/boolean diff --git a/cdist/conf/type/__update_alternatives/explorer/alternatives b/cdist/conf/type/__update_alternatives/explorer/alternatives new file mode 100755 index 00000000..34aaca56 --- /dev/null +++ b/cdist/conf/type/__update_alternatives/explorer/alternatives @@ -0,0 +1,4 @@ +#!/bin/sh -e + +update-alternatives --display "$__object_id" 2>/dev/null \ + | awk -F ' - ' '/priority [0-9]+$/ { print $1 }' diff --git a/cdist/conf/type/__update_alternatives/explorer/link b/cdist/conf/type/__update_alternatives/explorer/link new file mode 100755 index 00000000..6519e7c2 --- /dev/null +++ b/cdist/conf/type/__update_alternatives/explorer/link @@ -0,0 +1,40 @@ +#!/bin/sh -e + +# fedora's (update-)alternatives --display output doesn't have +# "link is " line, but debian does. so, let's find +# out how they store this information. +# +# debian and friends: +# https://salsa.debian.org/dpkg-team/dpkg/-/blob/master/utils/update-alternatives.c +# see calls to altdb_print_line function +# +# fedora and friends: +# https://github.com/fedora-sysv/chkconfig/blob/master/alternatives.c +# see calls to parseLine function +# +# conclusion: it is safe to assume that (master) link is on second line + +for altdir in \ + /var/lib/dpkg/alternatives \ + /var/lib/alternatives +do + if [ ! -f "$altdir/$__object_id" ] + then + continue + fi + + link="$( awk 'NR==2' "$altdir/$__object_id" )" + + if [ -n "$link" ] + then + break + fi +done + +if [ -z "$link" ] +then + echo "unable to get link for $__object_id" >&2 + exit 1 +fi + +echo "$link" diff --git a/cdist/conf/type/__update_alternatives/explorer/path_is b/cdist/conf/type/__update_alternatives/explorer/path_is new file mode 100755 index 00000000..fc304d5d --- /dev/null +++ b/cdist/conf/type/__update_alternatives/explorer/path_is @@ -0,0 +1,12 @@ +#!/bin/sh -e + +path_is="$( update-alternatives --display "$__object_id" 2>/dev/null \ + | awk '/link currently points to/ {print $5}' )" + +if [ -z "$path_is" ] +then + echo "unable to get current path for $__object_id" >&2 + exit 1 +fi + +echo "$path_is" diff --git a/cdist/conf/type/__update_alternatives/explorer/path_should_state b/cdist/conf/type/__update_alternatives/explorer/path_should_state new file mode 100755 index 00000000..59e015c5 --- /dev/null +++ b/cdist/conf/type/__update_alternatives/explorer/path_should_state @@ -0,0 +1,8 @@ +#!/bin/sh -e + +if [ -f "$( cat "$__object/parameter/path" )" ] +then + echo 'present' +else + echo 'absent' +fi diff --git a/cdist/conf/type/__update_alternatives/explorer/state b/cdist/conf/type/__update_alternatives/explorer/state deleted file mode 100755 index 04a78aaa..00000000 --- a/cdist/conf/type/__update_alternatives/explorer/state +++ /dev/null @@ -1,8 +0,0 @@ -#!/bin/sh -e -path="$(cat "$__object/parameter/path")" -name="$__object_id" -link="$(readlink "/etc/alternatives/$name")" -if [ "$path" = "$link" ] -then echo present -else echo absent -fi diff --git a/cdist/conf/type/__update_alternatives/gencode-remote b/cdist/conf/type/__update_alternatives/gencode-remote index c0b49814..e393cdef 100755 --- a/cdist/conf/type/__update_alternatives/gencode-remote +++ b/cdist/conf/type/__update_alternatives/gencode-remote @@ -1,6 +1,7 @@ #!/bin/sh -e # # 2013 Nico Schottelius (nico-cdist at schottelius.org) +# 2020 Ander Punnar (ander@kvlt.ee) # # This file is part of cdist. # @@ -16,12 +17,38 @@ # # You should have received a copy of the GNU General Public License # along with cdist. If not, see . -# -if [ "$(cat "$__object/explorer/state")" = 'present' ] -then exit 0 +path_is="$( cat "$__object/explorer/path_is" )" + +path_should="$( cat "$__object/parameter/path" )" + +if [ "$path_is" = "$path_should" ] +then + exit 0 +fi + +if [ "$( cat "$__object/explorer/path_should_state" )" = 'absent' ] && [ -z "$__cdist_dry_run" ] +then + echo "$path_should does not exist in target" >&2 + exit 1 fi -path="$(cat "$__object/parameter/path")" name="$__object_id" -echo "update-alternatives --quiet --set '$name' '$path'" + +alternatives="$( cat "$__object/explorer/alternatives" )" + +if ! echo "$alternatives" | grep -Fxq "$path_should" +then + if [ ! -f "$__object/parameter/install" ] + then + echo "$path_should is not in $name alternatives." >&2 + echo 'Please install missing packages or use --install to add path to alternatives.' >&2 + exit 1 + fi + + link="$( cat "$__object/explorer/link" )" + + echo "update-alternatives --install '$link' '$name' '$path_should' 1000" +fi + +echo "update-alternatives --set '$name' '$path_should'" diff --git a/cdist/conf/type/__update_alternatives/man.rst b/cdist/conf/type/__update_alternatives/man.rst index 73d82d11..0dc973f2 100644 --- a/cdist/conf/type/__update_alternatives/man.rst +++ b/cdist/conf/type/__update_alternatives/man.rst @@ -19,6 +19,12 @@ path Use this path for the given alternative +BOOLEAN PARAMETERS +------------------ +install + Add (``update-alternatives --install``) missing path to alternatives. + + EXAMPLES -------- @@ -36,11 +42,12 @@ SEE ALSO AUTHORS ------- Nico Schottelius +Ander Punnar COPYING ------- -Copyright \(C) 2013 Nico Schottelius. 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 +Copyright \(C) 2013 Nico Schottelius and 2020 Ander Punnar. 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. diff --git a/cdist/conf/type/__update_alternatives/parameter/boolean b/cdist/conf/type/__update_alternatives/parameter/boolean new file mode 100644 index 00000000..7c32f559 --- /dev/null +++ b/cdist/conf/type/__update_alternatives/parameter/boolean @@ -0,0 +1 @@ +install From 687c1d2dd9bed4130a65885d19ce31c87c9d79de Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Mon, 19 Oct 2020 06:57:00 +0200 Subject: [PATCH 069/366] ++changelog --- docs/changelog | 2 ++ 1 file changed, 2 insertions(+) diff --git a/docs/changelog b/docs/changelog index 8ad2c624..a0f1ead2 100644 --- a/docs/changelog +++ b/docs/changelog @@ -9,6 +9,8 @@ next: * Type __service: Fix calling __systemd_service (Mark Verboom) * Type __line: Add 'replace' state (Evil Ham) * Type __download: Fix man page (Matthias Stecher) + * Type __acl: Remove deprecated parameters, fix bugs (Ander Punnar) + * Type __update_alternatives: Rewrite, support --install (Ander Punnar) 6.8.0: 2020-09-11 * Type __locale_system: Fix for debian and ubuntu (Ander Punnar) From 367da4b77e79b82fe84c2ce1a7f75cfad92db81e Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Wed, 28 Oct 2020 18:18:24 +0100 Subject: [PATCH 070/366] [type/__file] Fix --state pre-exists --- cdist/conf/type/__file/gencode-remote | 5 ----- 1 file changed, 5 deletions(-) diff --git a/cdist/conf/type/__file/gencode-remote b/cdist/conf/type/__file/gencode-remote index 35356b13..2675b03a 100755 --- a/cdist/conf/type/__file/gencode-remote +++ b/cdist/conf/type/__file/gencode-remote @@ -87,11 +87,6 @@ case "$state_should" in fi ;; - pre-exists) - # pre-exists should never reach gencode-remote… - exit 1 - ;; - absent) if [ "$type" = "file" ]; then echo "rm -f '$destination'" From 9277e0ba19c346189658712acf17a772967dfa32 Mon Sep 17 00:00:00 2001 From: Darko Poljak Date: Thu, 29 Oct 2020 09:30:58 +0100 Subject: [PATCH 071/366] ++changelog --- docs/changelog | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/changelog b/docs/changelog index a0f1ead2..5977d80e 100644 --- a/docs/changelog +++ b/docs/changelog @@ -11,6 +11,7 @@ next: * Type __download: Fix man page (Matthias Stecher) * Type __acl: Remove deprecated parameters, fix bugs (Ander Punnar) * Type __update_alternatives: Rewrite, support --install (Ander Punnar) + * Type __file: Fix state pre-exists (Dennis Camera) 6.8.0: 2020-09-11 * Type __locale_system: Fix for debian and ubuntu (Ander Punnar) From 82a9aa79020ff2369b1f4d530a3a6d5ca41915f8 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Thu, 29 Oct 2020 10:39:42 +0100 Subject: [PATCH 072/366] [type/__apt_norecommends] Use 00InstallRecommends file as debian-installer does debian-installer can be preseeded with `base-installer/install-recommends` to disable installation of recommended packages already during OS installation. d-i will then create the file `/etc/apt/apt.conf.d/00InstallRecommends` (cf. https://salsa.debian.org/installer-team/base-installer/-/blob/master/library.sh). __apt_norecommends should use the same file to avoid having two config files effectively doing the same thing. --- cdist/conf/type/__apt_norecommends/man.rst | 9 +++-- cdist/conf/type/__apt_norecommends/manifest | 41 +++++++++++---------- 2 files changed, 27 insertions(+), 23 deletions(-) diff --git a/cdist/conf/type/__apt_norecommends/man.rst b/cdist/conf/type/__apt_norecommends/man.rst index 001fffe4..9297b518 100644 --- a/cdist/conf/type/__apt_norecommends/man.rst +++ b/cdist/conf/type/__apt_norecommends/man.rst @@ -32,11 +32,12 @@ EXAMPLES AUTHORS ------- Steven Armstrong +Dennis Camera COPYING ------- -Copyright \(C) 2014 Steven Armstrong. 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. +Copyright \(C) 2014 Steven Armstrong, 2020 Dennis Camera. +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. diff --git a/cdist/conf/type/__apt_norecommends/manifest b/cdist/conf/type/__apt_norecommends/manifest index e737df89..fc187784 100755 --- a/cdist/conf/type/__apt_norecommends/manifest +++ b/cdist/conf/type/__apt_norecommends/manifest @@ -1,6 +1,7 @@ #!/bin/sh -e # # 2014 Steven Armstrong (steven-cdist at armstrong.cc) +# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) # # This file is part of cdist. # @@ -19,26 +20,28 @@ # -os=$(cat "$__global/explorer/os") +os=$(cat "${__global:?}/explorer/os") -case "$os" in - ubuntu|debian|devuan) - # No stinking recommends thank you very much. - # If I want something installed I will do so myself. - __file /etc/apt/apt.conf.d/99-no-recommends \ - --owner root --group root --mode 644 \ - --source - << DONE -APT::Install-Recommends "0"; -APT::Install-Suggests "0"; -APT::AutoRemove::RecommendsImportant "0"; -APT::AutoRemove::SuggestsImportant "0"; -DONE - ;; - *) - cat >&2 << DONE +case ${os} +in + (ubuntu|debian|devuan) + __file /etc/apt/apt.conf.d/00InstallRecommends --state present \ + --owner root --group root --mode 0644 --source - <<-'EOF' + APT::Install-Recommends "false"; + APT::Install-Suggests "false"; + APT::AutoRemove::RecommendsImportant "false"; + APT::AutoRemove::SuggestsImportant "false"; + EOF + + # TODO: Remove the following object after some time + require=__file/etc/apt/apt.conf.d/00InstallRecommends \ + __file /etc/apt/apt.conf.d/99-no-recommends --state absent + ;; + (*) + cat >&2 < Date: Thu, 29 Oct 2020 18:03:27 +0100 Subject: [PATCH 073/366] [scanner] begin scanner implementation - non invasive --- cdist/scan.py | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 cdist/scan.py diff --git a/cdist/scan.py b/cdist/scan.py new file mode 100644 index 00000000..fa1bf5de --- /dev/null +++ b/cdist/scan.py @@ -0,0 +1,42 @@ +# -*- coding: utf-8 -*- +# +# 2020 Nico Schottelius (nico-cdist at schottelius.org) +# +# 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 . +# +# + +from scapy.all import * +from scapy.data import ETHER_TYPES + + +class Scanner(object): + def recv_msg_cpu(self, pkg): +# print(pkg.__repr__()) + if ICMPv6EchoReply in pkg: + host = pkg['IPv6'].src + print(f"Host {host} is alive") + + + def scan(self): + sniff(iface="wlan0", + filter="icmp6", + prn=self.recv_msg_cpu) + + +if __name__ == '__main__': + s = Scanner() + s.scan() From 87b46a622411362f441b62199972e71d85d7d2d6 Mon Sep 17 00:00:00 2001 From: Nico Schottelius Date: Thu, 29 Oct 2020 18:49:20 +0100 Subject: [PATCH 074/366] [scanner] finish prototype ping @poljakowski - it's your turn now --- cdist/scan.py | 138 +++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 131 insertions(+), 7 deletions(-) diff --git a/cdist/scan.py b/cdist/scan.py index fa1bf5de..e2100499 100644 --- a/cdist/scan.py +++ b/cdist/scan.py @@ -19,24 +19,148 @@ # # -from scapy.all import * -from scapy.data import ETHER_TYPES +# +# Interface to be implemented: +# - cdist scan --mode {scan, trigger, install, config}, --mode can be repeated +# scan: scan / listen for icmp6 replies +# trigger: send trigger to multicast +# config: configure newly detected hosts +# install: install newly detected hosts +# +# Scanner logic +# - save results to configdir: +# basedir = ~/.cdist/scan/ +# last_seen = ~/.cdist/scan//last_seen -- record unix time or similar +# last_configured = ~/.cdist/scan//last_configured -- record unix time or similar +# last_installed = ~/.cdist/scan//last_configured -- record unix time or similar +# +# +# +# +# cdist scan --list +# Show all known hosts including last seen flag +# +# Logic for reconfiguration: +# +# - record when configured last time +# - introduce a parameter --reconfigure-after that takes time argument +# - reconfigure if a) host alive and b) reconfigure-after time passed +# +from multiprocessing import Process +import os + +# FIXME: fail gracefully if non existent - i.e. "scapy required for scanner - please install python3-scapy" +from scapy.all import * + +# Datetime overwrites scapy.all.datetime - needs to be imported AFTER +import datetime + +class Trigger(object): + """ + Trigger an ICMPv6EchoReply from all hosts that are alive + """ + + def __init__(self, interfaces=None, verbose=False): + self.interfaces = interfaces + self.verbose = verbose + + # Wait 5 seconds before triggering again - FIXME: add parameter + self.sleeptime = 5 + + def start(self): + self.processes = [] + for interface in self.interfaces: + p = Process(target=self.run_interface, args=(interface,)) + self.processes.append(p) + p.start() + + def join(self): + for process in self.processes: + process.join() + + def run_interface(self, interface): + while True: + self.trigger(interface) + time.sleep(self.sleeptime) + + def trigger(self, interface): + packet = IPv6(dst=f"ff02::1%{interface}") / ICMPv6EchoRequest() + send(packet, verbose=self.verbose) + class Scanner(object): - def recv_msg_cpu(self, pkg): -# print(pkg.__repr__()) + """ + Scan for replies of hosts, maintain the up-to-date database + """ + + def __init__(self, interfaces=None, outdir=None): + self.interfaces = interfaces + + if outdir: + self.outdir = outdir + else: + self.outdir = "." + + def handle_pkg(self, pkg): if ICMPv6EchoReply in pkg: host = pkg['IPv6'].src print(f"Host {host} is alive") + dir = os.path.join(self.outdir, host) + fname = os.path.join(dir, "last_seen") + + now = datetime.datetime.now() + + os.makedirs(dir, exist_ok=True) + + # FIXME: maybe adjust the format so we can easily parse again + with open(fname, "w") as fd: + fd.write(f"{now}\n") + def scan(self): - sniff(iface="wlan0", + sniff(iface=self.interfaces, filter="icmp6", - prn=self.recv_msg_cpu) + prn=self.handle_pkg) if __name__ == '__main__': - s = Scanner() + t = Trigger(interfaces=["wlan0"]) + t.start() + + # Scanner can listen on many interfaces at the same time + s = Scanner(interfaces=["wlan0"]) s.scan() + + # Join back the trigger processes + t.join() + + # Test in my lan shows: + # [18:48] bridge:cdist% ls -1d fe80::* + # fe80::142d:f0a5:725b:1103 + # fe80::20d:b9ff:fe49:ac11 + # fe80::20d:b9ff:fe4c:547d + # fe80::219:d2ff:feb2:2e12 + # fe80::21b:fcff:feee:f446 + # fe80::21b:fcff:feee:f45c + # fe80::21b:fcff:feee:f4b1 + # fe80::21b:fcff:feee:f4ba + # fe80::21b:fcff:feee:f4bc + # fe80::21b:fcff:feee:f4c1 + # fe80::21d:72ff:fe86:46b + # fe80::42b0:34ff:fe6f:f6f0 + # fe80::42b0:34ff:fe6f:f863 + # fe80::42b0:34ff:fe6f:f9b2 + # fe80::4a5d:60ff:fea1:e55f + # fe80::77a3:5e3f:82cc:f2e5 + # fe80::9e93:4eff:fe6c:c1f4 + # fe80::ba69:f4ff:fec5:6041 + # fe80::ba69:f4ff:fec5:8db7 + # fe80::bad8:12ff:fe65:313d + # fe80::bad8:12ff:fe65:d9b1 + # fe80::ce2d:e0ff:fed4:2611 + # fe80::ce32:e5ff:fe79:7ea7 + # fe80::d66d:6dff:fe33:e00 + # fe80::e2ff:f7ff:fe00:20e6 + # fe80::f29f:c2ff:fe7c:275e From 91d99bf08accb3bde0da420294fc9fb68d3d4068 Mon Sep 17 00:00:00 2001 From: Nico Schottelius Date: Thu, 29 Oct 2020 21:22:36 +0100 Subject: [PATCH 075/366] [RFC] scanner documentation --- docs/dev/logs/2020-10-29.org | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 docs/dev/logs/2020-10-29.org diff --git a/docs/dev/logs/2020-10-29.org b/docs/dev/logs/2020-10-29.org new file mode 100644 index 00000000..718fd68c --- /dev/null +++ b/docs/dev/logs/2020-10-29.org @@ -0,0 +1,34 @@ +* The scanner, 2020-10-29, Hacking Villa Diesbach +** Motivation + - The purpose of cdist is to ensure systems are in a configured state + - If systems reboot into a clean (think: netboot) state they are + stuck in an unconfigured mode + - We can either trigger *from* those machines + - this is what cdist trigger is for + - Or we can regulary *scan* for machines + - This method does not need any modification to standard OS +** How it works + - cdist scan uses the all nodes multicast group ff02::1 + - It sends a ping packet there in regular intervals + - This even works in non-IPv6 networks, as all operating systems + are IPv6 capable and usually IPv6 enabled by default + - Link local is always accessible! + - cdist scan receives an answer from all alive hosts + - These results are stored in ~/.cdist/scan/${hostip} + - We record the last_seen date ~/.cdist/scan/${hostip}/last_seen + - After a host is detected, cdist *can* try to configure it + - It saves the result (+/- logging needs to be defined) in + ~/.cdist/scan/${hostip}/{config, install}_result + - If logging is saved: maybe in ~/.cdist/scan/${hostip}/{config, install}_log + - Final naming TBD +** Benefits from the scanning approach + - We know when a host is alive/dead + - We can use standard OS w/o trigger customisation + - Only requirement: we can ssh into it + - Can make use f.i. of Alpine Linux w/ ssh keys feeding in + - We can trigger regular reconfiguration + - If alive && last_config_time > 1d -> reconfigure + - Data can be exported to f.i. prometheus + - Record when configured (successfully) + - Record when seen + - Enables configurations in stateless environments From 09dfcfe81e0d9e6520d9bf98598037f86c84641e Mon Sep 17 00:00:00 2001 From: Nico Schottelius Date: Thu, 29 Oct 2020 23:16:08 +0100 Subject: [PATCH 076/366] [scanner] add to beta commands --- cdist/argparse.py | 32 ++++++++++++++++++++- cdist/scan/commandline.py | 55 ++++++++++++++++++++++++++++++++++++ cdist/{ => scan}/scan.py | 20 +++++++++---- docs/dev/logs/2020-10-29.org | 23 +++++++++++++++ 4 files changed, 124 insertions(+), 6 deletions(-) create mode 100644 cdist/scan/commandline.py rename cdist/{ => scan}/scan.py (90%) diff --git a/cdist/argparse.py b/cdist/argparse.py index 1d16bb25..ff195e8c 100644 --- a/cdist/argparse.py +++ b/cdist/argparse.py @@ -8,10 +8,11 @@ import cdist.configuration import cdist.log import cdist.preos import cdist.info +import cdist.scan.commandline # set of beta sub-commands -BETA_COMMANDS = set(('install', 'inventory', )) +BETA_COMMANDS = set(('install', 'inventory', 'scan', )) # set of beta arguments for sub-commands BETA_ARGS = { 'config': set(('tag', 'all_tagged_hosts', 'use_archiving', )), @@ -470,6 +471,35 @@ def get_parsers(): 'pattern', nargs='?', help='Glob pattern.') parser['info'].set_defaults(func=cdist.info.Info.commandline) + # Scan = config + further + parser['scan'] = parser['sub'].add_parser('scan', add_help=False, + parents=[parser['config']]) + + parser['scan'] = parser['sub'].add_parser( + 'scan', parents=[parser['loglevel'], + parser['beta'], + parser['colored_output'], + parser['common'], + parser['config_main']]) + + parser['scan'].add_argument( + '-m', '--mode', help='Which modes should run', + action='append', default=[], + choices=['scan', 'trigger']) + parser['scan'].add_argument( + '--config', + action='store_true', + help='Try to configure detected hosts') + parser['scan'].add_argument( + '-I', '--interfaces', + action='append', default=[], + help='On which interfaces to scan/trigger') + parser['scan'].add_argument( + '-d', '--delay', + action='store', default=3600, + help='How long to wait before reconfiguring after last try') + parser['scan'].set_defaults(func=cdist.scan.commandline.commandline) + for p in parser: parser[p].epilog = EPILOG diff --git a/cdist/scan/commandline.py b/cdist/scan/commandline.py new file mode 100644 index 00000000..0fb718e5 --- /dev/null +++ b/cdist/scan/commandline.py @@ -0,0 +1,55 @@ +# -*- coding: utf-8 -*- +# +# 2020 Nico Schottelius (nico-cdist at schottelius.org) +# +# 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 . +# +# + +import logging + +log = logging.getLogger("scan") + + +# define this outside of the class to not handle scapy import errors by default +def commandline(args): + log.debug(args) + + try: + import cdist.scan.scan as scan + except ModuleNotFoundError: + print('cdist scan requires scapy to be installed') + + processes = [] + + if not args.mode: + # By default scan and trigger, but do not call any action + args.mode = ['scan', 'trigger' ] + + if 'trigger' in args.mode: + t = scan.Trigger(interfaces=args.interfaces) + t.start() + processes.append(t) + log.debug("Trigger started") + + if 'scan' in args.mode: + s = scan.Scanner(interfaces=args.interfaces, args=args) + s.start() + processes.append(s) + log.debug("Scanner started") + + for process in processes: + process.join() diff --git a/cdist/scan.py b/cdist/scan/scan.py similarity index 90% rename from cdist/scan.py rename to cdist/scan/scan.py index e2100499..fcbf1899 100644 --- a/cdist/scan.py +++ b/cdist/scan/scan.py @@ -50,13 +50,14 @@ from multiprocessing import Process import os - -# FIXME: fail gracefully if non existent - i.e. "scapy required for scanner - please install python3-scapy" +import logging from scapy.all import * # Datetime overwrites scapy.all.datetime - needs to be imported AFTER import datetime +log = logging.getLogger("scan") + class Trigger(object): """ Trigger an ICMPv6EchoReply from all hosts that are alive @@ -87,6 +88,7 @@ class Trigger(object): def trigger(self, interface): packet = IPv6(dst=f"ff02::1%{interface}") / ICMPv6EchoRequest() + log.debug(f"Sending request on {interface}") send(packet, verbose=self.verbose) class Scanner(object): @@ -94,18 +96,18 @@ class Scanner(object): Scan for replies of hosts, maintain the up-to-date database """ - def __init__(self, interfaces=None, outdir=None): + def __init__(self, interfaces=None, args=None, outdir=None): self.interfaces = interfaces if outdir: self.outdir = outdir else: - self.outdir = "." + self.outdir = os.path.join(os.environ['HOME'], '.cdist', 'scan') def handle_pkg(self, pkg): if ICMPv6EchoReply in pkg: host = pkg['IPv6'].src - print(f"Host {host} is alive") + log.verbose(f"Host {host} is alive") dir = os.path.join(self.outdir, host) fname = os.path.join(dir, "last_seen") @@ -118,13 +120,21 @@ class Scanner(object): with open(fname, "w") as fd: fd.write(f"{now}\n") + def start(self): + self.process = Process(target=self.scan) + self.process.start() + + def join(self): + self.process.join() def scan(self): + log.debug("Scanning - zzzzz") sniff(iface=self.interfaces, filter="icmp6", prn=self.handle_pkg) + if __name__ == '__main__': t = Trigger(interfaces=["wlan0"]) t.start() diff --git a/docs/dev/logs/2020-10-29.org b/docs/dev/logs/2020-10-29.org index 718fd68c..4461be8c 100644 --- a/docs/dev/logs/2020-10-29.org +++ b/docs/dev/logs/2020-10-29.org @@ -32,3 +32,26 @@ - Record when configured (successfully) - Record when seen - Enables configurations in stateless environments +** Sample output v2020-10-29 +23:14] bridge:~% sudo cdist scan -b -I wlan0 -vv +VERBOSE: cdist: version 6.8.0-36-g91d99bf0 +VERBOSE: scan: Host fe80::21d:72ff:fe86:46b is alive +VERBOSE: scan: Host fe80::ce2d:e0ff:fed4:2611 is alive +VERBOSE: scan: Host fe80::21b:fcff:feee:f4c1 is alive +VERBOSE: scan: Host fe80::e2ff:f7ff:fe00:20e6 is alive +VERBOSE: scan: Host fe80::20d:b9ff:fe49:ac11 is alive +VERBOSE: scan: Host fe80::9e93:4eff:fe6c:c1f4 is alive +VERBOSE: scan: Host fe80::ce32:e5ff:fe79:7ea7 is alive +VERBOSE: scan: Host fe80::219:d2ff:feb2:2e12 is alive +VERBOSE: scan: Host fe80::d66d:6dff:fe33:e00 is alive +VERBOSE: scan: Host fe80::21b:fcff:feee:f446 is alive +VERBOSE: scan: Host fe80::21b:fcff:feee:f4b1 is alive +VERBOSE: scan: Host fe80::20d:b9ff:fe4c:547d is alive +VERBOSE: scan: Host fe80::bad8:12ff:fe65:313d is alive +VERBOSE: scan: Host fe80::42b0:34ff:fe6f:f6f0 is alive +VERBOSE: scan: Host fe80::ba69:f4ff:fec5:6041 is alive +VERBOSE: scan: Host fe80::f29f:c2ff:fe7c:275e is alive +VERBOSE: scan: Host fe80::ba69:f4ff:fec5:8db7 is alive +VERBOSE: scan: Host fe80::42b0:34ff:fe6f:f863 is alive +VERBOSE: scan: Host fe80::21b:fcff:feee:f4bc is alive +... From e30ecdda535200ac75a6e774590d2e1b5e1b5b28 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Wed, 3 Jun 2020 13:05:40 +0200 Subject: [PATCH 077/366] Add __uci and __uci_commit types --- cdist/conf/type/__uci/explorer/state | 89 +++++++++++++++++++ cdist/conf/type/__uci/gencode-remote | 67 ++++++++++++++ cdist/conf/type/__uci/man.rst | 77 ++++++++++++++++ cdist/conf/type/__uci/manifest | 40 +++++++++ cdist/conf/type/__uci/parameter/default/state | 1 + .../type/__uci/parameter/default/transaction | 1 + cdist/conf/type/__uci/parameter/optional | 2 + .../type/__uci/parameter/required_multiple | 1 + cdist/conf/type/__uci_commit/gencode-remote | 21 +++++ cdist/conf/type/__uci_commit/man.rst | 58 ++++++++++++ cdist/conf/type/__uci_commit/nonparallel | 0 11 files changed, 357 insertions(+) create mode 100644 cdist/conf/type/__uci/explorer/state create mode 100755 cdist/conf/type/__uci/gencode-remote create mode 100644 cdist/conf/type/__uci/man.rst create mode 100755 cdist/conf/type/__uci/manifest create mode 100644 cdist/conf/type/__uci/parameter/default/state create mode 100644 cdist/conf/type/__uci/parameter/default/transaction create mode 100644 cdist/conf/type/__uci/parameter/optional create mode 100644 cdist/conf/type/__uci/parameter/required_multiple create mode 100755 cdist/conf/type/__uci_commit/gencode-remote create mode 100644 cdist/conf/type/__uci_commit/man.rst create mode 100644 cdist/conf/type/__uci_commit/nonparallel diff --git a/cdist/conf/type/__uci/explorer/state b/cdist/conf/type/__uci/explorer/state new file mode 100644 index 00000000..2e98b606 --- /dev/null +++ b/cdist/conf/type/__uci/explorer/state @@ -0,0 +1,89 @@ +#!/bin/sh +# +# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) +# +# 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 . +# +# This explorer retrieves the current state of the configuration option +# The output of this explorer is one of these values: +# present +# The configuration option is present and has the value of the +# parameter --value. +# absent +# The configuration option is not defined. +# different +# The configuration option is present but has a different value than the +# parameter --value. +# rearranged +# The configuration option is present (a list) and has the same values as +# the parameter --value, but in a different order. + +RS=$(printf '\036') + +option=${__object_id:?} + +values_is=$(uci -s -N -d "${RS}" get "${option}" 2>/dev/null) || { + echo absent + exit 0 +} + +# strip off trailing newline +printf '%s' "${values_is}" \ +| awk ' +BEGIN { + state = "present" # assume all is fine +} +NR == FNR { + # memoize "should" state + should[FNR] = $0 + + # go to next line (important!) + next +} + +# compare "is" state +$0 == should[FNR] { next } + +FNR > length(should) { + # there are more "is" records than "should" -> definitely different + state = "different" + exit +} + +{ + # see if we can find the value somewhere in should + for (i in should) { + if ($0 == should[i]) { + # ... value found -> rearranged + # FIXME: Duplicate values are not properly handled here. Do they matter? + state = "rearranged" + next + } + } + + state = "different" + exit +} + +END { + if (FNR < length(should)) { + # "is" was shorter than "should" -> different + state = "different" + } + + print state +} +' "${__object:?}/parameter/value" RS="${RS}" - diff --git a/cdist/conf/type/__uci/gencode-remote b/cdist/conf/type/__uci/gencode-remote new file mode 100755 index 00000000..48f114fe --- /dev/null +++ b/cdist/conf/type/__uci/gencode-remote @@ -0,0 +1,67 @@ +#!/bin/sh -e +# +# 2020 Dennis Camera (dennis.camera@ssrq-sds-fds.ch) +# +# 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 . +# + +in_list() { printf '%s\n' "$@" | { grep -qxF "$(read -r NDL; echo "${NDL}")"; } } + +config=${__object_id:?} + +state_is=$(cat "${__object:?}/explorer/state") +state_should=$(cat "${__object:?}/parameter/state") + +case ${state_should} +in + (present) + if in_list "${state_is}" 'present' 'rearranged' + then + # NOTE: order is ignored so rearranged is also fine. + exit 0 + fi + + if test "$(wc -l "${__object:?}/parameter/value")" -gt 1 + then + # "should" is a list + if test "${state_is}" != 'absent' + then + printf "uci delete '%s'\n" "${config}" + fi + + while read -r value + do + printf "uci add_list '%s'='%s'\n" "${config}" "${value}" + done <"${__object:?}/parameter/value" + else + # "should" is a scalar + value=$(cat "${__object:?}/parameter/value") + printf "uci set '%s'='%s'\n" "${config}" "${value}" + fi + ;; + (absent) + if in_list "${state_is}" 'absent' + then + exit 0 + fi + + printf "uci delete '%s'\n" "${config}" + ;; + (*) + printf 'Invalid --state: %s\n' "${state_should}" >&2 + exit 1 + ;; +esac diff --git a/cdist/conf/type/__uci/man.rst b/cdist/conf/type/__uci/man.rst new file mode 100644 index 00000000..d23d8b2b --- /dev/null +++ b/cdist/conf/type/__uci/man.rst @@ -0,0 +1,77 @@ +cdist-type__uci(7) +================== + +NAME +---- +cdist-type__uci - Manage configuration values in OpenWrt's +Unified Configuration Interface (UCI) + + +DESCRIPTION +----------- +This cdist type can be used to alter configuration options in OpenWrt's UCI +system. + +Options can be applied in batches if the `--transaction` parameter is used. +It is important to ensure that the `__uci_commit` object is executed before a +new transaction is started. + +REQUIRED PARAMETERS +------------------- +value + The value to be set. Can be used multiple times. + + Due to the way cdist handles arguments, values **must not** contain newline + characters. + + +OPTIONAL PARAMETERS +------------------- +state + `present` or `absent`, defaults to `present`. +transaction + The name of the transaction this option belongs to. + If none is given: "default" is used. + + +BOOLEAN PARAMETERS +------------------ +None. + + +EXAMPLES +-------- + +.. code-block:: sh + + # Set the system hostname + __uci system.@system[0].hostname --value 'OpenWrt' + + # Enable NTP and NTPd (in one transaction) + __uci system.ntp.enabled --value 1 --transaction ntp + __uci system.ntp.enable_server --value 1 --transaction ntp + __uci system.ntp.server --transaction ntp \ + --value '0.openwrt.pool.ntp.org' \ + --value '1.openwrt.pool.ntp.org' \ + --value '2.openwrt.pool.ntp.org' \ + --value '3.openwrt.pool.ntp.org' + export require=__uci_commit/ntp + + +SEE ALSO +-------- +- https://openwrt.org/docs/guide-user/base-system/uci +- :strong:`cdist-type__uci_commit`\ (7) + + +AUTHORS +------- +Dennis Camera + + +COPYING +------- +Copyright \(C) 2020 Dennis Camera. 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. diff --git a/cdist/conf/type/__uci/manifest b/cdist/conf/type/__uci/manifest new file mode 100755 index 00000000..e5b0fb30 --- /dev/null +++ b/cdist/conf/type/__uci/manifest @@ -0,0 +1,40 @@ +#!/bin/sh -e +# +# 2020 Dennis Camera (dennis.camera@ssrq-sds-fds.ch) +# +# 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 . +# + + +os=$(cat "${__global:?}/explorer/os") + +transaction_name=$(cat "${__object:?}/parameter/transaction") + +case ${os} +in + (openwrt) + # okay + ;; + (*) + printf "Your operating system (%s) is currently not supported by this type (%s)\n" "${os}" "${__type##*/}" >&2 + printf "Please contribute an implementation for it if you can.\n" >&2 + exit 1 + ;; +esac + + +# Make sure the changes are being commited +require=${__object_name:?} __uci_commit "${transaction_name}" diff --git a/cdist/conf/type/__uci/parameter/default/state b/cdist/conf/type/__uci/parameter/default/state new file mode 100644 index 00000000..e7f6134f --- /dev/null +++ b/cdist/conf/type/__uci/parameter/default/state @@ -0,0 +1 @@ +present diff --git a/cdist/conf/type/__uci/parameter/default/transaction b/cdist/conf/type/__uci/parameter/default/transaction new file mode 100644 index 00000000..4ad96d51 --- /dev/null +++ b/cdist/conf/type/__uci/parameter/default/transaction @@ -0,0 +1 @@ +default diff --git a/cdist/conf/type/__uci/parameter/optional b/cdist/conf/type/__uci/parameter/optional new file mode 100644 index 00000000..ddbbba16 --- /dev/null +++ b/cdist/conf/type/__uci/parameter/optional @@ -0,0 +1,2 @@ +state +transaction diff --git a/cdist/conf/type/__uci/parameter/required_multiple b/cdist/conf/type/__uci/parameter/required_multiple new file mode 100644 index 00000000..6d4e1507 --- /dev/null +++ b/cdist/conf/type/__uci/parameter/required_multiple @@ -0,0 +1 @@ +value diff --git a/cdist/conf/type/__uci_commit/gencode-remote b/cdist/conf/type/__uci_commit/gencode-remote new file mode 100755 index 00000000..bed0eefb --- /dev/null +++ b/cdist/conf/type/__uci_commit/gencode-remote @@ -0,0 +1,21 @@ +#!/bin/sh -e +# +# 2020 Dennis Camera (dennis.camera@ssrq-sds-fds.ch) +# +# 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 . +# + +echo 'uci commit' diff --git a/cdist/conf/type/__uci_commit/man.rst b/cdist/conf/type/__uci_commit/man.rst new file mode 100644 index 00000000..c55d06e5 --- /dev/null +++ b/cdist/conf/type/__uci_commit/man.rst @@ -0,0 +1,58 @@ +cdist-type__uci_commit(7) +========================= + +NAME +---- +cdist-type__uci_commit - Commit a UCI transaction. + + +DESCRIPTION +----------- +This type executes the "uci commit" command on the target. +It is usually not required to use this type. Use the `--transaction` parameter +of `cdist-type__uci`\ (7) instead. + + +REQUIRED PARAMETERS +------------------- +None. + + +OPTIONAL PARAMETERS +------------------- +None. + + +BOOLEAN PARAMETERS +------------------ +None. + + +EXAMPLES +-------- + +.. code-block:: sh + + # Commit the default transaction + __uci_commit default + + # Commit another transaction + __uci_commit my_transaction + + +SEE ALSO +-------- +:strong:`cdist-type__uci`\ (7) + + +AUTHORS +------- +Dennis Camera + + +COPYING +------- +Copyright \(C) 2020 Dennis Camera. 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. diff --git a/cdist/conf/type/__uci_commit/nonparallel b/cdist/conf/type/__uci_commit/nonparallel new file mode 100644 index 00000000..e69de29b From 55e7b32449c9a86e34054c321b20a49795c6652f Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Wed, 3 Jun 2020 14:00:29 +0200 Subject: [PATCH 078/366] [type/__uci] Only generate __uci_commit if changes are required --- cdist/conf/type/__uci/manifest | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/cdist/conf/type/__uci/manifest b/cdist/conf/type/__uci/manifest index e5b0fb30..74524513 100755 --- a/cdist/conf/type/__uci/manifest +++ b/cdist/conf/type/__uci/manifest @@ -18,9 +18,12 @@ # along with cdist. If not, see . # +in_list() { printf '%s\n' "$@" | { grep -qxF "$(read -r ndl; echo "${ndl}")"; } } os=$(cat "${__global:?}/explorer/os") +state_is=$(cat "${__object:?}/explorer/state") +state_should=$(cat "${__object:?}/parameter/state") transaction_name=$(cat "${__object:?}/parameter/transaction") case ${os} @@ -35,6 +38,25 @@ in ;; esac +changes_required=false -# Make sure the changes are being commited -require=${__object_name:?} __uci_commit "${transaction_name}" +case ${state_should} +in + (present) + # NOTE: order is ignored so rearranged is also fine. + in_list "${state_is}" 'present' 'rearranged' || changes_required=true + ;; + (absent) + in_list "${state_is}" 'absent' || changes_required=true + ;; + (*) + printf 'Invalid --state: %s\n' "${state_should}" >&2 + exit 1 + ;; +esac + +if ${changes_required} +then + # Make sure the changes are being committed + require=${__object_name:?} __uci_commit "${transaction_name}" +fi From a09120977f231fe3dda1c7c07073fc7e03fc5ea0 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Wed, 3 Jun 2020 14:07:10 +0200 Subject: [PATCH 079/366] [type/__uci] Allow omission of --value parameter if --state absent --- cdist/conf/type/__uci/explorer/state | 10 +++++++++- cdist/conf/type/__uci/man.rst | 1 + cdist/conf/type/__uci/manifest | 5 +++++ .../parameter/{required_multiple => optional_multiple} | 0 4 files changed, 15 insertions(+), 1 deletion(-) rename cdist/conf/type/__uci/parameter/{required_multiple => optional_multiple} (100%) diff --git a/cdist/conf/type/__uci/explorer/state b/cdist/conf/type/__uci/explorer/state index 2e98b606..6fb4b173 100644 --- a/cdist/conf/type/__uci/explorer/state +++ b/cdist/conf/type/__uci/explorer/state @@ -40,6 +40,14 @@ values_is=$(uci -s -N -d "${RS}" get "${option}" 2>/dev/null) || { exit 0 } +if test -f "${__object:?}/parameter/value" +then + should_file="${__object:?}/parameter/value" +else + should_file='/dev/null' +fi + + # strip off trailing newline printf '%s' "${values_is}" \ | awk ' @@ -86,4 +94,4 @@ END { print state } -' "${__object:?}/parameter/value" RS="${RS}" - +' "${should_file}" RS="${RS}" - diff --git a/cdist/conf/type/__uci/man.rst b/cdist/conf/type/__uci/man.rst index d23d8b2b..c6bb81ab 100644 --- a/cdist/conf/type/__uci/man.rst +++ b/cdist/conf/type/__uci/man.rst @@ -20,6 +20,7 @@ REQUIRED PARAMETERS ------------------- value The value to be set. Can be used multiple times. + This parameter is allowed to be omitted if `--state` is `absent`. Due to the way cdist handles arguments, values **must not** contain newline characters. diff --git a/cdist/conf/type/__uci/manifest b/cdist/conf/type/__uci/manifest index 74524513..e56462e5 100755 --- a/cdist/conf/type/__uci/manifest +++ b/cdist/conf/type/__uci/manifest @@ -43,6 +43,11 @@ changes_required=false case ${state_should} in (present) + test -s "${__object:?}/parameter/value" || { + echo 'The parameter --value is required.' >&2 + exit 1 + } + # NOTE: order is ignored so rearranged is also fine. in_list "${state_is}" 'present' 'rearranged' || changes_required=true ;; diff --git a/cdist/conf/type/__uci/parameter/required_multiple b/cdist/conf/type/__uci/parameter/optional_multiple similarity index 100% rename from cdist/conf/type/__uci/parameter/required_multiple rename to cdist/conf/type/__uci/parameter/optional_multiple From d8f20a6a204142360e7fb7afd6603c69ae613e2d Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Fri, 12 Jun 2020 19:39:19 +0200 Subject: [PATCH 080/366] [type/__uci] Implement "real" transactions using batch files --- .../__uci/{gencode-remote => gencode-local} | 22 ++++++++++++++++--- cdist/conf/type/__uci/man.rst | 3 +-- cdist/conf/type/__uci_commit/gencode-remote | 21 +++++++++++++++++- 3 files changed, 40 insertions(+), 6 deletions(-) rename cdist/conf/type/__uci/{gencode-remote => gencode-local} (79%) diff --git a/cdist/conf/type/__uci/gencode-remote b/cdist/conf/type/__uci/gencode-local similarity index 79% rename from cdist/conf/type/__uci/gencode-remote rename to cdist/conf/type/__uci/gencode-local index 48f114fe..bba5944d 100755 --- a/cdist/conf/type/__uci/gencode-remote +++ b/cdist/conf/type/__uci/gencode-local @@ -20,11 +20,27 @@ in_list() { printf '%s\n' "$@" | { grep -qxF "$(read -r NDL; echo "${NDL}")"; } } +uci_cmd() { + printf 'printf "%s\n"' "$1" + shift + printf " '%s'" "$@" + printf " >>'%s'\n" "${tmpfile}" +} + config=${__object_id:?} state_is=$(cat "${__object:?}/explorer/state") state_should=$(cat "${__object:?}/parameter/state") +transaction_name=$(cat "${__object:?}/parameter/transaction") + +tmpdir="${__global:?}/tmp/__uci" +# HACK +mkdir -p "${tmpdir}" + +tmpfile="${tmpdir}/${transaction_name}.txt" + + case ${state_should} in (present) @@ -44,12 +60,12 @@ in while read -r value do - printf "uci add_list '%s'='%s'\n" "${config}" "${value}" + uci_cmd "add_list '%s'='%s'" "${config}" "${value}" done <"${__object:?}/parameter/value" else # "should" is a scalar value=$(cat "${__object:?}/parameter/value") - printf "uci set '%s'='%s'\n" "${config}" "${value}" + uci_cmd "set '%s'='%s'" "${config}" "${value}" fi ;; (absent) @@ -58,7 +74,7 @@ in exit 0 fi - printf "uci delete '%s'\n" "${config}" + uci_cmd "delete '%s'" "${config}" ;; (*) printf 'Invalid --state: %s\n' "${state_should}" >&2 diff --git a/cdist/conf/type/__uci/man.rst b/cdist/conf/type/__uci/man.rst index c6bb81ab..2f64355b 100644 --- a/cdist/conf/type/__uci/man.rst +++ b/cdist/conf/type/__uci/man.rst @@ -13,8 +13,7 @@ This cdist type can be used to alter configuration options in OpenWrt's UCI system. Options can be applied in batches if the `--transaction` parameter is used. -It is important to ensure that the `__uci_commit` object is executed before a -new transaction is started. + REQUIRED PARAMETERS ------------------- diff --git a/cdist/conf/type/__uci_commit/gencode-remote b/cdist/conf/type/__uci_commit/gencode-remote index bed0eefb..ac74e5e4 100755 --- a/cdist/conf/type/__uci_commit/gencode-remote +++ b/cdist/conf/type/__uci_commit/gencode-remote @@ -18,4 +18,23 @@ # along with cdist. If not, see . # -echo 'uci commit' +transaction_name=${__object_id:?} +batchfile="${__global:?}/tmp/__uci/${transaction_name}.txt" + +test -s "${batchfile}" || exit 0 + +cat <<'EOF' +rollback() { + uci changes \ + | sed -e 's/\..*$//' -e 's/^-//' \ + | while read -r package + do + uci revert "${package}" + done +} + +EOF + +echo "uci batch <<'EOF' && uci commit || rollback" +cat "${batchfile}" +echo 'EOF' From d3574b2d3e807ae436afd90aa1ed9d01b78ca6a3 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Sat, 20 Jun 2020 16:43:16 +0200 Subject: [PATCH 081/366] [type/__uci] Send messages when options are set to be altered --- cdist/conf/type/__uci/gencode-local | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/cdist/conf/type/__uci/gencode-local b/cdist/conf/type/__uci/gencode-local index bba5944d..ba27dc5e 100755 --- a/cdist/conf/type/__uci/gencode-local +++ b/cdist/conf/type/__uci/gencode-local @@ -53,6 +53,8 @@ in if test "$(wc -l "${__object:?}/parameter/value")" -gt 1 then # "should" is a list + printf 'set_list %s\n' "${config}" >>"${__messages_out:?}" + if test "${state_is}" != 'absent' then printf "uci delete '%s'\n" "${config}" @@ -64,6 +66,8 @@ in done <"${__object:?}/parameter/value" else # "should" is a scalar + printf 'set %s\n' "${config}" >>"${__messages_out:?}" + value=$(cat "${__object:?}/parameter/value") uci_cmd "set '%s'='%s'" "${config}" "${value}" fi @@ -74,6 +78,7 @@ in exit 0 fi + printf 'delete %s\n' "${config}" >>"${__messages_out:?}" uci_cmd "delete '%s'" "${config}" ;; (*) From 3a3be3631010b1bf17d084c775e306f11429f5d9 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Fri, 12 Jun 2020 19:53:26 +0200 Subject: [PATCH 082/366] [type/__uci_commit] Send message on commit of a transaction --- cdist/conf/type/__uci_commit/gencode-remote | 2 ++ 1 file changed, 2 insertions(+) diff --git a/cdist/conf/type/__uci_commit/gencode-remote b/cdist/conf/type/__uci_commit/gencode-remote index ac74e5e4..0332d7e0 100755 --- a/cdist/conf/type/__uci_commit/gencode-remote +++ b/cdist/conf/type/__uci_commit/gencode-remote @@ -23,6 +23,8 @@ batchfile="${__global:?}/tmp/__uci/${transaction_name}.txt" test -s "${batchfile}" || exit 0 +printf 'commit transaction %s\n' "${transaction_name}" >>"${__messages_out:?}" + cat <<'EOF' rollback() { uci changes \ From e7369a1f9957ac31830a69de8101c434666da5da Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Wed, 17 Jun 2020 13:00:24 +0200 Subject: [PATCH 083/366] [type/__uci_commit] Abort if uncommited changes are present on the target --- cdist/conf/type/__uci_commit/explorer/changes | 22 ++++++++++++++++ cdist/conf/type/__uci_commit/gencode-remote | 25 +++++++++++++------ 2 files changed, 39 insertions(+), 8 deletions(-) create mode 100644 cdist/conf/type/__uci_commit/explorer/changes diff --git a/cdist/conf/type/__uci_commit/explorer/changes b/cdist/conf/type/__uci_commit/explorer/changes new file mode 100644 index 00000000..2690def7 --- /dev/null +++ b/cdist/conf/type/__uci_commit/explorer/changes @@ -0,0 +1,22 @@ +#!/bin/sh +# +# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) +# +# 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 . +# +# This explorer outputs the uncommited UCI changes on the target. + +uci changes diff --git a/cdist/conf/type/__uci_commit/gencode-remote b/cdist/conf/type/__uci_commit/gencode-remote index 0332d7e0..dd1d839c 100755 --- a/cdist/conf/type/__uci_commit/gencode-remote +++ b/cdist/conf/type/__uci_commit/gencode-remote @@ -23,20 +23,29 @@ batchfile="${__global:?}/tmp/__uci/${transaction_name}.txt" test -s "${batchfile}" || exit 0 +if test -s "${__object:?}/explorer/changes" +then + echo 'Uncommited UCI changes were found on the target:' + cat "${__object:?}/explorer/changes" + echo + echo 'This can be caused by manual changes or due to a previous failed run.' + echo 'Please investigate the situation, revert or commit the changes, and try again.' + exit 1 +fi >&2 + printf 'commit transaction %s\n' "${transaction_name}" >>"${__messages_out:?}" -cat <<'EOF' +cat < Date: Sun, 5 Jul 2020 10:25:36 +0200 Subject: [PATCH 084/366] [type/__uci_commit] Move uncommited changes check from explorer to code-remote This is done to prevent false positives/negatives (see NOTE in code) --- cdist/conf/type/__uci_commit/explorer/changes | 22 ------------------- cdist/conf/type/__uci_commit/gencode-remote | 21 +++++++++++++----- 2 files changed, 15 insertions(+), 28 deletions(-) delete mode 100644 cdist/conf/type/__uci_commit/explorer/changes diff --git a/cdist/conf/type/__uci_commit/explorer/changes b/cdist/conf/type/__uci_commit/explorer/changes deleted file mode 100644 index 2690def7..00000000 --- a/cdist/conf/type/__uci_commit/explorer/changes +++ /dev/null @@ -1,22 +0,0 @@ -#!/bin/sh -# -# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) -# -# 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 . -# -# This explorer outputs the uncommited UCI changes on the target. - -uci changes diff --git a/cdist/conf/type/__uci_commit/gencode-remote b/cdist/conf/type/__uci_commit/gencode-remote index dd1d839c..504f7c3b 100755 --- a/cdist/conf/type/__uci_commit/gencode-remote +++ b/cdist/conf/type/__uci_commit/gencode-remote @@ -23,19 +23,28 @@ batchfile="${__global:?}/tmp/__uci/${transaction_name}.txt" test -s "${batchfile}" || exit 0 -if test -s "${__object:?}/explorer/changes" +printf 'commit transaction %s\n' "${transaction_name}" >>"${__messages_out:?}" + +# NOTE: Uncommited changes are checked in code-remote instead of in an explorer +# because in cdist there is no interlocking between explorers and code +# execution. +# Checking for uncommited changes in an explorer leaves a time slot in +# which changes made are not detected by the code. +# Furthermore an explorer running concurrently with another transactions +# code-remote could lead to a false positive. + +cat <&2 -printf 'commit transaction %s\n' "${transaction_name}" >>"${__messages_out:?}" - -cat < Date: Mon, 20 Jul 2020 12:12:15 +0200 Subject: [PATCH 085/366] [type/__uci_commit] Fail when uci(1) reports errors --- cdist/conf/type/__uci_commit/gencode-remote | 37 ++++++++++++++++----- 1 file changed, 29 insertions(+), 8 deletions(-) diff --git a/cdist/conf/type/__uci_commit/gencode-remote b/cdist/conf/type/__uci_commit/gencode-remote index 504f7c3b..e5c93966 100755 --- a/cdist/conf/type/__uci_commit/gencode-remote +++ b/cdist/conf/type/__uci_commit/gencode-remote @@ -45,16 +45,37 @@ then exit 1 fi >&2 -rollback() { - uci changes \\ - | sed -e 's/\..*\$//' -e 's/^-//' \\ - | while read -r package - do - uci revert "\${package}" - done +check_errors() { + # reads stdin and forwards non-empty lines to stderr. + # returns 0 if stdin is empty, else 1. + ! grep -e . >&2 } -uci batch <<'EOF' && uci commit || rollback +commit() { + uci commit +} + +rollback() { + echo >&2 + echo 'An error occurred when trying to commit transaction ${transaction_name}!' >&2 + + uci changes \\ + | sed -e 's/^-//' -e 's/\..*\$//' \\ + | sort -u \\ + | while read -r _package + do + uci revert "\${_package}" + echo "\${_package}" # for logging + done \\ + | awk ' + BEGIN { printf "Reverted changes in: " } + { printf "%s%s", (FNR > 1 ? ", " : ""), \$0 } + END { printf "\\n" }' >&2 + + return 1 +} + +uci batch <<'EOF' 2>&1 | check_errors && commit || rollback $(cat "${batchfile}") EOF CODE From 4da39681188493981ec3c55ea918787195655972 Mon Sep 17 00:00:00 2001 From: Dennis Camera Date: Sat, 20 Jun 2020 20:04:04 +0200 Subject: [PATCH 086/366] [type/__uci_section] Add type --- cdist/conf/type/__uci_section/explorer/match | 68 ++++++++++ .../conf/type/__uci_section/explorer/options | 28 ++++ cdist/conf/type/__uci_section/explorer/type | 25 ++++ cdist/conf/type/__uci_section/man.rst | 76 +++++++++++ cdist/conf/type/__uci_section/manifest | 122 ++++++++++++++++++ .../__uci_section/parameter/default/state | 1 + .../parameter/default/transaction | 1 + .../type/__uci_section/parameter/optional | 4 + .../__uci_section/parameter/optional_multiple | 1 + 9 files changed, 326 insertions(+) create mode 100644 cdist/conf/type/__uci_section/explorer/match create mode 100644 cdist/conf/type/__uci_section/explorer/options create mode 100644 cdist/conf/type/__uci_section/explorer/type create mode 100644 cdist/conf/type/__uci_section/man.rst create mode 100755 cdist/conf/type/__uci_section/manifest create mode 100644 cdist/conf/type/__uci_section/parameter/default/state create mode 100644 cdist/conf/type/__uci_section/parameter/default/transaction create mode 100644 cdist/conf/type/__uci_section/parameter/optional create mode 100644 cdist/conf/type/__uci_section/parameter/optional_multiple diff --git a/cdist/conf/type/__uci_section/explorer/match b/cdist/conf/type/__uci_section/explorer/match new file mode 100644 index 00000000..9cf1ea3e --- /dev/null +++ b/cdist/conf/type/__uci_section/explorer/match @@ -0,0 +1,68 @@ +#!/bin/sh -e +# +# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) +# +# 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 . +# +# This explorer the "prefix" of the section matching --match. + +squote_values() { + sed -e '/=".*"$/{s/="/='\''/;s/"$/'\''/}' \ + -e "/='.*'$/"'!{s/=/='\''/;s/$/'\''/}' +} + +RS=$(printf '\036') + +if ! test -e "${__object:?}/parameter/match" +then + if echo "${__object_id:?}" | grep -qvE '^[^.]+\.[^.]+$' + then + echo 'Section identifiers are a package and section name separated by a "." (period).' >&2 + exit 1 + fi + + # If no --match is given, we take the __object_id as the section identifier. + echo "${__object_id:?}" + exit 0 +fi + +test -s "${__object:?}/parameter/match" \ +&& test -s "${__object:?}/parameter/type" \ +|| { + echo 'Parameters --match and --type must be used together.' >&2 + exit 1 +} + +# Find by match +match=$(cat "${__object:?}/parameter/match") +sect_type_filter=$(cat "${__object:?}/parameter/type") + +package_filter=${sect_type_filter%%.*} +section_filter=${sect_type_filter##*.} +regex="^${package_filter}\.@${section_filter}\[[0-9]\{1,\}\]\.${match%%=*}=" + +matched_sections=$( + uci -s -N -d "${RS}" show "${package_filter}" 2>/dev/null \ + | grep -e "${regex}" \ + | sed -e 's/\.[^.]*=.*$//') + +if test "$(echo "${matched_sections}" | wc -l)" -gt 1 +then + printf 'Found multiple matching sections:\n%s\n' "${matched_sections}" >&2 + exit 1 +fi + +echo "${matched_sections}" diff --git a/cdist/conf/type/__uci_section/explorer/options b/cdist/conf/type/__uci_section/explorer/options new file mode 100644 index 00000000..67b4f83f --- /dev/null +++ b/cdist/conf/type/__uci_section/explorer/options @@ -0,0 +1,28 @@ +#!/bin/sh -e +# +# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) +# +# 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 . +# +# This explorer retrieves the current options of the configuration section. + +RS=$(printf '\036') + +section=$("${__type_explorer:?}/match") +test -n "${section}" || exit 0 + +uci -s -N -d "${RS}" show "${section}" 2>/dev/null \ +| grep -v -e "^${section}=" || true diff --git a/cdist/conf/type/__uci_section/explorer/type b/cdist/conf/type/__uci_section/explorer/type new file mode 100644 index 00000000..1675c2e0 --- /dev/null +++ b/cdist/conf/type/__uci_section/explorer/type @@ -0,0 +1,25 @@ +#!/bin/sh -e +# +# 2020 Dennis Camera (dennis.camera at ssrq-sds-fds.ch) +# +# 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 . +# +# This explorer retrieves the current section type. + +section=$("${__type_explorer:?}/match") +test -n "${section}" || exit 0 + +uci -s -N get "${section}" 2>/dev/null || true diff --git a/cdist/conf/type/__uci_section/man.rst b/cdist/conf/type/__uci_section/man.rst new file mode 100644 index 00000000..3468bfc3 --- /dev/null +++ b/cdist/conf/type/__uci_section/man.rst @@ -0,0 +1,76 @@ +cdist-type__uci_section(7) +========================== + +NAME +---- +cdist-type__uci_section - Manage configuration sections in OpenWrt's +Unified Configuration Interface (UCI) + + +DESCRIPTION +----------- +This cdist type can be used to replace whole configuration sections in OpenWrt's +UCI system. +It can be thought of as syntactic sugar for `cdist-type__uci`\ (7), as this type +will generate the required `__uci` objects to make the section contain exactly +the options specified via ``--option``. + +Since many default UCI sections are unnamed, this type allows to find the +matching section by one of its options using the ``--match`` parameter. + + +REQUIRED PARAMETERS +------------------- +None. + + +OPTIONAL PARAMETERS +------------------- +match + Allows to find a section to "replace" through one of its parameters. + The value to this parameter is a ``