Repository: cobbler/cobbler

Issue:
OpenSuSE: 'getpwnam(): name not found: apache'
Hi, I'm having problems to get cobbler run in the current OpenSuSE LEAP 15.0. On a fresh and updated install I want to run cobblerd and it quits with the following error

    systemctl status cobblerd
    [...]
    Dec 17 14:31:03 hydrogen cobblerd[3162]: Traceback (most recent call last):
    Dec 17 14:31:03 hydrogen cobblerd[3162]:   File "/usr/bin/cobblerd", line 90, in main
    Dec 17 14:31:03 hydrogen cobblerd[3162]:     app.core(api)
    Dec 17 14:31:03 hydrogen cobblerd[3162]:   File "/usr/lib/python2.7/site-packages/cobbler/cobblerd.py", line 43, in core
    Dec 17 14:31:03 hydrogen cobblerd[3162]:     regen_ss_file()
    Dec 17 14:31:03 hydrogen cobblerd[3162]:   File "/usr/lib/python2.7/site-packages/cobbler/cobblerd.py", line 64, in regen_ss_file
    Dec 17 14:31:03 hydrogen cobblerd[3162]:     os.lchown("/var/lib/cobbler/web.ss", pwd.getpwnam(http_user)[2], -1)
    Dec 17 14:31:03 hydrogen cobblerd[3162]: KeyError: 'getpwnam(): name not found: apache'

I could trace back the issue to the function `utils.check_dist()` in cobblerd.py:62 which results in an empty string being returned. The check returns `dist = platform.linux_distribution()[0].lower().strip()`, and that seems to returns an empty string.
I tested the following in a `python` environment:

    $ python
    Python 2.7.14 (default, Oct 12 2017, 15:50:02) [GCC] on linux2
    Type "help", "copyright", "credits" or "license" for more information.
    >>> import platform
    >>> dist = platform.linux_distribution()[0].lower().strip()
    >>> dist
    ''

I have added the user `apache` and the daemon started successfully, although this is not an elegant or intended solution. Is there anything known about this issue?

Greetings,
Felix


Discussion:
This is a long standing [issue](https://bugs.python.org/issue1322) with Python. There are plans to remove `platform.linux_distribution()` from Python. One not too bad solution would be to switch to [distro](https://pypi.org/project/distro/). The project seems to be actively maintained and the switch wouldn't cause too much trouble.

Resolving patch:
diff --git a/cobbler/action_reposync.py b/cobbler/action_reposync.py
--- a/cobbler/action_reposync.py
+++ b/cobbler/action_reposync.py
@@ -27,6 +27,7 @@
 import os.path
 import pipes
 import stat
+import distro
 
 HAS_YUM = True
 try:
@@ -690,9 +691,10 @@ def update_permissions(self, repo_path):
         """
         # all_path = os.path.join(repo_path, "*")
         owner = "root:apache"
-        if os.path.exists("/etc/SuSE-release"):
+        distribution = distro.linux_distribution()[0]
+        if distribution.lower() in ("sles", "opensuse leap", "opensuse tumbleweed"):
             owner = "root:www"
-        elif os.path.exists("/etc/debian_version"):
+        elif "debian" in distribution.lower()
             owner = "root:www-data"
 
         cmd1 = "chown -R " + owner + " %s" % repo_path
diff --git a/cobbler/utils.py b/cobbler/utils.py
--- a/cobbler/utils.py
+++ b/cobbler/utils.py
@@ -47,6 +47,7 @@
 import urllib.error
 import urllib.parse
 import yaml
+import distro
 
 from .cexceptions import FileNotFoundException, CX
 from cobbler import clogger
@@ -120,7 +121,7 @@ def log_exc(logger):
     (t, v, tb) = sys.exc_info()
     logger.info("Exception occured: %s" % t)
     logger.info("Exception value: %s" % v)
-    logger.info("Exception Info:\n%s" % string.join(traceback.format_list(traceback.extract_tb(tb))))
+    logger.info("Exception Info:\n%s" % "\n".join(traceback.format_list(traceback.extract_tb(tb))))
 
 
 def get_exc(exc, full=True):
@@ -909,9 +910,9 @@ def get_family():
     for item in redhat_list:
         if item in dist:
             return "redhat"
-    if dist in ("debian", "ubuntu"):
+    if "debian" in dist or "ubuntu" in dist:
         return "debian"
-    if "suse" in dist:
+    if dist in ("opensuse tumbleweed", "opensuse leap", "sles"):
         return "suse"
     return dist
 
@@ -920,15 +921,10 @@ def check_dist():
     """
     Determines what distro we're running under.
     """
-    import platform
-    try:
-        return platform.linux_distribution()[0].lower().strip()
-    except AttributeError:
-        return platform.dist()[0].lower().strip()
+    return distro.linux_distribution()[0].lower().strip()
 
 
 def os_release():
-
     family = get_family()
     if family == "redhat":
         fh = open("/etc/redhat-release")
@@ -965,16 +961,11 @@ def os_release():
             return (make, float(version))
 
     elif family == "suse":
-        fd = open("/etc/SuSE-release")
-        for line in fd.read().split("\n"):
-            if line.find("VERSION") != -1:
-                version = line.replace("VERSION = ", "")
-            if line.find("PATCHLEVEL") != -1:
-                rest = line.replace("PATCHLEVEL = ", "")
         make = "suse"
+        distribution, version = distro.linux_distribution()[:2]
+        if distribution.lower() not in ("sles", "opensuse leap", "opensuse tumbleweed"):
+            make = "unknown"
         return (make, float(version))
-    else:
-        return ("unknown", 0)
 
 
 def tftpboot_location():
diff --git a/setup.py b/setup.py
--- a/setup.py
+++ b/setup.py
@@ -603,7 +603,8 @@ def parse_os_release():
             "Cheetah3",
             "coverage",
             "Django",
-            "pymongo"
+            "pymongo",
+            "distro",
         ],
         packages=[
             "cobbler",


Test patch: