From e9efce579d2e07360fd98393ced2206fdcf5d688 Mon Sep 17 00:00:00 2001 From: Aleksey Lim Date: Wed, 15 Aug 2012 21:33:03 +0000 Subject: Mount.mounted is an Event to simplify waiting for connection; handy volume_commands to install scripts --- diff --git a/sugar-network-service b/sugar-network-service index 5df3b53..ce425b3 100755 --- a/sugar-network-service +++ b/sugar-network-service @@ -16,6 +16,9 @@ # along with this program. If not, see . import os +import sys +import json +import shlex import signal import locale import logging @@ -88,21 +91,12 @@ class Application(application.Application): coroutine.signal(signal.SIGCHLD, self.__SIGCHLD_cb) @application.command( - '[PREFIX-PATH [CONTEXT-GUID ..]]\n' - 'Index local Sugar Network database; if CONTEXT-GUIDs ' - 'were specified, make these context favorited') + 'index local Sugar Network database') def index(self): if self.check_for_instance(): printf.info('%s already started, no need in index', self.name) return - root = '' - if self.args: - root = abspath(self.args.pop(0)) - local.activity_dirs.value = [root + abspath(i) \ - for i in local.activity_dirs.value] - local.local_root.value = root + local.local_root.value - if not exists(sugar.profile_path('owner.key')): # Command was launched in foreign environment sugar.uid = lambda: 'index' @@ -113,25 +107,16 @@ class Application(application.Application): volume = Volume(self._db_path) try: volume.populate() - activities.populate(volume['context'], local.activity_dirs.value, - root) - - directory = volume['context'] - while self.args: - context = self.args.pop(0) - if directory.exists(context): - directory.update(context, keep=True) - else: - printf.info('Cannot find %r context', context) + activities.populate(volume['context'], local.activity_dirs.value) finally: volume.close() @application.command( - '[PATH]\n' - 'start sneakernet synchronization; if PATH is specified, ' - 'use it as a synchronization directory; otherwise, ' - 'look for mounts (in --mounts-root) that contain ' - 'sugar-network-sync/ subdirectory') + 'start sneakernet synchronization; if PATH is specified, ' + 'use it as a synchronization directory; otherwise, ' + 'look for mounts (in --mounts-root) that contain ' + 'sugar-network-sync/ subdirectory', + args='[PATH]') def offline_sync(self): with self._rendezvous(): path = None @@ -140,6 +125,19 @@ class Application(application.Application): Client.call('POST', cmd='start_sync', rewind=True, path=path) self._events['sync_complete'].wait() + @application.command(hidden=True) + def POST(self): + self._call('POST', sys.stdin.read()) + + @application.command(hidden=True) + def PUT(self): + self._call('PUT', sys.stdin.read()) + + @application.command(hidden=True) + def GET(self): + result = self._call('GET', None) + print json.dumps(result, indent=2) + @application.command( 'start service and log to standard output') def debug(self): @@ -245,6 +243,22 @@ class Application(application.Application): if server is not None: server.kill() + def _call(self, method, content=None): + kwargs = {} + for arg in self.args: + pair = shlex.split(arg) + if not pair: + continue + pair = pair[0] + enforce('=' in pair, 'No "=" assign symbol in %r expression', arg) + arg, value = pair.split('=', 1) + arg = arg.strip() + enforce(arg, 'No argument name in %r expression', arg) + kwargs[arg] = value + + with self._rendezvous(): + return Client.call(method, content=content, **kwargs) + @property def _db_path(self): return join(local.local_root.value, 'local') diff --git a/sugar_network/local/activities.py b/sugar_network/local/activities.py index 7d67e66..20dcf9c 100644 --- a/sugar_network/local/activities.py +++ b/sugar_network/local/activities.py @@ -78,7 +78,7 @@ def monitor(contexts, paths): inotify.serve_forever() -def populate(contexts, paths, prefix): +def populate(contexts, paths): inotify = _Inotify(contexts) inotify.add_watch = lambda *args: None inotify.setup(paths) diff --git a/sugar_network/local/mounts.py b/sugar_network/local/mounts.py index 8318627..747d3fa 100644 --- a/sugar_network/local/mounts.py +++ b/sugar_network/local/mounts.py @@ -44,7 +44,7 @@ class _Mount(object): def __init__(self): self.mountpoint = None self.publisher = None - self._mounted = False + self.mounted = coroutine.Event() @property def name(self): @@ -54,14 +54,13 @@ class _Mount(object): def private(self): return type(self) in (LocalMount, HomeMount) - @property - def mounted(self): - return self._mounted - def set_mounted(self, value): - if self._mounted == value: + if self.mounted.is_set() == value: return - self._mounted = value + if value: + self.mounted.set() + else: + self.mounted.clear() self.publish({ 'event': 'mount' if value else 'unmount', 'mountpoint': self.mountpoint, @@ -331,7 +330,7 @@ class RemoteMount(ad.CommandsProcessor, _Mount, _ProxyCommands): return self._proxy_call(request, response, super_call) def set_mounted(self, value): - if value != self.mounted: + if value != self.mounted.is_set(): if value: self.mount() else: @@ -361,7 +360,8 @@ class RemoteMount(ad.CommandsProcessor, _Mount, _ProxyCommands): def mount(self, url=None): if url and url not in self._api_urls: self._api_urls.append(url) - if self._api_urls and not self.mounted and not self._connections: + if self._api_urls and not self.mounted.is_set() and \ + not self._connections: self._connections.spawn(self._connect) def _connect(self): diff --git a/sugar_network/local/mountset.py b/sugar_network/local/mountset.py index e9e081e..f81476b 100644 --- a/sugar_network/local/mountset.py +++ b/sugar_network/local/mountset.py @@ -69,7 +69,7 @@ class Mountset(dict, ad.CommandsProcessor, SyncCommands): def mounts(self): result = [] for path, mount in self.items(): - if path == '/' or mount.mounted: + if path == '/' or mount.mounted.is_set(): result.append({ 'mountpoint': path, 'name': mount.name, @@ -84,7 +84,35 @@ class Mountset(dict, ad.CommandsProcessor, SyncCommands): return False if mountpoint == '/': mount.set_mounted(True) - return mount.mounted + return mount.mounted.is_set() + + @ad.volume_command(method='PUT', cmd='checkin') + def checkin(self, mountpoint, request): + mount = self.get(mountpoint) + enforce(mount is not None, 'No such mountpoint') + mount.mounted.wait() + + for guid in (request.content or '').split(): + _logger.info('Checkin %r context', guid) + mount.call( + ad.Request(method='PUT', document='context', guid=guid, + accept_language=[self._locale], + content={'keep_impl': 2, 'keep': False}), + ad.Response()) + + @ad.volume_command(method='PUT', cmd='keep') + def keep(self, mountpoint, request): + mount = self.get(mountpoint) + enforce(mount is not None, 'No such mountpoint') + mount.mounted.wait() + + for guid in (request.content or '').split(): + _logger.info('Keep %r context', guid) + mount.call( + ad.Request(method='PUT', document='context', guid=guid, + accept_language=[self._locale], + content={'keep': True}), + ad.Response()) @ad.volume_command(method='POST', cmd='publish') def republish(self, request): @@ -100,11 +128,13 @@ class Mountset(dict, ad.CommandsProcessor, SyncCommands): try: result = ad.CommandsProcessor.call(self, request, response) except ad.CommandNotFound: + enforce('mountpoint' in request, 'No \'mountpoint\' argument') request.pop('mountpoint') mount = self[mountpoint] if mountpoint == '/': mount.set_mounted(True) - enforce(mount.mounted, '%r is not mounted', mountpoint) + enforce(mount.mounted.is_set(), + '%r is not mounted', mountpoint) result = mount.call(request, response) except Exception: util.exception(_logger, 'Failed to call %s on %r', diff --git a/tests/__init__.py b/tests/__init__.py index 6235904..a1a83af 100644 --- a/tests/__init__.py +++ b/tests/__init__.py @@ -242,17 +242,7 @@ class Test(unittest.TestCase): def start_ipc_and_restful_server(self, classes=None, **kwargs): pid = self.fork(self.restful_server, classes) self.start_server(classes) - - if not self.mounts['/'].mounted: - - def wait_connect(event): - if event.get('mountpoint') == '/' and event['event'] == 'mount': - connected.set() - - connected = coroutine.Event() - self.mounts.connect(wait_connect) - connected.wait() - + self.mounts['/'].mounted.wait() return pid def restful_server(self, classes=None): diff --git a/tests/units/mountset.py b/tests/units/mountset.py index 7fddceb..9188509 100755 --- a/tests/units/mountset.py +++ b/tests/units/mountset.py @@ -188,7 +188,7 @@ class MountsetTest(tests.Test): mountset['/'] = RemoteMount(mountset.home_volume) self.assertEqual(['/'], [i['mountpoint'] for i in mountset.mounts()]) - assert not mountset['/'].mounted + assert not mountset['/'].mounted.is_set() self.assertEqual(0, client.Context.cursor().total) self.assertRaises(RuntimeError, client.Context(type='activity', title='', summary='', description='').post) @@ -199,7 +199,7 @@ class MountsetTest(tests.Test): self.assertRaises(RuntimeError, client.Context(type='activity', title='', summary='', description='').post) self.mounted.wait() self.mounted.clear() - assert mountset['/'].mounted + assert mountset['/'].mounted.is_set() self.assertEqual([('mount', '/')], self.mount_events) del self.mount_events[:] @@ -217,7 +217,7 @@ class MountsetTest(tests.Test): self.mounted.wait() self.mounted.clear() - assert not mountset['/'].mounted + assert not mountset['/'].mounted.is_set() self.assertEqual([('unmount', '/')], self.mount_events) del self.mount_events[:] self.assertEqual(0, client.Context.cursor().total) @@ -230,7 +230,7 @@ class MountsetTest(tests.Test): self.assertRaises(RuntimeError, client.Context(type='activity', title='', summary='', description='').post) self.mounted.wait() self.mounted.clear() - assert mountset['/'].mounted + assert mountset['/'].mounted.is_set() self.assertEqual([('mount', '/')], self.mount_events) del self.mount_events[:] -- cgit v0.9.1