mirror of https://github.com/jumpserver/jumpserver
perf: Custom secret change
parent
a9433bc48e
commit
c7eb170942
|
@ -97,7 +97,7 @@ def main():
|
||||||
msg='No command found, please go to the platform details to add'
|
msg='No command found, please go to the platform details to add'
|
||||||
)
|
)
|
||||||
with SSHClient(module) as client:
|
with SSHClient(module) as client:
|
||||||
output, err_msg = client.execute(commands, answers)
|
__, err_msg = client.execute(commands, answers)
|
||||||
if err_msg:
|
if err_msg:
|
||||||
module.fail_json(
|
module.fail_json(
|
||||||
msg='There was a problem executing the command: %s' % err_msg
|
msg='There was a problem executing the command: %s' % err_msg
|
||||||
|
|
|
@ -116,7 +116,7 @@ def main():
|
||||||
|
|
||||||
try:
|
try:
|
||||||
client.close()
|
client.close()
|
||||||
except Exception:
|
except Exception: # noqa
|
||||||
pass
|
pass
|
||||||
|
|
||||||
return module.exit_json(**result)
|
return module.exit_json(**result)
|
||||||
|
|
|
@ -7,14 +7,44 @@ __metaclass__ = type
|
||||||
DOCUMENTATION = '''
|
DOCUMENTATION = '''
|
||||||
---
|
---
|
||||||
module: custom_rdp_ping
|
module: custom_rdp_ping
|
||||||
short_description: Use rdp to probe whether an asset is connectable
|
short_description: Use RDP to probe whether an asset is connectable.
|
||||||
description:
|
description:
|
||||||
- Use rdp to probe whether an asset is connectable
|
- Use RDP to probe whether an asset is connectable.
|
||||||
|
options:
|
||||||
|
login_host:
|
||||||
|
description: Target host to connect.
|
||||||
|
type: str
|
||||||
|
required: False
|
||||||
|
default: localhost
|
||||||
|
login_port:
|
||||||
|
description: Target port to connect.
|
||||||
|
type: int
|
||||||
|
required: False
|
||||||
|
default: 22
|
||||||
|
login_user:
|
||||||
|
description: Login user for the connection.
|
||||||
|
type: str
|
||||||
|
required: False
|
||||||
|
default: root
|
||||||
|
login_password:
|
||||||
|
description: Login password.
|
||||||
|
type: str
|
||||||
|
required: False
|
||||||
|
no_log: True
|
||||||
|
login_secret_type:
|
||||||
|
description: Authentication method.
|
||||||
|
type: str
|
||||||
|
required: False
|
||||||
|
default: password
|
||||||
|
gateway_args:
|
||||||
|
description: Arguments for setting up an SSH tunnel.
|
||||||
|
type: dict
|
||||||
|
required: False
|
||||||
|
default: null
|
||||||
'''
|
'''
|
||||||
|
|
||||||
EXAMPLES = '''
|
EXAMPLES = '''
|
||||||
- name: >
|
- name: Ping asset server using RDP.
|
||||||
Ping asset server.
|
|
||||||
custom_rdp_ping:
|
custom_rdp_ping:
|
||||||
login_host: 127.0.0.1
|
login_host: 127.0.0.1
|
||||||
login_port: 3389
|
login_port: 3389
|
||||||
|
@ -24,12 +54,12 @@ EXAMPLES = '''
|
||||||
|
|
||||||
RETURN = '''
|
RETURN = '''
|
||||||
is_available:
|
is_available:
|
||||||
description: Windows server availability.
|
description: Indicates if the Windows asset is available.
|
||||||
returned: always
|
returned: always
|
||||||
type: bool
|
type: bool
|
||||||
sample: true
|
sample: true
|
||||||
conn_err_msg:
|
conn_err_msg:
|
||||||
description: Connection error message.
|
description: Connection error message (if any).
|
||||||
returned: always
|
returned: always
|
||||||
type: str
|
type: str
|
||||||
sample: ''
|
sample: ''
|
||||||
|
@ -41,11 +71,6 @@ from sshtunnel import SSHTunnelForwarder
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
|
||||||
|
|
||||||
# =========================================
|
|
||||||
# Module execution.
|
|
||||||
#
|
|
||||||
|
|
||||||
|
|
||||||
def common_argument_spec():
|
def common_argument_spec():
|
||||||
options = dict(
|
options = dict(
|
||||||
login_host=dict(type='str', required=False, default='localhost'),
|
login_host=dict(type='str', required=False, default='localhost'),
|
||||||
|
@ -67,13 +92,12 @@ class RDPConnectionManager:
|
||||||
self.result_queue = multiprocessing.Queue()
|
self.result_queue = multiprocessing.Queue()
|
||||||
|
|
||||||
def build_connection_details(self):
|
def build_connection_details(self):
|
||||||
connection_details = {
|
return {
|
||||||
'hostname': self.params['login_host'],
|
'hostname': self.params['login_host'],
|
||||||
'port': self.params['login_port'],
|
'port': self.params['login_port'],
|
||||||
'username': self.params['username'],
|
'username': self.params['login_user'],
|
||||||
'password': self.params['password']
|
'password': self.params['login_password']
|
||||||
}
|
}
|
||||||
return connection_details
|
|
||||||
|
|
||||||
def setup_ssh_tunnel(self):
|
def setup_ssh_tunnel(self):
|
||||||
gateway_args = self.params['gateway_args'] or {}
|
gateway_args = self.params['gateway_args'] or {}
|
||||||
|
@ -90,8 +114,8 @@ class RDPConnectionManager:
|
||||||
self.connection_details['port']
|
self.connection_details['port']
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
tunnel.start()
|
tunnel.start()
|
||||||
|
|
||||||
self.connection_details['hostname'] = '127.0.0.1'
|
self.connection_details['hostname'] = '127.0.0.1'
|
||||||
self.connection_details['port'] = tunnel.local_bind_port
|
self.connection_details['port'] = tunnel.local_bind_port
|
||||||
self.ssh_tunnel = tunnel
|
self.ssh_tunnel = tunnel
|
||||||
|
@ -107,13 +131,23 @@ class RDPConnectionManager:
|
||||||
self.close_ssh_tunnel()
|
self.close_ssh_tunnel()
|
||||||
|
|
||||||
def check_rdp_connectivity(self):
|
def check_rdp_connectivity(self):
|
||||||
connect_params = list(self.connection_details.values()) + ['', 0]
|
connect_params = [
|
||||||
is_reachable = pyfreerdp.check_connectivity(*connect_params)
|
self.connection_details['hostname'],
|
||||||
|
self.connection_details['port'],
|
||||||
|
self.connection_details['username'],
|
||||||
|
self.connection_details['password'],
|
||||||
|
'', # extra parameter (if needed)
|
||||||
|
0 # timeout (if needed)
|
||||||
|
]
|
||||||
|
try:
|
||||||
|
is_reachable = pyfreerdp.check_connectivity(*connect_params)
|
||||||
|
except Exception as ex:
|
||||||
|
is_reachable = False
|
||||||
self.result_queue.put(is_reachable)
|
self.result_queue.put(is_reachable)
|
||||||
|
|
||||||
def attempt_connection(self):
|
def attempt_connection(self):
|
||||||
if self.params['login_secret_type'] != 'password':
|
if self.params['login_secret_type'] != 'password':
|
||||||
error_message = f'unsupported authentication method: {self.params["login_secret_type"]}'
|
error_message = f"Unsupported authentication method: {self.params['login_secret_type']}"
|
||||||
return False, error_message
|
return False, error_message
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
@ -138,17 +172,21 @@ class RDPConnectionManager:
|
||||||
def main():
|
def main():
|
||||||
argument_spec = common_argument_spec()
|
argument_spec = common_argument_spec()
|
||||||
module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True)
|
module = AnsibleModule(argument_spec=argument_spec, supports_check_mode=True)
|
||||||
result = {'changed': False}
|
rdp_manager = RDPConnectionManager(module.params)
|
||||||
module_params = module.params
|
|
||||||
rdp_manager = RDPConnectionManager(module_params)
|
|
||||||
is_available, error_message = rdp_manager.attempt_connection()
|
is_available, error_message = rdp_manager.attempt_connection()
|
||||||
result['is_available'] = is_available
|
|
||||||
|
# Prepare the result structure.
|
||||||
|
result = {
|
||||||
|
'changed': False,
|
||||||
|
'is_available': is_available,
|
||||||
|
'conn_err_msg': error_message
|
||||||
|
}
|
||||||
|
|
||||||
if not is_available:
|
if not is_available:
|
||||||
module.fail_json(msg=f'Unable to connect to asset: {error_message}')
|
module.fail_json(msg=f"Unable to connect to asset: {error_message}", **result)
|
||||||
|
else:
|
||||||
return module.exit_json(**result)
|
module.exit_json(**result)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
main()
|
main()
|
||||||
|
|
|
@ -4,18 +4,35 @@ from __future__ import absolute_import, division, print_function
|
||||||
|
|
||||||
__metaclass__ = type
|
__metaclass__ = type
|
||||||
|
|
||||||
|
|
||||||
DOCUMENTATION = '''
|
DOCUMENTATION = '''
|
||||||
---
|
---
|
||||||
module: ssh_ping
|
module: ssh_ping
|
||||||
short_description: Use ssh to probe whether an asset is connectable
|
short_description: Use ssh to probe whether an asset is connectable
|
||||||
description:
|
description:
|
||||||
- Use ssh to probe whether an asset is connectable
|
- Use ssh to probe whether an asset is connectable.
|
||||||
|
options:
|
||||||
|
login_host:
|
||||||
|
description: The target host to connect.
|
||||||
|
type: str
|
||||||
|
required: True
|
||||||
|
login_port:
|
||||||
|
description: The port on the target host.
|
||||||
|
type: int
|
||||||
|
required: False
|
||||||
|
default: 22
|
||||||
|
login_user:
|
||||||
|
description: The username for the SSH connection.
|
||||||
|
type: str
|
||||||
|
required: True
|
||||||
|
login_password:
|
||||||
|
description: The password for the SSH connection.
|
||||||
|
type: str
|
||||||
|
required: True
|
||||||
|
no_log: True
|
||||||
'''
|
'''
|
||||||
|
|
||||||
EXAMPLES = '''
|
EXAMPLES = '''
|
||||||
- name: >
|
- name: Ping asset server using SSH.
|
||||||
Ping asset server.
|
|
||||||
ssh_ping:
|
ssh_ping:
|
||||||
login_host: 127.0.0.1
|
login_host: 127.0.0.1
|
||||||
login_port: 22
|
login_port: 22
|
||||||
|
@ -25,36 +42,27 @@ EXAMPLES = '''
|
||||||
|
|
||||||
RETURN = '''
|
RETURN = '''
|
||||||
is_available:
|
is_available:
|
||||||
description: Ping server availability.
|
description: Indicate whether the target server is reachable via SSH.
|
||||||
returned: always
|
returned: always
|
||||||
type: bool
|
type: bool
|
||||||
sample: true
|
sample: true
|
||||||
'''
|
'''
|
||||||
|
|
||||||
|
|
||||||
from ansible.module_utils.basic import AnsibleModule
|
from ansible.module_utils.basic import AnsibleModule
|
||||||
|
from libs.ansible.modules_utils.remote_client import SSHClient, common_argument_spec
|
||||||
from libs.ansible.modules_utils.remote_client import (
|
|
||||||
SSHClient, common_argument_spec
|
|
||||||
)
|
|
||||||
|
|
||||||
|
|
||||||
# =========================================
|
|
||||||
# Module execution.
|
|
||||||
#
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
options = common_argument_spec()
|
options = common_argument_spec()
|
||||||
module = AnsibleModule(argument_spec=options, supports_check_mode=True,)
|
module = AnsibleModule(argument_spec=options, supports_check_mode=True)
|
||||||
|
|
||||||
|
result = {'changed': False, 'is_available': False}
|
||||||
|
|
||||||
result = {
|
|
||||||
'changed': False, 'is_available': False
|
|
||||||
}
|
|
||||||
with SSHClient(module) as client:
|
with SSHClient(module) as client:
|
||||||
client.connect()
|
client.connect()
|
||||||
|
|
||||||
result['is_available'] = True
|
result['is_available'] = True
|
||||||
return module.exit_json(**result)
|
module.exit_json(**result)
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
if __name__ == '__main__':
|
||||||
|
|
|
@ -1,55 +1,25 @@
|
||||||
import re
|
import re
|
||||||
import signal
|
import signal
|
||||||
import time
|
import time
|
||||||
|
|
||||||
import paramiko
|
|
||||||
|
|
||||||
from functools import wraps
|
from functools import wraps
|
||||||
|
|
||||||
|
import paramiko
|
||||||
from sshtunnel import SSHTunnelForwarder
|
from sshtunnel import SSHTunnelForwarder
|
||||||
|
|
||||||
|
|
||||||
DEFAULT_RE = '.*'
|
DEFAULT_RE = '.*'
|
||||||
SU_PROMPT_LOCALIZATIONS = [
|
SU_PROMPT_LOCALIZATIONS = [
|
||||||
'Password',
|
'Password', '암호', 'パスワード', 'Adgangskode', 'Contraseña', 'Contrasenya',
|
||||||
'암호',
|
'Hasło', 'Heslo', 'Jelszó', 'Lösenord', 'Mật khẩu', 'Mot de passe',
|
||||||
'パスワード',
|
'Parola', 'Parool', 'Pasahitza', 'Passord', 'Passwort', 'Salasana',
|
||||||
'Adgangskode',
|
'Sandi', 'Senha', 'Wachtwoord', 'ססמה', 'Лозинка', 'Парола', 'Пароль',
|
||||||
'Contraseña',
|
'गुप्तशब्द', 'शब्दकूट', 'సంకేతపదము', 'හස්පදය', '密码', '密碼', '口令',
|
||||||
'Contrasenya',
|
]
|
||||||
'Hasło',
|
|
||||||
'Heslo',
|
|
||||||
'Jelszó',
|
|
||||||
'Lösenord',
|
|
||||||
'Mật khẩu',
|
|
||||||
'Mot de passe',
|
|
||||||
'Parola',
|
|
||||||
'Parool',
|
|
||||||
'Pasahitza',
|
|
||||||
'Passord',
|
|
||||||
'Passwort',
|
|
||||||
'Salasana',
|
|
||||||
'Sandi',
|
|
||||||
'Senha',
|
|
||||||
'Wachtwoord',
|
|
||||||
'ססמה',
|
|
||||||
'Лозинка',
|
|
||||||
'Парола',
|
|
||||||
'Пароль',
|
|
||||||
'गुप्तशब्द',
|
|
||||||
'शब्दकूट',
|
|
||||||
'సంకేతపదము',
|
|
||||||
'හස්පදය',
|
|
||||||
'密码',
|
|
||||||
'密碼',
|
|
||||||
'口令',
|
|
||||||
]
|
|
||||||
|
|
||||||
|
|
||||||
def get_become_prompt_re():
|
def get_become_prompt_re():
|
||||||
b_password_string = "|".join((r'(\w+\'s )?' + p) for p in SU_PROMPT_LOCALIZATIONS)
|
pattern_segments = (r'(\w+\'s )?' + p for p in SU_PROMPT_LOCALIZATIONS)
|
||||||
b_password_string = b_password_string + ' ?(:|:) ?'
|
prompt_pattern = "|".join(pattern_segments) + r' ?(:|:) ?'
|
||||||
return re.compile(b_password_string, flags=re.IGNORECASE)
|
return re.compile(prompt_pattern, flags=re.IGNORECASE)
|
||||||
|
|
||||||
|
|
||||||
become_prompt_re = get_become_prompt_re()
|
become_prompt_re = get_become_prompt_re()
|
||||||
|
@ -88,8 +58,8 @@ def raise_timeout(name=''):
|
||||||
def handler(signum, frame):
|
def handler(signum, frame):
|
||||||
raise TimeoutError(f'{name} timed out, wait {timeout}s')
|
raise TimeoutError(f'{name} timed out, wait {timeout}s')
|
||||||
|
|
||||||
|
timeout = getattr(self, 'timeout', 0)
|
||||||
try:
|
try:
|
||||||
timeout = getattr(self, 'timeout', 0)
|
|
||||||
if timeout > 0:
|
if timeout > 0:
|
||||||
signal.signal(signal.SIGALRM, handler)
|
signal.signal(signal.SIGALRM, handler)
|
||||||
signal.alarm(timeout)
|
signal.alarm(timeout)
|
||||||
|
@ -97,7 +67,9 @@ def raise_timeout(name=''):
|
||||||
except Exception as error:
|
except Exception as error:
|
||||||
signal.alarm(0)
|
signal.alarm(0)
|
||||||
raise error
|
raise error
|
||||||
|
|
||||||
return wrapper
|
return wrapper
|
||||||
|
|
||||||
return decorate
|
return decorate
|
||||||
|
|
||||||
|
|
||||||
|
@ -122,8 +94,8 @@ class SSHClient:
|
||||||
self.client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
self.client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
|
||||||
self.connect_params = self.get_connect_params()
|
self.connect_params = self.get_connect_params()
|
||||||
self._channel = None
|
self._channel = None
|
||||||
|
|
||||||
self.buffer_size = 1024
|
self.buffer_size = 1024
|
||||||
self.connect_params = self.get_connect_params()
|
|
||||||
self.prompt = self.module.params['prompt']
|
self.prompt = self.module.params['prompt']
|
||||||
self.timeout = self.module.params['recv_timeout']
|
self.timeout = self.module.params['recv_timeout']
|
||||||
|
|
||||||
|
@ -134,45 +106,54 @@ class SSHClient:
|
||||||
return self._channel
|
return self._channel
|
||||||
|
|
||||||
def get_connect_params(self):
|
def get_connect_params(self):
|
||||||
|
p = self.module.params
|
||||||
params = {
|
params = {
|
||||||
'allow_agent': False, 'look_for_keys': False,
|
'allow_agent': False,
|
||||||
'hostname': self.module.params['login_host'],
|
'look_for_keys': False,
|
||||||
'port': self.module.params['login_port'],
|
'hostname': p['login_host'],
|
||||||
'key_filename': self.module.params['login_private_key_path'] or None
|
'port': p['login_port'],
|
||||||
|
'key_filename': p['login_private_key_path'] or None
|
||||||
}
|
}
|
||||||
if self.module.params['become']:
|
|
||||||
params['username'] = self.module.params['become_user']
|
if p['become']:
|
||||||
params['password'] = self.module.params['become_password']
|
params['username'] = p['become_user']
|
||||||
params['key_filename'] = self.module.params['become_private_key_path'] or None
|
params['password'] = p['become_password']
|
||||||
|
params['key_filename'] = p['become_private_key_path'] or None
|
||||||
else:
|
else:
|
||||||
params['username'] = self.module.params['login_user']
|
params['username'] = p['login_user']
|
||||||
params['password'] = self.module.params['login_password']
|
params['password'] = p['login_password']
|
||||||
params['key_filename'] = self.module.params['login_private_key_path'] or None
|
params['key_filename'] = p['login_private_key_path'] or None
|
||||||
if self.module.params['old_ssh_version']:
|
|
||||||
|
if p['old_ssh_version']:
|
||||||
params['transport_factory'] = OldSSHTransport
|
params['transport_factory'] = OldSSHTransport
|
||||||
|
|
||||||
return params
|
return params
|
||||||
|
|
||||||
def switch_user(self):
|
def switch_user(self):
|
||||||
if not self.module.params['become']:
|
p = self.module.params
|
||||||
return
|
if not p['become']:
|
||||||
method = self.module.params['become_method']
|
|
||||||
username = self.module.params['login_user']
|
|
||||||
if method == 'sudo':
|
|
||||||
switch_method = 'sudo su -'
|
|
||||||
password = self.module.params['become_password']
|
|
||||||
elif method == 'su':
|
|
||||||
switch_method = 'su -'
|
|
||||||
password = self.module.params['login_password']
|
|
||||||
else:
|
|
||||||
self.module.fail_json(msg='Become method %s not support' % method)
|
|
||||||
return
|
return
|
||||||
|
|
||||||
__, e_msg = self.execute(
|
method = p['become_method']
|
||||||
[f'{switch_method} {username}', password, 'whoami'],
|
username = p['login_user']
|
||||||
|
|
||||||
|
if method == 'sudo':
|
||||||
|
switch_cmd = 'sudo su -'
|
||||||
|
pword = p['become_password']
|
||||||
|
elif method == 'su':
|
||||||
|
switch_cmd = 'su -'
|
||||||
|
pword = p['login_password']
|
||||||
|
else:
|
||||||
|
self.module.fail_json(msg=f'Become method {method} not supported.')
|
||||||
|
return
|
||||||
|
|
||||||
|
# Expected to see a prompt, type the password, and check the username
|
||||||
|
output, error = self.execute(
|
||||||
|
[f'{switch_cmd} {username}', pword, 'whoami'],
|
||||||
[become_prompt_re, DEFAULT_RE, username]
|
[become_prompt_re, DEFAULT_RE, username]
|
||||||
)
|
)
|
||||||
if e_msg:
|
if error:
|
||||||
self.module.fail_json(msg='Become user %s failed.' % username)
|
self.module.fail_json(msg=f'Failed to become user {username}. Output: {output}')
|
||||||
|
|
||||||
def connect(self):
|
def connect(self):
|
||||||
self.before_runner_start()
|
self.before_runner_start()
|
||||||
|
@ -193,28 +174,32 @@ class SSHClient:
|
||||||
return answers
|
return answers
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def __match(re_, content):
|
def __match(expression, content):
|
||||||
re_pattern = re_
|
if isinstance(expression, str):
|
||||||
if isinstance(re_, str):
|
expression = re.compile(expression, re.DOTALL | re.IGNORECASE)
|
||||||
re_pattern = re.compile(re_, re.DOTALL | re.IGNORECASE)
|
elif not isinstance(expression, re.Pattern):
|
||||||
elif not isinstance(re_pattern, re.Pattern):
|
raise ValueError(f'{expression} should be a regular expression')
|
||||||
raise ValueError(f'{re_} should be a regular expression')
|
|
||||||
return bool(re_pattern.search(content))
|
return bool(expression.search(content))
|
||||||
|
|
||||||
@raise_timeout('Recv message')
|
@raise_timeout('Recv message')
|
||||||
def _get_match_recv(self, answer_reg=DEFAULT_RE):
|
def _get_match_recv(self, answer_reg=DEFAULT_RE):
|
||||||
last_output, output = '', ''
|
buffer_str = ''
|
||||||
|
prev_str = ''
|
||||||
|
|
||||||
|
check_reg = self.prompt if answer_reg == DEFAULT_RE else answer_reg
|
||||||
while True:
|
while True:
|
||||||
if self.channel.recv_ready():
|
if self.channel.recv_ready():
|
||||||
recv = self.channel.recv(self.buffer_size).decode()
|
chunk = self.channel.recv(self.buffer_size).decode('utf-8', 'replace')
|
||||||
output += recv
|
buffer_str += chunk
|
||||||
if output and last_output != output:
|
|
||||||
fin_reg = self.prompt if answer_reg == DEFAULT_RE else answer_reg
|
if buffer_str and buffer_str != prev_str:
|
||||||
if self.__match(fin_reg, output):
|
if self.__match(check_reg, buffer_str):
|
||||||
break
|
break
|
||||||
last_output = output
|
prev_str = buffer_str
|
||||||
time.sleep(0.01)
|
time.sleep(0.01)
|
||||||
return output
|
|
||||||
|
return buffer_str
|
||||||
|
|
||||||
@raise_timeout('Wait send message')
|
@raise_timeout('Wait send message')
|
||||||
def _check_send(self):
|
def _check_send(self):
|
||||||
|
@ -223,38 +208,44 @@ class SSHClient:
|
||||||
time.sleep(self.module.params['delay_time'])
|
time.sleep(self.module.params['delay_time'])
|
||||||
|
|
||||||
def execute(self, commands, answers=None):
|
def execute(self, commands, answers=None):
|
||||||
all_output, error_msg = '', ''
|
combined_output = ''
|
||||||
|
error_msg = ''
|
||||||
|
|
||||||
try:
|
try:
|
||||||
answers = self._fit_answers(commands, answers)
|
answers = self._fit_answers(commands, answers)
|
||||||
for index, command in enumerate(commands):
|
for cmd, ans_regex in zip(commands, answers):
|
||||||
self._check_send()
|
self._check_send()
|
||||||
self.channel.send(command + '\n')
|
self.channel.send(cmd + '\n')
|
||||||
all_output += f'{self._get_match_recv(answers[index])}\n'
|
combined_output += self._get_match_recv(ans_regex) + '\n'
|
||||||
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
error_msg = str(e)
|
error_msg = str(e)
|
||||||
return all_output, error_msg
|
|
||||||
|
return combined_output, error_msg
|
||||||
|
|
||||||
def local_gateway_prepare(self):
|
def local_gateway_prepare(self):
|
||||||
gateway_args = self.module.params['gateway_args'] or ''
|
gateway_args = self.module.params['gateway_args'] or ''
|
||||||
pattern = r"(?:sshpass -p ([^ ]+))?\s*ssh -o Port=(\d+)\s+-o StrictHostKeyChecking=no\s+([\w@]+)@([" \
|
pattern = (
|
||||||
r"\d.]+)\s+-W %h:%p -q(?: -i (.+))?'"
|
r"(?:sshpass -p ([^ ]+))?\s*ssh -o Port=(\d+)\s+-o StrictHostKeyChecking=no\s+"
|
||||||
|
r"([\w@]+)@([\d.]+)\s+-W %h:%p -q(?: -i (.+))?'"
|
||||||
|
)
|
||||||
match = re.search(pattern, gateway_args)
|
match = re.search(pattern, gateway_args)
|
||||||
|
|
||||||
if not match:
|
if not match:
|
||||||
return
|
return
|
||||||
|
|
||||||
password, port, username, address, private_key_path = match.groups()
|
password, port, username, remote_addr, key_path = match.groups()
|
||||||
password = password if password else None
|
password = password or None
|
||||||
private_key_path = private_key_path if private_key_path else None
|
key_path = key_path or None
|
||||||
remote_hostname = self.module.params['login_host']
|
|
||||||
remote_port = self.module.params['login_port']
|
|
||||||
|
|
||||||
server = SSHTunnelForwarder(
|
server = SSHTunnelForwarder(
|
||||||
(address, int(port)),
|
(remote_addr, int(port)),
|
||||||
ssh_username=username,
|
ssh_username=username,
|
||||||
ssh_password=password,
|
ssh_password=password,
|
||||||
ssh_pkey=private_key_path,
|
ssh_pkey=key_path,
|
||||||
remote_bind_address=(remote_hostname, remote_port)
|
remote_bind_address=(
|
||||||
|
self.module.params['login_host'],
|
||||||
|
self.module.params['login_port']
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
server.start()
|
server.start()
|
||||||
|
@ -263,11 +254,8 @@ class SSHClient:
|
||||||
self.gateway_server = server
|
self.gateway_server = server
|
||||||
|
|
||||||
def local_gateway_clean(self):
|
def local_gateway_clean(self):
|
||||||
gateway_server = self.gateway_server
|
if self.gateway_server:
|
||||||
if not gateway_server:
|
self.gateway_server.stop()
|
||||||
return
|
|
||||||
|
|
||||||
gateway_server.stop()
|
|
||||||
|
|
||||||
def before_runner_start(self):
|
def before_runner_start(self):
|
||||||
self.local_gateway_prepare()
|
self.local_gateway_prepare()
|
||||||
|
|
Loading…
Reference in New Issue