Add test cluster config for version 2.9
[ganeti-local] / test / py / cfgupgrade_unittest.py
1 #!/usr/bin/python
2 #
3
4 # Copyright (C) 2010, 2012, 2013 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 """Script for testing tools/cfgupgrade"""
23
24 import os
25 import sys
26 import unittest
27 import shutil
28 import tempfile
29 import operator
30
31 from ganeti import constants
32 from ganeti import utils
33 from ganeti import serializer
34 from ganeti import netutils
35
36 import testutils
37
38
39 def GetMinimalConfig():
40   return {
41     "version": constants.CONFIG_VERSION,
42     "cluster": {
43       "master_node": "node1-uuid"
44     },
45     "instances": {},
46     "nodegroups": {},
47     "nodes": {
48       "node1-uuid": {
49         "name": "node1",
50         "uuid": "node1-uuid"
51       }
52     },
53   }
54
55
56 def _RunUpgrade(path, dry_run, no_verify, ignore_hostname=True,
57                 downgrade=False):
58   cmd = [sys.executable, "%s/tools/cfgupgrade" % testutils.GetSourceDir(),
59          "--debug", "--force", "--path=%s" % path, "--confdir=%s" % path]
60
61   if ignore_hostname:
62     cmd.append("--ignore-hostname")
63   if dry_run:
64     cmd.append("--dry-run")
65   if no_verify:
66     cmd.append("--no-verify")
67   if downgrade:
68     cmd.append("--downgrade")
69
70   result = utils.RunCmd(cmd, cwd=os.getcwd())
71   if result.failed:
72     raise Exception("cfgupgrade failed: %s, output %r" %
73                     (result.fail_reason, result.output))
74
75
76 class TestCfgupgrade(unittest.TestCase):
77   def setUp(self):
78     self.tmpdir = tempfile.mkdtemp()
79
80     self.config_path = utils.PathJoin(self.tmpdir, "config.data")
81     self.noded_cert_path = utils.PathJoin(self.tmpdir, "server.pem")
82     self.rapi_cert_path = utils.PathJoin(self.tmpdir, "rapi.pem")
83     self.rapi_users_path = utils.PathJoin(self.tmpdir, "rapi", "users")
84     self.rapi_users_path_pre24 = utils.PathJoin(self.tmpdir, "rapi_users")
85     self.known_hosts_path = utils.PathJoin(self.tmpdir, "known_hosts")
86     self.confd_hmac_path = utils.PathJoin(self.tmpdir, "hmac.key")
87     self.cds_path = utils.PathJoin(self.tmpdir, "cluster-domain-secret")
88     self.ss_master_node_path = utils.PathJoin(self.tmpdir, "ssconf_master_node")
89     self.file_storage_paths = utils.PathJoin(self.tmpdir, "file-storage-paths")
90
91   def tearDown(self):
92     shutil.rmtree(self.tmpdir)
93
94   def _LoadConfig(self):
95     return serializer.LoadJson(utils.ReadFile(self.config_path))
96
97   def _LoadTestDataConfig(self, filename):
98     return serializer.LoadJson(testutils.ReadTestData(filename))
99
100   def _CreateValidConfigDir(self):
101     utils.WriteFile(self.noded_cert_path, data="")
102     utils.WriteFile(self.known_hosts_path, data="")
103     utils.WriteFile(self.ss_master_node_path,
104                     data="node.has.another.name.example.net")
105
106   def testNoConfigDir(self):
107     self.assertFalse(utils.ListVisibleFiles(self.tmpdir))
108     self.assertRaises(Exception, _RunUpgrade, self.tmpdir, False, True)
109     self.assertRaises(Exception, _RunUpgrade, self.tmpdir, True, True)
110
111   def testWrongHostname(self):
112     self._CreateValidConfigDir()
113
114     utils.WriteFile(self.config_path,
115                     data=serializer.DumpJson(GetMinimalConfig()))
116
117     hostname = netutils.GetHostname().name
118     assert hostname != utils.ReadOneLineFile(self.ss_master_node_path)
119
120     self.assertRaises(Exception, _RunUpgrade, self.tmpdir, False, True,
121                       ignore_hostname=False)
122
123   def testCorrectHostname(self):
124     self._CreateValidConfigDir()
125
126     utils.WriteFile(self.config_path,
127                     data=serializer.DumpJson(GetMinimalConfig()))
128
129     utils.WriteFile(self.ss_master_node_path,
130                     data="%s\n" % netutils.GetHostname().name)
131
132     _RunUpgrade(self.tmpdir, False, True, ignore_hostname=False)
133
134   def testInconsistentConfig(self):
135     self._CreateValidConfigDir()
136     # There should be no "config_version"
137     cfg = GetMinimalConfig()
138     cfg["version"] = 0
139     cfg["cluster"]["config_version"] = 0
140     utils.WriteFile(self.config_path, data=serializer.DumpJson(cfg))
141     self.assertRaises(Exception, _RunUpgrade, self.tmpdir, False, True)
142
143   def testInvalidConfig(self):
144     self._CreateValidConfigDir()
145     # Missing version from config
146     utils.WriteFile(self.config_path, data=serializer.DumpJson({}))
147     self.assertRaises(Exception, _RunUpgrade, self.tmpdir, False, True)
148
149   def _TestUpgradeFromFile(self, filename, dry_run):
150     cfg = self._LoadTestDataConfig(filename)
151     self._TestUpgradeFromData(cfg, dry_run)
152
153   def _TestSimpleUpgrade(self, from_version, dry_run,
154                          file_storage_dir=None,
155                          shared_file_storage_dir=None):
156     cfg = GetMinimalConfig()
157     cfg["version"] = from_version
158     cluster = cfg["cluster"]
159
160     if file_storage_dir:
161       cluster["file_storage_dir"] = file_storage_dir
162     if shared_file_storage_dir:
163       cluster["shared_file_storage_dir"] = shared_file_storage_dir
164
165     self._TestUpgradeFromData(cfg, dry_run)
166
167   def _TestUpgradeFromData(self, cfg, dry_run):
168     assert "version" in cfg
169     from_version = cfg["version"]
170     self._CreateValidConfigDir()
171     utils.WriteFile(self.config_path, data=serializer.DumpJson(cfg))
172
173     self.assertFalse(os.path.isfile(self.rapi_cert_path))
174     self.assertFalse(os.path.isfile(self.confd_hmac_path))
175     self.assertFalse(os.path.isfile(self.cds_path))
176
177     _RunUpgrade(self.tmpdir, dry_run, True)
178
179     if dry_run:
180       expversion = from_version
181       checkfn = operator.not_
182     else:
183       expversion = constants.CONFIG_VERSION
184       checkfn = operator.truth
185
186     self.assert_(checkfn(os.path.isfile(self.rapi_cert_path)))
187     self.assert_(checkfn(os.path.isfile(self.confd_hmac_path)))
188     self.assert_(checkfn(os.path.isfile(self.cds_path)))
189
190     newcfg = self._LoadConfig()
191     self.assertEqual(newcfg["version"], expversion)
192
193   def testRapiUsers(self):
194     self.assertFalse(os.path.exists(self.rapi_users_path))
195     self.assertFalse(os.path.exists(self.rapi_users_path_pre24))
196     self.assertFalse(os.path.exists(os.path.dirname(self.rapi_users_path)))
197
198     utils.WriteFile(self.rapi_users_path_pre24, data="some user\n")
199     self._TestSimpleUpgrade(constants.BuildVersion(2, 3, 0), False)
200
201     self.assertTrue(os.path.isdir(os.path.dirname(self.rapi_users_path)))
202     self.assert_(os.path.islink(self.rapi_users_path_pre24))
203     self.assert_(os.path.isfile(self.rapi_users_path))
204     self.assertEqual(os.readlink(self.rapi_users_path_pre24),
205                      self.rapi_users_path)
206     for path in [self.rapi_users_path, self.rapi_users_path_pre24]:
207       self.assertEqual(utils.ReadFile(path), "some user\n")
208
209   def testRapiUsers24AndAbove(self):
210     self.assertFalse(os.path.exists(self.rapi_users_path))
211     self.assertFalse(os.path.exists(self.rapi_users_path_pre24))
212
213     os.mkdir(os.path.dirname(self.rapi_users_path))
214     utils.WriteFile(self.rapi_users_path, data="other user\n")
215     self._TestSimpleUpgrade(constants.BuildVersion(2, 3, 0), False)
216
217     self.assert_(os.path.islink(self.rapi_users_path_pre24))
218     self.assert_(os.path.isfile(self.rapi_users_path))
219     self.assertEqual(os.readlink(self.rapi_users_path_pre24),
220                      self.rapi_users_path)
221     for path in [self.rapi_users_path, self.rapi_users_path_pre24]:
222       self.assertEqual(utils.ReadFile(path), "other user\n")
223
224   def testRapiUsersExistingSymlink(self):
225     self.assertFalse(os.path.exists(self.rapi_users_path))
226     self.assertFalse(os.path.exists(self.rapi_users_path_pre24))
227
228     os.mkdir(os.path.dirname(self.rapi_users_path))
229     os.symlink(self.rapi_users_path, self.rapi_users_path_pre24)
230     utils.WriteFile(self.rapi_users_path, data="hello world\n")
231
232     self._TestSimpleUpgrade(constants.BuildVersion(2, 2, 0), False)
233
234     self.assert_(os.path.isfile(self.rapi_users_path) and
235                  not os.path.islink(self.rapi_users_path))
236     self.assert_(os.path.islink(self.rapi_users_path_pre24))
237     self.assertEqual(os.readlink(self.rapi_users_path_pre24),
238                      self.rapi_users_path)
239     for path in [self.rapi_users_path, self.rapi_users_path_pre24]:
240       self.assertEqual(utils.ReadFile(path), "hello world\n")
241
242   def testRapiUsersExistingTarget(self):
243     self.assertFalse(os.path.exists(self.rapi_users_path))
244     self.assertFalse(os.path.exists(self.rapi_users_path_pre24))
245
246     os.mkdir(os.path.dirname(self.rapi_users_path))
247     utils.WriteFile(self.rapi_users_path, data="other user\n")
248     utils.WriteFile(self.rapi_users_path_pre24, data="hello world\n")
249
250     self.assertRaises(Exception, self._TestSimpleUpgrade,
251                       constants.BuildVersion(2, 2, 0), False)
252
253     for path in [self.rapi_users_path, self.rapi_users_path_pre24]:
254       self.assert_(os.path.isfile(path) and not os.path.islink(path))
255     self.assertEqual(utils.ReadFile(self.rapi_users_path), "other user\n")
256     self.assertEqual(utils.ReadFile(self.rapi_users_path_pre24),
257                      "hello world\n")
258
259   def testRapiUsersDryRun(self):
260     self.assertFalse(os.path.exists(self.rapi_users_path))
261     self.assertFalse(os.path.exists(self.rapi_users_path_pre24))
262
263     utils.WriteFile(self.rapi_users_path_pre24, data="some user\n")
264     self._TestSimpleUpgrade(constants.BuildVersion(2, 3, 0), True)
265
266     self.assertFalse(os.path.isdir(os.path.dirname(self.rapi_users_path)))
267     self.assertTrue(os.path.isfile(self.rapi_users_path_pre24) and
268                     not os.path.islink(self.rapi_users_path_pre24))
269     self.assertFalse(os.path.exists(self.rapi_users_path))
270
271   def testRapiUsers24AndAboveDryRun(self):
272     self.assertFalse(os.path.exists(self.rapi_users_path))
273     self.assertFalse(os.path.exists(self.rapi_users_path_pre24))
274
275     os.mkdir(os.path.dirname(self.rapi_users_path))
276     utils.WriteFile(self.rapi_users_path, data="other user\n")
277     self._TestSimpleUpgrade(constants.BuildVersion(2, 3, 0), True)
278
279     self.assertTrue(os.path.isfile(self.rapi_users_path) and
280                     not os.path.islink(self.rapi_users_path))
281     self.assertFalse(os.path.exists(self.rapi_users_path_pre24))
282     self.assertEqual(utils.ReadFile(self.rapi_users_path), "other user\n")
283
284   def testRapiUsersExistingSymlinkDryRun(self):
285     self.assertFalse(os.path.exists(self.rapi_users_path))
286     self.assertFalse(os.path.exists(self.rapi_users_path_pre24))
287
288     os.mkdir(os.path.dirname(self.rapi_users_path))
289     os.symlink(self.rapi_users_path, self.rapi_users_path_pre24)
290     utils.WriteFile(self.rapi_users_path, data="hello world\n")
291
292     self._TestSimpleUpgrade(constants.BuildVersion(2, 2, 0), True)
293
294     self.assertTrue(os.path.islink(self.rapi_users_path_pre24))
295     self.assertTrue(os.path.isfile(self.rapi_users_path) and
296                     not os.path.islink(self.rapi_users_path))
297     self.assertEqual(os.readlink(self.rapi_users_path_pre24),
298                      self.rapi_users_path)
299     for path in [self.rapi_users_path, self.rapi_users_path_pre24]:
300       self.assertEqual(utils.ReadFile(path), "hello world\n")
301
302   def testFileStoragePathsDryRun(self):
303     self.assertFalse(os.path.exists(self.file_storage_paths))
304
305     self._TestSimpleUpgrade(constants.BuildVersion(2, 6, 0), True,
306                             file_storage_dir=self.tmpdir,
307                             shared_file_storage_dir="/tmp")
308
309     self.assertFalse(os.path.exists(self.file_storage_paths))
310
311   def testFileStoragePathsBoth(self):
312     self.assertFalse(os.path.exists(self.file_storage_paths))
313
314     self._TestSimpleUpgrade(constants.BuildVersion(2, 6, 0), False,
315                             file_storage_dir=self.tmpdir,
316                             shared_file_storage_dir="/tmp")
317
318     lines = utils.ReadFile(self.file_storage_paths).splitlines()
319     self.assertTrue(lines.pop(0).startswith("# "))
320     self.assertTrue(lines.pop(0).startswith("# cfgupgrade"))
321     self.assertEqual(lines.pop(0), self.tmpdir)
322     self.assertEqual(lines.pop(0), "/tmp")
323     self.assertFalse(lines)
324     self.assertEqual(os.stat(self.file_storage_paths).st_mode & 0777,
325                      0600, msg="Wrong permissions")
326
327   def testFileStoragePathsSharedOnly(self):
328     self.assertFalse(os.path.exists(self.file_storage_paths))
329
330     self._TestSimpleUpgrade(constants.BuildVersion(2, 5, 0), False,
331                             file_storage_dir=None,
332                             shared_file_storage_dir=self.tmpdir)
333
334     lines = utils.ReadFile(self.file_storage_paths).splitlines()
335     self.assertTrue(lines.pop(0).startswith("# "))
336     self.assertTrue(lines.pop(0).startswith("# cfgupgrade"))
337     self.assertEqual(lines.pop(0), self.tmpdir)
338     self.assertFalse(lines)
339
340   def testUpgradeFrom_2_0(self):
341     self._TestSimpleUpgrade(constants.BuildVersion(2, 0, 0), False)
342
343   def testUpgradeFrom_2_1(self):
344     self._TestSimpleUpgrade(constants.BuildVersion(2, 1, 0), False)
345
346   def testUpgradeFrom_2_2(self):
347     self._TestSimpleUpgrade(constants.BuildVersion(2, 2, 0), False)
348
349   def testUpgradeFrom_2_3(self):
350     self._TestSimpleUpgrade(constants.BuildVersion(2, 3, 0), False)
351
352   def testUpgradeFrom_2_4(self):
353     self._TestSimpleUpgrade(constants.BuildVersion(2, 4, 0), False)
354
355   def testUpgradeFrom_2_5(self):
356     self._TestSimpleUpgrade(constants.BuildVersion(2, 5, 0), False)
357
358   def testUpgradeFrom_2_6(self):
359     self._TestSimpleUpgrade(constants.BuildVersion(2, 6, 0), False)
360
361   def testUpgradeFrom_2_7(self):
362     self._TestSimpleUpgrade(constants.BuildVersion(2, 7, 0), False)
363
364   def testUpgradeFullConfigFrom_2_7(self):
365     self._TestUpgradeFromFile("cluster_config_2.7.json", False)
366
367   def testUpgradeFullConfigFrom_2_8(self):
368     self._TestUpgradeFromFile("cluster_config_2.8.json", False)
369
370   def testUpgradeCurrent(self):
371     self._TestSimpleUpgrade(constants.CONFIG_VERSION, False)
372
373   def _RunDowngradeUpgrade(self):
374     oldconf = self._LoadConfig()
375     _RunUpgrade(self.tmpdir, False, True, downgrade=True)
376     _RunUpgrade(self.tmpdir, False, True)
377     newconf = self._LoadConfig()
378     self.assertEqual(oldconf, newconf)
379
380   def testDowngrade(self):
381     self._TestSimpleUpgrade(constants.CONFIG_VERSION, False)
382     self._RunDowngradeUpgrade()
383
384   def testDowngradeFullConfig(self):
385     """Test for upgrade + downgrade combination."""
386     # This test can work only with the previous version of a configuration!
387     oldconfname = "cluster_config_2.8.json"
388     self._TestUpgradeFromFile(oldconfname, False)
389     _RunUpgrade(self.tmpdir, False, True, downgrade=True)
390     oldconf = self._LoadTestDataConfig(oldconfname)
391     newconf = self._LoadConfig()
392     self.assertEqual(oldconf, newconf)
393
394   def testDowngradeFullConfigBackwardFrom_2_7(self):
395     """Test for upgrade + downgrade + upgrade combination."""
396     self._TestUpgradeFromFile("cluster_config_2.7.json", False)
397     self._RunDowngradeUpgrade()
398
399   def _RunDowngradeTwice(self):
400     """Make sure that downgrade is idempotent."""
401     _RunUpgrade(self.tmpdir, False, True, downgrade=True)
402     oldconf = self._LoadConfig()
403     _RunUpgrade(self.tmpdir, False, True, downgrade=True)
404     newconf = self._LoadConfig()
405     self.assertEqual(oldconf, newconf)
406
407   def testDowngradeTwice(self):
408     self._TestSimpleUpgrade(constants.CONFIG_VERSION, False)
409     self._RunDowngradeTwice()
410
411   def testDowngradeTwiceFullConfigFrom_2_7(self):
412     self._TestUpgradeFromFile("cluster_config_2.7.json", False)
413     self._RunDowngradeTwice()
414
415   def testUpgradeDryRunFrom_2_0(self):
416     self._TestSimpleUpgrade(constants.BuildVersion(2, 0, 0), True)
417
418   def testUpgradeDryRunFrom_2_1(self):
419     self._TestSimpleUpgrade(constants.BuildVersion(2, 1, 0), True)
420
421   def testUpgradeDryRunFrom_2_2(self):
422     self._TestSimpleUpgrade(constants.BuildVersion(2, 2, 0), True)
423
424   def testUpgradeDryRunFrom_2_3(self):
425     self._TestSimpleUpgrade(constants.BuildVersion(2, 3, 0), True)
426
427   def testUpgradeDryRunFrom_2_4(self):
428     self._TestSimpleUpgrade(constants.BuildVersion(2, 4, 0), True)
429
430   def testUpgradeDryRunFrom_2_5(self):
431     self._TestSimpleUpgrade(constants.BuildVersion(2, 5, 0), True)
432
433   def testUpgradeDryRunFrom_2_6(self):
434     self._TestSimpleUpgrade(constants.BuildVersion(2, 6, 0), True)
435
436   def testUpgradeCurrentDryRun(self):
437     self._TestSimpleUpgrade(constants.CONFIG_VERSION, True)
438
439   def testDowngradeDryRun(self):
440     self._TestSimpleUpgrade(constants.CONFIG_VERSION, False)
441     oldconf = self._LoadConfig()
442     _RunUpgrade(self.tmpdir, True, True, downgrade=True)
443     newconf = self._LoadConfig()
444     self.assertEqual(oldconf["version"], newconf["version"])
445
446 if __name__ == "__main__":
447   testutils.GanetiTestProgram()