feat: 支持 ansible receptor private 方式认证, 支持运行完成工作空间清理

pull/12996/head
Aaron3S 8 months ago committed by Bryan
parent fa5d9d3df4
commit 574639d5e1

@ -137,16 +137,13 @@ class BaseAccount(VaultModelMixin, JMSOrgBaseModel):
else:
return None
@property
def private_key_path(self):
def get_private_key_path(self, path):
if self.secret_type != SecretType.SSH_KEY \
or not self.secret \
or not self.private_key:
return None
project_dir = settings.PROJECT_DIR
tmp_dir = os.path.join(project_dir, 'tmp')
key_name = '.' + md5(self.private_key.encode('utf-8')).hexdigest()
key_path = os.path.join(tmp_dir, key_name)
key_path = os.path.join(path, key_name)
if not os.path.exists(key_path):
# https://github.com/ansible/ansible-runner/issues/544
# ssh requires OpenSSH format keys to have a full ending newline.
@ -158,6 +155,12 @@ class BaseAccount(VaultModelMixin, JMSOrgBaseModel):
os.chmod(key_path, 0o400)
return key_path
@property
def private_key_path(self):
project_dir = settings.PROJECT_DIR
tmp_dir = os.path.join(project_dir, 'tmp')
return self.get_private_key_path(tmp_dir)
def get_private_key(self):
if not self.private_key:
return None

@ -73,3 +73,7 @@ class Gateway(Host):
def private_key_path(self):
account = self.select_account
return account.private_key_path if account else None
def get_private_key_path(self, path):
account = self.select_account
return account.get_private_key_path(path) if account else None

@ -0,0 +1,36 @@
import os
import shutil
from functools import wraps
from settings.api import settings
def cleanup_post_run(func):
def get_instance(*args):
if not len(args) > 0:
return
return args[0]
@wraps(func)
def wrapper(*args, **kwargs):
instance = get_instance(*args)
if not instance or not issubclass(type(instance), WorkPostRunCleaner):
raise NotImplementedError("you should extend 'WorkPostRunCleaner'")
try:
return func(*args, **kwargs)
finally:
instance.clean_post_run()
return wrapper
class WorkPostRunCleaner:
@property
def clean_dir(self):
raise NotImplemented
def clean_post_run(self):
if settings.DEBUG_DEV:
return
if self.clean_dir and os.path.exists(self.clean_dir):
shutil.rmtree(self.clean_dir)

@ -45,7 +45,7 @@ class JMSInventory:
return groups
@staticmethod
def make_proxy_command(gateway):
def make_proxy_command(gateway, path_dir):
proxy_command_list = [
"ssh", "-o", "Port={}".format(gateway.port),
"-o", "StrictHostKeyChecking=no",
@ -58,7 +58,7 @@ class JMSInventory:
0, "sshpass -p {}".format(gateway.password)
)
if gateway.private_key:
proxy_command_list.append("-i {}".format(gateway.private_key_path))
proxy_command_list.append("-i {}".format(gateway.get_private_key_path(path_dir)))
proxy_command = "-o ProxyCommand='{}'".format(
" ".join(proxy_command_list)
@ -66,7 +66,7 @@ class JMSInventory:
return {"ansible_ssh_common_args": proxy_command}
@staticmethod
def make_account_ansible_vars(account):
def make_account_ansible_vars(account, path_dir):
var = {
'ansible_user': account.username,
}
@ -76,18 +76,18 @@ class JMSInventory:
if account.secret_type == 'password':
var['ansible_password'] = account.escape_jinja2_syntax(account.secret)
elif account.secret_type == 'ssh_key':
var['ansible_ssh_private_key_file'] = account.private_key_path
var['ansible_ssh_private_key_file'] = account.get_private_key_path(path_dir)
return var
@staticmethod
def make_custom_become_ansible_vars(account, su_from_auth):
def make_custom_become_ansible_vars(account, su_from_auth, path_dir):
su_method = su_from_auth['ansible_become_method']
var = {
'custom_become': True,
'custom_become_method': su_method,
'custom_become_user': account.su_from.username,
'custom_become_password': account.escape_jinja2_syntax(account.su_from.secret),
'custom_become_private_key_path': account.su_from.private_key_path
'custom_become_private_key_path': account.su_from.get_private_key_path(path_dir)
}
return var
@ -100,7 +100,7 @@ class JMSInventory:
setting = getattr(p, 'setting')
host['old_ssh_version'] = setting.get('old_ssh_version', False)
def make_account_vars(self, host, asset, account, automation, protocol, platform, gateway):
def make_account_vars(self, host, asset, account, automation, protocol, platform, gateway, path_dir):
from accounts.const import AutomationTypes
if not account:
host['error'] = _("No account available")
@ -114,15 +114,15 @@ class JMSInventory:
if platform.su_enabled and su_from:
su_from_auth = account.get_ansible_become_auth()
host.update(su_from_auth)
host.update(self.make_custom_become_ansible_vars(account, su_from_auth))
host.update(self.make_custom_become_ansible_vars(account, su_from_auth, path_dir))
elif platform.su_enabled and not su_from and \
self.task_type in (AutomationTypes.change_secret, AutomationTypes.push_account):
host.update(self.make_account_ansible_vars(account))
host.update(self.make_account_ansible_vars(account, path_dir))
host['ansible_become'] = True
host['ansible_become_user'] = 'root'
host['ansible_become_password'] = account.escape_jinja2_syntax(account.secret)
else:
host.update(self.make_account_ansible_vars(account))
host.update(self.make_account_ansible_vars(account, path_dir))
if platform.name == 'Huawei':
host['ansible_connection'] = 'network_cli'
@ -134,11 +134,11 @@ class JMSInventory:
host['gateway'] = {
'address': gateway.address, 'port': gateway.port,
'username': gateway.username, 'secret': gateway.password,
'private_key_path': gateway.private_key_path
'private_key_path': gateway.get_private_key_path(path_dir)
}
host['jms_asset']['port'] = port
else:
ansible_ssh_common_args = self.make_proxy_command(gateway)
ansible_ssh_common_args = self.make_proxy_command(gateway, path_dir)
host['jms_asset'].update(ansible_ssh_common_args)
host.update(ansible_ssh_common_args)
@ -168,7 +168,7 @@ class JMSInventory:
ansible_config['ansible_winrm_connection_timeout'] = 120
return ansible_config
def asset_to_host(self, asset, account, automation, protocols, platform):
def asset_to_host(self, asset, account, automation, protocols, platform, path_dir):
try:
ansible_config = dict(automation.ansible_config)
except (AttributeError, TypeError):
@ -191,7 +191,7 @@ class JMSInventory:
'jms_account': {
'id': str(account.id), 'username': account.username,
'secret': account.escape_jinja2_syntax(account.secret),
'secret_type': account.secret_type, 'private_key_path': account.private_key_path
'secret_type': account.secret_type, 'private_key_path': account.get_private_key_path(path_dir)
} if account else None
}
@ -210,7 +210,7 @@ class JMSInventory:
gateway = asset.domain.select_gateway()
self.make_account_vars(
host, asset, account, automation, protocol, platform, gateway
host, asset, account, automation, protocol, platform, gateway, path_dir
)
return host
@ -274,7 +274,7 @@ class JMSInventory:
for asset in assets:
protocols = self.set_platform_protocol_setting_to_asset(asset, platform_protocols)
account = self.select_account(asset)
host = self.asset_to_host(asset, account, automation, protocols, platform)
host = self.asset_to_host(asset, account, automation, protocols, platform, path_dir)
if not automation.ansible_enabled:
host['error'] = _('Ansible disabled')

@ -2,11 +2,14 @@ import concurrent.futures
import os
import queue
import socket
from django.conf import settings
import ansible_runner
from django.utils.functional import LazyObject
from receptorctl import ReceptorControl
from ops.ansible.cleaner import WorkPostRunCleaner, cleanup_post_run
class WarpedReceptorctl(LazyObject):
def _setup(self):
@ -33,7 +36,7 @@ def run(**kwargs):
return receptor_runner.run()
class AnsibleReceptorRunner:
class AnsibleReceptorRunner(WorkPostRunCleaner):
def __init__(self, **kwargs):
self.runner_params = kwargs
self.unit_id = None
@ -46,6 +49,11 @@ class AnsibleReceptorRunner:
f.write(self.unit_id)
f.flush()
@property
def clean_dir(self):
return self.runner_params.get("private_data_dir", None)
@cleanup_post_run
def run(self):
input, output = socket.socketpair()

Loading…
Cancel
Save