#
#

# Copyright (C) 2007, 2008, 2009, 2010, 2011 Google Inc.
# All rights reserved.
#
# Redistribution and use in source and binary forms, with or without
# modification, are permitted provided that the following conditions are
# met:
#
# 1. Redistributions of source code must retain the above copyright notice,
# this list of conditions and the following disclaimer.
#
# 2. Redistributions in binary form must reproduce the above copyright
# notice, this list of conditions and the following disclaimer in the
# documentation and/or other materials provided with the distribution.
#
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
# IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
# TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
# PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR
# CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
# EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
# PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
# LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
# NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


"""Daemon related QA tests.

"""

import time

from ganeti import utils
from ganeti import pathutils

from qa import qa_config
from qa import qa_utils
from qa import qa_error

from qa_utils import AssertMatch, AssertCommand, StartSSH, GetCommandOutput


def _InstanceRunning(name):
  """Checks whether an instance is running.

  @param name: full name of the instance

  """
  master = qa_config.GetMasterNode()

  cmd = (utils.ShellQuoteArgs(["gnt-instance", "list", "-o", "status", name]) +
         ' | grep running')
  ret = StartSSH(master.primary, cmd).wait()
  return ret == 0


def _ShutdownInstance(name):
  """Shuts down instance without recording state and waits for completion.

  @param name: full name of the instance

  """
  AssertCommand(["gnt-instance", "shutdown", "--no-remember", name])

  if _InstanceRunning(name):
    raise qa_error.Error("instance shutdown failed")


def _StartInstance(name):
  """Starts instance and waits for completion.

  @param name: full name of the instance

  """
  AssertCommand(["gnt-instance", "start", name])

  if not bool(_InstanceRunning(name)):
    raise qa_error.Error("instance start failed")


def _ResetWatcherDaemon():
  """Removes the watcher daemon's state file.

  """
  path = \
    qa_utils.MakeNodePath(qa_config.GetMasterNode(),
                          pathutils.WATCHER_GROUP_STATE_FILE % "*-*-*-*")

  AssertCommand(["bash", "-c", "rm -vf %s" % path])


def RunWatcherDaemon():
  """Runs the ganeti-watcher daemon on the master node.

  """
  AssertCommand(["ganeti-watcher", "-d", "--ignore-pause", "--wait-children"])


def TestPauseWatcher():
  """Tests and pauses the watcher.

  """
  master = qa_config.GetMasterNode()

  AssertCommand(["gnt-cluster", "watcher", "pause", "4h"])

  cmd = ["gnt-cluster", "watcher", "info"]
  output = GetCommandOutput(master.primary,
                            utils.ShellQuoteArgs(cmd))
  AssertMatch(output, r"^.*\bis paused\b.*")


def TestResumeWatcher():
  """Tests and unpauses the watcher.

  """
  master = qa_config.GetMasterNode()

  AssertCommand(["gnt-cluster", "watcher", "continue"])

  cmd = ["gnt-cluster", "watcher", "info"]
  output = GetCommandOutput(master.primary,
                            utils.ShellQuoteArgs(cmd))
  AssertMatch(output, r"^.*\bis not paused\b.*")


def TestInstanceAutomaticRestart(instance):
  """Test automatic restart of instance by ganeti-watcher.

  """
  inst_name = qa_utils.ResolveInstanceName(instance.name)

  _ResetWatcherDaemon()
  _ShutdownInstance(inst_name)

  RunWatcherDaemon()
  time.sleep(5)

  if not _InstanceRunning(inst_name):
    raise qa_error.Error("Daemon didn't restart instance")

  AssertCommand(["gnt-instance", "info", inst_name])


def TestInstanceConsecutiveFailures(instance):
  """Test five consecutive instance failures.

  """
  inst_name = qa_utils.ResolveInstanceName(instance.name)
  inst_was_running = bool(_InstanceRunning(inst_name))

  _ResetWatcherDaemon()

  for should_start in ([True] * 5) + [False]:
    _ShutdownInstance(inst_name)
    RunWatcherDaemon()
    time.sleep(5)

    if bool(_InstanceRunning(inst_name)) != should_start:
      if should_start:
        msg = "Instance not started when it should"
      else:
        msg = "Instance started when it shouldn't"
      raise qa_error.Error(msg)

  AssertCommand(["gnt-instance", "info", inst_name])

  if inst_was_running:
    _StartInstance(inst_name)
