~didrocks/ubuntuone-client/use_result_var

« back to all changes in this revision

Viewing changes to ubuntuone/u1sync/metadata.py

  • Committer: Bazaar Package Importer
  • Author(s): Rodney Dawes
  • Date: 2011-02-11 16:18:11 UTC
  • mto: This revision was merged to the branch mainline in revision 67.
  • Revision ID: james.westby@ubuntu.com-20110211161811-n18dj9lde7dxqjzr
Tags: upstream-1.5.4
ImportĀ upstreamĀ versionĀ 1.5.4

Show diffs side-by-side

added added

removed removed

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