diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_ha_db_table_backup.py b/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_db_table_backup.py similarity index 85% rename from dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_ha_db_table_backup.py rename to dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_db_table_backup.py index 4a077debb0..528f444e17 100644 --- a/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_ha_db_table_backup.py +++ b/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_db_table_backup.py @@ -8,18 +8,15 @@ 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 collections import logging import uuid from dataclasses import asdict from typing import Dict, Optional -from django.core.exceptions import ObjectDoesNotExist from django.utils.translation import ugettext as _ from backend.configuration.constants import DBType -from backend.db_meta.enums import ClusterType, InstanceInnerRole -from backend.db_meta.exceptions import ClusterNotExistException, DBMetaBaseException +from backend.db_meta.enums import ClusterType, InstanceInnerRole, InstanceStatus from backend.db_meta.models import Cluster from backend.flow.consts import DBA_SYSTEM_USER from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder @@ -42,7 +39,7 @@ logger = logging.getLogger("flow") -class MySQLHADBTableBackupFlow(object): +class MySQLDBTableBackupFlow(object): """ 支持跨云操作 """ @@ -58,7 +55,7 @@ def backup_flow(self): "uid": "2022051612120001", "created_by": "xxx", "bk_biz_id": "152", - "ticket_type": "MYSQL_HA_DB_TABLE_BACKUP", + "ticket_type": "MYSQL_DB_TABLE_BACKUP", "infos": [ { "cluster_id": int, @@ -74,30 +71,19 @@ def backup_flow(self): 增加单据临时ADMIN账号的添加和删除逻辑 """ cluster_ids = [job["cluster_id"] for job in self.data["infos"]] - dup_cluster_ids = [item for item, count in collections.Counter(cluster_ids).items() if count > 1] - if dup_cluster_ids: - raise DBMetaBaseException(message="duplicate clusters found: {}".format(dup_cluster_ids)) - backup_pipeline = Builder( root_id=self.root_id, data=self.data, need_random_pass_cluster_ids=list(set(cluster_ids)) ) sub_pipes = [] for job in self.data["infos"]: - try: - cluster_obj = Cluster.objects.get( - pk=job["cluster_id"], bk_biz_id=self.data["bk_biz_id"], cluster_type=ClusterType.TenDBHA.value - ) - except ObjectDoesNotExist: - raise ClusterNotExistException( - cluster_type=ClusterType.TenDBHA.value, cluster_id=job["cluster_id"], immute_domain="" - ) + cluster_obj = Cluster.objects.get(pk=job["cluster_id"], bk_biz_id=self.data["bk_biz_id"]) - try: + if cluster_obj.cluster_type == ClusterType.TenDBHA: instance_obj = cluster_obj.storageinstance_set.get( - instance_inner_role=InstanceInnerRole.SLAVE.value, is_stand_by=True + instance_inner_role=InstanceInnerRole.SLAVE.value, is_stand_by=True, status=InstanceStatus.RUNNING ) - except ObjectDoesNotExist: - raise DBMetaBaseException(message=_("{} standby slave 不存在".format(cluster_obj.immute_domain))) + else: + instance_obj = cluster_obj.storageinstance_set.filter(status=InstanceStatus.RUNNING).first() sub_pipe = SubBuilder( root_id=self.root_id, diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_ha_full_backup_flow.py b/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_full_backup_flow.py similarity index 70% rename from dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_ha_full_backup_flow.py rename to dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_full_backup_flow.py index 24df2cd91b..1089a0d1c9 100644 --- a/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_ha_full_backup_flow.py +++ b/dbm-ui/backend/flow/engine/bamboo/scene/mysql/mysql_full_backup_flow.py @@ -13,12 +13,10 @@ from dataclasses import asdict from typing import Dict, Optional -from django.core.exceptions import ObjectDoesNotExist from django.utils.translation import ugettext as _ from backend.configuration.constants import DBType -from backend.db_meta.enums import ClusterType, InstanceInnerRole -from backend.db_meta.exceptions import ClusterNotExistException, DBMetaBaseException +from backend.db_meta.enums import ClusterType from backend.db_meta.models import Cluster from backend.flow.consts import DBA_SYSTEM_USER, LONG_JOB_TIMEOUT from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder @@ -36,7 +34,7 @@ logger = logging.getLogger("flow") -class MySQLHAFullBackupFlow(object): +class MySQLFullBackupFlow(object): """ mysql 库表备份流程 支持跨云管理 @@ -53,47 +51,37 @@ def full_backup_flow(self): "uid": "398346234", "created_type": "xxx", "bk_biz_id": "152", - "ticket_type": "MYSQL_HA_FULL_BACKUP", - "infos": { - "backup_type": enum of backend.flow.consts.MySQLBackupTypeEnum - "file_tag": enum of backend.flow.consts.MySQLBackupFileTagEnum - "clusters": [{"cluster_id":"", "backup_local": enum []}] - } + "ticket_type": "MYSQL_HA_FULL_BACKUP|MYSQL_SINGLE_FULL_BACKUP", + "backup_type": enum of backend.flow.consts.MySQLBackupTypeEnum + "file_tag": enum of backend.flow.consts.MySQLBackupFileTagEnum + "infos": [ + { + "cluster_id": int, + "backup_local": enum + } + ] } 增加单据临时ADMIN账号的添加和删除逻辑 """ - clusters = self.data["infos"]["clusters"] - cluster_ids = [cluster["cluster_id"] for cluster in clusters] + cluster_ids = [job["cluster_id"] for job in self.data["infos"]] backup_pipeline = Builder( root_id=self.root_id, data=self.data, need_random_pass_cluster_ids=list(set(cluster_ids)) ) sub_pipes = [] - for cluster in clusters: - cluster_id = cluster["cluster_id"] - backup_local = cluster["backup_local"] - - try: - cluster_obj = Cluster.objects.get( - pk=cluster_id, bk_biz_id=self.data["bk_biz_id"], cluster_type=ClusterType.TenDBHA.value - ) - except ObjectDoesNotExist: - raise ClusterNotExistException( - cluster_type=ClusterType.TenDBHA.value, cluster_id=cluster_id, immute_domain="" - ) - - if backup_local == InstanceInnerRole.MASTER.value: - backend_obj = cluster_obj.storageinstance_set.get(instance_inner_role=InstanceInnerRole.MASTER.value) - elif backup_local == InstanceInnerRole.SLAVE.value: - try: - backend_obj = cluster_obj.storageinstance_set.get( - instance_inner_role=InstanceInnerRole.SLAVE.value, is_stand_by=True - ) - except ObjectDoesNotExist: - raise DBMetaBaseException(message=_("{} standby slave 不存在".format(cluster_obj.immute_domain))) + for job in self.data["infos"]: + cluster_id = job["cluster_id"] + backup_local = job["backup_local"] + + cluster_obj = Cluster.objects.get(pk=cluster_id, bk_biz_id=self.data["bk_biz_id"]) + + if cluster_obj.cluster_type == ClusterType.TenDBSingle: + backend_obj = cluster_obj.storageinstance_set.first() + elif cluster_obj.cluster_type == ClusterType.TenDBHA: + backend_obj = cluster_obj.storageinstance_set.get(instance_inner_role=backup_local, is_stand_by=True) else: - raise MySQLBackupLocalException(msg=_("不支持的备份位置 {}".format(backup_local))) + raise MySQLBackupLocalException(_("不支持的集群类型 {}".format(cluster_obj.cluster_type))) sub_pipe = SubBuilder( root_id=self.root_id, @@ -104,8 +92,8 @@ def full_backup_flow(self): "ticket_type": self.data["ticket_type"], "ip": backend_obj.machine.ip, "port": backend_obj.port, - "file_tag": self.data["infos"]["file_tag"], - "backup_type": self.data["infos"]["backup_type"], + "file_tag": self.data["file_tag"], + "backup_type": self.data["backup_type"], "backup_id": uuid.uuid1(), "backup_gsd": ["all"], "role": backend_obj.instance_role, @@ -136,7 +124,6 @@ def full_backup_flow(self): get_mysql_payload_func=MysqlActPayload.mysql_backup_demand_payload.__name__, ) ), - # write_payload_var="backup_report_response", ) sub_pipe.add_act( diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_cluster_db_table_backup.py b/dbm-ui/backend/flow/engine/bamboo/scene/spider/db_table_backup.py similarity index 94% rename from dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_cluster_db_table_backup.py rename to dbm-ui/backend/flow/engine/bamboo/scene/spider/db_table_backup.py index f4a4350da6..b911e8b6c8 100644 --- a/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_cluster_db_table_backup.py +++ b/dbm-ui/backend/flow/engine/bamboo/scene/spider/db_table_backup.py @@ -11,17 +11,15 @@ import copy import logging import uuid -from collections import Counter, defaultdict +from collections import defaultdict from dataclasses import asdict from typing import Dict, List, Optional -from django.core.exceptions import ObjectDoesNotExist from django.utils.translation import ugettext as _ from backend.configuration.constants import DBType from backend.constants import IP_PORT_DIVIDER from backend.db_meta.enums import ClusterType, InstanceInnerRole, TenDBClusterSpiderRole -from backend.db_meta.exceptions import ClusterNotExistException, DBMetaBaseException from backend.db_meta.models import Cluster, StorageInstanceTuple from backend.flow.consts import DBA_SYSTEM_USER from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder, SubProcess @@ -68,16 +66,11 @@ def backup_flow(self): "table_patterns": ["tb_role%", "tb_mail%", "*"], "ignore_tables": ["tb_role1", "tb_mail10"], }, - ... - ... ] } 增加单据临时ADMIN账号的添加和删除逻辑 """ cluster_ids = [job["cluster_id"] for job in self.data["infos"]] - dup_cluster_ids = [item for item, count in Counter(cluster_ids).items() if count > 1] - if dup_cluster_ids: - raise DBMetaBaseException(message="duplicate clusters found: {}".format(dup_cluster_ids)) backup_pipeline = Builder( root_id=self.root_id, data=self.data, need_random_pass_cluster_ids=list(set(cluster_ids)) @@ -85,14 +78,9 @@ def backup_flow(self): cluster_pipes = [] for job in self.data["infos"]: - try: - cluster_obj = Cluster.objects.get( - pk=job["cluster_id"], bk_biz_id=self.data["bk_biz_id"], cluster_type=ClusterType.TenDBCluster.value - ) - except ObjectDoesNotExist: - raise ClusterNotExistException( - cluster_type=ClusterType.TenDBCluster.value, cluster_id=job["cluster_id"], immute_domain="" - ) + cluster_obj = Cluster.objects.get( + pk=job["cluster_id"], bk_biz_id=self.data["bk_biz_id"], cluster_type=ClusterType.TenDBCluster.value + ) backup_id = uuid.uuid1() diff --git a/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_cluster_full_backup.py b/dbm-ui/backend/flow/engine/bamboo/scene/spider/full_backup.py similarity index 87% rename from dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_cluster_full_backup.py rename to dbm-ui/backend/flow/engine/bamboo/scene/spider/full_backup.py index 2052c56c59..3a7081340d 100644 --- a/dbm-ui/backend/flow/engine/bamboo/scene/spider/spider_cluster_full_backup.py +++ b/dbm-ui/backend/flow/engine/bamboo/scene/spider/full_backup.py @@ -15,18 +15,16 @@ from dataclasses import asdict from typing import Dict, List, Optional -from django.core.exceptions import ObjectDoesNotExist from django.utils.translation import ugettext as _ from backend.configuration.constants import DBType from backend.constants import IP_PORT_DIVIDER -from backend.db_meta.enums import ClusterType, InstanceInnerRole, TenDBClusterSpiderRole -from backend.db_meta.exceptions import ClusterNotExistException +from backend.db_meta.enums import InstanceInnerRole, TenDBClusterSpiderRole from backend.db_meta.models import Cluster, StorageInstanceTuple from backend.flow.consts import DBA_SYSTEM_USER, LONG_JOB_TIMEOUT from backend.flow.engine.bamboo.scene.common.builder import Builder, SubBuilder, SubProcess from backend.flow.engine.bamboo.scene.common.get_file_list import GetFileList -from backend.flow.engine.exceptions import IncompatibleBackupTypeAndLocal, MySQLBackupLocalException +from backend.flow.engine.exceptions import MySQLBackupLocalException from backend.flow.plugins.components.collections.mysql.exec_actuator_script import ExecuteDBActuatorScriptComponent from backend.flow.plugins.components.collections.mysql.mysql_link_backup_id_bill_id import ( MySQLLinkBackupIdBillIdComponent, @@ -51,49 +49,51 @@ def full_backup_flow(self): "created_type": "xxx", "bk_biz_id": "152", "ticket_type": "TENDBCLUSTER_FULL_BACKUP", - "infos": { - "backup_type": enum of backend.flow.consts.MySQLBackupTypeEnum, - "file_tag": enum of backend.flow.consts.MySQLBackupFileTagEnum, - “clusters": [ + "backup_type": enum of backend.flow.consts.MySQLBackupTypeEnum, + "file_tag": enum of backend.flow.consts.MySQLBackupFileTagEnum, + "infos": [ { "cluster_id": int, "backup_local": enum [backend.db_meta.enum.InstanceInnerRole, SPIDER_MNT], "spider_mnt_address": "x.x.x.x:y" # 如果 backup_local 是 spider_mnt }, ... - ], - } + ] } 增加单据临时ADMIN账号的添加和删除逻辑 """ - clusters = self.data["infos"]["clusters"] - cluster_ids = [i["cluster_id"] for i in self.data["infos"]["clusters"]] + cluster_ids = [job["cluster_id"] for job in self.data["infos"]] backup_pipeline = Builder( root_id=self.root_id, data=self.data, need_random_pass_cluster_ids=list(set(cluster_ids)) ) cluster_pipes = [] - for cluster in clusters: - if ( - self.data["infos"]["backup_type"] == "physical" - and cluster["backup_local"] == TenDBClusterSpiderRole.SPIDER_MNT.value - ): - IncompatibleBackupTypeAndLocal( - backup_type=self.data["infos"]["backup_type"], backup_local=cluster["backup_local"] - ) - - try: - cluster_obj = Cluster.objects.get( - pk=cluster["cluster_id"], - bk_biz_id=self.data["bk_biz_id"], - cluster_type=ClusterType.TenDBCluster.value, - ) - except ObjectDoesNotExist: - raise ClusterNotExistException( - cluster_type=ClusterType.TenDBCluster.value, cluster_id=cluster["cluster_id"], immute_domain="" - ) + # for cluster in clusters: + # if ( + # self.data["infos"]["backup_type"] == "physical" + # and cluster["backup_local"] == TenDBClusterSpiderRole.SPIDER_MNT.value + # ): + # IncompatibleBackupTypeAndLocal( + # backup_type=self.data["infos"]["backup_type"], backup_local=cluster["backup_local"] + # ) + for job in self.data["infos"]: + cluster_id = job["cluster_id"] + backup_local = job["backup_local"] + + cluster_obj = Cluster.objects.get(pk=cluster_id, bk_biz_id=self.data["bk_biz_id"]) + + # try: + # cluster_obj = Cluster.objects.get( + # pk=cluster["cluster_id"], + # bk_biz_id=self.data["bk_biz_id"], + # cluster_type=ClusterType.TenDBCluster.value, + # ) + # except ObjectDoesNotExist: + # raise ClusterNotExistException( + # cluster_type=ClusterType.TenDBCluster.value, cluster_id=cluster["cluster_id"], immute_domain="" + # ) backup_id = uuid.uuid1() cluster_pipe = SubBuilder( @@ -113,22 +113,22 @@ def full_backup_flow(self): sub_flow=self.backup_on_spider_ctl(backup_id=backup_id, cluster_obj=cluster_obj) ) - if cluster["backup_local"] == InstanceInnerRole.SLAVE.value: # "remote": + if backup_local == InstanceInnerRole.SLAVE.value: # "remote": cluster_pipe.add_parallel_sub_pipeline( sub_flow_list=self.backup_on_remote_slave(backup_id=backup_id, cluster_obj=cluster_obj) ) - elif cluster["backup_local"] == InstanceInnerRole.MASTER.value: + elif backup_local == InstanceInnerRole.MASTER.value: cluster_pipe.add_parallel_sub_pipeline( sub_flow_list=self.backup_on_remote_master(backup_id=backup_id, cluster_obj=cluster_obj) ) - elif cluster["backup_local"] == TenDBClusterSpiderRole.SPIDER_MNT.value: + elif backup_local == TenDBClusterSpiderRole.SPIDER_MNT.value: cluster_pipe.add_sub_pipeline( sub_flow=self.backup_on_spider_mnt( - backup_id=backup_id, cluster_obj=cluster_obj, spider_mnt_address=cluster["spider_mnt_address"] + backup_id=backup_id, cluster_obj=cluster_obj, spider_mnt_address=job["spider_mnt_address"] ) ) else: - raise MySQLBackupLocalException(msg=_("不支持的备份位置 {}".format(cluster["backup_local"]))) + raise MySQLBackupLocalException(msg=_("不支持的备份位置 {}".format(backup_local))) cluster_pipe.add_act( act_name=_("关联备份id"), diff --git a/dbm-ui/backend/flow/engine/controller/mysql.py b/dbm-ui/backend/flow/engine/controller/mysql.py index e9790eed15..ef72cb69b2 100644 --- a/dbm-ui/backend/flow/engine/controller/mysql.py +++ b/dbm-ui/backend/flow/engine/controller/mysql.py @@ -20,15 +20,15 @@ from backend.flow.engine.bamboo.scene.mysql.mysql_checksum import MysqlChecksumFlow from backend.flow.engine.bamboo.scene.mysql.mysql_clone_rules import MySQLCloneRules from backend.flow.engine.bamboo.scene.mysql.mysql_data_migrate_flow import MysqlDataMigrateFlow +from backend.flow.engine.bamboo.scene.mysql.mysql_db_table_backup import MySQLDBTableBackupFlow from backend.flow.engine.bamboo.scene.mysql.mysql_edit_config_flow import MysqlEditConfigFlow from backend.flow.engine.bamboo.scene.mysql.mysql_fake_sql_semantic_check import MySQLFakeSemanticCheck from backend.flow.engine.bamboo.scene.mysql.mysql_flashback_flow import MysqlFlashbackFlow +from backend.flow.engine.bamboo.scene.mysql.mysql_full_backup_flow import MySQLFullBackupFlow from backend.flow.engine.bamboo.scene.mysql.mysql_ha_apply_flow import MySQLHAApplyFlow -from backend.flow.engine.bamboo.scene.mysql.mysql_ha_db_table_backup import MySQLHADBTableBackupFlow from backend.flow.engine.bamboo.scene.mysql.mysql_ha_destroy_flow import MySQLHADestroyFlow from backend.flow.engine.bamboo.scene.mysql.mysql_ha_disable_flow import MySQLHADisableFlow from backend.flow.engine.bamboo.scene.mysql.mysql_ha_enable_flow import MySQLHAEnableFlow -from backend.flow.engine.bamboo.scene.mysql.mysql_ha_full_backup_flow import MySQLHAFullBackupFlow from backend.flow.engine.bamboo.scene.mysql.mysql_ha_metadata_import import TenDBHAMetadataImportFlow from backend.flow.engine.bamboo.scene.mysql.mysql_ha_standardize_flow import MySQLHAStandardizeFlow from backend.flow.engine.bamboo.scene.mysql.mysql_ha_upgrade import ( @@ -398,19 +398,18 @@ def mysql_migrate_remote_scene(self): flow = MySQLMigrateClusterRemoteFlow(root_id=self.root_id, ticket_data=self.ticket_data) flow.migrate_cluster_flow() - def mysql_ha_db_table_backup_scene(self): + def mysql_db_table_backup_scene(self): """ - TenDBHA 库表备份 + MySQL 库表备份 ticket_data 参数结构样例 { "uid": "2022051612120001", "created_by": "xxx", "bk_biz_id": "152", - "ticket_type": "MYSQL_HA_DB_TABLE_BACKUP", + "ticket_type": "MYSQL_DB_TABLE_BACKUP", "infos": [ { "cluster_id": int, - "backup_on": str enum InstanceInnerRole "db_patterns": ["db1%", "db2%"], "ignore_dbs": ["db11", "db12", "db23"], "table_patterns": ["tb_role%", "tb_mail%", "*"], @@ -421,7 +420,7 @@ def mysql_ha_db_table_backup_scene(self): ] } """ - flow = MySQLHADBTableBackupFlow(root_id=self.root_id, data=self.ticket_data) + flow = MySQLDBTableBackupFlow(root_id=self.root_id, data=self.ticket_data) flow.backup_flow() def mysql_ha_switch_scene(self): @@ -490,7 +489,7 @@ def mysql_flashback_scene(self): flow.mysql_flashback_flow() def mysql_full_backup_scene(self): - flow = MySQLHAFullBackupFlow(root_id=self.root_id, data=self.ticket_data) + flow = MySQLFullBackupFlow(root_id=self.root_id, data=self.ticket_data) flow.full_backup_flow() def mysql_edit_config_scene(self): diff --git a/dbm-ui/backend/flow/engine/controller/spider.py b/dbm-ui/backend/flow/engine/controller/spider.py index f0bdffd8a1..5a1c4868aa 100644 --- a/dbm-ui/backend/flow/engine/controller/spider.py +++ b/dbm-ui/backend/flow/engine/controller/spider.py @@ -10,6 +10,8 @@ from backend.db_meta.enums import ClusterType from backend.flow.engine.bamboo.scene.spider.append_deploy_ctl_flow import AppendDeployCTLFlow +from backend.flow.engine.bamboo.scene.spider.db_table_backup import TenDBClusterDBTableBackupFlow +from backend.flow.engine.bamboo.scene.spider.full_backup import TenDBClusterFullBackupFlow from backend.flow.engine.bamboo.scene.spider.import_sqlfile_flow import ImportSQLFlow from backend.flow.engine.bamboo.scene.spider.remote_local_slave_recover import TenDBRemoteSlaveLocalRecoverFlow from backend.flow.engine.bamboo.scene.spider.remote_master_fail_over import RemoteMasterFailOverFlow @@ -19,13 +21,11 @@ from backend.flow.engine.bamboo.scene.spider.spider_add_mnt import TenDBClusterAddSpiderMNTFlow from backend.flow.engine.bamboo.scene.spider.spider_add_nodes import TenDBClusterAddNodesFlow from backend.flow.engine.bamboo.scene.spider.spider_checksum import SpiderChecksumFlow -from backend.flow.engine.bamboo.scene.spider.spider_cluster_db_table_backup import TenDBClusterDBTableBackupFlow from backend.flow.engine.bamboo.scene.spider.spider_cluster_deploy import TenDBClusterApplyFlow from backend.flow.engine.bamboo.scene.spider.spider_cluster_destroy import TenDBClusterDestroyFlow from backend.flow.engine.bamboo.scene.spider.spider_cluster_disable_deploy import SpiderClusterDisableFlow from backend.flow.engine.bamboo.scene.spider.spider_cluster_enable_deploy import SpiderClusterEnableFlow from backend.flow.engine.bamboo.scene.spider.spider_cluster_flashback import TenDBClusterFlashbackFlow -from backend.flow.engine.bamboo.scene.spider.spider_cluster_full_backup import TenDBClusterFullBackupFlow from backend.flow.engine.bamboo.scene.spider.spider_cluster_metadata_import_flow import SpiderClusterMetadataImportFlow from backend.flow.engine.bamboo.scene.spider.spider_cluster_rollback_flow import TenDBRollBackDataFlow from backend.flow.engine.bamboo.scene.spider.spider_cluster_standardize_flow import SpiderClusterStandardizeFlow diff --git a/dbm-ui/backend/flow/utils/mysql/db_table_filter/filter.py b/dbm-ui/backend/flow/utils/mysql/db_table_filter/filter.py index ce8d14875b..78a52b2b43 100644 --- a/dbm-ui/backend/flow/utils/mysql/db_table_filter/filter.py +++ b/dbm-ui/backend/flow/utils/mysql/db_table_filter/filter.py @@ -25,81 +25,81 @@ def __init__( exclude_db_patterns: List[str], exclude_table_patterns: List[str], ): - self.include_db_patterns = include_db_patterns - self.include_table_patterns = include_table_patterns - self.exclude_db_patterns = exclude_db_patterns - self.exclude_table_patterns = exclude_table_patterns + self.__include_db_patterns = include_db_patterns + self.__include_table_patterns = include_table_patterns + self.__exclude_db_patterns = exclude_db_patterns + self.__exclude_table_patterns = exclude_table_patterns - self.system_table_parts = [] - self.system_db_parts = [] + self.__system_table_parts = [] + self.__system_db_parts = [] - self._validate() - self._build_db_filter_regexp() - self._build_table_filter_regexp() + self.__validate() + self.__build_db_filter_regexp() + self.__build_table_filter_regexp() - def _validate(self): - if not self.include_db_patterns or not self.include_table_patterns: - raise DbTableFilterValidateException(msg=_("include patterns 不能为空")) + def __validate(self): + if not self.__include_db_patterns or not self.__include_table_patterns: + raise DbTableFilterValidateException(msg=_("include db/table patterns 不能为空")) # if not ( # (self.exclude_db_patterns and self.exclude_table_patterns) # or (not self.exclude_db_patterns and not self.exclude_table_patterns) # ): # raise DbTableFilterValidateException(msg=_("exclude patterns 要么同时为空, 要么都不为空")) - if "*" in self.exclude_db_patterns or "*" in self.exclude_table_patterns: + if "*" in self.__exclude_db_patterns or "*" in self.__exclude_table_patterns: raise DbTableFilterValidateException(msg=_("exclude patterns 不能包含 *")) for patterns in [ - self.include_db_patterns, - self.include_table_patterns, - self.exclude_db_patterns, - self.exclude_table_patterns, + self.__include_db_patterns, + self.__include_table_patterns, + self.__exclude_db_patterns, + self.__exclude_table_patterns, ]: glob_check(patterns) - def _build_db_filter_regexp(self): - include_parts = ["{}$".format(replace_glob(db)) for db in self.include_db_patterns] - exclude_parts = ["{}$".format(replace_glob(db)) for db in self.exclude_db_patterns] + self.system_db_parts + def __build_db_filter_regexp(self): + include_parts = ["{}$".format(replace_glob(db)) for db in self.__include_db_patterns] + exclude_parts = ["{}$".format(replace_glob(db)) for db in self.__exclude_db_patterns] + self.__system_db_parts - self.db_filter_include_regex = build_include_regexp(include_parts) - self.db_filter_exclude_regex = build_exclude_regexp(exclude_parts) + self.__db_filter_include_regex = build_include_regexp(include_parts) + self.__db_filter_exclude_regex = build_exclude_regexp(exclude_parts) - def _build_table_filter_regexp(self): + def __build_table_filter_regexp(self): include_parts = [ r"{}\.{}$".format(replace_glob(ele[0]), replace_glob(ele[1])) - for ele in itertools.product(self.include_db_patterns, self.include_table_patterns) + for ele in itertools.product(self.__include_db_patterns, self.__include_table_patterns) ] # 库排除 - exclude_parts = [r"{}\.{}$".format(replace_glob(edb), replace_glob("*")) for edb in self.exclude_db_patterns] + exclude_parts = [r"{}\.{}$".format(replace_glob(edb), replace_glob("*")) for edb in self.__exclude_db_patterns] # 表排除 exclude_parts += [ - r"{}\.{}$".format(replace_glob("*"), replace_glob(etb)) for etb in self.exclude_table_patterns + r"{}\.{}$".format(replace_glob("*"), replace_glob(etb)) for etb in self.__exclude_table_patterns ] - exclude_parts += self.system_db_parts + exclude_parts += self.__system_db_parts - self.table_filter_include_regex = build_include_regexp(include_parts) - self.table_filter_exclude_regex = build_exclude_regexp(exclude_parts) + self.__table_filter_include_regex = build_include_regexp(include_parts) + self.__table_filter_exclude_regex = build_exclude_regexp(exclude_parts) def table_filter_regexp(self) -> str: - return r"^{}{}".format(self.table_filter_include_regex, self.table_filter_exclude_regex) + return r"^{}{}".format(self.__table_filter_include_regex, self.__table_filter_exclude_regex) def db_filter_regexp(self) -> str: - return r"^{}{}".format(self.db_filter_include_regex, self.db_filter_exclude_regex) + return r"^{}{}".format(self.__db_filter_include_regex, self.__db_filter_exclude_regex) def table_filter_exclude_regexp_as_include(self) -> str: - return self.table_filter_exclude_regex.replace("!", "=", 1) + return self.__table_filter_exclude_regex.replace("!", "=", 1) def db_filter_exclude_regexp_as_include(self) -> str: - return self.db_filter_exclude_regex.replace("!", "=", 1) + return self.__db_filter_exclude_regex.replace("!", "=", 1) def inject_system_dbs(self, system_dbs: List[str]): - self.system_table_parts = [r"{}\..*$".format(replace_glob(sd)) for sd in system_dbs] - self.system_db_parts = [r"{}$".format(replace_glob(sd)) for sd in system_dbs] + self.__system_table_parts = [r"{}\..*$".format(replace_glob(sd)) for sd in system_dbs] + self.__system_db_parts = [r"{}$".format(replace_glob(sd)) for sd in system_dbs] - self._build_db_filter_regexp() - self._build_table_filter_regexp() + self.__build_db_filter_regexp() + self.__build_table_filter_regexp() def check_inclusion(self) -> Dict[str, List[Tuple[str, str]]]: """ @@ -108,8 +108,8 @@ def check_inclusion(self) -> Dict[str, List[Tuple[str, str]]]: 类似 [('p%', 'p2???'), ('p%', 'p211'), ('p%', 'p4'), ('p%', 'p5')] """ return { - "include-db": pattern_inclusion(self.include_db_patterns), - "exclude-db": pattern_inclusion(self.exclude_db_patterns), - "include-table": pattern_inclusion(self.include_table_patterns), - "exclude-table": pattern_inclusion(self.exclude_table_patterns), + "include-db": pattern_inclusion(self.__include_db_patterns), + "exclude-db": pattern_inclusion(self.__exclude_db_patterns), + "include-table": pattern_inclusion(self.__include_table_patterns), + "exclude-table": pattern_inclusion(self.__exclude_table_patterns), } diff --git a/dbm-ui/backend/flow/utils/mysql/db_table_filter/tools.py b/dbm-ui/backend/flow/utils/mysql/db_table_filter/tools.py index 5863d7a46e..d2e23820c6 100644 --- a/dbm-ui/backend/flow/utils/mysql/db_table_filter/tools.py +++ b/dbm-ui/backend/flow/utils/mysql/db_table_filter/tools.py @@ -54,6 +54,8 @@ def replace_glob(p: str) -> str: def _build_regexp(parts: List[str], template: str) -> str: if parts: return template.format("|".join(parts)) + else: + return "" def build_include_regexp(parts: List[str]) -> str: diff --git a/dbm-ui/backend/flow/views/mysql_ha_db_table_backup.py b/dbm-ui/backend/flow/views/mysql_ha_db_table_backup.py index 118d835852..a309f476f9 100644 --- a/dbm-ui/backend/flow/views/mysql_ha_db_table_backup.py +++ b/dbm-ui/backend/flow/views/mysql_ha_db_table_backup.py @@ -35,6 +35,6 @@ def post(request): logger.info("define root_id: {}".format(root_id)) c = MySQLController(root_id=root_id, ticket_data=request.data) - c.mysql_ha_db_table_backup_scene() + c.mysql_db_table_backup_scene() return Response({"root_id": root_id}) diff --git a/dbm-ui/backend/ticket/builders/mysql/mysql_db_table_backup.py b/dbm-ui/backend/ticket/builders/mysql/mysql_db_table_backup.py new file mode 100644 index 0000000000..606c73add2 --- /dev/null +++ b/dbm-ui/backend/ticket/builders/mysql/mysql_db_table_backup.py @@ -0,0 +1,147 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at https://opensource.org/licenses/MIT +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +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 collections + +from django.utils.translation import ugettext_lazy as _ +from rest_framework import serializers + +from backend.db_meta.enums import ClusterType, InstanceInnerRole, InstanceStatus +from backend.db_meta.models import Cluster +from backend.flow.engine.controller.mysql import MySQLController +from backend.ticket import builders +from backend.ticket.builders.mysql.base import ( + BaseMySQLHATicketFlowBuilder, + DBTableField, + MySQLBaseOperateDetailSerializer, +) +from backend.ticket.constants import FlowRetryType, TicketType + + +class MySQLDBTableBackupDetailSerializer(MySQLBaseOperateDetailSerializer): + class DBTableBackupDataInfoSerializer(serializers.Serializer): + cluster_id = serializers.IntegerField(help_text=_("集群ID")) + db_patterns = serializers.ListField(help_text=_("匹配DB列表"), child=DBTableField(db_field=True)) + ignore_dbs = serializers.ListField(help_text=_("忽略DB列表"), child=DBTableField(db_field=True)) + table_patterns = serializers.ListField(help_text=_("匹配Table列表"), child=DBTableField()) + ignore_tables = serializers.ListField(help_text=_("忽略Table列表"), child=DBTableField()) + + infos = serializers.ListSerializer(help_text=_("备份信息列表"), child=DBTableBackupDataInfoSerializer()) + + def validate(self, attrs): + """验证库表数据库的数据""" + super().validate(attrs) + cluster_ids = [info["cluster_id"] for info in attrs["infos"]] + + errors = [] + + msg = self.__validate_cluster_id_unique(cluster_ids=cluster_ids) + if msg: + errors.append(msg) + + msg = self.__validate_cluster_type(cluster_ids=cluster_ids) + if msg: + errors.append(msg) + + msg = self.__validate_cluster_exists(cluster_ids=cluster_ids) + if msg: + errors.append(msg) + + msg = self.__validate_cluster_status(cluster_ids=cluster_ids) + if msg: + errors.append(msg) + + if errors: + raise serializers.ValidationError(errors) + + # 库表选择器校验 + super().validate_database_table_selector(attrs) + + return attrs + + @staticmethod + def __validate_cluster_id_unique(cluster_ids) -> str: + """ + 集群 id 不能重复出现 + """ + dup_cluster_ids = [cid for cid, cnt in collections.Counter(cluster_ids) if cnt > 1] + if dup_cluster_ids: + return _( + "重复输入集群: {}".format( + Cluster.objects.filter(pk__in=dup_cluster_ids).values_list("immute_domain", flat=True) + ) + ) + + @staticmethod + def __validate_cluster_type(cluster_ids) -> str: + """ + 集群类型不能混合 + """ + bad = [] + cluster_types = [] + for cluster_obj in Cluster.objects.filter(pk__in=cluster_ids): + if cluster_obj.cluster_type not in [ClusterType.TenDBHA, ClusterType.TenDBSingle]: + bad.append(_("不支持的集群类型 {} {}".format(cluster_obj.immute_domain, cluster_obj.cluster_type))) + + cluster_types.append(cluster_obj.cluster_type) + + if len(cluster_types) > 1: + bad.append(_("集群类型混合输入")) + + if bad: + return ", ".join(bad) + + @staticmethod + def __validate_cluster_exists(cluster_ids) -> str: + """ + 集群 id 必须存在 + """ + exists_cluster_ids = list( + Cluster.objects.filter( + pk__in=cluster_ids, cluster_type__in=[ClusterType.TenDBHA, ClusterType.TenDBSingle] + ).values_list("cluster_id", flat=True) + ) + not_exists_cluster_ids = list(set(cluster_ids) - set(exists_cluster_ids)) + if not_exists_cluster_ids: + return _("cluster id: {} 不存在".format(cluster_ids)) + + @staticmethod + def __validate_cluster_status(cluster_ids) -> str: + """ + 库表备份强制在 slave 备份, 所以集群的 standby slave 必须正常 + """ + bad = [] + for cluster_id in cluster_ids: + cluster_obj = Cluster.objects.get(pk=cluster_id) + if ( + cluster_obj.cluster_type == ClusterType.TenDBHA + and not cluster_obj.storageinstance_set.filter( + status=InstanceStatus.RUNNING, is_stand_by=True, instance_inner_role=InstanceInnerRole.SLAVE + ).exists() + ): + bad.append(_("{} 缺少状态正常的 standby slave".format(cluster_obj.immute_domain))) + elif cluster_obj.storageinstance_set.filter(status=InstanceStatus.RUNNING).exists(): + bad.append(_("{} 缺少状态正常的存储实例".format(cluster_obj.immute_domain))) + + if bad: + return _("{} 缺少状态正常的 standby slave".format(bad)) + + +class MySQLDBTableBackupFlowParamBuilder(builders.FlowParamBuilder): + controller = MySQLController.mysql_db_table_backup_scene + + +@builders.BuilderFactory.register(TicketType.MYSQL_HA_DB_TABLE_BACKUP) +class TenDBHADBTableBackupFlowBuilder(BaseMySQLHATicketFlowBuilder): + serializer = MySQLDBTableBackupDetailSerializer + inner_flow_builder = MySQLDBTableBackupFlowParamBuilder + inner_flow_name = _("TenDBHA 库表备份执行") + retry_type = FlowRetryType.MANUAL_RETRY diff --git a/dbm-ui/backend/ticket/builders/mysql/mysql_full_backup.py b/dbm-ui/backend/ticket/builders/mysql/mysql_full_backup.py new file mode 100644 index 0000000000..5d843a3d33 --- /dev/null +++ b/dbm-ui/backend/ticket/builders/mysql/mysql_full_backup.py @@ -0,0 +1,155 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at https://opensource.org/licenses/MIT +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +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 collections + +from django.utils.translation import ugettext_lazy as _ +from rest_framework import serializers + +from backend.db_meta.enums import ClusterType, InstanceInnerRole, InstanceStatus +from backend.db_meta.models import Cluster, StorageInstance +from backend.flow.consts import MySQLBackupFileTagEnum, MySQLBackupTypeEnum +from backend.flow.engine.controller.mysql import MySQLController +from backend.ticket import builders +from backend.ticket.builders.mysql.base import BaseMySQLHATicketFlowBuilder, MySQLBaseOperateDetailSerializer +from backend.ticket.constants import FlowRetryType, TicketType + + +class MySQLFullBackupDetailSerializer(MySQLBaseOperateDetailSerializer): + class MySQLFullBackupInfoSerializer(serializers.Serializer): + cluster_id = serializers.IntegerField(help_text=_("集群ID")) + backup_local = serializers.ChoiceField( + help_text=_("备份位置"), choices=InstanceInnerRole.get_choices(), default=InstanceInnerRole.SLAVE.value + ) + + backup_type = serializers.ChoiceField(help_text=_("备份类型"), choices=MySQLBackupTypeEnum.get_choices()) + file_tag = serializers.ChoiceField(help_text=_("备份文件tag"), choices=MySQLBackupFileTagEnum.get_choices()) + infos = serializers.ListSerializer(child=MySQLFullBackupInfoSerializer()) + + def validate(self, attrs): + cluster_ids = [info["cluster_id"] for info in attrs["infos"]] + + errors = [] + + msg = self.__validate_cluster_id_unique(cluster_ids=cluster_ids) + if msg: + errors.append(msg) + + msg = self.__validate_cluster_type(cluster_ids=cluster_ids) + if msg: + errors.append(msg) + + msg = self.__validate_backup_local(attrs=attrs) + if msg: + errors.append(msg) + + msg = self.__validate_cluster_status(attrs=attrs) + if msg: + errors.append(msg) + + if errors: + raise serializers.ValidationError(errors) + + return attrs + + @staticmethod + def __validate_cluster_id_unique(cluster_ids) -> str: + """ + 集群 id 不能重复出现 + """ + dup_cluster_ids = [cid for cid, cnt in collections.Counter(cluster_ids) if cnt > 1] + if dup_cluster_ids: + return _( + "重复输入集群: {}".format( + Cluster.objects.filter(pk__in=dup_cluster_ids).values_list("immute_domain", flat=True) + ) + ) + + @staticmethod + def __validate_cluster_type(cluster_ids) -> str: + """ + 集群类型不能混合 + """ + bad = [] + cluster_types = [] + for cluster_obj in Cluster.objects.filter(pk__in=cluster_ids): + if cluster_obj.cluster_type not in [ClusterType.TenDBHA, ClusterType.TenDBSingle]: + bad.append(_("不支持的集群类型 {} {}".format(cluster_obj.immute_domain, cluster_obj.cluster_type))) + + cluster_types.append(cluster_obj.cluster_type) + + if len(cluster_types) > 1: + bad.append(_("集群类型混合输入")) + + if bad: + return ", ".join(bad) + + @staticmethod + def __validate_backup_local(attrs) -> str: + bad = [] + + for info in attrs["infos"]: + backup_local = info["backup_local"] + cluster_id = info["cluster_id"] + cluster_obj = Cluster.objects.get(pk=cluster_id) + + if cluster_obj.cluster_type == ClusterType.TenDBSingle and backup_local != InstanceInnerRole.ORPHAN: + bad.append(_("{} 备份位置只能是 {}".format(cluster_obj.immute_domain, InstanceInnerRole.ORPHAN))) + elif cluster_obj.cluster_type == ClusterType.TenDBHA and backup_local not in [ + InstanceInnerRole.MASTER, + InstanceInnerRole.SLAVE, + ]: + bad.append( + _( + "{} 备份位置只能是 {}".format( + cluster_obj.immute_domain, [InstanceInnerRole.MASTER, InstanceInnerRole.SLAVE] + ) + ) + ) + + if bad: + return ", ".join(bad) + + @staticmethod + def __validate_cluster_status(attrs) -> str: + bad = [] + + for info in attrs["infos"]: + backup_local = info["backup_local"] + cluster_id = info["cluster_id"] + cluster_obj = Cluster.objects.get(pk=cluster_id) + if not StorageInstance.objects.filter( + cluster=cluster_obj, instance_inner_role=backup_local, is_stand_by=True, status=InstanceStatus.RUNNING + ).exists(): + bad.append( + _( + "{} 没找到正常的 {} 实例".format( + cluster_obj.immute_domain, + backup_local, + ) + ) + ) + + if bad: + return ", ".join(bad) + + +class MySQLFullBackupFlowParamBuilder(builders.FlowParamBuilder): + """TenDB HA 备份执行单据参数""" + + controller = MySQLController.mysql_full_backup_scene + + +@builders.BuilderFactory.register(TicketType.MYSQL_HA_FULL_BACKUP) +class TenDBHAFullBackupFlowBuilder(BaseMySQLHATicketFlowBuilder): + serializer = MySQLFullBackupDetailSerializer + inner_flow_builder = MySQLFullBackupFlowParamBuilder + inner_flow_name = _("TenDBHA 全库备份执行") + retry_type = FlowRetryType.MANUAL_RETRY diff --git a/dbm-ui/backend/ticket/builders/mysql/mysql_ha_backup.py b/dbm-ui/backend/ticket/builders/mysql/mysql_ha_backup.py deleted file mode 100644 index 9a49e33e69..0000000000 --- a/dbm-ui/backend/ticket/builders/mysql/mysql_ha_backup.py +++ /dev/null @@ -1,58 +0,0 @@ -# -*- coding: utf-8 -*- -""" -TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. -Copyright (C) 2017-2023 THL A29 Limited, a Tencent company. All rights reserved. -Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. -You may obtain a copy of the License at https://opensource.org/licenses/MIT -Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on -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. -""" - -from django.utils.translation import ugettext_lazy as _ -from rest_framework import serializers - -from backend.flow.engine.controller.mysql import MySQLController -from backend.ticket import builders -from backend.ticket.builders.mysql.base import ( - BaseMySQLHATicketFlowBuilder, - DBTableField, - MySQLBaseOperateDetailSerializer, -) -from backend.ticket.constants import FlowRetryType, TicketType - - -class MySQLHaBackupDetailSerializer(MySQLBaseOperateDetailSerializer): - class BackupDataInfoSerializer(serializers.Serializer): - cluster_id = serializers.IntegerField(help_text=_("集群ID")) - db_patterns = serializers.ListField(help_text=_("匹配DB列表"), child=DBTableField(db_field=True)) - ignore_dbs = serializers.ListField(help_text=_("忽略DB列表"), child=DBTableField(db_field=True)) - table_patterns = serializers.ListField(help_text=_("匹配Table列表"), child=DBTableField()) - ignore_tables = serializers.ListField(help_text=_("忽略Table列表"), child=DBTableField()) - # 废弃backup_on参数,暂时不需要传递 - # backup_on = serializers.ChoiceField(choices=InstanceInnerRole.get_choices(), help_text=_("备份源")) - - infos = serializers.ListSerializer(help_text=_("备份信息列表"), child=BackupDataInfoSerializer()) - - def validate(self, attrs): - """验证库表数据库的数据""" - super().validate(attrs) - - # 库表选择器校验 - super().validate_database_table_selector(attrs) - - return attrs - - -class MySQLHaBackupFlowParamBuilder(builders.FlowParamBuilder): - """MySQL HA 备份执行单据参数""" - - controller = MySQLController.mysql_ha_db_table_backup_scene - - -@builders.BuilderFactory.register(TicketType.MYSQL_HA_DB_TABLE_BACKUP) -class MySQLHaBackupFlowBuilder(BaseMySQLHATicketFlowBuilder): - serializer = MySQLHaBackupDetailSerializer - inner_flow_builder = MySQLHaBackupFlowParamBuilder - inner_flow_name = _("库表备份执行") - retry_type = FlowRetryType.MANUAL_RETRY diff --git a/dbm-ui/backend/ticket/builders/mysql/mysql_ha_full_backup.py b/dbm-ui/backend/ticket/builders/mysql/mysql_ha_full_backup.py deleted file mode 100644 index 8776c658a6..0000000000 --- a/dbm-ui/backend/ticket/builders/mysql/mysql_ha_full_backup.py +++ /dev/null @@ -1,68 +0,0 @@ -# -*- coding: utf-8 -*- -""" -TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. -Copyright (C) 2017-2023 THL A29 Limited, a Tencent company. All rights reserved. -Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. -You may obtain a copy of the License at https://opensource.org/licenses/MIT -Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on -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. -""" - -from django.utils.translation import ugettext_lazy as _ -from rest_framework import serializers - -from backend.db_meta.enums import ClusterDBHAStatusFlags, InstanceInnerRole -from backend.db_meta.models import Cluster -from backend.flow.consts import MySQLBackupFileTagEnum, MySQLBackupTypeEnum -from backend.flow.engine.controller.mysql import MySQLController -from backend.ticket import builders -from backend.ticket.builders.common.base import fetch_cluster_ids -from backend.ticket.builders.mysql.base import BaseMySQLHATicketFlowBuilder, MySQLBaseOperateDetailSerializer -from backend.ticket.constants import FlowRetryType, TicketType - - -class MySQLHaFullBackupDetailSerializer(MySQLBaseOperateDetailSerializer): - class FullBackupDataInfoSerializer(serializers.Serializer): - class ClusterDetailSerializer(serializers.Serializer): - cluster_id = serializers.IntegerField(help_text=_("集群ID")) - backup_local = serializers.ChoiceField( - help_text=_("备份位置"), choices=InstanceInnerRole.get_choices(), default=InstanceInnerRole.SLAVE.value - ) - - # 废弃online,暂时不需要传递 - # online = serializers.BooleanField(help_text=_("是否在线备份"), required=False) - backup_type = serializers.ChoiceField(help_text=_("备份类型"), choices=MySQLBackupTypeEnum.get_choices()) - file_tag = serializers.ChoiceField(help_text=_("备份文件tag"), choices=MySQLBackupFileTagEnum.get_choices()) - clusters = serializers.ListSerializer(help_text=_("集群信息"), child=ClusterDetailSerializer()) - - infos = FullBackupDataInfoSerializer() - - def validate(self, attrs): - try: - self.validate_cluster_can_access(attrs) - except serializers.ValidationError as e: - clusters = Cluster.objects.filter(id__in=fetch_cluster_ids(details=attrs)) - id__cluster = {cluster.id: cluster for cluster in clusters} - # 如果备份位置选的是master,但是slave异常,则认为是可以的 - for info in attrs["infos"]["clusters"]: - if info["backup_local"] != InstanceInnerRole.MASTER: - raise serializers.ValidationError(e) - if id__cluster[info["cluster_id"]].status_flag & ClusterDBHAStatusFlags.BackendMasterUnavailable: - raise serializers.ValidationError(e) - - return attrs - - -class MySQLHaFullBackupFlowParamBuilder(builders.FlowParamBuilder): - """MySQL HA 备份执行单据参数""" - - controller = MySQLController.mysql_full_backup_scene - - -@builders.BuilderFactory.register(TicketType.MYSQL_HA_FULL_BACKUP) -class MySQLHaFullBackupFlowBuilder(BaseMySQLHATicketFlowBuilder): - serializer = MySQLHaFullBackupDetailSerializer - inner_flow_builder = MySQLHaFullBackupFlowParamBuilder - inner_flow_name = _("全库备份执行") - retry_type = FlowRetryType.MANUAL_RETRY diff --git a/dbm-ui/backend/ticket/builders/mysql/mysql_import_sqlfile.py b/dbm-ui/backend/ticket/builders/mysql/mysql_import_sqlfile.py index 9066919938..a052f4ee6e 100644 --- a/dbm-ui/backend/ticket/builders/mysql/mysql_import_sqlfile.py +++ b/dbm-ui/backend/ticket/builders/mysql/mysql_import_sqlfile.py @@ -85,7 +85,7 @@ def get_params(self): class MysqlSqlImportBackUpFlowParamBuilder(builders.FlowParamBuilder): - controller = MySQLController.mysql_ha_db_table_backup_scene + controller = MySQLController.mysql_db_table_backup_scene def format_ticket_data(self): backup_infos_list = [] diff --git a/dbm-ui/backend/ticket/builders/tendbcluster/db_table_backup.py b/dbm-ui/backend/ticket/builders/tendbcluster/db_table_backup.py new file mode 100644 index 0000000000..ad25a8f08f --- /dev/null +++ b/dbm-ui/backend/ticket/builders/tendbcluster/db_table_backup.py @@ -0,0 +1,159 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at https://opensource.org/licenses/MIT +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +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 collections + +from django.utils.translation import gettext_lazy as _ +from rest_framework import serializers + +from backend.db_meta.enums import ClusterType, InstanceInnerRole, InstanceStatus, TenDBClusterSpiderRole +from backend.db_meta.models import Cluster +from backend.flow.engine.controller.spider import SpiderController +from backend.ticket import builders +from backend.ticket.builders.mysql.base import DBTableField +from backend.ticket.builders.tendbcluster.base import BaseTendbTicketFlowBuilder, TendbBaseOperateDetailSerializer +from backend.ticket.constants import TicketType + + +class TenDBClusterDBTableBackUpDetailSerializer(TendbBaseOperateDetailSerializer): + class TenDBClusterDBTableBackupInfoSerializer(serializers.Serializer): + cluster_id = serializers.IntegerField(help_text=_("集群ID")) + backup_local = serializers.CharField(help_text=_("备份位置")) + spider_mnt_address = serializers.CharField(help_text=_("运维节点地址"), required=False) + db_patterns = serializers.ListField(help_text=_("匹配DB列表"), child=DBTableField(db_field=True)) + ignore_dbs = serializers.ListField(help_text=_("忽略DB列表"), child=DBTableField(db_field=True)) + table_patterns = serializers.ListField(help_text=_("匹配Table列表"), child=DBTableField()) + ignore_tables = serializers.ListField(help_text=_("忽略Table列表"), child=DBTableField()) + + infos = serializers.ListSerializer(help_text=_("库表备份信息"), child=TenDBClusterDBTableBackupInfoSerializer()) + + def validate(self, attrs): + # 集群不允许重复 + cluster_ids = [info["cluster_id"] for info in attrs["infos"]] + + errors = [] + + msg = self.__validate_cluster_id_unique(cluster_ids=cluster_ids) + if msg: + errors.append(msg) + + msg = self.__validate_cluster_type(cluster_ids=cluster_ids) + if msg: + errors.append(msg) + + msg = self.__validate_cluster_exists(cluster_ids=cluster_ids) + if msg: + errors.append(msg) + + msg = self.__validate_backup_local(attrs=attrs) + if msg: + errors.append(msg) + + msg = self.__validate_cluster_status(attrs=attrs) + if msg: + errors.append(msg) + + # 库表选择器校验 + super().validate_database_table_selector(attrs, role_key="backup_local") + return attrs + + @staticmethod + def __validate_cluster_id_unique(cluster_ids) -> str: + """ + 集群 id 不能重复出现 + """ + dup_cluster_ids = [cid for cid, cnt in collections.Counter(cluster_ids) if cnt > 1] + if dup_cluster_ids: + return _( + "重复输入集群: {}".format( + Cluster.objects.filter(pk__in=dup_cluster_ids).values_list("immute_domain", flat=True) + ) + ) + + @staticmethod + def __validate_cluster_type(cluster_ids) -> str: + """ + 集群类型不能混合 + """ + bad = list( + Cluster.objects.filter(pk__in=cluster_ids) + .exclude(cluster_type=ClusterType.TenDBCluster) + .values_list("immute_domain", flat=True) + ) + if bad: + return _("不支持的集群类型 {}".format(", ".join(bad))) + + @staticmethod + def __validate_cluster_exists(cluster_ids) -> str: + """ + 集群 id 必须存在 + """ + exists_cluster_ids = list( + Cluster.objects.filter(pk__in=cluster_ids, cluster_type=ClusterType.TenDBCluster).values_list( + "cluster_id", flat=True + ) + ) + not_exists_cluster_ids = list(set(cluster_ids) - set(exists_cluster_ids)) + if not_exists_cluster_ids: + return _("cluster id: {} 不存在".format(cluster_ids)) + + @staticmethod + def __validate_backup_local(attrs): + bad = [] + + for info in attrs["infos"]: + backup_local = info["backup_local"] + if backup_local not in ["remote", "spider_mnt"]: + bad.append(_("不支持的备份位置 {}".format(backup_local))) + + if backup_local == "spider_mnt" and "spider_mnt_address" not in info: + bad.append(_("缺少 spider_mnt_address")) + + if bad: + return ", ".join(list(set(bad))) + + @staticmethod + def __validate_cluster_status(attrs): + bad = [] + for info in attrs["infos"]: + cluster_id = info["cluster_id"] + backup_local = info["backup_local"] + cluster_obj = Cluster.objects.get(pk=cluster_id) + if ( + backup_local == "spider_mnt" + and not cluster_obj.proxyinstance_set.filter( + tendbclusterspiderext__spider_role=TenDBClusterSpiderRole.SPIDER_MNT, status=InstanceStatus.RUNNING + ).exists() + ): + bad.append(cluster_obj.immute_domain) + elif ( + backup_local == "remote" + and cluster_obj.storageinstance_set.filter( + InstanceInnerRole.SLAVE, + is_stand_by=True, + ) + .exclude(status=InstanceStatus.RUNNING) + .exists() + ): + bad.append(cluster_obj.immute_domain) + + if bad: + return _("集群状态异常: {}".format(", ".join(list(set(bad))))) + + +class TenDBClusterDBTableBackUpFlowParamBuilder(builders.FlowParamBuilder): + controller = SpiderController.database_table_backup + + +@builders.BuilderFactory.register(TicketType.TENDBCLUSTER_DB_TABLE_BACKUP) +class TendDBClusterDBTableBackUpFlowBuilder(BaseTendbTicketFlowBuilder): + serializer = TenDBClusterDBTableBackUpDetailSerializer + inner_flow_builder = TenDBClusterDBTableBackUpFlowParamBuilder + inner_flow_name = _("TenDB Cluster 库表备份") diff --git a/dbm-ui/backend/ticket/builders/tendbcluster/full_backup.py b/dbm-ui/backend/ticket/builders/tendbcluster/full_backup.py new file mode 100644 index 0000000000..4147f3841e --- /dev/null +++ b/dbm-ui/backend/ticket/builders/tendbcluster/full_backup.py @@ -0,0 +1,155 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at https://opensource.org/licenses/MIT +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +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 collections + +from django.utils.translation import gettext_lazy as _ +from rest_framework import serializers + +from backend.db_meta.enums import ClusterType, InstanceInnerRole, InstanceStatus, TenDBClusterSpiderRole +from backend.db_meta.models import Cluster +from backend.flow.consts import MySQLBackupFileTagEnum, MySQLBackupTypeEnum +from backend.flow.engine.controller.spider import SpiderController +from backend.ticket import builders +from backend.ticket.builders.tendbcluster.base import BaseTendbTicketFlowBuilder, TendbBaseOperateDetailSerializer +from backend.ticket.constants import TicketType + + +class TenDBClusterFullBackUpDetailSerializer(TendbBaseOperateDetailSerializer): + class TenDBClusterFullBackupInfoSerializer(serializers.Serializer): + cluster_id = serializers.IntegerField(help_text=_("集群ID")) + backup_local = serializers.CharField(help_text=_("备份位置信息"), default=InstanceInnerRole.SLAVE) + spider_mnt_address = serializers.CharField(help_text=_("运维节点地址"), required=False) + + backup_type = serializers.ChoiceField(help_text=_("备份选项"), choices=MySQLBackupTypeEnum.get_choices()) + file_tag = serializers.ChoiceField(help_text=_("备份保存时间"), choices=MySQLBackupFileTagEnum.get_choices()) + + infos = serializers.ListSerializer(child=TenDBClusterFullBackupInfoSerializer()) + + def validate(self, attrs): + cluster_ids = [info["cluster_id"] for info in attrs["infos"]] + + errors = [] + + msg = self.__validate_cluster_id_unique(cluster_ids=cluster_ids) + if msg: + errors.append(msg) + + msg = self.__validate_cluster_type(cluster_ids=cluster_ids) + if msg: + errors.append(msg) + + msg = self.__validate_cluster_exists(cluster_ids=cluster_ids) + if msg: + errors.append(msg) + + msg = self.__validate_backup_local(attrs=attrs) + if msg: + errors.append(msg) + + msg = self.__validate_cluster_status(attrs=attrs) + if msg: + errors.append(msg) + + return attrs + + @staticmethod + def __validate_cluster_id_unique(cluster_ids) -> str: + """ + 集群 id 不能重复出现 + """ + dup_cluster_ids = [cid for cid, cnt in collections.Counter(cluster_ids) if cnt > 1] + if dup_cluster_ids: + return _( + "重复输入集群: {}".format( + Cluster.objects.filter(pk__in=dup_cluster_ids).values_list("immute_domain", flat=True) + ) + ) + + @staticmethod + def __validate_cluster_type(cluster_ids) -> str: + """ + 集群类型不能混合 + """ + bad = list( + Cluster.objects.filter(pk__in=cluster_ids) + .exclude(cluster_type=ClusterType.TenDBCluster) + .values_list("immute_domain", flat=True) + ) + if bad: + return _("不支持的集群类型 {}".format(", ".join(bad))) + + @staticmethod + def __validate_cluster_exists(cluster_ids) -> str: + """ + 集群 id 必须存在 + """ + exists_cluster_ids = list( + Cluster.objects.filter(pk__in=cluster_ids, cluster_type=ClusterType.TenDBCluster).values_list( + "cluster_id", flat=True + ) + ) + not_exists_cluster_ids = list(set(cluster_ids) - set(exists_cluster_ids)) + if not_exists_cluster_ids: + return _("cluster id: {} 不存在".format(cluster_ids)) + + @staticmethod + def __validate_backup_local(attrs): + bad = [] + + for info in attrs["infos"]: + backup_local = info["backup_local"] + if backup_local not in ["master", "slave", "spider_mnt"]: + bad.append(_("不支持的备份位置 {}".format(backup_local))) + + if backup_local == "spider_mnt" and "spider_mnt_address" not in info: + bad.append(_("缺少 spider_mnt_address")) + + if bad: + return ", ".join(list(set(bad))) + + @staticmethod + def __validate_cluster_status(attrs): + bad = [] + for info in attrs["infos"]: + cluster_id = info["cluster_id"] + backup_local = info["backup_local"] + cluster_obj = Cluster.objects.get(pk=cluster_id) + if ( + backup_local == "spider_mnt" + and not cluster_obj.proxyinstance_set.filter( + tendbclusterspiderext__spider_role=TenDBClusterSpiderRole.SPIDER_MNT, status=InstanceStatus.RUNNING + ).exists() + ): + bad.append(cluster_obj.immute_domain) + elif ( + backup_local in ["remote", "slave"] + and cluster_obj.storageinstance_set.filter( + backup_local, + is_stand_by=True, + ) + .exclude(status=InstanceStatus.RUNNING) + .exists() + ): + bad.append(cluster_obj.immute_domain) + + if bad: + return _("集群状态异常: {}".format(", ".join(list(set(bad))))) + + +class TenDBClusterFullBackUpFlowParamBuilder(builders.FlowParamBuilder): + controller = SpiderController.full_backup + + +@builders.BuilderFactory.register(TicketType.TENDBCLUSTER_FULL_BACKUP) +class TenDBClusterFullBackUpFlowBuilder(BaseTendbTicketFlowBuilder): + serializer = TenDBClusterFullBackUpDetailSerializer + inner_flow_builder = TenDBClusterFullBackUpFlowParamBuilder + inner_flow_name = _("TenDB Cluster 全库备份") diff --git a/dbm-ui/backend/ticket/builders/tendbcluster/tendb_backup.py b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_backup.py deleted file mode 100644 index 51cf63d13d..0000000000 --- a/dbm-ui/backend/ticket/builders/tendbcluster/tendb_backup.py +++ /dev/null @@ -1,51 +0,0 @@ -# -*- coding: utf-8 -*- -""" -TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. -Copyright (C) 2017-2023 THL A29 Limited, a Tencent company. All rights reserved. -Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. -You may obtain a copy of the License at https://opensource.org/licenses/MIT -Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on -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. -""" - -from django.utils.translation import gettext_lazy as _ -from rest_framework import serializers - -from backend.flow.engine.controller.spider import SpiderController -from backend.ticket import builders -from backend.ticket.builders.mysql.base import DBTableField -from backend.ticket.builders.tendbcluster.base import BaseTendbTicketFlowBuilder, TendbBaseOperateDetailSerializer -from backend.ticket.builders.tendbcluster.tendb_full_backup import TendbFullBackUpDetailSerializer -from backend.ticket.constants import TicketType - - -class TendbBackUpDetailSerializer(TendbBaseOperateDetailSerializer): - class TendbBackUpItemSerializer(serializers.Serializer): - cluster_id = serializers.IntegerField(help_text=_("集群ID")) - backup_local = serializers.CharField(help_text=_("备份位置")) - db_patterns = serializers.ListField(help_text=_("匹配DB列表"), child=DBTableField(db_field=True)) - ignore_dbs = serializers.ListField(help_text=_("忽略DB列表"), child=DBTableField(db_field=True)) - table_patterns = serializers.ListField(help_text=_("匹配Table列表"), child=DBTableField()) - ignore_tables = serializers.ListField(help_text=_("忽略Table列表"), child=DBTableField()) - - infos = serializers.ListSerializer(help_text=_("库表备份信息"), child=TendbBackUpItemSerializer()) - - def validate(self, attrs): - for info in attrs["infos"]: - TendbFullBackUpDetailSerializer.get_backup_local_params(info) - - # 库表选择器校验 - super().validate_database_table_selector(attrs, role_key="backup_local") - return attrs - - -class TendbBackUpFlowParamBuilder(builders.FlowParamBuilder): - controller = SpiderController.database_table_backup - - -@builders.BuilderFactory.register(TicketType.TENDBCLUSTER_DB_TABLE_BACKUP) -class TendbBackUpFlowBuilder(BaseTendbTicketFlowBuilder): - serializer = TendbBackUpDetailSerializer - inner_flow_builder = TendbBackUpFlowParamBuilder - inner_flow_name = _("TenDB Cluster 库表备份") diff --git a/dbm-ui/backend/ticket/builders/tendbcluster/tendb_full_backup.py b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_full_backup.py deleted file mode 100644 index 8812ca8659..0000000000 --- a/dbm-ui/backend/ticket/builders/tendbcluster/tendb_full_backup.py +++ /dev/null @@ -1,70 +0,0 @@ -# -*- coding: utf-8 -*- -""" -TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. -Copyright (C) 2017-2023 THL A29 Limited, a Tencent company. All rights reserved. -Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. -You may obtain a copy of the License at https://opensource.org/licenses/MIT -Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on -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. -""" - -from django.utils.translation import gettext_lazy as _ -from rest_framework import serializers - -from backend.db_meta.enums import InstanceInnerRole -from backend.flow.consts import MySQLBackupFileTagEnum, MySQLBackupTypeEnum, TenDBBackUpLocation -from backend.flow.engine.controller.spider import SpiderController -from backend.ticket import builders -from backend.ticket.builders.tendbcluster.base import BaseTendbTicketFlowBuilder, TendbBaseOperateDetailSerializer -from backend.ticket.constants import TicketType - - -class TendbFullBackUpDetailSerializer(TendbBaseOperateDetailSerializer): - class FullBackUpItemSerializer(serializers.Serializer): - class FullBackUpClusterItemSerializer(serializers.Serializer): - cluster_id = serializers.IntegerField(help_text=_("集群ID")) - backup_local = serializers.CharField(help_text=_("备份位置信息"), default=InstanceInnerRole.SLAVE) - - backup_type = serializers.ChoiceField(help_text=_("备份选项"), choices=MySQLBackupTypeEnum.get_choices()) - file_tag = serializers.ChoiceField(help_text=_("备份保存时间"), choices=MySQLBackupFileTagEnum.get_choices()) - clusters = serializers.ListSerializer(help_text=_("集群备份信息"), child=FullBackUpClusterItemSerializer()) - - infos = FullBackUpItemSerializer() - - @classmethod - def get_backup_local_params(cls, info): - """ - 对备份位置进行提取, - 两种情况:remote/spider_mnt::127.0.0.1 - """ - divider = "::" - if divider not in info["backup_local"]: - return info - - backup_local, spider_mnt_address = info["backup_local"].split(divider) - info["backup_local"] = backup_local - info["spider_mnt_address"] = spider_mnt_address - - return info - - def validate(self, attrs): - for cluster_info in attrs["infos"]["clusters"]: - self.get_backup_local_params(cluster_info) - - for cluster in attrs["infos"]["clusters"]: - if cluster["backup_local"] == TenDBBackUpLocation.SPIDER_MNT and "spider_mnt_address" not in cluster: - raise serializers.ValidationError(_("备份位置选择spider_mnt时,请提供运维节点的地址")) - - return attrs - - -class TendbFullBackUpFlowParamBuilder(builders.FlowParamBuilder): - controller = SpiderController.full_backup - - -@builders.BuilderFactory.register(TicketType.TENDBCLUSTER_FULL_BACKUP) -class TendbFullBackUpFlowBuilder(BaseTendbTicketFlowBuilder): - serializer = TendbFullBackUpDetailSerializer - inner_flow_builder = TendbFullBackUpFlowParamBuilder - inner_flow_name = _("TenDB Cluster 全库备份") diff --git a/dbm-ui/backend/ticket/builders/tendbcluster/tendb_import_sqlfile.py b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_import_sqlfile.py index fd174f8f46..b1ad160b04 100644 --- a/dbm-ui/backend/ticket/builders/tendbcluster/tendb_import_sqlfile.py +++ b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_import_sqlfile.py @@ -22,7 +22,7 @@ MysqlSqlImportItsmParamBuilder, ) from backend.ticket.builders.tendbcluster.base import BaseTendbTicketFlowBuilder -from backend.ticket.builders.tendbcluster.tendb_full_backup import TendbFullBackUpDetailSerializer +from backend.ticket.builders.tendbcluster.full_backup import TenDBClusterFullBackUpDetailSerializer from backend.ticket.constants import TicketType logger = logging.getLogger("root") @@ -43,7 +43,7 @@ def format_ticket_data(self): super().format_ticket_data() for info in self.ticket_data["infos"]: info["backup_local"] = info["backup_on"] - TendbFullBackUpDetailSerializer.get_backup_local_params(info) + TenDBClusterFullBackUpDetailSerializer.get_backup_local_params(info) class TenDBClusterSqlImportFlowParamBuilder(MysqlSqlImportFlowParamBuilder): diff --git a/dbm-ui/backend/ticket/builders/tendbsingle/db_table_backup.py b/dbm-ui/backend/ticket/builders/tendbsingle/db_table_backup.py new file mode 100644 index 0000000000..982f6e8632 --- /dev/null +++ b/dbm-ui/backend/ticket/builders/tendbsingle/db_table_backup.py @@ -0,0 +1,28 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at https://opensource.org/licenses/MIT +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +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. +""" + +from django.utils.translation import ugettext_lazy as _ + +from backend.ticket import builders +from backend.ticket.builders.mysql.base import BaseMySQLSingleTicketFlowBuilder +from backend.ticket.builders.mysql.mysql_db_table_backup import ( + MySQLDBTableBackupDetailSerializer, + MySQLDBTableBackupFlowParamBuilder, +) +from backend.ticket.constants import FlowRetryType, TicketType + + +@builders.BuilderFactory.register(TicketType.MYSQL_SINGLE_DB_TABLE_BACKUP) +class TenDBSingleDBTableBackupFlowBuilder(BaseMySQLSingleTicketFlowBuilder): + serializer = MySQLDBTableBackupDetailSerializer + inner_flow_builder = MySQLDBTableBackupFlowParamBuilder + inner_flow_name = _("TenDBSingle 库表备份执行") + retry_type = FlowRetryType.MANUAL_RETRY diff --git a/dbm-ui/backend/ticket/builders/tendbsingle/fullbackup.py b/dbm-ui/backend/ticket/builders/tendbsingle/fullbackup.py new file mode 100644 index 0000000000..3aad765cd9 --- /dev/null +++ b/dbm-ui/backend/ticket/builders/tendbsingle/fullbackup.py @@ -0,0 +1,27 @@ +# -*- coding: utf-8 -*- +""" +TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-DB管理系统(BlueKing-BK-DBM) available. +Copyright (C) 2017-2023 THL A29 Limited, a Tencent company. All rights reserved. +Licensed under the MIT License (the "License"); you may not use this file except in compliance with the License. +You may obtain a copy of the License at https://opensource.org/licenses/MIT +Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on +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. +""" +from django.utils.translation import ugettext_lazy as _ + +from backend.ticket import builders +from backend.ticket.builders.mysql.base import BaseMySQLSingleTicketFlowBuilder +from backend.ticket.builders.mysql.mysql_full_backup import ( + MySQLFullBackupDetailSerializer, + MySQLFullBackupFlowParamBuilder, +) +from backend.ticket.constants import FlowRetryType, TicketType + + +@builders.BuilderFactory.register(TicketType.MYSQL_SINGLE_FULL_BACKUP) +class TenDBSingleFullBackupFlowBuilder(BaseMySQLSingleTicketFlowBuilder): + serializer = MySQLFullBackupDetailSerializer + inner_flow_builder = MySQLFullBackupFlowParamBuilder + inner_flow_name = _("TenDBHA 全库备份执行") + retry_type = FlowRetryType.MANUAL_RETRY diff --git a/dbm-ui/backend/ticket/constants.py b/dbm-ui/backend/ticket/constants.py index 9308ee4212..1a7ccc668d 100644 --- a/dbm-ui/backend/ticket/constants.py +++ b/dbm-ui/backend/ticket/constants.py @@ -194,14 +194,16 @@ def get_cluster_type_by_ticket(cls, ticket_type): MYSQL_INSTANCE_CLONE_RULES = TicketEnumField("MYSQL_INSTANCE_CLONE_RULES", _("MySQL DB实例权限克隆"), _("权限管理")) MYSQL_HA_RENAME_DATABASE = TicketEnumField("MYSQL_HA_RENAME_DATABASE", _("MySQL 高可用DB重命名"), _("集群维护")) MYSQL_HA_TRUNCATE_DATA = TicketEnumField("MYSQL_HA_TRUNCATE_DATA", _("MySQL 高可用清档"), _("数据处理")) - MYSQL_HA_DB_TABLE_BACKUP = TicketEnumField("MYSQL_HA_DB_TABLE_BACKUP", _("MySQL 高可用库表备份"), _("备份")) + MYSQL_HA_DB_TABLE_BACKUP = TicketEnumField("MYSQL_HA_DB_TABLE_BACKUP", _("TenDBHA 库表备份"), _("备份")) + MYSQL_SINGLE_DB_TABLE_BACKUP = TicketEnumField("MYSQL_SINGLE_DB_TABLE_BACKUP", _("TenDBSingle 库表备份"), _("备份")) MYSQL_CHECKSUM = TicketEnumField("MYSQL_CHECKSUM", _("MySQL 数据校验修复"), _("数据处理")) MYSQL_PARTITION = TicketEnumField("MYSQL_PARTITION", _("MySQL 分区"), _("分区管理")) MYSQL_PARTITION_CRON = TicketEnumField("MYSQL_PARTITION_CRON", _("MySQL 分区定时任务"), register_iam=False) # noqa MYSQL_DATA_REPAIR = TicketEnumField("MYSQL_DATA_REPAIR", _("MySQL 数据修复"), register_iam=False) MYSQL_FLASHBACK = TicketEnumField("MYSQL_FLASHBACK", _("MySQL 闪回"), _("回档")) MYSQL_ROLLBACK_CLUSTER = TicketEnumField("MYSQL_ROLLBACK_CLUSTER", _("MySQL 定点构造"), _("回档")) - MYSQL_HA_FULL_BACKUP = TicketEnumField("MYSQL_HA_FULL_BACKUP", _("MySQL 高可用全库备份"), _("备份")) + MYSQL_HA_FULL_BACKUP = TicketEnumField("MYSQL_HA_FULL_BACKUP", _("TenDB HA全库备份"), _("备份")) + MYSQL_SINGLE_FULL_BACKUP = TicketEnumField("MYSQL_SINGLE_FULL_BACKUP", _("TenDB Single全库备份"), _("备份")) MYSQL_SINGLE_TRUNCATE_DATA = TicketEnumField("MYSQL_SINGLE_TRUNCATE_DATA", _("MySQL 单节点清档"), _("数据处理")) MYSQL_SINGLE_RENAME_DATABASE = TicketEnumField("MYSQL_SINGLE_RENAME_DATABASE", _("MySQL 单节点DB重命名"), _("集群维护")) # noqa MYSQL_HA_STANDARDIZE = TicketEnumField("MYSQL_HA_STANDARDIZE", _("TendbHA 标准化"), register_iam=False) @@ -212,7 +214,7 @@ def get_cluster_type_by_ticket(cls, ticket_type): MYSQL_LOCAL_UPGRADE = TicketEnumField("MYSQL_LOCAL_UPGRADE", _("MySQL 原地升级"), _("版本升级")) MYSQL_MIGRATE_UPGRADE = TicketEnumField("MYSQL_MIGRATE_UPGRADE", _("MySQL 迁移升级"), _("版本升级")) MYSQL_SLAVE_MIGRATE_UPGRADE = TicketEnumField("MYSQL_SLAVE_MIGRATE_UPGRADE", _("MySQL Slave 迁移升级"), _("版本升级")) - MYSQL_RO_SLAVE_UNINSTALL = TicketEnumField("MYSQL_RO_SLAVE_UNINSTALL", _("MySQL非stanby slave下架"), _("集群维护")) + MYSQL_RO_SLAVE_UNINSTALL = TicketEnumField("MYSQL_RO_SLAVE_UNINSTALL", _("MySQL非standby slave下架"), _("集群维护")) MYSQL_PROXY_UPGRADE = TicketEnumField("MYSQL_PROXY_UPGRADE", _("MySQL Proxy升级"), _("版本升级")) MYSQL_HA_TRANSFER_TO_OTHER_BIZ = TicketEnumField("MYSQL_HA_TRANSFER_TO_OTHER_BIZ", _("TendbHA集群迁移至其他业务"), register_iam=False)# noqa MYSQL_PUSH_PERIPHERAL_CONFIG = TicketEnumField("MYSQL_PUSH_PERIPHERAL_CONFIG", _("推送周边配置"), register_iam=False)