py.lib
11:9a4eb7660c11
Go to Latest
py.lib/ssh_tools.py
+ Добавлены простые логгеры
1 # -*- coding: utf-8 -*-
3 from datetime import datetime
14 def promptChecker(user, host, buf):
15 Signs = ['[', ']', '#', '<', '>', '$', '!', '%', '(', ')', '@', '+', ':', ';', ' ', '\t']
20 for i in range(1, len(buf) + 1):
23 endBuf = buf[-i] + endBuf
28 endBuf = buf[-i] + endBuf
33 _startProc = len(buf) - i
34 _endProc = len(buf) - len(endBuf)
35 buf = buf[_startProc:_endProc]
37 for i in [user, host]:
42 for ch in buf[:index][::-1]:
47 index = index + len(i)
48 for ch in buf[index:]:
53 searchCond.append(bufL + i + bufR)
56 if buf.endswith(endBuf):
57 endSearch = len(buf) - len(endBuf)
58 startSearch = buf.rfind('\n', 0, len(buf) - len(endBuf))
60 if startSearch == -1: startSearch = 0
62 _buf = buf[startSearch:endSearch]
64 if _buf.find(i) == -1: return False
71 class SSHError(Exception): pass
73 class TOCheck(object):
75 Взято из: https://bitbucket.org/awgur/pylib/src/default/timetools.py
77 def __init__(self, timeOut):
78 self.start = datetime.now()
79 self.timeOut = timeOut
81 def __call__(self, timeOut=None):
83 timeOut = self.timeOut
85 buf = datetime.now() - self.start
87 if buf.seconds > timeOut:
96 class SSHSubsh(object):
97 """Класс для облегчения работы с контекстами, когда приглашение коммандной оболочки
98 меняется в результате исполнения комманды.
100 def __init__(self, ssh, prompt):
102 self.prompt = SSHPromptCheck(prompt)
104 def __call__(self, cmd):
105 res = self.ssh(cmd, wait=False)
106 res += self.waitPrompt(self.prompt)
109 class SSHPromptCheck(object):
111 Проверяет, появилось ли приглашение коммандной строки в выводе сервера
115 Строка, которая должна однозначно идентифицировать приглашение
116 коммандной строки, либо функция которой передаётся строка, и она
117 должна вернуть True, если строка завершается приглашением коммандной
118 строки либо False в обратном случае. Кроме того можно передат сам объект,
121 def __init__(self, prompt):
122 if isinstance(prompt, self.__class__):
123 self.prompt = prompt.prompt
124 self.check = prompt.check
127 if type(prompt) == type(isFunc):
133 if self.prompt == None:
134 raise SSHError('Prompt not set')
137 def check(self, buf):
138 if buf.endswith(self.prompt):
143 def __call__(self, buf):
144 if isinstance(buf, SSHOutput):
145 return self.check(buf.out)
147 return self.check(buf)
152 class SSHOutput(object):
153 """Содержит вывод удалённого сервера и помогает управляться с ним.
155 def __init__(self, out = '', err = ''):
160 def error(self, buf):
161 "Добавить буфер к буферу сообщения об ошибке"
164 def __add__(self, buf):
165 if isinstance(buf, self.__class__):
179 if self.out or self.err:
186 buf += '---- OUTPUT -----------'
188 buf += '---- ERRORS -----------'
192 def __getitem__(self, key):
193 return self.out.splittines()[key]
196 for line in self.out.splitlines():
200 if self.out[-1] == '\n':
206 class SSHCommunicate(object):
207 """Взаимодействие с командой ssh. В сосотоянии отправлять ей данные и принимать данные от неё.
209 Обеспчивает работу с exec_command в SSH
211 def __init__(self, stdin, stdout, stderr):
216 def __call__(self, buf=None):
219 return SSHOutput(self.out.read(), self.err.read())
228 """Метакласс для SSH соединений
232 - user: Имя пользователя
233 - passwd(не обязательно, если есть key): пароль пользователя
234 - key(не обязательно если есть passwd): файл ключа
235 - hostKeyPol[HOSTKEY_autoAdd | HOSTKEY_warn | HOSTKEY_reject]:
236 HOSTKEY_autoAdd(По умолчанию):
237 если ключ удалённого узла нам неизвестен, добавить, соединение разрешить.
238 HOSTKEY_warn: если ключ удалённого узла нам неизвестен, предупредить.
239 HOSTKEY_reject: если ключ удалённого узла нам неизвестен, разорвать соединение.
240 - timeout: таймаут операций с сервером.
242 def __init__(self, host, user, passwd=None, key=None,
243 hostKeyPol=HOSTKEY_autoAdd, timeout=None
245 if passwd == None and key == None:
246 raise SSHError('Auth type not set')
248 if hostKeyPol == HOSTKEY_autoAdd:
249 _hostKeyPol = paramiko.AutoAddPolicy()
250 elif hostKeyPol == HOSTKEY_warn:
251 _hostKeyPol = paramiko.WarningPolicy()
252 elif hostKeyPol == HOSTKEY_reject:
253 _hostKeyPol = paramiko.RejectPolicy()
255 raise SSHError('Unknown policy type')
257 self.ssh = paramiko.SSHClient()
258 self.ssh.set_missing_host_key_policy(_hostKeyPol)
260 self.ssh.connect(hostname=host, username=user, password=passwd, look_for_keys=False, allow_agent=False)
262 self.ssh.connect(hostname=host, username=user, key_filename=key, look_for_keys=False, allow_agent=False)
265 self.timeout = timeout
267 self.timeout = TIMEOUT
279 class InterSSH(_SSH):
280 """Класс Взаимодействия с сервером SSH
282 Кроме аргументов необходимых для метакласса принимает:
284 Параметр конструктора SSHPromptCheck. Принимается строго по имени.
286 def __init__(self, *a, **kwa):
288 prompt = kwa['prompt']
291 _SSH.__init__(self, *a, **kwa)
293 self.shell = self.ssh.invoke_shell()
294 self.shell.settimeout(self.timeout)
296 prompt = self._getPrompt()
300 self.prompt = SSHPromptCheck(prompt)
304 "Готово ли соединение принимать новые комманды"
307 def _getPrompt(self):
308 """Возвращает объект проверки на приглашение комммандной строки из текщего
312 while self.shell.recv_ready():
313 buf += self.shell.recv(1024)
315 func = promptChecker(self.user, self.host, buf)
316 return SSHPromptCheck(func)
319 def waitPrompt(self, prompt=None):
320 """Ожидание приглашения коммандной строки
322 Принимает необязательный аргумент: Экземпляр класса SSHPromptCheck, с помощью котороого
323 проверяется присутствие приглашения командного интерпретатора в выводе.
328 prompt = SSHPromptCheck(prompt)
331 timeCheck = TOCheck(self.timeout)
334 while self.shell.recv_stderr_ready():
335 res.error(self.shell.recv_stderr(1024))
337 while self.shell.recv_ready():
338 res += self.shell.recv(1024)
344 raise SSHError('Command call timeout\n' + repr(res))
349 def __call__(self, cmd, wait=True):
354 - wait(необязательный аргумент): Признак ожидания завершения комманды.
356 Возвращает: Экземпляр класса SSHOutput
360 res += self.waitPrompt()
361 bytes = self.shell.send('%s\n' % cmd)
363 raise SSHError('Channel closed')
365 res += self.waitPrompt()
370 def subShell(self, cmd, prompt=None):
371 """Возвращает объект SSHSubsh, через который удобно работать
372 интерпретаторами, запускаемыми из текущего, либо с ситуацией изменения приглашения
373 командного интерпретатора.
375 self(cmd, wait=False)
377 prompt = self._getPropt()
378 return SSHSubsh(self, prompt)
383 res += self.waitPrompt()
388 """Класс призванный управлять выполнением единичных комманд, без привлечения контекста.
390 Принимает аргументы необходимые для метакласса _SSH
392 def __init__(self, *a, **kwa):
393 _SSH.__init__(self, *a, **kwa)
395 def __call__(self, cmd):
396 return SSHCommunicate(*self.ssh.exec_command(cmd, timeout=self.timeout))