身為網路管理者,隨時掌握網路裝置的連線狀態至關重要。本文將引導您使用 Python 建立一個主動式監控系統,結合 Docker 的便利性與 Sendmail 的通知功能,實作高效的網路管理。

Python 網路自動化的優勢

Python 擁有豐富的網路函式庫,例如 socketsmtplib,簡化了網路程式設計的複雜度,同時具備跨平台特性,讓您的監控程式碼在不同作業系統上都能順暢執行。

實戰演練:建構網路監控程式

以下步驟將逐步引導您在 Docker 環境中建立 Python 網路監控程式。

步驟一:設定 Docker 環境

首先,請確認您的系統已安裝 Docker,接著啟動一個 Ubuntu 20.04 的 Docker 容器:

docker run -it --name pynetauto_ubuntu20sendmail -p 20-22:20-22 -p 25:25 -p 12020-12025:12020-12025 pynetauto/pynetauto_ubuntu20 /bin/bash

步驟二:建立專案目錄

在 Docker 容器中,建立並進入 monitoring 目錄:

mkdir /home/monitoring && cd /home/monitoring

步驟三:建立 Python 檔案

monitoring 目錄下,建立 monitor.pyemail_notifier.py 兩個檔案:

touch monitor.py email_notifier.py

步驟四:編寫連線檢查程式碼 (monitor.py)

首先,建立 monitor.py,用於檢查目標裝置的 SSH 連線埠 (port 22) 是否開啟:

import socket
import time
from datetime import datetime

TARGET_IP = '192.168.1.10'  # 目標裝置 IP 位址
TARGET_PORT = 22  # SSH 連線埠
TIMEOUT = 3  # 連線逾時時間(秒)
MAX_FAILURES = 10 # 最大連線失敗次數

def check_connection(ip, port):
    try:
        with socket.create_connection((ip, port), timeout=TIMEOUT) as s:
            return True
    except OSError:
        return False

def monitor_device():
    failure_count = 0
    while True:
        with open('monitoring_log.txt', 'a') as log_file:
            current_time = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
            if check_connection(TARGET_IP, TARGET_PORT):
                log_file.write(f"{current_time} 連線埠 {TARGET_PORT} 已開啟\n")
                print(f"連線埠 {TARGET_PORT} 已開啟")
                failure_count = 0
            else:
                failure_count += 1
                log_file.write(f"{current_time} 連線埠 {TARGET_PORT} 已關閉\n")
                print(f"連線埠 {TARGET_PORT} 已關閉")
                if failure_count >= MAX_FAILURES:
                    send_alert()  # 傳送警示通知
                    failure_count = 0 # 重置計數器
            time.sleep(3) # 間隔三秒檢查

def send_alert():
    from email_notifier import send_email
    send_email("您的裝置連線中斷!", "請立即檢查您的裝置連線狀態。") # 傳送警示郵件

if __name__ == "__main__":
    monitor_device()

此程式碼使用 socket.create_connection() 函式檢查連線狀態,並將結果記錄至 monitoring_log.txt 檔案。若連線失敗次數達到 MAX_FAILURES,則呼叫 send_alert() 函式傳送警示。

步驟五:編寫電子郵件通知程式碼 (email_notifier.py)

接著,建立 email_notifier.py,負責傳送電子郵件警示:

import smtplib
from email.mime.text import MIMEText

def send_email(subject, message):
    sender_email = "your_sender_email@gmail.com"  # 寄件者 email
    receiver_email = "your_receiver_email@example.com"  # 收件者 email
    password = "your_email_password"  # 您的 email 密碼或應用程式密碼

    msg = MIMEText(message)
    msg["Subject"] = subject
    msg["From"] = sender_email
    msg["To"] = receiver_email

    try:
        with smtplib.SMTP_SSL("smtp.gmail.com", 465) as server:
            server.login(sender_email, password)
            server.send_message(msg)
        print("已傳送電子郵件通知")
    except Exception as e:
        print(f"傳送電子郵件失敗: {e}")

此程式碼使用 smtplib 函式庫傳送電子郵件通知。請將 sender_emailreceiver_emailpassword 替換為您的實際資訊。

  graph LR
    B[B]
    D[D]
    No[No]
    Yes[Yes]
    A[monitor.py] --> B{連線正常?};
    B -- Yes --> C[持續監控];
    B -- No --> D{連續失敗次數 >= 10?};
    D -- Yes --> E[email_notifier.py];
    E --> F[傳送 Email 通知];
    D -- No --> C;

圖表説明: 此流程圖展示了監控程式的運作邏輯,清晰呈現了程式如何根據連線狀態執行不同操作。

這個案例示範瞭如何使用 Python 建立一個主動式網路裝置連線狀態監控系統。透過結合 Docker 和 Sendmail,您可以輕鬆地佈署和管理您的監控系統。

這個架構可以進一步擴充套件,例如整合其他監控指標,例如 CPU 使用率、記憶體使用量等,並根據不同的警示級別傳送不同型別的通知,例如 SMS 或 Slack 訊息。 透過持續的改進和最佳化,您可以開發一個更加完善和強大的網路監控系統。

import subprocess
import re
import time
from twilio.rest import Client
from easysnmp import Session

# --- 設定區段 ---
ROUTER_IP = "192.168.1.1"  # 路由器 IP 位址,請修改為你的裝置 IP
SNMP_COMMUNITY = "public"  # SNMP Community 字串,請修改為你的裝置設定
CPU_THRESHOLD = 90  # CPU 使用率閾值 (%)
CHECK_INTERVAL = 300  # 檢查間隔 (秒)

# Twilio 設定 (請替換為你的帳號資訊)
TWILIO_ACCOUNT_SID = "ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
TWILIO_AUTH_TOKEN = "your_auth_token"
TWILIO_PHONE_NUMBER = "+11234567890"
MY_PHONE_NUMBER = "+886912345678"

# --- SNMP 查詢函式 ---
def get_cpu_utilization(router_ip, snmp_community):
    try:
        session = Session(hostname=router_ip, community=snmp_community, version=2)
        cpu_oid = '1.3.6.1.4.1.9.9.109.1.1.1.1.5' # 五分鐘平均 CPU 使用率 OID
        cpu_utilization = int(session.get(cpu_oid).value)
        return cpu_utilization
    except Exception as e:
        print(f"SNMP 查詢錯誤: {e}")
        return None

# --- Twilio 簡訊傳送函式 ---
def send_sms_alert(message):
    try:
        client = Client(TWILIO_ACCOUNT_SID, TWILIO_AUTH_TOKEN)
        message = client.messages.create(
            body=message,
            from_=TWILIO_PHONE_NUMBER,
            to=MY_PHONE_NUMBER
        )
        print(f"已傳送簡訊警示 (SID: {message.sid})")
    except Exception as e:
        print(f"簡訊傳送失敗: {e}")

# --- CPU 監控主迴圈 ---
if __name__ == "__main__":
    while True:
        cpu_usage = get_cpu_utilization(ROUTER_IP, SNMP_COMMUNITY)

        if cpu_usage is not None:
            current_time = time.strftime("%Y-%m-%d %H:%M:%S")
            print(f"{current_time} - 路由器 {ROUTER_IP} CPU 使用率: {cpu_usage}%")

            if cpu_usage > CPU_THRESHOLD:
                alert_message = f"警告:路由器 {ROUTER_IP} CPU 使用率過高 ({cpu_usage}%)!"
                send_sms_alert(alert_message)

        time.sleep(CHECK_INTERVAL)

此程式碼整合了 SNMP 和 Twilio 的功能,實作了 CPU 負載監控和告警。它利用 easysnmp 函式庫簡化了 SNMP 查詢的過程,並使用 twilio 函式庫傳送 SMS 警示。程式碼中包含了錯誤處理機制,以應對 SNMP 查詢或簡訊傳送失敗的情況。設定區段允許使用者自定義路由器 IP、SNMP Community、CPU 閾值、檢查間隔以及 Twilio 帳號資訊。主迴圈持續監控 CPU 使用率,並在超過閾值時傳送警示。

  graph LR
    A[啟動監控程式] --> B{SNMP 查詢 CPU 使用率}
    B -- 成功 --> C{CPU 使用率 > 閾值?}
    C -- 是 --> D[傳送 Twilio SMS 警示]
    C -- 否 --> E[等待下一次檢查]
    B -- 失敗 --> F[記錄錯誤]
    D --> E
    F --> E

圖表説明: 此流程圖描述了 CPU 監控程式的執行流程。程式首先透過 SNMP 查詢 CPU 使用率。如果查詢成功,則檢查 CPU 使用率是否超過設定的閾值。如果超過閾值,則傳送 Twilio SMS 警示。無論查詢成功與否,程式都會等待一段時間後再次執行檢查。

這個程式碼範例提供了一個更完善與易於理解的 CPU 監控解決方案,並包含了詳細的註解和錯誤處理機制,更貼近實際應用場景。

這個版本更精簡與功能更強大,並符合玄貓的程式碼風格和註解規範。

import subprocess
import os
import time
from twilio.rest import Client  # 匯入 Twilio 模組

# 設定 Twilio 帳戶 SID 和 Auth Token
account_sid = "ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"  # 替換為您的帳戶 SID
auth_token = "your_auth_token"  # 替換為您的 Auth Token
twilio_phone_number = "+11234567890"  # 替換為您的 Twilio 電話號碼
recipient_phone_number = "+886912345678"  # 替換為接收簡訊的手機號碼


class NetworkDevice:  # 基礎類別,定義網路裝置的共同屬性
    def __init__(self, ip_address, hostname, device_type):
        self.ip_address = ip_address
        self.hostname = hostname
        self.device_type = device_type

    def connect(self):  # 連線至裝置
        print(f"連線至 {self.device_type}: {self.hostname} ({self.ip_address})")

    def get_cpu_utilization(self):  # 取得 CPU 使用率
        try:
            output = subprocess.check_output(["snmpwalk", "-v", "2c", "-c", "public", self.ip_address, "iso.3.6.1.4.1.9.9.109.1.1.1.1.5.1"], stderr=subprocess.STDOUT, text=True, timeout=5)  # 設定 timeout
            cpu_util = int(output.split("Gauge32: ")[1].strip())
            return cpu_util
        except subprocess.TimeoutExpired:
            return "Timeout: No Response from {}".format(self.ip_address)
        except (subprocess.CalledProcessError, IndexError, ValueError):
            return "Error: Could not retrieve CPU utilization from {}".format(self.ip_address)


class Router(NetworkDevice):  # 路由器類別,繼承自 NetworkDevice
    def __init__(self, ip_address, hostname, model, routing_protocol):
        super().__init__(ip_address, hostname, "路由器")  # 呼叫父類別的建構子
        self.model = model
        self.routing_protocol = routing_protocol

    def configure_routing(self):
        print(f"設定路由協定: {self.routing_protocol}")


class Switch(NetworkDevice):  # 交換器類別,繼承自 NetworkDevice
    def __init__(self, ip_address, hostname, model, vlan):
        super().__init__(ip_address, hostname, "交換器")  # 呼叫父類別的建構子
        self.model = model
        self.vlan = vlan

    def configure_vlan(self):
        print(f"設定 VLAN: {self.vlan}")



def send_sms_alert(message):  # 使用 Twilio API 傳送 SMS 告警
    try:
        client = Client(account_sid, auth_token)
        message = client.messages.create(
            body=message,
            from_=twilio_phone_number,
            to=recipient_phone_number
        )
        print(f"已傳送 SMS 告警:{message.sid}")
    except Exception as e:
        print(f"傳送 SMS 告警失敗:{e}")



# 建立路由器物件
r1 = Router("192.168.1.1", "R1", "Cisco 8000v", "OSPF")
r1.connect()


# 監控 CPU 使用率並傳送告警
while True:
    cpu_utilization = r1.get_cpu_utilization()
    if isinstance(cpu_utilization, int):
        print(f"CPU 使用率:{cpu_utilization}%")
        if cpu_utilization > 90:
            alert_message = f"{r1.hostname} ({r1.ip_address}) CPU 使用率過高:{cpu_utilization}%"
            send_sms_alert(alert_message)
    else:
        print(cpu_utilization)  # 輸出錯誤訊息
        alert_message = f"{r1.hostname} ({r1.ip_address}) CPU 使用率取得失敗:{cpu_utilization}"
        send_sms_alert(alert_message)

    time.sleep(60)  # 每 60 秒檢查一次

這個程式碼版本進行了以下改進:

  1. 物件導向程式設計: 使用類別 NetworkDeviceRouterSwitch 來組織程式碼,並利用繼承和多型等 OOP 概念。
  2. 錯誤處理: get_cpu_utilization 方法加入了 try...except 區塊來處理潛在的錯誤,例如 SNMP 超時、命令執行錯誤、資料解析錯誤等。並提供更具體的錯誤訊息。
  3. Timeout 設定:subprocess.check_output 中加入 timeout 引數,避免程式無限期等待 SNMP 回應。
  4. Twilio 整合: 加入 send_sms_alert 函式,使用 Twilio API 傳送 SMS 告警。請記得替換程式碼中的佔位符為您的實際 Twilio 帳戶資訊和電話號碼。
  5. 迴圈監控: 使用 while True 迴圈持續監控 CPU 使用率,並設定 time.sleep(60) 每 60 秒檢查一次。

這個版本更具結構性、可讀性和可維護性,並且能夠更有效地處理錯誤和傳送告警。

  sequenceDiagram
    participant User
    participant Python Script
    participant Router

    User->>Python Script: 執行指令碼
    activate Python Script
    Python Script->>Router: SNMP 查詢 CPU 使用率
    activate Router
    Router-->>Python Script: CPU 使用率資料
    deactivate Router
    Python Script->>Python Script: 判斷 CPU 使用率
    Python Script-->>User: 顯示 CPU 使用率
    alt CPU 使用率 > 90%
        Python Script->>Twilio: 傳送 SMS 告警
        activate Twilio
        Twilio-->>Python Script: 告警傳送成功
        deactivate Twilio
        Python Script-->>User: 顯示告警訊息
    end
    deactivate Python Script

圖表説明: 這個序列圖描述了 Python 指令碼如何與路由器互動並透過 Twilio 傳送 SMS 告警的流程。

注意: 請確保已安裝必要的 Python 模組:twiliopysnmp。可以使用 pip 安裝:pip install twilio pysnmp

這個修改後的版本更符合玄貓的風格,並包含更詳細的程式碼説明和 圖表。

from getpass import getpass

def get_credentials():
    username1 = input("請輸入網路管理員 1 的 ID:")
    password1 = None
    while not password1:
        password1 = getpass("請輸入網路管理員 1 的密碼:")
        password1_verify = getpass("請確認網路管理員 1 的密碼:")
        if password1 != password1_verify:
            print("密碼不符,請重新輸入。")
            password1 = None

    print("使用者名稱 1:", username1)  # 移除密碼顯示

    yes_or_no = input("網路管理員 2 的憑證是否與網路管理員 1 相同?(Yes/No):").lower()
    expected_response = ['yes', 'y', 'no', 'n']
    while yes_or_no not in expected_response:
        yes_or_no = input("請輸入 yes 或 no:").lower()

    if yes_or_no in ('yes', 'y'):
        username2 = username1
        password2 = password1
        print("使用者名稱 2:", username2) # 移除密碼顯示
    else:
        username2 = input("請輸入網路管理員 2 的 ID:")
        password2 = None
        while not password2:
            password2 = getpass("請輸入網路管理員 2 的密碼:")
            password2_verify = getpass("請確認網路管理員 2 的密碼:")
            if password2 != password2_verify:
                print("密碼不符,請重新輸入。")
                password2 = None
        print("使用者名稱 2:", username2) # 移除密碼顯示

get_credentials()

此程式碼強化了使用者憑證收集的安全性,加入了密碼驗證機制。使用者需要輸入兩次密碼,確保輸入一致。同時,為了安全起見,程式碼不再顯示使用者輸入的密碼。

  graph LR
    B[B]
    C[C]
    D[D]
    G[G]
    H[H]
    No[No]
    Yes[Yes]
    A[輸入使用者名稱 1] --> B{輸入密碼 1};
    B --> C{確認密碼 1};
    C -- 密碼相符 --> D{詢問管理員 2 憑證是否相同};
    C -- 密碼不符 --> B;
    D -- Yes --> E[使用相同憑證];
    D -- No --> F[輸入使用者名稱 2];
    F --> G{輸入密碼 2};
    G --> H{確認密碼 2};
    H -- 密碼相符 --> I[完成];
    H -- 密碼不符 --> G;

圖表説明: 此流程圖描述了使用者憑證收集的流程,包含密碼驗證和憑證複用選項。

透過逐步完善 yes/no 輸入和使用者憑證收集的程式碼,我們展示瞭如何構建更強健、更安全的 Python 應用程式。妥善處理使用者輸入和驗證,是開發任何互動式應用程式的重要環節。

手動升級 IOS XE:逐步解析

在開始自動化之前,先了解手動升級的步驟非常重要。這有助於理解整個流程,並為後續的自動化指令碼編寫打下基礎。

  1. 準備工作: 首先,您需要準備好 Cisco Catalyst 8000v 的 .ova 檔案和升級用的 .bin 映像檔。這些檔案可以透過 Cisco 官方管道取得。

  2. 佈署虛擬機器: 使用 .ova 檔案在 VMware Workstation 上佈署虛擬機器。設定網路介面,確保虛擬路由器可以連線到網路。

  3. 連線到路由器: 使用終端軟體,例如 PuTTY 或 SecureCRT,連線到虛擬路由器。

  4. 上傳映像檔: 使用 copy 指令將 .bin 映像檔上傳到路由器的快閃記憶體中。例如:copy tftp://<TFTP 伺服器 IP 位址>/<映像檔名稱> flash:

  5. 驗證映像檔: 使用 verify /md5 flash:<映像檔名稱> 指令驗證上傳的映像檔完整性。

  6. 設定啟動組態: 使用 configure terminal 進入全域組態模式,並使用 boot system flash:<映像檔名稱> 指令設定路由器在下次啟動時使用新的映像檔。

  7. 儲存組態: 使用 copy running-config startup-config 指令儲存組態。

  8. 重新啟動路由器: 使用 reload 指令重新啟動路由器,使新的 IOS XE 映像檔生效。

  graph LR
    A[準備 .ova 和 .bin 檔案] --> B(佈署虛擬機器)
    B --> C(連線到路由器)
    C --> D(上傳映像檔)
    D --> E(驗證映像檔)
    E --> F(設定啟動組態)
    F --> G(儲存組態)
    G --> H(重新啟動路由器)

以上流程圖展示了手動升級 IOS XE 的步驟。每個步驟都至關重要,確保升級過程順利完成。

Python 自動化升級:提升效率

手動升級過程繁瑣與容易出錯。使用 Python 指令碼可以自動化這些步驟,提高效率並減少錯誤。以下是一個 Python 指令碼範例,使用 netmiko 函式庫與路由器互動:

from netmiko import ConnectHandler

def upgrade_ios_xe(device, image_file, tftp_server):
    try:
        with ConnectHandler(**device) as ssh:
            ssh.enable()
            ssh.send_command_timing(f"copy tftp://{tftp_server}/{image_file} flash:") # 複製映像檔
            ssh.send_command_timing("verify /md5 flash:" + image_file) # 驗證映像檔
            ssh.send_config_set(["boot system flash:" + image_file]) # 設定啟動組態
            ssh.send_command_timing("copy running-config startup-config") # 儲存組態
            ssh.send_command_timing("reload") # 重新啟動路由器
            print("IOS XE 升級完成!")
    except Exception as e:
        print(f"升級過程中發生錯誤:{e}")

# 裝置資訊
device = {
    "device_type": "cisco_ios",
    "host": "<路由器 IP 位址>",
    "username": "<使用者名稱>",
    "password": "<密碼>",
}

# 映像檔和 TFTP 伺服器資訊
image_file = "<映像檔名稱>"
tftp_server = "<TFTP 伺服器 IP 位址>"

# 執行升級
upgrade_ios_xe(device, image_file, tftp_server)

這個 Python 指令碼使用 netmiko 函式庫建立與路由器的 SSH 連線,並自動執行上傳映像檔、驗證、設定啟動組態、儲存組態和重新啟動等步驟。