diff --git a/src/asset/fields.py b/src/asset/fields.py index 4f18f2e..c60578d 100644 --- a/src/asset/fields.py +++ b/src/asset/fields.py @@ -3,6 +3,7 @@ asset marshal fields """ from flask_restful import fields +from common.fields import DateTime HostFields = { @@ -16,7 +17,7 @@ HostFields = { "memory": fields.Integer, "tags": fields.List(fields.String), "labels": fields.Raw, - "created": fields.DateTime, + "created": DateTime, } HostSimpleFields = { diff --git a/src/common/fields.py b/src/common/fields.py new file mode 100644 index 0000000..4c3abeb --- /dev/null +++ b/src/common/fields.py @@ -0,0 +1,20 @@ +from flask_restful import fields + + +class DateTime(fields.DateTime): + """时间字段格式化,默认的 `rfc822` 不直观,可以指定 `dt_format` 自定义格式""" + + def __init__(self, dt_format='%Y-%m-%d %H:%M:%S', **kwargs): + super(fields.DateTime, self).__init__(**kwargs) + self.dt_format = dt_format + + def format(self, value): + try: + if self.dt_format in ('rfc822', 'iso8601'): + return super(fields.DateTime.format(self, value)) + else: + if isinstance(value, str): + return value + return value.strftime(self.dt_format) + except AttributeError as ae: + raise fields.MarshallingException(ae) diff --git a/src/project/fields.py b/src/project/fields.py index 2bf3761..7c4f236 100644 --- a/src/project/fields.py +++ b/src/project/fields.py @@ -9,6 +9,7 @@ ProjectFields = { "id": fields.String, "name": fields.String, "fork": fields.String, + "fullname": fields.String, "domain": fields.String, "www_ip": fields.String, "ops_ip": fields.String, @@ -25,6 +26,7 @@ ProjectSimpleField = { "id": fields.String, "name": fields.String, "fork": fields.String, + "fullname": fields.String, "domain": fields.String, } @@ -58,6 +60,7 @@ ChannelSimpleFields = { ServerFields = { "id": fields.String, "num": fields.Integer, + "agent": fields.String, "channel": fields.Nested(ChannelSimpleFields), "status": fields.String, "host": fields.Nested(HostSimpleFields), @@ -66,6 +69,27 @@ ServerFields = { "version": fields.Nested(VersionFields), "weight": fields.Integer, "slot": fields.Integer, - "tag": fields.List(fields.String), + "tags": fields.List(fields.String), "labels": fields.Raw, } + +# 主要关联字段平铺了的区服信息 +AgentServerFields = { + "id": fields.String, + "num": fields.Integer, + "spid": fields.String, + "agent": fields.String, + # "channel_id": fields.String, + "project": fields.String, + "public_ip": fields.String, + "private_ip": fields.String, + # "host_id": fields.String, + "domain": fields.String, + "port": fields.Integer, + "version": fields.Nested(VersionFields), + "status": fields.String, + "weight": fields.Integer, + "slot": fields.Integer, + # "tags": fields.List(fields.String), + # "labels": fields.Raw, +} diff --git a/src/project/models.py b/src/project/models.py index b3744e2..08b48b7 100644 --- a/src/project/models.py +++ b/src/project/models.py @@ -24,6 +24,7 @@ class Project(DocumentBase): tags = mongo.ListField(mongo.StringField(), default=list) # tags 默认是空列表 labels = mongo.DictField(default=dict) + @property def fullname(self): return f"{self.name}/{self.fork}" @@ -76,7 +77,7 @@ class Server(DocumentBase): # 机器字段,TODO 先允许为空 host = mongo.ReferenceField(Host, null=True) - domain = mongo.StringField(max_length=128, required=False) + domain = mongo.StringField(max_length=128, required=False, default="") port = mongo.IntField() version = mongo.EmbeddedDocumentField(Version) weight = mongo.IntField(default=1) @@ -86,3 +87,44 @@ class Server(DocumentBase): tags = mongo.ListField(mongo.StringField(), default=list) # tags 默认是空列表 labels = mongo.DictField(default=dict) + @property + def agent(self): + if self.channel: + return f"{self.channel.spid}_s{self.num}" + return f"_s{self.num}" + + @property + def spid(self): + if self.channel: + return self.channel.spid + return "" + + @property + def channel_id(self): + if self.channel: + return self.channel.id + return "" + + @property + def project(self): + if self.channel: + return self.channel.project.fullname + return "" + + @property + def public_ip(self): + if self.host: + return self.host.public_ip + return "" + + @property + def private_ip(self): + if self.host: + return self.host.private_ip + return "" + + @property + def host_id(self): + if self.host: + return self.host.id + return "" diff --git a/src/project/routes.py b/src/project/routes.py index a1454ad..8d275f0 100644 --- a/src/project/routes.py +++ b/src/project/routes.py @@ -22,3 +22,6 @@ api.add_resource(views.ServerViews, '/server/', endpoint="server") api.add_resource(views.ServerDetailView, '/server//', endpoint="server-detail") # 区服信息同步接口,更新区服信息 api.add_resource(operation.ServerSyncView, '/server/sync/', endpoint="server-sync") + +# 机器信息 +api.add_resource(operation.AgentInfo, '/agent/info//', endpoint="agent-info") diff --git a/src/project/views/operation.py b/src/project/views/operation.py index 9a8fc7b..ebebb62 100644 --- a/src/project/views/operation.py +++ b/src/project/views/operation.py @@ -1,12 +1,13 @@ import datetime from flask import current_app as app -from flask_restful import reqparse +from flask_restful import reqparse, Resource, marshal, fields as F from asset.models import Host +from asset.fields import HostFields from project import fields from project.models import Project, Channel, Server, Version -from common.views import CreateMixin +from common.views import CreateMixin, RetrieveMixin from common.permission import token_header_required from common.utils import make_response @@ -81,7 +82,7 @@ class ServerSyncView(CreateMixin): try: if not host: - proj = self.project.fullname() + proj = self.project.fullname hostObj, created = Host.get_or_create( public_ip=ip, defaults=dict(public_ip=ip, tags=[proj], created=datetime.datetime.now())) # 项目写入机器的tags @@ -114,3 +115,33 @@ class ServerSyncView(CreateMixin): return make_response(200, 1000, "success") + +class AgentInfo(RetrieveMixin): + """返回对应机器的所有信息:项目、渠道、区服内容""" + fields = { + "host": F.Nested(HostFields), + "project": F.List(F.Nested(fields.ProjectFields)), + "channel": F.List(F.Nested(fields.ChannelFields)), + "servers": F.List(F.Nested(fields.AgentServerFields)) + } + + def get(self, agent_id): + """ + :param agent_id: 客户端 id + :return: + """ + # 找到机器,不存在就404 + host = Host.objects(minion_id=agent_id).first_or_404(message="resource not found") + # 根据机器找区服集合,返回 + servers = Server.objects(host=host).all() + channels = servers.values_list("channel").distinct("channel") + projects = [channel.project for channel in channels] + # 填充数据 + data = { + "host": host, + "project": projects, + "channel": channels, + "servers": servers, + } + # 返回 + return marshal(data, self.fields)