Skip to content

Commit

Permalink
feat: 订阅接口支持指定用户操作进程 (closed TencentBlueKing#2297)
Browse files Browse the repository at this point in the history
  • Loading branch information
Huayeaaa committed Sep 25, 2024
1 parent 8b3747a commit d5d8a28
Show file tree
Hide file tree
Showing 8 changed files with 90 additions and 3 deletions.
9 changes: 8 additions & 1 deletion apps/backend/components/collections/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = []
Expand All @@ -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
Expand All @@ -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"],
Expand Down
20 changes: 20 additions & 0 deletions apps/backend/subscription/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,11 @@ def validate(self, attrs):
raise ValidationError("目前机器参数必须要有 bk_host_id 或者 (ip/bk_host_innerip + bk_cloud_id)")


class HostOperateInfoSerializer(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=[])
Expand All @@ -83,6 +88,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=HostOperateInfoSerializer(), default=[], label="操作信息")
system_account = serializers.DictField(required=False, label=_("操作系统对应账户"))

# 策略新参数
plugin_name = serializers.CharField(required=False, label="插件名")
Expand All @@ -102,6 +109,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

Expand Down Expand Up @@ -147,12 +158,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=HostOperateInfoSerializer(), 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")
Expand Down
9 changes: 8 additions & 1 deletion apps/backend/subscription/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand All @@ -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"),
)

# 创建订阅步骤
Expand Down Expand Up @@ -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()
Expand Down
13 changes: 12 additions & 1 deletion apps/node_man/handlers/plugin_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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}
Expand Down
28 changes: 28 additions & 0 deletions apps/node_man/migrations/0083_subscription_operate_info.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# -*- coding: utf-8 -*-
"""
TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-节点管理(BlueKing-BK-NODEMAN) available.
Copyright (C) 2017-2022 THL A29 Limited, a Tencent company. All rights reserved.
Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License.
You may obtain a copy of the License at https://opensource.org/licenses/MIT
Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
specific language governing permissions and limitations under the License.
"""

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=None, null=True, verbose_name="操作信息"),
),
]
1 change: 1 addition & 0 deletions apps/node_man/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -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=None, null=True)

pid = models.BigIntegerField(_("父订阅ID"), default=ROOT, db_index=True)

Expand Down
11 changes: 11 additions & 0 deletions apps/node_man/serializers/plugin_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -189,20 +189,31 @@ 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=_("插件名称"))
# 一次性任务范围
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:
raise exceptions.PluginNotExistError(_("不存在名称为: {name} 的插件").format(name=data["plugin_name"]))

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

Expand Down
2 changes: 2 additions & 0 deletions apps/node_man/views/plugin_v2.py
Original file line number Diff line number Diff line change
Expand Up @@ -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"),
)
)

Expand Down

0 comments on commit d5d8a28

Please sign in to comment.