diff options
author | Lars Wirzenius <liw@liw.fi> | 2013-05-05 19:39:29 +0100 |
---|---|---|
committer | Lars Wirzenius <liw@liw.fi> | 2013-05-05 19:39:29 +0100 |
commit | 4d52a1ab3acc09da27245afb7707587117d42f4d (patch) | |
tree | a0818d482faba49dc1b11506a8a6184eec95bb65 | |
parent | 8b64b3d5b21e1d1bf62affd0689bfd06a8d037e7 (diff) | |
download | obnam-4d52a1ab3acc09da27245afb7707587117d42f4d.tar.gz |
Start RepositoryInterface class
-rw-r--r-- | obnamlib/__init__.py | 2 | ||||
-rw-r--r-- | obnamlib/repo_dummy.py | 28 | ||||
-rw-r--r-- | obnamlib/repo_dummy_tests.py | 26 | ||||
-rw-r--r-- | obnamlib/repo_interface.py | 194 | ||||
-rw-r--r-- | without-tests | 1 |
5 files changed, 251 insertions, 0 deletions
diff --git a/obnamlib/__init__.py b/obnamlib/__init__.py index cb091a34..1b652cb3 100644 --- a/obnamlib/__init__.py +++ b/obnamlib/__init__.py @@ -85,6 +85,8 @@ from vfs import VirtualFileSystem, VfsFactory, VfsTests from vfs_local import LocalFS from metadata import (read_metadata, set_metadata, Metadata, metadata_fields, metadata_verify_fields, encode_metadata, decode_metadata) +from repo_interface import RepositoryInterface, RepositoryInterfaceTests +from repo_dummy import RepositoryFormatDummy from repo_tree import RepositoryTree from chunklist import ChunkList from clientlist import ClientList diff --git a/obnamlib/repo_dummy.py b/obnamlib/repo_dummy.py new file mode 100644 index 00000000..bd68198c --- /dev/null +++ b/obnamlib/repo_dummy.py @@ -0,0 +1,28 @@ +# Copyright 2013 Lars Wirzenius +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +# =*= License: GPL-3+ =*= + + +import obnamlib + + +class RepositoryFormatDummy(obnamlib.RepositoryInterface): + + format = 'dummy' + + def set_fs(self, fs): + pass + diff --git a/obnamlib/repo_dummy_tests.py b/obnamlib/repo_dummy_tests.py new file mode 100644 index 00000000..eca24ed9 --- /dev/null +++ b/obnamlib/repo_dummy_tests.py @@ -0,0 +1,26 @@ +# Copyright 2013 Lars Wirzenius +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +# =*= License: GPL-3+ =*= + + +import obnamlib + + +class RepositoryFormatDummyTests(obnamlib.RepositoryInterfaceTests): + + def setUp(self): + self.repo = obnamlib.RepositoryFormatDummy() + diff --git a/obnamlib/repo_interface.py b/obnamlib/repo_interface.py new file mode 100644 index 00000000..a08b7803 --- /dev/null +++ b/obnamlib/repo_interface.py @@ -0,0 +1,194 @@ +# repo_interface.py -- interface class for repository access +# +# Copyright 2013 Lars Wirzenius +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. +# +# =*= License: GPL-3+ =*= + + +import unittest + + +class RepositoryInterface(object): + + '''Abstract interface to Obnam backup repositories. + + An Obnam backup repository stores backups for backup clients. + As development of Obnam progresses, the details of how things + are stored can change. This is usually necessary for performance + improvements. + + To allow Obnam to access, both for reading and writing, any + version of the repository format, this class defines an interface + for repository access. Every different version of the format + implements a class with this interface, so that the rest of + Obnam can just use the interface. + + The interface is suitably high level that using the repository + is convenient, and that it allows a variety of implementations. + At the same time it concentrates on the needs of repository + access only. + + The interface also specifies the interface with which the + implementation accesses the actual filesystem: it is the + Obnam VFS layer. + + [rest of Obnam code] + | + | calls RepositoryInterface API + | + V + [RepositoryFormatX implementing RepositoryInterface API] + | + | calls VFS API + | + V + [FooFS implementing VirtualFileSystem API] + + The VFS API implementation is given to the RepositoryInterface + implementation with the ``set_fs`` method. + + It must be stressed that ALL access to the repository go via + an implemention of RepositoryInterface. Further, all the + implementation classes must be instantiated via RepositoryFactory. + + The abstraction RepositoryInterface provides for repositories + consists of a few key concepts: + + * A repository contains data about one or more clients. + * For each client, there is some metadata, plus a list of generations. + * For each generation, there is some metadata, plus a list of + files (where directories are treated as files). + * For each file, there is some metadata, plus a list of chunk + identifiers. + * File contents data is split into chunks, each given a unique + identifier. + * There is optionally some indexing for content based lookups of + chunks (e.g., look up chunks based on an MD5 checksum). + * There are three levels of locking: the list of clients, + the per-client data (information about generations), and + the chunk lookup indexes are all locked up individually. + * All metadata is stored as key/value pairs, where the key is + one of a strictly limited list of strings, and the value is + a binary string. + + Further, the repository format version implementation is given + a directory in which it stores the repository, using any number + of files it wishes. No other files will be in that directory. + (RepositoryFactory creates the actual directory.) The only + restriction is that within that directory, the + ``metadata/format``file MUST be a plain text file (no encryption, + compression), containing a single line, giving the format + of the repository, as an arbitrary string. Each RepositoryInterface + implementation will work with exactly one such format, and have + a class attribute ``format`` which contains the string. + + Each RepositoryInterface implementation can have a custom + initialiser. RepositoryFactory will know how to call it, + giving it all the information it needs. + + ''' + + # Operations on the repository itself. + + def set_fs(self, fs): + '''Set the Obnam VFS instance for accessing the filesystem.''' + raise NotImplementedError() + + #def init_repo(self): + #def destroy_repo(self): + #def get_repo_keys(self): + #def get_repo_key_value(self, key): + #def set_repo_key_value(self, key, value): + #def remove_repo_key(self, key): + #def lock_repo(self): + #def unlock_repo(self): + + ## Client list. + #def get_client_names(self): + #def lock_client_list(self): + #def commit_client_list(self): + #def unlock_client_list(self): + #def force_client_list_lock(self): + #def add_client(self, client_name): + #def remove_client(self, client_name): + #def rename_client(self, old_client_name, new_client_name): + + ## A particular client. + #def get_client_keys(self, client_name): + #def get_client_key_value(self, client_name, key): + #def set_client_key_value(self, client_name, key, value): + #def remove_client_key(self, clientname, key): + #def lock_client(self, client_name): + #def commit_client(self, client_name): # commits started generation too + #def unlock_client(self, client_name): + #def force_client_lock(self, client_name): + #def get_client_generation_ids(self, client_name): + #def create_generation(self, client_name): # return generation_id + + ## Generations. The generation id identifies client as well. + #def get_generation_keys(self, generation_id): + #def get_generation_key_value(self, generation_id, key): + #def set_generation_key_value(self, generation_id, key, value): + #def remove_generation_key(self, generation_id, key): + #def remove_generation(self, generation_id): + #def get_chunk_ids_in_generation(self, generation_id): + #def interpret_generation_spec(self, genspec): # returns generation id + #def walk_generation(self, generation_id, filename): # generator + + ## Individual files and directories in a generation. + #def file_exists(self, generation_id, filename): + #def add_file(self, generation_id, filename): + #def remove_file(self, generation_id, filename): + #def get_file_keys(self, generation_id, filename): + #def get_file_key_value(self, generation_id, filename, key): + #def set_file_key_value(self, generation_id, filename, key, value): + #def remove_file_key(self, generation_id, filename, key): + #def get_file_chunk_ids(self, generation_id, filename): + #def clear_file_chunk_ids(self, generation_id, filename): + #def append_file_chunk_id(self, generation_id, filename, chunk_id): + #def get_file_children(self, generation_id, filename): + + ## Chunks. + #def put_chunk_content(self, data): # return new chunk_id + #def get_chunk_content(self, chunk_id): + #def lock_chunk_indexes(self): + #def unlock_chunk_indexes(self): + #def force_chunk_index_lock(self): + #def put_chunk_into_indexes(self, chunk_id, data): + #def find_chunk_id_by_content(self, data): + #def remove_chunk(self, chunk_id): + + ## Fsck. + #def get_fsck_work_items(self): + + +class RepositoryInterfaceTests(unittest.TestCase): # pragma: no cover + + '''Tests for implementations of RepositoryInterface. + + Each implementation of RepositoryInterface should have a corresponding + test class, which inherits this class. The test subclass must set + ``self.repo`` to an instance of the class to be tested. + + ''' + + def test_has_format_attribute(self): + self.assertEqual(type(self.repo.format), str) + + def test_has_set_fs_method(self): + # We merely test that set_fs can be called. + self.assertEqual(self.repo.set_fs(None), None) + diff --git a/without-tests b/without-tests index 48fbd09d..56e50bbf 100644 --- a/without-tests +++ b/without-tests @@ -28,3 +28,4 @@ ./.pc/debian-changes-0.22-2/obnamlib/plugins/fsck_plugin.py ./.pc/debian-changes-0.22-2/obnamlib/plugins/restore_plugin.py obnamlib/plugins/convert5to6_plugin.py +obnamlib/repo_interface.py |