Aruba CX Switch Network Analytics Engine (NAE) – 定期開關1/1/1介面PoE

入門介紹看此

Aruba CX Switch Network Analytics Engine (NAE) 入門 – JN的電腦網路日常

上回帶領瞭解與如何造出自己的NAE,本回會繼續深入,完成一項難以置信工程創舉,定期開關特定介面PoE,這個介面接著Aruba AP,因此能讓Aruba AP透過CX Switch NAE功能定時重啟!

本篇在Aruba AP接至特定介面並透過PoE供電為主,我們已知是什麼介面,所以針對該介面進行操作即可。

規劃

基礎:
每十分鐘觸發一次 > 檢查PoE Status() > 開啟 > 觸發指令關閉
每十分鐘觸發一次 > 檢查PoE Status > 關閉 > 觸發指令開啟

進階:
設定每週每月特定時間,計算下次觸發時間
每一分鐘觸發一次 > 檢查當前時間是否超過觸發時間 > 是 > 觸發指令關閉,並設定下次恢復為啟用
每一分鐘觸發一次 > 檢查下次恢復為啟用 > 是 > 觸發指令開啟,並設定下次恢復為關閉,計算與設定下次觸發時間。

本文只介紹基礎的設置。

回顧NAE Agent四個要件

「定期開關1/1/1介面PoE。」這目標。

定期為10min,若poe_status為disable,則enable。若poe_status為enable,則disable。
接著會繼續延伸,在每週四特定時間先Disable,再Enable,促使AP重啟。(選週四是因為筆者是在週四撰寫本文。

NAE Agent四要件就是:
監控(Moniter):透過REST API去取得特定介面1/1/1的poe_status。
規則(Rule):方便閱讀的條件說明「interface 1/1/1 poe_status」。
條件(Condition):一個判斷式,這邊定期10分鐘(every 600 seconds),。
行動(Action):傳入參數event,並執行類別函式,此函式則是依據狀態去下對應指令。

搭配上次的成果,如果有成功,我每20分鐘就能收到Telegram的通知,因為link_status down。

探索PoE_Status

一樣藉助CX Switch的WebUI,並使用其中的API。

記得先Login

找到能看到介面PoE狀態的API

介面「config」中有「admin_disable」。

只需要紀錄config.admin_disable,URL要改為以下

/rest/v10.13/system/interfaces/1%2F1%2F1/poe_interface?attributes=config.admin_disable

# .後面能接屬性,這樣能確切指出是config.admin_disable的值

知道目標狀態的URL在哪,本文不使用Monitor監控,而是會透過REST API取得狀態。

※筆者對於REST API最有信心,查不太各類別內建支援什麼method的相關說明文件,只好看官方NAE Scripts,嘗試self.get_rest_request_json(HTTP_ADDRESS + uri, retry=3)中,但無進展。

Action寫法

https://arubanetworking.hpe.com/techdocs/AOS-CX/10.13/HTML/nae/Content/Chp_Scrpt/act-cli-act.htm

#關閉1/1/1的PoE狀態
config
interface 1/1/1
no power-over-ethernet
exit
exit

#轉換成Python
ActionCLI("config\ninterface 1/1/1\nno power-over-ethernet\nexit\nexit")

# 打開poe版本
ActionCLI("config\ninterface 1/1/1\npower-over-ethernet\nexit\nexit")

# 轉換成Python 第二種寫法
ActionCLI(
    f'config\n'
    f'interface {self.params["specific_poe_interface"]}\n'
    f'no power-over-ethernet\n'
    f'exit\n'
    f'exit'
)

Time Series Data Record

NAE是時間序列,使用every時,會去對齊交換器的時間,而不是從開始時間去計算。

例如:我使用every 10 minute,就會在00:00、00:10、00:20、00:30、00:40、00:50準時執行,而不是從agent開始時間點計算。

要比擬的話,linux系統的Crontab類似。

透過Rest API取得CX Switch Status

由於CX Switch支援外部Requests,本文是使用本身URL與API,技術上仍可以延伸至取得其他台CX Switch狀態與控制。

上次例子使用telegram API,這次是使用CX Switch API。

CX Switch API使用流程在先前也有提過,先login,再執行你的安排,最後logout。本文範例流程是登入,取得PoE Status,最後登出,然後回傳PoE Status。

ParameterDefinitions = {
    'API_URL': {
        'Name': 'POE_STATUS_URL',
        'Description': 'POE_STATUS_URL',
        'Type': 'string',
        'Required': True,
        'Default': 'https://172.26.1.104/rest/v10.13/'
    },
    'API_ACCOUNT': {
        'Name': 'API_ACCOUNT',
        'Description': 'API_ACCOUNT',
        'Type': 'string',
        'Required': True,
        'Default': ''
    },
    'API_PASSWORD': {
        'Name': 'API_PASSWORD',
        'Description': 'API_PASSWORD',
        'Type': 'string',
        'Required': True,
        'Default': ''
    }               
    }

......

    def getPoEStatus(self):
        BASE_URL = self.params["API_URL"].value
        USERNAME = self.params["API_ACCOUNT"].value
        PASSWORD = self.params["API_PASSWORD"].value      

        session = requests.Session()

        login_url = BASE_URL + 'login?'
        resp = session.post(
            login_url,
            params={'username': USERNAME, 'password': PASSWORD},
            verify=False,
            timeout=5
        )
        resp.raise_for_status()

        poe_url = BASE_URL + 'system/interfaces/' + self.int_encoded + '/poe_interface?attributes=config'
        resp = session.get(poe_url, verify=False, timeout=5)
        resp.raise_for_status()
        payload = resp.json()               

        poe_status = True
        admin_disable = payload.get('config', {}).get('admin_disable', False)  
        if admin_disable:
            poe_status = False 
            
            self.logger.info("admin_disable = True ")
        else:
            
            self.logger.info("admin_disable = False ")
        logout_url = BASE_URL + 'logout'
        resp = session.post(logout_url, verify=False, timeout=5)
        return poe_status

成果

本範例Monitor不是必要的,沒有Monitor也能正常運行。

透過Rule與Condition定期執行,定期透過REST API取得狀態,然後決定CX Switch要使用什麼指令。

# jnnetlab.com
# JN
from urllib.parse import quote
import requests

Manifest = {
    'Name': 'jnpoe2',
    'Description': 'interface_poe_disable_and_enable',
    'Version': '1',
    'Author': 'JN'
}

ParameterDefinitions = {
    'poe_int': {
        'Name': 'poe_int',
        'Description': 'poe_int',
        'Type': 'string',
        'Required': True,
        'Default': '1/1/1'
    },
    'API_URL': {
        'Name': 'POE_STATUS_URL',
        'Description': 'POE_STATUS_URL',
        'Type': 'string',
        'Required': True,
        'Default': 'https://172.26.1.104/rest/v10.13/'
    },
    'API_ACCOUNT': {
        'Name': 'API_ACCOUNT',
        'Description': 'API_ACCOUNT',
        'Type': 'string',
        'Required': True,
        'Default': ''
    },
    'API_PASSWORD': {
        'Name': 'API_PASSWORD',
        'Description': 'API_PASSWORD',
        'Type': 'string',
        'Required': True,
        'Default': ''
    }               
    }

class Agent(NAE):
    def __init__(self):
        self.int_encoded = quote(self.params["poe_int"].value, safe="")
        self.r = Rule('Periodic Check')
        self.r.condition("every 120 seconds")
        self.r.action(self.timed_action)

    def timed_action(self, event):
        ActionSyslog("every 120 seconds")
        if self.getPoEStatus():
            ActionCLI(
                    f'config\n'
                    f'interface {self.params["poe_int"]}\n'
                    f'no power-over-ethernet\n'
                    f'exit\n'
                    f'exit'
                )
            self.set_alert_level(AlertLevel.CRITICAL)
        else:
            ActionCLI(
                    f'config\n'
                    f'interface {self.params["poe_int"]}\n'
                    f'power-over-ethernet\n'
                    f'exit\nexit'
                )            
            self.remove_alert_level()



    def getPoEStatus(self):
        BASE_URL = self.params["API_URL"].value
        USERNAME = self.params["API_ACCOUNT"].value
        PASSWORD = self.params["API_PASSWORD"].value      

        session = requests.Session()

        login_url = BASE_URL + 'login?'
        resp = session.post(
            login_url,
            params={'username': USERNAME, 'password': PASSWORD},
            verify=False,
            timeout=5
        )
        resp.raise_for_status()

        poe_url = BASE_URL + 'system/interfaces/' + self.int_encoded + '/poe_interface?attributes=config'
        resp = session.get(poe_url, verify=False, timeout=5)
        resp.raise_for_status()
        payload = resp.json()               

        poe_status = True
        admin_disable = payload.get('config', {}).get('admin_disable', False)  
        if admin_disable:
            poe_status = False 
            
            self.logger.info("admin_disable = True ")
        else:
            
            self.logger.info("admin_disable = False ")
        logout_url = BASE_URL + 'logout'
        resp = session.post(logout_url, verify=False, timeout=5)
        return poe_status

搭配我上一回的NAE,可以看出週期性的UP與DOWN,也間接證明腳本正常運作。

※為了讓成果可以快速呈現,我調短週期。

每次通電時,都會先短暫UP然後DOWN,再次UP才會穩定。