Revision b4478d34

b/lib/jstore.py
88 88
        raise
89 89

  
90 90
  # Lock queue
91
  queue_lock = utils.FileLock(constants.JOB_QUEUE_LOCK_FILE)
91
  queue_lock = utils.FileLock.Open(constants.JOB_QUEUE_LOCK_FILE)
92 92
  try:
93 93
    # The queue needs to be locked in exclusive mode to write to the serial and
94 94
    # version files.
b/lib/ssconf.py
307 307
    @param values: Dictionary of (name, value)
308 308

  
309 309
    """
310
    ssconf_lock = utils.FileLock(constants.SSCONF_LOCK_FILE)
310
    ssconf_lock = utils.FileLock.Open(constants.SSCONF_LOCK_FILE)
311 311

  
312 312
    # Get lock while writing files
313 313
    ssconf_lock.Exclusive(blocking=True, timeout=SSCONF_LOCK_TIMEOUT)
b/lib/utils.py
2446 2446
  """Utility class for file locks.
2447 2447

  
2448 2448
  """
2449
  def __init__(self, filename):
2449
  def __init__(self, fd, filename):
2450 2450
    """Constructor for FileLock.
2451 2451

  
2452
    This will open the file denoted by the I{filename} argument.
2453

  
2452
    @type fd: file
2453
    @param fd: File object
2454 2454
    @type filename: str
2455
    @param filename: path to the file to be locked
2455
    @param filename: Path of the file opened at I{fd}
2456 2456

  
2457 2457
    """
2458
    self.fd = fd
2458 2459
    self.filename = filename
2459
    self.fd = open(self.filename, "w")
2460

  
2461
  @classmethod
2462
  def Open(cls, filename):
2463
    """Creates and opens a file to be used as a file-based lock.
2464

  
2465
    @type filename: string
2466
    @param filename: path to the file to be locked
2467

  
2468
    """
2469
    # Using "os.open" is necessary to allow both opening existing file
2470
    # read/write and creating if not existing. Vanilla "open" will truncate an
2471
    # existing file -or- allow creating if not existing.
2472
    return cls(os.fdopen(os.open(filename, os.O_RDWR | os.O_CREAT), "w+"),
2473
               filename)
2460 2474

  
2461 2475
  def __del__(self):
2462 2476
    self.Close()
b/test/ganeti.utils_unittest.py
992 992
      self.failUnlessEqual(TailFile(fname, lines=i), data[-i:])
993 993

  
994 994

  
995
class TestFileLock(unittest.TestCase):
995
class _BaseFileLockTest:
996 996
  """Test case for the FileLock class"""
997 997

  
998
  def setUp(self):
999
    self.tmpfile = tempfile.NamedTemporaryFile()
1000
    self.lock = utils.FileLock(self.tmpfile.name)
1001

  
1002 998
  def testSharedNonblocking(self):
1003 999
    self.lock.Shared(blocking=False)
1004 1000
    self.lock.Close()
......
1035 1031
    self.lock.Unlock(blocking=False)
1036 1032
    self.lock.Close()
1037 1033

  
1034
  def testSimpleTimeout(self):
1035
    # These will succeed on the first attempt, hence a short timeout
1036
    self.lock.Shared(blocking=True, timeout=10.0)
1037
    self.lock.Exclusive(blocking=False, timeout=10.0)
1038
    self.lock.Unlock(blocking=True, timeout=10.0)
1039
    self.lock.Close()
1040

  
1041
  @staticmethod
1042
  def _TryLockInner(filename, shared, blocking):
1043
    lock = utils.FileLock.Open(filename)
1044

  
1045
    if shared:
1046
      fn = lock.Shared
1047
    else:
1048
      fn = lock.Exclusive
1049

  
1050
    try:
1051
      # The timeout doesn't really matter as the parent process waits for us to
1052
      # finish anyway.
1053
      fn(blocking=blocking, timeout=0.01)
1054
    except errors.LockError, err:
1055
      return False
1056

  
1057
    return True
1058

  
1059
  def _TryLock(self, *args):
1060
    return utils.RunInSeparateProcess(self._TryLockInner, self.tmpfile.name,
1061
                                      *args)
1062

  
1063
  def testTimeout(self):
1064
    for blocking in [True, False]:
1065
      self.lock.Exclusive(blocking=True)
1066
      self.failIf(self._TryLock(False, blocking))
1067
      self.failIf(self._TryLock(True, blocking))
1068

  
1069
      self.lock.Shared(blocking=True)
1070
      self.assert_(self._TryLock(True, blocking))
1071
      self.failIf(self._TryLock(False, blocking))
1072

  
1038 1073
  def testCloseShared(self):
1039 1074
    self.lock.Close()
1040 1075
    self.assertRaises(AssertionError, self.lock.Shared, blocking=False)
......
1048 1083
    self.assertRaises(AssertionError, self.lock.Unlock, blocking=False)
1049 1084

  
1050 1085

  
1086
class TestFileLockWithFilename(testutils.GanetiTestCase, _BaseFileLockTest):
1087
  TESTDATA = "Hello World\n" * 10
1088

  
1089
  def setUp(self):
1090
    testutils.GanetiTestCase.setUp(self)
1091

  
1092
    self.tmpfile = tempfile.NamedTemporaryFile()
1093
    utils.WriteFile(self.tmpfile.name, data=self.TESTDATA)
1094
    self.lock = utils.FileLock.Open(self.tmpfile.name)
1095

  
1096
    # Ensure "Open" didn't truncate file
1097
    self.assertFileContent(self.tmpfile.name, self.TESTDATA)
1098

  
1099
  def tearDown(self):
1100
    self.assertFileContent(self.tmpfile.name, self.TESTDATA)
1101

  
1102
    testutils.GanetiTestCase.tearDown(self)
1103

  
1104

  
1105
class TestFileLockWithFileObject(unittest.TestCase, _BaseFileLockTest):
1106
  def setUp(self):
1107
    self.tmpfile = tempfile.NamedTemporaryFile()
1108
    self.lock = utils.FileLock(open(self.tmpfile.name, "w"), self.tmpfile.name)
1109

  
1110

  
1051 1111
class TestTimeFunctions(unittest.TestCase):
1052 1112
  """Test case for time functions"""
1053 1113

  

Also available in: Unified diff