Remove 15-second sleep from LUInstanceCreate
[ganeti-local] / test / ganeti.rapi.rlib2_unittest.py
1 #!/usr/bin/python
2 #
3
4 # Copyright (C) 2010 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 unittesting the RAPI rlib2 module
23
24 """
25
26
27 import unittest
28 import tempfile
29
30 from ganeti import constants
31 from ganeti import opcodes
32 from ganeti import compat
33 from ganeti import http
34 from ganeti import query
35
36 from ganeti.rapi import rlib2
37
38 import testutils
39
40
41 class TestConstants(unittest.TestCase):
42   def testConsole(self):
43     # Exporting the console field without authentication might expose
44     # information
45     assert "console" in query.INSTANCE_FIELDS
46     self.assertTrue("console" not in rlib2.I_FIELDS)
47
48
49 class TestParseInstanceCreateRequestVersion1(testutils.GanetiTestCase):
50   def setUp(self):
51     testutils.GanetiTestCase.setUp(self)
52
53     self.Parse = rlib2._ParseInstanceCreateRequestVersion1
54
55   def test(self):
56     disk_variants = [
57       # No disks
58       [],
59
60       # Two disks
61       [{"size": 5, }, {"size": 100, }],
62
63       # Disk with mode
64       [{"size": 123, "mode": constants.DISK_RDWR, }],
65       ]
66
67     nic_variants = [
68       # No NIC
69       [],
70
71       # Three NICs
72       [{}, {}, {}],
73
74       # Two NICs
75       [
76         { "ip": "192.0.2.6", "mode": constants.NIC_MODE_ROUTED,
77           "mac": "01:23:45:67:68:9A",
78         },
79         { "mode": constants.NIC_MODE_BRIDGED, "link": "br1" },
80       ],
81       ]
82
83     beparam_variants = [
84       None,
85       {},
86       { constants.BE_VCPUS: 2, },
87       { constants.BE_MEMORY: 123, },
88       { constants.BE_VCPUS: 2,
89         constants.BE_MEMORY: 1024,
90         constants.BE_AUTO_BALANCE: True, }
91       ]
92
93     hvparam_variants = [
94       None,
95       { constants.HV_BOOT_ORDER: "anc", },
96       { constants.HV_KERNEL_PATH: "/boot/fookernel",
97         constants.HV_ROOT_PATH: "/dev/hda1", },
98       ]
99
100     for mode in [constants.INSTANCE_CREATE, constants.INSTANCE_IMPORT]:
101       for nics in nic_variants:
102         for disk_template in constants.DISK_TEMPLATES:
103           for disks in disk_variants:
104             for beparams in beparam_variants:
105               for hvparams in hvparam_variants:
106                 data = {
107                   "name": "inst1.example.com",
108                   "hypervisor": constants.HT_FAKE,
109                   "disks": disks,
110                   "nics": nics,
111                   "mode": mode,
112                   "disk_template": disk_template,
113                   "os": "debootstrap",
114                   }
115
116                 if beparams is not None:
117                   data["beparams"] = beparams
118
119                 if hvparams is not None:
120                   data["hvparams"] = hvparams
121
122                 for dry_run in [False, True]:
123                   op = self.Parse(data, dry_run)
124                   self.assert_(isinstance(op, opcodes.OpInstanceCreate))
125                   self.assertEqual(op.mode, mode)
126                   self.assertEqual(op.disk_template, disk_template)
127                   self.assertEqual(op.dry_run, dry_run)
128                   self.assertEqual(len(op.disks), len(disks))
129                   self.assertEqual(len(op.nics), len(nics))
130
131                   for opdisk, disk in zip(op.disks, disks):
132                     for key in constants.IDISK_PARAMS:
133                       self.assertEqual(opdisk.get(key), disk.get(key))
134                     self.assertFalse("unknown" in opdisk)
135
136                   for opnic, nic in zip(op.nics, nics):
137                     for key in constants.INIC_PARAMS:
138                       self.assertEqual(opnic.get(key), nic.get(key))
139                     self.assertFalse("unknown" in opnic)
140                     self.assertFalse("foobar" in opnic)
141
142                   if beparams is None:
143                     self.assertFalse(hasattr(op, "beparams"))
144                   else:
145                     self.assertEqualValues(op.beparams, beparams)
146
147                   if hvparams is None:
148                     self.assertFalse(hasattr(op, "hvparams"))
149                   else:
150                     self.assertEqualValues(op.hvparams, hvparams)
151
152   def testLegacyName(self):
153     name = "inst29128.example.com"
154     data = {
155       "name": name,
156       "disks": [],
157       "nics": [],
158       "mode": constants.INSTANCE_CREATE,
159       "disk_template": constants.DT_PLAIN,
160       }
161     op = self.Parse(data, False)
162     self.assert_(isinstance(op, opcodes.OpInstanceCreate))
163     self.assertEqual(op.instance_name, name)
164     self.assertFalse(hasattr(op, "name"))
165
166     # Define both
167     data = {
168       "name": name,
169       "instance_name": "other.example.com",
170       "disks": [],
171       "nics": [],
172       "mode": constants.INSTANCE_CREATE,
173       "disk_template": constants.DT_PLAIN,
174       }
175     self.assertRaises(http.HttpBadRequest, self.Parse, data, False)
176
177   def testLegacyOs(self):
178     name = "inst4673.example.com"
179     os = "linux29206"
180     data = {
181       "name": name,
182       "os_type": os,
183       "disks": [],
184       "nics": [],
185       "mode": constants.INSTANCE_CREATE,
186       "disk_template": constants.DT_PLAIN,
187       }
188     op = self.Parse(data, False)
189     self.assert_(isinstance(op, opcodes.OpInstanceCreate))
190     self.assertEqual(op.instance_name, name)
191     self.assertEqual(op.os_type, os)
192     self.assertFalse(hasattr(op, "os"))
193
194     # Define both
195     data = {
196       "instance_name": name,
197       "os": os,
198       "os_type": "linux9584",
199       "disks": [],
200       "nics": [],
201       "mode": constants.INSTANCE_CREATE,
202       "disk_template": constants.DT_PLAIN,
203       }
204     self.assertRaises(http.HttpBadRequest, self.Parse, data, False)
205
206   def testErrors(self):
207     # Test all required fields
208     reqfields = {
209       "name": "inst1.example.com",
210       "disks": [],
211       "nics": [],
212       "mode": constants.INSTANCE_CREATE,
213       "disk_template": constants.DT_PLAIN,
214       }
215
216     for name in reqfields.keys():
217       self.assertRaises(http.HttpBadRequest, self.Parse,
218                         dict(i for i in reqfields.iteritems() if i[0] != name),
219                         False)
220
221     # Invalid disks and nics
222     for field in ["disks", "nics"]:
223       invalid_values = [None, 1, "", {}, [1, 2, 3], ["hda1", "hda2"],
224                         [{"_unknown_": 999, }]]
225
226       for invvalue in invalid_values:
227         data = reqfields.copy()
228         data[field] = invvalue
229         self.assertRaises(http.HttpBadRequest, self.Parse, data, False)
230
231
232 class TestParseExportInstanceRequest(testutils.GanetiTestCase):
233   def setUp(self):
234     testutils.GanetiTestCase.setUp(self)
235
236     self.Parse = rlib2._ParseExportInstanceRequest
237
238   def test(self):
239     name = "instmoo"
240     data = {
241       "mode": constants.EXPORT_MODE_REMOTE,
242       "destination": [(1, 2, 3), (99, 99, 99)],
243       "shutdown": True,
244       "remove_instance": True,
245       "x509_key_name": ["name", "hash"],
246       "destination_x509_ca": "---cert---"
247       }
248     op = self.Parse(name, data)
249     self.assert_(isinstance(op, opcodes.OpBackupExport))
250     self.assertEqual(op.instance_name, name)
251     self.assertEqual(op.mode, constants.EXPORT_MODE_REMOTE)
252     self.assertEqual(op.shutdown, True)
253     self.assertEqual(op.remove_instance, True)
254     self.assertEqualValues(op.x509_key_name, ("name", "hash"))
255     self.assertEqual(op.destination_x509_ca, "---cert---")
256
257   def testDefaults(self):
258     name = "inst1"
259     data = {
260       "destination": "node2",
261       "shutdown": False,
262       }
263     op = self.Parse(name, data)
264     self.assert_(isinstance(op, opcodes.OpBackupExport))
265     self.assertEqual(op.instance_name, name)
266     self.assertEqual(op.target_node, "node2")
267     self.assertFalse(hasattr(op, "mode"))
268     self.assertFalse(hasattr(op, "remove_instance"))
269     self.assertFalse(hasattr(op, "destination"))
270
271   def testErrors(self):
272     self.assertRaises(http.HttpBadRequest, self.Parse, "err1",
273                       { "remove_instance": "True", })
274     self.assertRaises(http.HttpBadRequest, self.Parse, "err1",
275                       { "remove_instance": "False", })
276
277
278 class TestParseMigrateInstanceRequest(testutils.GanetiTestCase):
279   def setUp(self):
280     testutils.GanetiTestCase.setUp(self)
281
282     self.Parse = rlib2._ParseMigrateInstanceRequest
283
284   def test(self):
285     name = "instYooho6ek"
286
287     for cleanup in [False, True]:
288       for mode in constants.HT_MIGRATION_MODES:
289         data = {
290           "cleanup": cleanup,
291           "mode": mode,
292           }
293         op = self.Parse(name, data)
294         self.assert_(isinstance(op, opcodes.OpInstanceMigrate))
295         self.assertEqual(op.instance_name, name)
296         self.assertEqual(op.mode, mode)
297         self.assertEqual(op.cleanup, cleanup)
298
299   def testDefaults(self):
300     name = "instnohZeex0"
301
302     op = self.Parse(name, {})
303     self.assert_(isinstance(op, opcodes.OpInstanceMigrate))
304     self.assertEqual(op.instance_name, name)
305     self.assertFalse(hasattr(op, "mode"))
306     self.assertFalse(hasattr(op, "cleanup"))
307
308
309 class TestParseRenameInstanceRequest(testutils.GanetiTestCase):
310   def setUp(self):
311     testutils.GanetiTestCase.setUp(self)
312
313     self.Parse = rlib2._ParseRenameInstanceRequest
314
315   def test(self):
316     name = "instij0eeph7"
317
318     for new_name in ["ua0aiyoo", "fai3ongi"]:
319       for ip_check in [False, True]:
320         for name_check in [False, True]:
321           data = {
322             "new_name": new_name,
323             "ip_check": ip_check,
324             "name_check": name_check,
325             }
326
327           op = self.Parse(name, data)
328           self.assert_(isinstance(op, opcodes.OpInstanceRename))
329           self.assertEqual(op.instance_name, name)
330           self.assertEqual(op.new_name, new_name)
331           self.assertEqual(op.ip_check, ip_check)
332           self.assertEqual(op.name_check, name_check)
333
334   def testDefaults(self):
335     name = "instahchie3t"
336
337     for new_name in ["thag9mek", "quees7oh"]:
338       data = {
339         "new_name": new_name,
340         }
341
342       op = self.Parse(name, data)
343       self.assert_(isinstance(op, opcodes.OpInstanceRename))
344       self.assertEqual(op.instance_name, name)
345       self.assertEqual(op.new_name, new_name)
346       self.assertFalse(hasattr(op, "ip_check"))
347       self.assertFalse(hasattr(op, "name_check"))
348
349
350 class TestParseModifyInstanceRequest(testutils.GanetiTestCase):
351   def setUp(self):
352     testutils.GanetiTestCase.setUp(self)
353
354     self.Parse = rlib2._ParseModifyInstanceRequest
355
356   def test(self):
357     name = "instush8gah"
358
359     test_disks = [
360       [],
361       [(1, { constants.IDISK_MODE: constants.DISK_RDWR, })],
362       ]
363
364     for osparams in [{}, { "some": "value", "other": "Hello World", }]:
365       for hvparams in [{}, { constants.HV_KERNEL_PATH: "/some/kernel", }]:
366         for beparams in [{}, { constants.BE_MEMORY: 128, }]:
367           for force in [False, True]:
368             for nics in [[], [(0, { constants.INIC_IP: "192.0.2.1", })]]:
369               for disks in test_disks:
370                 for disk_template in constants.DISK_TEMPLATES:
371                   data = {
372                     "osparams": osparams,
373                     "hvparams": hvparams,
374                     "beparams": beparams,
375                     "nics": nics,
376                     "disks": disks,
377                     "force": force,
378                     "disk_template": disk_template,
379                     }
380
381                   op = self.Parse(name, data)
382                   self.assert_(isinstance(op, opcodes.OpInstanceSetParams))
383                   self.assertEqual(op.instance_name, name)
384                   self.assertEqual(op.hvparams, hvparams)
385                   self.assertEqual(op.beparams, beparams)
386                   self.assertEqual(op.osparams, osparams)
387                   self.assertEqual(op.force, force)
388                   self.assertEqual(op.nics, nics)
389                   self.assertEqual(op.disks, disks)
390                   self.assertEqual(op.disk_template, disk_template)
391                   self.assertFalse(hasattr(op, "remote_node"))
392                   self.assertFalse(hasattr(op, "os_name"))
393                   self.assertFalse(hasattr(op, "force_variant"))
394
395   def testDefaults(self):
396     name = "instir8aish31"
397
398     op = self.Parse(name, {})
399     self.assert_(isinstance(op, opcodes.OpInstanceSetParams))
400     self.assertEqual(op.instance_name, name)
401     for i in ["hvparams", "beparams", "osparams", "force", "nics", "disks",
402               "disk_template", "remote_node", "os_name", "force_variant"]:
403       self.assertFalse(hasattr(op, i))
404
405
406 class TestParseInstanceReinstallRequest(testutils.GanetiTestCase):
407   def setUp(self):
408     testutils.GanetiTestCase.setUp(self)
409
410     self.Parse = rlib2._ParseInstanceReinstallRequest
411
412   def _Check(self, ops, name):
413     expcls = [
414       opcodes.OpInstanceShutdown,
415       opcodes.OpInstanceReinstall,
416       opcodes.OpInstanceStartup,
417       ]
418
419     self.assert_(compat.all(isinstance(op, exp)
420                             for op, exp in zip(ops, expcls)))
421     self.assert_(compat.all(op.instance_name == name for op in ops))
422
423   def test(self):
424     name = "shoo0tihohma"
425
426     ops = self.Parse(name, {"os": "sys1", "start": True,})
427     self.assertEqual(len(ops), 3)
428     self._Check(ops, name)
429     self.assertEqual(ops[1].os_type, "sys1")
430     self.assertFalse(ops[1].osparams)
431
432     ops = self.Parse(name, {"os": "sys2", "start": False,})
433     self.assertEqual(len(ops), 2)
434     self._Check(ops, name)
435     self.assertEqual(ops[1].os_type, "sys2")
436
437     osparams = {
438       "reformat": "1",
439       }
440     ops = self.Parse(name, {"os": "sys4035", "start": True,
441                             "osparams": osparams,})
442     self.assertEqual(len(ops), 3)
443     self._Check(ops, name)
444     self.assertEqual(ops[1].os_type, "sys4035")
445     self.assertEqual(ops[1].osparams, osparams)
446
447   def testDefaults(self):
448     name = "noolee0g"
449
450     ops = self.Parse(name, {"os": "linux1"})
451     self.assertEqual(len(ops), 3)
452     self._Check(ops, name)
453     self.assertEqual(ops[1].os_type, "linux1")
454     self.assertFalse(ops[1].osparams)
455
456
457 class TestParseRenameGroupRequest(testutils.GanetiTestCase):
458   def setUp(self):
459     testutils.GanetiTestCase.setUp(self)
460
461     self.Parse = rlib2._ParseRenameGroupRequest
462
463   def test(self):
464     name = "instij0eeph7"
465     data = {
466       "new_name": "ua0aiyoo",
467       }
468
469     op = self.Parse(name, data, False)
470
471     self.assert_(isinstance(op, opcodes.OpGroupRename))
472     self.assertEqual(op.group_name, name)
473     self.assertEqual(op.new_name, "ua0aiyoo")
474     self.assertFalse(op.dry_run)
475
476   def testDryRun(self):
477     name = "instij0eeph7"
478     data = {
479       "new_name": "ua0aiyoo",
480       }
481
482     op = self.Parse(name, data, True)
483
484     self.assert_(isinstance(op, opcodes.OpGroupRename))
485     self.assertEqual(op.group_name, name)
486     self.assertEqual(op.new_name, "ua0aiyoo")
487     self.assert_(op.dry_run)
488
489
490 class TestParseInstanceReplaceDisksRequest(unittest.TestCase):
491   def setUp(self):
492     self.Parse = rlib2._ParseInstanceReplaceDisksRequest
493
494   def test(self):
495     name = "inst22568"
496
497     for disks in [range(1, 4), "1,2,3", "1, 2, 3"]:
498       data = {
499         "mode": constants.REPLACE_DISK_SEC,
500         "disks": disks,
501         "iallocator": "myalloc",
502         }
503
504       op = self.Parse(name, data)
505       self.assert_(isinstance(op, opcodes.OpInstanceReplaceDisks))
506       self.assertEqual(op.mode, constants.REPLACE_DISK_SEC)
507       self.assertEqual(op.disks, [1, 2, 3])
508       self.assertEqual(op.iallocator, "myalloc")
509
510   def testDefaults(self):
511     name = "inst11413"
512     data = {
513       "mode": constants.REPLACE_DISK_AUTO,
514       }
515
516     op = self.Parse(name, data)
517     self.assert_(isinstance(op, opcodes.OpInstanceReplaceDisks))
518     self.assertEqual(op.mode, constants.REPLACE_DISK_AUTO)
519     self.assertFalse(hasattr(op, "iallocator"))
520     self.assertFalse(hasattr(op, "disks"))
521
522   def testWrong(self):
523     self.assertRaises(http.HttpBadRequest, self.Parse, "inst",
524                       { "mode": constants.REPLACE_DISK_AUTO,
525                         "disks": "hello world",
526                       })
527
528
529 class TestParseModifyGroupRequest(unittest.TestCase):
530   def setUp(self):
531     self.Parse = rlib2._ParseModifyGroupRequest
532
533   def test(self):
534     name = "group6002"
535
536     for policy in constants.VALID_ALLOC_POLICIES:
537       data = {
538         "alloc_policy": policy,
539         }
540
541       op = self.Parse(name, data)
542       self.assert_(isinstance(op, opcodes.OpGroupSetParams))
543       self.assertEqual(op.group_name, name)
544       self.assertEqual(op.alloc_policy, policy)
545
546   def testUnknownPolicy(self):
547     data = {
548       "alloc_policy": "_unknown_policy_",
549       }
550
551     self.assertRaises(http.HttpBadRequest, self.Parse, "name", data)
552
553   def testDefaults(self):
554     name = "group6679"
555     data = {}
556
557     op = self.Parse(name, data)
558     self.assert_(isinstance(op, opcodes.OpGroupSetParams))
559     self.assertEqual(op.group_name, name)
560     self.assertFalse(hasattr(op, "alloc_policy"))
561
562
563 class TestParseCreateGroupRequest(unittest.TestCase):
564   def setUp(self):
565     self.Parse = rlib2._ParseCreateGroupRequest
566
567   def test(self):
568     name = "group3618"
569
570     for policy in constants.VALID_ALLOC_POLICIES:
571       data = {
572         "group_name": name,
573         "alloc_policy": policy,
574         }
575
576       op = self.Parse(data, False)
577       self.assert_(isinstance(op, opcodes.OpGroupAdd))
578       self.assertEqual(op.group_name, name)
579       self.assertEqual(op.alloc_policy, policy)
580       self.assertFalse(op.dry_run)
581
582   def testUnknownPolicy(self):
583     data = {
584       "alloc_policy": "_unknown_policy_",
585       }
586
587     self.assertRaises(http.HttpBadRequest, self.Parse, "name", data)
588
589   def testDefaults(self):
590     name = "group15395"
591     data = {
592       "group_name": name,
593       }
594
595     op = self.Parse(data, True)
596     self.assert_(isinstance(op, opcodes.OpGroupAdd))
597     self.assertEqual(op.group_name, name)
598     self.assertFalse(hasattr(op, "alloc_policy"))
599     self.assertTrue(op.dry_run)
600
601   def testLegacyName(self):
602     name = "group29852"
603     data = {
604       "name": name,
605       }
606
607     op = self.Parse(data, True)
608     self.assert_(isinstance(op, opcodes.OpGroupAdd))
609     self.assertEqual(op.group_name, name)
610
611
612 if __name__ == '__main__':
613   testutils.GanetiTestProgram()