~nataliabidart/magicicada-server/embed-u1sync

« back to all changes in this revision

Viewing changes to lib/u1sync/metadata.py

  • Committer: Natalia
  • Date: 2018-04-20 01:17:24 UTC
  • Revision ID: natalia.bidart@ubuntu.com-20180420011724-17kqadnmz6zeql3j
Added u1sync inside magicicada folder.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# Copyright 2009 Canonical Ltd.
2
 
# Copyright 2015-2018 Chicharreros (https://launchpad.net/~chicharreros)
3
 
#
4
 
# This program is free software: you can redistribute it and/or modify it
5
 
# under the terms of the GNU General Public License version 3, as published
6
 
# by the Free Software Foundation.
7
 
#
8
 
# This program is distributed in the hope that it will be useful, but
9
 
# WITHOUT ANY WARRANTY; without even the implied warranties of
10
 
# MERCHANTABILITY, SATISFACTORY QUALITY, or FITNESS FOR A PARTICULAR
11
 
# PURPOSE.  See the GNU General Public License for more details.
12
 
#
13
 
# You should have received a copy of the GNU General Public License along
14
 
# with this program.  If not, see <http://www.gnu.org/licenses/>.
15
 
 
16
 
"""Routines for loading/storing u1sync mirror metadata."""
17
 
 
18
 
from __future__ import with_statement
19
 
 
20
 
import os
21
 
import uuid
22
 
 
23
 
from contextlib import contextmanager
24
 
import cPickle as pickle
25
 
from errno import ENOENT
26
 
 
27
 
from ubuntuone.storageprotocol.dircontent_pb2 import DIRECTORY
28
 
 
29
 
from u1sync.merge import MergeNode
30
 
from u1sync.utils import safe_unlink
31
 
 
32
 
 
33
 
class Metadata(object):
34
 
    """Object representing mirror metadata."""
35
 
 
36
 
    def __init__(self, local_tree=None, remote_tree=None, share_uuid=None,
37
 
                 root_uuid=None, path=None):
38
 
        """Populate fields."""
39
 
        self.local_tree = local_tree
40
 
        self.remote_tree = remote_tree
41
 
        self.share_uuid = share_uuid
42
 
        self.root_uuid = root_uuid
43
 
        self.path = path
44
 
 
45
 
 
46
 
def read(metadata_dir):
47
 
    """Read metadata for a mirror rooted at directory."""
48
 
    index_file = os.path.join(metadata_dir, "local-index")
49
 
    share_uuid_file = os.path.join(metadata_dir, "share-uuid")
50
 
    root_uuid_file = os.path.join(metadata_dir, "root-uuid")
51
 
    path_file = os.path.join(metadata_dir, "path")
52
 
 
53
 
    index = read_pickle_file(index_file, {})
54
 
    share_uuid = read_uuid_file(share_uuid_file)
55
 
    root_uuid = read_uuid_file(root_uuid_file)
56
 
    path = read_string_file(path_file, '/')
57
 
 
58
 
    local_tree = index.get("tree", None)
59
 
    remote_tree = index.get("remote_tree", None)
60
 
 
61
 
    if local_tree is None:
62
 
        local_tree = MergeNode(node_type=DIRECTORY, children={})
63
 
    if remote_tree is None:
64
 
        remote_tree = MergeNode(node_type=DIRECTORY, children={})
65
 
 
66
 
    return Metadata(local_tree=local_tree, remote_tree=remote_tree,
67
 
                    share_uuid=share_uuid, root_uuid=root_uuid,
68
 
                    path=path)
69
 
 
70
 
 
71
 
def write(metadata_dir, info):
72
 
    """Writes all metadata for the mirror rooted at directory."""
73
 
    share_uuid_file = os.path.join(metadata_dir, "share-uuid")
74
 
    root_uuid_file = os.path.join(metadata_dir, "root-uuid")
75
 
    index_file = os.path.join(metadata_dir, "local-index")
76
 
    path_file = os.path.join(metadata_dir, "path")
77
 
    if info.share_uuid is not None:
78
 
        write_uuid_file(share_uuid_file, info.share_uuid)
79
 
    else:
80
 
        safe_unlink(share_uuid_file)
81
 
    if info.root_uuid is not None:
82
 
        write_uuid_file(root_uuid_file, info.root_uuid)
83
 
    else:
84
 
        safe_unlink(root_uuid_file)
85
 
    write_string_file(path_file, info.path)
86
 
    write_pickle_file(index_file, {"tree": info.local_tree,
87
 
                                   "remote_tree": info.remote_tree})
88
 
 
89
 
 
90
 
def write_pickle_file(filename, value):
91
 
    """Writes a pickled python object to a file."""
92
 
    with atomic_update_file(filename) as stream:
93
 
        pickle.dump(value, stream, 2)
94
 
 
95
 
 
96
 
def write_string_file(filename, value):
97
 
    """Writes a string to a file with an added line feed, or
98
 
    deletes the file if value is None.
99
 
    """
100
 
    if value is not None:
101
 
        with atomic_update_file(filename) as stream:
102
 
            stream.write(value)
103
 
            stream.write('\n')
104
 
    else:
105
 
        safe_unlink(filename)
106
 
 
107
 
 
108
 
def write_uuid_file(filename, value):
109
 
    """Writes a UUID to a file."""
110
 
    write_string_file(filename, str(value))
111
 
 
112
 
 
113
 
def read_pickle_file(filename, default_value=None):
114
 
    """Reads a pickled python object from a file."""
115
 
    try:
116
 
        with open(filename, "rb") as stream:
117
 
            return pickle.load(stream)
118
 
    except IOError as e:
119
 
        if e.errno != ENOENT:
120
 
            raise
121
 
        return default_value
122
 
 
123
 
 
124
 
def read_string_file(filename, default_value=None):
125
 
    """Reads a string from a file, discarding the final character."""
126
 
    try:
127
 
        with open(filename, "r") as stream:
128
 
            return stream.read()[:-1]
129
 
    except IOError as e:
130
 
        if e.errno != ENOENT:
131
 
            raise
132
 
        return default_value
133
 
 
134
 
 
135
 
def read_uuid_file(filename, default_value=None):
136
 
    """Reads a UUID from a file."""
137
 
    try:
138
 
        with open(filename, "r") as stream:
139
 
            return uuid.UUID(stream.read()[:-1])
140
 
    except IOError as e:
141
 
        if e.errno != ENOENT:
142
 
            raise
143
 
        return default_value
144
 
 
145
 
 
146
 
@contextmanager
147
 
def atomic_update_file(filename):
148
 
    """Returns a context manager for atomically updating a file."""
149
 
    temp_filename = "%s.%s" % (filename, uuid.uuid4())
150
 
    try:
151
 
        with open(temp_filename, "w") as stream:
152
 
            yield stream
153
 
        os.rename(temp_filename, filename)
154
 
    finally:
155
 
        safe_unlink(temp_filename)