mirror of
https://github.com/xcat2/confluent.git
synced 2024-11-22 17:43:14 +00:00
5aa6e6a26c
Here, fix a few mistakes with how relation objects got messaged out. Add the ability to cd and ls in the confetty interactive mode
271 lines
8.0 KiB
Python
Executable File
271 lines
8.0 KiB
Python
Executable File
#!/usr/bin/env python
|
|
|
|
# ultimately, this should provide an interactive cli for navigating confluent
|
|
# tree and doing console with a socket. <ESC>]0;<string><BELL> can be used to
|
|
# present info such as whether it is in a console or other mode and, if in
|
|
# console, how many other connections are live and looking
|
|
# this means 'wcons' simply needs to make a terminal run and we'll take care of
|
|
# the title while providing more info
|
|
# this also means the socket interface needs to have ways to convey more
|
|
# interesting pieces of data (like concurrent connection count)
|
|
# socket will probably switch to a TLV scheme:
|
|
# 32 bit TL, 8 bits of type code and 24 bit size
|
|
# type codes:
|
|
# 0: string data
|
|
# 1: json data
|
|
# 24 bit size allows the peer to avoid having to do any particular parsing to
|
|
# understand message boundaries (which is a significant burden on the xCAT
|
|
# protocol)
|
|
|
|
# When in a console client mode, will recognize two escape sequences by
|
|
# default:
|
|
# Ctrl-E, c, ?: mimick conserver behavior
|
|
# ctrl-]: go to interactive prompt (telnet escape, but not telnet prompt)
|
|
# esc-( would interfere with normal esc use too much
|
|
# ~ I will not use for now...
|
|
|
|
import fcntl
|
|
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)
|
|
|
|
import confluent.common.tlvdata as tlvdata
|
|
|
|
SO_PASSCRED = 16
|
|
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(']:')
|
|
elif serverstring[0] == '[':
|
|
server = serverstring[1:-1]
|
|
port = 4001
|
|
elif -1 != opts.server.find(':'):
|
|
server, port = opts.server.split(":")
|
|
else:
|
|
server = serverstring
|
|
port = 4001
|
|
return (server, port)
|
|
|
|
|
|
def parse_command(command):
|
|
args = shlex.split(command, posix=True)
|
|
return args
|
|
|
|
|
|
currchildren = None
|
|
|
|
def send_request(operation, path, server):
|
|
tlvdata.send_tlvdata(server, {'operation': operation, 'path': path})
|
|
result = tlvdata.recv_tlvdata(server)
|
|
while '_requestdone' not in result:
|
|
yield result
|
|
result = tlvdata.recv_tlvdata(server)
|
|
|
|
def do_command(command, server):
|
|
global target
|
|
global currchildren
|
|
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])
|
|
for res in send_request('retrieve', target, server):
|
|
if 'error' in res:
|
|
print target + ': ' + res['error']
|
|
target = otarget
|
|
if argv[0] in ('show', 'ls'):
|
|
for res in send_request('retrieve', target, server):
|
|
if type(res['item']) == dict:
|
|
print res['item']["href"]
|
|
else:
|
|
for item in res['item']:
|
|
print item["href"]
|
|
|
|
|
|
|
|
|
|
|
|
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)
|
|
server.close()
|
|
sys.exit(code)
|
|
|
|
def conserver_command(fh, command):
|
|
while not command:
|
|
ready, _, _ = select.select((fh,), (), (), 1)
|
|
if ready:
|
|
command += fh.read()
|
|
if command[0] == '.':
|
|
print("disconnect]\r")
|
|
exit()
|
|
elif command[0] == '?':
|
|
print("help]\r")
|
|
print(". disconnect\r")
|
|
print("<cr> abort command\r")
|
|
elif command[0] == '\x0d':
|
|
print("ignored]\r")
|
|
else: #not a command at all..
|
|
print("unknown -- use '?']\r")
|
|
|
|
|
|
def check_escape_seq(input, fh):
|
|
while conserversequence.startswith(input):
|
|
if input.startswith(conserversequence): # We have full sequence
|
|
sys.stdout.write("[")
|
|
sys.stdout.flush()
|
|
return conserver_command(fh, input[len(conserversequence):])
|
|
ready, _, _ = select.select((fh,), (), (), 3)
|
|
if not ready: # 3 seconds of no typing
|
|
break
|
|
input += fh.read()
|
|
return input
|
|
|
|
|
|
def connect_unix_server(sockpath):
|
|
server = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
|
server.setsockopt(socket.SOL_SOCKET, SO_PASSCRED, 1)
|
|
server.connect(sockpath)
|
|
return server
|
|
|
|
|
|
def connect_tls_server(serverstring):
|
|
host, port = parseservervalue(serverstring)
|
|
for res in socket.getaddrinfo(host, port, socket.AF_UNSPEC,
|
|
socket.SOCK_STREAM):
|
|
af, socktype, proto, cononname, sa = res
|
|
try:
|
|
server = socket.socket(af, socktype, proto)
|
|
except:
|
|
server = None
|
|
continue
|
|
try:
|
|
server.settimeout(5)
|
|
server.connect(sa)
|
|
except:
|
|
server.close()
|
|
server = None
|
|
continue
|
|
break
|
|
if server is None:
|
|
sys.stderr.write("Failed to connect to %s\n" % serverstring)
|
|
sys.exit(1)
|
|
secserver = ssl.wrap_socket(server)
|
|
return secserver
|
|
|
|
parser = optparse.OptionParser()
|
|
parser.add_option("-s", "--server", dest="server",
|
|
help="TLS server to connect to", metavar="SERVER:PORT")
|
|
parser.add_option("-u", "--unixsocket", dest="unixsock",
|
|
help="TLS server to connect to", metavar="UNIXDOMAINSOCKET")
|
|
opts, args = parser.parse_args()
|
|
if opts.server: # going over a TLS network
|
|
server = connect_tls_server(opts.server)
|
|
elif opts.unixsock:
|
|
server = connect_unix_server(opts.unixsock)
|
|
|
|
#Next stop, reading and writing from whichever of stdin and server goes first.
|
|
#see pyghmi code for solconnect.py
|
|
|
|
banner = tlvdata.recv_tlvdata(server)
|
|
authinfo = tlvdata.recv_tlvdata(server)
|
|
while authinfo['authpassed'] != 1:
|
|
username = raw_input("Name: ")
|
|
passphrase = getpass.getpass("Passphrase: ")
|
|
tlvdata.send_tlvdata(server,
|
|
{'username': username, 'passphrase': passphrase})
|
|
authinfo = tlvdata.recv_tlvdata(server)
|
|
# 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:
|
|
if fh == server:
|
|
#fh.read()
|
|
try:
|
|
data = tlvdata.recv_tlvdata(fh)
|
|
except Exception:
|
|
data = None
|
|
if data is not None:
|
|
sys.stdout.write(data)
|
|
sys.stdout.flush()
|
|
else:
|
|
doexit = True
|
|
sys.stdout.write("\r\n[remote disconnected]\r\n")
|
|
break
|
|
else:
|
|
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()
|