root / tools / confd-client @ f14863bc
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 | 8bec868e | Iustin Pop | [constants.CONFD_REQFIELD_NAME, |
217 | 8bec868e | Iustin Pop | constants.CONFD_REQFIELD_IP, |
218 | 8bec868e | Iustin Pop | 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() |