Statistics
| Branch: | Tag: | Revision:

root / tools / confd-client @ 514dcbda

History | View | Annotate | Download (8.5 kB)

1 4ef0399b Iustin Pop
#!/usr/bin/python
2 4ef0399b Iustin Pop
#
3 4ef0399b Iustin Pop
4 4ef0399b Iustin Pop
# Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011 Google Inc.
5 4ef0399b Iustin Pop
#
6 4ef0399b Iustin Pop
# This program is free software; you can redistribute it and/or modify
7 4ef0399b Iustin Pop
# it under the terms of the GNU General Public License as published by
8 4ef0399b Iustin Pop
# the Free Software Foundation; either version 2 of the License, or
9 4ef0399b Iustin Pop
# (at your option) any later version.
10 4ef0399b Iustin Pop
#
11 4ef0399b Iustin Pop
# This program is distributed in the hope that it will be useful, but
12 4ef0399b Iustin Pop
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 4ef0399b Iustin Pop
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 4ef0399b Iustin Pop
# General Public License for more details.
15 4ef0399b Iustin Pop
#
16 4ef0399b Iustin Pop
# You should have received a copy of the GNU General Public License
17 4ef0399b Iustin Pop
# along with this program; if not, write to the Free Software
18 4ef0399b Iustin Pop
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 4ef0399b Iustin Pop
# 02110-1301, USA.
20 4ef0399b Iustin Pop
21 4ef0399b Iustin Pop
# pylint: disable=C0103
22 4ef0399b Iustin Pop
23 4ef0399b Iustin Pop
"""confd client program
24 4ef0399b Iustin Pop
25 4ef0399b Iustin Pop
This is can be used to test and debug confd daemon functionality.
26 4ef0399b Iustin Pop
27 4ef0399b Iustin Pop
"""
28 4ef0399b Iustin Pop
29 4ef0399b Iustin Pop
import sys
30 4ef0399b Iustin Pop
import optparse
31 4ef0399b Iustin Pop
import time
32 4ef0399b Iustin Pop
33 4ef0399b Iustin Pop
from ganeti import constants
34 4ef0399b Iustin Pop
from ganeti import cli
35 4ef0399b Iustin Pop
from ganeti import utils
36 09bf5d24 Michael Hanselmann
from ganeti import pathutils
37 4ef0399b Iustin Pop
38 4ef0399b Iustin Pop
from ganeti.confd import client as confd_client
39 4ef0399b Iustin Pop
40 4ef0399b Iustin Pop
USAGE = ("\tconfd-client [--addr=host] [--hmac=key]")
41 4ef0399b Iustin Pop
42 4ef0399b Iustin Pop
LOG_HEADERS = {
43 4ef0399b Iustin Pop
  0: "- ",
44 4ef0399b Iustin Pop
  1: "* ",
45 3c286190 Dimitris Aragiorgis
  2: "",
46 4ef0399b Iustin Pop
  }
47 4ef0399b Iustin Pop
48 4ef0399b Iustin Pop
OPTIONS = [
49 4ef0399b Iustin Pop
  cli.cli_option("--hmac", dest="hmac", default=None,
50 4ef0399b Iustin Pop
                 help="Specify HMAC key instead of reading"
51 4ef0399b Iustin Pop
                 " it from the filesystem",
52 4ef0399b Iustin Pop
                 metavar="<KEY>"),
53 4ef0399b Iustin Pop
  cli.cli_option("-a", "--address", dest="mc", default="localhost",
54 4ef0399b Iustin Pop
                 help="Server IP to query (default: 127.0.0.1)",
55 4ef0399b Iustin Pop
                 metavar="<ADDRESS>"),
56 4ef0399b Iustin Pop
  cli.cli_option("-r", "--requests", dest="requests", default=100,
57 4ef0399b Iustin Pop
                 help="Number of requests for the timing tests",
58 4ef0399b Iustin Pop
                 type="int", metavar="<REQUESTS>"),
59 4ef0399b Iustin Pop
  ]
60 4ef0399b Iustin Pop
61 8bec868e Iustin Pop
62 4ef0399b Iustin Pop
def Log(msg, *args, **kwargs):
63 4ef0399b Iustin Pop
  """Simple function that prints out its argument.
64 4ef0399b Iustin Pop
65 4ef0399b Iustin Pop
  """
66 4ef0399b Iustin Pop
  if args:
67 4ef0399b Iustin Pop
    msg = msg % args
68 4ef0399b Iustin Pop
  indent = kwargs.get("indent", 0)
69 4ef0399b Iustin Pop
  sys.stdout.write("%*s%s%s\n" % (2 * indent, "",
70 4ef0399b Iustin Pop
                                  LOG_HEADERS.get(indent, "  "), msg))
71 4ef0399b Iustin Pop
  sys.stdout.flush()
72 4ef0399b Iustin Pop
73 4ef0399b Iustin Pop
74 4ef0399b Iustin Pop
def LogAtMost(msgs, count, **kwargs):
75 4ef0399b Iustin Pop
  """Log at most count of given messages.
76 4ef0399b Iustin Pop
77 4ef0399b Iustin Pop
  """
78 4ef0399b Iustin Pop
  for m in msgs[:count]:
79 4ef0399b Iustin Pop
    Log(m, **kwargs)
80 4ef0399b Iustin Pop
  if len(msgs) > count:
81 4ef0399b Iustin Pop
    Log("...", **kwargs)
82 4ef0399b Iustin Pop
83 4ef0399b Iustin Pop
84 4ef0399b Iustin Pop
def Err(msg, exit_code=1):
85 4ef0399b Iustin Pop
  """Simple error logging that prints to stderr.
86 4ef0399b Iustin Pop
87 4ef0399b Iustin Pop
  """
88 4ef0399b Iustin Pop
  sys.stderr.write(msg + "\n")
89 4ef0399b Iustin Pop
  sys.stderr.flush()
90 4ef0399b Iustin Pop
  sys.exit(exit_code)
91 4ef0399b Iustin Pop
92 4ef0399b Iustin Pop
93 4ef0399b Iustin Pop
def Usage():
94 4ef0399b Iustin Pop
  """Shows program usage information and exits the program."""
95 4ef0399b Iustin Pop
96 4ef0399b Iustin Pop
  print >> sys.stderr, "Usage:"
97 4ef0399b Iustin Pop
  print >> sys.stderr, USAGE
98 4ef0399b Iustin Pop
  sys.exit(2)
99 4ef0399b Iustin Pop
100 4ef0399b Iustin Pop
101 4ef0399b Iustin Pop
class TestClient(object):
102 4ef0399b Iustin Pop
  """Confd test client."""
103 4ef0399b Iustin Pop
104 4ef0399b Iustin Pop
  def __init__(self):
105 4ef0399b Iustin Pop
    """Constructor."""
106 4ef0399b Iustin Pop
    self.opts = None
107 4ef0399b Iustin Pop
    self.cluster_master = None
108 4ef0399b Iustin Pop
    self.instance_ips = None
109 4ef0399b Iustin Pop
    self.is_timing = False
110 4ef0399b Iustin Pop
    self.ParseOptions()
111 4ef0399b Iustin Pop
112 4ef0399b Iustin Pop
  def ParseOptions(self):
113 4ef0399b Iustin Pop
    """Parses the command line options.
114 4ef0399b Iustin Pop
115 4ef0399b Iustin Pop
    In case of command line errors, it will show the usage and exit the
116 4ef0399b Iustin Pop
    program.
117 4ef0399b Iustin Pop
118 4ef0399b Iustin Pop
    """
119 4ef0399b Iustin Pop
    parser = optparse.OptionParser(usage="\n%s" % USAGE,
120 4ef0399b Iustin Pop
                                   version=("%%prog (ganeti) %s" %
121 4ef0399b Iustin Pop
                                            constants.RELEASE_VERSION),
122 4ef0399b Iustin Pop
                                   option_list=OPTIONS)
123 4ef0399b Iustin Pop
124 4ef0399b Iustin Pop
    options, args = parser.parse_args()
125 4ef0399b Iustin Pop
    if args:
126 4ef0399b Iustin Pop
      Usage()
127 4ef0399b Iustin Pop
128 4ef0399b Iustin Pop
    if options.hmac is None:
129 09bf5d24 Michael Hanselmann
      options.hmac = utils.ReadFile(pathutils.CONFD_HMAC_KEY)
130 4ef0399b Iustin Pop
    self.hmac_key = options.hmac
131 4ef0399b Iustin Pop
132 4ef0399b Iustin Pop
    self.mc_list = [options.mc]
133 4ef0399b Iustin Pop
134 4ef0399b Iustin Pop
    self.opts = options
135 4ef0399b Iustin Pop
136 4ef0399b Iustin Pop
  def ConfdCallback(self, reply):
137 4ef0399b Iustin Pop
    """Callback for confd queries"""
138 4ef0399b Iustin Pop
    if reply.type == confd_client.UPCALL_REPLY:
139 4ef0399b Iustin Pop
      answer = reply.server_reply.answer
140 4ef0399b Iustin Pop
      reqtype = reply.orig_request.type
141 4ef0399b Iustin Pop
      if reply.server_reply.status != constants.CONFD_REPL_STATUS_OK:
142 4ef0399b Iustin Pop
        Log("Query %s gave non-ok status %s: %s" % (reply.orig_request,
143 4ef0399b Iustin Pop
                                                    reply.server_reply.status,
144 4ef0399b Iustin Pop
                                                    reply.server_reply))
145 4ef0399b Iustin Pop
        if self.is_timing:
146 4ef0399b Iustin Pop
          Err("Aborting timing tests")
147 4ef0399b Iustin Pop
        if reqtype == constants.CONFD_REQ_CLUSTER_MASTER:
148 4ef0399b Iustin Pop
          Err("Cannot continue after master query failure")
149 4ef0399b Iustin Pop
        if reqtype == constants.CONFD_REQ_INSTANCES_IPS_LIST:
150 4ef0399b Iustin Pop
          Err("Cannot continue after instance IP list query failure")
151 4ef0399b Iustin Pop
        return
152 4ef0399b Iustin Pop
      if self.is_timing:
153 4ef0399b Iustin Pop
        return
154 4ef0399b Iustin Pop
      if reqtype == constants.CONFD_REQ_PING:
155 4ef0399b Iustin Pop
        Log("Ping: OK")
156 4ef0399b Iustin Pop
      elif reqtype == constants.CONFD_REQ_CLUSTER_MASTER:
157 4ef0399b Iustin Pop
        Log("Master: OK (%s)", answer)
158 4ef0399b Iustin Pop
        if self.cluster_master is None:
159 4ef0399b Iustin Pop
          # only assign the first time, in the plain query
160 4ef0399b Iustin Pop
          self.cluster_master = answer
161 4ef0399b Iustin Pop
      elif reqtype == constants.CONFD_REQ_NODE_ROLE_BYNAME:
162 4ef0399b Iustin Pop
        if answer == constants.CONFD_NODE_ROLE_MASTER:
163 4ef0399b Iustin Pop
          Log("Node role for master: OK",)
164 4ef0399b Iustin Pop
        else:
165 4ef0399b Iustin Pop
          Err("Node role for master: wrong: %s" % answer)
166 4ef0399b Iustin Pop
      elif reqtype == constants.CONFD_REQ_NODE_PIP_LIST:
167 4ef0399b Iustin Pop
        Log("Node primary ip query: OK")
168 4ef0399b Iustin Pop
        LogAtMost(answer, 5, indent=1)
169 4ef0399b Iustin Pop
      elif reqtype == constants.CONFD_REQ_MC_PIP_LIST:
170 4ef0399b Iustin Pop
        Log("Master candidates primary IP query: OK")
171 4ef0399b Iustin Pop
        LogAtMost(answer, 5, indent=1)
172 4ef0399b Iustin Pop
      elif reqtype == constants.CONFD_REQ_INSTANCES_IPS_LIST:
173 4ef0399b Iustin Pop
        Log("Instance primary IP query: OK")
174 4ef0399b Iustin Pop
        if not answer:
175 4ef0399b Iustin Pop
          Log("no IPs received", indent=1)
176 4ef0399b Iustin Pop
        else:
177 4ef0399b Iustin Pop
          LogAtMost(answer, 5, indent=1)
178 4ef0399b Iustin Pop
        self.instance_ips = answer
179 4ef0399b Iustin Pop
      elif reqtype == constants.CONFD_REQ_NODE_PIP_BY_INSTANCE_IP:
180 4ef0399b Iustin Pop
        Log("Instance IP to node IP query: OK")
181 4ef0399b Iustin Pop
        if not answer:
182 4ef0399b Iustin Pop
          Log("no mapping received", indent=1)
183 4ef0399b Iustin Pop
        else:
184 4ef0399b Iustin Pop
          LogAtMost(answer, 5, indent=1)
185 4ef0399b Iustin Pop
      else:
186 4ef0399b Iustin Pop
        Log("Unhandled reply %s, please fix the client", reqtype)
187 4ef0399b Iustin Pop
        print answer
188 4ef0399b Iustin Pop
189 4ef0399b Iustin Pop
  def DoConfdRequestReply(self, req):
190 4ef0399b Iustin Pop
    self.confd_counting_callback.RegisterQuery(req.rsalt)
191 4ef0399b Iustin Pop
    self.confd_client.SendRequest(req, async=False)
192 4ef0399b Iustin Pop
    while not self.confd_counting_callback.AllAnswered():
193 4ef0399b Iustin Pop
      if not self.confd_client.ReceiveReply():
194 4ef0399b Iustin Pop
        Err("Did not receive all expected confd replies")
195 4ef0399b Iustin Pop
        break
196 4ef0399b Iustin Pop
197 4ef0399b Iustin Pop
  def TestConfd(self):
198 4ef0399b Iustin Pop
    """Run confd queries for the cluster.
199 4ef0399b Iustin Pop
200 4ef0399b Iustin Pop
    """
201 4ef0399b Iustin Pop
    Log("Checking confd results")
202 4ef0399b Iustin Pop
203 4ef0399b Iustin Pop
    filter_callback = confd_client.ConfdFilterCallback(self.ConfdCallback)
204 4ef0399b Iustin Pop
    counting_callback = confd_client.ConfdCountingCallback(filter_callback)
205 4ef0399b Iustin Pop
    self.confd_counting_callback = counting_callback
206 4ef0399b Iustin Pop
207 4ef0399b Iustin Pop
    self.confd_client = confd_client.ConfdClient(self.hmac_key,
208 4ef0399b Iustin Pop
                                                 self.mc_list,
209 4ef0399b Iustin Pop
                                                 counting_callback)
210 4ef0399b Iustin Pop
211 4ef0399b Iustin Pop
    tests = [
212 8bec868e Iustin Pop
      {"type": constants.CONFD_REQ_PING},
213 8bec868e Iustin Pop
      {"type": constants.CONFD_REQ_CLUSTER_MASTER},
214 4ef0399b Iustin Pop
      {"type": constants.CONFD_REQ_CLUSTER_MASTER,
215 8bec868e Iustin Pop
       "query": {constants.CONFD_REQQ_FIELDS:
216 ba174485 Jose A. Lopes
                 [str(constants.CONFD_REQFIELD_NAME),
217 ba174485 Jose A. Lopes
                  str(constants.CONFD_REQFIELD_IP),
218 ba174485 Jose A. Lopes
                  str(constants.CONFD_REQFIELD_MNODE_PIP),
219 8bec868e Iustin Pop
                  ]}},
220 8bec868e Iustin Pop
      {"type": constants.CONFD_REQ_NODE_ROLE_BYNAME},
221 8bec868e Iustin Pop
      {"type": constants.CONFD_REQ_NODE_PIP_LIST},
222 8bec868e Iustin Pop
      {"type": constants.CONFD_REQ_MC_PIP_LIST},
223 4ef0399b Iustin Pop
      {"type": constants.CONFD_REQ_INSTANCES_IPS_LIST,
224 4ef0399b Iustin Pop
       "query": None},
225 8bec868e Iustin Pop
      {"type": constants.CONFD_REQ_NODE_PIP_BY_INSTANCE_IP},
226 4ef0399b Iustin Pop
      ]
227 4ef0399b Iustin Pop
228 4ef0399b Iustin Pop
    for kwargs in tests:
229 4ef0399b Iustin Pop
      if kwargs["type"] == constants.CONFD_REQ_NODE_ROLE_BYNAME:
230 4ef0399b Iustin Pop
        assert self.cluster_master is not None
231 4ef0399b Iustin Pop
        kwargs["query"] = self.cluster_master
232 4ef0399b Iustin Pop
      elif kwargs["type"] == constants.CONFD_REQ_NODE_PIP_BY_INSTANCE_IP:
233 8bec868e Iustin Pop
        kwargs["query"] = {constants.CONFD_REQQ_IPLIST: self.instance_ips}
234 4ef0399b Iustin Pop
235 4ef0399b Iustin Pop
      # pylint: disable=W0142
236 4ef0399b Iustin Pop
      # used ** magic
237 4ef0399b Iustin Pop
      req = confd_client.ConfdClientRequest(**kwargs)
238 4ef0399b Iustin Pop
      self.DoConfdRequestReply(req)
239 4ef0399b Iustin Pop
240 4ef0399b Iustin Pop
  def TestTiming(self):
241 4ef0399b Iustin Pop
    """Run timing tests.
242 4ef0399b Iustin Pop
243 4ef0399b Iustin Pop
    """
244 4ef0399b Iustin Pop
    # timing tests
245 4ef0399b Iustin Pop
    if self.opts.requests <= 0:
246 4ef0399b Iustin Pop
      return
247 4ef0399b Iustin Pop
    Log("Timing tests")
248 4ef0399b Iustin Pop
    self.is_timing = True
249 4ef0399b Iustin Pop
    self.TimingOp("ping", {"type": constants.CONFD_REQ_PING})
250 4ef0399b Iustin Pop
    self.TimingOp("instance ips",
251 4ef0399b Iustin Pop
                  {"type": constants.CONFD_REQ_INSTANCES_IPS_LIST})
252 4ef0399b Iustin Pop
253 4ef0399b Iustin Pop
  def TimingOp(self, name, kwargs):
254 4ef0399b Iustin Pop
    """Run a single timing test.
255 4ef0399b Iustin Pop
256 4ef0399b Iustin Pop
    """
257 4ef0399b Iustin Pop
    start = time.time()
258 8bec868e Iustin Pop
    for _ in range(self.opts.requests):
259 8bec868e Iustin Pop
      # pylint: disable=W0142
260 4ef0399b Iustin Pop
      req = confd_client.ConfdClientRequest(**kwargs)
261 4ef0399b Iustin Pop
      self.DoConfdRequestReply(req)
262 4ef0399b Iustin Pop
    stop = time.time()
263 8bec868e Iustin Pop
    per_req = 1000 * (stop - start) / self.opts.requests
264 4ef0399b Iustin Pop
    Log("%.3fms per %s request", per_req, name, indent=1)
265 4ef0399b Iustin Pop
266 4ef0399b Iustin Pop
  def Run(self):
267 4ef0399b Iustin Pop
    """Run all the tests.
268 4ef0399b Iustin Pop
269 4ef0399b Iustin Pop
    """
270 4ef0399b Iustin Pop
    self.TestConfd()
271 4ef0399b Iustin Pop
    self.TestTiming()
272 4ef0399b Iustin Pop
273 8bec868e Iustin Pop
274 4ef0399b Iustin Pop
def main():
275 4ef0399b Iustin Pop
  """Main function.
276 4ef0399b Iustin Pop
277 4ef0399b Iustin Pop
  """
278 4ef0399b Iustin Pop
  return TestClient().Run()
279 4ef0399b Iustin Pop
280 4ef0399b Iustin Pop
281 4ef0399b Iustin Pop
if __name__ == "__main__":
282 4ef0399b Iustin Pop
  main()