#!/usr/bin/python
#

# Copyright (C) 2006, 2007 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.


import sys
from optparse import make_option

from ganeti.cli import *
from ganeti import opcodes
from ganeti import logger
from ganeti import objects
from ganeti import utils
from ganeti import errors
from ganeti import constants


def ListOS(opts, args):
  """List the OSes existing on this node.

  """
  op = opcodes.OpDiagnoseOS(output_fields=["name", "valid"], names=[])
  result = SubmitOpCode(op)

  if not result:
    logger.ToStdout("Can't get the OS list")
    return 1

  if not opts.no_headers:
    headers = {"name": "Name"}
  else:
    headers = None

  data = GenerateTable(separator=None, headers=headers, fields=["name"],
                       data=[[row[0]] for row in result if row[1]])

  for line in data:
    logger.ToStdout(line)

  return 0


def DiagnoseOS(opts, args):
  """Analyse all OSes on this cluster.

  """
  op = opcodes.OpDiagnoseOS(output_fields=["name", "valid", "node_status"],
                            names=[])
  result = SubmitOpCode(op)

  if not result:
    logger.ToStdout("Can't get the OS list")
    return 1

  has_bad = False

  for os_name, os_valid, node_data in result:
    nodes_valid = {}
    nodes_bad = {}
    nodes_hidden = {}
    for node_name, node_info in node_data.iteritems():
      nodes_hidden[node_name] = []
      if node_info: # at least one entry in the per-node list
        first_os_status, first_os_path = node_info.pop(0)
        first_os_msg = ("%s (path: %s)" %
                        (first_os_status, first_os_path))
        if first_os_status == constants.OS_VALID_STATUS:
          nodes_valid[node_name] = first_os_msg
        else:
          nodes_bad[node_name] = first_os_msg
        for hstatus, hpath in node_info:
          nodes_hidden[node_name].append("    [hidden] path: %s, status: %s" %
                                         (hpath, hstatus))
      else:
        nodes_bad[node_name] = "OS not found"

    if nodes_valid and not nodes_bad:
      status = "valid"
    elif not nodes_valid and nodes_bad:
      status = "invalid"
      has_bad = True
    else:
      status = "partial valid"
      has_bad = True

    def _OutputPerNodeOSStatus(msg_map):
      map_k = utils.NiceSort(msg_map.keys())
      for node_name in map_k:
        logger.ToStdout("  Node: %s, status: %s" %
                        (node_name, msg_map[node_name]))
        for msg in nodes_hidden[node_name]:
          logger.ToStdout(msg)

    logger.ToStdout("OS: %s [global status: %s]" % (os_name, status))
    _OutputPerNodeOSStatus(nodes_valid)
    _OutputPerNodeOSStatus(nodes_bad)
    logger.ToStdout("")

  return int(has_bad)


commands = {
  'list': (ListOS, ARGS_NONE, [DEBUG_OPT, NOHDR_OPT], "",
           "Lists all valid OSes on the master"),
  'diagnose': (DiagnoseOS, ARGS_NONE, [DEBUG_OPT], "",
               "Diagnose all OSes"),
  }

if __name__ == '__main__':
  sys.exit(GenericMain(commands))
