|
| 1 | +import re |
| 2 | +import socket |
| 3 | +from StringIO import StringIO |
| 4 | +from abc import ABCMeta, abstractmethod |
| 5 | +from multiprocessing.pool import ThreadPool |
| 6 | +from uuid import uuid4 |
| 7 | + |
| 8 | +import time |
| 9 | + |
| 10 | +import requests |
| 11 | +import winrm |
| 12 | +from paramiko.ssh_exception import NoValidConnectionsError |
| 13 | +from winrm.exceptions import WinRMTransportError |
| 14 | + |
| 15 | +from pip._vendor.requests import ConnectionError |
| 16 | +from paramiko import SSHClient, AutoAddPolicy, RSAKey |
| 17 | + |
| 18 | + |
| 19 | +class IVMConnectionService(object): |
| 20 | + __metaclass__ = ABCMeta |
| 21 | + |
| 22 | + @abstractmethod |
| 23 | + def check_connection(self, target_host, logger, ansible_port): |
| 24 | + pass |
| 25 | + |
| 26 | + |
| 27 | +class ExcutorConnectionError(EnvironmentError): |
| 28 | + def __init__(self, error_code, inner_error): |
| 29 | + self.errno = error_code |
| 30 | + self.inner_error = inner_error |
| 31 | + |
| 32 | + |
| 33 | +class WindowsConnectionService(IVMConnectionService): |
| 34 | + def check_connection(self, target_host, logger, ansible_port): |
| 35 | + |
| 36 | + ip = target_host.ip + ":" + ansible_port if ansible_port else target_host.ip |
| 37 | + logger.info("Session IP: " + ip) |
| 38 | + |
| 39 | + logger.info("Creating a session.") |
| 40 | + if target_host.connection_secured: |
| 41 | + logger.info("session connection_secured=True") |
| 42 | + session = winrm.Session(ip, auth=(target_host.username, target_host.password), transport='ssl') |
| 43 | + else: |
| 44 | + logger.info("session connection_secured=False") |
| 45 | + session = winrm.Session(ip, auth=(target_host.username, target_host.password)) |
| 46 | + |
| 47 | + logger.info("Session created.") |
| 48 | + |
| 49 | + try: |
| 50 | + logger.info("test connection") |
| 51 | + uid = str(uuid4()) |
| 52 | + result = session.run_cmd('@echo ' + uid) |
| 53 | + assert uid in result.std_out |
| 54 | + except requests.ConnectionError as e: |
| 55 | + # Time out is allowed exception |
| 56 | + raise ExcutorConnectionError(10060, e) |
| 57 | + except ConnectionError as e: |
| 58 | + match = re.search(r'\[Errno (?P<errno>\d+)\]', str(e.message)) |
| 59 | + error_code = int(match.group('errno')) if match else 0 |
| 60 | + raise ExcutorConnectionError(error_code, e) |
| 61 | + except WinRMTransportError as e: |
| 62 | + match = re.search(r'Code (?P<errno>\d+)', str(e.message)) |
| 63 | + error_code = int(match.group('errno')) if match else 0 |
| 64 | + raise ExcutorConnectionError(error_code, e) |
| 65 | + except Exception as e: |
| 66 | + raise ExcutorConnectionError(0, e) |
| 67 | + |
| 68 | + |
| 69 | +class LinuxConnectionService(IVMConnectionService): |
| 70 | + def check_connection(self, target_host, logger, ansible_port): |
| 71 | + """ |
| 72 | +
|
| 73 | + :param target_host: |
| 74 | + :param logger Logger: |
| 75 | + :return: |
| 76 | + """ |
| 77 | + try: |
| 78 | + logger.info("Creating a session.") |
| 79 | + |
| 80 | + session = SSHClient() |
| 81 | + session.set_missing_host_key_policy(AutoAddPolicy()) |
| 82 | + logger.info("Test connection") |
| 83 | + if target_host.password: |
| 84 | + session.connect(target_host.ip, port=ansible_port, username=target_host.username, |
| 85 | + password=target_host.password) |
| 86 | + elif target_host.access_key: |
| 87 | + key_stream = StringIO(target_host.access_key) |
| 88 | + key_obj = RSAKey.from_private_key(key_stream) |
| 89 | + session.connect(target_host.ip, port=ansible_port, username=target_host.username, pkey=key_obj) |
| 90 | + else: |
| 91 | + raise Exception('Both password and access key are empty.') |
| 92 | + logger.info("Done testing connection") |
| 93 | + except NoValidConnectionsError as e: |
| 94 | + error_code = next(e.errors.itervalues(), type('e', (object,), {'errno': 0})).errno |
| 95 | + raise ExcutorConnectionError(error_code, e) |
| 96 | + except socket.error as e: |
| 97 | + raise ExcutorConnectionError(e.errno, e) |
| 98 | + except Exception as e: |
| 99 | + raise ExcutorConnectionError(0, e) |
| 100 | + |
| 101 | + |
| 102 | +class ConnectionService(object): |
| 103 | + def __init__(self): |
| 104 | + self.valid_errnos = [10060, 10061, 10064, 10065, 500, 113, 111, 110] |
| 105 | + self.linuxConnectionService = LinuxConnectionService() |
| 106 | + self.windowsConnectionService = WindowsConnectionService() |
| 107 | + |
| 108 | + def check_connection(self, logger, target_host, ansible_port=None, timeout_minutes=10): |
| 109 | + """ |
| 110 | +
|
| 111 | + :param timeout_minutes: |
| 112 | + :param ansible_port: |
| 113 | + :param Logger logger: |
| 114 | + :param cloudshell.cm.ansible.domain.ansible_configuration.HostConfiguration target_host: |
| 115 | + :return: |
| 116 | + """ |
| 117 | + # 10060 ETIMEDOUT Operation timed out |
| 118 | + # 10061 ECONNREFUSED Connection refused (happense when host found, port not) |
| 119 | + # 10064 EHOSTDOWN Host is down |
| 120 | + # 10065 EHOSTUNREACH Host is unreachable |
| 121 | + # 500 Bad http response (winrm) |
| 122 | + # 113 EHOSTUNREACH No route to host (winrm - OpenStack) |
| 123 | + # 111 ERROR_SSH_APPLICATION_CLOSED User on the other side of connection closed |
| 124 | + # application that led to disconnection |
| 125 | + # 110 ERROR_SSH_CONNECTION_LOST Connection was lost by some reason |
| 126 | + interval_seconds = 10 |
| 127 | + start_time = time.time() |
| 128 | + while True: |
| 129 | + try: |
| 130 | + logger.info("check connection") |
| 131 | + if target_host.connection_method == 'winrm': |
| 132 | + logger.info("Check connection on windows") |
| 133 | + |
| 134 | + self.windowsConnectionService.check_connection(target_host=target_host, |
| 135 | + logger=logger, |
| 136 | + ansible_port=ansible_port) |
| 137 | + |
| 138 | + logger.info("Done checking connection on windows") |
| 139 | + else: |
| 140 | + logger.info("Check connection on linux") |
| 141 | + self.linuxConnectionService.check_connection(target_host=target_host, |
| 142 | + logger=logger, |
| 143 | + ansible_port=int(ansible_port)) |
| 144 | + logger.info("Done checking connection on linux") |
| 145 | + break |
| 146 | + except ExcutorConnectionError as e: |
| 147 | + if e.errno not in self.valid_errnos: |
| 148 | + raise e.inner_error |
| 149 | + if time.time() - start_time >= timeout_minutes * 60: |
| 150 | + raise e.inner_error |
| 151 | + time.sleep(interval_seconds) |
0 commit comments