diff --git a/daemons/ganeti-watcher b/daemons/ganeti-watcher index f16d56fb6482ccf55c2358b843dbcb8ad2fcf112..509d800388b3a09acda0fc3cca4a0262b6d9d1ac 100755 --- a/daemons/ganeti-watcher +++ b/daemons/ganeti-watcher @@ -1,7 +1,7 @@ #!/usr/bin/python # -# Copyright (C) 2006, 2007 Google Inc. +# Copyright (C) 2006, 2007, 2008 Google Inc. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -93,6 +93,18 @@ def DoCmd(cmd): return res +def LockFile(fd): + """Locks a file using POSIX locks. + + """ + try: + fcntl.flock(fd, fcntl.LOCK_EX | fcntl.LOCK_NB) + except IOError, err: + if err.errno == errno.EAGAIN: + raise StandardError("File already locked") + raise + + class WatcherState(object): """Interface to a state file recording restart attempts. @@ -106,17 +118,10 @@ class WatcherState(object): # The two-step dance below is necessary to allow both opening existing # file read/write and creating if not existing. Vanilla open will truncate # an existing file -or- allow creating if not existing. - f = os.open(constants.WATCHER_STATEFILE, os.O_RDWR | os.O_CREAT) - f = os.fdopen(f, 'w+') + fd = os.open(constants.WATCHER_STATEFILE, os.O_RDWR | os.O_CREAT) + self.statefile = os.fdopen(fd, 'w+') - try: - fcntl.flock(f.fileno(), fcntl.LOCK_EX|fcntl.LOCK_NB) - except IOError, x: - if x.errno == errno.EAGAIN: - raise StandardError("State file already locked") - raise - - self.statefile = f + LockFile(self.statefile.fileno()) try: self.data = serializer.Load(self.statefile.read()) @@ -131,21 +136,26 @@ class WatcherState(object): if "node" not in self.data: self.data["node"] = {} - def __del__(self): - """Called on destruction. + def Save(self): + """Save state to file, then unlock and close it. """ - if self.statefile: - self._Close() + assert self.statefile + + # We need to make sure the file is locked before renaming it, otherwise + # starting ganeti-watcher again at the same time will create a conflict. + fd = utils.WriteFile(constants.WATCHER_STATEFILE, + data=serializer.Dump(self.data), + prewrite=LockFile, close=False) + self.statefile = os.fdopen(fd, 'w+') - def _Close(self): + def Close(self): """Unlock configuration file and close it. """ assert self.statefile - fcntl.flock(self.statefile.fileno(), fcntl.LOCK_UN) - + # Files are automatically unlocked when closing them self.statefile.close() self.statefile = None @@ -217,19 +227,6 @@ class WatcherState(object): if instance.name in idata: del idata[instance.name] - def Save(self): - """Save state to file, then unlock and close it. - - """ - assert self.statefile - - self.statefile.seek(0) - self.statefile.truncate() - - self.statefile.write(serializer.Dump(self.data)) - - self._Close() - class Instance(object): """Abstraction for a Virtual Machine instance.