#!/usr/bin/python3
#

# Copyright (C) 2014 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.


"""Tool to query the cluster configuration over RConfD

"""
# tool name shouldn't follow module naming.
# pylint: disable=C0103


import optparse
import sys

from ganeti import constants
from ganeti import cli
from ganeti import utils
from ganeti import pathutils

from ganeti.confd import client as confd_client

USAGE = ("\tquery-config [--addr=host] [--hmac=key] QUERY [QUERY...]")

OPTIONS = [
  cli.cli_option("--hmac", dest="hmac", default=None,
                 help="Specify HMAC key instead of reading"
                 " it from the filesystem",
                 metavar="<KEY>"),
  cli.cli_option("-a", "--address", dest="mc", default="127.0.0.1",
                 help="Server IP to query (default: 127.0.0.1)",
                 metavar="<ADDRESS>")
  ]


def Err(msg, exit_code=1):
  """Simple error logging that prints to stderr.

  """
  sys.stderr.write(msg + "\n")
  sys.stderr.flush()
  sys.exit(exit_code)


def Usage():
  """Shows program usage information and exits the program.

  """

  print("Usage:", file=sys.stderr)
  print(USAGE, file=sys.stderr)
  sys.exit(2)


class QueryClient(object):
  """Confd client for querying the configuration JSON.

  """
  def __init__(self):
    """Constructor.

    """
    self.opts = None
    self.cluster_master = None
    self.instance_ips = None
    self.is_timing = False
    self.ParseOptions()

  def ParseOptions(self):
    """Parses the command line options.

    In case of command line errors, it will show the usage and exit the
    program.

    @return: a tuple (options, args), as returned by OptionParser.parse_args

    """
    parser = optparse.OptionParser(usage="\n%s" % USAGE,
                                   version=("%%prog (ganeti) %s" %
                                            constants.RELEASE_VERSION),
                                   option_list=OPTIONS)

    options, args = parser.parse_args()
    if args == []:
      Usage()

    self.paths = args

    if options.hmac is None:
      options.hmac = utils.ReadFile(pathutils.CONFD_HMAC_KEY)

    self.hmac_key = options.hmac

    self.mc_list = [options.mc]

    self.opts = options

  def Run(self):
    self.store_callback = confd_client.StoreResultCallback()

    self.confd_client = confd_client.ConfdClient(
        self.hmac_key, self.mc_list, self.store_callback)

    responses = []
    for path in self.paths:
      req = confd_client.ConfdClientRequest(
        type=constants.CONFD_REQ_CONFIG_QUERY, query=path)
      _, response = self.DoConfdRequestReply(req)
      responses.append(str(response.server_reply.answer))
    table = zip(self.paths, responses)
    longest_path = max(len(p) for p in self.paths)
    for p, a in table:
      print("%s\t%s" % (p.ljust(longest_path), a))

  def DoConfdRequestReply(self, req):
    """Send request to Confd and await all responses.

    """
    self.confd_client.SendRequest(req, async_=False)
    if not self.confd_client.ReceiveReply():
      Err("Did not receive all expected confd replies")
    return self.store_callback.GetResponse(req.rsalt)


def main():
  """Application entry point.

  """
  QueryClient().Run()


if __name__ == "__main__":
  main()
