Package starcluster :: Package plugins :: Module tmux
[hide private]
[frames] | no frames]

Source Code for Module starcluster.plugins.tmux

  1  from starcluster import utils 
  2  from starcluster import exception 
  3  from starcluster import clustersetup 
  4  from starcluster.logger import log 
  5   
  6   
7 -class TmuxControlCenter(clustersetup.DefaultClusterSetup):
8 """ 9 Starts a TMUX session on StarCluster configured with split panes for all 10 nodes. This allows you to interactively run commands on all nodes and see 11 all the output at once. 12 """ 13 _layouts = ['even-horizontal', 'even-vertical', 'main-horizontal', 14 'main-vertical', 'tiled'] 15
16 - def __init__(self, envname="starcluster"):
17 self._envname = envname 18 self._nodes = None 19 self._master = None 20 self._user = None 21 self._user_shell = None 22 self._volumes = None
23
24 - def _supports_layout(self, node, envname, layout, window=''):
25 if layout not in self._layouts: 26 raise exception.PluginError("unknown layout (options: %s)" % 27 ", ".join(self._layouts)) 28 return self._select_layout(node, envname, layout, window) == 0
29
30 - def _select_layout(self, node, envname, layout="main-vertical", window=''):
31 if layout not in self._layouts: 32 raise exception.PluginError("unknown layout (options: %s)" % 33 ", ".join(self._layouts)) 34 cmd = 'tmux select-layout -t %s:%s %s' 35 return node.ssh.get_status(cmd % (envname, window, layout))
36
37 - def _resize_pane(self, node, envname, pane, units, up=False):
38 upordown = '-D %s' % units 39 if up: 40 upordown = '-D %s' % units 41 cmd = 'tmux resize-pane -t %s:%s %s' % (envname, pane, upordown) 42 return node.ssh.execute(cmd)
43
44 - def _split_window(self, node, envname, window='', vertical=False):
45 cmd = 'tmux split-window' 46 if vertical: 47 cmd += ' -h' 48 return node.ssh.execute('%s -t %s:%s' % (cmd, envname, window))
49
50 - def _rename_window(self, node, envname, window, name):
51 cmd = 'tmux rename-window -t %s:%s %s' % (envname, window, name) 52 return node.ssh.execute(cmd)
53
54 - def _has_session(self, node, envname):
55 status = node.ssh.get_status('tmux has-session -t %s' % envname) 56 return status == 0
57
58 - def _send_keys(self, node, envname, cmd, window=''):
59 node.ssh.execute('tmux send-keys -t %s:%s "%s"' % (envname, window, 60 cmd)) 61 node.ssh.execute('tmux send-keys -t %s:%s "Enter"' % (envname, window))
62
63 - def _new_session(self, node, envname):
64 node.ssh.execute('tmux new-session -d -s %s' % envname, detach=True)
65
66 - def _kill_session(self, node, envname):
67 node.ssh.execute('tmux kill-session -t %s' % envname)
68
69 - def _new_window(self, node, envname, title):
70 node.ssh.execute('tmux new-window -n %s -t %s:' % (title, envname))
71
72 - def _select_window(self, node, envname, window=''):
73 node.ssh.execute('tmux select-window -t %s:%s' % (envname, window))
74
75 - def _select_pane(self, node, envname, window, pane):
76 node.ssh.execute('tmux select-pane -t %s:%s.%s' % 77 (envname, window, pane))
78
79 - def create_session(self, node, envname, num_windows=5):
80 if not self._has_session(node, envname): 81 self._new_session(node, envname) 82 for i in range(1, num_windows): 83 self._new_window(node, envname, i)
84
85 - def setup_tmuxcc(self, client=None, nodes=None, user='root', 86 layout='tiled'):
87 log.info("Creating TMUX Control Center for user '%s'" % user) 88 client = client or self._master 89 nodes = nodes or self._nodes 90 envname = self._envname 91 orig_user = client.ssh._username 92 if orig_user != user: 93 client.ssh.connect(username=user) 94 chunks = [chunk for chunk in utils.chunk_list(nodes, items=8)] 95 num_windows = len(chunks) + len(nodes) 96 if len(nodes) == 0: 97 log.error("Cluster has no nodes, exiting...") 98 return 99 self.create_session(client, envname, num_windows=num_windows) 100 if len(nodes) == 1 and client == nodes[0]: 101 return 102 if not self._supports_layout(client, envname, layout, window=0): 103 log.warn("failed to select layout '%s', defaulting to " 104 "'main-vertical'" % layout) 105 layout = "main-vertical" 106 status = self._select_layout(client, envname, layout, window=0) 107 if status != 0: 108 raise exception.PluginError("failed to set a layout") 109 for i, chunk in enumerate(chunks): 110 self._rename_window(client, envname, i, 'all%s' % i) 111 for j, node in enumerate(chunk): 112 if j != 0: 113 self._split_window(client, envname, i) 114 self._select_layout(client, envname, window=i, layout=layout) 115 if node.alias != client.alias: 116 self._send_keys(client, envname, cmd='ssh %s' % node.alias, 117 window="%d.%d" % (i, j)) 118 for i, node in enumerate(nodes): 119 window = i + len(chunks) 120 self._rename_window(client, envname, window, node.alias) 121 if node.alias != client.alias: 122 self._send_keys(client, envname, cmd='ssh %s' % node.alias, 123 window=window) 124 self._select_window(client, envname, window=0) 125 self._select_pane(client, envname, window=0, pane=0) 126 if orig_user != user: 127 client.ssh.connect(username=orig_user)
128
129 - def add_to_utmp_group(self, client, user):
130 """ 131 Adds user (if exists) to 'utmp' group (if exists) 132 """ 133 try: 134 client.add_user_to_group(user, 'utmp') 135 except exception.BaseException: 136 pass
137
138 - def run(self, nodes, master, user, user_shell, volumes):
139 log.info("Starting TMUX Control Center...") 140 self._nodes = nodes 141 self._master = master 142 self._user = user 143 self._user_shell = user_shell 144 self._volumes = volumes 145 self.add_to_utmp_group(master, user) 146 self.setup_tmuxcc(user=user) 147 self.setup_tmuxcc(user='root')
148
149 - def on_add_node(self, node, nodes, master, user, user_shell, volumes):
150 log.info("Adding %s to TMUX Control Center" % node.alias)
151 #user_home = node.getpwnam(user).pw_dir 152
153 - def on_remove_node(self, node, nodes, master, user, user_shell, volumes):
154 log.info("Removing %s from TMUX Control Center" % node.alias)
155