From db77cff31d1b01db163472cf6320019392bcc842 Mon Sep 17 00:00:00 2001 From: Ravi Bhure Date: Mon, 29 Sep 2014 22:45:40 -0600 Subject: [PATCH 1/5] Added module haproxy, to handle enable/disable backend server with shutdown_sessions feature in haproxy. --- net_infrastructure/haproxy.py | 391 ++++++++++++++++++++++++++++++++++ 1 file changed, 391 insertions(+) create mode 100644 net_infrastructure/haproxy.py diff --git a/net_infrastructure/haproxy.py b/net_infrastructure/haproxy.py new file mode 100644 index 00000000000..b37e7bff745 --- /dev/null +++ b/net_infrastructure/haproxy.py @@ -0,0 +1,391 @@ +#!/usr/bin/python +# -*- coding: utf-8 -*- + +# (c) 2014, Ravi Bhure +# +# 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 . + +DOCUMENTATION = ''' +--- +module: haproxy +short_description: An Ansible module to handle actions enable/disable server and set/get weight from haproxy using socket commands. +description: + - Enable/Diable Haproxy Backend Server, + - Get/Set weight of Haproxy Backend Server, + using haproxy socket commands - http://haproxy.1wt.eu +notes: + - "enable or disable or set weight commands are restricted and can only be issued on sockets configured for level 'admin', " + - "Check - http://haproxy.1wt.eu/download/1.5/doc/configuration.txt, " + - "Example: 'stats socket /var/run/haproxy.sock level admin'" +options: + action: + description: + - Action to take. + required: true + default: null + choices: [ "enable_server", "disable_server", "get_weight", "set_weight" ] + host: + description: + - Host (backend) to operate in Haproxy. + required: true + default: null + socket: + description: + - Haproxy socket file name with path. + required: false + default: /var/run/haproxy.sock + backend: + description: + - Name of the haproxy backend pool. + Required, else auto-detection applied. + required: false + default: auto-detected + weight: + description: + - The value passed in argument. If the value ends with the '%' sign, then the new weight will be relative to the initially cnfigured weight. Relative weights are only permitted between 0 and 100% and absolute weights are permitted between 0 and 256. + required: false + default: null + shutdown_sessions: + description: + - When disabling 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, for instance. + required: false + default: false +''' + +EXAMPLES = ''' +# disable backend server in 'www' backend +- haproxy: action=disable_server host={{ inventory_hostname }} backend=www + +# disable backend server without backend name (applied to all) +- haproxy: action=disable_server host={{ inventory_hostname }} + +# disable server, provide socket file +- haproxy: action=disable_server host={{ inventory_hostname }} socket=/var/run/haproxy.sock backend=www + +# disable backend server in 'www' backend and drop open sessions to it +- haproxy: action=disable_server host={{ inventory_hostname }} backend=www shutdown_sessions=true + +# enable backend server in 'www' backend +- haproxy: action=enable_server host={{ inventory_hostname }} backend=www + +# report a server's current weight in 'www' backend +- haproxy: action=get_weight host={{ inventory_hostname }} backend=www + +# change a server's current weight in 'www' backend +- haproxy: action=set_weight host={{ inventory_hostname }} backend=www weight=10 + +author: Ravi Bhure +version_added: "1.6" +''' + +import logging +import socket +import re + +logger = logging.getLogger(__name__) + +DEFAULT_SOCKET_LOCATION="/var/run/haproxy.sock" +RECV_SIZE = 1024 + +def main(): + ACTION_CHOICES = [ + 'enable_server', + 'disable_server', + 'get_weight', + 'set_weight' + ] + + # load ansible module object + module = AnsibleModule( + argument_spec = dict( + action = dict(required=True, default=None, choices=ACTION_CHOICES), + host=dict(required=True, default=None), + backend=dict(required=False, default=None), + weight=dict(required=False, default=None), + socket = dict(required=False, default=DEFAULT_SOCKET_LOCATION), + shutdown_sessions=dict(required=False, default=False), + ), + ) + action = module.params['action'] + host = module.params['host'] + backend = module.params['backend'] + weight = module.params['weight'] + socket = module.params['socket'] + shutdown_sessions = module.params['shutdown_sessions'] + + ################################################################## + # Required args per action: + # (enable/disable)_server = (host) + # + # AnsibleModule will verify most stuff, we need to verify + # 'socket' manually. + + ################################################################## + + if action in ['enable_server', 'disable_server', 'get_weight', 'set_weight']: + if not host: + module.fail_json(msg='no host specified for action requiring one') + ################################################################## + if not socket: + module.fail_json('unable to locate haproxy.sock') + + ################################################################## + supports_check_mode=True, + required_one_of=[['action', 'host']] + + ansible_haproxy = HAProxy(module, **module.params) + if module.check_mode: + module.exit_json(changed=True) + else: + ansible_haproxy.act() + ################################################################## + +###################################################################### +class TimeoutException(Exception): + pass + +class HAProxy(object): + """ + Used for communicating with HAProxy through its local UNIX socket interface. + Perform common tasks in Haproxy related to enable server and + disable server. + + The complete set of external commands Haproxy handles is documented + on their website: + + http://haproxy.1wt.eu/download/1.5/doc/configuration.txt#Unix Socket commands + """ + + def __init__(self, module, **kwargs): + self.module = module + self.action = kwargs['action'] + self.host = kwargs['host'] + self.backend = kwargs['backend'] + self.weight = kwargs['weight'] + self.socket = kwargs['socket'] + self.shutdown_sessions = kwargs['shutdown_sessions'] + + self.command_results = [] + + def execute(self, cmd, timeout=200): + """ + Executes a HAProxy command by sending a message to a HAProxy's local + UNIX socket and waiting up to 'timeout' milliseconds for the response. + """ + + buffer = "" + + + self.client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + self.client.connect(self.socket) + self.client.sendall('%s\n' % cmd) + result = '' + buf = '' + buf = self.client.recv(RECV_SIZE) + while buf: + result += buf + buf = self.client.recv(RECV_SIZE) + self.command_results = result.strip() + self.client.close() + return result + + def enable_server(self, host, backend): + """ + Enables backend server for a particular backend. + + enable server / + If the server was previously marked as DOWN for maintenance, this marks the + server UP and checks are re-enabled. + + Both the backend and the server may be specified either by their name or by + their numeric ID, prefixed with a sharp ('#'). + + This command is restricted and can only be issued on sockets configured for + level "admin". + + Syntax: enable server / + """ + svname = host + if self.backend is None: + output = self.execute('show stat') + #sanitize and make a list of lines + output = output.lstrip('# ').strip() + output = output.split('\n') + result = output + + for line in result: + if 'BACKEND' in line: + result = line.split(',')[0] + pxname = result + cmd = "enable server %s/%s" % (pxname, svname) + self.execute(cmd) + + else: + pxname = backend + cmd = "enable server %s/%s" % (pxname, svname) + self.execute(cmd) + + def disable_server(self, host, backend, shutdown_sessions): + """ + Disable backend server for a particular backend. + + disable server / + Mark the server DOWN for maintenance. In this mode, no more checks will be + performed on the server until it leaves maintenance. + If the server is tracked by other servers, those servers will be set to DOWN + during the maintenance. + + In the statistics page, a server DOWN for maintenance will appear with a + "MAINT" status, its tracking servers with the "MAINT(via)" one. + + Both the backend and the server may be specified either by their name or by + their numeric ID, prefixed with a sharp ('#'). + + This command is restricted and can only be issued on sockets configured for + level "admin". + + Syntax: disable server / + """ + svname = host + if self.backend is None: + output = self.execute('show stat') + #sanitize and make a list of lines + output = output.lstrip('# ').strip() + output = output.split('\n') + result = output + + for line in result: + if 'BACKEND' in line: + result = line.split(',')[0] + pxname = result + cmd = "disable server %s/%s" % (pxname, svname) + if shutdown_sessions: + cmd += "; shutdown sessions server %s/%s" % (pxname, svname) + self.execute(cmd) + + else: + pxname = backend + cmd = "disable server %s/%s" % (pxname, svname) + if shutdown_sessions: + cmd += "; shutdown sessions server %s/%s" % (pxname, svname) + self.execute(cmd) + + def get_weight(self, host, backend): + """ + Report a server's current weight. + + get weight / + Report the current weight and the initial weight of server in + backend or an error if either doesn't exist. The initial weight is + the one that appears in the configuration file. Both are normally equal + unless the current weight has been changed. Both the backend and the server + may be specified either by their name or by their numeric ID, prefixed with a + sharp ('#'). + + Syntax: get weight / + """ + svname = host + if self.backend is None: + output = self.execute('show stat') + #sanitize and make a list of lines + output = output.lstrip('# ').strip() + output = output.split('\n') + result = output + + for line in result: + if 'BACKEND' in line: + result = line.split(',')[0] + pxname = result + cmd = "get weight %s/%s" % (pxname, svname) + self.execute(cmd) + + else: + pxname = backend + cmd = "get weight %s/%s" % (pxname, svname) + self.execute(cmd) + + def set_weight(self, host, backend, weight): + """ + Change a server's current weight. + + set weight / [%] + Change a server's weight to the value passed in argument. If the value ends + with the '%' sign, then the new weight will be relative to the initially + configured weight. Relative weights are only permitted between 0 and 100%, + and absolute weights are permitted between 0 and 256. Servers which are part + of a farm running a static load-balancing algorithm have stricter limitations + because the weight cannot change once set. Thus for these servers, the only + accepted values are 0 and 100% (or 0 and the initial weight). Changes take + effect immediately, though certain LB algorithms require a certain amount of + requests to consider changes. A typical usage of this command is to disable + a server during an update by setting its weight to zero, then to enable it + again after the update by setting it back to 100%. This command is restricted + and can only be issued on sockets configured for level "admin". Both the + backend and the server may be specified either by their name or by their + numeric ID, prefixed with a sharp ('#'). + + Syntax: set weight / + """ + svname = host + weight = weight + if self.backend is None: + output = self.execute('show stat') + #sanitize and make a list of lines + output = output.lstrip('# ').strip() + output = output.split('\n') + result = output + + for line in result: + if 'BACKEND' in line: + result = line.split(',')[0] + pxname = result + cmd = "set weight %s/%s %s" % (pxname, svname, weight) + self.execute(cmd) + + else: + pxname = backend + cmd = "set weight %s/%s %s" % (pxname, svname, weight) + self.execute(cmd) + + def act(self): + """ + Figure out what you want to do from ansible, and then do the + needful (at the earliest). + """ + # toggle enable/disbale server + if self.action == 'enable_server': + self.enable_server(self.host, self.backend) + + elif self.action == 'disable_server': + self.disable_server(self.host, self.backend, self.shutdown_sessions) + + # toggle get/set weight + elif self.action == 'get_weight': + self.get_weight(self.host, self.backend) + + elif self.action == 'set_weight': + self.set_weight(self.host, self.backend, self.weight) + # wtf? + else: + self.module.fail_json(msg="unknown action specified: '%s'" % \ + self.action) + + self.module.exit_json(stdout=self.command_results, changed=True) + +# import module snippets +from ansible.module_utils.basic import * + +main() From 78365ecf06197af311bca477927983af196530be Mon Sep 17 00:00:00 2001 From: Ravi Bhure Date: Mon, 1 Dec 2014 19:20:53 -0700 Subject: [PATCH 2/5] updated version 1.9 and moved haproxy module to network category --- {net_infrastructure => network}/haproxy.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) rename {net_infrastructure => network}/haproxy.py (99%) diff --git a/net_infrastructure/haproxy.py b/network/haproxy.py similarity index 99% rename from net_infrastructure/haproxy.py rename to network/haproxy.py index b37e7bff745..d524e20e0cb 100644 --- a/net_infrastructure/haproxy.py +++ b/network/haproxy.py @@ -88,7 +88,7 @@ EXAMPLES = ''' - haproxy: action=set_weight host={{ inventory_hostname }} backend=www weight=10 author: Ravi Bhure -version_added: "1.6" +version_added: "1.9" ''' import logging From bcbe945a4132aa009320ae9883127d501dbc6dc4 Mon Sep 17 00:00:00 2001 From: Ravi Bhure Date: Wed, 3 Dec 2014 08:14:35 -0700 Subject: [PATCH 3/5] Instead of action [enable_server,disable_server] we prefer to use state [enabled,disabled] [FIXED] misplaced the checkmode support (#L146) [FIXED] no need to check if host is not set as the argument spec (#L138), it should already complain about that [FIXED] --- network/haproxy.py | 40 +++++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 19 deletions(-) diff --git a/network/haproxy.py b/network/haproxy.py index d524e20e0cb..8e74b14c842 100644 --- a/network/haproxy.py +++ b/network/haproxy.py @@ -36,7 +36,7 @@ options: - Action to take. required: true default: null - choices: [ "enable_server", "disable_server", "get_weight", "set_weight" ] + choices: [ "enabled", "disabled", "get_weight", "set_weight" ] host: description: - Host (backend) to operate in Haproxy. @@ -67,19 +67,19 @@ options: EXAMPLES = ''' # disable backend server in 'www' backend -- haproxy: action=disable_server host={{ inventory_hostname }} backend=www +- haproxy: action=disabled host={{ inventory_hostname }} backend=www # disable backend server without backend name (applied to all) -- haproxy: action=disable_server host={{ inventory_hostname }} +- haproxy: action=disabled host={{ inventory_hostname }} # disable server, provide socket file -- haproxy: action=disable_server host={{ inventory_hostname }} socket=/var/run/haproxy.sock backend=www +- haproxy: action=disabled host={{ inventory_hostname }} socket=/var/run/haproxy.sock backend=www # disable backend server in 'www' backend and drop open sessions to it -- haproxy: action=disable_server host={{ inventory_hostname }} backend=www shutdown_sessions=true +- haproxy: action=disabled host={{ inventory_hostname }} backend=www shutdown_sessions=true # enable backend server in 'www' backend -- haproxy: action=enable_server host={{ inventory_hostname }} backend=www +- haproxy: action=enabled host={{ inventory_hostname }} backend=www # report a server's current weight in 'www' backend - haproxy: action=get_weight host={{ inventory_hostname }} backend=www @@ -102,8 +102,8 @@ RECV_SIZE = 1024 def main(): ACTION_CHOICES = [ - 'enable_server', - 'disable_server', + 'enabled', + 'disabled', 'get_weight', 'set_weight' ] @@ -118,6 +118,8 @@ def main(): socket = dict(required=False, default=DEFAULT_SOCKET_LOCATION), shutdown_sessions=dict(required=False, default=False), ), + supports_check_mode=True, + ) action = module.params['action'] host = module.params['host'] @@ -128,22 +130,22 @@ def main(): ################################################################## # Required args per action: - # (enable/disable)_server = (host) + # (enabled/disabled) = (host) # # AnsibleModule will verify most stuff, we need to verify # 'socket' manually. ################################################################## - if action in ['enable_server', 'disable_server', 'get_weight', 'set_weight']: - if not host: - module.fail_json(msg='no host specified for action requiring one') + if action in ['set_weight']: + if not weight: + module.fail_json(msg='no weight specified for action, require value in number') + ################################################################## if not socket: module.fail_json('unable to locate haproxy.sock') ################################################################## - supports_check_mode=True, required_one_of=[['action', 'host']] ansible_haproxy = HAProxy(module, **module.params) @@ -202,7 +204,7 @@ class HAProxy(object): self.client.close() return result - def enable_server(self, host, backend): + def enabled(self, host, backend): """ Enables backend server for a particular backend. @@ -238,7 +240,7 @@ class HAProxy(object): cmd = "enable server %s/%s" % (pxname, svname) self.execute(cmd) - def disable_server(self, host, backend, shutdown_sessions): + def disabled(self, host, backend, shutdown_sessions): """ Disable backend server for a particular backend. @@ -366,11 +368,11 @@ class HAProxy(object): needful (at the earliest). """ # toggle enable/disbale server - if self.action == 'enable_server': - self.enable_server(self.host, self.backend) + if self.action == 'enabled': + self.enabled(self.host, self.backend) - elif self.action == 'disable_server': - self.disable_server(self.host, self.backend, self.shutdown_sessions) + elif self.action == 'disabled': + self.disabled(self.host, self.backend, self.shutdown_sessions) # toggle get/set weight elif self.action == 'get_weight': From 44b2cdb5f2682b3823e18300d412832c0de27cc9 Mon Sep 17 00:00:00 2001 From: Ravi Bhure Date: Fri, 5 Dec 2014 11:44:43 -0700 Subject: [PATCH 4/5] used ansible conventions to use trigered action ==> state, now only two states are available enabled/disabled_ get weight for backend server, is default behavior for both state, supports set weight while enable server in lb pool --- network/haproxy.py | 178 ++++++++++++--------------------------------- 1 file changed, 47 insertions(+), 131 deletions(-) diff --git a/network/haproxy.py b/network/haproxy.py index 8e74b14c842..9f2fbbe8d2d 100644 --- a/network/haproxy.py +++ b/network/haproxy.py @@ -21,22 +21,26 @@ DOCUMENTATION = ''' --- module: haproxy -short_description: An Ansible module to handle actions enable/disable server and set/get weight from haproxy using socket commands. +short_description: An Ansible module to handle states enable/disable server and set weight to backend host in haproxy using socket commands. description: - - Enable/Diable Haproxy Backend Server, - - Get/Set weight of Haproxy Backend Server, - using haproxy socket commands - http://haproxy.1wt.eu + - The Enable Haproxy Backend Server, with + supports get current weight for server (default) and + set weight for haproxy backend server when provides. + + - The Disable Haproxy Backend Server, with + supports get current weight for server (default) and + shutdown sessions while disabling backend host server. notes: - - "enable or disable or set weight commands are restricted and can only be issued on sockets configured for level 'admin', " + - "enable or disable commands are restricted and can only be issued on sockets configured for level 'admin', " - "Check - http://haproxy.1wt.eu/download/1.5/doc/configuration.txt, " - "Example: 'stats socket /var/run/haproxy.sock level admin'" options: - action: + state: description: - - Action to take. + - describe the desired state of the given host in lb pool. required: true default: null - choices: [ "enabled", "disabled", "get_weight", "set_weight" ] + choices: [ "enabled", "disabled" ] host: description: - Host (backend) to operate in Haproxy. @@ -66,26 +70,25 @@ options: ''' EXAMPLES = ''' -# disable backend server in 'www' backend -- haproxy: action=disabled host={{ inventory_hostname }} backend=www +examples: -# disable backend server without backend name (applied to all) -- haproxy: action=disabled host={{ inventory_hostname }} +# disable server in 'www' backend pool +- haproxy: state=disabled host={{ inventory_hostname }} backend=www + +# disable server without backend pool name (apply to all available backend pool) +- haproxy: state=disabled host={{ inventory_hostname }} # disable server, provide socket file -- haproxy: action=disabled host={{ inventory_hostname }} socket=/var/run/haproxy.sock backend=www +- haproxy: state=disabled host={{ inventory_hostname }} socket=/var/run/haproxy.sock backend=www -# disable backend server in 'www' backend and drop open sessions to it -- haproxy: action=disabled host={{ inventory_hostname }} backend=www shutdown_sessions=true +# disable backend server in 'www' backend pool and drop open sessions to it +- haproxy: state=disabled host={{ inventory_hostname }} backend=www socket=/var/run/haproxy.sock shutdown_sessions=true -# enable backend server in 'www' backend -- haproxy: action=enabled host={{ inventory_hostname }} backend=www +# enable server in 'www' backend pool +- haproxy: state=enabled host={{ inventory_hostname }} backend=www -# report a server's current weight in 'www' backend -- haproxy: action=get_weight host={{ inventory_hostname }} backend=www - -# change a server's current weight in 'www' backend -- haproxy: action=set_weight host={{ inventory_hostname }} backend=www weight=10 +# enable server in 'www' backend pool with change server(s) weight +- haproxy: state=enabled host={{ inventory_hostname }} socket=/var/run/haproxy.sock weight=10 backend=www author: Ravi Bhure version_added: "1.9" @@ -104,14 +107,12 @@ def main(): ACTION_CHOICES = [ 'enabled', 'disabled', - 'get_weight', - 'set_weight' ] # load ansible module object module = AnsibleModule( argument_spec = dict( - action = dict(required=True, default=None, choices=ACTION_CHOICES), + state = dict(required=True, default=None, choices=ACTION_CHOICES), host=dict(required=True, default=None), backend=dict(required=False, default=None), weight=dict(required=False, default=None), @@ -121,7 +122,7 @@ def main(): supports_check_mode=True, ) - action = module.params['action'] + state = module.params['state'] host = module.params['host'] backend = module.params['backend'] weight = module.params['weight'] @@ -129,7 +130,7 @@ def main(): shutdown_sessions = module.params['shutdown_sessions'] ################################################################## - # Required args per action: + # Required args per state: # (enabled/disabled) = (host) # # AnsibleModule will verify most stuff, we need to verify @@ -137,16 +138,12 @@ def main(): ################################################################## - if action in ['set_weight']: - if not weight: - module.fail_json(msg='no weight specified for action, require value in number') - ################################################################## if not socket: module.fail_json('unable to locate haproxy.sock') ################################################################## - required_one_of=[['action', 'host']] + required_one_of=[['state', 'host']] ansible_haproxy = HAProxy(module, **module.params) if module.check_mode: @@ -173,7 +170,7 @@ class HAProxy(object): def __init__(self, module, **kwargs): self.module = module - self.action = kwargs['action'] + self.state = kwargs['state'] self.host = kwargs['host'] self.backend = kwargs['backend'] self.weight = kwargs['weight'] @@ -190,7 +187,6 @@ class HAProxy(object): buffer = "" - self.client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) self.client.connect(self.socket) self.client.sendall('%s\n' % cmd) @@ -200,11 +196,11 @@ class HAProxy(object): while buf: result += buf buf = self.client.recv(RECV_SIZE) - self.command_results = result.strip() + self.command_results = result.strip() self.client.close() return result - def enabled(self, host, backend): + def enabled(self, host, backend, weight): """ Enables backend server for a particular backend. @@ -232,12 +228,16 @@ class HAProxy(object): if 'BACKEND' in line: result = line.split(',')[0] pxname = result - cmd = "enable server %s/%s" % (pxname, svname) + cmd = "get weight %s/%s ; enable server %s/%s" % (pxname, svname, pxname, svname) + if weight: + cmd += "; set weight %s/%s %s" % (pxname, svname, weight) self.execute(cmd) else: pxname = backend - cmd = "enable server %s/%s" % (pxname, svname) + cmd = "get weight %s/%s ; enable server %s/%s" % (pxname, svname, pxname, svname) + if weight: + cmd += "; set weight %s/%s %s" % (pxname, svname, weight) self.execute(cmd) def disabled(self, host, backend, shutdown_sessions): @@ -273,117 +273,33 @@ class HAProxy(object): if 'BACKEND' in line: result = line.split(',')[0] pxname = result - cmd = "disable server %s/%s" % (pxname, svname) - if shutdown_sessions: + cmd = "get weight %s/%s ; disable server %s/%s" % (pxname, svname, pxname, svname) + if shutdown_sessions == 'true': cmd += "; shutdown sessions server %s/%s" % (pxname, svname) self.execute(cmd) else: pxname = backend - cmd = "disable server %s/%s" % (pxname, svname) - if shutdown_sessions: + cmd = "get weight %s/%s ; disable server %s/%s" % (pxname, svname, pxname, svname) + if shutdown_sessions == 'true': cmd += "; shutdown sessions server %s/%s" % (pxname, svname) self.execute(cmd) - def get_weight(self, host, backend): - """ - Report a server's current weight. - - get weight / - Report the current weight and the initial weight of server in - backend or an error if either doesn't exist. The initial weight is - the one that appears in the configuration file. Both are normally equal - unless the current weight has been changed. Both the backend and the server - may be specified either by their name or by their numeric ID, prefixed with a - sharp ('#'). - - Syntax: get weight / - """ - svname = host - if self.backend is None: - output = self.execute('show stat') - #sanitize and make a list of lines - output = output.lstrip('# ').strip() - output = output.split('\n') - result = output - - for line in result: - if 'BACKEND' in line: - result = line.split(',')[0] - pxname = result - cmd = "get weight %s/%s" % (pxname, svname) - self.execute(cmd) - - else: - pxname = backend - cmd = "get weight %s/%s" % (pxname, svname) - self.execute(cmd) - - def set_weight(self, host, backend, weight): - """ - Change a server's current weight. - - set weight / [%] - Change a server's weight to the value passed in argument. If the value ends - with the '%' sign, then the new weight will be relative to the initially - configured weight. Relative weights are only permitted between 0 and 100%, - and absolute weights are permitted between 0 and 256. Servers which are part - of a farm running a static load-balancing algorithm have stricter limitations - because the weight cannot change once set. Thus for these servers, the only - accepted values are 0 and 100% (or 0 and the initial weight). Changes take - effect immediately, though certain LB algorithms require a certain amount of - requests to consider changes. A typical usage of this command is to disable - a server during an update by setting its weight to zero, then to enable it - again after the update by setting it back to 100%. This command is restricted - and can only be issued on sockets configured for level "admin". Both the - backend and the server may be specified either by their name or by their - numeric ID, prefixed with a sharp ('#'). - - Syntax: set weight / - """ - svname = host - weight = weight - if self.backend is None: - output = self.execute('show stat') - #sanitize and make a list of lines - output = output.lstrip('# ').strip() - output = output.split('\n') - result = output - - for line in result: - if 'BACKEND' in line: - result = line.split(',')[0] - pxname = result - cmd = "set weight %s/%s %s" % (pxname, svname, weight) - self.execute(cmd) - - else: - pxname = backend - cmd = "set weight %s/%s %s" % (pxname, svname, weight) - self.execute(cmd) - def act(self): """ Figure out what you want to do from ansible, and then do the needful (at the earliest). """ # toggle enable/disbale server - if self.action == 'enabled': - self.enabled(self.host, self.backend) + if self.state == 'enabled': + self.enabled(self.host, self.backend, self.weight) - elif self.action == 'disabled': + elif self.state == 'disabled': self.disabled(self.host, self.backend, self.shutdown_sessions) - # toggle get/set weight - elif self.action == 'get_weight': - self.get_weight(self.host, self.backend) - - elif self.action == 'set_weight': - self.set_weight(self.host, self.backend, self.weight) - # wtf? else: - self.module.fail_json(msg="unknown action specified: '%s'" % \ - self.action) + self.module.fail_json(msg="unknown state specified: '%s'" % \ + self.state) self.module.exit_json(stdout=self.command_results, changed=True) From d4a096f7ff05a322c399d2017df9824fcdb5d4e6 Mon Sep 17 00:00:00 2001 From: Ravi Bhure Date: Sun, 7 Dec 2014 02:29:16 -0700 Subject: [PATCH 5/5] Recorded the changes suggested by brian coca --- network/haproxy.py | 191 +++++++++++++++++++-------------------------- 1 file changed, 80 insertions(+), 111 deletions(-) diff --git a/network/haproxy.py b/network/haproxy.py index 9f2fbbe8d2d..0128776af21 100644 --- a/network/haproxy.py +++ b/network/haproxy.py @@ -102,55 +102,7 @@ logger = logging.getLogger(__name__) DEFAULT_SOCKET_LOCATION="/var/run/haproxy.sock" RECV_SIZE = 1024 - -def main(): - ACTION_CHOICES = [ - 'enabled', - 'disabled', - ] - - # load ansible module object - module = AnsibleModule( - argument_spec = dict( - state = dict(required=True, default=None, choices=ACTION_CHOICES), - host=dict(required=True, default=None), - backend=dict(required=False, default=None), - weight=dict(required=False, default=None), - socket = dict(required=False, default=DEFAULT_SOCKET_LOCATION), - shutdown_sessions=dict(required=False, default=False), - ), - supports_check_mode=True, - - ) - state = module.params['state'] - host = module.params['host'] - backend = module.params['backend'] - weight = module.params['weight'] - socket = module.params['socket'] - shutdown_sessions = module.params['shutdown_sessions'] - - ################################################################## - # Required args per state: - # (enabled/disabled) = (host) - # - # AnsibleModule will verify most stuff, we need to verify - # 'socket' manually. - - ################################################################## - - ################################################################## - if not socket: - module.fail_json('unable to locate haproxy.sock') - - ################################################################## - required_one_of=[['state', 'host']] - - ansible_haproxy = HAProxy(module, **module.params) - if module.check_mode: - module.exit_json(changed=True) - else: - ansible_haproxy.act() - ################################################################## +ACTION_CHOICES = ['enabled', 'disabled'] ###################################################################### class TimeoutException(Exception): @@ -194,95 +146,70 @@ class HAProxy(object): buf = '' buf = self.client.recv(RECV_SIZE) while buf: - result += buf - buf = self.client.recv(RECV_SIZE) + result += buf + buf = self.client.recv(RECV_SIZE) self.command_results = result.strip() self.client.close() return result def enabled(self, host, backend, weight): """ - Enables backend server for a particular backend. - - enable server / - If the server was previously marked as DOWN for maintenance, this marks the - server UP and checks are re-enabled. - - Both the backend and the server may be specified either by their name or by - their numeric ID, prefixed with a sharp ('#'). - - This command is restricted and can only be issued on sockets configured for - level "admin". - - Syntax: enable server / + Enabled action, marks server to UP and checks are re-enabled, + also supports to get current weight for server (default) and + set the weight for haproxy backend server when provides. """ svname = host if self.backend is None: - output = self.execute('show stat') - #sanitize and make a list of lines - output = output.lstrip('# ').strip() - output = output.split('\n') - result = output + output = self.execute('show stat') + #sanitize and make a list of lines + output = output.lstrip('# ').strip() + output = output.split('\n') + result = output - for line in result: - if 'BACKEND' in line: - result = line.split(',')[0] - pxname = result - cmd = "get weight %s/%s ; enable server %s/%s" % (pxname, svname, pxname, svname) - if weight: - cmd += "; set weight %s/%s %s" % (pxname, svname, weight) - self.execute(cmd) + for line in result: + if 'BACKEND' in line: + result = line.split(',')[0] + pxname = result + cmd = "get weight %s/%s ; enable server %s/%s" % (pxname, svname, pxname, svname) + if weight: + cmd += "; set weight %s/%s %s" % (pxname, svname, weight) + self.execute(cmd) else: pxname = backend cmd = "get weight %s/%s ; enable server %s/%s" % (pxname, svname, pxname, svname) if weight: - cmd += "; set weight %s/%s %s" % (pxname, svname, weight) + cmd += "; set weight %s/%s %s" % (pxname, svname, weight) self.execute(cmd) def disabled(self, host, backend, shutdown_sessions): """ - Disable backend server for a particular backend. - - disable server / - Mark the server DOWN for maintenance. In this mode, no more checks will be - performed on the server until it leaves maintenance. - If the server is tracked by other servers, those servers will be set to DOWN - during the maintenance. - - In the statistics page, a server DOWN for maintenance will appear with a - "MAINT" status, its tracking servers with the "MAINT(via)" one. - - Both the backend and the server may be specified either by their name or by - their numeric ID, prefixed with a sharp ('#'). - - This command is restricted and can only be issued on sockets configured for - level "admin". - - Syntax: disable server / + Disabled action, marks server to DOWN for maintenance. In this mode, no more checks will be + performed on the server until it leaves maintenance, + also it shutdown sessions while disabling backend host server. """ svname = host if self.backend is None: - output = self.execute('show stat') - #sanitize and make a list of lines - output = output.lstrip('# ').strip() - output = output.split('\n') - result = output + output = self.execute('show stat') + #sanitize and make a list of lines + output = output.lstrip('# ').strip() + output = output.split('\n') + result = output - for line in result: - if 'BACKEND' in line: - result = line.split(',')[0] - pxname = result - cmd = "get weight %s/%s ; disable server %s/%s" % (pxname, svname, pxname, svname) - if shutdown_sessions == 'true': - cmd += "; shutdown sessions server %s/%s" % (pxname, svname) - self.execute(cmd) + for line in result: + if 'BACKEND' in line: + result = line.split(',')[0] + pxname = result + cmd = "get weight %s/%s ; disable server %s/%s" % (pxname, svname, pxname, svname) + if shutdown_sessions: + cmd += "; shutdown sessions server %s/%s" % (pxname, svname) + self.execute(cmd) else: pxname = backend cmd = "get weight %s/%s ; disable server %s/%s" % (pxname, svname, pxname, svname) - if shutdown_sessions == 'true': - cmd += "; shutdown sessions server %s/%s" % (pxname, svname) + if shutdown_sessions: + cmd += "; shutdown sessions server %s/%s" % (pxname, svname) self.execute(cmd) def act(self): @@ -290,6 +217,7 @@ class HAProxy(object): Figure out what you want to do from ansible, and then do the needful (at the earliest). """ + # toggle enable/disbale server if self.state == 'enabled': self.enabled(self.host, self.backend, self.weight) @@ -303,6 +231,47 @@ class HAProxy(object): self.module.exit_json(stdout=self.command_results, changed=True) +def main(): + + # load ansible module object + module = AnsibleModule( + argument_spec = dict( + state = dict(required=True, default=None, choices=ACTION_CHOICES), + host=dict(required=True, default=None), + backend=dict(required=False, default=None), + weight=dict(required=False, default=None), + socket = dict(required=False, default=DEFAULT_SOCKET_LOCATION), + shutdown_sessions=dict(required=False, default=False), + ), + + ) + state = module.params['state'] + host = module.params['host'] + backend = module.params['backend'] + weight = module.params['weight'] + socket = module.params['socket'] + shutdown_sessions = module.params['shutdown_sessions'] + + ################################################################## + # Required args per state: + # (enabled/disabled) = (host) + # + # AnsibleModule will verify most stuff, we need to verify + # 'socket' manually. + + ################################################################## + + ################################################################## + if not socket: + module.fail_json(msg="unable to locate haproxy socket") + + ################################################################## + required_one_of=[['state', 'host']] + + ansible_haproxy = HAProxy(module, **module.params) + ansible_haproxy.act() + ################################################################## + # import module snippets from ansible.module_utils.basic import *