Skip to content
Snippets Groups Projects
Commit 30cc77cd authored by Stéphane Diemer's avatar Stéphane Diemer
Browse files

Use single quotes in postgresql test

parent 432eeba1
No related branches found
No related tags found
No related merge requests found
#!/usr/bin/env python3 #!/usr/bin/env python3
""" '''
Criticality: High Criticality: High
Checks the current state of the PostgreSQL database cluster. Checks the current state of the PostgreSQL database cluster.
""" '''
from pathlib import Path from pathlib import Path
import re import re
...@@ -26,7 +26,7 @@ from utilities.config import load_conf # noqa: E402 ...@@ -26,7 +26,7 @@ from utilities.config import load_conf # noqa: E402
def check_listen(host: str, port: int) -> bool: def check_listen(host: str, port: int) -> bool:
"""Check if server is listening (TCP only). '''Check if server is listening (TCP only).
:param host: The hostname or IP address to bind :param host: The hostname or IP address to bind
:param port: The port number to bind :param port: The port number to bind
...@@ -34,7 +34,7 @@ def check_listen(host: str, port: int) -> bool: ...@@ -34,7 +34,7 @@ def check_listen(host: str, port: int) -> bool:
:type port: int :type port: int
:return: Wether the `host` is listening on TCP/`port` :return: Wether the `host` is listening on TCP/`port`
:rtype: bool :rtype: bool
""" '''
# try to connect to the port used by psql-primary frontend # try to connect to the port used by psql-primary frontend
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
...@@ -44,14 +44,14 @@ def check_listen(host: str, port: int) -> bool: ...@@ -44,14 +44,14 @@ def check_listen(host: str, port: int) -> bool:
return result == 0 return result == 0
def get_haproxy_conf(path: str = "/etc/haproxy/haproxy.cfg") -> dict: def get_haproxy_conf(path: str = '/etc/haproxy/haproxy.cfg') -> dict:
"""Get HAProxy configuration in a dictionary. '''Get HAProxy configuration in a dictionary.
:param path: HAProxy configuration file, defaults to "/etc/haproxy/haproxy.cfg" :param path: HAProxy configuration file, defaults to '/etc/haproxy/haproxy.cfg'
:type path: str :type path: str
:return: HAProxy configuration file content :return: HAProxy configuration file content
:rtype: dict :rtype: dict
""" '''
# init configuration dictionary # init configuration dictionary
conf = {} conf = {}
...@@ -64,8 +64,8 @@ def get_haproxy_conf(path: str = "/etc/haproxy/haproxy.cfg") -> dict: ...@@ -64,8 +64,8 @@ def get_haproxy_conf(path: str = "/etc/haproxy/haproxy.cfg") -> dict:
return conf return conf
# define patterns # define patterns
pattern_block = re.compile(r"^([a-zA-Z0-9_.-]+ *[a-zA-Z0-9_.-]+)") pattern_block = re.compile(r'^([a-zA-Z0-9_.-]+ *[a-zA-Z0-9_.-]+)')
pattern_param = re.compile(r"^\s+([ /:()|a-zA-Z0-9_.-]+)") pattern_param = re.compile(r'^\s+([ /:()|a-zA-Z0-9_.-]+)')
# parse configuration file # parse configuration file
for line in lines: for line in lines:
...@@ -83,41 +83,41 @@ def get_haproxy_conf(path: str = "/etc/haproxy/haproxy.cfg") -> dict: ...@@ -83,41 +83,41 @@ def get_haproxy_conf(path: str = "/etc/haproxy/haproxy.cfg") -> dict:
def get_nodes(conf: dict) -> dict: def get_nodes(conf: dict) -> dict:
"""Get the list of nodes from HAProxy configuration. '''Get the list of nodes from HAProxy configuration.
:param conf: The HAProxy configuration file content :param conf: The HAProxy configuration file content
:type conf: dict :type conf: dict
:return: The list of nodes found in HAProxy configuration :return: The list of nodes found in HAProxy configuration
:rtype: dict :rtype: dict
""" '''
servers = {} servers = {}
for item in conf.keys(): for item in conf.keys():
if "pgsql-primary" in item: if 'pgsql-primary' in item:
# filter `server` lines # filter `server` lines
server_lines = [x for x in conf[item] if x.startswith("server ")] server_lines = [x for x in conf[item] if x.startswith('server ')]
for line in server_lines: for line in server_lines:
# split line # split line
elements = line.split() elements = line.split()
# get needed elements # get needed elements
name = elements[1] name = elements[1]
address = elements[2].split(":") address = elements[2].split(':')
host = address[0] host = address[0]
port = int(address[1]) port = int(address[1])
rephacheck = elements[7] rephacheck = elements[7]
# update dictionary # update dictionary
servers.update( servers.update(
{name: {"host": host, "port": port, "rephacheck": rephacheck}} {name: {'host': host, 'port': port, 'rephacheck': rephacheck}}
) )
return servers return servers
def get_node_state(host: str, port: int) -> str: def get_node_state(host: str, port: int) -> str:
"""Get the curent state of node from its RepHACheck daemon. '''Get the curent state of node from its RepHACheck daemon.
:param node: The node's hostname or IP address :param node: The node's hostname or IP address
:param port: The node's port on which RepHACheck is listening :param port: The node's port on which RepHACheck is listening
...@@ -125,7 +125,7 @@ def get_node_state(host: str, port: int) -> str: ...@@ -125,7 +125,7 @@ def get_node_state(host: str, port: int) -> str:
:type port: int :type port: int
:return: The current state of the node accordind to its RepHACheck daemon :return: The current state of the node accordind to its RepHACheck daemon
:rtype: str :rtype: str
""" '''
# connect and get tcp stream data # connect and get tcp stream data
client = socket.socket(socket.AF_INET, socket.SOCK_STREAM) client = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
...@@ -133,31 +133,31 @@ def get_node_state(host: str, port: int) -> str: ...@@ -133,31 +133,31 @@ def get_node_state(host: str, port: int) -> str:
data = client.recv(1024) data = client.recv(1024)
client.close() client.close()
# extract string from data output # extract string from data output
state = data.decode("utf-8").rstrip() state = data.decode('utf-8').rstrip()
return state return state
def check_fenced(nodes: dict) -> tuple: def check_fenced(nodes: dict) -> tuple:
"""Check if the cluster have a fenced node. '''Check if the cluster have a fenced node.
:param nodes: The dictionary containing nodes and their informations :param nodes: The dictionary containing nodes and their informations
:type nodes: dict :type nodes: dict
:return: Wether the nodes list contains a fenced server :return: Wether the nodes list contains a fenced server
:rtype: tuple :rtype: tuple
""" '''
for node in nodes.keys(): for node in nodes.keys():
host = nodes[node]["host"] host = nodes[node]['host']
port = int(nodes[node]["rephacheck"]) port = int(nodes[node]['rephacheck'])
if get_node_state(host, port) == "fenced": if get_node_state(host, port) == 'fenced':
return True, node return True, node
return False, None return False, None
def check_psql(db_conn: dict, query: str) -> tuple: def check_psql(db_conn: dict, query: str) -> tuple:
"""Check if we can write data on this node. '''Check if we can write data on this node.
:param db_conn: Database connection parameters :param db_conn: Database connection parameters
:type db_conn: dict :type db_conn: dict
...@@ -165,22 +165,22 @@ def check_psql(db_conn: dict, query: str) -> tuple: ...@@ -165,22 +165,22 @@ def check_psql(db_conn: dict, query: str) -> tuple:
:type query: str :type query: str
:return: Wether the query can be executed or not :return: Wether the query can be executed or not
:rtype: tuple :rtype: tuple
""" '''
# build database connection uri # build database connection uri
if "password" in db_conn: if 'password' in db_conn:
uri = "postgresql://{}:{}@{}:{}/{}".format( uri = 'postgresql://{}:{}@{}:{}/{}'.format(
db_conn["user"], db_conn['user'],
urllib.parse.quote_plus(db_conn["password"]), urllib.parse.quote_plus(db_conn['password']),
db_conn["host"], db_conn['host'],
db_conn["port"], db_conn['port'],
db_conn["dbname"], db_conn['dbname'],
) )
else: else:
uri = "postgresql:///{}".format(db_conn["dbname"]) uri = 'postgresql:///{}'.format(db_conn['dbname'])
# format command # format command
command = ["su -l postgres -c \"psql {} -c '{}'\"".format(uri, query)] command = ['su -l postgres -c "psql {} -c \'{}\'"'.format(uri, query)]
# execute # execute
try: try:
...@@ -192,7 +192,7 @@ def check_psql(db_conn: dict, query: str) -> tuple: ...@@ -192,7 +192,7 @@ def check_psql(db_conn: dict, query: str) -> tuple:
def check_replication(primary: dict, standby: dict) -> tuple: def check_replication(primary: dict, standby: dict) -> tuple:
"""Check if replication is working between the primary and standby servers. '''Check if replication is working between the primary and standby servers.
:param primary: Database connection parameters for primary server :param primary: Database connection parameters for primary server
:type primary: dict :type primary: dict
...@@ -200,7 +200,7 @@ def check_replication(primary: dict, standby: dict) -> tuple: ...@@ -200,7 +200,7 @@ def check_replication(primary: dict, standby: dict) -> tuple:
:type standby: dict :type standby: dict
:return: Wether replication between primary/stanbdy is working or not :return: Wether replication between primary/stanbdy is working or not
:rtype: tuple :rtype: tuple
""" '''
# connections # connections
try: try:
...@@ -211,8 +211,8 @@ def check_replication(primary: dict, standby: dict) -> tuple: ...@@ -211,8 +211,8 @@ def check_replication(primary: dict, standby: dict) -> tuple:
# random id # random id
rand = uuid.uuid4().hex rand = uuid.uuid4().hex
write_query = "CREATE TABLE es_test_{} (id serial PRIMARY KEY);".format(rand) write_query = 'CREATE TABLE es_test_{} (id serial PRIMARY KEY);'.format(rand)
read_query = "SELECT * FROM es_test_{};".format(rand) read_query = 'SELECT * FROM es_test_{};'.format(rand)
# write # write
try: try:
...@@ -230,7 +230,7 @@ def check_replication(primary: dict, standby: dict) -> tuple: ...@@ -230,7 +230,7 @@ def check_replication(primary: dict, standby: dict) -> tuple:
try: try:
standby_psql = primary_client.cursor() standby_psql = primary_client.cursor()
standby_psql.execute(read_query) standby_psql.execute(read_query)
msg = "took ~{}s".format(str(timer)) msg = 'took ~{}s'.format(str(timer))
break break
except psycopg2.Error as repl_read_error: except psycopg2.Error as repl_read_error:
msg = str(repl_read_error).rstrip() msg = str(repl_read_error).rstrip()
...@@ -239,7 +239,7 @@ def check_replication(primary: dict, standby: dict) -> tuple: ...@@ -239,7 +239,7 @@ def check_replication(primary: dict, standby: dict) -> tuple:
# delete # delete
try: try:
primary_psql.execute("DROP TABLE es_test_{};".format(rand)) primary_psql.execute('DROP TABLE es_test_{};'.format(rand))
except psycopg2.Error: except psycopg2.Error:
pass pass
...@@ -253,7 +253,7 @@ def check_replication(primary: dict, standby: dict) -> tuple: ...@@ -253,7 +253,7 @@ def check_replication(primary: dict, standby: dict) -> tuple:
def check_ha(db_conn: dict, errors: int = 0) -> int: def check_ha(db_conn: dict, errors: int = 0) -> int:
"""Run all tests for a highly-available setup. '''Run all tests for a highly-available setup.
:param db_conn: Database connection parameters :param db_conn: Database connection parameters
:type db_conn: dict :type db_conn: dict
...@@ -261,7 +261,7 @@ def check_ha(db_conn: dict, errors: int = 0) -> int: ...@@ -261,7 +261,7 @@ def check_ha(db_conn: dict, errors: int = 0) -> int:
:param errors: int, optional :param errors: int, optional
:return: Number of errors :return: Number of errors
:rtype: int :rtype: int
""" '''
# get haproxy conf # get haproxy conf
ha_conf = get_haproxy_conf() ha_conf = get_haproxy_conf()
...@@ -270,56 +270,56 @@ def check_ha(db_conn: dict, errors: int = 0) -> int: ...@@ -270,56 +270,56 @@ def check_ha(db_conn: dict, errors: int = 0) -> int:
nodes = get_nodes(ha_conf) nodes = get_nodes(ha_conf)
# check haproxy # check haproxy
lg.log("Checking local HAProxy frontends:") lg.log('Checking local HAProxy frontends:')
if not check_listen(db_conn["host"], 54321): if not check_listen(db_conn['host'], 54321):
lg.error("HAProxy pgsql-primary frontend is not listening") lg.error('HAProxy pgsql-primary frontend is not listening')
errors += 1 errors += 1
else: else:
lg.success("HAProxy pgsql-primary frontend is listening") lg.success('HAProxy pgsql-primary frontend is listening')
if not check_listen(db_conn["host"], 54322): if not check_listen(db_conn['host'], 54322):
lg.error("HAProxy pgsql-standby frontend is not listening") lg.error('HAProxy pgsql-standby frontend is not listening')
errors += 1 errors += 1
else: else:
lg.success("HAProxy pgsql-standby frontend is listening") lg.success('HAProxy pgsql-standby frontend is listening')
# check remotes # check remotes
lg.log("Checking remote PostgreSQL nodes:") lg.log('Checking remote PostgreSQL nodes:')
for node in nodes: for node in nodes:
node_host = nodes[node]["host"] node_host = nodes[node]['host']
node_port = nodes[node]["port"] node_port = nodes[node]['port']
if not check_listen(node_host, node_port): if not check_listen(node_host, node_port):
lg.error("cannot bind {}:{}".format(node_host, node_port)) lg.error('cannot bind {}:{}'.format(node_host, node_port))
errors += 1 errors += 1
else: else:
lg.success("can bind {}:{}".format(node_host, node_port)) lg.success('can bind {}:{}'.format(node_host, node_port))
# check fenced # check fenced
lg.log("Checking cluster state:") lg.log('Checking cluster state:')
fenced, node = check_fenced(nodes) fenced, node = check_fenced(nodes)
if fenced: if fenced:
lg.error("Node `{}` is fenced".format(node)) lg.error('Node `{}` is fenced'.format(node))
errors += 1 errors += 1
else: else:
lg.success("No fenced node found") lg.success('No fenced node found')
# check replication # check replication
lg.log("Checking replication state:") lg.log('Checking replication state:')
primary = db_conn.copy() primary = db_conn.copy()
primary["port"] = 54321 primary['port'] = 54321
standby = db_conn.copy() standby = db_conn.copy()
standby["port"] = 54322 standby['port'] = 54322
status, info = check_replication(primary, standby) status, info = check_replication(primary, standby)
if not status: if not status:
lg.error("cannot replicate between primary/standby ({})".format(info)) lg.error('cannot replicate between primary/standby ({})'.format(info))
errors += 1 errors += 1
else: else:
lg.success("can replicate between primary/standby ({})".format(info)) lg.success('can replicate between primary/standby ({})'.format(info))
return errors return errors
def check_local(db_conn: dict, errors: int = 0) -> int: def check_local(db_conn: dict, errors: int = 0) -> int:
"""Run all tests for a highly-available setup. '''Run all tests for a highly-available setup.
:param db_conn: Database connection parameters :param db_conn: Database connection parameters
:type db_conn: dict :type db_conn: dict
...@@ -327,60 +327,60 @@ def check_local(db_conn: dict, errors: int = 0) -> int: ...@@ -327,60 +327,60 @@ def check_local(db_conn: dict, errors: int = 0) -> int:
:param errors: int, optional :param errors: int, optional
:return: Number of errors :return: Number of errors
:rtype: int :rtype: int
""" '''
host = db_conn["host"] host = db_conn['host']
port = db_conn["port"] port = db_conn['port']
user = db_conn["user"] user = db_conn['user']
# check listen # check listen
lg.log("Checking local PostgreSQL node:") lg.log('Checking local PostgreSQL node:')
if not check_listen(host, port): if not check_listen(host, port):
lg.error("cannot connect to {}:{}".format(host, port)) lg.error('cannot connect to {}:{}'.format(host, port))
errors += 1 errors += 1
else: else:
lg.success("can connect to {}:{}".format(host, port)) lg.success('can connect to {}:{}'.format(host, port))
# check read # check read
lg.log("Checking read operation:") lg.log('Checking read operation:')
read_query = "SELECT 1;" read_query = 'SELECT 1;'
status, info = check_psql(db_conn, read_query) status, info = check_psql(db_conn, read_query)
if not status: if not status:
lg.error("cannot read from {}@{}:{} ({})".format(user, host, port, info)) lg.error('cannot read from {}@{}:{} ({})'.format(user, host, port, info))
errors += 1 errors += 1
else: else:
lg.success("can read from {}@{}:{}".format(user, host, port)) lg.success('can read from {}@{}:{}'.format(user, host, port))
# get replication state if available # get replication state if available
if check_listen("127.0.0.1", 8543): if check_listen('127.0.0.1', 8543):
state = get_node_state("127.0.0.1", 8543) state = get_node_state('127.0.0.1', 8543)
else: else:
state = 'primary' state = 'primary'
# check write # check write
lg.log("Checking write operation:") lg.log('Checking write operation:')
if state != "primary": if state != 'primary':
lg.info("this database is in {} state".format(state)) lg.info('this database is in {} state'.format(state))
else: else:
rand = uuid.uuid4().hex rand = uuid.uuid4().hex
write_query = "CREATE TABLE es_test_{} (id serial PRIMARY KEY);".format(rand) write_query = 'CREATE TABLE es_test_{} (id serial PRIMARY KEY);'.format(rand)
status, info = check_psql(db_conn, write_query) status, info = check_psql(db_conn, write_query)
if not status: if not status:
lg.error("cannot write on {}@{}:{} ({})".format(user, host, port, info)) lg.error('cannot write on {}@{}:{} ({})'.format(user, host, port, info))
errors += 1 errors += 1
else: else:
lg.success("can write on {}@{}:{}".format(user, host, port)) lg.success('can write on {}@{}:{}'.format(user, host, port))
# remove test table # remove test table
check_psql(db_conn, "DROP TABLE es_test_{};".format(rand)) check_psql(db_conn, 'DROP TABLE es_test_{};'.format(rand))
return errors return errors
def main(): def main():
"""Run all checks and exits with corresponding exit code.""" '''Run all checks and exits with corresponding exit code.'''
apt = Apt() apt = Apt()
pattern_postgresql_client = re.compile(r"postgresql-client.*") pattern_postgresql_client = re.compile(r'postgresql-client.*')
if not list(filter(pattern_postgresql_client.match, apt.installed_packages)): if not list(filter(pattern_postgresql_client.match, apt.installed_packages)):
exit(2) exit(2)
...@@ -388,21 +388,21 @@ def main(): ...@@ -388,21 +388,21 @@ def main():
conf = load_conf() conf = load_conf()
# get database configuration # get database configuration
db_host = conf.get("DB_HOST") if conf.get("DB_HOST") else "127.0.0.1" db_host = conf.get('DB_HOST') if conf.get('DB_HOST') else '127.0.0.1'
db_port = 54321 if check_listen("127.0.0.1", 54321) else 5432 db_port = 54321 if check_listen('127.0.0.1', 54321) else 5432
db_user = conf.get("DB_USER") if conf.get("DB_USER") else "postgres" db_user = conf.get('DB_USER') if conf.get('DB_USER') else 'postgres'
db_pass = conf.get("DB_PG_ROOT_PWD") db_pass = conf.get('DB_PG_ROOT_PWD')
db_conn = {"dbname": db_user, "host": db_host, "port": db_port, "user": db_user} db_conn = {'dbname': db_user, 'host': db_host, 'port': db_port, 'user': db_user}
if db_pass: if db_pass:
db_conn.update({"password": db_pass}) db_conn.update({'password': db_pass})
# determine if HA setup and run according tests # determine if HA setup and run according tests
lg.log("Checking availibility mode:") lg.log('Checking availibility mode:')
if check_listen("127.0.0.1", 54321) and check_listen("127.0.0.1", 54322): if check_listen('127.0.0.1', 54321) and check_listen('127.0.0.1', 54322):
lg.info("this setup is using a master and standby database") lg.info('this setup is using a primary and standby database')
errors = check_ha(db_conn) errors = check_ha(db_conn)
else: else:
lg.info("this setup is using a single database") lg.info('this setup is using a single database')
errors = check_local(db_conn) errors = check_local(db_conn)
if errors: if errors:
...@@ -411,5 +411,5 @@ def main(): ...@@ -411,5 +411,5 @@ def main():
sys.exit(0) sys.exit(0)
if __name__ == "__main__": if __name__ == '__main__':
main() main()
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment