]> Frank Brehm's Git Trees - pixelpark/admin-tools.git/commitdiff
Splitted pp_lib/test_home_app.py into two different modules
authorFrank Brehm <frank.brehm@pixelpark.com>
Wed, 19 Apr 2017 16:06:58 +0000 (18:06 +0200)
committerFrank Brehm <frank.brehm@pixelpark.com>
Wed, 19 Apr 2017 16:06:58 +0000 (18:06 +0200)
pp_lib/homes_admin.py [new file with mode: 0644]
pp_lib/test_home_app.py
test-home

diff --git a/pp_lib/homes_admin.py b/pp_lib/homes_admin.py
new file mode 100644 (file)
index 0000000..085573a
--- /dev/null
@@ -0,0 +1,322 @@
+#!/usr/bin/env python
+# -*- coding: utf-8 -*-
+"""
+@author: Frank Brehm
+@contact: frank.brehm@pixelpark.com
+@copyright: © 2017 by Frank Brehm, Berlin
+@summary: The base module all maintaining scripts for the home directories
+"""
+from __future__ import absolute_import
+
+# Standard modules
+import sys
+import os
+import logging
+import logging.config
+import re
+import traceback
+import textwrap
+import pwd
+import copy
+import glob
+
+# Third party modules
+import six
+
+# Own modules
+from .global_version import __version__ as __global_version__
+
+from .errors import FunctionNotImplementedError, PpAppError
+
+from .common import pp, terminal_can_colors, to_bytes, to_bool
+
+from .cfg_app import PpCfgAppError, PpConfigApplication
+
+__version__ = '0.1.1'
+LOG = logging.getLogger(__name__)
+
+
+# =============================================================================
+class PpHomesAdminError(PpCfgAppError):
+    pass
+
+
+# =============================================================================
+class PpHomesAdminApp(PpConfigApplication):
+    """
+    Base class for applications maintaining the global Home directories.
+    """
+
+    # /mnt/nfs
+    default_chroot_homedir = os.sep + os.path.join('mnt', 'nfs')
+    # /home
+    default_home_root = os.sep + 'home'
+
+    # /etc/pixelpark/exclude_homes
+    default_exclude_file = os.sep + os.path.join('etc', 'pixelpark', 'exclude_homes')
+
+    comment_re = re.compile(r'\s*#.*')
+
+    # -------------------------------------------------------------------------
+    def __init__(self, appname=None, description=None, version=__version__):
+
+        self.default_mail_recipients = [
+            'admin.berlin@pixelpark.com'
+        ]
+        self.default_mail_cc = []
+
+        self.chroot_homedir = self.default_chroot_homedir
+        self.home_root_abs = self.default_home_root
+        self.home_root_rel = os.path.relpath(self.home_root_abs, os.sep)
+
+        self.exclude_file = self.default_exclude_file
+
+        self.exclude_dirs = []
+        self.passwd_home_dirs = []
+        self.unnecessary_dirs = []
+
+        super(PpHomesAdminApp, self).__init__(
+            appname=appname, version=version, description=description,
+            cfg_stems='homes-admin'
+        )
+
+    # -------------------------------------------------------------------------
+    def init_arg_parser(self):
+
+        homes_group = self.arg_parser.add_argument_group('Homes administration options.')
+
+        homes_group.add_argument(
+            '-R', '--chroot-dir',
+            metavar='DIR', dest='chroot_homedir',
+            help=("Directory, where the {h!r} share is mounted from the "
+                "NFS server. Maybe '/', default: {d!r}.").format(
+                    h=self.default_home_root, d=self.default_chroot_homedir)
+        )
+
+        homes_group.add_argument(
+            '-H', '--homes',
+            metavar='DIR', dest='home_root',
+            help=("The shared directory on the NFS server for all home directories. "
+                "Default: {!r}.").format(self.default_home_root)
+        )
+
+        homes_group.add_argument(
+            '-E', '--exclude-file',
+            metavar='FILE', dest='exclude_file',
+            help=("The file containing all directories underneath {h!r}, which are  "
+                "excluded from all operations. Default: {f!r}.").format(
+                    h=self.default_home_root, f=self.default_exclude_file)
+        )
+
+        super(PpHomesAdminApp, self).init_arg_parser()
+
+    # -------------------------------------------------------------------------
+    def perform_config(self):
+
+        super(PpHomesAdminApp, self).perform_config()
+
+        for section_name in self.cfg.keys():
+
+            if self.verbose > 3:
+                LOG.debug("Checking config section {!r} ...".format(section_name))
+
+            section = self.cfg[section_name]
+
+            if section_name.lower() not in (
+                    'test-home', 'test_home', 'testhome', 'homes', 'admin') :
+                continue
+
+            if self.verbose > 2:
+                LOG.debug("Evaluating config section {n!r}:\n{s}".format(
+                    n=section_name, s=pp(section)))
+
+            if section_name.lower() == 'homes':
+
+                if 'chroot_homedir' in section:
+                    v = section['chroot_homedir']
+                    if not os.path.isabs(v):
+                        msg = (
+                            "The chrooted path of the home directories must be an "
+                            "absolute pathname (found [{s}]/chroot_homedir "
+                            "=> {v!r} in configuration.").format(s=section_name, v=v)
+                        raise PpHomesAdminError(msg)
+                    self.chroot_homedir = v
+
+                if 'home_root' in section:
+                    v = section['home_root']
+                    if not os.path.isabs(v):
+                        msg = (
+                            "The root path of the home directories must be an "
+                            "absolute pathname (found [{s}]/home_root "
+                            "=> {v!r} in configuration.").format(s=section_name, v=v)
+                        raise PpHomesAdminError(msg)
+                    self.home_root_abs = v
+
+            elif section_name.lower() == 'admin':
+
+                if 'exclude_file' in section:
+                    v = section['exclude_file']
+                    if not os.path.isabs(v):
+                        msg = (
+                            "The path of file of excluded directories must be an "
+                            "absolute pathname (found [{s}]/exclude_file "
+                            "=> {v!r} in configuration.").format(s=section_name, v=v)
+                        raise PpHomesAdminError(msg)
+                    self.exclude_file = v
+
+        if hasattr(self.args, 'chroot_homedir') and self.args.chroot_homedir:
+            v = self.args.chroot_homedir
+            if not os.path.isabs(v):
+                msg = (
+                    "The chrooted path of the home directories must be an "
+                    "absolute pathname (got {!r} as command line parameter).").format(v)
+                raise PpHomesAdminError(msg)
+            self.chroot_homedir = v
+
+        if hasattr(self.args, 'home_root') and self.args.home_root:
+            v = self.args.home_root
+            if not os.path.isabs(v):
+                msg = (
+                    "The root path of the home directories must be an "
+                    "absolute pathname (got {!r} as command line parameter).").format(v)
+                raise PpHomesAdminError(msg)
+            self.home_root_abs = v
+
+        if hasattr(self.args, 'exclude_file') and self.args.exclude_file:
+            v = self.args.exclude_file
+            if not os.path.isabs(v):
+                msg = (
+                    "The path of file of excluded directories must be an "
+                    "absolute pathname (got {!r} as command line parameter).").format(v)
+                raise PpHomesAdminError(msg)
+            self.exclude_file = v
+
+        self.home_root_rel = os.path.relpath(self.home_root_abs, os.sep)
+        self.home_root_real = os.path.join(self.chroot_homedir, self.home_root_rel)
+
+    # -------------------------------------------------------------------------
+    def read_exclude_dirs(self):
+
+        LOG.info("Reading exclude file {!r} ...".format(self.exclude_file))
+        upper_dir = os.pardir + os.sep
+
+        if not os.path.exists(self.exclude_file):
+            msg = "Exclude file {!r} does not exists.".format(self.exclude_file)
+            LOG.error(msg)
+            return
+
+        if not os.path.isfile(self.exclude_file):
+            msg = "Exclude file {!r} is not a regular file.".format(self.exclude_file)
+            LOG.error(msg)
+            return
+
+        if not os.access(self.exclude_file, os.R_OK):
+            msg = "No read access to exclude file {!r}.".format(self.exclude_file)
+            LOG.error(msg)
+            return
+
+        open_args = {}
+        if six.PY3:
+            open_args['encoding'] = 'utf-8'
+            open_args['errors'] = 'surrogateescape'
+
+        with open(self.exclude_file, 'r', **open_args) as fh:
+            lnr = 0
+            for line in fh.readlines():
+                lnr += 1
+                line = line.strip()
+                if not line:
+                    continue
+                line = self.comment_re.sub('', line)
+                if not line:
+                    continue
+                if self.verbose > 3:
+                    LOG.debug("Evaluating line {l!r} (file {f!r}, line {lnr}).".format(
+                        l=line, f=self.exclude_file, lnr=lnr))
+                tokens = self.whitespace_re.split(line)
+                for token in tokens:
+                    if not os.path.isabs(token):
+                        LOG.warn((
+                            "Entry {e!r} in file {f!r}, line {l}, "
+                            "is not an absolute path.").format(
+                            e=token, f=self.exclude_file, l=lnr))
+                        continue
+                    home_relative = os.path.relpath(token, self.home_root_abs)
+                    if token == os.sep or home_relative.startswith(upper_dir):
+                        LOG.warn((
+                            "Entry {e!r} in file {f!r}, line {l}, "
+                            "is outside home root {h!r}.").format(
+                            e=token, f=self.exclude_file, l=lnr, h=self.home_root_abs))
+                        continue
+                    if token not in self.exclude_dirs:
+                        self.exclude_dirs.append(token)
+
+        self.exclude_dirs.sort(key=str.lower)
+
+        LOG.debug("Found {} directories to exclude.".format(len(self.exclude_dirs)))
+        if self.verbose > 2:
+            LOG.debug("Found directories to exclude:\n{}".format(pp(self.exclude_dirs)))
+
+    # -------------------------------------------------------------------------
+    def read_passwd_homes(self):
+
+        LOG.info("Reading all home directories from 'getent passwd' ...")
+
+        upper_dir = os.pardir + os.sep
+        entries = pwd.getpwall()
+
+        for entry in entries:
+            home = entry.pw_dir
+            if not home:
+                continue
+            home_relative = os.path.relpath(home, self.home_root_abs)
+            if home == os.sep or home_relative.startswith(upper_dir):
+                if self.verbose > 1:
+                    LOG.debug((
+                        "Home directory {d!r} of user {u!r} "
+                        "is outside home root {h!r}.").format(
+                        d=home, u=entry.pw_name, h=self.home_root_abs))
+                continue
+            if home not in self.passwd_home_dirs:
+                self.passwd_home_dirs.append(home)
+
+        self.passwd_home_dirs.sort(key=str.lower)
+
+        LOG.debug("Found {} home directories in passwd.".format(len(self.passwd_home_dirs)))
+        if self.verbose > 2:
+            LOG.debug("Home directories in passwd:\n{}".format(pp(self.passwd_home_dirs)))
+
+    # -------------------------------------------------------------------------
+    def check_homes(self):
+
+        LOG.info("Checking for unnecessary home directories ...")
+
+        glob_pattern = os.path.join(self.home_root_real, '*')
+        all_home_entries = glob.glob(glob_pattern)
+
+        for path in all_home_entries:
+            if not os.path.isdir(path):
+                continue
+            home_rel = os.sep + os.path.relpath(path, self.chroot_homedir)
+            if self.verbose > 2:
+                LOG.debug("Checking {p!r} ({h!r}) ...".format(
+                    p=path, h=home_rel))
+            if home_rel in self.passwd_home_dirs:
+                continue
+            if home_rel in self.exclude_dirs:
+                continue
+            LOG.debug("Marking {!r} as unnecessary.".format(home_rel))
+            self.unnecessary_dirs.append(home_rel)
+
+        self.unnecessary_dirs.sort(key=str.lower)
+
+
+# =============================================================================
+
+if __name__ == "__main__":
+
+    pass
+
+# =============================================================================
+
+# vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4 list
index 1c89a699ac723199e922c81cb1b493d7c4c6080f..e225d7c47aba087d98304513b02770d30e86d61c 100644 (file)
@@ -30,53 +30,30 @@ from .errors import FunctionNotImplementedError, PpAppError
 
 from .common import pp, terminal_can_colors, to_bytes, to_bool
 
-from .cfg_app import PpCfgAppError, PpConfigApplication
+from .cfg_app import PpCfgAppError
 
-__version__ = '0.4.4'
+from .homes_admin import PpHomesAdminError, PpHomesAdminApp
+
+__version__ = '0.5.1'
 LOG = logging.getLogger(__name__)
 
 
 # =============================================================================
-class PpTestHomeError(PpCfgAppError):
+class PpTestHomeError(PpHomesAdminError):
     pass
 
 
 # =============================================================================
-class PpTestHomeApp(PpConfigApplication):
+class PpTestHomeApp(PpHomesAdminApp):
     """
     Class for the 'test-home' application to check for unnacessary home directories.
     """
 
-    # /mnt/nfs
-    default_chroot_homedir = os.sep + os.path.join('mnt', 'nfs')
-    # /home
-    default_home_root = os.sep + 'home'
-
-    # /etc/pixelpark/exclude_homes
-    default_exclude_file = os.sep + os.path.join('etc', 'pixelpark', 'exclude_homes')
-
-    comment_re = re.compile(r'\s*#.*')
-
     # -------------------------------------------------------------------------
     def __init__(self, appname=None, version=__version__):
 
-        self.default_mail_recipients = [
-            'admin.berlin@pixelpark.com'
-        ]
-        self.default_mail_cc = []
-
         self.default_reply_to = 'noreply@pixelpark.com'
 
-        self.chroot_homedir = self.default_chroot_homedir
-        self.home_root_abs = self.default_home_root
-        self.home_root_rel = os.path.relpath(self.home_root_abs, os.sep)
-
-        self.exclude_file = self.default_exclude_file
-
-        self.exclude_dirs = []
-        self.passwd_home_dirs = []
-        self.unnecessary_dirs = []
-
         description = textwrap.dedent('''\
             This scripts detects unnecessary home directories - without an
             appropriate home directory in the passwd database and not excluded
@@ -85,61 +62,12 @@ class PpTestHomeApp(PpConfigApplication):
 
         super(PpTestHomeApp, self).__init__(
             appname=appname, version=version, description=description,
-            cfg_stems='test-home'
         )
 
-        self.initialized = True
-
-    # -------------------------------------------------------------------------
-    def perform_config(self):
-
-        super(PpTestHomeApp, self).perform_config()
+        if not self.mail_recipients:
+            self.exit(5)
 
-        for section_name in self.cfg.keys():
-
-            if self.verbose > 3:
-                LOG.debug("Checking config section {!r} ...".format(section_name))
-
-            if section_name.lower() not in ('test-home', 'test_home', 'testhome') :
-                continue
-
-            section = self.cfg[section_name]
-            if self.verbose > 2:
-                LOG.debug("Evaluating config section {n!r}:\n{s}".format(
-                    n=section_name, s=pp(section)))
-
-            if 'chroot_homedir' in section:
-                v = section['chroot_homedir']
-                if not os.path.isabs(v):
-                    msg = (
-                        "The chrooted path of the home directories must be an "
-                        "absolute pathname (found [{s}]/chroot_homedir "
-                        "=> {v!r} in configuration.").format(s=section_name, v=v)
-                    raise PpMkHomeError(msg)
-                self.chroot_homedir = v
-
-            if 'home_root' in section:
-                v = section['home_root']
-                if not os.path.isabs(v):
-                    msg = (
-                        "The root path of the home directories must be an "
-                        "absolute pathname (found [{s}]/home_root "
-                        "=> {v!r} in configuration.").format(s=section_name, v=v)
-                    raise PpMkHomeError(msg)
-                self.home_root_abs = v
-
-            if 'exclude_file' in section:
-                v = section['exclude_file']
-                if not os.path.isabs(v):
-                    msg = (
-                        "The path of file of excluded directories must be an "
-                        "absolute pathname (found [{s}]/exclude_file "
-                        "=> {v!r} in configuration.").format(s=section_name, v=v)
-                    raise PpMkHomeError(msg)
-                self.exclude_file = v
-
-        self.home_root_rel = os.path.relpath(self.home_root_abs, os.sep)
-        self.home_root_real = os.path.join(self.chroot_homedir, self.home_root_rel)
+        self.initialized = True
 
     # -------------------------------------------------------------------------
     def _run(self):
@@ -149,122 +77,6 @@ class PpTestHomeApp(PpConfigApplication):
         self.check_homes()
         self.send_results()
 
-    # -------------------------------------------------------------------------
-    def read_exclude_dirs(self):
-
-        LOG.info("Reading exclude file {!r} ...".format(self.exclude_file))
-        upper_dir = os.pardir + os.sep
-
-        if not os.path.exists(self.exclude_file):
-            msg = "Exclude file {!r} does not exists.".format(self.exclude_file)
-            LOG.error(msg)
-            return
-
-        if not os.path.isfile(self.exclude_file):
-            msg = "Exclude file {!r} is not a regular file.".format(self.exclude_file)
-            LOG.error(msg)
-            return
-
-        if not os.access(self.exclude_file, os.R_OK):
-            msg = "No read access to exclude file {!r}.".format(self.exclude_file)
-            LOG.error(msg)
-            return
-
-        open_args = {}
-        if six.PY3:
-            open_args['encoding'] = 'utf-8'
-            open_args['errors'] = 'surrogateescape'
-
-        with open(self.exclude_file, 'r', **open_args) as fh:
-            lnr = 0
-            for line in fh.readlines():
-                lnr += 1
-                line = line.strip()
-                if not line:
-                    continue
-                line = self.comment_re.sub('', line)
-                if not line:
-                    continue
-                if self.verbose > 3:
-                    LOG.debug("Evaluating line {l!r} (file {f!r}, line {lnr}).".format(
-                        l=line, f=self.exclude_file, lnr=lnr))
-                tokens = self.whitespace_re.split(line)
-                for token in tokens:
-                    if not os.path.isabs(token):
-                        LOG.warn((
-                            "Entry {e!r} in file {f!r}, line {l}, "
-                            "is not an absolute path.").format(
-                            e=token, f=self.exclude_file, l=lnr))
-                        continue
-                    home_relative = os.path.relpath(token, self.home_root_abs)
-                    if token == os.sep or home_relative.startswith(upper_dir):
-                        LOG.warn((
-                            "Entry {e!r} in file {f!r}, line {l}, "
-                            "is outside home root {h!r}.").format(
-                            e=token, f=self.exclude_file, l=lnr, h=self.home_root_abs))
-                        continue
-                    if token not in self.exclude_dirs:
-                        self.exclude_dirs.append(token)
-
-        self.exclude_dirs.sort(key=str.lower)
-
-        LOG.debug("Found {} directories to exclude.".format(len(self.exclude_dirs)))
-        if self.verbose > 2:
-            LOG.debug("Found directories to exclude:\n{}".format(pp(self.exclude_dirs)))
-
-    # -------------------------------------------------------------------------
-    def read_passwd_homes(self):
-
-        LOG.info("Reading all home directories from 'getent passwd' ...")
-
-        upper_dir = os.pardir + os.sep
-        entries = pwd.getpwall()
-
-        for entry in entries:
-            home = entry.pw_dir
-            if not home:
-                continue
-            home_relative = os.path.relpath(home, self.home_root_abs)
-            if home == os.sep or home_relative.startswith(upper_dir):
-                if self.verbose > 1:
-                    LOG.debug((
-                        "Home directory {d!r} of user {u!r} "
-                        "is outside home root {h!r}.").format(
-                        d=home, u=entry.pw_name, h=self.home_root_abs))
-                continue
-            if home not in self.passwd_home_dirs:
-                self.passwd_home_dirs.append(home)
-
-        self.passwd_home_dirs.sort(key=str.lower)
-
-        LOG.debug("Found {} home directories in passwd.".format(len(self.passwd_home_dirs)))
-        if self.verbose > 2:
-            LOG.debug("Home directories in passwd:\n{}".format(pp(self.passwd_home_dirs)))
-
-    # -------------------------------------------------------------------------
-    def check_homes(self):
-
-        LOG.info("Checking for unnecessary home directories ...")
-
-        glob_pattern = os.path.join(self.home_root_real, '*')
-        all_home_entries = glob.glob(glob_pattern)
-
-        for path in all_home_entries:
-            if not os.path.isdir(path):
-                continue
-            home_rel = os.sep + os.path.relpath(path, self.chroot_homedir)
-            if self.verbose > 2:
-                LOG.debug("Checking {p!r} ({h!r}) ...".format(
-                    p=path, h=home_rel))
-            if home_rel in self.passwd_home_dirs:
-                continue
-            if home_rel in self.exclude_dirs:
-                continue
-            LOG.debug("Marking {!r} as unnecessary.".format(home_rel))
-            self.unnecessary_dirs.append(home_rel)
-
-        self.unnecessary_dirs.sort(key=str.lower)
-
     # -------------------------------------------------------------------------
     def send_results(self):
 
index 32eb03e89cc40cb35c51119013e8f4c4a7333a08..05b4684ada5e6fa47bbf37aeb7096e0c40b4a9ab 100755 (executable)
--- a/test-home
+++ b/test-home
@@ -39,5 +39,3 @@ app()
 sys.exit(0)
 
 # vim: tabstop=4 expandtab shiftwidth=4 softtabstop=4
-
-# vim: ts=4