py.lib
23:1668cc57225b Browse Files
. Рефакторинг бессмысленный и беспощадный
Alarm.py db/migrator.py ldap_utils/ldap.py ldap_utils/ldap_passwd_changer.py ldap_utils/ldap_util.py log/slog_syslogger_tiny.py ssh_tools.py
1.1 --- a/Alarm.py Sat Nov 27 12:29:59 2021 +0300 1.2 +++ b/Alarm.py Wed Feb 23 19:27:33 2022 +0300 1.3 @@ -3,12 +3,15 @@ 1.4 1.5 from signal import SIGALRM, alarm, signal 1.6 1.7 + 1.8 class AlarmTimeout(Exception): pass 1.9 1.10 + 1.11 def _handler(sig, frame): 1.12 raise AlarmTimeout('Operation timeout') 1.13 1.14 -class MkAlarm(object): 1.15 + 1.16 +class Alarm(object): 1.17 def __init__(self, timeout): 1.18 signal(SIGALRM, _handler) 1.19 alarm(timeout) 1.20 @@ -16,8 +19,8 @@ 1.21 def __enter__(self): 1.22 pass 1.23 1.24 - def __exit__(self, eType, eObj, tb): 1.25 - if eObj == None: 1.26 + def __exit__(self, e_type, e_obj, tb): 1.27 + if e_obj is None: 1.28 alarm(0) 1.29 1.30 def __del__(self):
2.1 --- a/db/migrator.py Sat Nov 27 12:29:59 2021 +0300 2.2 +++ b/db/migrator.py Wed Feb 23 19:27:33 2022 +0300 2.3 @@ -9,7 +9,8 @@ 2.4 from os import listdir 2.5 2.6 2.7 -class MigrateError(Exception): pass 2.8 +class MigrateError(Exception): 2.9 + pass 2.10 2.11 2.12 class MigrateManager(object): 2.13 @@ -21,11 +22,11 @@ 2.14 2.15 self.schema = p_join(migrate_env, 'schema.sql') 2.16 if not exists(self.schema): 2.17 - raise MigrateError('Schema file not found: %s' % self.schema) 2.18 + raise MigrateError(f'Schema file not found: {self.schema}') 2.19 2.20 self.patch_dir = p_join(migrate_env, 'patch') 2.21 if not isdir(self.patch_dir): 2.22 - raise MigrateError('Patch dir not found or not directory: %s' % self.patch_dir) 2.23 + raise MigrateError(f'Patch dir not found or not directory: {self.patch_dir}') 2.24 2.25 def get_patch_files(self, ver: int): 2.26 res = {} 2.27 @@ -39,17 +40,13 @@ 2.28 _ver = int(_f[0]) 2.29 2.30 except (TypeError, ValueError) as e: 2.31 - raise MigrateError('Error on parse version "%(ver)s" of file "%(f)s": %(e)s' % { 2.32 - 'ver': _f[0], 2.33 - 'f': f, 2.34 - 'e': e, 2.35 - }) 2.36 + raise MigrateError(f'Error on parse version "{_f[0]}" of file "{f}": {e}') 2.37 2.38 except IndexError: 2.39 - raise MigrateError('Error on get version from filename: %s' % f) 2.40 + raise MigrateError(f'Error on get version from filename: {f}') 2.41 2.42 if _ver in res: 2.43 - raise MigrateError('Version duplicates on parse file: %s' % f) 2.44 + raise MigrateError(f'Version duplicates on parse file: {f}') 2.45 2.46 res[_ver] = p_join(self.patch_dir, f) 2.47 2.48 @@ -83,7 +80,7 @@ 2.49 2.50 def check(self, db): 2.51 cursor = db.cursor() 2.52 - cursor.execute("SELECT version FROM %s" % self.control_table) 2.53 + cursor.execute(f"SELECT version FROM {self.control_table}") 2.54 q = cursor.fetchone() 2.55 del cursor 2.56 2.57 @@ -100,12 +97,12 @@ 2.58 cursor.execute(cmd) 2.59 db.commit() 2.60 2.61 - cursor.execute("DELETE FROM %s" % self.control_table) 2.62 + cursor.execute(f"DELETE FROM {self.control_table}") 2.63 2.64 - cursor.execute(""" 2.65 - INSERT INTO %s (version) 2.66 - VALUES (%s) 2.67 - """ % (self.control_table, new_ver)) 2.68 + cursor.execute(f""" 2.69 + INSERT INTO {self.control_table} (version) 2.70 + VALUES ({new_ver}) 2.71 + """) 2.72 db.commit() 2.73 2.74 @staticmethod
3.1 --- a/ldap_utils/ldap.py Sat Nov 27 12:29:59 2021 +0300 3.2 +++ b/ldap_utils/ldap.py Wed Feb 23 19:27:33 2022 +0300 3.3 @@ -62,20 +62,23 @@ 3.4 auth_domain: str = '' 3.5 ): 3.6 3.7 - self.server_uri = server_uri # URL Доступа к северу: 'ldap://servername:port/' 3.8 - self.user_name = user_name # Имя пользователя для подключения к серверу LDAP 3.9 - self.passwd = passwd # Пароль для подключения к серверу LDAP 3.10 - self.base_dn = base_dn # База поиска 3.11 - self.timeout = timeout # Выставляемые таймауты на операцию 3.12 - self.reconnects = reconnects # Количество попыток переподключиться 3.13 - self.auth_domain = auth_domain # Имя домена авторизации 3.14 + self.server_uri = server_uri # URL Доступа к северу: 'ldap://servername:port/' 3.15 + self.user_name = user_name # Имя пользователя для подключения к серверу LDAP 3.16 + self.passwd = passwd # Пароль для подключения к серверу LDAP 3.17 + self.base_dn = base_dn # База поиска 3.18 + self.timeout = timeout # Выставляемые таймауты на операцию 3.19 + self.reconnects = reconnects # Количество попыток переподключиться 3.20 + self.auth_domain = auth_domain # Имя домена авторизации 3.21 3.22 def __str__(self): 3.23 - return f'{self.user_name}@{self.server_uri} ({self.base_dn} timeout="{self.timeout}" auth_domain="{self.auth_domain}")' 3.24 + return (f'{self.user_name}@{self.server_uri}' 3.25 + f' ({self.base_dn} timeout="{self.timeout}" auth_domain="{self.auth_domain}")') 3.26 3.27 3.28 class Ldap(object): 3.29 - escape_filter_chars = escape_filter_chars 3.30 + @staticmethod 3.31 + def filter_chars(val: str, encoding=None) -> str: 3.32 + return escape_filter_chars(val, encoding) 3.33 3.34 def __init__(self, config: LdapConfig): 3.35 self.server = Server(config.server_uri, connect_timeout=config.timeout, mode=IP_V4_PREFERRED) 3.36 @@ -151,7 +154,6 @@ 3.37 else: 3.38 raise LdapError(f'Нет атрибута CN в имени: {group}') 3.39 3.40 - 3.41 @staticmethod 3.42 def decode_acc_status(acc_status): 3.43 res = []
4.1 --- a/ldap_utils/ldap_passwd_changer.py Sat Nov 27 12:29:59 2021 +0300 4.2 +++ b/ldap_utils/ldap_passwd_changer.py Wed Feb 23 19:27:33 2022 +0300 4.3 @@ -9,41 +9,52 @@ 4.4 from secrets import token_urlsafe 4.5 import string 4.6 4.7 + 4.8 class LdapError(Exception): 4.9 @staticmethod 4.10 - def errFmt(e): 4.11 - _buf = ' '.join(str(e).split()) # всё в одну строку с одним пробелом 4.12 - # между словами 4.13 + def err_fmt(e): 4.14 + _buf = ' '.join(str(e).split()) # всё в одну строку с одним пробелом 4.15 + # между словами 4.16 return "%s(%s)" % (type(e).__name__, _buf) 4.17 - 4.18 -class LdapServerError(LdapError): pass 4.19 -class LdapPassGenError(LdapError): pass 4.20 -class LdapNotFound(LdapError): pass 4.21 + 4.22 + 4.23 +class LdapServerError(LdapError): 4.24 + pass 4.25 + 4.26 + 4.27 +class LdapPassGenError(LdapError): 4.28 + pass 4.29 + 4.30 + 4.31 +class LdapNotFound(LdapError): 4.32 + pass 4.33 4.34 4.35 class LdapPasswdChanger(object): 4.36 - def __init__(self, ldap_server_uri, ldap_user, ldap_passwd, ldap_baseDN, ldap_timeout=120, ldap_reconnect=5, passwd_len=25): 4.37 + def __init__(self, ldap_server_uri, ldap_user, ldap_passwd, ldap_base_dn, ldap_timeout=120, ldap_reconnect=5, 4.38 + passwd_len=25): 4.39 self.ldap_server_uri = ldap_server_uri # URI к серверу: ldaps://server_name:port_number 4.40 - self.ldap_user = ldap_user # Пользователь, имеющий право сброса паролей 4.41 - self.ldap_passwd = ldap_passwd # Пароль к тому пользователю 4.42 - self.ldap_baseDN = ldap_baseDN 4.43 - self.ldap_timeout = ldap_timeout # Таймаут операций с сервером 4.44 - self.passwd_len = passwd_len # Желаемая длинна пароля 4.45 - self.ldap_reconnect = ldap_reconnect # Количество попыток переподключения к серверу 4.46 + self.ldap_user = ldap_user # Пользователь, имеющий право сброса паролей 4.47 + self.ldap_passwd = ldap_passwd # Пароль к тому пользователю 4.48 + self.ldap_base_dn = ldap_base_dn 4.49 + self.ldap_timeout = ldap_timeout # Таймаут операций с сервером 4.50 + self.passwd_len = passwd_len # Желаемая длинна пароля 4.51 + self.ldap_reconnect = ldap_reconnect # Количество попыток переподключения к серверу 4.52 4.53 # Заготовка под пароли 4.54 self._set_punct = set(string.punctuation) 4.55 self._set_uper = set(string.ascii_uppercase) 4.56 self._set_lower = set(string.ascii_lowercase) 4.57 4.58 - def getPasswd(self): 4.59 + def get_passwd(self): 4.60 _cnt = 0 4.61 while True: 4.62 res = token_urlsafe(self.passwd_len) 4.63 _res_set = set(res) 4.64 - if ((_res_set & self._set_punct) 4.65 - and (_res_set & self._set_uper) 4.66 - and (_res_set & self._set_lower) 4.67 + if ( 4.68 + (_res_set & self._set_punct) 4.69 + and (_res_set & self._set_uper) 4.70 + and (_res_set & self._set_lower) 4.71 ): 4.72 return res 4.73 4.74 @@ -51,61 +62,57 @@ 4.75 if _cnt > 60: 4.76 raise LdapPassGenError('Не удалось сгенерировать пароль') 4.77 4.78 - 4.79 - def changePass(self, login): 4.80 + def change_passwd(self, login): 4.81 _reconnects = self.ldap_reconnect 4.82 if _reconnects is None or _reconnects < 1: 4.83 _reconnects = 1 4.84 4.85 - ldapConn = None 4.86 while True: 4.87 _reconnects -= 1 4.88 try: 4.89 - ldapSrv = Server(self.ldap_server_uri, connect_timeout=self.ldap_timeout) 4.90 - ldapConn = Connection(ldapSrv, authentication=SIMPLE, 4.91 - user=self.ldap_user, password=self.ldap_passwd, 4.92 - check_names=True, 4.93 - auto_referrals=False, raise_exceptions=True, auto_range=True, 4.94 - ) 4.95 - ldapConn.open() 4.96 - if not ldapConn.bind(): 4.97 + ldap_srv = Server(self.ldap_server_uri, connect_timeout=self.ldap_timeout) 4.98 + ldap_conn = Connection(ldap_srv, authentication=SIMPLE, 4.99 + user=self.ldap_user, password=self.ldap_passwd, 4.100 + check_names=True, 4.101 + auto_referrals=False, raise_exceptions=True, auto_range=True, 4.102 + ) 4.103 + ldap_conn.open() 4.104 + if not ldap_conn.bind(): 4.105 continue 4.106 4.107 break 4.108 + 4.109 except LDAPException as e: 4.110 if not _reconnects > 0: 4.111 - raise LdapServerError('Ошибка подключения к серверу: %s' % LdapServerError.errFmt(e)) 4.112 + raise LdapServerError(f'Ошибка подключения к серверу: {LdapServerError.err_fmt(e)}') 4.113 4.114 - filter = '(sAMAccountName=%s)' % login 4.115 + filter = f'(sAMAccountName={login})' 4.116 try: 4.117 - if not ldapConn.search(self.ldap_baseDN, filter): 4.118 - raise LdapServerError('Сервер вернул статус ощибки: %(desc)s(%(code)s) - %(msg)s' % { 4.119 - 'desc': ldapConn.result['description'], 4.120 - 'code': ldapConn.result['result'], 4.121 - 'msg': ldapConn.result['message'], 4.122 - }) 4.123 + if not ldap_conn.search(self.ldap_base_dn, filter): 4.124 + raise LdapServerError('Сервер вернул статус ошибки: %(desc)s(%(code)s) - %(msg)s' % { 4.125 + 'desc': ldap_conn.result['description'], 4.126 + 'code': ldap_conn.result['result'], 4.127 + 'msg': ldap_conn.result['message'], 4.128 + }) 4.129 + 4.130 except LDAPException as e: 4.131 - raise LdapServerError('Ошибка при поиска УЗ в ldap: %s' % LdapServerError.errFmt(e)) 4.132 + raise LdapServerError(f'Ошибка при поиска УЗ в ldap: {LdapServerError.err_fmt(e)}') 4.133 4.134 - if not len(ldapConn.entries) > 0: 4.135 - raise LdapNotFound('Не найдена учётная запись: %s' % login) 4.136 - elif len(ldapConn.entries) != 1: 4.137 - raise LdapError('Наёдено более одной УЗ по заданному имени %s: %s - %s' %( 4.138 - login, 4.139 - len(ldapConn.entries), 4.140 - '; '.join([i.entry_dn for i in ldapConn.entries[:10]]) 4.141 - )) 4.142 + if not len(ldap_conn.entries) > 0: 4.143 + raise LdapNotFound(f'Не найдена учётная запись: {login}') 4.144 + 4.145 + elif len(ldap_conn.entries) != 1: 4.146 + _found_names = '; '.join([i.entry_dn for i in ldap_conn.entries[:10]]) 4.147 4.148 - login_dn = ldapConn.entries[0].entry_dn 4.149 - new_passwd = self.getPasswd() 4.150 + raise LdapError(f'Найдено более одной УЗ по заданному имени {login}: {len(ldap_conn.entries)} ' 4.151 + f'- {_found_names}') 4.152 + 4.153 + login_dn = ldap_conn.entries[0].entry_dn 4.154 + new_passwd = self.get_passwd() 4.155 try: 4.156 - ldapConn.extend.microsoft.modify_password(login_dn, new_password=new_passwd, old_password=None) 4.157 + ldap_conn.extend.microsoft.modify_password(login_dn, new_password=new_passwd, old_password=None) 4.158 + 4.159 except LDAPException as e: 4.160 - raise LdapServerError('Ошибка при смене пароля на УЗ: %s' % LdapServerError.errFmt(e)) 4.161 + raise LdapServerError('Ошибка при смене пароля на УЗ: %s' % LdapServerError.err_fmt(e)) 4.162 4.163 return new_passwd 4.164 - 4.165 - 4.166 - 4.167 - 4.168 -
5.1 --- a/ldap_utils/ldap_util.py Sat Nov 27 12:29:59 2021 +0300 5.2 +++ b/ldap_utils/ldap_util.py Wed Feb 23 19:27:33 2022 +0300 5.3 @@ -2,85 +2,98 @@ 5.4 import ldap 5.5 5.6 # =========================================================== 5.7 -# Статус завершения оперций 5.8 +# Статус завершения операций 5.9 STATUS_OK = 0 # Всё ОК. 5.10 STATUS_BADAUTH = 1 # Не верные пользователь или пароль. 5.11 STATUS_SERVERDOWN = 2 # Сервер не доступен. 5.12 STATUS_SERVERERROR = 3 # Ошибка при взаимодействии с сервером. 5.13 5.14 -class LdapError(Exception): pass 5.15 + 5.16 +class LdapError(Exception): 5.17 + pass 5.18 + 5.19 5.20 class LdapAuth(object): 5.21 - def __init__(self, server, userPrefix = 'CORP\\', baseDN = 'DC=example,DC=net', attr = []): 5.22 - self.server = server 5.23 - self.prefix = userPrefix 5.24 - self.status = '' 5.25 - self.baseDN = baseDN 5.26 - self.statusCode = 0 5.27 - self.groups = None 5.28 - self.attr = None 5.29 - self._needAttr = [ i for i in map(str, attr) ] 5.30 - 5.31 - def __call__(self, user, passwd): 5.32 - try: 5.33 - conn = ldap.initialize(self.server) 5.34 - conn.set_option(ldap.OPT_REFERRALS, 0) 5.35 - conn.simple_bind_s(self.prefix + user, passwd) 5.36 - except ldap.INVALID_CREDENTIALS: 5.37 - conn.unbind() 5.38 - self.status = 'Invalid credentials' 5.39 - self.statusCode = STATUS_BADAUTH 5.40 - return False 5.41 - except ldap.SERVER_DOWN: 5.42 - self.status = 'Server is down' 5.43 - self.statusCode = STATUS_SERVERDOWN 5.44 - return False 5.45 - 5.46 - self.groups = [] 5.47 - try: 5.48 - ldapData = conn.search_s(self.baseDN, ldap.SCOPE_SUBTREE, '(cn=%s)' % user, ['memberOf'] + self._needAttr)[0][1] 5.49 - for i in ldapData['memberOf']: 5.50 - self.groups.append(i.split(',')[0].split('=')[1].decode('utf-8')) 5.51 - del ldapData['memberOf'] 5.52 - self.attr = ldapData 5.53 - except KeyError: 5.54 - self.status = 'User object from LDAP is wrong, it can be anonymous logon' 5.55 - self.statusCode = STATUS_SERVERERROR 5.56 - return False 5.57 - finally: 5.58 - conn.unbind() 5.59 + def __init__(self, server, user_prefix='CORP\\', base_dn='DC=example,DC=net', attr=None): 5.60 + self.server = server 5.61 + self.prefix = user_prefix 5.62 + self.status = '' 5.63 + self.baseDN = base_dn 5.64 + self.status_code = 0 5.65 + self.groups = None 5.66 + self.attr = None 5.67 + self._needAttr = [i for i in map(str, attr)] if attr is not None else [] 5.68 + 5.69 + def __call__(self, user, passwd): 5.70 + conn = None 5.71 + try: 5.72 + conn = ldap.initialize(self.server) 5.73 + conn.set_option(ldap.OPT_REFERRALS, 0) 5.74 + conn.simple_bind_s(self.prefix + user, passwd) 5.75 + 5.76 + except ldap.INVALID_CREDENTIALS: 5.77 + if conn is not None: 5.78 + conn.unbind() 5.79 + 5.80 + self.status = 'Invalid credentials' 5.81 + self.status_code = STATUS_BADAUTH 5.82 + return False 5.83 + 5.84 + except ldap.SERVER_DOWN: 5.85 + self.status = 'Server is down' 5.86 + self.status_code = STATUS_SERVERDOWN 5.87 + return False 5.88 + 5.89 + self.groups = [] 5.90 + try: 5.91 + ldap_data = conn.search_s(self.baseDN, ldap.SCOPE_SUBTREE, f'(cn={user})', 5.92 + ['memberOf'] + self._needAttr)[0][1] 5.93 + 5.94 + for i in ldap_data['memberOf']: 5.95 + self.groups.append(i.split(',')[0].split('=')[1].decode('utf-8')) 5.96 + 5.97 + del ldap_data['memberOf'] 5.98 + self.attr = ldap_data 5.99 5.100 - return True 5.101 - 5.102 - def __getitem__(self, key): 5.103 - return self.attr[key] 5.104 - 5.105 - def memberOf(self, group): 5.106 - """Проверка на присутствие у пользователя некоторой группы 5.107 - """ 5.108 - if self.groups == None: 5.109 - raise LdapError('Request membership before auth call.') 5.110 - 5.111 - if not isinstance(group, unicode): 5.112 - if isinstance(group, str): 5.113 - group = group.decode('utf-8') 5.114 - else: 5.115 - group = str(group).decode('utf-8') 5.116 - 5.117 - if group in self.groups: 5.118 - return True 5.119 - else: 5.120 - return False 5.121 - 5.122 - def __contains__(self, group): 5.123 - return self.memberOf(group) 5.124 - 5.125 - def memberOfGroups(self, groups): 5.126 - if not len(groups) > 0: 5.127 - return False 5.128 - 5.129 - for group in groups: 5.130 - if not group in self: 5.131 - return False 5.132 - 5.133 - return True 5.134 + except KeyError: 5.135 + self.status = 'User object from LDAP is wrong, it can be anonymous logon' 5.136 + self.status_code = STATUS_SERVERERROR 5.137 + return False 5.138 + 5.139 + finally: 5.140 + conn.unbind() 5.141 + 5.142 + return True 5.143 + 5.144 + def __getitem__(self, key): 5.145 + return self.attr[key] 5.146 + 5.147 + def member_of(self, group): 5.148 + """Проверка на присутствие у пользователя некоторой группы 5.149 + """ 5.150 + if self.groups is None: 5.151 + raise LdapError('Request membership before auth call') 5.152 + 5.153 + if not isinstance(group, unicode): 5.154 + if isinstance(group, str): 5.155 + group = group.decode('utf-8') 5.156 + else: 5.157 + group = str(group).decode('utf-8') 5.158 + 5.159 + if group in self.groups: 5.160 + return True 5.161 + else: 5.162 + return False 5.163 + 5.164 + def __contains__(self, group): 5.165 + return self.member_of(group) 5.166 + 5.167 + def member_of_groups(self, groups): 5.168 + if not len(groups) > 0: 5.169 + return False 5.170 + 5.171 + for group in groups: 5.172 + if group not in self: 5.173 + return False 5.174 + 5.175 + return True
6.1 --- a/log/slog_syslogger_tiny.py Sat Nov 27 12:29:59 2021 +0300 6.2 +++ b/log/slog_syslogger_tiny.py Wed Feb 23 19:27:33 2022 +0300 6.3 @@ -46,11 +46,11 @@ 6.4 6.5 class SimpleSysLogger(object): 6.6 @staticmethod 6.7 - def initSyslog(ident): 6.8 + def init_syslog(ident): 6.9 syslog.openlog(ident, syslog.LOG_PID) 6.10 6.11 @staticmethod 6.12 - def getTiming(name=None): 6.13 + def get_timing(name=None): 6.14 return Timing(name) 6.15 6.16 def __init__(self, prefix, facility=syslog.LOG_USER): 6.17 @@ -59,7 +59,7 @@ 6.18 6.19 def _write(self, flag, mark, msg): 6.20 for l in str(msg).splitlines(): 6.21 - syslog.syslog(self.facility | flag, '%s: %s %s' % (self.prefix, mark, l)) 6.22 + syslog.syslog(self.facility | flag, f'{self.prefix}: {mark} {l}') 6.23 6.24 def __call__(self, msg): 6.25 self._write(syslog.LOG_INFO, '.', msg) 6.26 @@ -76,24 +76,28 @@ 6.27 def alert(self, msg): 6.28 self._write(syslog.LOG_ALERT, '#', msg) 6.29 6.30 - def sublog(self, prefix): 6.31 + def sub_log(self, prefix): 6.32 return self.__class__('%s/%s' % (self.prefix, prefix), self.facility) 6.33 6.34 - def excpt(self, msg, eClass=None, eObj=None, eTb=None, stack_skip=0): 6.35 - if eClass is None: 6.36 - eClass, eObj, eTb = exc_info() 6.37 + def excpt(self, msg, e_class=None, e_obj=None, e_tback=None, stack_skip=0): 6.38 + if e_class is None: 6.39 + e_class, e_obj, e_tback = exc_info() 6.40 6.41 - tbData_tb = list(extract_tb(eTb))[::-1] 6.42 - tbData_stack = list(extract_stack())[::-1][(2 + stack_skip):] 6.43 + tb_data_tb = list(extract_tb(e_tback))[::-1] 6.44 + tb_data_stack = list(extract_stack())[::-1][(2 + stack_skip):] 6.45 + 6.46 self.err(msg) 6.47 self.err('--- EXCEPTION ---') 6.48 - self.err(' %s (%s)' % (eClass.__name__, eObj)) 6.49 + self.err(' %s (%s)' % (e_class.__name__, e_obj)) 6.50 + 6.51 self.err('--- TRACEBACK ---') 6.52 - for _tbFile, _tbLine, _tbFunc, _tbText in tbData_tb: 6.53 - self.err('File: %s, line %s in %s' % (_tbFile, _tbLine, _tbFunc)) 6.54 - self.err(' %s' % _tbText) 6.55 + for _tb_file, _tb_line, _tb_func, _tb_text in tb_data_tb: 6.56 + self.err('File: %s, line %s in %s' % (_tb_file, _tb_line, _tb_func)) 6.57 + self.err(' %s' % _tb_text) 6.58 + 6.59 self.err('>>> Exception Handler <<<') 6.60 - for _tbFile, _tbLine, _tbFunc, _tbText in tbData_stack: 6.61 - self.err('File: %s, line %s in %s' % (_tbFile, _tbLine, _tbFunc)) 6.62 - self.err(' %s' % _tbText) 6.63 + for _tb_file, _tb_line, _tb_func, _tb_text in tb_data_stack: 6.64 + self.err('File: %s, line %s in %s' % (_tb_file, _tb_line, _tb_func)) 6.65 + self.err(' %s' % _tb_text) 6.66 + 6.67 self.err('--- END EXCEPTION ---')
7.1 --- a/ssh_tools.py Sat Nov 27 12:29:59 2021 +0300 7.2 +++ b/ssh_tools.py Wed Feb 23 19:27:33 2022 +0300 7.3 @@ -11,387 +11,423 @@ 7.4 HOSTKEY_warn = 1 7.5 HOSTKEY_reject = 2 7.6 7.7 -def promptChecker(user, host, buf): 7.8 - Signs = ['[', ']', '#', '<', '>', '$', '!', '%', '(', ')', '@', '+', ':', ';', ' ', '\t'] 7.9 - isPrompt = False 7.10 - isSep = True 7.11 - newLites = 0 7.12 - endBuf = '' 7.13 - for i in range(1, len(buf) + 1): 7.14 - if not isPrompt: 7.15 - if buf[-i] == '\n': 7.16 - endBuf = buf[-i] + endBuf 7.17 - continue 7.18 - isPrompt = True 7.19 - if isSep: 7.20 - if buf[-i] in Signs: 7.21 - endBuf = buf[-i] + endBuf 7.22 - continue 7.23 - isSep = False 7.24 - if buf[-i] == '\n': 7.25 - break 7.26 - _startProc = len(buf) - i 7.27 - _endProc = len(buf) - len(endBuf) 7.28 - buf = buf[_startProc:_endProc] 7.29 - searchCond = [] 7.30 - for i in [user, host]: 7.31 - bufL = '' 7.32 - bufR = '' 7.33 - index = buf.find(i) 7.34 - if index != -1: 7.35 - for ch in buf[:index][::-1]: 7.36 - if ch in Signs: 7.37 - bufL = ch + bufL 7.38 - else: 7.39 - break 7.40 - index = index + len(i) 7.41 - for ch in buf[index:]: 7.42 - if ch in Signs: 7.43 - bufR += ch 7.44 - else: 7.45 - break 7.46 - searchCond.append(bufL + i + bufR) 7.47 - 7.48 - def Check(buf): 7.49 - if buf.endswith(endBuf): 7.50 - endSearch = len(buf) - len(endBuf) 7.51 - startSearch = buf.rfind('\n', 0, len(buf) - len(endBuf)) 7.52 - 7.53 - if startSearch == -1: startSearch = 0 7.54 - 7.55 - _buf = buf[startSearch:endSearch] 7.56 - for i in searchCond: 7.57 - if _buf.find(i) == -1: return False 7.58 - return True 7.59 - else: 7.60 - return False 7.61 - return Check 7.62 - 7.63 + 7.64 +def prompt_checker(user, host, buf): 7.65 + signs = ['[', ']', '#', '<', '>', '$', '!', '%', '(', ')', '@', '+', ':', ';', ' ', '\t'] 7.66 + is_prompt = False 7.67 + is_sep = True 7.68 + end_buf = '' 7.69 + 7.70 + ch_pos = 0 7.71 + for ch_pos in range(1, len(buf) + 1): 7.72 + if not is_prompt: 7.73 + if buf[-ch_pos] == '\n': 7.74 + end_buf = buf[-ch_pos] + end_buf 7.75 + continue 7.76 + is_prompt = True 7.77 + 7.78 + if is_sep: 7.79 + if buf[-ch_pos] in signs: 7.80 + end_buf = buf[-ch_pos] + end_buf 7.81 + continue 7.82 + is_sep = False 7.83 + 7.84 + if buf[-ch_pos] == '\n': 7.85 + break 7.86 + 7.87 + _startProc = len(buf) - ch_pos 7.88 + _endProc = len(buf) - len(end_buf) 7.89 + buf = buf[_startProc:_endProc] 7.90 + search_cond = [] 7.91 7.92 -class SSHError(Exception): pass 7.93 + for ch_pos in [user, host]: 7.94 + buf_l = '' 7.95 + buf_r = '' 7.96 + index = buf.find(ch_pos) 7.97 + if index != -1: 7.98 + for ch in buf[:index][::-1]: 7.99 + if ch in signs: 7.100 + buf_l = ch + buf_l 7.101 + else: 7.102 + break 7.103 + index = index + len(ch_pos) 7.104 + for ch in buf[index:]: 7.105 + if ch in signs: 7.106 + buf_r += ch 7.107 + else: 7.108 + break 7.109 + search_cond.append(buf_l + ch_pos + buf_r) 7.110 + 7.111 + def check(check_buf): 7.112 + if check_buf.endswith(end_buf): 7.113 + end_search = len(check_buf) - len(end_buf) 7.114 + start_search = check_buf.rfind('\n', 0, len(check_buf) - len(end_buf)) 7.115 + 7.116 + if start_search == -1: 7.117 + start_search = 0 7.118 + 7.119 + _buf = check_buf[start_search:end_search] 7.120 + for i in search_cond: 7.121 + if _buf.find(i) == -1: 7.122 + return False 7.123 + 7.124 + return True 7.125 + 7.126 + else: 7.127 + return False 7.128 + 7.129 + return check 7.130 + 7.131 + 7.132 +class SSHError(Exception): 7.133 + pass 7.134 + 7.135 7.136 class TOCheck(object): 7.137 - """ 7.138 - Взято из: https://bitbucket.org/awgur/pylib/src/default/timetools.py 7.139 - """ 7.140 - def __init__(self, timeOut): 7.141 - self.start = datetime.now() 7.142 - self.timeOut = timeOut 7.143 - 7.144 - def __call__(self, timeOut=None): 7.145 - if not timeOut: 7.146 - timeOut = self.timeOut 7.147 - 7.148 - buf = datetime.now() - self.start 7.149 - 7.150 - if buf.seconds > timeOut: 7.151 - return True 7.152 - else: 7.153 - return False 7.154 - 7.155 - def __bool__(self): 7.156 - return not self(): 7.157 + """ 7.158 + Взято из: https://bitbucket.org/awgur/pylib/src/default/timetools.py 7.159 + """ 7.160 + def __init__(self, timeout): 7.161 + self.start = datetime.now() 7.162 + self.timeout = timeout 7.163 + 7.164 + def __call__(self, timeout=None): 7.165 + if not timeout: 7.166 + timeout = self.timeout 7.167 + 7.168 + buf = datetime.now() - self.start 7.169 + 7.170 + if buf.seconds > timeout: 7.171 + return True 7.172 + 7.173 + else: 7.174 + return False 7.175 + 7.176 + def __bool__(self): 7.177 + return not self() 7.178 7.179 7.180 class SSHSubsh(object): 7.181 - """Класс для облегчения работы с контекстами, когда приглашение коммандной оболочки 7.182 - меняется в результате исполнения комманды. 7.183 - """ 7.184 - def __init__(self, ssh, prompt): 7.185 - self.ssh = ssh 7.186 - self.prompt = SSHPromptCheck(prompt) 7.187 - 7.188 - def __call__(self, cmd): 7.189 - res = self.ssh(cmd, wait=False) 7.190 - res += self.waitPrompt(self.prompt) 7.191 - return res 7.192 + """Класс для облегчения работы с контекстами, когда приглашение коммандной оболочки 7.193 + меняется в результате исполнения комманды. 7.194 + """ 7.195 + def __init__(self, ssh, prompt): 7.196 + self.ssh = ssh 7.197 + self.prompt = SSHPromptCheck(prompt) 7.198 + 7.199 + def __call__(self, cmd): 7.200 + res = self.ssh(cmd, wait=False) 7.201 + res += self.waitPrompt(self.prompt) 7.202 + return res 7.203 + 7.204 7.205 class SSHPromptCheck(object): 7.206 - """ 7.207 - Проверяет, появилось ли приглашение коммандной строки в выводе сервера 7.208 - 7.209 - Принимает: 7.210 - - prompt: 7.211 - Строка, которая должна однозначно идентифицировать приглашение 7.212 - коммандной строки, либо функция которой передаётся строка, и она 7.213 - должна вернуть True, если строка завершается приглашением коммандной 7.214 - строки либо False в обратном случае. Кроме того можно передат сам объект, 7.215 - созданный заранее. 7.216 - """ 7.217 - def __init__(self, prompt): 7.218 - if isinstance(prompt, self.__class__): 7.219 - self.prompt = prompt.prompt 7.220 - self.check = prompt.check 7.221 - else: 7.222 - def isFunc(): pass 7.223 - if type(prompt) == type(isFunc): 7.224 - self.check = prompt 7.225 - self.prompt = None 7.226 - else: 7.227 - self.prompt = prompt 7.228 - 7.229 - if self.prompt == None: 7.230 - raise SSHError('Prompt not set') 7.231 - 7.232 - 7.233 - def check(self, buf): 7.234 - if buf.endswith(self.prompt): 7.235 - return True 7.236 - else: 7.237 - return False 7.238 - 7.239 - def __call__(self, buf): 7.240 - if isinstance(buf, SSHOutput): 7.241 - return self.check(buf.out) 7.242 - elif buf: 7.243 - return self.check(buf) 7.244 - else: 7.245 - return False 7.246 - 7.247 - 7.248 + """ 7.249 + Проверяет, появилось ли приглашение коммандной строки в выводе сервера 7.250 + 7.251 + Принимает: 7.252 + - prompt: 7.253 + Строка, которая должна однозначно идентифицировать приглашение 7.254 + коммандной строки, либо функция которой передаётся строка, и она 7.255 + должна вернуть True, если строка завершается приглашением коммандной 7.256 + строки либо False в обратном случае. Кроме того можно передат сам объект, 7.257 + созданный заранее. 7.258 + """ 7.259 + def __init__(self, prompt): 7.260 + if isinstance(prompt, self.__class__): 7.261 + self.prompt = prompt.prompt 7.262 + self.check = prompt.check 7.263 + 7.264 + else: 7.265 + def is_func(): 7.266 + pass 7.267 + 7.268 + if type(prompt) == type(is_func): 7.269 + self.check = prompt 7.270 + self.prompt = None 7.271 + 7.272 + else: 7.273 + self.prompt = prompt 7.274 + 7.275 + if self.prompt is None: 7.276 + raise SSHError('Prompt not set') 7.277 + 7.278 + def check(self, buf): 7.279 + if buf.endswith(self.prompt): 7.280 + return True 7.281 + 7.282 + else: 7.283 + return False 7.284 + 7.285 + def __call__(self, buf): 7.286 + if isinstance(buf, SSHOutput): 7.287 + return self.check(buf.out) 7.288 + 7.289 + elif buf: 7.290 + return self.check(buf) 7.291 + 7.292 + else: 7.293 + return False 7.294 + 7.295 + 7.296 class SSHOutput(object): 7.297 - """Содержит вывод удалённого сервера и помогает управляться с ним. 7.298 - """ 7.299 - def __init__(self, out = '', err = ''): 7.300 - self.out = out 7.301 - self.err = err 7.302 - 7.303 - 7.304 - def error(self, buf): 7.305 - "Добавить буфер к буферу сообщения об ошибке" 7.306 - self.err += buf 7.307 - 7.308 - def __add__(self, buf): 7.309 - if isinstance(buf, self.__class__): 7.310 - self.out += buf.out 7.311 - self.err += buf.err 7.312 - else: 7.313 - self.out += buf 7.314 - return self 7.315 - 7.316 - def __str__(self): 7.317 - return self.out 7.318 - 7.319 - def getError(self): 7.320 - return self.err 7.321 - 7.322 - def __bool__(self): 7.323 - if self.out or self.err: 7.324 - return True 7.325 - else: 7.326 - return False 7.327 - 7.328 - def __repr__(self): 7.329 - buf = '' 7.330 - buf += '---- OUTPUT -----------' 7.331 - buf += self.out 7.332 - buf += '---- ERRORS -----------' 7.333 - buf += self.err 7.334 - return buf 7.335 - 7.336 - def __getitem__(self, key): 7.337 - return self.out.splittines()[key] 7.338 - 7.339 - def __iter__(self): 7.340 - for line in self.out.splitlines(): 7.341 - yield line 7.342 - 7.343 - def isEol(self): 7.344 - if self.out[-1] == '\n': 7.345 - return True 7.346 - else: 7.347 - return False 7.348 - 7.349 - 7.350 + """Содержит вывод удалённого сервера и помогает управляться с ним. 7.351 + """ 7.352 + 7.353 + def __init__(self, out='', err=''): 7.354 + self.out = out 7.355 + self.err = err 7.356 + 7.357 + def error(self, buf): 7.358 + """Добавить буфер к буферу сообщения об ошибке""" 7.359 + 7.360 + self.err += buf 7.361 + 7.362 + def __add__(self, buf): 7.363 + if isinstance(buf, self.__class__): 7.364 + self.out += buf.out 7.365 + self.err += buf.err 7.366 + else: 7.367 + self.out += buf 7.368 + return self 7.369 + 7.370 + def __str__(self): 7.371 + return self.out 7.372 + 7.373 + def get_error(self): 7.374 + return self.err 7.375 + 7.376 + def __bool__(self): 7.377 + if self.out or self.err: 7.378 + return True 7.379 + else: 7.380 + return False 7.381 + 7.382 + def __repr__(self): 7.383 + buf = '' 7.384 + buf += '---- OUTPUT -----------' 7.385 + buf += self.out 7.386 + buf += '---- ERRORS -----------' 7.387 + buf += self.err 7.388 + return buf 7.389 + 7.390 + def __getitem__(self, key): 7.391 + return self.out.splittines()[key] 7.392 + 7.393 + def __iter__(self): 7.394 + for line in self.out.splitlines(): 7.395 + yield line 7.396 + 7.397 + def is_eol(self): 7.398 + if self.out[-1] == '\n': 7.399 + return True 7.400 + else: 7.401 + return False 7.402 + 7.403 + 7.404 class SSHCommunicate(object): 7.405 - """Взаимодействие с командой ssh. В сосотоянии отправлять ей данные и принимать данные от неё. 7.406 - 7.407 - Обеспчивает работу с exec_command в SSH 7.408 - """ 7.409 - def __init__(self, stdin, stdout, stderr): 7.410 - self.inC = stdin 7.411 - self.out = stdout 7.412 - self.err = stderr 7.413 - 7.414 - def __call__(self, buf=None): 7.415 - if buf != None: 7.416 - self.inC.write(buf) 7.417 - return SSHOutput(self.out.read(), self.err.read()) 7.418 - 7.419 - def __str__(self): 7.420 - return str(self()) 7.421 - 7.422 - def __repr__(self): 7.423 - return repr(self()) 7.424 + """\ 7.425 + Взаимодействие с командой ssh. В сосотоянии отправлять ей данные и принимать данные от неё. 7.426 + 7.427 + Обеспечивает работу с exec_command в SSH 7.428 + """ 7.429 + 7.430 + def __init__(self, stdin, stdout, stderr): 7.431 + self.inC = stdin 7.432 + self.out = stdout 7.433 + self.err = stderr 7.434 + 7.435 + def __call__(self, buf=None): 7.436 + if buf is not None: 7.437 + self.inC.write(buf) 7.438 + return SSHOutput(self.out.read(), self.err.read()) 7.439 + 7.440 + def __str__(self): 7.441 + return str(self()) 7.442 + 7.443 + def __repr__(self): 7.444 + return repr(self()) 7.445 + 7.446 7.447 class _SSH(object): 7.448 - """Метакласс для SSH соединений 7.449 - 7.450 - Принимает: 7.451 - - host: Имя узла 7.452 - - user: Имя пользователя 7.453 - - passwd(не обязательно, если есть key): пароль пользователя 7.454 - - key(не обязательно если есть passwd): файл ключа 7.455 - - hostKeyPol[HOSTKEY_autoAdd | HOSTKEY_warn | HOSTKEY_reject]: 7.456 - HOSTKEY_autoAdd(По умолчанию): 7.457 - если ключ удалённого узла нам неизвестен, добавить, соединение разрешить. 7.458 - HOSTKEY_warn: если ключ удалённого узла нам неизвестен, предупредить. 7.459 - HOSTKEY_reject: если ключ удалённого узла нам неизвестен, разорвать соединение. 7.460 - - timeout: таймаут операций с сервером. 7.461 - """ 7.462 - def __init__(self, host, user, passwd=None, key=None, 7.463 - hostKeyPol=HOSTKEY_autoAdd, timeout=None 7.464 - ): 7.465 - if passwd == None and key == None: 7.466 - raise SSHError('Auth type not set') 7.467 - 7.468 - if hostKeyPol == HOSTKEY_autoAdd: 7.469 - _hostKeyPol = paramiko.AutoAddPolicy() 7.470 - elif hostKeyPol == HOSTKEY_warn: 7.471 - _hostKeyPol = paramiko.WarningPolicy() 7.472 - elif hostKeyPol == HOSTKEY_reject: 7.473 - _hostKeyPol = paramiko.RejectPolicy() 7.474 - else: 7.475 - raise SSHError('Unknown policy type') 7.476 - 7.477 - self.ssh = paramiko.SSHClient() 7.478 - self.ssh.set_missing_host_key_policy(_hostKeyPol) 7.479 - if passwd: 7.480 - self.ssh.connect(hostname=host, username=user, password=passwd, look_for_keys=False, allow_agent=False) 7.481 - else: 7.482 - self.ssh.connect(hostname=host, username=user, key_filename=key, look_for_keys=False, allow_agent=False) 7.483 - 7.484 - if timeout != None: 7.485 - self.timeout = timeout 7.486 - else: 7.487 - self.timeout = TIMEOUT 7.488 - 7.489 - self.user = user 7.490 - self.host = host 7.491 - 7.492 - def __del__(self): 7.493 - try: 7.494 - self.ssh.close() 7.495 - except: 7.496 - pass 7.497 - del self.ssh 7.498 - 7.499 + """\ 7.500 + Метакласс для SSH соединений 7.501 + 7.502 + Принимает: 7.503 + - host: Имя узла 7.504 + - user: Имя пользователя 7.505 + - passwd(не обязательно, если есть key): пароль пользователя 7.506 + - key(не обязательно если есть passwd): файл ключа 7.507 + - hostKeyPol[HOSTKEY_autoAdd | HOSTKEY_warn | HOSTKEY_reject]: 7.508 + HOSTKEY_autoAdd(По умолчанию): 7.509 + если ключ удалённого узла нам неизвестен, добавить, соединение разрешить. 7.510 + HOSTKEY_warn: если ключ удалённого узла нам неизвестен, предупредить. 7.511 + HOSTKEY_reject: если ключ удалённого узла нам неизвестен, разорвать соединение. 7.512 + - timeout: таймаут операций с сервером. 7.513 + """ 7.514 + 7.515 + def __init__(self, host, user, passwd=None, key=None, 7.516 + host_key_pol=HOSTKEY_autoAdd, timeout=None 7.517 + ): 7.518 + 7.519 + if passwd is None and key is None: 7.520 + raise SSHError('Auth type not set') 7.521 + 7.522 + if host_key_pol == HOSTKEY_autoAdd: 7.523 + _hostKeyPol = paramiko.AutoAddPolicy() 7.524 + elif host_key_pol == HOSTKEY_warn: 7.525 + _hostKeyPol = paramiko.WarningPolicy() 7.526 + elif host_key_pol == HOSTKEY_reject: 7.527 + _hostKeyPol = paramiko.RejectPolicy() 7.528 + else: 7.529 + raise SSHError('Unknown policy type') 7.530 + 7.531 + self.ssh = paramiko.SSHClient() 7.532 + self.ssh.set_missing_host_key_policy(_hostKeyPol) 7.533 + if passwd: 7.534 + self.ssh.connect(hostname=host, username=user, password=passwd, look_for_keys=False, allow_agent=False) 7.535 + else: 7.536 + self.ssh.connect(hostname=host, username=user, key_filename=key, look_for_keys=False, allow_agent=False) 7.537 + 7.538 + if timeout is not None: 7.539 + self.timeout = timeout 7.540 + 7.541 + else: 7.542 + self.timeout = TIMEOUT 7.543 + 7.544 + self.user = user 7.545 + self.host = host 7.546 + 7.547 + def __del__(self): 7.548 + try: 7.549 + self.ssh.close() 7.550 + 7.551 + except: 7.552 + pass 7.553 + 7.554 + del self.ssh 7.555 + 7.556 + 7.557 class InterSSH(_SSH): 7.558 - """Класс Взаимодействия с сервером SSH 7.559 - 7.560 - Кроме аргументов необходимых для метакласса принимает: 7.561 - - prompt: 7.562 - Параметр конструктора SSHPromptCheck. Принимается строго по имени. 7.563 - """ 7.564 - def __init__(self, *a, **kwa): 7.565 - if 'prompt' in kwa: 7.566 - prompt = kwa['prompt'] 7.567 - del kwa['prompt'] 7.568 - 7.569 - _SSH.__init__(self, *a, **kwa) 7.570 - 7.571 - self.shell = self.ssh.invoke_shell() 7.572 - self.shell.settimeout(self.timeout) 7.573 - if prompt == None: 7.574 - prompt = self._getPrompt() 7.575 - self.ready = True 7.576 - else: 7.577 - self.ready = False 7.578 - self.prompt = SSHPromptCheck(prompt) 7.579 + """Класс Взаимодействия с сервером SSH 7.580 + 7.581 + Кроме аргументов необходимых для метакласса принимает: 7.582 + - prompt: 7.583 + Параметр конструктора SSHPromptCheck. Принимается строго по имени. 7.584 + """ 7.585 + def __init__(self, *a, **kwa): 7.586 + prompt = None 7.587 + if 'prompt' in kwa: 7.588 + prompt = kwa['prompt'] 7.589 + del kwa['prompt'] 7.590 + 7.591 + _SSH.__init__(self, *a, **kwa) 7.592 + 7.593 + self.shell = self.ssh.invoke_shell() 7.594 + self.shell.settimeout(self.timeout) 7.595 + if prompt is None: 7.596 + prompt = self._get_prompt() 7.597 + self.ready = True 7.598 + 7.599 + else: 7.600 + self.ready = False 7.601 + 7.602 + self.prompt = SSHPromptCheck(prompt) 7.603 7.604 - 7.605 - def __bool__(self): 7.606 - "Готово ли соединение принимать новые комманды" 7.607 - return self.ready 7.608 - 7.609 - def _getPrompt(self): 7.610 - """Возвращает объект проверки на приглашение комммандной строки из текщего 7.611 - буфера вывода. 7.612 - """ 7.613 - buf = '' 7.614 - while self.shell.recv_ready(): 7.615 - buf += self.shell.recv(1024) 7.616 - 7.617 - func = promptChecker(self.user, self.host, buf) 7.618 - return SSHPromptCheck(func) 7.619 + def __bool__(self): 7.620 + """Готово ли соединение принимать новые команды""" 7.621 + return self.ready 7.622 + 7.623 + def _get_prompt(self): 7.624 + """Возвращает объект проверки на приглашение комммандной строки из текщего 7.625 + буфера вывода. 7.626 + """ 7.627 + buf = '' 7.628 + while self.shell.recv_ready(): 7.629 + buf += self.shell.recv(1024) 7.630 + 7.631 + func = prompt_checker(self.user, self.host, buf) 7.632 + return SSHPromptCheck(func) 7.633 + 7.634 + def wait_prompt(self, prompt=None): 7.635 + """\ 7.636 + Ожидание приглашения командной строки 7.637 7.638 - 7.639 - def waitPrompt(self, prompt=None): 7.640 - """Ожидание приглашения коммандной строки 7.641 - 7.642 - Принимает необязательный аргумент: Экземпляр класса SSHPromptCheck, с помощью котороого 7.643 - проверяется присутствие приглашения командного интерпретатора в выводе. 7.644 - """ 7.645 - if prompt == None: 7.646 - prompt = self.prompt 7.647 - else: 7.648 - prompt = SSHPromptCheck(prompt) 7.649 - 7.650 - if not self.ready: 7.651 - timeCheck = TOCheck(self.timeout) 7.652 - res = SSHOutput() 7.653 - while True: 7.654 - while self.shell.recv_stderr_ready(): 7.655 - res.error(self.shell.recv_stderr(1024)) 7.656 + Принимает необязательный аргумент: Экземпляр класса SSHPromptCheck, с помощью котороого 7.657 + проверяется присутствие приглашения командного интерпретатора в выводе. 7.658 + """ 7.659 + 7.660 + if prompt is None: 7.661 + prompt = self.prompt 7.662 + 7.663 + else: 7.664 + prompt = SSHPromptCheck(prompt) 7.665 7.666 - while self.shell.recv_ready(): 7.667 - res += self.shell.recv(1024) 7.668 + res = SSHOutput() 7.669 + if not self.ready: 7.670 + time_check = TOCheck(self.timeout) 7.671 + while True: 7.672 + while self.shell.recv_stderr_ready(): 7.673 + res.error(self.shell.recv_stderr(1024)) 7.674 + 7.675 + while self.shell.recv_ready(): 7.676 + res += self.shell.recv(1024) 7.677 + 7.678 + if prompt(res): 7.679 + break 7.680 7.681 - if prompt(res): 7.682 - break 7.683 - 7.684 - if timeCheck(): 7.685 - raise SSHError('Command call timeout\n' + repr(res)) 7.686 - 7.687 - self.ready = True 7.688 - return res 7.689 + if time_check(): 7.690 + raise SSHError('Command call timeout\n' + repr(res)) 7.691 + 7.692 + self.ready = True 7.693 + return res 7.694 + 7.695 + def __call__(self, cmd, wait=True): 7.696 + """Запуск команды 7.697 + 7.698 + Принимает: 7.699 + - cmd: Комманда 7.700 + - wait(необязательный аргумент): Признак ожидания завершения комманды. 7.701 7.702 - def __call__(self, cmd, wait=True): 7.703 - """Запуск команды 7.704 - 7.705 - Принимает: 7.706 - - cmd: Комманда 7.707 - - wait(необязательный аргумент): Признак ожидания завершения комманды. 7.708 - 7.709 - Возвращает: Экземпляр класса SSHOutput 7.710 - """ 7.711 - res = SSHOutput() 7.712 - if not self.ready: 7.713 - res += self.waitPrompt() 7.714 - bytes = self.shell.send('%s\n' % cmd) 7.715 - if bytes == 0: 7.716 - raise SSHError('Channel closed') 7.717 - if wait: 7.718 - res += self.waitPrompt() 7.719 - else: 7.720 - self.ready = False 7.721 - return res 7.722 + Возвращает: Экземпляр класса SSHOutput 7.723 + """ 7.724 + res = SSHOutput() 7.725 + if not self.ready: 7.726 + res += self.wait_prompt() 7.727 + bytes = self.shell.send('%s\n' % cmd) 7.728 + if bytes == 0: 7.729 + raise SSHError('Channel closed') 7.730 + if wait: 7.731 + res += self.wait_prompt() 7.732 + else: 7.733 + self.ready = False 7.734 + return res 7.735 7.736 - def subShell(self, cmd, prompt=None): 7.737 - """Возвращает объект SSHSubsh, через который удобно работать 7.738 - интерпретаторами, запускаемыми из текущего, либо с ситуацией изменения приглашения 7.739 - командного интерпретатора. 7.740 - """ 7.741 - self(cmd, wait=False) 7.742 - if prompt == None: 7.743 - prompt = self._getPropt() 7.744 - return SSHSubsh(self, prompt) 7.745 - 7.746 - def close(self): 7.747 - res = SSHOutput 7.748 - if not self: 7.749 - res += self.waitPrompt() 7.750 - self.ssh.close() 7.751 - return res 7.752 + def sub_shell(self, cmd, prompt=None): 7.753 + """Возвращает объект SSHSubsh, через который удобно работать 7.754 + интерпретаторами, запускаемыми из текущего, либо с ситуацией изменения приглашения 7.755 + командного интерпретатора. 7.756 + """ 7.757 + self(cmd, wait=False) 7.758 + if prompt is None: 7.759 + prompt = self._getPropt() 7.760 + return SSHSubsh(self, prompt) 7.761 + 7.762 + def close(self): 7.763 + res = SSHOutput 7.764 + if not self: 7.765 + res += self.wait_prompt() 7.766 + self.ssh.close() 7.767 + return res 7.768 + 7.769 7.770 class SSH(_SSH): 7.771 - """Класс призванный управлять выполнением единичных комманд, без привлечения контекста. 7.772 - 7.773 - Принимает аргументы необходимые для метакласса _SSH 7.774 - """ 7.775 - def __init__(self, *a, **kwa): 7.776 - _SSH.__init__(self, *a, **kwa) 7.777 - 7.778 - def __call__(self, cmd): 7.779 - return SSHCommunicate(*self.ssh.exec_command(cmd, timeout=self.timeout)) 7.780 - 7.781 \ No newline at end of file 7.782 + """Класс призванный управлять выполнением единичных комманд, без привлечения контекста. 7.783 + 7.784 + Принимает аргументы необходимые для метакласса _SSH 7.785 + """ 7.786 + def __init__(self, *a, **kwa): 7.787 + _SSH.__init__(self, *a, **kwa) 7.788 + 7.789 + def __call__(self, cmd): 7.790 + return SSHCommunicate(*self.ssh.exec_command(cmd, timeout=self.timeout))