Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.cmdlib_unittest.py @ eedf99b5

History | View | Annotate | Download (13.4 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

    
44
import testutils
45
import mocks
46

    
47

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

    
52
    self.tmpdir = tempfile.mkdtemp()
53

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

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

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

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

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

    
70

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

    
83

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

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

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

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

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

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

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

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

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

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

    
137

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

    
145

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

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

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

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

    
161

    
162
class TestLUGroupAssignNodes(unittest.TestCase):
163

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

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

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

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

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

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

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

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

    
212

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

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

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

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

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

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

    
271

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

    
280
    if args:
281
      msg = msg % args
282

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

    
286
  _VerifyFiles = cmdlib.LUClusterVerifyGroup._VerifyFiles
287

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

    
343
    self._VerifyFiles(compat.partial(self._FakeErrorIf, errors), nodeinfo,
344
                      master_name, nvinfo,
345
                      (files_all, files_all_opt, files_mc, files_vm))
346
    self.assertEqual(sorted(errors), sorted([
347
      (None, ("File %s found with 2 different checksums (variant 1 on"
348
              " node2.example.com, node3.example.com, node4.example.com;"
349
              " variant 2 on master.example.com)" % constants.RAPI_CERT_FILE)),
350
      (None, ("File %s is missing from node(s) node2.example.com" %
351
              constants.CLUSTER_DOMAIN_SECRET_FILE)),
352
      (None, ("File %s should not exist on node(s) node4.example.com" %
353
              constants.CLUSTER_CONF_FILE)),
354
      (None, ("File %s is missing from node(s) node3.example.com" %
355
              constants.CLUSTER_CONF_FILE)),
356
      (None, ("File %s found with 2 different checksums (variant 1 on"
357
              " master.example.com; variant 2 on node4.example.com)" %
358
              constants.CLUSTER_CONF_FILE)),
359
      (None, ("File %s is optional, but it must exist on all or no nodes (not"
360
              " found on master.example.com, node2.example.com,"
361
              " node3.example.com)" % constants.RAPI_USERS_FILE)),
362
      ("nodata.example.com", "Node did not return file checksum data"),
363
      ]))
364

    
365

    
366
if __name__ == "__main__":
367
  testutils.GanetiTestProgram()