summaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorChris St. Pierre <chris.a.st.pierre@gmail.com>2012-10-26 13:33:33 -0400
committerChris St. Pierre <chris.a.st.pierre@gmail.com>2012-10-26 13:34:37 -0400
commitb3ca0205e83f178d5bd785676412424dd32391b4 (patch)
tree065e0f991b58dfbfe73459e7128d2047f9929281 /src
parent4d45a37747d8de98d22e73ce2fefa233f64540d0 (diff)
downloadbcfg2-b3ca0205e83f178d5bd785676412424dd32391b4.tar.gz
bcfg2-b3ca0205e83f178d5bd785676412424dd32391b4.tar.bz2
bcfg2-b3ca0205e83f178d5bd785676412424dd32391b4.zip
only permit one thread to write to clients.xml (or clients.xml.new) at a time
Diffstat (limited to 'src')
-rw-r--r--src/lib/Bcfg2/Server/Plugins/Metadata.py35
1 files changed, 27 insertions, 8 deletions
diff --git a/src/lib/Bcfg2/Server/Plugins/Metadata.py b/src/lib/Bcfg2/Server/Plugins/Metadata.py
index 932c35545..ae268d8b7 100644
--- a/src/lib/Bcfg2/Server/Plugins/Metadata.py
+++ b/src/lib/Bcfg2/Server/Plugins/Metadata.py
@@ -6,6 +6,7 @@ import os
import sys
import time
import copy
+import errno
import fcntl
import socket
import logging
@@ -159,18 +160,36 @@ class XMLMetadataConfig(Bcfg2.Server.Plugin.XMLFileBacked):
def write_xml(self, fname, xmltree):
"""Write changes to xml back to disk."""
tmpfile = "%s.new" % fname
- try:
- datafile = open(tmpfile, 'w')
- except IOError:
- msg = "Failed to write %s: %s" % (tmpfile, sys.exc_info()[1])
- self.logger.error(msg)
- raise Bcfg2.Server.Plugin.MetadataRuntimeError(msg)
+ datafile = None
+ fd = None
+ i = 0 # counter to avoid flooding logs with lock messages
+ while datafile is None:
+ try:
+ fd = os.open(tmpfile, os.O_CREAT | os.O_EXCL | os.O_WRONLY)
+ datafile = os.fdopen(fd, 'w')
+ except OSError:
+ err = sys.exc_info()[1]
+ if err.errno == errno.EEXIST:
+ # note: not a real lock. this is here to avoid
+ # the scenario where two threads write to the file
+ # at the same-ish time, and one writes to
+ # foo.xml.new, then the other one writes to it
+ # (losing the first thread's changes), then the
+ # first renames it, then the second tries to
+ # rename it and borks.
+ if (i % 10) == 0:
+ self.logger.info("%s is locked, waiting" % fname)
+ i += 1
+ time.sleep(0.1)
+ else:
+ msg = "Failed to write %s: %s" % (tmpfile, err)
+ self.logger.error(msg)
+ raise Bcfg2.Server.Plugin.MetadataRuntimeError(msg)
# prep data
dataroot = xmltree.getroot()
newcontents = lxml.etree.tostring(dataroot, xml_declaration=False,
pretty_print=True).decode('UTF-8')
- fd = datafile.fileno()
while locked(fd) == True:
pass
try:
@@ -383,7 +402,7 @@ class Metadata(Bcfg2.Server.Plugin.Metadata,
self.groups_xml = self._handle_file("groups.xml")
if (self._use_db and
os.path.exists(os.path.join(self.data, "clients.xml"))):
- self.logger.warning("Metadata: database enabled but clients.xml"
+ self.logger.warning("Metadata: database enabled but clients.xml "
"found, parsing in compatibility mode")
self.clients_xml = self._handle_file("clients.xml")
elif not self._use_db: