From 9c33a3958d6a1b2aa0b9b46a656c373f3c625203 Mon Sep 17 00:00:00 2001 From: dcd <1151627903@qq.com> Date: Thu, 18 Jul 2024 20:44:37 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E8=AE=A2=E9=98=85=E6=8E=A5=E5=8F=A3?= =?UTF-8?q?=E6=94=AF=E6=8C=81=E6=8C=87=E5=AE=9A=E7=94=A8=E6=88=B7=E6=93=8D?= =?UTF-8?q?=E4=BD=9C=E8=BF=9B=E7=A8=8B=20(closed=20#2297)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/backend/components/collections/plugin.py | 9 ++++- apps/backend/subscription/serializers.py | 35 +++++++++++++++++++ apps/backend/subscription/views.py | 9 ++++- apps/node_man/handlers/plugin_v2.py | 13 ++++++- .../0083_subscription_operate_info.py | 19 ++++++++++ apps/node_man/models.py | 1 + apps/node_man/serializers/plugin_v2.py | 11 ++++++ apps/node_man/views/plugin_v2.py | 2 ++ 8 files changed, 96 insertions(+), 3 deletions(-) create mode 100644 apps/node_man/migrations/0083_subscription_operate_info.py diff --git a/apps/backend/components/collections/plugin.py b/apps/backend/components/collections/plugin.py index b8412e95d..d87312bd2 100644 --- a/apps/backend/components/collections/plugin.py +++ b/apps/backend/components/collections/plugin.py @@ -1168,6 +1168,12 @@ def _execute(self, data, parent_data, common_data: PluginCommonData): plugin = policy_step_adapter.plugin_desc group_id_instance_map = common_data.group_id_instance_map host_id_obj_map = common_data.host_id_obj_map + operate_info: List = common_data.subscription.operate_info + host_id_user_map = {} + system_account = {} + if operate_info: + host_id_user_map: Dict[int, str] = {info.get("bk_host_id"): info.get("user") for info in operate_info} + system_account: Dict[str, str] = operate_info[0] host_id__resource_policy_map = self.get_resource_policy(common_data.bk_host_ids, plugin.name) proc_operate_req = [] @@ -1178,6 +1184,7 @@ def _execute(self, data, parent_data, common_data: PluginCommonData): for process_status in process_statuses: bk_host_id = process_status.bk_host_id host = host_id_obj_map.get(bk_host_id) + operate_user = host_id_user_map.get(bk_host_id) or system_account.get(host.os_type) subscription_instance = group_id_instance_map.get(process_status.group_id) package = self.get_package_by_process_status(process_status, common_data) package_control = package.proc_control @@ -1204,7 +1211,7 @@ def _execute(self, data, parent_data, common_data: PluginCommonData): "proc_name": package_control.process_name or plugin.name, "setup_path": process_status.setup_path, "pid_path": process_status.pid_path, - "user": constants.ACCOUNT_MAP.get(host.os_type, "root"), + "user": operate_user or constants.ACCOUNT_MAP.get(host.os_type, "root"), }, "control": gse_control, "resource": host_id__resource_policy_map[bk_host_id]["resource"], diff --git a/apps/backend/subscription/serializers.py b/apps/backend/subscription/serializers.py index be69cafd6..ecb01dd53 100644 --- a/apps/backend/subscription/serializers.py +++ b/apps/backend/subscription/serializers.py @@ -70,6 +70,26 @@ def validate(self, attrs): raise ValidationError("目前机器参数必须要有 bk_host_id 或者 (ip/bk_host_innerip + bk_cloud_id)") +class OperateInfoSerializer(serializers.Serializer): + bk_host_id = serializers.IntegerField(required=False, label=_("主机ID")) + user = serializers.CharField(required=False, label=_("操作用户")) + system_account = serializers.DictField(required=False, label=_("操作系统对应账户")) + + def validate(self, attrs): + if attrs.get("bk_host_id") and attrs.get("system_account"): + raise ValidationError(_("仅支持一种方式: 实例 or 操作系统 指定操作用户")) + if attrs.get("system_account"): + for key in attrs["system_account"]: + if key not in constants.OS_TUPLE: + raise ValidationError(_(f"操作系统类型只能为{constants.OS_TUPLE}")) + return attrs + + +class HostUserInfoSerializer(serializers.Serializer): + bk_host_id = serializers.IntegerField(label=_("主机ID")) + user = serializers.CharField(label=_("操作用户")) + + class CreateSubscriptionSerializer(GatewaySerializer): class CreateStepSerializer(serializers.Serializer): id = serializers.CharField(label="步骤标识符", validators=[]) @@ -83,6 +103,8 @@ class CreateStepSerializer(serializers.Serializer): target_hosts = TargetHostSerializer(many=True, label="下发的目标机器列表", required=False, allow_empty=False) run_immediately = serializers.BooleanField(required=False, default=False, label="是否立即执行") is_main = serializers.BooleanField(required=False, default=False, label="是否为主配置") + operate_info = serializers.ListField(required=False, child=HostUserInfoSerializer(), default=[], label="操作信息") + system_account = serializers.DictField(required=False, label=_("操作系统对应账户")) # 策略新参数 plugin_name = serializers.CharField(required=False, label="插件名") @@ -102,6 +124,10 @@ def validate(self, attrs): ): raise ValidationError(_("订阅范围包含Gse2.0灰度业务")) step_types = {step["type"] for step in attrs["steps"]} + if attrs.get("system_account"): + for key in attrs["system_account"]: + if key not in constants.OS_TUPLE: + raise ValidationError(_(f"操作系统类型只能为{constants.OS_TUPLE}")) if constants.SubStepType.AGENT not in step_types: return attrs @@ -147,12 +173,21 @@ class UpdateStepSerializer(serializers.Serializer): scope = UpdateScopeSerializer() steps = serializers.ListField(child=UpdateStepSerializer()) run_immediately = serializers.BooleanField(required=False, default=False) + operate_info = serializers.ListField(required=False, child=HostUserInfoSerializer(), default=[], label="操作信息") + system_account = serializers.DictField(required=False, label=_("操作系统对应账户")) # 策略新参数 plugin_name = serializers.CharField(required=False) bk_biz_scope = serializers.ListField(child=serializers.IntegerField(), required=False, default=[]) category = serializers.CharField(required=False) + def validate(self, attrs): + if attrs.get("system_account"): + for key in attrs["system_account"]: + if key not in constants.OS_TUPLE: + raise ValidationError(_(f"操作系统类型只能为{constants.OS_TUPLE}")) + return attrs + class DeleteSubscriptionSerializer(GatewaySerializer): subscription_id = serializers.IntegerField(label="订阅ID") diff --git a/apps/backend/subscription/views.py b/apps/backend/subscription/views.py index 8935ef2c6..2b8ac6d74 100644 --- a/apps/backend/subscription/views.py +++ b/apps/backend/subscription/views.py @@ -75,7 +75,8 @@ def create_subscription(self, request): if category == models.Subscription.CategoryType.POLICY: # 策略类型订阅默认开启 enable = True - + if params.get("system_account"): + params["operate_info"].insert(0, params["system_account"]) with transaction.atomic(): # 创建订阅 subscription = models.Subscription.objects.create( @@ -95,6 +96,8 @@ def create_subscription(self, request): category=params.get("category"), plugin_name=params.get("plugin_name"), pid=params.get("pid", models.Subscription.ROOT), + # 指定操作进程用户新增 + operate_info=params.get("operate_info"), ) # 创建订阅步骤 @@ -212,6 +215,10 @@ def update_subscription(self, request): # 策略部署新增 subscription.plugin_name = params.get("plugin_name") subscription.bk_biz_scope = params.get("bk_biz_scope") + # 指定操作进程用户新增 + if params.get("system_account"): + params["operate_info"].insert(0, params["system_account"]) + subscription.operate_info = params["operate_info"] subscription.save() step_ids: Set[str] = set() diff --git a/apps/node_man/handlers/plugin_v2.py b/apps/node_man/handlers/plugin_v2.py index 588cbeb5c..983136b83 100644 --- a/apps/node_man/handlers/plugin_v2.py +++ b/apps/node_man/handlers/plugin_v2.py @@ -152,7 +152,14 @@ def history(query_params: Dict): return packages @staticmethod - def operate(job_type: str, plugin_name: str, scope: Dict, steps: List[Dict]): + def operate( + job_type: str, + plugin_name: str, + scope: Dict, + steps: List[Dict], + operate_info: List[Dict] = None, + system_account: Dict = None, + ): bk_biz_scope = list(set([node["bk_biz_id"] for node in scope["nodes"]])) CmdbHandler().check_biz_permission(bk_biz_scope, IamActionType.plugin_operate) @@ -166,6 +173,10 @@ def operate(job_type: str, plugin_name: str, scope: Dict, steps: List[Dict]): "scope": scope, "bk_biz_scope": bk_biz_scope, } + if operate_info: + base_create_kwargs["operate_info"] = operate_info + if system_account: + base_create_kwargs["system_account"] = system_account if job_type == constants.JobType.MAIN_INSTALL_PLUGIN: create_data = {**base_create_kwargs, "steps": steps} diff --git a/apps/node_man/migrations/0083_subscription_operate_info.py b/apps/node_man/migrations/0083_subscription_operate_info.py new file mode 100644 index 000000000..c9700e1b4 --- /dev/null +++ b/apps/node_man/migrations/0083_subscription_operate_info.py @@ -0,0 +1,19 @@ +# Generated by Django 3.2.4 on 2024-09-10 10:32 + +import django_mysql.models +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ("node_man", "0082_host_dept_name"), + ] + + operations = [ + migrations.AddField( + model_name="subscription", + name="operate_info", + field=django_mysql.models.JSONField(default=list, verbose_name="操作信息"), + ), + ] diff --git a/apps/node_man/models.py b/apps/node_man/models.py index fb57dfe19..819193a0c 100644 --- a/apps/node_man/models.py +++ b/apps/node_man/models.py @@ -1859,6 +1859,7 @@ class CategoryType(object): category = models.CharField(_("订阅类别"), max_length=32, null=True, blank=True, db_index=True) plugin_name = models.CharField(_("插件名称"), max_length=64, null=True, blank=True, db_index=True) bk_biz_scope = JSONField(_("业务范围"), default=list) + operate_info = JSONField(_("操作信息"), default=list) pid = models.BigIntegerField(_("父订阅ID"), default=ROOT, db_index=True) diff --git a/apps/node_man/serializers/plugin_v2.py b/apps/node_man/serializers/plugin_v2.py index 0e73dc578..80a5bcd2b 100644 --- a/apps/node_man/serializers/plugin_v2.py +++ b/apps/node_man/serializers/plugin_v2.py @@ -189,6 +189,11 @@ def validate(self, data): return data +class HostUserInfoSerializer(serializers.Serializer): + bk_host_id = serializers.IntegerField(label=_("主机ID")) + user = serializers.CharField(label=_("操作用户")) + + class PluginOperateSerializer(serializers.Serializer): job_type = serializers.ChoiceField(label=_("任务类型"), choices=list(constants.PLUGIN_JOB_TUPLE)) plugin_name = serializers.CharField(label=_("插件名称")) @@ -196,6 +201,8 @@ class PluginOperateSerializer(serializers.Serializer): scope = base.ScopeSerializer() # 参数配置 steps = serializers.ListField(child=base.StepSerializer(), required=False, default=[]) + operate_info = serializers.ListField(label=_("操作信息"), child=HostUserInfoSerializer(), default=[], required=False) + system_account = serializers.DictField(required=False, label=_("操作系统对应账户")) def validate(self, data): if models.GsePluginDesc.objects.filter(name=data["plugin_name"]).first() is None: @@ -203,6 +210,10 @@ def validate(self, data): if data["job_type"] == constants.JobType.MAIN_INSTALL_PLUGIN and not data["steps"]: raise ValidationError(_("插件安装/更新需要传递steps")) + if data.get("system_account"): + for key in data["system_account"]: + if key not in constants.OS_TUPLE: + raise ValidationError(_(f"操作系统类型只能为{constants.OS_TUPLE}")) return data diff --git a/apps/node_man/views/plugin_v2.py b/apps/node_man/views/plugin_v2.py index db55c45f9..7df98ba81 100644 --- a/apps/node_man/views/plugin_v2.py +++ b/apps/node_man/views/plugin_v2.py @@ -749,6 +749,8 @@ def operate(self, request): plugin_name=params["plugin_name"], scope=params["scope"], steps=params.get("steps"), + operate_info=params.get("operate_info"), + system_account=params.get("system_account"), ) )