This commit is contained in:
QuangMinh_123
2026-05-27 13:50:27 +07:00
parent 7aebcf9567
commit 2683cdb882
30 changed files with 2091 additions and 17 deletions

View File

@@ -0,0 +1,149 @@
# pyrefly: ignore [missing-import]
from flask import request
from common.response.api_response import success_response
from modules.monitor_config.service import (
get_monitor_config_service,
update_monitor_config_service,
test_connection_service
)
from modules.monitor_config.schemas import (
UpdateMonitorConfigSchema,
TestConnectionSchema
)
# ============================================
# CONTROLLER: Nhận request → Validate → Gọi Service → Trả response
# ============================================
# Luồng đi tổng thể:
#
# Client (Frontend/Postman)
# │
# ├── GET /api/devices/{device_id}/monitor-config → get_monitor_config()
# ├── PUT /api/devices/{device_id}/monitor-config → update_monitor_config()
# └── POST /api/devices/{device_id}/monitor-config/test → test_connection()
# │
# ▼
# Controller (file này)
# │ 1. Lấy dữ liệu từ request (params, body)
# │ 2. Validate bằng Marshmallow Schema
# │ 3. Gọi Service xử lý logic
# │
# ▼
# Service → Repository → Database
#
# Controller KHÔNG chứa business logic (check tồn tại, tính toán...)
# Controller CHỈ là cầu nối giữa HTTP request và Service layer
# ============================================
def get_monitor_config(device_id):
"""
GET /api/devices/<device_id>/monitor-config
Lấy cấu hình giám sát hiện tại của thiết bị.
Luồng đi:
1. Nhận device_id từ URL path
2. Gọi service → service check thiết bị tồn tại → trả config
3. Trả response thành công kèm data
"""
config = get_monitor_config_service(device_id)
return success_response(
data=config,
message="Monitor config retrieved successfully"
)
def update_monitor_config(device_id):
"""
PUT /api/devices/<device_id>/monitor-config
Cập nhật cấu hình giám sát của thiết bị.
Luồng đi:
1. Nhận device_id từ URL path + JSON body từ request
2. Validate body bằng UpdateMonitorConfigSchema
→ Marshmallow kiểm tra: kiểu dữ liệu, range, enum...
→ Nếu sai → ném ValidationError → Global handler trả 400
→ Nếu đúng → loại bỏ trường thừa, áp dụng defaults
3. Gọi service → service check tồn tại → cập nhật DB → reschedule job
4. Trả response thành công kèm config đã cập nhật
Ví dụ request body:
{
"enable_ping": true,
"ping_interval": 30,
"ping_count": 5,
"enable_snmp": true,
"snmp_community": "public",
"snmp_version": "v2c",
"snmp_port": 161
}
"""
body = request.get_json()
# Validate bằng schema — loại bỏ trường không hợp lệ, check range/enum
schema = UpdateMonitorConfigSchema()
data = schema.load(body)
# Gọi service để xử lý logic nghiệp vụ
updated_config = update_monitor_config_service(device_id, data)
return success_response(
data=updated_config,
message="Monitor config updated successfully"
)
def test_connection(device_id):
"""
POST /api/devices/<device_id>/monitor-config/test
Kiểm tra kết nối tới thiết bị (Ping và/hoặc SNMP).
Luồng đi:
1. Nhận device_id từ URL path + JSON body chứa tham số test
2. Validate body bằng TestConnectionSchema
3. Gọi service → service lấy IP thiết bị → chạy Ping/SNMP test
4. Trả kết quả kiểm tra (Up/Down, RTT, chi tiết SNMP)
Ví dụ request body (test cả Ping và SNMP):
{
"test_ping": true,
"ping_count": 3,
"ping_timeout": 5,
"test_snmp": true,
"snmp_community": "public",
"snmp_version": "v2c",
"snmp_port": 161,
"snmp_timeout": 5
}
Ví dụ response:
{
"success": true,
"data": {
"ping_result": {
"status": "up",
"method": "icmplib",
"avg_rtt_ms": 12.5,
"packet_loss": 0.0
},
"snmp_result": {
"status": "up",
"method": "snmp",
"snmp_data": {"1.3.6.1.2.1.1.1.0": "Cisco IOS XR Software..."}
}
}
}
"""
body = request.get_json() or {}
# Validate tham số test
schema = TestConnectionSchema()
test_params = schema.load(body)
# Gọi service chạy test kết nối
results = test_connection_service(device_id, test_params)
return success_response(
data=results,
message="Connection test completed"
)

View File

@@ -0,0 +1,12 @@
from common.exceptions.app_exception import NotFoundException
class MonitorConfigNotFoundException(NotFoundException):
"""
Exception ném ra khi không tìm thấy cấu hình giám sát (MonitorConfig) của thiết bị.
Kế thừa từ NotFoundException để trả về HTTP status 404 cho client.
"""
def __init__(self, device_id):
super().__init__(
message=f"Monitor config not found for device with id={device_id}",
payload={"device_id": device_id}
)

View File

@@ -0,0 +1,147 @@
import json
from config.database import get_connection, release_connection
# ============================================
# REPOSITORY LAYER: Tương tác trực tiếp với bảng monitor_config trong PostgreSQL
# ============================================
# Luồng đi tổng thể:
# Controller → Service → Repository → Database
# Repository CHỈ chịu trách nhiệm thực thi SQL, KHÔNG chứa logic nghiệp vụ.
#
# Bảng monitor_config có quan hệ 1-1 với bảng device:
# - Mỗi device có đúng 1 bản ghi monitor_config (tạo tự động khi tạo device)
# - Khóa ngoại: device_id → device(id) ON DELETE CASCADE
# - Ràng buộc UNIQUE trên device_id → đảm bảo không trùng
# ============================================
def _row_to_dict(row):
"""
Chuyển đổi một dòng kết quả (tuple) từ câu query thành dictionary.
Thứ tự các cột phải khớp với SELECT trong các hàm bên dưới.
"""
if not row:
return None
return {
"id": str(row[0]),
"device_id": str(row[1]),
"enable_ping": row[2],
"ping_count": row[3],
"ping_timeout": row[4],
"ping_interval": row[5],
"enable_snmp": row[6],
"snmp_version": row[7],
"snmp_community": row[8],
"snmp_port": row[9],
"snmp_interval": row[10],
"snmp_timeout": row[11],
"snmp_custom_oids": row[12], # PostgreSQL JSONB → Python dict (tự động)
"created": row[13].isoformat() if row[13] else None,
"modified": row[14].isoformat() if row[14] else None
}
# ============================================
# FIND BY DEVICE ID: Lấy cấu hình giám sát của một thiết bị
# ============================================
# Luồng đi:
# GET /api/devices/{device_id}/monitor-config
# → Controller → Service gọi hàm này
# → Trả về dict nếu tìm thấy, None nếu không
# ============================================
def find_monitor_config_by_device_id(device_id):
conn = get_connection()
cur = None
try:
cur = conn.cursor()
cur.execute("""
SELECT id, device_id, enable_ping, ping_count, ping_timeout, ping_interval,
enable_snmp, snmp_version, snmp_community, snmp_port, snmp_interval,
snmp_timeout, snmp_custom_oids, created, modified
FROM monitor_config
WHERE device_id = %s
""", (device_id,))
row = cur.fetchone()
return _row_to_dict(row) if row else None
finally:
if cur:
cur.close()
release_connection(conn)
# ============================================
# UPDATE: Cập nhật cấu hình giám sát của thiết bị
# ============================================
# Luồng đi:
# PUT /api/devices/{device_id}/monitor-config
# → Controller validate body → Service kiểm tra tồn tại → Repository cập nhật DB
#
# Cách hoạt động:
# - Dùng Dynamic UPDATE: chỉ cập nhật các trường có trong data (không ghi đè toàn bộ)
# - Trường snmp_custom_oids cần chuyển sang JSON string trước khi lưu vào JSONB
# - Tự động cập nhật cột modified = CURRENT_TIMESTAMP
# - RETURNING: trả về toàn bộ row sau khi update (tránh phải SELECT lại)
# ============================================
def update_monitor_config_db(device_id, data):
conn = get_connection()
cur = None
try:
cur = conn.cursor()
# Xây dựng câu lệnh UPDATE động — chỉ SET các trường có trong data
update_fields = []
params = []
# Danh sách các trường được phép cập nhật
allowed_fields = [
"enable_ping", "ping_count", "ping_timeout", "ping_interval",
"enable_snmp", "snmp_version", "snmp_community", "snmp_port",
"snmp_interval", "snmp_timeout", "snmp_custom_oids"
]
for key in allowed_fields:
if key in data:
value = data[key]
# ⚠️ XỬ LÝ ĐẶC BIỆT: snmp_custom_oids là JSONB trong PostgreSQL
# Python dict cần chuyển sang JSON string trước khi INSERT/UPDATE
# Ví dụ: {"sysName": "1.3.6.1.2.1.1.5.0"} → '{"sysName": "1.3.6.1.2.1.1.5.0"}'
if key == "snmp_custom_oids" and isinstance(value, dict):
value = json.dumps(value)
update_fields.append(f"{key} = %s")
params.append(value)
if not update_fields:
# Không có trường nào cần cập nhật → trả về config hiện tại
return find_monitor_config_by_device_id(device_id)
# Thêm cập nhật thời gian modified
update_fields.append("modified = CURRENT_TIMESTAMP")
sql = f"""
UPDATE monitor_config
SET {', '.join(update_fields)}
WHERE device_id = %s
RETURNING id, device_id, enable_ping, ping_count, ping_timeout, ping_interval,
enable_snmp, snmp_version, snmp_community, snmp_port, snmp_interval,
snmp_timeout, snmp_custom_oids, created, modified
"""
params.append(device_id)
cur.execute(sql, tuple(params))
row = cur.fetchone()
conn.commit()
return _row_to_dict(row) if row else None
except Exception:
conn.rollback()
raise
finally:
if cur:
cur.close()
release_connection(conn)

View File

@@ -0,0 +1,164 @@
# pyrefly: ignore [missing-import]
from marshmallow import Schema, fields, validate
# ============================================
# SCHEMAS: Validate dữ liệu đầu vào cho Monitor Config
# ============================================
# Luồng đi: Client gửi JSON body → Controller nhận → Schema validate → Service xử lý
# Nếu dữ liệu không hợp lệ → Marshmallow ném ValidationError → Global handler trả 400
# ============================================
# Danh sách các SNMP version hệ thống hỗ trợ
VALID_SNMP_VERSIONS = ["v1", "v2c", "v3"]
class UpdateMonitorConfigSchema(Schema):
"""
Schema để validate dữ liệu khi cập nhật cấu hình giám sát.
Tất cả các trường đều optional vì người dùng có thể chỉ cập nhật một phần.
Ví dụ request body:
{
"enable_ping": true,
"ping_interval": 30,
"enable_snmp": true,
"snmp_community": "public",
"snmp_version": "v2c"
}
"""
# ─── Cấu hình Ping (ICMP) ────────────────────────
enable_ping = fields.Boolean(required=False)
ping_count = fields.Integer(
required=False,
validate=validate.Range(min=1, max=10)
# Số lượng gói tin ping gửi mỗi lần kiểm tra
# min=1: ít nhất 1 gói, max=10: tránh gửi quá nhiều gây tải mạng
)
ping_timeout = fields.Integer(
required=False,
validate=validate.Range(min=1, max=30)
# Thời gian chờ phản hồi tối đa (giây)
# Nếu thiết bị không phản hồi trong khoảng này → coi như timeout
)
ping_interval = fields.Integer(
required=False,
validate=validate.Range(min=5, max=86400)
# Tần suất kiểm tra (giây): min=5s (thiết bị quan trọng), max=86400s (1 ngày)
# APScheduler sẽ chạy job Ping theo interval này
)
# ─── Cấu hình SNMP ───────────────────────────────
enable_snmp = fields.Boolean(required=False)
snmp_version = fields.String(
required=False,
allow_none=True,
validate=validate.OneOf(VALID_SNMP_VERSIONS)
# Chỉ chấp nhận: "v1", "v2c", "v3"
)
snmp_community = fields.String(
required=False,
allow_none=True,
validate=validate.Length(max=256)
# Community string dùng để xác thực SNMP (ví dụ: "public", "private")
)
snmp_port = fields.Integer(
required=False,
allow_none=True,
validate=validate.Range(min=1, max=65535)
# Port SNMP mặc định là 161, nhưng cho phép tùy chỉnh
)
snmp_interval = fields.Integer(
required=False,
allow_none=True,
validate=validate.Range(min=5, max=86400)
# Tần suất kiểm tra SNMP (giây), tương tự ping_interval
)
snmp_timeout = fields.Integer(
required=False,
allow_none=True,
validate=validate.Range(min=1, max=30)
# Thời gian chờ phản hồi SNMP tối đa (giây)
)
snmp_custom_oids = fields.Dict(
required=False,
allow_none=True
# Danh sách OID tùy chỉnh dưới dạng JSON object
# Ví dụ: {"sysName": "1.3.6.1.2.1.1.5.0", "ifNumber": "1.3.6.1.2.1.2.1.0"}
# Lưu vào PostgreSQL dưới dạng JSONB
)
class TestConnectionSchema(Schema):
"""
Schema để validate dữ liệu khi người dùng bấm nút "Test kết nối".
Cho phép test trước khi lưu cấu hình — client truyền lên cấu hình tạm để test.
Luồng đi:
1. Người dùng nhập cấu hình trên giao diện (chưa bấm Lưu)
2. Bấm nút "Test" → Client gửi POST /api/devices/{id}/monitor-config/test
3. Server nhận cấu hình tạm → Chạy Ping/SNMP test ngay lập tức
4. Trả kết quả (Up/Down, RTT, chi tiết) → Hiển thị trên giao diện
5. Nếu OK → Người dùng mới bấm "Lưu" để PUT cập nhật cấu hình thực
"""
# Test ping hay snmp?
test_ping = fields.Boolean(
required=False,
load_default=True
# Mặc định test ping
)
test_snmp = fields.Boolean(
required=False,
load_default=False
)
# ─── Tham số Ping (dùng cho test) ─────────────────
ping_count = fields.Integer(
required=False,
load_default=3,
validate=validate.Range(min=1, max=10)
)
ping_timeout = fields.Integer(
required=False,
load_default=5,
validate=validate.Range(min=1, max=30)
)
# ─── Tham số SNMP (dùng cho test) ─────────────────
snmp_version = fields.String(
required=False,
allow_none=True,
validate=validate.OneOf(VALID_SNMP_VERSIONS)
)
snmp_community = fields.String(
required=False,
allow_none=True,
validate=validate.Length(max=256)
)
snmp_port = fields.Integer(
required=False,
allow_none=True,
load_default=161,
validate=validate.Range(min=1, max=65535)
)
snmp_timeout = fields.Integer(
required=False,
allow_none=True,
load_default=5,
validate=validate.Range(min=1, max=30)
)

View File

@@ -0,0 +1,323 @@
import subprocess
import platform
# ============================================
# SERVICE LAYER: Business Logic cho Module Monitor Config
# ============================================
# Luồng đi tổng thể của module này:
#
# ┌──────────┐ ┌──────────┐ ┌──────────────┐ ┌──────────┐
# │ Controller│ ──→ │ Service │ ──→ │ Repository │ ──→ │ Database │
# └──────────┘ └──────────┘ └──────────────┘ └──────────┘
# │
# ├──→ Device Repository (kiểm tra thiết bị tồn tại)
# ├──→ Scheduler (cập nhật job giám sát khi config thay đổi)
# └──→ ICMP/SNMP Test (kiểm tra kết nối trực tiếp)
#
# Service chứa 3 chức năng chính:
# 1. Lấy cấu hình giám sát (GET)
# 2. Cập nhật cấu hình giám sát (PUT) + reschedule job
# 3. Test kết nối trực tiếp (POST /test) — Ping hoặc SNMP
# ============================================
from modules.monitor_config.repository import (
find_monitor_config_by_device_id,
update_monitor_config_db
)
from modules.monitor_config.exceptions import MonitorConfigNotFoundException
from modules.device.repository import find_device_by_id
from modules.device.exceptions import DeviceNotFoundException
from scheduler.scheduler import reschedule_device_monitoring_job
# ============================================
# Dynamic Import: icmplib và pysnmp
# ============================================
# Tại sao cần Dynamic Import?
# - icmplib cần quyền root/sudo trên một số hệ điều hành để gửi ICMP raw socket
# - pysnmp có thể chưa được cài đặt trên máy phát triển
# - Nếu import thất bại → app vẫn chạy được, chỉ fallback sang cách khác
# ============================================
try:
from icmplib import ping as icmp_ping
HAS_ICMPLIB = True
except ImportError:
HAS_ICMPLIB = False
try:
from pysnmp.hlapi import (
SnmpEngine, CommunityData, UdpTransportTarget,
ContextData, ObjectType, ObjectIdentity, getCmd
)
HAS_PYSNMP = True
except ImportError:
HAS_PYSNMP = False
# ============================================
# 1. GET: Lấy cấu hình giám sát của thiết bị
# ============================================
# Luồng đi:
# GET /api/devices/{device_id}/monitor-config
# → Controller gọi hàm này
# → Kiểm tra device tồn tại (DeviceNotFoundException nếu không)
# → Kiểm tra config tồn tại (MonitorConfigNotFoundException nếu không)
# → Trả về dict cấu hình
# ============================================
def get_monitor_config_service(device_id):
# Bước 1: Kiểm tra thiết bị có tồn tại trong hệ thống không
device = find_device_by_id(device_id)
if not device:
raise DeviceNotFoundException(device_id)
# Bước 2: Lấy cấu hình giám sát từ DB
# (Bản ghi monitor_config được tạo tự động khi tạo device trong module device)
config = find_monitor_config_by_device_id(device_id)
if not config:
raise MonitorConfigNotFoundException(device_id)
return config
# ============================================
# 2. PUT: Cập nhật cấu hình giám sát
# ============================================
# Luồng đi:
# PUT /api/devices/{device_id}/monitor-config
# → Controller validate body bằng UpdateMonitorConfigSchema
# → Service gọi hàm này
# → Kiểm tra device + config tồn tại
# → Cập nhật DB (Repository)
# → Gọi Scheduler reschedule_job để áp dụng tần suất mới NGAY LẬP TỨC
# → Trả về config đã cập nhật
#
# Ví dụ: Người dùng đổi ping_interval từ 60s → 30s
# → DB được cập nhật
# → Scheduler reschedule job: lần kiểm tra tiếp theo sẽ chạy sau 30s thay vì 60s
# → KHÔNG cần restart server
# ============================================
def update_monitor_config_service(device_id, data):
# Bước 1: Kiểm tra thiết bị tồn tại
device = find_device_by_id(device_id)
if not device:
raise DeviceNotFoundException(device_id)
# Bước 2: Kiểm tra config tồn tại
existing_config = find_monitor_config_by_device_id(device_id)
if not existing_config:
raise MonitorConfigNotFoundException(device_id)
# Bước 3: Cập nhật vào DB
updated_config = update_monitor_config_db(device_id, data)
# Bước 4: Thông báo cho Scheduler cập nhật lại job
# reschedule_device_monitoring_job sẽ đọc config mới từ DB
# và áp dụng interval mới cho job ping/snmp
reschedule_device_monitoring_job(device_id, updated_config)
return updated_config
# ============================================
# 3. POST: Test kết nối (Ping hoặc SNMP)
# ============================================
# Luồng đi:
# POST /api/devices/{device_id}/monitor-config/test
# → Controller validate body bằng TestConnectionSchema
# → Service gọi hàm này
# → Lấy IP thiết bị từ DB
# → Chạy Ping test (nếu test_ping=True)
# → Chạy SNMP test (nếu test_snmp=True)
# → Trả về kết quả chi tiết {ping_result, snmp_result}
#
# Cơ chế Fallback cho Ping:
# Ưu tiên 1: icmplib (thư viện Python, cần cài pip install icmplib)
# → async_ping() hoặc ping() với privileged=False
# Ưu tiên 2: Lệnh ping hệ điều hành (subprocess)
# → Chạy "ping -c 3 <ip>" (Linux/Mac) hoặc "ping -n 3 <ip>" (Windows)
# → Parse kết quả từ stdout
# ============================================
def test_connection_service(device_id, test_params):
# Bước 1: Kiểm tra thiết bị tồn tại và lấy IP
device = find_device_by_id(device_id)
if not device:
raise DeviceNotFoundException(device_id)
ip_address = device["ip_address"]
results = {}
# Bước 2: Test Ping nếu được yêu cầu
if test_params.get("test_ping", True):
ping_count = test_params.get("ping_count", 3)
ping_timeout = test_params.get("ping_timeout", 5)
results["ping_result"] = _run_ping_test(ip_address, ping_count, ping_timeout)
# Bước 3: Test SNMP nếu được yêu cầu
if test_params.get("test_snmp", False):
snmp_params = {
"community": test_params.get("snmp_community", "public"),
"version": test_params.get("snmp_version", "v2c"),
"port": test_params.get("snmp_port", 161),
"timeout": test_params.get("snmp_timeout", 5)
}
results["snmp_result"] = _run_snmp_test(ip_address, snmp_params)
return results
# ============================================
# PRIVATE: Hàm chạy Ping test
# ============================================
# Cơ chế:
# 1. Thử dùng icmplib trước (nhanh, chính xác, có RTT)
# → privileged=False: không cần quyền root, dùng UDP probe
# 2. Nếu icmplib không có hoặc lỗi → Fallback sang subprocess
# → Chạy lệnh ping của hệ điều hành qua subprocess.run()
# → Parse returncode: 0 = thành công, != 0 = thất bại
# ============================================
def _run_ping_test(ip_address, count, timeout):
# ─── Ưu tiên 1: Dùng icmplib ─────────────────────
if HAS_ICMPLIB:
try:
# privileged=False: Không cần quyền root
# Trên macOS/Linux không root, icmplib sẽ dùng UDP probe
result = icmp_ping(
ip_address,
count=count,
timeout=timeout,
privileged=False
)
return {
"status": "up" if result.is_alive else "down",
"method": "icmplib",
"packets_sent": result.packets_sent,
"packets_received": result.packets_received,
"packet_loss": result.packet_loss, # 0.0 ~ 1.0
"avg_rtt_ms": round(result.avg_rtt, 2), # Thời gian phản hồi trung bình (ms)
"min_rtt_ms": round(result.min_rtt, 2),
"max_rtt_ms": round(result.max_rtt, 2)
}
except Exception as e:
# icmplib có nhưng gặp lỗi (ví dụ: permission denied)
# → Fallback sang subprocess
pass
# ─── Ưu tiên 2: Fallback sang lệnh ping hệ điều hành ───
try:
# Xác định tham số theo hệ điều hành
# Windows dùng -n (number), Linux/macOS dùng -c (count)
param = "-n" if platform.system().lower() == "windows" else "-c"
timeout_param = "-w" if platform.system().lower() == "windows" else "-W"
cmd = ["ping", param, str(count), timeout_param, str(timeout), ip_address]
result = subprocess.run(
cmd,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
timeout=timeout * count + 5 # Timeout tổng = timeout mỗi gói × số gói + buffer
)
is_alive = result.returncode == 0
return {
"status": "up" if is_alive else "down",
"method": "system_ping",
"detail": result.stdout.decode("utf-8", errors="replace")[:500]
# Giới hạn 500 ký tự đầu ra để tránh response quá lớn
}
except subprocess.TimeoutExpired:
return {
"status": "down",
"method": "system_ping",
"detail": f"Ping timeout after {timeout * count + 5} seconds"
}
except Exception as e:
return {
"status": "error",
"method": "system_ping",
"detail": str(e)
}
# ============================================
# PRIVATE: Hàm chạy SNMP test
# ============================================
# Cơ chế:
# 1. Dùng pysnmp gửi SNMP GET request lấy sysDescr (OID: 1.3.6.1.2.1.1.1.0)
# → Đây là OID tiêu chuẩn mà MỌI thiết bị SNMP đều hỗ trợ
# → Nếu lấy được giá trị → thiết bị Up + hỗ trợ SNMP
# 2. Nếu pysnmp không có → trả về lỗi yêu cầu cài đặt
#
# Tham số SNMP:
# - community: chuỗi xác thực (mặc định "public")
# - version: phiên bản SNMP (v1, v2c, v3)
# - port: cổng SNMP trên thiết bị (mặc định 161)
# - timeout: thời gian chờ phản hồi (giây)
# ============================================
def _run_snmp_test(ip_address, snmp_params):
if not HAS_PYSNMP:
return {
"status": "error",
"method": "snmp",
"detail": "pysnmp library is not installed. Run: pip install pysnmp"
}
try:
community = snmp_params.get("community", "public")
port = snmp_params.get("port", 161)
timeout_val = snmp_params.get("timeout", 5)
# Xác định mpModel (SNMP version) cho pysnmp
# v1 → mpModel=0, v2c → mpModel=1
version = snmp_params.get("version", "v2c")
mp_model = 0 if version == "v1" else 1
# Gửi SNMP GET request để lấy sysDescr
# OID: 1.3.6.1.2.1.1.1.0 = iso.org.dod.internet.mgmt.mib-2.system.sysDescr.0
# Đây là mô tả hệ thống — mọi thiết bị SNMP đều phải trả lời OID này
error_indication, error_status, error_index, var_binds = next(
getCmd(
SnmpEngine(),
CommunityData(community, mpModel=mp_model),
UdpTransportTarget((ip_address, port), timeout=timeout_val, retries=1),
ContextData(),
ObjectType(ObjectIdentity("1.3.6.1.2.1.1.1.0")) # sysDescr
)
)
# Phân tích kết quả
if error_indication:
# Lỗi transport: timeout, không kết nối được, v.v.
return {
"status": "down",
"method": "snmp",
"detail": str(error_indication)
}
elif error_status:
# Lỗi SNMP protocol: OID không tồn tại, quyền truy cập bị từ chối, v.v.
return {
"status": "down",
"method": "snmp",
"detail": f"SNMP error: {error_status.prettyPrint()} at {error_index}"
}
else:
# Thành công! Thiết bị phản hồi SNMP
snmp_data = {}
for var_bind in var_binds:
oid = str(var_bind[0])
value = str(var_bind[1])
snmp_data[oid] = value
return {
"status": "up",
"method": "snmp",
"snmp_data": snmp_data,
"detail": f"SNMP {version} response received successfully"
}
except Exception as e:
return {
"status": "error",
"method": "snmp",
"detail": f"SNMP test failed: {str(e)}"
}