From abab0e5cae9b2779672473208b61850808080006 Mon Sep 17 00:00:00 2001 From: Jarrod Johnson Date: Sun, 9 Feb 2014 17:35:49 -0500 Subject: [PATCH] Flesh out the confetty interactive console slightly --- bin/confetty | 81 +++++++++++++++++++++++++++++++++++++++---- confluent/messages.py | 2 +- confluent/sockapi.py | 36 +++++++++++++++++-- 3 files changed, 110 insertions(+), 9 deletions(-) diff --git a/bin/confetty b/bin/confetty index c4b1ebec..fcaf8f13 100755 --- a/bin/confetty +++ b/bin/confetty @@ -29,12 +29,14 @@ import getpass import optparse import os import select +import shlex import socket import ssl import sys import termios import tty +target = "/" path = os.path.dirname(os.path.realpath(__file__)) path = os.path.realpath(os.path.join(path, '..')) sys.path.append(path) @@ -47,6 +49,10 @@ conserversequence = '\x05c' # ctrl-e, c oldtcattr = termios.tcgetattr(sys.stdin.fileno()) server = None +def prompt(): + sys.stdout.write(target + ' -> ') + sys.stdout.flush() + def parseservervalue(serverstring): if serverstring.find(']:') != -1: server, port = serverstring[1:].split(']:') @@ -61,6 +67,62 @@ def parseservervalue(serverstring): return (server, port) +def parse_command(command): + args = shlex.split(command, posix=True) + return args + +def do_command(command, server): + global target + if command == "": # result of ctrl-d + command = "exit\n" + print("exit") + command = command[:-1] + command = command.lower() + argv = parse_command(command) + if len(argv) == 0: + prompt() + return + if argv[0] == "exit": + server.close() + sys.exit(0) + if argv[0] == "cd": + otarget = target + change_target(argv[1]) + tlvdata.send_tlvdata(server, {"operation": 'retrieve', 'path': target}) + result = tlvdata.recv_tlvdata(server) + if 'error' in result: + print target + ':' + result['error'] + target = otarget + + + + prompt() + + +def change_target(path): + global target + pathcomponents = path.split("/") + if pathcomponents[0] == "": # absolute path + target = path + else: + targparts = target.split("/")[:-1] + for component in pathcomponents: + if component in ('.',''): # ignore these + continue + elif component == '..': + if len(targparts) > 0: + del targparts[-1] + else: + targparts.append(component) + targparts.append('') + target = '/'.join(targparts) + if len(target) ==0 or target[-1] != '/': + target += '/' + +def startconsole(): + tty.setraw(sys.stdin.fileno()) + fcntl.fcntl(sys.stdin.fileno(), fcntl.F_SETFL, os.O_NONBLOCK) + def exit(code=0): termios.tcsetattr(sys.stdin.fileno(), termios.TCSANOW, oldtcattr) server.shutdown(socket.SHUT_RDWR) @@ -151,13 +213,15 @@ while authinfo['authpassed'] != 1: tlvdata.send_tlvdata(server, {'username': username, 'passphrase': passphrase}) authinfo = tlvdata.recv_tlvdata(server) -tty.setraw(sys.stdin.fileno()) -fcntl.fcntl(sys.stdin.fileno(), fcntl.F_SETFL, os.O_NONBLOCK) # clear on start can help with readable of TUI, but it # can be annoying, so for now don't do it. # sys.stdout.write('\x1b[H\x1b[J') # sys.stdout.flush() + doexit = False +inconsole = False +pendingcommand = "" +prompt() while not doexit: rdylist, _, _ = select.select((sys.stdin, server), (), (), 60) for fh in rdylist: @@ -175,8 +239,13 @@ while not doexit: sys.stdout.write("\r\n[remote disconnected]\r\n") break else: - input = fh.read() - input = check_escape_seq(input, fh) - if input: - tlvdata.send_tlvdata(server, input) + if inconsole: + input = fh.read() + input = check_escape_seq(input, fh) + if input: + tlvdata.send_tlvdata(server, input) + else: + command = fh.readline() + inconsole = do_command(command, server) + exit() diff --git a/confluent/messages.py b/confluent/messages.py index 3e5c95c2..4cb633f2 100644 --- a/confluent/messages.py +++ b/confluent/messages.py @@ -91,7 +91,7 @@ class ConfluentChoiceMessage(ConfluentMessage): class LinkRelation(ConfluentMessage): - def json_hal(self): + def json(self): """Provide json_hal style representation of the relation. This currently only makes sense for the socket api. diff --git a/confluent/sockapi.py b/confluent/sockapi.py index c907546b..655cf9a3 100644 --- a/confluent/sockapi.py +++ b/confluent/sockapi.py @@ -9,9 +9,13 @@ import confluent.auth as auth import confluent.common.tlvdata as tlvdata import confluent.consoleserver as consoleserver import confluent.config.configmanager as configmanager +import confluent.exceptions as exc +import confluent.messages +import confluent.pluginapi as pluginapi import eventlet.green.socket as socket import eventlet.green.ssl as ssl import eventlet +import json import os import struct @@ -19,7 +23,7 @@ SO_PEERCRED = 17 class ClientConsole(object): def __init__(self, client): - self.client = client + self.client = clientn def sendall(self, data): tlvdata.send_tlvdata(self.client, data) @@ -41,7 +45,7 @@ def sessionhdl(connection, authname): response = tlvdata.recv_tlvdata(connection) username = response['username'] passphrase = response['passphrase'] - # NOTE(jbjohnso): Here, we need to authenticate, but not + # note(jbjohnso): here, we need to authenticate, but not # authorize a user. When authorization starts understanding # element path, that authorization will need to be called # per request the user makes @@ -50,6 +54,34 @@ def sessionhdl(connection, authname): authenticated = True cfm = authdata[1] tlvdata.send_tlvdata(connection, {'authpassed': 1}) + request = tlvdata.recv_tlvdata(connection) + while request is not None: + process_request(connection, request, cfm, authdata) + request = tlvdata.recv_tlvdata(connection) + + +def send_response(responses, connection): + for rsp in responses: + tlvdata.send_tlvdata(connection, json.dumps(rsp.json())) + +def process_request(connection, request, cfm, authdata): + #TODO(jbjohnso): authorize each request + if type(request) == dict: + operation = request['operation'] + path = request['path'] + params = request.get('parameters', None) + try: + hdlr = pluginapi.handle_path(path, operation, cfm, params) + except exc.NotFoundException: + tlvdata.send_tlvdata(connection, {"errorcode": 404, + "error": "Target not found"}) + return + except exc.InvalidArgumentException: + tlvdata.send_tlvdata(connection, {"errorcode": 400, + "error": "Bad Request"}) + return + send_response(hdlr, connection) + return ccons = ClientConsole(connection) consession = consoleserver.ConsoleSession(node='n4', configmanager=cfm, datacallback=ccons.sendall)