parent
5b03e4add4
commit
a351fc500b
|
@ -53,6 +53,15 @@ LOGGING = {
|
||||||
'backupCount': 5,
|
'backupCount': 5,
|
||||||
'formatter': 'verbose',
|
'formatter': 'verbose',
|
||||||
},
|
},
|
||||||
|
'views': {
|
||||||
|
'level': 'DEBUG',
|
||||||
|
'class': 'logging.handlers.RotatingFileHandler',
|
||||||
|
'filename': os.path.join(os.path.dirname(BASE_DIR), 'logs/views.log'),
|
||||||
|
'encoding': 'utf-8',
|
||||||
|
'maxBytes': 1024 * 1024 * 5,
|
||||||
|
'backupCount': 5,
|
||||||
|
'formatter': 'verbose',
|
||||||
|
},
|
||||||
},
|
},
|
||||||
'loggers': {
|
'loggers': {
|
||||||
'root': {
|
'root': {
|
||||||
|
@ -64,6 +73,11 @@ LOGGING = {
|
||||||
'handlers': ['common'],
|
'handlers': ['common'],
|
||||||
'level': 'DEBUG',
|
'level': 'DEBUG',
|
||||||
'propagate': False,
|
'propagate': False,
|
||||||
|
},
|
||||||
|
'views': { # 视图的日志
|
||||||
|
'handlers': ['views'],
|
||||||
|
'level': 'DEBUG',
|
||||||
|
'propagate': False,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,6 +13,9 @@ api.add_resource(views.HostDetailViews, '/host/<string:pk>/', endpoint="host-det
|
||||||
|
|
||||||
api.add_resource(views.MySQLInstanceViews, '/database/mysql/', endpoint="db-mysql")
|
api.add_resource(views.MySQLInstanceViews, '/database/mysql/', endpoint="db-mysql")
|
||||||
api.add_resource(views.MySQLInstanceDetail, '/database/mysql/<string:pk>/', endpoint="db-mysql-detail")
|
api.add_resource(views.MySQLInstanceDetail, '/database/mysql/<string:pk>/', endpoint="db-mysql-detail")
|
||||||
|
# 数据实例中的库详情
|
||||||
|
api.add_resource(views.DatabaseViews, '/database/<string:pk>/db/', endpoint="db-database")
|
||||||
|
api.add_resource(views.DatabaseDetailViews, '/database/<string:pk>/db/<string:db>/', endpoint="db-database-detail")
|
||||||
|
|
||||||
api.add_resource(views.RedisInstanceViews, '/database/redis/', endpoint="db-redis")
|
api.add_resource(views.RedisInstanceViews, '/database/redis/', endpoint="db-redis")
|
||||||
api.add_resource(views.RedisInstanceDetail, '/database/redis/<string:pk>/', endpoint="db-redis-detail")
|
api.add_resource(views.RedisInstanceDetail, '/database/redis/<string:pk>/', endpoint="db-redis-detail")
|
||||||
|
|
|
@ -1,10 +1,13 @@
|
||||||
|
import logging
|
||||||
from typing import List, Dict
|
from typing import List, Dict
|
||||||
|
|
||||||
from flask import request, current_app as app
|
from flask import request
|
||||||
from flask_restful import abort, Resource, marshal, fields, reqparse
|
from flask_restful import abort, Resource, marshal, fields, reqparse
|
||||||
|
|
||||||
from common.utils import abort_response
|
from common.utils import abort_response
|
||||||
|
|
||||||
|
logger = logging.getLogger("views")
|
||||||
|
|
||||||
|
|
||||||
def parse_uniq_fields(args, many_field) -> List[Dict]:
|
def parse_uniq_fields(args, many_field) -> List[Dict]:
|
||||||
""" 注意,请再请求解析中判断必须的字段!!!
|
""" 注意,请再请求解析中判断必须的字段!!!
|
||||||
|
@ -132,7 +135,7 @@ class ModelViewBase(Resource):
|
||||||
assert int(val, 16), f"{key} 不合法"
|
assert int(val, 16), f"{key} 不合法"
|
||||||
assert model.objects(**{pk: val}).first(), f"{key} 不存在"
|
assert model.objects(**{pk: val}).first(), f"{key} 不存在"
|
||||||
except (AssertionError, ValueError, TypeError):
|
except (AssertionError, ValueError, TypeError):
|
||||||
app.logger.exception(f"{key} 异常")
|
logger.exception(f"{key} 异常")
|
||||||
abort_response(400, 1002, msg=f"{key} 不存在")
|
abort_response(400, 1002, msg=f"{key} 不存在")
|
||||||
|
|
||||||
def validate_relation_fields(self, args):
|
def validate_relation_fields(self, args):
|
||||||
|
@ -190,7 +193,7 @@ class ListMixin(ModelViewBase):
|
||||||
except:
|
except:
|
||||||
# 返回空 queryset
|
# 返回空 queryset
|
||||||
queryset = self.model.objects.none()
|
queryset = self.model.objects.none()
|
||||||
app.logger.exception(f"查询出错 {self.model} query_params={query_params}")
|
logger.exception(f"查询出错 {self.model} query_params={query_params}")
|
||||||
return queryset
|
return queryset
|
||||||
|
|
||||||
def get(self):
|
def get(self):
|
||||||
|
@ -216,7 +219,7 @@ class CreateMixin(ModelViewBase):
|
||||||
"""创建前钩子,接收参数,可以对参数进行处理,最后保存此方法返回的数据"""
|
"""创建前钩子,接收参数,可以对参数进行处理,最后保存此方法返回的数据"""
|
||||||
return args
|
return args
|
||||||
|
|
||||||
def post(self):
|
def post(self, *args, **kwargs):
|
||||||
"""创建对象"""
|
"""创建对象"""
|
||||||
|
|
||||||
assert self.request_parse is not None, "缺少校验规则"
|
assert self.request_parse is not None, "缺少校验规则"
|
||||||
|
@ -239,7 +242,7 @@ class CreateMixin(ModelViewBase):
|
||||||
obj = self.model(**validated_data)
|
obj = self.model(**validated_data)
|
||||||
obj.save()
|
obj.save()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
app.logger.exception(f"{self.model} 创建对象失败! data={args}")
|
logger.exception(f"{self.model} 创建对象失败! data={args}")
|
||||||
abort_response(500, 1500, msg=f"保存对象失败!{str(e)}")
|
abort_response(500, 1500, msg=f"保存对象失败!{str(e)}")
|
||||||
return
|
return
|
||||||
# 返回创建信息
|
# 返回创建信息
|
||||||
|
@ -285,7 +288,7 @@ class UpdateMixin(ModelViewBase):
|
||||||
# 重新读取数据
|
# 重新读取数据
|
||||||
obj.reload()
|
obj.reload()
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
app.logger.exception(f"{self.model} 保存对象失败!pk={pk} data={args}")
|
logger.exception(f"{self.model} 保存对象失败!pk={pk} data={args}")
|
||||||
abort_response(500, 1500, msg=f"保存对象失败!{str(e)}")
|
abort_response(500, 1500, msg=f"保存对象失败!{str(e)}")
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
|
@ -44,6 +44,19 @@ class DatabaseServerParse:
|
||||||
self.request_parse.add_argument("labels", required=False, type=dict, location='json')
|
self.request_parse.add_argument("labels", required=False, type=dict, location='json')
|
||||||
|
|
||||||
|
|
||||||
|
class DatabaseParse:
|
||||||
|
request_parse = None
|
||||||
|
|
||||||
|
def init_parse(self):
|
||||||
|
self.request_parse = reqparse.RequestParser()
|
||||||
|
self.request_parse.add_argument("username", type=str, location='json')
|
||||||
|
self.request_parse.add_argument("password", type=str, location='json')
|
||||||
|
|
||||||
|
self.request_parse.add_argument("data", required=False, type=dict, location='json')
|
||||||
|
self.request_parse.add_argument("tags", required=False, type=list, location='json')
|
||||||
|
self.request_parse.add_argument("labels", required=False, type=dict, location='json')
|
||||||
|
|
||||||
|
|
||||||
class MiddlewareParse:
|
class MiddlewareParse:
|
||||||
request_parse = None
|
request_parse = None
|
||||||
# 给子类用的唯一字段,用于校验
|
# 给子类用的唯一字段,用于校验
|
||||||
|
|
|
@ -1,12 +1,18 @@
|
||||||
import datetime
|
import datetime
|
||||||
|
import logging
|
||||||
|
|
||||||
from flask_restful import reqparse
|
from flask_restful import reqparse, marshal, Resource
|
||||||
|
|
||||||
from models.asset import fields as assetField
|
from models.asset import fields as assetField
|
||||||
from models.asset import models as assetModel
|
from models.asset import models as assetModel
|
||||||
from common.views import ListCreateViewSet, DetailViewSet
|
from common.views import ListCreateViewSet, DetailViewSet, CreateMixin, UpdateMixin, DestroyMixin
|
||||||
from common.permission import session_or_token_required
|
from common.permission import session_or_token_required
|
||||||
|
from common.utils import abort_response
|
||||||
from controller.asset import parsers
|
from controller.asset import parsers
|
||||||
|
from common.crypto import quick_crypto
|
||||||
|
from settings.common import SECRET_KEY
|
||||||
|
|
||||||
|
logger = logging.getLogger("views")
|
||||||
|
|
||||||
|
|
||||||
class HostViews(parsers.HostParse, ListCreateViewSet):
|
class HostViews(parsers.HostParse, ListCreateViewSet):
|
||||||
|
@ -37,7 +43,8 @@ class MySQLInstanceViews(parsers.DatabaseServerParse, ListCreateViewSet):
|
||||||
model = assetModel.MySQLInstance
|
model = assetModel.MySQLInstance
|
||||||
fields = assetField.MySQLInstanceFields
|
fields = assetField.MySQLInstanceFields
|
||||||
method_decorators = [session_or_token_required]
|
method_decorators = [session_or_token_required]
|
||||||
filter_fields = (("name", "icontains"), ("host", "icontains"), ("manage", ""), ("tags", ""))
|
filter_fields = (("name", "icontains"), ("host", "icontains"), ("manage", ""), ("tags", ""),
|
||||||
|
("databases__name", ""), )
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
self.init_parse()
|
self.init_parse()
|
||||||
|
@ -53,6 +60,14 @@ class MySQLInstanceViews(parsers.DatabaseServerParse, ListCreateViewSet):
|
||||||
self.request_parse.add_argument("core", required=True, type=int, location='json')
|
self.request_parse.add_argument("core", required=True, type=int, location='json')
|
||||||
super(MySQLInstanceViews, self).__init__()
|
super(MySQLInstanceViews, self).__init__()
|
||||||
|
|
||||||
|
def pre_create(self, args):
|
||||||
|
"""加密保存密码,若提交了密码,必须加密存储"""
|
||||||
|
args = super().pre_create(args)
|
||||||
|
if "password" in args:
|
||||||
|
password = args.get("password")
|
||||||
|
args["password"] = quick_crypto(password)
|
||||||
|
return args
|
||||||
|
|
||||||
|
|
||||||
class MySQLInstanceDetail(parsers.DatabaseServerParse, DetailViewSet):
|
class MySQLInstanceDetail(parsers.DatabaseServerParse, DetailViewSet):
|
||||||
model = assetModel.MySQLInstance
|
model = assetModel.MySQLInstance
|
||||||
|
@ -74,6 +89,112 @@ class MySQLInstanceDetail(parsers.DatabaseServerParse, DetailViewSet):
|
||||||
self.request_parse.add_argument("core", required=False, type=int, location='json')
|
self.request_parse.add_argument("core", required=False, type=int, location='json')
|
||||||
super(MySQLInstanceDetail, self).__init__()
|
super(MySQLInstanceDetail, self).__init__()
|
||||||
|
|
||||||
|
def pre_update(self, obj: assetModel.MySQLInstance, args: dict):
|
||||||
|
"""加密保存密码,若提交了密码,必须加密存储"""
|
||||||
|
data = super().pre_update(obj, args)
|
||||||
|
if "password" in data:
|
||||||
|
password = data.get("password")
|
||||||
|
if password != obj.password:
|
||||||
|
data["password"] = quick_crypto(password)
|
||||||
|
return data
|
||||||
|
|
||||||
|
|
||||||
|
class DatabaseViews(parsers.DatabaseParse, Resource):
|
||||||
|
model = assetModel.DatabaseServer
|
||||||
|
db_model = assetModel.Database
|
||||||
|
db_fields = assetField.DatabaseFields
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.init_parse()
|
||||||
|
self.request_parse.add_argument("name", type=str, location='json', required=True)
|
||||||
|
|
||||||
|
def post(self, pk):
|
||||||
|
"""创建实例中的内嵌数据库对象"""
|
||||||
|
# 解析参数
|
||||||
|
args = self.request_parse.parse_args()
|
||||||
|
|
||||||
|
# 找到数据库服务器示例,传入的pk是数据库实例的id
|
||||||
|
try:
|
||||||
|
db_server = assetModel.DatabaseServer.objects(id=pk).first_or_404(message="db instance not found.")
|
||||||
|
except:
|
||||||
|
abort_response(404, code=1404, msg="db instance not found.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 库名不能重复
|
||||||
|
name = args.get("name")
|
||||||
|
exists = db_server.databases.filter(name=name).first()
|
||||||
|
if exists:
|
||||||
|
abort_response(400, 1400, msg=f"数据库{name}在实例中已存在!")
|
||||||
|
|
||||||
|
# 加密存储密码
|
||||||
|
if "password" in args:
|
||||||
|
password = args["password"]
|
||||||
|
if password:
|
||||||
|
args["password"] = quick_crypto(password)
|
||||||
|
|
||||||
|
# 保存对象,追加到实例的数据库列表中
|
||||||
|
try:
|
||||||
|
obj = self.db_model(**args)
|
||||||
|
db_server.databases.append(obj)
|
||||||
|
db_server.save()
|
||||||
|
except Exception as e:
|
||||||
|
logger.exception(f"{self.db_model} 创建对象失败! data={args}")
|
||||||
|
abort_response(500, 1500, msg=f"保存对象失败!{str(e)}")
|
||||||
|
return
|
||||||
|
# 返回创建信息
|
||||||
|
return marshal(obj, self.db_fields)
|
||||||
|
|
||||||
|
|
||||||
|
class DatabaseDetailViews(parsers.DatabaseParse, Resource):
|
||||||
|
model = assetModel.DatabaseServer
|
||||||
|
db_model = assetModel.Database
|
||||||
|
db_fields = assetField.DatabaseFields
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.init_parse()
|
||||||
|
self.request_parse.add_argument("name", type=str, location='json', required=True)
|
||||||
|
|
||||||
|
def put(self, pk, db):
|
||||||
|
"""创建实例中的内嵌数据库对象"""
|
||||||
|
# 解析参数
|
||||||
|
args = self.request_parse.parse_args()
|
||||||
|
# 找到数据库服务器示例,传入的pk是数据库实例的id,db为内嵌数据库列表的对象id
|
||||||
|
try:
|
||||||
|
db_server = assetModel.DatabaseServer.objects(id=pk).first_or_404(message="db instance not found.")
|
||||||
|
db_obj = db_server.databases.filter(id=db).first()
|
||||||
|
assert db_obj
|
||||||
|
except Exception as e:
|
||||||
|
print(e)
|
||||||
|
abort_response(404, code=1404, msg="db not found.")
|
||||||
|
return
|
||||||
|
name = args.get("name")
|
||||||
|
if name:
|
||||||
|
exists = db_server.databases.filter(name=name).first()
|
||||||
|
if exists and exists.id != db_obj.id:
|
||||||
|
abort_response(400, 1400, msg=f"数据库{name}在实例中已存在!")
|
||||||
|
|
||||||
|
# 若更新密码,重新加密保存
|
||||||
|
if "password" in args:
|
||||||
|
password = args["password"]
|
||||||
|
if password != db_obj.password:
|
||||||
|
args["password"] = quick_crypto(password)
|
||||||
|
# 去除id
|
||||||
|
args.pop("id", "")
|
||||||
|
|
||||||
|
# 更新内嵌对象
|
||||||
|
try:
|
||||||
|
db_server.databases.filter(id=db).update(**args)
|
||||||
|
db_server.save()
|
||||||
|
db_server.reload()
|
||||||
|
except Exception as e:
|
||||||
|
logger.exception(f"{self.db_model} 更新对象失败! data={args}")
|
||||||
|
abort_response(500, 1500, msg=f"更新对象失败!{str(e)}")
|
||||||
|
return
|
||||||
|
# 重载数据
|
||||||
|
db_obj = db_server.databases.filter(id=db).first()
|
||||||
|
# 返回信息
|
||||||
|
return marshal(db_obj, self.db_fields)
|
||||||
|
|
||||||
|
|
||||||
class RedisInstanceViews(parsers.DatabaseServerParse, ListCreateViewSet):
|
class RedisInstanceViews(parsers.DatabaseServerParse, ListCreateViewSet):
|
||||||
model = assetModel.RedisInstance
|
model = assetModel.RedisInstance
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
|
import logging
|
||||||
import datetime
|
import datetime
|
||||||
|
|
||||||
from flask import current_app as app
|
|
||||||
from flask_restful import Resource, reqparse, marshal, fields as F
|
from flask_restful import Resource, reqparse, marshal, fields as F
|
||||||
|
|
||||||
from models.asset.models import Host
|
from models.asset.models import Host
|
||||||
|
@ -12,6 +12,8 @@ from common.permission import token_header_required
|
||||||
from common.utils import make_response
|
from common.utils import make_response
|
||||||
from service.project.sync import sync_project_for_ops1
|
from service.project.sync import sync_project_for_ops1
|
||||||
|
|
||||||
|
logger = logging.getLogger("views")
|
||||||
|
|
||||||
|
|
||||||
class ProjectSyncView(Resource):
|
class ProjectSyncView(Resource):
|
||||||
"""从 OPS1 同步项目,触发接口"""
|
"""从 OPS1 同步项目,触发接口"""
|
||||||
|
@ -112,12 +114,12 @@ class ServerSyncView(CreateMixin):
|
||||||
defaults = dict(num=num, channel_id=channel, host_id=host, version=version, port=port, status=status)
|
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)
|
obj, created = Server.update_or_create(defaults=defaults, num=num, channel_id=channel)
|
||||||
if created: # 创建
|
if created: # 创建
|
||||||
app.logger.debug(f"创建 {spid}_{num} 区服信息成功")
|
logger.debug(f"创建 {spid}_{num} 区服信息成功")
|
||||||
continue
|
continue
|
||||||
# 更新对象日志
|
# 更新对象日志
|
||||||
app.logger.debug(f"更新 {spid}_{num} 区服信息成功")
|
logger.debug(f"更新 {spid}_{num} 区服信息成功")
|
||||||
except:
|
except:
|
||||||
app.logger.exception("同步区服出错")
|
logger.exception("同步区服出错")
|
||||||
errors.append(f"{spid}_s{num}")
|
errors.append(f"{spid}_s{num}")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
|
|
@ -43,6 +43,9 @@ DatabaseFields = {
|
||||||
"name": fields.String,
|
"name": fields.String,
|
||||||
"username": fields.String,
|
"username": fields.String,
|
||||||
"password": fields.String,
|
"password": fields.String,
|
||||||
|
"data": fields.Raw,
|
||||||
|
"tags": fields.List(fields.String),
|
||||||
|
"labels": fields.Raw,
|
||||||
}
|
}
|
||||||
|
|
||||||
MySQLInstanceFields = {
|
MySQLInstanceFields = {
|
||||||
|
|
|
@ -52,6 +52,9 @@ class Database(mongo.EmbeddedDocument):
|
||||||
name = mongo.StringField(required=True)
|
name = mongo.StringField(required=True)
|
||||||
username = mongo.StringField(default="")
|
username = mongo.StringField(default="")
|
||||||
password = mongo.StringField(default="")
|
password = mongo.StringField(default="")
|
||||||
|
data = mongo.DictField(default=dict)
|
||||||
|
tags = mongo.ListField(mongo.StringField(), default=list) # tags 默认是空列表
|
||||||
|
labels = mongo.DictField(default=dict)
|
||||||
|
|
||||||
|
|
||||||
class MySQLInstance(DatabaseServer):
|
class MySQLInstance(DatabaseServer):
|
||||||
|
|
Loading…
Reference in New Issue