Skip to content

Commit 0d169f0

Browse files
committed
added --prefix-format option and simple error reporting
1 parent 5141286 commit 0d169f0

File tree

2 files changed

+90
-65
lines changed

2 files changed

+90
-65
lines changed

README.md

Lines changed: 40 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,83 +1,84 @@
1-
phpmyadmin_sql_backup.py
2-
==============================
1+
# phpmyadmin_sql_backup.py
32

4-
What is it?
5-
-----------
63

7-
A Python 3 script to automate the download of SQL backups via a [phpMyAdmin](https://www.phpmyadmin.net/) web interface.
4+
## What is it?
85

9-
This is useful when your web hosting provider does not grant you access the the console but you want to automate the backup of your database (without having to manually use the browser).
6+
A Python 3 script to __automate the download of SQL backups via a [phpMyAdmin](https://www.phpmyadmin.net/) web interface__.
107

11-
It has been tested with Python 3.5 and phpMyAdmin 4.3.6 + 4.5.4.1 + 4.7.0-dev
8+
This is useful when your web hosting provider does not grant you access the the console (for `mysqldump`) but you want to automate the backup of your database (without having to manually use the browser).
9+
10+
It has been tested with Python 3.4+ and phpMyAdmin 4.3.6, 4.5.4.1, 4.7.0-dev
1211

1312
_Note_: The web interface of phpMyAdmin may change in the future and break this script. Please file a bug report (including your version of phpMyAdmin) if you encounter this issue.
1413

15-
### Usage
14+
## Usage
1615

1716
usage: phpmyadmin_sql_backup.py [-h] [-o OUTPUT_DIRECTORY] [-p]
1817
[-e EXCLUDE_DBS]
1918
[--compression {none,zip,gzip}]
2019
[--basename BASENAME] [--timeout TIMEOUT]
21-
[--overwrite-existing] [--dry-run]
22-
url user password
20+
[--overwrite-existing]
21+
[--prefix-format PREFIX_FORMAT] [--dry-run]
22+
URL USERNAME PASSWORD
2323

2424
Automates the download of SQL dump backups via a phpMyAdmin web interface.
2525

2626
positional arguments:
27-
url phpMyAdmin login page url
28-
user phpMyAdmin login user
29-
password phpMyAdmin login password
27+
URL phpMyAdmin login page url
28+
USERNAME phpMyAdmin login username
29+
PASSWORD phpMyAdmin login password
3030

3131
optional arguments:
3232
-h, --help show this help message and exit
3333
-o OUTPUT_DIRECTORY, --output-directory OUTPUT_DIRECTORY
34-
output directory for the SQL dump file
35-
-p, --prepend-date prepend the current UTC date + time to the filename:
36-
"YYYY-MM-DD--HH-MM-SS-UTC_"
34+
output directory for the SQL dump file (default: the
35+
current working directory)
36+
-p, --prepend-date prepend current UTC date & time to the filename; see
37+
the --prefix-format option for custom formatting
3738
-e EXCLUDE_DBS, --exclude-dbs EXCLUDE_DBS
38-
comma separated list for database names to exclude
39-
from the dump
39+
comma-separated list of database names to exclude from
40+
the dump
4041
--compression {none,zip,gzip}
41-
compression method of the output file (default: none)
42+
compression method for the output file - must be
43+
supported by the server (default: none)
4244
--basename BASENAME the desired basename (without extension) of the SQL
43-
dump file (default: the name given by phpMyAdmin
45+
dump file (default: the name given by phpMyAdmin)
4446
--timeout TIMEOUT timeout in seconds for the requests (default: 60)
45-
--overwrite-existing overwrite existing backup files (instead of appending
46-
a number to the name)
47-
--dry-run dry run, do not actually download the dump
47+
--overwrite-existing overwrite existing SQL dump files (instead of
48+
appending a number to the name)
49+
--prefix-format PREFIX_FORMAT
50+
the prefix format for --prepend-date (default:
51+
"%Y-%m-%d--%H-%M-%S-UTC_"); in Python's strftime
52+
format. Must be used with --prepend-date to be in
53+
effect
54+
--dry-run dry run, do not actually download any file
4855

49-
Written by Christoph Haunschmidt. Version: 2016-03-10.0
56+
Written by Christoph Haunschmidt, version: 2016-03-12.0
5057

51-
#### Examples
58+
### Examples
5259

53-
`phpmyadmin_sql_backup.py "http://www.example.com/phpmyadmin/" your_user your_password`
60+
phpmyadmin_sql_backup.py "http://www.example.com/phpmyadmin/" your_user your_password
5461

5562
Downloads a plain text `.sql` backup of all tables to the current working directory.
5663

5764
---
5865

59-
`phpmyadmin_sql_backup.py "http://www.example.com/phpmyadmin/" your_user your_password --exclude mydb2,mydb4 -p --basename example_dump -o /tmp --compression zip`
66+
phpmyadmin_sql_backup.py "http://www.example.com/phpmyadmin/" your_user your_password --exclude-dbs mydb2,mydb4 --prepend-date --basename example_dump --output-directory /tmp --compression zip
6067

6168
Downloads a zipped dump with databases `mydb2` & `mydb4` excluded, the base name `example_dump` and a prepended UTC date / time to the directory `/tmp`, e.g. `/tmp/2016-03-11--15-19-04-UTC_example_dump.zip`.
6269

63-
Requirements
64-
------------
70+
## Requirements
6571

66-
- [Grab - python web-scraping framework](http://grablib.org/) - install via ``pip install -U grab``
72+
- [Grab - python web-scraping framework](http://grablib.org/): Install via `pip install -U Grab` or see the [installation instructions](http://docs.grablib.org/en/latest/usage/installation.html) if you run into problems.
6773

68-
Changelog
69-
---------
74+
## Changelog
7075

7176
Currently, there is no changelog; the best option at the moment is to read the commit messages.
7277

73-
License
74-
-------
75-
76-
GNU GPL
78+
## License
7779

78-
Acknowledgements
79-
----------------
80+
[GNU GPL3](https://www.gnu.org/licenses/gpl-3.0.html)
8081

81-
### Authors
82+
## Author
8283

8384
- Christoph Haunschmidt

phpmyadmin_sql_backup.py

Lines changed: 50 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -18,24 +18,29 @@
1818
# A Python script to automate the download of SQL dump backups
1919
# via a phpMyAdmin web interface.
2020
#
21-
# tested on Python 3.5
21+
# tested on Python 3.4+
22+
# requires: grab (http://grablib.org/)
2223
#
2324
# Christoph Haunschmidt 2016-03
2425

2526
import argparse
2627
import os
2728
import re
29+
import sys
2830
import datetime
2931

3032
import grab
3133

32-
__version__ = '2016-03-10.0'
34+
__version__ = '2016-03-12.0'
3335

3436
CONTENT_DISPOSITION_FILENAME_RE = re.compile(r'^.*filename="(?P<filename>[^"]+).*$')
37+
DEFAULT_PREFIX_FORMAT = r'%Y-%m-%d--%H-%M-%S-UTC_'
3538

3639

3740
def download_sql_backup(url, user, password, dry_run=False, overwrite_existing=False, prepend_date=True, basename='',
38-
output_directory=os.getcwd(), exclude_dbs=None, compression='none', timeout=60, **kwargs):
41+
output_directory=os.getcwd(), exclude_dbs=None, compression='none', prefix_format=None,
42+
timeout=60, **kwargs):
43+
prefix_format = prefix_format or DEFAULT_PREFIX_FORMAT
3944
exclude_dbs = exclude_dbs.split(',') or []
4045
encoding = '' if compression == 'gzip' else 'gzip'
4146

@@ -46,34 +51,40 @@ def download_sql_backup(url, user, password, dry_run=False, overwrite_existing=F
4651
g.doc.set_input_by_id('input_password', password)
4752
g.doc.submit()
4853

49-
g.doc.text_assert('server_export.php')
54+
try:
55+
g.doc.text_assert('server_export.php')
56+
except Exception as e:
57+
raise ValueError('Could not login - did you provide the correct username / password? ({})'.format(e))
5058
export_url = g.doc.select("id('topmenu')//a[contains(@href,'server_export.php')]/@href").text()
5159
g.go(export_url)
5260

5361
dbs_available = [option.attrib['value'] for option in g.doc.form.inputs['db_select[]']]
62+
dbs_to_dump = [db_name for db_name in dbs_available if db_name not in exclude_dbs]
63+
if not dbs_to_dump:
64+
print('Warning: no databases to dump (databases available: "{}")'.format('", "'.join(dbs_available)),
65+
file=sys.stderr)
5466

5567
file_response = g.doc.submit(
56-
extra_post=[('db_select[]', db_name) for db_name in dbs_available if db_name not in exclude_dbs] + [
57-
('compression', compression)])
68+
extra_post=[('db_select[]', db_name) for db_name in dbs_to_dump] + [('compression', compression)])
5869

59-
m = CONTENT_DISPOSITION_FILENAME_RE.match(g.response.headers['Content-Disposition'])
60-
if not m:
70+
re_match = CONTENT_DISPOSITION_FILENAME_RE.match(g.response.headers['Content-Disposition'])
71+
if not re_match:
6172
raise ValueError(
6273
'Could not determine SQL backup filename from {}'.format(g.response.headers['Content-Disposition']))
6374

64-
content_filename = m.group('filename')
75+
content_filename = re_match.group('filename')
6576
filename = content_filename if not basename else basename + os.path.splitext(content_filename)[1]
6677
if prepend_date:
67-
prefix = datetime.datetime.utcnow().strftime(r'%Y-%m-%d--%H-%M-%S-UTC_')
78+
prefix = datetime.datetime.utcnow().strftime(prefix_format)
6879
filename = prefix + filename
6980
out_filename = os.path.join(output_directory, filename)
7081

7182
if os.path.isfile(out_filename) and not overwrite_existing:
7283
basename, ext = os.path.splitext(out_filename)
7384
n = 1
74-
print('File {} already exists, to overwrite it use: --overwrite-existing'.format(out_filename))
85+
print('File {} already exists, to overwrite it use --overwrite-existing'.format(out_filename), file=sys.stderr)
7586
while True:
76-
alternate_out_filename = basename + '_({})'.format(n) + ext
87+
alternate_out_filename = '{}_({}){}'.format(basename, n, ext)
7788
if not os.path.isfile(alternate_out_filename):
7889
out_filename = alternate_out_filename
7990
break
@@ -88,29 +99,42 @@ def download_sql_backup(url, user, password, dry_run=False, overwrite_existing=F
8899
if __name__ == '__main__':
89100
parser = argparse.ArgumentParser(
90101
description='Automates the download of SQL dump backups via a phpMyAdmin web interface.',
91-
epilog='Written by Christoph Haunschmidt. Version: {}'.format(__version__))
102+
epilog='Written by Christoph Haunschmidt, version: {}'.format(__version__))
92103

93-
parser.add_argument('url', help='phpMyAdmin login page url')
94-
parser.add_argument('user', help='phpMyAdmin login user')
95-
parser.add_argument('password', help='phpMyAdmin login password')
96-
parser.add_argument('-o', '--output-directory', default=os.getcwd(), help='output directory for the SQL dump file')
104+
parser.add_argument('url', metavar='URL', help='phpMyAdmin login page url')
105+
parser.add_argument('user', metavar='USERNAME', help='phpMyAdmin login username')
106+
parser.add_argument('password', metavar='PASSWORD', help='phpMyAdmin login password')
107+
parser.add_argument('-o', '--output-directory', default=os.getcwd(),
108+
help='output directory for the SQL dump file (default: the current working directory)')
97109
parser.add_argument('-p', '--prepend-date', action='store_true', default=False,
98-
help='prepend the current UTC date + time to the filename: "YYYY-MM-DD--HH-MM-SS-UTC_"')
110+
help='prepend current UTC date & time to the filename; see the --prefix-format option for custom formatting')
99111
parser.add_argument('-e', '--exclude-dbs', default='',
100-
help='comma separated list for database names to exclude from the dump')
112+
help='comma-separated list of database names to exclude from the dump')
101113
parser.add_argument('--compression', default='none', choices=['none', 'zip', 'gzip'],
102-
help='compression method of the output file (default: %(default)s)')
114+
help='compression method for the output file - must be supported by the server (default: %(default)s)')
103115
parser.add_argument('--basename',
104-
help='the desired basename (without extension) of the SQL dump file (default: the name given by phpMyAdmin')
116+
help='the desired basename (without extension) of the SQL dump file (default: the name given by phpMyAdmin)')
105117
parser.add_argument('--timeout', type=int, default=60,
106118
help='timeout in seconds for the requests (default: %(default)s)')
107119
parser.add_argument('--overwrite-existing', action='store_true', default=False,
108-
help='overwrite existing backup files (instead of appending a number to the name)')
120+
help='overwrite existing SQL dump files (instead of appending a number to the name)')
121+
parser.add_argument('--prefix-format', default='',
122+
help=str('the prefix format for --prepend-date (default: "{}"); in Python\'s strftime format. '
123+
'Must be used with --prepend-date to be in effect'.format(DEFAULT_PREFIX_FORMAT.replace('%', '%%'))))
109124
parser.add_argument('--dry-run', action='store_true', default=False,
110-
help='dry run, do not actually download the dump')
125+
help='dry run, do not actually download any file')
111126

112-
ARGS = parser.parse_args()
127+
args = parser.parse_args()
113128

114-
dump_fn = download_sql_backup(**vars(ARGS))
129+
if args.prefix_format and not args.prepend_date:
130+
print('Error: --prefix-format given without --prepend-date', file=sys.stderr)
131+
sys.exit(2)
115132

116-
print('{} saved SQL dump to: {}'.format(('Would have' if ARGS.dry_run else 'Successfully'), dump_fn))
133+
try:
134+
dump_fn = download_sql_backup(**vars(args))
135+
except Exception as e:
136+
print('Error: {}'.format(e), file=sys.stderr)
137+
sys.exit(1)
138+
139+
print('{} saved SQL dump to: {}'.format(('Would have' if args.dry_run else 'Successfully'), dump_fn),
140+
file=sys.stdout)

0 commit comments

Comments
 (0)