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.
pull/28762/head
Daniel Moore 7 years ago committed by ansibot
parent 549ef0f45d
commit c342866016

@ -35,6 +35,14 @@ options:
- Name of the HAProxy backend pool.
required: false
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:
description:
- Name of the backend host to change.
@ -44,7 +52,8 @@ options:
description:
- When disabling a server, immediately terminate all the sessions attached
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
default: false
socket:
@ -122,6 +131,19 @@ EXAMPLES = '''
backend: www
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
- haproxy:
state: disabled
@ -219,6 +241,7 @@ class HAProxy(object):
self.wait = self.module.params['wait']
self.wait_retries = self.module.params['wait_retries']
self.wait_interval = self.module.params['wait_interval']
self.drain = self.module.params['drain']
self.command_results = {}
def execute(self, cmd, timeout=200, capture_output=True):
@ -308,7 +331,7 @@ class HAProxy(object):
r = csv.DictReader(data.splitlines())
state = tuple(
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)
)
)
@ -326,7 +349,8 @@ 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
if state[0]['status'] == status:
return True
if not self.drain or (state[0]['scur'] == '0' and state == 'MAINT'):
return True
else:
time.sleep(self.wait_interval)
@ -354,7 +378,7 @@ class HAProxy(object):
cmd += "; shutdown sessions server $pxname/$svname"
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.
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)
if haproxy_version and (1, 5) <= haproxy_version:
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):
"""
@ -378,6 +402,8 @@ class HAProxy(object):
# toggle enable/disbale server
if self.state == 'enabled':
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':
self.disabled(self.host, self.backend, self.shutdown_sessions)
elif self.state == 'drain':
@ -413,6 +439,7 @@ def main():
wait=dict(required=False, default=False, type='bool'),
wait_retries=dict(required=False, default=WAIT_RETRIES, type='int'),
wait_interval=dict(required=False, default=WAIT_INTERVAL, type='int'),
drain=dict(default=False, type='bool'),
),
)

Loading…
Cancel
Save