Skip to content

Commit

Permalink
feat(backend): 单据todo处理人统一化 TencentBlueKing#6755
Browse files Browse the repository at this point in the history
  • Loading branch information
iSecloud committed Dec 16, 2024
1 parent ec9498e commit d669f9e
Show file tree
Hide file tree
Showing 18 changed files with 110 additions and 164 deletions.
11 changes: 11 additions & 0 deletions dbm-ui/backend/configuration/models/system.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from backend.bk_web.constants import LEN_LONG, LEN_NORMAL
from backend.bk_web.models import AuditedModel
from backend.configuration import constants
from backend.configuration.constants import BizSettingsEnum
from backend.db_meta.enums import ClusterType

logger = logging.getLogger("root")
Expand Down Expand Up @@ -192,3 +193,13 @@ def get_exact_hosting_biz(cls, bk_biz_id: int, cluster_type: str) -> int:
return bk_biz_id

return env.DBA_APP_BK_BIZ_ID

@classmethod
def get_assistance(cls, bk_biz_id: int):
"""
获取业务协助人
"""
assistance_flag = BizSettings.get_setting_value(bk_biz_id, key=BizSettingsEnum.BIZ_ASSISTANCE_SWITCH) or False
if not assistance_flag:
return []
return BizSettings.get_setting_value(bk_biz_id, key=BizSettingsEnum.BIZ_ASSISTANCE_VARS, default=[])
4 changes: 2 additions & 2 deletions dbm-ui/backend/db_services/bigdata/resources/query.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
from backend.db_proxy.models import ClusterExtension
from backend.db_services.dbbase.resources import query
from backend.db_services.ipchooser.query.resource import ResourceQueryHelper
from backend.ticket.constants import TICKET_RUNNING_STATUS
from backend.ticket.constants import TICKET_RUNNING_STATUS_SET
from backend.ticket.models import InstanceOperateRecord
from backend.utils.time import datetime2str

Expand Down Expand Up @@ -65,7 +65,7 @@ def _filter_instance_hook(cls, bk_biz_id, query_params, instances, **kwargs):

# 获取实例的操作与实例记录
records = InstanceOperateRecord.objects.filter(
instance_id__in=instance_ids, ticket__status__in=TICKET_RUNNING_STATUS
instance_id__in=instance_ids, ticket__status__in=TICKET_RUNNING_STATUS_SET
)
instance_operate_records_map: Dict[int, List] = defaultdict(list)
for record in records:
Expand Down
4 changes: 2 additions & 2 deletions dbm-ui/backend/db_services/mysql/dumper/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from backend.db_meta.enums import InstanceInnerRole
from backend.db_meta.models import Cluster
from backend.db_services.mysql.dumper.models import DumperSubscribeConfig
from backend.ticket.constants import TICKET_RUNNING_STATUS, FlowType, TicketFlowStatus, TicketStatus, TicketType
from backend.ticket.constants import TICKET_RUNNING_STATUS_SET, FlowType, TicketFlowStatus, TicketStatus, TicketType
from backend.ticket.models import Flow, Ticket


Expand Down Expand Up @@ -66,7 +66,7 @@ def patch_dumper_list_info(cls, dumper_results: List[Dict], bk_biz_id: int = 0,
dumper_ticket_types.remove(TicketType.TBINLOGDUMPER_INSTALL)
dumper_ticket_types.extend([TicketType.MYSQL_MASTER_SLAVE_SWITCH, TicketType.MYSQL_MASTER_FAIL_OVER])
active_tickets = Ticket.objects.filter(
bk_biz_id=bk_biz_id, status__in=TICKET_RUNNING_STATUS, ticket_type__in=dumper_ticket_types
bk_biz_id=bk_biz_id, status__in=TICKET_RUNNING_STATUS_SET, ticket_type__in=dumper_ticket_types
)
# 获取每个dumper单据状态与id的映射
dumper_inst_id__ticket: Dict[int, str] = {}
Expand Down
9 changes: 7 additions & 2 deletions dbm-ui/backend/iam_app/handlers/drf_perm/storage.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,10 +39,15 @@ def __init__(self, actions: List[ActionMeta] = None, resource_meta: ResourceMeta
def instance_id_getter(self, request, view):
file_path = get_request_key_id(request, "file_path")
file_path_list = get_request_key_id(request, "file_path_list") or [file_path]
medium_types = MediumEnum.get_values()

# 解析路径下的文件类型
try:
is_all_pkg = set([(path.strip("/").split("/")[1] in medium_types) for path in file_path_list])
except IndexError:
raise PermissionDeniedError(_("文件操作路径{}不合法,请联系管理员").format(file_path_list))

# 保证文件列表都是同种类型,即不允许同时操作介质文件和业务文件(一般也无此需求)
medium_types = MediumEnum.get_values()
is_all_pkg = set([(path.strip("/").split("/")[1] in medium_types) for path in file_path_list])
if len(is_all_pkg) > 1:
raise PermissionDeniedError(_("不允许同时操作业务临时文件和介质文件"))

Expand Down
2 changes: 1 addition & 1 deletion dbm-ui/backend/ticket/builders/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ def get_params(self):
{"key": "domain", "value": "\n".join(cluster_domains)},
{"key": "summary", "value": self.ticket.remark},
{"key": "approver", "value": self.get_approvers()},
{"key": "ticket_url", "value": f"{self.ticket.url}&isFullscreen=true"},
{"key": "ticket_url", "value": self.ticket.iframe_url},
],
"dynamic_fields": [],
"meta": {
Expand Down
67 changes: 0 additions & 67 deletions dbm-ui/backend/ticket/builders/mysql/mysql_ha_full_backup.py

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
class RedisScaleUpDownDetailSerializer(SkipToRepresentationMixin, serializers.Serializer):
"""redis集群容量变更"""

class InfoSerializer(DisplayInfoSerializer, ClusterValidateMixin, serializers.Serializer):
class InfoSerializer(DisplayInfoSerializer, ClusterValidateMixin):
class ResourceSpecSerializer(serializers.Serializer):
class BackendGroupSerializer(serializers.Serializer):
spec_id = serializers.IntegerField(help_text=_("规格ID"))
Expand Down
6 changes: 3 additions & 3 deletions dbm-ui/backend/ticket/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -96,23 +96,23 @@ class TicketStatus(str, StructuredEnum):


# 单据[正在进行]的状态合集
TICKET_RUNNING_STATUS = [
TICKET_RUNNING_STATUS_SET = [
TicketStatus.APPROVE,
TicketStatus.TODO,
TicketStatus.RESOURCE_REPLENISH,
TicketStatus.RUNNING,
TicketStatus.TIMER,
]
# 单据[包含TODO]的状态合集
TICKET_TODO_STATUS = [
TICKET_TODO_STATUS_SET = [
TicketStatus.APPROVE,
TicketStatus.TODO,
TicketStatus.RESOURCE_REPLENISH,
TicketStatus.FAILED,
TicketStatus.RUNNING,
]
# 单据[失败]的状态合集
TICKET_FAILED_STATUS = [TicketStatus.REVOKED, TicketStatus.TERMINATED, TicketStatus.FAILED]
TICKET_FAILED_STATUS_SET = [TicketStatus.REVOKED, TicketStatus.TERMINATED, TicketStatus.FAILED]


class TicketFlowStatus(str, StructuredEnum):
Expand Down
6 changes: 2 additions & 4 deletions dbm-ui/backend/ticket/flow_manager/inner.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,8 @@
from backend.ticket.constants import (
BAMBOO_STATE__TICKET_STATE_MAP,
FlowCallbackType,
FlowMsgType,
FlowErrCode,
FlowMsgType,
TicketFlowStatus,
TicketType,
TodoStatus,
Expand Down Expand Up @@ -122,13 +122,11 @@ def _url(self) -> str:
return f"{env.BK_SAAS_HOST}/{self.ticket.bk_biz_id}/task-history/detail/{self.root_id}"

def create_failed_todo(self):
# 创建一条todo失败记录,在失败时变更为TODO状态
Todo.objects.create(
name=_("【{}】单据任务执行失败,待处理").format(self.ticket.get_ticket_type_display()),
flow=self.flow_obj,
ticket=self.ticket,
type=TodoType.INNER_FAILED,
operators=[self.ticket.creator],
context=BaseTodoContext(self.flow_obj.id, self.ticket.id).to_dict(),
status=TodoStatus.DONE_SUCCESS,
)
Expand Down Expand Up @@ -280,7 +278,7 @@ def _raw_status(self) -> str:
@property
def _status(self) -> str:
status = self._raw_status
if status in [constants.TicketFlowStatus.SUCCEEDED, *constants.TICKET_FAILED_STATUS]:
if status in [constants.TicketFlowStatus.SUCCEEDED, *constants.TICKET_FAILED_STATUS_SET]:
return constants.TicketFlowStatus.SUCCEEDED

return status
3 changes: 0 additions & 3 deletions dbm-ui/backend/ticket/flow_manager/itsm.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,14 +113,11 @@ def _url(self) -> str:

def _run(self) -> str:
itsm_fields = {f["key"]: f["value"] for f in self.flow_obj.details["fields"]}
# 创建审批todo
operators = itsm_fields["approver"].split(",")
Todo.objects.create(
name=_("【{}】单据等待审批").format(self.ticket.get_ticket_type_display()),
flow=self.flow_obj,
ticket=self.ticket,
type=TodoType.ITSM,
operators=operators,
context=ItsmTodoContext(self.flow_obj.id, self.ticket.id).to_dict(),
)
# 创建单据
Expand Down
7 changes: 2 additions & 5 deletions dbm-ui/backend/ticket/flow_manager/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,17 +204,14 @@ def create_replenish_todo(self):

from backend.ticket.todos.pause_todo import ResourceReplenishTodoContext

user = self.ticket.creator
admins = DBAdministrator.get_biz_db_type_admins(self.ticket.bk_biz_id, self.ticket.group)
dba = DBAdministrator.get_biz_db_type_admins(self.ticket.bk_biz_id, self.ticket.group)
Todo.objects.create(
name=_("【{}】流程所需资源不足").format(self.ticket.get_ticket_type_display()),
flow=self.flow_obj,
ticket=self.ticket,
type=TodoType.RESOURCE_REPLENISH,
# 需要提单人和管理人员共同关注todo单
operators=[user, *admins],
context=ResourceReplenishTodoContext(
flow_id=self.flow_obj.id, ticket_id=self.ticket.id, user=user, administrators=admins
flow_id=self.flow_obj.id, ticket_id=self.ticket.id, user=self.ticket.creator, administrators=dba
).to_dict(),
)

Expand Down
1 change: 0 additions & 1 deletion dbm-ui/backend/ticket/flow_manager/timer.py
Original file line number Diff line number Diff line change
Expand Up @@ -98,7 +98,6 @@ def _run(self) -> Union[int, str]:
flow=self.flow_obj,
ticket=self.ticket,
type=TodoType.APPROVE,
operators=[self.ticket.creator],
context=PauseTodoContext(self.flow_obj.id, self.ticket.id).to_dict(),
)
else:
Expand Down
83 changes: 37 additions & 46 deletions dbm-ui/backend/ticket/handler.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
import itertools
import json
import logging
import time
from typing import Dict, List

from django.db import transaction
Expand All @@ -36,7 +35,6 @@
TicketFlowStatus,
TicketStatus,
TicketType,
TodoStatus,
TodoType,
)
from backend.ticket.exceptions import TicketFlowsConfigException
Expand Down Expand Up @@ -240,11 +238,11 @@ def approve_itsm_ticket(cls, ticket_id, action, operator, **kwargs):
{"key": itsm_fields[1], "value": act_msg},
]
params.update(sn=sn, state_id=state_id, action_type=action, operator=operator, fields=fields)
ItsmApi.operate_node(params, use_admin=True)
ItsmApi.operate_node(params)
# 终止/撤销单据
elif action in [OperateNodeActionType.TERMINATE, OperateNodeActionType.WITHDRAW]:
params.update(sn=sn, action_type=action, operator=operator)
ItsmApi.operate_ticket(params, use_admin=True)
ItsmApi.operate_ticket(params)

return sn

Expand Down Expand Up @@ -424,51 +422,44 @@ def ticket_status_standardization(cls):
batch = 50

# 标准化只针对running的单据,其他状态单据不影响
running_tickets = Ticket.objects.filter(status=TicketStatus.RUNNING)
count = running_tickets.count()
for current in range(0, count, batch):
for ticket in running_tickets[current : current + batch]:
raw_status = ticket.status
ticket.status = RUNNING_FLOW__TICKET_STATUS[ticket.current_flow().flow_type]
ticket.save()
print(f"ticket[{ticket.id}] status {raw_status} ---> {ticket.status}")
time.sleep(1)
running_tickets = list(Ticket.objects.filter(status=TicketStatus.RUNNING))
for ticket in running_tickets:
raw_status = ticket.status
ticket.status = RUNNING_FLOW__TICKET_STATUS[ticket.current_flow().flow_type]
ticket.save()
print(f"ticket[{ticket.id}] status {raw_status} ---> {ticket.status}")

# 失败的单据要增加一条todo关联
failed_tickets = Ticket.objects.prefetch_related("flows").filter(status=TicketStatus.FAILED)
for current in range(0, count, batch):
for ticket in failed_tickets[current : current + batch]:
inner_flow = ticket.flows.filter(flow_type=FlowType.INNER_FLOW, status=TicketFlowStatus.FAILED).first()
if not inner_flow or inner_flow.todo_of_flow.exists():
continue
Todo.objects.create(
name=_("【{}】单据任务执行失败,待处理").format(ticket.get_ticket_type_display()),
flow=inner_flow,
ticket=ticket,
type=TodoType.INNER_FAILED,
operators=[ticket.creator],
context=BaseTodoContext(inner_flow.id, ticket.id).to_dict(),
status=TodoStatus.TODO,
)
print(f"ticket[{ticket.id}] add a failed todo")
time.sleep(1)
todos = []
for ticket in failed_tickets:
inner_flow = ticket.flows.filter(flow_type=FlowType.INNER_FLOW, status=TicketFlowStatus.FAILED).first()
if not inner_flow or inner_flow.todo_of_flow.exists():
continue
todo = Todo(
name=_("【{}】单据任务执行失败,待处理").format(ticket.get_ticket_type_display()),
flow=inner_flow,
ticket=ticket,
type=TodoType.INNER_FAILED,
context=BaseTodoContext(inner_flow.id, ticket.id).to_dict(),
)
todos.append(todo)
print(f"ticket[{ticket.id}] add a failed todo")

# 待审批的单据要增加一条todo关联
itsm_tickets = Ticket.objects.prefetch_related("flows").filter(status=TicketStatus.FAILED)
for current in range(0, count, batch):
for ticket in itsm_tickets[current : current + batch]:
itsm_flow = ticket.flows.filter(flow_type=FlowType.BK_ITSM, status=TicketFlowStatus.RUNNING).first()
if not itsm_flow or itsm_flow.todo_of_flow.exists():
continue
itsm_fields = {f["key"]: f["value"] for f in itsm_flow.details["fields"]}
operators = itsm_fields["approver"].split(",")
Todo.objects.create(
name=_("【{}】单据等待审批").format(ticket.get_ticket_type_display()),
flow=itsm_flow,
ticket=ticket,
type=TodoType.ITSM,
operators=operators,
context=ItsmTodoContext(itsm_flow.id, ticket.id).to_dict(),
)
print(f"ticket[{ticket.id}] add a itsm todo")
time.sleep(1)
for ticket in itsm_tickets:
itsm_flow = ticket.flows.filter(flow_type=FlowType.BK_ITSM, status=TicketFlowStatus.RUNNING).first()
if not itsm_flow or itsm_flow.todo_of_flow.exists():
continue
todo = Todo(
name=_("【{}】单据等待审批").format(ticket.get_ticket_type_display()),
flow=itsm_flow,
ticket=ticket,
type=TodoType.ITSM,
context=ItsmTodoContext(itsm_flow.id, ticket.id).to_dict(),
)
todos.append(todo)
print(f"ticket[{ticket.id}] add a itsm todo")

Todo.objects.bulk_create(todos, batch_size=batch)
Loading

0 comments on commit d669f9e

Please sign in to comment.