blob: e546b7281e6424010d3d8d32d855d34460f96f1a [file] [log] [blame]
koder aka kdanilove06762a2015-03-22 23:32:09 +02001import re
koder aka kdanilov3a6633e2015-03-26 18:20:00 +02002import time
koder aka kdanilove06762a2015-03-22 23:32:09 +02003import logging
4import os.path
koder aka kdanilov3a6633e2015-03-26 18:20:00 +02005import getpass
koder aka kdanilove06762a2015-03-22 23:32:09 +02006
koder aka kdanilov3a6633e2015-03-26 18:20:00 +02007import socket
8import paramiko
koder aka kdanilove06762a2015-03-22 23:32:09 +02009
koder aka kdanilove06762a2015-03-22 23:32:09 +020010
11logger = logging.getLogger("io-perf-tool")
koder aka kdanilove06762a2015-03-22 23:32:09 +020012
13
koder aka kdanilov3a6633e2015-03-26 18:20:00 +020014def ssh_connect(creds, retry_count=60, timeout=1):
15 ssh = paramiko.SSHClient()
16 ssh.load_host_keys('/dev/null')
17 ssh.set_missing_host_key_policy(paramiko.AutoAddPolicy())
18 ssh.known_hosts = None
19 for i in range(retry_count):
20 try:
21 if creds.user is None:
22 user = getpass.getuser()
23 else:
24 user = creds.user
25
26 if creds.passwd is not None:
27 ssh.connect(creds.host,
28 username=user,
29 password=creds.passwd,
30 port=creds.port,
31 allow_agent=False,
32 look_for_keys=False)
33 return ssh
34
35 if creds.key_file is not None:
36 ssh.connect(creds.host,
37 username=user,
38 key_filename=creds.key_file,
39 look_for_keys=False,
40 port=creds.port)
41 return ssh
42
43 key_file = os.path.expanduser('~/.ssh/id_rsa')
44 ssh.connect(creds.host,
45 username=user,
46 key_filename=key_file,
47 look_for_keys=False,
48 port=creds.port)
49 return ssh
50 # raise ValueError("Wrong credentials {0}".format(creds.__dict__))
51 except paramiko.PasswordRequiredException:
52 raise
53 except socket.error:
54 if i == retry_count - 1:
55 raise
56 time.sleep(timeout)
57
58
koder aka kdanilove06762a2015-03-22 23:32:09 +020059def normalize_dirpath(dirpath):
60 while dirpath.endswith("/"):
61 dirpath = dirpath[:-1]
62 return dirpath
63
64
koder aka kdanilov2c473092015-03-29 17:12:13 +030065ALL_RWX_MODE = ((1 << 9) - 1)
66
67
68def ssh_mkdir(sftp, remotepath, mode=ALL_RWX_MODE, intermediate=False):
koder aka kdanilove06762a2015-03-22 23:32:09 +020069 remotepath = normalize_dirpath(remotepath)
70 if intermediate:
71 try:
72 sftp.mkdir(remotepath, mode=mode)
73 except IOError:
74 ssh_mkdir(sftp, remotepath.rsplit("/", 1)[0], mode=mode,
75 intermediate=True)
76 return sftp.mkdir(remotepath, mode=mode)
77 else:
78 sftp.mkdir(remotepath, mode=mode)
79
80
81def ssh_copy_file(sftp, localfile, remfile, preserve_perm=True):
82 sftp.put(localfile, remfile)
83 if preserve_perm:
koder aka kdanilov2c473092015-03-29 17:12:13 +030084 sftp.chmod(remfile, os.stat(localfile).st_mode & ALL_RWX_MODE)
koder aka kdanilove06762a2015-03-22 23:32:09 +020085
86
87def put_dir_recursively(sftp, localpath, remotepath, preserve_perm=True):
88 "upload local directory to remote recursively"
89
90 # hack for localhost connection
91 if hasattr(sftp, "copytree"):
92 sftp.copytree(localpath, remotepath)
93 return
94
95 assert remotepath.startswith("/"), "%s must be absolute path" % remotepath
96
97 # normalize
98 localpath = normalize_dirpath(localpath)
99 remotepath = normalize_dirpath(remotepath)
100
101 try:
102 sftp.chdir(remotepath)
103 localsuffix = localpath.rsplit("/", 1)[1]
104 remotesuffix = remotepath.rsplit("/", 1)[1]
105 if localsuffix != remotesuffix:
106 remotepath = os.path.join(remotepath, localsuffix)
107 except IOError:
108 pass
109
110 for root, dirs, fls in os.walk(localpath):
111 prefix = os.path.commonprefix([localpath, root])
112 suffix = root.split(prefix, 1)[1]
113 if suffix.startswith("/"):
114 suffix = suffix[1:]
115
116 remroot = os.path.join(remotepath, suffix)
117
118 try:
119 sftp.chdir(remroot)
120 except IOError:
121 if preserve_perm:
koder aka kdanilov2c473092015-03-29 17:12:13 +0300122 mode = os.stat(root).st_mode & ALL_RWX_MODE
koder aka kdanilove06762a2015-03-22 23:32:09 +0200123 else:
koder aka kdanilov2c473092015-03-29 17:12:13 +0300124 mode = ALL_RWX_MODE
koder aka kdanilove06762a2015-03-22 23:32:09 +0200125 ssh_mkdir(sftp, remroot, mode=mode, intermediate=True)
126 sftp.chdir(remroot)
127
128 for f in fls:
129 remfile = os.path.join(remroot, f)
130 localfile = os.path.join(root, f)
131 ssh_copy_file(sftp, localfile, remfile, preserve_perm)
132
133
134def copy_paths(conn, paths):
135 sftp = conn.open_sftp()
136 try:
137 for src, dst in paths.items():
138 try:
139 if os.path.isfile(src):
140 ssh_copy_file(sftp, src, dst)
141 elif os.path.isdir(src):
142 put_dir_recursively(sftp, src, dst)
143 else:
144 templ = "Can't copy {0!r} - " + \
145 "it neither a file not a directory"
146 msg = templ.format(src)
147 raise OSError(msg)
148 except Exception as exc:
149 tmpl = "Scp {0!r} => {1!r} failed - {2!r}"
150 msg = tmpl.format(src, dst, exc)
151 raise OSError(msg)
152 finally:
153 sftp.close()
154
155
156class ConnCreds(object):
koder aka kdanilov2c473092015-03-29 17:12:13 +0300157 conn_uri_attrs = ("user", "passwd", "host", "port", "path")
158
koder aka kdanilove06762a2015-03-22 23:32:09 +0200159 def __init__(self):
koder aka kdanilov2c473092015-03-29 17:12:13 +0300160 for name in self.conn_uri_attrs:
koder aka kdanilove06762a2015-03-22 23:32:09 +0200161 setattr(self, name, None)
162
163
164uri_reg_exprs = []
165
166
167class URIsNamespace(object):
168 class ReParts(object):
169 user_rr = "[^:]*?"
170 host_rr = "[^:]*?"
171 port_rr = "\\d+"
172 key_file_rr = "[^:@]*"
173 passwd_rr = ".*?"
174
175 re_dct = ReParts.__dict__
176
177 for attr_name, val in re_dct.items():
178 if attr_name.endswith('_rr'):
179 new_rr = "(?P<{0}>{1})".format(attr_name[:-3], val)
180 setattr(ReParts, attr_name, new_rr)
181
182 re_dct = ReParts.__dict__
183
184 templs = [
185 "^{host_rr}$",
186 "^{user_rr}@{host_rr}::{key_file_rr}$",
187 "^{user_rr}@{host_rr}:{port_rr}:{key_file_rr}$",
188 "^{user_rr}:{passwd_rr}@@{host_rr}$",
189 "^{user_rr}:{passwd_rr}@@{host_rr}:{port_rr}$",
190 ]
191
192 for templ in templs:
193 uri_reg_exprs.append(templ.format(**re_dct))
194
195
196def parse_ssh_uri(uri):
197 # user:passwd@@ip_host:port
198 # user:passwd@@ip_host
199 # user@ip_host:port
200 # user@ip_host
201 # ip_host:port
202 # ip_host
203 # user@ip_host:port:path_to_key_file
204 # user@ip_host::path_to_key_file
205 # ip_host:port:path_to_key_file
206 # ip_host::path_to_key_file
207
208 res = ConnCreds()
209 res.port = "22"
210 res.key_file = None
211 res.passwd = None
212
213 for rr in uri_reg_exprs:
214 rrm = re.match(rr, uri)
215 if rrm is not None:
216 res.__dict__.update(rrm.groupdict())
217 return res
218 raise ValueError("Can't parse {0!r} as ssh uri value".format(uri))
219
220
221def connect(uri):
222 creds = parse_ssh_uri(uri)
223 creds.port = int(creds.port)
224 return ssh_connect(creds)
225
226
koder aka kdanilov2c473092015-03-29 17:12:13 +0300227# def get_ssh_runner(uris,
228# conn_func,
229# latest_start_time=None,
230# keep_temp_files=False):
231# logger.debug("Connecting to servers")
koder aka kdanilove06762a2015-03-22 23:32:09 +0200232
koder aka kdanilov2c473092015-03-29 17:12:13 +0300233# with ThreadPoolExecutor(max_workers=16) as executor:
234# connections = list(executor.map(connect, uris))
koder aka kdanilove06762a2015-03-22 23:32:09 +0200235
koder aka kdanilov2c473092015-03-29 17:12:13 +0300236# result_queue = Queue.Queue()
237# barrier = get_barrier(len(uris), threaded=True)
koder aka kdanilove06762a2015-03-22 23:32:09 +0200238
koder aka kdanilov2c473092015-03-29 17:12:13 +0300239# def closure(obj):
240# ths = []
241# obj.set_result_cb(result_queue.put)
koder aka kdanilove06762a2015-03-22 23:32:09 +0200242
koder aka kdanilov2c473092015-03-29 17:12:13 +0300243# params = (obj, barrier, latest_start_time)
koder aka kdanilove06762a2015-03-22 23:32:09 +0200244
koder aka kdanilov2c473092015-03-29 17:12:13 +0300245# logger.debug("Start tests")
246# for conn in connections:
247# th = threading.Thread(None, conn_func, None,
248# params + (conn,))
249# th.daemon = True
250# th.start()
251# ths.append(th)
koder aka kdanilove06762a2015-03-22 23:32:09 +0200252
koder aka kdanilov2c473092015-03-29 17:12:13 +0300253# for th in ths:
254# th.join()
koder aka kdanilove06762a2015-03-22 23:32:09 +0200255
koder aka kdanilov2c473092015-03-29 17:12:13 +0300256# test_result = []
257# while not result_queue.empty():
258# test_result.append(result_queue.get())
koder aka kdanilove06762a2015-03-22 23:32:09 +0200259
koder aka kdanilov2c473092015-03-29 17:12:13 +0300260# logger.debug("Done. Closing connection")
261# for conn in connections:
262# conn.close()
koder aka kdanilove06762a2015-03-22 23:32:09 +0200263
koder aka kdanilov2c473092015-03-29 17:12:13 +0300264# return test_result
265# return closure