/
run_ssh_tunnels
executable file
·159 lines (129 loc) · 4.53 KB
/
run_ssh_tunnels
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
#!/usr/bin/python
# -*- coding: utf-8 -*-
"""
Executes a series of ssh tunnels. This is particularly useful when
setting up access to remote instances of server processes behind an SSH
layer, for instance, database instances. If muktiple conn
"""
__author__ = 'ryan faulkner'
__email__ = 'rfaulkner@wikimedia.org'
__date__ = "02-21-2013"
__license__ = "GPL (version 2 or later)"
import sys
from re import search
from os import kill
from signal import SIGKILL
import logging
import argparse
import subprocess
import settings as conf
logging.basicConfig(level=logging.DEBUG, stream=sys.stderr,
format='%(asctime)s %(levelname)-8s %(message)s',
datefmt='%b-%d %H:%M:%S')
def parseargs():
"""
Process CLI arguments.
**hosts** - a list of host names to setup ssh tunnels to.
"""
parser = argparse.ArgumentParser(
description=
"""
Command line arguments for setting up ssh tunnels.
""",
epilog="",
conflict_handler="resolve",
usage="run_ssh_tunnels <host proxy> [<host proxy>]* [-q --quiet]* "
"[-s --silent]* [-v --verbose]*"
)
parser.allow_interspersed_args = False
defaults = {
"quiet": 0,
"silent": False,
"verbose": 0,
}
parser.add_argument('hosts',
nargs='*',
type=str,
help='The metric to compute.',
default=['s1']
)
parser.add_argument("-q", "--quiet",
default=defaults["quiet"],
action="count",
help="decrease the logging verbosity")
parser.add_argument("-s", "--silent",
default=defaults["silent"],
action="store_true",
help="silence the logger")
parser.add_argument("-v", "--verbose",
default=defaults["verbose"],
action="count",
help="increase the logging verbosity")
return parser.parse_args()
def call_tunnel(host_alias):
"""
Executes call that opens an ssh tunnel to remote host.
"""
cmd = 'ssh {0}@{1} -f -N -L {2}:{3}:{4}'.\
format(conf.TUNNEL_DATA[host_alias]['user'],
conf.TUNNEL_DATA[host_alias]['cluster_host'],
str(conf.TUNNEL_DATA[host_alias]['tunnel_port']),
conf.TUNNEL_DATA[host_alias]['db_host'],
str(conf.TUNNEL_DATA[host_alias]['remote_port'])
)
return subprocess.Popen(cmd.split(),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
def kill_procs_on_port(port):
"""
Kills processes attached to the local port supplied. The shell
process spawns a child process so calling 'lsof' helps to
locate actual attached processes rather than parents.
"""
logging.debug('Killing procs on port {0}'.format(port))
cmd = 'lsof -w -n -i tcp:{0}'.format(port)
p = subprocess.Popen(cmd.split(),
stdout=subprocess.PIPE,
stderr=subprocess.PIPE)
for row in p.communicate()[0].strip().splitlines():
if not search('(ssh|LISTEN)', row):
continue
elems = row.split()
try:
pid = int(elems[1])
except ValueError:
continue
try:
kill(pid, SIGKILL)
except OSError:
continue
logging.info('Killed {0}'.format(pid))
def main(args):
logging.debug('Begin execution.')
# Initiate tunnels
tunnels = dict()
for host in args.hosts:
tunnels[host] = call_tunnel(host)
logging.info('Starting tunnel for host {0} on port {1}'.format(
conf.TUNNEL_DATA[host]['db_host'],
conf.TUNNEL_DATA[host]['tunnel_port']
))
# Process loop
try:
while tunnels:
user_in = raw_input('Type "x" to exit.\n')
if user_in == 'x':
break
# Check for dead procs
for host, proc in tunnels.iteritems():
if proc.poll():
logging.info('Host {0} died.'.format(host))
del tunnels[host]
finally:
for host, proc in tunnels.iteritems():
kill_procs_on_port(conf.TUNNEL_DATA[host]['tunnel_port'])
logging.debug('Terminate execution.')
if __name__ == "__main__":
args = parseargs()
logging.debug(args)
sys.exit(main(args))