py.lib

Yohn Y. 2022-02-23 Parent:7e0e6ee3031b Child:ff755f64cda8

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))