"""
Support for haproxy
.. versionadded:: 2014.7.0
"""
import logging
import os
import stat
import time
try:
import haproxy.cmds # pylint: disable=no-name-in-module
import haproxy.conn # pylint: disable=no-name-in-module
HAS_HAPROXY = True
except ImportError:
HAS_HAPROXY = False
log = logging.getLogger(__name__)
__virtualname__ = "haproxy"
# Default socket location
DEFAULT_SOCKET_URL = "/var/run/haproxy.sock"
# Numeric fields returned by stats
FIELD_NUMERIC = ["weight", "bin", "bout"]
# Field specifying the actual server name
FIELD_NODE_NAME = "name"
[docs]
def __virtual__():
"""
Only load the module if haproxyctl is installed
"""
if HAS_HAPROXY:
return __virtualname__
return (
False,
"The haproxyconn execution module cannot be loaded: haproxyctl module not available",
)
def _get_conn(socket=DEFAULT_SOCKET_URL):
"""
Get connection to haproxy socket.
"""
assert os.path.exists(socket), f"{socket} does not exist."
issock = os.stat(socket).st_mode
assert stat.S_ISSOCK(issock), f"{socket} is not a socket."
ha_conn = haproxy.conn.HaPConn(socket)
return ha_conn
[docs]
def list_servers(backend, socket=DEFAULT_SOCKET_URL, objectify=False):
"""
List servers in haproxy backend.
backend
haproxy backend
socket
haproxy stats socket, default ``/var/run/haproxy.sock``
CLI Example:
.. code-block:: bash
salt '*' haproxy.list_servers mysql
"""
ha_conn = _get_conn(socket)
ha_cmd = haproxy.cmds.listServers(backend=backend)
return ha_conn.sendCmd(ha_cmd, objectify=objectify)
[docs]
def wait_state(backend, server, value="up", timeout=60 * 5, socket=DEFAULT_SOCKET_URL):
"""
Wait for a specific server state
backend
haproxy backend
server
targeted server
value
state value
timeout
timeout before giving up state value, default 5 min
socket
haproxy stats socket, default ``/var/run/haproxy.sock``
CLI Example:
.. code-block:: bash
salt '*' haproxy.wait_state mysql server01 up 60
"""
t = time.time() + timeout
while time.time() < t:
if get_backend(backend=backend, socket=socket)[server]["status"].lower() == value.lower():
return True
return False
[docs]
def get_backend(backend, socket=DEFAULT_SOCKET_URL):
"""
Receive information about a specific backend.
backend
haproxy backend
socket
haproxy stats socket, default ``/var/run/haproxy.sock``
CLI Example:
.. code-block:: bash
salt '*' haproxy.get_backend mysql
"""
backend_data = list_servers(backend=backend, socket=socket).replace("\n", " ").split(" ")
result = {}
# Convert given string to Integer
def num(s):
try:
return int(s)
except ValueError:
return s
active_field = ""
for data in backend_data:
# Check if field or server name
if ":" in data:
active_field = data.replace(":", "").lower()
continue
if active_field.lower() == FIELD_NODE_NAME:
active_server = data
result[active_server] = {}
continue
# Format and set returned field data to active server
if active_field in FIELD_NUMERIC:
if data == "":
result[active_server][active_field] = 0
else:
result[active_server][active_field] = num(data)
else:
result[active_server][active_field] = data
return result
[docs]
def enable_server(name, backend, socket=DEFAULT_SOCKET_URL):
"""
Enable Server in haproxy
name
Server to enable
backend
haproxy backend, or all backends if "*" is supplied
socket
haproxy stats socket, default ``/var/run/haproxy.sock``
CLI Example:
.. code-block:: bash
salt '*' haproxy.enable_server web1.example.com www
"""
if backend == "*":
backends = show_backends(socket=socket).split("\n")
else:
backends = [backend]
results = {}
for backend in backends:
ha_conn = _get_conn(socket)
ha_cmd = haproxy.cmds.enableServer(server=name, backend=backend)
ha_conn.sendCmd(ha_cmd)
results[backend] = list_servers(backend, socket=socket)
return results
[docs]
def disable_server(name, backend, socket=DEFAULT_SOCKET_URL):
"""
Disable server in haproxy.
name
Server to disable
backend
haproxy backend, or all backends if "*" is supplied
socket
haproxy stats socket, default ``/var/run/haproxy.sock``
CLI Example:
.. code-block:: bash
salt '*' haproxy.disable_server db1.example.com mysql
"""
if backend == "*":
backends = show_backends(socket=socket).split("\n")
else:
backends = [backend]
results = {}
for backend in backends:
ha_conn = _get_conn(socket)
ha_cmd = haproxy.cmds.disableServer(server=name, backend=backend)
ha_conn.sendCmd(ha_cmd)
results[backend] = list_servers(backend, socket=socket)
return results
[docs]
def get_weight(name, backend, socket=DEFAULT_SOCKET_URL):
"""
Get server weight
name
Server name
backend
haproxy backend
socket
haproxy stats socket, default ``/var/run/haproxy.sock``
CLI Example:
.. code-block:: bash
salt '*' haproxy.get_weight web1.example.com www
"""
ha_conn = _get_conn(socket)
ha_cmd = haproxy.cmds.getWeight(server=name, backend=backend)
return ha_conn.sendCmd(ha_cmd)
[docs]
def set_weight(name, backend, weight=0, socket=DEFAULT_SOCKET_URL):
"""
Set server weight
name
Server name
backend
haproxy backend
weight
Server Weight
socket
haproxy stats socket, default ``/var/run/haproxy.sock``
CLI Example:
.. code-block:: bash
salt '*' haproxy.set_weight web1.example.com www 13
"""
ha_conn = _get_conn(socket)
ha_cmd = haproxy.cmds.getWeight(server=name, backend=backend, weight=weight)
ha_conn.sendCmd(ha_cmd)
return get_weight(name, backend, socket=socket)
[docs]
def set_state(name, backend, state, socket=DEFAULT_SOCKET_URL):
"""
Force a server's administrative state to a new state. This can be useful to
disable load balancing and/or any traffic to a server. Setting the state to
"ready" puts the server in normal mode, and the command is the equivalent of
the "enable server" command. Setting the state to "maint" disables any traffic
to the server as well as any health checks. This is the equivalent of the
"disable server" command. Setting the mode to "drain" only removes the server
from load balancing but still allows it to be checked and to accept new
persistent connections. Changes are propagated to tracking servers if any.
name
Server name
backend
haproxy backend
state
A string of the state to set. Must be 'ready', 'drain', or 'maint'
socket
haproxy stats socket, default ``/var/run/haproxy.sock``
CLI Example:
.. code-block:: bash
salt '*' haproxy.set_state my_proxy_server my_backend ready
"""
# Pulling this in from the latest 0.5 release which is not yet in PyPi.
# https://github.com/neurogeek/haproxyctl
# pylint: disable=invalid-name
class setServerState(haproxy.cmds.Cmd):
"""Set server state command."""
cmdTxt = "set server %(backend)s/%(server)s state %(value)s\r\n"
p_args = ["backend", "server", "value"]
helpTxt = "Force a server's administrative state to a new state."
ha_conn = _get_conn(socket)
ha_cmd = setServerState(server=name, backend=backend, value=state)
return ha_conn.sendCmd(ha_cmd)
[docs]
def show_frontends(socket=DEFAULT_SOCKET_URL):
"""
Show HaProxy frontends
socket
haproxy stats socket, default ``/var/run/haproxy.sock``
CLI Example:
.. code-block:: bash
salt '*' haproxy.show_frontends
"""
ha_conn = _get_conn(socket)
ha_cmd = haproxy.cmds.showFrontends()
return ha_conn.sendCmd(ha_cmd)
[docs]
def list_frontends(socket=DEFAULT_SOCKET_URL):
"""
List HaProxy frontends
socket
haproxy stats socket, default ``/var/run/haproxy.sock``
CLI Example:
.. code-block:: bash
salt '*' haproxy.list_frontends
"""
return show_frontends(socket=socket).split("\n")
[docs]
def show_backends(socket=DEFAULT_SOCKET_URL):
"""
Show HaProxy Backends
socket
haproxy stats socket, default ``/var/run/haproxy.sock``
CLI Example:
.. code-block:: bash
salt '*' haproxy.show_backends
"""
ha_conn = _get_conn(socket)
ha_cmd = haproxy.cmds.showBackends()
return ha_conn.sendCmd(ha_cmd)
[docs]
def list_backends(servers=True, socket=DEFAULT_SOCKET_URL):
"""
List HaProxy Backends
socket
haproxy stats socket, default ``/var/run/haproxy.sock``
servers
list backends with servers
CLI Example:
.. code-block:: bash
salt '*' haproxy.list_backends
"""
if not servers:
return show_backends(socket=socket).split("\n")
else:
result = {}
for backend in list_backends(servers=False, socket=socket):
result[backend] = get_backend(backend=backend, socket=socket)
return result
[docs]
def get_sessions(name, backend, socket=DEFAULT_SOCKET_URL):
"""
.. versionadded:: 2016.11.0
Get number of current sessions on server in backend (scur)
name
Server name
backend
haproxy backend
socket
haproxy stats socket, default ``/var/run/haproxy.sock``
CLI Example:
.. code-block:: bash
salt '*' haproxy.get_sessions web1.example.com www
"""
# pylint: disable=invalid-name
class getStats(haproxy.cmds.Cmd):
p_args = ["backend", "server"]
cmdTxt = "show stat\r\n"
helpText = "Fetch all statistics"
ha_conn = _get_conn(socket)
ha_cmd = getStats(server=name, backend=backend)
result = ha_conn.sendCmd(ha_cmd)
for line in result.split("\n"):
if line.startswith(backend):
out_cols = line.split(",")
if out_cols[1] == name:
return out_cols[4]