Commit 66d1f035 authored by René Nussbaumer's avatar René Nussbaumer

Introducing gnt-cluster epo

This is a convenience command to do an automated EPO in the possible limits of
Ganeti.
Signed-off-by: default avatarRené Nussbaumer <rn@google.com>
Reviewed-by: default avatarMichael Hanselmann <hansmi@google.com>
parent 41543d8b
......@@ -471,6 +471,7 @@ python_tests = \
test/ganeti.backend_unittest.py \
test/ganeti.bdev_unittest.py \
test/ganeti.cli_unittest.py \
test/ganeti.client.gnt_cluster_unittest.py \
test/ganeti.client.gnt_instance_unittest.py \
test/ganeti.daemon_unittest.py \
test/ganeti.cmdlib_unittest.py \
......
This diff is collapsed.
......@@ -93,6 +93,23 @@ Remove all configuration files related to the cluster, so that a
Since this is a dangerous command, you are required to pass the
argument *--yes-do-it.*
EPO
~~~
**epo** [--on] [--groups|--all] *arguments*
Performs an emergency power-off on nodes given as arguments. If ``--groups``
is given, arguments are node groups. If ``--all`` is provided, the whole
cluster will be shut down.
The ``--on`` flag recovers the cluster after an emergency power-off
Please note that the master node will not be turned down or up automatically.
It will just be left in a state, where you can manully perform the shutdown of
that one node. If the master is in the list of affected nodes and this is not a
complete cluster emergency power-off (e.g. using ``--all``), you're required to
do a master failover to another node not affected.
GETMASTER
~~~~~~~~~
......
......@@ -411,6 +411,7 @@ def RunQa():
instance = RunTest(qa_instance.TestInstanceAddWithPlainDisk, pnode)
RunCommonInstanceTests(instance)
RunGroupListTests()
RunTest(qa_cluster.TestClusterEpo)
RunExportImportTests(instance, pnode, None)
RunDaemonTests(instance, pnode)
RunTest(qa_instance.TestInstanceRemove, instance)
......
......@@ -27,13 +27,14 @@ import tempfile
import os.path
from ganeti import constants
from ganeti import compat
from ganeti import utils
import qa_config
import qa_utils
import qa_error
from qa_utils import AssertEqual, AssertCommand
from qa_utils import AssertEqual, AssertCommand, GetCommandOutput
def _RemoveFileFromAllNodes(filename):
......@@ -150,6 +151,44 @@ def TestClusterOob():
"oob_program="])
def TestClusterEpo():
"""gnt-cluster epo"""
master = qa_config.GetMasterNode()
# Assert that OOB is unavailable for all nodes
result_output = GetCommandOutput(master["primary"],
"gnt-node list --verbose --no-header -o"
" powered")
AssertEqual(compat.all(powered == "(unavail)"
for powered in result_output.splitlines()), True)
# Conflicting
AssertCommand(["gnt-cluster", "epo", "--groups", "--all"], fail=True)
# --all doesn't expect arguments
AssertCommand(["gnt-cluster", "epo", "--all", "some_arg"], fail=True)
# Unless --all is given master is not allowed to be in the list
AssertCommand(["gnt-cluster", "epo", "-f", master["primary"]], fail=True)
# This shouldn't fail
AssertCommand(["gnt-cluster", "epo", "-f", "--all"])
# All instances should have been stopped now
result_output = GetCommandOutput(master["primary"],
"gnt-instance list --no-header -o status")
AssertEqual(compat.all(status == "ADMIN_down"
for status in result_output.splitlines()), True)
# Now start everything again
AssertCommand(["gnt-cluster", "epo", "--on", "-f", "--all"])
# All instances should have been started now
result_output = GetCommandOutput(master["primary"],
"gnt-instance list --no-header -o status")
AssertEqual(compat.all(status == "running"
for status in result_output.splitlines()), True)
def TestClusterVerify():
"""gnt-cluster verify"""
AssertCommand(["gnt-cluster", "verify"])
......
#!/usr/bin/python
#
# Copyright (C) 2011 Google Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program 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
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.
"""Script for testing ganeti.client.gnt_cluster"""
import unittest
from ganeti.client import gnt_cluster
from ganeti import utils
from ganeti import compat
import testutils
class TestEpo(unittest.TestCase):
def setUp(self):
self.nodes2ip = dict(("node%s" % i, "192.0.2.%s" % i) for i in range(1, 10))
self.nodes = set(self.nodes2ip.keys())
self.ips2node = dict((v, k) for (k, v) in self.nodes2ip.items())
def _FakeAction(*args):
return True
def _FakePing(ip, port, live_port_needed=False):
self.assert_(live_port_needed)
self.assertEqual(port, 0)
return True
def _FakeSleep(secs):
self.assert_(secs >= 0 and secs <= 5)
return
def testPingFnRemoveHostsUp(self):
seen = set()
def _FakeSeenPing(ip, *args, **kwargs):
node = self.ips2node[ip]
self.assertFalse(node in seen)
seen.add(node)
return True
helper = gnt_cluster._RunWhenNodesReachableHelper(self.nodes,
self._FakeAction,
self.nodes2ip, port=0,
_ping_fn=_FakeSeenPing,
_sleep_fn=self._FakeSleep)
nodes_len = len(self.nodes)
for (num, _) in enumerate(self.nodes):
helper.Wait(5)
if num < nodes_len - 1:
self.assertRaises(utils.RetryAgain, helper)
else:
helper()
self.assertEqual(seen, self.nodes)
self.assertFalse(helper.down)
self.assertEqual(helper.up, self.nodes)
def testActionReturnFalseSetsHelperFalse(self):
called = False
def _FalseAction(*args):
return called
helper = gnt_cluster._RunWhenNodesReachableHelper(self.nodes, _FalseAction,
self.nodes2ip, port=0,
_ping_fn=self._FakePing,
_sleep_fn=self._FakeSleep)
for _ in self.nodes:
try:
helper()
except utils.RetryAgain:
called = True
self.assertFalse(helper.success)
def testMaybeInstanceStartup(self):
instances_arg = []
def _FakeInstanceStart(opts, instances, start):
instances_arg.append(set(instances))
return None
inst_map = {
"inst1": set(["node1", "node2"]),
"inst2": set(["node1", "node3"]),
"inst3": set(["node2", "node1"]),
"inst4": set(["node2", "node1", "node3"]),
"inst5": set(["node4"]),
}
fn = _FakeInstanceStart
self.assert_(gnt_cluster._MaybeInstanceStartup(None, inst_map, set(),
_instance_start_fn=fn))
self.assertFalse(instances_arg)
result = gnt_cluster._MaybeInstanceStartup(None, inst_map, set(["node1"]),
_instance_start_fn=fn)
self.assert_(result)
self.assertFalse(instances_arg)
result = gnt_cluster._MaybeInstanceStartup(None, inst_map,
set(["node1", "node3"]),
_instance_start_fn=fn)
self.assert_(result is None)
self.assertEqual(instances_arg.pop(0), set(["inst2"]))
self.assertFalse("inst2" in inst_map)
result = gnt_cluster._MaybeInstanceStartup(None, inst_map,
set(["node1", "node3"]),
_instance_start_fn=fn)
self.assert_(result)
self.assertFalse(instances_arg)
result = gnt_cluster._MaybeInstanceStartup(None, inst_map,
set(["node1", "node3", "node2"]),
_instance_start_fn=fn)
self.assertEqual(instances_arg.pop(0), set(["inst1", "inst3", "inst4"]))
self.assert_(result is None)
result = gnt_cluster._MaybeInstanceStartup(None, inst_map,
set(["node1", "node3", "node2",
"node4"]),
_instance_start_fn=fn)
self.assert_(result is None)
self.assertEqual(instances_arg.pop(0), set(["inst5"]))
self.assertFalse(inst_map)
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