Files
WPIQ/wo/core/mysql.py
Malin fa5bf17eb8
Some checks failed
CI / test WordOps (ubuntu-22.04) (push) Has been cancelled
CI / test WordOps (ubuntu-24.04) (push) Has been cancelled
feat: convert WordOps from Nginx to OpenLiteSpeed + LSPHP + LSCache
Complete conversion of the WordOps stack from Nginx + PHP-FPM to
OpenLiteSpeed + LSPHP + LSCache. This is a full rewrite across all 7
phases of the codebase:

- Foundation: OLS paths, variables, services, removed pynginxconfig dep
- Templates: 11 new OLS mustache templates, removed nginx-specific ones
- Stack: stack_pref, stack, stack_services, stack_upgrade, stack_migrate
- Site: site_functions, site, site_create, site_update
- Plugins: debug, info, log, clean rewritten for OLS
- SSL/ACME: acme.sh deploy uses lswsctrl, OLS vhssl blocks
- Other: secure, backup, clone, install script

Additional features:
- Debian 13 (trixie) support
- PHP 8.5 support
- WP Fort Knox mu-plugin integration (wo secure --lockdown/--unlock)
- --nginx CLI flag preserved for backward compatibility

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-08 18:55:16 +01:00

178 lines
6.4 KiB
Python

"""WordOps MySQL core classes."""
import os
from os.path import expanduser
import pymysql
from pymysql import DatabaseError, Error, connections
from wo.core.logging import Log
from wo.core.variables import WOVar
from wo.core.shellexec import WOShellExec
class MySQLConnectionError(Exception):
"""Custom Exception when MySQL server Not Connected"""
pass
class StatementExcecutionError(Exception):
"""Custom Exception when any Query Fails to execute"""
pass
class DatabaseNotExistsError(Exception):
"""Custom Exception when Database not Exist"""
pass
class WOMysql():
"""Method for MySQL connection"""
def connect(self):
# Makes connection with MySQL server
try:
if os.path.exists('/etc/mysql/conf.d/my.cnf'):
connection = pymysql.connect(
read_default_file='/etc/mysql/conf.d/my.cnf')
else:
connection = pymysql.connect(read_default_file='~/.my.cnf')
return connection
except ValueError as e:
Log.debug(self, str(e))
raise MySQLConnectionError
except pymysql.err.InternalError as e:
Log.debug(self, str(e))
raise MySQLConnectionError
def dbConnection(self, db_name):
try:
if os.path.exists('/etc/mysql/conf.d/my.cnf'):
connection = pymysql.connect(
db=db_name, read_default_file='/etc/mysql/conf.d/my.cnf')
else:
connection = pymysql.connect(
db=db_name, read_default_file='~/.my.cnf')
return connection
except pymysql.err.InternalError as e:
Log.debug(self, str(e))
raise MySQLConnectionError
except DatabaseError as e:
if e.args[1] == '#42000Unknown database \'{0}\''.format(db_name):
raise DatabaseNotExistsError
else:
raise MySQLConnectionError
except Exception as e:
Log.debug(self, "[Error]Setting up database: \'" + str(e) + "\'")
raise MySQLConnectionError
def execute(self, statement, errormsg='', log=True):
# Get login details from /etc/mysql/conf.d/my.cnf
# & Execute MySQL query
connection = WOMysql.connect(self)
log and Log.debug(self, "Executing MySQL Statement : {0}"
.format(statement))
try:
cursor = connection.cursor()
sql = statement
cursor.execute(sql)
# connection is not autocommit by default.
# So you must commit to save your changes.
connection.commit()
except AttributeError as e:
Log.debug(self, str(e))
raise StatementExcecutionError
except Error as e:
Log.debug(self, str(e))
raise StatementExcecutionError
finally:
connection.close()
def backupAll(self, fulldump=False):
import subprocess
try:
Log.info(self, "Backing up database at location: "
"/var/lib/wo-backup/mysql")
# Setup backup directory
if not os.path.exists('/var/lib/wo-backup/mysql'):
Log.debug(self, 'Creating directory'
'/var/lib/wo-backup/mysql')
os.makedirs('/var/lib/wo-backup/mysql')
if not fulldump:
db = subprocess.check_output(
["/usr/bin/mysql "
"-Bse \'show databases\'"],
universal_newlines=True,
shell=True).split('\n')
for dbs in db:
if dbs == "":
continue
Log.info(self, "Backing up {0} database".format(dbs))
p1 = subprocess.Popen(
"/usr/bin/mysqldump {0} --max_allowed_packet=1024M "
"--single-transaction --hex-blob".format(dbs),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE, shell=True)
p2 = subprocess.Popen(
"/usr/bin/zstd -c > "
"/var/lib/wo-backup/mysql/{0}{1}.sql.zst"
.format(dbs, WOVar.wo_date),
stdin=p1.stdout, shell=True)
# Allow p1 to receive a SIGPIPE if p2 exits
p1.stdout.close()
output = p1.stderr.read()
p1.wait()
if p1.returncode == 0:
Log.debug(self, "done")
else:
Log.error(self, output.decode("utf-8"))
else:
Log.info(self, "Backing up all databases")
p1 = subprocess.Popen(
"/usr/bin/mysqldump --all-databases "
"--max_allowed_packet=1024M --hex-blob "
"--single-transaction --events",
stdout=subprocess.PIPE,
stderr=subprocess.PIPE, shell=True)
p2 = subprocess.Popen(
"/usr/bin/zstd -c > "
"/var/lib/wo-backup/mysql/fulldump-{0}.sql.zst"
.format(WOVar.wo_date),
stdin=p1.stdout, shell=True)
p1.stdout.close()
output = p1.stderr.read()
p1.wait()
if p1.returncode == 0:
Log.debug(self, "done")
else:
Log.error(self, output.decode("utf-8"))
except Exception as e:
Log.error(self, "Error: process exited with status %s"
% e)
def check_db_exists(self, db_name):
try:
if WOMysql.dbConnection(self, db_name):
return True
except DatabaseNotExistsError as e:
Log.debug(self, str(e))
return False
except MySQLConnectionError as e:
Log.debug(self, str(e))
return False
def mariadb_ping(self):
if os.path.exists('/usr/bin/mariadb-admin'):
mariadb_admin = "/usr/bin/mariadb-admin"
elif os.path.exists('/usr/bin/mysqladmin'):
mariadb_admin = "/usr/bin/mysqladmin"
else:
return False
if WOShellExec.cmd_exec(self, f"{mariadb_admin} ping"):
return True
else:
Log.info(self, "Unable to connect to MariaDB server")
return False