Merge branch 'master' of https://github.com/tahoe-lafs/tahoe-lafs into 1432.osx-watchdog-stable.5
This commit is contained in:
commit
6c307f68a0
1
.gitignore
vendored
1
.gitignore
vendored
@ -9,7 +9,6 @@
|
|||||||
|
|
||||||
# these are generated at build time, and never checked in
|
# these are generated at build time, and never checked in
|
||||||
/src/allmydata/_version.py
|
/src/allmydata/_version.py
|
||||||
/src/allmydata/_appname.py
|
|
||||||
|
|
||||||
# these are generated too
|
# these are generated too
|
||||||
/bin/tahoe
|
/bin/tahoe
|
||||||
|
19
setup.py
19
setup.py
@ -30,19 +30,6 @@ def read_version_py(infname):
|
|||||||
VERSION_PY_FILENAME = 'src/allmydata/_version.py'
|
VERSION_PY_FILENAME = 'src/allmydata/_version.py'
|
||||||
version = read_version_py(VERSION_PY_FILENAME)
|
version = read_version_py(VERSION_PY_FILENAME)
|
||||||
|
|
||||||
APPNAME='tahoe-lafs'
|
|
||||||
APPNAMEFILE = os.path.join('src', 'allmydata', '_appname.py')
|
|
||||||
APPNAMEFILESTR = "__appname__ = '%s'" % (APPNAME,)
|
|
||||||
try:
|
|
||||||
curappnamefilestr = open(APPNAMEFILE, 'rU').read()
|
|
||||||
except EnvironmentError:
|
|
||||||
# No file, or unreadable or something, okay then let's try to write one.
|
|
||||||
open(APPNAMEFILE, "w").write(APPNAMEFILESTR)
|
|
||||||
else:
|
|
||||||
if curappnamefilestr.strip() != APPNAMEFILESTR:
|
|
||||||
print("Error -- this setup.py file is configured with the 'application name' to be '%s', but there is already a file in place in '%s' which contains the contents '%s'. If the file is wrong, please remove it and setup.py will regenerate it and write '%s' into it." % (APPNAME, APPNAMEFILE, curappnamefilestr, APPNAMEFILESTR))
|
|
||||||
sys.exit(-1)
|
|
||||||
|
|
||||||
# Tahoe's dependencies are managed by the find_links= entry in setup.cfg and
|
# Tahoe's dependencies are managed by the find_links= entry in setup.cfg and
|
||||||
# the _auto_deps.install_requires list, which is used in the call to setup()
|
# the _auto_deps.install_requires list, which is used in the call to setup()
|
||||||
# below.
|
# below.
|
||||||
@ -214,8 +201,8 @@ Warning: no version information found. This may cause tests to fail.
|
|||||||
""")
|
""")
|
||||||
|
|
||||||
def try_from_git(self):
|
def try_from_git(self):
|
||||||
# If we change APPNAME, the release tag names should also change from then on.
|
# If we change the release tag names, we must change this too
|
||||||
versions = versions_from_git(APPNAME + '-')
|
versions = versions_from_git("tahoe-lafs-")
|
||||||
|
|
||||||
# setup.py might be run by either py2 or py3 (when run by tox, which
|
# setup.py might be run by either py2 or py3 (when run by tox, which
|
||||||
# uses py3 on modern debian/ubuntu distros). We want this generated
|
# uses py3 on modern debian/ubuntu distros). We want this generated
|
||||||
@ -241,7 +228,7 @@ setup_args = {}
|
|||||||
if version:
|
if version:
|
||||||
setup_args["version"] = version
|
setup_args["version"] = version
|
||||||
|
|
||||||
setup(name=APPNAME,
|
setup(name="tahoe-lafs", # also set in __init__.py
|
||||||
description='secure, decentralized, fault-tolerant file store',
|
description='secure, decentralized, fault-tolerant file store',
|
||||||
long_description=open('README.rst', 'rU').read(),
|
long_description=open('README.rst', 'rU').read(),
|
||||||
author='the Tahoe-LAFS project',
|
author='the Tahoe-LAFS project',
|
||||||
|
@ -30,15 +30,10 @@ except ImportError:
|
|||||||
# branch is. This should not happen very often.
|
# branch is. This should not happen very often.
|
||||||
pass
|
pass
|
||||||
|
|
||||||
__appname__ = "unknown"
|
__appname__ = "tahoe-lafs"
|
||||||
try:
|
|
||||||
from allmydata._appname import __appname__
|
|
||||||
except ImportError:
|
|
||||||
# We're running in a tree that hasn't run "./setup.py". This shouldn't happen.
|
|
||||||
pass
|
|
||||||
|
|
||||||
# __full_version__ is the one that you ought to use when identifying yourself in the
|
# __full_version__ is the one that you ought to use when identifying yourself
|
||||||
# "application" part of the Tahoe versioning scheme:
|
# in the "application" part of the Tahoe versioning scheme:
|
||||||
# https://tahoe-lafs.org/trac/tahoe-lafs/wiki/Versioning
|
# https://tahoe-lafs.org/trac/tahoe-lafs/wiki/Versioning
|
||||||
__full_version__ = __appname__ + '/' + str(__version__)
|
__full_version__ = __appname__ + '/' + str(__version__)
|
||||||
|
|
||||||
|
@ -124,6 +124,7 @@ class Client(node.Node, pollmixin.PollMixin):
|
|||||||
node.Node.__init__(self, basedir)
|
node.Node.__init__(self, basedir)
|
||||||
# All tub.registerReference must happen *after* we upcall, since
|
# All tub.registerReference must happen *after* we upcall, since
|
||||||
# that's what does tub.setLocation()
|
# that's what does tub.setLocation()
|
||||||
|
self._magic_folder = None
|
||||||
self.started_timestamp = time.time()
|
self.started_timestamp = time.time()
|
||||||
self.logSource="Client"
|
self.logSource="Client"
|
||||||
self.encoding_params = self.DEFAULT_ENCODING_PARAMETERS.copy()
|
self.encoding_params = self.DEFAULT_ENCODING_PARAMETERS.copy()
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
import sys, os
|
import sys, os
|
||||||
import os.path
|
import os.path
|
||||||
from collections import deque
|
from collections import deque
|
||||||
|
from datetime import datetime
|
||||||
import time
|
import time
|
||||||
|
|
||||||
from twisted.internet import defer, reactor, task
|
from twisted.internet import defer, reactor, task
|
||||||
@ -22,6 +23,7 @@ from allmydata.util.progress import PercentProgress
|
|||||||
from allmydata.util.encodingutil import listdir_filepath, to_filepath, \
|
from allmydata.util.encodingutil import listdir_filepath, to_filepath, \
|
||||||
extend_filepath, unicode_from_filepath, unicode_segments_from, \
|
extend_filepath, unicode_from_filepath, unicode_segments_from, \
|
||||||
quote_filepath, quote_local_unicode_path, quote_output, FilenameEncodingError
|
quote_filepath, quote_local_unicode_path, quote_output, FilenameEncodingError
|
||||||
|
from allmydata.util.time_format import format_time
|
||||||
from allmydata.immutable.upload import FileName, Data
|
from allmydata.immutable.upload import FileName, Data
|
||||||
from allmydata import magicfolderdb, magicpath
|
from allmydata import magicfolderdb, magicpath
|
||||||
|
|
||||||
@ -82,7 +84,18 @@ class MagicFolder(service.MultiService):
|
|||||||
|
|
||||||
self.uploader = Uploader(client, local_path_u, db, upload_dirnode, pending_delay, clock)
|
self.uploader = Uploader(client, local_path_u, db, upload_dirnode, pending_delay, clock)
|
||||||
self.downloader = Downloader(client, local_path_u, db, collective_dirnode,
|
self.downloader = Downloader(client, local_path_u, db, collective_dirnode,
|
||||||
upload_dirnode.get_readonly_uri(), clock, self.uploader.is_pending, umask)
|
upload_dirnode.get_readonly_uri(), clock, self.uploader.is_pending, umask,
|
||||||
|
self.set_public_status)
|
||||||
|
self._public_status = (False, ['Magic folder has not yet started'])
|
||||||
|
|
||||||
|
def get_public_status(self):
|
||||||
|
"""
|
||||||
|
For the web UI, basically.
|
||||||
|
"""
|
||||||
|
return self._public_status
|
||||||
|
|
||||||
|
def set_public_status(self, status, *messages):
|
||||||
|
self._public_status = (status, messages)
|
||||||
|
|
||||||
def startService(self):
|
def startService(self):
|
||||||
# TODO: why is this being called more than once?
|
# TODO: why is this being called more than once?
|
||||||
@ -719,7 +732,8 @@ class Downloader(QueueMixin, WriteFileMixin):
|
|||||||
scan_interval = 3
|
scan_interval = 3
|
||||||
|
|
||||||
def __init__(self, client, local_path_u, db, collective_dirnode,
|
def __init__(self, client, local_path_u, db, collective_dirnode,
|
||||||
upload_readonly_dircap, clock, is_upload_pending, umask):
|
upload_readonly_dircap, clock, is_upload_pending, umask,
|
||||||
|
status_reporter):
|
||||||
QueueMixin.__init__(self, client, local_path_u, db, 'downloader', clock, delay=self.scan_interval)
|
QueueMixin.__init__(self, client, local_path_u, db, 'downloader', clock, delay=self.scan_interval)
|
||||||
|
|
||||||
if not IDirectoryNode.providedBy(collective_dirnode):
|
if not IDirectoryNode.providedBy(collective_dirnode):
|
||||||
@ -733,6 +747,7 @@ class Downloader(QueueMixin, WriteFileMixin):
|
|||||||
self._upload_readonly_dircap = upload_readonly_dircap
|
self._upload_readonly_dircap = upload_readonly_dircap
|
||||||
self._is_upload_pending = is_upload_pending
|
self._is_upload_pending = is_upload_pending
|
||||||
self._umask = umask
|
self._umask = umask
|
||||||
|
self._status_reporter = status_reporter
|
||||||
|
|
||||||
@defer.inlineCallbacks
|
@defer.inlineCallbacks
|
||||||
def start_downloading(self):
|
def start_downloading(self):
|
||||||
@ -750,9 +765,16 @@ class Downloader(QueueMixin, WriteFileMixin):
|
|||||||
break
|
break
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
|
self._status_reporter(
|
||||||
|
False, "Initial scan has failed",
|
||||||
|
"Last tried at %s" % self.nice_current_time(),
|
||||||
|
)
|
||||||
twlog.msg("Magic Folder failed initial scan: %s" % (e,))
|
twlog.msg("Magic Folder failed initial scan: %s" % (e,))
|
||||||
yield task.deferLater(self._clock, self.scan_interval, lambda: None)
|
yield task.deferLater(self._clock, self.scan_interval, lambda: None)
|
||||||
|
|
||||||
|
def nice_current_time(self):
|
||||||
|
return format_time(datetime.fromtimestamp(self._clock.seconds()).timetuple())
|
||||||
|
|
||||||
def stop(self):
|
def stop(self):
|
||||||
self._log("stop")
|
self._log("stop")
|
||||||
self._stopped = True
|
self._stopped = True
|
||||||
@ -839,6 +861,10 @@ class Downloader(QueueMixin, WriteFileMixin):
|
|||||||
scan_batch[relpath_u] += [(file_node, metadata)]
|
scan_batch[relpath_u] += [(file_node, metadata)]
|
||||||
else:
|
else:
|
||||||
scan_batch[relpath_u] = [(file_node, metadata)]
|
scan_batch[relpath_u] = [(file_node, metadata)]
|
||||||
|
self._status_reporter(
|
||||||
|
True, 'Magic folder is working',
|
||||||
|
'Last scan: %s' % self.nice_current_time(),
|
||||||
|
)
|
||||||
|
|
||||||
d.addCallback(scan_listing)
|
d.addCallback(scan_listing)
|
||||||
d.addBoth(self._logcb, "end of _scan_remote_dmd")
|
d.addBoth(self._logcb, "end of _scan_remote_dmd")
|
||||||
@ -894,9 +920,17 @@ class Downloader(QueueMixin, WriteFileMixin):
|
|||||||
x = None
|
x = None
|
||||||
try:
|
try:
|
||||||
x = yield self._scan(None)
|
x = yield self._scan(None)
|
||||||
|
self._status_reporter(
|
||||||
|
True, 'Magic folder is working',
|
||||||
|
'Last scan: %s' % self.nice_current_time(),
|
||||||
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
twlog.msg("Remote scan failed: %s" % (e,))
|
twlog.msg("Remote scan failed: %s" % (e,))
|
||||||
self._log("_scan failed: %s" % (repr(e),))
|
self._log("_scan failed: %s" % (repr(e),))
|
||||||
|
self._status_reporter(
|
||||||
|
False, 'Remote scan has failed: %s' % str(e),
|
||||||
|
'Last attempted at %s' % self.nice_current_time(),
|
||||||
|
)
|
||||||
defer.returnValue(x)
|
defer.returnValue(x)
|
||||||
|
|
||||||
def _scan(self, ign):
|
def _scan(self, ign):
|
||||||
|
@ -14,7 +14,7 @@ from allmydata.scripts import runner
|
|||||||
from allmydata.client import Client
|
from allmydata.client import Client
|
||||||
from allmydata.test import common_util
|
from allmydata.test import common_util
|
||||||
import allmydata
|
import allmydata
|
||||||
from allmydata._appname import __appname__
|
from allmydata import __appname__
|
||||||
|
|
||||||
|
|
||||||
timeout = 240
|
timeout = 240
|
||||||
|
@ -224,6 +224,7 @@ class FakeClient(Client):
|
|||||||
# don't upcall to Client.__init__, since we only want to initialize a
|
# don't upcall to Client.__init__, since we only want to initialize a
|
||||||
# minimal subset
|
# minimal subset
|
||||||
service.MultiService.__init__(self)
|
service.MultiService.__init__(self)
|
||||||
|
self._magic_folder = None
|
||||||
self.all_contents = {}
|
self.all_contents = {}
|
||||||
self.nodeid = "fake_nodeid"
|
self.nodeid = "fake_nodeid"
|
||||||
self.nickname = u"fake_nickname \u263A"
|
self.nickname = u"fake_nickname \u263A"
|
||||||
|
@ -16,31 +16,26 @@ class MagicFolderWebApi(TokenOnlyWebApi):
|
|||||||
req.setHeader("content-type", "application/json")
|
req.setHeader("content-type", "application/json")
|
||||||
|
|
||||||
data = []
|
data = []
|
||||||
try:
|
for item in self.client._magic_folder.uploader.get_status():
|
||||||
for item in self.client._magic_folder.uploader.get_status():
|
d = dict(
|
||||||
d = dict(
|
path=item.relpath_u,
|
||||||
path=item.relpath_u,
|
status=item.status_history()[-1][0],
|
||||||
status=item.status_history()[-1][0],
|
kind='upload',
|
||||||
kind='upload',
|
)
|
||||||
)
|
for (status, ts) in item.status_history():
|
||||||
for (status, ts) in item.status_history():
|
d[status + '_at'] = ts
|
||||||
d[status + '_at'] = ts
|
d['percent_done'] = item.progress.progress
|
||||||
d['percent_done'] = item.progress.progress
|
data.append(d)
|
||||||
data.append(d)
|
|
||||||
|
|
||||||
for item in self.client._magic_folder.downloader.get_status():
|
for item in self.client._magic_folder.downloader.get_status():
|
||||||
d = dict(
|
d = dict(
|
||||||
path=item.relpath_u,
|
path=item.relpath_u,
|
||||||
status=item.status_history()[-1][0],
|
status=item.status_history()[-1][0],
|
||||||
kind='download',
|
kind='download',
|
||||||
)
|
)
|
||||||
for (status, ts) in item.status_history():
|
for (status, ts) in item.status_history():
|
||||||
d[status + '_at'] = ts
|
d[status + '_at'] = ts
|
||||||
d['percent_done'] = item.progress.progress
|
d['percent_done'] = item.progress.progress
|
||||||
data.append(d)
|
data.append(d)
|
||||||
except Exception as e:
|
|
||||||
data.append({
|
|
||||||
"error": str(e),
|
|
||||||
})
|
|
||||||
|
|
||||||
return simplejson.dumps(data)
|
return simplejson.dumps(data)
|
||||||
|
@ -186,6 +186,25 @@ class Root(rend.Page):
|
|||||||
def data_my_nickname(self, ctx, data):
|
def data_my_nickname(self, ctx, data):
|
||||||
return self.client.nickname
|
return self.client.nickname
|
||||||
|
|
||||||
|
def render_magic_folder(self, ctx, data):
|
||||||
|
if self.client._magic_folder is None:
|
||||||
|
return T.p()
|
||||||
|
|
||||||
|
(ok, messages) = self.client._magic_folder.get_public_status()
|
||||||
|
|
||||||
|
if ok:
|
||||||
|
ctx.fillSlots("magic_folder_status", "yes")
|
||||||
|
ctx.fillSlots("magic_folder_status_alt", "working")
|
||||||
|
else:
|
||||||
|
ctx.fillSlots("magic_folder_status", "no")
|
||||||
|
ctx.fillSlots("magic_folder_status_alt", "not working")
|
||||||
|
|
||||||
|
status = T.ul()
|
||||||
|
for msg in messages:
|
||||||
|
status[T.li[str(msg)]]
|
||||||
|
|
||||||
|
return ctx.tag[status]
|
||||||
|
|
||||||
def render_services(self, ctx, data):
|
def render_services(self, ctx, data):
|
||||||
ul = T.ul()
|
ul = T.ul()
|
||||||
try:
|
try:
|
||||||
|
@ -159,6 +159,14 @@
|
|||||||
</div><!--/span-->
|
</div><!--/span-->
|
||||||
</div><!--/row-->
|
</div><!--/row-->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div n:render="magic_folder" class="row-fluid">
|
||||||
|
<h2>
|
||||||
|
<div class="status-indicator"><img><n:attr name="src">img/connected-<n:slot name="magic_folder_status" />.png</n:attr><n:attr name="alt"><n:slot name="magic_folder_status_alt" /></n:attr></img></div>
|
||||||
|
Magic Folder
|
||||||
|
</h2>
|
||||||
|
</div><!--/row-->
|
||||||
|
|
||||||
<div class="row-fluid">
|
<div class="row-fluid">
|
||||||
<h2>
|
<h2>
|
||||||
Connected to <span n:render="string" n:data="connected_storage_servers" />
|
Connected to <span n:render="string" n:data="connected_storage_servers" />
|
||||||
|
Loading…
Reference in New Issue
Block a user