Index: /branches/ipp-259_genericise_backups/tools/backups/backup.py
===================================================================
--- /branches/ipp-259_genericise_backups/tools/backups/backup.py	(revision 40930)
+++ /branches/ipp-259_genericise_backups/tools/backups/backup.py	(revision 40931)
@@ -3,4 +3,5 @@
 import os.path as path
 import socket
+import subprocess
 
 import backups.utils.config_parse_helper as cfg_help
@@ -156,10 +157,10 @@
             return 0
 
-        dests = self.default_dict['backup_paths']
+        backup_paths = self.default_dict['backup_paths']
 
         filename = self.tar_dict['target_filename']
         if 'prefix_date' in self.tar_dict.keys() and self.tar_dict['prefix_date']:
             filename = f"{get_date()}_{filename}"
-        filename = path.join(dests[0], filename)  # may need some tar suffix magic
+        filename = path.join(backup_paths[0], filename)  # may need some tar suffix magic
 
         add_args = [] if 'additional_args' not in self.tar_dict.keys() else self.tar_dict['additional_args']
@@ -171,8 +172,8 @@
             sub_utils.chmod_wrapper(['774', filename])
             # copy to backups
-            if len(dests) == 1:
+            if len(backup_paths) == 1:
                 return 0
-            for d in range(1, len(dests)):
-                sub_utils.cp_wrapper([ filename, dests[d]])
+            for d in range(1, len(backup_paths)):
+                sub_utils.cp_wrapper([ filename, backup_paths[d]])
         except errs.SubprocessError as e:
             raise errs.SubprocessError() from e
@@ -183,5 +184,45 @@
 
     def _run_mysqldump(self):
-        raise Exception("Not implemented")
+
+        # cfg_help.ConfigSchemaLine('user', True, str)
+        # cfg_help.ConfigSchemaLine('password', True, str)
+        # cfg_help.ConfigSchemaLine('db_name', True, str)
+        # cfg_help.ConfigSchemaLine('additional_args', False, list, cfg_help.SpecialSchemaProcessing.space_separated)
+        # cfg_help.ConfigSchemaLine('target_filename', True, str)
+        # cfg_help.ConfigSchemaLine('prefix_date', True, bool)
+
+        backup_paths = self.default_dict['backup_paths']
+
+        # Setup the commands:
+        mysqldump_args = [ 'mysqldump'
+                         , '-u', self.mysqldump_dict['user']
+                         , f"-p{self.mysqldump_dict['password']}"
+                         , '--databases', self.mysqldump_dict['db_name']
+                         ]
+        if 'additional_args' in self.mysqldump_dict.keys():
+            mysqldump_args = mysqldump_args + self.mysqldump_dict['additional_args']
+
+        dump_filename = self.mysqldump_dict['target_filename']
+        if 'prefix_date' in self.mysqldump_dict.keys() and self.mysqldump_dict['prefix_date']:
+            dump_filename = f"{get_date()}_{dump_filename}"
+        dump_filename = path.join(backup_paths[0], dump_filename)
+
+        if path.exists(dump_filename):
+            raise errs.ValidationError(f"ValidationError: mysqldump already exists {dump_filename}")
+
+        with open(dump_filename, "w+") as df:
+            mysqldump_process = subprocess.Popen(mysqldump_args, stdout=df, stderr=subprocess.PIPE)
+            dump_result = mysqldump_process.communicate()
+
+        # Get the piped std err. See if it contains the word error
+        stdo, stde = dump_result
+        if stde is not None:
+            decoded_stderr = stde.decode()
+            if decoded_stderr.casefold().find('error'.casefold()) > -1:
+                raise errs.SubprocessError(f"SubprocessError: Errors detected in mysqldump:\n{decoded_stderr}")
+
+        # copy the dump to other locations
+        for d in range(1, len(backup_paths)):
+            sub_utils.cp_wrapper([dump_filename, backup_paths[d]])
 
 
Index: /branches/ipp-259_genericise_backups/tools/backups/backup_test.py
===================================================================
--- /branches/ipp-259_genericise_backups/tools/backups/backup_test.py	(revision 40930)
+++ /branches/ipp-259_genericise_backups/tools/backups/backup_test.py	(revision 40931)
@@ -294,6 +294,4 @@
         assert not os.path.exists(expected_tar2)
         backy._run_tar()
-        print(expected_tar1)
-        print(expected_tar2)
         assert os.path.exists(expected_tar1)
         assert os.path.exists(expected_tar2)
@@ -309,9 +307,52 @@
 
 
-# class TestRunningMysqldump():
-
-#     def test_simply_mysqldump(self):
-#         assert True is False
-
-#     def test_mysqldump_with_additional_args(self):
-#         assert True is False
+@pytest.mark.skip(reason="Requires a fake database to be setup")
+class TestMysqlBackup(object):
+    """ The mySQL dump tests require the machine on which they are
+running to have a mysql database running with a user called
+'dumper' and a password set to 'not_a_real_password'.
+
+In production, a proper password should be set on the server and in the config
+file. DO NOT USE THE ABOVE IN PRODUCTION
+
+To change the password:
+UPDATE mysql.user SET authentication_string = PASSWORD('NEW_USER_PASSWORD')WHERE User = 'user-name' AND Host = 'localhost';FLUSH PRIVILEGES;
+"""
+
+    dumper_password = "not_a_real_password"
+
+    def test_mysql_dump_is_ok(self, tmpdir):
+        config = make_default_config(tmpdir, ['bck1', 'bck2'])
+        config['MYSQLDUMP'] = \
+        { 'user' : 'dumper'
+        , 'password' : 'not_a_real_password'
+        , 'db_name' : 'jiradb'
+        , 'target_filename' : "cool_dump.sql"
+        , 'prefix_date' : True
+        }
+
+        backy = Backup(config)
+
+        date = bckup.get_date()
+
+        expected_dump1 = os.path.join(tmpdir, 'bck1', f'{date}_cool_dump.sql')
+        expected_dump2 = os.path.join(tmpdir, 'bck2', f'{date}_cool_dump.sql')
+        assert not os.path.exists(expected_dump1)
+        assert not os.path.exists(expected_dump2)
+        backy._run_mysqldump()
+        assert os.path.exists(expected_dump1)
+        assert os.path.exists(expected_dump2)
+
+    def test_mysql_dump_fails_with_incorrect_password(self, tmpdir):
+        config = make_default_config(tmpdir, ['bck1', 'bck2'])
+        config['MYSQLDUMP'] = \
+        { 'user' : 'dumper'
+        , 'password' : 'incorrect_password'
+        , 'db_name' : 'jiradb'
+        , 'target_filename' : "cool_dump.sql"
+        , 'prefix_date' : True
+        }
+
+        backy = Backup(config)
+        with pytest.raises(errs.SubprocessError):
+            backy._run_mysqldump()
