URL: https://github.com/SSSD/sssd/pull/685
Author: jhrozek
 Title: #685: Allow re-reading kcm configuration by just restarting the 
sssd-kcm service
Action: synchronized

To pull the PR as Git branch:
git remote add ghsssd https://github.com/SSSD/sssd
git fetch ghsssd pull/685/head:pr685
git checkout pr685
From cbb15fdc21f69fc89b80d5d12fd8c351e1b4cbd8 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhro...@redhat.com>
Date: Tue, 23 Oct 2018 23:12:20 +0200
Subject: [PATCH 1/6] INI: Return errno, not -1 on failure from
 sss_ini_get_stat

sss_ini_get_stat() has two branches for two libini versions. The newer
version directly returns EIO on failure, but the old version would have
returned the return value from fstat() directly. And fstat() returns -1
on failure but sets errno. This patch returns errno on failure and EOK
on success.
---
 src/util/sss_ini.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/src/util/sss_ini.c b/src/util/sss_ini.c
index 175a4cfab..9a059fc00 100644
--- a/src/util/sss_ini.c
+++ b/src/util/sss_ini.c
@@ -156,8 +156,13 @@ int sss_ini_get_stat(struct sss_ini_initdata *init_data)
 
     return EOK;
 #else
+    int ret;
 
-    return fstat(init_data->file, &init_data->cstat);
+    ret = fstat(init_data->file, &init_data->cstat);
+    if (ret != 0) {
+        return errno;
+    }
+    return EOK;
 #endif
 }
 

From e791ebd107b2791d71bf90c340d39c8fa477f98f Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhro...@redhat.com>
Date: Fri, 5 Oct 2018 13:50:37 +0200
Subject: [PATCH 2/6] MONITOR: Don't check for pidfile if SSSD is already
 running

Related:
https://pagure.io/SSSD/sssd/issue/3862

The --genconf option of sssd is meant to be used to reload configuration from a
systemd socket-activated service. But it would only work if sssd was not
running, which defies its purpose.
---
 src/monitor/monitor.c | 18 +++++++++++-------
 1 file changed, 11 insertions(+), 7 deletions(-)

diff --git a/src/monitor/monitor.c b/src/monitor/monitor.c
index 335b2070b..ea689c604 100644
--- a/src/monitor/monitor.c
+++ b/src/monitor/monitor.c
@@ -2514,13 +2514,17 @@ int main(int argc, const char *argv[])
         }
     }
 
-    /* Check if the SSSD is already running */
-    ret = check_file(SSSD_PIDFILE, 0, 0, S_IFREG|0600, 0, NULL, false);
-    if (ret == EOK) {
-        DEBUG(SSSDBG_FATAL_FAILURE,
-              "pidfile exists at %s\n", SSSD_PIDFILE);
-        ERROR("SSSD is already running\n");
-        return 2;
+    /* Check if the SSSD is already running unless we're only interested
+     * in re-reading the configuration
+     */
+    if (opt_genconf == 0) {
+        ret = check_file(SSSD_PIDFILE, 0, 0, S_IFREG|0600, 0, NULL, false);
+        if (ret == EOK) {
+            DEBUG(SSSDBG_FATAL_FAILURE,
+                "pidfile exists at %s\n", SSSD_PIDFILE);
+            ERROR("SSSD is already running\n");
+            return 2;
+        }
     }
 
     /* Parse config file, fail if cannot be done */

From 7377eda4990bf2734e288ae35f68e2f43fbcd5ab Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhro...@redhat.com>
Date: Tue, 9 Oct 2018 15:32:12 +0200
Subject: [PATCH 3/6] SSSD: Allow refreshing only certain section with
 --genconf

Related:
https://pagure.io/SSSD/sssd/issue/3862

Adds a new option --genconf-section for the sssd binary.  If this new
option --genconf-section is used, then only the section passed as this
option's value is refreshed.

Conversely, if this section no longer exists in the config file, then it
is removed from the confdb
---
 src/confdb/confdb_setup.c    | 80 +++++++++++++++++++++++++++---------
 src/confdb/confdb_setup.h    |  1 +
 src/man/sssd.8.xml           | 27 ++++++++++++
 src/monitor/monitor.c        | 17 +++++++-
 src/tools/common/sss_tools.c |  1 +
 src/util/sss_ini.c           | 54 ++++++++++++++++++++++++
 src/util/sss_ini.h           |  1 +
 7 files changed, 160 insertions(+), 21 deletions(-)

diff --git a/src/confdb/confdb_setup.c b/src/confdb/confdb_setup.c
index c2b7f9f73..7acefbe6b 100644
--- a/src/confdb/confdb_setup.c
+++ b/src/confdb/confdb_setup.c
@@ -138,6 +138,7 @@ static int confdb_create_base(struct confdb_ctx *cdb)
 static int confdb_ldif_from_ini_file(TALLOC_CTX *mem_ctx,
                                      const char *config_file,
                                      const char *config_dir,
+                                     const char *only_section,
                                      struct sss_ini_initdata *init_data,
                                      const char **_timestr,
                                      const char **_ldif)
@@ -222,7 +223,7 @@ static int confdb_ldif_from_ini_file(TALLOC_CTX *mem_ctx,
         }
     }
 
-    ret = sss_confdb_create_ldif(mem_ctx, init_data, _ldif);
+    ret = sss_confdb_create_ldif(mem_ctx, init_data, only_section, _ldif);
     if (ret != EOK) {
         DEBUG(SSSDBG_FATAL_FAILURE, "Could not create LDIF for confdb\n");
         return ret;
@@ -249,7 +250,50 @@ static int confdb_fallback_ldif(TALLOC_CTX *mem_ctx,
     return EOK;
 }
 
-static int confdb_init_db(const char *config_file, const char *config_dir,
+static int confdb_write_ldif(struct confdb_ctx *cdb,
+                             const char *config_ldif,
+                             bool replace_whole_db)
+{
+    int ret;
+    struct ldb_ldif *ldif;
+
+    while ((ldif = ldb_ldif_read_string(cdb->ldb, &config_ldif))) {
+        if (ldif->changetype == LDB_CHANGETYPE_DELETE) {
+            /* We should remove this section */
+            ret = ldb_delete(cdb->ldb, ldif->msg->dn);
+            if (ret == LDB_ERR_NO_SUCH_OBJECT) {
+                /* Removing a non-existing section is not an error */
+                ret = LDB_SUCCESS;
+            }
+        } else {
+            ret = ldb_add(cdb->ldb, ldif->msg);
+            if (ret != LDB_SUCCESS && replace_whole_db == false) {
+                /* This section already existed, remove and re-add it. We
+                * really want to replace the whole thing instead of messing
+                * around with changetypes and flags on individual elements
+                */
+                ret = ldb_delete(cdb->ldb, ldif->msg->dn);
+                if (ret == LDB_SUCCESS) {
+                    ret = ldb_add(cdb->ldb, ldif->msg);
+                }
+            }
+        }
+
+        if (ret != LDB_SUCCESS) {
+            DEBUG(SSSDBG_FATAL_FAILURE,
+                "Failed to initialize DB (%d,[%s]), aborting!\n",
+                ret, ldb_errstring(cdb->ldb));
+            return EIO;
+        }
+        ldb_ldif_read_free(cdb->ldb, ldif);
+    }
+
+    return EOK;
+}
+
+static int confdb_init_db(const char *config_file,
+                          const char *config_dir,
+                          const char *only_section,
                           struct confdb_ctx *cdb)
 {
     TALLOC_CTX *tmp_ctx;
@@ -259,7 +303,6 @@ static int confdb_init_db(const char *config_file, const char *config_dir,
     const char *timestr = NULL;
     const char *config_ldif;
     const char *vals[2] = { NULL, NULL };
-    struct ldb_ldif *ldif;
     struct sss_ini_initdata *init_data;
 
     tmp_ctx = talloc_new(cdb);
@@ -281,6 +324,7 @@ static int confdb_init_db(const char *config_file, const char *config_dir,
         ret = confdb_ldif_from_ini_file(tmp_ctx,
                                         config_file,
                                         config_dir,
+                                        only_section,
                                         init_data,
                                         &timestr,
                                         &config_ldif);
@@ -318,24 +362,21 @@ static int confdb_init_db(const char *config_file, const char *config_dir,
     }
     in_transaction = true;
 
-    /* Purge existing database */
-    ret = confdb_purge(cdb);
-    if (ret != EOK) {
-        DEBUG(SSSDBG_FATAL_FAILURE,
-              "Could not purge existing configuration\n");
-        goto done;
-    }
-
-    while ((ldif = ldb_ldif_read_string(cdb->ldb, &config_ldif))) {
-        ret = ldb_add(cdb->ldb, ldif->msg);
-        if (ret != LDB_SUCCESS) {
+    /* Purge existing database, if we are reinitializing the confdb completely */
+    if (only_section == NULL) {
+        ret = confdb_purge(cdb);
+        if (ret != EOK) {
             DEBUG(SSSDBG_FATAL_FAILURE,
-                  "Failed to initialize DB (%d,[%s]), aborting!\n",
-                  ret, ldb_errstring(cdb->ldb));
-            ret = EIO;
+                "Could not purge existing configuration\n");
             goto done;
         }
-        ldb_ldif_read_free(cdb->ldb, ldif);
+    }
+
+    ret = confdb_write_ldif(cdb,
+                            config_ldif,
+                            only_section == NULL ? true : false);
+    if (ret != EOK) {
+        goto done;
     }
 
     /* now store the lastUpdate time so that we do not re-init if nothing
@@ -377,6 +418,7 @@ errno_t confdb_setup(TALLOC_CTX *mem_ctx,
                      const char *cdb_file,
                      const char *config_file,
                      const char *config_dir,
+                     const char *only_section,
                      struct confdb_ctx **_cdb)
 {
     TALLOC_CTX *tmp_ctx;
@@ -432,7 +474,7 @@ errno_t confdb_setup(TALLOC_CTX *mem_ctx,
         goto done;
     }
 
-    ret = confdb_init_db(config_file, config_dir, cdb);
+    ret = confdb_init_db(config_file, config_dir, only_section, cdb);
     if (ret != EOK) {
         DEBUG(SSSDBG_FATAL_FAILURE, "ConfDB initialization has failed "
               "[%d]: %s\n", ret, sss_strerror(ret));
diff --git a/src/confdb/confdb_setup.h b/src/confdb/confdb_setup.h
index 9f647ec16..c7fe59541 100644
--- a/src/confdb/confdb_setup.h
+++ b/src/confdb/confdb_setup.h
@@ -49,6 +49,7 @@ errno_t confdb_setup(TALLOC_CTX *mem_ctx,
                      const char *cdb_file,
                      const char *config_file,
                      const char *config_dir,
+                     const char *only_section,
                      struct confdb_ctx **_cdb);
 
 #endif /* CONFDB_SETUP_H_ */
diff --git a/src/man/sssd.8.xml b/src/man/sssd.8.xml
index f2cbe015b..ff3d8825d 100644
--- a/src/man/sssd.8.xml
+++ b/src/man/sssd.8.xml
@@ -164,6 +164,33 @@
                     </para>
                 </listitem>
             </varlistentry>
+            <varlistentry>
+                <term>
+                    <option>-g</option>,<option>--genconf</option>
+                </term>
+                <listitem>
+                    <para>
+                        Do not start the SSSD, but refresh the configuration
+                        database from the contents of
+                        <filename>/etc/sssd/sssd.conf</filename> and exit.
+                    </para>
+                </listitem>
+            </varlistentry>
+            <varlistentry>
+                <term>
+                    <option>-s</option>,<option>--genconf-section</option>
+                </term>
+                <listitem>
+                    <para>
+                        Similar to <quote>--genconf</quote>, but only refresh
+                        a single section from the configuration file.  This
+                        option is useful mainly to be called from systemd
+                        unit files to allow socket-activated responders
+                        to refresh their configuration without requiring
+                        the administrator to restart the whole SSSD.
+                    </para>
+                </listitem>
+            </varlistentry>
             <xi:include xmlns:xi="http://www.w3.org/2001/XInclude"; href="include/param_help.xml" />
             <varlistentry>
                 <term>
diff --git a/src/monitor/monitor.c b/src/monitor/monitor.c
index ea689c604..136cf8f27 100644
--- a/src/monitor/monitor.c
+++ b/src/monitor/monitor.c
@@ -1579,6 +1579,7 @@ static int monitor_ctx_destructor(void *mem)
 errno_t load_configuration(TALLOC_CTX *mem_ctx,
                            const char *config_file,
                            const char *config_dir,
+                           const char *only_section,
                            struct mt_ctx **monitor)
 {
     errno_t ret;
@@ -1600,7 +1601,8 @@ errno_t load_configuration(TALLOC_CTX *mem_ctx,
         goto done;
     }
 
-    ret = confdb_setup(ctx, cdb_file, config_file, config_dir, &ctx->cdb);
+    ret = confdb_setup(ctx, cdb_file, config_file, config_dir, only_section,
+                       &ctx->cdb);
     if (ret != EOK) {
         DEBUG(SSSDBG_FATAL_FAILURE, "Unable to setup ConfDB [%d]: %s\n",
              ret, sss_strerror(ret));
@@ -2329,6 +2331,7 @@ int main(int argc, const char *argv[])
     char *opt_config_file = NULL;
     char *opt_logger = NULL;
     char *config_file = NULL;
+    char *opt_genconf_section = NULL;
     int flags = 0;
     struct main_context *main_ctx;
     TALLOC_CTX *tmp_ctx;
@@ -2352,6 +2355,9 @@ int main(int argc, const char *argv[])
         {"genconf", 'g', POPT_ARG_NONE, &opt_genconf, 0, \
          _("Refresh the configuration database, then exit"), \
          NULL}, \
+        {"genconf-section", 's', POPT_ARG_STRING, &opt_genconf_section, 0, \
+         _("Similar to --genconf, but only refreshes the given section"), \
+         NULL}, \
         {"version", '\0', POPT_ARG_NONE, &opt_version, 0, \
          _("Print version number and exit"), NULL }, \
         POPT_TABLEEND
@@ -2378,6 +2384,13 @@ int main(int argc, const char *argv[])
         return EXIT_SUCCESS;
     }
 
+    if (opt_genconf_section) {
+        /* --genconf-section implies genconf, just restricted to a single
+         * section
+         */
+        opt_genconf = 1;
+    }
+
     /* If the level or timestamps was passed at the command-line, we want
      * to save it and pass it to the children later.
      */
@@ -2529,7 +2542,7 @@ int main(int argc, const char *argv[])
 
     /* Parse config file, fail if cannot be done */
     ret = load_configuration(tmp_ctx, config_file, CONFDB_DEFAULT_CONFIG_DIR,
-                             &monitor);
+                             opt_genconf_section, &monitor);
     if (ret != EOK) {
         switch (ret) {
         case EPERM:
diff --git a/src/tools/common/sss_tools.c b/src/tools/common/sss_tools.c
index 701db2d93..0d918f164 100644
--- a/src/tools/common/sss_tools.c
+++ b/src/tools/common/sss_tools.c
@@ -98,6 +98,7 @@ static errno_t sss_tool_confdb_init(TALLOC_CTX *mem_ctx,
 
     ret = confdb_setup(mem_ctx, path,
                        SSSD_CONFIG_FILE, CONFDB_DEFAULT_CONFIG_DIR,
+                       NULL,
                        &confdb);
     talloc_zfree(path);
     if (ret != EOK) {
diff --git a/src/util/sss_ini.c b/src/util/sss_ini.c
index 9a059fc00..3c15b2809 100644
--- a/src/util/sss_ini.c
+++ b/src/util/sss_ini.c
@@ -414,6 +414,7 @@ void sss_ini_config_destroy(struct sss_ini_initdata *init_data)
 
 int sss_confdb_create_ldif(TALLOC_CTX *mem_ctx,
                            struct sss_ini_initdata *init_data,
+                           const char *only_section,
                            const char **config_ldif)
 {
     int ret, i, j;
@@ -436,6 +437,14 @@ int sss_confdb_create_ldif(TALLOC_CTX *mem_ctx,
 #else
     struct collection_item *obj = NULL;
 #endif
+    bool section_handled = true;
+
+    if (only_section != NULL) {
+        /* If the section is specified, we must handle it, either by adding
+         * its contents or by deleting the section if it doesn't exist
+         */
+        section_handled = false;
+    }
 
     ldif_len = strlen(CONFDB_INTERNAL_LDIF);
     ldif = talloc_array(mem_ctx, char, ldif_len+1);
@@ -466,6 +475,18 @@ int sss_confdb_create_ldif(TALLOC_CTX *mem_ctx,
             goto error;
         }
 
+        if (only_section != NULL) {
+            if (strcasecmp(only_section, sections[i])) {
+                DEBUG(SSSDBG_TRACE_FUNC, "Skipping section %s\n", sections[i]);
+                continue;
+            } else {
+                /* Mark the requested section as handled so that we don't
+                 * try to re-add it later
+                 */
+                section_handled = true;
+            }
+        }
+
         dn = talloc_asprintf(tmp_ctx,
                              "dn: %s,cn=config\n"
                              "cn: %s\n",
@@ -552,6 +573,39 @@ int sss_confdb_create_ldif(TALLOC_CTX *mem_ctx,
         talloc_free(dn);
     }
 
+
+    if (only_section != NULL && section_handled == false) {
+        /* If only a single section was supposed to be
+         * handled, but it wasn't found in the INI file,
+         * create an LDIF that would remove the section
+         */
+        ret = parse_section(tmp_ctx, only_section, &sec_dn, NULL);
+        if (ret != EOK) {
+            goto error;
+        }
+
+        dn = talloc_asprintf(tmp_ctx,
+                             "dn: %s,cn=config\n"
+                             "changetype: delete\n\n",
+                             sec_dn);
+        if (dn == NULL) {
+            ret = ENOMEM;
+            goto error;
+        }
+        dn_size = strlen(dn);
+
+        tmp_ldif = talloc_realloc(mem_ctx, ldif, char,
+                                  ldif_len+dn_size+1);
+        if (!tmp_ldif) {
+            ret = ENOMEM;
+            goto error;
+        }
+
+        ldif = tmp_ldif;
+        memcpy(ldif+ldif_len, dn, dn_size);
+        ldif_len += dn_size;
+    }
+
     ldif[ldif_len] = '\0';
 
     free_section_list(sections);
diff --git a/src/util/sss_ini.h b/src/util/sss_ini.h
index 0b173831d..470b88f99 100644
--- a/src/util/sss_ini.h
+++ b/src/util/sss_ini.h
@@ -77,6 +77,7 @@ void sss_ini_config_destroy(struct sss_ini_initdata *init_data);
 /* Create LDIF */
 int sss_confdb_create_ldif(TALLOC_CTX *mem_ctx,
                            struct sss_ini_initdata *init_data,
+                           const char *only_section,
                            const char **config_ldif);
 
 /* Validate sssd.conf if libini_config support it */

From 9ebd7eb9206f72ec6582d771c9059362a2b4b121 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhro...@redhat.com>
Date: Tue, 9 Oct 2018 15:41:44 +0200
Subject: [PATCH 4/6] SYSTEMD: Re-read KCM configuration on systemctl restart
 kcm

Related:
https://pagure.io/SSSD/sssd/issue/3862

Uses the sssd command together with the --genconf-section=kcm option to
refresh the kcm configuration when the sssd-kcm systemd service is
restarted.

This allows the administrator to e.g. just drop a snippet to
/etc/sssd.conf.d/ or create the [kcm] section directly in the main sssd
config file, then just restart the sssd-kcm service for the changes to
apply.
---
 src/man/sssd-kcm.8.xml               | 33 ++++++++++++++++++++++++++++
 src/sysv/systemd/sssd-kcm.service.in |  1 +
 2 files changed, 34 insertions(+)

diff --git a/src/man/sssd-kcm.8.xml b/src/man/sssd-kcm.8.xml
index ec27aa57b..fff8b0a16 100644
--- a/src/man/sssd-kcm.8.xml
+++ b/src/man/sssd-kcm.8.xml
@@ -127,6 +127,39 @@ systemctl enable sssd-kcm.socket
         </para>
     </refsect1>
 
+    <refsect1 id='debugging'>
+        <title>OBTAINING DEBUG LOGS</title>
+        <para>
+            The sssd-kcm service is typically socket-activated
+            <citerefentry>
+                <refentrytitle>systemd</refentrytitle>
+                <manvolnum>1</manvolnum>
+            </citerefentry>. To generate debug logs, add the following
+            either to the <filename>/etc/sssd/sssd.conf</filename>
+            file directly or as a configuration snippet to
+            <filename>/etc/sssd/conf.d/</filename> directory:
+            <programlisting>
+[kcm]
+debug_level = 10
+            </programlisting>
+            Then, restart the sssd-kcm service:
+            <programlisting>
+systemctl restart sssd-kcm.service
+            </programlisting>
+            Finally, run whatever use-case doesn't work for you. The KCM
+            logs will be generated at
+            <filename>/var/log/sssd/sssd_kcm.log</filename>. It is
+            recommended to disable the debug logs when you no longer need
+            the debugging to be enabled as the sssd-kcm service can generate
+            quite a large amount of debugging information.
+        </para>
+        <para>
+            Please note that configuration snippets are, at the moment,
+            only processed if the main configuration file at
+            <filename>/etc/sssd/sssd.conf</filename> exists at all.
+        </para>
+    </refsect1>
+
     <refsect1 id='options'>
         <title>CONFIGURATION OPTIONS</title>
         <para>
diff --git a/src/sysv/systemd/sssd-kcm.service.in b/src/sysv/systemd/sssd-kcm.service.in
index 8d689bfd7..5c82bee7d 100644
--- a/src/sysv/systemd/sssd-kcm.service.in
+++ b/src/sysv/systemd/sssd-kcm.service.in
@@ -9,4 +9,5 @@ Also=sssd-kcm.socket
 
 [Service]
 Environment=DEBUG_LOGGER=--logger=files
+ExecStartPre=-@sbindir@/sssd --genconf-section=kcm
 ExecStart=@libexecdir@/sssd/sssd_kcm --uid 0 --gid 0 ${DEBUG_LOGGER}

From 9ece6573483610287d09f1f580f1d94115d997f3 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhro...@redhat.com>
Date: Tue, 9 Oct 2018 13:38:10 +0200
Subject: [PATCH 5/6] TEST: Add a multihost test for sssd --genconf

Related:
https://pagure.io/SSSD/sssd/issue/3862

Adds a multihost test to make sure the genconf switch of sssd works in
general and even with sssd running.
---
 src/tests/multihost/basic/test_config.py  | 98 +++++++++++++++++++++++
 src/tests/multihost/basic/utils_config.py | 32 ++++++++
 2 files changed, 130 insertions(+)
 create mode 100644 src/tests/multihost/basic/test_config.py
 create mode 100644 src/tests/multihost/basic/utils_config.py

diff --git a/src/tests/multihost/basic/test_config.py b/src/tests/multihost/basic/test_config.py
new file mode 100644
index 000000000..3e3a76bea
--- /dev/null
+++ b/src/tests/multihost/basic/test_config.py
@@ -0,0 +1,98 @@
+""" SSSD Configuration-related Test Cases """
+import configparser as ConfigParser
+import pytest
+from utils_config import set_param, remove_section
+
+
+class TestSSSDConfig(object):
+    """
+    Test cases around SSSD config management
+    """
+    def _assert_config_value(self, multihost, section, key, value):
+        # This would really be much, much nicer to implement using python-ldb
+        # but at the moment, the multihost tests rely on a virtual environment
+        # where everything is pip-installed..and python-ldb is not present in
+        # pip
+        confdb_dn = 'cn=%s,cn=config' % (section)
+        ldb_cmd = 'ldbsearch -H /var/lib/sss/db/config.ldb -b %s' % (confdb_dn)
+        cmd = multihost.master[0].run_command(ldb_cmd)
+        check_str = '%s: %s' % (key, value)
+        assert check_str in cmd.stdout_text
+
+    def test_sssd_genconf_sssd_running(self, multihost):
+        """
+        Test that sssd --genconf is able to re-generate the configuration
+        even while SSSD is running.
+        """
+        multihost.master[0].service_sssd('restart')
+
+        self._assert_config_value(multihost, 'pam', 'debug_level', '9')
+
+        set_param(multihost, 'pam', 'debug_level', '1')
+        multihost.master[0].run_command('/usr/sbin/sssd --genconf')
+        self._assert_config_value(multihost, 'pam', 'debug_level', '1')
+
+        set_param(multihost, 'pam', 'debug_level', '9')
+
+    def test_sssd_genconf_section_only(self, multihost):
+        """
+        Test that --genconf-section only refreshes those sections given
+        on the command line
+        """
+        multihost.master[0].service_sssd('restart')
+
+        self._assert_config_value(multihost, 'pam', 'debug_level', '9')
+        self._assert_config_value(multihost, 'nss', 'debug_level', '9')
+
+        set_param(multihost, 'pam', 'debug_level', '1')
+        set_param(multihost, 'nss', 'debug_level', '1')
+        multihost.master[0].run_command(
+                '/usr/sbin/sssd --genconf-section=pam')
+
+        # We only told genconf to touch the pam section..
+        self._assert_config_value(multihost, 'pam', 'debug_level', '1')
+        # ..so the NSS section shouldn't be updated at all
+        self._assert_config_value(multihost, 'nss', 'debug_level', '9')
+
+        set_param(multihost, 'nss', 'debug_level', '9')
+        set_param(multihost, 'pam', 'debug_level', '9')
+
+    def test_sssd_genconf_add_remove_section(self, multihost):
+        """
+        Test that --genconf-section can not only modify existing
+        configuration sections, but also add a new section
+        """
+        # Establish a baseline
+        multihost.master[0].service_sssd('restart')
+        self._assert_config_value(multihost, 'pam', 'debug_level', '9')
+        self._assert_config_value(multihost, 'nss', 'debug_level', '9')
+
+        set_param(multihost, 'foo', 'bar', 'baz')
+
+        multihost.master[0].run_command(
+                '/usr/sbin/sssd --genconf-section=foo')
+
+        ldb_cmd = 'ldbsearch -H /var/lib/sss/db/config.ldb -b cn=foo,cn=config'
+        cmd = multihost.master[0].run_command(ldb_cmd)
+        assert 'bar: baz' in cmd.stdout_text
+
+        remove_section(multihost, 'foo')
+        multihost.master[0].run_command(
+                '/usr/sbin/sssd --genconf-section=foo')
+
+        ldb_cmd = 'ldbsearch -H /var/lib/sss/db/config.ldb -b cn=foo,cn=config'
+        cmd = multihost.master[0].run_command(ldb_cmd)
+        assert 'foo' not in cmd.stdout_text
+        # Also make sure the existing sections were intact
+        self._assert_config_value(multihost, 'pam', 'debug_level', '9')
+        self._assert_config_value(multihost, 'nss', 'debug_level', '9')
+
+    def test_sssd_genconf_no_such_section(self, multihost):
+        """
+        Referencing a non-existant section must not fail, because
+        we want to call this command from the systemd unit files
+        and by default the sections don't have to be present
+        """
+        multihost.master[0].service_sssd('restart')
+        multihost.master[0].run_command(
+                '/usr/sbin/sssd --genconf-section=xyz')
diff --git a/src/tests/multihost/basic/utils_config.py b/src/tests/multihost/basic/utils_config.py
new file mode 100644
index 000000000..11b718e13
--- /dev/null
+++ b/src/tests/multihost/basic/utils_config.py
@@ -0,0 +1,32 @@
+""" Various utilities for manipulating SSSD configuration """
+import configparser as ConfigParser
+
+
+def set_param(multihost, section, key, value):
+    multihost.master[0].transport.get_file('/etc/sssd/sssd.conf',
+                                           '/tmp/sssd.conf')
+    sssdconfig = ConfigParser.ConfigParser()
+    sssdconfig.read('/tmp/sssd.conf')
+    if section not in sssdconfig.sections():
+        sssdconfig.add_section(section)
+
+    sssdconfig.set(section, key, value)
+    with open(str('/tmp/sssd.conf'), "w") as sssconf:
+        sssdconfig.write(sssconf)
+
+    multihost.master[0].transport.put_file('/tmp/sssd.conf',
+                                           '/etc/sssd/sssd.conf')
+
+
+def remove_section(multihost, section):
+    multihost.master[0].transport.get_file('/etc/sssd/sssd.conf',
+                                           '/tmp/sssd.conf')
+    sssdconfig = ConfigParser.ConfigParser()
+    sssdconfig.read('/tmp/sssd.conf')
+    sssdconfig.remove_section(section)
+
+    with open(str('/tmp/sssd.conf'), "w") as sssconf:
+        sssdconfig.write(sssconf)
+
+    multihost.master[0].transport.put_file('/tmp/sssd.conf',
+                                           '/etc/sssd/sssd.conf')

From 61798a6f9e730241a0b80c396449442fda2ba696 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhro...@redhat.com>
Date: Tue, 23 Oct 2018 16:16:18 +0200
Subject: [PATCH 6/6] TESTS: Add a multihost test for changing sssd-kcm debug
 level by just restarting the KCM service

Related:
https://pagure.io/SSSD/sssd/issue/3862

Adds a test for the previous patches to make sure just restarting
sssd-kcm with changes to the [kcm] section applies the configuration
options from the kcm section.
---
 src/tests/multihost/basic/test_kcm.py | 85 ++++++++++++++++++++++++++-
 1 file changed, 82 insertions(+), 3 deletions(-)

diff --git a/src/tests/multihost/basic/test_kcm.py b/src/tests/multihost/basic/test_kcm.py
index 93cfcfb3b..87e325bd7 100644
--- a/src/tests/multihost/basic/test_kcm.py
+++ b/src/tests/multihost/basic/test_kcm.py
@@ -2,14 +2,45 @@
 from sssd.testlib.common.utils import SSHClient
 import paramiko
 import pytest
+import os
+from utils_config import set_param, remove_section
 
 
 class TestSanityKCM(object):
     """ KCM Sanity Test cases """
-    def test_kinit_kcm(self, multihost):
+    def _kcm_service_op(self, multihost, svc_op):
+        systemd_kcm_op = 'systemctl %s sssd-kcm' % (svc_op)
+        multihost.master[0].run_command(systemd_kcm_op)
+
+    def _start_kcm(self, multihost):
+        self._kcm_service_op(multihost, 'start')
+
+    def _stop_kcm(self, multihost):
+        self._kcm_service_op(multihost, 'stop')
+
+    def _restart_kcm(self, multihost):
+        self._kcm_service_op(multihost, 'restart')
+
+    def _remove_kcm_log_file(self, multihost):
+        multihost.master[0].run_command('rm -f /var/log/sssd/sssd_kcm.log')
+
+    def _kcm_log_length(self, multihost):
+        basename = 'sssd_kcm.log'
+        kcm_log_file = '/var/log/sssd/' + basename
+        local_kcm_log_file = '/tmp/kcm.log'
+        try:
+            multihost.master[0].transport.get_file(kcm_log_file,
+                                                   local_kcm_log_file)
+        except FileNotFoundError:
+            return 0
+
+        nlines = sum(1 for line in open(local_kcm_log_file))
+        os.remove(local_kcm_log_file)
+        return nlines
+
+    def test_kinit_kcm(self, multihost, enable_kcm):
         """ Run kinit with KRB5CCNAME=KCM: """
-        start_kcm = 'systemctl start sssd-kcm'
-        multihost.master[0].run_command(start_kcm)
+        self._start_kcm(multihost)
         try:
             ssh = SSHClient(multihost.master[0].sys_hostname,
                             username='foo3', password='Secret123')
@@ -43,3 +74,51 @@ def test_ssh_login_kcm(self, multihost, enable_kcm):
         else:
             assert True
             ssh.close()
+
+    def test_kcm_debug_level_set(self, multihost, enable_kcm):
+        """
+        Test that just adding a [kcm] section and restarting the kcm
+        service enables debugging without having to restart the
+        whole sssd
+        """
+        # Start from a known-good state where the configuration is refreshed
+        # by the monitor and logging is completely disabled
+        multihost.master[0].service_sssd('stop')
+        self._stop_kcm(multihost)
+        self._remove_kcm_log_file(multihost)
+        set_param(multihost, 'kcm', 'debug_level', '0')
+        multihost.master[0].service_sssd('start')
+        self._start_kcm(multihost)
+
+        log_lines_pre = self._kcm_log_length(multihost)
+
+        # Debugging is disabled, kinit and make sure that no debug messages
+        # were produced
+        try:
+            ssh = SSHClient(multihost.master[0].sys_hostname,
+                            username='foo3', password='Secret123')
+        except paramiko.ssh_exception.AuthenticationException:
+            pytest.fail("Authentication Failed as user %s" % ('foo3'))
+        else:
+            ssh.execute_cmd('kdestroy')
+            ssh.close()
+
+        log_lines_nodebug = self._kcm_log_length(multihost)
+        assert log_lines_nodebug == log_lines_pre
+
+        # Enable debugging, restart only the kcm service, make sure some
+        # debug messages were produced
+        set_param(multihost, 'kcm', 'debug_level', '9')
+        self._restart_kcm(multihost)
+
+        try:
+            ssh = SSHClient(multihost.master[0].sys_hostname,
+                            username='foo3', password='Secret123')
+        except paramiko.ssh_exception.AuthenticationException:
+            pytest.fail("Authentication Failed as user %s" % ('foo3'))
+        else:
+            ssh.execute_cmd('kdestroy')
+            ssh.close()
+
+        log_lines_debug = self._kcm_log_length(multihost)
+        assert log_lines_debug > log_lines_pre + 100
_______________________________________________
sssd-devel mailing list -- sssd-devel@lists.fedorahosted.org
To unsubscribe send an email to sssd-devel-le...@lists.fedorahosted.org
Fedora Code of Conduct: https://getfedora.org/code-of-conduct.html
List Guidelines: https://fedoraproject.org/wiki/Mailing_list_guidelines
List Archives: 
https://lists.fedorahosted.org/archives/list/sssd-devel@lists.fedorahosted.org

Reply via email to