mirror of
https://github.com/netbirdio/plugins.git
synced 2026-05-22 18:44:07 -07:00
net/haproxy: replace bundled haproxyctl library
The bundled library is a modified version of haproxyctl: https://github.com/neurogeek/haproxyctl Now a proper fork was created: https://github.com/markt-de/haproxy-cli The FreeBSD port is pending approval: https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=275562
This commit is contained in:
@@ -2,7 +2,7 @@ PLUGIN_NAME= haproxy
|
||||
PLUGIN_VERSION= 4.1
|
||||
PLUGIN_REVISION= 1
|
||||
PLUGIN_COMMENT= Reliable, high performance TCP/HTTP load balancer
|
||||
PLUGIN_DEPENDS= haproxy26
|
||||
PLUGIN_DEPENDS= haproxy26 py${PLUGIN_PYTHON}-haproxy-cli
|
||||
PLUGIN_MAINTAINER= opnsense@moov.de
|
||||
|
||||
.include "../../Mk/plugins.mk"
|
||||
|
||||
@@ -9,6 +9,9 @@ Plugin Changelog
|
||||
Fixed:
|
||||
* fix typo in cert sync script
|
||||
|
||||
Changed:
|
||||
* replace bundled haproxyctl library with haproxy-cli
|
||||
|
||||
4.1
|
||||
|
||||
Fixed:
|
||||
|
||||
@@ -1,3 +0,0 @@
|
||||
"""haproxy lib for socket commands.
|
||||
Based on: https://github.com/neurogeek/haproxyctl"""
|
||||
__version__ = "1.0"
|
||||
@@ -1,335 +0,0 @@
|
||||
"""cmds.py - Implementations of the different HAProxy commands"""
|
||||
import re
|
||||
import csv
|
||||
import json
|
||||
from collections import OrderedDict
|
||||
from io import StringIO
|
||||
|
||||
class Cmd():
|
||||
"""Cmd - Command base class"""
|
||||
req_args = []
|
||||
args = {}
|
||||
cmdTxt = ""
|
||||
helpTxt = ""
|
||||
|
||||
# pylint: disable=unused-argument
|
||||
def __init__(self, *args, **kwargs):
|
||||
"""Argument to the command are given in kwargs only. We ignore *args."""
|
||||
self.args = kwargs
|
||||
valid_kwargs = [k for (k, v) in kwargs.items() if v is not None]
|
||||
|
||||
if not all([a in valid_kwargs for a in self.req_args]):
|
||||
raise Exception(f"Wrong number of arguments. Required arguments are: {self.WhatArgs()}")
|
||||
|
||||
def WhatArgs(self):
|
||||
"""Returns a formatted string of arguments to this command."""
|
||||
return ",".join(self.req_args)
|
||||
|
||||
@classmethod
|
||||
def getHelp(cls):
|
||||
"""Get formatted help string for this command."""
|
||||
txtArgs = ",".join(cls.req_args)
|
||||
|
||||
if not txtArgs:
|
||||
txtArgs = "None"
|
||||
return " ".join((cls.helpTxt, "Arguments: %s" % txtArgs))
|
||||
|
||||
def getCmd(self):
|
||||
"""Gets the command line for this command.
|
||||
The default behavior is to apply the args dict to cmdTxt
|
||||
"""
|
||||
return self.cmdTxt % self.args
|
||||
|
||||
def getBootstrapOutput(self, resObj):
|
||||
""" Returns results gathered from HAProxy as jquery bootstrap output """
|
||||
args = {
|
||||
"rows": resObj,
|
||||
"page": int(self.args['page']) if self.args['page'] != None else 1,
|
||||
"page_rows": int(self.args['page_rows']) if self.args['page_rows'] != None else len(rows),
|
||||
"search": self.args['search'],
|
||||
"sort_col": self.args['sort_col'] if self.args['sort_col'] else 'id',
|
||||
"sort_dir": self.args['sort_dir'],
|
||||
}
|
||||
rows = args['rows']
|
||||
# search
|
||||
if args['search']:
|
||||
filtered_rows = []
|
||||
for row in rows:
|
||||
def inner(row):
|
||||
for k, v in row.items():
|
||||
if args['search'] in v:
|
||||
return row
|
||||
return None
|
||||
|
||||
match = inner(row)
|
||||
if match:
|
||||
filtered_rows.append(match)
|
||||
rows = filtered_rows
|
||||
|
||||
# sort
|
||||
rows.sort(key=lambda k: k[args['sort_col']], reverse=True if args['sort_dir'] == 'desc' else False)
|
||||
|
||||
# pager
|
||||
total = len(rows)
|
||||
pages = [rows[i:i + args['page_rows']] for i in range(0, total, args['page_rows'])]
|
||||
if pages and (args['page'] > len(pages) or args['page'] < 1):
|
||||
raise KeyError(f"Current page {args['page']} does not exist. Available pages: {len(pages)}")
|
||||
page = pages[args['page'] - 1] if pages else []
|
||||
|
||||
return json.dumps({
|
||||
"rows": page,
|
||||
"total": total,
|
||||
"rowCount": args['page_rows'],
|
||||
"current": args['page']
|
||||
})
|
||||
|
||||
def getJsonOutput(self, resObj):
|
||||
"""Returns results gathered from HAProxy as json"""
|
||||
return json.dumps(resObj)
|
||||
|
||||
def getResult(self, res):
|
||||
"""Returns raw results gathered from HAProxy"""
|
||||
if res == '\n':
|
||||
res = None
|
||||
|
||||
if self.args['output'] == 'json':
|
||||
return self.getJsonOutput(self.getResultObj(res))
|
||||
|
||||
if self.args['output'] == 'bootstrap':
|
||||
return self.getBootstrapOutput(self.getResultObj(res))
|
||||
|
||||
return res
|
||||
|
||||
def getResultObj(self, res):
|
||||
"""Returns refined output from HAProxy, packed inside a Python obj i.e. a dict()"""
|
||||
return res
|
||||
|
||||
class setServerAgent(Cmd):
|
||||
cmdTxt = "set server %(backend)s/%(server)s agent %(value)s\r\n"
|
||||
req_args = ['backend', 'server', 'value']
|
||||
helpTxt = "Force a server's agent to a new state."
|
||||
|
||||
class setServerHealth(Cmd):
|
||||
cmdTxt = "set server %(backend)s/%(server)s health %(value)s\r\n"
|
||||
req_args = ['backend', 'server', 'value']
|
||||
helpTxt = "Force a server's health to a new state."
|
||||
|
||||
class setServerState(Cmd):
|
||||
cmdTxt = "set server %(backend)s/%(server)s state %(value)s\r\n"
|
||||
req_args = ['backend', 'server', 'value']
|
||||
helpTxt = "Force a server's administrative state to a new state."
|
||||
|
||||
class setServerWeight(Cmd):
|
||||
cmdTxt = "set server %(backend)s/%(server)s weight %(value)s\r\n"
|
||||
req_args = ['backend', 'server', 'value']
|
||||
helpTxt = "Force a server's weight to a new state."
|
||||
|
||||
class showSslCrtLists(Cmd):
|
||||
cmdTxt = "show ssl crt-list\r\n"
|
||||
helpTxt = "Show the list of crt-lists."
|
||||
|
||||
def getResultObj(self, res):
|
||||
result = { "crt_lists": []}
|
||||
for line in res.split("\n"):
|
||||
if line.startswith('/'):
|
||||
result["crt_lists"].append(line)
|
||||
return result
|
||||
|
||||
class showSslCrtList(Cmd):
|
||||
cmdTxt = "show ssl crt-list -n %(crt_list)s\r\n"
|
||||
req_args = ['crt_list']
|
||||
helpTxt = "Show the the content of a crt-list."
|
||||
|
||||
def getResultObj(self, res):
|
||||
result = {}
|
||||
list_id = None
|
||||
for line in res.split("\n"):
|
||||
if line.startswith('# '):
|
||||
list_id = line.split("# ")[1]
|
||||
result["certs"] = []
|
||||
|
||||
if list_id and line.startswith('/'):
|
||||
result["certs"].append(line)
|
||||
|
||||
if result:
|
||||
return result
|
||||
|
||||
return {"error": res.strip()}
|
||||
|
||||
class showSslCerts(Cmd):
|
||||
cmdTxt = "show ssl cert\r\n"
|
||||
helpTxt = "Display the SSL certificates used in memory."
|
||||
|
||||
def getResultObj(self, res):
|
||||
result = {
|
||||
"transaction": [],
|
||||
"filename": []
|
||||
}
|
||||
for line in res.split("\n"):
|
||||
if line.startswith('*'):
|
||||
result['transaction'].append(line)
|
||||
elif line.startswith('/'):
|
||||
result['filename'].append(line)
|
||||
return result
|
||||
|
||||
class showSslCert(Cmd):
|
||||
cmdTxt = "show ssl cert %(certfile)s\r\n"
|
||||
req_args = ['certfile']
|
||||
helpTxt = "Display the details of a SSL certificate used in memory."
|
||||
|
||||
def getResultObj(self, res):
|
||||
result = {}
|
||||
cert_id = None
|
||||
for line in res.split("\n"):
|
||||
if line:
|
||||
key = line.split(":")[0]
|
||||
val = line.split(":")[1].strip()
|
||||
|
||||
if key == 'Filename':
|
||||
cert_id = val
|
||||
|
||||
if cert_id:
|
||||
result[key] = val
|
||||
|
||||
if result:
|
||||
return result
|
||||
|
||||
return {"error": res.strip()}
|
||||
|
||||
class addToSslCrtList(Cmd):
|
||||
cmdTxt = "add ssl crt-list %(crt_list)s %(certfile)s\r\n"
|
||||
req_args = ['crt_list', 'certfile']
|
||||
helpTxt = "Add a ssl cert to a crt-list."
|
||||
|
||||
class delFromSslCrtList(Cmd):
|
||||
cmdTxt = "del ssl crt-list %(crt_list)s %(certfile)s\r\n"
|
||||
req_args = ['crt_list', 'certfile']
|
||||
helpTxt = "Delete a ssl cert from a crt-list."
|
||||
|
||||
class newSslCrt(Cmd):
|
||||
"""" Create an empty slot for the certificate in HAProxy’s memory """
|
||||
cmdTxt = "new ssl cert %(certfile)s\r\n"
|
||||
req_args = ['certfile']
|
||||
helpTxt = "Create a new certificate file to be used in a crt-list or a directory."
|
||||
|
||||
class updateSslCrt(Cmd):
|
||||
"""" Begin a transaction to upload the certificate into a slot in HAProxy’s memory """
|
||||
cmdTxt = "set ssl cert %(certfile)s <<\n%(payload)s\r\n"
|
||||
req_args = ['certfile', 'payload']
|
||||
helpTxt = "Replace a certificate file."
|
||||
|
||||
class delSslCrt(Cmd):
|
||||
"""" Begin a transaction to remove the certificate from a slot in HAProxy’s memory """
|
||||
cmdTxt = "del ssl cert %(certfile)s\r\n"
|
||||
req_args = ['certfile']
|
||||
helpTxt = "Delete delete an unused certificate file."
|
||||
|
||||
class commitSslCrt(Cmd):
|
||||
""" Commit the transaction so HAProxy detects the change. """
|
||||
cmdTxt = "commit ssl cert %(certfile)s\r\n"
|
||||
req_args = ['certfile']
|
||||
helpTxt = "Commit a certificate file."
|
||||
|
||||
class abortSslCrt(Cmd):
|
||||
cmdTxt = "abort ssl cert %(certfile)s\r\n"
|
||||
req_args = ['certfile']
|
||||
helpTxt = "Abort a transaction for a certificate file."
|
||||
|
||||
class showFBEnds(Cmd):
|
||||
"""Base class for getting a listing Frontends and Backends"""
|
||||
switch = ""
|
||||
cmdTxt = "show stat\r\n"
|
||||
|
||||
def getResult(self, res):
|
||||
return "\n".join(self._getResult(res))
|
||||
|
||||
def getResultObj(self, res):
|
||||
return self._getResult(res)
|
||||
|
||||
def _getResult(self, res):
|
||||
"""Show Frontend/Backends. To do this, we extract info from
|
||||
the stat command and filter out by a specific
|
||||
switch (FRONTEND/BACKEND)"""
|
||||
|
||||
if not self.switch:
|
||||
raise Exception("No action specified")
|
||||
|
||||
result = []
|
||||
lines = res.split('\n')
|
||||
cl = re.compile("^[^,].+," + self.switch.upper() + ",.*$")
|
||||
|
||||
for e in lines:
|
||||
me = re.match(cl, e)
|
||||
if me:
|
||||
print(e)
|
||||
result.append(e.split(",")[0])
|
||||
return result
|
||||
|
||||
class showFrontends(showFBEnds):
|
||||
"""Show frontends command."""
|
||||
switch = "frontend"
|
||||
helpTxt = "List all Frontends."
|
||||
|
||||
class showBackends(showFBEnds):
|
||||
"""Show backends command."""
|
||||
switch = "backend"
|
||||
helpTxt = "List all Backends."
|
||||
|
||||
class showInfo(Cmd):
|
||||
"""Show info HAProxy command"""
|
||||
cmdTxt = "show info\r\n"
|
||||
helpTxt = "Show info on HAProxy instance."
|
||||
|
||||
def getResultObj(self, res):
|
||||
resDict = {}
|
||||
for line in res.split('\n'):
|
||||
k, v = line.split(':')
|
||||
resDict[k] = v
|
||||
|
||||
return resDict
|
||||
|
||||
class showSessions(Cmd):
|
||||
"""Show sess HAProxy command"""
|
||||
cmdTxt = "show sess\r\n"
|
||||
helpTxt = "Show HAProxy sessions."
|
||||
|
||||
def getResultObj(self, res):
|
||||
return res.split('\n')
|
||||
|
||||
class baseStat(Cmd):
|
||||
"""Base class for stats commands."""
|
||||
|
||||
def getDict(self, res):
|
||||
# clean response
|
||||
res = re.sub(r'^# ', '', res, re.MULTILINE)
|
||||
res = re.sub(r',\n', '\n', res, re.MULTILINE)
|
||||
res = re.sub(r',\n\n', '\n', res, re.MULTILINE)
|
||||
|
||||
csv_string = StringIO(res)
|
||||
return csv.DictReader(csv_string, delimiter=',')
|
||||
|
||||
class showServers(baseStat):
|
||||
"""Show all servers. If backend is given, show only servers for this backend. """
|
||||
cmdTxt = "show stat\r\n"
|
||||
helpTxt = "Lists all servers. Filter for servers in backend, if set."
|
||||
|
||||
def getResultObj(self, res):
|
||||
servers = []
|
||||
|
||||
reader = self.getDict(res)
|
||||
for row in reader:
|
||||
row = OrderedDict(row)
|
||||
# show only server
|
||||
if row['svname'] in ['BACKEND', 'FRONTEND']:
|
||||
continue
|
||||
|
||||
# filter server for given backend
|
||||
if self.args['backend'] and row['pxname'] != self.args['backend']:
|
||||
continue
|
||||
|
||||
# add id
|
||||
row['id'] = f"{row['pxname']}/{row['svname']}"
|
||||
row.move_to_end('id', last=False)
|
||||
servers.append(dict(row))
|
||||
|
||||
return servers
|
||||
@@ -1,83 +0,0 @@
|
||||
# pylint: disable=locally-disabled, too-few-public-methods, no-self-use, invalid-name
|
||||
"""conn.py - Connection module."""
|
||||
import re
|
||||
from socket import socket, AF_INET, AF_UNIX, SOCK_STREAM
|
||||
from haproxy import const
|
||||
|
||||
class HapError(Exception):
|
||||
"""Generic exception for haproxyctl."""
|
||||
pass
|
||||
|
||||
class HaPConn(object):
|
||||
"""HAProxy Socket object.
|
||||
This class abstract the socket interface so
|
||||
commands can be sent to HAProxy and results received and
|
||||
parse by the command objects"""
|
||||
|
||||
def __init__(self, sfile, socket_module=socket):
|
||||
"""Initializes an HAProxy and opens a connection to it
|
||||
(sfile, type) -> Path for the UNIX socket"""
|
||||
|
||||
self.sock = None
|
||||
sfile = sfile.strip()
|
||||
stype = AF_UNIX
|
||||
self.socket_module = socket_module
|
||||
|
||||
mobj = re.match(
|
||||
'(?P<proto>unix://|tcp://)(?P<addr>[^:]+):*(?P<port>[0-9]*)$', sfile)
|
||||
|
||||
if mobj:
|
||||
proto = mobj.groupdict().get('proto', None)
|
||||
addr = mobj.groupdict().get('addr', None)
|
||||
port = mobj.groupdict().get('port', '')
|
||||
|
||||
if not addr or not proto:
|
||||
raise HapError('Could not determine type of socket.')
|
||||
|
||||
if proto == const.HAP_TCP_PATH:
|
||||
if not port:
|
||||
raise HapError('When using a tcp socket, a port is needed.')
|
||||
stype = AF_INET
|
||||
sfile = (addr, int(port))
|
||||
|
||||
if proto == const.HAP_UNIX_PATH:
|
||||
stype = AF_UNIX
|
||||
sfile = addr
|
||||
|
||||
# Fallback should be sfile/AF_UNIX by default
|
||||
self.sfile = (sfile, stype)
|
||||
self.open()
|
||||
|
||||
def open(self):
|
||||
"""Opens a connection for the socket.
|
||||
This function should only be called if
|
||||
self.closed() method was called"""
|
||||
|
||||
sfile, stype = self.sfile
|
||||
self.sock = self.socket_module(stype, SOCK_STREAM)
|
||||
self.sock.connect(sfile)
|
||||
|
||||
def sendCmd(self, cmd, objectify=False):
|
||||
"""Receives a command obj and sends it to the socket. Receives the output and passes it
|
||||
through the command to parse it.
|
||||
objectify -> Return an object instead of plain text"""
|
||||
|
||||
res = ""
|
||||
try:
|
||||
self.sock.send(cmd.getCmd())
|
||||
except TypeError:
|
||||
self.sock.send(bytearray(cmd.getCmd(), 'ASCII'))
|
||||
output = self.sock.recv(const.HAP_BUFSIZE)
|
||||
|
||||
while output:
|
||||
res += output.decode('UTF-8')
|
||||
output = self.sock.recv(const.HAP_BUFSIZE)
|
||||
|
||||
if objectify:
|
||||
return cmd.getResultObj(res)
|
||||
|
||||
return cmd.getResult(res)
|
||||
|
||||
def close(self):
|
||||
"""Closes the socket"""
|
||||
self.sock.close()
|
||||
@@ -1,7 +0,0 @@
|
||||
"""const.py - Constants for haproxyctl."""
|
||||
HAP_OK = 1
|
||||
HAP_ERR = 2
|
||||
HAP_SOCK_ERR = 3
|
||||
HAP_BUFSIZE = 8192
|
||||
HAP_UNIX_PATH = 'unix://'
|
||||
HAP_TCP_PATH = 'tcp://'
|
||||
@@ -1,291 +0,0 @@
|
||||
# pylint: disable=star-args, locally-disabled, too-few-public-methods, no-self-use, invalid-name
|
||||
"""test_cmds.py - Unittests related to command implementations."""
|
||||
import sys, os, unittest
|
||||
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..'))
|
||||
from haproxy import cmds
|
||||
|
||||
|
||||
class TestCommands(unittest.TestCase):
|
||||
"""Tests all of the commands."""
|
||||
|
||||
def setUp(self):
|
||||
self.maxDiff = None
|
||||
self.pem_cert_content = """
|
||||
-----BEGIN CERTIFICATE-----
|
||||
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
|
||||
+VCAIWMkC36Dvg==
|
||||
-----END CERTIFICATE-----
|
||||
|
||||
-----BEGIN RSA PRIVATE KEY-----
|
||||
MIIJKgIBAAKCAgEAvsNKU59F2gpl1fzbk7hD64Q3zk1hH9Im1IsN9ppF5tqdsPPG
|
||||
n3Q8QgcKpnJ/SrtnPl4Xqp3bJyV20lJWTxuVU7ImM8g4tStQB4XojTtv8A4E7/wN
|
||||
T0beUBFI5CQ+YLzRqEYBbcXV5pxx/TyqdxYIbEopeZ4v2ZsCwDiTr7qkirxhEGow
|
||||
ITZuRFD+xwBrykvuazmFx441Z+qMcLXbMinJq++d81VnrPtCXAcSHdFqOTUhocvI
|
||||
Tv736hDfNeoW0zqwG9eW9WHCyLOCQfdmh03zXJy8eg/UxaocYdfSSVERD2pGse5K
|
||||
6oeyMkdfyC1uojZn0NU5RQS6dsiQiZPCpiN7m2nuDhFAlSAB2xq7I3TY1N15R5yL
|
||||
4kO8DcUcmkC0Du0Nch41zI+NGGjXuLhVQhxhqy7kkMXSJI8NP9KvG3oSwOe0MUNy
|
||||
jUdcywWpSExtjb6BRYLc/Sf3xVZI74EK0B0Vtzj43dAQzxGdAojzpYdH++dUOiKA
|
||||
6EAu6ldrpzOMsYyNpIsMjSfInkzzWvxm4Fla7ZDodOKQ2ahaXfDrH71a9jpNB306
|
||||
kKNBS7XdQxUByjwIYQ6PRmqGEtNxxfx3E8r/TVtcy0VGK8l97q8wv47Q3nglsjlx
|
||||
peZoR1wS5rMBUla3Vk8YVMyOLD87RLodvRVgkpeqMmz233k7kykv1bhi8bECAwEA
|
||||
AQKCAgEAswbSPXJPetahRdcdNyAKVgBq4ykJinSOTpAF1bZo/cOTlFrjwAe0+X5k
|
||||
R1tTDQ6dURG7AjtNTgrB3Za6O1m2paqeYaB5X8U7QSQx4EG0xsRRa+vPjeQDhX8D
|
||||
OmCtTdpGpLa2Zo/xM5EFBVUm4cYCt6ZOED4dyAnK5hzytUvjWfR6343Yh4LurxyY
|
||||
TqidgGgMZALDA0n54wFjNe/lu8kt5Ddns9MmDlhrqbRVEzjSiMfNPWvjHAf7IGcf
|
||||
JBkBvNDqL+b/XGCYDgUxrLkDNt44E2VhGOi8lZkVM9n5FyeGbEIgAKKTGlGpMbh8
|
||||
MoA4wPFwMrO5IIXUfN+zjfnnBkZsnAomGQYDh/hrsQPwU7MoyfO0Wzw+RzLWK8JH
|
||||
EnjR7O/Lgh+A2AdLhCLiRC5td2uuJ2yLRIRUlcQPsCsYnCCL6Ip9IwK1idmQySGw
|
||||
bG83decXNSJUv5h3qF6f3fl+JPrHnAbviBzEJ67xAf1MdHbFxwYvRFVfEHj9RZ3W
|
||||
z+cw7ofD8XVHTfXn0XipvYqI/bVsitMXI35pOt+/ZV8rjJlXopw+IV6U9/60cBkk
|
||||
BXC7ONDyH2pNwxPbRgcLm2sEK0L9qhxRzCj0iD1WyOAiFJX4ytVbJhR7pt0goiun
|
||||
i2XDh2l8hoK1lKZNS/yJ+VhnbX595mdqScmIXD8utlgK8f0bLfECggEBAORXimSK
|
||||
gzegnsBjieTtzC6MmRRxxN46vnMZ2LCeLMxhs3vM7LBcBfsQYqbt/FVFtYBRpr+d
|
||||
TGTmfPXqKuSqbtAbghxAMo/lECXzALa0nQSsz1fFhX8B7slFarsDmmCb1GmXF/kG
|
||||
ku/Uoa7jmY3htBj5rjVHjDKPZFVetU+2wbuwlU17Bj4nlSzqud4NMlu56pm3FZ/1
|
||||
BAhMxm3z6dLnOgqJzpN1QmKZHNkjLmi8fza/HQM5pP3DpQcPiyuLzywGIqHaO1qT
|
||||
OIdpZfLEvNpMV7bJ2bagv5nX3TVRWWsBkh0HCAuH30qqaVPpQvkPem1zsM3x+D5q
|
||||
+PhMIPGpbQiUyCUCggEBANXefd0ZcJymG15WJyO44eFwzgMz9ezfdB8INa+vCOiZ
|
||||
Y7FtYDgEKu4uzBxtMjO4mQO6DCkfi7JwTJFN4ag3dJEJNGmrf7Xe84IAImJQk0Of
|
||||
BojAXCFAuNf1Xl3prkvnvtzNirwQMHCUbv5wYzOqglgj2i/hjIj3/Wbt91riq5j+
|
||||
4qQT4kkw/XgCtbQ27HohKIcC/mXbHchEi7NtXrGoM1xqmu1mGH1uul3LQ6p5VwHc
|
||||
ZFiIAC0awsx9Qe9khZ5EGpZuS0tqJsREcv8ygYMvWcPJEv8aMQM7Nj4biA5rKEgo
|
||||
L+66ibpntldvbz2qntEvJ2rKzGci0RDUQHy4sW8/d50CggEBAKCZaX7ZZPzk/YL2
|
||||
/2+CSQ+cV7ZnZj2fN4Ag96UROxTsyp4SPY60yogQuDIMRGN9SfDcfNlcOvTkn5Me
|
||||
hdiafqHkFxjjlixawYbPaPsYAS/ek156UDBKHbZ2GmE6YYP9VeKGIJhHpWUFOkqV
|
||||
TdTaoB7IzVwv3E1bSQg6Om+8bHoj8n6yPmvMz0DuPpgM1BRrqLNAb/c3DwT/ari+
|
||||
ywBJHSt4TVCtMmnCouWdtvB3U0ogFLnF+2N4DUPwDMQt6yJdllIb+Y706NdkrA2Z
|
||||
jfJDq5WmVnf6i4gaqTzs4GVAj5HW9jOV9ti/DqGz+CTQXB1LN1lCDIVqG34XnTwb
|
||||
G9LjQfkCggEAZwYAt4tTtgJGWNFDlW+wT/sZIm3bX7ncpD4+Ll0w+2s4nPXFTfaj
|
||||
/4zHgkIP1t5rx2HODdlGYDS8jZpow7HDE0LN3sFgienWf5808QtDhWWLrkCLoPEe
|
||||
mdl3FeJFtgby6EaTODjMPM8kEKlvACp5E6BhsIMEQc7EYNrtNvjOFKtj3go+DWfu
|
||||
EeusQB3dGI/0h+UnS0WcOSbb7RkYbphJ9ZDdBNMTpQi7+ga6l9pP0XOrWwJYo2Gq
|
||||
yPrl0j4oJ69C54hF+RQvjIg0pT5dKSacJTYtUnn5dkcFwDFe/yMbinbhcCynwAXJ
|
||||
zqC9g4U3cCk44bbDdENPVr4IOox13NND+QKCAQEAilm2oMZoP3WGkBMTSzJl6OGd
|
||||
F8NnE95noleknNFYuThhCT6T4Z1s28VpxXV7d0DTNOtXj+TzeZq4jrwkgOSZbif0
|
||||
8ky4gRZmm0iFwvAu8ZXk1olHbhMZnCOfh0Qhd4bU2tSoWgWVIAQWEHUhDI7Q1rsX
|
||||
s4sCjYHKuNMEKdfYvxtKeiunoFqdmT65hwM9o3TfvJfm/RChb7i/nVruXQ6IhPEM
|
||||
9WYZS7hlKyqVBESJuonR15biy7Xov5ELl6A821cskZO3vTwtlBSeCDiqaeVLpKR3
|
||||
aYwf5YZo7v+N8KBSLEdLNjoKK4PfXUdczD7uOUllbd4/MRgCn4EmFvmpljGiEQ==
|
||||
-----END RSA PRIVATE KEY-----
|
||||
|
||||
-----BEGIN CERTIFICATE-----
|
||||
MIIEqzCCApOgAwIBAgIRAIvhKg5ZRO08VGQx8JdhT+UwDQYJKoZIhvcNAQELBQAw
|
||||
GjEYMBYGA1UEAwwPRmFrZSBMRSBSb290IFgxMB4XDTE2MDUyMzIyMDc1OVoXDTM2
|
||||
MDUyMzIyMDc1OVowIjEgMB4GA1UEAwwXRmFrZSBMRSBJbnRlcm1lZGlhdGUgWDEw
|
||||
ggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDtWKySDn7rWZc5ggjz3ZB0
|
||||
8jO4xti3uzINfD5sQ7Lj7hzetUT+wQob+iXSZkhnvx+IvdbXF5/yt8aWPpUKnPym
|
||||
oLxsYiI5gQBLxNDzIec0OIaflWqAr29m7J8+NNtApEN8nZFnf3bhehZW7AxmS1m0
|
||||
ZnSsdHw0Fw+bgixPg2MQ9k9oefFeqa+7Kqdlz5bbrUYV2volxhDFtnI4Mh8BiWCN
|
||||
xDH1Hizq+GKCcHsinDZWurCqder/afJBnQs+SBSL6MVApHt+d35zjBD92fO2Je56
|
||||
dhMfzCgOKXeJ340WhW3TjD1zqLZXeaCyUNRnfOmWZV8nEhtHOFbUCU7r/KkjMZO9
|
||||
AgMBAAGjgeMwgeAwDgYDVR0PAQH/BAQDAgGGMBIGA1UdEwEB/wQIMAYBAf8CAQAw
|
||||
HQYDVR0OBBYEFMDMA0a5WCDMXHJw8+EuyyCm9Wg6MHoGCCsGAQUFBwEBBG4wbDA0
|
||||
BggrBgEFBQcwAYYoaHR0cDovL29jc3Auc3RnLXJvb3QteDEubGV0c2VuY3J5cHQu
|
||||
b3JnLzA0BggrBgEFBQcwAoYoaHR0cDovL2NlcnQuc3RnLXJvb3QteDEubGV0c2Vu
|
||||
Y3J5cHQub3JnLzAfBgNVHSMEGDAWgBTBJnSkikSg5vogKNhcI5pFiBh54DANBgkq
|
||||
hkiG9w0BAQsFAAOCAgEABYSu4Il+fI0MYU42OTmEj+1HqQ5DvyAeyCA6sGuZdwjF
|
||||
UGeVOv3NnLyfofuUOjEbY5irFCDtnv+0ckukUZN9lz4Q2YjWGUpW4TTu3ieTsaC9
|
||||
AFvCSgNHJyWSVtWvB5XDxsqawl1KzHzzwr132bF2rtGtazSqVqK9E07sGHMCf+zp
|
||||
DQVDVVGtqZPHwX3KqUtefE621b8RI6VCl4oD30Olf8pjuzG4JKBFRFclzLRjo/h7
|
||||
IkkfjZ8wDa7faOjVXx6n+eUQ29cIMCzr8/rNWHS9pYGGQKJiY2xmVC9h12H99Xyf
|
||||
zWE9vb5zKP3MVG6neX1hSdo7PEAb9fqRhHkqVsqUvJlIRmvXvVKTwNCP3eCjRCCI
|
||||
PTAvjV+4ni786iXwwFYNz8l3PmPLCyQXWGohnJ8iBm+5nk7O2ynaPVW0U2W+pt2w
|
||||
SVuvdDM5zGv2f9ltNWUiYZHJ1mmO97jSY/6YfdOUH66iRtQtDkHBRdkNBsMbD+Em
|
||||
2TgBldtHNSJBfB3pm9FblgOcJ0FSWcUDWJ7vO0+NTXlgrRofRT6pVywzxVo6dND0
|
||||
WzYlTWeUVsO40xJqhgUQRER9YLOLxJ0O6C8i0xFxAMKOtSdodMB3RIwt7RFQ0uyt
|
||||
n5Z5MqkYhlMI3J1tPRTp1nEt9fyGspBOO05gi148Qasp+3N+svqKomoQglNoAxU=
|
||||
-----END CERTIFICATE-----
|
||||
"""
|
||||
|
||||
self.Resp = {
|
||||
"disable": "disable server redis-ro/redis-ro0",
|
||||
"set-server-agent": "set server redis-ro/redis-ro0 agent up",
|
||||
"set-server-health": "set server redis-ro/redis-ro0 health stopping",
|
||||
"set-server-state": "set server redis-ro/redis-ro0 state drain",
|
||||
"set-server-weight": "set server redis-ro/redis-ro0 weight 10",
|
||||
"frontends": "show stat",
|
||||
"info": "show info",
|
||||
"sessions": "show sess",
|
||||
"servers": "show stat",
|
||||
"show-ssl-crt-lists": "show ssl crt-list",
|
||||
"show-ssl-crt-list": "show ssl crt-list -n /tmp/haproxy/ssl/601a7392cc9984.99301413.certlist",
|
||||
"show-ssl-certs": "show ssl cert",
|
||||
"show-ssl-cert": "show ssl cert /tmp/haproxy/ssl/601a70e4844b0.pem",
|
||||
"add-to-crt-list": "add ssl crt-list /tmp/haproxy/ssl/601a7392cc9984.99301413.certlist /tmp/haproxy/ssl/601a70e4844b0.pem",
|
||||
"del-from-crt-list": "del ssl crt-list /tmp/haproxy/ssl/601a7392cc9984.99301413.certlist /tmp/haproxy/ssl/601a70e4844b0.pem",
|
||||
"new-ssl-cert": "new ssl cert /tmp/haproxy/ssl/601a70e4844b0.pem",
|
||||
"update-ssl-cert": "set ssl cert /tmp/haproxy/ssl/601a70e4844b0.pem <<\n%s" % self.pem_cert_content,
|
||||
"del-ssl-cert": "del ssl cert /tmp/haproxy/ssl/601a70e4844b0.pem",
|
||||
"commit-ssl-cert": "commit ssl cert /tmp/haproxy/ssl/601a70e4844b0.pem",
|
||||
"abort-ssl-cert": "abort ssl cert /tmp/haproxy/ssl/601a70e4844b0.pem",
|
||||
}
|
||||
|
||||
self.Resp = dict([(k, v + "\r\n") for k, v in self.Resp.items()])
|
||||
|
||||
def test_setServerAgent(self):
|
||||
"""Test 'set server agent' command"""
|
||||
args = {"backend": "redis-ro", "server": "redis-ro0", "value": "up"}
|
||||
cmdOutput = cmds.setServerAgent(**args).getCmd()
|
||||
self.assertEqual(cmdOutput, self.Resp["set-server-agent"])
|
||||
|
||||
def test_setServerHealth(self):
|
||||
"""Test 'set server health' command"""
|
||||
args = {"backend": "redis-ro", "server": "redis-ro0", "value": "stopping"}
|
||||
cmdOutput = cmds.setServerHealth(**args).getCmd()
|
||||
self.assertEqual(cmdOutput, self.Resp["set-server-health"])
|
||||
|
||||
def test_setServerState(self):
|
||||
"""Test 'set server state' command"""
|
||||
args = {"backend": "redis-ro", "server": "redis-ro0", "value": "drain"}
|
||||
cmdOutput = cmds.setServerState(**args).getCmd()
|
||||
self.assertEqual(cmdOutput, self.Resp["set-server-state"])
|
||||
|
||||
def test_setServerWeight(self):
|
||||
"""Test 'set server weight' command"""
|
||||
args = {"backend": "redis-ro", "server": "redis-ro0", "value": "10"}
|
||||
cmdOutput = cmds.setServerWeight(**args).getCmd()
|
||||
self.assertEqual(cmdOutput, self.Resp["set-server-weight"])
|
||||
|
||||
def test_showFrontends(self):
|
||||
"""Test 'frontends/backends' commands"""
|
||||
args = {}
|
||||
cmdOutput = cmds.showFrontends(**args).getCmd()
|
||||
self.assertEqual(cmdOutput, self.Resp["frontends"])
|
||||
|
||||
def test_showInfo(self):
|
||||
"""Test 'show info' command"""
|
||||
cmdOutput = cmds.showInfo().getCmd()
|
||||
self.assertEqual(cmdOutput, self.Resp["info"])
|
||||
|
||||
def test_showSessions(self):
|
||||
"""Test 'show sess' command"""
|
||||
cmdOutput = cmds.showSessions().getCmd()
|
||||
self.assertEqual(cmdOutput, self.Resp["sessions"])
|
||||
|
||||
def test_showServers(self):
|
||||
"""Test 'show stat' command"""
|
||||
args = {"backend": "redis-ro"}
|
||||
cmdOutput = cmds.showServers(**args).getCmd()
|
||||
self.assertEqual(cmdOutput, self.Resp["servers"])
|
||||
|
||||
def test_showSslCrtLists(self):
|
||||
"""Test 'show ssl crt-list' command"""
|
||||
cmdOutput = cmds.showSslCrtLists().getCmd()
|
||||
self.assertEqual(cmdOutput, self.Resp["show-ssl-crt-lists"])
|
||||
|
||||
def test_showSslCrtList(self):
|
||||
"""Test 'show ssl crt-list <crt-list>' command"""
|
||||
args = {
|
||||
"crt_list": "/tmp/haproxy/ssl/601a7392cc9984.99301413.certlist",
|
||||
}
|
||||
cmdOutput = cmds.showSslCrtList(**args).getCmd()
|
||||
self.assertEqual(cmdOutput, self.Resp["show-ssl-crt-list"])
|
||||
|
||||
def test_showSslCerts(self):
|
||||
"""Test 'show ssl cert' command"""
|
||||
cmdOutput = cmds.showSslCerts().getCmd()
|
||||
self.assertEqual(cmdOutput, self.Resp["show-ssl-certs"])
|
||||
|
||||
def test_showSslCert(self):
|
||||
"""Test 'show ssl cert <certfile>' command"""
|
||||
args = {
|
||||
"certfile": "/tmp/haproxy/ssl/601a70e4844b0.pem"
|
||||
}
|
||||
cmdOutput = cmds.showSslCert(**args).getCmd()
|
||||
self.assertEqual(cmdOutput, self.Resp["show-ssl-cert"])
|
||||
|
||||
def test_addToSslCrtList(self):
|
||||
"""Test 'add ssl crt-list <crt-list> <certfile>' command"""
|
||||
args = {
|
||||
"crt_list": "/tmp/haproxy/ssl/601a7392cc9984.99301413.certlist",
|
||||
"certfile": "/tmp/haproxy/ssl/601a70e4844b0.pem"
|
||||
}
|
||||
cmdOutput = cmds.addToSslCrtList(**args).getCmd()
|
||||
self.assertEqual(cmdOutput, self.Resp["add-to-crt-list"])
|
||||
|
||||
def test_delFromSslCrtList(self):
|
||||
"""Test 'del ssl crt-list <crt-list> <certfile>' command"""
|
||||
args = {
|
||||
"crt_list": "/tmp/haproxy/ssl/601a7392cc9984.99301413.certlist",
|
||||
"certfile": "/tmp/haproxy/ssl/601a70e4844b0.pem"
|
||||
}
|
||||
cmdOutput = cmds.delFromSslCrtList(**args).getCmd()
|
||||
self.assertEqual(cmdOutput, self.Resp["del-from-crt-list"])
|
||||
|
||||
def test_newSslCrt(self):
|
||||
"""Test 'new ssl cert <certfile>' command"""
|
||||
args = {
|
||||
"certfile": "/tmp/haproxy/ssl/601a70e4844b0.pem",
|
||||
}
|
||||
cmdOutput = cmds.newSslCrt(**args).getCmd()
|
||||
self.assertEqual(cmdOutput, self.Resp["new-ssl-cert"])
|
||||
|
||||
def test_updateSslCrt(self):
|
||||
"""Test 'set ssl cert <certfile> <payload>' command"""
|
||||
args = {
|
||||
"certfile": "/tmp/haproxy/ssl/601a70e4844b0.pem",
|
||||
"payload": "%s" % self.pem_cert_content
|
||||
}
|
||||
cmdOutput = cmds.updateSslCrt(**args).getCmd()
|
||||
self.assertEqual(cmdOutput, self.Resp["update-ssl-cert"])
|
||||
|
||||
def test_delSslCrt(self):
|
||||
"""Test 'del ssl cert <certfile>' command"""
|
||||
args = {
|
||||
"certfile": "/tmp/haproxy/ssl/601a70e4844b0.pem",
|
||||
}
|
||||
cmdOutput = cmds.delSslCrt(**args).getCmd()
|
||||
self.assertEqual(cmdOutput, self.Resp["del-ssl-cert"])
|
||||
|
||||
def test_commitSslCrt(self):
|
||||
"""Test 'commit ssl cert <certfile>' command"""
|
||||
args = {
|
||||
"certfile": "/tmp/haproxy/ssl/601a70e4844b0.pem",
|
||||
}
|
||||
cmdOutput = cmds.commitSslCrt(**args).getCmd()
|
||||
self.assertEqual(cmdOutput, self.Resp["commit-ssl-cert"])
|
||||
|
||||
def test_abortSslCrt(self):
|
||||
"""Test 'abort ssl cert <certfile>' command"""
|
||||
args = {
|
||||
"certfile": "/tmp/haproxy/ssl/601a70e4844b0.pem",
|
||||
}
|
||||
cmdOutput = cmds.abortSslCrt(**args).getCmd()
|
||||
self.assertEqual(cmdOutput, self.Resp["abort-ssl-cert"])
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -1,59 +0,0 @@
|
||||
# pylint: disable=locally-disabled, too-few-public-methods, no-self-use, invalid-name, broad-except
|
||||
"""test_conn.py - Unittests related to connections to HAProxy."""
|
||||
import sys, os
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), '..', '..'))
|
||||
from haproxy import conn
|
||||
import unittest
|
||||
from socket import AF_INET, AF_UNIX
|
||||
|
||||
class SimpleConnMock(object):
|
||||
"""Simple socket mock."""
|
||||
def __init__(self, stype, stream):
|
||||
self.stype = stype
|
||||
self.stream = stream
|
||||
|
||||
def connect(self, addr):
|
||||
"""Mocked socket.connect method."""
|
||||
pass
|
||||
|
||||
class TestConnection(unittest.TestCase):
|
||||
"""Tests different aspects of haproxyctl's connections to HAProxy."""
|
||||
|
||||
def testConnSimple(self):
|
||||
"""Tests that connection to non-protocol path works and fallsback to UNIX socket."""
|
||||
sfile = "/some/path/to/socket.sock"
|
||||
c = conn.HaPConn(sfile, socket_module=SimpleConnMock)
|
||||
addr, stype = c.sfile
|
||||
self.assertEqual(sfile, addr)
|
||||
self.assertEqual(stype, AF_UNIX)
|
||||
|
||||
def testConnUnixString(self):
|
||||
"""Tests that unix:// protocol works and connects to a socket."""
|
||||
sfile = "unix:///some/path/to/socket.socket"
|
||||
c = conn.HaPConn(sfile, socket_module=SimpleConnMock)
|
||||
addr, stype = c.sfile
|
||||
self.assertEqual("/some/path/to/socket.socket", addr)
|
||||
self.assertEqual(stype, AF_UNIX)
|
||||
|
||||
def testConnTCPString(self):
|
||||
"""Tests that tcp:// protocol works and connects to an IP."""
|
||||
sfile = "tcp://1.2.3.4:8080"
|
||||
c = conn.HaPConn(sfile, socket_module=SimpleConnMock)
|
||||
addr, stype = c.sfile
|
||||
ip, port = addr
|
||||
self.assertEqual("1.2.3.4", ip)
|
||||
self.assertEqual(8080, port)
|
||||
self.assertEqual(stype, AF_INET)
|
||||
|
||||
def testConnTCPStringNoPort(self):
|
||||
"""Tests that passing a tcp:// address with no port, raises an Exception."""
|
||||
sfile = "tcp://1.2.3.4"
|
||||
# Not using assertRaises because we still support 2.6
|
||||
try:
|
||||
conn.HaPConn(sfile, socket_module=SimpleConnMock)
|
||||
raise Exception('Connection should have thrown an exception')
|
||||
except conn.HapError:
|
||||
pass
|
||||
|
||||
if __name__ == '__main__':
|
||||
unittest.main()
|
||||
@@ -4,7 +4,6 @@ import sys
|
||||
import argparse
|
||||
import traceback
|
||||
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), 'lib'))
|
||||
from haproxy.conn import HaPConn
|
||||
from haproxy import cmds
|
||||
|
||||
|
||||
@@ -9,7 +9,6 @@ import OpenSSL
|
||||
import json
|
||||
from typing import List
|
||||
|
||||
sys.path.append(os.path.join(os.path.dirname(__file__), 'lib'))
|
||||
from haproxy.conn import HaPConn
|
||||
from haproxy import cmds
|
||||
|
||||
|
||||
Reference in New Issue
Block a user