diff --git a/lib/backend.py b/lib/backend.py index 7d7b04073e88d92a42470a08812a99696b723ba4..5a09b57f01b713baf5f89743824512ee1b02dac3 100644 --- a/lib/backend.py +++ b/lib/backend.py @@ -823,7 +823,8 @@ def _InstanceLogName(kind, os_name, instance): @param instance: the name of the instance being imported/added/etc. """ - base = "%s-%s-%s-%d.log" % (kind, os_name, instance, int(time.time())) + base = ("%s-%s-%s-%s.log" % + (kind, os_name, instance, utils.TimestampForFilename())) return utils.PathJoin(constants.LOG_OS_DIR, base) diff --git a/lib/utils.py b/lib/utils.py index 5f80279ab833f0000fb2807d7eca496f4c156733..e6a191e37f5b66be6a90f5873d3a9601bc2193f4 100644 --- a/lib/utils.py +++ b/lib/utils.py @@ -1119,6 +1119,16 @@ def RemoveHostFromEtcHosts(hostname): RemoveEtcHostsEntry(constants.ETC_HOSTS, hi.ShortName()) +def TimestampForFilename(): + """Returns the current time formatted for filenames. + + The format doesn't contain colons as some shells and applications them as + separators. + + """ + return time.strftime("%Y-%m-%d_%H_%M_%S") + + def CreateBackup(file_name): """Creates a backup of a file. @@ -1133,7 +1143,8 @@ def CreateBackup(file_name): raise errors.ProgrammerError("Can't make a backup of a non-file '%s'" % file_name) - prefix = '%s.backup-%d.' % (os.path.basename(file_name), int(time.time())) + prefix = ("%s.backup-%s." % + (os.path.basename(file_name), TimestampForFilename())) dir_name = os.path.dirname(file_name) fsrc = open(file_name, 'rb') @@ -1141,6 +1152,7 @@ def CreateBackup(file_name): (fd, backup_name) = tempfile.mkstemp(prefix=prefix, dir=dir_name) fdst = os.fdopen(fd, 'wb') try: + logging.debug("Backing up %s at %s", file_name, backup_name) shutil.copyfileobj(fsrc, fdst) finally: fdst.close() diff --git a/test/ganeti.utils_unittest.py b/test/ganeti.utils_unittest.py index eb24ebd8371e445dad940cfb680778ac800fd8ca..676aa5de02910a60c342e6685d41dbfd50a26f36 100755 --- a/test/ganeti.utils_unittest.py +++ b/test/ganeti.utils_unittest.py @@ -38,6 +38,7 @@ import string import OpenSSL import warnings import distutils.version +import glob import ganeti import testutils @@ -513,6 +514,55 @@ class TestMatchNameComponent(unittest.TestCase): None) +class TestTimestampForFilename(unittest.TestCase): + def test(self): + self.assert_("." not in utils.TimestampForFilename()) + self.assert_(":" not in utils.TimestampForFilename()) + + +class TestCreateBackup(testutils.GanetiTestCase): + def setUp(self): + testutils.GanetiTestCase.setUp(self) + + self.tmpdir = tempfile.mkdtemp() + + def tearDown(self): + testutils.GanetiTestCase.tearDown(self) + + shutil.rmtree(self.tmpdir) + + def testEmpty(self): + filename = utils.PathJoin(self.tmpdir, "config.data") + utils.WriteFile(filename, data="") + bname = utils.CreateBackup(filename) + self.assertFileContent(bname, "") + self.assertEqual(len(glob.glob("%s*" % filename)), 2) + utils.CreateBackup(filename) + self.assertEqual(len(glob.glob("%s*" % filename)), 3) + utils.CreateBackup(filename) + self.assertEqual(len(glob.glob("%s*" % filename)), 4) + + fifoname = utils.PathJoin(self.tmpdir, "fifo") + os.mkfifo(fifoname) + self.assertRaises(errors.ProgrammerError, utils.CreateBackup, fifoname) + + def testContent(self): + bkpcount = 0 + for data in ["", "X", "Hello World!\n" * 100, "Binary data\0\x01\x02\n"]: + for rep in [1, 2, 10, 127]: + testdata = data * rep + + filename = utils.PathJoin(self.tmpdir, "test.data_") + utils.WriteFile(filename, data=testdata) + self.assertFileContent(filename, testdata) + + for _ in range(3): + bname = utils.CreateBackup(filename) + bkpcount += 1 + self.assertFileContent(bname, testdata) + self.assertEqual(len(glob.glob("%s*" % filename)), 1 + bkpcount) + + class TestFormatUnit(unittest.TestCase): """Test case for the FormatUnit function"""