# -*- coding: utf-8 -*- import json import logging import random from sqlalchemy import text from website import consts from website import db_mysql, errors from website.db.alg_model.alg_model import ModelRepositry as DB_AlgModel from website.db.device_classification import device_classification as DB_DeviceClassification from website.db.enterprise_busi_model import ( enterprise_busi_model as DB_BusiModel, ) from website.db.enterprise_busi_model import enterprise_busi_model_node_device as DB_BusiModelNodeDevice from website.db.enterprise_device import enterprise_device as DB_Device from website.db.enterprise_node import enterprise_node as DB_Node from website.db.enterprise_node import enterprise_node_base_model_conf as DB_NodeBaseModelConf from website.handler import APIHandler, authenticated, operation_log from website.util import date_util from website.util import shortuuid class DeviceClassificationAddHandler(APIHandler): """ - 描述:添加设备分类 - 请求方式:post - 请求参数: >- entity_id, int, 企业id >- name, string, 设备分类名称 - 返回值: ``` { "id": 123 } ``` """ @authenticated @operation_log("企业管理", "设备管理", consts.op_type_add_str, "添加设备分类") def post(self): name = self.get_escaped_argument("name", "") with self.app_mysql.connect() as conn: cur = conn.execute( text("SELECT id FROM device_classification WHERE name=:name and del=0"), {"name": name}, ) row = cur.fetchone() if row: raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "设备分类已存在") result = conn.execute( text( "INSERT INTO device_classification (name, suid) VALUES (:name, :suid)" ), {"name": name, "suid": shortuuid.ShortUUID().random(10)}, ) conn.commit() last_id = result.lastrowid self.finish({"id": last_id}) class DeviceClassificationHandler(APIHandler): """ - 描述:设备分类列表 - 请求方式:post - 请求参数: >- 无 - 返回值: ``` { "data": [ { "id": 123, "suid": "xxx", "name": "xxx" }, ... ] } ``` """ @authenticated @operation_log("企业管理", "设备管理", consts.op_type_list_str, "设备分类列表") def post(self): with self.app_mysql.connect() as conn: cur = conn.execute( text( """ SELECT id, suid, name, CASE WHEN EXISTS ( SELECT 1 FROM enterprise_device WHERE classification = device_classification.suid ) THEN '1' ELSE '0' END AS used FROM device_classification where del=0 ORDER BY id DESC """ ) ) res = db_mysql.to_json_list(cur) res = res and res or [] self.finish({"data": res}) class DeviceClassificationDeleteHandler(APIHandler): """ - 描述:删除设备分类 - 请求方式:post - 请求参数: >- id - 返回值:无 """ @authenticated @operation_log("企业管理", "设备管理", consts.op_type_delete_str, "删除设备分类") def post(self): did = self.get_int_argument("id") with self.app_mysql.connect() as conn: cur = conn.execute( text( """ select d.id from enterprise_device d, device_classification c where d.classification=c.suid and c.id=:id and c.del=0 """ ), {"id": did}, ) rows = cur.fetchall() if rows: raise errors.HTTPAPIError( errors.ERROR_BAD_REQUEST, "该分类使用中,无法删除" ) conn.execute( text("update device_classification set del=1 WHERE id=:id"), {"id": did} ) conn.commit() self.finish() class DeviceClassificationEditHandler(APIHandler): """ - 描述:编辑设备分类 - 请求方式:post - 请求参数: >- id >- name - 返回值:无 """ @authenticated @operation_log("企业管理", "设备管理", consts.op_type_edit_str, "编辑设备分类") def post(self): did = self.get_int_argument("id") name = self.get_escaped_argument("name", "") with self.app_mysql.connect() as conn: conn.execute( text("update device_classification set name=:name WHERE id=:id"), {"id": did, "name": name}, ) conn.commit() self.finish() class DeviceAddHandler(APIHandler): """ - 描述:企业节点,添加设备 - 请求方式:post - 请求参数: >- entity_id, int, 企业id >- node_id, int >- name, string, 设备名称 >- addr, string, 设备位置 >- classification, string, 设备分类 >- device_model, string, 设备型号 >- param, string, 设备参数 >- comment, string, 备注 - 返回值: ``` 无 ``` """ @authenticated @operation_log("企业管理", "设备管理", consts.op_type_add_str, "企业节点,添加设备") def post(self): entity_id = self.get_int_argument("entity_id") node_id = self.get_int_argument("node_id") name = self.get_escaped_argument("name", "") addr = self.get_escaped_argument("addr", "") classification = self.get_escaped_argument("classification", "") device_model = self.get_escaped_argument("device_model", "") param = self.get_escaped_argument("param", "") comment = self.get_escaped_argument("comment", "") if not name: raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "设备名称不能为空") if not entity_id or not node_id: raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "企业节点不能为空") # 判断设备分类是否存在 db_classification = DB_DeviceClassification.DeviceClassificationReporitory() row = db_classification.get_row_by_suid(classification) if not row: raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "设备分类不存在") device_data = { "entity_id": entity_id, "node_id": node_id, "name": name, "addr": addr, "classification": classification, "device_model": device_model, "param": param, "comment": comment, } db_device = DB_Device.EnterpriseDeviceRepository() db_device.add_device(device_data) self.finish() class DeviceEditHandler(APIHandler): """ - 描述:企业节点,编辑设备 - 请求方式:post - 请求参数: >- device_id, int, 设备id >- name, string, 设备名称 >- addr, string, 设备位置 >- classification, string, 设备分类的short uuid >- device_model, string, 设备型号 >- param, string, 设备参数 >- comment, string, 备注 - 返回值:无 """ @authenticated @operation_log("企业管理", "设备管理", consts.op_type_edit_str, "企业节点,编辑设备") def post(self): device_id = self.get_int_argument("device_id") name = self.get_escaped_argument("name", "") addr = self.get_escaped_argument("addr", "") classification = self.get_escaped_argument("classification", "") device_model = self.get_escaped_argument("device_model", "") param = self.get_escaped_argument("param", "") comment = self.get_escaped_argument("comment", "") if not device_id: raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "设备id不能为空") if not name: raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "设备名称不能为空") device_data = { "id": device_id, "name": name, "addr": addr, "classification": classification, "device_model": device_model, "param": param, "comment": comment, } db_device = DB_Device.EnterpriseDeviceRepository() db_device.edit_device(device_data) self.finish() class DeviceDeleteHandler(APIHandler): """ - 描述:企业节点,删除设备 - 请求方式:post - 请求参数: >- node_id, int >- device_id, int, 设备id - 返回值:无 """ @authenticated @operation_log("企业管理", "设备管理", consts.op_type_delete_str, "企业节点,删除设备") def post(self): # node_id = self.get_int_argument('node_id') device_id = self.get_int_argument("device_id") if not device_id: raise errors.HTTPAPIError( errors.ERROR_BAD_REQUEST, "企业节点或设备不能为空" ) db_device = DB_Device.EnterpriseDeviceRepository() db_device.delete_device(device_id) self.finish() class DeviceListHandler(APIHandler): """ - 描述:企业节点,设备列表 - 请求方式:post - 请求参数: > - pageNo > - pageSize > - node_id, int, 节点id - 返回值: ``` { "count": 123, "data": [ { "device_id": 123, "device_name": "xxx", "device_class": "xxx", 设备类型 "deployed": 0, # 是否部署, 0/未部署, 1/已部署 }, ... ] } ``` """ @authenticated @operation_log("企业管理", "设备管理", consts.op_type_list_str, "企业节点,设备列表") def post(self): node_id = self.get_int_argument("node_id") pageNo = self.get_int_argument("pageNo", 1) pageSize = self.get_int_argument("pageSize", 20) if not node_id: raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "企业节点不能为空") db_device = DB_Device.EnterpriseDeviceRepository() devices = db_device.list_devices( node_id=node_id, pageNo=pageNo, pageSize=pageSize ) logging.info(devices) self.finish({"count": devices["total_count"], "data": devices["devices"]}) class DeviceListSimpleHandler(APIHandler): """获取节点下所有设备的简单信息""" @authenticated def post(self): node_id = self.get_int_argument("node_id") if not node_id: raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "企业节点不能为空") db_device = DB_Device.EnterpriseDeviceRepository() devices = db_device.list_simple_devices(node_id=node_id) logging.info(devices) self.finish({"data": devices}) class DeviceInfoHandler(APIHandler): """ - 描述:企业节点,设备信息 - 请求方式:post - 请求参数: > - device_id, int, 设备id - 返回值: ``` { "name": "xxx", "addr": "xxx", "classification": "xxx", "device_model": "xxx", "param": "xxx", "comment": "xxx", } ``` """ @authenticated @operation_log("企业管理", "设备管理", consts.op_type_list_str, "企业节点,设备信息") def post(self): device_id = self.get_int_argument("device_id") if not device_id: raise errors.HTTPAPIError( errors.ERROR_BAD_REQUEST, "企业节点或设备不能为空" ) db_device = DB_Device.EnterpriseDeviceRepository() device = db_device.get_device(device_id=device_id) logging.info(device) self.finish(device) class DeviceBasemodelListHandler(APIHandler): """ - 描述:企业节点,节点信息 -> 设备列表 -> 基础模型配置 -> 模型列表 - 请求方式:post - 请求参数: > - pageNo > - pageSize > - device_id, int, 设备id - 返回值: ``` { "count": 123, "data": [ { "busi_model": "xxx", # 业务模型的名称 "base_model": [ { "model_id": 123, # 基础模型id "model_name": "xxx" # 基础模型name "model_version": "xxx", # 基础模型的版本 "model_hub_image": "xxx", # 运行库镜像 }, ... ] }, ... ] } ``` """ @authenticated @operation_log("企业管理", "节点设置", consts.op_type_list_str, "企业节点,节点信息 -> 设备列表 -> 基础模型配置 -> 模型列表") def post(self): device_id = self.get_int_argument("device_id") if not device_id: raise errors.HTTPAPIError( errors.ERROR_BAD_REQUEST, "企业节点或设备不能为空" ) pageNo = self.get_int_argument("pageNo", 1) pageSize = self.get_int_argument("pageSize", 10) db_busi_model = DB_BusiModelNodeDevice.EnterpriseBusiModelNodeDeviceRepository() busi_models, count = db_busi_model.get_busi_model_by_device(device_id=device_id, pagination=True, page_no=pageNo, page_size=pageSize) if count == 0: self.finish({"count": count, "data": []}) return res = [] for item in busi_models: busi_model_id = item["busi_model_id"] busi_model_name = item["name"] base_model_list = json.loads(item["base_models"]) base_models = [] for base_model in base_model_list: base_model_id = base_model["id"] base_model_suid = base_model["suid"] base_model_name = base_model["name"] db_alg_model = DB_AlgModel() 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( 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 base_models.append({ "model_id": base_model_id, "model_name": base_model_name, "model_version": base_model_version, "model_hub_image": base_model_hub_image, }) res.append({ "busi_model_id": busi_model_id, "busi_model_name": busi_model_name, "base_models": base_models }) self.finish({"count": count, "data": res}) class DeviceBaseModelCustomConfigHandler(APIHandler): """ - 描述:企业节点,节点信息 -> 设备列表 -> 基础模型配置 -> 基础模型参数配置 - 请求方式:post - 请求参数: > - device_id, int, 设备id > - node_id, int, 节点id > - base_model_id, int, 基础模型id > - busi_conf_file, string, 业务参数配置文件md5 > - busi_conf_str, string, 业务参数配置,json字符串,eg:'{"key1": "value1", "key2": "value2"}' > - model_hub_image, string, 运行库地址 > - model_conf_file, string, 模型参数配置文件md5 > - model_conf_str, string, 模型参数配置,json字符串,eg:'{"key1": "value1", "key2": "value2"}' - 返回值:无 """ @authenticated @operation_log("企业管理", "节点设置", consts.op_type_add_str, "企业节点,节点信息 -> 设备列表 -> 基础模型配置 -> 基础模型参数配置") def post(self): device_id = self.get_int_argument("device_id") node_id = self.get_int_argument("node_id") busi_model_id = self.get_int_argument("busi_model_id") base_model_id = self.get_int_argument("base_model_id") busi_conf_file = self.get_escaped_argument("busi_conf_file") busi_conf_str = self.get_escaped_argument("busi_conf_str") model_hub_image = self.get_escaped_argument("model_hub_image") model_conf_file = self.get_escaped_argument("model_conf_file") model_conf_str = self.get_escaped_argument("model_conf_str") db_device = DB_Device.EnterpriseDeviceRepository() device = db_device.get_device(device_id=device_id) if not device: raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "device not exist") if device["node_id"] != node_id: raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "device not belong to this node") db_alg_model = DB_AlgModel() base_model = db_alg_model.get_model_dict_by_id(base_model_id) if not base_model: raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "base model not exist") device_suid = device["suid"] node_suid = device["node_suid"] entity_id = device["entity_id"] entity_suid = device["entity_suid"] db_busi_model = DB_BusiModel.EnterpriseBusiModelRepository() busi_model = db_busi_model.get_busi_model_by_id(busi_model_id) busi_model_suid = busi_model.suid data = dict() data.update( entity_id=entity_id, entity_suid=entity_suid, node_id=node_id, node_suid=node_suid, device_id=device_id, device_suid=device_suid, busi_model_id=busi_model_id, busi_model_suid=busi_model_suid, base_model_id=base_model_id, base_model_suid=base_model["suid"], busi_conf_file=busi_conf_file, busi_conf_str=busi_conf_str, model_hub_image=model_hub_image, model_conf_file=model_conf_file, model_conf_str=model_conf_str ) db_conf = DB_NodeBaseModelConf.EnterpriseNodeDeviceBMCusConfRepository() db_conf.create_busi_model_custom_config(data=data) self.finish() class StatusListHandler(APIHandler): """ - 描述:设备状态列表 - 请求方式:post - 请求参数: > - entity_id, int, 企业id > - classification_suid, string, 分类id > - status, int, 状态,1000/all/默认, 1001/在线,1002/离线,1003/运行中,1004/故障 > - pageNo > - pageSize - 返回值:无 ``` { "count": 123, "data": [ { "id": 123, "name": "xxx", "status": 1001, "cpu": 123, "mem": 123, "storage": 123, "gpu": 123, }, ... ] } ``` """ @authenticated @operation_log("企业管理", "设备状态", consts.op_type_list_str,"设备状态列表") def post(self): entity_id = self.get_int_argument("entity_id") group = self.get_int_argument("group") classification = self.get_escaped_argument("classification", "") status = self.get_int_argument("status", 1000) pageNo = self.get_int_argument("pageNo", 1) pageSize = self.get_int_argument("pageSize", 20) if not entity_id: raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "企业节点不能为空") if status not in consts.device_status_map: raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "状态参数错误") db_device = DB_Device.EnterpriseDeviceRepository() res = db_device.list_entity_devices( entity_id=entity_id, pageno=pageNo, pagesize=pageSize, classification=classification, status=status, group=group ) count = res["count"] devices = res["devices"] data = [] for item, _ in devices: logging.info(item) data.append( { "id": item.id, "name": item.name, "status": item.status, "cpu": random.randint(20, 30), "mem": random.randint(20, 30), "storage": random.randint(20, 30), "gpu": random.randint(20, 30), } ) status_dic = { consts.device_status_online: 0, consts.device_status_offline: 0, consts.device_status_ongoing: 0, consts.device_status_error: 0, } status_count = db_device.status_count(entity_id=entity_id, classification=classification) for key in status_count: status_dic.update({key: status_count[key]}) self.finish({"count": count, "data": data, "status_count": status_dic}) class StatusGroupHandler(APIHandler): @authenticated def post(self): entity_id = self.get_int_argument("entity_id") db_node = DB_Node.EnterpriseNodeRepository() res = db_node.simple_list(entity_id) self.finish({"result": res}) class StatusInfoHandler(APIHandler): """ """ @authenticated @operation_log("企业管理", "设备状态", consts.op_type_list_str, "设备信息") def post(self): device_id = self.get_int_argument("id") if not device_id: raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "设备id不能为空") # 查询设备信息 db_device = DB_Device.EnterpriseDeviceRepository() res = db_device.get_devices(device_ids=[device_id]) if not res: raise errors.HTTPAPIError(errors.ERROR_BAD_REQUEST, "设备不存在") res = res[0] node_id = res["node_id"] device_suid = res["suid"] device_name = res["name"] device_comment = res["comment"] classification = res["classification"] db_cls = DB_DeviceClassification.DeviceClassificationReporitory() row_cls = db_cls.get_row_by_suid(suid=classification) device_classification_name = row_cls.name db_node = DB_Node.EnterpriseNodeRepository() node_info = db_node.get_node_by_id(node_id) node_name = node_info["name"] db_busi_model = DB_BusiModelNodeDevice.EnterpriseBusiModelNodeDeviceRepository() busi_models, _ = db_busi_model.get_busi_model_by_device(device_id=device_id) models = [] for item in busi_models: busi_model_id = item["busi_model_id"] busi_model_name = item["name"] base_model_list = json.loads(item["base_models"]) base_models = [] for base_model in base_model_list: base_model_id = base_model["id"] base_model_name = base_model["name"] db_alg_model = DB_AlgModel() 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"]) base_model_hub_image = "" if conf: base_model_hub_image = conf.model_hub_image base_models.append({ "model_id": base_model_id, "model_name": base_model_name, "model_version": base_model_version, "model_hub_image": base_model_hub_image, }) models.append({ "busi_model": busi_model_name, "base_models": base_models }) self.finish({ "classification": device_classification_name, "name": device_name, "comment": device_comment, "ID": device_suid, "cpu": random.randint(20, 30), "mem": random.randint(20, 30), "storage": random.randint(20, 30), "gpu": random.randint(20, 30), "models": models, "group": node_name }) class StatusLogHandler(APIHandler): """ - 描述:设备状态日志 - 请求方式:post - 请求参数: > - entity_id, int, 企业id > - pageNo > - pageSize - 返回值: ``` { "count": 123, "data": [ { "ID": "xxx", "name": "xxx", "classification": "xxx", "IP": "xxx", "duration": "xxx", } ] } ``` """ @authenticated def post(self): entity_id = self.get_int_argument("entity_id") pageNo = self.get_int_argument("pageNo", 1) pageSize = self.get_int_argument("pageSize", 10) db_device = DB_Device.EnterpriseDeviceRepository() res = db_device.list_entity_devices(entity_id=entity_id, pageno=pageNo, pagesize=pageSize) count = res["count"] devices = res["devices"] result = [] for device, classification_name in devices: device_name = device.name device_suid = device.suid device_ip = "" duration = date_util.time_diff(str(device.create_time)) result.append({ "ID": device_suid, "name": device_name, "classification": classification_name, "IP": device_ip, "duration": duration, }) self.finish({"count": count, "data": result})