diff --git a/dbm-ui/backend/db_meta/enums/comm.py b/dbm-ui/backend/db_meta/enums/comm.py index 89a82b24f4..e696744984 100644 --- a/dbm-ui/backend/db_meta/enums/comm.py +++ b/dbm-ui/backend/db_meta/enums/comm.py @@ -26,8 +26,9 @@ class DBCCModule(str, StructuredEnum): class TagType(str, StructuredEnum): - CUSTOM = EnumField("custom", _("custom")) - SYSTEM = EnumField("system", _("system")) + CUSTOM = EnumField("custom", _("自定义标签")) + SYSTEM = EnumField("system", _("系统标签")) + BUILTIN = EnumField("builtin", _("内置标签")) class SystemTagEnum(str, StructuredEnum): @@ -36,6 +37,16 @@ class SystemTagEnum(str, StructuredEnum): TEMPORARY = EnumField("temporary", _("temporary")) +class TagResourceEnum(int, StructuredEnum): + """资源类型""" + + CLUSTER = EnumField(0, _("集群")) + STORAGE_INSTANCE = EnumField(1, _("存储实例")) + PROXY_INSTANCE = EnumField(2, _("代理实例")) + MACHINE = EnumField(3, _("实例机器")) + RESOURCE_MACHINE = EnumField(4, _("资源池机器")) + + class RedisVerUpdateNodeType(str, StructuredEnum): """redis版本升级节点类型""" diff --git a/dbm-ui/backend/db_meta/migrations/0042_auto_20240823_1533.py b/dbm-ui/backend/db_meta/migrations/0042_auto_20240823_1533.py new file mode 100644 index 0000000000..11cd5f764f --- /dev/null +++ b/dbm-ui/backend/db_meta/migrations/0042_auto_20240823_1533.py @@ -0,0 +1,79 @@ +# Generated by Django 3.2.25 on 2024-08-23 07:33 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ("db_meta", "0041_auto_20240819_1031"), + ] + + operations = [ + migrations.AddField( + model_name="cluster", + name="tags", + field=models.ManyToManyField(blank=True, help_text="标签(外键)", to="db_meta.Tag"), + ), + migrations.AddField( + model_name="tag", + name="key", + field=models.CharField(default="", help_text="标签键", max_length=64), + ), + migrations.AddField( + model_name="tag", + name="value", + field=models.CharField(default="", help_text="标签值", max_length=64), + ), + migrations.AlterField( + model_name="spec", + name="cpu", + field=models.JSONField(help_text='cpu规格描述:{"min":1,"max":10}', null=True), + ), + migrations.AlterField( + model_name="spec", + name="device_class", + field=models.JSONField(help_text='实际机器机型: ["class1","class2"]', null=True), + ), + migrations.AlterField( + model_name="spec", + name="mem", + field=models.JSONField(help_text='mem规格描述:{"min":100,"max":1000}', null=True), + ), + migrations.AlterField( + model_name="spec", + name="qps", + field=models.JSONField(default=dict, help_text='qps规格描述:{"min": 1, "max": 100}'), + ), + migrations.AlterField( + model_name="spec", + name="storage_spec", + field=models.JSONField(help_text='存储磁盘需求配置:[{"mount_point":"/data","size":500,"type":"ssd"}]', null=True), + ), + migrations.AlterField( + model_name="tag", + name="bk_biz_id", + field=models.IntegerField(default=0, help_text="业务 ID"), + ), + migrations.AlterField( + model_name="tag", + name="type", + field=models.CharField( + choices=[("custom", "自定义标签"), ("system", "系统标签"), ("builtin", "内置标签")], + help_text="tag类型", + max_length=64, + ), + ), + migrations.AlterUniqueTogether( + name="tag", + unique_together={("bk_biz_id", "key", "value")}, + ), + migrations.RemoveField( + model_name="tag", + name="cluster", + ), + migrations.RemoveField( + model_name="tag", + name="name", + ), + ] diff --git a/dbm-ui/backend/db_meta/models/cluster.py b/dbm-ui/backend/db_meta/models/cluster.py index 9198d7637e..d3dfab721b 100644 --- a/dbm-ui/backend/db_meta/models/cluster.py +++ b/dbm-ui/backend/db_meta/models/cluster.py @@ -44,6 +44,7 @@ ClusterSqlserverStatusFlags, ) from backend.db_meta.exceptions import ClusterExclusiveOperateException, DBMetaException +from backend.db_meta.models.tag import Tag from backend.db_services.version.constants import LATEST, PredixyVersion, TwemproxyVersion from backend.flow.consts import DEFAULT_RIAK_PORT from backend.ticket.constants import TicketType @@ -68,6 +69,7 @@ class Cluster(AuditedModel): max_length=128, help_text=_("容灾要求"), choices=AffinityEnum.get_choices(), default=AffinityEnum.NONE.value ) time_zone = models.CharField(max_length=16, default=DEFAULT_TIME_ZONE, help_text=_("集群所在的时区")) + tags = models.ManyToManyField(Tag, blank=True, help_text=_("标签(外键)")) class Meta: unique_together = [("bk_biz_id", "immute_domain", "cluster_type", "db_module_id"), ("immute_domain",)] diff --git a/dbm-ui/backend/db_meta/models/tag.py b/dbm-ui/backend/db_meta/models/tag.py index ecdd353bd5..f45dd16317 100644 --- a/dbm-ui/backend/db_meta/models/tag.py +++ b/dbm-ui/backend/db_meta/models/tag.py @@ -12,20 +12,27 @@ from django.utils.translation import ugettext_lazy as _ from backend.bk_web.models import AuditedModel +from backend.configuration.constants import PLAT_BIZ_ID from backend.db_meta.enums.comm import TagType -from backend.db_meta.models import Cluster class Tag(AuditedModel): - bk_biz_id = models.IntegerField(default=0) - name = models.CharField(max_length=64, default="", help_text=_("tag名称")) - type = models.CharField(max_length=64, help_text=_("tag类型"), choices=TagType.get_choices()) - cluster = models.ManyToManyField(Cluster, blank=True, help_text=_("关联集群")) + bk_biz_id = models.IntegerField(help_text=_("业务 ID"), default=0) + key = models.CharField(help_text=_("标签键"), default="", max_length=64) + value = models.CharField(help_text=_("标签值"), default="", max_length=64) + type = models.CharField(help_text=_("tag类型"), max_length=64, choices=TagType.get_choices()) class Meta: - unique_together = ["bk_biz_id", "name"] + unique_together = ["bk_biz_id", "key", "value"] @property def tag_desc(self): """仅返回tag的信息""" - return {"bk_biz_id": self.bk_biz_id, "name": self.name, "type": self.type} + return {"bk_biz_id": self.bk_biz_id, "key": self.key, "type": self.type} + + @classmethod + def get_or_create_system_tag(cls, key: str, value: str): + tag, created = cls.objects.get_or_create( + bk_biz_id=PLAT_BIZ_ID, key=key, value=value, type=TagType.SYSTEM.value + ) + return tag diff --git a/dbm-ui/backend/db_services/dbbase/resources/query.py b/dbm-ui/backend/db_services/dbbase/resources/query.py index 2fec367ee5..6f087523e8 100644 --- a/dbm-ui/backend/db_services/dbbase/resources/query.py +++ b/dbm-ui/backend/db_services/dbbase/resources/query.py @@ -213,6 +213,8 @@ def export_instance(cls, bk_biz_id: int, bk_host_ids: list) -> HttpResponse: @classmethod def get_temporary_cluster_info(cls, cluster, ticket_type): """如果当前集群是临时集群,则补充临时集群相关信息。""" + # TODO 临时关闭 tag + return {} tags = [tag.name for tag in cluster.tag_set.all()] if SystemTagEnum.TEMPORARY.value not in tags: return {} @@ -395,8 +397,14 @@ def _list_clusters( for param in filter_params_map: if query_params.get(param): query_filters &= filter_params_map[param] + + # 对标签进行过滤,标签“且”查询,需以追加 filter 的方式实现 + cluster_queryset = Cluster.objects.filter(query_filters) + for tag_id in query_params.get("tag_ids", "").split(","): + cluster_queryset = cluster_queryset.filter(tags__id=tag_id) + # 一join多的一方会有重复的数据,去重 - cluster_queryset = Cluster.objects.filter(query_filters).distinct() + cluster_queryset = cluster_queryset.distinct() def filter_inst_queryset(_cluster_queryset, _proxy_queryset, _storage_queryset, _filters): # 注意这里用新的变量获取过滤后的queryset,不要用原queryset过滤,会影响后续集群关联实例的获取 @@ -470,7 +478,7 @@ def _filter_cluster_hook( Prefetch("proxyinstance_set", queryset=proxy_queryset.select_related("machine"), to_attr="proxies"), Prefetch("storageinstance_set", queryset=storage_queryset.select_related("machine"), to_attr="storages"), Prefetch("clusterentry_set", to_attr="entries"), - "tag_set", + "tags", ) cluster_ids = list(cluster_queryset.values_list("id", flat=True)) @@ -562,6 +570,7 @@ def _to_cluster_representation( "updater": cluster.updater, "create_at": datetime2str(cluster.create_at), "update_at": datetime2str(cluster.update_at), + "tags": [{tag.key: tag.value} for tag in cluster.tags.all()], } @classmethod diff --git a/dbm-ui/backend/db_services/dbbase/resources/serializers.py b/dbm-ui/backend/db_services/dbbase/resources/serializers.py index a3aaec8878..8a39d2fe05 100644 --- a/dbm-ui/backend/db_services/dbbase/resources/serializers.py +++ b/dbm-ui/backend/db_services/dbbase/resources/serializers.py @@ -33,6 +33,7 @@ class ListResourceSLZ(serializers.Serializer): db_module_id = serializers.CharField(required=False, help_text=_("所属DB模块")) bk_cloud_id = serializers.CharField(required=False, help_text=_("管控区域")) cluster_type = serializers.CharField(required=False, help_text=_("集群类型")) + tag_ids = serializers.CharField(required=False, help_text=_("标签")) class ListMySQLResourceSLZ(ListResourceSLZ): diff --git a/dbm-ui/backend/ticket/builders/tendbcluster/tendb_fixpoint_rollback.py b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_fixpoint_rollback.py index 53b4acdb73..eda8768ed4 100644 --- a/dbm-ui/backend/ticket/builders/tendbcluster/tendb_fixpoint_rollback.py +++ b/dbm-ui/backend/ticket/builders/tendbcluster/tendb_fixpoint_rollback.py @@ -97,9 +97,9 @@ def pre_callback(self): # 对临时集群记录变更 temporary_tag, _ = Tag.objects.get_or_create( - bk_biz_id=self.ticket.bk_biz_id, name=SystemTagEnum.TEMPORARY.value, type=TagType.SYSTEM.value + bk_biz_id=self.ticket.bk_biz_id, key=SystemTagEnum.TEMPORARY.value, value=True, type=TagType.SYSTEM.value ) - target_cluster.tag_set.add(temporary_tag) + target_cluster.tags.add(temporary_tag) ClusterOperateRecord.objects.get_or_create( cluster_id=target_cluster.id, ticket=self.ticket, flow=rollback_flow ) diff --git a/dbm-ui/scripts/ci/code_quality.sh b/dbm-ui/scripts/ci/code_quality.sh index 5a9c5774c0..4e7e437d48 100755 --- a/dbm-ui/scripts/ci/code_quality.sh +++ b/dbm-ui/scripts/ci/code_quality.sh @@ -57,7 +57,7 @@ echo "未通过数: $TEST_NOT_SUCCESS_COUNT" if [[ $TEST_NOT_SUCCESS_COUNT -ne 0 ]]; then - echo -e "$TEST_LOGS" + echo -e "\033[1;31m $TEST_LOGS \033[0m" exit 1 fi diff --git a/dbm-ui/scripts/ci/install.sh b/dbm-ui/scripts/ci/install.sh index a25c698417..e0595ff237 100755 --- a/dbm-ui/scripts/ci/install.sh +++ b/dbm-ui/scripts/ci/install.sh @@ -23,7 +23,7 @@ poetry export --without-hashes -f requirements.txt --output requirements.txt pip install -r requirements.txt >> /tmp/pip_install.log if [[ $? -ne 0 ]]; then - echo "Error: pip install -r requirements.txt error!" + echo "\033[1;31m Error: pip install -r requirements.txt error! \033[0m" cat /tmp/pip_install.log FAILED_COUNT=$[$FAILED_COUNT+1] fi @@ -57,7 +57,7 @@ echo "开始执行 python manage.py migrate --database=report_db" python manage.py migrate --database=report_db >> /tmp/migrate_report_db.log if [[ $? -ne 0 ]]; then - echo "Error: python manage.py migrate --database=report_db 执行失败!请检查 migrations 文件" + echo "\033[1;31m Error: python manage.py migrate --database=report_db 执行失败!请检查 migrations 文件 \033[0m" cat /tmp/migrate_report_db.log FAILED_COUNT=$[$FAILED_COUNT+1] fi @@ -66,7 +66,7 @@ echo "开始执行 python manage.py migrate" python manage.py migrate >> /tmp/migrate.log if [[ $? -ne 0 ]]; then - echo "Error: python manage.py migrate 执行失败!请检查 migrations 文件" + echo "\033[1;31m Error: python manage.py migrate 执行失败!请检查 migrations 文件 \033[0m" cat /tmp/migrate.log FAILED_COUNT=$[$FAILED_COUNT+1] fi @@ -76,13 +76,13 @@ python manage.py createcachetable django_cache python manage.py language_finder -p backend/ -m error if [[ $? -ne 0 ]]; then - echo "Error: python manage.py language_finder -p backend/ -m error 执行失败!请检查中文是否已标记" + echo "\033[1;31m Error: python manage.py language_finder -p backend/ -m error 执行失败!请检查中文是否已标记 \033[0m" FAILED_COUNT=$[$FAILED_COUNT+1] fi if [[ $FAILED_COUNT -ne 0 ]]; then - echo "Error: 前置命令未通过! 前置命令执行失败数量: $FAILED_COUNT" + echo "\033[1;31m Error: 前置命令未通过! 前置命令执行失败数量: $FAILED_COUNT \033[0m" exit 1 else echo "前置命令已通过"