Device
This commit is contained in:
109
backend/modules/device/controller.py
Normal file
109
backend/modules/device/controller.py
Normal file
@@ -0,0 +1,109 @@
|
||||
# pyrefly: ignore [missing-import]
|
||||
from flask import request
|
||||
from common.response.api_response import success_response
|
||||
from common.constants.status_code import HTTP_CREATED
|
||||
from modules.device.service import (
|
||||
get_devices_service,
|
||||
get_device_by_id_service,
|
||||
create_device_service,
|
||||
update_device_service,
|
||||
delete_device_service
|
||||
)
|
||||
from modules.device.schemas import CreateDeviceSchema, UpdateDeviceSchema
|
||||
|
||||
|
||||
def get_devices():
|
||||
"""GET /api/devices — Lấy danh sách tất cả các thiết bị mạng"""
|
||||
devices = get_devices_service()
|
||||
return success_response(
|
||||
data=devices,
|
||||
message="Devices retrieved successfully"
|
||||
)
|
||||
|
||||
|
||||
def get_device_by_id(device_id):
|
||||
"""GET /api/devices/<device_id> — Lấy chi tiết thiết bị theo ID"""
|
||||
device = get_device_by_id_service(device_id)
|
||||
return success_response(
|
||||
data=device,
|
||||
message="Device retrieved successfully"
|
||||
)
|
||||
|
||||
|
||||
def create_device():
|
||||
"""
|
||||
POST /api/devices — Thêm mới một thiết bị mạng
|
||||
"""
|
||||
body = request.get_json()
|
||||
|
||||
# Validate bằng schema
|
||||
schema = CreateDeviceSchema()
|
||||
data = schema.load(body)
|
||||
|
||||
# Gọi service xử lý logic nghiệp vụ
|
||||
new_device = create_device_service(data)
|
||||
|
||||
return success_response(
|
||||
data=new_device,
|
||||
message="Device created successfully",
|
||||
status_code=HTTP_CREATED
|
||||
)
|
||||
|
||||
|
||||
def update_device(device_id):
|
||||
"""
|
||||
PUT /api/devices/<device_id> — Cập nhật thông tin thiết bị mạng
|
||||
"""
|
||||
body = request.get_json()
|
||||
|
||||
# Validate bằng schema
|
||||
schema = UpdateDeviceSchema()
|
||||
data = schema.load(body)
|
||||
|
||||
# Gọi service để kiểm tra và cập nhật DB
|
||||
updated_device = update_device_service(device_id, data)
|
||||
|
||||
return success_response(
|
||||
data=updated_device,
|
||||
message="Device updated successfully"
|
||||
)
|
||||
|
||||
|
||||
def delete_device(device_id):
|
||||
"""
|
||||
DELETE /api/devices/<device_id> — Xóa thiết bị mạng khỏi hệ thống
|
||||
"""
|
||||
delete_device_service(device_id)
|
||||
return success_response(
|
||||
message="Device deleted successfully"
|
||||
)
|
||||
|
||||
|
||||
# def upload_device_avatar_controller(device_id):
|
||||
# """
|
||||
# POST /api/devices/<device_id>/avatar — Upload ảnh đại diện thiết bị lên S3/MinIO
|
||||
# và cập nhật URL vào database.
|
||||
# """
|
||||
# # 1. Kiểm tra tồn tại của thiết bị
|
||||
# device = get_device_by_id_service(device_id)
|
||||
|
||||
# # 2. Lấy file ảnh từ request.files
|
||||
# file = request.files.get("file")
|
||||
# if not file:
|
||||
# raise BadRequestException("Missing image file in request")
|
||||
|
||||
# try:
|
||||
# # 3. Thực hiện upload lên S3/MinIO
|
||||
# avatar_url = upload_device_avatar(file, device["name"])
|
||||
|
||||
# # 4. Cập nhật trường avatar_url của thiết bị trong DB
|
||||
# updated_device = update_device_service(device_id, {"avatar_url": avatar_url})
|
||||
|
||||
# return success_response(
|
||||
# data=updated_device,
|
||||
# message="Device avatar uploaded successfully",
|
||||
# status_code=HTTP_CREATED
|
||||
# )
|
||||
# except ValueError as e:
|
||||
# # ValidationError trong quá trình validate loại file
|
||||
# raise BadRequestException(str(e))
|
||||
25
backend/modules/device/exceptions.py
Normal file
25
backend/modules/device/exceptions.py
Normal file
@@ -0,0 +1,25 @@
|
||||
from common.exceptions.app_exception import (
|
||||
NotFoundException,
|
||||
ConflictException
|
||||
)
|
||||
|
||||
class DeviceNotFoundException(NotFoundException):
|
||||
def __init__(self, device_id):
|
||||
super().__init__(
|
||||
message=f"Device not found with id={device_id}",
|
||||
payload={"device_id": device_id}
|
||||
)
|
||||
|
||||
class DeviceAlreadyExistsException(ConflictException):
|
||||
def __init__(self, name):
|
||||
super().__init__(
|
||||
message=f"Device already exists with name={name}",
|
||||
payload={"name": name}
|
||||
)
|
||||
|
||||
class DeviceIPAlreadyExistsException(ConflictException):
|
||||
def __init__(self, ip_address):
|
||||
super().__init__(
|
||||
message=f"Device already exists with IP address={ip_address}",
|
||||
payload={"ip_address": ip_address}
|
||||
)
|
||||
293
backend/modules/device/repository.py
Normal file
293
backend/modules/device/repository.py
Normal file
@@ -0,0 +1,293 @@
|
||||
from config.database import get_connection, release_connection
|
||||
|
||||
def _row_to_dict(row):
|
||||
"""
|
||||
Chuyển đổi một dòng kết quả từ tuple (từ câu lệnh JOIN) thành dictionary.
|
||||
"""
|
||||
if not row:
|
||||
return None
|
||||
|
||||
device_dict = {
|
||||
"id": str(row[0]),
|
||||
"device_type_id": str(row[1]),
|
||||
"name": row[2],
|
||||
"description": row[3],
|
||||
"ip_address": row[4],
|
||||
"port": row[5],
|
||||
"latitude": row[6],
|
||||
"longitude": row[7],
|
||||
"color": row[8],
|
||||
"avatar_url": row[9],
|
||||
"is_active": row[10],
|
||||
"created": row[11].isoformat() if row[11] else None,
|
||||
"modified": row[12].isoformat() if row[12] else None
|
||||
}
|
||||
|
||||
# Nếu câu truy vấn có JOIN với device_type
|
||||
if len(row) > 13:
|
||||
device_dict["device_type_name"] = row[13]
|
||||
device_dict["device_type_icon"] = row[14]
|
||||
|
||||
return device_dict
|
||||
|
||||
|
||||
def find_all_devices():
|
||||
"""
|
||||
Lấy danh sách tất cả các thiết bị trong database, JOIN với bảng device_type
|
||||
để hiển thị tên loại thiết bị và icon đại diện.
|
||||
"""
|
||||
conn = get_connection()
|
||||
cur = None
|
||||
try:
|
||||
cur = conn.cursor()
|
||||
cur.execute("""
|
||||
SELECT
|
||||
d.id, d.device_type_id, d.name, d.description, d.ip_address, d.port,
|
||||
d.latitude, d.longitude, d.color, d.avatar_url, d.is_active, d.created, d.modified,
|
||||
dt.name as device_type_name, dt.icon_url as device_type_icon
|
||||
FROM device d
|
||||
JOIN device_type dt ON d.device_type_id = dt.id
|
||||
ORDER BY d.created DESC
|
||||
""")
|
||||
rows = cur.fetchall()
|
||||
|
||||
devices = []
|
||||
for row in rows:
|
||||
devices.append(_row_to_dict(row))
|
||||
|
||||
return devices
|
||||
finally:
|
||||
if cur:
|
||||
cur.close()
|
||||
release_connection(conn)
|
||||
|
||||
|
||||
def find_device_by_id(device_id):
|
||||
"""
|
||||
Tìm thiết bị theo ID, JOIN với device_type để lấy thông tin chi tiết.
|
||||
"""
|
||||
conn = get_connection()
|
||||
cur = None
|
||||
try:
|
||||
cur = conn.cursor()
|
||||
cur.execute("""
|
||||
SELECT
|
||||
d.id, d.device_type_id, d.name, d.description, d.ip_address, d.port,
|
||||
d.latitude, d.longitude, d.color, d.avatar_url, d.is_active, d.created, d.modified,
|
||||
dt.name as device_type_name, dt.icon_url as device_type_icon
|
||||
FROM device d
|
||||
JOIN device_type dt ON d.device_type_id = dt.id
|
||||
WHERE d.id = %s
|
||||
""", (device_id,))
|
||||
|
||||
row = cur.fetchone()
|
||||
return _row_to_dict(row) if row else None
|
||||
finally:
|
||||
if cur:
|
||||
cur.close()
|
||||
release_connection(conn)
|
||||
|
||||
|
||||
def find_device_by_name(name):
|
||||
"""
|
||||
Tìm thiết bị theo tên (không phân biệt hoa thường) để phục vụ validate trùng tên.
|
||||
"""
|
||||
conn = get_connection()
|
||||
cur = None
|
||||
try:
|
||||
cur = conn.cursor()
|
||||
cur.execute("""
|
||||
SELECT id, device_type_id, name, description, ip_address, port, latitude, longitude, color, avatar_url, is_active, created, modified
|
||||
FROM device
|
||||
WHERE LOWER(name) = LOWER(%s)
|
||||
""", (name,))
|
||||
|
||||
row = cur.fetchone()
|
||||
return _row_to_dict(row) if row else None
|
||||
finally:
|
||||
if cur:
|
||||
cur.close()
|
||||
release_connection(conn)
|
||||
|
||||
|
||||
def find_device_by_ip(ip_address):
|
||||
"""
|
||||
Tìm thiết bị theo địa chỉ IP để phục vụ validate tránh trùng lặp IP thiết bị.
|
||||
"""
|
||||
conn = get_connection()
|
||||
cur = None
|
||||
try:
|
||||
cur = conn.cursor()
|
||||
cur.execute("""
|
||||
SELECT id, device_type_id, name, description, ip_address, port, latitude, longitude, color, avatar_url, is_active, created, modified
|
||||
FROM device
|
||||
WHERE ip_address = %s
|
||||
""", (ip_address,))
|
||||
|
||||
row = cur.fetchone()
|
||||
return _row_to_dict(row) if row else None
|
||||
finally:
|
||||
if cur:
|
||||
cur.close()
|
||||
release_connection(conn)
|
||||
|
||||
|
||||
def insert_device(data):
|
||||
"""
|
||||
Tạo mới một thiết bị và cấu hình mặc định (MonitorConfig & AlertConfig)
|
||||
trong cùng một database transaction.
|
||||
"""
|
||||
conn = get_connection()
|
||||
cur = None
|
||||
try:
|
||||
cur = conn.cursor()
|
||||
|
||||
# 1. Thêm thiết bị vào bảng device
|
||||
cur.execute("""
|
||||
INSERT INTO device (device_type_id, name, description, ip_address, port, latitude, longitude, color, avatar_url, is_active)
|
||||
VALUES (%s, %s, %s, %s, %s, %s, %s, %s, %s, %s)
|
||||
RETURNING id, device_type_id, name, description, ip_address, port, latitude, longitude, color, avatar_url, is_active, created, modified
|
||||
""", (
|
||||
data["device_type_id"],
|
||||
data["name"],
|
||||
data.get("description"),
|
||||
data["ip_address"],
|
||||
data.get("port"),
|
||||
data["latitude"],
|
||||
data["longitude"],
|
||||
data["color"],
|
||||
data.get("avatar_url"),
|
||||
data.get("is_active", True)
|
||||
))
|
||||
|
||||
device_row = cur.fetchone()
|
||||
device_id = device_row[0]
|
||||
|
||||
# 2. Thêm cấu hình giám sát mặc định (MonitorConfig) cho thiết bị vừa tạo
|
||||
# enable_ping mặc định bật True để thực hiện Ping giám sát
|
||||
cur.execute("""
|
||||
INSERT INTO monitor_config (device_id, enable_ping, ping_count, ping_timeout, ping_interval, enable_snmp)
|
||||
VALUES (%s, %s, %s, %s, %s, %s)
|
||||
""", (
|
||||
device_id,
|
||||
True, # enable_ping
|
||||
3, # ping_count
|
||||
5, # ping_timeout (giây)
|
||||
60, # ping_interval (giây)
|
||||
False # enable_snmp
|
||||
))
|
||||
|
||||
# 3. Thêm cấu hình cảnh báo mặc định (AlertConfig) cho thiết bị vừa tạo
|
||||
cur.execute("""
|
||||
INSERT INTO alert_config (device_id, is_enabled, fail_threshold, cooldown_minutes, notify_web, notify_email)
|
||||
VALUES (%s, %s, %s, %s, %s, %s)
|
||||
""", (
|
||||
device_id,
|
||||
True, # is_enabled
|
||||
3, # fail_threshold (số lần fail liên tiếp trước khi alert)
|
||||
30, # cooldown_minutes (thời gian tránh spam alert)
|
||||
True, # notify_web (hiển thị thông báo trên web)
|
||||
False # notify_email (mặc định tắt gửi email)
|
||||
))
|
||||
|
||||
# 4. Lấy lại thiết bị kèm thông tin DeviceType đã JOIN để trả về đầy đủ dữ liệu
|
||||
cur.execute("""
|
||||
SELECT
|
||||
d.id, d.device_type_id, d.name, d.description, d.ip_address, d.port,
|
||||
d.latitude, d.longitude, d.color, d.avatar_url, d.is_active, d.created, d.modified,
|
||||
dt.name as device_type_name, dt.icon_url as device_type_icon
|
||||
FROM device d
|
||||
JOIN device_type dt ON d.device_type_id = dt.id
|
||||
WHERE d.id = %s
|
||||
""", (device_id,))
|
||||
full_row = cur.fetchone()
|
||||
|
||||
# Commit toàn bộ transaction
|
||||
conn.commit()
|
||||
|
||||
return _row_to_dict(full_row)
|
||||
|
||||
except Exception:
|
||||
conn.rollback()
|
||||
raise
|
||||
finally:
|
||||
if cur:
|
||||
cur.close()
|
||||
release_connection(conn)
|
||||
|
||||
|
||||
def update_device_db(device_id, data):
|
||||
"""
|
||||
Cập nhật thông tin chi tiết của một thiết bị.
|
||||
"""
|
||||
conn = get_connection()
|
||||
cur = None
|
||||
try:
|
||||
cur = conn.cursor()
|
||||
|
||||
update_fields = []
|
||||
params = []
|
||||
|
||||
fields_list = [
|
||||
"device_type_id", "name", "description",
|
||||
"ip_address", "port", "latitude",
|
||||
"longitude", "color", "avatar_url", "is_active"
|
||||
]
|
||||
|
||||
for key in fields_list:
|
||||
if key in data:
|
||||
update_fields.append(f"{key} = %s")
|
||||
params.append(data[key])
|
||||
|
||||
if not update_fields:
|
||||
return find_device_by_id(device_id)
|
||||
|
||||
update_fields.append("modified = CURRENT_TIMESTAMP")
|
||||
|
||||
sql = f"""
|
||||
UPDATE device
|
||||
SET {', '.join(update_fields)}
|
||||
WHERE id = %s
|
||||
RETURNING id, device_type_id, name, description, ip_address, port, latitude, longitude, color, avatar_url, is_active, created, modified
|
||||
"""
|
||||
params.append(device_id)
|
||||
|
||||
cur.execute(sql, tuple(params))
|
||||
row = cur.fetchone()
|
||||
conn.commit()
|
||||
|
||||
if row:
|
||||
# Lấy chi tiết có kèm JOIN device_type để trả về đầy đủ
|
||||
return find_device_by_id(device_id)
|
||||
return None
|
||||
|
||||
except Exception:
|
||||
conn.rollback()
|
||||
raise
|
||||
finally:
|
||||
if cur:
|
||||
cur.close()
|
||||
release_connection(conn)
|
||||
|
||||
|
||||
def delete_device_db(device_id):
|
||||
"""
|
||||
Xóa thiết bị khỏi database. Do có khóa ngoại với Cascade Delete,
|
||||
tất cả các dòng liên quan trong monitor_config, alert_config, device_status, alert_log sẽ tự động bị xóa.
|
||||
"""
|
||||
conn = get_connection()
|
||||
cur = None
|
||||
try:
|
||||
cur = conn.cursor()
|
||||
cur.execute("""
|
||||
DELETE FROM device
|
||||
WHERE id = %s
|
||||
""", (device_id,))
|
||||
conn.commit()
|
||||
except Exception:
|
||||
conn.rollback()
|
||||
raise
|
||||
finally:
|
||||
if cur:
|
||||
cur.close()
|
||||
release_connection(conn)
|
||||
20
backend/modules/device/routes.py
Normal file
20
backend/modules/device/routes.py
Normal file
@@ -0,0 +1,20 @@
|
||||
# pyrefly: ignore [missing-import]
|
||||
from flask import Blueprint
|
||||
|
||||
from modules.device.controller import (
|
||||
get_devices,
|
||||
get_device_by_id,
|
||||
create_device,
|
||||
update_device,
|
||||
delete_device
|
||||
)
|
||||
|
||||
# Khởi tạo Blueprint cho Module Device
|
||||
device_bp = Blueprint("device", __name__)
|
||||
|
||||
# Đăng ký các endpoints với controller tương ứng
|
||||
device_bp.route("", methods=["GET"])(get_devices)
|
||||
device_bp.route("/<device_id>", methods=["GET"])(get_device_by_id)
|
||||
device_bp.route("", methods=["POST"])(create_device)
|
||||
device_bp.route("/<device_id>", methods=["PUT"])(update_device)
|
||||
device_bp.route("/<device_id>", methods=["DELETE"])(delete_device)
|
||||
120
backend/modules/device/schemas.py
Normal file
120
backend/modules/device/schemas.py
Normal file
@@ -0,0 +1,120 @@
|
||||
import ipaddress
|
||||
# pyrefly: ignore [missing-import]
|
||||
from marshmallow import Schema, fields, validate, ValidationError
|
||||
|
||||
HEX_COLOR_REGEX = r"^#[0-9A-Fa-f]{6}$"
|
||||
|
||||
def validate_ip_address(val):
|
||||
try:
|
||||
ipaddress.ip_address(val)
|
||||
except ValueError:
|
||||
raise ValidationError("Invalid IP address format. Must be a valid IPv4 or IPv6 address.")
|
||||
|
||||
class CreateDeviceSchema(Schema):
|
||||
device_type_id = fields.UUID(
|
||||
required=True,
|
||||
error_messages={"required": "Device type ID is required."}
|
||||
)
|
||||
|
||||
name = fields.String(
|
||||
required=True,
|
||||
validate=validate.Length(min=1, max=200),
|
||||
error_messages={"required": "Device name is required."}
|
||||
)
|
||||
|
||||
description = fields.String(
|
||||
required=False,
|
||||
allow_none=True
|
||||
)
|
||||
|
||||
ip_address = fields.String(
|
||||
required=True,
|
||||
validate=validate_ip_address,
|
||||
error_messages={"required": "IP address is required."}
|
||||
)
|
||||
|
||||
port = fields.Integer(
|
||||
required=False,
|
||||
allow_none=True,
|
||||
validate=validate.Range(min=1, max=65535)
|
||||
)
|
||||
|
||||
latitude = fields.Float(
|
||||
required=True,
|
||||
validate=validate.Range(min=-90.0, max=90.0),
|
||||
error_messages={"required": "Latitude is required."}
|
||||
)
|
||||
|
||||
longitude = fields.Float(
|
||||
required=True,
|
||||
validate=validate.Range(min=-180.0, max=180.0),
|
||||
error_messages={"required": "Longitude is required."}
|
||||
)
|
||||
|
||||
color = fields.String(
|
||||
required=True,
|
||||
validate=validate.Regexp(HEX_COLOR_REGEX),
|
||||
error_messages={"required": "Color code (HEX) is required."}
|
||||
)
|
||||
|
||||
avatar_url = fields.String(
|
||||
required=False,
|
||||
allow_none=True,
|
||||
validate=validate.Length(max=512)
|
||||
)
|
||||
|
||||
is_active = fields.Boolean(
|
||||
required=False,
|
||||
load_default=True
|
||||
)
|
||||
|
||||
class UpdateDeviceSchema(Schema):
|
||||
device_type_id = fields.UUID(
|
||||
required=False
|
||||
)
|
||||
|
||||
name = fields.String(
|
||||
required=False,
|
||||
validate=validate.Length(min=1, max=200)
|
||||
)
|
||||
|
||||
description = fields.String(
|
||||
required=False,
|
||||
allow_none=True
|
||||
)
|
||||
|
||||
ip_address = fields.String(
|
||||
required=False,
|
||||
validate=validate_ip_address
|
||||
)
|
||||
|
||||
port = fields.Integer(
|
||||
required=False,
|
||||
allow_none=True,
|
||||
validate=validate.Range(min=1, max=65535)
|
||||
)
|
||||
|
||||
latitude = fields.Float(
|
||||
required=False,
|
||||
validate=validate.Range(min=-90.0, max=90.0)
|
||||
)
|
||||
|
||||
longitude = fields.Float(
|
||||
required=False,
|
||||
validate=validate.Range(min=-180.0, max=180.0)
|
||||
)
|
||||
|
||||
color = fields.String(
|
||||
required=False,
|
||||
validate=validate.Regexp(HEX_COLOR_REGEX)
|
||||
)
|
||||
|
||||
avatar_url = fields.String(
|
||||
required=False,
|
||||
allow_none=True,
|
||||
validate=validate.Length(max=512)
|
||||
)
|
||||
|
||||
is_active = fields.Boolean(
|
||||
required=False
|
||||
)
|
||||
138
backend/modules/device/service.py
Normal file
138
backend/modules/device/service.py
Normal file
@@ -0,0 +1,138 @@
|
||||
from modules.device.repository import (
|
||||
find_all_devices,
|
||||
find_device_by_id,
|
||||
find_device_by_name,
|
||||
find_device_by_ip,
|
||||
insert_device,
|
||||
update_device_db,
|
||||
delete_device_db
|
||||
)
|
||||
from modules.device.exceptions import (
|
||||
DeviceNotFoundException,
|
||||
DeviceAlreadyExistsException,
|
||||
DeviceIPAlreadyExistsException
|
||||
)
|
||||
from modules.device_type.repository import find_device_type_by_id
|
||||
from modules.device_type.exceptions import DeviceTypeNotFoundException
|
||||
from scheduler.scheduler import (
|
||||
add_device_monitoring_job,
|
||||
remove_device_monitoring_job,
|
||||
reschedule_device_monitoring_job
|
||||
)
|
||||
|
||||
|
||||
def get_devices_service():
|
||||
"""Lấy danh sách tất cả thiết bị"""
|
||||
return find_all_devices()
|
||||
|
||||
|
||||
def get_device_by_id_service(device_id):
|
||||
"""
|
||||
Lấy thông tin chi tiết một thiết bị theo ID.
|
||||
Ném lỗi DeviceNotFoundException nếu không tồn tại.
|
||||
"""
|
||||
device = find_device_by_id(device_id)
|
||||
if not device:
|
||||
raise DeviceNotFoundException(device_id)
|
||||
return device
|
||||
|
||||
|
||||
def create_device_service(data):
|
||||
"""
|
||||
Tạo mới một thiết bị mạng.
|
||||
Các bước xử lý:
|
||||
1. Kiểm tra loại thiết bị (device_type_id) có tồn tại trong hệ thống hay không.
|
||||
2. Kiểm tra trùng lặp tên thiết bị (case-insensitive).
|
||||
3. Kiểm tra trùng lặp địa chỉ IP.
|
||||
4. Thêm thiết bị và cấu hình mặc định (monitor & alert) vào database.
|
||||
5. Đăng ký công việc giám sát vào Scheduler nền.
|
||||
"""
|
||||
# 1. Kiểm tra DeviceType
|
||||
device_type = find_device_type_by_id(data["device_type_id"])
|
||||
if not device_type:
|
||||
raise DeviceTypeNotFoundException(data["device_type_id"])
|
||||
|
||||
# 2. Kiểm tra trùng tên
|
||||
existing_name = find_device_by_name(data["name"])
|
||||
if existing_name:
|
||||
raise DeviceAlreadyExistsException(data["name"])
|
||||
|
||||
# 3. Kiểm tra trùng IP
|
||||
existing_ip = find_device_by_ip(data["ip_address"])
|
||||
if existing_ip:
|
||||
raise DeviceIPAlreadyExistsException(data["ip_address"])
|
||||
|
||||
# 4. Insert DB
|
||||
new_device = insert_device(data)
|
||||
|
||||
# 5. Kích hoạt Job giám sát trên Background Scheduler
|
||||
# Truyền kèm thông tin cấu hình mặc định (enable_ping=True, v.v...)
|
||||
add_device_monitoring_job(new_device["id"], None)
|
||||
|
||||
return new_device
|
||||
|
||||
|
||||
def update_device_service(device_id, data):
|
||||
"""
|
||||
Cập nhật thông tin thiết bị mạng.
|
||||
Các bước xử lý:
|
||||
1. Kiểm tra sự tồn tại của thiết bị.
|
||||
2. Nếu cập nhật device_type_id -> kiểm tra xem có tồn tại không.
|
||||
3. Nếu cập nhật name -> kiểm tra xem có bị trùng với thiết bị khác không.
|
||||
4. Nếu cập nhật ip_address -> kiểm tra xem có bị trùng với thiết bị khác không.
|
||||
5. Cập nhật dữ liệu vào DB.
|
||||
6. Cập nhật lại cấu hình giám sát trong Scheduler.
|
||||
"""
|
||||
# 1. Kiểm tra thiết bị có tồn tại hay không
|
||||
existing = find_device_by_id(device_id)
|
||||
if not existing:
|
||||
raise DeviceNotFoundException(device_id)
|
||||
|
||||
# 2. Kiểm tra loại thiết bị nếu có truyền vào
|
||||
new_device_type_id = data.get("device_type_id")
|
||||
if new_device_type_id and new_device_type_id != existing["device_type_id"]:
|
||||
device_type = find_device_type_by_id(new_device_type_id)
|
||||
if not device_type:
|
||||
raise DeviceTypeNotFoundException(new_device_type_id)
|
||||
|
||||
# 3. Kiểm tra trùng tên khi tên bị thay đổi
|
||||
new_name = data.get("name")
|
||||
if new_name and new_name.lower() != existing["name"].lower():
|
||||
conflict_name = find_device_by_name(new_name)
|
||||
if conflict_name and conflict_name["id"] != device_id:
|
||||
raise DeviceAlreadyExistsException(new_name)
|
||||
|
||||
# 4. Kiểm tra trùng IP khi IP bị thay đổi
|
||||
new_ip = data.get("ip_address")
|
||||
if new_ip and new_ip != existing["ip_address"]:
|
||||
conflict_ip = find_device_by_ip(new_ip)
|
||||
if conflict_ip and conflict_ip["id"] != device_id:
|
||||
raise DeviceIPAlreadyExistsException(new_ip)
|
||||
|
||||
# 5. Cập nhật DB
|
||||
updated_device = update_device_db(device_id, data)
|
||||
|
||||
# 6. Cập nhật lại thông tin giám sát trong Scheduler
|
||||
reschedule_device_monitoring_job(device_id, None)
|
||||
|
||||
return updated_device
|
||||
|
||||
|
||||
def delete_device_service(device_id):
|
||||
"""
|
||||
Xóa thiết bị mạng.
|
||||
Các bước xử lý:
|
||||
1. Kiểm tra sự tồn tại của thiết bị.
|
||||
2. Thực hiện xóa khỏi DB (tự động xóa cấu hình và lịch sử liên quan).
|
||||
3. Hủy bỏ job giám sát khỏi Scheduler.
|
||||
"""
|
||||
# 1. Kiểm tra tồn tại
|
||||
existing = find_device_by_id(device_id)
|
||||
if not existing:
|
||||
raise DeviceNotFoundException(device_id)
|
||||
|
||||
# 2. Xóa khỏi DB
|
||||
delete_device_db(device_id)
|
||||
|
||||
# 3. Hủy công việc giám sát trong Scheduler
|
||||
remove_device_monitoring_job(device_id)
|
||||
Reference in New Issue
Block a user