summaryrefslogtreecommitdiff
path: root/obnam/obj.py
diff options
context:
space:
mode:
Diffstat (limited to 'obnam/obj.py')
-rw-r--r--obnam/obj.py521
1 files changed, 0 insertions, 521 deletions
diff --git a/obnam/obj.py b/obnam/obj.py
deleted file mode 100644
index 5168900c..00000000
--- a/obnam/obj.py
+++ /dev/null
@@ -1,521 +0,0 @@
-# Copyright (C) 2006, 2007, 2008 Lars Wirzenius <liw@iki.fi>
-#
-# 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 2 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, write to the Free Software Foundation, Inc.,
-# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-
-"""Backup objects"""
-
-import logging
-
-import uuid
-
-import obnam.cmp
-import obnam.varint
-
-
-# Magic cookie at the beginning of every block
-
-BLOCK_COOKIE = "blockhead\n"
-
-
-# Version of the storage format
-
-FORMAT_VERSION = "1"
-
-
-# Constants of object kinds
-
-_object_kinds = {}
-
-def _define_kind(code, name):
- assert code not in _object_kinds
- assert name not in _object_kinds.values()
- _object_kinds[code] = name
- return code
-
-FILEPART = _define_kind( 1, "FILEPART")
-# object kind 2 used to be INODE, but it's been removed
-GEN = _define_kind( 3, "GEN")
-SIG = _define_kind( 4, "SIG")
-HOST = _define_kind( 5, "HOST")
-FILECONTENTS = _define_kind( 6, "FILECONTENTS")
-FILELIST = _define_kind( 7, "FILELIST")
-DELTA = _define_kind( 8, "DELTA")
-DELTAPART = _define_kind( 9, "DELTAPART")
-DIR = _define_kind(10, "DIR")
-FILEGROUP = _define_kind(11, "FILEGROUP")
-
-
-def kind_name(kind):
- """Return a textual name for a numeric object kind"""
- return _object_kinds.get(kind, "UNKNOWN")
-
-
-def object_id_new():
- """Return a string that is a universally unique ID for an object"""
- id = str(uuid.uuid4())
- logging.debug("Creating object id %s" % id)
- return id
-
-
-class StorageObject(object):
-
- """Implement a storage object in memory.
-
- There should be a sub-class of this class for every kind of storage
- object. Sub-class may implement a constructor, but their construct
- MUST accept a components= argument and pass it on to the base class
- constructor.
-
- Additionally, sub-classes MUST define the "kind" attribute to refer
- to the kind of storage object they are. This is required for
- the StorageObjectFactory to work.
-
- """
-
- kind = None
-
- def __init__(self, components=None, id=None):
- assert components is not None or id is not None
- if components:
- self._components = components
- else:
- self._components = []
-
- if id:
- self.set_id(id)
- if self.first_varint_by_kind(obnam.cmp.OBJKIND) is None and self.kind:
- self.add(obnam.cmp.Component(obnam.cmp.OBJKIND,
- obnam.varint.encode(self.kind)))
-
- def remove(self, kind):
- """Remove all components of a given kind."""
- self._components = [c for c in self.get_components()
- if c.get_kind() != kind]
-
- def add(self, c):
- """Add a component"""
- self._components.append(c)
-
- def replace(self, c):
- """Remove any existing components of this kind, then add this one."""
- self.remove(c.get_kind())
- self.add(c)
-
- def get_kind(self):
- """Return the kind of an object"""
- return self.first_varint_by_kind(obnam.cmp.OBJKIND)
-
- def get_id(self):
- """Return the identifier for an object"""
- return self.first_string_by_kind(obnam.cmp.OBJID)
-
- def set_id(self, id):
- """Set the identifier for this object."""
- self.replace(obnam.cmp.Component(obnam.cmp.OBJID, id))
-
- def get_components(self):
- """Return list of all components in an object"""
- return self._components
-
- def find_by_kind(self, wanted_kind):
- """Find all components of a desired kind inside this object"""
- return [c for c in self.get_components()
- if c.get_kind() == wanted_kind]
-
- def find_strings_by_kind(self, wanted_kind):
- """Find all components of a desired kind, return string values"""
- return [c.get_string_value() for c in self.find_by_kind(wanted_kind)]
-
- def find_varints_by_kind(self, wanted_kind):
- """Find all components of a desired kind, return varint values"""
- return [c.get_varint_value() for c in self.find_by_kind(wanted_kind)]
-
- def first_by_kind(self, wanted_kind):
- """Find first component of a desired kind"""
- for c in self.get_components():
- if c.get_kind() == wanted_kind:
- return c
- return None
-
- def first_string_by_kind(self, wanted_kind):
- """Find string value of first component of a desired kind"""
- c = self.first_by_kind(wanted_kind)
- if c:
- return c.get_string_value()
- else:
- return None
-
- def first_varint_by_kind(self, wanted_kind):
- """Find string value of first component of a desired kind"""
- c = self.first_by_kind(wanted_kind)
- if c:
- return c.get_varint_value()
- else:
- return None
-
- def encode(self):
- """Encode an object as a string"""
- return "".join(c.encode() for c in self.get_components())
-
-
-# This function is only used during testing.
-def decode(encoded):
- """Decode an object from a string"""
- parser = obnam.cmp.Parser(encoded)
- list = parser.decode_all()
- return StorageObject(components=list)
-
-
-class ObjectQueue:
-
- def __init__(self):
- self.clear()
-
- def add(self, object_id, encoded_object):
- """Add an encoded object into an object queue"""
- self.queue.append((object_id, encoded_object))
- self.size += len(encoded_object)
-
- def clear(self):
- """Remove all objects from an object queue"""
- self.queue = []
- self.size = 0
-
- def is_empty(self):
- """Is an object queue empty?"""
- return self.size == 0
-
- def combined_size(self):
- """Return the combined size of all objects in an object queue"""
- return self.size
-
- def ids(self):
- """Return identifiers for all the objects in the object queue"""
- return [x[0] for x in self.queue]
-
- def as_block(self, blkid):
- """Create a block from an object queue"""
- logging.debug("Creating block %s" % blkid)
- blkid = obnam.cmp.Component(obnam.cmp.BLKID, blkid)
- objects = [obnam.cmp.Component(obnam.cmp.OBJECT, x[1])
- for x in self.queue]
- return "".join([BLOCK_COOKIE] +
- [c.encode() for c in [blkid] + objects])
-
-
-class BlockWithoutCookie(obnam.ObnamException):
-
- def __init__(self, block):
- self._msg = ("Block does not start with cookie: %s" %
- " ".join("%02x" % ord(c) for c in block[:32]))
-
-
-class EmptyBlock(obnam.ObnamException):
-
- def __init__(self):
- self._msg = "Block has no components."
-
-
-def block_decode(block):
- """Return list of decoded components in block, or None on error"""
- if block.startswith(BLOCK_COOKIE):
- parser = obnam.cmp.Parser(block, len(BLOCK_COOKIE))
- list = parser.decode_all()
- if not list:
- raise EmptyBlock()
- return list
- else:
- raise BlockWithoutCookie(block)
-
-
-class SignatureObject(StorageObject):
-
- kind = SIG
-
- def __init__(self, components=None, id=None, sigdata=None):
- StorageObject.__init__(self, components=components, id=id)
- if sigdata:
- c = obnam.cmp.Component(obnam.cmp.SIGDATA, sigdata)
- self.add(c)
-
-
-class DeltaObject(StorageObject):
-
- kind = DELTA
-
- def __init__(self, components=None, id=None, deltapart_refs=None,
- cont_ref=None, delta_ref=None):
- StorageObject.__init__(self, components=components, id=id)
- if deltapart_refs:
- for deltapart_ref in deltapart_refs:
- c = obnam.cmp.Component(obnam.cmp.DELTAPARTREF, deltapart_ref)
- self.add(c)
- if cont_ref:
- c = obnam.cmp.Component(obnam.cmp.CONTREF, cont_ref)
- self.add(c)
- elif delta_ref:
- c = obnam.cmp.Component(obnam.cmp.DELTAREF, delta_ref)
- self.add(c)
-
-
-class GenerationObject(StorageObject):
-
- kind = GEN
-
- def __init__(self, components=None, id=None, filelist_id=None,
- dirrefs=None, filegrouprefs=None, start=None, end=None):
- StorageObject.__init__(self, components=components, id=id)
- if filelist_id:
- self.add(obnam.cmp.Component(obnam.cmp.FILELISTREF, filelist_id))
- if dirrefs:
- for ref in dirrefs:
- self.add(obnam.cmp.Component(obnam.cmp.DIRREF, ref))
- if filegrouprefs:
- for ref in filegrouprefs:
- self.add(obnam.cmp.Component(obnam.cmp.FILEGROUPREF, ref))
- if start:
- self.add(obnam.cmp.Component(obnam.cmp.GENSTART,
- obnam.varint.encode(start)))
- if end:
- self.add(obnam.cmp.Component(obnam.cmp.GENEND,
- obnam.varint.encode(end)))
-
- def get_filelistref(self):
- return self.first_string_by_kind(obnam.cmp.FILELISTREF)
-
- def get_dirrefs(self):
- return self.find_strings_by_kind(obnam.cmp.DIRREF)
-
- def get_filegrouprefs(self):
- return self.find_strings_by_kind(obnam.cmp.FILEGROUPREF)
-
- def get_start_time(self):
- return self.first_varint_by_kind(obnam.cmp.GENSTART)
-
- def get_end_time(self):
- return self.first_varint_by_kind(obnam.cmp.GENEND)
-
-
-# This is used only by testing.
-def generation_object_decode(gen):
- """Decode a generation object into objid, file list ref"""
-
- o = decode(gen)
- return o.get_id(), \
- o.first_string_by_kind(obnam.cmp.FILELISTREF), \
- o.find_strings_by_kind(obnam.cmp.DIRREF), \
- o.find_strings_by_kind(obnam.cmp.FILEGROUPREF), \
- o.first_varint_by_kind(obnam.cmp.GENSTART), \
- o.first_varint_by_kind(obnam.cmp.GENEND)
-
-
-class HostBlockObject(StorageObject):
-
- kind = HOST
-
- def __init__(self, components=None, host_id=None, gen_ids=None,
- map_block_ids=None, contmap_block_ids=None):
- StorageObject.__init__(self, components=components, id=host_id)
-
- if components is None:
- c = obnam.cmp.Component(obnam.cmp.FORMATVERSION, FORMAT_VERSION)
- self.add(c)
-
- if gen_ids:
- for gen_id in gen_ids:
- c = obnam.cmp.Component(obnam.cmp.GENREF, gen_id)
- self.add(c)
-
- if map_block_ids:
- for map_block_id in map_block_ids:
- c = obnam.cmp.Component(obnam.cmp.MAPREF, map_block_id)
- self.add(c)
-
- if contmap_block_ids:
- for map_block_id in contmap_block_ids:
- c = obnam.cmp.Component(obnam.cmp.CONTMAPREF, map_block_id)
- self.add(c)
-
- def get_generation_ids(self):
- """Return IDs of all generations for this host."""
- return self.find_strings_by_kind(obnam.cmp.GENREF)
-
- def get_map_block_ids(self):
- """Return IDs of all map blocks for this host."""
- return self.find_strings_by_kind(obnam.cmp.MAPREF)
-
- def get_contmap_block_ids(self):
- """Return IDs of all map blocks for this host."""
- return self.find_strings_by_kind(obnam.cmp.CONTMAPREF)
-
- def encode(self):
- oq = ObjectQueue()
- oq.add(self.get_id(), StorageObject.encode(self))
- return oq.as_block(self.get_id())
-
-
-def create_host_from_block(block):
- """Decode a host block into a HostBlockObject"""
-
- list = block_decode(block)
-
- host_id = obnam.cmp.first_string_by_kind(list, obnam.cmp.BLKID)
-
- gen_ids = []
- map_ids = []
- contmap_ids = []
-
- objparts = obnam.cmp.find_by_kind(list, obnam.cmp.OBJECT)
- for objpart in objparts:
- gen_ids += objpart.find_strings_by_kind(obnam.cmp.GENREF)
- map_ids += objpart.find_strings_by_kind(obnam.cmp.MAPREF)
- contmap_ids += objpart.find_strings_by_kind(obnam.cmp.CONTMAPREF)
-
- return HostBlockObject(host_id=host_id, gen_ids=gen_ids,
- map_block_ids=map_ids,
- contmap_block_ids=contmap_ids)
-
-
-class DirObject(StorageObject):
-
- kind = DIR
-
- def __init__(self, components=None, id=None, name=None, stat=None,
- dirrefs=None, filegrouprefs=None):
- StorageObject.__init__(self, components=components, id=id)
- if name:
- self.add(obnam.cmp.Component(obnam.cmp.FILENAME, name))
- if stat:
- self.add(obnam.cmp.create_stat_component(stat))
- if dirrefs:
- for ref in dirrefs:
- self.add(obnam.cmp.Component(obnam.cmp.DIRREF, ref))
- if filegrouprefs:
- for ref in filegrouprefs:
- self.add(obnam.cmp.Component(obnam.cmp.FILEGROUPREF, ref))
-
- def get_name(self):
- return self.first_by_kind(obnam.cmp.FILENAME).get_string_value()
-
- def get_stat(self):
- st = self.first_by_kind(obnam.cmp.STAT)
- return obnam.cmp.parse_stat_component(st)
-
- def get_dirrefs(self):
- return [c.get_string_value()
- for c in self.find_by_kind(obnam.cmp.DIRREF)]
-
- def get_filegrouprefs(self):
- return [c.get_string_value()
- for c in self.find_by_kind(obnam.cmp.FILEGROUPREF)]
-
-
-class FileGroupObject(StorageObject):
-
- kind = FILEGROUP
-
- def add_file(self, name, stat, contref, sigref, deltaref):
- c = obnam.filelist.create_file_component_from_stat(name, stat,
- contref, sigref,
- deltaref)
- self.add(c)
-
- def get_files(self):
- return self.find_by_kind(obnam.cmp.FILE)
-
- def get_file(self, name):
- for file in self.get_files():
- fname = file.first_string_by_kind(obnam.cmp.FILENAME)
- if name == fname:
- return file
- return None
-
- def get_string_from_file(self, file, kind):
- return file.first_string_by_kind(kind)
-
- def get_stat_from_file(self, file):
- c = file.first_by_kind(obnam.cmp.STAT)
- return obnam.cmp.parse_stat_component(c)
-
- def get_names(self):
- return [self.get_string_from_file(x, obnam.cmp.FILENAME)
- for x in self.get_files()]
-
- def get_stat(self, filename):
- return self.get_stat_from_file(self.get_file(filename))
-
- def get_contref(self, filename):
- return self.get_string_from_file(self.get_file(filename),
- obnam.cmp.CONTREF)
-
- def get_sigref(self, filename):
- return self.get_string_from_file(self.get_file(filename),
- obnam.cmp.SIGREF)
-
- def get_deltaref(self, filename):
- return self.get_string_from_file(self.get_file(filename),
- obnam.cmp.DELTAREF)
-
-
-class FileListObject(StorageObject):
-
- kind = FILELIST
-
-
-class FilePartObject(StorageObject):
-
- kind = FILEPART
-
-
-class FileContentsObject(StorageObject):
-
- kind = FILECONTENTS
-
-
-class DeltaPartObject(StorageObject):
-
- kind = DELTAPART
-
-
-class UnknownStorageObjectKind(obnam.ObnamException):
-
- def __init__(self, kind):
- self._msg = "Unknown storage object kind %s" % kind
-
-
-class StorageObjectFactory:
-
- """Create the right kind of Object subclass instance.
-
- Given a parsed component representing an object, figure out the type
- of the object and instantiate the right sub-class of Object.
-
- """
-
- def __init__(self):
- self._classes = []
- for n, klass in globals().iteritems():
- if (type(klass) is type(StorageObject) and
- klass != StorageObject and
- issubclass(klass, StorageObject)):
- self._classes.append(klass)
-
- def get_object(self, components):
- kind = obnam.cmp.first_varint_by_kind(components, obnam.cmp.OBJKIND)
- for klass in self._classes:
- if klass.kind == kind:
- return klass(components=components)
- raise UnknownStorageObjectKind(kind)