diff --git a/lib/query.py b/lib/query.py index 4eba17ea5a498d04271a74c87a4f4d5ab603831b..84a8607053da048e45c7c3c257b217e4125fddff 100644 --- a/lib/query.py +++ b/lib/query.py @@ -277,14 +277,18 @@ def _VerifyResultRow(fields, row): (utils.CommaJoin(errors), row)) -def _PrepareFieldList(fields): +def _PrepareFieldList(fields, aliases): """Prepares field list for use by L{Query}. Converts the list to a dictionary and does some verification. - @type fields: list of tuples; (L{objects.QueryFieldDefinition}, data kind, - retrieval function) - @param fields: List of fields, see L{Query.__init__} for a better description + @type fields: list of tuples; (L{objects.QueryFieldDefinition}, data + kind, retrieval function) + @param fields: List of fields, see L{Query.__init__} for a better + description + @type aliases: list of tuples; (alias, target) + @param aliases: list of tuples containing aliases; for each + alias/target pair, a duplicate will be created in the field list @rtype: dict @return: Field dictionary for L{Query} @@ -308,7 +312,15 @@ def _PrepareFieldList(fields): result[fdef.name] = field - assert len(result) == len(fields) + for alias, target in aliases: + assert alias not in result, "Alias %s overrides an existing field" % alias + assert target in result, "Missing target %s for alias %s" % (target, alias) + (fdef, k, fn) = result[target] + fdef = fdef.Copy() + fdef.name = alias + result[alias] = (fdef, k, fn) + + assert len(result) == len(fields) + len(aliases) assert compat.all(name == fdef.name for (name, (fdef, _, _)) in result.items()) @@ -644,7 +656,7 @@ def _BuildNodeFields(): # Add timestamps fields.extend(_GetItemTimestampFields(NQ_CONFIG)) - return _PrepareFieldList(fields) + return _PrepareFieldList(fields, []) class InstanceQueryData: @@ -1119,7 +1131,7 @@ def _BuildInstanceFields(): fields.extend(_GetInstanceNetworkFields()) fields.extend(_GetItemTimestampFields(IQ_CONFIG)) - return _PrepareFieldList(fields) + return _PrepareFieldList(fields, []) class LockQueryData: @@ -1175,7 +1187,7 @@ def _BuildLockFields(): lambda ctx, (name, mode, owners, pending): mode), (_MakeField("owner", "Owner", QFT_OTHER), LQ_OWNER, _GetLockOwners), (_MakeField("pending", "Pending", QFT_OTHER), LQ_PENDING, _GetLockPending), - ]) + ], []) class GroupQueryData: @@ -1247,7 +1259,7 @@ def _BuildGroupFields(): fields.extend(_GetItemTimestampFields(GQ_CONFIG)) - return _PrepareFieldList(fields) + return _PrepareFieldList(fields, []) #: Fields available for node queries diff --git a/test/ganeti.query_unittest.py b/test/ganeti.query_unittest.py index 800c82139377cd4122ae80466bbe1dba05447032..04723805190ffecadfeac650e869f9af5f9e5e61 100755 --- a/test/ganeti.query_unittest.py +++ b/test/ganeti.query_unittest.py @@ -74,7 +74,7 @@ class TestQuery(unittest.TestCase): [(query._MakeField("disk%s.size" % i, "DiskSize%s" % i, constants.QFT_UNIT), DISK, compat.partial(_GetDiskSize, i)) - for i in range(4)]) + for i in range(4)], []) q = query.Query(fielddef, ["name"]) self.assertEqual(q.RequestedData(), set([STATIC])) @@ -176,40 +176,40 @@ class TestQuery(unittest.TestCase): lambda *args: None), (query._MakeField("other", a, constants.QFT_TEXT), None, lambda *args: None), - ]) + ], []) # Non-lowercase names self.assertRaises(AssertionError, query._PrepareFieldList, [ (query._MakeField("NAME", "Name", constants.QFT_TEXT), None, lambda *args: None), - ]) + ], []) self.assertRaises(AssertionError, query._PrepareFieldList, [ (query._MakeField("Name", "Name", constants.QFT_TEXT), None, lambda *args: None), - ]) + ], []) # Empty name self.assertRaises(AssertionError, query._PrepareFieldList, [ (query._MakeField("", "Name", constants.QFT_TEXT), None, lambda *args: None), - ]) + ], []) # Empty title self.assertRaises(AssertionError, query._PrepareFieldList, [ (query._MakeField("name", "", constants.QFT_TEXT), None, lambda *args: None), - ]) + ], []) # Whitespace in title self.assertRaises(AssertionError, query._PrepareFieldList, [ (query._MakeField("name", "Co lu mn", constants.QFT_TEXT), None, lambda *args: None), - ]) + ], []) # No callable function self.assertRaises(AssertionError, query._PrepareFieldList, [ (query._MakeField("name", "Name", constants.QFT_TEXT), None, None), - ]) + ], []) def testUnknown(self): fielddef = query._PrepareFieldList([ @@ -221,7 +221,7 @@ class TestQuery(unittest.TestCase): None, lambda *args: query._FS_NODATA ), (query._MakeField("unavail", "Unavail", constants.QFT_BOOL), None, lambda *args: query._FS_UNAVAIL), - ]) + ], []) for selected in [["foo"], ["Hello", "World"], ["name1", "other", "foo"]]: @@ -254,6 +254,25 @@ class TestQuery(unittest.TestCase): (constants.QRFS_UNKNOWN, None)] for i in range(1, 10)]) + def testAliases(self): + fields = [ + (query._MakeField("a", "a-title", constants.QFT_TEXT), None, + lambda *args: None), + (query._MakeField("b", "b-title", constants.QFT_TEXT), None, + lambda *args: None), + ] + # duplicate field + self.assertRaises(AssertionError, query._PrepareFieldList, fields, + [("b", "a")]) + self.assertRaises(AssertionError, query._PrepareFieldList, fields, + [("c", "b"), ("c", "a")]) + # missing target + self.assertRaises(AssertionError, query._PrepareFieldList, fields, + [("c", "d")]) + fdefs = query._PrepareFieldList(fields, [("c", "b")]) + self.assertEqual(len(fdefs), 3) + self.assertEqual(fdefs["b"][1:], fdefs["c"][1:]) + class TestGetNodeRole(unittest.TestCase): def testMaster(self):