From 3454da076f911d3d196df28cb41d9f3a2cb68f61 Mon Sep 17 00:00:00 2001
From: Darko Poljak <darko.poljak@gmail.com>
Date: Sat, 9 Sep 2017 21:17:29 +0200
Subject: [PATCH] Add -l/--log-level option. Honor __cdist_loglevel env var.
 (#572)

Add -l/--log-level option, __cdist_loglevel -> __cdist_log_level; honor __cdist_log_level env var
---
 cdist/argparse.py                             | 28 +++++++--
 .../conf/type/__install_config/gencode-local  | 17 ------
 .../conf/type/__install_stage/gencode-remote  |  2 +-
 cdist/configuration.py                        |  1 +
 cdist/core/__init__.py                        |  1 +
 cdist/core/code.py                            |  2 +
 cdist/core/explorer.py                        |  2 +
 cdist/core/manifest.py                        |  6 +-
 cdist/core/util.py                            |  5 ++
 cdist/emulator.py                             |  6 +-
 cdist/test/code/__init__.py                   |  3 +
 .../type/__dump_environment/gencode-local     |  1 +
 cdist/test/emulator/__init__.py               | 12 ++--
 cdist/test/explorer/__init__.py               | 25 ++++++++
 .../conf/type/__dump_env/explorer/dump        |  8 +++
 cdist/test/manifest/__init__.py               | 20 +++++--
 .../fixtures/conf/manifest/dump_environment   |  1 +
 .../conf/type/__dump_environment/manifest     |  1 +
 docs/changelog                                |  2 +
 docs/src/cdist-reference.rst.sh               | 12 +++-
 docs/src/cdist-type.rst                       |  5 +-
 docs/src/man1/cdist.rst                       | 58 ++++++++++++-------
 scripts/cdist                                 |  3 +-
 23 files changed, 152 insertions(+), 69 deletions(-)
 create mode 100755 cdist/test/explorer/fixtures/conf/type/__dump_env/explorer/dump

diff --git a/cdist/argparse.py b/cdist/argparse.py
index 1b655ebf..5bce1b76 100644
--- a/cdist/argparse.py
+++ b/cdist/argparse.py
@@ -3,6 +3,7 @@ import cdist
 import multiprocessing
 import logging
 import collections
+import functools
 import cdist.configuration
 
 
@@ -72,15 +73,15 @@ def check_beta(args_dict):
                     raise cdist.CdistBetaRequired(cmd, arg)
 
 
-def check_positive_int(value):
+def check_lower_bounded_int(value, lower_bound, name):
     try:
         val = int(value)
     except ValueError:
         raise argparse.ArgumentTypeError(
                 "{} is invalid int value".format(value))
-    if val <= 0:
+    if val < lower_bound:
         raise argparse.ArgumentTypeError(
-                "{} is invalid positive int value".format(val))
+                "{} is invalid {} value".format(val, name))
     return val
 
 
@@ -94,6 +95,17 @@ def get_parsers():
         parser = {}
     # Options _all_ parsers have in common
     parser['loglevel'] = argparse.ArgumentParser(add_help=False)
+    parser['loglevel'].add_argument(
+            '-l', '--log-level', metavar='LOGLEVEL',
+            type=functools.partial(check_lower_bounded_int, lower_bound=-1,
+                                   name="log level"),
+            help=('Set the specified verbosity level. '
+                  'The levels, in order from the lowest to the highest, are: '
+                  'ERROR (-1), WARNING (0), INFO (1), VERBOSE (2), DEBUG (3) '
+                  'TRACE (4 or higher). If used along with -v then -v '
+                  'increases last set value and -l overwrites last set '
+                  'value.'),
+            action='store', dest='verbose', required=False)
     parser['loglevel'].add_argument(
             '-q', '--quiet',
             help='Quiet mode: disables logging, including WARNING and ERROR.',
@@ -105,7 +117,9 @@ def get_parsers():
                   'is 0 which includes ERROR and WARNING levels. '
                   'The levels, in order from the lowest to the highest, are: '
                   'ERROR (-1), WARNING (0), INFO (1), VERBOSE (2), DEBUG (3) '
-                  'TRACE (4 or higher).'),
+                  'TRACE (4 or higher). If used along with -l then -l '
+                  'overwrites last set value and -v increases last set '
+                  'value.'),
             action='count', default=None)
 
     parser['beta'] = argparse.ArgumentParser(add_help=False)
@@ -164,7 +178,8 @@ def get_parsers():
            dest='manifest', required=False)
     parser['config_main'].add_argument(
            '-j', '--jobs', nargs='?',
-           type=check_positive_int,
+           type=functools.partial(check_lower_bounded_int, lower_bound=1,
+                                  name="positive int"),
            help=('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. '
@@ -229,7 +244,8 @@ def get_parsers():
             dest='hostfile', required=False)
     parser['config_args'].add_argument(
            '-p', '--parallel', nargs='?', metavar='HOST_MAX',
-           type=check_positive_int,
+           type=functools.partial(check_lower_bounded_int, lower_bound=1,
+                                  name="positive int"),
            help=('Operate on multiple hosts in parallel for specified maximum '
                  'hosts at a time. Without argument CPU count is used by '
                  'default.'),
diff --git a/cdist/conf/type/__install_config/gencode-local b/cdist/conf/type/__install_config/gencode-local
index 41517bd8..a298a5cc 100755
--- a/cdist/conf/type/__install_config/gencode-local
+++ b/cdist/conf/type/__install_config/gencode-local
@@ -22,27 +22,10 @@ chroot="$(cat "$__object/parameter/chroot")"
 remote_exec="$__type/files/remote/exec"
 remote_copy="$__type/files/remote/copy"
 
-cdist_args=""
-case "$__cdist_loglevel" in
-   INFO)
-      cdist_args="-v"
-   ;;
-   VERBOSE)
-      cdist_args="-vv"
-   ;;
-   DEBUG)
-      cdist_args="-vvv"
-   ;;
-   TRACE)
-      cdist_args="-vvvv"
-   ;;
-esac
-
 cat << DONE
 export __default_remote_exec="$__remote_exec"
 export __default_remote_copy="$__remote_copy"
 cdist config \
-   $cdist_args \
    --remote-exec="$remote_exec $chroot" \
    --remote-copy="$remote_copy $chroot" \
    $__target_host
diff --git a/cdist/conf/type/__install_stage/gencode-remote b/cdist/conf/type/__install_stage/gencode-remote
index 214e4b52..4d013e3d 100755
--- a/cdist/conf/type/__install_stage/gencode-remote
+++ b/cdist/conf/type/__install_stage/gencode-remote
@@ -22,7 +22,7 @@ uri="$(cat "$__object/parameter/uri" 2>/dev/null \
    || echo "$__object_id")"
 target="$(cat "$__object/parameter/target")"
 
-case "$__cdist_loglevel" in
+case "$__cdist_log_level" in
     DEBUG|TRACE)
         curl="curl"
         tar="tar -xvzp"
diff --git a/cdist/configuration.py b/cdist/configuration.py
index 8fc5c473..30176200 100644
--- a/cdist/configuration.py
+++ b/cdist/configuration.py
@@ -278,6 +278,7 @@ class Configuration(metaclass=Singleton):
         'CDIST_REMOTE_COPY': 'remote_copy',
         'CDIST_INVENTORY_DIR': 'inventory_dir',
         'CDIST_CACHE_PATH_PATTERN': 'cache_path_pattern',
+        '__cdist_log_level': 'verbosity',
     }
     ENV_VAR_BOOLEAN_OPTIONS = ('CDIST_BETA', )
 
diff --git a/cdist/core/__init__.py b/cdist/core/__init__.py
index 8c384b3c..adec60b2 100644
--- a/cdist/core/__init__.py
+++ b/cdist/core/__init__.py
@@ -28,3 +28,4 @@ from cdist.core.explorer import Explorer
 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
diff --git a/cdist/core/code.py b/cdist/core/code.py
index 94ede0f9..5ada4c14 100644
--- a/cdist/core/code.py
+++ b/cdist/core/code.py
@@ -22,6 +22,7 @@
 #
 
 import os
+from . import util
 
 
 '''
@@ -107,6 +108,7 @@ class Code(object):
             '__global': self.local.base_path,
             '__files': self.local.files_path,
             '__target_host_tags': self.local.target_host_tags,
+            '__cdist_log_level': util.log_level_env_var_val(local.log),
         }
 
     def _run_gencode(self, cdist_object, which):
diff --git a/cdist/core/explorer.py b/cdist/core/explorer.py
index 38f2a921..8d7cd593 100644
--- a/cdist/core/explorer.py
+++ b/cdist/core/explorer.py
@@ -25,6 +25,7 @@ import os
 import glob
 import multiprocessing
 from cdist.mputil import mp_pool_run
+from . import util
 
 '''
 common:
@@ -78,6 +79,7 @@ class Explorer(object):
             '__target_fqdn': self.target_host[2],
             '__explorer': self.remote.global_explorer_path,
             '__target_host_tags': self.local.target_host_tags,
+            '__cdist_log_level': util.log_level_env_var_val(self.log),
         }
         self._type_explorers_transferred = []
         self.jobs = jobs
diff --git a/cdist/core/manifest.py b/cdist/core/manifest.py
index 98a44629..e69db63f 100644
--- a/cdist/core/manifest.py
+++ b/cdist/core/manifest.py
@@ -24,6 +24,7 @@ import logging
 import os
 
 import cdist
+from . import util
 
 '''
 common:
@@ -111,12 +112,9 @@ class Manifest(object):
             '__target_fqdn': self.target_host[2],
             '__files': self.local.files_path,
             '__target_host_tags': self.local.target_host_tags,
+            '__cdist_log_level': util.log_level_env_var_val(self.log),
         }
 
-        self.env.update(
-            {'__cdist_loglevel': logging.getLevelName(
-                self.log.getEffectiveLevel())})
-
     def _open_logger(self):
         self.log = logging.getLogger(self.target_host[0])
 
diff --git a/cdist/core/util.py b/cdist/core/util.py
index ca48c4c8..cce608bc 100644
--- a/cdist/core/util.py
+++ b/cdist/core/util.py
@@ -20,6 +20,7 @@
 #
 
 import os
+import logging
 
 
 def listdir(path='.', include_dot=False):
@@ -34,3 +35,7 @@ def listdir(path='.', include_dot=False):
 
 def _ishidden(path):
     return path[0] in ('.', b'.'[0])
+
+
+def log_level_env_var_val(log):
+    return logging.getLevelName(log.getEffectiveLevel())
diff --git a/cdist/emulator.py b/cdist/emulator.py
index f3e9b9b0..0655fe9c 100644
--- a/cdist/emulator.py
+++ b/cdist/emulator.py
@@ -109,9 +109,9 @@ class Emulator(object):
     def __init_log(self):
         """Setup logging facility"""
 
-        if '__cdist_loglevel' in self.env:
+        if '__cdist_log_level' in self.env:
             try:
-                loglevel = self.env['__cdist_loglevel']
+                loglevel = self.env['__cdist_log_level']
                 # For a text level it returns its numerical value.
                 level = logging.getLevelName(loglevel)
             except ValueError:
@@ -121,7 +121,7 @@ class Emulator(object):
         try:
             logging.root.setLevel(level)
         except (ValueError, TypeError):
-            # if invalid __cdist_loglevel value
+            # if invalid __cdist_log_level value
             logging.root.setLevel(logging.WARNING)
 
         self.log = logging.getLogger(self.target_host[0])
diff --git a/cdist/test/code/__init__.py b/cdist/test/code/__init__.py
index 50da2b8a..4d0427aa 100644
--- a/cdist/test/code/__init__.py
+++ b/cdist/test/code/__init__.py
@@ -23,6 +23,7 @@
 import getpass
 import os
 import shutil
+import logging
 
 import cdist
 from cdist import core
@@ -100,6 +101,7 @@ class CodeTestCase(test.CdistTestCase):
         self.assertEqual(output_dict['__files'], self.local.files_path)
         self.assertEqual(output_dict['__target_host_tags'],
                          self.local.target_host_tags)
+        self.assertEqual(output_dict['__cdist_log_level'], 'WARNING')
 
     def test_run_gencode_remote_environment(self):
         output_string = self.code.run_gencode_remote(self.cdist_object)
@@ -125,6 +127,7 @@ class CodeTestCase(test.CdistTestCase):
         self.assertEqual(output_dict['__files'], self.local.files_path)
         self.assertEqual(output_dict['__target_host_tags'],
                          self.local.target_host_tags)
+        self.assertEqual(output_dict['__cdist_log_level'], 'WARNING')
 
     def test_transfer_code_remote(self):
         self.cdist_object.code_remote = self.code.run_gencode_remote(
diff --git a/cdist/test/code/fixtures/conf/type/__dump_environment/gencode-local b/cdist/test/code/fixtures/conf/type/__dump_environment/gencode-local
index 56744a27..35120f10 100755
--- a/cdist/test/code/fixtures/conf/type/__dump_environment/gencode-local
+++ b/cdist/test/code/fixtures/conf/type/__dump_environment/gencode-local
@@ -10,3 +10,4 @@ echo "echo __object_id: $__object_id"
 echo "echo __object_name: $__object_name"
 echo "echo __files: $__files"
 echo "echo __target_host_tags: $__target_host_tags"
+echo "echo __cdist_log_level: $__cdist_log_level"
diff --git a/cdist/test/emulator/__init__.py b/cdist/test/emulator/__init__.py
index a63ce04a..48c5362e 100644
--- a/cdist/test/emulator/__init__.py
+++ b/cdist/test/emulator/__init__.py
@@ -64,8 +64,8 @@ class EmulatorTestCase(test.CdistTestCase):
         self.manifest = core.Manifest(self.target_host, self.local)
         self.env = self.manifest.env_initial_manifest(self.script)
         self.env['__cdist_object_marker'] = self.local.object_marker_name
-        if '__cdist_loglevel' in self.env:
-            del self.env['__cdist_loglevel']
+        if '__cdist_log_level' in self.env:
+            del self.env['__cdist_log_level']
 
     def tearDown(self):
         shutil.rmtree(self.temp_dir)
@@ -124,11 +124,11 @@ class EmulatorTestCase(test.CdistTestCase):
         emu = emulator.Emulator(argv, env=self.env)
         emu_loglevel = emu.log.getEffectiveLevel()
         self.assertEqual(emu_loglevel, logging.WARNING)
-        self.env['__cdist_loglevel'] = logging.getLevelName(logging.DEBUG)
+        self.env['__cdist_log_level'] = logging.getLevelName(logging.DEBUG)
         emu = emulator.Emulator(argv, env=self.env)
         emu_loglevel = emu.log.getEffectiveLevel()
         self.assertEqual(emu_loglevel, logging.DEBUG)
-        del self.env['__cdist_loglevel']
+        del self.env['__cdist_log_level']
 
     def test_invalid_loglevel_value(self):
         argv = ['__file', '/tmp/foobar']
@@ -137,11 +137,11 @@ class EmulatorTestCase(test.CdistTestCase):
         emu_loglevel = emu.log.getEffectiveLevel()
         self.assertEqual(emu_loglevel, logging.WARNING)
         # lowercase is invalid
-        self.env['__cdist_loglevel'] = 'debug'
+        self.env['__cdist_log_level'] = 'debug'
         emu = emulator.Emulator(argv, env=self.env)
         emu_loglevel = emu.log.getEffectiveLevel()
         self.assertEqual(emu_loglevel, logging.WARNING)
-        del self.env['__cdist_loglevel']
+        del self.env['__cdist_log_level']
 
     def test_requirement_via_order_dependency(self):
         self.env['CDIST_ORDER_DEPENDENCY'] = 'on'
diff --git a/cdist/test/explorer/__init__.py b/cdist/test/explorer/__init__.py
index 928b4e0d..48b54cd7 100644
--- a/cdist/test/explorer/__init__.py
+++ b/cdist/test/explorer/__init__.py
@@ -210,6 +210,31 @@ class ExplorerClassTestCase(test.CdistTestCase):
         self.assertEqual(names, output)
         shutil.rmtree(out_path)
 
+    def test_explorer_environment(self):
+        cdist_type = core.CdistType(self.local.type_path, '__dump_env')
+        cdist_object = core.CdistObject(cdist_type, self.local.object_path,
+                                        self.local.object_marker_name,
+                                        'whatever')
+        self.explorer.transfer_type_explorers(cdist_type)
+        output = self.explorer.run_type_explorer('dump', cdist_object)
+
+        output_dict = {}
+        for line in output.split('\n'):
+            if line:
+                key, value = line.split(': ')
+                output_dict[key] = value
+        self.assertEqual(output_dict['__target_host'],
+                         self.local.target_host[0])
+        self.assertEqual(output_dict['__target_hostname'],
+                         self.local.target_host[1])
+        self.assertEqual(output_dict['__target_fqdn'],
+                         self.local.target_host[2])
+        self.assertEqual(output_dict['__explorer'],
+                         self.remote.global_explorer_path)
+        self.assertEqual(output_dict['__target_host_tags'],
+                         self.local.target_host_tags)
+        self.assertEqual(output_dict['__cdist_log_level'], 'WARNING')
+
 
 if __name__ == '__main__':
     import unittest
diff --git a/cdist/test/explorer/fixtures/conf/type/__dump_env/explorer/dump b/cdist/test/explorer/fixtures/conf/type/__dump_env/explorer/dump
new file mode 100755
index 00000000..ae477cd0
--- /dev/null
+++ b/cdist/test/explorer/fixtures/conf/type/__dump_env/explorer/dump
@@ -0,0 +1,8 @@
+#!/bin/sh
+
+echo "__target_host: $__target_host"
+echo "__target_hostname: $__target_hostname"
+echo "__target_fqdn: $__target_fqdn"
+echo "__explorer: $__explorer"
+echo "__target_host_tags: $__target_host_tags"
+echo "__cdist_log_level: $__cdist_log_level"
diff --git a/cdist/test/manifest/__init__.py b/cdist/test/manifest/__init__.py
index 3462c9ca..5b372f35 100644
--- a/cdist/test/manifest/__init__.py
+++ b/cdist/test/manifest/__init__.py
@@ -73,7 +73,10 @@ class ManifestTestCase(test.CdistTestCase):
         handle, output_file = self.mkstemp(dir=self.temp_dir)
         os.close(handle)
         os.environ['__cdist_test_out'] = output_file
-        self.manifest.run_initial_manifest(initial_manifest)
+        old_loglevel = logging.root.getEffectiveLevel()
+        self.log.setLevel(logging.VERBOSE)
+        manifest = cdist.core.manifest.Manifest(self.target_host, self.local)
+        manifest.run_initial_manifest(initial_manifest)
 
         with open(output_file, 'r') as fd:
             output_string = fd.read()
@@ -96,6 +99,8 @@ class ManifestTestCase(test.CdistTestCase):
         self.assertEqual(output_dict['__files'], self.local.files_path)
         self.assertEqual(output_dict['__target_host_tags'],
                          self.local.target_host_tags)
+        self.assertEqual(output_dict['__cdist_log_level'], 'VERBOSE')
+        self.log.setLevel(old_loglevel)
 
     def test_type_manifest_environment(self):
         cdist_type = core.CdistType(self.local.type_path, '__dump_environment')
@@ -105,7 +110,10 @@ class ManifestTestCase(test.CdistTestCase):
         handle, output_file = self.mkstemp(dir=self.temp_dir)
         os.close(handle)
         os.environ['__cdist_test_out'] = output_file
-        self.manifest.run_type_manifest(cdist_object)
+        old_loglevel = self.log.getEffectiveLevel()
+        self.log.setLevel(logging.VERBOSE)
+        manifest = cdist.core.manifest.Manifest(self.target_host, self.local)
+        manifest.run_type_manifest(cdist_object)
 
         with open(output_file, 'r') as fd:
             output_string = fd.read()
@@ -131,13 +139,15 @@ class ManifestTestCase(test.CdistTestCase):
         self.assertEqual(output_dict['__files'], self.local.files_path)
         self.assertEqual(output_dict['__target_host_tags'],
                          self.local.target_host_tags)
+        self.assertEqual(output_dict['__cdist_log_level'], 'VERBOSE')
+        self.log.setLevel(old_loglevel)
 
-    def test_debug_env_setup(self):
+    def test_loglevel_env_setup(self):
         current_level = self.log.getEffectiveLevel()
         self.log.setLevel(logging.DEBUG)
         manifest = cdist.core.manifest.Manifest(self.target_host, self.local)
-        self.assertTrue("__cdist_loglevel" in manifest.env)
-        self.assertEqual(manifest.env["__cdist_loglevel"], 'DEBUG')
+        self.assertTrue("__cdist_log_level" in manifest.env)
+        self.assertEqual(manifest.env["__cdist_log_level"], 'DEBUG')
         self.log.setLevel(current_level)
 
 
diff --git a/cdist/test/manifest/fixtures/conf/manifest/dump_environment b/cdist/test/manifest/fixtures/conf/manifest/dump_environment
index 9f9df372..adf3c3e0 100755
--- a/cdist/test/manifest/fixtures/conf/manifest/dump_environment
+++ b/cdist/test/manifest/fixtures/conf/manifest/dump_environment
@@ -10,4 +10,5 @@ __cdist_type_base_path: $__cdist_type_base_path
 __manifest: $__manifest
 __files: $__files
 __target_host_tags: $__target_host_tags
+__cdist_log_level: $__cdist_log_level
 DONE
diff --git a/cdist/test/manifest/fixtures/conf/type/__dump_environment/manifest b/cdist/test/manifest/fixtures/conf/type/__dump_environment/manifest
index fec5cb3f..f2ae74e2 100755
--- a/cdist/test/manifest/fixtures/conf/type/__dump_environment/manifest
+++ b/cdist/test/manifest/fixtures/conf/type/__dump_environment/manifest
@@ -14,4 +14,5 @@ __object_id: $__object_id
 __object_name: $__object_name
 __files: $__files
 __target_host_tags: $__target_host_tags
+__cdist_log_level: $__cdist_log_level
 DONE
diff --git a/docs/changelog b/docs/changelog
index 3006e188..5105a503 100644
--- a/docs/changelog
+++ b/docs/changelog
@@ -13,6 +13,8 @@ next:
 	* Documentation: Document __cdist_loglevel type variable (Darko Poljak)
 	* Type __install_stage: Fix __debug -> __cdist_loglevel (Darko Poljak)
 	* Core, types: Make __cdist_loglevel value more expressive (Darko Poljak)
+	* Core: Add -l/--log-level option (Darko Poljak)
+	* Core: __cdist_loglevel -> __cdist_log_level and make cdist honor __cdist_log_level env var (Darko Poljak)
 
 4.6.1: 2017-08-30
 	* Type __user: Explore with /etc files (passwd, group, shadow) (Philippe Gregoire)
diff --git a/docs/src/cdist-reference.rst.sh b/docs/src/cdist-reference.rst.sh
index 0117c04d..ec3216c2 100755
--- a/docs/src/cdist-reference.rst.sh
+++ b/docs/src/cdist-reference.rst.sh
@@ -198,10 +198,11 @@ Environment variables (for reading)
 -----------------------------------
 The following environment variables are exported by cdist:
 
-__cdist_loglevel
+__cdist_log_level
     String value of cdist log level. One of OFF, ERROR, WARNING, INFO,
     VERBOSE, DEBUG and TRACE.
-    Available for: initial manifest, type manifest, type gencode.
+    Available for: initial manifest, explorer, type manifest, type explorer,
+    type gencode.
 __explorer
     Directory that contains all global explorers.
     Available for: initial manifest, explorer, type explorer, shell.
@@ -264,6 +265,13 @@ The following environment variables influence the behaviour of cdist:
 require
     Setup dependencies between objects (see \`cdist manifest <cdist-manifest.html>\`_).
 
+__cdist_log_level
+    String value of cdist log level. One of OFF, ERROR, WARNING, INFO,
+    VERBOSE, DEBUG and TRACE. If set cdist will set this log level in
+    accordance with configuration rules. If cdist invokation is used
+    in types then nested cdist will honor this specified log level if
+    not specified otherwise while invoking it.
+
 CDIST_PATH
     Colon delimited list of config directories.
 
diff --git a/docs/src/cdist-type.rst b/docs/src/cdist-type.rst
index 2e6cbc96..dbecc409 100644
--- a/docs/src/cdist-type.rst
+++ b/docs/src/cdist-type.rst
@@ -333,9 +333,10 @@ So when you generate a script with the following content, it will work:
 
 Log level in types
 ------------------
-cdist log level can be accessed from __cdist_loglevel variable.
+cdist log level can be accessed from __cdist_log_level variable.
 Value is a string, one of OFF, ERROR, WARNING, INFO, VERBOSE, DEBUG and
-TRACE. It is available for initial manifest, type manifest and type gencode.
+TRACE. It is available for initial manifest, explorer, type manifest,
+type explorer, type gencode.
 
 
 Hints for typewriters
diff --git a/docs/src/man1/cdist.rst b/docs/src/man1/cdist.rst
index 0e258d8c..c21b9996 100644
--- a/docs/src/man1/cdist.rst
+++ b/docs/src/man1/cdist.rst
@@ -11,11 +11,12 @@ SYNOPSIS
 
 ::
 
-    cdist [-h] [-q] [-v] [-V] {banner,config,install,inventory,shell} ...
+    cdist [-h] [-l LOGLEVEL] [-q] [-v] [-V]
+          {banner,config,install,inventory,shell} ...
 
-    cdist banner [-h] [-q] [-v]
+    cdist banner [-h] [-l LOGLEVEL] [-q] [-v]
 
-    cdist config [-h] [-q] [-v] [-b] [-g CONFIG_FILE]
+    cdist config [-h] [-l LOGLEVEL] [-q] [-v] [-b] [-g CONFIG_FILE]
                  [-C CACHE_PATH_PATTERN] [-c CONF_DIR] [-i MANIFEST]
                  [-j [JOBS]] [-n] [-o OUT_PATH] [-R [{tar,tgz,tbz2,txz}]]
                  [-r REMOTE_OUT_DIR] [--remote-copy REMOTE_COPY]
@@ -23,7 +24,7 @@ SYNOPSIS
                  [-f HOSTFILE] [-p [HOST_MAX]] [-s] [-t]
                  [host [host ...]] 
 
-    cdist install [-h] [-q] [-v] [-b] [-g CONFIG_FILE]
+    cdist install [-h] [-l LOGLEVEL] [-q] [-v] [-b] [-g CONFIG_FILE]
                   [-C CACHE_PATH_PATTERN] [-c CONF_DIR] [-i MANIFEST]
                   [-j [JOBS]] [-n] [-o OUT_PATH] [-R [{tar,tgz,tbz2,txz}]]
                   [-r REMOTE_OUT_DIR] [--remote-copy REMOTE_COPY]
@@ -31,32 +32,35 @@ SYNOPSIS
                   [-f HOSTFILE] [-p [HOST_MAX]] [-s] [-t]
                   [host [host ...]] 
 
-    cdist inventory [-h] [-q] [-v] [-b] [-g CONFIG_FILE] [-I INVENTORY_DIR]
+    cdist inventory [-h] [-l LOGLEVEL] [-q] [-v] [-b] [-g CONFIG_FILE]
+                    [-I INVENTORY_DIR]
                     {add-host,add-tag,del-host,del-tag,list} ...
 
-    cdist inventory add-host [-h] [-q] [-v] [-b] [-g CONFIG_FILE]
-                             [-I INVENTORY_DIR] [-f HOSTFILE]
+    cdist inventory add-host [-h] [-l LOGLEVEL] [-q] [-v] [-b]
+                             [-g CONFIG_FILE] [-I INVENTORY_DIR]
+                             [-f HOSTFILE]
                              [host [host ...]]
 
-    cdist inventory add-tag [-h] [-q] [-v] [-b] [-g CONFIG_FILE]
-                            [-I INVENTORY_DIR] [-f HOSTFILE] [-T TAGFILE]
-                            [-t TAGLIST]
+    cdist inventory add-tag [-h] [-l LOGLEVEL] [-q] [-v] [-b]
+                            [-g CONFIG_FILE] [-I INVENTORY_DIR]
+                            [-f HOSTFILE] [-T TAGFILE] [-t TAGLIST]
                             [host [host ...]]
 
-    cdist inventory del-host [-h] [-q] [-v] [-b] [-g CONFIG_FILE]
-                             [-I INVENTORY_DIR] [-a] [-f HOSTFILE]
+    cdist inventory del-host [-h] [-l LOGLEVEL] [-q] [-v] [-b]
+                             [-g CONFIG_FILE] [-I INVENTORY_DIR] [-a]
+                             [-f HOSTFILE]
                              [host [host ...]]
 
-    cdist inventory del-tag [-h] [-q] [-v] [-b] [-g CONFIG_FILE]
-                            [-I INVENTORY_DIR] [-a] [-f HOSTFILE]
-                            [-T TAGFILE] [-t TAGLIST]
+    cdist inventory del-tag [-h] [-l LOGLEVEL] [-q] [-v] [-b]
+                            [-g CONFIG_FILE] [-I INVENTORY_DIR] [-a]
+                            [-f HOSTFILE] [-T TAGFILE] [-t TAGLIST]
                             [host [host ...]]
 
-    cdist inventory list [-h] [-q] [-v] [-b] [-g CONFIG_FILE]
+    cdist inventory list [-h] [-l LOGLEVEL] [-q] [-v] [-b] [-g CONFIG_FILE]
                          [-I INVENTORY_DIR] [-a] [-f HOSTFILE] [-H] [-t]
                          [host [host ...]]
 
-    cdist shell [-h] [-q] [-v] [-s SHELL]
+    cdist shell [-h] [-l LOGLEVEL] [-q] [-v] [-s SHELL]
 
 
 DESCRIPTION
@@ -75,16 +79,28 @@ All commands accept the following options:
 
     Show the help screen.
 
+.. option:: -l LOGLEVEL, --log-level LOGLEVEL
+
+    Set the specified verbosity level. The levels, in
+    order from the lowest to the highest, are: ERROR (-1),
+    WARNING (0), INFO (1), VERBOSE (2), DEBUG (3) TRACE (4
+    or higher). If used along with -v then -v increases
+    last set value and -l overwrites last set value.
+
 .. option:: -q, --quiet
 
     Quiet mode: disables logging, including WARNING and ERROR.
 
 .. option:: -v, --verbose
 
-    Increase the verbosity level. Every instance of -v increments the verbosity
-    level by one. Its default value is 0 which includes ERROR and WARNING levels.
-    The levels, in order from the lowest to the highest, are: 
-    ERROR (-1), WARNING (0), INFO (1), VERBOSE (2), DEBUG (3) TRACE (4 or higher).
+    Increase the verbosity level. Every instance of -v
+    increments the verbosity level by one. Its default
+    value is 0 which includes ERROR and WARNING levels.
+    The levels, in order from the lowest to the highest,
+    are: ERROR (-1), WARNING (0), INFO (1), VERBOSE (2),
+    DEBUG (3) TRACE (4 or higher). If used along with -l
+    then -l overwrites last set value and -v increases
+    last set value.
 
 .. option:: -V, --version
 
diff --git a/scripts/cdist b/scripts/cdist
index 93fa392d..088e4dc2 100755
--- a/scripts/cdist
+++ b/scripts/cdist
@@ -22,6 +22,7 @@
 #
 
 import logging
+import sys
 import cdist
 import cdist.argparse
 import cdist.banner
@@ -56,8 +57,6 @@ def commandline():
 
 
 if __name__ == "__main__":
-    import sys
-
     cdistpythonversion = '3.2'
     if sys.version < cdistpythonversion:
         print('Python >= {} is required on the source host.'.format(