Revision 99d0e277

b/tools/pithos-sync
36 36
import os
37 37
import sqlite3
38 38
import sys
39
import shutil
39 40

  
40 41
from lib import transfer
41 42
from lib.client import Pithos_Client, Fault
......
52 53
        return DEFAULT_CONTAINER
53 54

  
54 55

  
55
SQL_CREATE_TABLE = '''CREATE TABLE IF NOT EXISTS files (
56
                        path TEXT PRIMARY KEY, hash TEXT)'''
56
def create_dir(path):
57
    if not os.path.exists(path):
58
        os.makedirs(path)
59
    if not os.path.isdir(path):
60
        raise RuntimeError("Cannot open '%s'" % (path,))
61

  
57 62

  
58 63
client = None
64
lpath = None
65
trash = None
59 66
lstate = None
60 67
cstate = None
61 68
rstate = None
62 69

  
70
class Trash(object):
71
    def __init__(self):
72
        self.tpath = os.path.join(lpath, 'trash')
73
        create_dir(self.tpath)
74
        
75
        dbpath = os.path.join(lpath, 'trash.db')
76
        self.conn = sqlite3.connect(dbpath)
77
        sql = '''CREATE TABLE IF NOT EXISTS files (
78
                    path TEXT PRIMARY KEY, hash TEXT)'''
79
        self.conn.execute(sql)
80
        self.conn.commit()
81
    
82
    def put(self, path, hash):
83
        # XXX Maintain path.
84
        shutil.copy(path, basename(path))
85
        sql = 'INSERT OR REPLACE INTO files VALUES (?, ?)'
86
        self.conn.execute(sql, (basename(path), hash))
87
        self.conn.commit()
88
    
89
    def search(self, hash):
90
        sql = 'SELECT path FROM files WHERE hash = ?'
91
        ret = self.conn.execute(sql, (hash,)).fetchone()
92
        return ret[0] if ret else None
93
    
94
    def empty(self):
95
        sql = 'DELETE FROM files'
96
        self.conn.execute(sql)
97
        self.conn.commit()
98
        shutil.rmtree(self.tpath)
99

  
100

  
101
class CurrentState(object):
102
    def __init__(self, dir):
103
        self.dir = dir
104
    
105
    def get(self, path):
106
        fullpath = os.path.join(self.dir, path)
107
        if os.path.exists(fullpath):
108
            if os.path.isdir(fullpath):
109
                return 'DIR'
110
            else:
111
                return merkle(fullpath)
112
        else:
113
            return 'DEL'
114
    
115
    def fullpath(self, path):
116
        return os.path.join(self.dir, path)
117

  
63 118

  
64 119
class LocalState(object):
65 120
    def __init__(self):
66
        dbpath = os.path.expanduser('~/.psyncdb')
121
        dbpath = os.path.join(lpath, 'state.db')
67 122
        self.conn = sqlite3.connect(dbpath)
68
        self.conn.execute(SQL_CREATE_TABLE)
123
        sql = '''CREATE TABLE IF NOT EXISTS files (
124
                    path TEXT PRIMARY KEY, hash TEXT)'''
125
        self.conn.execute(sql)
69 126
        self.conn.commit()
70 127
    
71 128
    def get(self, path):
......
78 135
        self.conn.execute(sql, (path, hash))
79 136
        self.conn.commit()
80 137

  
138
    def search(self, hash):
139
        sql = 'SELECT path FROM files WHERE hash = ?'
140
        ret = self.conn.execute(sql, (hash,)).fetchone()
141
        return ret[0] if ret else None
142

  
81 143

  
82 144
class CurrentState(object):
83 145
    def __init__(self, dir):
......
113 175
            return meta['etag']
114 176

  
115 177

  
116
def download(path, S):
178
def update_local(path, S):
117 179
    fullpath = cstate.fullpath(path)
118 180
    if S == 'DEL':
119 181
        os.remove(fullpath)
......
122 184
            os.remove(fullpath)
123 185
        os.mkdir(fullpath)
124 186
    else:
187
        # XXX Covert paths.
188
        # file = lstate.search(S)
189
        # if file:
190
        #     shutil.copy(file, fullpath)
191
        # else:
192
        #     transfer.download(client, get_container(), path, fullpath)
125 193
        transfer.download(client, get_container(), path, fullpath)
194
        # XXX Check either MD5 or Merkle hashes.
126 195
        assert cstate.get(path) == S
127 196

  
128 197

  
129
def upload(path, S):
198
def update_remote(path, S):
130 199
    fullpath = cstate.fullpath(path)
131 200
    if S == 'DEL':
132 201
        client.delete_object(get_container(), path)
......
154 223
    if C == L:
155 224
        # No local changes
156 225
        if R != L:
157
            download(path, R)
226
            update_local(path, R)
158 227
            lstate.put(path, R)
159 228
        return
160 229
    
161 230
    if R == L:
162 231
        # No remote changes
163 232
        if C != L:
164
            upload(path, C)
233
            update_remote(path, C)
165 234
            lstate.put(path, C)
166 235
        return
167 236
    
......
173 242
    else:
174 243
        # Conflict, try to resolve it
175 244
        resolve_conflict(path)
176
        download(path, R)
245
        update_local(path, R)
177 246
        lstate.put(path, R)
178 247

  
179 248

  
......
212 281

  
213 282

  
214 283
def main():
215
    global client, lstate, cstate, rstate
284
    global client, lpath, trash, lstate, cstate, rstate
216 285
    
217 286
    if len(sys.argv) != 2:
218 287
        print 'syntax: %s <dir>' % sys.argv[0]
......
221 290
    dir = sys.argv[1]
222 291
    client = Pithos_Client(get_server(), get_auth(), get_user())
223 292
    
293
    lpath = os.path.expanduser('~/.pithos-sync/')
294
    create_dir(lpath)
295
    
296
    trash = Trash()
224 297
    lstate = LocalState()
225 298
    cstate = CurrentState(dir)
226 299
    rstate = RemoteState(client)

Also available in: Unified diff