]> git.apps.os.sepia.ceph.com Git - teuthology.git/commitdiff
Verify that the correct ceph version is installed on updates and install 451/head
authorAndrew Schoen <aschoen@redhat.com>
Thu, 26 Feb 2015 21:12:29 +0000 (15:12 -0600)
committerAndrew Schoen <aschoen@redhat.com>
Thu, 12 Mar 2015 16:25:44 +0000 (11:25 -0500)
This adds a function to teuthology.packaging that can be reused to
fetch the installed version of a package for the given remote.

Signed-off-by: Andrew Schoen <aschoen@redhat.com>
teuthology/packaging.py
teuthology/task/install.py
teuthology/test/task/__init__.py [new file with mode: 0644]
teuthology/test/task/test_install.py [new file with mode: 0644]
teuthology/test/test_packaging.py

index c49861b3740db2de8cb301f2ffc99e2b7ffcd0de..df6786ed155386dc46430a734afddc6eb67bc2dc 100644 (file)
@@ -1,5 +1,6 @@
 import logging
 import ast
+import re
 
 from cStringIO import StringIO
 
@@ -197,3 +198,42 @@ def get_koji_package_name(package, build_info, arch="x86_64"):
     )
 
     return pkg_name
+
+
+def get_package_version(remote, package):
+    installed_ver = None
+    if remote.os.package_type == "deb":
+        proc = remote.run(
+            args=[
+                'dpkg-query', '-W', '-f', '${Version}', package
+            ],
+            stdout=StringIO(),
+        )
+    else:
+        proc = remote.run(
+            args=[
+                'rpm', '-q', package, '--qf', '%{VERSION}'
+            ],
+            stdout=StringIO(),
+        )
+    if proc.exitstatus == 0:
+        installed_ver = proc.stdout.getvalue().strip()
+        # Does this look like a version string?
+        # this assumes a version string starts with non-alpha characters
+        if installed_ver and re.match('^[^a-zA-Z]', installed_ver):
+            log.info("The installed version of {pkg} is {ver}".format(
+                pkg=package,
+                ver=installed_ver,
+            ))
+        else:
+            installed_ver = None
+    else:
+        # should this throw an exception and stop the job?
+        log.warning(
+            "Unable to determine if {pkg} is installed: {stdout}".format(
+                pkg=package,
+                stdout=proc.stdout.getvalue().strip(),
+            )
+        )
+
+    return installed_ver
index 4f235b8459485d9fbda8d2250de1b3b6b806d902..72a7cee41c7a6263983df821b5a608a6679e80b7 100644 (file)
@@ -9,7 +9,7 @@ import subprocess
 
 from teuthology.config import config as teuth_config
 from teuthology import misc as teuthology
-from teuthology import contextutil
+from teuthology import contextutil, packaging
 from teuthology.exceptions import VersionNotFoundError
 from teuthology.parallel import parallel
 from ..orchestra import run
@@ -238,6 +238,16 @@ def _block_looking_for_package_version(remote, base_url, wait=False):
             raise VersionNotFoundError(base_url)
         break
     version = r.stdout.getvalue().strip()
+    # FIXME: 'version' as retreived from the repo is actually the RPM version
+    # PLUS *part* of the release. Example:
+    # Right now, ceph master is given the following version in the repo file:
+    # v0.67-rc3.164.gd5aa3a9 - whereas in reality the RPM version is 0.61.7
+    # and the release is 37.g1243c97.el6 (for centos6).
+    # Point being, I have to mangle a little here.
+    if version[0] == 'v':
+        version = version[1:]
+    if '-' in version:
+        version = version.split('-')[0]
     return version
 
 def _get_local_dir(config, remote):
@@ -470,6 +480,32 @@ def _update_rpm_package_list_and_install(ctx, remote, rpm, config):
                         run.Raw(';'), 'fi'])
 
 
+def verify_ceph_version(ctx, config, remote):
+    """
+    Ensures that the version of ceph installed is what
+    was asked for in the config.
+    """
+    base_url = _get_baseurl(ctx, remote, config)
+    version = _block_looking_for_package_version(
+        remote,
+        base_url,
+        config.get('wait_for_package', False)
+    )
+    installed_ver = packaging.get_package_version(remote, 'ceph')
+    if installed_ver and version in installed_ver:
+        msg = "The correct ceph version {ver} is installed.".format(
+            ver=version
+        )
+        log.info(msg)
+    else:
+        raise RuntimeError(
+            "Ceph version {ver} was not installed, found {installed}.".format(
+                ver=version,
+                installed=installed_ver
+            )
+        )
+
+
 def purge_data(ctx):
     """
     Purge /var/lib/ceph on every remote in ctx.
@@ -527,6 +563,10 @@ def install_packages(ctx, pkgs, config):
                 install_pkgs[system_type],
                 ctx, remote, pkgs[system_type], config)
 
+    for remote in ctx.cluster.remotes.iterkeys():
+        # verifies that the install worked as expected
+        verify_ceph_version(ctx, config, remote)
+
 
 def _remove_deb(ctx, config, remote, debs):
     """
@@ -906,10 +946,6 @@ def _upgrade_rpm_packages(ctx, config, remote, pkgs):
             arch=distinfo['arch'],)
     )
 
-    log.info("Getting version and release for the currently installed ceph...")
-    remote.run(args=[
-        'rpm', '-q', 'ceph', '--qf', '%{VERSION}-%{RELEASE}'
-    ])
     base_url = _get_baseurl(ctx, remote, config)
     log.info('Repo base URL: %s', base_url)
     project = config.get('project', 'ceph')
@@ -943,10 +979,6 @@ def _upgrade_rpm_packages(ctx, config, remote, pkgs):
     args = ['sudo', 'yum', '-y', 'install']
     args += pkgs
     remote.run(args=args)
-    log.info("Getting version and release for the newly upgraded ceph...")
-    remote.run(args=[
-        'rpm', '-q', 'ceph', '--qf', '%{VERSION}-%{RELEASE}'
-    ])
 
 
 def upgrade_old_style(ctx, node, remote, pkgs, system_type):
@@ -1042,6 +1074,7 @@ def upgrade_common(ctx, config, deploy_style):
         node['project'] = project
 
         deploy_style(ctx, node, remote, pkgs, system_type)
+        verify_ceph_version(ctx, node, remote)
 
 
 docstring_for_upgrade = """"
diff --git a/teuthology/test/task/__init__.py b/teuthology/test/task/__init__.py
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/teuthology/test/task/test_install.py b/teuthology/test/task/test_install.py
new file mode 100644 (file)
index 0000000..3052b56
--- /dev/null
@@ -0,0 +1,27 @@
+import pytest
+
+from mock import patch, Mock
+
+from teuthology.task import install
+
+
+class TestInstall(object):
+
+    @patch("teuthology.task.install._get_baseurl")
+    @patch("teuthology.task.install._block_looking_for_package_version")
+    @patch("teuthology.task.install.packaging.get_package_version")
+    def test_verify_ceph_version_success(self, m_get_package_version, m_block,
+                                         m_get_baseurl):
+        m_block.return_value = "0.89.0"
+        m_get_package_version.return_value = "0.89.0"
+        install.verify_ceph_version(Mock(), Mock(), Mock())
+
+    @patch("teuthology.task.install._get_baseurl")
+    @patch("teuthology.task.install._block_looking_for_package_version")
+    @patch("teuthology.task.install.packaging.get_package_version")
+    def test_verify_ceph_version_failed(self, m_get_package_version, m_block,
+                                        m_get_baseurl):
+        m_block.return_value = "0.89.0"
+        m_get_package_version.return_value = "0.89.1"
+        with pytest.raises(RuntimeError):
+            install.verify_ceph_version(Mock(), Mock(), Mock())
index 04f91d8173ec7a0efbd06ed0025345b3ae9e76f1..3f74bf6019eb9d7e50a0bc023a54863ddc0fb9d3 100644 (file)
@@ -145,3 +145,62 @@ class TestPackaging(object):
         m_ctx.summary = dict()
         with pytest.raises(RuntimeError):
             packaging.get_koji_build_info(1, m_remote, m_ctx)
+
+    def test_get_package_version_deb_found(self):
+        remote = Mock()
+        remote.os.package_type = "deb"
+        proc = Mock()
+        proc.exitstatus = 0
+        proc.stdout.getvalue.return_value = "2.2"
+        remote.run.return_value = proc
+        result = packaging.get_package_version(remote, "apache2")
+        assert result == "2.2"
+
+    def test_get_package_version_deb_command(self):
+        remote = Mock()
+        remote.os.package_type = "deb"
+        packaging.get_package_version(remote, "apache2")
+        args, kwargs = remote.run.call_args
+        expected_args = ['dpkg-query', '-W', '-f', '${Version}', 'apache2']
+        assert expected_args == kwargs['args']
+
+    def test_get_package_version_rpm_found(self):
+        remote = Mock()
+        remote.os.package_type = "rpm"
+        proc = Mock()
+        proc.exitstatus = 0
+        proc.stdout.getvalue.return_value = "2.2"
+        remote.run.return_value = proc
+        result = packaging.get_package_version(remote, "httpd")
+        assert result == "2.2"
+
+    def test_get_package_version_rpm_command(self):
+        remote = Mock()
+        remote.os.package_type = "rpm"
+        packaging.get_package_version(remote, "httpd")
+        args, kwargs = remote.run.call_args
+        expected_args = ['rpm', '-q', 'httpd', '--qf', '%{VERSION}']
+        assert expected_args == kwargs['args']
+
+    def test_get_package_version_not_found(self):
+        remote = Mock()
+        remote.os.package_type = "rpm"
+        proc = Mock()
+        proc.exitstatus = 1
+        proc.stdout.getvalue.return_value = "not installed"
+        remote.run.return_value = proc
+        result = packaging.get_package_version(remote, "httpd")
+        assert result is None
+
+    def test_get_package_version_invalid_version(self):
+        # this tests the possibility that the package is not found
+        # but the exitstatus is still 0.  Not entirely sure we'll ever
+        # hit this condition, but I want to test the codepath regardless
+        remote = Mock()
+        remote.os.package_type = "rpm"
+        proc = Mock()
+        proc.exitstatus = 0
+        proc.stdout.getvalue.return_value = "not installed"
+        remote.run.return_value = proc
+        result = packaging.get_package_version(remote, "httpd")
+        assert result is None