Am 22. Februar 2011 13:55 schrieb René Nussbaumer <[email protected]>:
> --- a/lib/client/gnt_cluster.py
> +++ b/lib/client/gnt_cluster.py
> +def _InstanceStart(opts, inst_list, start):
> […]
> +  results = jex.GetResults()
> +  bad_cnt = len([False for (success, _) in results if not success])

No need for a list, just use a generator.

> +class _RunWhenNodesReachableHelper:
> +  """Helper class to make shared internal state sharing easier.
> +
> +  @ivar success: Indicates if all action_cb calls were successful
> +
> +  """
> +  def __init__(self, node_list, action_cb, node2ip, port=None,
> +               _ping_fn=netutils.TcpPing, _sleep_fn=time.sleep):
> +    self.down = set(node_list)
> +    self.up = set()
> +    self.node2ip = node2ip
> +    self.success = True
> +    self.action_cb = action_cb
> +    self._ping_fn = _ping_fn
> +    self._sleep_fn = _sleep_fn
> +
> +    if port is None:
> +      self.port = netutils.GetDaemonPort(constants.NODED)
> +    else:
> +      self.port = port

Please make the port a mandatory argument and call GetDaemonPort in
the caller. Doing so reduces dependencies of the helper class.

> +  def Wait(self, secs):
> +    """Checks if a host is up or waits reamingin seconds.

s/reamingin/remaining

> --- a/man/gnt-cluster.rst
> +++ b/man/gnt-cluster.rst
> +EPO
> +~~~
> +
> +**epo** [--on] [--groups|--all] *arguments*
> +
> +Performans an emergency power-off on nodes given as arguments. If 
> ``--groups``

s/Performans/Performs/

> +is given, arguments are node groups. If ``--all`` is provided, the whole
> +cluster will be shut down.

> --- a/qa/qa_cluster.py
> +++ b/qa/qa_cluster.py
> @@ -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,37 @@ def TestClusterOob():
>                  "oob_program="])
>
>
> +def TestClusterEpo():
> +  """gnt-cluster epo"""
> +  master = qa_config.GetMasterNode()
> +
> +  # 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"])

Won't this shut down all machines?

> +  # All instances should have been stopped now
> +  result_output = GetCommandOutput(master["primary"],
> +                                   "gnt-instance list --no-header -o status")
> +  AssertEqual(compat.any(status == "running"
> +                         for status in result_output.splitlines()), False)

You should rather check that all are stopped. They could be, after
all, in some error status.

> +  # Now start everything again
> +  AssertCommand(["gnt-cluster", "epo", "--on", "-f", "--all"])

Don't restart the QA machines. Can't you use a fake OOB script?

> --- /dev/null
> +++ b/test/ganeti.client.gnt_cluster_unittest.py
> +from ganeti.client import gnt_cluster
> +from ganeti.utils import retry

Just import “utils” and use the functions directly, e.g. “utils.Retry”.

> +class TestEpo(unittest.TestCase):
> +  def setUp(self):
> +    self.nodes = set()
> +    self.nodes2ip = {}
> +    self.ips2node = {}
> +    for ip in range(1, 10):
> +      name = "node%d" % ip
> +      self.nodes.add(name)
> +      self.nodes2ip[name] = ip
> +      self.ips2node[ip] = name

This can be done with less code:

nodes2ip = dict(("node%s" % i, "192.0.2.%s" % i) for i in range(1, 10))
nodes = nodes.keys()
ip2node = dict((v, k) for (k, v) in nodes2ip.items())

> +  def _FakeAction(*args):
> +    return True
> +
> +  def _FakePing(*args, **kwargs):
> +    return True

Here you should verify that live_port, etc. is set correctly.

> +  def _FakeSleep(secs):

Assert that secs is >= 0 and <= some value.

> +    return
> +
> +  def testPingFnRemoveHostsUp(self):
> +    seen = {}
> +    def _FakeSeenPing(ip, *args, **kwargs):
> +      node = self.ips2node[ip]
> +
> +      if node in seen:
> +        seen[node] += 1
> +      else:
> +        seen[node] = 1
> +      return True

If you make “seen” a set, this becomes much simpler.

self.assertFalse(node not in seen)
seen.add(node)

> +    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 range(nodes_len):

for num in enumerate(self.nodes):

> +      helper.Wait(0)
> +      if num < nodes_len - 1:
> +        self.assertRaises(retry.RetryAgain, helper)
> +      else:
> +        helper()
> +
> +    self.assert_(compat.all(count == 1 for count in seen.values()))

See above.

> +    self.assertEqual(set(seen.keys()), 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 retry.RetryAgain:
> +        called = True

Unfortunately there are no real closures in Python :-(

> +        pass

The “pass” is pointless.

> +    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"]),

Can you also test with instances with one, three and four nodes? The
algorithm must be generic.

> +      }
> +
> +    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.assert_(result is None)
> +    self.assertEqual(instances_arg.pop(0), set(["inst1", "inst3"]))
> +    self.assertFalse(inst_map)

I may have missed it, but is there a test verifying that the action
callback only gets started nodes?

Michael

Reply via email to