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):
958 self.custom_diskparams = {
959 constants.DT_DRBD8: {
960 constants.DRBD_DEFAULT_METAVG: "foobar",
965 objects.NodeGroup(name="default",
966 uuid="c0e89160-18e7-11e0-a46e-001d0904baeb",
967 alloc_policy=constants.ALLOC_POLICY_PREFERRED,
968 ipolicy=objects.MakeEmptyIPolicy(),
972 objects.NodeGroup(name="restricted",
973 uuid="d2a40a74-18e7-11e0-9143-001d0904baeb",
974 alloc_policy=constants.ALLOC_POLICY_LAST_RESORT,
975 ipolicy=objects.MakeEmptyIPolicy(),
977 diskparams=self.custom_diskparams,
980 self.cluster = objects.Cluster(cluster_name="testcluster",
981 hvparams=constants.HVC_DEFAULTS,
983 constants.PP_DEFAULT: constants.BEC_DEFAULTS,
986 constants.PP_DEFAULT: constants.NICC_DEFAULTS,
988 ndparams=constants.NDC_DEFAULTS,
989 ipolicy=constants.IPOLICY_DEFAULTS,
990 diskparams=constants.DISK_DT_DEFAULTS,
993 def _Create(self, selected):
994 return query.Query(query.GROUP_FIELDS, selected)
996 def testSimple(self):
997 q = self._Create(["name", "uuid", "alloc_policy"])
998 gqd = query.GroupQueryData(self.cluster, self.groups, None, None, False)
1000 self.assertEqual(q.RequestedData(), set([query.GQ_CONFIG]))
1002 self.assertEqual(q.Query(gqd),
1003 [[(constants.RS_NORMAL, "default"),
1004 (constants.RS_NORMAL, "c0e89160-18e7-11e0-a46e-001d0904baeb"),
1005 (constants.RS_NORMAL, constants.ALLOC_POLICY_PREFERRED)
1007 [(constants.RS_NORMAL, "restricted"),
1008 (constants.RS_NORMAL, "d2a40a74-18e7-11e0-9143-001d0904baeb"),
1009 (constants.RS_NORMAL, constants.ALLOC_POLICY_LAST_RESORT)
1013 def testNodes(self):
1015 "c0e89160-18e7-11e0-a46e-001d0904baeb": ["node1", "node2"],
1016 "d2a40a74-18e7-11e0-9143-001d0904baeb": ["node1", "node10", "node9"],
1019 q = self._Create(["name", "node_cnt", "node_list"])
1020 gqd = query.GroupQueryData(self.cluster, self.groups, groups_to_nodes, None,
1023 self.assertEqual(q.RequestedData(), set([query.GQ_CONFIG, query.GQ_NODE]))
1025 self.assertEqual(q.Query(gqd),
1026 [[(constants.RS_NORMAL, "default"),
1027 (constants.RS_NORMAL, 2),
1028 (constants.RS_NORMAL, ["node1", "node2"]),
1030 [(constants.RS_NORMAL, "restricted"),
1031 (constants.RS_NORMAL, 3),
1032 (constants.RS_NORMAL, ["node1", "node9", "node10"]),
1036 def testInstances(self):
1037 groups_to_instances = {
1038 "c0e89160-18e7-11e0-a46e-001d0904baeb": ["inst1", "inst2"],
1039 "d2a40a74-18e7-11e0-9143-001d0904baeb": ["inst1", "inst10", "inst9"],
1042 q = self._Create(["pinst_cnt", "pinst_list"])
1043 gqd = query.GroupQueryData(self.cluster, self.groups, None,
1044 groups_to_instances, False)
1046 self.assertEqual(q.RequestedData(), set([query.GQ_INST]))
1048 self.assertEqual(q.Query(gqd),
1049 [[(constants.RS_NORMAL, 2),
1050 (constants.RS_NORMAL, ["inst1", "inst2"]),
1052 [(constants.RS_NORMAL, 3),
1053 (constants.RS_NORMAL, ["inst1", "inst9", "inst10"]),
1057 def testDiskparams(self):
1058 q = self._Create(["name", "uuid", "diskparams", "custom_diskparams"])
1059 gqd = query.GroupQueryData(self.cluster, self.groups, None, None, True)
1061 self.assertEqual(q.RequestedData(),
1062 set([query.GQ_CONFIG, query.GQ_DISKPARAMS]))
1064 self.assertEqual(q.Query(gqd),
1065 [[(constants.RS_NORMAL, "default"),
1066 (constants.RS_NORMAL, "c0e89160-18e7-11e0-a46e-001d0904baeb"),
1067 (constants.RS_NORMAL, constants.DISK_DT_DEFAULTS),
1068 (constants.RS_NORMAL, {}),
1070 [(constants.RS_NORMAL, "restricted"),
1071 (constants.RS_NORMAL, "d2a40a74-18e7-11e0-9143-001d0904baeb"),
1072 (constants.RS_NORMAL, objects.FillDiskParams(constants.DISK_DT_DEFAULTS,
1073 self.custom_diskparams)),
1074 (constants.RS_NORMAL, self.custom_diskparams),
1079 class TestOsQuery(unittest.TestCase):
1080 def _Create(self, selected):
1081 return query.Query(query.OS_FIELDS, selected)
1084 variants = ["v00", "plain", "v3", "var0", "v33", "v20"]
1085 api_versions = [10, 0, 15, 5]
1086 parameters = ["zpar3", "apar9"]
1088 assert variants != sorted(variants) and variants != utils.NiceSort(variants)
1089 assert (api_versions != sorted(api_versions) and
1090 api_versions != utils.NiceSort(variants))
1091 assert (parameters != sorted(parameters) and
1092 parameters != utils.NiceSort(parameters))
1095 query.OsInfo(name="debian", valid=False, hidden=False, blacklisted=False,
1096 variants=set(), api_versions=set(), parameters=set(),
1097 node_status={ "some": "status", }),
1098 query.OsInfo(name="dos", valid=True, hidden=False, blacklisted=True,
1099 variants=set(variants),
1100 api_versions=set(api_versions),
1101 parameters=set(parameters),
1102 node_status={ "some": "other", "status": None, }),
1106 q = self._Create(["name", "valid", "hidden", "blacklisted", "variants",
1107 "api_versions", "parameters", "node_status"])
1108 self.assertEqual(q.RequestedData(), set([]))
1109 self.assertEqual(q.Query(data),
1110 [[(constants.RS_NORMAL, "debian"),
1111 (constants.RS_NORMAL, False),
1112 (constants.RS_NORMAL, False),
1113 (constants.RS_NORMAL, False),
1114 (constants.RS_NORMAL, []),
1115 (constants.RS_NORMAL, []),
1116 (constants.RS_NORMAL, []),
1117 (constants.RS_NORMAL, {"some": "status"})],
1118 [(constants.RS_NORMAL, "dos"),
1119 (constants.RS_NORMAL, True),
1120 (constants.RS_NORMAL, False),
1121 (constants.RS_NORMAL, True),
1122 (constants.RS_NORMAL,
1123 ["plain", "v00", "v3", "v20", "v33", "var0"]),
1124 (constants.RS_NORMAL, [0, 5, 10, 15]),
1125 (constants.RS_NORMAL, ["apar9", "zpar3"]),
1126 (constants.RS_NORMAL,
1127 { "some": "other", "status": None, })
1131 class TestQueryFields(unittest.TestCase):
1132 def testAllFields(self):
1133 for fielddefs in query.ALL_FIELD_LISTS:
1134 result = query.QueryFields(fielddefs, None)
1135 self.assert_(isinstance(result, dict))
1136 response = objects.QueryFieldsResponse.FromDict(result)
1137 self.assertEqual([(fdef.name, fdef.title) for fdef in response.fields],
1138 [(fdef2.name, fdef2.title)
1139 for (fdef2, _, _, _) in utils.NiceSort(fielddefs.values(),
1140 key=lambda x: x[0].name)])
1142 def testSomeFields(self):
1143 rnd = random.Random(5357)
1146 for fielddefs in query.ALL_FIELD_LISTS:
1147 if len(fielddefs) > 20:
1148 sample_size = rnd.randint(5, 20)
1150 sample_size = rnd.randint(1, max(1, len(fielddefs) - 1))
1151 fields = [fdef for (fdef, _, _, _) in rnd.sample(fielddefs.values(),
1153 result = query.QueryFields(fielddefs, [fdef.name for fdef in fields])
1154 self.assert_(isinstance(result, dict))
1155 response = objects.QueryFieldsResponse.FromDict(result)
1156 self.assertEqual([(fdef.name, fdef.title) for fdef in response.fields],
1157 [(fdef2.name, fdef2.title) for fdef2 in fields])
1160 class TestQueryFilter(unittest.TestCase):
1161 def testRequestedNames(self):
1162 for (what, fielddefs) in query.ALL_FIELDS.items():
1163 if what == constants.QR_JOB:
1165 elif what == constants.QR_EXPORT:
1166 namefield = "export"
1170 assert namefield in fielddefs
1172 innerfilter = [["=", namefield, "x%s" % i] for i in range(4)]
1175 q = query.Query(fielddefs, [namefield], qfilter=["=", namefield, "abc"],
1177 self.assertEqual(q.RequestedNames(), None)
1180 q = query.Query(fielddefs, [namefield], qfilter=None, namefield=namefield)
1181 self.assertEqual(q.RequestedNames(), None)
1184 q = query.Query(fielddefs, [namefield], qfilter=["|"],
1185 namefield=namefield)
1186 self.assertEqual(q.RequestedNames(), None)
1189 q = query.Query(fielddefs, [namefield], qfilter=["|"] + innerfilter,
1190 namefield=namefield)
1191 self.assertEqual(q.RequestedNames(), ["x0", "x1", "x2", "x3"])
1193 # Check reverse order
1194 q = query.Query(fielddefs, [namefield],
1195 qfilter=["|"] + list(reversed(innerfilter)),
1196 namefield=namefield)
1197 self.assertEqual(q.RequestedNames(), ["x3", "x2", "x1", "x0"])
1200 q = query.Query(fielddefs, [namefield],
1201 qfilter=["|"] + innerfilter + list(reversed(innerfilter)),
1202 namefield=namefield)
1203 self.assertEqual(q.RequestedNames(), ["x0", "x1", "x2", "x3"])
1205 # Unknown name field
1206 self.assertRaises(AssertionError, query.Query, fielddefs, [namefield],
1207 namefield="_unknown_field_")
1210 q = query.Query(fielddefs, [namefield],
1211 qfilter=["|", ["=", namefield, "foo"],
1212 ["&", ["=", namefield, ""]]],
1213 namefield=namefield)
1214 self.assertTrue(q.RequestedNames() is None)
1217 q = query.Query(fielddefs, [namefield],
1218 qfilter=["|", ["=", namefield, "foo"],
1219 ["!", ["=", namefield, ""]]],
1220 namefield=namefield)
1221 self.assertTrue(q.RequestedNames() is None)
1223 # Filter with only OR (names must be in correct order)
1224 q = query.Query(fielddefs, [namefield],
1225 qfilter=["|", ["=", namefield, "x17361"],
1226 ["|", ["=", namefield, "x22015"]],
1227 ["|", ["|", ["=", namefield, "x13193"]]],
1228 ["=", namefield, "x15215"]],
1229 namefield=namefield)
1230 self.assertEqual(q.RequestedNames(),
1231 ["x17361", "x22015", "x13193", "x15215"])
1234 def _GenNestedFilter(namefield, op, depth):
1235 nested = ["=", namefield, "value"]
1236 for i in range(depth):
1237 nested = [op, nested]
1240 def testCompileFilter(self):
1241 levels_max = query._FilterCompilerHelper._LEVELS_MAX
1243 for (what, fielddefs) in query.ALL_FIELDS.items():
1244 if what == constants.QR_JOB:
1246 elif what == constants.QR_EXPORT:
1247 namefield = "export"
1252 [], ["="], ["=", "foo"], ["unknownop"], ["!"],
1253 ["=", "_unknown_field", "value"],
1254 self._GenNestedFilter(namefield, "|", levels_max),
1255 self._GenNestedFilter(namefield, "|", levels_max * 3),
1256 self._GenNestedFilter(namefield, "!", levels_max),
1259 for qfilter in checks:
1260 self.assertRaises(errors.ParameterError, query._CompileFilter,
1261 fielddefs, None, qfilter)
1263 for op in ["|", "!"]:
1264 qfilter = self._GenNestedFilter(namefield, op, levels_max - 1)
1265 self.assertTrue(callable(query._CompileFilter(fielddefs, None,
1268 def testQueryInputOrder(self):
1269 fielddefs = query._PrepareFieldList([
1270 (query._MakeField("pnode", "PNode", constants.QFT_TEXT, "Primary"),
1271 None, 0, lambda ctx, item: item["pnode"]),
1272 (query._MakeField("snode", "SNode", constants.QFT_TEXT, "Secondary"),
1273 None, 0, lambda ctx, item: item["snode"]),
1277 { "pnode": "node1", "snode": "node44", },
1278 { "pnode": "node30", "snode": "node90", },
1279 { "pnode": "node25", "snode": "node1", },
1280 { "pnode": "node20", "snode": "node1", },
1283 qfilter = ["|", ["=", "pnode", "node1"], ["=", "snode", "node1"]]
1285 q = query.Query(fielddefs, ["pnode", "snode"], namefield="pnode",
1287 self.assertTrue(q.RequestedNames() is None)
1288 self.assertFalse(q.RequestedData())
1289 self.assertEqual(q.Query(data),
1290 [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1291 [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1292 [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")]])
1294 # Try again with reversed input data
1295 self.assertEqual(q.Query(reversed(data)),
1296 [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1297 [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1298 [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")]])
1300 # No name field, result must be in incoming order
1301 q = query.Query(fielddefs, ["pnode", "snode"], namefield=None,
1303 self.assertFalse(q.RequestedData())
1304 self.assertEqual(q.Query(data),
1305 [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1306 [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1307 [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")]])
1308 self.assertEqual(q.OldStyleQuery(data), [
1309 ["node1", "node44"],
1310 ["node25", "node1"],
1311 ["node20", "node1"],
1313 self.assertEqual(q.Query(reversed(data)),
1314 [[(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1315 [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1316 [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")]])
1317 self.assertEqual(q.OldStyleQuery(reversed(data)), [
1318 ["node20", "node1"],
1319 ["node25", "node1"],
1320 ["node1", "node44"],
1323 # Name field, but no sorting, result must be in incoming order
1324 q = query.Query(fielddefs, ["pnode", "snode"], namefield="pnode")
1325 self.assertFalse(q.RequestedData())
1326 self.assertEqual(q.Query(data, sort_by_name=False),
1327 [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")],
1328 [(constants.RS_NORMAL, "node30"), (constants.RS_NORMAL, "node90")],
1329 [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1330 [(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")]])
1331 self.assertEqual(q.OldStyleQuery(data, sort_by_name=False), [
1332 ["node1", "node44"],
1333 ["node30", "node90"],
1334 ["node25", "node1"],
1335 ["node20", "node1"],
1337 self.assertEqual(q.Query(reversed(data), sort_by_name=False),
1338 [[(constants.RS_NORMAL, "node20"), (constants.RS_NORMAL, "node1")],
1339 [(constants.RS_NORMAL, "node25"), (constants.RS_NORMAL, "node1")],
1340 [(constants.RS_NORMAL, "node30"), (constants.RS_NORMAL, "node90")],
1341 [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "node44")]])
1342 self.assertEqual(q.OldStyleQuery(reversed(data), sort_by_name=False), [
1343 ["node20", "node1"],
1344 ["node25", "node1"],
1345 ["node30", "node90"],
1346 ["node1", "node44"],
1349 def testEqualNamesOrder(self):
1350 fielddefs = query._PrepareFieldList([
1351 (query._MakeField("pnode", "PNode", constants.QFT_TEXT, "Primary"),
1352 None, 0, lambda ctx, item: item["pnode"]),
1353 (query._MakeField("num", "Num", constants.QFT_NUMBER, "Num"),
1354 None, 0, lambda ctx, item: item["num"]),
1358 { "pnode": "node1", "num": 100, },
1359 { "pnode": "node1", "num": 25, },
1360 { "pnode": "node2", "num": 90, },
1361 { "pnode": "node2", "num": 30, },
1364 q = query.Query(fielddefs, ["pnode", "num"], namefield="pnode",
1365 qfilter=["|", ["=", "pnode", "node1"],
1366 ["=", "pnode", "node2"],
1367 ["=", "pnode", "node1"]])
1368 self.assertEqual(q.RequestedNames(), ["node1", "node2"],
1369 msg="Did not return unique names")
1370 self.assertFalse(q.RequestedData())
1371 self.assertEqual(q.Query(data),
1372 [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 100)],
1373 [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 25)],
1374 [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 90)],
1375 [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 30)]])
1376 self.assertEqual(q.Query(data, sort_by_name=False),
1377 [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 100)],
1378 [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, 25)],
1379 [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 90)],
1380 [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, 30)]])
1383 { "pnode": "nodeX", "num": 50, },
1384 { "pnode": "nodeY", "num": 40, },
1385 { "pnode": "nodeX", "num": 30, },
1386 { "pnode": "nodeX", "num": 20, },
1387 { "pnode": "nodeM", "num": 10, },
1390 q = query.Query(fielddefs, ["pnode", "num"], namefield="pnode",
1391 qfilter=["|", ["=", "pnode", "nodeX"],
1392 ["=", "pnode", "nodeY"],
1393 ["=", "pnode", "nodeY"],
1394 ["=", "pnode", "nodeY"],
1395 ["=", "pnode", "nodeM"]])
1396 self.assertEqual(q.RequestedNames(), ["nodeX", "nodeY", "nodeM"],
1397 msg="Did not return unique names")
1398 self.assertFalse(q.RequestedData())
1400 # First sorted by name, then input order
1401 self.assertEqual(q.Query(data, sort_by_name=True),
1402 [[(constants.RS_NORMAL, "nodeM"), (constants.RS_NORMAL, 10)],
1403 [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 50)],
1404 [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 30)],
1405 [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 20)],
1406 [(constants.RS_NORMAL, "nodeY"), (constants.RS_NORMAL, 40)]])
1409 self.assertEqual(q.Query(data, sort_by_name=False),
1410 [[(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 50)],
1411 [(constants.RS_NORMAL, "nodeY"), (constants.RS_NORMAL, 40)],
1412 [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 30)],
1413 [(constants.RS_NORMAL, "nodeX"), (constants.RS_NORMAL, 20)],
1414 [(constants.RS_NORMAL, "nodeM"), (constants.RS_NORMAL, 10)]])
1416 def testFilter(self):
1417 (DK_A, DK_B) = range(1000, 1002)
1419 fielddefs = query._PrepareFieldList([
1420 (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1421 DK_A, 0, lambda ctx, item: item["name"]),
1422 (query._MakeField("other", "Other", constants.QFT_TEXT, "Other"),
1423 DK_B, 0, lambda ctx, item: item["other"]),
1427 { "name": "node1", "other": "foo", },
1428 { "name": "node2", "other": "bar", },
1429 { "name": "node3", "other": "Hello", },
1433 q = query.Query(fielddefs, ["name", "other"], namefield="name",
1435 self.assertTrue(q.RequestedNames() is None)
1436 self.assertEqual(q.RequestedData(), set([DK_A, DK_B]))
1437 self.assertEqual(q.Query(data), [])
1440 q = query.Query(fielddefs, ["name", "other"], namefield="name",
1441 qfilter=["=", "name", "node1"])
1442 self.assertEqual(q.RequestedNames(), ["node1"])
1443 self.assertEqual(q.Query(data),
1444 [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")]])
1446 q = query.Query(fielddefs, ["name", "other"], namefield="name",
1447 qfilter=(["|", ["=", "name", "node1"],
1448 ["=", "name", "node3"]]))
1449 self.assertEqual(q.RequestedNames(), ["node1", "node3"])
1450 self.assertEqual(q.Query(data),
1451 [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")],
1452 [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, "Hello")]])
1455 q = query.Query(fielddefs, ["name", "other"], namefield="name",
1456 qfilter=(["|", ["=", "name", "node1"],
1457 ["|", ["=", "name", "node3"],
1458 ["=", "name", "node2"]],
1459 ["=", "name", "node3"]]))
1460 self.assertEqual(q.RequestedNames(), ["node1", "node3", "node2"])
1461 self.assertEqual(q.RequestedData(), set([DK_A, DK_B]))
1462 self.assertEqual(q.Query(data),
1463 [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")],
1464 [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, "bar")],
1465 [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, "Hello")]])
1467 # Filter data type mismatch
1468 for i in [-1, 0, 1, 123, [], None, True, False]:
1469 self.assertRaises(errors.ParameterError, query.Query,
1470 fielddefs, ["name", "other"], namefield="name",
1471 qfilter=["=", "name", i])
1474 q = query.Query(fielddefs, ["name", "other"], namefield="name",
1475 qfilter=["!", ["|", ["=", "name", "node1"],
1476 ["=", "name", "node3"]]])
1477 self.assertTrue(q.RequestedNames() is None)
1478 self.assertEqual(q.Query(data),
1479 [[(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, "bar")]])
1482 q = query.Query(fielddefs, ["name", "other"], namefield="name",
1483 qfilter=["!=", "name", "node3"])
1484 self.assertTrue(q.RequestedNames() is None)
1485 self.assertEqual(q.Query(data),
1486 [[(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, "foo")],
1487 [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, "bar")]])
1490 q = query.Query(fielddefs, [], namefield="name",
1491 qfilter=["|", ["=", "other", "bar"],
1492 ["=", "name", "foo"]])
1493 self.assertTrue(q.RequestedNames() is None)
1494 self.assertEqual(q.RequestedData(), set([DK_A, DK_B]))
1495 self.assertEqual(q.Query(data), [[]])
1497 # Only one data type
1498 q = query.Query(fielddefs, ["other"], namefield="name",
1499 qfilter=["=", "other", "bar"])
1500 self.assertTrue(q.RequestedNames() is None)
1501 self.assertEqual(q.RequestedData(), set([DK_B]))
1502 self.assertEqual(q.Query(data), [[(constants.RS_NORMAL, "bar")]])
1504 q = query.Query(fielddefs, [], namefield="name",
1505 qfilter=["=", "other", "bar"])
1506 self.assertTrue(q.RequestedNames() is None)
1507 self.assertEqual(q.RequestedData(), set([DK_B]))
1508 self.assertEqual(q.Query(data), [[]])
1510 def testFilterContains(self):
1511 fielddefs = query._PrepareFieldList([
1512 (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1513 None, 0, lambda ctx, item: item["name"]),
1514 (query._MakeField("other", "Other", constants.QFT_OTHER, "Other"),
1515 None, 0, lambda ctx, item: item["other"]),
1519 { "name": "node2", "other": ["x", "y", "bar"], },
1520 { "name": "node3", "other": "Hello", },
1521 { "name": "node1", "other": ["a", "b", "foo"], },
1522 { "name": "empty", "other": []},
1525 q = query.Query(fielddefs, ["name", "other"], namefield="name",
1526 qfilter=["=[]", "other", "bar"])
1527 self.assertTrue(q.RequestedNames() is None)
1528 self.assertEqual(q.Query(data), [
1529 [(constants.RS_NORMAL, "node2"),
1530 (constants.RS_NORMAL, ["x", "y", "bar"])],
1533 q = query.Query(fielddefs, ["name", "other"], namefield="name",
1534 qfilter=["|", ["=[]", "other", "bar"],
1535 ["=[]", "other", "a"],
1536 ["=[]", "other", "b"]])
1537 self.assertTrue(q.RequestedNames() is None)
1538 self.assertEqual(q.Query(data), [
1539 [(constants.RS_NORMAL, "node1"),
1540 (constants.RS_NORMAL, ["a", "b", "foo"])],
1541 [(constants.RS_NORMAL, "node2"),
1542 (constants.RS_NORMAL, ["x", "y", "bar"])],
1544 self.assertEqual(q.OldStyleQuery(data), [
1545 ["node1", ["a", "b", "foo"]],
1546 ["node2", ["x", "y", "bar"]],
1550 q = query.Query(fielddefs, ["name", "other"], namefield="name",
1551 qfilter=["?", "other"])
1552 self.assertEqual(q.OldStyleQuery(data), [
1553 ["node1", ["a", "b", "foo"]],
1554 ["node2", ["x", "y", "bar"]],
1558 q = query.Query(fielddefs, ["name", "other"], namefield="name",
1559 qfilter=["!", ["?", "other"]])
1560 self.assertEqual(q.OldStyleQuery(data), [
1564 def testFilterHostname(self):
1565 fielddefs = query._PrepareFieldList([
1566 (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1567 None, query.QFF_HOSTNAME, lambda ctx, item: item["name"]),
1571 { "name": "node1.example.com", },
1572 { "name": "node2.example.com", },
1573 { "name": "node2.example.net", },
1576 q = query.Query(fielddefs, ["name"], namefield="name",
1577 qfilter=["=", "name", "node2"])
1578 self.assertEqual(q.RequestedNames(), ["node2"])
1579 self.assertEqual(q.Query(data), [
1580 [(constants.RS_NORMAL, "node2.example.com")],
1581 [(constants.RS_NORMAL, "node2.example.net")],
1584 q = query.Query(fielddefs, ["name"], namefield="name",
1585 qfilter=["=", "name", "node1"])
1586 self.assertEqual(q.RequestedNames(), ["node1"])
1587 self.assertEqual(q.Query(data), [
1588 [(constants.RS_NORMAL, "node1.example.com")],
1591 q = query.Query(fielddefs, ["name"], namefield="name",
1592 qfilter=["=", "name", "othername"])
1593 self.assertEqual(q.RequestedNames(), ["othername"])
1594 self.assertEqual(q.Query(data), [])
1596 q = query.Query(fielddefs, ["name"], namefield="name",
1597 qfilter=["|", ["=", "name", "node1.example.com"],
1598 ["=", "name", "node2"]])
1599 self.assertEqual(q.RequestedNames(), ["node1.example.com", "node2"])
1600 self.assertEqual(q.Query(data), [
1601 [(constants.RS_NORMAL, "node1.example.com")],
1602 [(constants.RS_NORMAL, "node2.example.com")],
1603 [(constants.RS_NORMAL, "node2.example.net")],
1605 self.assertEqual(q.OldStyleQuery(data), [
1606 ["node1.example.com"],
1607 ["node2.example.com"],
1608 ["node2.example.net"],
1611 q = query.Query(fielddefs, ["name"], namefield="name",
1612 qfilter=["!=", "name", "node1"])
1613 self.assertTrue(q.RequestedNames() is None)
1614 self.assertEqual(q.Query(data), [
1615 [(constants.RS_NORMAL, "node2.example.com")],
1616 [(constants.RS_NORMAL, "node2.example.net")],
1618 self.assertEqual(q.OldStyleQuery(data), [
1619 ["node2.example.com"],
1620 ["node2.example.net"],
1623 def testFilterBoolean(self):
1624 fielddefs = query._PrepareFieldList([
1625 (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1626 None, query.QFF_HOSTNAME, lambda ctx, item: item["name"]),
1627 (query._MakeField("value", "Value", constants.QFT_BOOL, "Value"),
1628 None, 0, lambda ctx, item: item["value"]),
1632 { "name": "node1", "value": False, },
1633 { "name": "node2", "value": True, },
1634 { "name": "node3", "value": True, },
1637 q = query.Query(fielddefs, ["name", "value"],
1638 qfilter=["|", ["=", "value", False],
1639 ["=", "value", True]])
1640 self.assertTrue(q.RequestedNames() is None)
1641 self.assertEqual(q.Query(data), [
1642 [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1643 [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1644 [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1647 q = query.Query(fielddefs, ["name", "value"],
1648 qfilter=["|", ["=", "value", False],
1649 ["!", ["=", "value", False]]])
1650 self.assertTrue(q.RequestedNames() is None)
1651 self.assertEqual(q.Query(data), [
1652 [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1653 [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1654 [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1657 # Comparing bool with string
1658 for i in ["False", "True", "0", "1", "no", "yes", "N", "Y"]:
1659 self.assertRaises(errors.ParameterError, query.Query,
1660 fielddefs, ["name", "value"],
1661 qfilter=["=", "value", i])
1664 q = query.Query(fielddefs, ["name", "value"], qfilter=["?", "value"])
1665 self.assertTrue(q.RequestedNames() is None)
1666 self.assertEqual(q.Query(data), [
1667 [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1668 [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1671 # Negative bool filter
1672 q = query.Query(fielddefs, ["name", "value"], qfilter=["!", ["?", "value"]])
1673 self.assertTrue(q.RequestedNames() is None)
1674 self.assertEqual(q.Query(data), [
1675 [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1678 # Complex truth filter
1679 q = query.Query(fielddefs, ["name", "value"],
1680 qfilter=["|", ["&", ["=", "name", "node1"],
1681 ["!", ["?", "value"]]],
1683 self.assertTrue(q.RequestedNames() is None)
1684 self.assertEqual(q.Query(data), [
1685 [(constants.RS_NORMAL, "node1"), (constants.RS_NORMAL, False)],
1686 [(constants.RS_NORMAL, "node2"), (constants.RS_NORMAL, True)],
1687 [(constants.RS_NORMAL, "node3"), (constants.RS_NORMAL, True)],
1690 def testFilterRegex(self):
1691 fielddefs = query._PrepareFieldList([
1692 (query._MakeField("name", "Name", constants.QFT_TEXT, "Name"),
1693 None, 0, lambda ctx, item: item["name"]),
1697 { "name": "node1.example.com", },
1698 { "name": "node2.site.example.com", },
1699 { "name": "node2.example.net", },
1705 q = query.Query(fielddefs, ["name"], namefield="name",
1706 qfilter=["=~", "name", "site"])
1707 self.assertTrue(q.RequestedNames() is None)
1708 self.assertEqual(q.Query(data), [
1709 [(constants.RS_NORMAL, "node2.site.example.com")],
1712 q = query.Query(fielddefs, ["name"], namefield="name",
1713 qfilter=["=~", "name", "^node2"])
1714 self.assertTrue(q.RequestedNames() is None)
1715 self.assertEqual(q.Query(data), [
1716 [(constants.RS_NORMAL, "node2.example.net")],
1717 [(constants.RS_NORMAL, "node2.site.example.com")],
1720 q = query.Query(fielddefs, ["name"], namefield="name",
1721 qfilter=["=~", "name", r"(?i)\.COM$"])
1722 self.assertTrue(q.RequestedNames() is None)
1723 self.assertEqual(q.Query(data), [
1724 [(constants.RS_NORMAL, "node1.example.com")],
1725 [(constants.RS_NORMAL, "node2.site.example.com")],
1728 q = query.Query(fielddefs, ["name"], namefield="name",
1729 qfilter=["=~", "name", r"."])
1730 self.assertTrue(q.RequestedNames() is None)
1731 self.assertEqual(q.Query(data), [
1732 [(constants.RS_NORMAL, "node1.example.com")],
1733 [(constants.RS_NORMAL, "node2.example.net")],
1734 [(constants.RS_NORMAL, "node2.site.example.com")],
1737 q = query.Query(fielddefs, ["name"], namefield="name",
1738 qfilter=["=~", "name", r"^$"])
1739 self.assertTrue(q.RequestedNames() is None)
1740 self.assertEqual(q.Query(data), [
1741 [(constants.RS_NORMAL, "")],
1744 # Invalid regular expression
1745 self.assertRaises(errors.ParameterError, query.Query, fielddefs, ["name"],
1746 qfilter=["=~", "name", r"["])
1748 def testFilterLessGreater(self):
1749 fielddefs = query._PrepareFieldList([
1750 (query._MakeField("value", "Value", constants.QFT_NUMBER, "Value"),
1751 None, 0, lambda ctx, item: item),
1756 q = query.Query(fielddefs, ["value"],
1757 qfilter=["<", "value", 20])
1758 self.assertTrue(q.RequestedNames() is None)
1759 self.assertEqual(q.Query(data),
1760 [[(constants.RS_NORMAL, i)] for i in range(20)])
1762 q = query.Query(fielddefs, ["value"],
1763 qfilter=["<=", "value", 30])
1764 self.assertTrue(q.RequestedNames() is None)
1765 self.assertEqual(q.Query(data),
1766 [[(constants.RS_NORMAL, i)] for i in range(31)])
1768 q = query.Query(fielddefs, ["value"],
1769 qfilter=[">", "value", 40])
1770 self.assertTrue(q.RequestedNames() is None)
1771 self.assertEqual(q.Query(data),
1772 [[(constants.RS_NORMAL, i)] for i in range(41, 100)])
1774 q = query.Query(fielddefs, ["value"],
1775 qfilter=[">=", "value", 50])
1776 self.assertTrue(q.RequestedNames() is None)
1777 self.assertEqual(q.Query(data),
1778 [[(constants.RS_NORMAL, i)] for i in range(50, 100)])
1781 if __name__ == "__main__":
1782 testutils.GanetiTestProgram()