Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 14 additions & 1 deletion apps/backend/components/collections/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -44,12 +44,12 @@
get_all_subscription_steps_context,
render_config_files_by_config_templates,
)
from apps.core.ipchooser.handlers.host_handler import HostHandler
from apps.core.tag import targets
from apps.core.tag.models import Tag
from apps.exceptions import AppBaseException, ComponentCallError
from apps.node_man import constants, exceptions, models
from apps.node_man.handlers.cmdb import CmdbHandler
from apps.core.ipchooser.handlers.host_handler import HostHandler
from apps.prometheus import metrics
from apps.prometheus.helper import SetupObserve
from apps.utils import cache, md5
Expand Down Expand Up @@ -1520,6 +1520,13 @@ def _execute(self, data, parent_data, common_data: PluginCommonData):
)


class DirectDeleteSubscriptionService(PluginBaseService):
def _execute(self, data, parent_data, common_data: PluginCommonData):
subscription = common_data.subscription
subscription.delete()
self.log_info(log_content=_("订阅 -> {id} 删除成功").format(id=subscription.id))


class SwitchSubscriptionEnableService(PluginBaseService):
def inputs_format(self):
return [
Expand Down Expand Up @@ -1735,6 +1742,12 @@ class DeleteSubscriptionComponent(Component):
bound_service = DeleteSubscriptionService


class DirectDeleteSubscriptionComponent(Component):
name = "DirectDeleteSubscriptionComponent"
code = "direct_delete_subscription"
bound_service = DirectDeleteSubscriptionService


class SwitchSubscriptionEnableComponent(Component):
name = "SwitchSubscriptionEnableComponent"
code = "switch_subscription_enable"
Expand Down
1 change: 1 addition & 0 deletions apps/backend/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ class InstNodeType(object):
"RESTART",
"START",
"STOP",
"UNINSTALL_AND_DELETE",
# Agent
"INSTALL_AGENT",
"REINSTALL_AGENT",
Expand Down
4 changes: 4 additions & 0 deletions apps/backend/plugin/manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,10 @@ def delete_subscription(self):
act = PluginServiceActivity(component_code=plugin.DeleteSubscriptionComponent.code, name=_("删除策略"))
return act

def direct_delete_subscription(self):
act = PluginServiceActivity(component_code=plugin.DirectDeleteSubscriptionComponent.code, name=_("删除订阅"))
return act

def switch_subscription_enable(self, enable: bool):
act = PluginServiceActivity(component_code=plugin.SwitchSubscriptionEnableComponent.code, name=_("切换订阅启用状态"))
act.component.inputs.enable = Var(type=Var.PLAIN, value=enable)
Expand Down
39 changes: 39 additions & 0 deletions apps/backend/subscription/steps/plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ def get_supported_actions(self) -> Dict:
backend_const.ActionNameType.MAIN_STOP_AND_DELETE_PLUGIN: MainStopAndDeletePlugin,
backend_const.ActionNameType.DEBUG_PLUGIN: DebugPlugin,
backend_const.ActionNameType.STOP_DEBUG_PLUGIN: StopDebugPlugin,
backend_const.ActionNameType.UNINSTALL_AND_DELETE: UninstallAndDeletePlugin,
}
if self.plugin_desc.is_official:
# 官方插件是基于多配置的管理模式,安装、卸载、启用、停用等操作仅涉及到配置的增删
Expand Down Expand Up @@ -1099,6 +1100,44 @@ def _generate_activities(self, plugin_manager):
return activities, None


class UninstallAndDeletePlugin(PluginAction):
"""
卸载插件并删除订阅
"""

ACTION_NAME = backend_const.ActionNameType.UNINSTALL_AND_DELETE
ACTION_DESCRIPTION = _("卸载插件并删除订阅")

def _generate_activities(self, plugin_manager):
# 停用插件 -> 卸载插件
activities = [
plugin_manager.operate_proc(constants.GseOpType.STOP, self.step.plugin_desc),
plugin_manager.uninstall_package(),
plugin_manager.set_process_status(constants.ProcStateType.REMOVED),
]
return activities, None

def generate_activities(
self,
subscription_instances: List[models.SubscriptionInstanceRecord],
global_pipeline_data: Data,
meta: Dict[str, Any],
current_activities=None,
):
plugin_manager = self.get_plugin_manager(subscription_instances)
activities, pipeline_data = super().generate_activities(
subscription_instances, global_pipeline_data, meta, current_activities
)
# 最后一个批次删除订阅
if meta.get("is_last_batch", False):
activities.append(plugin_manager.direct_delete_subscription())
for act in activities:
act.component.inputs.plugin_name = Var(type=Var.PLAIN, value=self.step.plugin_name)
act.component.inputs.subscription_step_id = Var(type=Var.PLAIN, value=self.step.subscription_step.id)
act.component.inputs.meta = Var(type=Var.PLAIN, value=meta)
return activities, pipeline_data


class PushConfig(PluginAction):
"""
下发插件配置
Expand Down
20 changes: 19 additions & 1 deletion apps/backend/subscription/tasks.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ def build_instances_task(
step_actions: Dict[str, str],
subscription: models.Subscription,
global_pipeline_data: Data,
all_instance_ids: List[str] = None,
):
"""
对同类step_actions任务进行任务编排
Expand All @@ -68,6 +69,7 @@ def build_instances_task(
:param step_actions: {"basereport": "MAIN_INSTALL_PLUGIN"}
:param subscription: 订阅对象
:param global_pipeline_data: 全局pipeline公共变量
:param all_instance_ids: 全部的实例id,用于卸载并删除订阅
:return:
"""
# 首先获取当前订阅对应步骤的工厂类
Expand All @@ -93,6 +95,16 @@ def build_instances_task(

# 将 step_actions 信息注入 meta
inject_meta: Dict[str, Any] = {**meta, "STEPS": list(step_id_record_step_map.values())}
if ActionNameType.UNINSTALL_AND_DELETE in step_actions.values() and all_instance_ids:
total = len(all_instance_ids)
task_host_limit = models.GlobalSettings.get_config(
models.GlobalSettings.KeyEnum.TASK_HOST_LIMIT.value, default=TASK_HOST_LIMIT
)
last_group_start = total - (total % task_host_limit or task_host_limit)
last_group_instance_ids = set(all_instance_ids[last_group_start:])
batch_ids = [inst.instance_id for inst in subscription_instances]
# 判断当前批次是否为最后一个批次并注入 meta
inject_meta["is_last_batch"] = set(batch_ids).issubset(last_group_instance_ids)

# 对流程步骤进行编排
current_activities = []
Expand Down Expand Up @@ -216,10 +228,13 @@ def create_pipeline(

sub_insts_gby_metadata: Dict[str, List[models.SubscriptionInstanceRecord]] = defaultdict(list)
md5_value__metadata = {}
all_instance_ids = []
for instance_id, step_actions in instances_action.items():
if instance_id not in subscription_instance_map:
continue
sub_inst = subscription_instance_map[instance_id]
if ActionNameType.UNINSTALL_AND_DELETE in step_actions.values():
all_instance_ids.append(sub_inst.instance_id)
# metadata 包含:meta-任务元数据、step_actions-操作步骤及类型
metadata = {"meta": sub_inst.instance_info["meta"], "step_actions": step_actions}
metadata_md5_value = md5.count_md5(metadata)
Expand Down Expand Up @@ -247,6 +262,7 @@ def create_pipeline(
metadata["step_actions"],
subscription,
global_pipeline_data,
all_instance_ids=all_instance_ids if all_instance_ids else None,
)
sub_processes.append(activities_start_event)
start = start + task_host_limit
Expand Down Expand Up @@ -731,7 +747,9 @@ def run_subscription_task_and_create_instance(
# 传入的nodes 范围在CC中不存在使用最近的Recoreds记录
# 如果被删掉的实例在 CMDB 找不到,那么就使用最近一次的 InstanceRecord 的快照数据
instance_host_id_map = {
node["id"]: node.get("bk_host_id") for node in scope["nodes"] if node.get("bk_host_id") is not None
node["id"]: node["bk_host_id"]
for node in scope["nodes"]
if "id" in node and node.get("bk_host_id") is not None
}
not_exist_instance_id = []
for node in scope["nodes"]:
Expand Down
Loading