Commit 20400d59 authored by Antoine Millet's avatar Antoine Millet
Browse files

Added file repository manager

parent 1e63683f
Loading
Loading
Loading
Loading
+116 −0
Original line number Diff line number Diff line
""" A repository class used to manage a file repository.
"""

import os
from threading import Lock
from hashlib import sha1

from cloudcontrol.server.db import SObject

from cloudcontrol.common.tql.db.tag import StaticTag


class RepositoryOperationError(Exception):
    """ Error while repository operation.
    """


class Repository(object):

    """ A class abstracting operations on a file repository.
    """

    def __init__(self, logger, server, directory, role='file'):
        self.logger = logger
        self._server = server
        self._directory = directory
        self._lock = Lock()
        self._objects = {}  # Mapping between file names and tql objects
        self._role = role

        # Load TQL objects of already existing files:
        for name in self.list():
            obj_id = self._object_id(name)
            sha1_hash, content = self.load(name)
            tql_object = SObject(obj_id)
            tql_object.register(StaticTag('r', self._role))
            tql_object.register(StaticTag('hash', sha1_hash))
            tql_object.register(StaticTag('name', name))
            tql_object.register(StaticTag('size', len(content)))
            self._objects[name] = tql_object
            self._server.db.register(tql_object)

    def _fullname(self, name):
        """ Return the file fullname depending to its name.
        """
        return os.path.join(self._directory, name)

    def _object_id(self, name):
        """ Return the TQL object id of the file.
        """
        return '%s-%s' % (self._role, name)

    def save(self, name, content):
        """ Save a file to the repository.
        """
        fullname = self._fullname(name)
        self.logger.debug('Saving file %r to %s', name, fullname)
        try:
            with open(fullname, 'w') as ffile:
                ffile.write(content)
        except IOError as err:
            msg = 'Error while saving: %s' % err
            raise RepositoryOperationError(msg)
        else:
            obj_id = self._object_id(name)
            sha1_hash = sha1(content).hexdigest()
            if name in self._objects:
                tql_object = self._objects[name]
                tql_object['hash'].value = sha1_hash
                tql_object['size'].value = len(content)
            else:
                tql_object = SObject(obj_id)
                tql_object.register(StaticTag('r', self._role))
                tql_object.register(StaticTag('hash', sha1_hash))
                tql_object.register(StaticTag('name', name))
                tql_object.register(StaticTag('size', len(content)))
                self._server.db.register(tql_object)
                self._objects[name] = tql_object
            return obj_id

    def load(self, name):
        """ Load a file from the repository and compute its sha1sum.

        :return: a tuple (sha1_hash, content)
        """
        fullname = self._fullname(name)
        try:
            with open(fullname, 'r') as ffile:
                content = ffile.read()
        except IOError as err:
            msg = 'Error while saving: %s' % err
            raise RepositoryOperationError(msg)
        else:
            sha1_hash = sha1(content).hexdigest()
            self.logger.debug('Loaded plugin %r, hash = %s', name, sha1_hash)
            return sha1_hash, content

    def delete(self, name):
        """ Delete a file from the repository.

        :param name: name of the file to delete
        """
        tql_object = self._objects.pop(name)
        if tql_object is None:
            raise RepositoryOperationError('Unknown object')
        self._server.db.unregister(tql_object)
        try:
            os.unlink(self._fullname(name))
        except OSError as err:
            msg = 'Error while deleting: %s' % err
            raise RepositoryOperationError(msg)

    def list(self):
        """ List files of the repository.
        """
        return os.listdir(self._directory)
 No newline at end of file