Commit c8e69c74 authored by Klaus Aehlig's avatar Klaus Aehlig

Merge branch 'stable-2.12' into stable-2.13

* stable-2.12
  Tell git to ignore tools/ssl-update
  Use 'exclude_daemons' option for master only
  Disable superfluous restarting of daemons
  Add tests exercising the "crashed" state handling
  Add proper handling of the "crashed" Xen state

* stable-2.11
  Fix capitalization of TestCase
  Trigger renew-crypto on downgrade to 2.11

Conflicts:
	.gitignore: use all additions
Signed-off-by: default avatarKlaus Aehlig <aehlig@google.com>
Reviewed-by: default avatarPetr Pudlak <pudlak@google.com>
parents a665b34a e36f73ab
......@@ -135,6 +135,7 @@
/tools/prepare-node-join
/tools/shebang/
/tools/ssh-update
/tools/ssl-update
# scripts
/scripts/gnt-backup
......
......@@ -1780,6 +1780,7 @@ TEST_FILES = \
test/data/vgreduce-removemissing-2.02.66-ok.txt \
test/data/vgs-missing-pvs-2.02.02.txt \
test/data/vgs-missing-pvs-2.02.66.txt \
test/data/xen-xl-list-4.4-crashed-instances.txt \
test/data/xen-xm-info-4.0.1.txt \
test/data/xen-xm-list-4.0.1-dom0-only.txt \
test/data/xen-xm-list-4.0.1-four-instances.txt \
......
......@@ -1465,9 +1465,8 @@ class _RunWhileDaemonsStoppedHelper(object):
@type ssh_ports: list
@param ssh_ports: List of SSH ports of online nodes
@type exclude_daemons: list of string
@param exclude_daemons: list of daemons to shutdown
@param exclude_daemons: list of daemons that will be restarted after
all others are shutdown
@param exclude_daemons: list of daemons that will be restarted on master
after all others are shutdown
@type debug: boolean
@param debug: show debug output
@type verbose: boolesn
......@@ -1541,12 +1540,11 @@ class _RunWhileDaemonsStoppedHelper(object):
self.feedback_fn("Stopping daemons on %s" % node_name)
self._RunCmd(node_name, [pathutils.DAEMON_UTIL, "stop-all"])
# Starting any daemons listed as exception
for daemon in self.exclude_daemons:
if (daemon in constants.DAEMONS_MASTER and
node_name != self.master_node):
continue
self.feedback_fn("Starting daemon '%s' on %s" % (daemon, node_name))
self._RunCmd(node_name, [pathutils.DAEMON_UTIL, "start", daemon])
if node_name == self.master_node:
for daemon in self.exclude_daemons:
self.feedback_fn("Starting daemon '%s' on %s" % (daemon,
node_name))
self._RunCmd(node_name, [pathutils.DAEMON_UTIL, "start", daemon])
# All daemons are shut down now
try:
......@@ -1562,12 +1560,12 @@ class _RunWhileDaemonsStoppedHelper(object):
# Stopping any daemons listed as exception.
# This might look unnecessary, but it makes sure that daemon-util
# starts all daemons in the right order.
for daemon in self.exclude_daemons:
if (daemon in constants.DAEMONS_MASTER and
node_name != self.master_node):
continue
self.feedback_fn("Stopping daemon '%s' on %s" % (daemon, node_name))
self._RunCmd(node_name, [pathutils.DAEMON_UTIL, "stop", daemon])
if node_name == self.master_node:
self.exclude_daemons.reverse()
for daemon in self.exclude_daemons:
self.feedback_fn("Stopping daemon '%s' on %s" % (daemon,
node_name))
self._RunCmd(node_name, [pathutils.DAEMON_UTIL, "stop", daemon])
self.feedback_fn("Starting daemons on %s" % node_name)
self._RunCmd(node_name, [pathutils.DAEMON_UTIL, "start-all"])
......@@ -1582,8 +1580,10 @@ def RunWhileDaemonsStopped(feedback_fn, exclude_daemons, fn, *args, **kwargs):
@type feedback_fn: callable
@param feedback_fn: Feedback function
@type exclude_daemons: list of string
@param exclude_daemons: list of daemons that are NOT stopped. If None,
all daemons will be stopped.
@param exclude_daemons: list of daemons that stopped, but immediately
restarted on the master to be available when calling
'fn'. If None, all daemons will be stopped and none
will be started before calling 'fn'.
@type fn: callable
@param fn: Function to be called when daemons are stopped
......
......@@ -1161,8 +1161,7 @@ def _RenewCrypto(new_cluster_cert, new_rapi_cert, # pylint: disable=R0911
_RenewClientCerts(ctx)
if new_cluster_cert or new_rapi_cert or new_spice_cert \
or new_confd_hmac_key or new_cds:
if new_rapi_cert or new_spice_cert or new_confd_hmac_key or new_cds:
RunWhileClusterStopped(ToStdout, _RenewCryptoInner)
# If only node certficates are recreated, call _RenewClientCerts only.
......
......@@ -116,6 +116,15 @@ def _RunInstanceList(fn, instance_list_errors):
return result.stdout.splitlines()
class _InstanceCrashed(errors.GenericError):
"""Instance has reached a violent ending.
This is raised within the Xen hypervisor only, and should not be seen or used
outside.
"""
def _ParseInstanceList(lines, include_node):
"""Parses the output of listing instances by xen.
......@@ -147,6 +156,10 @@ def _ParseInstanceList(lines, include_node):
except (TypeError, ValueError), err:
raise errors.HypervisorError("Can't parse instance list,"
" line: %s, error: %s" % (line, err))
except _InstanceCrashed:
# The crashed instance can be interpreted as being down, so we omit it
# from the instance list.
continue
# skip the Domain-0 (optional)
if include_node or data[0] != _DOM0_NAME:
......@@ -241,6 +254,18 @@ def _IgnorePaused(instance_info):
return instance_info.replace('p', '-')
def _IsCrashed(instance_info):
"""Returns whether an instance is in the crashed Xen state.
When a horrible misconfiguration happens to a Xen domain, it can crash,
meaning that it encounters a violent ending. While this state usually flashes
only temporarily before the domain is restarted, being able to check for it
allows Ganeti not to act confused and do something about it.
"""
return instance_info.count('c') > 0
def _XenToHypervisorInstanceState(instance_info):
"""Maps Xen states to hypervisor states.
......@@ -251,6 +276,9 @@ def _XenToHypervisorInstanceState(instance_info):
"""
instance_info = _IgnorePaused(instance_info)
if _IsCrashed(instance_info):
raise _InstanceCrashed("Instance detected as crashed, should be omitted")
if _IsInstanceRunning(instance_info):
return hv_base.HvInstanceState.RUNNING
elif _IsInstanceShutdown(instance_info):
......
......@@ -181,3 +181,29 @@ def IsBefore(version, major, minor, revision):
return True
return version < (major, minor, revision)
def IsEqual(version, major, minor, revision):
"""Decide if a given version matches the given version.
If the revision is set to None, only major and minor are compared.
@param version: (major, minor, revision) or None, with None being
before all versions
@type version: (int, int, int) or None
@param major: major version
@type major: int
@param minor: minor version
@type minor: int
@param revision: revision
@type revision: int
"""
if version is None:
return False
if revision is None:
current_major, current_minor, _ = version
return (current_major, current_minor) == (major, minor)
return version == (major, minor, revision)
Name ID Mem VCPUs State Time(s)
Domain-0 0 1023 1 r----- 154706.1
server01.example.com 1 1024 1 -b---- 167643.2
(null) 28441 789 1 --psc- 1.2
alsodying.example.com 28448 1024 1 --psc- 1.4
......@@ -198,6 +198,19 @@ class TestInstanceStateParsing(unittest.TestCase):
self.assertEqual(hv_xen._XenToHypervisorInstanceState(state),
hv_base.HvInstanceState.SHUTDOWN)
def testCrashingStates(self):
states = [
"--psc-",
"---sc-",
"---scd",
"--p-c-",
"----c-",
"----cd",
]
for state in states:
self.assertRaises(hv_xen._InstanceCrashed,
hv_xen._XenToHypervisorInstanceState, state)
class TestGetInstanceList(testutils.GanetiTestCase):
def _Fail(self):
......@@ -241,6 +254,23 @@ class TestGetInstanceList(testutils.GanetiTestCase):
self.assertEqual(fn.Count(), 1)
def testOmitCrashed(self):
data = testutils.ReadTestData("xen-xl-list-4.4-crashed-instances.txt")
fn = testutils.CallCounter(compat.partial(self._Success, data))
result = hv_xen._GetAllInstanceList(fn, True, delays=(0.02, 1.0, 0.03),
timeout=0.1)
self.assertEqual(len(result), 2)
self.assertEqual(map(compat.fst, result), [
"Domain-0",
"server01.example.com",
])
self.assertEqual(fn.Count(), 1)
class TestParseNodeInfo(testutils.GanetiTestCase):
def testEmpty(self):
......
......@@ -91,6 +91,16 @@ class IsBeforeTest(unittest.TestCase):
self.assertTrue(version.IsBefore((2, 10, 1), 2, 11, 0))
self.assertFalse(version.IsBefore((2, 11, 0), 2, 10, 3))
class IsEqualTest(unittest.TestCase):
def testIsEqual(self):
self.assertTrue(version.IsEqual((2, 10, 0), 2, 10, 0))
self.assertFalse(version.IsEqual((2, 10, 0), 2, 10, 2))
self.assertFalse(version.IsEqual((2, 10, 0), 2, 12, 0))
self.assertFalse(version.IsEqual((2, 10, 0), 3, 10, 0))
self.assertTrue(version.IsEqual((2, 10, 0), 2, 10, None))
self.assertTrue(version.IsEqual((2, 10, 5), 2, 10, None))
self.assertFalse(version.IsEqual((2, 11, 5), 2, 10, None))
self.assertFalse(version.IsEqual((3, 10, 5), 2, 10, None))
if __name__ == "__main__":
testutils.GanetiTestProgram()
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment