Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.cmdlib_unittest.py @ 0ad1ea40

History | View | Annotate | Download (14.3 kB)

1
#!/usr/bin/python
2
#
3

    
4
# Copyright (C) 2008, 2011 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
# 0.0510-1301, USA.
20

    
21

    
22
"""Script for unittesting the cmdlib module"""
23

    
24

    
25
import os
26
import unittest
27
import time
28
import tempfile
29
import shutil
30
import operator
31

    
32
from ganeti import constants
33
from ganeti import mcpu
34
from ganeti import cmdlib
35
from ganeti import opcodes
36
from ganeti import errors
37
from ganeti import utils
38
from ganeti import luxi
39
from ganeti import ht
40
from ganeti import objects
41
from ganeti import compat
42
from ganeti import rpc
43
from ganeti.hypervisor import hv_xen
44

    
45
import testutils
46
import mocks
47

    
48

    
49
class TestCertVerification(testutils.GanetiTestCase):
50
  def setUp(self):
51
    testutils.GanetiTestCase.setUp(self)
52

    
53
    self.tmpdir = tempfile.mkdtemp()
54

    
55
  def tearDown(self):
56
    shutil.rmtree(self.tmpdir)
57

    
58
  def testVerifyCertificate(self):
59
    cmdlib._VerifyCertificate(self._TestDataFilename("cert1.pem"))
60

    
61
    nonexist_filename = os.path.join(self.tmpdir, "does-not-exist")
62

    
63
    (errcode, msg) = cmdlib._VerifyCertificate(nonexist_filename)
64
    self.assertEqual(errcode, cmdlib.LUClusterVerifyConfig.ETYPE_ERROR)
65

    
66
    # Try to load non-certificate file
67
    invalid_cert = self._TestDataFilename("bdev-net.txt")
68
    (errcode, msg) = cmdlib._VerifyCertificate(invalid_cert)
69
    self.assertEqual(errcode, cmdlib.LUClusterVerifyConfig.ETYPE_ERROR)
70

    
71

    
72
class TestOpcodeParams(testutils.GanetiTestCase):
73
  def testParamsStructures(self):
74
    for op in sorted(mcpu.Processor.DISPATCH_TABLE):
75
      lu = mcpu.Processor.DISPATCH_TABLE[op]
76
      lu_name = lu.__name__
77
      self.failIf(hasattr(lu, "_OP_REQP"),
78
                  msg=("LU '%s' has old-style _OP_REQP" % lu_name))
79
      self.failIf(hasattr(lu, "_OP_DEFS"),
80
                  msg=("LU '%s' has old-style _OP_DEFS" % lu_name))
81
      self.failIf(hasattr(lu, "_OP_PARAMS"),
82
                  msg=("LU '%s' has old-style _OP_PARAMS" % lu_name))
83

    
84

    
85
class TestIAllocatorChecks(testutils.GanetiTestCase):
86
  def testFunction(self):
87
    class TestLU(object):
88
      def __init__(self, opcode):
89
        self.cfg = mocks.FakeConfig()
90
        self.op = opcode
91

    
92
    class OpTest(opcodes.OpCode):
93
       OP_PARAMS = [
94
        ("iallocator", None, ht.NoType, None),
95
        ("node", None, ht.NoType, None),
96
        ]
97

    
98
    default_iallocator = mocks.FakeConfig().GetDefaultIAllocator()
99
    other_iallocator = default_iallocator + "_not"
100

    
101
    op = OpTest()
102
    lu = TestLU(op)
103

    
104
    c_i = lambda: cmdlib._CheckIAllocatorOrNode(lu, "iallocator", "node")
105

    
106
    # Neither node nor iallocator given
107
    op.iallocator = None
108
    op.node = None
109
    c_i()
110
    self.assertEqual(lu.op.iallocator, default_iallocator)
111
    self.assertEqual(lu.op.node, None)
112

    
113
    # Both, iallocator and node given
114
    op.iallocator = "test"
115
    op.node = "test"
116
    self.assertRaises(errors.OpPrereqError, c_i)
117

    
118
    # Only iallocator given
119
    op.iallocator = other_iallocator
120
    op.node = None
121
    c_i()
122
    self.assertEqual(lu.op.iallocator, other_iallocator)
123
    self.assertEqual(lu.op.node, None)
124

    
125
    # Only node given
126
    op.iallocator = None
127
    op.node = "node"
128
    c_i()
129
    self.assertEqual(lu.op.iallocator, None)
130
    self.assertEqual(lu.op.node, "node")
131

    
132
    # No node, iallocator or default iallocator
133
    op.iallocator = None
134
    op.node = None
135
    lu.cfg.GetDefaultIAllocator = lambda: None
136
    self.assertRaises(errors.OpPrereqError, c_i)
137

    
138

    
139
class TestLUTestJqueue(unittest.TestCase):
140
  def test(self):
141
    self.assert_(cmdlib.LUTestJqueue._CLIENT_CONNECT_TIMEOUT <
142
                 (luxi.WFJC_TIMEOUT * 0.75),
143
                 msg=("Client timeout too high, might not notice bugs"
144
                      " in WaitForJobChange"))
145

    
146

    
147
class TestLUQuery(unittest.TestCase):
148
  def test(self):
149
    self.assertEqual(sorted(cmdlib._QUERY_IMPL.keys()),
150
                     sorted(constants.QR_VIA_OP))
151

    
152
    assert constants.QR_NODE in constants.QR_VIA_OP
153
    assert constants.QR_INSTANCE in constants.QR_VIA_OP
154

    
155
    for i in constants.QR_VIA_OP:
156
      self.assert_(cmdlib._GetQueryImplementation(i))
157

    
158
    self.assertRaises(errors.OpPrereqError, cmdlib._GetQueryImplementation, "")
159
    self.assertRaises(errors.OpPrereqError, cmdlib._GetQueryImplementation,
160
                      "xyz")
161

    
162

    
163
class TestLUGroupAssignNodes(unittest.TestCase):
164

    
165
  def testCheckAssignmentForSplitInstances(self):
166
    node_data = dict((name, objects.Node(name=name, group=group))
167
                     for (name, group) in [("n1a", "g1"), ("n1b", "g1"),
168
                                           ("n2a", "g2"), ("n2b", "g2"),
169
                                           ("n3a", "g3"), ("n3b", "g3"),
170
                                           ("n3c", "g3"),
171
                                           ])
172

    
173
    def Instance(name, pnode, snode):
174
      if snode is None:
175
        disks = []
176
        disk_template = constants.DT_DISKLESS
177
      else:
178
        disks = [objects.Disk(dev_type=constants.LD_DRBD8,
179
                              logical_id=[pnode, snode, 1, 17, 17])]
180
        disk_template = constants.DT_DRBD8
181

    
182
      return objects.Instance(name=name, primary_node=pnode, disks=disks,
183
                              disk_template=disk_template)
184

    
185
    instance_data = dict((name, Instance(name, pnode, snode))
186
                         for name, pnode, snode in [("inst1a", "n1a", "n1b"),
187
                                                    ("inst1b", "n1b", "n1a"),
188
                                                    ("inst2a", "n2a", "n2b"),
189
                                                    ("inst3a", "n3a", None),
190
                                                    ("inst3b", "n3b", "n1b"),
191
                                                    ("inst3c", "n3b", "n2b"),
192
                                                    ])
193

    
194
    # Test first with the existing state.
195
    (new, prev) = \
196
      cmdlib.LUGroupAssignNodes.CheckAssignmentForSplitInstances([],
197
                                                                 node_data,
198
                                                                 instance_data)
199

    
200
    self.assertEqual([], new)
201
    self.assertEqual(set(["inst3b", "inst3c"]), set(prev))
202

    
203
    # And now some changes.
204
    (new, prev) = \
205
      cmdlib.LUGroupAssignNodes.CheckAssignmentForSplitInstances([("n1b",
206
                                                                   "g3")],
207
                                                                 node_data,
208
                                                                 instance_data)
209

    
210
    self.assertEqual(set(["inst1a", "inst1b"]), set(new))
211
    self.assertEqual(set(["inst3c"]), set(prev))
212

    
213

    
214
class TestClusterVerifySsh(unittest.TestCase):
215
  def testMultipleGroups(self):
216
    fn = cmdlib.LUClusterVerifyGroup._SelectSshCheckNodes
217
    mygroupnodes = [
218
      objects.Node(name="node20", group="my", offline=False),
219
      objects.Node(name="node21", group="my", offline=False),
220
      objects.Node(name="node22", group="my", offline=False),
221
      objects.Node(name="node23", group="my", offline=False),
222
      objects.Node(name="node24", group="my", offline=False),
223
      objects.Node(name="node25", group="my", offline=False),
224
      objects.Node(name="node26", group="my", offline=True),
225
      ]
226
    nodes = [
227
      objects.Node(name="node1", group="g1", offline=True),
228
      objects.Node(name="node2", group="g1", offline=False),
229
      objects.Node(name="node3", group="g1", offline=False),
230
      objects.Node(name="node4", group="g1", offline=True),
231
      objects.Node(name="node5", group="g1", offline=False),
232
      objects.Node(name="node10", group="xyz", offline=False),
233
      objects.Node(name="node11", group="xyz", offline=False),
234
      objects.Node(name="node40", group="alloff", offline=True),
235
      objects.Node(name="node41", group="alloff", offline=True),
236
      objects.Node(name="node50", group="aaa", offline=False),
237
      ] + mygroupnodes
238
    assert not utils.FindDuplicates(map(operator.attrgetter("name"), nodes))
239

    
240
    (online, perhost) = fn(mygroupnodes, "my", nodes)
241
    self.assertEqual(online, ["node%s" % i for i in range(20, 26)])
242
    self.assertEqual(set(perhost.keys()), set(online))
243

    
244
    self.assertEqual(perhost, {
245
      "node20": ["node10", "node2", "node50"],
246
      "node21": ["node11", "node3", "node50"],
247
      "node22": ["node10", "node5", "node50"],
248
      "node23": ["node11", "node2", "node50"],
249
      "node24": ["node10", "node3", "node50"],
250
      "node25": ["node11", "node5", "node50"],
251
      })
252

    
253
  def testSingleGroup(self):
254
    fn = cmdlib.LUClusterVerifyGroup._SelectSshCheckNodes
255
    nodes = [
256
      objects.Node(name="node1", group="default", offline=True),
257
      objects.Node(name="node2", group="default", offline=False),
258
      objects.Node(name="node3", group="default", offline=False),
259
      objects.Node(name="node4", group="default", offline=True),
260
      ]
261
    assert not utils.FindDuplicates(map(operator.attrgetter("name"), nodes))
262

    
263
    (online, perhost) = fn(nodes, "default", nodes)
264
    self.assertEqual(online, ["node2", "node3"])
265
    self.assertEqual(set(perhost.keys()), set(online))
266

    
267
    self.assertEqual(perhost, {
268
      "node2": [],
269
      "node3": [],
270
      })
271

    
272

    
273
class TestClusterVerifyFiles(unittest.TestCase):
274
  @staticmethod
275
  def _FakeErrorIf(errors, cond, ecode, item, msg, *args, **kwargs):
276
    assert ((ecode == cmdlib.LUClusterVerifyGroup.ENODEFILECHECK and
277
             ht.TNonEmptyString(item)) or
278
            (ecode == cmdlib.LUClusterVerifyGroup.ECLUSTERFILECHECK and
279
             item is None))
280

    
281
    if args:
282
      msg = msg % args
283

    
284
    if cond:
285
      errors.append((item, msg))
286

    
287
  _VerifyFiles = cmdlib.LUClusterVerifyGroup._VerifyFiles
288

    
289
  def test(self):
290
    errors = []
291
    master_name = "master.example.com"
292
    nodeinfo = [
293
      objects.Node(name=master_name, offline=False, vm_capable=True),
294
      objects.Node(name="node2.example.com", offline=False, vm_capable=True),
295
      objects.Node(name="node3.example.com", master_candidate=True,
296
                   vm_capable=False),
297
      objects.Node(name="node4.example.com", offline=False, vm_capable=True),
298
      objects.Node(name="nodata.example.com", offline=False, vm_capable=True),
299
      objects.Node(name="offline.example.com", offline=True),
300
      ]
301
    cluster = objects.Cluster(modify_etc_hosts=True,
302
                              enabled_hypervisors=[constants.HT_XEN_HVM])
303
    files_all = set([
304
      constants.CLUSTER_DOMAIN_SECRET_FILE,
305
      constants.RAPI_CERT_FILE,
306
      constants.RAPI_USERS_FILE,
307
      ])
308
    files_opt = set([
309
      constants.RAPI_USERS_FILE,
310
      hv_xen.XL_CONFIG_FILE,
311
      constants.VNC_PASSWORD_FILE,
312
      ])
313
    files_mc = set([
314
      constants.CLUSTER_CONF_FILE,
315
      ])
316
    files_vm = set([
317
      hv_xen.XEND_CONFIG_FILE,
318
      hv_xen.XL_CONFIG_FILE,
319
      constants.VNC_PASSWORD_FILE,
320
      ])
321
    nvinfo = {
322
      master_name: rpc.RpcResult(data=(True, {
323
        constants.NV_FILELIST: {
324
          constants.CLUSTER_CONF_FILE: "82314f897f38b35f9dab2f7c6b1593e0",
325
          constants.RAPI_CERT_FILE: "babbce8f387bc082228e544a2146fee4",
326
          constants.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
327
          hv_xen.XEND_CONFIG_FILE: "b4a8a824ab3cac3d88839a9adeadf310",
328
          hv_xen.XL_CONFIG_FILE: "77935cee92afd26d162f9e525e3d49b9"
329
        }})),
330
      "node2.example.com": rpc.RpcResult(data=(True, {
331
        constants.NV_FILELIST: {
332
          constants.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
333
          hv_xen.XEND_CONFIG_FILE: "b4a8a824ab3cac3d88839a9adeadf310",
334
          }
335
        })),
336
      "node3.example.com": rpc.RpcResult(data=(True, {
337
        constants.NV_FILELIST: {
338
          constants.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
339
          constants.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
340
          }
341
        })),
342
      "node4.example.com": rpc.RpcResult(data=(True, {
343
        constants.NV_FILELIST: {
344
          constants.RAPI_CERT_FILE: "97f0356500e866387f4b84233848cc4a",
345
          constants.CLUSTER_CONF_FILE: "conf-a6d4b13e407867f7a7b4f0f232a8f527",
346
          constants.CLUSTER_DOMAIN_SECRET_FILE: "cds-47b5b3f19202936bb4",
347
          constants.RAPI_USERS_FILE: "rapiusers-ea3271e8d810ef3",
348
          hv_xen.XL_CONFIG_FILE: "77935cee92afd26d162f9e525e3d49b9"
349
          }
350
        })),
351
      "nodata.example.com": rpc.RpcResult(data=(True, {})),
352
      "offline.example.com": rpc.RpcResult(offline=True),
353
      }
354
    assert set(nvinfo.keys()) == set(map(operator.attrgetter("name"), nodeinfo))
355

    
356
    self._VerifyFiles(compat.partial(self._FakeErrorIf, errors), nodeinfo,
357
                      master_name, nvinfo,
358
                      (files_all, files_opt, files_mc, files_vm))
359
    self.assertEqual(sorted(errors), sorted([
360
      (None, ("File %s found with 2 different checksums (variant 1 on"
361
              " node2.example.com, node3.example.com, node4.example.com;"
362
              " variant 2 on master.example.com)" % constants.RAPI_CERT_FILE)),
363
      (None, ("File %s is missing from node(s) node2.example.com" %
364
              constants.CLUSTER_DOMAIN_SECRET_FILE)),
365
      (None, ("File %s should not exist on node(s) node4.example.com" %
366
              constants.CLUSTER_CONF_FILE)),
367
      (None, ("File %s is missing from node(s) node4.example.com" %
368
              hv_xen.XEND_CONFIG_FILE)),
369
      (None, ("File %s is missing from node(s) node3.example.com" %
370
              constants.CLUSTER_CONF_FILE)),
371
      (None, ("File %s found with 2 different checksums (variant 1 on"
372
              " master.example.com; variant 2 on node4.example.com)" %
373
              constants.CLUSTER_CONF_FILE)),
374
      (None, ("File %s is optional, but it must exist on all or no nodes (not"
375
              " found on master.example.com, node2.example.com,"
376
              " node3.example.com)" % constants.RAPI_USERS_FILE)),
377
      (None, ("File %s is optional, but it must exist on all or no nodes (not"
378
              " found on node2.example.com)" % hv_xen.XL_CONFIG_FILE)),
379
      ("nodata.example.com", "Node did not return file checksum data"),
380
      ]))
381

    
382

    
383
if __name__ == "__main__":
384
  testutils.GanetiTestProgram()