4 # Copyright (C) 2010, 2011, 2012 Google Inc.
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.
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.
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
22 """Script for testing ganeti.query"""
28 from ganeti import constants
29 from ganeti import utils
30 from ganeti import compat
31 from ganeti import errors
32 from ganeti import query
33 from ganeti import objects
34 from ganeti import cmdlib
39 class TestConstants(unittest.TestCase):
41 self.assertEqual(set(query._VERIFY_FN.keys()),
46 def __init__(self, data, **kwargs):
49 for name, value in kwargs.items():
50 setattr(self, name, value)
53 return iter(self.data)
56 def _GetDiskSize(nr, ctx, item):
61 return query._FS_UNAVAIL
64 class TestQuery(unittest.TestCase):
66 (STATIC, DISK) = range(10, 12)
68 fielddef = query._PrepareFieldList([
69 (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
70 STATIC, 0, lambda ctx, item: item["name"]),
71 (query._MakeField("master", "Master", constants.QFT_BOOL, "Master"),
72 STATIC, 0, lambda ctx, item: ctx.mastername == item["name"]),
74 [(query._MakeField("disk%s.size" % i, "DiskSize%s" % i,
75 constants.QFT_UNIT, "Disk size %s" % i),
76 DISK, 0, compat.partial(_GetDiskSize, i))
77 for i in range(4)], [])
79 q = query.Query(fielddef, ["name"])
80 self.assertEqual(q.RequestedData(), set([STATIC]))
81 self.assertEqual(len(q._fields), 1)
82 self.assertEqual(len(q.GetFields()), 1)
83 self.assertEqual(q.GetFields()[0].ToDict(),
84 objects.QueryFieldDefinition(name="name",
86 kind=constants.QFT_TEXT,
89 # Create data only once query has been prepared
91 { "name": "node1", "disks": [0, 1, 2], },
92 { "name": "node2", "disks": [3, 4], },
93 { "name": "node3", "disks": [5, 6, 7], },
96 self.assertEqual(q.Query(_QueryData(data, mastername="node3")),
97 [[(constants.RS_NORMAL, "node1")],
98 [(constants.RS_NORMAL, "node2")],
99 [(constants.RS_NORMAL, "node3")]])
100 self.assertEqual(q.OldStyleQuery(_QueryData(data, mastername="node3")),
101 [["node1"], ["node2"], ["node3"]])
103 q = query.Query(fielddef, ["name", "master"])
104 self.assertEqual(q.RequestedData(), set([STATIC]))
105 self.assertEqual(len(q._fields), 2)
106 self.assertEqual(q.Query(_QueryData(data, mastername="node3")),
107 [[(constants.RS_NORMAL, "node1"),
108 (constants.RS_NORMAL, False)],
109 [(constants.RS_NORMAL, "node2"),
110 (constants.RS_NORMAL, False)],
111 [(constants.RS_NORMAL, "node3"),
112 (constants.RS_NORMAL, True)],
115 q = query.Query(fielddef, ["name", "master", "disk0.size"])
116 self.assertEqual(q.RequestedData(), set([STATIC, DISK]))
117 self.assertEqual(len(q._fields), 3)
118 self.assertEqual(q.Query(_QueryData(data, mastername="node2")),
119 [[(constants.RS_NORMAL, "node1"),
120 (constants.RS_NORMAL, False),
121 (constants.RS_NORMAL, 0)],
122 [(constants.RS_NORMAL, "node2"),
123 (constants.RS_NORMAL, True),
124 (constants.RS_NORMAL, 3)],
125 [(constants.RS_NORMAL, "node3"),
126 (constants.RS_NORMAL, False),
127 (constants.RS_NORMAL, 5)],
130 # With unknown column
131 q = query.Query(fielddef, ["disk2.size", "disk1.size", "disk99.size",
133 self.assertEqual(q.RequestedData(), set([DISK]))
134 self.assertEqual(len(q._fields), 4)
135 self.assertEqual(q.Query(_QueryData(data, mastername="node2")),
136 [[(constants.RS_NORMAL, 2),
137 (constants.RS_NORMAL, 1),
138 (constants.RS_UNKNOWN, None),
139 (constants.RS_NORMAL, 0)],
140 [(constants.RS_UNAVAIL, None),
141 (constants.RS_NORMAL, 4),
142 (constants.RS_UNKNOWN, None),
143 (constants.RS_NORMAL, 3)],
144 [(constants.RS_NORMAL, 7),
145 (constants.RS_NORMAL, 6),
146 (constants.RS_UNKNOWN, None),
147 (constants.RS_NORMAL, 5)],
149 self.assertRaises(errors.OpPrereqError, q.OldStyleQuery,
150 _QueryData(data, mastername="node2"))
151 self.assertEqual([fdef.ToDict() for fdef in q.GetFields()], [
152 { "name": "disk2.size", "title": "DiskSize2",
153 "kind": constants.QFT_UNIT, "doc": "Disk size 2", },
154 { "name": "disk1.size", "title": "DiskSize1",
155 "kind": constants.QFT_UNIT, "doc": "Disk size 1", },
156 { "name": "disk99.size", "title": "disk99.size",
157 "kind": constants.QFT_UNKNOWN,
158 "doc": "Unknown field 'disk99.size'", },
159 { "name": "disk0.size", "title": "DiskSize0",
160 "kind": constants.QFT_UNIT, "doc": "Disk size 0", },
164 q = query.Query(fielddef, [])
165 self.assertEqual(q.RequestedData(), set([]))
166 self.assertEqual(len(q._fields), 0)
167 self.assertEqual(q.Query(_QueryData(data, mastername="node2")),
169 self.assertEqual(q.OldStyleQuery(_QueryData(data, mastername="node2")),
171 self.assertEqual(q.GetFields(), [])
173 def testPrepareFieldList(self):
175 for (a, b) in [("name", "name"), ("NAME", "name")]:
176 self.assertRaises(AssertionError, query._PrepareFieldList, [
177 (query._MakeField("name", b, constants.QFT_TEXT, "Name"), None, 0,
179 (query._MakeField("other", a, constants.QFT_TEXT, "Other"), None, 0,
183 # Non-lowercase names
184 self.assertRaises(AssertionError, query._PrepareFieldList, [
185 (query._MakeField("NAME", "Name", constants.QFT_TEXT, "Name"), None, 0,
188 self.assertRaises(AssertionError, query._PrepareFieldList, [
189 (query._MakeField("Name", "Name", constants.QFT_TEXT, "Name"), None, 0,
194 self.assertRaises(AssertionError, query._PrepareFieldList, [
195 (query._MakeField("", "Name", constants.QFT_TEXT, "Name"), None, 0,
200 self.assertRaises(AssertionError, query._PrepareFieldList, [
201 (query._MakeField("name", "", constants.QFT_TEXT, "Name"), None, 0,
205 # Whitespace in title
206 self.assertRaises(AssertionError, query._PrepareFieldList, [
207 (query._MakeField("name", "Co lu mn", constants.QFT_TEXT, "Name"),
208 None, 0, lambda *args: None),
211 # No callable function
212 self.assertRaises(AssertionError, query._PrepareFieldList, [
213 (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
217 # Invalid documentation
218 for doc in ["", ".", "Hello world\n", "Hello\nWo\nrld", "Hello World!",
219 "HelloWorld.", "only lowercase", ",", " x y z .\t", " "]:
220 self.assertRaises(AssertionError, query._PrepareFieldList, [
221 (query._MakeField("name", "Name", constants.QFT_TEXT, doc),
222 None, 0, lambda *args: None),
225 # Duplicate field name
226 self.assertRaises(ValueError, query._PrepareFieldList, [
227 (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
228 None, 0, lambda *args: None),
229 (query._MakeField("name", "Other", constants.QFT_OTHER, "Other"),
230 None, 0, lambda *args: None),
233 def testUnknown(self):
234 fielddef = query._PrepareFieldList([
235 (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
236 None, 0, lambda _, item: "name%s" % item),
237 (query._MakeField("other0", "Other0", constants.QFT_TIMESTAMP, "Other"),
238 None, 0, lambda *args: 1234),
239 (query._MakeField("nodata", "NoData", constants.QFT_NUMBER, "No data"),
240 None, 0, lambda *args: query._FS_NODATA ),
241 (query._MakeField("unavail", "Unavail", constants.QFT_BOOL, "Unavail"),
242 None, 0, lambda *args: query._FS_UNAVAIL),
245 for selected in [["foo"], ["Hello", "World"],
246 ["name1", "other", "foo"]]:
247 q = query.Query(fielddef, selected)
248 self.assertEqual(len(q._fields), len(selected))
249 self.assert_(compat.all(len(row) == len(selected)
250 for row in q.Query(_QueryData(range(1, 10)))))
251 self.assertEqual(q.Query(_QueryData(range(1, 10))),
252 [[(constants.RS_UNKNOWN, None)] * len(selected)
253 for i in range(1, 10)])
254 self.assertEqual([fdef.ToDict() for fdef in q.GetFields()],
255 [{ "name": name, "title": name,
256 "kind": constants.QFT_UNKNOWN,
257 "doc": "Unknown field '%s'" % name}
258 for name in selected])
260 q = query.Query(fielddef, ["name", "other0", "nodata", "unavail"])
261 self.assertEqual(len(q._fields), 4)
262 self.assertEqual(q.OldStyleQuery(_QueryData(range(1, 10))), [
263 ["name%s" % i, 1234, None, None]
264 for i in range(1, 10)
267 q = query.Query(fielddef, ["name", "other0", "nodata", "unavail", "unk"])
268 self.assertEqual(len(q._fields), 5)
269 self.assertEqual(q.Query(_QueryData(range(1, 10))),
270 [[(constants.RS_NORMAL, "name%s" % i),
271 (constants.RS_NORMAL, 1234),
272 (constants.RS_NODATA, None),
273 (constants.RS_UNAVAIL, None),
274 (constants.RS_UNKNOWN, None)]
275 for i in range(1, 10)])
277 def testAliases(self):
279 (query._MakeField("a", "a-title", constants.QFT_TEXT, "Field A"),
280 None, 0, lambda *args: None),
281 (query._MakeField("b", "b-title", constants.QFT_TEXT, "Field B"),
282 None, 0, lambda *args: None),
285 self.assertRaises(AssertionError, query._PrepareFieldList, fields,
287 self.assertRaises(AssertionError, query._PrepareFieldList, fields,
288 [("c", "b"), ("c", "a")])
290 self.assertRaises(AssertionError, query._PrepareFieldList, fields,
292 fdefs = query._PrepareFieldList(fields, [("c", "b")])
293 self.assertEqual(len(fdefs), 3)
294 self.assertEqual(fdefs["b"][1:], fdefs["c"][1:])
297 class TestGetNodeRole(unittest.TestCase):
302 (constants.NR_MASTER, "node1", objects.Node(name="node1")),
303 (constants.NR_MCANDIDATE, "master",
304 objects.Node(name="node1", master_candidate=True)),
305 (constants.NR_REGULAR, "master", objects.Node(name="node1")),
306 (constants.NR_DRAINED, "master",
307 objects.Node(name="node1", drained=True)),
308 (constants.NR_OFFLINE,
309 "master", objects.Node(name="node1", offline=True)),
312 for (role, master_name, node) in checks:
313 result = query._GetNodeRole(node, master_name)
314 self.assertEqual(result, role)
315 tested_role.add(result)
317 self.assertEqual(tested_role, constants.NR_ALL)
320 class TestNodeQuery(unittest.TestCase):
321 def _Create(self, selected):
322 return query.Query(query.NODE_FIELDS, selected)
324 def testSimple(self):
325 cluster = objects.Cluster(cluster_name="testcluster",
326 ndparams=constants.NDC_DEFAULTS.copy())
327 grp1 = objects.NodeGroup(name="default",
328 uuid="c0e89160-18e7-11e0-a46e-001d0904baeb",
329 alloc_policy=constants.ALLOC_POLICY_PREFERRED,
330 ipolicy=objects.MakeEmptyIPolicy(),
333 grp2 = objects.NodeGroup(name="group2",
334 uuid="c0e89160-18e7-11e0-a46e-001d0904babe",
335 alloc_policy=constants.ALLOC_POLICY_PREFERRED,
336 ipolicy=objects.MakeEmptyIPolicy(),
337 ndparams={constants.ND_SPINDLE_COUNT: 2},
339 groups = {grp1.uuid: grp1, grp2.uuid: grp2}
341 objects.Node(name="node1", drained=False, group=grp1.uuid, ndparams={}),
342 objects.Node(name="node2", drained=True, group=grp2.uuid, ndparams={}),
343 objects.Node(name="node3", drained=False, group=grp1.uuid,
344 ndparams={constants.ND_SPINDLE_COUNT: 4}),
346 for live_data in [None, dict.fromkeys([node.name for node in nodes], {})]:
347 nqd = query.NodeQueryData(nodes, live_data, None, None, None,
348 groups, None, cluster)
350 q = self._Create(["name", "drained"])
351 self.assertEqual(q.RequestedData(), set([query.NQ_CONFIG]))
352 self.assertEqual(q.Query(nqd),
353 [[(constants.RS_NORMAL, "node1"),
354 (constants.RS_NORMAL, False)],
355 [(constants.RS_NORMAL, "node2"),
356 (constants.RS_NORMAL, True)],
357 [(constants.RS_NORMAL, "node3"),
358 (constants.RS_NORMAL, False)],
360 self.assertEqual(q.OldStyleQuery(nqd),
364 q = self._Create(["ndp/spindle_count"])
365 self.assertEqual(q.RequestedData(), set([query.NQ_GROUP]))
366 self.assertEqual(q.Query(nqd),
367 [[(constants.RS_NORMAL,
368 constants.NDC_DEFAULTS[constants.ND_SPINDLE_COUNT])],
369 [(constants.RS_NORMAL,
370 grp2.ndparams[constants.ND_SPINDLE_COUNT])],
371 [(constants.RS_NORMAL,
372 nodes[2].ndparams[constants.ND_SPINDLE_COUNT])],
376 selected = query.NODE_FIELDS.keys()
377 field_index = dict((field, idx) for idx, field in enumerate(selected))
379 q = self._Create(selected)
380 self.assertEqual(q.RequestedData(),
381 set([query.NQ_CONFIG, query.NQ_LIVE, query.NQ_INST,
382 query.NQ_GROUP, query.NQ_OOB]))
384 cluster = objects.Cluster(cluster_name="testcluster",
385 hvparams=constants.HVC_DEFAULTS,
387 constants.PP_DEFAULT: constants.BEC_DEFAULTS,
390 constants.PP_DEFAULT: constants.NICC_DEFAULTS,
392 ndparams=constants.NDC_DEFAULTS,
395 node_names = ["node%s" % i for i in range(20)]
396 master_name = node_names[3]
398 objects.Node(name=name,
399 primary_ip="192.0.2.%s" % idx,
400 secondary_ip="192.0.100.%s" % idx,
401 serial_no=7789 * idx,
402 master_candidate=(name != master_name and idx % 3 == 0),
407 master_capable=False,
412 uuid="fd9ccebe-6339-43c9-a82e-94bbe575%04d" % idx)
413 for idx, name in enumerate(node_names)
416 master_node = nodes[3]
417 master_node.AddTag("masternode")
418 master_node.AddTag("another")
419 master_node.AddTag("tag")
420 master_node.ctime = None
421 master_node.mtime = None
422 assert master_node.name == master_name
424 live_data_name = node_names[4]
425 assert live_data_name != master_name
428 "bootid": "a2504766-498e-4b25-b21e-d23098dc3af4",
435 "dfree": 5 * 1024 * 1024,
436 "dtotal": 100 * 1024 * 1024,
439 assert (sorted(query._NODE_LIVE_FIELDS.keys()) ==
440 sorted(fake_live_data.keys()))
442 live_data = dict.fromkeys(node_names, {})
443 live_data[live_data_name] = \
444 dict((query._NODE_LIVE_FIELDS[name][2], value)
445 for name, value in fake_live_data.items())
447 node_to_primary = dict((name, set()) for name in node_names)
448 node_to_primary[master_name].update(["inst1", "inst2"])
450 node_to_secondary = dict((name, set()) for name in node_names)
451 node_to_secondary[live_data_name].update(["instX", "instY", "instZ"])
453 ng_uuid = "492b4b74-8670-478a-b98d-4c53a76238e6"
455 ng_uuid: objects.NodeGroup(name="ng1", uuid=ng_uuid, ndparams={}),
458 oob_not_powered_node = node_names[0]
459 nodes[0].powered = False
460 oob_support = dict((name, False) for name in node_names)
461 oob_support[master_name] = True
462 oob_support[oob_not_powered_node] = True
464 master_node.group = ng_uuid
466 nqd = query.NodeQueryData(nodes, live_data, master_name,
467 node_to_primary, node_to_secondary, groups,
468 oob_support, cluster)
469 result = q.Query(nqd)
470 self.assert_(compat.all(len(row) == len(selected) for row in result))
471 self.assertEqual([row[field_index["name"]] for row in result],
472 [(constants.RS_NORMAL, name) for name in node_names])
474 node_to_row = dict((row[field_index["name"]][1], idx)
475 for idx, row in enumerate(result))
477 master_row = result[node_to_row[master_name]]
478 self.assert_(master_row[field_index["master"]])
479 self.assert_(master_row[field_index["role"]], "M")
480 self.assertEqual(master_row[field_index["group"]],
481 (constants.RS_NORMAL, "ng1"))
482 self.assertEqual(master_row[field_index["group.uuid"]],
483 (constants.RS_NORMAL, ng_uuid))
484 self.assertEqual(master_row[field_index["ctime"]],
485 (constants.RS_UNAVAIL, None))
486 self.assertEqual(master_row[field_index["mtime"]],
487 (constants.RS_UNAVAIL, None))
489 self.assert_(row[field_index["pip"]] == node.primary_ip and
490 row[field_index["sip"]] == node.secondary_ip and
491 set(row[field_index["tags"]]) == node.GetTags() and
492 row[field_index["serial_no"]] == node.serial_no and
493 row[field_index["role"]] == query._GetNodeRole(node,
495 (node.name == master_name or
496 (row[field_index["group"]] == "<unknown>" and
497 row[field_index["group.uuid"]] is None and
498 row[field_index["ctime"]] == (constants.RS_NORMAL,
500 row[field_index["mtime"]] == (constants.RS_NORMAL,
502 row[field_index["powered"]] == (constants.RS_NORMAL,
504 (node.name == oob_not_powered_node and
505 row[field_index["powered"]] == (constants.RS_NORMAL,
507 row[field_index["powered"]] == (constants.RS_UNAVAIL, None)
508 for row, node in zip(result, nodes))
510 live_data_row = result[node_to_row[live_data_name]]
512 for (field, value) in fake_live_data.items():
513 self.assertEqual(live_data_row[field_index[field]],
514 (constants.RS_NORMAL, value))
516 self.assertEqual(master_row[field_index["pinst_cnt"]],
517 (constants.RS_NORMAL, 2))
518 self.assertEqual(live_data_row[field_index["sinst_cnt"]],
519 (constants.RS_NORMAL, 3))
520 self.assertEqual(master_row[field_index["pinst_list"]],
521 (constants.RS_NORMAL,
522 list(node_to_primary[master_name])))
523 self.assertEqual(live_data_row[field_index["sinst_list"]],
524 (constants.RS_NORMAL,
525 list(node_to_secondary[live_data_name])))
527 def testGetLiveNodeField(self):
529 objects.Node(name="node1", drained=False, offline=False,
531 objects.Node(name="node2", drained=True, offline=False,
533 objects.Node(name="node3", drained=False, offline=False,
535 objects.Node(name="node4", drained=False, offline=True,
537 objects.Node(name="node5", drained=False, offline=False,
540 live_data = dict.fromkeys([node.name for node in nodes], {})
543 nqd = query.NodeQueryData(None, None, None, None, None, None, None, None)
544 self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
549 ctx = _QueryData(None, curlive_data={
553 self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
557 # Wrong format/datatype
558 ctx = _QueryData(None, curlive_data={
559 "hello": ["Hello World"],
562 self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
567 assert nodes[3].offline
568 ctx = _QueryData(None, curlive_data={})
569 self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
571 query._FS_OFFLINE, None)
574 ctx = _QueryData(None, curlive_data={"hello": 123})
575 self.assertRaises(AssertionError, query._GetLiveNodeField,
576 "hello", constants.QFT_BOOL, ctx, nodes[0])
578 # Non-vm_capable node
579 assert not nodes[4].vm_capable
580 ctx = _QueryData(None, curlive_data={})
581 self.assertEqual(query._GetLiveNodeField("hello", constants.QFT_NUMBER,
583 query._FS_UNAVAIL, None)
586 class TestInstanceQuery(unittest.TestCase):
587 def _Create(self, selected):
588 return query.Query(query.INSTANCE_FIELDS, selected)
590 def testSimple(self):
591 q = self._Create(["name", "be/maxmem", "ip"])
592 self.assertEqual(q.RequestedData(), set([query.IQ_CONFIG]))
594 cluster = objects.Cluster(cluster_name="testcluster",
595 hvparams=constants.HVC_DEFAULTS,
597 constants.PP_DEFAULT: constants.BEC_DEFAULTS,
600 constants.PP_DEFAULT: constants.NICC_DEFAULTS,
606 objects.Instance(name="inst1", hvparams={}, beparams={}, osparams={},
608 objects.Instance(name="inst2", hvparams={}, nics=[], osparams={},
611 constants.BE_MAXMEM: 512,
613 objects.Instance(name="inst3", hvparams={}, beparams={}, osparams={},
614 os="dos", nics=[objects.NIC(ip="192.0.2.99", nicparams={})]),
617 iqd = query.InstanceQueryData(instances, cluster, None, [], [], {},
618 set(), {}, None, None)
619 self.assertEqual(q.Query(iqd),
620 [[(constants.RS_NORMAL, "inst1"),
621 (constants.RS_NORMAL, 128),
622 (constants.RS_UNAVAIL, None),
624 [(constants.RS_NORMAL, "inst2"),
625 (constants.RS_NORMAL, 512),
626 (constants.RS_UNAVAIL, None),
628 [(constants.RS_NORMAL, "inst3"),
629 (constants.RS_NORMAL, 128),
630 (constants.RS_NORMAL, "192.0.2.99"),
632 self.assertEqual(q.OldStyleQuery(iqd),
633 [["inst1", 128, None],
634 ["inst2", 512, None],
635 ["inst3", 128, "192.0.2.99"]])
638 selected = query.INSTANCE_FIELDS.keys()
639 fieldidx = dict((field, idx) for idx, field in enumerate(selected))
641 macs = ["00:11:22:%02x:%02x:%02x" % (i % 255, i % 3, (i * 123) % 255)
644 q = self._Create(selected)
645 self.assertEqual(q.RequestedData(),
646 set([query.IQ_CONFIG, query.IQ_LIVE, query.IQ_DISKUSAGE,
647 query.IQ_CONSOLE, query.IQ_NODES]))
649 cluster = objects.Cluster(cluster_name="testcluster",
650 hvparams=constants.HVC_DEFAULTS,
652 constants.PP_DEFAULT: constants.BEC_DEFAULTS,
655 constants.PP_DEFAULT: constants.NICC_DEFAULTS,
658 tcpudp_port_pool=set(),
661 "clean_install": "yes",
665 offline_nodes = ["nodeoff1", "nodeoff2"]
666 bad_nodes = ["nodebad1", "nodebad2", "nodebad3"] + offline_nodes
667 nodes = ["node%s" % i for i in range(10)] + bad_nodes
670 objects.Instance(name="inst1", hvparams={}, beparams={}, nics=[],
671 uuid="f90eccb3-e227-4e3c-bf2a-94a21ca8f9cd",
672 ctime=1291244000, mtime=1291244400, serial_no=30,
673 admin_state=constants.ADMINST_UP, hypervisor=constants.HT_XEN_PVM,
675 primary_node="node1",
676 disk_template=constants.DT_PLAIN,
679 objects.Instance(name="inst2", hvparams={}, nics=[],
680 uuid="73a0f8a7-068c-4630-ada2-c3440015ab1a",
681 ctime=1291211000, mtime=1291211077, serial_no=1,
682 admin_state=constants.ADMINST_UP, hypervisor=constants.HT_XEN_HVM,
684 primary_node="node5",
685 disk_template=constants.DT_DISKLESS,
688 constants.BE_MAXMEM: 512,
689 constants.BE_MINMEM: 256,
692 objects.Instance(name="inst3", hvparams={}, beparams={},
693 uuid="11ec8dff-fb61-4850-bfe0-baa1803ff280",
694 ctime=1291011000, mtime=1291013000, serial_no=1923,
695 admin_state=constants.ADMINST_DOWN, hypervisor=constants.HT_KVM,
697 primary_node="node6",
698 disk_template=constants.DT_DRBD8,
701 objects.NIC(ip="192.0.2.99", mac=macs.pop(),
703 constants.NIC_LINK: constants.DEFAULT_BRIDGE,
705 objects.NIC(ip=None, mac=macs.pop(), nicparams={}),
708 objects.Instance(name="inst4", hvparams={}, beparams={},
709 uuid="68dab168-3ef5-4c9d-b4d3-801e0672068c",
710 ctime=1291244390, mtime=1291244395, serial_no=25,
711 admin_state=constants.ADMINST_DOWN, hypervisor=constants.HT_XEN_PVM,
713 primary_node="nodeoff2",
714 disk_template=constants.DT_DRBD8,
717 objects.NIC(ip="192.0.2.1", mac=macs.pop(),
719 constants.NIC_LINK: constants.DEFAULT_BRIDGE,
721 objects.NIC(ip="192.0.2.2", mac=macs.pop(), nicparams={}),
722 objects.NIC(ip="192.0.2.3", mac=macs.pop(),
724 constants.NIC_MODE: constants.NIC_MODE_ROUTED,
726 objects.NIC(ip="192.0.2.4", mac=macs.pop(),
728 constants.NIC_MODE: constants.NIC_MODE_BRIDGED,
729 constants.NIC_LINK: "eth123",
733 objects.Instance(name="inst5", hvparams={}, nics=[],
734 uuid="0e3dca12-5b42-4e24-98a2-415267545bd0",
735 ctime=1231211000, mtime=1261200000, serial_no=3,
736 admin_state=constants.ADMINST_UP, hypervisor=constants.HT_XEN_HVM,
738 primary_node="nodebad2",
739 disk_template=constants.DT_DISKLESS,
742 constants.BE_MAXMEM: 512,
743 constants.BE_MINMEM: 512,
746 objects.Instance(name="inst6", hvparams={}, nics=[],
747 uuid="72de6580-c8d5-4661-b902-38b5785bb8b3",
748 ctime=7513, mtime=11501, serial_no=13390,
749 admin_state=constants.ADMINST_DOWN, hypervisor=constants.HT_XEN_HVM,
751 primary_node="node7",
752 disk_template=constants.DT_DISKLESS,
755 constants.BE_MAXMEM: 768,
756 constants.BE_MINMEM: 256,
759 "clean_install": "no",
761 objects.Instance(name="inst7", hvparams={}, nics=[],
762 uuid="ceec5dc4-b729-4f42-ae28-69b3cd24920e",
763 ctime=None, mtime=None, serial_no=1947,
764 admin_state=constants.ADMINST_DOWN, hypervisor=constants.HT_XEN_HVM,
766 primary_node="node6",
767 disk_template=constants.DT_DISKLESS,
771 objects.Instance(name="inst8", hvparams={}, nics=[],
772 uuid="ceec5dc4-b729-4f42-ae28-69b3cd24920f",
773 ctime=None, mtime=None, serial_no=19478,
774 admin_state=constants.ADMINST_OFFLINE, hypervisor=constants.HT_XEN_HVM,
776 primary_node="node6",
777 disk_template=constants.DT_DISKLESS,
783 assert not utils.FindDuplicates(inst.name for inst in instances)
785 instbyname = dict((inst.name, inst) for inst in instances)
787 disk_usage = dict((inst.name,
788 cmdlib._ComputeDiskSize(inst.disk_template,
790 for disk in inst.disks]))
791 for inst in instances)
794 "inst3": [constants.DEFAULT_BRIDGE, constants.DEFAULT_BRIDGE],
795 "inst4": [constants.DEFAULT_BRIDGE, constants.DEFAULT_BRIDGE,
813 wrongnode_inst = set(["inst7"])
815 consinfo = dict((inst.name, None) for inst in instances)
816 consinfo["inst7"] = \
817 objects.InstanceConsole(instance="inst7", kind=constants.CONS_SSH,
818 host=instbyname["inst7"].primary_node,
819 user=constants.GANETI_RUNAS,
820 command=["hostname"]).ToDict()
822 iqd = query.InstanceQueryData(instances, cluster, disk_usage,
823 offline_nodes, bad_nodes, live_data,
824 wrongnode_inst, consinfo, {}, {})
825 result = q.Query(iqd)
826 self.assertEqual(len(result), len(instances))
827 self.assert_(compat.all(len(row) == len(selected)
830 assert len(set(bad_nodes) & set(offline_nodes)) == len(offline_nodes), \
831 "Offline nodes not included in bad nodes"
833 tested_status = set()
835 for (inst, row) in zip(instances, result):
836 assert inst.primary_node in nodes
838 self.assertEqual(row[fieldidx["name"]],
839 (constants.RS_NORMAL, inst.name))
841 if inst.primary_node in offline_nodes:
842 exp_status = constants.INSTST_NODEOFFLINE
843 elif inst.primary_node in bad_nodes:
844 exp_status = constants.INSTST_NODEDOWN
845 elif inst.name in live_data:
846 if inst.name in wrongnode_inst:
847 exp_status = constants.INSTST_WRONGNODE
848 elif inst.admin_state == constants.ADMINST_UP:
849 exp_status = constants.INSTST_RUNNING
851 exp_status = constants.INSTST_ERRORUP
852 elif inst.admin_state == constants.ADMINST_UP:
853 exp_status = constants.INSTST_ERRORDOWN
854 elif inst.admin_state == constants.ADMINST_DOWN:
855 exp_status = constants.INSTST_ADMINDOWN
857 exp_status = constants.INSTST_ADMINOFFLINE
859 self.assertEqual(row[fieldidx["status"]],
860 (constants.RS_NORMAL, exp_status))
862 (_, status) = row[fieldidx["status"]]
863 tested_status.add(status)
865 #FIXME(dynmem): check oper_ram vs min/max mem
866 for (field, livefield) in [("oper_vcpus", "vcpus")]:
867 if inst.primary_node in bad_nodes:
868 exp = (constants.RS_NODATA, None)
869 elif inst.name in live_data:
870 value = live_data[inst.name].get(livefield, None)
872 exp = (constants.RS_UNAVAIL, None)
874 exp = (constants.RS_NORMAL, value)
876 exp = (constants.RS_UNAVAIL, None)
878 self.assertEqual(row[fieldidx[field]], exp)
880 bridges = inst_bridges.get(inst.name, [])
881 self.assertEqual(row[fieldidx["nic.bridges"]],
882 (constants.RS_NORMAL, bridges))
884 self.assertEqual(row[fieldidx["bridge"]],
885 (constants.RS_NORMAL, bridges[0]))
887 self.assertEqual(row[fieldidx["bridge"]],
888 (constants.RS_UNAVAIL, None))
890 for i in range(constants.MAX_NICS):
891 if i < len(bridges) and bridges[i] is not None:
892 exp = (constants.RS_NORMAL, bridges[i])
894 exp = (constants.RS_UNAVAIL, None)
895 self.assertEqual(row[fieldidx["nic.bridge/%s" % i]], exp)
897 if inst.primary_node in bad_nodes:
898 exp = (constants.RS_NODATA, None)
900 exp = (constants.RS_NORMAL, inst.name in live_data)
901 self.assertEqual(row[fieldidx["oper_state"]], exp)
903 cust_exp = (constants.RS_NORMAL, {})
904 if inst.os == "deb99":
905 if inst.name == "inst6":
906 exp = (constants.RS_NORMAL, {"clean_install": "no"})
909 exp = (constants.RS_NORMAL, {"clean_install": "yes"})
911 exp = (constants.RS_NORMAL, {})
912 self.assertEqual(row[fieldidx["osparams"]], exp)
913 self.assertEqual(row[fieldidx["custom_osparams"]], cust_exp)
915 usage = disk_usage[inst.name]
918 self.assertEqual(row[fieldidx["disk_usage"]],
919 (constants.RS_NORMAL, usage))
921 for alias, target in [("sda_size", "disk.size/0"),
922 ("sdb_size", "disk.size/1"),
923 ("vcpus", "be/vcpus"),
925 ("mac", "nic.mac/0"),
926 ("bridge", "nic.bridge/0"),
927 ("nic_mode", "nic.mode/0"),
928 ("nic_link", "nic.link/0"),
930 self.assertEqual(row[fieldidx[alias]], row[fieldidx[target]])
932 for field in ["ctime", "mtime"]:
933 if getattr(inst, field) is None:
935 exp = (constants.RS_UNAVAIL, None)
937 exp = (constants.RS_NORMAL, getattr(inst, field))
938 self.assertEqual(row[fieldidx[field]], exp)
940 self._CheckInstanceConsole(inst, row[fieldidx["console"]])
942 # Ensure all possible status' have been tested
943 self.assertEqual(tested_status, constants.INSTST_ALL)
945 def _CheckInstanceConsole(self, instance, (status, consdata)):
946 if instance.name == "inst7":
947 self.assertEqual(status, constants.RS_NORMAL)
948 console = objects.InstanceConsole.FromDict(consdata)
949 self.assertTrue(console.Validate())
950 self.assertEqual(console.host, instance.primary_node)
952 self.assertEqual(status, constants.RS_UNAVAIL)
955 class TestGroupQuery(unittest.TestCase):
959 objects.NodeGroup(name="default",
960 uuid="c0e89160-18e7-11e0-a46e-001d0904baeb",
961 alloc_policy=constants.ALLOC_POLICY_PREFERRED,
962 ipolicy=objects.MakeEmptyIPolicy(),
965 objects.NodeGroup(name="restricted",
966 uuid="d2a40a74-18e7-11e0-9143-001d0904baeb",
967 alloc_policy=constants.ALLOC_POLICY_LAST_RESORT,
968 ipolicy=objects.MakeEmptyIPolicy(),
972 self.cluster = objects.Cluster(cluster_name="testcluster",
973 hvparams=constants.HVC_DEFAULTS,
975 constants.PP_DEFAULT: constants.BEC_DEFAULTS,
978 constants.PP_DEFAULT: constants.NICC_DEFAULTS,
980 ndparams=constants.NDC_DEFAULTS,
981 ipolicy=constants.IPOLICY_DEFAULTS,
984 def _Create(self, selected):
985 return query.Query(query.GROUP_FIELDS, selected)
987 def testSimple(self):
988 q = self._Create(["name", "uuid", "alloc_policy"])
989 gqd = query.GroupQueryData(self.cluster, self.groups, None, None)
991 self.assertEqual(q.RequestedData(), set([query.GQ_CONFIG]))
993 self.assertEqual(q.Query(gqd),
994 [[(constants.RS_NORMAL, "default"),
995 (constants.RS_NORMAL, "c0e89160-18e7-11e0-a46e-001d0904baeb"),
996 (constants.RS_NORMAL, constants.ALLOC_POLICY_PREFERRED)
998 [(constants.RS_NORMAL, "restricted"),
999 (constants.RS_NORMAL, "d2a40a74-18e7-11e0-9143-001d0904baeb"),
1000 (constants.RS_NORMAL, constants.ALLOC_POLICY_LAST_RESORT)
1004 def testNodes(self):
1006 "c0e89160-18e7-11e0-a46e-001d0904baeb": ["node1", "node2"],
1007 "d2a40a74-18e7-11e0-9143-001d0904baeb": ["node1", "node10", "node9"],
1010 q = self._Create(["name", "node_cnt", "node_list"])
1011 gqd = query.GroupQueryData(self.cluster, self.groups, groups_to_nodes, None)
1013 self.assertEqual(q.RequestedData(), set([query.GQ_CONFIG, query.GQ_NODE]))
1015 self.assertEqual(q.Query(gqd),
1016 [[(constants.RS_NORMAL, "default"),
1017 (constants.RS_NORMAL, 2),
1018 (constants.RS_NORMAL, ["node1", "node2"]),
1020 [(constants.RS_NORMAL, "restricted"),
1021 (constants.RS_NORMAL, 3),
1022 (constants.RS_NORMAL, ["node1", "node9", "node10"]),
1026 def testInstances(self):
1027 groups_to_instances = {
1028 "c0e89160-18e7-11e0-a46e-001d0904baeb": ["inst1", "inst2"],
1029 "d2a40a74-18e7-11e0-9143-001d0904baeb": ["inst1", "inst10", "inst9"],
1032 q = self._Create(["pinst_cnt", "pinst_list"])
1033 gqd = query.GroupQueryData(self.cluster, self.groups, None,
1034 groups_to_instances)
1036 self.assertEqual(q.RequestedData(), set([query.GQ_INST]))
1038 self.assertEqual(q.Query(gqd),
1039 [[(constants.RS_NORMAL, 2),
1040 (constants.RS_NORMAL, ["inst1", "inst2"]),
1042 [(constants.RS_NORMAL, 3),
1043 (constants.RS_NORMAL, ["inst1", "inst9", "inst10"]),
1048 class TestOsQuery(unittest.TestCase):
1049 def _Create(self, selected):
1050 return query.Query(query.OS_FIELDS, selected)
1053 variants = ["v00", "plain", "v3", "var0", "v33", "v20"]
1054 api_versions = [10, 0, 15, 5]
1055 parameters = ["zpar3", "apar9"]
1057 assert variants != sorted(variants) and variants != utils.NiceSort(variants)
1058 assert (api_versions != sorted(api_versions) and
1059 api_versions != utils.NiceSort(variants))
1060 assert (parameters != sorted(parameters) and
1061 parameters != utils.NiceSort(parameters))
1064 query.OsInfo(name="debian", valid=False, hidden=False, blacklisted=False,
1065 variants=set(), api_versions=set(), parameters=set(),
1066 node_status={ "some": "status", }),
1067 query.OsInfo(name="dos", valid=True, hidden=False, blacklisted=True,
1068 variants=set(variants),
1069 api_versions=set(api_versions),
1070 parameters=set(parameters),
1071 node_status={ "some": "other", "status": None, }),
1075 q = self._Create(["name", "valid", "hidden", "blacklisted", "variants",
1076 "api_versions", "parameters", "node_status"])
1077 self.assertEqual(q.RequestedData(), set([]))
1078 self.assertEqual(q.Query(data),
1079 [[(constants.RS_NORMAL, "debian"),
1080 (constants.RS_NORMAL, False),
1081 (constants.RS_NORMAL, False),
1082 (constants.RS_NORMAL, False),
1083 (constants.RS_NORMAL, []),
1084 (constants.RS_NORMAL, []),
1085 (constants.RS_NORMAL, []),
1086 (constants.RS_NORMAL, {"some": "status"})],
1087 [(constants.RS_NORMAL, "dos"),
1088 (constants.RS_NORMAL, True),
1089 (constants.RS_NORMAL, False),
1090 (constants.RS_NORMAL, True),
1091 (constants.RS_NORMAL,
1092 ["plain", "v00", "v3", "v20", "v33", "var0"]),
1093 (constants.RS_NORMAL, [0, 5, 10, 15]),
1094 (constants.RS_NORMAL, ["apar9", "zpar3"]),
1095 (constants.RS_NORMAL,
1096 { "some": "other", "status": None, })
1100 class TestQueryFields(unittest.TestCase):
1101 def testAllFields(self):
1102 for fielddefs in query.ALL_FIELD_LISTS:
1103 result = query.QueryFields(fielddefs, None)
1104 self.assert_(isinstance(result, dict))
1105 response = objects.QueryFieldsResponse.FromDict(result)
1106 self.assertEqual([(fdef.name, fdef.title) for fdef in response.fields],
1107 [(fdef2.name, fdef2.title)
1108 for (fdef2, _, _, _) in utils.NiceSort(fielddefs.values(),
1109 key=lambda x: x[0].name)])
1111 def testSomeFields(self):
1112 rnd = random.Random(5357)
1115 for fielddefs in query.ALL_FIELD_LISTS:
1116 if len(fielddefs) > 20:
1117 sample_size = rnd.randint(5, 20)
1119 sample_size = rnd.randint(1, max(1, len(fielddefs) - 1))
1120 fields = [fdef for (fdef, _, _, _) in rnd.sample(fielddefs.values(),
1122 result = query.QueryFields(fielddefs, [fdef.name for fdef in fields])
1123 self.assert_(isinstance(result, dict))
1124 response = objects.QueryFieldsResponse.FromDict(result)
1125 self.assertEqual([(fdef.name, fdef.title) for fdef in response.fields],
1126 [(fdef2.name, fdef2.title) for fdef2 in fields])
1129 class TestQueryFilter(unittest.TestCase):
1130 def testRequestedNames(self):
1131 innerfilter = [["=", "name", "x%s" % i] for i in range(4)]
1133 for fielddefs in query.ALL_FIELD_LISTS:
1134 assert "name" in fielddefs
1137 q = query.Query(fielddefs, ["name"], qfilter=["=", "name", "abc"],
1139 self.assertEqual(q.RequestedNames(), None)
1142 q = query.Query(fielddefs, ["name"], qfilter=None, namefield="name")
1143 self.assertEqual(q.RequestedNames(), None)
1146 q = query.Query(fielddefs, ["name"], qfilter=["|"], namefield="name")
1147 self.assertEqual(q.RequestedNames(), None)
1150 q = query.Query(fielddefs, ["name"], qfilter=["|"] + innerfilter,
1152 self.assertEqual(q.RequestedNames(), ["x0", "x1", "x2", "x3"])
1154 # Check reverse order
1155 q = query.Query(fielddefs, ["name"],
1156 qfilter=["|"] + list(reversed(innerfilter)),
1158 self.assertEqual(q.RequestedNames(), ["x3", "x2", "x1", "x0"])
1161 q = query.Query(fielddefs, ["name"],
1162 qfilter=["|"] + innerfilter + list(reversed(innerfilter)),
1164 self.assertEqual(q.RequestedNames(), ["x0", "x1", "x2", "x3"])
1166 # Unknown name field
1167 self.assertRaises(AssertionError, query.Query, fielddefs, ["name"],
1168 namefield="_unknown_field_")
1171 q = query.Query(fielddefs, ["name"],
1172 qfilter=["|", ["=", "name", "foo"],
1173 ["&", ["=", "name", ""]]],
1175 self.assertTrue(q.RequestedNames() is None)
1178 q = query.Query(fielddefs, ["name"],
1179 qfilter=["|", ["=", "name", "foo"],
1180 ["!", ["=", "name", ""]]],
1182 self.assertTrue(q.RequestedNames() is None)
1184 # Filter with only OR (names must be in correct order)
1185 q = query.Query(fielddefs, ["name"],
1186 qfilter=["|", ["=", "name", "x17361"],
1187 ["|", ["=", "name", "x22015"]],
1188 ["|", ["|", ["=", "name", "x13193"]]],
1189 ["=", "name", "x15215"]],
1191 self.assertEqual(q.RequestedNames(),
1192 ["x17361", "x22015", "x13193", "x15215"])
1195 def _GenNestedFilter(op, depth):
1196 nested = ["=", "name", "value"]
1197 for i in range(depth):
1198 nested = [op, nested]
1201 def testCompileFilter(self):
1202 levels_max = query._FilterCompilerHelper._LEVELS_MAX
1205 [], ["="], ["=", "foo"], ["unknownop"], ["!"],
1206 ["=", "_unknown_field", "value"],
1207 self._GenNestedFilter("|", levels_max),
1208 self._GenNestedFilter("|", levels_max * 3),
1209 self._GenNestedFilter("!", levels_max),
1212 for fielddefs in query.ALL_FIELD_LISTS:
1213 for qfilter in checks:
1214 self.assertRaises(errors.ParameterError, query._CompileFilter,
1215 fielddefs, None, qfilter)
1217 for op in ["|", "!"]:
1218 qfilter = self._GenNestedFilter(op, levels_max - 1)
1219 self.assertTrue(callable(query._CompileFilter(fielddefs, None,
1222 def testQueryInputOrder(self):
1223 fielddefs = query._PrepareFieldList([
1224 (query._MakeField("pnode", "PNode", constants.QFT_TEXT, "Primary"),
1225 None, 0, lambda ctx, item: item["pnode"]),
1226 (query._MakeField("snode", "SNode", constants.QFT_TEXT, "Secondary"),
1227 None, 0, lambda ctx, item: item["snode"]),
1231 { "pnode": "node1", "snode": "node44", },
1232 { "pnode": "node30", "snode": "node90", },
1233 { "pnode": "node25", "snode": "node1", },
1234 { "pnode": "node20", "snode": "node1", },
1237 qfilter = ["|", ["=", "pnode", "node1"], ["=", "snode", "node1"]]
1239 q = query.Query(fielddefs, ["pnode", "snode"], namefield="pnode",
1241 self.assertTrue(q.RequestedNames() is None)
1242 self.assertFalse(q.RequestedData())
1243 self.assertEqual(q.Query(data),
1244 [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1245 [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1246 [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")]])
1248 # Try again with reversed input data
1249 self.assertEqual(q.Query(reversed(data)),
1250 [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1251 [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1252 [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")]])
1254 # No name field, result must be in incoming order
1255 q = query.Query(fielddefs, ["pnode", "snode"], namefield=None,
1257 self.assertFalse(q.RequestedData())
1258 self.assertEqual(q.Query(data),
1259 [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1260 [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1261 [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")]])
1262 self.assertEqual(q.OldStyleQuery(data), [
1263 ["node1", "node44"],
1264 ["node25", "node1"],
1265 ["node20", "node1"],
1267 self.assertEqual(q.Query(reversed(data)),
1268 [[(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1269 [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1270 [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")]])
1271 self.assertEqual(q.OldStyleQuery(reversed(data)), [
1272 ["node20", "node1"],
1273 ["node25", "node1"],
1274 ["node1", "node44"],
1277 # Name field, but no sorting, result must be in incoming order
1278 q = query.Query(fielddefs, ["pnode", "snode"], namefield="pnode")
1279 self.assertFalse(q.RequestedData())
1280 self.assertEqual(q.Query(data, sort_by_name=False),
1281 [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1282 [(constants.RS_NORMAL, "node30"), (constants.RS_NORMAL, "node90")],
1283 [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1284 [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")]])
1285 self.assertEqual(q.OldStyleQuery(data, sort_by_name=False), [
1286 ["node1", "node44"],
1287 ["node30", "node90"],
1288 ["node25", "node1"],
1289 ["node20", "node1"],
1291 self.assertEqual(q.Query(reversed(data), sort_by_name=False),
1292 [[(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1293 [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1294 [(constants.RS_NORMAL, "node30"), (constants.RS_NORMAL, "node90")],
1295 [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")]])
1296 self.assertEqual(q.OldStyleQuery(reversed(data), sort_by_name=False), [
1297 ["node20", "node1"],
1298 ["node25", "node1"],
1299 ["node30", "node90"],
1300 ["node1", "node44"],
1303 def testEqualNamesOrder(self):
1304 fielddefs = query._PrepareFieldList([
1305 (query._MakeField("pnode", "PNode", constants.QFT_TEXT, "Primary"),
1306 None, 0, lambda ctx, item: item["pnode"]),
1307 (query._MakeField("num", "Num", constants.QFT_NUMBER, "Num"),
1308 None, 0, lambda ctx, item: item["num"]),
1312 { "pnode": "node1", "num": 100, },
1313 { "pnode": "node1", "num": 25, },
1314 { "pnode": "node2", "num": 90, },
1315 { "pnode": "node2", "num": 30, },
1318 q = query.Query(fielddefs, ["pnode", "num"], namefield="pnode",
1319 qfilter=["|", ["=", "pnode", "node1"],
1320 ["=", "pnode", "node2"],
1321 ["=", "pnode", "node1"]])
1322 self.assertEqual(q.RequestedNames(), ["node1", "node2"],
1323 msg="Did not return unique names")
1324 self.assertFalse(q.RequestedData())
1325 self.assertEqual(q.Query(data),
1326 [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 100)],
1327 [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 25)],
1328 [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 90)],
1329 [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 30)]])
1330 self.assertEqual(q.Query(data, sort_by_name=False),
1331 [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 100)],
1332 [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 25)],
1333 [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 90)],
1334 [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 30)]])
1337 { "pnode": "nodeX", "num": 50, },
1338 { "pnode": "nodeY", "num": 40, },
1339 { "pnode": "nodeX", "num": 30, },
1340 { "pnode": "nodeX", "num": 20, },
1341 { "pnode": "nodeM", "num": 10, },
1344 q = query.Query(fielddefs, ["pnode", "num"], namefield="pnode",
1345 qfilter=["|", ["=", "pnode", "nodeX"],
1346 ["=", "pnode", "nodeY"],
1347 ["=", "pnode", "nodeY"],
1348 ["=", "pnode", "nodeY"],
1349 ["=", "pnode", "nodeM"]])
1350 self.assertEqual(q.RequestedNames(), ["nodeX", "nodeY", "nodeM"],
1351 msg="Did not return unique names")
1352 self.assertFalse(q.RequestedData())
1354 # First sorted by name, then input order
1355 self.assertEqual(q.Query(data, sort_by_name=True),
1356 [[(constants.RS_NORMAL, "nodeM"), (constants.RS_NORMAL, 10)],
1357 [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 50)],
1358 [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 30)],
1359 [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 20)],
1360 [(constants.RS_NORMAL, "nodeY"), (constants.RS_NORMAL, 40)]])
1363 self.assertEqual(q.Query(data, sort_by_name=False),
1364 [[(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 50)],
1365 [(constants.RS_NORMAL, "nodeY"), (constants.RS_NORMAL, 40)],
1366 [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 30)],
1367 [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 20)],
1368 [(constants.RS_NORMAL, "nodeM"), (constants.RS_NORMAL, 10)]])
1370 def testFilter(self):
1371 (DK_A, DK_B) = range(1000, 1002)
1373 fielddefs = query._PrepareFieldList([
1374 (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1375 DK_A, 0, lambda ctx, item: item["name"]),
1376 (query._MakeField("other", "Other", constants.QFT_TEXT, "Other"),
1377 DK_B, 0, lambda ctx, item: item["other"]),
1381 { "name": "node1", "other": "foo", },
1382 { "name": "node2", "other": "bar", },
1383 { "name": "node3", "other": "Hello", },
1387 q = query.Query(fielddefs, ["name", "other"], namefield="name",
1389 self.assertTrue(q.RequestedNames() is None)
1390 self.assertEqual(q.RequestedData(), set([DK_A, DK_B]))
1391 self.assertEqual(q.Query(data), [])
1394 q = query.Query(fielddefs, ["name", "other"], namefield="name",
1395 qfilter=["=", "name", "node1"])
1396 self.assertEqual(q.RequestedNames(), ["node1"])
1397 self.assertEqual(q.Query(data),
1398 [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")]])
1400 q = query.Query(fielddefs, ["name", "other"], namefield="name",
1401 qfilter=(["|", ["=", "name", "node1"],
1402 ["=", "name", "node3"]]))
1403 self.assertEqual(q.RequestedNames(), ["node1", "node3"])
1404 self.assertEqual(q.Query(data),
1405 [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")],
1406 [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, "Hello")]])
1409 q = query.Query(fielddefs, ["name", "other"], namefield="name",
1410 qfilter=(["|", ["=", "name", "node1"],
1411 ["|", ["=", "name", "node3"],
1412 ["=", "name", "node2"]],
1413 ["=", "name", "node3"]]))
1414 self.assertEqual(q.RequestedNames(), ["node1", "node3", "node2"])
1415 self.assertEqual(q.RequestedData(), set([DK_A, DK_B]))
1416 self.assertEqual(q.Query(data),
1417 [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")],
1418 [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, "bar")],
1419 [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, "Hello")]])
1421 # Filter data type mismatch
1422 for i in [-1, 0, 1, 123, [], None, True, False]:
1423 self.assertRaises(errors.ParameterError, query.Query,
1424 fielddefs, ["name", "other"], namefield="name",
1425 qfilter=["=", "name", i])
1428 q = query.Query(fielddefs, ["name", "other"], namefield="name",
1429 qfilter=["!", ["|", ["=", "name", "node1"],
1430 ["=", "name", "node3"]]])
1431 self.assertTrue(q.RequestedNames() is None)
1432 self.assertEqual(q.Query(data),
1433 [[(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, "bar")]])
1436 q = query.Query(fielddefs, ["name", "other"], namefield="name",
1437 qfilter=["!=", "name", "node3"])
1438 self.assertTrue(q.RequestedNames() is None)
1439 self.assertEqual(q.Query(data),
1440 [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")],
1441 [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, "bar")]])
1444 q = query.Query(fielddefs, [], namefield="name",
1445 qfilter=["|", ["=", "other", "bar"],
1446 ["=", "name", "foo"]])
1447 self.assertTrue(q.RequestedNames() is None)
1448 self.assertEqual(q.RequestedData(), set([DK_A, DK_B]))
1449 self.assertEqual(q.Query(data), [[]])
1451 # Only one data type
1452 q = query.Query(fielddefs, ["other"], namefield="name",
1453 qfilter=["=", "other", "bar"])
1454 self.assertTrue(q.RequestedNames() is None)
1455 self.assertEqual(q.RequestedData(), set([DK_B]))
1456 self.assertEqual(q.Query(data), [[(constants.RS_NORMAL, "bar")]])
1458 q = query.Query(fielddefs, [], namefield="name",
1459 qfilter=["=", "other", "bar"])
1460 self.assertTrue(q.RequestedNames() is None)
1461 self.assertEqual(q.RequestedData(), set([DK_B]))
1462 self.assertEqual(q.Query(data), [[]])
1464 def testFilterContains(self):
1465 fielddefs = query._PrepareFieldList([
1466 (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1467 None, 0, lambda ctx, item: item["name"]),
1468 (query._MakeField("other", "Other", constants.QFT_OTHER, "Other"),
1469 None, 0, lambda ctx, item: item["other"]),
1473 { "name": "node2", "other": ["x", "y", "bar"], },
1474 { "name": "node3", "other": "Hello", },
1475 { "name": "node1", "other": ["a", "b", "foo"], },
1476 { "name": "empty", "other": []},
1479 q = query.Query(fielddefs, ["name", "other"], namefield="name",
1480 qfilter=["=[]", "other", "bar"])
1481 self.assertTrue(q.RequestedNames() is None)
1482 self.assertEqual(q.Query(data), [
1483 [(constants.RS_NORMAL, "node2"),
1484 (constants.RS_NORMAL, ["x", "y", "bar"])],
1487 q = query.Query(fielddefs, ["name", "other"], namefield="name",
1488 qfilter=["|", ["=[]", "other", "bar"],
1489 ["=[]", "other", "a"],
1490 ["=[]", "other", "b"]])
1491 self.assertTrue(q.RequestedNames() is None)
1492 self.assertEqual(q.Query(data), [
1493 [(constants.RS_NORMAL, "node1"),
1494 (constants.RS_NORMAL, ["a", "b", "foo"])],
1495 [(constants.RS_NORMAL, "node2"),
1496 (constants.RS_NORMAL, ["x", "y", "bar"])],
1498 self.assertEqual(q.OldStyleQuery(data), [
1499 ["node1", ["a", "b", "foo"]],
1500 ["node2", ["x", "y", "bar"]],
1504 q = query.Query(fielddefs, ["name", "other"], namefield="name",
1505 qfilter=["?", "other"])
1506 self.assertEqual(q.OldStyleQuery(data), [
1507 ["node1", ["a", "b", "foo"]],
1508 ["node2", ["x", "y", "bar"]],
1512 q = query.Query(fielddefs, ["name", "other"], namefield="name",
1513 qfilter=["!", ["?", "other"]])
1514 self.assertEqual(q.OldStyleQuery(data), [
1518 def testFilterHostname(self):
1519 fielddefs = query._PrepareFieldList([
1520 (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1521 None, query.QFF_HOSTNAME, lambda ctx, item: item["name"]),
1525 { "name": "node1.example.com", },
1526 { "name": "node2.example.com", },
1527 { "name": "node2.example.net", },
1530 q = query.Query(fielddefs, ["name"], namefield="name",
1531 qfilter=["=", "name", "node2"])
1532 self.assertEqual(q.RequestedNames(), ["node2"])
1533 self.assertEqual(q.Query(data), [
1534 [(constants.RS_NORMAL, "node2.example.com")],
1535 [(constants.RS_NORMAL, "node2.example.net")],
1538 q = query.Query(fielddefs, ["name"], namefield="name",
1539 qfilter=["=", "name", "node1"])
1540 self.assertEqual(q.RequestedNames(), ["node1"])
1541 self.assertEqual(q.Query(data), [
1542 [(constants.RS_NORMAL, "node1.example.com")],
1545 q = query.Query(fielddefs, ["name"], namefield="name",
1546 qfilter=["=", "name", "othername"])
1547 self.assertEqual(q.RequestedNames(), ["othername"])
1548 self.assertEqual(q.Query(data), [])
1550 q = query.Query(fielddefs, ["name"], namefield="name",
1551 qfilter=["|", ["=", "name", "node1.example.com"],
1552 ["=", "name", "node2"]])
1553 self.assertEqual(q.RequestedNames(), ["node1.example.com", "node2"])
1554 self.assertEqual(q.Query(data), [
1555 [(constants.RS_NORMAL, "node1.example.com")],
1556 [(constants.RS_NORMAL, "node2.example.com")],
1557 [(constants.RS_NORMAL, "node2.example.net")],
1559 self.assertEqual(q.OldStyleQuery(data), [
1560 ["node1.example.com"],
1561 ["node2.example.com"],
1562 ["node2.example.net"],
1565 q = query.Query(fielddefs, ["name"], namefield="name",
1566 qfilter=["!=", "name", "node1"])
1567 self.assertTrue(q.RequestedNames() is None)
1568 self.assertEqual(q.Query(data), [
1569 [(constants.RS_NORMAL, "node2.example.com")],
1570 [(constants.RS_NORMAL, "node2.example.net")],
1572 self.assertEqual(q.OldStyleQuery(data), [
1573 ["node2.example.com"],
1574 ["node2.example.net"],
1577 def testFilterBoolean(self):
1578 fielddefs = query._PrepareFieldList([
1579 (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1580 None, query.QFF_HOSTNAME, lambda ctx, item: item["name"]),
1581 (query._MakeField("value", "Value", constants.QFT_BOOL, "Value"),
1582 None, 0, lambda ctx, item: item["value"]),
1586 { "name": "node1", "value": False, },
1587 { "name": "node2", "value": True, },
1588 { "name": "node3", "value": True, },
1591 q = query.Query(fielddefs, ["name", "value"],
1592 qfilter=["|", ["=", "value", False],
1593 ["=", "value", True]])
1594 self.assertTrue(q.RequestedNames() is None)
1595 self.assertEqual(q.Query(data), [
1596 [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1597 [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1598 [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1601 q = query.Query(fielddefs, ["name", "value"],
1602 qfilter=["|", ["=", "value", False],
1603 ["!", ["=", "value", False]]])
1604 self.assertTrue(q.RequestedNames() is None)
1605 self.assertEqual(q.Query(data), [
1606 [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1607 [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1608 [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1611 # Comparing bool with string
1612 for i in ["False", "True", "0", "1", "no", "yes", "N", "Y"]:
1613 self.assertRaises(errors.ParameterError, query.Query,
1614 fielddefs, ["name", "value"],
1615 qfilter=["=", "value", i])
1618 q = query.Query(fielddefs, ["name", "value"], qfilter=["?", "value"])
1619 self.assertTrue(q.RequestedNames() is None)
1620 self.assertEqual(q.Query(data), [
1621 [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1622 [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1625 # Negative bool filter
1626 q = query.Query(fielddefs, ["name", "value"], qfilter=["!", ["?", "value"]])
1627 self.assertTrue(q.RequestedNames() is None)
1628 self.assertEqual(q.Query(data), [
1629 [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1632 # Complex truth filter
1633 q = query.Query(fielddefs, ["name", "value"],
1634 qfilter=["|", ["&", ["=", "name", "node1"],
1635 ["!", ["?", "value"]]],
1637 self.assertTrue(q.RequestedNames() is None)
1638 self.assertEqual(q.Query(data), [
1639 [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1640 [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1641 [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1644 def testFilterRegex(self):
1645 fielddefs = query._PrepareFieldList([
1646 (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1647 None, 0, lambda ctx, item: item["name"]),
1651 { "name": "node1.example.com", },
1652 { "name": "node2.site.example.com", },
1653 { "name": "node2.example.net", },
1659 q = query.Query(fielddefs, ["name"], namefield="name",
1660 qfilter=["=~", "name", "site"])
1661 self.assertTrue(q.RequestedNames() is None)
1662 self.assertEqual(q.Query(data), [
1663 [(constants.RS_NORMAL, "node2.site.example.com")],
1666 q = query.Query(fielddefs, ["name"], namefield="name",
1667 qfilter=["=~", "name", "^node2"])
1668 self.assertTrue(q.RequestedNames() is None)
1669 self.assertEqual(q.Query(data), [
1670 [(constants.RS_NORMAL, "node2.example.net")],
1671 [(constants.RS_NORMAL, "node2.site.example.com")],
1674 q = query.Query(fielddefs, ["name"], namefield="name",
1675 qfilter=["=~", "name", r"(?i)\.COM$"])
1676 self.assertTrue(q.RequestedNames() is None)
1677 self.assertEqual(q.Query(data), [
1678 [(constants.RS_NORMAL, "node1.example.com")],
1679 [(constants.RS_NORMAL, "node2.site.example.com")],
1682 q = query.Query(fielddefs, ["name"], namefield="name",
1683 qfilter=["=~", "name", r"."])
1684 self.assertTrue(q.RequestedNames() is None)
1685 self.assertEqual(q.Query(data), [
1686 [(constants.RS_NORMAL, "node1.example.com")],
1687 [(constants.RS_NORMAL, "node2.example.net")],
1688 [(constants.RS_NORMAL, "node2.site.example.com")],
1691 q = query.Query(fielddefs, ["name"], namefield="name",
1692 qfilter=["=~", "name", r"^$"])
1693 self.assertTrue(q.RequestedNames() is None)
1694 self.assertEqual(q.Query(data), [
1695 [(constants.RS_NORMAL, "")],
1698 # Invalid regular expression
1699 self.assertRaises(errors.ParameterError, query.Query, fielddefs, ["name"],
1700 qfilter=["=~", "name", r"["])
1703 if __name__ == "__main__":
1704 testutils.GanetiTestProgram()