Skip to content

Commit

Permalink
feature: 提供 Agent 包管理后台接口 (closed TencentBlueKing#1683)
Browse files Browse the repository at this point in the history
  • Loading branch information
wyyalt committed Oct 13, 2023
1 parent 791a8a2 commit 26bc8ca
Show file tree
Hide file tree
Showing 10 changed files with 774 additions and 1 deletion.
4 changes: 4 additions & 0 deletions apps/generic.py
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,10 @@ def get_serializer_class(self, *args, **kwargs):
return type(self.serializer_class.__name__, (self.serializer_class,), {"Meta": self.serializer_meta})


class ApiMixinModelViewSet(ApiMixin, _ModelViewSet):
pagination_class = DataPageNumberPagination


def custom_exception_handler(exc, context):
"""
自定义错误处理方式
Expand Down
28 changes: 28 additions & 0 deletions apps/node_man/handlers/meta.py
Original file line number Diff line number Diff line change
Expand Up @@ -469,6 +469,32 @@ def fetch_os_type_children(os_types: Tuple = constants.OsType):
os_type_children.append({"id": os_type, "name": constants.OS_CHN.get(os_type, os_type)})
return os_type_children

@staticmethod
def fetch_agent_pkg_manager_children():
mock_version = [
{"name": "2.1.8", "id": "2.1.8"},
{"name": "2.1.7", "id": "2.1.7"},
]
mock_tags = [
{"name": "稳定版本", "id": "stable"},
{"name": "最新版本", "id": "latest"},
]
mock_creator = [
{"name": "user1", "id": "user1"},
{"name": "user2", "id": "user2"},
]
mock_is_ready = [
{"name": "启用", "id": True},
{"name": "停用", "id": False},
]

return [
{"name": _("版本号"), "id": "version", "children": mock_version},
{"name": _("标签信息"), "id": "tags", "children": mock_tags},
{"name": _("上传用户"), "id": "creator", "children": mock_creator},
{"name": _("状态"), "id": "is_ready", "children": mock_is_ready},
]

def filter_condition(self, category):
"""
获取过滤条件
Expand All @@ -495,6 +521,8 @@ def filter_condition(self, category):
elif category == "os_type":
ret = self.fetch_os_type_children()
return ret
elif category == "agent_pkg_manage":
return self.fetch_agent_pkg_manager_children()

@staticmethod
def install_default_values_formatter(install_default_values: Dict[str, Dict[str, Any]]):
Expand Down
52 changes: 52 additions & 0 deletions apps/node_man/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2441,3 +2441,55 @@ class Meta:

def __str__(self) -> str:
return f"{self.name}-{self.version}-{self.agent_name}-{self.os}-{self.cpu_arch}"


class AgentPackages(models.Model):
pkg_name = models.CharField(_("压缩包名"), max_length=128)
version = models.CharField(_("版本号"), max_length=128)
module = models.CharField(_("所属服务"), max_length=32)
project = models.CharField(_("工程名"), max_length=32, db_index=True)
pkg_size = models.IntegerField(_("包大小"))
pkg_path = models.CharField(_("包路径"), max_length=128)
md5 = models.CharField(_("md5值"), max_length=32)
pkg_mtime = models.CharField(_("包更新时间"), max_length=48)
pkg_ctime = models.CharField(_("包创建时间"), max_length=48)
location = models.CharField(_("安装包链接"), max_length=512)
os = models.CharField(
_("系统类型"),
max_length=32,
choices=constants.PLUGIN_OS_CHOICES,
default=constants.PluginOsType.linux,
db_index=True,
)
cpu_arch = models.CharField(
_("CPU类型"), max_length=32, choices=constants.CPU_CHOICES, default=constants.CpuType.x86_64, db_index=True
)
creator = models.CharField(_("操作人"), max_length=45, default="admin")

is_release_version = models.BooleanField(_("是否已经发布版本"), default=True, db_index=True)
# 由于创建记录时,文件可能仍然在传输过程中,因此需要标志位判断是否已经可用
is_ready = models.BooleanField(_("插件是否可用"), default=True)

version_log = models.TextField(_("版本日志"), null=True, blank=True)
version_log_en = models.TextField(_("英文版本日志"), null=True, blank=True)

class Meta:
verbose_name = _("Agent包(AgentPackages)")
verbose_name_plural = _("Agent包(AgentPackages)")


class AgentPackageDesc(models.Model):
"""
Agent包信息表
"""

# 安装包名需要全局唯一,防止冲突
name = models.CharField(_("安装包名"), max_length=32, unique=True, db_index=True)
description = models.TextField(_("安装包描述"))
module = models.CharField(_("所属服务"), max_length=32)
description_en = models.TextField(_("英文插件描述"), null=True, blank=True)
category = models.CharField(_("所属范围"), max_length=32, choices=constants.CATEGORY_CHOICES)

class Meta:
verbose_name = _("Agent信息(AgentPackageDesc)")
verbose_name_plural = _("Agent信息(AgentPackageDesc)")
4 changes: 4 additions & 0 deletions apps/node_man/serializers/meta.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,3 +25,7 @@ class JobSettingSerializer(serializers.Serializer):
install_download_limit_speed = serializers.IntegerField(label=_("安装下载限速"), max_value=JOB_MAX_VALUE, min_value=0)
parallel_install_number = serializers.IntegerField(label=_("并行安装数"), max_value=JOB_MAX_VALUE, min_value=0)
node_man_log_level = serializers.ChoiceField(label=_("节点管理日志级别"), choices=list(NODE_MAN_LOG_LEVEL))


class FilterConditionSerializer(serializers.Serializer):
category = serializers.ChoiceField(label=_("分类"), choices=["agent_pkg_manage", "agent_pkg_quick_search"])
132 changes: 132 additions & 0 deletions apps/node_man/serializers/packager_manage.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,132 @@
# -*- coding: utf-8 -*-
"""
TencentBlueKing is pleased to support the open source community by making 蓝鲸智云-节点管理(BlueKing-BK-NODEMAN) available.
Copyright (C) 2017-2022 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 apps.exceptions import ValidationError
from apps.node_man.models import AgentPackages


class AgentPackageSerializer(serializers.ModelSerializer):
class meta:
model = AgentPackages
fields = "__all__"


class TagsSerializer(serializers.Serializer):
id = serializers.CharField()
name = serializers.CharField()


class ConditionsSerializer(serializers.Serializer):
key = serializers.ChoiceField(choices=["version", "os_cpu_arch", "tags", "is_ready"])
values = serializers.ListField()


class SearchSerializer(serializers.Serializer):
os_cpu_arch = serializers.CharField(required=False)
tags = serializers.ListField(required=False)


class PackageDescSearchSerializer(serializers.Serializer):
os_cpu_arch = serializers.CharField(required=False)


class PackageSerializer(serializers.Serializer):
id = serializers.IntegerField()
pkg_name = serializers.CharField()
version = serializers.CharField()
os = serializers.CharField()
cpu_arch = serializers.CharField()
tags = TagsSerializer(many=True)
creator = serializers.CharField()
pkg_ctime = serializers.DateTimeField()
host_count = serializers.IntegerField()
is_ready = serializers.BooleanField()


class PackageDescSerializer(serializers.Serializer):
id = serializers.IntegerField()
version = serializers.CharField()
tags = TagsSerializer(many=True)
packages = PackageSerializer(many=True)
is_ready = serializers.BooleanField()


class SearchResponseSerializer(serializers.Serializer):
total = serializers.IntegerField()
list = PackageSerializer(many=True)


class PackageDescResponseSerialiaer(serializers.Serializer):
total = serializers.IntegerField()
list = PackageDescSerializer(many=True)


class OperateSerializer(serializers.Serializer):
is_ready = serializers.BooleanField()


# TODO 与plugin相同可抽取公共Serializer
class UploadSerializer(serializers.Serializer):
class PkgFileField(serializers.FileField):
def to_internal_value(self, data):
data = super().to_internal_value(data)
file_name = data.name
if not (file_name.endswith(".tgz") or file_name.endswith(".tar.gz")):
raise ValidationError(_("仅支持'tgz', 'tar.gz'拓展名的文件"))
return data

module = serializers.ChoiceField(choices=["gse_agent", "gse_proxy"], required=False, default="gse_agent")
package_file = PkgFileField()


class UploadResponseSerializer(serializers.Serializer):
id = serializers.IntegerField()
name = serializers.CharField()
pkg_size = serializers.IntegerField()


class ParseSerializer(serializers.Serializer):
file_name = serializers.CharField()


class ParseResponseSerializer(serializers.Serializer):
class ParsePackageSerializer(serializers.Serializer):
module = serializers.ChoiceField(choices=["agent", "proxy"])
pkg_name = serializers.CharField()
pkg_abs_path = serializers.CharField()
version = serializers.CharField()
os = serializers.CharField()
cpu_arch = serializers.CharField()
config_templates = serializers.ListField()

description = serializers.CharField()
packages = ParsePackageSerializer(many=True)


class AgentRegisterSerializer(serializers.Serializer):
class RegisterPackageSerializer(serializers.Serializer):
pkg_abs_path = serializers.CharField()
tags = serializers.ListField()

is_release = serializers.BooleanField()
packages = RegisterPackageSerializer(many=True)


class AgentRegisterTaskSerializer(serializers.Serializer):
job_id = serializers.IntegerField()


class AgentRegisterTaskResponseSerializer(serializers.Serializer):
is_finish = serializers.BooleanField()
status = serializers.ChoiceField(choices=["SUCCESS", "FAILED", "RUNNING"])
message = serializers.CharField()
6 changes: 6 additions & 0 deletions apps/node_man/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@
)
from apps.node_man.views.healthz import HealthzViewSet
from apps.node_man.views.host_v2 import HostV2ViewSet
from apps.node_man.views.package_manage import (
AgentPackageDescViewSet,
PackageManageViewSet,
)
from apps.node_man.views.plugin import GsePluginViewSet
from apps.node_man.views.plugin_v2 import PluginV2ViewSet
from apps.node_man.views.sync_task import SyncTaskViewSet
Expand Down Expand Up @@ -67,6 +71,8 @@
router.register(r"v2/plugin", PluginV2ViewSet, basename="plugin_v2")
router.register(r"healthz", HealthzViewSet, basename="healthz")
router.register(r"sync_task", SyncTaskViewSet, basename="sync_task")
router.register(r"agent/package", PackageManageViewSet, basename="package_manage")
router.register(r"agent/package_desc", AgentPackageDescViewSet, basename="package_desc")

biz_dispatcher = DjangoBasicResourceApiDispatcher(iam, settings.BK_IAM_SYSTEM_ID)
biz_dispatcher.register("biz", BusinessResourceProvider())
Expand Down
6 changes: 5 additions & 1 deletion apps/node_man/views/meta.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,10 @@
from apps.node_man.exceptions import NotSuperUserError
from apps.node_man.handlers.iam import IamHandler
from apps.node_man.handlers.meta import MetaHandler
from apps.node_man.serializers.meta import JobSettingSerializer
from apps.node_man.serializers.meta import (
FilterConditionSerializer,
JobSettingSerializer,
)
from apps.utils.local import get_request_username

META_VIEW_TAGS = ["meta"]
Expand All @@ -26,6 +29,7 @@
class MetaViews(APIViewSet):
@swagger_auto_schema(
operation_summary="获取过滤条件",
query_serializer=FilterConditionSerializer,
tags=META_VIEW_TAGS,
)
@action(detail=False)
Expand Down
Loading

0 comments on commit 26bc8ca

Please sign in to comment.