Statistics
| Branch: | Tag: | Revision:

root / test / py / ganeti.hooks_unittest.py @ 4869595d

History | View | Annotate | Download (18.5 kB)

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

    
4
# Copyright (C) 2006, 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
"""Script for unittesting the hooks module"""
23

    
24

    
25
import unittest
26
import os
27
import time
28
import tempfile
29
import os.path
30

    
31
from ganeti import errors
32
from ganeti import opcodes
33
from ganeti import hooksmaster
34
from ganeti import backend
35
from ganeti import constants
36
from ganeti import cmdlib
37
from ganeti.rpc import node as rpc
38
from ganeti import compat
39
from ganeti import pathutils
40
from ganeti.constants import HKR_SUCCESS, HKR_FAIL, HKR_SKIP
41

    
42
from mocks import FakeConfig, FakeProc, FakeContext
43

    
44
import testutils
45

    
46

    
47
class FakeLU(cmdlib.LogicalUnit):
48
  HPATH = "test"
49

    
50
  def BuildHooksEnv(self):
51
    return {}
52

    
53
  def BuildHooksNodes(self):
54
    return ["a"], ["a"]
55

    
56

    
57
class TestHooksRunner(unittest.TestCase):
58
  """Testing case for HooksRunner"""
59
  def setUp(self):
60
    self.torm = []
61
    self.tmpdir = tempfile.mkdtemp()
62
    self.torm.append((self.tmpdir, True))
63
    self.logdir = tempfile.mkdtemp()
64
    self.torm.append((self.logdir, True))
65
    self.hpath = "fake"
66
    self.ph_dirs = {}
67
    for i in (constants.HOOKS_PHASE_PRE, constants.HOOKS_PHASE_POST):
68
      dname = "%s/%s-%s.d" % (self.tmpdir, self.hpath, i)
69
      os.mkdir(dname)
70
      self.torm.append((dname, True))
71
      self.ph_dirs[i] = dname
72
    self.hr = backend.HooksRunner(hooks_base_dir=self.tmpdir)
73

    
74
  def tearDown(self):
75
    self.torm.reverse()
76
    for path, kind in self.torm:
77
      if kind:
78
        os.rmdir(path)
79
      else:
80
        os.unlink(path)
81

    
82
  def _rname(self, fname):
83
    return "/".join(fname.split("/")[-2:])
84

    
85
  def testEmpty(self):
86
    """Test no hooks"""
87
    for phase in (constants.HOOKS_PHASE_PRE, constants.HOOKS_PHASE_POST):
88
      self.failUnlessEqual(self.hr.RunHooks(self.hpath, phase, {}), [])
89

    
90
  def testSkipNonExec(self):
91
    """Test skip non-exec file"""
92
    for phase in (constants.HOOKS_PHASE_PRE, constants.HOOKS_PHASE_POST):
93
      fname = "%s/test" % self.ph_dirs[phase]
94
      f = open(fname, "w")
95
      f.close()
96
      self.torm.append((fname, False))
97
      self.failUnlessEqual(self.hr.RunHooks(self.hpath, phase, {}),
98
                           [(self._rname(fname), HKR_SKIP, "")])
99

    
100
  def testSkipInvalidName(self):
101
    """Test skip script with invalid name"""
102
    for phase in (constants.HOOKS_PHASE_PRE, constants.HOOKS_PHASE_POST):
103
      fname = "%s/a.off" % self.ph_dirs[phase]
104
      f = open(fname, "w")
105
      f.write("#!/bin/sh\nexit 0\n")
106
      f.close()
107
      os.chmod(fname, 0700)
108
      self.torm.append((fname, False))
109
      self.failUnlessEqual(self.hr.RunHooks(self.hpath, phase, {}),
110
                           [(self._rname(fname), HKR_SKIP, "")])
111

    
112
  def testSkipDir(self):
113
    """Test skip directory"""
114
    for phase in (constants.HOOKS_PHASE_PRE, constants.HOOKS_PHASE_POST):
115
      fname = "%s/testdir" % self.ph_dirs[phase]
116
      os.mkdir(fname)
117
      self.torm.append((fname, True))
118
      self.failUnlessEqual(self.hr.RunHooks(self.hpath, phase, {}),
119
                           [(self._rname(fname), HKR_SKIP, "")])
120

    
121
  def testSuccess(self):
122
    """Test success execution"""
123
    for phase in (constants.HOOKS_PHASE_PRE, constants.HOOKS_PHASE_POST):
124
      fname = "%s/success" % self.ph_dirs[phase]
125
      f = open(fname, "w")
126
      f.write("#!/bin/sh\nexit 0\n")
127
      f.close()
128
      self.torm.append((fname, False))
129
      os.chmod(fname, 0700)
130
      self.failUnlessEqual(self.hr.RunHooks(self.hpath, phase, {}),
131
                           [(self._rname(fname), HKR_SUCCESS, "")])
132

    
133
  def testSymlink(self):
134
    """Test running a symlink"""
135
    for phase in (constants.HOOKS_PHASE_PRE, constants.HOOKS_PHASE_POST):
136
      fname = "%s/success" % self.ph_dirs[phase]
137
      os.symlink("/bin/true", fname)
138
      self.torm.append((fname, False))
139
      self.failUnlessEqual(self.hr.RunHooks(self.hpath, phase, {}),
140
                           [(self._rname(fname), HKR_SUCCESS, "")])
141

    
142
  def testFail(self):
143
    """Test success execution"""
144
    for phase in (constants.HOOKS_PHASE_PRE, constants.HOOKS_PHASE_POST):
145
      fname = "%s/success" % self.ph_dirs[phase]
146
      f = open(fname, "w")
147
      f.write("#!/bin/sh\nexit 1\n")
148
      f.close()
149
      self.torm.append((fname, False))
150
      os.chmod(fname, 0700)
151
      self.failUnlessEqual(self.hr.RunHooks(self.hpath, phase, {}),
152
                           [(self._rname(fname), HKR_FAIL, "")])
153

    
154
  def testCombined(self):
155
    """Test success, failure and skip all in one test"""
156
    for phase in (constants.HOOKS_PHASE_PRE, constants.HOOKS_PHASE_POST):
157
      expect = []
158
      for fbase, ecode, rs in [("00succ", 0, HKR_SUCCESS),
159
                               ("10fail", 1, HKR_FAIL),
160
                               ("20inv.", 0, HKR_SKIP),
161
                               ]:
162
        fname = "%s/%s" % (self.ph_dirs[phase], fbase)
163
        f = open(fname, "w")
164
        f.write("#!/bin/sh\nexit %d\n" % ecode)
165
        f.close()
166
        self.torm.append((fname, False))
167
        os.chmod(fname, 0700)
168
        expect.append((self._rname(fname), rs, ""))
169
      self.failUnlessEqual(self.hr.RunHooks(self.hpath, phase, {}), expect)
170

    
171
  def testOrdering(self):
172
    for phase in (constants.HOOKS_PHASE_PRE, constants.HOOKS_PHASE_POST):
173
      expect = []
174
      for fbase in ["10s1",
175
                    "00s0",
176
                    "10sa",
177
                    "80sc",
178
                    "60sd",
179
                    ]:
180
        fname = "%s/%s" % (self.ph_dirs[phase], fbase)
181
        os.symlink("/bin/true", fname)
182
        self.torm.append((fname, False))
183
        expect.append((self._rname(fname), HKR_SUCCESS, ""))
184
      expect.sort()
185
      self.failUnlessEqual(self.hr.RunHooks(self.hpath, phase, {}), expect)
186

    
187
  def testEnv(self):
188
    """Test environment execution"""
189
    for phase in (constants.HOOKS_PHASE_PRE, constants.HOOKS_PHASE_POST):
190
      fbase = "success"
191
      fname = "%s/%s" % (self.ph_dirs[phase], fbase)
192
      os.symlink("/usr/bin/env", fname)
193
      self.torm.append((fname, False))
194
      env_snt = {"PHASE": phase}
195
      env_exp = "PHASE=%s" % phase
196
      self.failUnlessEqual(self.hr.RunHooks(self.hpath, phase, env_snt),
197
                           [(self._rname(fname), HKR_SUCCESS, env_exp)])
198

    
199

    
200
def FakeHooksRpcSuccess(node_list, hpath, phase, env):
201
  """Fake call_hooks_runner function.
202

203
  @rtype: dict of node -> L{rpc.RpcResult} with a successful script result
204
  @return: script execution from all nodes
205

206
  """
207
  rr = rpc.RpcResult
208
  return dict([(node, rr((True, [("utest", constants.HKR_SUCCESS, "ok")]),
209
                         node=node, call="FakeScriptOk"))
210
               for node in node_list])
211

    
212

    
213
class TestHooksMaster(unittest.TestCase):
214
  """Testing case for HooksMaster"""
215

    
216
  def _call_false(*args):
217
    """Fake call_hooks_runner function which returns False."""
218
    return False
219

    
220
  @staticmethod
221
  def _call_nodes_false(node_list, hpath, phase, env):
222
    """Fake call_hooks_runner function.
223

224
    @rtype: dict of node -> L{rpc.RpcResult} with an rpc error
225
    @return: rpc failure from all nodes
226

227
    """
228
    return dict([(node, rpc.RpcResult("error", failed=True,
229
                  node=node, call="FakeError")) for node in node_list])
230

    
231
  @staticmethod
232
  def _call_script_fail(node_list, hpath, phase, env):
233
    """Fake call_hooks_runner function.
234

235
    @rtype: dict of node -> L{rpc.RpcResult} with a failed script result
236
    @return: script execution failure from all nodes
237

238
    """
239
    rr = rpc.RpcResult
240
    return dict([(node, rr((True, [("utest", constants.HKR_FAIL, "err")]),
241
                           node=node, call="FakeScriptFail"))
242
                  for node in node_list])
243

    
244
  def setUp(self):
245
    self.op = opcodes.OpCode()
246
    self.context = FakeContext()
247
    # WARNING: here we pass None as RpcRunner instance since we know
248
    # our usage via HooksMaster will not use lu.rpc
249
    self.lu = FakeLU(FakeProc(), self.op, self.context, None)
250

    
251
  def testTotalFalse(self):
252
    """Test complete rpc failure"""
253
    hm = hooksmaster.HooksMaster.BuildFromLu(self._call_false, self.lu)
254
    self.failUnlessRaises(errors.HooksFailure,
255
                          hm.RunPhase, constants.HOOKS_PHASE_PRE)
256
    hm.RunPhase(constants.HOOKS_PHASE_POST)
257

    
258
  def testIndividualFalse(self):
259
    """Test individual node failure"""
260
    hm = hooksmaster.HooksMaster.BuildFromLu(self._call_nodes_false, self.lu)
261
    hm.RunPhase(constants.HOOKS_PHASE_PRE)
262
    #self.failUnlessRaises(errors.HooksFailure,
263
    #                      hm.RunPhase, constants.HOOKS_PHASE_PRE)
264
    hm.RunPhase(constants.HOOKS_PHASE_POST)
265

    
266
  def testScriptFalse(self):
267
    """Test individual rpc failure"""
268
    hm = hooksmaster.HooksMaster.BuildFromLu(self._call_script_fail, self.lu)
269
    self.failUnlessRaises(errors.HooksAbort,
270
                          hm.RunPhase, constants.HOOKS_PHASE_PRE)
271
    hm.RunPhase(constants.HOOKS_PHASE_POST)
272

    
273
  def testScriptSucceed(self):
274
    """Test individual rpc failure"""
275
    hm = hooksmaster.HooksMaster.BuildFromLu(FakeHooksRpcSuccess, self.lu)
276
    for phase in (constants.HOOKS_PHASE_PRE, constants.HOOKS_PHASE_POST):
277
      hm.RunPhase(phase)
278

    
279

    
280
class FakeEnvLU(cmdlib.LogicalUnit):
281
  HPATH = "env_test_lu"
282
  HTYPE = constants.HTYPE_GROUP
283

    
284
  def __init__(self, *args):
285
    cmdlib.LogicalUnit.__init__(self, *args)
286
    self.hook_env = None
287

    
288
  def BuildHooksEnv(self):
289
    assert self.hook_env is not None
290
    return self.hook_env
291

    
292
  def BuildHooksNodes(self):
293
    return (["a"], ["a"])
294

    
295

    
296
class FakeNoHooksLU(cmdlib.NoHooksLU):
297
  pass
298

    
299

    
300
class TestHooksRunnerEnv(unittest.TestCase):
301
  def setUp(self):
302
    self._rpcs = []
303

    
304
    self.op = opcodes.OpTestDummy(result=False, messages=[], fail=False)
305
    self.lu = FakeEnvLU(FakeProc(), self.op, FakeContext(), None)
306

    
307
  def _HooksRpc(self, *args):
308
    self._rpcs.append(args)
309
    return FakeHooksRpcSuccess(*args)
310

    
311
  def _CheckEnv(self, env, phase, hpath):
312
    self.assertTrue(env["PATH"].startswith("/sbin"))
313
    self.assertEqual(env["GANETI_HOOKS_PHASE"], phase)
314
    self.assertEqual(env["GANETI_HOOKS_PATH"], hpath)
315
    self.assertEqual(env["GANETI_OP_CODE"], self.op.OP_ID)
316
    self.assertEqual(env["GANETI_HOOKS_VERSION"], str(constants.HOOKS_VERSION))
317
    self.assertEqual(env["GANETI_DATA_DIR"], pathutils.DATA_DIR)
318
    if "GANETI_OBJECT_TYPE" in env:
319
      self.assertEqual(env["GANETI_OBJECT_TYPE"], constants.HTYPE_GROUP)
320
    else:
321
      self.assertTrue(self.lu.HTYPE is None)
322

    
323
  def testEmptyEnv(self):
324
    # Check pre-phase hook
325
    self.lu.hook_env = {}
326
    hm = hooksmaster.HooksMaster.BuildFromLu(self._HooksRpc, self.lu)
327
    hm.RunPhase(constants.HOOKS_PHASE_PRE)
328

    
329
    (node_list, hpath, phase, env) = self._rpcs.pop(0)
330
    self.assertEqual(node_list, set(["node_a.example.com"]))
331
    self.assertEqual(hpath, self.lu.HPATH)
332
    self.assertEqual(phase, constants.HOOKS_PHASE_PRE)
333
    self._CheckEnv(env, constants.HOOKS_PHASE_PRE, self.lu.HPATH)
334

    
335
    # Check post-phase hook
336
    self.lu.hook_env = {}
337
    hm.RunPhase(constants.HOOKS_PHASE_POST)
338

    
339
    (node_list, hpath, phase, env) = self._rpcs.pop(0)
340
    self.assertEqual(node_list, set(["node_a.example.com"]))
341
    self.assertEqual(hpath, self.lu.HPATH)
342
    self.assertEqual(phase, constants.HOOKS_PHASE_POST)
343
    self._CheckEnv(env, constants.HOOKS_PHASE_POST, self.lu.HPATH)
344

    
345
    self.assertRaises(IndexError, self._rpcs.pop)
346

    
347
  def testEnv(self):
348
    # Check pre-phase hook
349
    self.lu.hook_env = {
350
      "FOO": "pre-foo-value",
351
      }
352
    hm = hooksmaster.HooksMaster.BuildFromLu(self._HooksRpc, self.lu)
353
    hm.RunPhase(constants.HOOKS_PHASE_PRE)
354

    
355
    (node_list, hpath, phase, env) = self._rpcs.pop(0)
356
    self.assertEqual(node_list, set(["node_a.example.com"]))
357
    self.assertEqual(hpath, self.lu.HPATH)
358
    self.assertEqual(phase, constants.HOOKS_PHASE_PRE)
359
    self.assertEqual(env["GANETI_FOO"], "pre-foo-value")
360
    self.assertFalse(compat.any(key.startswith("GANETI_POST") for key in env))
361
    self._CheckEnv(env, constants.HOOKS_PHASE_PRE, self.lu.HPATH)
362

    
363
    # Check post-phase hook
364
    self.lu.hook_env = {
365
      "FOO": "post-value",
366
      "BAR": 123,
367
      }
368
    hm.RunPhase(constants.HOOKS_PHASE_POST)
369

    
370
    (node_list, hpath, phase, env) = self._rpcs.pop(0)
371
    self.assertEqual(node_list, set(["node_a.example.com"]))
372
    self.assertEqual(hpath, self.lu.HPATH)
373
    self.assertEqual(phase, constants.HOOKS_PHASE_POST)
374
    self.assertEqual(env["GANETI_FOO"], "pre-foo-value")
375
    self.assertEqual(env["GANETI_POST_FOO"], "post-value")
376
    self.assertEqual(env["GANETI_POST_BAR"], "123")
377
    self.assertFalse("GANETI_BAR" in env)
378
    self._CheckEnv(env, constants.HOOKS_PHASE_POST, self.lu.HPATH)
379

    
380
    self.assertRaises(IndexError, self._rpcs.pop)
381

    
382
    # Check configuration update hook
383
    hm.RunConfigUpdate()
384
    (node_list, hpath, phase, env) = self._rpcs.pop(0)
385
    self.assertEqual(set(node_list), set([self.lu.cfg.GetMasterNodeName()]))
386
    self.assertEqual(hpath, constants.HOOKS_NAME_CFGUPDATE)
387
    self.assertEqual(phase, constants.HOOKS_PHASE_POST)
388
    self._CheckEnv(env, constants.HOOKS_PHASE_POST,
389
                   constants.HOOKS_NAME_CFGUPDATE)
390
    self.assertFalse(compat.any(key.startswith("GANETI_POST") for key in env))
391
    self.assertEqual(env["GANETI_FOO"], "pre-foo-value")
392
    self.assertRaises(IndexError, self._rpcs.pop)
393

    
394
  def testConflict(self):
395
    for name in ["DATA_DIR", "OP_CODE"]:
396
      self.lu.hook_env = { name: "value" }
397

    
398
      # Test using a clean HooksMaster instance
399
      hm = hooksmaster.HooksMaster.BuildFromLu(self._HooksRpc, self.lu)
400

    
401
      for phase in [constants.HOOKS_PHASE_PRE, constants.HOOKS_PHASE_POST]:
402
        self.assertRaises(AssertionError, hm.RunPhase, phase)
403
        self.assertRaises(IndexError, self._rpcs.pop)
404

    
405
  def testNoNodes(self):
406
    self.lu.hook_env = {}
407
    hm = hooksmaster.HooksMaster.BuildFromLu(self._HooksRpc, self.lu)
408
    hm.RunPhase(constants.HOOKS_PHASE_PRE, node_names=[])
409
    self.assertRaises(IndexError, self._rpcs.pop)
410

    
411
  def testSpecificNodes(self):
412
    self.lu.hook_env = {}
413

    
414
    nodes = [
415
      "node1.example.com",
416
      "node93782.example.net",
417
      ]
418

    
419
    hm = hooksmaster.HooksMaster.BuildFromLu(self._HooksRpc, self.lu)
420

    
421
    for phase in [constants.HOOKS_PHASE_PRE, constants.HOOKS_PHASE_POST]:
422
      hm.RunPhase(phase, node_names=nodes)
423

    
424
      (node_list, hpath, rpc_phase, env) = self._rpcs.pop(0)
425
      self.assertEqual(set(node_list), set(nodes))
426
      self.assertEqual(hpath, self.lu.HPATH)
427
      self.assertEqual(rpc_phase, phase)
428
      self._CheckEnv(env, phase, self.lu.HPATH)
429

    
430
      self.assertRaises(IndexError, self._rpcs.pop)
431

    
432
  def testRunConfigUpdateNoPre(self):
433
    self.lu.hook_env = {
434
      "FOO": "value",
435
      }
436

    
437
    hm = hooksmaster.HooksMaster.BuildFromLu(self._HooksRpc, self.lu)
438
    hm.RunConfigUpdate()
439

    
440
    (node_list, hpath, phase, env) = self._rpcs.pop(0)
441
    self.assertEqual(set(node_list), set([self.lu.cfg.GetMasterNodeName()]))
442
    self.assertEqual(hpath, constants.HOOKS_NAME_CFGUPDATE)
443
    self.assertEqual(phase, constants.HOOKS_PHASE_POST)
444
    self.assertEqual(env["GANETI_FOO"], "value")
445
    self.assertFalse(compat.any(key.startswith("GANETI_POST") for key in env))
446
    self._CheckEnv(env, constants.HOOKS_PHASE_POST,
447
                   constants.HOOKS_NAME_CFGUPDATE)
448

    
449
    self.assertRaises(IndexError, self._rpcs.pop)
450

    
451
  def testNoPreBeforePost(self):
452
    self.lu.hook_env = {
453
      "FOO": "value",
454
      }
455

    
456
    hm = hooksmaster.HooksMaster.BuildFromLu(self._HooksRpc, self.lu)
457
    hm.RunPhase(constants.HOOKS_PHASE_POST)
458

    
459
    (node_list, hpath, phase, env) = self._rpcs.pop(0)
460
    self.assertEqual(node_list, set(["node_a.example.com"]))
461
    self.assertEqual(hpath, self.lu.HPATH)
462
    self.assertEqual(phase, constants.HOOKS_PHASE_POST)
463
    self.assertEqual(env["GANETI_FOO"], "value")
464
    self.assertEqual(env["GANETI_POST_FOO"], "value")
465
    self._CheckEnv(env, constants.HOOKS_PHASE_POST, self.lu.HPATH)
466

    
467
    self.assertRaises(IndexError, self._rpcs.pop)
468

    
469
  def testNoHooksLU(self):
470
    self.lu = FakeNoHooksLU(FakeProc(), self.op, FakeContext(), None)
471
    self.assertRaises(AssertionError, self.lu.BuildHooksEnv)
472
    self.assertRaises(AssertionError, self.lu.BuildHooksNodes)
473

    
474
    hm = hooksmaster.HooksMaster.BuildFromLu(self._HooksRpc, self.lu)
475
    self.assertEqual(hm.pre_env, {})
476
    self.assertRaises(IndexError, self._rpcs.pop)
477

    
478
    hm.RunPhase(constants.HOOKS_PHASE_PRE)
479
    self.assertRaises(IndexError, self._rpcs.pop)
480

    
481
    hm.RunPhase(constants.HOOKS_PHASE_POST)
482
    self.assertRaises(IndexError, self._rpcs.pop)
483

    
484
    hm.RunConfigUpdate()
485

    
486
    (node_list, hpath, phase, env) = self._rpcs.pop(0)
487
    self.assertEqual(set(node_list), set([self.lu.cfg.GetMasterNodeName()]))
488
    self.assertEqual(hpath, constants.HOOKS_NAME_CFGUPDATE)
489
    self.assertEqual(phase, constants.HOOKS_PHASE_POST)
490
    self.assertFalse(compat.any(key.startswith("GANETI_POST") for key in env))
491
    self._CheckEnv(env, constants.HOOKS_PHASE_POST,
492
                   constants.HOOKS_NAME_CFGUPDATE)
493
    self.assertRaises(IndexError, self._rpcs.pop)
494

    
495
    assert isinstance(self.lu, FakeNoHooksLU), "LU was replaced"
496

    
497

    
498
class FakeEnvWithNodeNameLU(cmdlib.LogicalUnit):
499
  HPATH = "env_test_lu"
500
  HTYPE = constants.HTYPE_GROUP
501

    
502
  def __init__(self, *args):
503
    cmdlib.LogicalUnit.__init__(self, *args)
504

    
505
  def BuildHooksEnv(self):
506
    return {}
507

    
508
  def BuildHooksNodes(self):
509
    return (["a"], ["a"], ["explicit.node1.com", "explicit.node2.com"])
510

    
511

    
512
class TestHooksRunnerEnv(unittest.TestCase):
513
  def setUp(self):
514
    self._rpcs = []
515

    
516
    self.op = opcodes.OpTestDummy(result=False, messages=[], fail=False)
517
    self.lu = FakeEnvWithNodeNameLU(FakeProc(), self.op, FakeContext(), None)
518

    
519
  def _HooksRpc(self, *args):
520
    self._rpcs.append(args)
521
    return FakeHooksRpcSuccess(*args)
522

    
523
  def testEmptyEnv(self):
524
    # Check pre-phase hook
525
    hm = hooksmaster.HooksMaster.BuildFromLu(self._HooksRpc, self.lu)
526
    hm.RunPhase(constants.HOOKS_PHASE_PRE)
527

    
528
    (node_list, hpath, phase, env) = self._rpcs.pop(0)
529
    self.assertEqual(node_list, set(["node_a.example.com"]))
530

    
531
    # Check post-phase hook
532
    hm.RunPhase(constants.HOOKS_PHASE_POST)
533

    
534
    (node_list, hpath, phase, env) = self._rpcs.pop(0)
535
    self.assertEqual(node_list, set(["node_a.example.com",
536
                                     "explicit.node1.com",
537
                                     "explicit.node2.com"]))
538

    
539
    self.assertRaises(IndexError, self._rpcs.pop)
540

    
541

    
542
if __name__ == "__main__":
543
  testutils.GanetiTestProgram()