2012-11-27 17:18:46 +01:00
#!/usr/bin/python
2012-08-23 19:41:26 +02:00
# -*- coding: utf-8 -*-
# (c) 2012, Jeroen Hoekx <jeroen@hoekx.be>
#
# This file is part of Ansible
#
# Ansible is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Ansible is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Ansible. If not, see <http://www.gnu.org/licenses/>.
import socket
import datetime
import time
import sys
2013-11-13 01:02:01 +01:00
import re
2012-08-23 19:41:26 +02:00
2012-09-29 00:49:02 +02:00
DOCUMENTATION = '''
---
module: wait_for
2013-11-13 01:02:01 +01:00
short_description: Waits for a condition before continuing.
2012-09-29 00:49:02 +02:00
description:
2013-11-13 01:02:01 +01:00
- Waiting for a port to become available is useful for when services
are not immediately available after their init scripts return -
which is true of certain Java application servers. It is also
useful when starting guests with the M(virt) module and
needing to pause until they are ready. This module can
2013-12-12 09:28:01 +01:00
also be used to wait for a file to be available or absent on the
filesystem or with a regex match a string to be present in a file.
2012-09-29 00:49:02 +02:00
version_added: "0.7"
options:
host:
description:
- hostname or IP address to wait for
required: false
default: "127.0.0.1"
aliases: []
timeout:
description:
- maximum number of seconds to wait for
required: false
default: 300
delay:
description:
- number of seconds to wait before starting to poll
required: false
default: 0
port:
description:
- port number to poll
2013-11-13 01:02:01 +01:00
required: false
2012-09-29 00:49:02 +02:00
state:
description:
2013-12-12 09:28:01 +01:00
- either C(present), C(started), or C(stopped), C(absent)
2013-11-13 01:02:01 +01:00
- When checking a port C(started) will ensure the port is open, C(stopped) will check that it is closed
2013-12-12 09:28:01 +01:00
- When checking for a file or a search string C(present) or C(started) will ensure that the file or string is present before continuing, C(absent) will check that file is absent or removed
choices: [ "present", "started", "stopped", "absent" ]
2012-09-29 00:49:02 +02:00
default: "started"
2013-11-13 01:02:01 +01:00
path:
version_added: "1.4"
required: false
description:
- path to a file on the filesytem that must exist before continuing
search_regex:
version_added: "1.4"
required: false
description:
- with the path option can be used match a string in the file that must match before continuing. Defaults to a multiline regex.
2012-09-29 00:49:02 +02:00
notes: []
2013-05-16 17:13:18 +02:00
requirements: []
2013-12-12 09:28:01 +01:00
author: Jeroen Hoekx, John Jarvis, Andrii Radyk
2012-09-29 00:49:02 +02:00
'''
2013-06-02 00:38:16 +02:00
EXAMPLES = '''
2013-11-13 01:02:01 +01:00
2013-06-02 00:38:16 +02:00
# wait 300 seconds for port 8000 to become open on the host, don't start checking for 10 seconds
2014-01-27 19:09:54 +01:00
- wait_for: port=8000 delay=10
2013-11-13 01:02:01 +01:00
# wait until the file /tmp/foo is present before continuing
- wait_for: path=/tmp/foo
# wait until the string "completed" is in the file /tmp/foo before continuing
- wait_for: path=/tmp/foo search_regex=completed
2013-12-12 09:28:01 +01:00
# wait until the lock file is removed
- wait_for: path=/var/lock/file.lock state=absent
# wait until the process is finished and pid was destroyed
- wait_for: path=/proc/3466/status state=absent
2013-06-02 00:38:16 +02:00
'''
2012-08-23 19:41:26 +02:00
def main():
module = AnsibleModule(
argument_spec = dict(
2012-08-24 20:58:05 +02:00
host=dict(default='127.0.0.1'),
2012-08-23 19:41:26 +02:00
timeout=dict(default=300),
2012-09-19 12:29:47 +02:00
connect_timeout=dict(default=5),
2012-08-24 20:58:05 +02:00
delay=dict(default=0),
2013-11-13 01:02:01 +01:00
port=dict(default=None),
path=dict(default=None),
search_regex=dict(default=None),
2013-12-12 09:28:01 +01:00
state=dict(default='started', choices=['started', 'stopped', 'present', 'absent']),
2012-08-23 19:41:26 +02:00
),
)
params = module.params
2012-08-24 20:58:05 +02:00
host = params['host']
2012-08-23 19:41:26 +02:00
timeout = int(params['timeout'])
2012-09-19 12:29:47 +02:00
connect_timeout = int(params['connect_timeout'])
2012-08-24 20:58:05 +02:00
delay = int(params['delay'])
2013-11-13 01:02:01 +01:00
if params['port']:
port = int(params['port'])
else:
port = None
2012-08-24 19:08:45 +02:00
state = params['state']
2013-11-13 01:02:01 +01:00
path = params['path']
search_regex = params['search_regex']
if port and path:
module.fail_json(msg="port and path parameter can not both be passed to wait_for")
if path and state == 'stopped':
module.fail_json(msg="state=stopped should only be used for checking a port in the wait_for module")
2013-06-28 02:37:40 +02:00
start = datetime.datetime.now()
2012-08-24 20:58:05 +02:00
if delay:
time.sleep(delay)
2013-12-12 09:28:01 +01:00
if state in [ 'stopped', 'absent' ]:
2013-11-13 01:02:01 +01:00
### first wait for the stop condition
2012-09-19 12:29:47 +02:00
end = start + datetime.timedelta(seconds=timeout)
2012-08-23 19:41:26 +02:00
2012-08-24 19:08:45 +02:00
while datetime.datetime.now() < end:
2013-12-12 09:28:01 +01:00
if path:
try:
f = open(path)
f.close()
time.sleep(1)
pass
except IOError:
break
elif port:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(connect_timeout)
try:
s.connect( (host, port) )
s.shutdown(socket.SHUT_RDWR)
s.close()
time.sleep(1)
except:
break
2012-08-24 19:08:45 +02:00
else:
2012-09-19 12:29:47 +02:00
elapsed = datetime.datetime.now() - start
2013-12-12 09:28:01 +01:00
if port:
module.fail_json(msg="Timeout when waiting for %s:%s to stop." % (host, port), elapsed=elapsed.seconds)
elif path:
module.fail_json(msg="Timeout when waiting for %s to be absent." % (path), elapsed=elapsed.seconds)
2012-08-23 19:41:26 +02:00
2013-11-13 01:02:01 +01:00
elif state in ['started', 'present']:
### wait for start condition
2012-09-19 12:29:47 +02:00
end = start + datetime.timedelta(seconds=timeout)
2012-08-24 19:08:45 +02:00
while datetime.datetime.now() < end:
2013-11-13 01:02:01 +01:00
if path:
try:
2013-11-25 09:01:24 +01:00
f = open(path)
try:
2013-11-13 01:02:01 +01:00
if search_regex:
if re.search(search_regex, f.read(), re.MULTILINE):
break
else:
time.sleep(1)
else:
break
2013-11-25 09:01:24 +01:00
finally:
f.close()
2013-11-13 01:02:01 +01:00
except IOError:
time.sleep(1)
pass
elif port:
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.settimeout(connect_timeout)
try:
s.connect( (host, port) )
s.shutdown(socket.SHUT_RDWR)
s.close()
break
except:
time.sleep(1)
pass
2012-08-24 19:08:45 +02:00
else:
2012-09-19 12:29:47 +02:00
elapsed = datetime.datetime.now() - start
2013-11-13 01:02:01 +01:00
if port:
module.fail_json(msg="Timeout when waiting for %s:%s" % (host, port), elapsed=elapsed.seconds)
elif path:
if search_regex:
module.fail_json(msg="Timeout when waiting for search string %s in %s" % (search_regex, path), elapsed=elapsed.seconds)
else:
module.fail_json(msg="Timeout when waiting for file %s" % (path), elapsed=elapsed.seconds)
2012-08-24 19:08:45 +02:00
2012-09-19 12:29:47 +02:00
elapsed = datetime.datetime.now() - start
2013-11-13 01:02:01 +01:00
module.exit_json(state=state, port=port, search_regex=search_regex, path=path, elapsed=elapsed.seconds)
2012-08-23 19:41:26 +02:00
2013-12-02 21:13:49 +01:00
# import module snippets
2013-12-02 21:11:23 +01:00
from ansible.module_utils.basic import *
2012-08-23 19:41:26 +02:00
main()