← Back to team overview

cloud-init-dev team mailing list archive

[Merge] ~rjschwei/cloud-init:noLnxDistro into cloud-init:master

 

Robert Schweikert has proposed merging ~rjschwei/cloud-init:noLnxDistro into cloud-init:master.

Requested reviews:
  cloud-init commiters (cloud-init-dev)
Related bugs:
  Bug #1745235 in cloud-init: "distribution detection"
  https://bugs.launchpad.net/cloud-init/+bug/1745235

For more details, see:
https://code.launchpad.net/~rjschwei/cloud-init/+git/cloud-init/+merge/336794

lp#1745235

Allow the user to set the distribution with --distro argument to setup.py. Fall back is to read /etc/os-release. Final backup is to use platform.dist() Python function. The platform.dist() function is deprecated and will be removed in Python 3.7
-- 
Your team cloud-init commiters is requested to review the proposed merge of ~rjschwei/cloud-init:noLnxDistro into cloud-init:master.
diff --git a/cloudinit/tests/test_util.py b/cloudinit/tests/test_util.py
index ba6bf69..3fb12c7 100644
--- a/cloudinit/tests/test_util.py
+++ b/cloudinit/tests/test_util.py
@@ -3,10 +3,12 @@
 """Tests for cloudinit.util"""
 
 import logging
+import platform
 
 import cloudinit.util as util
 
 from cloudinit.tests.helpers import CiTestCase, mock
+from textwrap import dedent
 
 LOG = logging.getLogger(__name__)
 
@@ -15,6 +17,29 @@ MOUNT_INFO = [
     '153 68 254:0 / /home rw,relatime shared:101 - xfs /dev/sda2 rw,attr2'
 ]
 
+OS_RELEASE_SLES = dedent("""\
+NAME="SLES"\n
+VERSION="12-SP3"\n
+VERSION_ID="12.3"\n
+PRETTY_NAME="SUSE Linux Enterprise Server 12 SP3"\n
+ID="sles"\nANSI_COLOR="0;32"\n
+CPE_NAME="cpe:/o:suse:sles:12:sp3"\n
+""")
+
+OS_RELEASE_UBUNTU = dedent("""\
+NAME="Ubuntu"\n
+VERSION="16.04.3 LTS (Xenial Xerus)"\n
+ID=ubuntu\n
+ID_LIKE=debian\n
+PRETTY_NAME="Ubuntu 16.04.3 LTS"\n
+VERSION_ID="16.04"\n
+HOME_URL="http://www.ubuntu.com/"\n
+SUPPORT_URL="http://help.ubuntu.com/"\n
+BUG_REPORT_URL="http://bugs.launchpad.net/ubuntu/"\n
+VERSION_CODENAME=xenial\n
+UBUNTU_CODENAME=xenial\n
+""")
+
 
 class TestUtil(CiTestCase):
 
@@ -44,3 +69,61 @@ class TestUtil(CiTestCase):
         m_mount_info.return_value = ('/dev/sda1', 'btrfs', '/', 'ro,relatime')
         is_rw = util.mount_is_read_write('/')
         self.assertEqual(is_rw, False)
+
+    @mock.patch('os.path.exists')
+    @mock.patch('cloudinit.util.load_file')
+    def test_get_linux_distro_quoted_name(self, m_os_release, m_path_exists):
+        m_os_release.return_value = OS_RELEASE_SLES
+        m_path_exists.side_effect = os_release_exists
+        dist = util.get_linux_distro()
+        self.assertEqual(('sles', '12.3', platform.machine()), dist)
+
+    @mock.patch('os.path.exists')
+    @mock.patch('cloudinit.util.load_file')
+    def test_get_linux_distro_bare_name(self, m_os_release, m_path_exists):
+        m_os_release.return_value = OS_RELEASE_UBUNTU
+        m_path_exists.side_effect = os_release_exists
+        dist = util.get_linux_distro()
+        self.assertEqual(('ubuntu', '16.04', platform.machine()), dist)
+
+    @mock.patch('os.path.exists')
+    @mock.patch('platform.dist')
+    def test_get_linux_distro_no_data(self, m_platform_dist, m_path_exists):
+        m_platform_dist.return_value = ('', '', '')
+        m_path_exists.return_value = 0
+        dist = util.get_linux_distro()
+        self.assertEqual(('', '', ''), dist)
+
+    @mock.patch('os.path.exists')
+    @mock.patch('platform.dist')
+    def test_get_linux_distro_no_impl(self, m_platform_dist, m_path_exists):
+        m_platform_dist.side_effect = Exception()
+        m_path_exists.return_value = 0
+        dist = util.get_linux_distro()
+        self.assertEqual(('', '', ''), dist)
+
+    @mock.patch('os.path.exists')
+    @mock.patch('platform.dist')
+    def test_get_linux_distro_plat_data(self, m_platform_dist, m_path_exists):
+        m_platform_dist.return_value = ('foo', '1.1', 'aarch64')
+        m_path_exists.return_value = 0
+        dist = util.get_linux_distro()
+        self.assertEqual(('foo', '1.1', 'aarch64'), dist)
+
+    @mock.patch('os.path.exists')
+    @mock.patch('cloudinit.util.load_file')
+    def test_get_linux_distro_user_set(self, m_user_data, m_path_exists):
+        m_user_data.return_value = 'debian'
+        m_path_exists.side_effect = user_set_distro
+        dist = util.get_linux_distro()
+        self.assertEqual(('debian', 'not set', platform.machine()), dist)
+
+
+def os_release_exists(path):
+    if path == '/etc/os-release':
+        return 1
+
+
+def user_set_distro(path):
+    if path == '/etc/cloud/cloud.cfg.d/cloud-init.user.distro':
+        return 1
diff --git a/cloudinit/util.py b/cloudinit/util.py
index 338fb97..9573411 100644
--- a/cloudinit/util.py
+++ b/cloudinit/util.py
@@ -576,6 +576,44 @@ def get_cfg_option_int(yobj, key, default=0):
     return int(get_cfg_option_str(yobj, key, default=default))
 
 
+def get_linux_distro():
+    distro_name = ''
+    distro_version = ''
+    if os.path.exists('/etc/cloud/cloud.cfg.d/cloud-init.user.distro'):
+        distro_name = load_file(
+            '/etc/cloud/cloud.cfg.d/cloud-init.user.distro')
+        distro_version = 'not set'
+    elif os.path.exists('/etc/os-release'):
+        os_release = load_file('/etc/os-release').split('\n')
+        for entry in os_release:
+            if entry.startswith('ID='):
+                distro_name = entry.split('=')[-1]
+                if '"' in distro_name:
+                    distro_name = distro_name.split('"')[1]
+            if entry.startswith('VERSION_ID='):
+                # Lets hope for the best that distros stay consistent ;)
+                distro_version = entry.split('"')[1]
+    else:
+        dist = ('', '', '')
+        try:
+            # Will be removed in 3.7
+            dist = platform.dist()  # pylint: disable=W1505
+        except Exception:
+            pass
+        finally:
+            found = None
+            for entry in dist:
+                if entry:
+                    found = 1
+            if not found:
+                msg = 'Unable to determine distribution, template expansion '
+                msg += 'may have unexpected results'
+                LOG.warning(msg)
+        return dist
+
+    return (distro_name, distro_version, platform.machine())
+
+
 def system_info():
     info = {
         'platform': platform.platform(),
@@ -583,19 +621,19 @@ def system_info():
         'release': platform.release(),
         'python': platform.python_version(),
         'uname': platform.uname(),
-        'dist': platform.dist(),  # pylint: disable=W1505
+        'dist': get_linux_distro()
     }
     system = info['system'].lower()
     var = 'unknown'
     if system == "linux":
         linux_dist = info['dist'][0].lower()
-        if linux_dist in ('centos', 'fedora', 'debian'):
+        if linux_dist in ('centos', 'debian', 'fedora', 'rhel', 'suse'):
             var = linux_dist
         elif linux_dist in ('ubuntu', 'linuxmint', 'mint'):
             var = 'ubuntu'
         elif linux_dist == 'redhat':
             var = 'rhel'
-        elif linux_dist == 'suse':
+        elif linux_dist in ('opensuse', 'sles'):
             var = 'suse'
         else:
             var = 'linux'
diff --git a/setup.py b/setup.py
index bc3f52a..6a5041d 100755
--- a/setup.py
+++ b/setup.py
@@ -25,7 +25,7 @@ from distutils.errors import DistutilsArgError
 import subprocess
 
 RENDERED_TMPD_PREFIX = "RENDERED_TEMPD"
-
+VARIANT = None
 
 def is_f(p):
     return os.path.isfile(p)
@@ -114,10 +114,20 @@ def render_tmpl(template):
     atexit.register(shutil.rmtree, tmpd)
     bname = os.path.basename(template).rstrip(tmpl_ext)
     fpath = os.path.join(tmpd, bname)
-    tiny_p([sys.executable, './tools/render-cloudcfg', template, fpath])
+    if VARIANT:
+        tiny_p([sys.executable, './tools/render-cloudcfg', '--variant',
+            VARIANT, template, fpath])
+    else:
+        tiny_p([sys.executable, './tools/render-cloudcfg', template, fpath])
     # return path relative to setup.py
     return os.path.join(os.path.basename(tmpd), bname)
 
+# User can set the variant for template rendering
+if '--distro' in sys.argv:
+    idx = sys.argv.index('--distro')
+    VARIANT = sys.argv[idx+1]
+    del sys.argv[idx+1]
+    sys.argv.remove('--distro')
 
 INITSYS_FILES = {
     'sysvinit': [f for f in glob('sysvinit/redhat/*') if is_f(f)],
@@ -227,6 +237,19 @@ if not in_virtualenv():
     for k in INITSYS_ROOTS.keys():
         INITSYS_ROOTS[k] = "/" + INITSYS_ROOTS[k]
 
+if VARIANT and sys.argv[1] == 'install':
+    base = ETC
+    config_dir = '/cloud/cloud.cfg.d'
+    if sys.argv.index('--root'):
+        root_idx = sys.argv.index('--root')
+        root_loc = sys.argv[root_idx+1]
+        base = root_loc + '/' + ETC
+    if not os.path.exists(base + config_dir):
+        os.makedirs(base + config_dir)
+    usr_distro = open(base + '/cloud/cloud.cfg.d/cloud-init.user.distro', 'w')
+    usr_distro.write(VARIANT)
+    usr_distro.close()
+
 data_files = [
     (ETC + '/cloud', [render_tmpl("config/cloud.cfg.tmpl")]),
     (ETC + '/cloud/cloud.cfg.d', glob('config/cloud.cfg.d/*')),
@@ -259,7 +282,7 @@ requirements = read_requires()
 setuptools.setup(
     name='cloud-init',
     version=get_version(),
-    description='EC2 initialisation magic',
+    description='Cloud instance initialisation magic',
     author='Scott Moser',
     author_email='scott.moser@xxxxxxxxxxxxx',
     url='http://launchpad.net/cloud-init/',
@@ -276,4 +299,7 @@ setuptools.setup(
     }
 )
 
+if os.path.exists('.setup_user_set_distro'):
+    os.unlink('.setup_user_set_distro')
+
 # vi: ts=4 expandtab

Follow ups