[Kimchi-devel] [PATCH 1/2] move RollbackContext from tests/utils to src/kimchi/rollbackcontext

Aline Manera alinefm at linux.vnet.ibm.com
Tue Dec 31 10:24:45 UTC 2013


Needs rebase

On 12/27/2013 08:40 AM, shaohef at linux.vnet.ibm.com wrote:
> From: ShaoHe Feng <shaohef at linux.vnet.ibm.com>
>
> Then kimchi source code can make use of it
> Also add src/kimchi/rollbackcontext.py in dist list
>
> Signed-off-by: Zhou Zheng Sheng <zhshzhou at linux.vnet.ibm.com>
> Signed-off-by: ShaoHe Feng <shaohef at linux.vnet.ibm.com>
> ---
>   src/kimchi/Makefile.am        | 59 +++++++++++++++++------------------
>   src/kimchi/rollbackcontext.py | 71 +++++++++++++++++++++++++++++++++++++++++++
>   tests/test_model.py           | 25 +++++++--------
>   tests/test_rest.py            |  3 +-
>   tests/utils.py                | 45 ---------------------------
>   5 files changed, 116 insertions(+), 87 deletions(-)
>   create mode 100644 src/kimchi/rollbackcontext.py
>
> diff --git a/src/kimchi/Makefile.am b/src/kimchi/Makefile.am
> index 47a3bd2..2f05ab7 100644
> --- a/src/kimchi/Makefile.am
> +++ b/src/kimchi/Makefile.am
> @@ -21,35 +21,36 @@
>   # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
>
>   kimchi_PYTHON = \
> -	asynctask.py      \
> -	auth.py           \
> -	controller.py     \
> -	disks.py          \
> -	distroloader.py   \
> -	exception.py      \
> -	__init__.py       \
> -	isoinfo.py        \
> -	netinfo.py        \
> -	network.py        \
> -	networkxml.py     \
> -	mockmodel.py      \
> -	model.py          \
> -	objectstore.py    \
> -	osinfo.py         \
> -	root.py           \
> -	scan.py           \
> -	screenshot.py     \
> -	server.py         \
> -	sslcert.py        \
> -	template.py       \
> -	vmtemplate.py     \
> -	vnc.py            \
> -	websocket.py      \
> -	websockify.py     \
> -	xmlutils.py       \
> -	utils.py          \
> -	cachebust.py      \
> -	featuretests.py
> +	__init__.py        \
> +	asynctask.py       \
> +	auth.py            \
> +	cachebust.py       \
> +	controller.py      \
> +	disks.py           \
> +	distroloader.py    \
> +	exception.py       \
> +	featuretests.py    \
> +	isoinfo.py         \
> +	mockmodel.py       \
> +	model.py           \
> +	netinfo.py         \
> +	network.py         \
> +	networkxml.py      \
> +	objectstore.py     \
> +	osinfo.py          \
> +	rollbackcontext.py \
> +	root.py            \
> +	scan.py            \
> +	screenshot.py      \
> +	server.py          \
> +	sslcert.py         \
> +	template.py        \
> +	utils.py           \
> +	vmtemplate.py      \
> +	vnc.py             \
> +	websocket.py       \
> +	websockify.py      \
> +	xmlutils.py
>
>   nodist_kimchi_PYTHON = config.py
>
> diff --git a/src/kimchi/rollbackcontext.py b/src/kimchi/rollbackcontext.py
> new file mode 100644
> index 0000000..2afd114
> --- /dev/null
> +++ b/src/kimchi/rollbackcontext.py
> @@ -0,0 +1,71 @@
> +#
> +# Project Kimchi
> +#
> +# Copyright IBM, Corp. 2013
> +#
> +# Authors:
> +#  Adam Litke <agl at linux.vnet.ibm.com>
> +#  ShaoHe Feng <shaohef at linux.vnet.ibm.com>
> +#
> +# This library is free software; you can redistribute it and/or
> +# modify it under the terms of the GNU Lesser General Public
> +# License as published by the Free Software Foundation; either
> +# version 2.1 of the License, or (at your option) any later version.
> +#
> +# This library is distributed in the hope that it will be useful,
> +# but WITHOUT ANY WARRANTY; without even the implied warranty of
> +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> +# Lesser General Public License for more details.
> +#
> +# You should have received a copy of the GNU Lesser General Public
> +# License along with this library; if not, write to the Free Software
> +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301 USA
> +#
> +
> +import sys
> +
> +
> +class RollbackContext(object):
> +    '''
> +    A context manager for recording and playing rollback.
> +    The first exception will be remembered and re-raised after rollback
> +
> +    Sample usage:
> +    with RollbackContext() as rollback:
> +        step1()
> +        rollback.prependDefer(lambda: undo step1)
> +        def undoStep2(arg): pass
> +        step2()
> +        rollback.prependDefer(undoStep2, arg)
> +    '''
> +    def __init__(self, *args):
> +        self._finally = []
> +
> +    def __enter__(self):
> +        return self
> +
> +    def __exit__(self, exc_type, exc_value, traceback):
> +        firstException = exc_value
> +
> +        for undo, args, kwargs in self._finally:
> +            try:
> +                undo(*args, **kwargs)
> +            except Exception as e:
> +                # keep the earliest exception info
> +                if not firstException:
> +                    firstException = e
> +                    # keep the original traceback info
> +                    traceback = sys.exc_info()[2]
> +
> +        # re-raise the earliest exception
> +        if firstException is not None:
> +            if type(firstException) is str:
> +                sys.stderr.write(firstException)
> +            else:
> +                raise firstException, None, traceback
> +
> +    def defer(self, func, *args, **kwargs):
> +        self._finally.append((func, args, kwargs))
> +
> +    def prependDefer(self, func, *args, **kwargs):
> +        self._finally.insert(0, (func, args, kwargs))
> diff --git a/tests/test_model.py b/tests/test_model.py
> index e19364f..7ecd8c6 100644
> --- a/tests/test_model.py
> +++ b/tests/test_model.py
> @@ -38,6 +38,7 @@ import utils
>   from kimchi import netinfo
>   from kimchi.exception import InvalidOperation, InvalidParameter
>   from kimchi.exception import NotFoundError, OperationFailed
> +from kimchi.rollbackcontext import RollbackContext
>
>
>   class ModelTests(unittest.TestCase):
> @@ -72,7 +73,7 @@ class ModelTests(unittest.TestCase):
>       def test_vm_lifecycle(self):
>           inst = kimchi.model.Model(objstore_loc=self.tmp_store)
>
> -        with utils.RollbackContext() as rollback:
> +        with RollbackContext() as rollback:
>               params = {'name': 'test', 'disks': []}
>               inst.templates_create(params)
>               rollback.prependDefer(inst.template_delete, 'test')
> @@ -97,7 +98,7 @@ class ModelTests(unittest.TestCase):
>       def test_vm_storage_provisioning(self):
>           inst = kimchi.model.Model(objstore_loc=self.tmp_store)
>
> -        with utils.RollbackContext() as rollback:
> +        with RollbackContext() as rollback:
>               params = {'name': 'test', 'disks': [{'size': 1}]}
>               inst.templates_create(params)
>               rollback.prependDefer(inst.template_delete, 'test')
> @@ -115,7 +116,7 @@ class ModelTests(unittest.TestCase):
>       def test_storagepool(self):
>           inst = kimchi.model.Model('qemu:///system', self.tmp_store)
>
> -        with utils.RollbackContext() as rollback:
> +        with RollbackContext() as rollback:
>               path = '/tmp/kimchi-images'
>               name = 'test-pool'
>               if not os.path.exists(path):
> @@ -168,7 +169,7 @@ class ModelTests(unittest.TestCase):
>       def test_storagevolume(self):
>           inst = kimchi.model.Model('qemu:///system', self.tmp_store)
>
> -        with utils.RollbackContext() as rollback:
> +        with RollbackContext() as rollback:
>               path = '/tmp/kimchi-images'
>               pool = 'test-pool'
>               vol = 'test-volume.img'
> @@ -223,7 +224,7 @@ class ModelTests(unittest.TestCase):
>       def test_template_storage_customise(self):
>           inst = kimchi.model.Model(objstore_loc=self.tmp_store)
>
> -        with utils.RollbackContext() as rollback:
> +        with RollbackContext() as rollback:
>               path = '/tmp/kimchi-images'
>               pool = 'test-pool'
>               if not os.path.exists(path):
> @@ -295,7 +296,7 @@ class ModelTests(unittest.TestCase):
>           orig_params = {'name': 'test', 'memory': '1024', 'cpus': '1'}
>           inst.templates_create(orig_params)
>
> -        with utils.RollbackContext() as rollback:
> +        with RollbackContext() as rollback:
>               params_1 = {'name': 'kimchi-vm1', 'template': '/templates/test'}
>               params_2 = {'name': 'kimchi-vm2', 'template': '/templates/test'}
>               inst.vms_create(params_1)
> @@ -326,7 +327,7 @@ class ModelTests(unittest.TestCase):
>       def test_network(self):
>           inst = kimchi.model.Model('qemu:///system', self.tmp_store)
>
> -        with utils.RollbackContext() as rollback:
> +        with RollbackContext() as rollback:
>               name = 'test-network'
>
>               networks = inst.networks_get_list()
> @@ -480,7 +481,7 @@ class ModelTests(unittest.TestCase):
>       def test_delete_running_vm(self):
>           inst = kimchi.model.Model(objstore_loc=self.tmp_store)
>
> -        with utils.RollbackContext() as rollback:
> +        with RollbackContext() as rollback:
>               params = {'name': u'test', 'disks': []}
>               inst.templates_create(params)
>               rollback.prependDefer(inst.template_delete, 'test')
> @@ -501,7 +502,7 @@ class ModelTests(unittest.TestCase):
>       def test_vm_list_sorted(self):
>           inst = kimchi.model.Model(objstore_loc=self.tmp_store)
>
> -        with utils.RollbackContext() as rollback:
> +        with RollbackContext() as rollback:
>               params = {'name': 'test', 'disks': []}
>               inst.templates_create(params)
>               rollback.prependDefer(inst.template_delete, 'test')
> @@ -517,7 +518,7 @@ class ModelTests(unittest.TestCase):
>       def test_use_test_host(self):
>           inst = kimchi.model.Model('test:///default', objstore_loc=self.tmp_store)
>
> -        with utils.RollbackContext() as rollback:
> +        with RollbackContext() as rollback:
>               params = {'name': 'test', 'disks': [],
>                          'storagepool': '/storagepools/default-pool',
>                          'domain': 'test',
> @@ -546,7 +547,7 @@ class ModelTests(unittest.TestCase):
>               inst.debugreport_delete(namePrefix + '*')
>           except NotFoundError:
>               pass
> -        with utils.RollbackContext() as rollback:
> +        with RollbackContext() as rollback:
>               report_list = inst.debugreports_get_list()
>               self.assertFalse(reportName in report_list)
>               try:
> @@ -617,7 +618,7 @@ class ModelTests(unittest.TestCase):
>       @unittest.skipUnless(utils.running_as_root(), 'Must be run as root')
>       def test_deep_scan(self):
>           inst = kimchi.model.Model('qemu:///system', objstore_loc=self.tmp_store)
> -        with utils.RollbackContext() as rollback:
> +        with RollbackContext() as rollback:
>               path = '/tmp/kimchi-images/tmpdir'
>               if not os.path.exists(path):
>                   os.makedirs(path)
> diff --git a/tests/test_rest.py b/tests/test_rest.py
> index 73946c0..1b7312f 100644
> --- a/tests/test_rest.py
> +++ b/tests/test_rest.py
> @@ -33,8 +33,9 @@ from functools import partial
>   import kimchi.mockmodel
>   import kimchi.server
>   from kimchi.asynctask import AsyncTask
> +from kimchi.rollbackcontext import RollbackContext
>   from utils import fake_user, get_free_port, https_request, patch_auth, request
> -from utils import RollbackContext, run_server
> +from utils import run_server
>
>
>   test_server = None
> diff --git a/tests/utils.py b/tests/utils.py
> index a7596e8..960e0be 100644
> --- a/tests/utils.py
> +++ b/tests/utils.py
> @@ -140,51 +140,6 @@ def https_request(host, port, path, data=None, method='GET', headers=None):
>       return _request(conn, path, data, method, headers)
>
>
> -class RollbackContext(object):
> -    '''
> -    A context manager for recording and playing rollback.
> -    The first exception will be remembered and re-raised after rollback
> -
> -    Sample usage:
> -    with RollbackContext() as rollback:
> -        step1()
> -        rollback.prependDefer(lambda: undo step1)
> -        def undoStep2(arg): pass
> -        step2()
> -        rollback.prependDefer(undoStep2, arg)
> -    '''
> -    def __init__(self, *args):
> -        self._finally = []
> -
> -    def __enter__(self):
> -        return self
> -
> -    def __exit__(self, exc_type, exc_value, traceback):
> -        firstException = exc_value
> -
> -        for undo, args, kwargs in self._finally:
> -            try:
> -                undo(*args, **kwargs)
> -            except Exception as e:
> -                # keep the earliest exception info
> -                if not firstException:
> -                    firstException = e
> -                    # keep the original traceback info
> -                    traceback = sys.exc_info()[2]
> -
> -        # re-raise the earliest exception
> -        if firstException is not None:
> -            if type(firstException) is str:
> -                sys.stderr.write(firstException)
> -            else:
> -                raise firstException, None, traceback
> -
> -    def defer(self, func, *args, **kwargs):
> -        self._finally.append((func, args, kwargs))
> -
> -    def prependDefer(self, func, *args, **kwargs):
> -        self._finally.insert(0, (func, args, kwargs))
> -
>   def patch_auth():
>       """
>       Override the authenticate function with a simple test against an




More information about the Kimchi-devel mailing list