diff --git a/lib/query.py b/lib/query.py index 80cb68af007d31810762075e0ae5278a65b7e443..814016979f717f6f148aba4f11bb6c3ded0bf331 100644 --- a/lib/query.py +++ b/lib/query.py @@ -277,6 +277,41 @@ def _GetItemAttr(attr): return lambda _, item: (constants.QRFS_NORMAL, getter(item)) +def _GetItemTimestamp(getter): + """Returns function for getting timestamp of item. + + @type getter: callable + @param getter: Function to retrieve timestamp attribute + + """ + def fn(_, item): + """Returns a timestamp of item. + + """ + timestamp = getter(item) + if timestamp is None: + # Old configs might not have all timestamps + return (constants.QRFS_UNAVAIL, None) + else: + return (constants.QRFS_NORMAL, timestamp) + + return fn + + +def _GetItemTimestampFields(datatype): + """Returns common timestamp fields. + + @param datatype: Field data type for use by L{Query.RequestedData} + + """ + return [ + (_MakeField("ctime", "CTime", constants.QFT_TIMESTAMP), datatype, + _GetItemTimestamp(operator.attrgetter("ctime"))), + (_MakeField("mtime", "MTime", constants.QFT_TIMESTAMP), datatype, + _GetItemTimestamp(operator.attrgetter("mtime"))), + ] + + class NodeQueryData: """Data container for node data queries. @@ -313,11 +348,9 @@ class NodeQueryData: #: Fields that are direct attributes of an L{objects.Node} object _NODE_SIMPLE_FIELDS = { - "ctime": ("CTime", constants.QFT_TIMESTAMP), "drained": ("Drained", constants.QFT_BOOL), "master_candidate": ("MasterC", constants.QFT_BOOL), "master_capable": ("MasterCapable", constants.QFT_BOOL), - "mtime": ("MTime", constants.QFT_TIMESTAMP), "name": ("Node", constants.QFT_TEXT), "offline": ("Offline", constants.QFT_BOOL), "serial_no": ("SerialNo", constants.QFT_NUMBER), @@ -439,6 +472,9 @@ def _BuildNodeFields(): for (name, (title, kind, nfield)) in _NODE_LIVE_FIELDS.items() ]) + # Add timestamps + fields.extend(_GetItemTimestampFields(NQ_CONFIG)) + return _PrepareFieldList(fields) @@ -876,10 +912,8 @@ def _GetInstanceParameterFields(): _INST_SIMPLE_FIELDS = { - "ctime": ("CTime", constants.QFT_TIMESTAMP), "disk_template": ("Disk_template", constants.QFT_TEXT), "hypervisor": ("Hypervisor", constants.QFT_TEXT), - "mtime": ("MTime", constants.QFT_TIMESTAMP), "name": ("Node", constants.QFT_TEXT), # Depending on the hypervisor, the port can be None "network_port": ("Network_port", constants.QFT_OTHER), @@ -923,6 +957,7 @@ def _BuildInstanceFields(): fields.extend(_GetInstanceParameterFields()) fields.extend(_GetInstanceDiskFields()) fields.extend(_GetInstanceNetworkFields()) + fields.extend(_GetItemTimestampFields(IQ_CONFIG)) return _PrepareFieldList(fields) diff --git a/test/ganeti.query_unittest.py b/test/ganeti.query_unittest.py index 48cb27bf6a5b236af6668ddbfae6e46e4a2b010b..b154070f683204fbc019ad2521a88cfafa0e860c 100755 --- a/test/ganeti.query_unittest.py +++ b/test/ganeti.query_unittest.py @@ -337,6 +337,8 @@ class TestNodeQuery(unittest.TestCase): master_node.AddTag("masternode") master_node.AddTag("another") master_node.AddTag("tag") + master_node.ctime = None + master_node.mtime = None assert master_node.name == master_name live_data_name = node_names[4] @@ -392,6 +394,10 @@ class TestNodeQuery(unittest.TestCase): (constants.QRFS_NORMAL, "ng1")) self.assertEqual(master_row[field_index["group.uuid"]], (constants.QRFS_NORMAL, ng_uuid)) + self.assertEqual(master_row[field_index["ctime"]], + (constants.QRFS_UNAVAIL, None)) + self.assertEqual(master_row[field_index["mtime"]], + (constants.QRFS_UNAVAIL, None)) self.assert_(row[field_index["pip"]] == node.primary_ip and row[field_index["sip"]] == node.secondary_ip and @@ -401,7 +407,11 @@ class TestNodeQuery(unittest.TestCase): master_name) and (node.name == master_name or (row[field_index["group"]] == "<unknown>" and - row[field_index["group.uuid"]] is None)) + row[field_index["group.uuid"]] is None and + row[field_index["ctime"]] == (constants.QRFS_NORMAL, + node.ctime) and + row[field_index["mtime"]] == (constants.QRFS_NORMAL, + node.mtime))) for row, node in zip(result, nodes)) live_data_row = result[node_to_row[live_data_name]] @@ -606,8 +616,18 @@ class TestInstanceQuery(unittest.TestCase): beparams={ constants.BE_MEMORY: 768, }), + objects.Instance(name="inst7", hvparams={}, nics=[], + uuid="ceec5dc4-b729-4f42-ae28-69b3cd24920e", + ctime=None, mtime=None, serial_no=1947, + admin_up=False, hypervisor=constants.HT_XEN_HVM, os="deb99", + primary_node="node6", + disk_template=constants.DT_DISKLESS, + disks=[], + beparams={}), ] + assert not utils.FindDuplicates(inst.name for inst in instances) + disk_usage = dict((inst.name, cmdlib._ComputeDiskSize(inst.disk_template, [{"size": disk.size} @@ -717,6 +737,14 @@ class TestInstanceQuery(unittest.TestCase): self.assertEqual(row[fieldidx["sda_size"]], row[fieldidx["disk.size/0"]]) self.assertEqual(row[fieldidx["sdb_size"]], row[fieldidx["disk.size/1"]]) + for field in ["ctime", "mtime"]: + if getattr(inst, field) is None: + # No ctime/mtime + exp = (constants.QRFS_UNAVAIL, None) + else: + exp = (constants.QRFS_NORMAL, getattr(inst, field)) + self.assertEqual(row[fieldidx[field]], exp) + # Ensure all possible status' have been tested self.assertEqual(tested_status, set(["ERROR_nodeoffline", "ERROR_nodedown",