From 6e797216e7bcd411e00f9f669895848f00eeb891 Mon Sep 17 00:00:00 2001 From: Michael Hanselmann <hansmi@google.com> Date: Thu, 18 Dec 2008 16:23:05 +0000 Subject: [PATCH] Add rename function automatically creating directories if needed Unfortunately, os.makedirs in Python 2.4 is not safe against multiple processes creating the same directory tree at the same time. This is only fixed in Python 2.5 and up. Adding more checks in our code doesn't make it any better. Reviewed-by: iustinp --- lib/utils.py | 26 ++++++++++++++++++++++++++ test/ganeti.utils_unittest.py | 30 ++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) diff --git a/lib/utils.py b/lib/utils.py index 5f6592000..76adfc577 100644 --- a/lib/utils.py +++ b/lib/utils.py @@ -285,6 +285,32 @@ def RemoveFile(filename): raise +def RenameFile(old, new, mkdir=False, mkdir_mode=0750): + """Renames a file. + + @type old: string + @param old: Original path + @type new: string + @param new: New path + @type mkdir: bool + @param mkdir: Whether to create target directory if it doesn't exist + @type mkdir_mode: int + @param mkdir_mode: Mode for newly created directories + + """ + try: + return os.rename(old, new) + except OSError, err: + # In at least one use case of this function, the job queue, directory + # creation is very rare. Checking for the directory before renaming is not + # as efficient. + if mkdir and err.errno == errno.ENOENT: + # Create directory and try again + os.makedirs(os.path.dirname(new), mkdir_mode) + return os.rename(old, new) + raise + + def _FingerprintFile(filename): """Compute the fingerprint of a file. diff --git a/test/ganeti.utils_unittest.py b/test/ganeti.utils_unittest.py index 34b992894..29fddfdd0 100755 --- a/test/ganeti.utils_unittest.py +++ b/test/ganeti.utils_unittest.py @@ -277,6 +277,36 @@ class TestRemoveFile(unittest.TestCase): self.fail("File '%s' not removed" % symlink) +class TestRename(unittest.TestCase): + """Test case for RenameFile""" + + def setUp(self): + """Create a temporary directory""" + self.tmpdir = tempfile.mkdtemp() + self.tmpfile = os.path.join(self.tmpdir, "test1") + + # Touch the file + open(self.tmpfile, "w").close() + + def tearDown(self): + """Remove temporary directory""" + shutil.rmtree(self.tmpdir) + + def testSimpleRename1(self): + """Simple rename 1""" + utils.RenameFile(self.tmpfile, os.path.join(self.tmpdir, "xyz")) + + def testSimpleRename2(self): + """Simple rename 2""" + utils.RenameFile(self.tmpfile, os.path.join(self.tmpdir, "xyz"), + mkdir=True) + + def testRenameMkdir(self): + """Rename with mkdir""" + utils.RenameFile(self.tmpfile, os.path.join(self.tmpdir, "test/xyz"), + mkdir=True) + + class TestCheckdict(unittest.TestCase): """Test case for the CheckDict function""" -- GitLab