diff --git a/lib/daemon.py b/lib/daemon.py
index 21aab9227c42a49c3542421535d9ad31c3a3b719..e386d8c4eee0a3ff2cc6ef60b24b521708d4a793 100644
--- a/lib/daemon.py
+++ b/lib/daemon.py
@@ -112,10 +112,12 @@ class AsyncUDPSocket(GanetiBaseAsyncoreDispatcher):
# this method is overriding an asyncore.dispatcher method
def handle_read(self):
- payload, address = utils.IgnoreSignals(self.recvfrom,
- constants.MAX_UDP_DATA_SIZE)
- ip, port = address
- self.handle_datagram(payload, ip, port)
+ recv_result = utils.IgnoreSignals(self.recvfrom,
+ constants.MAX_UDP_DATA_SIZE)
+ if recv_result is not None:
+ payload, address = recv_result
+ ip, port = address
+ self.handle_datagram(payload, ip, port)
def handle_datagram(self, payload, ip, port):
"""Handle an already read udp datagram
diff --git a/lib/utils.py b/lib/utils.py
index 216a9eaa96c264e53b98586745105f11ec4f3387..70f5b41fa545d7457ef88adbd9a33008ba3bccae 100644
--- a/lib/utils.py
+++ b/lib/utils.py
@@ -3104,12 +3104,16 @@ def IgnoreSignals(fn, *args, **kwargs):
try:
return fn(*args, **kwargs)
except EnvironmentError, err:
- if err.errno != errno.EINTR:
+ if err.errno == errno.EINTR:
+ return None
+ else:
raise
except (select.error, socket.error), err:
# In python 2.6 and above select.error is an IOError, so it's handled
# above, in 2.5 and below it's not, and it's handled here.
- if not (err.args and err.args[0] == errno.EINTR):
+ if err.args and err.args[0] == errno.EINTR:
+ return None
+ else:
raise
diff --git a/man/gnt-instance.sgml b/man/gnt-instance.sgml
index 9e10af9917858ce736df2593605185935e556a28..9d2a9fef6331b932324d4f8b6fdb5256536b6daf 100644
--- a/man/gnt-instance.sgml
+++ b/man/gnt-instance.sgml
@@ -690,7 +690,24 @@
<simpara>This option is only effective with kvm versions >= 87
and qemu-kvm versions >= 0.11.0.
</simpara>
+ </listitem>
+ </varlistentry>
+ <varlistentry>
+ <term>use_chroot</term>
+ <listitem>
+ <simpara>Valid for the KVM hypervisor.</simpara>
+
+ <simpara>This boolean option determines wether to run the KVM
+ instance in a chroot directory.
+ </simpara>
+ <para>If it is set to <quote>true</quote>, an empty directory
+ is created before starting the instance and its path is passed via
+ the -chroot flag to kvm.
+ The directory is removed when the instance is stopped.
+ </para>
+
+ <simpara>It is set to <quote>false</quote> by default.</simpara>
</listitem>
</varlistentry>
diff --git a/test/ganeti.compat_unittest.py b/test/ganeti.compat_unittest.py
index dbe9940e71403666d44c1d7f94f216a50b01172b..54a1c626f93ca9139b9d368a1028cf4fd7426af1 100755
--- a/test/ganeti.compat_unittest.py
+++ b/test/ganeti.compat_unittest.py
@@ -61,11 +61,31 @@ class TestPartial(testutils.GanetiTestCase):
class TestTryToRoman(testutils.GanetiTestCase):
"""test the compat.TryToRoman function"""
+ def setUp(self):
+ testutils.GanetiTestCase.setUp(self)
+ # Save the compat.roman module so we can alter it with a fake...
+ self.compat_roman_module = compat.roman
+
+ def tearDown(self):
+ # ...and restore it at the end of the test
+ compat.roman = self.compat_roman_module
+ testutils.GanetiTestCase.tearDown(self)
+
def testAFewIntegers(self):
+ # This test only works is the roman module is installed
+ if compat.roman is not None:
+ self.assertEquals(compat.TryToRoman(0), 0)
+ self.assertEquals(compat.TryToRoman(1), "I")
+ self.assertEquals(compat.TryToRoman(4), "IV")
+ self.assertEquals(compat.TryToRoman(5), "V")
+
+ def testWithNoRoman(self):
+ # compat.roman is saved/restored in setUp/tearDown
+ compat.roman = None
self.assertEquals(compat.TryToRoman(0), 0)
- self.assertEquals(compat.TryToRoman(1), "I")
- self.assertEquals(compat.TryToRoman(4), "IV")
- self.assertEquals(compat.TryToRoman(5), "V")
+ self.assertEquals(compat.TryToRoman(1), 1)
+ self.assertEquals(compat.TryToRoman(4), 4)
+ self.assertEquals(compat.TryToRoman(5), 5)
def testStrings(self):
self.assertEquals(compat.TryToRoman("astring"), "astring")
diff --git a/test/ganeti.daemon_unittest.py b/test/ganeti.daemon_unittest.py
index 143be9d4c23fd7661ae28a907b335d4a8bb2f447..977a073f9d36188f902de153e2862e06aceb4847 100755
--- a/test/ganeti.daemon_unittest.py
+++ b/test/ganeti.daemon_unittest.py
@@ -29,6 +29,7 @@ import time
from ganeti import daemon
from ganeti import errors
+from ganeti import utils
import testutils
@@ -172,10 +173,14 @@ class TestAsyncUDPSocket(testutils.GanetiTestCase):
self.client = _MyAsyncUDPSocket()
self.server.bind(("127.0.0.1", 0))
self.port = self.server.getsockname()[1]
+ # Save utils.IgnoreSignals so we can do evil things to it...
+ self.saved_utils_ignoresignals = utils.IgnoreSignals
def tearDown(self):
self.server.close()
self.client.close()
+ # ...and restore it as well
+ utils.IgnoreSignals = self.saved_utils_ignoresignals
testutils.GanetiTestCase.tearDown(self)
def testNoDoubleBind(self):
@@ -229,6 +234,17 @@ class TestAsyncUDPSocket(testutils.GanetiTestCase):
["p1", "p2", "error", "p3", "error", "terminate"])
self.assertEquals(self.server.error_count, 2)
+ def testSignaledWhileReceiving(self):
+ utils.IgnoreSignals = lambda fn, *args, **kwargs: None
+ self.client.enqueue_send("127.0.0.1", self.port, "p1")
+ self.client.enqueue_send("127.0.0.1", self.port, "p2")
+ self.server.handle_read()
+ self.assertEquals(self.server.received, [])
+ self.client.enqueue_send("127.0.0.1", self.port, "terminate")
+ utils.IgnoreSignals = self.saved_utils_ignoresignals
+ self.mainloop.Run()
+ self.assertEquals(self.server.received, ["p1", "p2", "terminate"])
+
if __name__ == "__main__":
testutils.GanetiTestProgram()