Mysqldump Action

Add the mysqldump and set-pxc-strict-mode actions to allow for dumping
databases in text form.

Change-Id: Ibb9c5e17d01c7bb1335d8a7c9087216af8d00b09
This commit is contained in:
David Ames 2020-02-18 15:35:19 -08:00
parent 1cf3fa1ac1
commit b73f5c4880
6 changed files with 179 additions and 1 deletions

View File

@ -34,3 +34,33 @@ bootstrap-pxc:
https://www.percona.com/blog/2014/09/01/galera-replication-how-to-recover-a-pxc-cluster/
notify-bootstrapped:
descripttion: Notify the cluster of the new bootstrap uuid.
set-pxc-strict-mode:
description: |
Set PXC strict mode.
params:
mode:
type: string
default: "ENFORCING"
description: |
PXC strict mode. Valid options are DISALBED, PERMISSIVE, ENFORCING or MASTER
See https://www.percona.com/doc/percona-xtradb-cluster/LATEST/features/pxc-strict-mode.html
for more detail.
mysqldump:
description: |
MySQL dump of databases. Action will return mysqldump-file location of the
requested backup in the results. If the databases parameter is unset all
databases will be dumped. If the databases parameter is set only the
databases specified will be dumped. Note it may be necessary to use the
set-pxc-strict-mode action first to set either PERMISSIVE or MASTER to
allow locking of tables for mysqldump to complete successfully.
See https://www.percona.com/doc/percona-xtradb-cluster/LATEST/features/pxc-strict-mode.html
for more detail.
params:
basedir:
type: string
default: "/var/backups/mysql"
description: The base directory for backups
databases:
description: |
Comma delimited database names to dump. If left unset, all databases
will be dumped.

View File

@ -6,6 +6,9 @@ import subprocess
import traceback
from time import gmtime, strftime
import MySQLdb
_path = os.path.dirname(os.path.realpath(__file__))
_hooks = os.path.abspath(os.path.join(_path, '../hooks'))
_root = os.path.abspath(os.path.join(_path, '..'))
@ -167,12 +170,67 @@ def notify_bootstrapped(args):
percona_utils.notify_bootstrapped()
def set_pxc_strict_mode(args):
"""Set PXC Strict Mode.
"""
mode = action_get("mode")
try:
percona_utils.set_pxc_strict_mode(mode)
action_set({"outcome": "Success"})
except (MySQLdb.OperationalError, ValueError) as e:
action_set({
"output": ", ".join(e.args),
"traceback": traceback.format_exc()})
action_fail("Setting PXC strict mode {} failed"
.format(mode))
def mysqldump(args):
"""Execute a mysqldump backup.
Execute mysqldump of the database(s). The mysqldump action will take
in the databases action parameter. If the databases parameter is unset all
databases will be dumped, otherwise only the named databases will be
dumped. The action will use the basedir action parameter to dump the
database into the base directory.
A successful mysqldump backup will set the action results key,
mysqldump-file, with the full path to the dump file.
:param args: sys.argv
:type args: sys.argv
:side effect: Calls instance.mysqldump
:returns: This function is called for its side effect
:rtype: None
:action param basedir: Base directory to dump the db(s)
:action param databases: Comma separated string of databases
:action return:
"""
basedir = action_get("basedir")
databases = action_get("databases")
try:
filename = percona_utils.mysqldump(basedir, databases=databases)
action_set({
"mysqldump-file": filename,
"outcome": "Success"}
)
except subprocess.CalledProcessError as e:
action_set({
"output": e.output,
"return-code": e.returncode,
"traceback": traceback.format_exc()})
action_fail("mysqldump failed")
# A dictionary of all the defined actions to callables (which take
# parsed arguments).
ACTIONS = {"pause": pause, "resume": resume, "backup": backup,
"complete-cluster-series-upgrade": complete_cluster_series_upgrade,
"bootstrap-pxc": bootstrap_pxc,
"notify-bootstrapped": notify_bootstrapped}
"notify-bootstrapped": notify_bootstrapped,
"set-pxc-strict-mode": set_pxc_strict_mode,
"mysqldump": mysqldump}
def main(args):

1
actions/mysqldump Symbolic link
View File

@ -0,0 +1 @@
actions.py

1
actions/set-pxc-strict-mode Symbolic link
View File

@ -0,0 +1 @@
actions.py

View File

@ -1,5 +1,6 @@
''' General utilities for percona '''
import collections
import datetime
import subprocess
from subprocess import Popen, PIPE
import socket
@ -15,6 +16,7 @@ import time
import yaml
from charmhelpers.core.decorators import retry_on_exception
from charmhelpers.core.templating import render
from charmhelpers.core.host import (
lsb_release,
mkdir,
@ -1605,3 +1607,85 @@ def maybe_notify_bootstrapped():
cluster_state_uuid = get_wsrep_value('wsrep_cluster_state_uuid')
if lead_cluster_state_uuid == cluster_state_uuid:
notify_bootstrapped(cluster_uuid=cluster_state_uuid)
def set_pxc_strict_mode(mode):
"""Set PXC Strict Mode.
:param mode: Strict mode: DISALBED, PERMISSIVE, ENFORCING or MASTER
:type backup_dir: str
:raises: Raises ValueError if mode is not a valide parameter
:raises: Raises MySQLdb.OperationalError if SQL is not successful
:returns: None
:rtype: None
"""
_VALID_PARAMS = ["DISABLED", "PERMISSIVE", "ENFORCING", "MASTER"]
mode = mode.upper()
if mode not in _VALID_PARAMS:
raise ValueError(
"'{}' is not a valid parameter for pxc_strict_mode. "
"Valid options are {}.".format(mode, ", ".join(_VALID_PARAMS)))
m_helper = get_db_helper()
m_helper.connect(password=m_helper.get_mysql_root_password())
m_helper.execute("SET GLOBAL pxc_strict_mode='{}'".format(mode))
def write_root_my_cnf():
"""Write root my.cnf
:side effect: calls render()
:returns: None
:rtype: None
"""
my_cnf_template = "root-my.cnf"
root_my_cnf = "/root/.my.cnf"
context = {"mysql_passwd": leader_get("mysql.passwd")}
render(my_cnf_template, root_my_cnf, context, perms=0o600)
def mysqldump(backup_dir, databases=None):
"""Execute a MySQL dump
:param backup_dir: Path to the backup directory
:type backup_dir: str
:param databases: Comma delimited database names
:type database: str
:side effect: Calls subprocess.check_call
:raises subprocess.CalledProcessError: If the mysqldump fails
:returns: Path to the mysqldump file
:rtype: str
"""
# In order to enable passwordless use of mysqldump
# write out my.cnf for user root
write_root_my_cnf()
# Enable use of my.cnf by setting HOME env variable
os.environ["HOME"] = "/root"
_user = "root"
_delimiter = ","
if not os.path.exists(backup_dir):
mkdir(
backup_dir, owner="mysql", group="mysql", perms=0o750)
bucmd = ["/usr/bin/mysqldump", "-u", _user,
"--column-statistics=0",
"--default-character-set=utf8",
"--triggers", "--routines", "--events",
"--ignore-table=mysql.event"]
if databases is not None:
_filename = os.path.join(
backup_dir,
"mysqldump-{}-{}".format(
"-".join(databases.split(_delimiter)),
datetime.datetime.now().strftime("%Y%m%d%H%M")))
bucmd.extend(["--result-file", _filename, "--databases"])
bucmd.extend(databases.split(_delimiter))
else:
_filename = os.path.join(
backup_dir,
"mysqldump-all-databases-{}".format(
datetime.datetime.now().strftime("%Y%m%d%H%M")))
bucmd.extend(["--result-file", _filename, "--all-databases"])
subprocess.check_call(bucmd)
gzcmd = ["gzip", _filename]
subprocess.check_call(gzcmd)
return "{}.gz".format(_filename)

4
templates/root-my.cnf Normal file
View File

@ -0,0 +1,4 @@
[client]
# The following password will be sent to all standard MySQL clients
password="{{ mysql_passwd }}"