created makedirs_safe function for use in cases of multiprocess

should fix #11126 and most race conditions
pull/11127/head
Brian Coca 10 years ago
parent 47be5b4166
commit 2590df6df1

@ -29,6 +29,7 @@ from ansible.errors import *
from ansible.plugins.action import ActionBase from ansible.plugins.action import ActionBase
from ansible.utils.boolean import boolean from ansible.utils.boolean import boolean
from ansible.utils.hashing import checksum, checksum_s, md5, secure_hash from ansible.utils.hashing import checksum, checksum_s, md5, secure_hash
from ansible.utils.path import makedirs_safe
class ActionModule(ActionBase): class ActionModule(ActionBase):
@ -125,8 +126,7 @@ class ActionModule(ActionBase):
if remote_checksum != local_checksum: if remote_checksum != local_checksum:
# create the containing directories, if needed # create the containing directories, if needed
if not os.path.isdir(os.path.dirname(dest)): makedirs_safe(os.path.dirname(dest))
os.makedirs(os.path.dirname(dest))
# fetch the file and check for changes # fetch the file and check for changes
if remote_data is None: if remote_data is None:

@ -42,6 +42,7 @@ from binascii import hexlify
from ansible import constants as C from ansible import constants as C
from ansible.errors import AnsibleError, AnsibleConnectionFailure, AnsibleFileNotFound from ansible.errors import AnsibleError, AnsibleConnectionFailure, AnsibleFileNotFound
from ansible.plugins.connections import ConnectionBase from ansible.plugins.connections import ConnectionBase
from ansible.utils.path import makedirs_safe
AUTHENTICITY_MSG=""" AUTHENTICITY_MSG="""
paramiko: The authenticity of host '%s' can't be established. paramiko: The authenticity of host '%s' can't be established.
@ -309,8 +310,7 @@ class Connection(ConnectionBase):
return False return False
path = os.path.expanduser("~/.ssh") path = os.path.expanduser("~/.ssh")
if not os.path.exists(path): makedirs_safe(path)
os.makedirs(path)
f = open(filename, 'w') f = open(filename, 'w')
@ -347,8 +347,7 @@ class Connection(ConnectionBase):
# add any new SSH host keys -- warning -- this could be slow # add any new SSH host keys -- warning -- this could be slow
lockfile = self.keyfile.replace("known_hosts",".known_hosts.lock") lockfile = self.keyfile.replace("known_hosts",".known_hosts.lock")
dirname = os.path.dirname(self.keyfile) dirname = os.path.dirname(self.keyfile)
if not os.path.exists(dirname): makedirs_safe(dirname)
os.makedirs(dirname)
KEY_LOCK = open(lockfile, 'w') KEY_LOCK = open(lockfile, 'w')
fcntl.lockf(KEY_LOCK, fcntl.LOCK_EX) fcntl.lockf(KEY_LOCK, fcntl.LOCK_EX)

@ -44,6 +44,7 @@ from ansible import constants as C
from ansible.errors import AnsibleError, AnsibleConnectionFailure, AnsibleFileNotFound from ansible.errors import AnsibleError, AnsibleConnectionFailure, AnsibleFileNotFound
from ansible.plugins.connections import ConnectionBase from ansible.plugins.connections import ConnectionBase
from ansible.plugins import shell_loader from ansible.plugins import shell_loader
from ansible.utils import makedirs_safe
class Connection(ConnectionBase): class Connection(ConnectionBase):
'''WinRM connections over HTTP/HTTPS.''' '''WinRM connections over HTTP/HTTPS.'''
@ -213,8 +214,7 @@ class Connection(ConnectionBase):
out_path = out_path.replace('\\', '/') out_path = out_path.replace('\\', '/')
self._display.vvv("FETCH %s TO %s" % (in_path, out_path), host=self._connection_info.remote_addr) self._display.vvv("FETCH %s TO %s" % (in_path, out_path), host=self._connection_info.remote_addr)
buffer_size = 2**19 # 0.5MB chunks buffer_size = 2**19 # 0.5MB chunks
if not os.path.exists(os.path.dirname(out_path)): makedirs_safe(os.path.dirname(out_path))
os.makedirs(os.path.dirname(out_path))
out_file = None out_file = None
try: try:
offset = 0 offset = 0
@ -251,8 +251,7 @@ class Connection(ConnectionBase):
else: else:
data = base64.b64decode(result.std_out.strip()) data = base64.b64decode(result.std_out.strip())
if data is None: if data is None:
if not os.path.exists(out_path): makedirs_safe(out_path)
os.makedirs(out_path)
break break
else: else:
if not out_file: if not out_file:

@ -30,6 +30,7 @@ from ansible import constants as C
from ansible.errors import AnsibleError from ansible.errors import AnsibleError
from ansible.plugins.lookup import LookupBase from ansible.plugins.lookup import LookupBase
from ansible.utils.encrypt import do_encrypt from ansible.utils.encrypt import do_encrypt
from ansible.utils import makedirs_safe
DEFAULT_LENGTH = 20 DEFAULT_LENGTH = 20
@ -98,11 +99,10 @@ class LookupModule(LookupBase):
path = self._loader.path_dwim(relpath) path = self._loader.path_dwim(relpath)
if not os.path.exists(path): if not os.path.exists(path):
pathdir = os.path.dirname(path) pathdir = os.path.dirname(path)
if not os.path.isdir(pathdir): try:
try: makedirs_safe(pathdir, mode=0o700)
os.makedirs(pathdir, mode=0o700) except OSError as e:
except OSError as e: raise AnsibleError("cannot create the path for the password lookup: %s (error was %s)" % (pathdir, str(e)))
raise AnsibleError("cannot create the path for the password lookup: %s (error was %s)" % (pathdir, str(e)))
chars = "".join([getattr(string,c,c) for c in use_chars]).replace('"','').replace("'",'') chars = "".join([getattr(string,c,c) for c in use_chars]).replace('"','').replace("'",'')
password = ''.join(random.choice(chars) for _ in range(length)) password = ''.join(random.choice(chars) for _ in range(length))

@ -19,6 +19,7 @@ __metaclass__ = type
import os import os
import stat import stat
from time import sleep
__all__ = ['is_executable', 'unfrackpath'] __all__ = ['is_executable', 'unfrackpath']
@ -35,3 +36,12 @@ def unfrackpath(path):
''' '''
return os.path.normpath(os.path.realpath(os.path.expandvars(os.path.expanduser(path)))) return os.path.normpath(os.path.realpath(os.path.expandvars(os.path.expanduser(path))))
def makedirs_safe(path, mode=None):
'''Safe way to create dirs in muliprocess/thread environments'''
while not os.path.exists(path):
try:
os.makedirs(path, mode)
except OSError, e:
if e.errno != 17:
raise
sleep(1)

Loading…
Cancel
Save