Refactored as a class, fixed bugs where -l and -p behaviour would ignore ssh configs.
This commit is contained in:
parent
b7ca428dfb
commit
9a27fa34f3
195
crussh.py
195
crussh.py
@ -26,107 +26,132 @@ except:
|
|||||||
parser = argparse.ArgumentParser(description="Connect to multiple servers in parallel.")
|
parser = argparse.ArgumentParser(description="Connect to multiple servers in parallel.")
|
||||||
parser.add_argument('hosts', metavar='HOST', nargs='+',
|
parser.add_argument('hosts', metavar='HOST', nargs='+',
|
||||||
help="Host(s) to connect to.")
|
help="Host(s) to connect to.")
|
||||||
parser.add_argument('-l', '--login', dest='login', default=getpass.getuser(),
|
parser.add_argument('-l', '--login', dest='login', default=None,
|
||||||
help="Login name to use.")
|
help="Login name to use.")
|
||||||
parser.add_argument('-p', '--port', dest='port', type=int, default=22,
|
parser.add_argument('-p', '--port', dest='port', type=int, default=None,
|
||||||
help="Alternate SSH port to use.")
|
help="Alternate SSH port to use.")
|
||||||
parser.add_argument('-s', '--fontsize', dest='fontsize', type=int, default=10,
|
parser.add_argument('-s', '--fontsize', dest='fontsize', type=int, default=10,
|
||||||
help="Font size to use. (default=10)")
|
help="Font size to use. (default=10)")
|
||||||
|
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
### Global Vars ###
|
### CruSSH! ###
|
||||||
Terminals = {}
|
class CruSSH:
|
||||||
TermMinWidth = 1
|
### State Vars ###
|
||||||
TermMinHeight = 1
|
Terminals = {}
|
||||||
|
TermMinWidth = 1
|
||||||
|
TermMinHeight = 1
|
||||||
|
|
||||||
### Configure Terminals ###
|
### GUI Objects ###
|
||||||
for host in args.hosts:
|
MainWin = gtk.Window(type=gtk.WINDOW_TOPLEVEL)
|
||||||
terminal = vte.Terminal()
|
ScrollWin = gtk.ScrolledWindow()
|
||||||
terminal.set_size(80, 24)
|
LayoutTable = gtk.Table()
|
||||||
terminal.set_font_from_string("Ubuntu Mono Bold " + str(args.fontsize))
|
EntryBox = gtk.Entry()
|
||||||
TermMinWidth = (terminal.get_char_width() * 80) + terminal.get_padding()[0]
|
|
||||||
TermMinHeight = (terminal.get_char_height() * 24) + terminal.get_padding()[1]
|
|
||||||
# TODO: disable only this terminal widget on child exit
|
|
||||||
# v.connect("child-exited", lambda term: gtk.main_quit())
|
|
||||||
cmd_str = "/usr/bin/ssh -l " + args.login + " -p " + str(args.port) + " " + host
|
|
||||||
cmd = cmd_str.split(' ')
|
|
||||||
terminal.fork_command(command=cmd[0], argv=cmd)
|
|
||||||
Terminals[host] = terminal
|
|
||||||
|
|
||||||
### Utility Functions and Vars ###
|
### Methods ###
|
||||||
def reflowTable(cols=1, rows=1):
|
def reflowTable(self, cols=1, rows=1):
|
||||||
# empty table and re-size
|
# empty table and re-size
|
||||||
for host in Terminals:
|
hosts = sorted(self.Terminals.keys(), reverse=True)
|
||||||
Table.remove(Terminals[host])
|
for host in hosts:
|
||||||
Table.resize(rows, cols)
|
self.LayoutTable.remove(self.Terminals[host])
|
||||||
# layout terminals
|
self.LayoutTable.resize(rows, cols)
|
||||||
hosts = sorted(args.hosts, reverse=True)
|
# layout terminals
|
||||||
for row in range(rows):
|
for row in range(rows):
|
||||||
for col in range(cols):
|
for col in range(cols):
|
||||||
if len(hosts) > 0:
|
if len(hosts) > 0:
|
||||||
host = hosts.pop()
|
host = hosts.pop()
|
||||||
Table.attach(Terminals[host], col, col+1, row, row+1)
|
self.LayoutTable.attach(self.Terminals[host], col, col+1, row, row+1)
|
||||||
Terminals[host].set_size(80, 24)
|
self.Terminals[host].set_size(80, 24)
|
||||||
|
|
||||||
def reflow():
|
def reflow(self):
|
||||||
size = MainWin.allocation
|
num_terms = len(self.Terminals)
|
||||||
cols = int(math.floor((size.width + Table.props.column_spacing) / TermMinWidth))
|
if num_terms < 1:
|
||||||
if cols < 1 or len(args.hosts) == 1:
|
gtk.main_quit()
|
||||||
cols = 1
|
size = self.MainWin.allocation
|
||||||
rows = int(math.ceil(len(Terminals)/cols))
|
cols = int(math.floor((size.width + self.LayoutTable.props.column_spacing) / self.TermMinWidth))
|
||||||
if rows < 1:
|
if cols < 1 or num_terms == 1:
|
||||||
rows = 1
|
cols = 1
|
||||||
if (Table.props.n_columns != cols) or (Table.props.n_rows != rows):
|
rows = int(math.ceil(num_terms/cols))
|
||||||
reflowTable(cols, rows)
|
if rows < 1:
|
||||||
MainWin.show_all()
|
rows = 1
|
||||||
|
if (self.LayoutTable.props.n_columns != cols) or (self.LayoutTable.props.n_rows != rows):
|
||||||
|
self.reflowTable(cols, rows)
|
||||||
|
self.MainWin.show_all()
|
||||||
|
|
||||||
### Setup GTK Interface ###
|
def removeTerminal(self, terminal):
|
||||||
MainWin = gtk.Window(type=gtk.WINDOW_TOPLEVEL)
|
# TODO: make this work. ;)
|
||||||
MainWin.set_title("crussh: " + ' '.join(args.hosts))
|
# del self.Terminals[terminal.props.window_title]
|
||||||
MainWin.set_role(role="crussh_main_win")
|
self.reflow()
|
||||||
MainWin.connect("delete-event", lambda window, event: gtk.main_quit())
|
|
||||||
|
|
||||||
ScrollWin = gtk.ScrolledWindow()
|
def initGUI(self):
|
||||||
ScrollWin.props.hscrollbar_policy = gtk.POLICY_NEVER
|
self.MainWin.set_title("crussh: " + ' '.join(self.Terminals.keys()))
|
||||||
ScrollWin.props.vscrollbar_policy = gtk.POLICY_ALWAYS
|
self.MainWin.set_role(role="crussh_main_win")
|
||||||
ScrollWin.props.shadow_type = gtk.SHADOW_ETCHED_IN
|
self.MainWin.connect("delete-event", lambda window, event: gtk.main_quit())
|
||||||
|
|
||||||
Table = gtk.Table()
|
self.ScrollWin.props.hscrollbar_policy = gtk.POLICY_NEVER
|
||||||
Table.set_homogeneous(True)
|
self.ScrollWin.props.vscrollbar_policy = gtk.POLICY_ALWAYS
|
||||||
Table.set_row_spacings(1)
|
self.ScrollWin.props.shadow_type = gtk.SHADOW_ETCHED_IN
|
||||||
Table.set_col_spacings(1)
|
|
||||||
ScrollWin.add_with_viewport(Table)
|
|
||||||
ScrollWin.set_size_request(TermMinWidth, TermMinHeight)
|
|
||||||
|
|
||||||
EntryBox = gtk.Entry()
|
self.LayoutTable.set_homogeneous(True)
|
||||||
# don't display chars while typing.
|
self.LayoutTable.set_row_spacings(1)
|
||||||
EntryBox.set_visibility(False)
|
self.LayoutTable.set_col_spacings(1)
|
||||||
EntryBox.set_invisible_char(' ')
|
self.ScrollWin.add_with_viewport(self.LayoutTable)
|
||||||
def feed_input(widget, event):
|
self.ScrollWin.set_size_request(self.TermMinWidth, self.TermMinHeight)
|
||||||
if event.type in [gtk.gdk.KEY_PRESS, gtk.gdk.KEY_RELEASE]:
|
|
||||||
# erase buffer on every entry, so that passwords aren't revealed
|
|
||||||
EntryBox.props.buffer.delete_text(0, -1)
|
|
||||||
# propagate to every terminal
|
|
||||||
for host in Terminals:
|
|
||||||
t_event = event.copy()
|
|
||||||
Terminals[host].event(t_event)
|
|
||||||
# this stops regular handler from firing, switching focus.
|
|
||||||
return True
|
|
||||||
EntryBox.connect("key_press_event", feed_input)
|
|
||||||
EntryBox.connect("key_release_event", feed_input)
|
|
||||||
|
|
||||||
VBox = gtk.VBox()
|
# don't display chars while typing.
|
||||||
VBox.pack_start(ScrollWin)
|
self.EntryBox.set_visibility(False)
|
||||||
VBox.pack_start(EntryBox, False, False)
|
self.EntryBox.set_invisible_char(' ')
|
||||||
MainWin.add(VBox)
|
# forward key events to all terminals
|
||||||
|
def feed_input(widget, event):
|
||||||
|
if event.type in [gtk.gdk.KEY_PRESS, gtk.gdk.KEY_RELEASE]:
|
||||||
|
# erase buffer on every entry, so that passwords aren't revealed
|
||||||
|
self.EntryBox.props.buffer.delete_text(0, -1)
|
||||||
|
# propagate to every terminal
|
||||||
|
for host in self.Terminals:
|
||||||
|
t_event = event.copy()
|
||||||
|
self.Terminals[host].event(t_event)
|
||||||
|
# this stops regular handler from firing, switching focus.
|
||||||
|
return True
|
||||||
|
self.EntryBox.connect("key_press_event", feed_input)
|
||||||
|
self.EntryBox.connect("key_release_event", feed_input)
|
||||||
|
|
||||||
# reflow layout on size change.
|
VBox = gtk.VBox()
|
||||||
def resize_event_handler(widget, allocation):
|
VBox.pack_start(self.ScrollWin)
|
||||||
reflow()
|
VBox.pack_start(self.EntryBox, False, False)
|
||||||
MainWin.connect("size-allocate", lambda widget, allocation: reflow())
|
self.MainWin.add(VBox)
|
||||||
|
|
||||||
|
# reflow layout on size change.
|
||||||
|
self.MainWin.connect("size-allocate", lambda widget, allocation: self.reflow())
|
||||||
|
|
||||||
|
# give EntryBox default focus on init
|
||||||
|
self.EntryBox.props.has_focus = True
|
||||||
|
|
||||||
|
def __init__(self, hosts):
|
||||||
|
# init all terminals
|
||||||
|
for host in hosts:
|
||||||
|
terminal = vte.Terminal()
|
||||||
|
terminal.set_size(80, 24)
|
||||||
|
terminal.set_font_from_string("Ubuntu Mono Bold " + str(args.fontsize))
|
||||||
|
self.TermMinWidth = (terminal.get_char_width() * 80) + terminal.get_padding()[0]
|
||||||
|
self.TermMinHeight = (terminal.get_char_height() * 24) + terminal.get_padding()[1]
|
||||||
|
# TODO: disable only this terminal widget on child exit
|
||||||
|
# v.connect("child-exited", lambda term: gtk.main_quit())
|
||||||
|
cmd_str = "/usr/bin/ssh"
|
||||||
|
if args.login != None:
|
||||||
|
cmd_str += " -l " + args.login
|
||||||
|
if args.port != None:
|
||||||
|
cmd_str += " -p " + str(args.port)
|
||||||
|
cmd_str += " " + host
|
||||||
|
cmd = cmd_str.split(' ')
|
||||||
|
terminal.fork_command(command=cmd[0], argv=cmd)
|
||||||
|
self.Terminals[host] = terminal
|
||||||
|
|
||||||
|
# hook terminals so they reflow layout on exit
|
||||||
|
self.Terminals[host].connect("child-exited", self.removeTerminal)
|
||||||
|
|
||||||
|
self.initGUI()
|
||||||
|
self.reflow()
|
||||||
|
|
||||||
### Start Execution ###
|
### Start Execution ###
|
||||||
reflowTable()
|
crussh = CruSSH(args.hosts)
|
||||||
reflow()
|
|
||||||
gtk.main()
|
gtk.main()
|
||||||
|
Loading…
Reference in New Issue
Block a user