This relies on the ReqConfigQuery and takes several RFC6901 paths and
will return a table of the keys and values. The data accessible is the
same as in config.data (typically found at /var/lib/ganeti/config.data).

Signed-off-by: Aaron Karper <[email protected]>
---
 Makefile.am                |   3 +-
 src/Ganeti/Confd/Server.hs |   3 +-
 src/Ganeti/Confd/Utils.hs  |   3 +-
 tools/query-config         | 145 +++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 151 insertions(+), 3 deletions(-)
 create mode 100755 tools/query-config

diff --git a/Makefile.am b/Makefile.am
index 3b0fa8f..34d4156 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -1292,7 +1292,8 @@ python_scripts = \
        tools/move-instance \
        tools/ovfconverter \
        tools/post-upgrade \
-       tools/sanitize-config
+       tools/sanitize-config \
+       tools/query-config

 dist_tools_SCRIPTS = \
        tools/kvm-console-wrapper \
diff --git a/src/Ganeti/Confd/Server.hs b/src/Ganeti/Confd/Server.hs
index 855d1fc..e59d5bc 100644
--- a/src/Ganeti/Confd/Server.hs
+++ b/src/Ganeti/Confd/Server.hs
@@ -233,7 +233,8 @@ buildResponse cdata req@(ConfdRequest { confdRqType = 
ReqInstanceDisks }) = do
     Bad e -> fail $ "Could not retrieve disks: " ++ show e

 -- | Return arbitrary configuration value given by a path.
-buildResponse cdata req@(ConfdRequest { confdRqType = ReqConfigQuery, 
confdRqQuery = pathQ }) = do
+buildResponse cdata req@(ConfdRequest { confdRqType = ReqConfigQuery
+                                      , confdRqQuery = pathQ }) = do
   let cfg = fst cdata
   path <-
     case pathQ of
diff --git a/src/Ganeti/Confd/Utils.hs b/src/Ganeti/Confd/Utils.hs
index b09340b..44151de 100644
--- a/src/Ganeti/Confd/Utils.hs
+++ b/src/Ganeti/Confd/Utils.hs
@@ -127,7 +127,8 @@ pointerFromString s = either J.Error J.Ok $ P.parseOnly 
parser $ pack s
       tokens <-  token `P.manyTill` P.endOfInput
       return $ Pointer tokens
     token = do
-      P.char '/' *> (P.many' $ P.choice [escaped, P.satisfy $ P.notInClass 
"~/"])
+      P.char '/' *> (P.many' $ P.choice [ escaped
+                                        , P.satisfy $ P.notInClass "~/"])
     escaped = P.choice [escapedSlash, escapedTilde]
     escapedSlash = P.string (pack "~1") *> return '/'
     escapedTilde = P.string (pack "~0") *> return '~'
diff --git a/tools/query-config b/tools/query-config
new file mode 100755
index 0000000..68e2b81
--- /dev/null
+++ b/tools/query-config
@@ -0,0 +1,145 @@
+#!/usr/bin/python
+#
+
+# Copyright (C) 2014 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.
+
+
+"""Tool to query the cluster configuration over RConfD
+
+"""
+
+# functions in this module need to have a given name structure, so:
+# 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>"),
+  cli.cli_option("-r", "--requests", dest="requests", default=100,
+                 help="Number of requests for the timing tests",
+                 type="int", metavar="<REQUESTS>"),
+  ]
+
+
+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 >> sys.stderr, "Usage:"
+  print >> sys.stderr, USAGE
+  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()
--
2.0.0.526.g5318336

Reply via email to