Statistics
| Branch: | Tag: | Revision:

root / tools / confd-client @ 36c70d4d

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