QA, burnin: allow selection of reboot types
[ganeti-local] / qa / qa_cluster.py
1 #
2 #
3
4 # Copyright (C) 2007 Google Inc.
5 #
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 # General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 # 02110-1301, USA.
20
21
22 """Cluster related QA tests.
23
24 """
25
26 import tempfile
27
28 from ganeti import constants
29 from ganeti import bootstrap
30 from ganeti import utils
31
32 import qa_config
33 import qa_utils
34 import qa_error
35
36 from qa_utils import AssertEqual, AssertNotEqual, StartSSH
37
38
39 def _RemoveFileFromAllNodes(filename):
40   """Removes a file from all nodes.
41
42   """
43   for node in qa_config.get('nodes'):
44     cmd = ['rm', '-f', filename]
45     AssertEqual(StartSSH(node['primary'],
46                          utils.ShellQuoteArgs(cmd)).wait(), 0)
47
48
49 def _CheckFileOnAllNodes(filename, content):
50   """Verifies the content of the given file on all nodes.
51
52   """
53   cmd = utils.ShellQuoteArgs(["cat", filename])
54   for node in qa_config.get('nodes'):
55     AssertEqual(qa_utils.GetCommandOutput(node['primary'], cmd),
56                 content)
57
58
59 def TestClusterInit(rapi_user, rapi_secret):
60   """gnt-cluster init"""
61   master = qa_config.GetMasterNode()
62
63   # First create the RAPI credentials
64   fh = tempfile.NamedTemporaryFile()
65   try:
66     fh.write("%s %s write\n" % (rapi_user, rapi_secret))
67     fh.flush()
68
69     tmpru = qa_utils.UploadFile(master["primary"], fh.name)
70     try:
71       cmd = ["mv", tmpru, constants.RAPI_USERS_FILE]
72       AssertEqual(StartSSH(master["primary"],
73                            utils.ShellQuoteArgs(cmd)).wait(), 0)
74     finally:
75       cmd = ["rm", "-f", tmpru]
76       AssertEqual(StartSSH(master["primary"],
77                            utils.ShellQuoteArgs(cmd)).wait(), 0)
78   finally:
79     fh.close()
80
81   # Initialize cluster
82   cmd = ['gnt-cluster', 'init']
83
84   if master.get('secondary', None):
85     cmd.append('--secondary-ip=%s' % master['secondary'])
86
87   bridge = qa_config.get('bridge', None)
88   if bridge:
89     cmd.append('--bridge=%s' % bridge)
90     cmd.append('--master-netdev=%s' % bridge)
91
92   htype = qa_config.get('enabled-hypervisors', None)
93   if htype:
94     cmd.append('--enabled-hypervisors=%s' % htype)
95
96   cmd.append(qa_config.get('name'))
97
98   AssertEqual(StartSSH(master['primary'],
99                        utils.ShellQuoteArgs(cmd)).wait(), 0)
100
101
102 def TestClusterRename():
103   """gnt-cluster rename"""
104   master = qa_config.GetMasterNode()
105
106   cmd = ['gnt-cluster', 'rename', '-f']
107
108   original_name = qa_config.get('name')
109   rename_target = qa_config.get('rename', None)
110   if rename_target is None:
111     print qa_utils.FormatError('"rename" entry is missing')
112     return
113
114   cmd_1 = cmd + [rename_target]
115   cmd_2 = cmd + [original_name]
116
117   cmd_verify = ['gnt-cluster', 'verify']
118
119   AssertEqual(StartSSH(master['primary'],
120                        utils.ShellQuoteArgs(cmd_1)).wait(), 0)
121
122   AssertEqual(StartSSH(master['primary'],
123                        utils.ShellQuoteArgs(cmd_verify)).wait(), 0)
124
125   AssertEqual(StartSSH(master['primary'],
126                        utils.ShellQuoteArgs(cmd_2)).wait(), 0)
127
128   AssertEqual(StartSSH(master['primary'],
129                        utils.ShellQuoteArgs(cmd_verify)).wait(), 0)
130
131
132 def TestClusterVerify():
133   """gnt-cluster verify"""
134   master = qa_config.GetMasterNode()
135
136   cmd = ['gnt-cluster', 'verify']
137   AssertEqual(StartSSH(master['primary'],
138                        utils.ShellQuoteArgs(cmd)).wait(), 0)
139
140
141 def TestClusterInfo():
142   """gnt-cluster info"""
143   master = qa_config.GetMasterNode()
144
145   cmd = ['gnt-cluster', 'info']
146   AssertEqual(StartSSH(master['primary'],
147                        utils.ShellQuoteArgs(cmd)).wait(), 0)
148
149
150 def TestClusterGetmaster():
151   """gnt-cluster getmaster"""
152   master = qa_config.GetMasterNode()
153
154   cmd = ['gnt-cluster', 'getmaster']
155   AssertEqual(StartSSH(master['primary'],
156                        utils.ShellQuoteArgs(cmd)).wait(), 0)
157
158
159 def TestClusterVersion():
160   """gnt-cluster version"""
161   master = qa_config.GetMasterNode()
162
163   cmd = ['gnt-cluster', 'version']
164   AssertEqual(StartSSH(master['primary'],
165                        utils.ShellQuoteArgs(cmd)).wait(), 0)
166
167
168 def TestClusterRenewCrypto():
169   """gnt-cluster renew-crypto"""
170   master = qa_config.GetMasterNode()
171
172   # Conflicting options
173   cmd = ["gnt-cluster", "renew-crypto", "--force",
174          "--new-cluster-certificate", "--new-confd-hmac-key",
175          "--new-rapi-certificate", "--rapi-certificate=/dev/null"]
176   AssertNotEqual(StartSSH(master["primary"],
177                           utils.ShellQuoteArgs(cmd)).wait(), 0)
178
179   # Invalid RAPI certificate
180   cmd = ["gnt-cluster", "renew-crypto", "--force",
181          "--rapi-certificate=/dev/null"]
182   AssertNotEqual(StartSSH(master["primary"],
183                           utils.ShellQuoteArgs(cmd)).wait(), 0)
184
185   rapi_cert_backup = qa_utils.BackupFile(master["primary"],
186                                          constants.RAPI_CERT_FILE)
187   try:
188     # Custom RAPI certificate
189     fh = tempfile.NamedTemporaryFile()
190
191     # Ensure certificate doesn't cause "gnt-cluster verify" to complain
192     validity = constants.SSL_CERT_EXPIRATION_WARN * 3
193
194     bootstrap.GenerateSelfSignedSslCert(fh.name, validity=validity)
195
196     tmpcert = qa_utils.UploadFile(master["primary"], fh.name)
197     try:
198       cmd = ["gnt-cluster", "renew-crypto", "--force",
199              "--rapi-certificate=%s" % tmpcert]
200       AssertEqual(StartSSH(master["primary"],
201                            utils.ShellQuoteArgs(cmd)).wait(), 0)
202     finally:
203       cmd = ["rm", "-f", tmpcert]
204       AssertEqual(StartSSH(master["primary"],
205                            utils.ShellQuoteArgs(cmd)).wait(), 0)
206
207     # Normal case
208     cmd = ["gnt-cluster", "renew-crypto", "--force",
209            "--new-cluster-certificate", "--new-confd-hmac-key",
210            "--new-rapi-certificate"]
211     AssertEqual(StartSSH(master["primary"],
212                          utils.ShellQuoteArgs(cmd)).wait(), 0)
213
214     # Restore RAPI certificate
215     cmd = ["gnt-cluster", "renew-crypto", "--force",
216            "--rapi-certificate=%s" % rapi_cert_backup]
217     AssertEqual(StartSSH(master["primary"],
218                          utils.ShellQuoteArgs(cmd)).wait(), 0)
219   finally:
220     cmd = ["rm", "-f", rapi_cert_backup]
221     AssertEqual(StartSSH(master["primary"],
222                          utils.ShellQuoteArgs(cmd)).wait(), 0)
223
224
225 def TestClusterBurnin():
226   """Burnin"""
227   master = qa_config.GetMasterNode()
228
229   options = qa_config.get('options', {})
230   disk_template = options.get('burnin-disk-template', 'drbd')
231   parallel = options.get('burnin-in-parallel', False)
232   check_inst = options.get('burnin-check-instances', False)
233   do_rename = options.get('burnin-rename', '')
234   do_reboot = options.get('burnin-reboot', True)
235   reboot_types = options.get("reboot-types", constants.REBOOT_TYPES)
236
237   # Get as many instances as we need
238   instances = []
239   try:
240     try:
241       num = qa_config.get('options', {}).get('burnin-instances', 1)
242       for _ in range(0, num):
243         instances.append(qa_config.AcquireInstance())
244     except qa_error.OutOfInstancesError:
245       print "Not enough instances, continuing anyway."
246
247     if len(instances) < 1:
248       raise qa_error.Error("Burnin needs at least one instance")
249
250     script = qa_utils.UploadFile(master['primary'], '../tools/burnin')
251     try:
252       # Run burnin
253       cmd = [script,
254              '--os=%s' % qa_config.get('os'),
255              '--disk-size=%s' % ",".join(qa_config.get('disk')),
256              '--disk-growth=%s' % ",".join(qa_config.get('disk-growth')),
257              '--disk-template=%s' % disk_template]
258       if parallel:
259         cmd.append('--parallel')
260         cmd.append('--early-release')
261       if check_inst:
262         cmd.append('--http-check')
263       if do_rename:
264         cmd.append('--rename=%s' % do_rename)
265       if not do_reboot:
266         cmd.append('--no-reboot')
267       else:
268         cmd.append('--reboot-types=%s' % ",".join(reboot_types))
269       cmd += [inst['name'] for inst in instances]
270       AssertEqual(StartSSH(master['primary'],
271                            utils.ShellQuoteArgs(cmd)).wait(), 0)
272     finally:
273       cmd = ['rm', '-f', script]
274       AssertEqual(StartSSH(master['primary'],
275                            utils.ShellQuoteArgs(cmd)).wait(), 0)
276   finally:
277     for inst in instances:
278       qa_config.ReleaseInstance(inst)
279
280
281 def TestClusterMasterFailover():
282   """gnt-cluster masterfailover"""
283   master = qa_config.GetMasterNode()
284
285   failovermaster = qa_config.AcquireNode(exclude=master)
286   try:
287     cmd = ['gnt-cluster', 'masterfailover']
288     AssertEqual(StartSSH(failovermaster['primary'],
289                          utils.ShellQuoteArgs(cmd)).wait(), 0)
290
291     cmd = ['gnt-cluster', 'masterfailover']
292     AssertEqual(StartSSH(master['primary'],
293                          utils.ShellQuoteArgs(cmd)).wait(), 0)
294   finally:
295     qa_config.ReleaseNode(failovermaster)
296
297
298 def TestClusterCopyfile():
299   """gnt-cluster copyfile"""
300   master = qa_config.GetMasterNode()
301
302   uniqueid = utils.NewUUID()
303
304   # Create temporary file
305   f = tempfile.NamedTemporaryFile()
306   f.write(uniqueid)
307   f.flush()
308   f.seek(0)
309
310   # Upload file to master node
311   testname = qa_utils.UploadFile(master['primary'], f.name)
312   try:
313     # Copy file to all nodes
314     cmd = ['gnt-cluster', 'copyfile', testname]
315     AssertEqual(StartSSH(master['primary'],
316                          utils.ShellQuoteArgs(cmd)).wait(), 0)
317     _CheckFileOnAllNodes(testname, uniqueid)
318   finally:
319     _RemoveFileFromAllNodes(testname)
320
321
322 def TestClusterCommand():
323   """gnt-cluster command"""
324   master = qa_config.GetMasterNode()
325
326   uniqueid = utils.NewUUID()
327   rfile = "/tmp/gnt%s" % utils.NewUUID()
328   rcmd = utils.ShellQuoteArgs(['echo', '-n', uniqueid])
329   cmd = utils.ShellQuoteArgs(['gnt-cluster', 'command',
330                               "%s >%s" % (rcmd, rfile)])
331
332   try:
333     AssertEqual(StartSSH(master['primary'], cmd).wait(), 0)
334     _CheckFileOnAllNodes(rfile, uniqueid)
335   finally:
336     _RemoveFileFromAllNodes(rfile)
337
338
339 def TestClusterDestroy():
340   """gnt-cluster destroy"""
341   master = qa_config.GetMasterNode()
342
343   cmd = ['gnt-cluster', 'destroy', '--yes-do-it']
344   AssertEqual(StartSSH(master['primary'],
345                        utils.ShellQuoteArgs(cmd)).wait(), 0)