diff --git a/lib/utils.py b/lib/utils.py index 919eab11cf0f14051382879bd9ddf266546e3a9f..aa84cb1f806d03363e651df9fd476769c9253597 100644 --- a/lib/utils.py +++ b/lib/utils.py @@ -1872,6 +1872,46 @@ def WriteFile(file_name, fn=None, data=None, return result +def GetFileID(path=None, fd=None): + """Returns the file 'id', i.e. the dev/inode and mtime information. + + Either the path to the file or the fd must be given. + + @param path: the file path + @param fd: a file descriptor + @return: a tuple of (device number, inode number, mtime) + + """ + if [path, fd].count(None) != 1: + raise errors.ProgrammerError("One and only one of fd/path must be given") + + if fd is None: + st = os.stat(path) + else: + st = os.fstat(fd) + + return (st.st_dev, st.st_ino, st.st_mtime) + + +def VerifyFileID(fi_disk, fi_ours): + """Verifies that two file IDs are matching. + + Differences in the inode/device are not accepted, but and older + timestamp for fi_disk is accepted. + + @param fi_disk: tuple (dev, inode, mtime) representing the actual + file data + @param fi_ours: tuple (dev, inode, mtime) representing the last + written file data + @rtype: boolean + + """ + (d1, i1, m1) = fi_disk + (d2, i2, m2) = fi_ours + + return (d1, i1) == (d2, i2) and m1 <= m2 + + def ReadOneLineFile(file_name, strict=False): """Return the first non-empty line from a file. diff --git a/test/ganeti.utils_unittest.py b/test/ganeti.utils_unittest.py index 08c753e3b5b10b2b73afbc820612c5e2e3fb18d1..7fc93db1f63ab98187ea536e3d397ccce9a3df2c 100755 --- a/test/ganeti.utils_unittest.py +++ b/test/ganeti.utils_unittest.py @@ -2356,5 +2356,24 @@ class TestFindMatch(unittest.TestCase): self.assert_(utils.FindMatch(data, "Hello World") is None) +class TestFileID(testutils.GanetiTestCase): + def testEquality(self): + name = self._CreateTempFile() + oldi = utils.GetFileID(path=name) + self.failUnless(utils.VerifyFileID(oldi, oldi)) + + def testUpdate(self): + name = self._CreateTempFile() + oldi = utils.GetFileID(path=name) + os.utime(name, None) + fd = os.open(name, os.O_RDWR) + try: + newi = utils.GetFileID(fd=fd) + self.failUnless(utils.VerifyFileID(oldi, newi)) + self.failUnless(utils.VerifyFileID(newi, oldi)) + finally: + os.close(fd) + + if __name__ == '__main__': testutils.GanetiTestProgram()