mirror of
https://github.com/ansible-collections/community.general.git
synced 2024-09-14 20:13:21 +02:00
HAProxy: add support for draining connections in maintenace mode (#25887)
* HAProxy: Support waiting for nodes to drain before maint mode A common task when working with a HAProxy-managed node is to first "drain" it and then place it into maintenance mode (to be repaired or redeployed). (Draining such a node consists of preventing new connections from being established while waiting for active sessions to expire/close.) This commit creates a new `drain' parameter for Ansible's HAProxy module, which, when set to `yes` in conjunction with `state: disabled` and `wait: yes`, causes the module to attempt to set a node to drain, wait for it to finish draining, and put the node into maintenance mode. The action is recorded as a success if these steps are completed before a maximum wait timeout is reached. Implements: https://github.com/ansible/ansible-modules-extras/issues/521 Acknowledgments: This is based on user krislindgren's pull request #21420 * Correct how drain option is handled Previously the `drain` parameter would have no effect, since the `disabled` state would still immediately place backend servers into maintenance mode.
This commit is contained in:
parent
549ef0f45d
commit
c342866016
1 changed files with 32 additions and 5 deletions
|
@ -35,6 +35,14 @@ options:
|
||||||
- Name of the HAProxy backend pool.
|
- Name of the HAProxy backend pool.
|
||||||
required: false
|
required: false
|
||||||
default: auto-detected
|
default: auto-detected
|
||||||
|
drain:
|
||||||
|
description:
|
||||||
|
- Wait until the server has no active connections or until the timeout
|
||||||
|
determined by wait_interval and wait_retries is reached. Continue only
|
||||||
|
after the status changes to 'MAINT'. This overrides the
|
||||||
|
shutdown_sessions option.
|
||||||
|
default: false
|
||||||
|
version_added: "2.4"
|
||||||
host:
|
host:
|
||||||
description:
|
description:
|
||||||
- Name of the backend host to change.
|
- Name of the backend host to change.
|
||||||
|
@ -44,7 +52,8 @@ options:
|
||||||
description:
|
description:
|
||||||
- When disabling a server, immediately terminate all the sessions attached
|
- When disabling a server, immediately terminate all the sessions attached
|
||||||
to the specified server. This can be used to terminate long-running
|
to the specified server. This can be used to terminate long-running
|
||||||
sessions after a server is put into maintenance mode.
|
sessions after a server is put into maintenance mode. Overridden by the
|
||||||
|
drain option.
|
||||||
required: false
|
required: false
|
||||||
default: false
|
default: false
|
||||||
socket:
|
socket:
|
||||||
|
@ -122,6 +131,19 @@ EXAMPLES = '''
|
||||||
backend: www
|
backend: www
|
||||||
wait: yes
|
wait: yes
|
||||||
|
|
||||||
|
# Place server in drain mode, providing a socket file. Then check the server's
|
||||||
|
# status every minute to see if it changes to maintenance mode, continuing if it
|
||||||
|
# does in an hour and failing otherwise.
|
||||||
|
- haproxy:
|
||||||
|
state: disabled
|
||||||
|
host: '{{ inventory_hostname }}'
|
||||||
|
socket: /var/run/haproxy.sock
|
||||||
|
backend: www
|
||||||
|
wait: yes
|
||||||
|
drain: yes
|
||||||
|
wait_interval: 1
|
||||||
|
wait_retries: 60
|
||||||
|
|
||||||
# disable backend server in 'www' backend pool and drop open sessions to it
|
# disable backend server in 'www' backend pool and drop open sessions to it
|
||||||
- haproxy:
|
- haproxy:
|
||||||
state: disabled
|
state: disabled
|
||||||
|
@ -219,6 +241,7 @@ class HAProxy(object):
|
||||||
self.wait = self.module.params['wait']
|
self.wait = self.module.params['wait']
|
||||||
self.wait_retries = self.module.params['wait_retries']
|
self.wait_retries = self.module.params['wait_retries']
|
||||||
self.wait_interval = self.module.params['wait_interval']
|
self.wait_interval = self.module.params['wait_interval']
|
||||||
|
self.drain = self.module.params['drain']
|
||||||
self.command_results = {}
|
self.command_results = {}
|
||||||
|
|
||||||
def execute(self, cmd, timeout=200, capture_output=True):
|
def execute(self, cmd, timeout=200, capture_output=True):
|
||||||
|
@ -308,7 +331,7 @@ class HAProxy(object):
|
||||||
r = csv.DictReader(data.splitlines())
|
r = csv.DictReader(data.splitlines())
|
||||||
state = tuple(
|
state = tuple(
|
||||||
map(
|
map(
|
||||||
lambda d: {'status': d['status'], 'weight': d['weight']},
|
lambda d: {'status': d['status'], 'weight': d['weight'], 'scur': d['scur']},
|
||||||
filter(lambda d: (pxname is None or d['pxname'] == pxname) and d['svname'] == svname, r)
|
filter(lambda d: (pxname is None or d['pxname'] == pxname) and d['svname'] == svname, r)
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
@ -326,6 +349,7 @@ class HAProxy(object):
|
||||||
|
|
||||||
# We can assume there will only be 1 element in state because both svname and pxname are always set when we get here
|
# We can assume there will only be 1 element in state because both svname and pxname are always set when we get here
|
||||||
if state[0]['status'] == status:
|
if state[0]['status'] == status:
|
||||||
|
if not self.drain or (state[0]['scur'] == '0' and state == 'MAINT'):
|
||||||
return True
|
return True
|
||||||
else:
|
else:
|
||||||
time.sleep(self.wait_interval)
|
time.sleep(self.wait_interval)
|
||||||
|
@ -354,7 +378,7 @@ class HAProxy(object):
|
||||||
cmd += "; shutdown sessions server $pxname/$svname"
|
cmd += "; shutdown sessions server $pxname/$svname"
|
||||||
self.execute_for_backends(cmd, backend, host, 'MAINT')
|
self.execute_for_backends(cmd, backend, host, 'MAINT')
|
||||||
|
|
||||||
def drain(self, host, backend):
|
def drain(self, host, backend, status='DRAIN'):
|
||||||
"""
|
"""
|
||||||
Drain action, sets the server to DRAIN mode.
|
Drain action, sets the server to DRAIN mode.
|
||||||
In this mode mode, the server will not accept any new connections
|
In this mode mode, the server will not accept any new connections
|
||||||
|
@ -365,7 +389,7 @@ class HAProxy(object):
|
||||||
# check if haproxy version suppots DRAIN state (starting with 1.5)
|
# check if haproxy version suppots DRAIN state (starting with 1.5)
|
||||||
if haproxy_version and (1, 5) <= haproxy_version:
|
if haproxy_version and (1, 5) <= haproxy_version:
|
||||||
cmd = "set server $pxname/$svname state drain"
|
cmd = "set server $pxname/$svname state drain"
|
||||||
self.execute_for_backends(cmd, backend, host, 'DRAIN')
|
self.execute_for_backends(cmd, backend, host, status)
|
||||||
|
|
||||||
def act(self):
|
def act(self):
|
||||||
"""
|
"""
|
||||||
|
@ -378,6 +402,8 @@ class HAProxy(object):
|
||||||
# toggle enable/disbale server
|
# toggle enable/disbale server
|
||||||
if self.state == 'enabled':
|
if self.state == 'enabled':
|
||||||
self.enabled(self.host, self.backend, self.weight)
|
self.enabled(self.host, self.backend, self.weight)
|
||||||
|
elif self.state == 'disabled' and self.drain:
|
||||||
|
self.drain(self.host, self.backend, status='MAINT')
|
||||||
elif self.state == 'disabled':
|
elif self.state == 'disabled':
|
||||||
self.disabled(self.host, self.backend, self.shutdown_sessions)
|
self.disabled(self.host, self.backend, self.shutdown_sessions)
|
||||||
elif self.state == 'drain':
|
elif self.state == 'drain':
|
||||||
|
@ -413,6 +439,7 @@ def main():
|
||||||
wait=dict(required=False, default=False, type='bool'),
|
wait=dict(required=False, default=False, type='bool'),
|
||||||
wait_retries=dict(required=False, default=WAIT_RETRIES, type='int'),
|
wait_retries=dict(required=False, default=WAIT_RETRIES, type='int'),
|
||||||
wait_interval=dict(required=False, default=WAIT_INTERVAL, type='int'),
|
wait_interval=dict(required=False, default=WAIT_INTERVAL, type='int'),
|
||||||
|
drain=dict(default=False, type='bool'),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue