From dcf113b87c16c8d3240993e790e46f141fcb6d20 Mon Sep 17 00:00:00 2001 From: Aaron3S Date: Wed, 9 Aug 2023 17:25:54 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E4=BD=9C=E4=B8=9A?= =?UTF-8?q?=E4=B8=AD=E5=BF=83=20sql=20=E6=94=AF=E6=8C=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/ops/const.py | 3 ++ apps/ops/models/job.py | 62 ++++++++++++++++++++++++++++++++++----- apps/users/models/user.py | 2 +- 3 files changed, 58 insertions(+), 9 deletions(-) diff --git a/apps/ops/const.py b/apps/ops/const.py index 6bb8ee750..01889a4b2 100644 --- a/apps/ops/const.py +++ b/apps/ops/const.py @@ -49,6 +49,9 @@ class Modules(models.TextChoices): shell = 'shell', _('Shell') winshell = 'win_shell', _('Powershell') python = 'python', _('Python') + mysql = 'mysql', _('MySQL') + postgresql = 'postgresql', _('PostgreSQL') + sqlserver = 'sqlserver', _('SQLServer') class JobStatus(models.TextChoices): diff --git a/apps/ops/models/job.py b/apps/ops/models/job.py index 3cd63e293..23105f297 100644 --- a/apps/ops/models/job.py +++ b/apps/ops/models/job.py @@ -19,6 +19,7 @@ from simple_history.models import HistoricalRecords from accounts.models import Account from acls.models import CommandFilterACL from assets.models import Asset +from common.db.encoder import ModelJSONFieldEncoder from ops.ansible import JMSInventory, AdHocRunner, PlaybookRunner, CommandInBlackListException from ops.mixin import PeriodTaskModelMixin from ops.variables import * @@ -42,12 +43,36 @@ def get_parent_keys(key, include_self=True): class JMSPermedInventory(JMSInventory): - def __init__(self, assets, account_policy='privileged_first', - account_prefer='root,Administrator', host_callback=None, user=None): + def __init__(self, + assets, + account_policy='privileged_first', + account_prefer='root,Administrator', + module=None, + host_callback=None, + user=None): super().__init__(assets, account_policy, account_prefer, host_callback, exclude_localhost=True) self.user = user + self.module = module self.assets_accounts_mapper = self.get_assets_accounts_mapper() + def make_account_vars(self, host, asset, account, automation, protocol, platform, gateway): + if not account: + host['error'] = _("No account available") + return host + + if protocol.name != self.module: + host['error'] = "Module {} is not suitable for this asset".format(self.module) + return host + + if protocol.name in ('mysql', 'postgresql', 'sqlserver'): + host['login_host'] = asset.address + host['login_port'] = protocol.port + host['login_user'] = account.username + host['login_password'] = account.secret + host['login_db'] = asset.spec_info.get('db_name', '') + return host + return super().make_account_vars(host, asset, account, automation, protocol, platform, gateway) + def get_asset_sorted_accounts(self, asset): accounts = self.assets_accounts_mapper.get(asset.id, []) return list(accounts) @@ -158,7 +183,9 @@ class Job(JMSOrgBaseModel, PeriodTaskModelMixin): @property def inventory(self): - return JMSPermedInventory(self.assets.all(), self.runas_policy, self.runas, user=self.creator) + return JMSPermedInventory(self.assets.all(), + self.runas_policy, self.runas, + user=self.creator, module=self.module) @property def material(self): @@ -187,8 +214,8 @@ class JobExecution(JMSOrgBaseModel): job = models.ForeignKey(Job, on_delete=models.SET_NULL, related_name='executions', null=True) job_version = models.IntegerField(default=0) parameters = models.JSONField(default=dict, verbose_name=_('Parameters')) - result = models.JSONField(blank=True, null=True, verbose_name=_('Result')) - summary = models.JSONField(default=dict, verbose_name=_('Summary')) + result = models.JSONField(encoder=ModelJSONFieldEncoder, blank=True, null=True, verbose_name=_('Result')) + summary = models.JSONField(encoder=ModelJSONFieldEncoder, default=dict, verbose_name=_('Summary')) creator = models.ForeignKey('users.User', verbose_name=_("Creator"), on_delete=models.SET_NULL, null=True) date_created = models.DateTimeField(auto_now_add=True, verbose_name=_('Date created')) date_start = models.DateTimeField(null=True, verbose_name=_('Date start'), db_index=True) @@ -256,7 +283,28 @@ class JobExecution(JMSOrgBaseModel): return module = self.current_job.module - # replace win_shell + + db_modules = ('mysql', 'postgresql', 'sqlserver', 'oracle') + db_module_name_map = { + 'mysql': 'community.mysql.mysql_query', + 'postgresql': 'community.postgresql.postgresql_query', + 'sqlserver': 'community.general.mssql_script:', + } + + if module in db_modules: + module = db_module_name_map.get(module, None) + if not module: + print('not support db module: {}'.format(module)) + raise Exception('not support db module: {}'.format(module)) + + login_args = "login_host={{login_host}} " \ + "login_user={{login_user}} " \ + "login_password={{login_password}} " \ + "login_port={{login_port}} " \ + "login_db={{login_db}}" + shell = "{} query=\"{}\" ".format(login_args, self.current_job.args) + return module, shell + if module == 'win_shell': module = 'ansible.windows.win_shell' @@ -284,12 +332,10 @@ class JobExecution(JMSOrgBaseModel): extra_vars = json.loads(self.parameters) else: extra_vars = {} - static_variables = self.gather_static_variables() extra_vars.update(static_variables) if self.current_job.type == 'adhoc': - module, args = self.compile_shell() runner = AdHocRunner( diff --git a/apps/users/models/user.py b/apps/users/models/user.py index 69ed7fa80..05c8ea084 100644 --- a/apps/users/models/user.py +++ b/apps/users/models/user.py @@ -45,7 +45,7 @@ class AuthMixin: save: Callable history_passwords: models.Manager sect_cache_tpl = 'user_sect_{}' - id: str | uuid.UUID + id: str @property def password_raw(self):