]> git.apps.os.sepia.ceph.com Git - teuthology.git/commitdiff
Move downburst-specific code to its own module
authorZack Cerza <zack@redhat.com>
Thu, 30 Jun 2016 15:59:09 +0000 (09:59 -0600)
committerZack Cerza <zack@redhat.com>
Thu, 30 Jun 2016 16:31:01 +0000 (10:31 -0600)
Signed-off-by: Zack Cerza <zack@redhat.com>
teuthology/lock.py
teuthology/provision/__init__.py
teuthology/provision/downburst.py [new file with mode: 0644]
teuthology/provision/test/test_downburst.py [new file with mode: 0644]
teuthology/test/test_provision.py [deleted file]
teuthology/test/test_vps_os_vers_parameter_checking.py

index 9ed3f53a1ae8d90a353e45c6e340a0bab6bb6e3a..bc6458c42fe8ed96de81d8676e6a691be3e2f244 100644 (file)
@@ -47,7 +47,7 @@ def get_distro_from_downburst():
                                  u'16.04(xenial)'],
                      u'sles': [u'11-sp2'],
                      u'debian': [u'6.0', u'7.0', u'8.0']}
-    executable_cmd = provision.downburst_executable()
+    executable_cmd = provision.downburst.downburst_executable()
     if not executable_cmd:
         log.warn("Downburst not found!")
         log.info('Using default values for supported os_type/os_version')
index afedbe93dfff09b58c08a196aa208eea8a202dfb..781863472e43fccb88e4e34b0ba28c01c08c9e30 100644 (file)
@@ -6,7 +6,6 @@ import re
 import subprocess
 import time
 import tempfile
-import yaml
 
 from subprocess import CalledProcessError
 
@@ -19,212 +18,10 @@ from ..exceptions import QuotaExceededError
 from ..misc import decanonicalize_hostname, get_distro, get_distro_version
 from ..lockstatus import get_status
 
+from .downburst import Downburst
 
-log = logging.getLogger(__name__)
-
-
-def downburst_executable():
-    """
-    First check for downburst in the user's path.
-    Then check in ~/src, ~ubuntu/src, and ~teuthology/src.
-    Return '' if no executable downburst is found.
-    """
-    if config.downburst:
-        return config.downburst
-    path = os.environ.get('PATH', None)
-    if path:
-        for p in os.environ.get('PATH', '').split(os.pathsep):
-            pth = os.path.join(p, 'downburst')
-            if os.access(pth, os.X_OK):
-                return pth
-    import pwd
-    little_old_me = pwd.getpwuid(os.getuid()).pw_name
-    for user in [little_old_me, 'ubuntu', 'teuthology']:
-        pth = os.path.expanduser(
-            "~%s/src/downburst/virtualenv/bin/downburst" % user)
-        if os.access(pth, os.X_OK):
-            return pth
-    return ''
-
-
-class Downburst(object):
-    """
-    A class that provides methods for creating and destroying virtual machine
-    instances using downburst: https://github.com/ceph/downburst
-    """
-    def __init__(self, name, os_type, os_version, status=None, user='ubuntu'):
-        self.name = name
-        self.os_type = os_type
-        self.os_version = os_version
-        self.status = status or get_status(self.name)
-        self.config_path = None
-        self.user_path = None
-        self.user = user
-        self.host = decanonicalize_hostname(self.status['vm_host']['name'])
-        self.executable = downburst_executable()
-
-    def create(self):
-        """
-        Launch a virtual machine instance.
-
-        If creation fails because an instance with the specified name is
-        already running, first destroy it, then try again. This process will
-        repeat two more times, waiting 60s between tries, before giving up.
-        """
-        if not self.executable:
-            log.error("No downburst executable found.")
-            return False
-        self.build_config()
-        success = None
-        with safe_while(sleep=60, tries=3,
-                        action="downburst create") as proceed:
-            while proceed():
-                (returncode, stdout, stderr) = self._run_create()
-                if returncode == 0:
-                    log.info("Downburst created %s: %s" % (self.name,
-                                                           stdout.strip()))
-                    success = True
-                    break
-                elif stderr:
-                    # If the guest already exists first destroy then re-create:
-                    if 'exists' in stderr:
-                        success = False
-                        log.info("Guest files exist. Re-creating guest: %s" %
-                                 (self.name))
-                        self.destroy()
-                    else:
-                        success = False
-                        log.info("Downburst failed on %s: %s" % (
-                            self.name, stderr.strip()))
-                        break
-            return success
-
-    def _run_create(self):
-        """
-        Used by create(), this method is what actually calls downburst when
-        creating a virtual machine instance.
-        """
-        if not self.config_path:
-            raise ValueError("I need a config_path!")
-        if not self.user_path:
-            raise ValueError("I need a user_path!")
-        shortname = decanonicalize_hostname(self.name)
 
-        args = [
-            self.executable,
-            '-c', self.host,
-            'create',
-            '--wait',
-            '--meta-data=%s' % self.config_path,
-            '--user-data=%s' % self.user_path,
-            shortname,
-        ]
-        log.info("Provisioning a {distro} {distroversion} vps".format(
-            distro=self.os_type,
-            distroversion=self.os_version
-        ))
-        proc = subprocess.Popen(args, stdout=subprocess.PIPE,
-                                stderr=subprocess.PIPE)
-        out, err = proc.communicate()
-        return (proc.returncode, out, err)
-
-    def destroy(self):
-        """
-        Destroy (shutdown and delete) a virtual machine instance.
-        """
-        executable = self.executable
-        if not executable:
-            log.error("No downburst executable found.")
-            return False
-        shortname = decanonicalize_hostname(self.name)
-        args = [executable, '-c', self.host, 'destroy', shortname]
-        proc = subprocess.Popen(args, stdout=subprocess.PIPE,
-                                stderr=subprocess.PIPE,)
-        out, err = proc.communicate()
-        if err:
-            log.error("Error destroying {machine}: {msg}".format(
-                machine=self.name, msg=err))
-            return False
-        elif proc.returncode == 0:
-            out_str = ': %s' % out if out else ''
-            log.info("Destroyed %s%s" % (self.name, out_str))
-            return True
-        else:
-            log.error("I don't know if the destroy of {node} succeded!".format(
-                node=self.name))
-            return False
-
-    def build_config(self):
-        """
-        Assemble a configuration to pass to downburst, and write it to a file.
-        """
-        config_fd = tempfile.NamedTemporaryFile(delete=False)
-
-        os_type = self.os_type.lower()
-        mac_address = self.status['mac_address']
-
-        file_info = {
-            'disk-size': '100G',
-            'ram': '3.8G',
-            'cpus': 1,
-            'networks': [
-                {'source': 'front', 'mac': mac_address}],
-            'distro': os_type,
-            'distroversion': self.os_version,
-            'additional-disks': 3,
-            'additional-disks-size': '200G',
-            'arch': 'x86_64',
-        }
-        fqdn = self.name.split('@')[1]
-        file_out = {
-            'downburst': file_info,
-            'local-hostname': fqdn,
-        }
-        yaml.safe_dump(file_out, config_fd)
-        self.config_path = config_fd.name
-
-        user_info = {
-            'user': self.user,
-            # Remove the user's password so console logins are possible
-            'runcmd': [
-                ['passwd', '-d', self.user],
-            ]
-        }
-        # On CentOS/RHEL/Fedora, write the correct mac address
-        if os_type in ['centos', 'rhel', 'fedora']:
-            user_info['runcmd'].extend([
-                ['sed', '-ie', 's/HWADDR=".*"/HWADDR="%s"/' % mac_address,
-                 '/etc/sysconfig/network-scripts/ifcfg-eth0'],
-            ])
-        # On Ubuntu, starting with 16.04, we need to install 'python' to get
-        # python2.7, which ansible needs
-        elif os_type == 'ubuntu':
-            if not 'packages' in user_info:
-                user_info['packages'] = list()
-            user_info['packages'].extend([
-                'python',
-            ])
-        user_fd = tempfile.NamedTemporaryFile(delete=False)
-        yaml.safe_dump(user_info, user_fd)
-        self.user_path = user_fd.name
-        return True
-
-    def remove_config(self):
-        """
-        Remove the downburst configuration file created by build_config()
-        """
-        if self.config_path and os.path.exists(self.config_path):
-            os.remove(self.config_path)
-            self.config_path = None
-            return True
-        if self.user_path and os.path.exists(self.user_path):
-            os.remove(self.user_path)
-            self.user_path = None
-            return True
-        return False
-
-    def __del__(self):
-        self.remove_config()
+log = logging.getLogger(__name__)
 
 
 class ProvisionOpenStack(OpenStack):
diff --git a/teuthology/provision/downburst.py b/teuthology/provision/downburst.py
new file mode 100644 (file)
index 0000000..b42ae4c
--- /dev/null
@@ -0,0 +1,217 @@
+import logging
+import os
+import subprocess
+import tempfile
+import yaml
+
+from ..config import config
+from ..contextutil import safe_while
+from ..misc import decanonicalize_hostname
+from ..lockstatus import get_status
+
+
+log = logging.getLogger(__name__)
+
+
+def downburst_executable():
+    """
+    First check for downburst in the user's path.
+    Then check in ~/src, ~ubuntu/src, and ~teuthology/src.
+    Return '' if no executable downburst is found.
+    """
+    if config.downburst:
+        return config.downburst
+    path = os.environ.get('PATH', None)
+    if path:
+        for p in os.environ.get('PATH', '').split(os.pathsep):
+            pth = os.path.join(p, 'downburst')
+            if os.access(pth, os.X_OK):
+                return pth
+    import pwd
+    little_old_me = pwd.getpwuid(os.getuid()).pw_name
+    for user in [little_old_me, 'ubuntu', 'teuthology']:
+        pth = os.path.expanduser(
+            "~%s/src/downburst/virtualenv/bin/downburst" % user)
+        if os.access(pth, os.X_OK):
+            return pth
+    return ''
+
+
+class Downburst(object):
+    """
+    A class that provides methods for creating and destroying virtual machine
+    instances using downburst: https://github.com/ceph/downburst
+    """
+    def __init__(self, name, os_type, os_version, status=None, user='ubuntu'):
+        self.name = name
+        self.os_type = os_type
+        self.os_version = os_version
+        self.status = status or get_status(self.name)
+        self.config_path = None
+        self.user_path = None
+        self.user = user
+        self.host = decanonicalize_hostname(self.status['vm_host']['name'])
+        self.executable = downburst_executable()
+
+    def create(self):
+        """
+        Launch a virtual machine instance.
+
+        If creation fails because an instance with the specified name is
+        already running, first destroy it, then try again. This process will
+        repeat two more times, waiting 60s between tries, before giving up.
+        """
+        if not self.executable:
+            log.error("No downburst executable found.")
+            return False
+        self.build_config()
+        success = None
+        with safe_while(sleep=60, tries=3,
+                        action="downburst create") as proceed:
+            while proceed():
+                (returncode, stdout, stderr) = self._run_create()
+                if returncode == 0:
+                    log.info("Downburst created %s: %s" % (self.name,
+                                                           stdout.strip()))
+                    success = True
+                    break
+                elif stderr:
+                    # If the guest already exists first destroy then re-create:
+                    if 'exists' in stderr:
+                        success = False
+                        log.info("Guest files exist. Re-creating guest: %s" %
+                                 (self.name))
+                        self.destroy()
+                    else:
+                        success = False
+                        log.info("Downburst failed on %s: %s" % (
+                            self.name, stderr.strip()))
+                        break
+            return success
+
+    def _run_create(self):
+        """
+        Used by create(), this method is what actually calls downburst when
+        creating a virtual machine instance.
+        """
+        if not self.config_path:
+            raise ValueError("I need a config_path!")
+        if not self.user_path:
+            raise ValueError("I need a user_path!")
+        shortname = decanonicalize_hostname(self.name)
+
+        args = [
+            self.executable,
+            '-c', self.host,
+            'create',
+            '--wait',
+            '--meta-data=%s' % self.config_path,
+            '--user-data=%s' % self.user_path,
+            shortname,
+        ]
+        log.info("Provisioning a {distro} {distroversion} vps".format(
+            distro=self.os_type,
+            distroversion=self.os_version
+        ))
+        proc = subprocess.Popen(args, stdout=subprocess.PIPE,
+                                stderr=subprocess.PIPE)
+        out, err = proc.communicate()
+        return (proc.returncode, out, err)
+
+    def destroy(self):
+        """
+        Destroy (shutdown and delete) a virtual machine instance.
+        """
+        executable = self.executable
+        if not executable:
+            log.error("No downburst executable found.")
+            return False
+        shortname = decanonicalize_hostname(self.name)
+        args = [executable, '-c', self.host, 'destroy', shortname]
+        proc = subprocess.Popen(args, stdout=subprocess.PIPE,
+                                stderr=subprocess.PIPE,)
+        out, err = proc.communicate()
+        if err:
+            log.error("Error destroying {machine}: {msg}".format(
+                machine=self.name, msg=err))
+            return False
+        elif proc.returncode == 0:
+            out_str = ': %s' % out if out else ''
+            log.info("Destroyed %s%s" % (self.name, out_str))
+            return True
+        else:
+            log.error("I don't know if the destroy of {node} succeded!".format(
+                node=self.name))
+            return False
+
+    def build_config(self):
+        """
+        Assemble a configuration to pass to downburst, and write it to a file.
+        """
+        config_fd = tempfile.NamedTemporaryFile(delete=False)
+
+        os_type = self.os_type.lower()
+        mac_address = self.status['mac_address']
+
+        file_info = {
+            'disk-size': '100G',
+            'ram': '3.8G',
+            'cpus': 1,
+            'networks': [
+                {'source': 'front', 'mac': mac_address}],
+            'distro': os_type,
+            'distroversion': self.os_version,
+            'additional-disks': 3,
+            'additional-disks-size': '200G',
+            'arch': 'x86_64',
+        }
+        fqdn = self.name.split('@')[1]
+        file_out = {
+            'downburst': file_info,
+            'local-hostname': fqdn,
+        }
+        yaml.safe_dump(file_out, config_fd)
+        self.config_path = config_fd.name
+
+        user_info = {
+            'user': self.user,
+            # Remove the user's password so console logins are possible
+            'runcmd': [
+                ['passwd', '-d', self.user],
+            ]
+        }
+        # On CentOS/RHEL/Fedora, write the correct mac address
+        if os_type in ['centos', 'rhel', 'fedora']:
+            user_info['runcmd'].extend([
+                ['sed', '-ie', 's/HWADDR=".*"/HWADDR="%s"/' % mac_address,
+                 '/etc/sysconfig/network-scripts/ifcfg-eth0'],
+            ])
+        # On Ubuntu, starting with 16.04, we need to install 'python' to get
+        # python2.7, which ansible needs
+        elif os_type == 'ubuntu':
+            if 'packages' not in user_info:
+                user_info['packages'] = list()
+            user_info['packages'].extend([
+                'python',
+            ])
+        user_fd = tempfile.NamedTemporaryFile(delete=False)
+        yaml.safe_dump(user_info, user_fd)
+        self.user_path = user_fd.name
+        return True
+
+    def remove_config(self):
+        """
+        Remove the downburst configuration file created by build_config()
+        """
+        if self.config_path and os.path.exists(self.config_path):
+            os.remove(self.config_path)
+            self.config_path = None
+            return True
+        if self.user_path and os.path.exists(self.user_path):
+            os.remove(self.user_path)
+            self.user_path = None
+            return True
+        return False
+
+    def __del__(self):
+        self.remove_config()
diff --git a/teuthology/provision/test/test_downburst.py b/teuthology/provision/test/test_downburst.py
new file mode 100644 (file)
index 0000000..ecbf400
--- /dev/null
@@ -0,0 +1,98 @@
+from mock import Mock, MagicMock, patch
+
+from teuthology import provision
+
+
+class TestDownburst(object):
+    def setup(self):
+        self.ctx = Mock()
+        self.ctx.os_type = 'rhel'
+        self.ctx.os_version = '7.0'
+        self.ctx.config = dict()
+        self.name = 'vpm999'
+        self.status = dict(
+            vm_host=dict(name='host999'),
+            is_vm=True,
+        )
+
+    def test_create_if_vm_success(self):
+        name = self.name
+        ctx = self.ctx
+        status = self.status
+
+        dbrst = provision.Downburst(name, ctx.os_type, ctx.os_version, status)
+        dbrst.executable = '/fake/path'
+        dbrst.build_config = MagicMock(name='build_config')
+        dbrst._run_create = MagicMock(name='_run_create')
+        dbrst._run_create.return_value = (0, '', '')
+        remove_config = MagicMock(name='remove_config')
+        dbrst.remove_config = remove_config
+
+        result = provision.create_if_vm(ctx, name, dbrst)
+        assert result is True
+
+        dbrst._run_create.assert_called_with()
+        dbrst.build_config.assert_called_with()
+        del dbrst
+        remove_config.assert_called_with()
+
+    def test_destroy_if_vm_success(self):
+        name = self.name
+        ctx = self.ctx
+        status = self.status
+
+        dbrst = provision.Downburst(name, ctx.os_type, ctx.os_version, status)
+        dbrst.destroy = MagicMock(name='destroy')
+        dbrst.destroy.return_value = True
+
+        result = provision.destroy_if_vm(ctx, name, _downburst=dbrst)
+        assert result is True
+
+        dbrst.destroy.assert_called_with()
+
+    def test_destroy_if_vm_wrong_owner(self):
+        name = self.name
+        ctx = self.ctx
+        status = self.status
+        status['locked_by'] = 'user@a'
+
+        dbrst = provision.Downburst(name, ctx.os_type, ctx.os_version, status)
+        dbrst.destroy = MagicMock(name='destroy', side_effect=RuntimeError)
+
+        result = provision.destroy_if_vm(ctx, name, user='user@b',
+                                         _downburst=dbrst)
+        assert result is False
+
+    def test_destroy_if_vm_wrong_description(self):
+        name = self.name
+        ctx = self.ctx
+        status = self.status
+        status['description'] = 'desc_a'
+
+        dbrst = provision.Downburst(name, ctx.os_type, ctx.os_version, status)
+        dbrst.destroy = MagicMock(name='destroy')
+        dbrst.destroy = MagicMock(name='destroy', side_effect=RuntimeError)
+
+        result = provision.destroy_if_vm(ctx, name, description='desc_b',
+                                         _downburst=dbrst)
+        assert result is False
+
+    @patch('teuthology.provision.downburst.downburst_executable')
+    def test_create_fails_without_executable(self, m_exec):
+        name = self.name
+        ctx = self.ctx
+        status = self.status
+        m_exec.return_value = ''
+        dbrst = provision.Downburst(name, ctx.os_type, ctx.os_version, status)
+        result = dbrst.create()
+        assert result is False
+
+    @patch('teuthology.provision.downburst.downburst_executable')
+    def test_destroy_fails_without_executable(self, m_exec):
+        name = self.name
+        ctx = self.ctx
+        status = self.status
+        m_exec.return_value = ''
+        dbrst = provision.Downburst(name, ctx.os_type, ctx.os_version, status)
+        result = dbrst.destroy()
+        assert result is False
diff --git a/teuthology/test/test_provision.py b/teuthology/test/test_provision.py
deleted file mode 100644 (file)
index 2925976..0000000
+++ /dev/null
@@ -1,98 +0,0 @@
-from mock import Mock, MagicMock, patch
-
-from teuthology import provision
-
-
-class TestDownburst(object):
-    def setup(self):
-        self.ctx = Mock()
-        self.ctx.os_type = 'rhel'
-        self.ctx.os_version = '7.0'
-        self.ctx.config = dict()
-        self.name = 'vpm999'
-        self.status = dict(
-            vm_host=dict(name='host999'),
-            is_vm=True,
-        )
-
-    def test_create_if_vm_success(self):
-        name = self.name
-        ctx = self.ctx
-        status = self.status
-
-        dbrst = provision.Downburst(name, ctx.os_type, ctx.os_version, status)
-        dbrst.executable = '/fake/path'
-        dbrst.build_config = MagicMock(name='build_config')
-        dbrst._run_create = MagicMock(name='_run_create')
-        dbrst._run_create.return_value = (0, '', '')
-        remove_config = MagicMock(name='remove_config')
-        dbrst.remove_config = remove_config
-
-        result = provision.create_if_vm(ctx, name, dbrst)
-        assert result is True
-
-        dbrst._run_create.assert_called_with()
-        dbrst.build_config.assert_called_with()
-        del dbrst
-        remove_config.assert_called_with()
-
-    def test_destroy_if_vm_success(self):
-        name = self.name
-        ctx = self.ctx
-        status = self.status
-
-        dbrst = provision.Downburst(name, ctx.os_type, ctx.os_version, status)
-        dbrst.destroy = MagicMock(name='destroy')
-        dbrst.destroy.return_value = True
-
-        result = provision.destroy_if_vm(ctx, name, _downburst=dbrst)
-        assert result is True
-
-        dbrst.destroy.assert_called_with()
-
-    def test_destroy_if_vm_wrong_owner(self):
-        name = self.name
-        ctx = self.ctx
-        status = self.status
-        status['locked_by'] = 'user@a'
-
-        dbrst = provision.Downburst(name, ctx.os_type, ctx.os_version, status)
-        dbrst.destroy = MagicMock(name='destroy', side_effect=RuntimeError)
-
-        result = provision.destroy_if_vm(ctx, name, user='user@b',
-                                         _downburst=dbrst)
-        assert result is False
-
-    def test_destroy_if_vm_wrong_description(self):
-        name = self.name
-        ctx = self.ctx
-        status = self.status
-        status['description'] = 'desc_a'
-
-        dbrst = provision.Downburst(name, ctx.os_type, ctx.os_version, status)
-        dbrst.destroy = MagicMock(name='destroy')
-        dbrst.destroy = MagicMock(name='destroy', side_effect=RuntimeError)
-
-        result = provision.destroy_if_vm(ctx, name, description='desc_b',
-                                         _downburst=dbrst)
-        assert result is False
-
-    @patch('teuthology.provision.downburst_executable')
-    def test_create_fails_without_executable(self, m_exec):
-        name = self.name
-        ctx = self.ctx
-        status = self.status
-        m_exec.return_value = ''
-        dbrst = provision.Downburst(name, ctx.os_type, ctx.os_version, status)
-        result = dbrst.create()
-        assert result is False
-
-    @patch('teuthology.provision.downburst_executable')
-    def test_destroy_fails_without_executable(self, m_exec):
-        name = self.name
-        ctx = self.ctx
-        status = self.status
-        m_exec.return_value = ''
-        dbrst = provision.Downburst(name, ctx.os_type, ctx.os_version, status)
-        result = dbrst.destroy()
-        assert result is False
index 893f87333ffdc5de70cd52f5df72030dc8f20f4a..9e9fa021906834cccfe973723d23266bafa8e7d5 100644 (file)
@@ -1,8 +1,7 @@
-from mock import patch
+from mock import patch, Mock
 
 from .. import lock
 
-class Mock: pass
 
 class TestVpsOsVersionParamCheck(object):
 
@@ -21,7 +20,7 @@ class TestVpsOsVersionParamCheck(object):
         self.fake_ctx.os_type = 'ubuntu'
         self.fake_ctx.os_version = 'precise'
         with patch.multiple(
-            lock.provision,
+            lock.provision.downburst,
             downburst_executable=self.fake_downburst_executable,
         ):
             check_value = lock.vps_version_or_type_valid(
@@ -35,7 +34,7 @@ class TestVpsOsVersionParamCheck(object):
         self.fake_ctx.os_type = 'ubuntu'
         self.fake_ctx.os_version = '12.04'
         with patch.multiple(
-            lock.provision,
+            lock.provision.downburst,
             downburst_executable=self.fake_downburst_executable,
         ):
             check_value = lock.vps_version_or_type_valid(
@@ -48,7 +47,7 @@ class TestVpsOsVersionParamCheck(object):
         self.fake_ctx.os_type = '6.5'
         self.fake_ctx.os_version = 'rhel'
         with patch.multiple(
-            lock.provision,
+            lock.provision.downburst,
             downburst_executable=self.fake_downburst_executable,
         ):
             check_value = lock.vps_version_or_type_valid(
@@ -61,7 +60,7 @@ class TestVpsOsVersionParamCheck(object):
         self.fake_ctx.os_type = 'aardvark'
         self.fake_ctx.os_version = '6.5'
         with patch.multiple(
-            lock.provision,
+            lock.provision.downburst,
             downburst_executable=self.fake_downburst_executable,
         ):
             check_value = lock.vps_version_or_type_valid(
@@ -74,7 +73,7 @@ class TestVpsOsVersionParamCheck(object):
         self.fake_ctx.os_type = 'rhel'
         self.fake_ctx.os_version = 'vampire_bat'
         with patch.multiple(
-            lock.provision,
+            lock.provision.downburst,
             downburst_executable=self.fake_downburst_executable,
         ):
             check_value = lock.vps_version_or_type_valid(