diff --git a/api/restapi.py b/api/restapi.py index 687ad275..69869829 100644 --- a/api/restapi.py +++ b/api/restapi.py @@ -29,6 +29,7 @@ from market.btcprice import BtcPrice from net.upnp import PortMapper from api.utils import sanitize_html +import db.backuptool DEFAULT_RECORDS_COUNT = 20 DEFAULT_RECORDS_OFFSET = 0 @@ -104,7 +105,6 @@ def login(self, request): self._failed_login(request.getHost().host) return json.dumps({"success": False, "reason": "invalid username or password"}) - @GET('^/api/v1/get_image') @authenticated def get_image(self, request): @@ -1061,6 +1061,18 @@ def get_response(num): request.finish() return server.NOT_DONE_YET + @POST('^/api/v1/backup_files') + def backup_files(self, request): + """Archives OpenBazaar files in a single tar archive.""" + output_name = request.args["output_name"][0] + return db.backuptool.backupfiles(output_name) + + @POST('^/api/v1/restore_files') + def restore_files(self, request): + """Restores files of given archive to OpenBazaar folder.""" + input_file = request.args["input_file"][0] + return db.backuptool.restorefiles(input_file) + @GET('^/api/v1/get_chat_messages') @authenticated def get_chat_messages(self, request): diff --git a/db/backuptool.py b/db/backuptool.py new file mode 100644 index 00000000..bc9a0622 --- /dev/null +++ b/db/backuptool.py @@ -0,0 +1,62 @@ +"""Import and export data for the OpenBazaar server.""" +__author__ = 'marc' +from constants import DATA_FOLDER +import db.datastore as db +import os +import sqlite3 as lite +import tarfile +import time + +BACKUP_FOLDER = 'backup' + +def _getdatabase(): + """Retrieves the OpenBazaar database file.""" + database = db.Database() + return database.DATABASE + +def backupfiles(output_name=None): + """Archives OpenBazaar files in a single tar archive.""" + os.chdir(DATA_FOLDER) + if not os.path.exists(BACKUP_FOLDER): + os.makedirs(BACKUP_FOLDER) + if not output_name: + output_name = 'backup_{0}.tar.gz'.format(time.strftime('%Y-%m-%d')) + if not os.path.isabs(output_name): + output = BACKUP_FOLDER + os.sep + output_name + if os.path.isfile(output): + raise IOError(output + ' already exists.') + + # Lock the database + db_file = _getdatabase() + db_connection = lite.connect(db_file) + db_connection.commit() + + # Archive files + files = os.listdir(DATA_FOLDER) + with tarfile.open(output, 'w:gz') as tar: + for fil in files: + if fil != BACKUP_FOLDER: + tar.add(fil) + tar.close() + + # Unlock database + db_connection.rollback() + db_connection.close() + + return True + + +def restorefiles(input_file): + """Restores files of given archive to OpenBazaar folder.""" + os.chdir(DATA_FOLDER) + if not os.path.isabs(input_file): + input_file = BACKUP_FOLDER + os.sep + input_file + + if not os.path.isfile(input_file): + raise IOError(input_file + ' does not exist.') + + # Unarchive files + with tarfile.open(input_file, 'r:gz') as tar: + tar.extractall() + + return True diff --git a/db/tests/test_backuptool.py b/db/tests/test_backuptool.py new file mode 100644 index 00000000..818b2035 --- /dev/null +++ b/db/tests/test_backuptool.py @@ -0,0 +1,47 @@ +"""Test for db/backuptool""" +from constants import DATA_FOLDER +import os +import unittest + +import db.backuptool as bt + +TEST_FOLDER = 'test' +TEST_TAR = 'test.tar.gz' +TEST_FILE_1 = 'test.txt' +TEST_FILE_2 = TEST_FOLDER + os.sep + 'test2.txt' + +class BackuptoolTest(unittest.TestCase): + """Test class for backuptool functions""" + def setUp(self): + os.chdir(DATA_FOLDER) + # Create test folder + if not os.path.exists(TEST_FOLDER): + os.makedirs(TEST_FOLDER) + # Create test files "test.txt", "test/test2.txt" + fil = open(TEST_FILE_1, 'w') + fil.close() + fil = open(TEST_FILE_2, 'w') + fil.close() + # Backup to "test.tar.gz" + if os.path.exists(bt.BACKUP_FOLDER + os.sep + TEST_TAR): + os.remove(bt.BACKUP_FOLDER + os.sep + TEST_TAR) + bt.backupfiles(TEST_TAR) + # Remove test files and directory + os.remove(TEST_FILE_1) + os.remove(TEST_FILE_2) + # Restore from "test.tar.gz" + bt.restorefiles(TEST_TAR) + + def tearDown(self): + os.remove(TEST_FILE_1) + os.remove(TEST_FILE_2) + os.remove(bt.BACKUP_FOLDER + os.sep + TEST_TAR) + + def test_backupexists(self): + """Checks if the backup file exists""" + self.assertTrue(os.path.isfile(bt.BACKUP_FOLDER + os.sep + TEST_TAR)) + + def test_restoreexists(self): + """Checks if the restored files exists""" + self.assertTrue(os.path.isfile(TEST_FILE_1)) + self.assertTrue(os.path.isfile(TEST_FILE_2))