Statistics
| Branch: | Tag: | Revision:

root / test / ganeti.rapi.client_unittest.py @ ebeb600f

History | View | Annotate | Download (19.8 kB)

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 client module"""
23

    
24

    
25
import re
26
import unittest
27
import warnings
28

    
29
from ganeti import http
30
from ganeti import serializer
31

    
32
from ganeti.rapi import connector
33
from ganeti.rapi import rlib2
34
from ganeti.rapi import client
35

    
36
import testutils
37

    
38

    
39
_URI_RE = re.compile(r"https://(?P<host>.*):(?P<port>\d+)(?P<path>/.*)")
40

    
41

    
42
def _GetPathFromUri(uri):
43
  """Gets the path and query from a URI.
44

45
  """
46
  match = _URI_RE.match(uri)
47
  if match:
48
    return match.groupdict()["path"]
49
  else:
50
    return None
51

    
52

    
53
class HttpResponseMock:
54
  """Dumb mock of httplib.HTTPResponse.
55

56
  """
57

    
58
  def __init__(self, code, data):
59
    self.code = code
60
    self._data = data
61

    
62
  def read(self):
63
    return self._data
64

    
65

    
66
class OpenerDirectorMock:
67
  """Mock for urllib.OpenerDirector.
68

69
  """
70

    
71
  def __init__(self, rapi):
72
    self._rapi = rapi
73
    self.last_request = None
74

    
75
  def open(self, req):
76
    self.last_request = req
77

    
78
    path = _GetPathFromUri(req.get_full_url())
79
    code, resp_body = self._rapi.FetchResponse(path, req.get_method())
80
    return HttpResponseMock(code, resp_body)
81

    
82

    
83
class RapiMock(object):
84
  def __init__(self):
85
    self._mapper = connector.Mapper()
86
    self._responses = []
87
    self._last_handler = None
88

    
89
  def AddResponse(self, response, code=200):
90
    self._responses.insert(0, (code, response))
91

    
92
  def GetLastHandler(self):
93
    return self._last_handler
94

    
95
  def FetchResponse(self, path, method):
96
    try:
97
      HandlerClass, items, args = self._mapper.getController(path)
98
      self._last_handler = HandlerClass(items, args, None)
99
      if not hasattr(self._last_handler, method.upper()):
100
        raise http.HttpNotImplemented(message="Method not implemented")
101

    
102
    except http.HttpException, ex:
103
      code = ex.code
104
      response = ex.message
105
    else:
106
      if not self._responses:
107
        raise Exception("No responses")
108

    
109
      (code, response) = self._responses.pop()
110

    
111
    return code, response
112

    
113

    
114
class RapiMockTest(unittest.TestCase):
115
  def test(self):
116
    rapi = RapiMock()
117
    path = "/version"
118
    self.assertEqual((404, None), rapi.FetchResponse("/foo", "GET"))
119
    self.assertEqual((501, "Method not implemented"),
120
                     rapi.FetchResponse("/version", "POST"))
121
    rapi.AddResponse("2")
122
    code, response = rapi.FetchResponse("/version", "GET")
123
    self.assertEqual(200, code)
124
    self.assertEqual("2", response)
125
    self.failUnless(isinstance(rapi.GetLastHandler(), rlib2.R_version))
126

    
127

    
128
class GanetiRapiClientTests(testutils.GanetiTestCase):
129
  def setUp(self):
130
    testutils.GanetiTestCase.setUp(self)
131

    
132
    self.rapi = RapiMock()
133
    self.http = OpenerDirectorMock(self.rapi)
134
    self.client = client.GanetiRapiClient('master.foo.com')
135
    self.client._http = self.http
136
    # Hard-code the version for easier testing.
137
    self.client._version = 2
138

    
139
  def assertHandler(self, handler_cls):
140
    self.failUnless(isinstance(self.rapi.GetLastHandler(), handler_cls))
141

    
142
  def assertQuery(self, key, value):
143
    self.assertEqual(value, self.rapi.GetLastHandler().queryargs.get(key, None))
144

    
145
  def assertItems(self, items):
146
    self.assertEqual(items, self.rapi.GetLastHandler().items)
147

    
148
  def assertBulk(self):
149
    self.assertTrue(self.rapi.GetLastHandler().useBulk())
150

    
151
  def assertDryRun(self):
152
    self.assertTrue(self.rapi.GetLastHandler().dryRun())
153

    
154
  def testEncodeQuery(self):
155
    query = [
156
      ("a", None),
157
      ("b", 1),
158
      ("c", 2),
159
      ("d", "Foo"),
160
      ("e", True),
161
      ]
162

    
163
    expected = [
164
      ("a", ""),
165
      ("b", 1),
166
      ("c", 2),
167
      ("d", "Foo"),
168
      ("e", 1),
169
      ]
170

    
171
    self.assertEqualValues(self.client._EncodeQuery(query),
172
                           expected)
173

    
174
    # invalid types
175
    for i in [[1, 2, 3], {"moo": "boo"}, (1, 2, 3)]:
176
      self.assertRaises(ValueError, self.client._EncodeQuery, [("x", i)])
177

    
178
  def testHttpError(self):
179
    self.rapi.AddResponse(None, code=404)
180
    try:
181
      self.client.GetJobStatus(15140)
182
    except client.GanetiApiError, err:
183
      self.assertEqual(err.code, 404)
184
    else:
185
      self.fail("Didn't raise exception")
186

    
187
  def testGetVersion(self):
188
    self.client._version = None
189
    self.rapi.AddResponse("2")
190
    self.assertEqual(2, self.client.GetVersion())
191
    self.assertHandler(rlib2.R_version)
192

    
193
  def testGetFeatures(self):
194
    for features in [[], ["foo", "bar", "baz"]]:
195
      self.rapi.AddResponse(serializer.DumpJson(features))
196
      self.assertEqual(features, self.client.GetFeatures())
197
      self.assertHandler(rlib2.R_2_features)
198

    
199
  def testGetOperatingSystems(self):
200
    self.rapi.AddResponse("[\"beos\"]")
201
    self.assertEqual(["beos"], self.client.GetOperatingSystems())
202
    self.assertHandler(rlib2.R_2_os)
203

    
204
  def testGetClusterTags(self):
205
    self.rapi.AddResponse("[\"tag\"]")
206
    self.assertEqual(["tag"], self.client.GetClusterTags())
207
    self.assertHandler(rlib2.R_2_tags)
208

    
209
  def testAddClusterTags(self):
210
    self.rapi.AddResponse("1234")
211
    self.assertEqual(1234,
212
        self.client.AddClusterTags(["awesome"], dry_run=True))
213
    self.assertHandler(rlib2.R_2_tags)
214
    self.assertDryRun()
215
    self.assertQuery("tag", ["awesome"])
216

    
217
  def testDeleteClusterTags(self):
218
    self.rapi.AddResponse("5107")
219
    self.assertEqual(5107, self.client.DeleteClusterTags(["awesome"],
220
                                                         dry_run=True))
221
    self.assertHandler(rlib2.R_2_tags)
222
    self.assertDryRun()
223
    self.assertQuery("tag", ["awesome"])
224

    
225
  def testGetInfo(self):
226
    self.rapi.AddResponse("{}")
227
    self.assertEqual({}, self.client.GetInfo())
228
    self.assertHandler(rlib2.R_2_info)
229

    
230
  def testGetInstances(self):
231
    self.rapi.AddResponse("[]")
232
    self.assertEqual([], self.client.GetInstances(bulk=True))
233
    self.assertHandler(rlib2.R_2_instances)
234
    self.assertBulk()
235

    
236
  def testGetInstance(self):
237
    self.rapi.AddResponse("[]")
238
    self.assertEqual([], self.client.GetInstance("instance"))
239
    self.assertHandler(rlib2.R_2_instances_name)
240
    self.assertItems(["instance"])
241

    
242
  def testGetInstanceInfo(self):
243
    self.rapi.AddResponse("21291")
244
    self.assertEqual(21291, self.client.GetInstanceInfo("inst3"))
245
    self.assertHandler(rlib2.R_2_instances_name_info)
246
    self.assertItems(["inst3"])
247
    self.assertQuery("static", None)
248

    
249
    self.rapi.AddResponse("3428")
250
    self.assertEqual(3428, self.client.GetInstanceInfo("inst31", static=False))
251
    self.assertHandler(rlib2.R_2_instances_name_info)
252
    self.assertItems(["inst31"])
253
    self.assertQuery("static", ["0"])
254

    
255
    self.rapi.AddResponse("15665")
256
    self.assertEqual(15665, self.client.GetInstanceInfo("inst32", static=True))
257
    self.assertHandler(rlib2.R_2_instances_name_info)
258
    self.assertItems(["inst32"])
259
    self.assertQuery("static", ["1"])
260

    
261
  def testCreateInstanceOldVersion(self):
262
    self.rapi.AddResponse(serializer.DumpJson([]))
263
    self.assertRaises(NotImplementedError, self.client.CreateInstance,
264
                      "create", "inst1.example.com", "plain", [], [],
265
                      dry_run=True)
266

    
267
  def testCreateInstance(self):
268
    self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_CREATE_REQV1]))
269
    self.rapi.AddResponse("23030")
270
    job_id = self.client.CreateInstance("create", "inst1.example.com",
271
                                        "plain", [], [], dry_run=True)
272
    self.assertEqual(job_id, 23030)
273
    self.assertHandler(rlib2.R_2_instances)
274
    self.assertDryRun()
275

    
276
    data = serializer.LoadJson(self.http.last_request.data)
277

    
278
    for field in ["dry_run", "beparams", "hvparams", "start"]:
279
      self.assertFalse(field in data)
280

    
281
    self.assertEqual(data["name"], "inst1.example.com")
282
    self.assertEqual(data["disk_template"], "plain")
283

    
284
  def testCreateInstance2(self):
285
    self.rapi.AddResponse(serializer.DumpJson([rlib2._INST_CREATE_REQV1]))
286
    self.rapi.AddResponse("24740")
287
    job_id = self.client.CreateInstance("import", "inst2.example.com",
288
                                        "drbd8", [{"size": 100,}],
289
                                        [{}, {"bridge": "br1", }],
290
                                        dry_run=False, start=True,
291
                                        pnode="node1", snode="node9",
292
                                        ip_check=False)
293
    self.assertEqual(job_id, 24740)
294
    self.assertHandler(rlib2.R_2_instances)
295

    
296
    data = serializer.LoadJson(self.http.last_request.data)
297
    self.assertEqual(data[rlib2._REQ_DATA_VERSION], 1)
298
    self.assertEqual(data["name"], "inst2.example.com")
299
    self.assertEqual(data["disk_template"], "drbd8")
300
    self.assertEqual(data["start"], True)
301
    self.assertEqual(data["ip_check"], False)
302
    self.assertEqualValues(data["disks"], [{"size": 100,}])
303
    self.assertEqualValues(data["nics"], [{}, {"bridge": "br1", }])
304

    
305
  def testDeleteInstance(self):
306
    self.rapi.AddResponse("1234")
307
    self.assertEqual(1234, self.client.DeleteInstance("instance", dry_run=True))
308
    self.assertHandler(rlib2.R_2_instances_name)
309
    self.assertItems(["instance"])
310
    self.assertDryRun()
311

    
312
  def testGetInstanceTags(self):
313
    self.rapi.AddResponse("[]")
314
    self.assertEqual([], self.client.GetInstanceTags("fooinstance"))
315
    self.assertHandler(rlib2.R_2_instances_name_tags)
316
    self.assertItems(["fooinstance"])
317

    
318
  def testAddInstanceTags(self):
319
    self.rapi.AddResponse("1234")
320
    self.assertEqual(1234,
321
        self.client.AddInstanceTags("fooinstance", ["awesome"], dry_run=True))
322
    self.assertHandler(rlib2.R_2_instances_name_tags)
323
    self.assertItems(["fooinstance"])
324
    self.assertDryRun()
325
    self.assertQuery("tag", ["awesome"])
326

    
327
  def testDeleteInstanceTags(self):
328
    self.rapi.AddResponse("25826")
329
    self.assertEqual(25826, self.client.DeleteInstanceTags("foo", ["awesome"],
330
                                                           dry_run=True))
331
    self.assertHandler(rlib2.R_2_instances_name_tags)
332
    self.assertItems(["foo"])
333
    self.assertDryRun()
334
    self.assertQuery("tag", ["awesome"])
335

    
336
  def testRebootInstance(self):
337
    self.rapi.AddResponse("6146")
338
    job_id = self.client.RebootInstance("i-bar", reboot_type="hard",
339
                                        ignore_secondaries=True, dry_run=True)
340
    self.assertEqual(6146, job_id)
341
    self.assertHandler(rlib2.R_2_instances_name_reboot)
342
    self.assertItems(["i-bar"])
343
    self.assertDryRun()
344
    self.assertQuery("type", ["hard"])
345
    self.assertQuery("ignore_secondaries", ["1"])
346

    
347
  def testShutdownInstance(self):
348
    self.rapi.AddResponse("1487")
349
    self.assertEqual(1487, self.client.ShutdownInstance("foo-instance",
350
                                                        dry_run=True))
351
    self.assertHandler(rlib2.R_2_instances_name_shutdown)
352
    self.assertItems(["foo-instance"])
353
    self.assertDryRun()
354

    
355
  def testStartupInstance(self):
356
    self.rapi.AddResponse("27149")
357
    self.assertEqual(27149, self.client.StartupInstance("bar-instance",
358
                                                        dry_run=True))
359
    self.assertHandler(rlib2.R_2_instances_name_startup)
360
    self.assertItems(["bar-instance"])
361
    self.assertDryRun()
362

    
363
  def testReinstallInstance(self):
364
    self.rapi.AddResponse("19119")
365
    self.assertEqual(19119, self.client.ReinstallInstance("baz-instance", "DOS",
366
                                                          no_startup=True))
367
    self.assertHandler(rlib2.R_2_instances_name_reinstall)
368
    self.assertItems(["baz-instance"])
369
    self.assertQuery("os", ["DOS"])
370
    self.assertQuery("nostartup", ["1"])
371

    
372
  def testReplaceInstanceDisks(self):
373
    self.rapi.AddResponse("999")
374
    job_id = self.client.ReplaceInstanceDisks("instance-name",
375
        disks=[0, 1], dry_run=True, iallocator="hail")
376
    self.assertEqual(999, job_id)
377
    self.assertHandler(rlib2.R_2_instances_name_replace_disks)
378
    self.assertItems(["instance-name"])
379
    self.assertQuery("disks", ["0,1"])
380
    self.assertQuery("mode", ["replace_auto"])
381
    self.assertQuery("iallocator", ["hail"])
382
    self.assertDryRun()
383

    
384
    self.rapi.AddResponse("1000")
385
    job_id = self.client.ReplaceInstanceDisks("instance-bar",
386
        disks=[1], mode="replace_on_secondary", remote_node="foo-node",
387
        dry_run=True)
388
    self.assertEqual(1000, job_id)
389
    self.assertItems(["instance-bar"])
390
    self.assertQuery("disks", ["1"])
391
    self.assertQuery("remote_node", ["foo-node"])
392
    self.assertDryRun()
393

    
394
    self.rapi.AddResponse("5175")
395
    self.assertEqual(5175, self.client.ReplaceInstanceDisks("instance-moo"))
396
    self.assertItems(["instance-moo"])
397
    self.assertQuery("disks", None)
398

    
399
  def testPrepareExport(self):
400
    self.rapi.AddResponse("8326")
401
    self.assertEqual(8326, self.client.PrepareExport("inst1", "local"))
402
    self.assertHandler(rlib2.R_2_instances_name_prepare_export)
403
    self.assertItems(["inst1"])
404
    self.assertQuery("mode", ["local"])
405

    
406
  def testExportInstance(self):
407
    self.rapi.AddResponse("19695")
408
    job_id = self.client.ExportInstance("inst2", "local", "nodeX",
409
                                        shutdown=True)
410
    self.assertEqual(job_id, 19695)
411
    self.assertHandler(rlib2.R_2_instances_name_export)
412
    self.assertItems(["inst2"])
413

    
414
    data = serializer.LoadJson(self.http.last_request.data)
415
    self.assertEqual(data["mode"], "local")
416
    self.assertEqual(data["destination"], "nodeX")
417
    self.assertEqual(data["shutdown"], True)
418

    
419
  def testGetJobs(self):
420
    self.rapi.AddResponse('[ { "id": "123", "uri": "\\/2\\/jobs\\/123" },'
421
                          '  { "id": "124", "uri": "\\/2\\/jobs\\/124" } ]')
422
    self.assertEqual([123, 124], self.client.GetJobs())
423
    self.assertHandler(rlib2.R_2_jobs)
424

    
425
  def testGetJobStatus(self):
426
    self.rapi.AddResponse("{\"foo\": \"bar\"}")
427
    self.assertEqual({"foo": "bar"}, self.client.GetJobStatus(1234))
428
    self.assertHandler(rlib2.R_2_jobs_id)
429
    self.assertItems(["1234"])
430

    
431
  def testWaitForJobChange(self):
432
    fields = ["id", "summary"]
433
    expected = {
434
      "job_info": [123, "something"],
435
      "log_entries": [],
436
      }
437

    
438
    self.rapi.AddResponse(serializer.DumpJson(expected))
439
    result = self.client.WaitForJobChange(123, fields, [], -1)
440
    self.assertEqualValues(expected, result)
441
    self.assertHandler(rlib2.R_2_jobs_id_wait)
442
    self.assertItems(["123"])
443

    
444
  def testCancelJob(self):
445
    self.rapi.AddResponse("[true, \"Job 123 will be canceled\"]")
446
    self.assertEqual([True, "Job 123 will be canceled"],
447
                     self.client.CancelJob(999, dry_run=True))
448
    self.assertHandler(rlib2.R_2_jobs_id)
449
    self.assertItems(["999"])
450
    self.assertDryRun()
451

    
452
  def testGetNodes(self):
453
    self.rapi.AddResponse("[ { \"id\": \"node1\", \"uri\": \"uri1\" },"
454
                          " { \"id\": \"node2\", \"uri\": \"uri2\" } ]")
455
    self.assertEqual(["node1", "node2"], self.client.GetNodes())
456
    self.assertHandler(rlib2.R_2_nodes)
457

    
458
    self.rapi.AddResponse("[ { \"id\": \"node1\", \"uri\": \"uri1\" },"
459
                          " { \"id\": \"node2\", \"uri\": \"uri2\" } ]")
460
    self.assertEqual([{"id": "node1", "uri": "uri1"},
461
                      {"id": "node2", "uri": "uri2"}],
462
                     self.client.GetNodes(bulk=True))
463
    self.assertHandler(rlib2.R_2_nodes)
464
    self.assertBulk()
465

    
466
  def testGetNode(self):
467
    self.rapi.AddResponse("{}")
468
    self.assertEqual({}, self.client.GetNode("node-foo"))
469
    self.assertHandler(rlib2.R_2_nodes_name)
470
    self.assertItems(["node-foo"])
471

    
472
  def testEvacuateNode(self):
473
    self.rapi.AddResponse("9876")
474
    job_id = self.client.EvacuateNode("node-1", remote_node="node-2")
475
    self.assertEqual(9876, job_id)
476
    self.assertHandler(rlib2.R_2_nodes_name_evacuate)
477
    self.assertItems(["node-1"])
478
    self.assertQuery("remote_node", ["node-2"])
479

    
480
    self.rapi.AddResponse("8888")
481
    job_id = self.client.EvacuateNode("node-3", iallocator="hail", dry_run=True)
482
    self.assertEqual(8888, job_id)
483
    self.assertItems(["node-3"])
484
    self.assertQuery("iallocator", ["hail"])
485
    self.assertDryRun()
486

    
487
    self.assertRaises(client.GanetiApiError,
488
                      self.client.EvacuateNode,
489
                      "node-4", iallocator="hail", remote_node="node-5")
490

    
491
  def testMigrateNode(self):
492
    self.rapi.AddResponse("1111")
493
    self.assertEqual(1111, self.client.MigrateNode("node-a", dry_run=True))
494
    self.assertHandler(rlib2.R_2_nodes_name_migrate)
495
    self.assertItems(["node-a"])
496
    self.assertQuery("live", ["1"])
497
    self.assertDryRun()
498

    
499
  def testGetNodeRole(self):
500
    self.rapi.AddResponse("\"master\"")
501
    self.assertEqual("master", self.client.GetNodeRole("node-a"))
502
    self.assertHandler(rlib2.R_2_nodes_name_role)
503
    self.assertItems(["node-a"])
504

    
505
  def testSetNodeRole(self):
506
    self.rapi.AddResponse("789")
507
    self.assertEqual(789,
508
        self.client.SetNodeRole("node-foo", "master-candidate", force=True))
509
    self.assertHandler(rlib2.R_2_nodes_name_role)
510
    self.assertItems(["node-foo"])
511
    self.assertQuery("force", ["1"])
512
    self.assertEqual("\"master-candidate\"", self.http.last_request.data)
513

    
514
  def testGetNodeStorageUnits(self):
515
    self.rapi.AddResponse("42")
516
    self.assertEqual(42,
517
        self.client.GetNodeStorageUnits("node-x", "lvm-pv", "fields"))
518
    self.assertHandler(rlib2.R_2_nodes_name_storage)
519
    self.assertItems(["node-x"])
520
    self.assertQuery("storage_type", ["lvm-pv"])
521
    self.assertQuery("output_fields", ["fields"])
522

    
523
  def testModifyNodeStorageUnits(self):
524
    self.rapi.AddResponse("14")
525
    self.assertEqual(14,
526
        self.client.ModifyNodeStorageUnits("node-z", "lvm-pv", "hda"))
527
    self.assertHandler(rlib2.R_2_nodes_name_storage_modify)
528
    self.assertItems(["node-z"])
529
    self.assertQuery("storage_type", ["lvm-pv"])
530
    self.assertQuery("name", ["hda"])
531
    self.assertQuery("allocatable", None)
532

    
533
    for allocatable, query_allocatable in [(True, "1"), (False, "0")]:
534
      self.rapi.AddResponse("7205")
535
      job_id = self.client.ModifyNodeStorageUnits("node-z", "lvm-pv", "hda",
536
                                                  allocatable=allocatable)
537
      self.assertEqual(7205, job_id)
538
      self.assertHandler(rlib2.R_2_nodes_name_storage_modify)
539
      self.assertItems(["node-z"])
540
      self.assertQuery("storage_type", ["lvm-pv"])
541
      self.assertQuery("name", ["hda"])
542
      self.assertQuery("allocatable", [query_allocatable])
543

    
544
  def testRepairNodeStorageUnits(self):
545
    self.rapi.AddResponse("99")
546
    self.assertEqual(99, self.client.RepairNodeStorageUnits("node-z", "lvm-pv",
547
                                                            "hda"))
548
    self.assertHandler(rlib2.R_2_nodes_name_storage_repair)
549
    self.assertItems(["node-z"])
550
    self.assertQuery("storage_type", ["lvm-pv"])
551
    self.assertQuery("name", ["hda"])
552

    
553
  def testGetNodeTags(self):
554
    self.rapi.AddResponse("[\"fry\", \"bender\"]")
555
    self.assertEqual(["fry", "bender"], self.client.GetNodeTags("node-k"))
556
    self.assertHandler(rlib2.R_2_nodes_name_tags)
557
    self.assertItems(["node-k"])
558

    
559
  def testAddNodeTags(self):
560
    self.rapi.AddResponse("1234")
561
    self.assertEqual(1234,
562
        self.client.AddNodeTags("node-v", ["awesome"], dry_run=True))
563
    self.assertHandler(rlib2.R_2_nodes_name_tags)
564
    self.assertItems(["node-v"])
565
    self.assertDryRun()
566
    self.assertQuery("tag", ["awesome"])
567

    
568
  def testDeleteNodeTags(self):
569
    self.rapi.AddResponse("16861")
570
    self.assertEqual(16861, self.client.DeleteNodeTags("node-w", ["awesome"],
571
                                                       dry_run=True))
572
    self.assertHandler(rlib2.R_2_nodes_name_tags)
573
    self.assertItems(["node-w"])
574
    self.assertDryRun()
575
    self.assertQuery("tag", ["awesome"])
576

    
577

    
578
if __name__ == '__main__':
579
  testutils.GanetiTestProgram()