Skip to content

Commit

Permalink
fix(backend): 主从迁移/slave重建增加备份校验 #8919
Browse files Browse the repository at this point in the history
  • Loading branch information
iSecloud committed Jan 7, 2025
1 parent b441e2a commit 2887b66
Show file tree
Hide file tree
Showing 5 changed files with 36 additions and 11 deletions.
13 changes: 13 additions & 0 deletions dbm-ui/backend/ticket/builders/mysql/base.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 datetime
import re
from typing import Any, Dict, List, Union

Expand All @@ -18,6 +19,7 @@
from backend.configuration.constants import DBType
from backend.db_meta.enums import AccessLayer, ClusterDBHAStatusFlags, ClusterType, InstanceInnerRole
from backend.db_meta.models.cluster import Cluster, ClusterPhase
from backend.db_services.mysql.fixpoint_rollback.handlers import FixPointRollbackHandler
from backend.flow.consts import SYSTEM_DBS
from backend.flow.utils.mysql.db_table_filter.exception import DbTableFilterValidateException
from backend.flow.utils.mysql.db_table_filter.tools import glob_check
Expand Down Expand Up @@ -166,6 +168,17 @@ def validate_slave_is_stand_by(self, attrs):
slave_insts = [f"{info['slave_ip']['ip']}" for info in attrs["infos"]]
CommonValidate.validate_slave_is_stand_by(slave_insts)

def validated_cluster_latest_backup(self, cluster_ids, backup_source, backup_type=None):
"""校验集群是否具有最近一次备份日志"""
now = datetime.datetime.now(datetime.timezone.utc)
for cluster in cluster_ids:
handler = FixPointRollbackHandler(cluster_id=cluster)
backup = handler.query_latest_backup_log(rollback_time=now, backup_source=backup_source)
if not backup:
raise serializers.ValidationError(_("集群{}无法找到最近一次备份").format(cluster))
if backup_type and backup["backup_type"] != backup_type:
raise serializers.ValidationError(_("集群{}最近一次备份类型不匹配{}").format(cluster, backup_type))

def validate(self, attrs):
# 默认全局校验只需要校验集群的状态
self.validate_cluster_can_access(attrs)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
from backend.db_services.dbbase.constants import IpSource
from backend.flow.engine.controller.mysql import MySQLController
from backend.ticket import builders
from backend.ticket.builders.common.base import BaseOperateResourceParamBuilder, HostInfoSerializer
from backend.ticket.builders.common.base import BaseOperateResourceParamBuilder, HostInfoSerializer, fetch_cluster_ids
from backend.ticket.builders.common.constants import MySQLBackupSource
from backend.ticket.builders.mysql.base import MySQLBaseOperateDetailSerializer
from backend.ticket.builders.mysql.mysql_master_slave_switch import (
Expand All @@ -43,10 +43,15 @@ class MigrateClusterInfoSerializer(serializers.Serializer):
is_safe = serializers.BooleanField(help_text=_("安全模式"), default=True)

def validate(self, attrs):
cluster_ids = fetch_cluster_ids(attrs)

# 校验集群是否可用,集群类型为高可用
super().validate_cluster_can_access(attrs)
super().validated_cluster_type(attrs, ClusterType.TenDBHA)

# 校验集群存在最近一次全备
super().validated_cluster_latest_backup(cluster_ids, attrs["backup_source"])

if attrs["ip_source"] == IpSource.RESOURCE_POOL:
return attrs

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
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 datetime
import itertools

from django.utils.translation import gettext_lazy as _
Expand All @@ -18,7 +17,6 @@
from backend.db_meta.enums import ClusterType, InstanceRole
from backend.db_meta.models import Cluster
from backend.db_services.dbbase.constants import IpSource
from backend.db_services.mysql.fixpoint_rollback.handlers import FixPointRollbackHandler
from backend.flow.consts import MySQLBackupTypeEnum
from backend.flow.engine.controller.mysql import MySQLController
from backend.ticket import builders
Expand Down Expand Up @@ -66,13 +64,8 @@ def validate(self, attrs):
super(MysqlMigrateUpgradeDetailSerializer, self).validated_cluster_type(attrs, ClusterType.TenDBHA)

# 校验集群最近一次备份记录是逻辑备份
now = datetime.datetime.now(datetime.timezone.utc)
cluster_ids = fetch_cluster_ids(attrs)
for cluster in cluster_ids:
handler = FixPointRollbackHandler(cluster_id=cluster)
backup = handler.query_latest_backup_log(rollback_time=now, backup_source=attrs["backup_source"])
if not backup or backup["backup_type"] != MySQLBackupTypeEnum.LOGICAL:
raise serializers.ValidationError(_("集群{}无法找到最近一次备份,或最近一次备份不为逻辑备份").format(cluster))
super().validated_cluster_latest_backup(cluster_ids, attrs["backup_source"], MySQLBackupTypeEnum.LOGICAL)

if attrs["ip_source"] == IpSource.RESOURCE_POOL:
return attrs
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from backend.db_meta.enums import ClusterType, InstanceInnerRole
from backend.flow.engine.controller.mysql import MySQLController
from backend.ticket import builders
from backend.ticket.builders.common.base import InstanceInfoSerializer
from backend.ticket.builders.common.base import InstanceInfoSerializer, fetch_cluster_ids
from backend.ticket.builders.common.constants import MySQLBackupSource
from backend.ticket.builders.mysql.base import BaseMySQLHATicketFlowBuilder, MySQLBaseOperateDetailSerializer
from backend.ticket.constants import TicketType
Expand All @@ -31,6 +31,8 @@ class SlaveInfoSerializer(serializers.Serializer):
force = serializers.BooleanField(help_text=_("是否强制执行"), required=False, default=False)

def validate(self, attrs):
cluster_ids = fetch_cluster_ids(attrs)

# 校验集群是否可用,集群类型为高可用
super(MysqlRestoreLocalSlaveDetailSerializer, self).validate_cluster_can_access(attrs)
super(MysqlRestoreLocalSlaveDetailSerializer, self).validated_cluster_type(attrs, ClusterType.TenDBHA)
Expand All @@ -45,6 +47,11 @@ def validate(self, attrs):
attrs, instance_key=["slave"], cluster_key=["cluster_id"], role=InstanceInnerRole.SLAVE
)

# 校验集群存在最近一次全备
super(MysqlRestoreLocalSlaveDetailSerializer, self).validated_cluster_latest_backup(
cluster_ids, attrs["backup_source"]
)

return attrs


Expand Down
9 changes: 8 additions & 1 deletion dbm-ui/backend/ticket/builders/mysql/mysql_restore_slave.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
from backend.db_meta.enums import ClusterType, InstanceInnerRole
from backend.flow.engine.controller.mysql import MySQLController
from backend.ticket import builders
from backend.ticket.builders.common.base import HostInfoSerializer, InstanceInfoSerializer
from backend.ticket.builders.common.base import HostInfoSerializer, InstanceInfoSerializer, fetch_cluster_ids
from backend.ticket.builders.common.constants import MySQLBackupSource
from backend.ticket.builders.mysql.base import BaseMySQLHATicketFlowBuilder, MySQLBaseOperateDetailSerializer
from backend.ticket.constants import TicketType
Expand All @@ -31,6 +31,8 @@ class RestoreInfoSerializer(serializers.Serializer):
infos = serializers.ListField(help_text=_("集群重建信息"), child=RestoreInfoSerializer())

def validate(self, attrs):
cluster_ids = fetch_cluster_ids(attrs)

# 校验集群是否可用,集群类型为高可用
super(MysqlRestoreSlaveDetailSerializer, self).validate_cluster_can_access(attrs)
super(MysqlRestoreSlaveDetailSerializer, self).validated_cluster_type(attrs, ClusterType.TenDBHA)
Expand All @@ -50,6 +52,11 @@ def validate(self, attrs):
attrs, host_key=["new_slave"], cluster_key=["cluster_ids"]
)

# 校验集群存在最近一次全备
super(MysqlRestoreSlaveDetailSerializer, self).validated_cluster_latest_backup(
cluster_ids, attrs["backup_source"]
)

return attrs


Expand Down

0 comments on commit 2887b66

Please sign in to comment.