mirror of
https://github.com/ansible-collections/community.general.git
synced 2025-07-25 14:20:22 -07:00
Use select in wait_for so that we don't get stuck in cornercases:
* reading from a socket that gave some data we weren't looking for and then closed. * read from a socket that stays open and never sends data. * reading from a socket that sends data but not the data we're looking for. Fixes #2051
This commit is contained in:
parent
f6353a548c
commit
fda9eeaa89
1 changed files with 63 additions and 39 deletions
|
@ -18,12 +18,14 @@
|
||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
import socket
|
|
||||||
import datetime
|
|
||||||
import time
|
|
||||||
import sys
|
|
||||||
import re
|
|
||||||
import binascii
|
import binascii
|
||||||
|
import datetime
|
||||||
|
import math
|
||||||
|
import re
|
||||||
|
import select
|
||||||
|
import socket
|
||||||
|
import sys
|
||||||
|
import time
|
||||||
|
|
||||||
HAS_PSUTIL = False
|
HAS_PSUTIL = False
|
||||||
try:
|
try:
|
||||||
|
@ -349,6 +351,10 @@ def main():
|
||||||
state = params['state']
|
state = params['state']
|
||||||
path = params['path']
|
path = params['path']
|
||||||
search_regex = params['search_regex']
|
search_regex = params['search_regex']
|
||||||
|
if search_regex is not None:
|
||||||
|
compiled_search_re = re.compile(search_regex, re.MULTILINE)
|
||||||
|
else:
|
||||||
|
compiled_search_re = None
|
||||||
|
|
||||||
if port and path:
|
if port and path:
|
||||||
module.fail_json(msg="port and path parameter can not both be passed to wait_for")
|
module.fail_json(msg="port and path parameter can not both be passed to wait_for")
|
||||||
|
@ -404,55 +410,72 @@ def main():
|
||||||
if path:
|
if path:
|
||||||
try:
|
try:
|
||||||
os.stat(path)
|
os.stat(path)
|
||||||
if search_regex:
|
|
||||||
try:
|
|
||||||
f = open(path)
|
|
||||||
try:
|
|
||||||
if re.search(search_regex, f.read(), re.MULTILINE):
|
|
||||||
break
|
|
||||||
else:
|
|
||||||
time.sleep(1)
|
|
||||||
finally:
|
|
||||||
f.close()
|
|
||||||
except IOError:
|
|
||||||
time.sleep(1)
|
|
||||||
pass
|
|
||||||
else:
|
|
||||||
break
|
|
||||||
except OSError, e:
|
except OSError, e:
|
||||||
# File not present
|
# If anything except file not present, throw an error
|
||||||
if e.errno == 2:
|
if e.errno != 2:
|
||||||
time.sleep(1)
|
|
||||||
else:
|
|
||||||
elapsed = datetime.datetime.now() - start
|
elapsed = datetime.datetime.now() - start
|
||||||
module.fail_json(msg="Failed to stat %s, %s" % (path, e.strerror), elapsed=elapsed.seconds)
|
module.fail_json(msg="Failed to stat %s, %s" % (path, e.strerror), elapsed=elapsed.seconds)
|
||||||
|
# file doesn't exist yet, so continue
|
||||||
|
else:
|
||||||
|
# File exists. Are there additional things to check?
|
||||||
|
if not compiled_search_re:
|
||||||
|
# nope, succeed!
|
||||||
|
break
|
||||||
|
try:
|
||||||
|
f = open(path)
|
||||||
|
try:
|
||||||
|
if re.search(compiled_search_re, f.read()):
|
||||||
|
# String found, success!
|
||||||
|
break
|
||||||
|
finally:
|
||||||
|
f.close()
|
||||||
|
except IOError:
|
||||||
|
pass
|
||||||
elif port:
|
elif port:
|
||||||
|
alt_connect_timeout = math.ceil((end - datetime.datetime.now()).total_seconds())
|
||||||
try:
|
try:
|
||||||
s = _create_connection( (host, port), connect_timeout)
|
s = _create_connection((host, port), min(connect_timeout, alt_connect_timeout))
|
||||||
if search_regex:
|
except:
|
||||||
|
# Failed to connect by connect_timeout. wait and try again
|
||||||
|
pass
|
||||||
|
else:
|
||||||
|
# Connected -- are there additional conditions?
|
||||||
|
if compiled_search_re:
|
||||||
data = ''
|
data = ''
|
||||||
matched = False
|
matched = False
|
||||||
while 1:
|
while datetime.datetime.now() < end:
|
||||||
data += s.recv(1024)
|
max_timeout = math.ceil((end - datetime.datetime.now()).total_seconds())
|
||||||
if not data:
|
(readable, w, e) = select.select([s], [], [], max_timeout)
|
||||||
|
if not readable:
|
||||||
|
# No new data. Probably means our timeout
|
||||||
|
# expired
|
||||||
|
continue
|
||||||
|
response = s.recv(1024)
|
||||||
|
if not response:
|
||||||
|
# Server shutdown
|
||||||
break
|
break
|
||||||
elif re.search(search_regex, data, re.MULTILINE):
|
data += response
|
||||||
|
if re.search(compiled_search_re, data):
|
||||||
matched = True
|
matched = True
|
||||||
break
|
break
|
||||||
|
|
||||||
|
# Shutdown the client socket
|
||||||
|
s.shutdown(socket.SHUT_RDWR)
|
||||||
|
s.close()
|
||||||
if matched:
|
if matched:
|
||||||
s.shutdown(socket.SHUT_RDWR)
|
# Found our string, success!
|
||||||
s.close()
|
|
||||||
break
|
break
|
||||||
else:
|
else:
|
||||||
|
# Connection established, success!
|
||||||
s.shutdown(socket.SHUT_RDWR)
|
s.shutdown(socket.SHUT_RDWR)
|
||||||
s.close()
|
s.close()
|
||||||
break
|
break
|
||||||
except:
|
|
||||||
time.sleep(1)
|
# Conditions not yet met, wait and try again
|
||||||
pass
|
time.sleep(1)
|
||||||
else:
|
|
||||||
time.sleep(1)
|
else: # while-else
|
||||||
else:
|
# Timeout expired
|
||||||
elapsed = datetime.datetime.now() - start
|
elapsed = datetime.datetime.now() - start
|
||||||
if port:
|
if port:
|
||||||
if search_regex:
|
if search_regex:
|
||||||
|
@ -485,4 +508,5 @@ def main():
|
||||||
|
|
||||||
# import module snippets
|
# import module snippets
|
||||||
from ansible.module_utils.basic import *
|
from ansible.module_utils.basic import *
|
||||||
main()
|
if __name__ == '__main__':
|
||||||
|
main()
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue