diff --git a/dbm-ui/backend/iam_app/dataclass/__init__.py b/dbm-ui/backend/iam_app/dataclass/__init__.py index aec002017f..ee00b9d488 100644 --- a/dbm-ui/backend/iam_app/dataclass/__init__.py +++ b/dbm-ui/backend/iam_app/dataclass/__init__.py @@ -26,7 +26,7 @@ from ..exceptions import BaseIAMError from ..handlers.client import IAM from ..handlers.permission import Permission -from .actions import _all_actions +from .actions import ActionEnum, _all_actions from .resources import ResourceEnum, ResourceMeta, _all_resources, _extra_instance_selections logger = logging.getLogger("root") @@ -221,7 +221,11 @@ def assign_auth_to_group(iam: IAM, biz: AppCache, group_id): """ 给单个用户组分配权限,这里的权限固定是DBA权限 """ - global_action_groups = [_("全局设置"), _("资源管理"), _("平台管理")] + global_action_groups = [ + ActionEnum.GLOBAL_MANAGE.group, + ActionEnum.RESOURCE_MANAGE.group, + ActionEnum.PLATFORM_MANAGE.group, + ] biz_actions = [ action for action in _all_actions.values() diff --git a/dbm-ui/backend/ticket/builders/mysql/base.py b/dbm-ui/backend/ticket/builders/mysql/base.py index 008cc5e994..76a99a959a 100644 --- a/dbm-ui/backend/ticket/builders/mysql/base.py +++ b/dbm-ui/backend/ticket/builders/mysql/base.py @@ -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 @@ -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 @@ -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_id in cluster_ids: + handler = FixPointRollbackHandler(cluster_id=cluster_id) + backup = handler.query_latest_backup_log(rollback_time=now, backup_source=backup_source) + if not backup: + raise serializers.ValidationError(_("集群{}无法找到最近一次备份").format(cluster_id)) + if backup_type and backup["backup_type"] != backup_type: + raise serializers.ValidationError(_("集群{}最近一次备份类型不匹配{}").format(cluster_id, backup_type)) + def validate(self, attrs): # 默认全局校验只需要校验集群的状态 self.validate_cluster_can_access(attrs) diff --git a/dbm-ui/backend/ticket/builders/mysql/mysql_migrate_cluster.py b/dbm-ui/backend/ticket/builders/mysql/mysql_migrate_cluster.py index c0ad45184b..f76daf3fa6 100644 --- a/dbm-ui/backend/ticket/builders/mysql/mysql_migrate_cluster.py +++ b/dbm-ui/backend/ticket/builders/mysql/mysql_migrate_cluster.py @@ -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 ( @@ -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 diff --git a/dbm-ui/backend/ticket/builders/mysql/mysql_migrate_upgrade.py b/dbm-ui/backend/ticket/builders/mysql/mysql_migrate_upgrade.py index a9268aec56..8536bb0474 100644 --- a/dbm-ui/backend/ticket/builders/mysql/mysql_migrate_upgrade.py +++ b/dbm-ui/backend/ticket/builders/mysql/mysql_migrate_upgrade.py @@ -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 _ @@ -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 @@ -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 diff --git a/dbm-ui/backend/ticket/builders/mysql/mysql_restore_local_slave.py b/dbm-ui/backend/ticket/builders/mysql/mysql_restore_local_slave.py index 2ea4b6b40e..6ba55571b3 100644 --- a/dbm-ui/backend/ticket/builders/mysql/mysql_restore_local_slave.py +++ b/dbm-ui/backend/ticket/builders/mysql/mysql_restore_local_slave.py @@ -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 @@ -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) @@ -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 diff --git a/dbm-ui/backend/ticket/builders/mysql/mysql_restore_slave.py b/dbm-ui/backend/ticket/builders/mysql/mysql_restore_slave.py index 568d483a43..bc41c3a790 100644 --- a/dbm-ui/backend/ticket/builders/mysql/mysql_restore_slave.py +++ b/dbm-ui/backend/ticket/builders/mysql/mysql_restore_slave.py @@ -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 @@ -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) @@ -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