diff --git a/website/db/enterprise_device/enterprise_device.py b/website/db/enterprise_device/enterprise_device.py index 2de52e0..b128322 100644 --- a/website/db/enterprise_device/enterprise_device.py +++ b/website/db/enterprise_device/enterprise_device.py @@ -216,11 +216,11 @@ class EnterpriseDeviceRepository(object): logging.error("Failed to list devices") raise e device_dicts = [] - for device in devices: + for device, cname in devices: device_dict = { "id": device.id, "name": device.name, - "classification_name": device.classification_name, + "classification_name": cname, } device_dicts.append(device_dict) return device_dicts diff --git a/website/db/enterprise_node/enterprise_node_alert.py b/website/db/enterprise_node/enterprise_node_alert.py index 8f56278..c2ff20b 100644 --- a/website/db/enterprise_node/enterprise_node_alert.py +++ b/website/db/enterprise_node/enterprise_node_alert.py @@ -20,14 +20,14 @@ class EnterpriseNodeAlertRepository(object): return to_json_list(cursor) def get_one( - self, enterprise_suid: str, enterprise_node_id: int + self, entity_suid: str, node_id: int ) -> Union[Dict, None]: with get_session() as session: sql = text( - """SELECT * FROM `enterprise_alert` WHERE `enterprise_suid`=:enterprise_suid and `node_id`=:node_id;""" + """SELECT * FROM `enterprise_alert` WHERE `entity_suid`=:entity_suid and `node_id`=:node_id;""" ) cursor = session.execute( - sql, {"enterprise_suid": enterprise_suid, "node_id": enterprise_node_id} + sql, {"entity_suid": entity_suid, "node_id": node_id} ) return to_json(cursor) @@ -35,11 +35,10 @@ class EnterpriseNodeAlertRepository(object): with get_session() as session: sql = text( """ - INSERT INTO `enterprise_alert` (`enterprise_suid`, `node_id`, `node_suid`, `is_sms`, `sms_to`, `is_email`, `email_to`, `freq`) - VALUES (:enterprise_suid, :node_id, :node_suid, :is_sms, :sms_to, :is_email, :email_to, :freq) + INSERT INTO `enterprise_alert` (`entity_suid`, `node_id`, `node_suid`, `is_sms`, `sms_to`, `is_email`, `email_to`, `freq`) + VALUES (:entity_suid, :node_id, :node_suid, :is_sms, :sms_to, :is_email, :email_to, :freq) ON DUPLICATE KEY UPDATE `node_suid`=:node_suid, `is_sms`=:is_sms, `sms_to`=:sms_to, `is_email`=:is_email, `email_to`=:email_to, `freq`=:freq - WHERE `enterprise_suid` = :enterprise_suid AND `node_id` = :node_id; """ ) session.execute(sql, data) diff --git a/website/db/enterprise_node/enterprise_node_base_model_conf.py b/website/db/enterprise_node/enterprise_node_base_model_conf.py index a39e70c..1097790 100644 --- a/website/db/enterprise_node/enterprise_node_base_model_conf.py +++ b/website/db/enterprise_node/enterprise_node_base_model_conf.py @@ -50,11 +50,12 @@ class EnterpriseNodeDeviceBMCusConfRepository(): session.commit() return - def get_busi_model_custom_config(self, node_id: int, device_id: int, busi_model_id: int): + def get_busi_model_custom_config(self, node_id: int, device_id: int, busi_model_id: int, base_model_id: int): with get_session() as session: data = session.query(EnterpriseNodeDeviceBMCusConf).filter( EnterpriseNodeDeviceBMCusConf.node_id == node_id, EnterpriseNodeDeviceBMCusConf.device_id == device_id, - EnterpriseNodeDeviceBMCusConf.busi_model_id == busi_model_id + EnterpriseNodeDeviceBMCusConf.busi_model_id == busi_model_id, + EnterpriseNodeDeviceBMCusConf.base_model_id == base_model_id ).first() return data \ No newline at end of file diff --git a/website/handler.py b/website/handler.py index c7d69d8..b520865 100644 --- a/website/handler.py +++ b/website/handler.py @@ -2,18 +2,19 @@ import ast import functools import hashlib +import json import logging import re import time import traceback import urllib -import json +from typing import Any # import urlparse from urllib.parse import parse_qs, unquote -from typing import Any -# from urllib import unquote +# from urllib import unquote import tornado +from sqlalchemy import text from tornado import escape from tornado.httpclient import AsyncHTTPClient, HTTPRequest from tornado.options import options @@ -102,6 +103,7 @@ class BaseHandler(BaseRequestHandler): @property def kafka_producer(self): return self.application.kafka_producer + # @property def es(self): @@ -206,8 +208,8 @@ class BaseHandler(BaseRequestHandler): def set_default_jsonbody(self): if self.request.headers.get('Content-Type') == 'application/json' and self.request.body: - # logging.info(self.request.headers.get('Content-Type')) - # if self.request.headers.get('Content-Type') == 'application/json; charset=UTF-8': + # logging.info(self.request.headers.get('Content-Type')) + # if self.request.headers.get('Content-Type') == 'application/json; charset=UTF-8': json_body = tornado.escape.json_decode(self.request.body) for key, value in json_body.items(): if value is not None: @@ -326,6 +328,7 @@ class BaseHandler(BaseRequestHandler): current_pos += len(substring) current_pos = target.find(substring, current_pos) + class WebHandler(BaseHandler): def finish(self, chunk=None, message=None): callback = escape.utf8(self.get_argument("callback", None)) @@ -467,6 +470,7 @@ def authenticated(method): # raise HTTPError(401) raise errors.HTTPAPIError(errors.ERROR_UNAUTHORIZED, "登录失效") return method(self, *args, **kwargs) + return wrapper @@ -481,28 +485,41 @@ def authenticated_admin(method): return wrapper -def operation_log(primary_module, secondary_module, operation_type, content, desc): +def operation_log(primary_menu, sub_menu, ope_type, content, comment): """ Add logging to a function. level is the logging level, name is the logger name, and message is the log message. If name and message aren't specified, they default to the function's module and name. """ - def decorate(func): + def decorate(func): @functools.wraps(func) def wrapper(self, *args, **kwargs): - self.db_app.insert( - "insert into system_log(user, ip, first_module, second_module, op_type, op_content, description) " - "values(%s, %s, %s, %s, %s, %s, %s)", - self.current_user.name, self.request.headers["X-Forwarded-For"], primary_module, secondary_module, operation_type, - content, desc - ) + with self.app_mysql.connect() as conn: + conn.execute(text( + "insert into sys_log(user, ip, primary_menu, sub_menu, op_type, content, comment) " + "values(:user, :ip, :primary_menu, :sub_menu, :op_type, :content, :comment)" + ), + {"user": self.current_user.name, + "ip": self.request.headers[ + "X-Forwarded-For"] if "X-Forwarded-For" in self.request.headers else self.request.remote_ip, + "primary_menu": primary_menu, + "sub_menu": sub_menu, + "op_type": ope_type, + "content": content, + "comment": comment + } + ) + conn.commit() return func(self, *args, **kwargs) + return wrapper + return decorate + def permission(codes): def decorate(func): @functools.wraps(func) @@ -517,9 +534,12 @@ def permission(codes): raise errors.HTTPAPIError(errors.ERROR_FORBIDDEN, "permission denied") return func(self, *args, **kwargs) + return wrapper + return decorate + def license_validate(codes): def decorate(func): @functools.wraps(func) @@ -531,7 +551,8 @@ def license_validate(codes): else: row = self.db_app.get("select syscode, expireat from license limit 1") if row: - self.r_app.set("system:license", json.dumps({"syscode":row["syscode"], "expireat":row["expireat"]})) + self.r_app.set("system:license", + json.dumps({"syscode": row["syscode"], "expireat": row["expireat"]})) license_info = row license_status = get_license_status(license_info) @@ -546,9 +567,12 @@ def license_validate(codes): # raise errors.HTTPAPIError(errors.ERROR_LICENSE_NOT_ACTIVE, "License授权过期") return func(self, *args, **kwargs) + return wrapper + return decorate + def userlog(method): @functools.wraps(method) def wrapper(self, *args, **kwargs): diff --git a/website/handlers/enterprise_device/handler.py b/website/handlers/enterprise_device/handler.py index dd38992..0a60987 100644 --- a/website/handlers/enterprise_device/handler.py +++ b/website/handlers/enterprise_device/handler.py @@ -395,6 +395,9 @@ class DeviceBasemodelListHandler(APIHandler): busi_model_id = item["busi_model_id"] busi_model_name = item["name"] base_model_list = json.loads(item["base_models"]) + logging.info("##############################################################") + logging.info(base_model_list) + logging.info("##############################################################") base_models = [] for base_model in base_model_list: base_model_id = base_model["id"] @@ -404,8 +407,12 @@ class DeviceBasemodelListHandler(APIHandler): base_model = db_alg_model.get_model_dict_by_id(base_model_id) base_model_version = base_model["default_version"] db_conf = DB_NodeBaseModelConf.EnterpriseNodeDeviceBMCusConfRepository() - conf = db_conf.get_busi_model_custom_config(busi_model_id=busi_model_id, device_id=device_id, - node_id=item["node_id"]) + conf = db_conf.get_busi_model_custom_config( + node_id=item["node_id"], + device_id=device_id, + busi_model_id=busi_model_id, + base_model_id=base_model_id + ) base_model_hub_image = "" if conf: base_model_hub_image = conf.model_hub_image diff --git a/website/handlers/enterprise_node/handler.py b/website/handlers/enterprise_node/handler.py index 5c97fcb..b260194 100644 --- a/website/handlers/enterprise_node/handler.py +++ b/website/handlers/enterprise_node/handler.py @@ -259,7 +259,7 @@ class BusimodelHandler(APIHandler): self.finish({"count": count, "data": models}) -class BusimodelInfoHandler(APIHandler): +class BusimodelInfoHandler(APIHandler): """ - 描述:企业节点,业务模型部署 -> 业务模型信息 - 请求方式:post @@ -364,7 +364,7 @@ class BusimodelDeployHandler(APIHandler): records.append( { - "suid": shortuuid.ShortUUID.random(length=10), + "suid": shortuuid.ShortUUID().random(length=10), "entity_suid": entity_suid_row["entity_suid"], "node_id": node_id, "node_suid": node_suid, diff --git a/website/handlers/enterprise_server/handler.py b/website/handlers/enterprise_server/handler.py index e7b459e..2067bce 100644 --- a/website/handlers/enterprise_server/handler.py +++ b/website/handlers/enterprise_server/handler.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +import logging import random from sqlalchemy import text @@ -7,7 +8,7 @@ from website import errors from website import settings from website.db.enterprise_device.enterprise_device import EnterpriseEntityRepository as EntRepo from website.db_mysql import to_json_list, to_json -from website.handler import APIHandler, authenticated +from website.handler import APIHandler, authenticated, operation_log from website.util import aes from website.util import shortuuid @@ -28,6 +29,7 @@ class AddHandler(APIHandler): """ @authenticated + @operation_log("企业项目", "服务器管理", "新建", "新建服务器", "") def post(self): entity_id = self.get_int_argument('entity_id') name = self.get_escaped_argument('name', '') @@ -66,6 +68,34 @@ class AddHandler(APIHandler): self.finish() +class EditHandler(APIHandler): + @authenticated + @operation_log("企业项目", "服务器管理", "编辑", "编辑服务器", "") + def post(self): + server_id = self.get_int_argument("id") + name = self.get_escaped_argument('name', '') + ip = self.get_escaped_argument('ip', '') + port = self.get_int_argument("port", 22) + username = self.get_escaped_argument('username', '') + passwd = self.get_escaped_argument('passwd', '') + with self.app_mysql.connect() as conn: + conn.execute( + text( + "update enterprise_server " + "set name=:name, ip=:ip, port=:port, username=:username, passwd=:passwd where id=:id" + ), { + "id": server_id, + "name": name, + "ip": ip, + "port": port, + "username": username, + "passwd": aes.encrypt(settings.enterprise_aes_key, passwd) + }) + conn.commit() + + self.finish() + + class ListHandler(APIHandler): """ - 描述:服务器列表 @@ -96,6 +126,7 @@ class ListHandler(APIHandler): """ @authenticated + @operation_log("企业项目", "服务器管理", "查询", "查询服务器列表", "") def post(self): entity_id = self.get_int_argument('entity_id') status = self.get_int_argument("status") @@ -118,7 +149,12 @@ class ListHandler(APIHandler): p.update({"status": status}) count = conn.scalar(text(sql_count), p) - data = conn.execute(text(sql_data), p).limit(pageSize).offset((pageNo - 1) * pageSize) + + sql_data += " order by id desc limit :pageSize offset :offset" + p.update({"offset": (pageNo - 1) * pageSize, "pageSize": pageSize}) + + data = conn.execute(text(sql_data), p) + data = to_json_list(data) status_dic = {1001: 0, 1002: 0} sql_status = "select status, count(id) c from enterprise_server where entity_id=:entity_id group by status" @@ -126,7 +162,8 @@ class ListHandler(APIHandler): status_list = to_json_list(status_cur) for item in status_list: status_dic[item["status"]] = item["c"] - + logging.info("####################### ") + logging.info(data) for item in data: item.update( {"cpu": random.randint(20, 30), @@ -134,7 +171,7 @@ class ListHandler(APIHandler): "storage": random.randint(20, 30), "gpu": random.randint(20, 30), }) - self.finish({"count": count, "data": to_json_list(data), "status": status_dic}) + self.finish({"count": count, "data": data, "status": status_dic}) class InfoHandler(APIHandler): @@ -159,6 +196,7 @@ class InfoHandler(APIHandler): """ @authenticated + @operation_log("企业项目", "服务器管理", "查询", "查询服务器信息", "") def post(self): server_id = self.get_int_argument('server_id') with self.app_mysql.connect() as conn: @@ -177,6 +215,7 @@ class InfoHandler(APIHandler): class DeleteHandler(APIHandler): @authenticated + @operation_log("企业项目", "服务器管理", "删除", "删除服务器", "") def post(self): server_id = self.get_int_argument('server_id') with self.app_mysql.connect() as conn: @@ -211,8 +250,11 @@ class LogHandler(APIHandler): p.update({"entity_id": entity_id}) count = conn.scalar(text(sql_count), p) - data = conn.execute(text(sql_data), p).order_by(text("id desc")).limit(pageSize).offset( - (pageNo - 1) * pageSize) + + sql_data += " order by id desc limit :pageSize offset :offset" + p.update({"offset": (pageNo - 1) * pageSize, "pageSize": pageSize}) + + data = conn.execute(text(sql_data), p) data = to_json_list(data) self.finish({"count": count, "data": data}) diff --git a/website/handlers/enterprise_server/url.py b/website/handlers/enterprise_server/url.py index 8c9b554..0737bd7 100644 --- a/website/handlers/enterprise_server/url.py +++ b/website/handlers/enterprise_server/url.py @@ -4,6 +4,7 @@ from website.handlers.enterprise_server import handler handlers = [ ("/enterprise/server/add", handler.AddHandler), + ("/enterprise/server/edit", handler.EditHandler), ("/enterprise/server/list", handler.ListHandler), ("/enterprise/server/info", handler.InfoHandler), ("/enterprise/server/log", handler.LogHandler), diff --git a/website/handlers/system/handler.py b/website/handlers/system/handler.py index 5406fb6..750cb92 100644 --- a/website/handlers/system/handler.py +++ b/website/handlers/system/handler.py @@ -13,7 +13,9 @@ import datetime from website import errors from website import settings from website.handler import APIHandler, WebHandler, authenticated, operation_log, permission +from website.db_mysql import to_json_list from website.util import sysinfo, rsa +from sqlalchemy import text class VersionHandler(APIHandler): @@ -125,3 +127,41 @@ class InfoHandler(APIHandler): def post(self): self.finish() + + +class LogHandler(APIHandler): + @authenticated + def post(self): + user = self.get_escaped_argument("user", "") + start = self.get_escaped_argument("startDate", "") + end = self.get_escaped_argument("endDate", "") + pageNo = self.get_int_argument("pageNo", 1) + pageSize = self.get_int_argument("pageSize", 20) + + users = user.split(",") + with self.app_mysql.connect() as conn: + sql = "select user, ip, content, op_type, content from sys_log where 1=1" + sql_count = "select count(*) from sys_log where 1=1" + p = {} + if users: + sql += " and user in :users" + sql_count += " and user in :users" + p["users"] = users + if start: + sql += " and date_format(create_time, '%Y-%m-%d') >= :start" + sql_count += " and date_format(create_time, '%Y-%m-%d') >= :start" + p["start"] = start + if end: + sql += " and date_format(create_time, '%Y-%m-%d') <= :end" + sql_count += " and date_format(create_time, '%Y-%m-%d') <= :end" + p["end"] = end + + count = conn.scalar(text(sql_count), p) + + sql += " order by create_time desc limit :pageNo, :pageSize" + p["pageNo"] = (pageNo - 1) * pageSize + p["pageSize"] = pageSize + res = conn.execute(text(sql), p) + data = to_json_list(res) + + self.finish({"count": count, "data": data}) \ No newline at end of file diff --git a/website/handlers/system/url.py b/website/handlers/system/url.py index c1bf531..7cd350b 100644 --- a/website/handlers/system/url.py +++ b/website/handlers/system/url.py @@ -8,6 +8,8 @@ handlers = [ ("/system/license/upload", handler.LicenseUploadHandler), ("/system/activate/info", handler.ActivateInfoHandler), ("/system/info", handler.InfoHandler), + + ("/system/log", handler.LogHandler), ] page_handlers = [ diff --git a/website/handlers/user/handler.py b/website/handlers/user/handler.py index 36a9064..2c4ed98 100644 --- a/website/handlers/user/handler.py +++ b/website/handlers/user/handler.py @@ -1,25 +1,19 @@ # -*- coding: utf-8 -*- -import logging -import json import base64 -import tornado.web +import json +import logging import uuid -import time -import datetime -import itertools -from io import StringIO, BytesIO +from io import BytesIO + +from sqlalchemy import text +from website import db_mysql from website import errors from website import settings -from website import db_mysql -from website import consts -from website.handler import APIHandler +from website.handler import APIHandler, authenticated from website.util import aes -from website.util.captcha import create_validate_code -from website.service.license import get_license_status from website.util import shortuuid -from sqlalchemy import text -import tornado.escape +from website.util.captcha import create_validate_code class CaptchaHandler(APIHandler): @@ -40,6 +34,7 @@ class CaptchaHandler(APIHandler): # output.close() self.finish({"token": self.tostr(token), "captcha": self.tostr(img_b64)}) + class LogoutHandler(APIHandler): def get(self): if self.current_user: @@ -52,6 +47,7 @@ class LogoutHandler(APIHandler): self.r_app.delete(settings.session_key_prefix % self.current_user.uuid) self.finish() + class LoginHandler(APIHandler): def post(self): suid = shortuuid.ShortUUID().random(10) @@ -93,7 +89,6 @@ class LoginHandler(APIHandler): # if self.tostr(captcha).lower() != self.tostr(code).lower(): # raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "验证码错误") - username = self.tostr(username) password = self.tostr(password) @@ -182,3 +177,17 @@ class UserInfoHandler(APIHandler): raise errors.HTTPAPIError(errors.ERROR_UNAUTHORIZED) self.finish({"name": user.name, "role": user.role}) + + +class UserListHandler(APIHandler): + @authenticated + def post(self): + with self.app_mysql.connect() as conn: + cur = conn.execute( + text( + "select name from sys_user" + ) + ) + res = db_mysql.to_json_list(cur) + names = [row["name"] for row in res] + self.finish({"data": names}) diff --git a/website/handlers/user/url.py b/website/handlers/user/url.py index 530c4a7..9743e31 100644 --- a/website/handlers/user/url.py +++ b/website/handlers/user/url.py @@ -8,6 +8,7 @@ handlers = [ # ("/user/info", handler.UserInfoHandler), ("/login", handler.LoginHandler), ("/logout", handler.LogoutHandler), + ("/users", handler.UserListHandler), ] page_handlers = [