diff options
| author | Peter Ward <peteraward@gmail.com> | 2014-09-09 22:34:51 +1000 | 
|---|---|---|
| committer | Peter Ward <peteraward@gmail.com> | 2014-09-09 22:34:51 +1000 | 
| commit | 42076870fd819e43cea9ad4ffe39228155c6c80b (patch) | |
| tree | 8aaac2cdd68de0860867975d1698145131cbd966 | |
| parent | 85b84bf04b654b7075f821536dcd25b85aa9cea4 (diff) | |
start work on server and client
| -rw-r--r-- | robots/client.py | 62 | ||||
| -rw-r--r-- | robots/server.py | 135 | ||||
| -rw-r--r-- | simple.py | 2 | 
3 files changed, 198 insertions, 1 deletions
diff --git a/robots/client.py b/robots/client.py new file mode 100644 index 0000000..7d60ba2 --- /dev/null +++ b/robots/client.py @@ -0,0 +1,62 @@ +import os +from threading import Thread +import time +import uuid +import zmq + +import logbook + +import network + +SERVER_KIND = 'robots' + +class Browser(object): +    def __init__(self, client, ctx=None): +        if ctx is None: +            ctx = zmq.Context.instance() + +        self.client = client +        self.running = False +        self.targets = {} +        self.ctx = ctx + +    def update(self, services): +        current_targets = set() + +        for service in services: +            target = service['target'] +            current_targets.add(target) + +            if target in self.targets: +                continue + +            sock = self.ctx.socket(zmq.REQ) +            sock.connect(target) +            sock.send_json({'action': 'list'}) +            response = sock.recv_json() + +            if response['status'] == 200: +                self.targets[target] = { +                    'name': str(service['name']), +                    'bots': response['bots'], +                } + +        for target in list(self.targets.keys()): +            if target not in current_targets: +                del self.targets[target] + +        network.browser.clear_terminal() +        for info in self.targets.values(): +            print(info['name'] + ':') +            for name in info['bots']: +                print(' * ' + name) +            print() + +    def run(self, client_run): +        client_run() + +client = network.Client(SERVER_KIND) +client.find_server( +    browser_cls=Browser, +    connect=False +) diff --git a/robots/server.py b/robots/server.py new file mode 100644 index 0000000..759766c --- /dev/null +++ b/robots/server.py @@ -0,0 +1,135 @@ +from functools import wraps +import json +import os +import uuid + +import logbook + +import network + +log = logbook.Logger(__name__) + +def bad_request(msg): +    return {'status': 400, 'message': msg} + +def not_found(msg): +    return {'status': 404, 'message': msg} + +def server_error(msg): +    return {'status': 500, 'message': msg} + +def success(**kwargs): +    rv = {'status': 200} +    rv.update(kwargs) +    return rv + +def json_handler(handle): +    SERVER_ERROR = json.dumps(server_error('Unexpected server error.')).encode('utf-8') + +    @wraps(handle) +    def handler(data): +        try: +            request = json.loads(data.decode('utf-8')) +        except ValueError: +            log.exception('Bad JSON data: {!r}', request) +        try: +            response = handle(request) +            data = json.dumps(response).encode('utf-8') +        except Exception: +            log.exception('Exception in handler.') +            data = SERVER_ERROR +        return data + +    return handler + +def get_options(fn): +    arguments = fn.__code__.co_varnames +    defaults = fn.__defaults__ + +    required = arguments[:-len(defaults)] +    optional = list(zip(defaults, arguments[-len(defaults):])) +    return (required, optional) + +class Server(object): +    SERVER_NAME = os.getenv('NAME') +    SERVER_KIND = 'robots' + +    def __init__(self): +        # Maps bot names to a function which will return the bot callable. +        # (or, you know, a class) +        self.bots = {} + +        # Maps instance ids to a bot callable. +        self.instances = {} + +    def add_bot(self, fn, name=None): +        if name is None: +            name = fn.__name__ +        self.bots[name] = fn + +    def add_simple_bot(self, fn, name=None): +        if name is None: +            name = fn.__name__ +        return self.add_bot(lambda: fn, name) + +    def list_bots(self): +        bots = { +            name: None and get_options(fn) +            for name, fn in self.bots.items() +        } +        return success(bots=bots) + +    def create(self, name, options): +        try: +            bot_class = self.bots[name] +        except KeyError: +            message = 'Could not find bot %s' % (name,) +            log.warning(message) +            return not_found(message) + +        try: +            instance = bot_class(**options) +        except Exception: +            message = 'Could not create bot %s' % (name,) +            log.exception(message) +            return server_error(message) + +        instance_id = str(uuid.uuid4()) +        self.instances[instance_id] = instance +        log.debug('Created new bot instance %s' % instance_id) +        return success(instance_id=instance_id) + +    def handle(self, request): +        try: +            action = request['action'] + +            if action == 'list': +                return self.list_bots() + +            elif action == 'create': +                name = request['name'] +                options = request['options'] +                return self.create(name, options) + +            elif action == 'process': +                instance_id = request['instance_id'] +                state = request['state'] +                return self.next_move(instance_id, state) + +            elif action == 'destroy': +                instance_id = request['instance_id'] +                return self.destroy(instance_id) + +            else: +                return bad_request('Unrecognised action %s' % (action,)) + +        except KeyError as e: +            return bad_request('No %s specified' % e.args) + +    def run(self): +        server = network.Server( +            self.SERVER_NAME, +            self.SERVER_KIND, +            handler=json_handler(self.handle), +        ) +        server.run() @@ -12,4 +12,4 @@ def random_walk(whoami, state):  if __name__ == '__main__':      server = robots.Server()      server.add_simple_bot(random_walk, 'Alice') -    server.run(debug=True) +    server.run()  | 
