取消 ReferenceField 字段,改用字符串字段存储对象id,手动关联,接口同步修改

This commit is contained in:
chenzuoqing 2021-12-06 14:20:12 +08:00
parent 7cb3848751
commit 8d54f9e8fd
4 changed files with 89 additions and 24 deletions

View File

@ -13,3 +13,11 @@ def is_ipaddr(ip):
ipaddress.ip_address(ip)
except:
raise mongo.ValidationError('The value must be a ip address')
def is_hex_string(string: str):
"""校验 hex 字符串(十六进制)"""
try:
int(string, 16)
except:
raise mongo.ValidationError('The value must be a hex string')

View File

@ -83,6 +83,8 @@ AgentServerFields = {
"is_cross": fields.Boolean,
# "channel_id": fields.String,
"project": fields.String,
"project_name": fields.String,
"project_fork": fields.String,
"public_ip": fields.String,
"private_ip": fields.String,
# "host_id": fields.String,

View File

@ -2,7 +2,7 @@ import mongoengine as mongo
from settings import common
from common.document import DocumentBase
from common.validator import isalnum
from common.validator import isalnum, is_hex_string
from asset.models import Host
@ -44,9 +44,9 @@ class Version(mongo.EmbeddedDocument):
class Channel(DocumentBase):
"""渠道"""
# spid项目内唯一
project = mongo.ReferenceField(Project, reverse_delete_rule=mongo.NULLIFY)
project_id = mongo.StringField(max_length=128, required=True, validation=is_hex_string)
name = mongo.StringField(max_length=32, default="")
spid = mongo.StringField(max_length=3, min_length=3, required=True, unique_with="project", validation=isalnum)
spid = mongo.StringField(max_length=3, min_length=3, required=True, unique_with="project_id", validation=isalnum)
version = mongo.EmbeddedDocumentField(Version) # 缺失时获取对象的此字段为 None
# 项目的代码仓库地址
@ -64,6 +64,20 @@ class Channel(DocumentBase):
"""渠道 spid 在 CROSS_SPID_SET表示跨服"""
return self.spid in common.CROSS_SPID_SET
@property
def project(self):
if self.project_id:
return Project.objects(id=self.project_id).first()
return None
@project.setter
def project(self, val):
if isinstance(val, str):
assert Project.objects(id=val).first(), f"对象不存在,id={val}"
self.project_id = val
if isinstance(val, Project):
self.project_id = str(val.id)
class Server(DocumentBase):
"""服务"""
@ -77,12 +91,13 @@ class Server(DocumentBase):
"error": "异常"
}
num = mongo.IntField(required=True, unique_with="channel")
channel = mongo.ReferenceField(Channel)
num = mongo.IntField(required=True, unique_with="channel_id")
# 关联channel表保存是一个channel._id的hex字符串
channel_id = mongo.StringField(max_length=128, required=True, validation=is_hex_string)
status = mongo.StringField(max_length=12, choices=STATUS.keys(), required=True, default="running")
# 机器字段TODO 先允许为空
host = mongo.ReferenceField(Host, null=True)
host_id = mongo.StringField(max_length=128, null=True, validation=is_hex_string)
domain = mongo.StringField(max_length=128, required=False, default="")
port = mongo.IntField()
version = mongo.EmbeddedDocumentField(Version)
@ -106,17 +121,38 @@ class Server(DocumentBase):
return ""
@property
def channel_id(self):
if self.channel:
return self.channel.id
def channel(self):
"""非强关联方便取出channel对象"""
if self.channel_id:
return Channel.objects(id=self.channel_id).first()
return ""
@channel.setter
def channel(self, val):
if isinstance(val, str):
assert Channel.objects(id=val).first(), f"对象不存在,id={val}"
self.channel_id = val
if isinstance(val, Channel):
self.channel_id = str(val.id)
@property
def project(self):
if self.channel:
if self.channel and self.channel.project:
return self.channel.project.fullname
return ""
@property
def project_name(self):
if self.channel and self.channel.project:
return self.channel.project.name
return ""
@property
def project_fork(self):
if self.channel and self.channel.project:
return self.channel.project.fork
return ""
@property
def public_ip(self):
if self.host:
@ -130,11 +166,20 @@ class Server(DocumentBase):
return ""
@property
def host_id(self):
if self.host:
return self.host.id
def host(self):
"""非强关联为了取出host对象"""
if self.host_id:
return Host.objects(id=self.host_id).first()
return ""
@host.setter
def host(self, val):
if isinstance(val, str):
assert Host.objects(id=val).first(), f"对象不存在,id={val}"
self.host_id = val
if isinstance(val, Host):
self.host_id = str(val.id)
@property
def is_cross(self):
if self.channel:

View File

@ -65,7 +65,7 @@ class ServerSyncView(CreateMixin):
if count != len(data):
return make_response(400, 1001, "quantity mismatch")
project_id = str(self.project.id)
hosts = {}
channels = {}
errors = []
@ -89,17 +89,17 @@ class ServerSyncView(CreateMixin):
if not created and proj not in hostObj.tags:
hostObj.tags.append(proj)
hostObj.save()
host = hostObj.id
hosts[ip] = hostObj.id
host = str(hostObj.id)
hosts[ip] = host
if not channel:
channelObj, _ = Channel.get_or_create(
project=self.project, spid=spid, defaults=dict(project=self.project, spid=spid))
channel = channelObj.id
channels[spid] = channelObj.id
project_id=project_id, spid=spid, defaults=dict(project_id=project_id, spid=spid))
channel = str(channelObj.id)
channels[spid] = channel
# 更新、创建的参数
defaults = dict(num=num, channel=channel, host=host, version=version, port=port, status=status)
obj, created = Server.update_or_create(defaults=defaults, num=num, channel=channel)
defaults = dict(num=num, channel_id=channel, host_id=host, version=version, port=port, status=status)
obj, created = Server.update_or_create(defaults=defaults, num=num, channel_id=channel)
if created: # 创建
app.logger.debug(f"创建 {spid}_{num} 区服信息成功")
continue
@ -119,6 +119,8 @@ class ServerSyncView(CreateMixin):
class AgentInfo(RetrieveMixin):
"""返回对应机器的所有信息:项目、渠道、区服内容"""
fields = {
# marshal schedule dict
"schedule": F.Raw,
"host": F.Nested(HostFields),
"project": F.List(F.Nested(fields.ProjectFields)),
"channel": F.List(F.Nested(fields.ChannelFields)),
@ -133,11 +135,19 @@ class AgentInfo(RetrieveMixin):
# 找到机器不存在就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]
servers = Server.objects(host_id=str(host.id)).all()
channels = Channel.objects(id__in=servers.values_list("channel_id").distinct("channel_id"))
projects = {channel.project for channel in channels}
# 填充数据
data = {
# TODO schedule 这里的内容可以动态配置,为 minion 增加计划任务
# "schedule": {
# # 如下为刷新pillar自动请求本接口先测试用20s
# "job_refresh": {
# "function": "saltutil.refresh_pillar",
# "seconds": 20,
# }
# },
"host": host,
"project": projects,
"channel": channels,