From: ShaoHe Feng <shaohef(a)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(a)linux.vnet.ibm.com>
Signed-off-by: ShaoHe Feng <shaohef(a)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(a)linux.vnet.ibm.com>
+# ShaoHe Feng <shaohef(a)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
--
1.8.4.2