Skip to content

Commit

Permalink
fix(backend): 增加对开区执行校验 #3452
Browse files Browse the repository at this point in the history
  • Loading branch information
iSecloud authored and zhangzhw8 committed Mar 7, 2024
1 parent 1ce42ed commit f431bec
Show file tree
Hide file tree
Showing 5 changed files with 126 additions and 35 deletions.
10 changes: 10 additions & 0 deletions dbm-ui/backend/components/mysql_partition/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,16 @@ def __init__(self):
url="partition/disable_partition",
description=_("禁用分区"),
)
self.enable_partition_cluster = self.generate_data_api(
method="POST",
url="partition/disable_partition",
description=_("禁用分区"),
)
self.disable_partition_cluster = self.generate_data_api(
method="POST",
url="partition/disable_partition",
description=_("禁用分区"),
)
self.query_log = self.generate_data_api(
method="POST",
url="partition/query_log",
Expand Down
118 changes: 94 additions & 24 deletions dbm-ui/backend/db_services/mysql/open_area/handlers.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
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 copy
import itertools
from collections import defaultdict
from typing import Any, Dict, List, Union
Expand All @@ -17,14 +18,16 @@
from backend.components import MySQLPrivManagerApi
from backend.db_meta.enums import ClusterType
from backend.db_meta.models import Cluster
from backend.db_services.mysql.open_area.exceptions import TendbOpenAreaBaseException
from backend.db_services.mysql.open_area.models import TendbOpenAreaConfig
from backend.db_services.mysql.permission.constants import AccountType
from backend.db_services.mysql.remote_service.handlers import RemoteServiceHandler


class OpenAreaHandler:
"""封装开区的一些处理函数"""

ALL_TABLE_FLAG = "*all*"

@classmethod
def validate_only_openarea(cls, bk_biz_id, config_name, config_id: int = -1) -> bool:
"""校验同一业务下的开区模板名称唯一"""
Expand All @@ -33,30 +36,73 @@ def validate_only_openarea(cls, bk_biz_id, config_name, config_id: int = -1) ->
return not is_duplicated

@classmethod
def openarea_result_preview(
cls, operator: str, config_id: int, config_data: List[Dict[str, Union[int, str, Dict]]]
) -> Dict[str, List[Dict[str, Any]]]:
config = TendbOpenAreaConfig.objects.get(id=config_id)
clusters = Cluster.objects.filter(id__in=[info["cluster_id"] for info in config_data])
cluster_id__cluster = {cluster.id: cluster for cluster in clusters}
def __check_db_list(cls, source_db, real_dbs):
"""检查库是否合法"""
if source_db not in real_dbs:
return _("源集群不存在库{},请检查或修改开区模板".format(source_db))
return ""

# 获取开区执行数据
@classmethod
def __check_table_list(cls, real_tables, source_db, check_tables):
"""检查表是否合法"""
if cls.ALL_TABLE_FLAG in check_tables:
check_tables = real_tables[source_db]

error_msg = ""
if cls.ALL_TABLE_FLAG not in check_tables and check_tables:
not_exist_tables = set(check_tables) - set(real_tables[source_db])
if not_exist_tables:
error_msg = _("源集群库{}中不存在表{},请检查或修改开区模板".format(source_db, not_exist_tables))

return check_tables, error_msg

@classmethod
def __get_openarea_execute_objects(cls, config, config_data, cluster_id__cluster):
"""获取开区执行数据结构体"""
remote_handler = RemoteServiceHandler(bk_biz_id=config.bk_biz_id)
openarea_results: List[Dict[str, Any]] = []

# 实时查询集群的库表
real_dbs = remote_handler.show_databases(cluster_ids=[config.source_cluster_id])[0]["databases"]
cluster_db_infos = [{"cluster_id": config.source_cluster_id, "dbs": real_dbs}]
real_tables = remote_handler.show_tables(cluster_db_infos)[0]["table_data"]

# 获取基础执行结构体
execute_objects_tpl = [
{
"source_db": config_rule["source_db"],
"target_db": config_rule["target_db_pattern"],
"schema_tblist": config_rule["schema_tblist"],
"data_tblist": config_rule["data_tblist"],
"priv_data": config_rule["priv_data"],
"authorize_ips": [],
}
for config_rule in config.config_rules
]
# 校验每个开区执行结构,如果存在不合法的库表,则填充错误信息
for info in execute_objects_tpl:
# 校验库是否存在
err_db_msg = cls.__check_db_list(info["source_db"], real_dbs)
# 校验schema_tblist是否合法
info["schema_tblist"], err_schema_tb_msg = cls.__check_table_list(
real_tables, info["source_db"], info["schema_tblist"]
)
# 校验data_tblist是否合法
info["data_tblist"], err_data_tb_msg = cls.__check_table_list(
real_tables, info["source_db"], info["data_tblist"]
)
err_msg_list = [err_db_msg, err_schema_tb_msg, err_data_tb_msg]
info["error_msg"] = "\n".join([msg for msg in err_msg_list if msg])

for data in config_data:
try:
execute_objects = [
{
"source_db": config_rule["source_db"],
"target_db": config_rule["target_db_pattern"].format(**data["vars"]),
"schema_tblist": config_rule["schema_tblist"],
"data_tblist": config_rule["data_tblist"],
"priv_data": config_rule["priv_data"],
"authorize_ips": data["authorize_ips"],
}
for config_rule in config.config_rules
]
except KeyError:
raise TendbOpenAreaBaseException(_("范式渲染缺少变量"))
# 获取开区执行数据
execute_objects = copy.deepcopy(execute_objects_tpl)
for info in execute_objects:
try:
info["target_db"] = info["target_db"].format(**data["vars"])
info["authorize_ips"] = data.get("authorize_ips", [])
except KeyError:
info["error_msg"] = info["error_msg"] + "\n" + _("范式{}渲染缺少变量".format(info["target_db"]))

openarea_results.append(
{
Expand All @@ -66,8 +112,16 @@ def openarea_result_preview(
}
)

# 获取开区授权规则
return openarea_results

@classmethod
def __get_openarea_rules_set(cls, config, config_data, operator, cluster_id__cluster):
"""获取开区授权数据"""
priv_ids = list(itertools.chain(*[rule["priv_data"] for rule in config.config_rules]))
# 如果没有授权ID,则直接返回为空
if not priv_ids:
return []

account_type = AccountType.TENDB if config.cluster_type == ClusterType.TenDBCluster else AccountType.MYSQL
authorize_rules = MySQLPrivManagerApi.list_account_rules(
{"bk_biz_id": config.bk_biz_id, "ids": priv_ids, "cluster_type": account_type}
Expand All @@ -92,5 +146,21 @@ def openarea_result_preview(
for data in config_data
for user in user__dbs_rules.keys()
]
return authorize_details

return {"config_data": openarea_results, "rules_set": authorize_details}
@classmethod
def openarea_result_preview(
cls, operator: str, config_id: int, config_data: List[Dict[str, Union[int, str, Dict]]]
) -> Dict[str, List[Dict[str, Any]]]:
config = TendbOpenAreaConfig.objects.get(id=config_id)
clusters = Cluster.objects.filter(id__in=[info["cluster_id"] for info in config_data])
cluster_id__cluster = {cluster.id: cluster for cluster in clusters}
# 获取开区执行数据
openarea_results: List[Dict[str, Any]] = cls.__get_openarea_execute_objects(
config, config_data, cluster_id__cluster
)
# 获取开区授权规则
rules_set: List[Dict[str, Any]] = cls.__get_openarea_rules_set(
config, config_data, operator, cluster_id__cluster
)
return {"config_data": openarea_results, "rules_set": rules_set}
Original file line number Diff line number Diff line change
Expand Up @@ -278,8 +278,11 @@ def mysql_open_area_flow(self):
if data_flag:
pipeline.add_sub_pipeline(sub_flow=self.open_area_data_flow())

# 对开区的新集群进行授权
pipeline.add_act(act_name=_("添加mysql规则授权"), act_component_code=AuthorizeRulesComponent.code, kwargs=self.data)
# 判断是否对开区的集群进行授权
if self.data.get("rules_set"):
pipeline.add_act(
act_name=_("添加mysql规则授权"), act_component_code=AuthorizeRulesComponent.code, kwargs=self.data
)

pipeline.run_pipeline(is_drop_random_user=True)

Expand Down
22 changes: 14 additions & 8 deletions dbm-ui/backend/flow/utils/mysql/mysql_db_meta.py
Original file line number Diff line number Diff line change
Expand Up @@ -470,10 +470,13 @@ def mysql_cluster_offline(self):
cluster = Cluster.objects.get(id=self.cluster["id"])
cluster.phase = ClusterPhase.OFFLINE
cluster.save()
# TODO: 修改分区配置为禁用状态
DBPartitionApi.disable_partition(
params={"cluster_type": cluster.cluster_type, "bk_biz_id": self.bk_biz_id, "cluster_ids": [cluster.id]}
)
# 修改分区配置为禁用状态 - offlinewithclu
disable_partition_params = {
"cluster_type": cluster.cluster_type,
"operator": self.ticket_data["creator"],
"cluster_ids": [cluster.id],
}
DBPartitionApi.disable_partition_cluster(params=disable_partition_params)

def mysql_cluster_online(self):
"""
Expand All @@ -482,10 +485,13 @@ def mysql_cluster_online(self):
cluster = Cluster.objects.get(id=self.cluster["id"])
cluster.phase = ClusterPhase.ONLINE
cluster.save()
# TODO: 修改分区配置为启用状态
DBPartitionApi.enable_partition(
params={"cluster_type": cluster.cluster_type, "bk_biz_id": self.bk_biz_id, "cluster_ids": [cluster.id]}
)
# 修改分区配置为启用状态 - online
disable_partition_params = {
"cluster_type": cluster.cluster_type,
"operator": self.ticket_data["creator"],
"cluster_ids": [cluster.id],
}
DBPartitionApi.enable_partition_cluster(params=disable_partition_params)

def mysql_migrate_cluster_switch_storage(self):
"""
Expand Down
4 changes: 3 additions & 1 deletion dbm-ui/backend/ticket/builders/mysql/mysql_openarea.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,9 @@ class AccountRulesSerializer(serializers.Serializer):
cluster_id = serializers.IntegerField(help_text=_("源集群ID"))
force = serializers.BooleanField(help_text=_("是否强制执行"), required=False, default=False)
config_data = serializers.ListSerializer(help_text=_("分区信息"), child=ConfigDataSerializer())
rules_set = serializers.ListSerializer(help_text=_("授权信息"), child=PrivDataSerializer())
rules_set = serializers.ListSerializer(
help_text=_("授权信息"), child=PrivDataSerializer(), required=False, allow_null=True, allow_empty=True
)


class MysqlOpenAreaParamBuilder(builders.FlowParamBuilder):
Expand Down

0 comments on commit f431bec

Please sign in to comment.