summaryrefslogtreecommitdiffstats
path: root/src/ceph/qa/tasks/calamari_nosetests.py
diff options
context:
space:
mode:
Diffstat (limited to 'src/ceph/qa/tasks/calamari_nosetests.py')
-rw-r--r--src/ceph/qa/tasks/calamari_nosetests.py289
1 files changed, 289 insertions, 0 deletions
diff --git a/src/ceph/qa/tasks/calamari_nosetests.py b/src/ceph/qa/tasks/calamari_nosetests.py
new file mode 100644
index 0000000..c6bbaf3
--- /dev/null
+++ b/src/ceph/qa/tasks/calamari_nosetests.py
@@ -0,0 +1,289 @@
+import contextlib
+import logging
+import os
+import textwrap
+import yaml
+
+from cStringIO import StringIO
+from teuthology import contextutil
+from teuthology import misc
+from teuthology import packaging
+from teuthology.orchestra import run
+
+log = logging.getLogger(__name__)
+
+# extra stuff we need to do our job here
+EXTRA_PKGS = [
+ 'git',
+]
+
+# stuff that would be in a devmode install, but should be
+# installed in the system for running nosetests against
+# a production install.
+EXTRA_NOSETEST_PKGS = [
+ 'python-psutil',
+ 'python-mock',
+]
+
+
+def find_client0(cluster):
+ ''' Find remote that has client.0 role, or None '''
+ for rem, roles in cluster.remotes.iteritems():
+ if 'client.0' in roles:
+ return rem
+ return None
+
+
+def pip(remote, package, venv=None, uninstall=False, force=False):
+ ''' {un}install a package with pip, possibly in a virtualenv '''
+ if venv:
+ pip = os.path.join(venv, 'bin', 'pip')
+ args = ['sudo', pip]
+ else:
+ args = ['sudo', 'pip']
+
+ if uninstall:
+ args.extend(['uninstall', '-y'])
+ else:
+ args.append('install')
+ if force:
+ args.append('-I')
+
+ args.append(package)
+ remote.run(args=args)
+
+
+@contextlib.contextmanager
+def install_epel(remote):
+ ''' install a disabled-by-default epel repo config file '''
+ remove = False
+ try:
+ if remote.os.package_type == 'deb':
+ yield
+ else:
+ remove = True
+ distromajor = remote.os.version.split('.')[0]
+
+ repofiledata = textwrap.dedent('''
+ [epel]
+ name=epel{version}
+ metalink=http://mirrors.fedoraproject.org/metalink?repo=epel-{version}&arch=$basearch
+ enabled=0
+ gpgcheck=0
+ ''').format(version=distromajor)
+
+ misc.create_file(remote, '/etc/yum.repos.d/epel.repo',
+ data=repofiledata, sudo=True)
+ remote.run(args='sudo yum clean all')
+ yield
+
+ finally:
+ if remove:
+ misc.delete_file(remote, '/etc/yum.repos.d/epel.repo', sudo=True)
+
+
+def enable_epel(remote, enable=True):
+ ''' enable/disable the epel repo '''
+ args = 'sudo sed -i'.split()
+ if enable:
+ args.extend(['s/enabled=0/enabled=1/'])
+ else:
+ args.extend(['s/enabled=1/enabled=0/'])
+ args.extend(['/etc/yum.repos.d/epel.repo'])
+
+ remote.run(args=args)
+ remote.run(args='sudo yum clean all')
+
+
+@contextlib.contextmanager
+def install_extra_pkgs(client):
+ ''' Install EXTRA_PKGS '''
+ try:
+ for pkg in EXTRA_PKGS:
+ packaging.install_package(pkg, client)
+ yield
+
+ finally:
+ for pkg in EXTRA_PKGS:
+ packaging.remove_package(pkg, client)
+
+
+@contextlib.contextmanager
+def clone_calamari(config, client):
+ ''' clone calamari source into current directory on remote '''
+ branch = config.get('calamari_branch', 'master')
+ url = config.get('calamari_giturl', 'git://github.com/ceph/calamari')
+ try:
+ out = StringIO()
+ # ensure branch is present (clone -b will succeed even if
+ # the branch doesn't exist, falling back to master)
+ client.run(
+ args='git ls-remote %s %s' % (url, branch),
+ stdout=out,
+ label='check for calamari branch %s existence' % branch
+ )
+ if len(out.getvalue()) == 0:
+ raise RuntimeError("Calamari branch %s doesn't exist" % branch)
+ client.run(args='git clone -b %s %s' % (branch, url))
+ yield
+ finally:
+ # sudo python setup.py develop may have left some root files around
+ client.run(args='sudo rm -rf calamari')
+
+
+@contextlib.contextmanager
+def write_info_yaml(cluster, client):
+ ''' write info.yaml to client for nosetests '''
+ try:
+ info = {
+ 'cluster': {
+ rem.name: {'roles': roles}
+ for rem, roles in cluster.remotes.iteritems()
+ }
+ }
+ misc.create_file(client, 'calamari/info.yaml',
+ data=yaml.safe_dump(info, default_flow_style=False))
+ yield
+ finally:
+ misc.delete_file(client, 'calamari/info.yaml')
+
+
+@contextlib.contextmanager
+def write_test_conf(client):
+ ''' write calamari/tests/test.conf to client for nosetests '''
+ try:
+ testconf = textwrap.dedent('''
+ [testing]
+
+ calamari_control = external
+ ceph_control = external
+ bootstrap = False
+ api_username = admin
+ api_password = admin
+ embedded_timeout_factor = 1
+ external_timeout_factor = 3
+ external_cluster_path = info.yaml
+ ''')
+ misc.create_file(client, 'calamari/tests/test.conf', data=testconf)
+ yield
+
+ finally:
+ misc.delete_file(client, 'calamari/tests/test.conf')
+
+
+@contextlib.contextmanager
+def prepare_nosetest_env(client):
+ try:
+ # extra dependencies that would be in the devmode venv
+ if client.os.package_type == 'rpm':
+ enable_epel(client, enable=True)
+ for package in EXTRA_NOSETEST_PKGS:
+ packaging.install_package(package, client)
+ if client.os.package_type == 'rpm':
+ enable_epel(client, enable=False)
+
+ # install nose itself into the calamari venv, force it in case it's
+ # already installed in the system, so we can invoke it by path without
+ # fear that it's not present
+ pip(client, 'nose', venv='/opt/calamari/venv', force=True)
+
+ # install a later version of requests into the venv as well
+ # (for precise)
+ pip(client, 'requests', venv='/opt/calamari/venv', force=True)
+
+ # link (setup.py develop) calamari/rest-api into the production venv
+ # because production does not include calamari_rest.management, needed
+ # for test_rest_api.py's ApiIntrospection
+ args = 'cd calamari/rest-api'.split() + [run.Raw(';')] + \
+ 'sudo /opt/calamari/venv/bin/python setup.py develop'.split()
+ client.run(args=args)
+
+ # because, at least in Python 2.6/Centos, site.py uses
+ # 'os.path.exists()' to process .pth file entries, and exists() uses
+ # access(2) to check for existence, all the paths leading up to
+ # $HOME/calamari/rest-api need to be searchable by all users of
+ # the package, which will include the WSGI/Django app, running
+ # as the Apache user. So make them all world-read-and-execute.
+ args = 'sudo chmod a+x'.split() + \
+ ['.', './calamari', './calamari/rest-api']
+ client.run(args=args)
+
+ # make one dummy request just to get the WSGI app to do
+ # all its log creation here, before the chmod below (I'm
+ # looking at you, graphite -- /var/log/calamari/info.log and
+ # /var/log/calamari/exception.log)
+ client.run(args='wget -q -O /dev/null http://localhost')
+
+ # /var/log/calamari/* is root-or-apache write-only
+ client.run(args='sudo chmod a+w /var/log/calamari/*')
+
+ yield
+
+ finally:
+ args = 'cd calamari/rest-api'.split() + [run.Raw(';')] + \
+ 'sudo /opt/calamari/venv/bin/python setup.py develop -u'.split()
+ client.run(args=args)
+ for pkg in ('nose', 'requests'):
+ pip(client, pkg, venv='/opt/calamari/venv', uninstall=True)
+ for package in EXTRA_NOSETEST_PKGS:
+ packaging.remove_package(package, client)
+
+
+@contextlib.contextmanager
+def run_nosetests(client):
+ ''' Actually run the tests '''
+ args = [
+ 'cd',
+ 'calamari',
+ run.Raw(';'),
+ 'CALAMARI_CONFIG=/etc/calamari/calamari.conf',
+ '/opt/calamari/venv/bin/nosetests',
+ '-v',
+ 'tests/',
+ ]
+ client.run(args=args)
+ yield
+
+
+@contextlib.contextmanager
+def task(ctx, config):
+ """
+ Run Calamari tests against an instance set up by 'calamari_server'.
+
+ -- clone the Calamari source into $HOME (see options)
+ -- write calamari/info.yaml describing the cluster
+ -- write calamari/tests/test.conf containing
+ 'external' for calamari_control and ceph_control
+ 'bootstrap = False' to disable test bootstrapping (installing minions)
+ no api_url necessary (inferred from client.0)
+ 'external_cluster_path = info.yaml'
+ -- modify the production Calamari install to allow test runs:
+ install nose in the venv
+ install EXTRA_NOSETEST_PKGS
+ link in, with setup.py develop, calamari_rest (for ApiIntrospection)
+ -- set CALAMARI_CONFIG to point to /etc/calamari/calamari.conf
+ -- nosetests -v tests/
+
+ Options are:
+ calamari_giturl: url from which to git clone calamari
+ (default: git://github.com/ceph/calamari)
+ calamari_branch: git branch of calamari to check out
+ (default: master)
+
+ Note: the tests must find a clean cluster, so don't forget to
+ set the crush default type appropriately, or install min_size OSD hosts
+ """
+ client0 = find_client0(ctx.cluster)
+ if client0 is None:
+ raise RuntimeError("must have client.0 role")
+
+ with contextutil.nested(
+ lambda: install_epel(client0),
+ lambda: install_extra_pkgs(client0),
+ lambda: clone_calamari(config, client0),
+ lambda: write_info_yaml(ctx.cluster, client0),
+ lambda: write_test_conf(client0),
+ lambda: prepare_nosetest_env(client0),
+ lambda: run_nosetests(client0),
+ ):
+ yield